From 3405c7e31393ca8924b422e2e8c97d152af2a51c Mon Sep 17 00:00:00 2001 From: Daniel Schmitz Date: Thu, 24 Mar 2016 01:26:57 +0800 Subject: [PATCH 0001/1335] Let's Encrypt: Always regenerate a new account-key, if staging --- lib/classes/ssl/class.lescript.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/classes/ssl/class.lescript.php b/lib/classes/ssl/class.lescript.php index 16db00e3..32ba7d09 100644 --- a/lib/classes/ssl/class.lescript.php +++ b/lib/classes/ssl/class.lescript.php @@ -50,7 +50,7 @@ class lescript { // Let's see if we have the private accountkey $this->accountKey = $certrow['leprivatekey']; - if (!$this->accountKey || $this->accountKey == 'unset') { + if (!$this->accountKey || $this->accountKey == 'unset' || Settings::Get('system.letsencryptca') != 'production') { // generate and save new private key for account // --------------------------------------------- From 032a991b8f4a0c07f0ca7490c97c443ffd406eaf Mon Sep 17 00:00:00 2001 From: eis_os Date: Thu, 31 Mar 2016 15:02:04 +0200 Subject: [PATCH 0002/1335] Simplify master cronjob --- scripts/froxlor_master_cronjob.php | 42 ++++++++++++++---------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/scripts/froxlor_master_cronjob.php b/scripts/froxlor_master_cronjob.php index 5db7af5b..3e4923d4 100644 --- a/scripts/froxlor_master_cronjob.php +++ b/scripts/froxlor_master_cronjob.php @@ -20,7 +20,6 @@ define('MASTER_CRONJOB', 1); include_once dirname(dirname(__FILE__)) . '/lib/cron_init.php'; $jobs_to_run = array(); -$lastrun_update = array(); /** * check for --help @@ -45,7 +44,6 @@ for ($x = 1; $x < count($argv); $x++) { if (isset($argv[$x])) { // --force if (strtolower($argv[$x]) == '--force') { - $crontasks = makeCorrectFile(FROXLOR_INSTALL_DIR.'/scripts/jobs/cron_tasks.php'); // really force re-generating of config-files by // inserting task 1 inserttask('1'); @@ -53,8 +51,7 @@ for ($x = 1; $x < count($argv); $x++) { inserttask('4'); // also regenerate cron.d-file inserttask('99'); - addToQueue($jobs_to_run, $crontasks); - $lastrun_update['tasks'] = $crontasks; + addToQueue($jobs_to_run, 'tasks'); } elseif (strtolower($argv[$x]) == '--debug') { define('CRON_DEBUG_FLAG', 1); @@ -62,9 +59,8 @@ for ($x = 1; $x < count($argv); $x++) { // --[cronname] elseif (substr(strtolower($argv[$x]), 0, 2) == '--') { if (strlen($argv[$x]) > 3) { - $cronfile = makeCorrectFile(FROXLOR_INSTALL_DIR.'/scripts/jobs/cron_'.substr(strtolower($argv[$x]), 2).'.php'); - addToQueue($jobs_to_run, $cronfile); - $lastrun_update[substr(strtolower($argv[$x]), 2)] = $cronfile; + $cronname = substr(strtolower($argv[$x]), 2); + addToQueue($jobs_to_run, $cronname); } } } @@ -76,8 +72,9 @@ $cronlog->setCronDebugFlag(defined('CRON_DEBUG_FLAG')); if (count($jobs_to_run) > 0) { // include all jobs we want to execute foreach ($jobs_to_run as $cron) { - updateLastRunOfCron($lastrun_update, $cron); - require_once $cron; + updateLastRunOfCron($cron); + $cronfile = getCronFile($cron); + require_once $cronfile; } } @@ -95,21 +92,22 @@ checkLastGuid(); include_once FROXLOR_INSTALL_DIR . '/lib/cron_shutdown.php'; // -- helper function -function addToQueue(&$jobs_to_run, $cronfile = null, $checkExists = true) { - if ($checkExists == false || ($checkExists && file_exists($cronfile))) { - if (!in_array($cronfile, $jobs_to_run)) { - array_unshift($jobs_to_run, $cronfile); +function getCronFile($cronname) { + return makeCorrectFile(FROXLOR_INSTALL_DIR.'/scripts/jobs/cron_'.$cronname.'.php'); +} + +function addToQueue(&$jobs_to_run, $cronname) { + if (!in_array($cronname, $jobs_to_run)) { + $cronfile = getCronFile($cronname); + if (file_exists($cronfile)) { + array_unshift($jobs_to_run, $cronname); } } } -function updateLastRunOfCron($update_arr, $cronfile) { - foreach ($update_arr as $cron => $cronf) { - if ($cronf == $cronfile) { - $upd_stmt = Database::prepare(" - UPDATE `".TABLE_PANEL_CRONRUNS."` SET `lastrun` = UNIX_TIMESTAMP() WHERE `cronfile` = :cron; - "); - Database::pexecute($upd_stmt, array('cron' => $cron)); - } - } +function updateLastRunOfCron($cronname) { + $upd_stmt = Database::prepare(" + UPDATE `".TABLE_PANEL_CRONRUNS."` SET `lastrun` = UNIX_TIMESTAMP() WHERE `cronfile` = :cron; + "); + Database::pexecute($upd_stmt, array('cron' => $cronname)); } From c14017c2444f61024e0a5fdcd4bb54c2a8b6d349 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 7 Apr 2016 07:56:16 +0200 Subject: [PATCH 0003/1335] fix display of path in customer-extras Signed-off-by: Michael Kaufmann (d00p) --- customer_extras.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/customer_extras.php b/customer_extras.php index 9d85f1f9..06a27a09 100644 --- a/customer_extras.php +++ b/customer_extras.php @@ -53,9 +53,9 @@ if ($page == 'overview') { while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) { if ($paging->checkDisplay($i)) { if (strpos($row['path'], $userinfo['documentroot']) === 0) { - $row['path'] = str_replace($userinfo['documentroot'], "/", $row['path']); + $row['path'] = str_replace($userinfo['documentroot'], "/", $row['path']); } - + $row['path'] = makeCorrectDir($row['path']); $row = htmlentities_array($row); eval("\$htpasswds.=\"" . getTemplate("extras/htpasswds_htpasswd") . "\";"); $count++; @@ -271,7 +271,7 @@ if ($page == 'overview') { if (strpos($row['path'], $userinfo['documentroot']) === 0) { $row['path'] = str_replace($userinfo['documentroot'], "/", $row['path']); } - + $row['path'] = makeCorrectDir($row['path']); $row['options_indexes'] = str_replace('1', $lng['panel']['yes'], $row['options_indexes']); $row['options_indexes'] = str_replace('0', $lng['panel']['no'], $row['options_indexes']); $row['options_cgi'] = str_replace('1', $lng['panel']['yes'], $row['options_cgi']); From 57f9c439f2fbfa341e2e57106fae2af0648cf163 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Fri, 8 Apr 2016 12:54:17 +0200 Subject: [PATCH 0004/1335] set version to 0.9.35 final for upcoming release Signed-off-by: Michael Kaufmann (d00p) --- install/froxlor.sql | 2 +- .../updates/froxlor/0.9/update_0.9.inc.php | 1185 +++++++++-------- lib/version.inc.php | 2 +- 3 files changed, 651 insertions(+), 538 deletions(-) diff --git a/install/froxlor.sql b/install/froxlor.sql index fb16e4af..26750025 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -555,7 +555,7 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES ('panel', 'password_numeric', '0'), ('panel', 'password_special_char_required', '0'), ('panel', 'password_special_char', '!?<>§$%+#=@'), - ('panel', 'version', '0.9.35-rc1'), + ('panel', 'version', '0.9.35'), ('panel', 'db_version', '201603150'); diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index 6ebc6c81..3d3be715 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -14,14 +14,9 @@ * @package Install * */ - -if (!defined('AREA') - || (defined('AREA') && AREA != 'admin') - || !isset($userinfo['loginname']) - || (isset($userinfo['loginname']) && $userinfo['loginname'] == '') -) { +if (! defined('AREA') || (defined('AREA') && AREA != 'admin') || ! isset($userinfo['loginname']) || (isset($userinfo['loginname']) && $userinfo['loginname'] == '')) { header('Location: ../../../../index.php'); - exit; + exit(); } if (isFroxlorVersion('0.9-r0')) { @@ -30,12 +25,11 @@ if (isFroxlorVersion('0.9-r0')) { showUpdateStep("Performing database updates"); // add missing database-updates if necessary (old: update/update_database.php) - if (Settings::Get('system.dbversion') !== null && (int)Settings::Get('system.dbversion') < 1) { + if (Settings::Get('system.dbversion') !== null && (int) Settings::Get('system.dbversion') < 1) { Database::query(" - ALTER TABLE `panel_databases` ADD `dbserver` INT( 11 ) UNSIGNED NOT NULL default '0';" - ); + ALTER TABLE `panel_databases` ADD `dbserver` INT( 11 ) UNSIGNED NOT NULL default '0';"); } - if (Settings::Get('system.dbversion') !== null && (int)Settings::Get('system.dbversion') < 2) { + if (Settings::Get('system.dbversion') !== null && (int) Settings::Get('system.dbversion') < 2) { Database::query("ALTER TABLE `panel_ipsandports` CHANGE `ssl_cert` `ssl_cert_file` VARCHAR( 255 ) NOT NULL, ADD `ssl_key_file` VARCHAR( 255 ) NOT NULL, ADD `ssl_ca_file` VARCHAR( 255 ) NOT NULL, @@ -46,7 +40,7 @@ if (isFroxlorVersion('0.9-r0')) { } // eof(lostuff) - //remove billing tables in database + // remove billing tables in database define('TABLE_BILLING_INVOICES', 'billing_invoices'); define('TABLE_BILLING_INVOICES_ADMINS', 'billing_invoices_admins'); define('TABLE_BILLING_INVOICE_CHANGES', 'billing_invoice_changes'); @@ -195,9 +189,7 @@ if (isFroxlorVersion('0.9-r1')) { Database::query("INSERT INTO `" . TABLE_PANEL_SETTINGS . "` (`settinggroup`, `varname`, `value`) VALUES ('spf', 'spf_entry', '@ IN TXT \"v=spf1 a mx -all\"');"); Database::query("UPDATE `" . TABLE_PANEL_SETTINGS . "` SET `varname` = 'froxlor_graphic' WHERE `varname` = 'syscp_graphic'"); - if (Settings::Get('admin.syscp_graphic') !== null - && Settings::Get('admin.syscp_graphic') != '' - ){ + if (Settings::Get('admin.syscp_graphic') !== null && Settings::Get('admin.syscp_graphic') != '') { Settings::Set('admin.froxlor_graphic', Settings::Get('admin.syscp_graphic')); } else { Settings::Set('admin.froxlor_graphic', 'images/header.gif'); @@ -241,30 +233,30 @@ if (isFroxlorVersion('0.9-r3')) { // checking for active ticket-module $ticket_active = 0; - if ((int)Settings::Get('ticket.enabled') == 1) { + if ((int) Settings::Get('ticket.enabled') == 1) { $ticket_active = 1; } // checking for active aps-module $aps_active = 0; - if ((int)Settings::Get('aps.aps_active') == 1) { + if ((int) Settings::Get('aps.aps_active') == 1) { $aps_active = 1; } // checking for active autoresponder-module $ar_active = 0; - if ((int)Settings::Get('autoresponder.autoresponder_active') == 1) { + if ((int) Settings::Get('autoresponder.autoresponder_active') == 1) { $ar_active = 1; } Database::query("INSERT INTO `cronjobs_run` (`module`, `cronfile`, `interval`, `isactive`, `desc_lng_key`) VALUES ('froxlor/core', 'cron_tasks.php', '5 MINUTE', '1', 'cron_tasks');"); Database::query("INSERT INTO `cronjobs_run` (`module`, `cronfile`, `interval`, `isactive`, `desc_lng_key`) VALUES ('froxlor/core', 'cron_legacy.php', '5 MINUTE', '1', 'cron_legacy');"); - Database::query("INSERT INTO `cronjobs_run` (`module`, `cronfile`, `interval`, `isactive`, `desc_lng_key`) VALUES ('froxlor/aps', 'cron_apsinstaller.php', '5 MINUTE', ".$aps_active.", 'cron_apsinstaller');"); - Database::query("INSERT INTO `cronjobs_run` (`module`, `cronfile`, `interval`, `isactive`, `desc_lng_key`) VALUES ('froxlor/autoresponder', 'cron_autoresponder.php', '5 MINUTE', ".$ar_active.", 'cron_autoresponder');"); - Database::query("INSERT INTO `cronjobs_run` (`module`, `cronfile`, `interval`, `isactive`, `desc_lng_key`) VALUES ('froxlor/aps', 'cron_apsupdater.php', '1 HOUR', ".$aps_active.", 'cron_apsupdater');"); + Database::query("INSERT INTO `cronjobs_run` (`module`, `cronfile`, `interval`, `isactive`, `desc_lng_key`) VALUES ('froxlor/aps', 'cron_apsinstaller.php', '5 MINUTE', " . $aps_active . ", 'cron_apsinstaller');"); + Database::query("INSERT INTO `cronjobs_run` (`module`, `cronfile`, `interval`, `isactive`, `desc_lng_key`) VALUES ('froxlor/autoresponder', 'cron_autoresponder.php', '5 MINUTE', " . $ar_active . ", 'cron_autoresponder');"); + Database::query("INSERT INTO `cronjobs_run` (`module`, `cronfile`, `interval`, `isactive`, `desc_lng_key`) VALUES ('froxlor/aps', 'cron_apsupdater.php', '1 HOUR', " . $aps_active . ", 'cron_apsupdater');"); Database::query("INSERT INTO `cronjobs_run` (`module`, `cronfile`, `interval`, `isactive`, `desc_lng_key`) VALUES ('froxlor/core', 'cron_traffic.php', '1 DAY', '1', 'cron_traffic');"); - Database::query("INSERT INTO `cronjobs_run` (`module`, `cronfile`, `interval`, `isactive`, `desc_lng_key`) VALUES ('froxlor/ticket', 'cron_used_tickets_reset.php', '1 MONTH', '".$ticket_active."', 'cron_ticketsreset');"); - Database::query("INSERT INTO `cronjobs_run` (`module`, `cronfile`, `interval`, `isactive`, `desc_lng_key`) VALUES ('froxlor/ticket', 'cron_ticketarchive.php', '1 MONTH', '".$ticket_active."', 'cron_ticketarchive');"); + Database::query("INSERT INTO `cronjobs_run` (`module`, `cronfile`, `interval`, `isactive`, `desc_lng_key`) VALUES ('froxlor/ticket', 'cron_used_tickets_reset.php', '1 MONTH', '" . $ticket_active . "', 'cron_ticketsreset');"); + Database::query("INSERT INTO `cronjobs_run` (`module`, `cronfile`, `interval`, `isactive`, `desc_lng_key`) VALUES ('froxlor/ticket', 'cron_ticketarchive.php', '1 MONTH', '" . $ticket_active . "', 'cron_ticketarchive');"); lastStepStatus(0); showUpdateStep("Updating old settings values"); @@ -297,13 +289,10 @@ if (isFroxlorVersion('0.9.1')) { showUpdateStep("Updating from 0.9.1 to 0.9.2", false); showUpdateStep("Checking whether last-system-guid is sane"); - $result_stmt = Database::query("SELECT MAX(`guid`) as `latestguid` FROM `".TABLE_PANEL_CUSTOMERS."`"); + $result_stmt = Database::query("SELECT MAX(`guid`) as `latestguid` FROM `" . TABLE_PANEL_CUSTOMERS . "`"); $result = $result_stmt->fetch(PDO::FETCH_ASSOC); - if (isset($result['latestguid']) - && (int)$result['latestguid'] > 0 - && $result['latestguid'] != Settings::Get('system.lastguid') - ) { + if (isset($result['latestguid']) && (int) $result['latestguid'] > 0 && $result['latestguid'] != Settings::Get('system.lastguid')) { checkLastGuid(); lastStepStatus(1, 'fixed'); } else { @@ -349,14 +338,14 @@ if (isFroxlorVersion('0.9.3-svn2')) { showUpdateStep("Correcting cron start-times"); // set specific times for some crons (traffic only at night, etc.) $ts = mktime(0, 0, 0, date('m', time()), date('d', time()), date('Y', time())); - Database::query("UPDATE `".TABLE_PANEL_CRONRUNS."` SET `lastrun` = '".$ts."' WHERE `cronfile` ='cron_traffic.php';"); + Database::query("UPDATE `" . TABLE_PANEL_CRONRUNS . "` SET `lastrun` = '" . $ts . "' WHERE `cronfile` ='cron_traffic.php';"); $ts = mktime(1, 0, 0, date('m', time()), date('d', time()), date('Y', time())); - Database::query("UPDATE `".TABLE_PANEL_CRONRUNS."` SET `lastrun` = '".$ts."' WHERE `cronfile` ='cron_used_tickets_reset.php';"); - Database::query("UPDATE `".TABLE_PANEL_CRONRUNS."` SET `lastrun` = '".$ts."' WHERE `cronfile` ='cron_ticketarchive.php';"); + Database::query("UPDATE `" . TABLE_PANEL_CRONRUNS . "` SET `lastrun` = '" . $ts . "' WHERE `cronfile` ='cron_used_tickets_reset.php';"); + Database::query("UPDATE `" . TABLE_PANEL_CRONRUNS . "` SET `lastrun` = '" . $ts . "' WHERE `cronfile` ='cron_ticketarchive.php';"); lastStepStatus(0); showUpdateStep("Adding new language: Polish"); - Database::query("INSERT INTO `".TABLE_PANEL_LANGUAGE."` SET `language` = 'Polski', `file` = 'lng/polish.lng.php'"); + Database::query("INSERT INTO `" . TABLE_PANEL_LANGUAGE . "` SET `language` = 'Polski', `file` = 'lng/polish.lng.php'"); lastStepStatus(0); updateToVersion('0.9.3-svn3'); @@ -403,29 +392,21 @@ if (isFroxlorVersion('0.9.4')) { * some users might still have the setting in their database * because we already had this back in older versions. * To not confuse Froxlor, we just update old settings. - */ - if(Settings::Get('system.awstats_path') !== null - && Settings::Get('system.awstats_path') != '' - ) { + */ + if (Settings::Get('system.awstats_path') !== null && Settings::Get('system.awstats_path') != '') { showUpdateStep("Updating awstats path setting"); Database::query("UPDATE `" . TABLE_PANEL_SETTINGS . "` SET `value` = '/usr/bin/' WHERE `settinggroup` = 'system' AND `varname` = 'awstats_path';"); lastStepStatus(0); - } - elseif(Settings::Get('system.awstats_path') == null) - { + } elseif (Settings::Get('system.awstats_path') == null) { showUpdateStep("Adding new awstats path setting"); Database::query("INSERT INTO `" . TABLE_PANEL_SETTINGS . "` (`settinggroup`, `varname`, `value`) VALUES ('system', 'awstats_path', '/usr/bin/');"); lastStepStatus(0); } - if(Settings::Get('system.awstats_domain_file') !== null - && Settings::Get('system.awstats_domain_file') != '' - ) { + if (Settings::Get('system.awstats_domain_file') !== null && Settings::Get('system.awstats_domain_file') != '') { showUpdateStep("Updating awstats configuration path setting"); Database::query("UPDATE `" . TABLE_PANEL_SETTINGS . "` SET `varname` = 'awstats_conf' WHERE `varname` = 'awstats_domain_file';"); - } - else - { + } else { showUpdateStep("Adding awstats configuration path settings"); Database::query("INSERT INTO `" . TABLE_PANEL_SETTINGS . "` (`settinggroup`, `varname`, `value`) VALUES ('system', 'awstats_conf', '/etc/awstats/');"); } @@ -446,15 +427,15 @@ if (isFroxlorVersion('0.9.4-svn1')) { if ($update_domains == 1) { showUpdateStep("Updating domains with iswildcarddomain=yes"); - $query = "SELECT `d`.`id` FROM `".TABLE_PANEL_DOMAINS."` `d`, `".TABLE_PANEL_CUSTOMERS."` `c` "; - $query.= "WHERE `parentdomainid`='0' AND `email_only` = '0' AND `d`.`customerid` = `c`.`customerid` AND `d`.`id` <> `c`.`standardsubdomain`"; + $query = "SELECT `d`.`id` FROM `" . TABLE_PANEL_DOMAINS . "` `d`, `" . TABLE_PANEL_CUSTOMERS . "` `c` "; + $query .= "WHERE `parentdomainid`='0' AND `email_only` = '0' AND `d`.`customerid` = `c`.`customerid` AND `d`.`id` <> `c`.`standardsubdomain`"; $result = Database::query($query); $updated_domains = 0; while ($domain = $result->fetch(PDO::FETCH_ASSOC)) { - Database::query("UPDATE `".TABLE_PANEL_DOMAINS."` SET `iswildcarddomain` = '1' WHERE `id` ='".(int)$domain['id']."'"); - $updated_domains++; + Database::query("UPDATE `" . TABLE_PANEL_DOMAINS . "` SET `iswildcarddomain` = '1' WHERE `id` ='" . (int) $domain['id'] . "'"); + $updated_domains ++; } - lastStepStatus(0, 'Updated '.$updated_domains.' domain(s)'); + lastStepStatus(0, 'Updated ' . $updated_domains . ' domain(s)'); } else { showUpdateStep("Won't update domains with iswildcarddomain=yes as requested"); lastStepStatus(1); @@ -497,15 +478,16 @@ if (isFroxlorVersion('0.9.6-svn1')) { if ($update_adminmail !== false) { showUpdateStep("Checking newly entered admin-mail"); - if (!PHPMailer::ValidateAddress($update_adminmail)) { + if (! PHPMailer::ValidateAddress($update_adminmail)) { $do_update = false; lastStepStatus(2, 'E-Mail still not valid, go back and try again'); } else { $stmt = Database::prepare(" UPDATE `" . TABLE_PANEL_SETTINGS . "` SET `value` = :adminmail - WHERE `settinggroup` = 'panel' AND `varname` = 'adminmail';" - ); - Database::pexecute($stmt, array('adminmail' => $update_adminmail)); + WHERE `settinggroup` = 'panel' AND `varname` = 'adminmail';"); + Database::pexecute($stmt, array( + 'adminmail' => $update_adminmail + )); lastStepStatus(0); } } @@ -534,51 +516,54 @@ if (isFroxlorVersion('0.9.6-svn2')) { INSERT INTO `" . TABLE_PANEL_SETTINGS . "` SET `settinggroup` = 'defaultwebsrverrhandler', `varname` = :varname, - `value` = :err" - ); + `value` = :err"); - if (isset($_POST['update_deferr_500']) - && trim($_POST['update_deferr_500']) != '' - ) { - Database::pexecute($stmt, array('varname' => 'err500', 'err' => $_POST['update_deferr_500'])); + if (isset($_POST['update_deferr_500']) && trim($_POST['update_deferr_500']) != '') { + Database::pexecute($stmt, array( + 'varname' => 'err500', + 'err' => $_POST['update_deferr_500'] + )); $err500 = true; } - if(isset($_POST['update_deferr_401']) - && trim($_POST['update_deferr_401']) != '' - ) { - Database::pexecute($stmt, array('varname' => 'err401', 'err' => $_POST['update_deferr_401'])); + if (isset($_POST['update_deferr_401']) && trim($_POST['update_deferr_401']) != '') { + Database::pexecute($stmt, array( + 'varname' => 'err401', + 'err' => $_POST['update_deferr_401'] + )); $err401 = true; } - if(isset($_POST['update_deferr_403']) - && trim($_POST['update_deferr_403']) != '' - ) { - Database::pexecute($stmt, array('varname' => 'err403', 'err' => $_POST['update_deferr_403'])); + if (isset($_POST['update_deferr_403']) && trim($_POST['update_deferr_403']) != '') { + Database::pexecute($stmt, array( + 'varname' => 'err403', + 'err' => $_POST['update_deferr_403'] + )); $err403 = true; } - if(isset($_POST['update_deferr_404']) - && trim($_POST['update_deferr_404']) != '' - ) { - Database::pexecute($stmt, array('varname' => 'err404', 'err' => $_POST['update_deferr_404'])); + if (isset($_POST['update_deferr_404']) && trim($_POST['update_deferr_404']) != '') { + Database::pexecute($stmt, array( + 'varname' => 'err404', + 'err' => $_POST['update_deferr_404'] + )); $err404 = true; } } - if(!$update_deferr_enable) { + if (! $update_deferr_enable) { Database::query("INSERT INTO `" . TABLE_PANEL_SETTINGS . "` (`settinggroup`, `varname`, `value`) VALUES ('defaultwebsrverrhandler', 'enabled', '0');"); } - if(!$err401) { + if (! $err401) { Database::query("INSERT INTO `" . TABLE_PANEL_SETTINGS . "` (`settinggroup`, `varname`, `value`) VALUES ('defaultwebsrverrhandler', 'err401', '');"); } - if(!$err403) { + if (! $err403) { Database::query("INSERT INTO `" . TABLE_PANEL_SETTINGS . "` (`settinggroup`, `varname`, `value`) VALUES ('defaultwebsrverrhandler', 'err403', '');"); } - if(!$err404) { + if (! $err404) { Database::query("INSERT INTO `" . TABLE_PANEL_SETTINGS . "` (`settinggroup`, `varname`, `value`) VALUES ('defaultwebsrverrhandler', 'err404', '');"); } - if(!$err500) { + if (! $err500) { Database::query("INSERT INTO `" . TABLE_PANEL_SETTINGS . "` (`settinggroup`, `varname`, `value`) VALUES ('defaultwebsrverrhandler', 'err500', '');"); } @@ -594,7 +579,7 @@ if (isFroxlorVersion('0.9.6-svn3')) { $update_deftic_priority = isset($_POST['update_deftic_priority']) ? intval($_POST['update_deftic_priority']) : 2; showUpdateStep("Setting default support-ticket priority"); - Database::query("INSERT INTO `" . TABLE_PANEL_SETTINGS . "` (`settinggroup`, `varname`, `value`) VALUES ('ticket', 'default_priority', '".(int)$update_deftic_priority."');"); + Database::query("INSERT INTO `" . TABLE_PANEL_SETTINGS . "` (`settinggroup`, `varname`, `value`) VALUES ('ticket', 'default_priority', '" . (int) $update_deftic_priority . "');"); lastStepStatus(0); updateToVersion('0.9.6-svn4'); @@ -606,13 +591,13 @@ if (isFroxlorVersion('0.9.6-svn4')) { $update_defsys_phpconfig = isset($_POST['update_defsys_phpconfig']) ? intval($_POST['update_defsys_phpconfig']) : 1; - if($update_defsys_phpconfig != 1) { - showUpdateStep("Setting default php-configuration to user defined config #".$update_defsys_phpconfig); + if ($update_defsys_phpconfig != 1) { + showUpdateStep("Setting default php-configuration to user defined config #" . $update_defsys_phpconfig); } else { showUpdateStep("Adding default php-configuration setting to the database"); } - Database::query("INSERT INTO `" . TABLE_PANEL_SETTINGS . "` (`settinggroup`, `varname`, `value`) VALUES ('system', 'mod_fcgid_defaultini', '".(int)$update_defsys_phpconfig."');"); + Database::query("INSERT INTO `" . TABLE_PANEL_SETTINGS . "` (`settinggroup`, `varname`, `value`) VALUES ('system', 'mod_fcgid_defaultini', '" . (int) $update_defsys_phpconfig . "');"); lastStepStatus(0); updateToVersion('0.9.6-svn5'); @@ -627,7 +612,9 @@ if (isFroxlorVersion('0.9.6-svn5')) { // add ftp server setting $stmt = Database::prepare("INSERT INTO `panel_settings` SET `settinggroup` = 'system', `varname` = 'ftpserver', `value` = :value;"); - Database::pexecute($stmt, array('value' => $update_defsys_ftpserver)); + Database::pexecute($stmt, array( + 'value' => $update_defsys_ftpserver + )); // add proftpd quota Database::query("CREATE TABLE `ftp_quotalimits` (`name` varchar(30) default NULL, `quota_type` enum('user','group','class','all') NOT NULL default 'user', `per_session` enum('false','true') NOT NULL default 'false', `limit_type` enum('soft','hard') NOT NULL default 'hard', `bytes_in_avail` float NOT NULL, `bytes_out_avail` float NOT NULL, `bytes_xfer_avail` float NOT NULL, `files_in_avail` int(10) unsigned NOT NULL, `files_out_avail` int(10) unsigned NOT NULL, `files_xfer_avail` int(10) unsigned NOT NULL) ENGINE=MyISAM;"); @@ -640,8 +627,7 @@ if (isFroxlorVersion('0.9.6-svn5')) { while ($row_ftp_users = $result_ftp_users_stmt->fetch(PDO::FETCH_ASSOC)) { $result_ftp_quota_stmt = Database::query(" SELECT diskspace_used FROM `" . TABLE_PANEL_CUSTOMERS . "` - WHERE loginname = SUBSTRING_INDEX('" . $row_ftp_users['username'] . "', '" . Settings::Get('customer.ftpprefix') . "', 1);" - ); + WHERE loginname = SUBSTRING_INDEX('" . $row_ftp_users['username'] . "', '" . Settings::Get('customer.ftpprefix') . "', 1);"); $row_ftp_quota = $result_ftp_quota_stmt->fetch(PDO::FETCH_ASSOC); Database::query("INSERT INTO `ftp_quotatallies` (`name`, `quota_type`, `bytes_in_used`, `bytes_out_used`, `bytes_xfer_used`, `files_in_used`, `files_out_used`, `files_xfer_used`) VALUES ('" . $row_ftp_users['username'] . "', 'user', '" . $row_ftp_quota['diskspace_used'] . "'*1024, '0', '0', '0', '0', '0');"); } @@ -661,7 +647,7 @@ if (isFroxlorVersion('0.9.6')) { showUpdateStep("Updating from 0.9.6 to 0.9.7-svn1", false); $update_customredirect_enable = isset($_POST['update_customredirect_enable']) ? 1 : 0; - $update_customredirect_default = isset($_POST['update_customredirect_default']) ? (int)$_POST['update_customredirect_default'] : 1; + $update_customredirect_default = isset($_POST['update_customredirect_default']) ? (int) $_POST['update_customredirect_default'] : 1; showUpdateStep("Adding new tables to database"); Database::query("CREATE TABLE IF NOT EXISTS `redirect_codes` ( @@ -687,17 +673,17 @@ if (isFroxlorVersion('0.9.6')) { lastStepStatus(0); showUpdateStep("Updating domains"); - $res = Database::query("SELECT `id` FROM `".TABLE_PANEL_DOMAINS."` ORDER BY `id` ASC"); + $res = Database::query("SELECT `id` FROM `" . TABLE_PANEL_DOMAINS . "` ORDER BY `id` ASC"); $updated_domains = 0; while ($d = $res->fetch(PDO::FETCH_ASSOC)) { - Database::query("INSERT INTO `domain_redirect_codes` (`rid`, `did`) VALUES ('".(int)$update_customredirect_default."', '".(int)$d['id']."');"); - $updated_domains++; + Database::query("INSERT INTO `domain_redirect_codes` (`rid`, `did`) VALUES ('" . (int) $update_customredirect_default . "', '" . (int) $d['id'] . "');"); + $updated_domains ++; } - lastStepStatus(0, 'Updated '.$updated_domains.' domain(s)'); + lastStepStatus(0, 'Updated ' . $updated_domains . ' domain(s)'); showUpdateStep("Adding new settings"); - Database::query("INSERT INTO `" . TABLE_PANEL_SETTINGS . "` (`settinggroup`, `varname`, `value`) VALUES ('customredirect', 'enabled', '".(int)$update_customredirect_enable."');"); - Database::query("INSERT INTO `" . TABLE_PANEL_SETTINGS . "` (`settinggroup`, `varname`, `value`) VALUES ('customredirect', 'default', '".(int)$update_customredirect_default."');"); + Database::query("INSERT INTO `" . TABLE_PANEL_SETTINGS . "` (`settinggroup`, `varname`, `value`) VALUES ('customredirect', 'enabled', '" . (int) $update_customredirect_enable . "');"); + Database::query("INSERT INTO `" . TABLE_PANEL_SETTINGS . "` (`settinggroup`, `varname`, `value`) VALUES ('customredirect', 'default', '" . (int) $update_customredirect_default . "');"); lastStepStatus(0); // need to fix default-error-copy-and-paste-shizzle @@ -723,7 +709,7 @@ if (isFroxlorVersion('0.9.7-svn1')) { showUpdateStep("Updating open_basedir due to security - issue"); $result = Database::query("SELECT `id` FROM `" . TABLE_PANEL_DOMAINS . "` WHERE `documentroot` LIKE '%:%' AND `documentroot` NOT LIKE 'http://%' AND `openbasedir_path` = '0' AND `openbasedir` = '1'"); while ($row = $result->fetch(PDO::FETCH_ASSOC)) { - Database::query("UPDATE `".TABLE_PANEL_DOMAINS."` SET `openbasedir_path` = '1' WHERE `id` = '" . (int)$row['id']."'"); + Database::query("UPDATE `" . TABLE_PANEL_DOMAINS . "` SET `openbasedir_path` = '1' WHERE `id` = '" . (int) $row['id'] . "'"); } lastStepStatus(0); @@ -767,7 +753,7 @@ if (isFroxlorVersion('0.9.8')) { $update_defdns_mailentry = isset($_POST['update_defdns_mailentry']) ? '1' : '0'; showUpdateStep("Adding new settings"); - Database::query("INSERT INTO `" . TABLE_PANEL_SETTINGS . "` (`settinggroup`, `varname`, `value`) VALUES ('system', 'dns_createmailentry', '".(int)$update_defdns_mailentry."');"); + Database::query("INSERT INTO `" . TABLE_PANEL_SETTINGS . "` (`settinggroup`, `varname`, `value`) VALUES ('system', 'dns_createmailentry', '" . (int) $update_defdns_mailentry . "');"); lastStepStatus(0); updateToVersion('0.9.9-svn1'); @@ -796,9 +782,10 @@ if (isFroxlorVersion('0.9.9')) { INSERT INTO `" . TABLE_PANEL_SETTINGS . "` SET `settinggroup` = 'system', `varname` = 'httpuser', - `value` = :user" - ); - Database::pexecute($stmt, array(':user' => $update_httpuser)); + `value` = :user"); + Database::pexecute($stmt, array( + ':user' => $update_httpuser + )); lastStepStatus(0); Settings::Set('system.httpuser', $update_httpuser); } @@ -810,9 +797,10 @@ if (isFroxlorVersion('0.9.9')) { INSERT INTO `" . TABLE_PANEL_SETTINGS . "` SET `settinggroup` = 'system', `varname` = 'httpgroup', - `value` = :grp" - ); - Database::pexecute($stmt, array(':grp' => $update_httpgroup)); + `value` = :grp"); + Database::pexecute($stmt, array( + ':grp' => $update_httpgroup + )); lastStepStatus(0); Settings::Set('system.httpgroup', $update_httpgroup); } @@ -820,7 +808,7 @@ if (isFroxlorVersion('0.9.9')) { $result_stmt = Database::query("SELECT * FROM `" . TABLE_PANEL_SETTINGS . "` WHERE `settinggroup` = 'system' AND `varname` = 'debug_cron'"); $result = $result_stmt->fetch(PDO::FETCH_ASSOC); - if (!isset($result) || !isset($result['value'])) { + if (! isset($result) || ! isset($result['value'])) { $nonefound = false; showUpdateStep("Adding missing setting 'debug_cron'"); Database::query("INSERT INTO `" . TABLE_PANEL_SETTINGS . "` (`settinggroup`, `varname`, `value`) VALUES ('system', 'debug_cron', '0');"); @@ -860,20 +848,19 @@ if (isFroxlorVersion('0.9.10-svn1')) { `databasename` = :dbname, `description` = :dbdesc, `dbserver` = '0', - `apsdb` = '1'" - ); + `apsdb` = '1'"); Database::pexecute($result, array( 'cid' => $cid, 'dbname' => $row['Database'], - 'dbdesc' => $databasedescription, + 'dbdesc' => $databasedescription )); - Database::query('UPDATE `' . TABLE_PANEL_CUSTOMERS . '` SET `mysqls_used`=`mysqls_used`+1 WHERE `customerid`="' . (int)$cid . '"'); - $count_dbupdates++; + Database::query('UPDATE `' . TABLE_PANEL_CUSTOMERS . '` SET `mysqls_used`=`mysqls_used`+1 WHERE `customerid`="' . (int) $cid . '"'); + $count_dbupdates ++; } } if ($count_dbupdates > 0) { - lastStepStatus(0, "Found ".$count_dbupdates." customer APS databases"); + lastStepStatus(0, "Found " . $count_dbupdates . " customer APS databases"); } else { lastStepStatus(0, "None found"); } @@ -885,10 +872,10 @@ if (isFroxlorVersion('0.9.10-svn2')) { showUpdateStep("Updating from 0.9.10-svn2 to 0.9.10", false); - $update_directlyviahostname = isset($_POST['update_directlyviahostname']) ? (int)$_POST['update_directlyviahostname'] : '0'; + $update_directlyviahostname = isset($_POST['update_directlyviahostname']) ? (int) $_POST['update_directlyviahostname'] : '0'; showUpdateStep("Adding new settings"); - Database::query("INSERT INTO `" . TABLE_PANEL_SETTINGS . "` (`settinggroup`, `varname`, `value`) VALUES ('system', 'froxlordirectlyviahostname', '".(int)$update_directlyviahostname."');"); + Database::query("INSERT INTO `" . TABLE_PANEL_SETTINGS . "` (`settinggroup`, `varname`, `value`) VALUES ('system', 'froxlordirectlyviahostname', '" . (int) $update_directlyviahostname . "');"); lastStepStatus(0); updateToVersion('0.9.10'); @@ -904,9 +891,10 @@ if (isFroxlorVersion('0.9.10')) { INSERT INTO `" . TABLE_PANEL_SETTINGS . "` SET `settinggroup` = 'panel', `varname` = 'password_regex', - `value` = :regex" - ); - Database::pexecute($stmt, array('regex' => $update_pwdregex)); + `value` = :regex"); + Database::pexecute($stmt, array( + 'regex' => $update_pwdregex + )); lastStepStatus(0); updateToVersion('0.9.11-svn1'); @@ -916,8 +904,8 @@ if (isFroxlorVersion('0.9.11-svn1')) { showUpdateStep("Updating from 0.9.11-svn1 to 0.9.11-svn2", false); showUpdateStep("Adding perl/CGI directory fields"); - Database::query("ALTER TABLE `".TABLE_PANEL_HTACCESS."` ADD `options_cgi` tinyint(1) NOT NULL default '0' AFTER `error401path`;"); - Database::query("ALTER TABLE `".TABLE_PANEL_CUSTOMERS."` ADD `perlenabled` tinyint(1) NOT NULL default '0' AFTER `aps_packages_used`;"); + Database::query("ALTER TABLE `" . TABLE_PANEL_HTACCESS . "` ADD `options_cgi` tinyint(1) NOT NULL default '0' AFTER `error401path`;"); + Database::query("ALTER TABLE `" . TABLE_PANEL_CUSTOMERS . "` ADD `perlenabled` tinyint(1) NOT NULL default '0' AFTER `aps_packages_used`;"); lastStepStatus(0); updateToVersion('0.9.11-svn2'); @@ -933,9 +921,10 @@ if (isFroxlorVersion('0.9.11-svn2')) { INSERT INTO `" . TABLE_PANEL_SETTINGS . "` SET `settinggroup` = 'system', `varname` = 'perl_path', - `value` = :path" - ); - Database::pexecute($stmt, array('path' => $update_perlpath)); + `value` = :path"); + Database::pexecute($stmt, array( + 'path' => $update_perlpath + )); lastStepStatus(0); updateToVersion('0.9.11-svn3'); @@ -950,14 +939,14 @@ if (isFroxlorVersion('0.9.11-svn3')) { if (isFroxlorVersion('0.9.11')) { showUpdateStep("Updating from 0.9.11 to 0.9.12-svn1", false); - $update_fcgid_ownvhost = isset($_POST['update_fcgid_ownvhost']) ? (int)$_POST['update_fcgid_ownvhost'] : '0'; + $update_fcgid_ownvhost = isset($_POST['update_fcgid_ownvhost']) ? (int) $_POST['update_fcgid_ownvhost'] : '0'; $update_fcgid_httpuser = isset($_POST['update_fcgid_httpuser']) ? $_POST['update_fcgid_httpuser'] : 'froxlorlocal'; $update_fcgid_httpgroup = isset($_POST['update_fcgid_httpgroup']) ? $_POST['update_fcgid_httpgroup'] : 'froxlorlocal'; - if($update_fcgid_httpuser == '') { + if ($update_fcgid_httpuser == '') { $update_fcgid_httpuser = 'froxlorlocal'; } - if($update_fcgid_httpgroup == '') { + if ($update_fcgid_httpgroup == '') { $update_fcgid_httpgroup = 'froxlorlocal'; } @@ -966,11 +955,19 @@ if (isFroxlorVersion('0.9.11')) { INSERT INTO `" . TABLE_PANEL_SETTINGS . "` SET `settinggroup` = 'system', `varname` = :varname, - `value` = :value" - ); - Database::pexecute($stmt, array('varname' => 'mod_fcgid_ownvhost', 'value' => $update_fcgid_ownvhost)); - Database::pexecute($stmt, array('varname' => 'mod_fcgid_httpuser', 'value' => $update_fcgid_httpuser)); - Database::pexecute($stmt, array('varname' => 'mod_fcgid_httpgroup', 'value' => $update_fcgid_httpgroup)); + `value` = :value"); + Database::pexecute($stmt, array( + 'varname' => 'mod_fcgid_ownvhost', + 'value' => $update_fcgid_ownvhost + )); + Database::pexecute($stmt, array( + 'varname' => 'mod_fcgid_httpuser', + 'value' => $update_fcgid_httpuser + )); + Database::pexecute($stmt, array( + 'varname' => 'mod_fcgid_httpgroup', + 'value' => $update_fcgid_httpgroup + )); lastStepStatus(0); updateToVersion('0.9.12-svn1'); @@ -980,10 +977,10 @@ if (isFroxlorVersion('0.9.12-svn1')) { showUpdateStep("Updating from 0.9.12-svn1 to 0.9.12-svn2", false); - $update_perl_suexecworkaround = isset($_POST['update_perl_suexecworkaround']) ? (int)$_POST['update_perl_suexecworkaround'] : '0'; + $update_perl_suexecworkaround = isset($_POST['update_perl_suexecworkaround']) ? (int) $_POST['update_perl_suexecworkaround'] : '0'; $update_perl_suexecpath = isset($_POST['update_perl_suexecpath']) ? makeCorrectDir($_POST['update_perl_suexecpath']) : '/var/www/cgi-bin/'; - if($update_perl_suexecpath == '') { + if ($update_perl_suexecpath == '') { $update_perl_suexecpath = '/var/www/cgi-bin/'; } @@ -992,10 +989,15 @@ if (isFroxlorVersion('0.9.12-svn1')) { INSERT INTO `" . TABLE_PANEL_SETTINGS . "` SET `settinggroup` = 'perl', `varname` = :varname, - `value` = :value" - ); - Database::pexecute($stmt, array('varname' => 'suexecworkaround', 'value' => $update_perl_suexecworkaround)); - Database::pexecute($stmt, array('varname' => 'suexecpath', 'value' => $update_perl_suexecpath)); + `value` = :value"); + Database::pexecute($stmt, array( + 'varname' => 'suexecworkaround', + 'value' => $update_perl_suexecworkaround + )); + Database::pexecute($stmt, array( + 'varname' => 'suexecpath', + 'value' => $update_perl_suexecpath + )); lastStepStatus(0); updateToVersion('0.9.12-svn2'); @@ -1006,7 +1008,7 @@ if (isFroxlorVersion('0.9.12-svn2')) { showUpdateStep("Updating from 0.9.12-svn2 to 0.9.12-svn3", false); showUpdateStep("Adding new field to domain table"); - Database::query("ALTER TABLE `".TABLE_PANEL_DOMAINS."` ADD `ismainbutsubto` int(11) unsigned NOT NULL default '0' AFTER `mod_fcgid_maxrequests`;"); + Database::query("ALTER TABLE `" . TABLE_PANEL_DOMAINS . "` ADD `ismainbutsubto` int(11) unsigned NOT NULL default '0' AFTER `mod_fcgid_maxrequests`;"); lastStepStatus(0); updateToVersion('0.9.12-svn3'); @@ -1023,9 +1025,10 @@ if (isFroxlorVersion('0.9.12-svn3')) { INSERT INTO `" . TABLE_PANEL_SETTINGS . "` SET `settinggroup` = 'system', `varname` = 'awstats_awstatspath', - `value` = :value" - ); - Database::pexecute($stmt, array('value' => $update_awstats_awstatspath)); + `value` = :value"); + Database::pexecute($stmt, array( + 'value' => $update_awstats_awstatspath + )); lastStepStatus(0); updateToVersion('0.9.12-svn4'); @@ -1047,7 +1050,7 @@ if (isFroxlorVersion('0.9.12-svn5')) { showUpdateStep("Updating from 0.9.12-svn5 to 0.9.12-svn6", false); showUpdateStep("Adding new field to table 'panel_htpasswds'"); - Database::query("ALTER TABLE `".TABLE_PANEL_HTPASSWDS."` ADD `authname` varchar(255) NOT NULL default 'Restricted Area' AFTER `password`;"); + Database::query("ALTER TABLE `" . TABLE_PANEL_HTPASSWDS . "` ADD `authname` varchar(255) NOT NULL default 'Restricted Area' AFTER `password`;"); lastStepStatus(0); updateToVersion('0.9.12-svn6'); @@ -1064,19 +1067,19 @@ if (isFroxlorVersion('0.9.12')) { showUpdateStep("Updating from 0.9.12 to 0.9.13-svn1", false); showUpdateStep("Adding new fields to admin-table"); - Database::query("ALTER TABLE `".TABLE_PANEL_ADMINS."` ADD `email_autoresponder` int(5) NOT NULL default '0' AFTER `aps_packages_used`;"); - Database::query("ALTER TABLE `".TABLE_PANEL_ADMINS."` ADD `email_autoresponder_used` int(5) NOT NULL default '0' AFTER `email_autoresponder`;"); + Database::query("ALTER TABLE `" . TABLE_PANEL_ADMINS . "` ADD `email_autoresponder` int(5) NOT NULL default '0' AFTER `aps_packages_used`;"); + Database::query("ALTER TABLE `" . TABLE_PANEL_ADMINS . "` ADD `email_autoresponder_used` int(5) NOT NULL default '0' AFTER `email_autoresponder`;"); lastStepStatus(0); showUpdateStep("Adding new fields to customer-table"); - Database::query("ALTER TABLE `".TABLE_PANEL_CUSTOMERS."` ADD `email_autoresponder` int(5) NOT NULL default '0' AFTER `perlenabled`;"); - Database::query("ALTER TABLE `".TABLE_PANEL_CUSTOMERS."` ADD `email_autoresponder_used` int(5) NOT NULL default '0' AFTER `email_autoresponder`;"); + Database::query("ALTER TABLE `" . TABLE_PANEL_CUSTOMERS . "` ADD `email_autoresponder` int(5) NOT NULL default '0' AFTER `perlenabled`;"); + Database::query("ALTER TABLE `" . TABLE_PANEL_CUSTOMERS . "` ADD `email_autoresponder_used` int(5) NOT NULL default '0' AFTER `email_autoresponder`;"); lastStepStatus(0); - if ((int)Settings::Get('autoresponder.autoresponder_active') == 1) { + if ((int) Settings::Get('autoresponder.autoresponder_active') == 1) { $update_autoresponder_default = isset($_POST['update_autoresponder_default']) ? intval_ressource($_POST['update_autoresponder_default']) : 0; if (isset($_POST['update_autoresponder_default_ul'])) { - $update_autoresponder_default = -1; + $update_autoresponder_default = - 1; } } else { $update_autoresponder_default = 0; @@ -1084,9 +1087,9 @@ if (isFroxlorVersion('0.9.12')) { showUpdateStep("Setting default amount of autoresponders"); // admin gets unlimited - Database::query("UPDATE `".TABLE_PANEL_ADMINS."` SET `email_autoresponder`='-1' WHERE `adminid` = '".(int)$userinfo['adminid']."'"); + Database::query("UPDATE `" . TABLE_PANEL_ADMINS . "` SET `email_autoresponder`='-1' WHERE `adminid` = '" . (int) $userinfo['adminid'] . "'"); // customers - Database::query("UPDATE `".TABLE_PANEL_CUSTOMERS."` SET `email_autoresponder`='".(int)$update_autoresponder_default."' WHERE `deactivated` = '0'"); + Database::query("UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET `email_autoresponder`='" . (int) $update_autoresponder_default . "' WHERE `deactivated` = '0'"); lastStepStatus(0); updateToVersion('0.9.13-svn1'); @@ -1101,16 +1104,17 @@ if (isFroxlorVersion('0.9.13-svn1')) { if (isFroxlorVersion('0.9.13')) { showUpdateStep("Updating from 0.9.13 to 0.9.13.1 final", false); - $update_defaultini_ownvhost = isset($_POST['update_defaultini_ownvhost']) ? (int)$_POST['update_defaultini_ownvhost'] : 1; + $update_defaultini_ownvhost = isset($_POST['update_defaultini_ownvhost']) ? (int) $_POST['update_defaultini_ownvhost'] : 1; showUpdateStep("Adding settings for Froxlor-vhost's PHP-configuration"); $stmt = Database::prepare(" INSERT INTO `" . TABLE_PANEL_SETTINGS . "` SET `settinggroup` = 'system', `varname` = 'mod_fcgid_defaultini_ownvhost', - `value` = :value" - ); - Database::pexecute($stmt, array('value' => $update_defaultini_ownvhost)); + `value` = :value"); + Database::pexecute($stmt, array( + 'value' => $update_defaultini_ownvhost + )); lastStepStatus(0); updateToVersion('0.9.13.1'); @@ -1146,9 +1150,10 @@ if (isFroxlorVersion('0.9.14-svn2')) { INSERT INTO `" . TABLE_PANEL_SETTINGS . "` SET `settinggroup` = 'system', `varname` = 'awstats_icons', - `value` = :value" - ); - Database::pexecute($stmt, array('value' => $update_awstats_icons)); + `value` = :value"); + Database::pexecute($stmt, array( + 'value' => $update_awstats_icons + )); lastStepStatus(0); updateToVersion('0.9.14-svn3'); @@ -1169,13 +1174,14 @@ if (isFroxlorVersion('0.9.14-svn3')) { INSERT INTO `" . TABLE_PANEL_SETTINGS . "` SET `settinggroup` = 'system', `varname` = 'ssl_cert_chainfile', - `value` = :value" - ); - Database::pexecute($stmt, array('value' => $update_ssl_cert_chainfile)); + `value` = :value"); + Database::pexecute($stmt, array( + 'value' => $update_ssl_cert_chainfile + )); lastStepStatus(0); showUpdateStep("Adding new field to IPs and ports for SSLCertificateChainFile"); - Database::query("ALTER TABLE `".TABLE_PANEL_IPSANDPORTS."` ADD `ssl_cert_chainfile` varchar(255) NOT NULL AFTER `default_vhostconf_domain`;"); + Database::query("ALTER TABLE `" . TABLE_PANEL_IPSANDPORTS . "` ADD `ssl_cert_chainfile` varchar(255) NOT NULL AFTER `default_vhostconf_domain`;"); lastStepStatus(0); updateToVersion('0.9.14-svn4'); @@ -1185,7 +1191,7 @@ if (isFroxlorVersion('0.9.14-svn4')) { showUpdateStep("Updating from 0.9.14-svn4 to 0.9.14-svn5", false); showUpdateStep("Adding docroot-field to IPs and ports for custom-docroot settings"); - Database::query("ALTER TABLE `".TABLE_PANEL_IPSANDPORTS."` ADD `docroot` varchar(255) NOT NULL default '' AFTER `ssl_cert_chainfile`;"); + Database::query("ALTER TABLE `" . TABLE_PANEL_IPSANDPORTS . "` ADD `docroot` varchar(255) NOT NULL default '' AFTER `ssl_cert_chainfile`;"); lastStepStatus(0); updateToVersion('0.9.14-svn5'); @@ -1195,16 +1201,17 @@ if (isFroxlorVersion('0.9.14-svn5')) { showUpdateStep("Updating from 0.9.14-svn5 to 0.9.14-svn6", false); - $update_allow_domain_login = isset($_POST['update_allow_domain_login']) ? (int)$_POST['update_allow_domain_login'] : '0'; + $update_allow_domain_login = isset($_POST['update_allow_domain_login']) ? (int) $_POST['update_allow_domain_login'] : '0'; showUpdateStep("Adding domain-login switch to the settings"); $stmt = Database::prepare(" INSERT INTO `" . TABLE_PANEL_SETTINGS . "` SET `settinggroup` = 'login', `varname` = 'domain_login', - `value` = :value" - ); - Database::pexecute($stmt, array('value' => $update_allow_domain_login)); + `value` = :value"); + Database::pexecute($stmt, array( + 'value' => $update_allow_domain_login + )); lastStepStatus(0); updateToVersion('0.9.14-svn6'); @@ -1234,7 +1241,7 @@ if (isFroxlorVersion('0.9.14-svn6')) { lastStepStatus(0); showUpdateStep("Removing deprecated legacy-cronjob from database"); - Database::query("DELETE FROM `".TABLE_PANEL_CRONRUNS."` WHERE `cronfile` ='cron_legacy.php';"); + Database::query("DELETE FROM `" . TABLE_PANEL_CRONRUNS . "` WHERE `cronfile` ='cron_legacy.php';"); lastStepStatus(0); updateToVersion('0.9.14-svn10'); @@ -1242,36 +1249,36 @@ if (isFroxlorVersion('0.9.14-svn6')) { /* * revert database changes we did for multiserver-support -* before branching - sorry guys :/ -*/ + * before branching - sorry guys :/ + */ if (isFroxlorVersion('0.9.14-svn9')) { showUpdateStep("Reverting multiserver-patches (svn)", false); - $update_allow_domain_login = isset($_POST['update_allow_domain_login']) ? (int)$_POST['update_allow_domain_login'] : '0'; + $update_allow_domain_login = isset($_POST['update_allow_domain_login']) ? (int) $_POST['update_allow_domain_login'] : '0'; showUpdateStep("Reverting database table-changes"); - Database::query("ALTER TABLE `".TABLE_PANEL_SETTINGS."` DROP `sid`;"); + Database::query("ALTER TABLE `" . TABLE_PANEL_SETTINGS . "` DROP `sid`;"); showUpdateStep("."); - Database::query("ALTER TABLE `".TABLE_PANEL_CUSTOMERS."` DROP `sid`;"); + Database::query("ALTER TABLE `" . TABLE_PANEL_CUSTOMERS . "` DROP `sid`;"); showUpdateStep("."); - Database::query("ALTER TABLE `".TABLE_MAIL_VIRTUAL."` DROP `sid`;"); + Database::query("ALTER TABLE `" . TABLE_MAIL_VIRTUAL . "` DROP `sid`;"); showUpdateStep("."); - Database::query("ALTER TABLE `".TABLE_FTP_USERS."` DROP `sid`;"); + Database::query("ALTER TABLE `" . TABLE_FTP_USERS . "` DROP `sid`;"); showUpdateStep("."); - Database::query("ALTER TABLE `".TABLE_PANEL_TASKS."` DROP `sid`;"); + Database::query("ALTER TABLE `" . TABLE_PANEL_TASKS . "` DROP `sid`;"); showUpdateStep("."); - Database::query("ALTER TABLE `".TABLE_APS_TASKS."` DROP `sid`;"); + Database::query("ALTER TABLE `" . TABLE_APS_TASKS . "` DROP `sid`;"); showUpdateStep("."); - Database::query("ALTER TABLE `".TABLE_PANEL_LOG."` DROP `sid`;"); + Database::query("ALTER TABLE `" . TABLE_PANEL_LOG . "` DROP `sid`;"); showUpdateStep("."); - Database::query("ALTER TABLE `".TABLE_PANEL_PHPCONFIGS."` DROP `sid`;"); + Database::query("ALTER TABLE `" . TABLE_PANEL_PHPCONFIGS . "` DROP `sid`;"); lastStepStatus(0); showUpdateStep("Removing froxlor-clients table"); @@ -1295,11 +1302,19 @@ if (isFroxlorVersion('0.9.14')) { INSERT INTO `" . TABLE_PANEL_SETTINGS . "` SET `settinggroup` = 'system', `varname` = :varname, - `value` = :value" - ); - Database::pexecute($stmt, array('varname' => 'nginx_php_backend', 'value' => '127.0.0.1:8888')); - Database::pexecute($stmt, array('varname' => 'perl_server', 'value' => 'unix:/var/run/nginx/cgiwrap-dispatch.sock')); - Database::pexecute($stmt, array('varname' => 'phpreload_command', 'value' => '')); + `value` = :value"); + Database::pexecute($stmt, array( + 'varname' => 'nginx_php_backend', + 'value' => '127.0.0.1:8888' + )); + Database::pexecute($stmt, array( + 'varname' => 'perl_server', + 'value' => 'unix:/var/run/nginx/cgiwrap-dispatch.sock' + )); + Database::pexecute($stmt, array( + 'varname' => 'phpreload_command', + 'value' => '' + )); lastStepStatus(0); updateToVersion('0.9.15-svn1'); @@ -1314,20 +1329,20 @@ if (isFroxlorVersion('0.9.15-svn1')) { if (isFroxlorVersion('0.9.15')) { showUpdateStep("Updating from 0.9.15 to 0.9.16-svn1", false); - $update_phpfpm_enabled = isset($_POST['update_phpfpm_enabled']) ? (int)$_POST['update_phpfpm_enabled'] : '0'; + $update_phpfpm_enabled = isset($_POST['update_phpfpm_enabled']) ? (int) $_POST['update_phpfpm_enabled'] : '0'; $update_phpfpm_configdir = isset($_POST['update_phpfpm_configdir']) ? makeCorrectDir($_POST['update_phpfpm_configdir']) : '/etc/php-fpm.d/'; $update_phpfpm_tmpdir = isset($_POST['update_phpfpm_tmpdir']) ? makeCorrectDir($_POST['update_phpfpm_tmpdir']) : '/var/customers/tmp'; $update_phpfpm_peardir = isset($_POST['update_phpfpm_peardir']) ? makeCorrectDir($_POST['update_phpfpm_peardir']) : '/usr/share/php/:/usr/share/php5/'; $update_phpfpm_reload = isset($_POST['update_phpfpm_reload']) ? $_POST['update_phpfpm_reload'] : '/etc/init.d/php-fpm restart'; $update_phpfpm_pm = isset($_POST['update_phpfpm_pm']) ? $_POST['update_phpfpm_pm'] : 'static'; - $update_phpfpm_max_children = isset($_POST['update_phpfpm_max_children']) ? (int)$_POST['update_phpfpm_max_children'] : '1'; - $update_phpfpm_max_requests = isset($_POST['update_phpfpm_max_requests']) ? (int)$_POST['update_phpfpm_max_requests'] : '0'; + $update_phpfpm_max_children = isset($_POST['update_phpfpm_max_children']) ? (int) $_POST['update_phpfpm_max_children'] : '1'; + $update_phpfpm_max_requests = isset($_POST['update_phpfpm_max_requests']) ? (int) $_POST['update_phpfpm_max_requests'] : '0'; if ($update_phpfpm_pm == 'dynamic') { - $update_phpfpm_start_servers = isset($_POST['update_phpfpm_start_servers']) ? (int)$_POST['update_phpfpm_start_servers'] : '20'; - $update_phpfpm_min_spare_servers = isset($_POST['update_phpfpm_min_spare_servers']) ? (int)$_POST['update_phpfpm_min_spare_servers'] : '5'; - $update_phpfpm_max_spare_servers = isset($_POST['update_phpfpm_max_spare_servers']) ? (int)$_POST['update_phpfpm_max_spare_servers'] : '35'; + $update_phpfpm_start_servers = isset($_POST['update_phpfpm_start_servers']) ? (int) $_POST['update_phpfpm_start_servers'] : '20'; + $update_phpfpm_min_spare_servers = isset($_POST['update_phpfpm_min_spare_servers']) ? (int) $_POST['update_phpfpm_min_spare_servers'] : '5'; + $update_phpfpm_max_spare_servers = isset($_POST['update_phpfpm_max_spare_servers']) ? (int) $_POST['update_phpfpm_max_spare_servers'] : '35'; } else { $update_phpfpm_start_servers = 20; $update_phpfpm_min_spare_servers = 5; @@ -1346,19 +1361,51 @@ if (isFroxlorVersion('0.9.15')) { INSERT INTO `" . TABLE_PANEL_SETTINGS . "` SET `settinggroup` = 'phpfpm', `varname` = :varname, - `value` = :value" - ); - Database::pexecute($stmt, array('varname' => 'enabled', 'value' => $update_phpfpm_enabled)); - Database::pexecute($stmt, array('varname' => 'configdir', 'value' => $update_phpfpm_configdir)); - Database::pexecute($stmt, array('varname' => 'reload', 'value' => $update_phpfpm_reload)); - Database::pexecute($stmt, array('varname' => 'pm', 'value' => $update_phpfpm_pm)); - Database::pexecute($stmt, array('varname' => 'max_children', 'value' => $update_phpfpm_max_children)); - Database::pexecute($stmt, array('varname' => 'max_requests', 'value' => $update_phpfpm_max_requests)); - Database::pexecute($stmt, array('varname' => 'start_servers', 'value' => $update_phpfpm_start_servers)); - Database::pexecute($stmt, array('varname' => 'min_spare_servers', 'value' => $update_phpfpm_min_spare_servers)); - Database::pexecute($stmt, array('varname' => 'max_spare_servers', 'value' => $update_phpfpm_max_spare_servers)); - Database::pexecute($stmt, array('varname' => 'tmpdir', 'value' => $update_phpfpm_tmpdir)); - Database::pexecute($stmt, array('varname' => 'peardir', 'value' => $update_phpfpm_peardir)); + `value` = :value"); + Database::pexecute($stmt, array( + 'varname' => 'enabled', + 'value' => $update_phpfpm_enabled + )); + Database::pexecute($stmt, array( + 'varname' => 'configdir', + 'value' => $update_phpfpm_configdir + )); + Database::pexecute($stmt, array( + 'varname' => 'reload', + 'value' => $update_phpfpm_reload + )); + Database::pexecute($stmt, array( + 'varname' => 'pm', + 'value' => $update_phpfpm_pm + )); + Database::pexecute($stmt, array( + 'varname' => 'max_children', + 'value' => $update_phpfpm_max_children + )); + Database::pexecute($stmt, array( + 'varname' => 'max_requests', + 'value' => $update_phpfpm_max_requests + )); + Database::pexecute($stmt, array( + 'varname' => 'start_servers', + 'value' => $update_phpfpm_start_servers + )); + Database::pexecute($stmt, array( + 'varname' => 'min_spare_servers', + 'value' => $update_phpfpm_min_spare_servers + )); + Database::pexecute($stmt, array( + 'varname' => 'max_spare_servers', + 'value' => $update_phpfpm_max_spare_servers + )); + Database::pexecute($stmt, array( + 'varname' => 'tmpdir', + 'value' => $update_phpfpm_tmpdir + )); + Database::pexecute($stmt, array( + 'varname' => 'peardir', + 'value' => $update_phpfpm_peardir + )); lastStepStatus(0); updateToVersion('0.9.16-svn1'); @@ -1367,7 +1414,7 @@ if (isFroxlorVersion('0.9.15')) { if (isFroxlorVersion('0.9.16-svn1')) { showUpdateStep("Updating from 0.9.16-svn1 to 0.9.16-svn2", false); - $update_phpfpm_enabled_ownvhost = isset($_POST['update_phpfpm_enabled_ownvhost']) ? (int)$_POST['update_phpfpm_enabled_ownvhost'] : '0'; + $update_phpfpm_enabled_ownvhost = isset($_POST['update_phpfpm_enabled_ownvhost']) ? (int) $_POST['update_phpfpm_enabled_ownvhost'] : '0'; $update_phpfpm_httpuser = isset($_POST['update_phpfpm_httpuser']) ? $_POST['update_phpfpm_httpuser'] : 'froxlorlocal'; $update_phpfpm_httpgroup = isset($_POST['update_phpfpm_httpgroup']) ? $_POST['update_phpfpm_httpgroup'] : 'froxlorlocal'; @@ -1383,11 +1430,19 @@ if (isFroxlorVersion('0.9.16-svn1')) { INSERT INTO `" . TABLE_PANEL_SETTINGS . "` SET `settinggroup` = 'phpfpm', `varname` = :varname, - `value` = :value" - ); - Database::pexecute($stmt, array('varname' => 'enabled_ownvhost', 'value' => $update_phpfpm_enabled_ownvhost)); - Database::pexecute($stmt, array('varname' => 'vhost_httpuser', 'value' => $update_phpfpm_httpuser)); - Database::pexecute($stmt, array('varname' => 'vhost_httpgroup', 'value' => $update_phpfpm_httpgroup)); + `value` = :value"); + Database::pexecute($stmt, array( + 'varname' => 'enabled_ownvhost', + 'value' => $update_phpfpm_enabled_ownvhost + )); + Database::pexecute($stmt, array( + 'varname' => 'vhost_httpuser', + 'value' => $update_phpfpm_httpuser + )); + Database::pexecute($stmt, array( + 'varname' => 'vhost_httpgroup', + 'value' => $update_phpfpm_httpgroup + )); lastStepStatus(0); updateToVersion('0.9.16-svn2'); @@ -1403,20 +1458,28 @@ if (isFroxlorVersion('0.9.16')) { showUpdateStep("Updating from 0.9.16 to 0.9.17-svn1", false); - $update_system_report_enable = isset($_POST['update_system_report_enable']) ? (int)$_POST['update_system_report_enable'] : '1'; - $update_system_report_webmax = isset($_POST['update_system_report_webmax']) ? (int)$_POST['update_system_report_webmax'] : '90'; - $update_system_report_trafficmax = isset($_POST['update_system_report_trafficmax']) ? (int)$_POST['update_system_report_trafficmax'] : '90'; + $update_system_report_enable = isset($_POST['update_system_report_enable']) ? (int) $_POST['update_system_report_enable'] : '1'; + $update_system_report_webmax = isset($_POST['update_system_report_webmax']) ? (int) $_POST['update_system_report_webmax'] : '90'; + $update_system_report_trafficmax = isset($_POST['update_system_report_trafficmax']) ? (int) $_POST['update_system_report_trafficmax'] : '90'; showUpdateStep("Adding new settings for web- and traffic-reporting"); $stmt = Database::prepare(" INSERT INTO `" . TABLE_PANEL_SETTINGS . "` SET `settinggroup` = 'system', `varname` = :varname, - `value` = :value" - ); - Database::pexecute($stmt, array('varname' => 'report_enable', 'value' => $update_system_report_enable)); - Database::pexecute($stmt, array('varname' => 'report_webmax', 'value' => $update_system_report_webmax)); - Database::pexecute($stmt, array('varname' => 'report_trafficmax', 'value' => $update_system_report_trafficmax)); + `value` = :value"); + Database::pexecute($stmt, array( + 'varname' => 'report_enable', + 'value' => $update_system_report_enable + )); + Database::pexecute($stmt, array( + 'varname' => 'report_webmax', + 'value' => $update_system_report_webmax + )); + Database::pexecute($stmt, array( + 'varname' => 'report_trafficmax', + 'value' => $update_system_report_trafficmax + )); lastStepStatus(0); showUpdateStep("Adding new cron-module for web- and traffic-reporting"); @@ -1428,35 +1491,31 @@ if (isFroxlorVersion('0.9.16')) { `interval` = '1 DAY', `desc_lng_key` = 'cron_usage_report', `lastrun` = :lastrun, - `isactive` = :isactive" - ); - Database::pexecute($stmt, array('lastrun' => $clastrun, 'isactive' => $update_system_report_enable)); + `isactive` = :isactive"); + Database::pexecute($stmt, array( + 'lastrun' => $clastrun, + 'isactive' => $update_system_report_enable + )); lastStepStatus(0); showUpdateStep("Updating various database-fields"); Database::query("DELETE FROM `" . TABLE_PANEL_SETTINGS . "` WHERE `settinggroup` = 'system' AND `varname` = 'last_traffic_report_run';"); $check_stmt = Database::query(" - SELECT `varname` FROM `" . TABLE_PANEL_TEMPLATES . "` WHERE `varname` = 'trafficninetypercent_subject';" - ); + SELECT `varname` FROM `" . TABLE_PANEL_TEMPLATES . "` WHERE `varname` = 'trafficninetypercent_subject';"); Database::pexecute($check_stmt); $check = $check_stmt->fetch(PDO::FETCH_ASSOC); - if (isset($check['varname']) - && $check['varname'] == 'trafficninetypercent_subject' - ) { + if (isset($check['varname']) && $check['varname'] == 'trafficninetypercent_subject') { Database::query("UPDATE `" . TABLE_PANEL_TEMPLATES . "` SET `varname` = 'trafficmaxpercent_subject' WHERE `varname` = 'trafficninetypercent_subject';"); } $check_stmt = Database::query(" - SELECT `varname` FROM `" . TABLE_PANEL_TEMPLATES . "` WHERE `varname` = 'trafficninetypercent_mailbody';" - ); + SELECT `varname` FROM `" . TABLE_PANEL_TEMPLATES . "` WHERE `varname` = 'trafficninetypercent_mailbody';"); Database::pexecute($check_stmt); $check = $check_stmt->fetch(PDO::FETCH_ASSOC); - if (isset($check['varname']) - && $check['varname'] == 'trafficninetypercent_mailbody' - ) { + if (isset($check['varname']) && $check['varname'] == 'trafficninetypercent_mailbody') { Database::query("UPDATE `" . TABLE_PANEL_TEMPLATES . "` SET `varname` = 'trafficmaxpercent_mailbody' WHERE `varname` = 'trafficninetypercent_mailbody';"); } lastStepStatus(0); @@ -1502,16 +1561,17 @@ if (isFroxlorVersion('0.9.17')) { $result_stmt = Database::query("SELECT * FROM `" . TABLE_PANEL_SETTINGS . "` WHERE `settinggroup` = 'system' AND `varname` = 'httpgroup'"); $result = $result_stmt->fetch(PDO::FETCH_ASSOC); - if (!isset($result) || !isset($result['value'])) { + if (! isset($result) || ! isset($result['value'])) { $nonefound = false; showUpdateStep("Adding missing setting 'httpgroup'"); $stmt = Database::prepare(" INSERT INTO `" . TABLE_PANEL_SETTINGS . "` SET `settinggroup` = 'system', `varname` = 'httpgroup', - `value` = :value" - ); - Database::pexecute($stmt, array('value' => Settings::Get('system.httpuser'))); + `value` = :value"); + Database::pexecute($stmt, array( + 'value' => Settings::Get('system.httpuser') + )); lastStepStatus(0); } @@ -1534,9 +1594,10 @@ if (isFroxlorVersion('0.9.18-svn1')) { INSERT INTO `" . TABLE_PANEL_SETTINGS . "` SET `settinggroup` = 'panel', `varname` = 'default_theme', - `value` = :value" - ); - Database::pexecute($stmt, array('value' => $update_default_theme)); + `value` = :value"); + Database::pexecute($stmt, array( + 'value' => $update_default_theme + )); lastStepStatus(0); showUpdateStep("Delete old setting for header-graphic"); @@ -1544,9 +1605,9 @@ if (isFroxlorVersion('0.9.18-svn1')) { lastStepStatus(0); showUpdateStep("Updating table layouts"); - Database::query("ALTER TABLE `".TABLE_PANEL_ADMINS."` ADD `theme` varchar(255) NOT NULL default 'Froxlor' AFTER `email_autoresponder_used`;"); - Database::query("ALTER TABLE `".TABLE_PANEL_CUSTOMERS."` ADD `theme` varchar(255) NOT NULL default 'Froxlor' AFTER `email_autoresponder_used`;"); - Database::query("ALTER TABLE `".TABLE_PANEL_SESSIONS."` ADD `theme` varchar(255) NOT NULL default '' AFTER `adminsession`;"); + Database::query("ALTER TABLE `" . TABLE_PANEL_ADMINS . "` ADD `theme` varchar(255) NOT NULL default 'Froxlor' AFTER `email_autoresponder_used`;"); + Database::query("ALTER TABLE `" . TABLE_PANEL_CUSTOMERS . "` ADD `theme` varchar(255) NOT NULL default 'Froxlor' AFTER `email_autoresponder_used`;"); + Database::query("ALTER TABLE `" . TABLE_PANEL_SESSIONS . "` ADD `theme` varchar(255) NOT NULL default '' AFTER `adminsession`;"); lastStepStatus(0); updateToVersion('0.9.18-svn2'); @@ -1579,8 +1640,7 @@ if (isFroxlorVersion('0.9.19')) { INSERT INTO `" . TABLE_PANEL_SETTINGS . "` SET `settinggroup` = 'system', `varname` = 'validate_domain', - `value` = '1'" - ); + `value` = '1'"); lastStepStatus(0); updateToVersion('0.9.20-svn1'); @@ -1635,7 +1695,7 @@ if (isFroxlorVersion('0.9.20.1')) { $columnfound = 1; } } - if (!$columnfound) { + if (! $columnfound) { Database::query("ALTER TABLE `" . TABLE_PANEL_CUSTOMERS . "` ADD `backup_allowed` TINYINT( 1 ) NOT NULL DEFAULT '1'"); Database::query("ALTER TABLE `" . TABLE_PANEL_CUSTOMERS . "` ADD `backup_enabled` TINYINT( 1 ) NOT NULL DEFAULT '0'"); } @@ -1827,9 +1887,10 @@ if (isFroxlorVersion('0.9.26')) { INSERT INTO `" . TABLE_PANEL_SETTINGS . "` SET `settinggroup` = 'system', `varname` = 'backup_ftp_enabled', - `value` = :value" - ); - Database::pexecute($stmt, array('value' => $state)); + `value` = :value"); + Database::pexecute($stmt, array( + 'value' => $state + )); } updateToVersion('0.9.27-svn1'); @@ -1951,7 +2012,7 @@ if (isFroxlorVersion('0.9.28-svn1')) { Database::query("UPDATE `panel_languages` SET `iso`='pl' WHERE `language` = 'Polski'"); break; default: - showUpdateStep("Sorry, but I don't know the ISO-639 language code for ".$language.". Please update the entry in `panel_languages` manually.\n"); + showUpdateStep("Sorry, but I don't know the ISO-639 language code for " . $language . ". Please update the entry in `panel_languages` manually.\n"); } } @@ -1981,9 +2042,7 @@ if (isFroxlorVersion('0.9.28-svn3')) { showUpdateStep("Updating from 0.9.28-svn3 to 0.9.28-svn4", true); lastStepStatus(0); - if (isset($_POST['classic_theme_replacement']) - && $_POST['classic_theme_replacement'] != '' - ) { + if (isset($_POST['classic_theme_replacement']) && $_POST['classic_theme_replacement'] != '') { $classic_theme_replacement = $_POST['classic_theme_replacement']; } else { $classic_theme_replacement = 'Froxlor'; @@ -1995,34 +2054,38 @@ if (isFroxlorVersion('0.9.28-svn3')) { $upd_stmt = Database::prepare(" UPDATE `" . TABLE_PANEL_SETTINGS . "` SET `value` = :theme - WHERE `varname` = 'default_theme';" - ); - Database::pexecute($upd_stmt, array('theme' => $classic_theme_replacement)); + WHERE `varname` = 'default_theme';"); + Database::pexecute($upd_stmt, array( + 'theme' => $classic_theme_replacement + )); } // Updating admin's theme setting $upd_stmt = Database::prepare(" UPDATE `" . TABLE_PANEL_ADMINS . "` SET `theme` = :theme - WHERE `theme` = 'Classic';" - ); - Database::pexecute($upd_stmt, array('theme' => $classic_theme_replacement)); + WHERE `theme` = 'Classic';"); + Database::pexecute($upd_stmt, array( + 'theme' => $classic_theme_replacement + )); // Updating customer's theme setting $upd_stmt = Database::prepare(" UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET `theme` = :theme - WHERE `theme` = 'Classic';" - ); - Database::pexecute($upd_stmt, array('theme' => $classic_theme_replacement)); + WHERE `theme` = 'Classic';"); + Database::pexecute($upd_stmt, array( + 'theme' => $classic_theme_replacement + )); // Updating theme setting of active sessions $upd_stmt = Database::prepare(" UPDATE `" . TABLE_PANEL_SESSIONS . "` SET `theme` = :theme - WHERE `theme` = 'Classic';" - ); - Database::pexecute($upd_stmt, array('theme' => $classic_theme_replacement)); + WHERE `theme` = 'Classic';"); + Database::pexecute($upd_stmt, array( + 'theme' => $classic_theme_replacement + )); lastStepStatus(0); @@ -2059,16 +2122,17 @@ if (isFroxlorVersion('0.9.28-svn5')) { showUpdateStep("Updating from 0.9.28-svn5 to 0.9.28-svn6", true); lastStepStatus(0); - $update_system_apache24 = isset($_POST['update_system_apache24']) ? (int)$_POST['update_system_apache24'] : '0'; + $update_system_apache24 = isset($_POST['update_system_apache24']) ? (int) $_POST['update_system_apache24'] : '0'; showUpdateStep('Setting value for apache-2.4 modification', true); // support for Apache-2.4 $stmt = Database::prepare(" INSERT INTO `" . TABLE_PANEL_SETTINGS . "` SET `settinggroup` = 'system', `varname` = 'apache24', - `value` = :value" - ); - Database::pexecute($stmt, array('value' => $update_system_apache24)); + `value` = :value"); + Database::pexecute($stmt, array( + 'value' => $update_system_apache24 + )); lastStepStatus(0); showUpdateStep("Inserting new tickets-see-all field to panel_admins", true); @@ -2079,9 +2143,10 @@ if (isFroxlorVersion('0.9.28-svn5')) { $stmt = Database::prepare(" UPDATE `" . TABLE_PANEL_ADMINS . "` SET `tickets_see_all` = '1' - WHERE `adminid` = :adminid" - ); - Database::pexecute($stmt, array('adminid' => $userinfo['adminid'])); + WHERE `adminid` = :adminid"); + Database::pexecute($stmt, array( + 'adminid' => $userinfo['adminid'] + )); lastStepStatus(0); showUpdateStep("Inserting new panel webfont-settings (default: off)", true); @@ -2098,9 +2163,10 @@ if (isFroxlorVersion('0.9.28-svn5')) { INSERT INTO `" . TABLE_PANEL_SETTINGS . "` SET `settinggroup` = 'nginx', `varname` = 'fastcgiparams', - `value` = :value" - ); - Database::pexecute($stmt, array('value' => $fastcgiparams)); + `value` = :value"); + Database::pexecute($stmt, array( + 'value' => $fastcgiparams + )); lastStepStatus(0); updateToVersion('0.9.28-svn6'); @@ -2117,15 +2183,16 @@ if (isFroxlorVersion('0.9.28-rc1')) { showUpdateStep("Updating from 0.9.28-rc1 to 0.9.28-rc2", true); lastStepStatus(0); - $update_system_documentroot_use_default_value = isset($_POST['update_system_documentroot_use_default_value']) ? (int)$_POST['update_system_documentroot_use_default_value'] : '0'; + $update_system_documentroot_use_default_value = isset($_POST['update_system_documentroot_use_default_value']) ? (int) $_POST['update_system_documentroot_use_default_value'] : '0'; showUpdateStep("Adding new settings for using domain name as default value for DocumentRoot path", true); $stmt = Database::prepare(" INSERT INTO `" . TABLE_PANEL_SETTINGS . "` SET `settinggroup` = 'system', `varname` = 'documentroot_use_default_value', - `value` = :value" - ); - Database::pexecute($stmt, array('value' => $update_system_documentroot_use_default_value)); + `value` = :value"); + Database::pexecute($stmt, array( + 'value' => $update_system_documentroot_use_default_value + )); lastStepStatus(0); updateToVersion('0.9.28-rc2'); @@ -2152,15 +2219,16 @@ if (isFroxlorVersion('0.9.28.1')) { showUpdateStep("Updating from 0.9.28.1 to 0.9.29-dev1", true); lastStepStatus(0); - $hide_stdsubdomains = isset($_POST['hide_stdsubdomains']) ? (int)$_POST['hide_stdsubdomains'] : '0'; + $hide_stdsubdomains = isset($_POST['hide_stdsubdomains']) ? (int) $_POST['hide_stdsubdomains'] : '0'; showUpdateStep('Setting value for "hide standard subdomains"', true); $stmt = Database::prepare(" INSERT INTO `" . TABLE_PANEL_SETTINGS . "` SET `settinggroup` = 'panel', `varname` = 'phpconfigs_hidestdsubdomain', - `value` = :value" - ); - Database::pexecute($stmt, array('value' => $hide_stdsubdomains)); + `value` = :value"); + Database::pexecute($stmt, array( + 'value' => $hide_stdsubdomains + )); lastStepStatus(0); // don't advertise security questions - just set a default silently @@ -2169,13 +2237,14 @@ if (isFroxlorVersion('0.9.28.1')) { $fastcgiparams = Settings::Get('nginx.fastcgiparams'); // check the faulty value explicitly if ($fastcgiparams == '/etc/nginx/fastcgi_params/') { - $fastcgiparams = makeCorrectFile(substr($fastcgiparams,0,-1)); + $fastcgiparams = makeCorrectFile(substr($fastcgiparams, 0, - 1)); $stmt = Database::prepare(" UPDATE `" . TABLE_PANEL_SETTINGS . "` SET `value` = :value - WHERE `varname` = 'fastcgiparams'" - ); - Database::pexecute($stmt, array('value' => $fastcgiparams)); + WHERE `varname` = 'fastcgiparams'"); + Database::pexecute($stmt, array( + 'value' => $fastcgiparams + )); } updateToVersion('0.9.29-dev1'); } @@ -2185,17 +2254,22 @@ if (isFroxlorVersion('0.9.29-dev1')) { showUpdateStep("Updating from 0.9.29-dev1 to 0.9.29-dev2", true); lastStepStatus(0); - $allow_themechange_c = isset($_POST['allow_themechange_c']) ? (int)$_POST['allow_themechange_c'] : '1'; - $allow_themechange_a = isset($_POST['allow_themechange_a']) ? (int)$_POST['allow_themechange_a'] : '1'; + $allow_themechange_c = isset($_POST['allow_themechange_c']) ? (int) $_POST['allow_themechange_c'] : '1'; + $allow_themechange_a = isset($_POST['allow_themechange_a']) ? (int) $_POST['allow_themechange_a'] : '1'; showUpdateStep("Inserting new setting to allow/disallow theme changes (default: on)", true); $stmt = Database::prepare(" INSERT INTO `" . TABLE_PANEL_SETTINGS . "` SET `settinggroup` = 'panel', `varname` = :varname, - `value` = :value" - ); - Database::pexecute($stmt, array('varname' => 'allow_theme_change_admin', 'value' => $allow_themechange_a)); - Database::pexecute($stmt, array('varname' => 'allow_theme_change_customer', 'value' => $allow_themechange_c)); + `value` = :value"); + Database::pexecute($stmt, array( + 'varname' => 'allow_theme_change_admin', + 'value' => $allow_themechange_a + )); + Database::pexecute($stmt, array( + 'varname' => 'allow_theme_change_customer', + 'value' => $allow_themechange_c + )); lastStepStatus(0); updateToVersion('0.9.29-dev2'); @@ -2222,9 +2296,10 @@ if (isFroxlorVersion('0.9.29-dev2')) { INSERT INTO `" . TABLE_PANEL_SETTINGS . "` SET `settinggroup` = 'system', `varname` = 'axfrservers', - `value` = :value" - ); - Database::pexecute($stmt, array('value' => $system_axfrservers)); + `value` = :value"); + Database::pexecute($stmt, array( + 'value' => $system_axfrservers + )); lastStepStatus(0); updateToVersion('0.9.29-dev3'); @@ -2256,9 +2331,10 @@ if (isFroxlorVersion('0.9.29-dev3')) { INSERT INTO `" . TABLE_PANEL_SETTINGS . "` SET `settinggroup` = 'system', `varname` = 'customer_ssl_path', - `value` = :value" - ); - Database::pexecute($stmt, array('value' => $system_customersslpath)); + `value` = :value"); + Database::pexecute($stmt, array( + 'value' => $system_customersslpath + )); updateToVersion('0.9.29-dev4'); } @@ -2277,12 +2353,17 @@ if (isFroxlorVersion('0.9.29-dev4')) { INSERT INTO `" . TABLE_PANEL_SETTINGS . "` SET `settinggroup` = 'phpfpm', `varname` = :varname, - `value` = :value" - ); + `value` = :value"); $dval = (Settings::Get('system.mod_fcgid_defaultini') !== null ? Settings::Get('system.mod_fcgid_defaultini') : '1'); - Database::pexecute($stmt, array('varname' => 'defaultini', 'value' => $dval)); + Database::pexecute($stmt, array( + 'varname' => 'defaultini', + 'value' => $dval + )); $dval = (Settings::Get('system.mod_fcgid_ownvhost') !== null ? Settings::Get('system.mod_fcgid_ownvhost') : '1'); - Database::pexecute($stmt, array('varname' => 'vhost_defaultini', 'value' => $dval)); + Database::pexecute($stmt, array( + 'varname' => 'vhost_defaultini', + 'value' => $dval + )); lastStepStatus(0); updateToVersion('0.9.29-rc1'); @@ -2301,7 +2382,7 @@ if (isFroxlorVersion('0.9.29')) { showUpdateStep("Adding new ip to domain - mapping-table"); Database::query("DROP TABLE IF EXISTS `panel_domaintoip`;"); - $sql = "CREATE TABLE `".TABLE_DOMAINTOIP."` ( + $sql = "CREATE TABLE `" . TABLE_DOMAINTOIP . "` ( `id_domain` int(11) unsigned NOT NULL, `id_ipandports` int(11) unsigned NOT NULL, PRIMARY KEY (`id_domain`, `id_ipandports`) @@ -2314,35 +2395,30 @@ if (isFroxlorVersion('0.9.29')) { while ($row = $result->fetch(PDO::FETCH_ASSOC)) { - if ((int)$row['ipandport'] != 0) { - Database::query("INSERT INTO `".TABLE_DOMAINTOIP."` SET - `id_domain` = " . (int)$row['id'] . ", - `id_ipandports` = " . (int)$row['ipandport']); + if ((int) $row['ipandport'] != 0) { + Database::query("INSERT INTO `" . TABLE_DOMAINTOIP . "` SET + `id_domain` = " . (int) $row['id'] . ", + `id_ipandports` = " . (int) $row['ipandport']); } - if ((int)$row['ssl_ipandport'] != 0) { - Database::query("INSERT INTO `".TABLE_DOMAINTOIP."` SET - `id_domain` = " . (int)$row['id'] . ", - `id_ipandports` = " . (int)$row['ssl_ipandport']); - } - // Subdomains also have ssl ports if the parent has - elseif ((int)$row['ssl_ipandport'] == 0 - && (int)$row['ssl_redirect'] != 0 - && (int)$row['parentdomainid'] != 0 - ) { - Database::query("INSERT INTO `".TABLE_DOMAINTOIP."` SET - `id_domain` = " . (int)$row['id'] . ", + if ((int) $row['ssl_ipandport'] != 0) { + Database::query("INSERT INTO `" . TABLE_DOMAINTOIP . "` SET + `id_domain` = " . (int) $row['id'] . ", + `id_ipandports` = " . (int) $row['ssl_ipandport']); + } // Subdomains also have ssl ports if the parent has + elseif ((int) $row['ssl_ipandport'] == 0 && (int) $row['ssl_redirect'] != 0 && (int) $row['parentdomainid'] != 0) { + Database::query("INSERT INTO `" . TABLE_DOMAINTOIP . "` SET + `id_domain` = " . (int) $row['id'] . ", `id_ipandports` = ( SELECT `ssl_ipandport` FROM `" . TABLE_PANEL_DOMAINS . "` - WHERE `id` = '".(int)$row['parentdomainid']."');" - ); + WHERE `id` = '" . (int) $row['parentdomainid'] . "');"); } } lastStepStatus(0); showUpdateStep("Updating table layouts"); - Database::query("ALTER TABLE `".TABLE_PANEL_DOMAINS."` DROP `ipandport`;"); - Database::query("ALTER TABLE `".TABLE_PANEL_DOMAINS."` DROP `ssl`;"); - Database::query("ALTER TABLE `".TABLE_PANEL_DOMAINS."` DROP `ssl_ipandport`;"); + Database::query("ALTER TABLE `" . TABLE_PANEL_DOMAINS . "` DROP `ipandport`;"); + Database::query("ALTER TABLE `" . TABLE_PANEL_DOMAINS . "` DROP `ssl`;"); + Database::query("ALTER TABLE `" . TABLE_PANEL_DOMAINS . "` DROP `ssl_ipandport`;"); lastStepStatus(0); updateToVersion('0.9.29.1-dev1'); @@ -2354,7 +2430,7 @@ if (isFroxlorVersion('0.9.29.1-dev1')) { lastStepStatus(0); showUpdateStep("Updating table layouts and contents"); - Database::query("ALTER TABLE `".TABLE_MAIL_USERS."` ADD `mboxsize` bigint(30) NOT NULL default '0' AFTER `imap`;"); + Database::query("ALTER TABLE `" . TABLE_MAIL_USERS . "` ADD `mboxsize` bigint(30) NOT NULL default '0' AFTER `imap`;"); Database::query("INSERT INTO `cronjobs_run` SET `module` = 'froxlor/core', `cronfile` = 'cron_mailboxsize.php', `interval` = '6 HOUR', `isactive` = '1', `desc_lng_key` = 'cron_mailboxsize';"); lastStepStatus(0); @@ -2383,11 +2459,11 @@ if (isFroxlorVersion('0.9.29.1-dev3')) { // If you upgraded from SysCP the edit_billingdata field has been // removed in one of the first upgrades to froxlor. Sadly, one field - // remained in the install.sql so we remove it now if it exists - $bd_exists = Database::query("SHOW COLUMNS FROM `".TABLE_PANEL_ADMINS."` LIKE 'edit_billingdata';"); + // remained in the install.sql so we remove it now if it exists + $bd_exists = Database::query("SHOW COLUMNS FROM `" . TABLE_PANEL_ADMINS . "` LIKE 'edit_billingdata';"); if (Database::num_rows() > 0) { showUpdateStep("Removing old billing-field from admin-users"); - Database::query("ALTER TABLE `".TABLE_PANEL_ADMINS."` DROP `edit_billingdata`"); + Database::query("ALTER TABLE `" . TABLE_PANEL_ADMINS . "` DROP `edit_billingdata`"); lastStepStatus(0); } @@ -2438,7 +2514,7 @@ if (isFroxlorVersion('0.9.31-dev1')) { showUpdateStep("Adding new phpfpm-ipcdir setting"); $ins_stmt = Database::prepare(" - INSERT INTO `".TABLE_PANEL_SETTINGS."` SET `settinggroup` = 'phpfpm', `varname` = 'fastcgi_ipcdir', `value` = :value + INSERT INTO `" . TABLE_PANEL_SETTINGS . "` SET `settinggroup` = 'phpfpm', `varname` = 'fastcgi_ipcdir', `value` = :value "); $params = array(); // set default for apache (which will suite in most cases) @@ -2485,12 +2561,12 @@ if (isFroxlorVersion('0.9.31-dev4')) { showUpdateStep("Updating from 0.9.31-dev4 to 0.9.31-dev5", true); lastStepStatus(0); - $update_error_report_admin = isset($_POST['update_error_report_admin']) ? (int)$_POST['update_error_report_admin'] : '1'; - $update_error_report_customer = isset($_POST['update_error_report_customer']) ? (int)$_POST['update_error_report_customer'] : '0'; + $update_error_report_admin = isset($_POST['update_error_report_admin']) ? (int) $_POST['update_error_report_admin'] : '1'; + $update_error_report_customer = isset($_POST['update_error_report_customer']) ? (int) $_POST['update_error_report_customer'] : '0'; showUpdateStep("Adding new error-reporting options"); $ins_stmt = Database::prepare(" - INSERT INTO `".TABLE_PANEL_SETTINGS."` SET `settinggroup` = 'system', `varname` = :varname, `value` = :value + INSERT INTO `" . TABLE_PANEL_SETTINGS . "` SET `settinggroup` = 'system', `varname` = :varname, `value` = :value "); $params = array(); // admins @@ -2513,9 +2589,9 @@ if (isFroxlorVersion('0.9.31-dev5')) { lastStepStatus(0); showUpdateStep("Adding new fpm-configuration options (slowlog)"); - Database::query("ALTER TABLE `".TABLE_PANEL_PHPCONFIGS."` ADD `fpm_slowlog` tinyint(1) NOT NULL default '0' AFTER `mod_fcgid_maxrequests`;"); - Database::query("ALTER TABLE `".TABLE_PANEL_PHPCONFIGS."` ADD `fpm_reqterm` varchar(15) NOT NULL default '60s' AFTER `fpm_slowlog`;"); - Database::query("ALTER TABLE `".TABLE_PANEL_PHPCONFIGS."` ADD `fpm_reqslow` varchar(15) NOT NULL default '5s' AFTER `fpm_reqterm`;"); + Database::query("ALTER TABLE `" . TABLE_PANEL_PHPCONFIGS . "` ADD `fpm_slowlog` tinyint(1) NOT NULL default '0' AFTER `mod_fcgid_maxrequests`;"); + Database::query("ALTER TABLE `" . TABLE_PANEL_PHPCONFIGS . "` ADD `fpm_reqterm` varchar(15) NOT NULL default '60s' AFTER `fpm_slowlog`;"); + Database::query("ALTER TABLE `" . TABLE_PANEL_PHPCONFIGS . "` ADD `fpm_reqslow` varchar(15) NOT NULL default '5s' AFTER `fpm_reqterm`;"); lastStepStatus(0); updateToVersion('0.9.31-dev6'); @@ -2531,12 +2607,14 @@ if (isFroxlorVersion('0.9.31-rc1')) { showUpdateStep("Updating from 0.9.31-rc1 to 0.9.31-rc2"); lastStepStatus(0); - $update_admin_news_feed = isset($_POST['update_admin_news_feed']) ? (int)$_POST['update_admin_news_feed'] : '1'; + $update_admin_news_feed = isset($_POST['update_admin_news_feed']) ? (int) $_POST['update_admin_news_feed'] : '1'; showUpdateStep("Adding new news-feed option"); $ins_stmt = Database::prepare(" - INSERT INTO `".TABLE_PANEL_SETTINGS."` SET `settinggroup` = 'admin', `varname` = 'show_news_feed', `value` = :value + INSERT INTO `" . TABLE_PANEL_SETTINGS . "` SET `settinggroup` = 'admin', `varname` = 'show_news_feed', `value` = :value "); - Database::pexecute($ins_stmt, array('value' => $update_admin_news_feed)); + Database::pexecute($ins_stmt, array( + 'value' => $update_admin_news_feed + )); lastStepStatus(0); updateToVersion('0.9.31-rc2'); @@ -2557,15 +2635,19 @@ if (isFroxlorVersion('0.9.31-rc2')) { // update default vhosts-config for froxlor if they are on the system-default if (Settings::Get('system.mod_fcgid_defaultini_ownvhost') == '1') { $upd_stmt = Database::prepare(" - UPDATE `".TABLE_PANEL_SETTINGS."` SET `value` = :value WHERE `settinggroup` = 'system' AND `varname` = 'mod_fcgid_defaultini_ownvhost' + UPDATE `" . TABLE_PANEL_SETTINGS . "` SET `value` = :value WHERE `settinggroup` = 'system' AND `varname` = 'mod_fcgid_defaultini_ownvhost' "); - Database::pexecute($upd_stmt, array('value' => $frxvhostconfid)); + Database::pexecute($upd_stmt, array( + 'value' => $frxvhostconfid + )); } if (Settings::Get('phpfpm.vhost_defaultini') == '1') { $upd_stmt = Database::prepare(" - UPDATE `".TABLE_PANEL_SETTINGS."` SET `value` = :value WHERE `settinggroup` = 'phpfpm' AND `varname` = 'vhost_defaultini' + UPDATE `" . TABLE_PANEL_SETTINGS . "` SET `value` = :value WHERE `settinggroup` = 'phpfpm' AND `varname` = 'vhost_defaultini' "); - Database::pexecute($upd_stmt, array('value' => $frxvhostconfid)); + Database::pexecute($upd_stmt, array( + 'value' => $frxvhostconfid + )); } lastStepStatus(0); @@ -2573,9 +2655,9 @@ if (isFroxlorVersion('0.9.31-rc2')) { } if (isFroxlorVersion('0.9.31-rc3')) { - showUpdateStep("Updating from 0.9.31-rc3 to 0.9.31 final", true); - lastStepStatus(0); - updateToVersion('0.9.31'); + showUpdateStep("Updating from 0.9.31-rc3 to 0.9.31 final", true); + lastStepStatus(0); + updateToVersion('0.9.31'); } if (isFroxlorVersion('0.9.31')) { @@ -2585,9 +2667,9 @@ if (isFroxlorVersion('0.9.31')) { } if (isFroxlorVersion('0.9.31.1')) { - showUpdateStep("Updating from 0.9.31.1 to 0.9.31.2 final", true); - lastStepStatus(0); - updateToVersion('0.9.31.2'); + showUpdateStep("Updating from 0.9.31.1 to 0.9.31.2 final", true); + lastStepStatus(0); + updateToVersion('0.9.31.2'); } if (isFroxlorVersion('0.9.31.2')) { @@ -2596,48 +2678,48 @@ if (isFroxlorVersion('0.9.31.2')) { lastStepStatus(0); showUpdateStep("Removing APS-module (deprecated)"); - Database::query("DELETE FROM `".TABLE_PANEL_SETTINGS."` WHERE `settinggroup` = 'aps';"); - Database::query("ALTER TABLE `".TABLE_PANEL_ADMINS."` DROP `can_manage_aps_packages`;"); - Database::query("ALTER TABLE `".TABLE_PANEL_ADMINS."` DROP `aps_packages`;"); - Database::query("ALTER TABLE `".TABLE_PANEL_ADMINS."` DROP `aps_packages_used`;"); - Database::query("ALTER TABLE `".TABLE_PANEL_CUSTOMERS."` DROP `aps_packages`;"); - Database::query("ALTER TABLE `".TABLE_PANEL_CUSTOMERS."` DROP `aps_packages_used`;"); - Database::query("ALTER TABLE `".TABLE_PANEL_DATABASES."` DROP `apsdb`;"); + Database::query("DELETE FROM `" . TABLE_PANEL_SETTINGS . "` WHERE `settinggroup` = 'aps';"); + Database::query("ALTER TABLE `" . TABLE_PANEL_ADMINS . "` DROP `can_manage_aps_packages`;"); + Database::query("ALTER TABLE `" . TABLE_PANEL_ADMINS . "` DROP `aps_packages`;"); + Database::query("ALTER TABLE `" . TABLE_PANEL_ADMINS . "` DROP `aps_packages_used`;"); + Database::query("ALTER TABLE `" . TABLE_PANEL_CUSTOMERS . "` DROP `aps_packages`;"); + Database::query("ALTER TABLE `" . TABLE_PANEL_CUSTOMERS . "` DROP `aps_packages_used`;"); + Database::query("ALTER TABLE `" . TABLE_PANEL_DATABASES . "` DROP `apsdb`;"); Database::query("DROP TABLE IF EXISTS `aps_packages`;"); Database::query("DROP TABLE IF EXISTS `aps_instances`;"); Database::query("DROP TABLE IF EXISTS `aps_settings`;"); Database::query("DROP TABLE IF EXISTS `aps_tasks`;"); Database::query("DROP TABLE IF EXISTS `aps_temp_settings`;"); - Database::query("DELETE FROM `".TABLE_PANEL_CRONRUNS."` WHERE `module` = 'froxlor/aps';"); + Database::query("DELETE FROM `" . TABLE_PANEL_CRONRUNS . "` WHERE `module` = 'froxlor/aps';"); lastStepStatus(0); showUpdateStep("Removing backup-module (deprecated)"); - Database::query("DELETE FROM `".TABLE_PANEL_SETTINGS."` WHERE `varname` = 'backup_enabled';"); - Database::query("DELETE FROM `".TABLE_PANEL_SETTINGS."` WHERE `varname` = 'backup_dir';"); - Database::query("DELETE FROM `".TABLE_PANEL_SETTINGS."` WHERE `varname` = 'backup_mysqldump_path';"); - Database::query("DELETE FROM `".TABLE_PANEL_SETTINGS."` WHERE `varname` = 'backup_count';"); - Database::query("DELETE FROM `".TABLE_PANEL_SETTINGS."` WHERE `varname` = 'backup_bigfile';"); - Database::query("DELETE FROM `".TABLE_PANEL_SETTINGS."` WHERE `varname` = 'backup_ftp_enabled';"); - Database::query("DELETE FROM `".TABLE_PANEL_SETTINGS."` WHERE `varname` = 'backup_ftp_server';"); - Database::query("DELETE FROM `".TABLE_PANEL_SETTINGS."` WHERE `varname` = 'backup_ftp_user';"); - Database::query("DELETE FROM `".TABLE_PANEL_SETTINGS."` WHERE `varname` = 'backup_ftp_pass';"); - Database::query("DELETE FROM `".TABLE_PANEL_SETTINGS."` WHERE `varname` = 'backup_ftp_passive';"); - Database::query("ALTER TABLE `".TABLE_PANEL_CUSTOMERS."` DROP `backup_allowed`;"); - Database::query("ALTER TABLE `".TABLE_PANEL_CUSTOMERS."` DROP `backup_enabled`;"); - Database::query("DELETE FROM `".TABLE_PANEL_CRONRUNS."` WHERE `module` = 'froxlor/backup';"); + Database::query("DELETE FROM `" . TABLE_PANEL_SETTINGS . "` WHERE `varname` = 'backup_enabled';"); + Database::query("DELETE FROM `" . TABLE_PANEL_SETTINGS . "` WHERE `varname` = 'backup_dir';"); + Database::query("DELETE FROM `" . TABLE_PANEL_SETTINGS . "` WHERE `varname` = 'backup_mysqldump_path';"); + Database::query("DELETE FROM `" . TABLE_PANEL_SETTINGS . "` WHERE `varname` = 'backup_count';"); + Database::query("DELETE FROM `" . TABLE_PANEL_SETTINGS . "` WHERE `varname` = 'backup_bigfile';"); + Database::query("DELETE FROM `" . TABLE_PANEL_SETTINGS . "` WHERE `varname` = 'backup_ftp_enabled';"); + Database::query("DELETE FROM `" . TABLE_PANEL_SETTINGS . "` WHERE `varname` = 'backup_ftp_server';"); + Database::query("DELETE FROM `" . TABLE_PANEL_SETTINGS . "` WHERE `varname` = 'backup_ftp_user';"); + Database::query("DELETE FROM `" . TABLE_PANEL_SETTINGS . "` WHERE `varname` = 'backup_ftp_pass';"); + Database::query("DELETE FROM `" . TABLE_PANEL_SETTINGS . "` WHERE `varname` = 'backup_ftp_passive';"); + Database::query("ALTER TABLE `" . TABLE_PANEL_CUSTOMERS . "` DROP `backup_allowed`;"); + Database::query("ALTER TABLE `" . TABLE_PANEL_CUSTOMERS . "` DROP `backup_enabled`;"); + Database::query("DELETE FROM `" . TABLE_PANEL_CRONRUNS . "` WHERE `module` = 'froxlor/backup';"); lastStepStatus(0); showUpdateStep("Removing autoresponder-module (deprecated)"); - Database::query("DELETE FROM `".TABLE_PANEL_SETTINGS."` WHERE `settinggroup` = 'autoresponder';"); - Database::query("ALTER TABLE `".TABLE_PANEL_ADMINS."` DROP `email_autoresponder`;"); - Database::query("ALTER TABLE `".TABLE_PANEL_ADMINS."` DROP `email_autoresponder_used`;"); - Database::query("ALTER TABLE `".TABLE_PANEL_CUSTOMERS."` DROP `email_autoresponder`;"); - Database::query("ALTER TABLE `".TABLE_PANEL_CUSTOMERS."` DROP `email_autoresponder_used`;"); + Database::query("DELETE FROM `" . TABLE_PANEL_SETTINGS . "` WHERE `settinggroup` = 'autoresponder';"); + Database::query("ALTER TABLE `" . TABLE_PANEL_ADMINS . "` DROP `email_autoresponder`;"); + Database::query("ALTER TABLE `" . TABLE_PANEL_ADMINS . "` DROP `email_autoresponder_used`;"); + Database::query("ALTER TABLE `" . TABLE_PANEL_CUSTOMERS . "` DROP `email_autoresponder`;"); + Database::query("ALTER TABLE `" . TABLE_PANEL_CUSTOMERS . "` DROP `email_autoresponder_used`;"); Database::query("DROP TABLE IF EXISTS `mail_autoresponder`;"); lastStepStatus(0); showUpdateStep("Updating ftp-groups entries"); - Database::query("UPDATE `".TABLE_FTP_GROUPS."` SET `members` = CONCAT(`members`, ',".Settings::Get('system.httpuser')."');"); + Database::query("UPDATE `" . TABLE_FTP_GROUPS . "` SET `members` = CONCAT(`members`, '," . Settings::Get('system.httpuser') . "');"); lastStepStatus(0); updateToVersion('0.9.32-dev1'); @@ -2650,14 +2732,29 @@ if (isFroxlorVersion('0.9.32-dev1')) { showUpdateStep("Adding mailserver - settings for traffic analysis"); $ins_stmt = Database::prepare(" - INSERT INTO `".TABLE_PANEL_SETTINGS."` SET `settinggroup` = 'system', `varname` = :varname, `value` = :value + INSERT INTO `" . TABLE_PANEL_SETTINGS . "` SET `settinggroup` = 'system', `varname` = :varname, `value` = :value "); - Database::pexecute($ins_stmt, array('varname' => 'mailtraffic_enabled', 'value' => isset($_POST['mailtraffic_enabled']) ? (int)$_POST['mailtraffic_enabled'] : '1')); - Database::pexecute($ins_stmt, array('varname' => 'mdalog', 'value' => isset($_POST['mdalog']) ? $_POST['mdalog'] : '/var/log/mail.log')); - Database::pexecute($ins_stmt, array('varname' => 'mtalog', 'value' => isset($_POST['mtalog']) ? $_POST['mtalog'] : '/var/log/mail.log')); - Database::pexecute($ins_stmt, array('varname' => 'mdaserver', 'value' => isset($_POST['mdaserver']) ? $_POST['mdaserver'] : 'dovecot')); - Database::pexecute($ins_stmt, array('varname' => 'mtaserver', 'value' => isset($_POST['mtaserver']) ? $_POST['mtaserver'] : 'postfix')); + Database::pexecute($ins_stmt, array( + 'varname' => 'mailtraffic_enabled', + 'value' => isset($_POST['mailtraffic_enabled']) ? (int) $_POST['mailtraffic_enabled'] : '1' + )); + Database::pexecute($ins_stmt, array( + 'varname' => 'mdalog', + 'value' => isset($_POST['mdalog']) ? $_POST['mdalog'] : '/var/log/mail.log' + )); + Database::pexecute($ins_stmt, array( + 'varname' => 'mtalog', + 'value' => isset($_POST['mtalog']) ? $_POST['mtalog'] : '/var/log/mail.log' + )); + Database::pexecute($ins_stmt, array( + 'varname' => 'mdaserver', + 'value' => isset($_POST['mdaserver']) ? $_POST['mdaserver'] : 'dovecot' + )); + Database::pexecute($ins_stmt, array( + 'varname' => 'mtaserver', + 'value' => isset($_POST['mtaserver']) ? $_POST['mtaserver'] : 'postfix' + )); lastStepStatus(0); updateToVersion('0.9.32-dev2'); @@ -2669,9 +2766,9 @@ if (isFroxlorVersion('0.9.32-dev2')) { lastStepStatus(0); showUpdateStep("Updating froxlor - theme"); - Database::query("UPDATE `".TABLE_PANEL_ADMINS."` SET `theme` = 'Sparkle_froxlor' WHERE `theme` = 'Froxlor';"); - Database::query("UPDATE `".TABLE_PANEL_CUSTOMERS."` SET `theme` = 'Sparkle_froxlor' WHERE `theme` = 'Froxlor';"); - Database::query("UPDATE `".TABLE_PANEL_SESSIONS."` SET `theme` = 'Sparkle_froxlor' WHERE `theme` = 'Froxlor';"); + Database::query("UPDATE `" . TABLE_PANEL_ADMINS . "` SET `theme` = 'Sparkle_froxlor' WHERE `theme` = 'Froxlor';"); + Database::query("UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET `theme` = 'Sparkle_froxlor' WHERE `theme` = 'Froxlor';"); + Database::query("UPDATE `" . TABLE_PANEL_SESSIONS . "` SET `theme` = 'Sparkle_froxlor' WHERE `theme` = 'Froxlor';"); if (Settings::Get('panel.default_theme') == 'Froxlor') { Settings::Set('panel.default_theme', 'Sparkle_froxlor'); } @@ -2686,7 +2783,7 @@ if (isFroxlorVersion('0.9.32-dev3')) { lastStepStatus(0); showUpdateStep("Adding new FTP-description field"); - Database::query("ALTER TABLE `".TABLE_FTP_USERS."` ADD `description` varchar(255) NOT NULL DEFAULT '' AFTER `customerid`;"); + Database::query("ALTER TABLE `" . TABLE_FTP_USERS . "` ADD `description` varchar(255) NOT NULL DEFAULT '' AFTER `customerid`;"); lastStepStatus(0); updateToVersion('0.9.32-dev4'); @@ -2698,7 +2795,7 @@ if (isFroxlorVersion('0.9.32-dev4')) { lastStepStatus(0); showUpdateStep("Updating cronjob table"); - Database::query("UPDATE `".TABLE_PANEL_CRONRUNS."` SET `cronfile` = REPLACE( REPLACE(`cronfile`, 'cron_', ''), '.php', '')"); + Database::query("UPDATE `" . TABLE_PANEL_CRONRUNS . "` SET `cronfile` = REPLACE( REPLACE(`cronfile`, 'cron_', ''), '.php', '')"); lastStepStatus(0); showUpdateStep("Adding new settings for cron"); @@ -2733,7 +2830,7 @@ if (isFroxlorVersion('0.9.32-dev6')) { showUpdateStep("Updating from 0.9.32-dev6 to 0.9.32-rc1", false); showUpdateStep("Enhancing tasks-table"); - Database::query("ALTER TABLE `".TABLE_PANEL_TASKS."` MODIFY `data` text NOT NULL default ''"); + Database::query("ALTER TABLE `" . TABLE_PANEL_TASKS . "` MODIFY `data` text NOT NULL default ''"); lastStepStatus(0); updateToVersion('0.9.32-rc1'); @@ -2744,7 +2841,7 @@ if (isFroxlorVersion('0.9.32-rc1')) { showUpdateStep("Updating from 0.9.32-rc1 to 0.9.32-rc2", false); showUpdateStep("Removing autoresponder-cronjob (deprecated)"); - Database::query("DELETE FROM `".TABLE_PANEL_CRONRUNS."` WHERE `module` = 'froxlor/autoresponder';"); + Database::query("DELETE FROM `" . TABLE_PANEL_CRONRUNS . "` WHERE `module` = 'froxlor/autoresponder';"); lastStepStatus(0); showUpdateStep("Adding new settings for cron"); @@ -2759,7 +2856,7 @@ if (isFroxlorVersion('0.9.32-rc1')) { lastStepStatus(0); showUpdateStep("Removing backup-module ftp-users (deprecated)"); - Database::query("DELETE FROM `".TABLE_FTP_USERS."` WHERE `username` LIKE '%_backup';"); + Database::query("DELETE FROM `" . TABLE_FTP_USERS . "` WHERE `username` LIKE '%_backup';"); lastStepStatus(0); updateToVersion('0.9.32-rc2'); @@ -2786,7 +2883,7 @@ if (isFroxlorVersion('0.9.32')) { showUpdateStep("Updating from 0.9.32 to 0.9.33-dev1", false); showUpdateStep("Adding settings for custom newsfeed on customer-dashboard"); - Settings::AddNew("customer.show_news_feed", isset($_POST['customer_show_news_feed']) ? (int)$_POST['customer_show_news_feed'] : '0'); + Settings::AddNew("customer.show_news_feed", isset($_POST['customer_show_news_feed']) ? (int) $_POST['customer_show_news_feed'] : '0'); Settings::AddNew("customer.news_feed_url", isset($_POST['customer_news_feed_url']) ? $_POST['customer_news_feed_url'] : ''); lastStepStatus(0); @@ -2797,7 +2894,7 @@ if (isFroxlorVersion('0.9.33-dev1')) { showUpdateStep("Updating from 0.9.33-dev1 to 0.9.33-dev2", false); showUpdateStep("Adding settings for hostname-dns-entry"); - Settings::AddNew("system.dns_createhostnameentry", isset($_POST['dns_createhostnameentry']) ? (int)$_POST['dns_createhostnameentry'] : '0'); + Settings::AddNew("system.dns_createhostnameentry", isset($_POST['dns_createhostnameentry']) ? (int) $_POST['dns_createhostnameentry'] : '0'); lastStepStatus(0); updateToVersion('0.9.33-dev2'); @@ -2825,28 +2922,28 @@ if (isFroxlorVersion('0.9.33-dev3')) { showUpdateStep("Updating from 0.9.33-dev3 to 0.9.33-rc1", false); showUpdateStep("Updating database-scheme"); - Database::query("ALTER TABLE `".TABLE_PANEL_DOMAINS."` MODIFY `dkim_privkey` text"); - Database::query("ALTER TABLE `".TABLE_PANEL_DOMAINS."` MODIFY `dkim_pubkey` text"); - Database::query("ALTER TABLE `".TABLE_PANEL_DOMAINS."` MODIFY `specialsettings` text"); - Database::query("ALTER TABLE `".TABLE_PANEL_IPSANDPORTS."` MODIFY `specialsettings` text"); - Database::query("ALTER TABLE `".TABLE_PANEL_IPSANDPORTS."` MODIFY `default_vhostconf_domain` text"); - Database::query("ALTER TABLE `".TABLE_PANEL_DOMAIN_SSL_SETTINGS."` MODIFY `ssl_ca_file` text"); - Database::query("ALTER TABLE `".TABLE_PANEL_DOMAIN_SSL_SETTINGS."` MODIFY `ssl_cert_chainfile` text"); + Database::query("ALTER TABLE `" . TABLE_PANEL_DOMAINS . "` MODIFY `dkim_privkey` text"); + Database::query("ALTER TABLE `" . TABLE_PANEL_DOMAINS . "` MODIFY `dkim_pubkey` text"); + Database::query("ALTER TABLE `" . TABLE_PANEL_DOMAINS . "` MODIFY `specialsettings` text"); + Database::query("ALTER TABLE `" . TABLE_PANEL_IPSANDPORTS . "` MODIFY `specialsettings` text"); + Database::query("ALTER TABLE `" . TABLE_PANEL_IPSANDPORTS . "` MODIFY `default_vhostconf_domain` text"); + Database::query("ALTER TABLE `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` MODIFY `ssl_ca_file` text"); + Database::query("ALTER TABLE `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` MODIFY `ssl_cert_chainfile` text"); lastStepStatus(0); showUpdateStep("Removing old settings"); - Database::query("DELETE FROM `".TABLE_PANEL_SETTINGS."` WHERE `settinggroup`='panel' AND `varname` = 'use_webfonts';"); - Database::query("DELETE FROM `".TABLE_PANEL_SETTINGS."` WHERE `settinggroup`='panel' AND `varname` = 'webfont';"); + Database::query("DELETE FROM `" . TABLE_PANEL_SETTINGS . "` WHERE `settinggroup`='panel' AND `varname` = 'use_webfonts';"); + Database::query("DELETE FROM `" . TABLE_PANEL_SETTINGS . "` WHERE `settinggroup`='panel' AND `varname` = 'webfont';"); lastStepStatus(0); showUpdateStep("Adding local froxlor group to customer groups"); - if ((int)Settings::Get('system.mod_fcgid_ownvhost') == 1 || (int)Settings::Get('phpfpm.enabled_ownvhost') == 1) { - if ((int)Settings::Get('system.mod_fcgid') == 1) { + if ((int) Settings::Get('system.mod_fcgid_ownvhost') == 1 || (int) Settings::Get('phpfpm.enabled_ownvhost') == 1) { + if ((int) Settings::Get('system.mod_fcgid') == 1) { $local_user = Settings::Get('system.mod_fcgid_httpuser'); } else { $local_user = Settings::Get('phpfpm.vhost_httpuser'); } - Database::query("UPDATE `".TABLE_FTP_GROUPS."` SET `members` = CONCAT(`members`, ',".$local_user."');"); + Database::query("UPDATE `" . TABLE_FTP_GROUPS . "` SET `members` = CONCAT(`members`, '," . $local_user . "');"); lastStepStatus(0); } else { lastStepStatus(1, "not needed"); @@ -2859,21 +2956,21 @@ if (isFroxlorVersion('0.9.33-rc1')) { showUpdateStep("Updating from 0.9.33-rc1 to 0.9.33-rc2", false); showUpdateStep("Add new setting for sending cron-errors via mail"); - $sendcronerrors = isset($_POST['system_send_cron_errors']) ? (int)$_POST['system_send_cron_errors'] : "0"; + $sendcronerrors = isset($_POST['system_send_cron_errors']) ? (int) $_POST['system_send_cron_errors'] : "0"; Settings::addNew('system.send_cron_errors', $sendcronerrors); lastStepStatus(0); showUpdateStep("Add new custom-notes field for admins and customer"); - Database::query("ALTER TABLE `".TABLE_PANEL_ADMINS."` ADD `custom_notes` text AFTER `theme`"); - Database::query("ALTER TABLE `".TABLE_PANEL_ADMINS."` ADD `custom_notes_show` tinyint(1) NOT NULL default '0' AFTER `custom_notes`"); - Database::query("ALTER TABLE `".TABLE_PANEL_CUSTOMERS."` ADD `custom_notes` text AFTER `theme`"); - Database::query("ALTER TABLE `".TABLE_PANEL_CUSTOMERS."` ADD `custom_notes_show` tinyint(1) NOT NULL default '0' AFTER `custom_notes`"); + Database::query("ALTER TABLE `" . TABLE_PANEL_ADMINS . "` ADD `custom_notes` text AFTER `theme`"); + Database::query("ALTER TABLE `" . TABLE_PANEL_ADMINS . "` ADD `custom_notes_show` tinyint(1) NOT NULL default '0' AFTER `custom_notes`"); + Database::query("ALTER TABLE `" . TABLE_PANEL_CUSTOMERS . "` ADD `custom_notes` text AFTER `theme`"); + Database::query("ALTER TABLE `" . TABLE_PANEL_CUSTOMERS . "` ADD `custom_notes_show` tinyint(1) NOT NULL default '0' AFTER `custom_notes`"); lastStepStatus(0); // go from varchar(50) to varchar(255) because of some hashes that are longer than that showUpdateStep("Updating table structure of admins and customers"); - Database::query("ALTER TABLE `".TABLE_PANEL_ADMINS."` MODIFY `password` varchar(255) NOT NULL default ''"); - Database::query("ALTER TABLE `".TABLE_PANEL_CUSTOMERS."` MODIFY `password` varchar(255) NOT NULL default ''"); + Database::query("ALTER TABLE `" . TABLE_PANEL_ADMINS . "` MODIFY `password` varchar(255) NOT NULL default ''"); + Database::query("ALTER TABLE `" . TABLE_PANEL_CUSTOMERS . "` MODIFY `password` varchar(255) NOT NULL default ''"); lastStepStatus(0); updateToVersion('0.9.33-rc2'); @@ -2884,197 +2981,207 @@ if (isFroxlorVersion('0.9.33-rc2')) { showUpdateStep("Updating from 0.9.33-rc2 to 0.9.33-rc3"); lastStepStatus(0); updateToVersion('0.9.33-rc3'); - } if (isFroxlorVersion('0.9.33-rc3')) { - showUpdateStep("Updating from 0.9.33-rc3 to 0.9.33 final"); - lastStepStatus(0); - updateToVersion('0.9.33'); - + showUpdateStep("Updating from 0.9.33-rc3 to 0.9.33 final"); + lastStepStatus(0); + updateToVersion('0.9.33'); } if (isFroxlorVersion('0.9.33')) { - showUpdateStep("Updating from 0.9.33 to 0.9.33.1"); - lastStepStatus(0); - updateToVersion('0.9.33.1'); - + showUpdateStep("Updating from 0.9.33 to 0.9.33.1"); + lastStepStatus(0); + updateToVersion('0.9.33.1'); } if (isFroxlorVersion('0.9.33.1')) { - showUpdateStep("Updating from 0.9.33.1 to 0.9.33.2"); - lastStepStatus(0); - updateToVersion('0.9.33.2'); - + showUpdateStep("Updating from 0.9.33.1 to 0.9.33.2"); + lastStepStatus(0); + updateToVersion('0.9.33.2'); } if (isFroxlorVersion('0.9.33.2')) { - showUpdateStep("Updating from 0.9.33.2 to 0.9.34-dev1", false); + showUpdateStep("Updating from 0.9.33.2 to 0.9.34-dev1", false); - showUpdateStep("Updating table structure of domains"); - Database::query("ALTER TABLE `".TABLE_PANEL_DOMAINS."` MODIFY `parentdomainid` int(11) NOT NULL default '0'"); - lastStepStatus(0); + showUpdateStep("Updating table structure of domains"); + Database::query("ALTER TABLE `" . TABLE_PANEL_DOMAINS . "` MODIFY `parentdomainid` int(11) NOT NULL default '0'"); + lastStepStatus(0); - showUpdateStep("Updating stored email-templates"); - $chk_stmt = Database::prepare("SELECT * FROM `".TABLE_PANEL_TEMPLATES."` WHERE `templategroup` = 'mails'"); - Database::pexecute($chk_stmt); - // do we have any? - if ($chk_stmt->rowCount() > 0) { - // prepare update-statement - $upd_stmt = Database::prepare("UPDATE `".TABLE_PANEL_TEMPLATES."` SET `language` = :lang WHERE `id` = :id"); - // get each row - while ($row = $chk_stmt->fetch()) { - // let htmlentities run over the language name and update the entry - Database::pexecute($upd_stmt, array('lang' => htmlentities($row['language'])), false); - } - lastStepStatus(0); - } else { - lastStepStatus(1, "not needed"); - } + showUpdateStep("Updating stored email-templates"); + $chk_stmt = Database::prepare("SELECT * FROM `" . TABLE_PANEL_TEMPLATES . "` WHERE `templategroup` = 'mails'"); + Database::pexecute($chk_stmt); + // do we have any? + if ($chk_stmt->rowCount() > 0) { + // prepare update-statement + $upd_stmt = Database::prepare("UPDATE `" . TABLE_PANEL_TEMPLATES . "` SET `language` = :lang WHERE `id` = :id"); + // get each row + while ($row = $chk_stmt->fetch()) { + // let htmlentities run over the language name and update the entry + Database::pexecute($upd_stmt, array( + 'lang' => htmlentities($row['language']) + ), false); + } + lastStepStatus(0); + } else { + lastStepStatus(1, "not needed"); + } - showUpdateStep("Updating language descriptions to be in the native language"); - $upd_stmt = Database::prepare("UPDATE `".TABLE_PANEL_LANGUAGE."` SET `language` = :lang WHERE `iso` = :iso"); - Database::pexecute($upd_stmt, array('lang' => 'Français', 'iso' => 'fr'), false); - Database::pexecute($upd_stmt, array('lang' => 'Português', 'iso' => 'pt'), false); - Database::pexecute($upd_stmt, array('lang' => 'Italiano', 'iso' => 'it'), false); - Database::pexecute($upd_stmt, array('lang' => 'Nederlands', 'iso' => 'nl'), false); - Database::pexecute($upd_stmt, array('lang' => 'Svenska', 'iso' => 'sv'), false); - lastStepStatus(0); + showUpdateStep("Updating language descriptions to be in the native language"); + $upd_stmt = Database::prepare("UPDATE `" . TABLE_PANEL_LANGUAGE . "` SET `language` = :lang WHERE `iso` = :iso"); + Database::pexecute($upd_stmt, array( + 'lang' => 'Français', + 'iso' => 'fr' + ), false); + Database::pexecute($upd_stmt, array( + 'lang' => 'Português', + 'iso' => 'pt' + ), false); + Database::pexecute($upd_stmt, array( + 'lang' => 'Italiano', + 'iso' => 'it' + ), false); + Database::pexecute($upd_stmt, array( + 'lang' => 'Nederlands', + 'iso' => 'nl' + ), false); + Database::pexecute($upd_stmt, array( + 'lang' => 'Svenska', + 'iso' => 'sv' + ), false); + lastStepStatus(0); - updateToVersion('0.9.34-dev1'); + updateToVersion('0.9.34-dev1'); } if (isFroxlorVersion('0.9.34-dev1')) { - showUpdateStep("Updating from 0.9.34-dev1 to 0.9.34-dev2", false); + showUpdateStep("Updating from 0.9.34-dev1 to 0.9.34-dev2", false); - showUpdateStep("Adding new settings for apache-itk-mpm"); - Settings::AddNew("system.apacheitksupport", '0'); - lastStepStatus(0); + showUpdateStep("Adding new settings for apache-itk-mpm"); + Settings::AddNew("system.apacheitksupport", '0'); + lastStepStatus(0); - showUpdateStep("Increase text-field size of domain-ssl table"); - Database::query("ALTER TABLE `".TABLE_PANEL_DOMAIN_SSL_SETTINGS."` MODIFY `ssl_cert_file` mediumtext NOT NULL"); - Database::query("ALTER TABLE `".TABLE_PANEL_DOMAIN_SSL_SETTINGS."` MODIFY `ssl_key_file` mediumtext NOT NULL"); - Database::query("ALTER TABLE `".TABLE_PANEL_DOMAIN_SSL_SETTINGS."` MODIFY `ssl_ca_file` mediumtext NOT NULL"); - Database::query("ALTER TABLE `".TABLE_PANEL_DOMAIN_SSL_SETTINGS."` MODIFY `ssl_cert_chainfile` mediumtext NOT NULL"); - lastStepStatus(0); - - updateToVersion('0.9.34-dev2'); + showUpdateStep("Increase text-field size of domain-ssl table"); + Database::query("ALTER TABLE `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` MODIFY `ssl_cert_file` mediumtext NOT NULL"); + Database::query("ALTER TABLE `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` MODIFY `ssl_key_file` mediumtext NOT NULL"); + Database::query("ALTER TABLE `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` MODIFY `ssl_ca_file` mediumtext NOT NULL"); + Database::query("ALTER TABLE `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` MODIFY `ssl_cert_chainfile` mediumtext NOT NULL"); + lastStepStatus(0); + updateToVersion('0.9.34-dev2'); } if (isFroxlorVersion('0.9.34-dev2')) { - showUpdateStep("Updating from 0.9.34-dev2 to 0.9.34-dev3", false); + showUpdateStep("Updating from 0.9.34-dev2 to 0.9.34-dev3", false); - $do_update = true; - showUpdateStep("Checking for required PHP mbstring-extension"); - if (!extension_loaded('mbstring')) { - $do_update = false; - lastStepStatus(2, 'not installed'); - } else { - lastStepStatus(0); - } + $do_update = true; + showUpdateStep("Checking for required PHP mbstring-extension"); + if (! extension_loaded('mbstring')) { + $do_update = false; + lastStepStatus(2, 'not installed'); + } else { + lastStepStatus(0); + } - if ($do_update) { - updateToVersion('0.9.34-dev3'); - } + if ($do_update) { + updateToVersion('0.9.34-dev3'); + } } - if (isFroxlorVersion('0.9.34-dev3')) { - showUpdateStep("Updating from 0.9.34-dev3 to 0.9.34-dev4", false); + showUpdateStep("Updating from 0.9.34-dev3 to 0.9.34-dev4", false); - showUpdateStep("Adding field umask to phpconfig table"); - Database::query("ALTER TABLE `".TABLE_PANEL_PHPCONFIGS."` ADD `mod_fcgid_umask` varchar(15) NOT NULL DEFAULT '022' AFTER `mod_fcgid_maxrequests`"); - lastStepStatus(0); + showUpdateStep("Adding field umask to phpconfig table"); + Database::query("ALTER TABLE `" . TABLE_PANEL_PHPCONFIGS . "` ADD `mod_fcgid_umask` varchar(15) NOT NULL DEFAULT '022' AFTER `mod_fcgid_maxrequests`"); + lastStepStatus(0); - updateToVersion('0.9.34-dev4'); + updateToVersion('0.9.34-dev4'); } if (isFroxlorVersion('0.9.34-dev4')) { - showUpdateStep("Updating from 0.9.34-dev4 to 0.9.34 final"); - lastStepStatus(0); + showUpdateStep("Updating from 0.9.34-dev4 to 0.9.34 final"); + lastStepStatus(0); - updateToVersion('0.9.34'); + updateToVersion('0.9.34'); } if (isFroxlorVersion('0.9.34')) { - showUpdateStep("Updating from 0.9.34 to 0.9.34.1"); - lastStepStatus(0); + showUpdateStep("Updating from 0.9.34 to 0.9.34.1"); + lastStepStatus(0); - updateToVersion('0.9.34.1'); + updateToVersion('0.9.34.1'); } if (isFroxlorVersion('0.9.34.1')) { - showUpdateStep("Updating from 0.9.34.1 to 0.9.34.2"); - lastStepStatus(0); + showUpdateStep("Updating from 0.9.34.1 to 0.9.34.2"); + lastStepStatus(0); - updateToVersion('0.9.34.2'); + updateToVersion('0.9.34.2'); } if (isFroxlorVersion('0.9.34.2')) { - showUpdateStep("Updating from 0.9.34.2 to 0.9.35-dev1", false); + showUpdateStep("Updating from 0.9.34.2 to 0.9.35-dev1", false); - showUpdateStep("Adding Let's Encrypt - certificate fields"); - Database::query("ALTER TABLE `".TABLE_PANEL_DOMAIN_SSL_SETTINGS."` ADD `expirationdate` DATETIME NULL AFTER `ssl_cert_chainfile`;"); - Database::query("ALTER TABLE `".TABLE_PANEL_CUSTOMERS."` ADD `lepublickey` MEDIUMTEXT DEFAULT NULL AFTER `custom_notes_show`"); - Database::query("ALTER TABLE `".TABLE_PANEL_CUSTOMERS."` ADD `leprivatekey` MEDIUMTEXT DEFAULT NULL AFTER `lepublickey`;"); - Database::query("ALTER TABLE `".TABLE_PANEL_DOMAINS."` ADD `letsencrypt` TINYINT(1) NOT NULL DEFAULT '0' AFTER `ismainbutsubto`;"); - Settings::AddNew("system.leprivatekey", 'unset'); - Settings::AddNew("system.lepublickey", 'unset'); - showUpdateStep("Adding new cron-module for Let's encrypt"); - $stmt = Database::prepare(" + showUpdateStep("Adding Let's Encrypt - certificate fields"); + Database::query("ALTER TABLE `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` ADD `expirationdate` DATETIME NULL AFTER `ssl_cert_chainfile`;"); + Database::query("ALTER TABLE `" . TABLE_PANEL_CUSTOMERS . "` ADD `lepublickey` MEDIUMTEXT DEFAULT NULL AFTER `custom_notes_show`"); + Database::query("ALTER TABLE `" . TABLE_PANEL_CUSTOMERS . "` ADD `leprivatekey` MEDIUMTEXT DEFAULT NULL AFTER `lepublickey`;"); + Database::query("ALTER TABLE `" . TABLE_PANEL_DOMAINS . "` ADD `letsencrypt` TINYINT(1) NOT NULL DEFAULT '0' AFTER `ismainbutsubto`;"); + Settings::AddNew("system.leprivatekey", 'unset'); + Settings::AddNew("system.lepublickey", 'unset'); + showUpdateStep("Adding new cron-module for Let's encrypt"); + $stmt = Database::prepare(" INSERT INTO `" . TABLE_PANEL_CRONRUNS . "` SET `module` = 'froxlor/letsencrypt', `cronfile` = 'letsencrypt', `interval` = '5 MINUTE', `desc_lng_key` = 'cron_letsencrypt', `lastrun` = UNIX_TIMESTAMP(), - `isactive` = 0" - ); - Database::pexecute($stmt); - lastStepStatus(0); + `isactive` = 0"); + Database::pexecute($stmt); + lastStepStatus(0); - updateToVersion('0.9.35-dev1'); + updateToVersion('0.9.35-dev1'); } if (isFroxlorVersion('0.9.35-dev1')) { - showUpdateStep("Updating from 0.9.35-dev1 to 0.9.35-dev2", false); + showUpdateStep("Updating from 0.9.35-dev1 to 0.9.35-dev2", false); - showUpdateStep("Adding Let's Encrypt - settings"); - Settings::AddNew("system.letsencryptca", 'production'); - Settings::AddNew("system.letsencryptcountrycode", 'DE'); - Settings::AddNew("system.letsencryptstate", 'Germany'); - lastStepStatus(0); + showUpdateStep("Adding Let's Encrypt - settings"); + Settings::AddNew("system.letsencryptca", 'production'); + Settings::AddNew("system.letsencryptcountrycode", 'DE'); + Settings::AddNew("system.letsencryptstate", 'Germany'); + lastStepStatus(0); - updateToVersion('0.9.35-dev2'); + updateToVersion('0.9.35-dev2'); } if (isFroxlorVersion('0.9.35-dev2')) { - showUpdateStep("Updating from 0.9.35-dev2 to 0.9.35-dev3", false); + showUpdateStep("Updating from 0.9.35-dev2 to 0.9.35-dev3", false); - showUpdateStep("Adding new domain fields for Let's Encrypt"); - Database::query("ALTER TABLE `".TABLE_PANEL_DOMAINS."` ADD `termination_date` date NOT NULL AFTER `registration_date`"); - lastStepStatus(0); + showUpdateStep("Adding new domain fields for Let's Encrypt"); + Database::query("ALTER TABLE `" . TABLE_PANEL_DOMAINS . "` ADD `termination_date` date NOT NULL AFTER `registration_date`"); + lastStepStatus(0); - updateToVersion('0.9.35-dev3'); + updateToVersion('0.9.35-dev3'); } if (isFroxlorVersion('0.9.35-dev3')) { - showUpdateStep("Updating from 0.9.35-dev3 to 0.9.35-dev4", false); + showUpdateStep("Updating from 0.9.35-dev3 to 0.9.35-dev4", false); // remove unused setting showUpdateStep("Removing unused setting "Send cron-errors to froxlor-admin via e-mail""); @@ -3084,19 +3191,18 @@ if (isFroxlorVersion('0.9.35-dev3')) { updateToVersion('0.9.35-dev4'); } - if (isFroxlorVersion('0.9.35-dev4')) { - showUpdateStep("Updating from 0.9.35-dev4 to 0.9.35-dev5", false); + showUpdateStep("Updating from 0.9.35-dev4 to 0.9.35-dev5", false); showUpdateStep("Adding more Let's Encrypt settings"); - Settings::AddNew("system.letsencryptchallengepath", FROXLOR_INSTALL_DIR); - Settings::AddNew("system.letsencryptkeysize", '4096'); - Settings::AddNew("system.letsencryptreuseold", 0); - Database::query("ALTER TABLE `".TABLE_PANEL_DOMAIN_SSL_SETTINGS."` ADD `ssl_csr_file` MEDIUMTEXT AFTER `ssl_cert_chainfile`;"); - Database::query("ALTER TABLE `".TABLE_PANEL_DOMAINS."` ADD `hsts` VARCHAR(10) NOT NULL DEFAULT '0' AFTER `letsencrypt`"); - Database::query("ALTER TABLE `".TABLE_PANEL_DOMAINS."` ADD `hsts_sub` TINYINT(1) NOT NULL DEFAULT '0' AFTER `hsts`"); - Database::query("ALTER TABLE `".TABLE_PANEL_DOMAINS."` ADD `hsts_preload` TINYINT(1) NOT NULL DEFAULT '1' AFTER `hsts_sub`"); + Settings::AddNew("system.letsencryptchallengepath", FROXLOR_INSTALL_DIR); + Settings::AddNew("system.letsencryptkeysize", '4096'); + Settings::AddNew("system.letsencryptreuseold", 0); + Database::query("ALTER TABLE `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` ADD `ssl_csr_file` MEDIUMTEXT AFTER `ssl_cert_chainfile`;"); + Database::query("ALTER TABLE `" . TABLE_PANEL_DOMAINS . "` ADD `hsts` VARCHAR(10) NOT NULL DEFAULT '0' AFTER `letsencrypt`"); + Database::query("ALTER TABLE `" . TABLE_PANEL_DOMAINS . "` ADD `hsts_sub` TINYINT(1) NOT NULL DEFAULT '0' AFTER `hsts`"); + Database::query("ALTER TABLE `" . TABLE_PANEL_DOMAINS . "` ADD `hsts_preload` TINYINT(1) NOT NULL DEFAULT '1' AFTER `hsts_sub`"); lastStepStatus(0); updateToVersion('0.9.35-dev5'); @@ -3118,8 +3224,8 @@ if (isFroxlorVersion('0.9.35-dev5')) { lastStepStatus(0); showUpdateStep("Adding new fields to panel_domains table"); - Database::query("ALTER TABLE `" . TABLE_PANEL_DOMAINS ."` ADD `vhost_usedefaultlocation` tinyint(1) NOT NULL default '1' AFTER `ssl_redirect`;"); - Database::query("ALTER TABLE `" . TABLE_PANEL_DOMAINS ."` ADD `vhostsettingid` tinyint(11) NOT NULL default '0' AFTER `vhost_usedefaultlocation`;"); + Database::query("ALTER TABLE `" . TABLE_PANEL_DOMAINS . "` ADD `vhost_usedefaultlocation` tinyint(1) NOT NULL default '1' AFTER `ssl_redirect`;"); + Database::query("ALTER TABLE `" . TABLE_PANEL_DOMAINS . "` ADD `vhostsettingid` tinyint(11) NOT NULL default '0' AFTER `vhost_usedefaultlocation`;"); lastStepStatus(0); updateToVersion('0.9.35-dev6'); @@ -3139,35 +3245,42 @@ if (isFroxlorVersion('0.9.35-dev6')) { if (isFroxlorVersion('0.9.35-dev7')) { - showUpdateStep("Updating from 0.9.35-dev7 to 0.9.35-rc1"); - lastStepStatus(0); + showUpdateStep("Updating from 0.9.35-dev7 to 0.9.35-rc1"); + lastStepStatus(0); - updateToVersion('0.9.35-rc1'); + updateToVersion('0.9.35-rc1'); } if (isFroxlorVersion('0.9.35-rc1') && isDatabaseVersion(null)) { - Settings::AddNew("panel.db_version", "201603070"); + Settings::AddNew("panel.db_version", "201603070"); - showUpdateStep("Removing unused table and fields from database"); - Database::query("DROP TABLE IF EXISTS `panel_vhostconfigs`;"); - Database::query("ALTER TABLE `" . TABLE_PANEL_DOMAINS ."` DROP `vhost_usedefaultlocation`;"); - Database::query("ALTER TABLE `" . TABLE_PANEL_DOMAINS ."` DROP `vhostsettingid`;"); - lastStepStatus(0); - - showUpdateStep("Adding new setting to enable/disable Let's Encrypt"); - $enable_letsencrypt = isset($_POST['enable_letsencrypt']) ? (int)$_POST['enable_letsencrypt'] : "1"; - Settings::AddNew("system.leenabled", $enable_letsencrypt); - Database::query("UPDATE `".TABLE_PANEL_CRONRUNS."` SET `isactive` = '".$enable_letsencrypt."' WHERE `cronfile` = 'letsencrypt'"); - lastStepStatus(0); + showUpdateStep("Removing unused table and fields from database"); + Database::query("DROP TABLE IF EXISTS `panel_vhostconfigs`;"); + Database::query("ALTER TABLE `" . TABLE_PANEL_DOMAINS . "` DROP `vhost_usedefaultlocation`;"); + Database::query("ALTER TABLE `" . TABLE_PANEL_DOMAINS . "` DROP `vhostsettingid`;"); + lastStepStatus(0); + showUpdateStep("Adding new setting to enable/disable Let's Encrypt"); + $enable_letsencrypt = isset($_POST['enable_letsencrypt']) ? (int) $_POST['enable_letsencrypt'] : "1"; + Settings::AddNew("system.leenabled", $enable_letsencrypt); + Database::query("UPDATE `" . TABLE_PANEL_CRONRUNS . "` SET `isactive` = '" . $enable_letsencrypt . "' WHERE `cronfile` = 'letsencrypt'"); + lastStepStatus(0); } if (isDatabaseVersion('201603070')) { showUpdateStep("Adding new php.ini directive to php-configurations: opcache.restrict_api"); - Database::query("UPDATE `" . TABLE_PANEL_PHPCONFIGS ."` SET `phpsettings` = CONCAT(`phpsettings`, '\r\nopcache.restrict_api = \"{DOCUMENT_ROOT}\"\r\n');"); + Database::query("UPDATE `" . TABLE_PANEL_PHPCONFIGS . "` SET `phpsettings` = CONCAT(`phpsettings`, '\r\nopcache.restrict_api = \"{DOCUMENT_ROOT}\"\r\n');"); lastStepStatus(0); updateToDbVersion('201603150'); } + +if (isFroxlorVersion('0.9.35-rc1')) { + + showUpdateStep("Updating from 0.9.35-rc1 to 0.9.35 final"); + lastStepStatus(0); + + updateToVersion('0.9.35'); +} diff --git a/lib/version.inc.php b/lib/version.inc.php index 654974fd..ea86f687 100644 --- a/lib/version.inc.php +++ b/lib/version.inc.php @@ -16,7 +16,7 @@ */ // Main version variable -$version = '0.9.35-rc1'; +$version = '0.9.35'; // Database version (YYYYMMDDC where C is a daily counter) $dbversion = '201603150'; From 533112720463fdb712140d116e058d9c223cc00e Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Fri, 8 Apr 2016 13:52:06 +0200 Subject: [PATCH 0005/1335] set version to 0.9.35.1; fix updater :x Signed-off-by: Michael Kaufmann (d00p) --- admin_updates.php | 2 +- install/froxlor.sql | 2 +- install/updates/froxlor/0.9/update_0.9.inc.php | 8 ++++++++ lib/version.inc.php | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/admin_updates.php b/admin_updates.php index a7398b08..33ba90a6 100644 --- a/admin_updates.php +++ b/admin_updates.php @@ -54,7 +54,7 @@ if ($page == 'overview') { } } - if (hasDbUpdates($dbversion)) { + if (hasDbUpdates($dbversion) || hasUpdates($version)) { $successful_update = false; $message = ''; diff --git a/install/froxlor.sql b/install/froxlor.sql index 26750025..ae2ec259 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -555,7 +555,7 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES ('panel', 'password_numeric', '0'), ('panel', 'password_special_char_required', '0'), ('panel', 'password_special_char', '!?<>§$%+#=@'), - ('panel', 'version', '0.9.35'), + ('panel', 'version', '0.9.35.1'), ('panel', 'db_version', '201603150'); diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index 3d3be715..2b435653 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -3284,3 +3284,11 @@ if (isFroxlorVersion('0.9.35-rc1')) { updateToVersion('0.9.35'); } + +if (isFroxlorVersion('0.9.35')) { + + showUpdateStep("Updating from 0.9.35 to 0.9.35.1"); + lastStepStatus(0); + + updateToVersion('0.9.35.1'); +} diff --git a/lib/version.inc.php b/lib/version.inc.php index ea86f687..39283a8f 100644 --- a/lib/version.inc.php +++ b/lib/version.inc.php @@ -16,7 +16,7 @@ */ // Main version variable -$version = '0.9.35'; +$version = '0.9.35.1'; // Database version (YYYYMMDDC where C is a daily counter) $dbversion = '201603150'; From 576c94f83cf6f692cade209de4542d2e29a423b7 Mon Sep 17 00:00:00 2001 From: Daniel Reichelt Date: Sun, 10 Apr 2016 02:56:06 +0200 Subject: [PATCH 0006/1335] fix #1615 --- scripts/jobs/cron_tasks.inc.dns.10.bind.php | 33 +++++++++++---------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/scripts/jobs/cron_tasks.inc.dns.10.bind.php b/scripts/jobs/cron_tasks.inc.dns.10.bind.php index bc7e6fe3..1561d717 100644 --- a/scripts/jobs/cron_tasks.inc.dns.10.bind.php +++ b/scripts/jobs/cron_tasks.inc.dns.10.bind.php @@ -121,8 +121,8 @@ class bind { } if (empty($domains)) { - $this->logger->logAction(CRON_ACTION, LOG_INFO, 'No domains found for nameserver-config, skipping...'); - return; + $this->logger->logAction(CRON_ACTION, LOG_INFO, 'No domains found for nameserver-config, skipping...'); + return; } // collect domain IDs of direct child domains as arrays in ['children'] column @@ -203,20 +203,23 @@ class bind { $subzones.= $this->walkDomainList($domains[$child_domain_id], $domains); } - if ($domain['ismainbutsubto'] == 0 && $domain['zonefile'] == '') { - $zonefile = $this->generateZone($domain); - $domain['zonefile'] = 'domains/' . $domain['domain'] . '.zone'; - $zonefile_name = makeCorrectFile(Settings::Get('system.bindconf_directory') . '/' . $domain['zonefile']); - $this->_known_filenames[] = basename($zonefile_name); - $zonefile_handler = fopen($zonefile_name, 'w'); - fwrite($zonefile_handler, $zonefile.$subzones); - fclose($zonefile_handler); - $this->logger->logAction(CRON_ACTION, LOG_INFO, '`' . $zonefile_name . '` zone written'); + if ($domain['zonefile'] == '') { + if ($domain['ismainbutsubto'] == 0) { + $zonefile = $this->generateZone($domain); + $domain['zonefile'] = 'domains/' . $domain['domain'] . '.zone'; + $zonefile_name = makeCorrectFile(Settings::Get('system.bindconf_directory') . '/' . $domain['zonefile']); + $this->_known_filenames[] = basename($zonefile_name); + $zonefile_handler = fopen($zonefile_name, 'w'); + fwrite($zonefile_handler, $zonefile.$subzones); + fclose($zonefile_handler); + $this->logger->logAction(CRON_ACTION, LOG_INFO, '`' . $zonefile_name . '` zone written'); + $this->_bindconf_file .= $this->_generateDomainConfig($domain); + } else { + return $this->generateZone($domain); + } } else { - return $this->generateZone($domain); - } - - if ($zonefile !== '') { + $this->logger->logAction(CRON_ACTION, LOG_INFO, 'Added zonefile ' . $domain['zonefile'] . ' for domain ' . $domain['domain'] . + ' - Note that you will also have to handle ALL records for ALL subdomains.'); $this->_bindconf_file .= $this->_generateDomainConfig($domain); } } From 2a05b89cc864657c34985731ab38625a4ef5c468 Mon Sep 17 00:00:00 2001 From: Daniel Reichelt Date: Sun, 10 Apr 2016 02:56:27 +0200 Subject: [PATCH 0007/1335] add explicit warning about sub-zones on usage of zonefiles --- lib/formfields/admin/domains/formfield.domains_add.php | 2 +- lib/formfields/admin/domains/formfield.domains_edit.php | 2 +- lng/english.lng.php | 1 + lng/german.lng.php | 1 + 4 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/formfields/admin/domains/formfield.domains_add.php b/lib/formfields/admin/domains/formfield.domains_add.php index 0b9972d6..c85d6500 100644 --- a/lib/formfields/admin/domains/formfield.domains_add.php +++ b/lib/formfields/admin/domains/formfield.domains_add.php @@ -207,7 +207,7 @@ return array( ), 'zonefile' => array( 'label' => 'Zonefile', - 'desc' => $lng['panel']['emptyfordefault'], + 'desc' => $lng['admin']['bindzonewarning'], 'type' => 'text' ) ) diff --git a/lib/formfields/admin/domains/formfield.domains_edit.php b/lib/formfields/admin/domains/formfield.domains_edit.php index 63ff7d50..68ffa009 100644 --- a/lib/formfields/admin/domains/formfield.domains_edit.php +++ b/lib/formfields/admin/domains/formfield.domains_edit.php @@ -232,7 +232,7 @@ return array( ), 'zonefile' => array( 'label' => 'Zonefile', - 'desc' => $lng['panel']['emptyfordefault'], + 'desc' => $lng['admin']['bindzonewarning'], 'type' => 'text', 'value' => $result['zonefile'] ) diff --git a/lng/english.lng.php b/lng/english.lng.php index bc3d3dc5..2ffd7934 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -318,6 +318,7 @@ $lng['admin']['templates']['USERNAME'] = 'Replaced with the customers account us $lng['admin']['templates']['PASSWORD'] = 'Replaced with the customers account password.'; $lng['admin']['templates']['EMAIL'] = 'Replaced with the address of the POP3/IMAP account.'; $lng['admin']['webserver'] = 'Webserver'; +$lng['admin']['bindzonewarning'] = $lng['panel']['emptyfordefault'] . '
ATTENTION: If you use a zonefile you will have to manage all required records for all sub-zones manually as well.'; /** * Serversettings diff --git a/lng/german.lng.php b/lng/german.lng.php index ecdabeac..f881cfc8 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -315,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']['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']['bindzonewarning'] = $lng['panel']['emptyfordefault'] . '
WARNUNG: Bei der Verwendung einer Zonendatei müssen alle benötigten Records aller Subdomains ebenfalls manuell verwaltet werden.'; /** * Serversettings From 0dfaf376c06bc7782b6c66987830ee805ce99255 Mon Sep 17 00:00:00 2001 From: Daniel Reichelt Date: Sun, 10 Apr 2016 02:48:03 +0200 Subject: [PATCH 0008/1335] remove obsolete language strings --- lng/dutch.lng.php | 2 -- lng/english.lng.php | 2 -- lng/french.lng.php | 2 -- lng/german.lng.php | 2 -- lng/italian.lng.php | 2 -- lng/portugues.lng.php | 2 -- lng/swedish.lng.php | 2 -- 7 files changed, 14 deletions(-) diff --git a/lng/dutch.lng.php b/lng/dutch.lng.php index 74742e6a..2ad74afe 100644 --- a/lng/dutch.lng.php +++ b/lng/dutch.lng.php @@ -310,8 +310,6 @@ $lng['serversettings']['bindconf_directory']['title'] = 'Bind configuratie map'; $lng['serversettings']['bindconf_directory']['description'] = 'Waar staan de bind configuratie bestanden?'; $lng['serversettings']['bindreload_command']['title'] = 'Bind reload commando'; $lng['serversettings']['bindreload_command']['description'] = 'Wat is het command om bind te herladen?'; -$lng['serversettings']['binddefaultzone']['title'] = 'Bind default zone'; -$lng['serversettings']['binddefaultzone']['description'] = 'Wat is de naam van de default zone?'; $lng['serversettings']['vmail_uid']['title'] = 'Mails-Uid'; $lng['serversettings']['vmail_uid']['description'] = 'Welk UserID moeten de e-mails hebben?'; $lng['serversettings']['vmail_gid']['title'] = 'Mails-Gid'; diff --git a/lng/english.lng.php b/lng/english.lng.php index 2ffd7934..242ff7c5 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -348,8 +348,6 @@ $lng['serversettings']['bindconf_directory']['title'] = 'Bind config directory'; $lng['serversettings']['bindconf_directory']['description'] = 'Where should bind configfiles be saved?'; $lng['serversettings']['bindreload_command']['title'] = 'Bind reload command'; $lng['serversettings']['bindreload_command']['description'] = 'What\'s the bind command to reload bind configfiles?'; -$lng['serversettings']['binddefaultzone']['title'] = 'Bind default zone'; -$lng['serversettings']['binddefaultzone']['description'] = 'What\'s the name of the default zone?'; $lng['serversettings']['vmail_uid']['title'] = 'Mails-UID'; $lng['serversettings']['vmail_uid']['description'] = 'Which UserID should mails have?'; $lng['serversettings']['vmail_gid']['title'] = 'Mails-GID'; diff --git a/lng/french.lng.php b/lng/french.lng.php index 25b26d15..e55f885f 100644 --- a/lng/french.lng.php +++ b/lng/french.lng.php @@ -344,8 +344,6 @@ $lng['serversettings']['bindconf_directory']['title'] = 'Emplacement du dossier $lng['serversettings']['bindconf_directory']['description'] = 'Oû doit être stocké la configuration de Bind / Named ?'; $lng['serversettings']['bindreload_command']['title'] = 'Commande de rechargement de Bind / Named'; $lng['serversettings']['bindreload_command']['description'] = 'Quelle est la commande pour recharger / redémarrer Bind / Named ?'; -$lng['serversettings']['binddefaultzone']['title'] = 'Nom du fichier de zone par défaut Bind / Named'; -$lng['serversettings']['binddefaultzone']['description'] = 'Quel est le nom du fichier de zone par défaut pour Bind / Named ?'; $lng['serversettings']['vmail_uid']['title'] = 'UID des e-mails'; $lng['serversettings']['vmail_uid']['description'] = 'Quel UID doivent avoir les e-mails ?'; $lng['serversettings']['vmail_gid']['title'] = 'GID des e-mails'; diff --git a/lng/german.lng.php b/lng/german.lng.php index f881cfc8..38815cc9 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -345,8 +345,6 @@ $lng['serversettings']['bindconf_directory']['title'] = 'Bind-Config-Directory'; $lng['serversettings']['bindconf_directory']['description'] = 'Wo liegen die Bind-Konfigurationsdateien?'; $lng['serversettings']['bindreload_command']['title'] = 'Bind-Reload-Command'; $lng['serversettings']['bindreload_command']['description'] = 'Wie heißt das Skript zum Neuladen der Bind-Konfigurationsdateien?'; -$lng['serversettings']['binddefaultzone']['title'] = 'Bind-Default-Zone'; -$lng['serversettings']['binddefaultzone']['description'] = 'Wie heißt die Default-Zone für alle Domains?'; $lng['serversettings']['vmail_uid']['title'] = 'Mail-UID'; $lng['serversettings']['vmail_uid']['description'] = 'Welche UID sollen die E-Mails haben?'; $lng['serversettings']['vmail_gid']['title'] = 'Mail-GID'; diff --git a/lng/italian.lng.php b/lng/italian.lng.php index caf7b64b..021be071 100644 --- a/lng/italian.lng.php +++ b/lng/italian.lng.php @@ -336,8 +336,6 @@ $lng['serversettings']['bindconf_directory']['title'] = 'Cartella configurazione $lng['serversettings']['bindconf_directory']['description'] = 'Dove sono i file di configurazione per Bind?'; $lng['serversettings']['bindreload_command']['title'] = 'Comando riavvio Bind'; $lng['serversettings']['bindreload_command']['description'] = 'Qual\'è il comando per riavviare Bind?'; -$lng['serversettings']['binddefaultzone']['title'] = 'Zona di default Bind'; -$lng['serversettings']['binddefaultzone']['description'] = 'Qual\'è il nome della zona di default Bind?'; $lng['serversettings']['vmail_uid']['title'] = 'UID Email'; $lng['serversettings']['vmail_uid']['description'] = 'Che UserID dovrebbe avere l\'utente che gestisce le Email?'; $lng['serversettings']['vmail_gid']['title'] = 'GID Email'; diff --git a/lng/portugues.lng.php b/lng/portugues.lng.php index 524935e4..f0122bf8 100644 --- a/lng/portugues.lng.php +++ b/lng/portugues.lng.php @@ -341,8 +341,6 @@ $lng['serversettings']['bindconf_directory']['title'] = 'Diretório de configura $lng['serversettings']['bindconf_directory']['description'] = 'Aonde estão os arquivos de configuração do bind?'; $lng['serversettings']['bindreload_command']['title'] = 'Comando de reiniciar o Bind'; $lng['serversettings']['bindreload_command']['description'] = 'Qual o comando para reiniciar o bind?'; -$lng['serversettings']['binddefaultzone']['title'] = 'Bind default zone'; -$lng['serversettings']['binddefaultzone']['description'] = 'Qual o nome da default zone?'; $lng['serversettings']['vmail_uid']['title'] = 'Mails-Uid'; $lng['serversettings']['vmail_uid']['description'] = 'Qual UserID os e-mails devem ter?'; $lng['serversettings']['vmail_gid']['title'] = 'Mails-Gid'; diff --git a/lng/swedish.lng.php b/lng/swedish.lng.php index f056a4fa..e0bf3ba7 100644 --- a/lng/swedish.lng.php +++ b/lng/swedish.lng.php @@ -328,8 +328,6 @@ $lng['serversettings']['bindconf_directory']['title'] = 'Bind konfigurationskata $lng['serversettings']['bindconf_directory']['description'] = 'Vilken sökväg skall det vara till bind:s konfigurationsfiler?'; $lng['serversettings']['bindreload_command']['title'] = 'Ange sökvägen till programmet som laddar om Bind (reload bind) konfigurationsfiler?'; $lng['serversettings']['bindreload_command']['description'] = 'Ange sökvägen till programmet som laddar om Bind (reload bind) konfigurationsfiler?'; -$lng['serversettings']['binddefaultzone']['title'] = 'Bind standard zone'; -$lng['serversettings']['binddefaultzone']['description'] = 'Vad är namnet på standard zonen?'; $lng['serversettings']['vmail_uid']['title'] = 'Mails-UID'; $lng['serversettings']['vmail_uid']['description'] = 'Vilket användarID (UserID) ska E-posten ha?'; $lng['serversettings']['vmail_gid']['title'] = 'Mails-GID'; From d474f2ec8f3dfd5cae24d5c72afeaac542d2ede7 Mon Sep 17 00:00:00 2001 From: floreno Date: Mon, 11 Apr 2016 06:55:48 +0200 Subject: [PATCH 0009/1335] min-height against height fix for ajax-changing-content --- templates/Sparkle/assets/js/main.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/Sparkle/assets/js/main.js b/templates/Sparkle/assets/js/main.js index 51a2f231..3d0ec460 100644 --- a/templates/Sparkle/assets/js/main.js +++ b/templates/Sparkle/assets/js/main.js @@ -70,7 +70,7 @@ $(document).ready(function() { var snheight = $('#sidenavigation').height(); var mainheight = $('#maincontent').height(); if (snheight > mainheight && !$('#newsfeed').length) { - $('#maincontent').height(snheight); + $('#maincontent').css("min-height", snheight); } // this is necessary for the special setting feature (ref #1010) $.getQueryVariable = function(key) { @@ -219,4 +219,4 @@ $(document).ready(function() { }); autosize($('textarea.shell')); -}); \ No newline at end of file +}); From 72d12826512ded5028dea98d759a7da1b9f7fae0 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 11 Apr 2016 08:00:38 +0200 Subject: [PATCH 0010/1335] require php-curl Signed-off-by: Michael Kaufmann (d00p) --- install/lib/class.FroxlorInstall.php | 570 ++++++++++++++------------- install/lng/english.lng.php | 1 - install/lng/french.lng.php | 1 - install/lng/german.lng.php | 1 - 4 files changed, 296 insertions(+), 277 deletions(-) diff --git a/install/lib/class.FroxlorInstall.php b/install/lib/class.FroxlorInstall.php index 73fd3eb8..89b6e853 100644 --- a/install/lib/class.FroxlorInstall.php +++ b/install/lib/class.FroxlorInstall.php @@ -23,17 +23,19 @@ * * Does the dirty work * - * @copyright (c) the authors - * @author Michael Kaufmann - * @author Froxlor team (2010-) - * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt - * @package Install + * @copyright (c) the authors + * @author Michael Kaufmann + * @author Froxlor team (2010-) + * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt + * @package Install * */ -class FroxlorInstall { +class FroxlorInstall +{ /** - * define froxlor basepath e.g. /var/www/froxlor + * define froxlor basepath e.g. + * /var/www/froxlor * * @var string */ @@ -64,21 +66,23 @@ class FroxlorInstall { * supported languages for install */ private $_languages = array( - 'german' => 'Deutsch', - 'english' => 'English', - 'french' => 'Français' + 'german' => 'Deutsch', + 'english' => 'English', + 'french' => 'Français' ); /** * currently used language + * * @var unknown - */ + */ private $_activelng = 'english'; /** * Class constructor */ - public function __construct() { + public function __construct() + { $this->_basepath = dirname(dirname(dirname(__FILE__))); $this->_data = array(); } @@ -86,15 +90,16 @@ class FroxlorInstall { /** * FC */ - public function run() { + public function run() + { // send headers $this->_sendHeaders(); // check if we have a valid installation already $this->_checkUserdataFile(); // include the functions - require $this->_basepath.'/lib/functions.php'; + require $this->_basepath . '/lib/functions.php'; // include the MySQL-Table-Definitions - require $this->_basepath.'/lib/tables.inc.php'; + require $this->_basepath . '/lib/tables.inc.php'; // include language $this->_includeLanguageFile(); // show the action @@ -104,15 +109,13 @@ class FroxlorInstall { /** * build up and show the install-process-pages */ - private function _showPage() { + private function _showPage() + { // set theme for templates $theme = $this->_theme; eval("echo \"" . $this->_getTemplate("header") . "\";"); // check install-state - if ((isset($_POST['installstep']) - && $_POST['installstep'] == '1') - || (isset($_GET['check']) && $_GET['check'] == '1') - ) { + if ((isset($_POST['installstep']) && $_POST['installstep'] == '1') || (isset($_GET['check']) && $_GET['check'] == '1')) { $pagetitle = $this->_lng['install']['title']; if ($this->_checkPostData()) { // ceck data and create userdata etc.etc.etc. @@ -123,8 +126,8 @@ class FroxlorInstall { } else { // this should not happen $result = array( - 'pagecontent' => "How did you manage to get here? Well, you shouldn't be here. Go back!", - 'pagenavigation' => '' + 'pagecontent' => "How did you manage to get here? Well, you shouldn't be here. Go back!", + 'pagenavigation' => '' ); } } else { @@ -146,7 +149,8 @@ class FroxlorInstall { * * @return boolean */ - private function _checkPostData() { + private function _checkPostData() + { $this->_guessServerName(); $this->_guessServerIP(); $this->_guessWebserver(); @@ -166,9 +170,7 @@ class FroxlorInstall { $posixgroup = posix_getgrgid(posix_getgid()); $this->_getPostField('httpgroup', $posixgroup['name']); - if ($this->_data['mysql_host'] == 'localhost' - || $this->_data['mysql_host'] == '127.0.0.1' - ) { + if ($this->_data['mysql_host'] == 'localhost' || $this->_data['mysql_host'] == '127.0.0.1') { $this->_data['mysql_access_host'] = $this->_data['mysql_host']; } else { $this->_data['mysql_access_host'] = $this->_data['serverip']; @@ -179,19 +181,7 @@ class FroxlorInstall { $this->_data['servername'] = ''; } - if (isset($_POST['installstep']) - && $_POST['installstep'] == '1' - && $this->_data['admin_pass1'] == $this->_data['admin_pass2'] - && $this->_data['admin_pass1'] != '' - && $this->_data['admin_pass2'] != '' - && $this->_data['mysql_unpriv_pass'] != '' - && $this->_data['mysql_root_pass'] != '' - && $this->_data['servername'] != '' - && $this->_data['serverip'] != '' - && $this->_data['httpuser'] != '' - && $this->_data['httpgroup'] != '' - && $this->_data['mysql_unpriv_user'] != $this->_data['mysql_root_user'] - ) { + if (isset($_POST['installstep']) && $_POST['installstep'] == '1' && $this->_data['admin_pass1'] == $this->_data['admin_pass2'] && $this->_data['admin_pass1'] != '' && $this->_data['admin_pass2'] != '' && $this->_data['mysql_unpriv_pass'] != '' && $this->_data['mysql_root_pass'] != '' && $this->_data['servername'] != '' && $this->_data['serverip'] != '' && $this->_data['httpuser'] != '' && $this->_data['httpgroup'] != '' && $this->_data['mysql_unpriv_user'] != $this->_data['mysql_root_user']) { return true; } return false; @@ -202,31 +192,31 @@ class FroxlorInstall { * * @return array */ - private function _doInstall() { - + private function _doInstall() + { $content = ""; // check for mysql-root-connection $content .= $this->_status_message('begin', $this->_lng['install']['testing_mysql']); - $options = array('PDO::MYSQL_ATTR_INIT_COMMAND' => 'set names utf8'); - $dsn = "mysql:host=".$this->_data['mysql_host'].";"; + $options = array( + 'PDO::MYSQL_ATTR_INIT_COMMAND' => 'set names utf8' + ); + $dsn = "mysql:host=" . $this->_data['mysql_host'] . ";"; $fatal_fail = false; try { - $db_root = new PDO( - $dsn, $this->_data['mysql_root_user'], $this->_data['mysql_root_pass'], $options - ); + $db_root = new PDO($dsn, $this->_data['mysql_root_user'], $this->_data['mysql_root_pass'], $options); } catch (PDOException $e) { // possibly without passwd? try { - $db_root = new PDO( - $dsn, $this->_data['mysql_root_user'], '', $options - ); + $db_root = new PDO($dsn, $this->_data['mysql_root_user'], '', $options); // set the given password $passwd_stmt = $db_root->prepare(" SET PASSWORD = PASSWORD(:passwd) "); - $passwd_stmt->execute(array('passwd' => $this->_data['mysql_root_pass'])); + $passwd_stmt->execute(array( + 'passwd' => $this->_data['mysql_root_pass'] + )); } catch (PDOException $e) { // nope $content .= $this->_status_message('red', $e->getMessage()); @@ -234,7 +224,7 @@ class FroxlorInstall { } } - if (!$fatal_fail) { + if (! $fatal_fail) { // ok, if we are here, the database connection is up and running $content .= $this->_status_message('green', "OK"); @@ -245,20 +235,21 @@ class FroxlorInstall { // importing data to new database $content .= $this->_importDatabaseData(); // create DB object for new database - $options = array('PDO::MYSQL_ATTR_INIT_COMMAND' => 'set names utf8'); - $dsn = "mysql:host=".$this->_data['mysql_host'].";dbname=".$this->_data['mysql_database'].";"; + $options = array( + 'PDO::MYSQL_ATTR_INIT_COMMAND' => 'set names utf8' + ); + $dsn = "mysql:host=" . $this->_data['mysql_host'] . ";dbname=" . $this->_data['mysql_database'] . ";"; $another_fail = false; try { - $db = new PDO( - $dsn, $this->_data['mysql_unpriv_user'], $this->_data['mysql_unpriv_pass'], $options - ); + $db = new PDO($dsn, $this->_data['mysql_unpriv_user'], $this->_data['mysql_unpriv_pass'], $options); } catch (PDOException $e) { // dafuq? this should have happened in _importDatabaseData() $content .= $this->_status_message('red', $e->getMessage()); $another_fail = true; - }; + } + ; - if (!$another_fail) { + if (! $another_fail) { // change settings accordingly $content .= $this->_doSettings($db); // create entries @@ -290,37 +281,39 @@ class FroxlorInstall { eval("\$navigation .= \"" . $this->_getTemplate("pagebottom") . "\";"); - return array('pagecontent' => $content, 'pagenavigation' => $navigation); + return array( + 'pagecontent' => $content, + 'pagenavigation' => $navigation + ); } /** * Create userdata.inc.php file */ - private function _createUserdataConf() { - + private function _createUserdataConf() + { $content = ""; $content .= $this->_status_message('begin', $this->_lng['install']['creating_configfile']); $userdata = "_data['mysql_host'], "'\\") . "';\n"; - $userdata.= "\$sql['user']='" . addcslashes($this->_data['mysql_unpriv_user'], "'\\") . "';\n"; - $userdata.= "\$sql['password']='" . addcslashes($this->_data['mysql_unpriv_pass'], "'\\") . "';\n"; - $userdata.= "\$sql['db']='" . addcslashes($this->_data['mysql_database'], "'\\") . "';\n"; - $userdata.= "\$sql_root[0]['caption']='Default';\n"; - $userdata.= "\$sql_root[0]['host']='" . addcslashes($this->_data['mysql_host'], "'\\") . "';\n"; - $userdata.= "\$sql_root[0]['user']='" . addcslashes($this->_data['mysql_root_user'], "'\\") . "';\n"; - $userdata.= "\$sql_root[0]['password']='" . addcslashes($this->_data['mysql_root_pass'], "'\\") . "';\n"; - $userdata.= "?>"; + $userdata .= "//automatically generated userdata.inc.php for Froxlor\n"; + $userdata .= "\$sql['host']='" . addcslashes($this->_data['mysql_host'], "'\\") . "';\n"; + $userdata .= "\$sql['user']='" . addcslashes($this->_data['mysql_unpriv_user'], "'\\") . "';\n"; + $userdata .= "\$sql['password']='" . addcslashes($this->_data['mysql_unpriv_pass'], "'\\") . "';\n"; + $userdata .= "\$sql['db']='" . addcslashes($this->_data['mysql_database'], "'\\") . "';\n"; + $userdata .= "\$sql_root[0]['caption']='Default';\n"; + $userdata .= "\$sql_root[0]['host']='" . addcslashes($this->_data['mysql_host'], "'\\") . "';\n"; + $userdata .= "\$sql_root[0]['user']='" . addcslashes($this->_data['mysql_root_user'], "'\\") . "';\n"; + $userdata .= "\$sql_root[0]['password']='" . addcslashes($this->_data['mysql_root_pass'], "'\\") . "';\n"; + $userdata .= "?>"; // test if we can store the userdata.inc.php in ../lib - if ($fp = @fopen(dirname(dirname(dirname(__FILE__))).'/lib/userdata.inc.php', 'w')) { + if ($fp = @fopen(dirname(dirname(dirname(__FILE__))) . '/lib/userdata.inc.php', 'w')) { $result = @fputs($fp, $userdata, strlen($userdata)); @fclose($fp); $content .= $this->_status_message('green', 'OK'); chmod('../lib/userdata.inc.php', 0440); - } - elseif ($fp = @fopen('/tmp/userdata.inc.php', 'w')) { + } 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']); @@ -341,42 +334,46 @@ class FroxlorInstall { * * @return string status messages */ - private function _doDataEntries(&$db) { - + private function _doDataEntries(&$db) + { $content = ""; $content .= $this->_status_message('begin', $this->_lng['install']['creating_entries']); // and lets insert the default ip and port $stmt = $db->prepare(" - INSERT INTO `".TABLE_PANEL_IPSANDPORTS."` SET + INSERT INTO `" . TABLE_PANEL_IPSANDPORTS . "` SET `ip`= :serverip, `port` = '80', `namevirtualhost_statement` = '1', `vhostcontainer` = '1', `vhostcontainer_servername_statement` = '1' "); - $stmt->execute(array('serverip' => $this->_data['serverip'])); + $stmt->execute(array( + 'serverip' => $this->_data['serverip'] + )); $defaultip = $db->lastInsertId(); // insert the defaultip $upd_stmt = $db->prepare(" - UPDATE `".TABLE_PANEL_SETTINGS."` SET + UPDATE `" . TABLE_PANEL_SETTINGS . "` SET `value` = :defaultip WHERE `settinggroup` = 'system' AND `varname` = 'defaultip' "); - $upd_stmt->execute(array('defaultip' => $defaultip)); + $upd_stmt->execute(array( + 'defaultip' => $defaultip + )); $content .= $this->_status_message('green', 'OK'); - //last but not least create the main admin + // last but not least create the main admin $content .= $this->_status_message('begin', $this->_lng['install']['adding_admin_user']); $ins_data = array( - 'loginname' => $this->_data['admin_user'], + 'loginname' => $this->_data['admin_user'], /* use SHA256 default crypt */ - 'password' => crypt($this->_data['admin_pass1'], '$5$'. md5(uniqid(microtime(), 1)) . md5(uniqid(microtime(), 1))), - 'email' => 'admin@' . $this->_data['servername'], - 'deflang' => $this->_languages[$this->_activelng] + 'password' => crypt($this->_data['admin_pass1'], '$5$' . md5(uniqid(microtime(), 1)) . md5(uniqid(microtime(), 1))), + 'email' => 'admin@' . $this->_data['servername'], + 'deflang' => $this->_languages[$this->_activelng] ); $ins_stmt = $db->prepare(" INSERT INTO `" . TABLE_PANEL_ADMINS . "` SET @@ -419,11 +416,12 @@ class FroxlorInstall { * @param string $varname * @param string $value */ - private function _updateSetting(&$stmt = null, $value = null, $group = null, $varname = null) { + private function _updateSetting(&$stmt = null, $value = null, $group = null, $varname = null) + { $stmt->execute(array( - 'group' => $group, - 'varname' => $varname, - 'value' => $value + 'group' => $group, + 'varname' => $varname, + 'value' => $value )); } @@ -434,8 +432,8 @@ class FroxlorInstall { * * @return string status messages */ - private function _doSettings(&$db) { - + private function _doSettings(&$db) + { $content = ""; $content .= $this->_status_message('begin', $this->_lng['install']['changing_data']); @@ -456,8 +454,8 @@ class FroxlorInstall { // necessary changes for webservers != apache2 if ($this->_data['webserver'] == "apache24") { - $this->_updateSetting($upd_stmt, 'apache2', 'system', 'webserver'); - $this->_updateSetting($upd_stmt, '1', 'system', 'apache24'); + $this->_updateSetting($upd_stmt, 'apache2', 'system', 'webserver'); + $this->_updateSetting($upd_stmt, '1', 'system', 'apache24'); } elseif ($this->_data['webserver'] == "lighttpd") { $this->_updateSetting($upd_stmt, '/etc/lighttpd/conf-enabled/', 'system', 'apacheconf_vhost'); $this->_updateSetting($upd_stmt, '/etc/lighttpd/froxlor-diroptions/', 'system', 'apacheconf_diroptions'); @@ -473,7 +471,7 @@ class FroxlorInstall { $this->_updateSetting($upd_stmt, '/etc/nginx/nginx.pem', 'system', 'ssl_cert_file'); $this->_updateSetting($upd_stmt, '/var/run/', 'phpfpm', 'fastcgi_ipcdir'); } - + $this->_updateSetting($upd_stmt, $this->_data['activate_newsfeed'], 'admin', 'show_news_feed'); $this->_updateSetting($upd_stmt, dirname(dirname(dirname(__FILE__))), 'system', 'letsencryptchallengepath'); @@ -482,13 +480,13 @@ class FroxlorInstall { // set specific times for some crons (traffic only at night, etc.) $ts = mktime(0, 0, 0, date('m', time()), date('d', time()), date('Y', time())); - $db->query("UPDATE `".TABLE_PANEL_CRONRUNS."` SET `lastrun` = '".$ts."' WHERE `cronfile` ='cron_traffic.php';"); + $db->query("UPDATE `" . TABLE_PANEL_CRONRUNS . "` SET `lastrun` = '" . $ts . "' WHERE `cronfile` ='cron_traffic.php';"); $ts = mktime(1, 0, 0, date('m', time()), date('d', time()), date('Y', time())); - $db->query("UPDATE `".TABLE_PANEL_CRONRUNS."` SET `lastrun` = '".$ts."' WHERE `cronfile` ='cron_used_tickets_reset.php';"); - $db->query("UPDATE `".TABLE_PANEL_CRONRUNS."` SET `lastrun` = '".$ts."' WHERE `cronfile` ='cron_ticketarchive.php';"); + $db->query("UPDATE `" . TABLE_PANEL_CRONRUNS . "` SET `lastrun` = '" . $ts . "' WHERE `cronfile` ='cron_used_tickets_reset.php';"); + $db->query("UPDATE `" . TABLE_PANEL_CRONRUNS . "` SET `lastrun` = '" . $ts . "' WHERE `cronfile` ='cron_ticketarchive.php';"); // insert task 99 to generate a correct cron.d-file automatically - $db->query("INSERT INTO `".TABLE_PANEL_TASKS."` SET `type` = '99';"); + $db->query("INSERT INTO `" . TABLE_PANEL_TASKS . "` SET `type` = '99';"); $content .= $this->_status_message('green', 'OK'); @@ -502,32 +500,33 @@ class FroxlorInstall { * * @return string status messages */ - private function _importDatabaseData() { - + private function _importDatabaseData() + { $content = ""; $content .= $this->_status_message('begin', $this->_lng['install']['testing_new_db']); - $options = array('PDO::MYSQL_ATTR_INIT_COMMAND' => 'set names utf8'); - $dsn = "mysql:host=".$this->_data['mysql_host'].";dbname=".$this->_data['mysql_database'].";"; + $options = array( + 'PDO::MYSQL_ATTR_INIT_COMMAND' => 'set names utf8' + ); + $dsn = "mysql:host=" . $this->_data['mysql_host'] . ";dbname=" . $this->_data['mysql_database'] . ";"; $fatal_fail = false; try { - $db = new PDO( - $dsn, $this->_data['mysql_unpriv_user'], $this->_data['mysql_unpriv_pass'], $options - ); + $db = new PDO($dsn, $this->_data['mysql_unpriv_user'], $this->_data['mysql_unpriv_pass'], $options); } catch (PDOException $e) { $content .= $this->_status_message('red', $e->getMessage()); $fatal_fail = true; - }; + } + ; - if (!$fatal_fail) { + if (! $fatal_fail) { $content .= $this->_status_message('green', 'OK'); $content .= $this->_status_message('begin', $this->_lng['install']['importing_data']); - $db_schema = dirname(dirname(__FILE__)).'/froxlor.sql'; + $db_schema = dirname(dirname(__FILE__)) . '/froxlor.sql'; $sql_query = @file_get_contents($db_schema); $sql_query = $this->_remove_remarks($sql_query); $sql_query = $this->_split_sql_file($sql_query, ';'); - for ($i = 0; $i < sizeof($sql_query); $i++) { + for ($i = 0; $i < sizeof($sql_query); $i ++) { if (trim($sql_query[$i]) != '') { $result = $db->query($sql_query[$i]); } @@ -547,8 +546,8 @@ class FroxlorInstall { * * @return string status messages */ - private function _createDatabaseAndUser(&$db_root) { - + private function _createDatabaseAndUser(&$db_root) + { $content = ""; // so first we have to delete the database and @@ -556,18 +555,30 @@ class FroxlorInstall { $content .= $this->_status_message('begin', $this->_lng['install']['prepare_db']); $del_stmt = $db_root->prepare("DELETE FROM `mysql`.`user` WHERE `User` = :user AND `Host` = :accesshost"); - $del_stmt->execute(array('user' => $this->_data['mysql_unpriv_user'], 'accesshost' => $this->_data['mysql_access_host'])); + $del_stmt->execute(array( + 'user' => $this->_data['mysql_unpriv_user'], + 'accesshost' => $this->_data['mysql_access_host'] + )); $del_stmt = $db_root->prepare("DELETE FROM `mysql`.`db` WHERE `User` = :user AND `Host` = :accesshost"); - $del_stmt->execute(array('user' => $this->_data['mysql_unpriv_user'], 'accesshost' => $this->_data['mysql_access_host'])); + $del_stmt->execute(array( + 'user' => $this->_data['mysql_unpriv_user'], + 'accesshost' => $this->_data['mysql_access_host'] + )); $del_stmt = $db_root->prepare("DELETE FROM `mysql`.`tables_priv` WHERE `User` = :user AND `Host` =:accesshost"); - $del_stmt->execute(array('user' => $this->_data['mysql_unpriv_user'], 'accesshost' => $this->_data['mysql_access_host'])); + $del_stmt->execute(array( + 'user' => $this->_data['mysql_unpriv_user'], + 'accesshost' => $this->_data['mysql_access_host'] + )); $del_stmt = $db_root->prepare("DELETE FROM `mysql`.`columns_priv` WHERE `User` = :user AND `Host` = :accesshost"); - $del_stmt->execute(array('user' => $this->_data['mysql_unpriv_user'], 'accesshost' => $this->_data['mysql_access_host'])); + $del_stmt->execute(array( + 'user' => $this->_data['mysql_unpriv_user'], + 'accesshost' => $this->_data['mysql_access_host'] + )); - $del_stmt = $db_root->prepare("DROP DATABASE IF EXISTS `".str_replace('`', '', $this->_data['mysql_database'])."`;"); + $del_stmt = $db_root->prepare("DROP DATABASE IF EXISTS `" . str_replace('`', '', $this->_data['mysql_database']) . "`;"); $del_stmt->execute(); $db_root->query("FLUSH PRIVILEGES;"); @@ -575,20 +586,16 @@ class FroxlorInstall { // we have to create a new user and database for the froxlor unprivileged mysql access $content .= $this->_status_message('begin', $this->_lng['install']['create_mysqluser_and_db']); - $ins_stmt = $db_root->prepare("CREATE DATABASE `".str_replace('`', '', $this->_data['mysql_database'])."` CHARACTER SET=utf8 COLLATE=utf8_general_ci"); + $ins_stmt = $db_root->prepare("CREATE DATABASE `" . str_replace('`', '', $this->_data['mysql_database']) . "` CHARACTER SET=utf8 COLLATE=utf8_general_ci"); $ins_stmt->execute(); $mysql_access_host_array = array_map('trim', explode(',', $this->_data['mysql_access_host'])); - if (in_array('127.0.0.1', $mysql_access_host_array) - && !in_array('localhost', $mysql_access_host_array) - ) { + if (in_array('127.0.0.1', $mysql_access_host_array) && ! in_array('localhost', $mysql_access_host_array)) { $mysql_access_host_array[] = 'localhost'; } - if (!in_array('127.0.0.1', $mysql_access_host_array) - && in_array('localhost', $mysql_access_host_array) - ) { + if (! in_array('127.0.0.1', $mysql_access_host_array) && in_array('localhost', $mysql_access_host_array)) { $mysql_access_host_array[] = '127.0.0.1'; } @@ -598,11 +605,17 @@ class FroxlorInstall { $stmt = $db_root->prepare(" GRANT ALL PRIVILEGES ON `" . $_db . "`.* TO :username@:host - IDENTIFIED BY 'password'" - ); - $stmt->execute(array("username" => $this->_data['mysql_unpriv_user'], "host" => $mysql_access_host)); + IDENTIFIED BY 'password'"); + $stmt->execute(array( + "username" => $this->_data['mysql_unpriv_user'], + "host" => $mysql_access_host + )); $stmt = $db_root->prepare("SET PASSWORD FOR :username@:host = PASSWORD(:password)"); - $stmt->execute(array("username" => $this->_data['mysql_unpriv_user'], "host" => $mysql_access_host, "password" => $this->_data['mysql_unpriv_pass'])); + $stmt->execute(array( + "username" => $this->_data['mysql_unpriv_user'], + "host" => $mysql_access_host, + "password" => $this->_data['mysql_unpriv_pass'] + )); } $db_root->query("FLUSH PRIVILEGES;"); @@ -619,15 +632,17 @@ class FroxlorInstall { * * @return string status messages */ - private function _backupExistingDatabase(&$db_root) { - + private function _backupExistingDatabase(&$db_root) + { $content = ""; // check for existing of former database $tables_exist = false; $sql = "SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = :database"; $result_stmt = $db_root->prepare($sql); - $result_stmt->execute(array('database' => $this->_data['mysql_database'])); + $result_stmt->execute(array( + 'database' => $this->_data['mysql_database'] + )); $rows = $db_root->query("SELECT FOUND_ROWS()")->fetchColumn(); // check result @@ -653,12 +668,12 @@ class FroxlorInstall { } 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 . " " . $this->_data['mysql_database'] . " -u " . $this->_data['mysql_root_user'] . " --password='" . $this->_data['mysql_root_pass'] . "' --result-file=" . $filename; $output = exec($command); if (stristr($output, "error")) { $content .= $this->_status_message('red', $this->_lng['install']['backup_failed']); } else { - $content .= $this->_status_message('green', 'OK ('.$filename.')'); + $content .= $this->_status_message('green', 'OK (' . $filename . ')'); } } else { $content .= $this->_status_message('red', $this->_lng['install']['backup_binary_missing']); @@ -671,18 +686,18 @@ class FroxlorInstall { /** * show form to collect all needed data for the install */ - private function _showDataForm() { - + private function _showDataForm() + { $content = ""; // form action $formaction = htmlspecialchars($_SERVER['PHP_SELF']); if (isset($_GET['check'])) { - $formaction .= '?check='.(int)$_GET['check']; + $formaction .= '?check=' . (int) $_GET['check']; } // language selection $language_options = ''; - while (list($language_file, $language_name) = each($this->_languages)) { - $language_options.= makeoption($language_name, $language_file, $this->_activelng, true, true); + while (list ($language_file, $language_name) = each($this->_languages)) { + $language_options .= makeoption($language_name, $language_file, $this->_activelng, true, true); } // get language-form-template eval("\$content .= \"" . $this->_getTemplate("lngform") . "\";"); @@ -701,51 +716,51 @@ class FroxlorInstall { // unpriv-user has to be different from root if ($this->_data['mysql_unpriv_user'] == $this->_data['mysql_root_user']) { $style = 'blue'; - } else { $style = ''; + } else { + $style = ''; } $formdata .= $this->_getSectionItemString('mysql_unpriv_user', true, $style); // is we posted and no password was given -> red - if (!empty($_POST['installstep']) && $this->_data['mysql_unpriv_pass'] == '') { + if (! empty($_POST['installstep']) && $this->_data['mysql_unpriv_pass'] == '') { $style = 'red'; - } else { $style = ''; + } else { + $style = ''; } $formdata .= $this->_getSectionItemString('mysql_unpriv_pass', true, $style, 'password'); // unpriv-user has to be different from root if ($this->_data['mysql_unpriv_user'] == $this->_data['mysql_root_user']) { $style = 'blue'; - } else { $style = ''; + } else { + $style = ''; } $formdata .= $this->_getSectionItemString('mysql_root_user', true, $style); // is we posted and no password was given -> red - if (!empty($_POST['installstep']) && $this->_data['mysql_root_pass'] == '') { + if (! empty($_POST['installstep']) && $this->_data['mysql_root_pass'] == '') { $style = 'red'; - } else { $style = ''; + } else { + $style = ''; } $formdata .= $this->_getSectionItemString('mysql_root_pass', true, $style, 'password'); /** * admin data - */ + */ $section = $this->_lng['install']['admin_account']; eval("\$formdata .= \"" . $this->_getTemplate("datasection") . "\";"); // user $formdata .= $this->_getSectionItemString('admin_user', true); // check for admin passwords to be equal - if (!empty($_POST['installstep']) && - ($this->_data['admin_pass1'] == '' - || $this->_data['admin_pass1'] != $this->_data['admin_pass2']) - ) { + if (! empty($_POST['installstep']) && ($this->_data['admin_pass1'] == '' || $this->_data['admin_pass1'] != $this->_data['admin_pass2'])) { $style = 'color:red;'; - } else { $style = ''; + } else { + $style = ''; } $formdata .= $this->_getSectionItemString('admin_pass1', true, $style, 'password'); // check for admin passwords to be equal - if (!empty($_POST['installstep']) && - ($this->_data['admin_pass2'] == '' - || $this->_data['admin_pass1'] != $this->_data['admin_pass2']) - ) { + if (! empty($_POST['installstep']) && ($this->_data['admin_pass2'] == '' || $this->_data['admin_pass1'] != $this->_data['admin_pass2'])) { $style = 'color:red;'; - } else { $style = ''; + } else { + $style = ''; } $formdata .= $this->_getSectionItemString('admin_pass2', true, $style, 'password'); // activate newsfeed? @@ -753,25 +768,28 @@ class FroxlorInstall { /** * Server data - */ + */ $section = $this->_lng['install']['serversettings']; eval("\$formdata .= \"" . $this->_getTemplate("datasection") . "\";"); // servername - if (!empty($_POST['installstep']) && $this->_data['servername'] == '') { + if (! empty($_POST['installstep']) && $this->_data['servername'] == '') { $style = 'color:red;'; - } else { $style = ''; + } else { + $style = ''; } $formdata .= $this->_getSectionItemString('servername', true, $style); // serverip - if (!empty($_POST['installstep']) && $this->_data['serverip'] == '') { + if (! empty($_POST['installstep']) && $this->_data['serverip'] == '') { $style = 'color:red;'; - } else { $style = ''; + } else { + $style = ''; } $formdata .= $this->_getSectionItemString('serverip', true, $style); // webserver - if (!empty($_POST['installstep']) && $this->_data['webserver'] == '') { + if (! empty($_POST['installstep']) && $this->_data['webserver'] == '') { $websrvstyle = 'color:red;'; - } else { $websrvstyle = ''; + } else { + $websrvstyle = ''; } // apache $formdata .= $this->_getSectionItemCheckbox('apache2', ($this->_data['webserver'] == 'apache2'), $websrvstyle); @@ -781,15 +799,17 @@ class FroxlorInstall { // nginx $formdata .= $this->_getSectionItemCheckbox('nginx', ($this->_data['webserver'] == 'nginx'), $websrvstyle); // webserver-user - if (!empty($_POST['installstep']) && $this->_data['httpuser'] == '') { + if (! empty($_POST['installstep']) && $this->_data['httpuser'] == '') { $style = 'color:red;'; - } else { $style = ''; + } else { + $style = ''; } $formdata .= $this->_getSectionItemString('httpuser', true, $style); // webserver-group - if (!empty($_POST['installstep']) && $this->_data['httpgroup'] == '') { + if (! empty($_POST['installstep']) && $this->_data['httpgroup'] == '') { $style = 'color:red;'; - } else { $style = ''; + } else { + $style = ''; } $formdata .= $this->_getSectionItemString('httpgroup', true, $style); @@ -798,7 +818,10 @@ class FroxlorInstall { eval("\$content .= \"" . $this->_getTemplate("dataform2") . "\";"); $navigation = ''; - return array('pagecontent' => $content, 'pagenavigation' => $navigation); + return array( + 'pagecontent' => $content, + 'pagenavigation' => $navigation + ); } /** @@ -806,12 +829,15 @@ class FroxlorInstall { * * @param string $fieldname * @param boolean $required - * @param string $style optional css - * @param string $type optional type of input-box (default: text) + * @param string $style + * optional css + * @param string $type + * optional type of input-box (default: text) * * @return string */ - private function _getSectionItemString($fieldname = null, $required = false, $style = "", $type = 'text') { + private function _getSectionItemString($fieldname = null, $required = false, $style = "", $type = 'text') + { $fieldlabel = $this->_lng['install'][$fieldname]; $fieldvalue = htmlspecialchars($this->_data[$fieldname]); if ($required) { @@ -831,7 +857,8 @@ class FroxlorInstall { * * @return string */ - private function _getSectionItemCheckbox($fieldname = null, $checked = false, $style = "") { + private function _getSectionItemCheckbox($fieldname = null, $checked = false, $style = "") + { $fieldlabel = $this->_lng['install'][$fieldname]; if ($checked) { $checked = 'checked="checked"'; @@ -850,20 +877,22 @@ class FroxlorInstall { * * @return string */ - private function _getSectionItemYesNo($fieldname = null, $checked = false, $style = "") { - $fieldlabel = $this->_lng['install'][$fieldname]; - if ($checked) { - $checked = 'checked="checked"'; - } - $sectionitem = ""; - eval("\$sectionitem .= \"" . $this->_getTemplate("dataitemyesno") . "\";"); - return $sectionitem; + private function _getSectionItemYesNo($fieldname = null, $checked = false, $style = "") + { + $fieldlabel = $this->_lng['install'][$fieldname]; + if ($checked) { + $checked = 'checked="checked"'; + } + $sectionitem = ""; + eval("\$sectionitem .= \"" . $this->_getTemplate("dataitemyesno") . "\";"); + return $sectionitem; } /** * check for requirements froxlor needs */ - private function _requirementCheck() { + private function _requirementCheck() + { // indicator whether we need to abort or not $_die = false; @@ -874,7 +903,7 @@ class FroxlorInstall { $content .= $this->_status_message('begin', $this->_lng['requirements']['phpversion']); if (version_compare("5.3.0", PHP_VERSION, ">=")) { - $content .= $this->_status_message('red', $this->_lng['requirements']['notfound'].' ('.PHP_VERSION.')'); + $content .= $this->_status_message('red', $this->_lng['requirements']['notfound'] . ' (' . PHP_VERSION . ')'); $_die = true; } else { $content .= $this->_status_message('green', PHP_VERSION); @@ -886,7 +915,7 @@ class FroxlorInstall { if (get_magic_quotes_runtime()) { // deactivate it set_magic_quotes_runtime(false); - $content .= $this->_status_message('orange', $this->_lng['requirements']['not_true'] . "
". $this->_lng['requirements']['phpmagic_quotes_runtime_description']); + $content .= $this->_status_message('orange', $this->_lng['requirements']['not_true'] . "
" . $this->_lng['requirements']['phpmagic_quotes_runtime_description']); } else { $content .= $this->_status_message('green', 'off'); } @@ -895,7 +924,7 @@ class FroxlorInstall { // check for php_pdo and pdo_mysql $content .= $this->_status_message('begin', $this->_lng['requirements']['phppdo']); - if (!extension_loaded('pdo') || in_array("mysql", PDO::getAvailableDrivers()) == false) { + if (! extension_loaded('pdo') || in_array("mysql", PDO::getAvailableDrivers()) == false) { $content .= $this->_status_message('red', $this->_lng['requirements']['notinstalled']); $_die = true; } else { @@ -905,7 +934,7 @@ class FroxlorInstall { // check for xml-extension $content .= $this->_status_message('begin', $this->_lng['requirements']['phpxml']); - if (!extension_loaded('xml')) { + if (! extension_loaded('xml')) { $content .= $this->_status_message('red', $this->_lng['requirements']['notinstalled']); $_die = true; } else { @@ -915,7 +944,7 @@ class FroxlorInstall { // check for filter-extension $content .= $this->_status_message('begin', $this->_lng['requirements']['phpfilter']); - if (!extension_loaded('filter')) { + if (! extension_loaded('filter')) { $content .= $this->_status_message('red', $this->_lng['requirements']['notinstalled']); $_die = true; } else { @@ -925,7 +954,7 @@ class FroxlorInstall { // check for posix-extension $content .= $this->_status_message('begin', $this->_lng['requirements']['phpposix']); - if (!extension_loaded('posix')) { + if (! extension_loaded('posix')) { $content .= $this->_status_message('red', $this->_lng['requirements']['notinstalled']); $_die = true; } else { @@ -935,18 +964,9 @@ class FroxlorInstall { // check for bstring-extension $content .= $this->_status_message('begin', $this->_lng['requirements']['phpmbstring']); - if (!extension_loaded('mbstring')) { - $content .= $this->_status_message('red', $this->_lng['requirements']['notinstalled']); - $_die = true; - } else { - $content .= $this->_status_message('green', $this->_lng['requirements']['installed']); - } - - // check for bcmath extension - $content .= $this->_status_message('begin', $this->_lng['requirements']['phpbcmath']); - - if (!extension_loaded('bcmath')) { - $content .= $this->_status_message('orange', $this->_lng['requirements']['notinstalled'] . "
" . $this->_lng['requirements']['bcmathdescription']); + if (! extension_loaded('mbstring')) { + $content .= $this->_status_message('red', $this->_lng['requirements']['notinstalled']); + $_die = true; } else { $content .= $this->_status_message('green', $this->_lng['requirements']['installed']); } @@ -954,8 +974,18 @@ class FroxlorInstall { // check for curl extension $content .= $this->_status_message('begin', $this->_lng['requirements']['phpcurl']); - if (!extension_loaded('curl')) { - $content .= $this->_status_message('orange', $this->_lng['requirements']['notinstalled'] . "
" . $this->_lng['requirements']['curldescription']); + if (! extension_loaded('curl')) { + $content .= $this->_status_message('red', $this->_lng['requirements']['notinstalled']); + $_die = true; + } else { + $content .= $this->_status_message('green', $this->_lng['requirements']['installed']); + } + + // check for bcmath extension + $content .= $this->_status_message('begin', $this->_lng['requirements']['phpbcmath']); + + if (! extension_loaded('bcmath')) { + $content .= $this->_status_message('orange', $this->_lng['requirements']['notinstalled'] . "
" . $this->_lng['requirements']['bcmathdescription']); } else { $content .= $this->_status_message('green', $this->_lng['requirements']['installed']); } @@ -963,7 +993,7 @@ class FroxlorInstall { // check for open_basedir $content .= $this->_status_message('begin', $this->_lng['requirements']['openbasedir']); $php_ob = @ini_get("open_basedir"); - if (!empty($php_ob) && $php_ob != '') { + if (! empty($php_ob) && $php_ob != '') { $content .= $this->_status_message('orange', $this->_lng['requirements']['activated'] . "
" . $this->_lng['requirements']['openbasedirenabled']); } else { $content .= $this->_status_message('green', 'off'); @@ -980,28 +1010,30 @@ class FroxlorInstall { } else { $msgcolor = 'green'; $message = $this->_lng['requirements']['froxlor_succ_checks']; - $link = htmlspecialchars($_SERVER['PHP_SELF']).'?check=1'; + $link = htmlspecialchars($_SERVER['PHP_SELF']) . '?check=1'; $linktext = $this->_lng['click_here_to_continue']; } eval("\$navigation .= \"" . $this->_getTemplate("pagebottom") . "\";"); - return array('pagecontent' => $content, 'pagenavigation' => $navigation); + return array( + 'pagecontent' => $content, + 'pagenavigation' => $navigation + ); } /** * send no-caching headers and set the default timezone */ - private function _sendHeaders() { + private function _sendHeaders() + { // no caching header("Cache-Control: no-store, no-cache, must-revalidate"); header("Pragma: no-cache"); - header('Last-Modified: ' . gmdate( 'D, d M Y H:i:s \G\M\T', time())); - header('Expires: ' . gmdate( 'D, d M Y H:i:s \G\M\T', time())); + header('Last-Modified: ' . gmdate('D, d M Y H:i:s \G\M\T', time())); + header('Expires: ' . gmdate('D, d M Y H:i:s \G\M\T', time())); // ensure that default timezone is set - if (function_exists("date_default_timezone_set") - && function_exists("date_default_timezone_get") - ) { + if (function_exists("date_default_timezone_set") && function_exists("date_default_timezone_get")) { @date_default_timezone_set(@date_default_timezone_get()); } } @@ -1010,18 +1042,17 @@ class FroxlorInstall { * check for the userdata - if it exists then froxlor is * already installed and we show a nice note */ - private function _checkUserDataFile() { - $userdata = $this->_basepath.'/lib/userdata.inc.php'; + private function _checkUserDataFile() + { + $userdata = $this->_basepath . '/lib/userdata.inc.php'; if (file_exists($userdata)) { // includes the usersettings (MySQL-Username/Passwort) // to test if Froxlor is already installed - require $this->_basepath.'/lib/userdata.inc.php'; + require $this->_basepath . '/lib/userdata.inc.php'; - if (isset($sql) - && is_array($sql) - ) { + if (isset($sql) && is_array($sql)) { // use sparkle theme for the notice - $installed_hint = file_get_contents($this->_basepath.'/templates/Sparkle/misc/alreadyinstalledhint.tpl'); + $installed_hint = file_get_contents($this->_basepath . '/templates/Sparkle/misc/alreadyinstalledhint.tpl'); $installed_hint = str_replace("", date('Y', time()), $installed_hint); die($installed_hint); } @@ -1031,18 +1062,15 @@ class FroxlorInstall { /** * include the chose language or else default (english) */ - private function _includeLanguageFile() { + private function _includeLanguageFile() + { // set default $standardlanguage = 'english'; // check either _GET or _POST - if (isset($_GET['language']) - && isset($this->_languages[$_GET['language']]) - ) { + if (isset($_GET['language']) && isset($this->_languages[$_GET['language']])) { $this->_activelng = $_GET['language']; - } elseif (isset($_POST['language']) - && isset($this->_languages[$_POST['language']]) - ) { + } elseif (isset($_POST['language']) && isset($this->_languages[$_POST['language']])) { $this->_activelng = $_POST['language']; } else { // try to guess the right language @@ -1060,7 +1088,7 @@ class FroxlorInstall { } } - $lngfile = $this->_basepath.'/install/lng/' . $this->_activelng . '.lng.php'; + $lngfile = $this->_basepath . '/install/lng/' . $this->_activelng . '.lng.php'; if (file_exists($lngfile)) { // includes file /lng/$language.lng.php if it exists require $lngfile; @@ -1071,17 +1099,17 @@ class FroxlorInstall { /** * Get template from filesystem * - * @param string $template name of the template including subdirectory + * @param string $template + * name of the template including subdirectory * * @return string */ - private function _getTemplate($template = null) { + private function _getTemplate($template = null) + { // build filename - $filename = $this->_basepath.'/install/templates/' . $template . '.tpl'; + $filename = $this->_basepath . '/install/templates/' . $template . '.tpl'; // check existence - if(file_exists($filename) - && is_readable($filename) - ) { + if (file_exists($filename) && is_readable($filename)) { $templatefile = addcslashes(file_get_contents($filename), '"\\'); // loop through template more than once in case we have an "if"-statement in another one while (preg_match('/(.*)(<\/if>|(.*)<\/if>)/Uis', $templatefile)) { @@ -1102,30 +1130,33 @@ class FroxlorInstall { * * @return string */ - private function _status_message($case, $text) { + private function _status_message($case, $text) + { if ($case == 'begin') { - return ''; + return ''; } } /** * get/guess servername */ - private function _guessServerName() { + private function _guessServerName() + { // from form? - if (!empty($_POST['servername'])) { + if (! empty($_POST['servername'])) { $this->_data['servername'] = $_POST['servername']; return; // from $_SERVER - } else if (!empty($_SERVER['SERVER_NAME'])) { - // no ips - if ($this->_validate_ip($_SERVER['SERVER_NAME']) == false) { - $this->_data['servername'] = $_SERVER['SERVER_NAME']; - return; + } else + if (! empty($_SERVER['SERVER_NAME'])) { + // no ips + if ($this->_validate_ip($_SERVER['SERVER_NAME']) == false) { + $this->_data['servername'] = $_SERVER['SERVER_NAME']; + return; + } } - } // empty $this->_data['servername'] = ''; } @@ -1133,13 +1164,14 @@ class FroxlorInstall { /** * get/guess serverip */ - private function _guessServerIP() { + private function _guessServerIP() + { // from form - if (!empty($_POST['serverip'])) { + if (! empty($_POST['serverip'])) { $this->_data['serverip'] = $_POST['serverip']; return; // from $_SERVER - } elseif(!empty($_SERVER['SERVER_ADDR'])) { + } elseif (! empty($_SERVER['SERVER_ADDR'])) { $this->_data['serverip'] = $_SERVER['SERVER_ADDR']; return; } @@ -1150,22 +1182,17 @@ class FroxlorInstall { /** * get/guess webserver-software */ - private function _guessWebserver() { + private function _guessWebserver() + { // post - if (!empty($_POST['webserver'])) { + if (! empty($_POST['webserver'])) { $this->_data['webserver'] = $_POST['webserver']; } else { - if (strtoupper(@php_sapi_name()) == "APACHE2HANDLER" - || stristr($_SERVER['SERVER_SOFTWARE'], "apache/2") - ) { + if (strtoupper(@php_sapi_name()) == "APACHE2HANDLER" || stristr($_SERVER['SERVER_SOFTWARE'], "apache/2")) { $this->_data['webserver'] = 'apache2'; - } elseif(substr(strtoupper(@php_sapi_name()), 0, 8) == "LIGHTTPD" - || stristr($_SERVER['SERVER_SOFTWARE'], "lighttpd") - ) { + } elseif (substr(strtoupper(@php_sapi_name()), 0, 8) == "LIGHTTPD" || stristr($_SERVER['SERVER_SOFTWARE'], "lighttpd")) { $this->_data['webserver'] = 'lighttpd'; - } elseif(substr(strtoupper(@php_sapi_name()), 0, 8) == "NGINX" - || stristr($_SERVER['SERVER_SOFTWARE'], "nginx") - ) { + } elseif (substr(strtoupper(@php_sapi_name()), 0, 8) == "NGINX" || stristr($_SERVER['SERVER_SOFTWARE'], "nginx")) { $this->_data['webserver'] = 'nginx'; } else { // we don't need to bail out, since unknown does not affect any critical installation routines @@ -1182,7 +1209,8 @@ class FroxlorInstall { * @param string $default * */ - private function _getPostField($fieldname = null, $default = null) { + private function _getPostField($fieldname = null, $default = null) + { // initialize $this->_data[$fieldname] = ''; // set default @@ -1190,7 +1218,7 @@ class FroxlorInstall { $this->_data[$fieldname] = $default; } // check field - if (!empty($_POST[$fieldname])) { + if (! empty($_POST[$fieldname])) { $this->_data[$fieldname] = $_POST[$fieldname]; } } @@ -1202,11 +1230,9 @@ class FroxlorInstall { * * @return boolean|string */ - private function _validate_ip($ip = null) { - if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false - && filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) === false - && filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_RES_RANGE) === false - ) { + private function _validate_ip($ip = null) + { + if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false && filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) === false && filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_RES_RANGE) === false) { return false; } return $ip; @@ -1219,17 +1245,15 @@ class FroxlorInstall { * * @return string */ - private function _remove_remarks($sql) { - + private function _remove_remarks($sql) + { $lines = explode("\n", $sql); // try to keep mem. use down $sql = ""; $linecount = count($lines); $output = ""; - for ($i = 0; $i < $linecount; $i++) { - if ($i != ($linecount - 1) - || strlen($lines[$i]) > 0 - ) { + for ($i = 0; $i < $linecount; $i ++) { + if ($i != ($linecount - 1) || strlen($lines[$i]) > 0) { if (substr($lines[$i], 0, 1) != "#") { $output .= $lines[$i] . "\n"; } else { @@ -1249,7 +1273,8 @@ class FroxlorInstall { * The whole function has been taken from the phpbb installer, * copyright by the phpbb team, phpbb in summer 2004. */ - private function _split_sql_file($sql, $delimiter) { + private function _split_sql_file($sql, $delimiter) + { // Split up our string into "possible" SQL statements. $tokens = explode($delimiter, $sql); @@ -1263,11 +1288,9 @@ class FroxlorInstall { // this is faster than calling count($tokens) every time through the loop. $token_count = count($tokens); - for ($i = 0; $i < $token_count; $i++) { + for ($i = 0; $i < $token_count; $i ++) { // Don't want to add an empty string as the last thing in the array. - if (($i != ($token_count - 1)) - || (strlen($tokens[$i] > 0)) - ) { + if (($i != ($token_count - 1)) || (strlen($tokens[$i] > 0))) { // This is the total number of single quotes in the token. $total_quotes = preg_match_all("/'/", $tokens[$i], $matches); @@ -1291,7 +1314,7 @@ class FroxlorInstall { $tokens[$i] = ""; // Do we have a complete statement yet? $complete_stmt = false; - for ($j = $i + 1; (!$complete_stmt && ($j < $token_count)); $j++) { + for ($j = $i + 1; (! $complete_stmt && ($j < $token_count)); $j ++) { // This is the total number of single quotes in the token. $total_quotes = preg_match_all("/'/", $tokens[$j], $matches); // Counts single quotes that are preceded by an odd number of backslashes, @@ -1323,5 +1346,4 @@ class FroxlorInstall { } return $output; } - } diff --git a/install/lng/english.lng.php b/install/lng/english.lng.php index bab624aa..4166c54f 100644 --- a/install/lng/english.lng.php +++ b/install/lng/english.lng.php @@ -34,7 +34,6 @@ $lng['requirements']['phpbcmath'] = 'PHP bcmath-extension...'; $lng['requirements']['phpcurl'] = 'PHP curl-extension...'; $lng['requirements']['phpmbstring'] = 'PHP mbstring-extension...'; $lng['requirements']['bcmathdescription'] = 'Traffic-calculation related functions will not work correctly!'; -$lng['requirements']['curldescription'] = 'Version-check and news-feed may not work correctly!'; $lng['requirements']['openbasedir'] = 'open_basedir...'; $lng['requirements']['openbasedirenabled'] = 'Froxlor will not work properly with open_basedir enabled. Please disable open_basedir for Froxlor in the coresponding php.ini'; $lng['requirements']['diedbecauseofrequirements'] = 'Cannot install Froxlor without these requirements! Try to fix them and retry.'; diff --git a/install/lng/french.lng.php b/install/lng/french.lng.php index ddcf815d..925b6220 100644 --- a/install/lng/french.lng.php +++ b/install/lng/french.lng.php @@ -34,7 +34,6 @@ $lng['requirements']['phpbcmath'] = 'extension PHP bcmath ...'; $lng['requirements']['phpcurl'] = 'extension PHP curl...'; $lng['requirements']['phpmbstring'] = 'extension PHP mbstring...'; $lng['requirements']['bcmathdescription'] = 'Les fonctions de calcul de traffic ne fonctionneront pas correctement!'; -$lng['requirements']['curldescription'] = 'Les vérifications de version et les flux d\'information peuvent ne pas fonctionner correctement!'; $lng['requirements']['openbasedir'] = 'open_basedir...'; $lng['requirements']['openbasedirenabled'] = 'Froxlor ne fonctionnera pas correctement avec open_basedir activé. Merci de désactiver open_basedir pour Froxlor dans le php.ini correspondant'; $lng['requirements']['diedbecauseofrequirements'] = 'Impossible d\'installer Froxlor sans ces prérequis! Essayez de les corriger et essayez à nouveau.'; diff --git a/install/lng/german.lng.php b/install/lng/german.lng.php index dd60e772..e87142cb 100644 --- a/install/lng/german.lng.php +++ b/install/lng/german.lng.php @@ -34,7 +34,6 @@ $lng['requirements']['phpbcmath'] = 'PHP bcmath-Erweiterung...'; $lng['requirements']['phpcurl'] = 'PHP curl-Erweiterung...'; $lng['requirements']['phpmbstring'] = 'PHP mbstring-Erweiterung...'; $lng['requirements']['bcmathdescription'] = 'Traffic-Berechnungs bezogene Funktionen stehen nicht vollständig zur Verfügung!'; -$lng['requirements']['curldescription'] = 'Versions-Prüfung und News-Feed stehen nicht vollständig zur Verfügung!'; $lng['requirements']['openbasedir'] = 'open_basedir genutzt wird...'; $lng['requirements']['openbasedirenabled'] = 'Froxlor wird mit aktiviertem open_basedir nicht vollständig funktionieren. Bitte deaktivieren Sie open_basedir für Froxlor in der entsprechenden php.ini'; $lng['requirements']['diedbecauseofrequirements'] = 'Kann Froxlor ohne diese Voraussetzungen nicht installieren! Beheben Sie die angezeigten Probleme und versuchen Sie es erneut.'; From 8565dbce8b0d674f7d639bd56b145a66a5b5fc77 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 11 Apr 2016 08:01:38 +0200 Subject: [PATCH 0011/1335] insert task 99 (regeneration of /etc/cron.d/froxlor file) to the list of task when clicking on 'Regenerate configfiles' Signed-off-by: Michael Kaufmann (d00p) --- admin_settings.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/admin_settings.php b/admin_settings.php index e8213225..18289d28 100644 --- a/admin_settings.php +++ b/admin_settings.php @@ -54,7 +54,7 @@ if ($page == 'overview' && $userinfo['change_serversettings'] == '1') { $settings_part = false; $only_enabledisable = true; } - + // check if the session timeout is too low #815 if (isset($_POST['session_sessiontimeout']) && $_POST['session_sessiontimeout'] < 60 @@ -160,6 +160,8 @@ if ($page == 'overview' && $userinfo['change_serversettings'] == '1') { inserttask('10'); // Using nameserver, insert a task which rebuilds the server config inserttask('4'); + // cron.d file + inserttask('99'); standard_success('rebuildingconfigs', '', array('filename' => 'admin_index.php')); From 84f1d94ad63088229497a7d0ed42933ba2c84c7a Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 11 Apr 2016 08:02:18 +0200 Subject: [PATCH 0012/1335] check for php-curl installed when cron_letsencrypt runs; format source Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/ssl/class.lescript.php | 784 +++++++++++++++-------------- scripts/jobs/cron_letsencrypt.php | 74 +-- 2 files changed, 442 insertions(+), 416 deletions(-) diff --git a/lib/classes/ssl/class.lescript.php b/lib/classes/ssl/class.lescript.php index 32ba7d09..189d2333 100644 --- a/lib/classes/ssl/class.lescript.php +++ b/lib/classes/ssl/class.lescript.php @@ -4,14 +4,14 @@ // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED @@ -28,274 +28,285 @@ // and modified to work without files and integrate in Froxlor class lescript { - public $license = 'https://letsencrypt.org/documents/LE-SA-v1.0.1-July-27-2015.pdf'; - private $logger; - private $client; - private $accountKey; + public $license = 'https://letsencrypt.org/documents/LE-SA-v1.0.1-July-27-2015.pdf'; - public function __construct($logger) - { - $this->logger = $logger; - if (Settings::Get('system.letsencryptca') == 'production') { - $ca = 'https://acme-v01.api.letsencrypt.org'; - } else { - $ca = 'https://acme-staging.api.letsencrypt.org'; - } - $this->client = new Client($ca); - $this->log("Using '$ca' to generate certificate"); - } + private $logger; - public function initAccount($certrow) - { - // Let's see if we have the private accountkey - $this->accountKey = $certrow['leprivatekey']; - if (!$this->accountKey || $this->accountKey == 'unset' || Settings::Get('system.letsencryptca') != 'production') { + private $client; - // generate and save new private key for account - // --------------------------------------------- + private $accountKey; - $this->log('Starting new account registration'); - $keys = $this->generateKey(); - // Only store the accountkey in production, in staging always generate a new key - if (Settings::Get('system.letsencryptca') == 'production') { - $upd_stmt = Database::prepare(" - UPDATE `".TABLE_PANEL_CUSTOMERS."` SET `lepublickey` = :public, `leprivatekey` = :private WHERE `customerid` = :customerid; - "); - Database::pexecute($upd_stmt, array('public' => $keys['public'], 'private' => $keys['private'], 'customerid' => $certrow['customerid'])); - } - $this->accountKey = $keys['private']; - $this->postNewReg(); - $this->log('New account certificate registered'); - - } else { - - $this->log('Account already registered. Continuing.'); - - } - } - - public function signDomains(array $domains, $domainkey = null, $csr = null) - { - - if (!$this->accountKey) { - throw new \RuntimeException("Account not initiated"); - } - - $this->log('Starting certificate generation process for domains'); - - $privateAccountKey = openssl_pkey_get_private($this->accountKey); - $accountKeyDetails = openssl_pkey_get_details($privateAccountKey); - - // start domains authentication - // ---------------------------- - - foreach($domains as $domain) { - - // 1. getting available authentication options - // ------------------------------------------- - - $this->log("Requesting challenge for $domain"); - - $response = $this->signedRequest( - "/acme/new-authz", - array("resource" => "new-authz", "identifier" => array("type" => "dns", "value" => $domain)) - ); - - // if response is not an array but a string, it's most likely a server-error, e.g. - // ErrorAn error occurred while processing your request. - //

Reference #179.d8be1402.1458059103.3613c4db - if (!is_array($response)) { - throw new RuntimeException("Invalid response from LE for domain $domain. Whole response: ".$response); - } - - if (!array_key_exists('challenges', $response)) { - throw new RuntimeException("No challenges received for $domain. Whole response: ".json_encode($response)); - } - - // choose http-01 challenge only - $challenge = array_reduce($response['challenges'], function($v, $w) { return $v ? $v : ($w['type'] == 'http-01' ? $w : false); }); - if(!$challenge) throw new RuntimeException("HTTP Challenge for $domain is not available. Whole response: ".json_encode($response)); - - $this->log("Got challenge token for $domain"); - $location = $this->client->getLastLocation(); - - - // 2. saving authentication token for web verification - // --------------------------------------------------- - - $directory = Settings::Get('system.letsencryptchallengepath').'/.well-known/acme-challenge'; - $tokenPath = $directory.'/'.$challenge['token']; - - if(!file_exists($directory) && !@mkdir($directory, 0755, true)) { - throw new \RuntimeException("Couldn't create directory to expose challenge: ${tokenPath}"); - } - - $header = array( - // need to be in precise order! - "e" => Base64UrlSafeEncoder::encode($accountKeyDetails["rsa"]["e"]), - "kty" => "RSA", - "n" => Base64UrlSafeEncoder::encode($accountKeyDetails["rsa"]["n"]) - - ); - $payload = $challenge['token'] . '.' . Base64UrlSafeEncoder::encode(hash('sha256', json_encode($header), true)); - - file_put_contents($tokenPath, $payload); - chmod($tokenPath, 0644); - - // 3. verification process itself - // ------------------------------- - - $uri = "http://${domain}/.well-known/acme-challenge/${challenge['token']}"; - - $this->log("Token for $domain saved at $tokenPath and should be available at $uri"); - - // simple self check - if($payload !== trim(@file_get_contents($uri))) { - $errmsg = json_encode(error_get_last()); - if ($errmsg != "null") { - $errmsg = "; PHP error: " . $errmsg; + public function __construct($logger) + { + $this->logger = $logger; + if (Settings::Get('system.letsencryptca') == 'production') { + $ca = 'https://acme-v01.api.letsencrypt.org'; } else { - $errmsg = ""; + $ca = 'https://acme-staging.api.letsencrypt.org'; } - @unlink($tokenPath); - throw new \RuntimeException("Please check $uri - token not available" . $errmsg); - } + $this->client = new Client($ca); + $this->log("Using '$ca' to generate certificate"); + } - $this->log("Sending request to challenge"); + public function initAccount($certrow) + { + // Let's see if we have the private accountkey + $this->accountKey = $certrow['leprivatekey']; + if (! $this->accountKey || $this->accountKey == 'unset' || Settings::Get('system.letsencryptca') != 'production') { - // send request to challenge - $result = $this->signedRequest( - $challenge['uri'], - array( - "resource" => "challenge", - "type" => "http-01", - "keyAuthorization" => $payload, - "token" => $challenge['token'] - ) - ); + // generate and save new private key for account + // --------------------------------------------- - // waiting loop - // we wait for a maximum of 30 seconds to avoid endless loops - $count = 0; - do { - if(empty($result['status']) || $result['status'] == "invalid") { - @unlink($tokenPath); - throw new \RuntimeException("Verification ended with error: ".json_encode($result)); - } - $ended = !($result['status'] === "pending"); + $this->log('Starting new account registration'); + $keys = $this->generateKey(); + // Only store the accountkey in production, in staging always generate a new key + if (Settings::Get('system.letsencryptca') == 'production') { + $upd_stmt = Database::prepare(" + UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET `lepublickey` = :public, `leprivatekey` = :private WHERE `customerid` = :customerid; + "); + Database::pexecute($upd_stmt, array( + 'public' => $keys['public'], + 'private' => $keys['private'], + 'customerid' => $certrow['customerid'] + )); + } + $this->accountKey = $keys['private']; + $this->postNewReg(); + $this->log('New account certificate registered'); + } else { - if(!$ended) { - $this->log("Verification pending, sleeping 1s"); - sleep(1); - $count++; - } + $this->log('Account already registered. Continuing.'); + } + } - $result = $this->client->get($location); + public function signDomains(array $domains, $domainkey = null, $csr = null) + { + if (! $this->accountKey) { + throw new \RuntimeException("Account not initiated"); + } - } while (!$ended && $count < 30); + $this->log('Starting certificate generation process for domains'); - $this->log("Verification ended with status: ${result['status']}"); - @unlink($tokenPath); - } + $privateAccountKey = openssl_pkey_get_private($this->accountKey); + $accountKeyDetails = openssl_pkey_get_details($privateAccountKey); - // requesting certificate - // ---------------------- + // start domains authentication + // ---------------------------- - // generate private key for domain if not exist - if(empty($domainkey) || Settings::Get('system.letsencryptreuseold') == 0) { - $keys = $this->generateKey(); - $domainkey = $keys['private']; - } + foreach ($domains as $domain) { - // load domain key - $privateDomainKey = openssl_pkey_get_private($domainkey); + // 1. getting available authentication options + // ------------------------------------------- - $this->client->getLastLinks(); + $this->log("Requesting challenge for $domain"); - if (empty($csrfile) || Settings::Get('system.letsencryptreuseold') == 0) { - $csr = $this->generateCSR($privateDomainKey, $domains); - } + $response = $this->signedRequest("/acme/new-authz", array( + "resource" => "new-authz", + "identifier" => array( + "type" => "dns", + "value" => $domain + ) + )); - // request certificates creation - $result = $this->signedRequest( - "/acme/new-cert", - array('resource' => 'new-cert', 'csr' => $csr) - ); - if ($this->client->getLastCode() !== 201) { - throw new \RuntimeException("Invalid response code: ".$this->client->getLastCode().", ".json_encode($result)); - } - $location = $this->client->getLastLocation(); + // if response is not an array but a string, it's most likely a server-error, e.g. + // ErrorAn error occurred while processing your request. + //

Reference #179.d8be1402.1458059103.3613c4db + if (! is_array($response)) { + throw new RuntimeException("Invalid response from LE for domain $domain. Whole response: " . $response); + } - // waiting loop - $certificates = array(); - while(1) { - $this->client->getLastLinks(); + if (! array_key_exists('challenges', $response)) { + throw new RuntimeException("No challenges received for $domain. Whole response: " . json_encode($response)); + } - $result = $this->client->get($location); + // choose http-01 challenge only + $challenge = array_reduce($response['challenges'], function ($v, $w) { + return $v ? $v : ($w['type'] == 'http-01' ? $w : false); + }); + if (! $challenge) + throw new RuntimeException("HTTP Challenge for $domain is not available. Whole response: " . json_encode($response)); - if($this->client->getLastCode() == 202) { + $this->log("Got challenge token for $domain"); + $location = $this->client->getLastLocation(); - $this->log("Certificate generation pending, sleeping 1s"); - sleep(1); + // 2. saving authentication token for web verification + // --------------------------------------------------- - } else if ($this->client->getLastCode() == 200) { + $directory = Settings::Get('system.letsencryptchallengepath') . '/.well-known/acme-challenge'; + $tokenPath = $directory . '/' . $challenge['token']; - $this->log("Got certificate! YAY!"); - $certificates[] = $this->parsePemFromBody($result); + if (! file_exists($directory) && ! @mkdir($directory, 0755, true)) { + throw new \RuntimeException("Couldn't create directory to expose challenge: ${tokenPath}"); + } + $header = array( + // need to be in precise order! + "e" => Base64UrlSafeEncoder::encode($accountKeyDetails["rsa"]["e"]), + "kty" => "RSA", + "n" => Base64UrlSafeEncoder::encode($accountKeyDetails["rsa"]["n"]) + ) + ; + $payload = $challenge['token'] . '.' . Base64UrlSafeEncoder::encode(hash('sha256', json_encode($header), true)); - foreach($this->client->getLastLinks() as $link) { - $this->log("Requesting chained cert at $link"); - $result = $this->client->get($link); - $certificates[] = $this->parsePemFromBody($result); - } + file_put_contents($tokenPath, $payload); + chmod($tokenPath, 0644); - break; - } else { + // 3. verification process itself + // ------------------------------- - throw new \RuntimeException("Can't get certificate: HTTP code ".$this->client->getLastCode()); + $uri = "http://${domain}/.well-known/acme-challenge/${challenge['token']}"; - } - } + $this->log("Token for $domain saved at $tokenPath and should be available at $uri"); - if(empty($certificates)) throw new \RuntimeException('No certificates generated'); + // simple self check + if ($payload !== trim(@file_get_contents($uri))) { + $errmsg = json_encode(error_get_last()); + if ($errmsg != "null") { + $errmsg = "; PHP error: " . $errmsg; + } else { + $errmsg = ""; + } + @unlink($tokenPath); + throw new \RuntimeException("Please check $uri - token not available" . $errmsg); + } - $fullchain = implode("\n", $certificates); - $crt = array_shift($certificates); - $chain = implode("\n", $certificates); + $this->log("Sending request to challenge"); - $this->log("Done, returning new certificates and key"); - return array('fullchain' => $fullchain, 'crt' => $crt, 'chain' => $chain, 'key' => $domainkey, 'csr' => $csr); - } + // send request to challenge + $result = $this->signedRequest($challenge['uri'], array( + "resource" => "challenge", + "type" => "http-01", + "keyAuthorization" => $payload, + "token" => $challenge['token'] + )); - private function parsePemFromBody($body) - { - $pem = chunk_split(base64_encode($body), 64, "\n"); - return "-----BEGIN CERTIFICATE-----\n" . $pem . "-----END CERTIFICATE-----\n"; - } + // waiting loop + // we wait for a maximum of 30 seconds to avoid endless loops + $count = 0; + do { + if (empty($result['status']) || $result['status'] == "invalid") { + @unlink($tokenPath); + throw new \RuntimeException("Verification ended with error: " . json_encode($result)); + } + $ended = ! ($result['status'] === "pending"); - private function postNewReg() - { - $this->log('Sending registration to letsencrypt server'); + if (! $ended) { + $this->log("Verification pending, sleeping 1s"); + sleep(1); + $count ++; + } - return $this->signedRequest( - '/acme/new-reg', - array('resource' => 'new-reg', 'agreement' => $this->license) - ); - } + $result = $this->client->get($location); + } while (! $ended && $count < 30); - private function generateCSR($privateKey, array $domains) - { - $domain = reset($domains); - $san = implode(",", array_map(function ($dns) { return "DNS:" . $dns; }, $domains)); - $tmpConf = tmpfile(); - $tmpConfMeta = stream_get_meta_data($tmpConf); - $tmpConfPath = $tmpConfMeta["uri"]; + $this->log("Verification ended with status: ${result['status']}"); + @unlink($tokenPath); + } - // workaround to get SAN working - fwrite($tmpConf, -'HOME = . + // requesting certificate + // ---------------------- + + // generate private key for domain if not exist + if (empty($domainkey) || Settings::Get('system.letsencryptreuseold') == 0) { + $keys = $this->generateKey(); + $domainkey = $keys['private']; + } + + // load domain key + $privateDomainKey = openssl_pkey_get_private($domainkey); + + $this->client->getLastLinks(); + + if (empty($csrfile) || Settings::Get('system.letsencryptreuseold') == 0) { + $csr = $this->generateCSR($privateDomainKey, $domains); + } + + // request certificates creation + $result = $this->signedRequest("/acme/new-cert", array( + 'resource' => 'new-cert', + 'csr' => $csr + )); + if ($this->client->getLastCode() !== 201) { + throw new \RuntimeException("Invalid response code: " . $this->client->getLastCode() . ", " . json_encode($result)); + } + $location = $this->client->getLastLocation(); + + // waiting loop + $certificates = array(); + while (1) { + $this->client->getLastLinks(); + + $result = $this->client->get($location); + + if ($this->client->getLastCode() == 202) { + + $this->log("Certificate generation pending, sleeping 1s"); + sleep(1); + } else + if ($this->client->getLastCode() == 200) { + + $this->log("Got certificate! YAY!"); + $certificates[] = $this->parsePemFromBody($result); + + foreach ($this->client->getLastLinks() as $link) { + $this->log("Requesting chained cert at $link"); + $result = $this->client->get($link); + $certificates[] = $this->parsePemFromBody($result); + } + + break; + } else { + + throw new \RuntimeException("Can't get certificate: HTTP code " . $this->client->getLastCode()); + } + } + + if (empty($certificates)) + throw new \RuntimeException('No certificates generated'); + + $fullchain = implode("\n", $certificates); + $crt = array_shift($certificates); + $chain = implode("\n", $certificates); + + $this->log("Done, returning new certificates and key"); + return array( + 'fullchain' => $fullchain, + 'crt' => $crt, + 'chain' => $chain, + 'key' => $domainkey, + 'csr' => $csr + ); + } + + private function parsePemFromBody($body) + { + $pem = chunk_split(base64_encode($body), 64, "\n"); + return "-----BEGIN CERTIFICATE-----\n" . $pem . "-----END CERTIFICATE-----\n"; + } + + private function postNewReg() + { + $this->log('Sending registration to letsencrypt server'); + + return $this->signedRequest('/acme/new-reg', array( + 'resource' => 'new-reg', + 'agreement' => $this->license + )); + } + + private function generateCSR($privateKey, array $domains) + { + $domain = reset($domains); + $san = implode(",", array_map(function ($dns) { + return "DNS:" . $dns; + }, $domains)); + $tmpConf = tmpfile(); + $tmpConfMeta = stream_get_meta_data($tmpConf); + $tmpConfPath = $tmpConfMeta["uri"]; + + // workaround to get SAN working + fwrite($tmpConf, 'HOME = . RANDFILE = $ENV::HOME/.rnd [ req ] default_bits = ' . Settings::Get('system.letsencryptkeysize') . ' @@ -306,197 +317,202 @@ req_extensions = v3_req countryName = Country Name (2 letter code) [ v3_req ] basicConstraints = CA:FALSE -subjectAltName = '.$san.' +subjectAltName = ' . $san . ' keyUsage = nonRepudiation, digitalSignature, keyEncipherment'); - $csr = openssl_csr_new( - array( - "CN" => $domain, - "ST" => Settings::Get('system.letsencryptstate'), - "C" => Settings::Get('system.letsencryptcountrycode'), - "O" => "Unknown", - ), - $privateKey, - array( - "config" => $tmpConfPath, - "digest_alg" => "sha256" - ) - ); + $csr = openssl_csr_new(array( + "CN" => $domain, + "ST" => Settings::Get('system.letsencryptstate'), + "C" => Settings::Get('system.letsencryptcountrycode'), + "O" => "Unknown" + ), $privateKey, array( + "config" => $tmpConfPath, + "digest_alg" => "sha256" + )); - if (!$csr) throw new \RuntimeException("CSR couldn't be generated! ".openssl_error_string()); + if (! $csr) + throw new \RuntimeException("CSR couldn't be generated! " . openssl_error_string()); - openssl_csr_export($csr, $csr); - fclose($tmpConf); + openssl_csr_export($csr, $csr); + fclose($tmpConf); - preg_match('~REQUEST-----(.*)-----END~s', $csr, $matches); + preg_match('~REQUEST-----(.*)-----END~s', $csr, $matches); - return trim(Base64UrlSafeEncoder::encode(base64_decode($matches[1]))); - } + return trim(Base64UrlSafeEncoder::encode(base64_decode($matches[1]))); + } - private function generateKey() - { - $res = openssl_pkey_new(array( - "private_key_type" => OPENSSL_KEYTYPE_RSA, - "private_key_bits" => (int)Settings::Get('system.letsencryptkeysize'), - )); + private function generateKey() + { + $res = openssl_pkey_new(array( + "private_key_type" => OPENSSL_KEYTYPE_RSA, + "private_key_bits" => (int) Settings::Get('system.letsencryptkeysize') + )); - if(!openssl_pkey_export($res, $privateKey)) { - throw new \RuntimeException("Key export failed!"); - } + if (! openssl_pkey_export($res, $privateKey)) { + throw new \RuntimeException("Key export failed!"); + } - $details = openssl_pkey_get_details($res); + $details = openssl_pkey_get_details($res); - return array('private' => $privateKey, 'public' => $details['key']); - } + return array( + 'private' => $privateKey, + 'public' => $details['key'] + ); + } - private function signedRequest($uri, array $payload) - { - $privateKey = openssl_pkey_get_private($this->accountKey); - $details = openssl_pkey_get_details($privateKey); + private function signedRequest($uri, array $payload) + { + $privateKey = openssl_pkey_get_private($this->accountKey); + $details = openssl_pkey_get_details($privateKey); - $header = array( - "alg" => "RS256", - "jwk" => array( - "kty" => "RSA", - "n" => Base64UrlSafeEncoder::encode($details["rsa"]["n"]), - "e" => Base64UrlSafeEncoder::encode($details["rsa"]["e"]), - ) - ); + $header = array( + "alg" => "RS256", + "jwk" => array( + "kty" => "RSA", + "n" => Base64UrlSafeEncoder::encode($details["rsa"]["n"]), + "e" => Base64UrlSafeEncoder::encode($details["rsa"]["e"]) + ) + ); - $protected = $header; - $protected["nonce"] = $this->client->getLastNonce(); + $protected = $header; + $protected["nonce"] = $this->client->getLastNonce(); + $payload64 = Base64UrlSafeEncoder::encode(str_replace('\\/', '/', json_encode($payload))); + $protected64 = Base64UrlSafeEncoder::encode(json_encode($protected)); - $payload64 = Base64UrlSafeEncoder::encode(str_replace('\\/', '/', json_encode($payload))); - $protected64 = Base64UrlSafeEncoder::encode(json_encode($protected)); + openssl_sign($protected64 . '.' . $payload64, $signed, $privateKey, "SHA256"); - openssl_sign($protected64.'.'.$payload64, $signed, $privateKey, "SHA256"); + $signed64 = Base64UrlSafeEncoder::encode($signed); - $signed64 = Base64UrlSafeEncoder::encode($signed); + $data = array( + 'header' => $header, + 'protected' => $protected64, + 'payload' => $payload64, + 'signature' => $signed64 + ); - $data = array( - 'header' => $header, - 'protected' => $protected64, - 'payload' => $payload64, - 'signature' => $signed64 - ); + $this->log("Sending signed request to $uri"); - $this->log("Sending signed request to $uri"); + return $this->client->post($uri, json_encode($data)); + } - return $this->client->post($uri, json_encode($data)); - } - - protected function log($message) - { - $this->logger->logAction(CRON_ACTION, LOG_INFO, "letsencrypt " . $message); - } + protected function log($message) + { + $this->logger->logAction(CRON_ACTION, LOG_INFO, "letsencrypt " . $message); + } } class Client { - private $lastCode; - private $lastHeader; - private $base; + private $lastCode; - public function __construct($base) - { - $this->base = $base; - } + private $lastHeader; - private function curl($method, $url, $data = null) - { - $headers = array('Accept: application/json', 'Content-Type: application/json'); - $handle = curl_init(); - curl_setopt($handle, CURLOPT_URL, preg_match('~^http~', $url) ? $url : $this->base.$url); - curl_setopt($handle, CURLOPT_HTTPHEADER, $headers); - curl_setopt($handle, CURLOPT_RETURNTRANSFER, true); - curl_setopt($handle, CURLOPT_HEADER, true); + private $base; - // DO NOT DO THAT! - // curl_setopt($handle, CURLOPT_SSL_VERIFYHOST, false); - // curl_setopt($handle, CURLOPT_SSL_VERIFYPEER, false); + public function __construct($base) + { + $this->base = $base; + } - switch ($method) { - case 'GET': - break; - case 'POST': - curl_setopt($handle, CURLOPT_POST, true); - curl_setopt($handle, CURLOPT_POSTFIELDS, $data); - break; - } - $response = curl_exec($handle); + private function curl($method, $url, $data = null) + { + $headers = array( + 'Accept: application/json', + 'Content-Type: application/json' + ); + $handle = curl_init(); + curl_setopt($handle, CURLOPT_URL, preg_match('~^http~', $url) ? $url : $this->base . $url); + curl_setopt($handle, CURLOPT_HTTPHEADER, $headers); + curl_setopt($handle, CURLOPT_RETURNTRANSFER, true); + curl_setopt($handle, CURLOPT_HEADER, true); - if(curl_errno($handle)) { - throw new \RuntimeException('Curl: '.curl_error($handle)); - } + // DO NOT DO THAT! + // curl_setopt($handle, CURLOPT_SSL_VERIFYHOST, false); + // curl_setopt($handle, CURLOPT_SSL_VERIFYPEER, false); - $header_size = curl_getinfo($handle, CURLINFO_HEADER_SIZE); + switch ($method) { + case 'GET': + break; + case 'POST': + curl_setopt($handle, CURLOPT_POST, true); + curl_setopt($handle, CURLOPT_POSTFIELDS, $data); + break; + } + $response = curl_exec($handle); - $header = substr($response, 0, $header_size); - $body = substr($response, $header_size); + if (curl_errno($handle)) { + throw new \RuntimeException('Curl: ' . curl_error($handle)); + } - $this->lastHeader = $header; - $this->lastCode = curl_getinfo($handle, CURLINFO_HTTP_CODE); + $header_size = curl_getinfo($handle, CURLINFO_HEADER_SIZE); - $data = json_decode($body, true); - return $data === null ? $body : $data; - } + $header = substr($response, 0, $header_size); + $body = substr($response, $header_size); - public function post($url, $data) - { - return $this->curl('POST', $url, $data); - } + $this->lastHeader = $header; + $this->lastCode = curl_getinfo($handle, CURLINFO_HTTP_CODE); - public function get($url) - { - return $this->curl('GET', $url); - } + $data = json_decode($body, true); + return $data === null ? $body : $data; + } - public function getLastNonce() - { - if(preg_match('~Replay\-Nonce: (.+)~i', $this->lastHeader, $matches)) { - return trim($matches[1]); - } + public function post($url, $data) + { + return $this->curl('POST', $url, $data); + } - $this->curl('GET', '/directory'); - return $this->getLastNonce(); - } + public function get($url) + { + return $this->curl('GET', $url); + } - public function getLastLocation() - { - if(preg_match('~Location: (.+)~i', $this->lastHeader, $matches)) { - return trim($matches[1]); - } - return null; - } + public function getLastNonce() + { + if (preg_match('~Replay\-Nonce: (.+)~i', $this->lastHeader, $matches)) { + return trim($matches[1]); + } - public function getLastCode() - { - return $this->lastCode; - } + $this->curl('GET', '/directory'); + return $this->getLastNonce(); + } - public function getLastLinks() - { - preg_match_all('~Link: <(.+)>;rel="up"~', $this->lastHeader, $matches); - return $matches[1]; - } + public function getLastLocation() + { + if (preg_match('~Location: (.+)~i', $this->lastHeader, $matches)) { + return trim($matches[1]); + } + return null; + } + + public function getLastCode() + { + return $this->lastCode; + } + + public function getLastLinks() + { + preg_match_all('~Link: <(.+)>;rel="up"~', $this->lastHeader, $matches); + return $matches[1]; + } } class Base64UrlSafeEncoder { - public static function encode($input) - { - return str_replace('=', '', strtr(base64_encode($input), '+/', '-_')); - } - public static function decode($input) - { - $remainder = strlen($input) % 4; - if ($remainder) { - $padlen = 4 - $remainder; - $input .= str_repeat('=', $padlen); - } - return base64_decode(strtr($input, '-_', '+/')); - } + public static function encode($input) + { + return str_replace('=', '', strtr(base64_encode($input), '+/', '-_')); + } + + public static function decode($input) + { + $remainder = strlen($input) % 4; + if ($remainder) { + $padlen = 4 - $remainder; + $input .= str_repeat('=', $padlen); + } + return base64_decode(strtr($input, '-_', '+/')); + } } diff --git a/scripts/jobs/cron_letsencrypt.php b/scripts/jobs/cron_letsencrypt.php index 720ae706..1848480f 100644 --- a/scripts/jobs/cron_letsencrypt.php +++ b/scripts/jobs/cron_letsencrypt.php @@ -1,4 +1,7 @@ - - * @author Froxlor team (2016-) - * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt - * @package Cron + * @copyright (c) the authors + * @author Florian Aders + * @author Froxlor team (2016-) + * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt + * @package Cron * - * @since 0.9.35 + * @since 0.9.35 * */ $cronlog->logAction(CRON_ACTION, LOG_INFO, "Updating Let's Encrypt certificates"); +if (! extension_loaded('curl')) { + $cronlog->logAction(CRON_ACTION, LOG_ERR, "Let's Encrypt requires the php cURL extension to be installed."); + exit; +} + $certificates_stmt = Database::query(" SELECT domssl.`id`, domssl.`domainid`, domssl.expirationdate, domssl.`ssl_cert_file`, domssl.`ssl_key_file`, domssl.`ssl_ca_file`, domssl.`ssl_csr_file`, dom.`domain`, dom.`iswildcarddomain`, dom.`wwwserveralias`, dom.`documentroot`, dom.`id` as 'domainid', dom.`ssl_redirect`, cust.`leprivatekey`, cust.`lepublickey`, cust.customerid, cust.loginname - FROM `".TABLE_PANEL_CUSTOMERS."` as cust, `".TABLE_PANEL_DOMAINS."` dom LEFT JOIN `".TABLE_PANEL_DOMAIN_SSL_SETTINGS."` domssl ON (dom.id = domssl.domainid) + FROM `" . TABLE_PANEL_CUSTOMERS . "` as cust, `" . TABLE_PANEL_DOMAINS . "` dom LEFT JOIN `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` domssl ON (dom.id = domssl.domainid) WHERE dom.customerid = cust.customerid AND dom.letsencrypt = 1 AND (domssl.expirationdate < DATE_ADD(NOW(), INTERVAL 30 DAY) OR domssl.expirationdate IS NULL) "); $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, expirationdate = :expirationdate + 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, expirationdate = :expirationdate "); $upddom_stmt = Database::prepare(" - UPDATE `".TABLE_PANEL_DOMAINS."` SET `ssl_redirect` = '1' WHERE `id` = :domainid + UPDATE `" . TABLE_PANEL_DOMAINS . "` SET `ssl_redirect` = '1' WHERE `id` = :domainid "); $changedetected = 0; $certrows = $certificates_stmt->fetchAll(PDO::FETCH_ASSOC); -foreach($certrows AS $certrow) { +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'])); + // set logger to corresponding loginname for the log to appear in the users system-log + $cronlog = FroxlorLogger::getInstanceOf(array( + 'loginname' => $certrow['loginname'] + )); // Only renew let's encrypt certificate if no broken ssl_redirect is enabled - if ($certrow['ssl_redirect'] != 2) - { + if ($certrow['ssl_redirect'] != 2) { $cronlog->logAction(CRON_ACTION, LOG_DEBUG, "Updating " . $certrow['domain']); if ($certrow['ssl_cert_file']) { @@ -55,12 +64,14 @@ foreach($certrows AS $certrow) { // We are interessted in the old SAN - data $san = explode(', ', $x509data['extensions']['subjectAltName']); $domains = array(); - foreach($san as $dnsname) { + foreach ($san as $dnsname) { $domains[] = substr($dnsname, 4); } } else { $cronlog->logAction(CRON_ACTION, LOG_DEBUG, "letsencrypt generating new key / SAN for " . $certrow['domain']); - $domains = array($certrow['domain']); + $domains = array( + $certrow['domain'] + ); // Add www. for SAN if ($certrow['wwwserveralias'] == 1) { $domains[] = 'www.' . $certrow['domain']; @@ -82,28 +93,25 @@ foreach($certrows AS $certrow) { // Store the new data Database::pexecute($updcert_stmt, array( - 'id' => $certrow['id'], - 'domainid' => $certrow['domainid'], - 'crt' => $return['crt'], - 'key' => $return['key'], - 'ca' => $return['chain'], - 'chain' => $return['chain'], - 'csr' => $return['csr'], - 'expirationdate' => date('Y-m-d H:i:s', $newcert['validTo_time_t']) - ) - ); + 'id' => $certrow['id'], + 'domainid' => $certrow['domainid'], + 'crt' => $return['crt'], + 'key' => $return['key'], + 'ca' => $return['chain'], + 'chain' => $return['chain'], + 'csr' => $return['csr'], + 'expirationdate' => date('Y-m-d H:i:s', $newcert['validTo_time_t']) + )); if ($certrow['ssl_redirect'] == 3) { Database::pexecute($upddom_stmt, array( - 'domainid' => $certrow['domainid'] - ) - ); + 'domainid' => $certrow['domainid'] + )); } $cronlog->logAction(CRON_ACTION, LOG_INFO, "Updated Let's Encrypt certificate for " . $certrow['domain']); $changedetected = 1; - } catch (Exception $e) { $cronlog->logAction(CRON_ACTION, LOG_ERR, "Could not get Let's Encrypt certificate for " . $certrow['domain'] . ": " . $e->getMessage()); } @@ -119,5 +127,7 @@ if ($changedetected) { } // reset logger -$cronlog = FroxlorLogger::getInstanceOf(array('loginname' => 'cronjob')); +$cronlog = FroxlorLogger::getInstanceOf(array( + 'loginname' => 'cronjob' +)); $cronlog->logAction(CRON_ACTION, LOG_INFO, "Let's Encrypt certificates have been updated"); From a014b5cc2b8fcc8af8d628ecfef5376ba6ab7c04 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 11 Apr 2016 09:19:43 +0200 Subject: [PATCH 0013/1335] minor preparations for system-hostname as 'normal' domain (to use ssl/LE/etc.) Signed-off-by: Michael Kaufmann (d00p) --- admin_domains.php | 995 +++++++++--------- .../Sparkle/admin/domains/domains_domain.tpl | 21 +- templates/Sparkle/assets/css/main.css | 20 +- 3 files changed, 547 insertions(+), 489 deletions(-) diff --git a/admin_domains.php b/admin_domains.php index 1ff3ea25..479e1bc5 100644 --- a/admin_domains.php +++ b/admin_domains.php @@ -16,29 +16,25 @@ * @package Panel * */ - define('AREA', 'admin'); require './lib/init.php'; if (isset($_POST['id'])) { $id = intval($_POST['id']); -} elseif(isset($_GET['id'])) { +} elseif (isset($_GET['id'])) { $id = intval($_GET['id']); } -if ($page == 'domains' - || $page == 'overview' -) { +if ($page == 'domains' || $page == 'overview') { // Let's see how many customers we have $stmt = Database::prepare(" - SELECT COUNT(`customerid`) as `countcustomers` FROM `" . TABLE_PANEL_CUSTOMERS . "` " . ($userinfo['customers_see_all'] ? '' : " WHERE `adminid` = :adminid") - ); + SELECT COUNT(`customerid`) as `countcustomers` FROM `" . TABLE_PANEL_CUSTOMERS . "` " . ($userinfo['customers_see_all'] ? '' : " WHERE `adminid` = :adminid")); $params = array(); if ($userinfo['customers_see_all'] == '0') { $params['adminid'] = $userinfo['adminid']; } $countcustomers = Database::pexecute_first($stmt, $params); - $countcustomers = (int)$countcustomers['countcustomers']; + $countcustomers = (int) $countcustomers['countcustomers']; if ($action == '') { @@ -52,16 +48,18 @@ if ($page == 'domains' 'd.aliasdomain' => $lng['domains']['aliasdomain'] ); $paging = new paging($userinfo, TABLE_PANEL_DOMAINS, $fields); - $domains = ''; + $domains = ""; + $syshostname = ""; + if (Settings::Get('system.hostname_id')) + { + $syshostname = "AND `d`.`id` <> " . Settings::Get('system.hostname_id'); + } $result_stmt = Database::prepare(" SELECT `d`.*, `c`.`loginname`, `c`.`name`, `c`.`firstname`, `c`.`company`, `c`.`standardsubdomain`, `ad`.`id` AS `aliasdomainid`, `ad`.`domain` AS `aliasdomain` FROM `" . TABLE_PANEL_DOMAINS . "` `d` LEFT JOIN `" . TABLE_PANEL_CUSTOMERS . "` `c` USING(`customerid`) LEFT JOIN `" . TABLE_PANEL_DOMAINS . "` `ad` ON `d`.`aliasdomain`=`ad`.`id` - WHERE `d`.`parentdomainid`='0' " . - ($userinfo['customers_see_all'] ? '' : " AND `d`.`adminid` = :adminid ") . - " " . $paging->getSqlWhere(true) . " " . $paging->getSqlOrderBy() . " " . $paging->getSqlLimit() - ); + WHERE `d`.`parentdomainid`='0' " . $syshostname . ($userinfo['customers_see_all'] ? '' : " AND `d`.`adminid` = :adminid ") . " " . $paging->getSqlWhere(true) . " " . $paging->getSqlOrderBy() . " " . $paging->getSqlLimit()); $params = array(); if ($userinfo['customers_see_all'] == '0') { $params['adminid'] = $userinfo['adminid']; @@ -77,54 +75,16 @@ if ($page == 'domains' while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) { - $row['domain'] = $idna_convert->decode($row['domain']); - $row['aliasdomain'] = $idna_convert->decode($row['aliasdomain']); + formatDomainEntry($row, $idna_convert); - $resultips_stmt = Database::prepare(" - SELECT `ips`.* FROM `".TABLE_DOMAINTOIP . "` AS `dti`, `".TABLE_PANEL_IPSANDPORTS."` AS `ips` - WHERE `dti`.`id_ipandports` = `ips`.`id` AND `dti`.`id_domain` = :domainid" - ); - Database::pexecute($resultips_stmt, array('domainid' => $row['id'])); - - $row['ipandport'] = ''; - while ($rowip = $resultips_stmt->fetch(PDO::FETCH_ASSOC)) { - - if (filter_var($rowip['ip'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { - $row['ipandport'] .= '[' . $rowip['ip'] . ']:' . $rowip['port'] . "\n"; - } else { - $row['ipandport'] .= $rowip['ip'] . ':' . $rowip['port'] . "\n"; - } - } - $row['ipandport'] = substr($row['ipandport'], 0, -1); - $row['termination_date'] = str_replace("0000-00-00", "", $row['termination_date']); - - if($row['termination_date'] != "") - { - $cdate = strtotime($row['termination_date'] . " 23:59:59"); - $today = time(); - - if($cdate < $today) - { - $row['termination_css'] = 'domain-expired'; - } - else - { - $row['termination_css'] = 'domain-canceled'; - } - } - - if (!isset($domain_array[$row['domain']])) { + if (! isset($domain_array[$row['domain']])) { $domain_array[$row['domain']] = $row; } else { $domain_array[$row['domain']] = array_merge($row, $domain_array[$row['domain']]); } - if (isset($row['aliasdomainid']) - && $row['aliasdomainid'] != null - && isset($row['aliasdomain']) - && $row['aliasdomain'] != '' - ) { - if (!isset($domain_array[$row['aliasdomain']])) { + if (isset($row['aliasdomainid']) && $row['aliasdomainid'] != null && isset($row['aliasdomain']) && $row['aliasdomain'] != '') { + if (! isset($domain_array[$row['aliasdomain']])) { $domain_array[$row['aliasdomain']] = array(); } $domain_array[$row['aliasdomain']]['domainaliasid'] = $row['id']; @@ -135,49 +95,54 @@ if ($page == 'domains' /** * We need ksort/krsort here to make sure idna-domains are also sorted correctly */ - if ($paging->sortfield == 'd.domain' - && $paging->sortorder == 'asc' - ) { + if ($paging->sortfield == 'd.domain' && $paging->sortorder == 'asc') { ksort($domain_array); - } elseif ($paging->sortfield == 'd.domain' - && $paging->sortorder == 'desc' - ) { + } elseif ($paging->sortfield == 'd.domain' && $paging->sortorder == 'desc') { krsort($domain_array); } + // show froxlor hostname as first entry + if (Settings::Get('system.hostname_id')) + { + $syshost_stmt = Database::prepare("SELECT * FROM `" . TABLE_PANEL_DOMAINS . "` WHERE `id` = :did"); + $row = Database::pexecute_first($syshost_stmt, array( + 'did' => Settings::Get('system.hostname_id') + )); + formatDomainEntry($row, $idna_convert); + $row['customername'] = 'Froxlor hostname'; + $row['loginname'] = null; + $row['termination_css'] = 'domain-hostname'; + $row['ipandport'] = str_replace("\n", "
", $row['ipandport']); + eval("\$domains.=\"" . getTemplate("domains/domains_domain") . "\";"); + } + $i = 0; $count = 0; foreach ($domain_array as $row) { - if (isset($row['domain']) - && $row['domain'] != '' - && $paging->checkDisplay($i) - ) { + if (isset($row['domain']) && $row['domain'] != '' && $paging->checkDisplay($i)) { $row['customername'] = getCorrectFullUserDetails($row); $row = htmlentities_array($row); // display a nice list of IP's $row['ipandport'] = str_replace("\n", "
", $row['ipandport']); eval("\$domains.=\"" . getTemplate("domains/domains_domain") . "\";"); - $count++; + $count ++; } - $i++; + $i ++; } $domainscount = $numrows_domains; // Display the list eval("echo \"" . getTemplate("domains/domains") . "\";"); - - } elseif($action == 'delete' - && $id != 0 - ) { + } elseif ($action == 'delete' && $id != 0) { $result_stmt = Database::prepare(" SELECT `d`.* FROM `" . TABLE_PANEL_DOMAINS . "` `d`, `" . TABLE_PANEL_CUSTOMERS . "` `c` - WHERE `d`.`id` = :id AND `d`.`id` <> `c`.`standardsubdomain`" . - ($userinfo['customers_see_all'] ? '' : " AND `d`.`adminid` = :adminid") + WHERE `d`.`id` = :id AND `d`.`id` <> `c`.`standardsubdomain`" . ($userinfo['customers_see_all'] ? '' : " AND `d`.`adminid` = :adminid")); + $params = array( + 'id' => $id ); - $params = array('id' => $id); if ($userinfo['customers_see_all'] == '0') { $params['adminid'] = $userinfo['adminid']; } @@ -185,16 +150,13 @@ if ($page == 'domains' $alias_check_stmt = Database::prepare(" SELECT COUNT(`id`) AS `count` FROM `" . TABLE_PANEL_DOMAINS . "` - WHERE `aliasdomain`= :id" - ); - $alias_check = Database::pexecute_first($alias_check_stmt, array('id' => $id)); + WHERE `aliasdomain`= :id"); + $alias_check = Database::pexecute_first($alias_check_stmt, array( + 'id' => $id + )); - if ($result['domain'] != '' - && $alias_check['count'] == 0 - ) { - if (isset($_POST['send']) - && $_POST['send'] == 'send' - ) { + if ($result['domain'] != '' && $alias_check['count'] == 0) { + if (isset($_POST['send']) && $_POST['send'] == 'send') { // check for deletion of main-domains which are logically subdomains, #329 $rsd_sql = ''; $remove_subbutmain_domains = isset($_POST['delete_userfiles']) ? 1 : 0; @@ -204,76 +166,84 @@ if ($page == 'domains' $subresult_stmt = Database::prepare(" SELECT `id` FROM `" . TABLE_PANEL_DOMAINS . "` - WHERE (`id` = :id OR `parentdomainid` = :id ".$rsd_sql.") AND `isemaildomain` = '1'" - ); - Database::pexecute($subresult_stmt, array('id' => $id)); + WHERE (`id` = :id OR `parentdomainid` = :id " . $rsd_sql . ") AND `isemaildomain` = '1'"); + Database::pexecute($subresult_stmt, array( + 'id' => $id + )); $idString = array(); $paramString = array(); while ($subRow = $subresult_stmt->fetch(PDO::FETCH_ASSOC)) { - $idString[] = "`domainid` = :domain_" . (int)$subRow['id']; - $paramString['domain_'.$subRow['id']] = $subRow['id']; + $idString[] = "`domainid` = :domain_" . (int) $subRow['id']; + $paramString['domain_' . $subRow['id']] = $subRow['id']; } $idString = implode(' OR ', $idString); if ($idString != '') { $del_stmt = Database::prepare(" - DELETE FROM `" . TABLE_MAIL_USERS . "` WHERE " . $idString - ); + DELETE FROM `" . TABLE_MAIL_USERS . "` WHERE " . $idString); Database::pexecute($del_stmt, $paramString); $del_stmt = Database::prepare(" - DELETE FROM `" . TABLE_MAIL_VIRTUAL . "` WHERE " . $idString - ); + DELETE FROM `" . TABLE_MAIL_VIRTUAL . "` WHERE " . $idString); Database::pexecute($del_stmt, $paramString); $log->logAction(ADM_ACTION, LOG_NOTICE, "deleted domain/s from mail-tables"); } $del_stmt = Database::prepare(" DELETE FROM `" . TABLE_PANEL_DOMAINS . "` - WHERE `id` = :id OR `parentdomainid` = :id ".$rsd_sql - ); - Database::pexecute($del_stmt, array('id' => $id)); + WHERE `id` = :id OR `parentdomainid` = :id " . $rsd_sql); + Database::pexecute($del_stmt, array( + 'id' => $id + )); $deleted_domains = $del_stmt->rowCount(); $upd_stmt = Database::prepare(" UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET `subdomains_used` = `subdomains_used` - :domaincount - WHERE `customerid` = :customerid" - ); - Database::pexecute($upd_stmt, array('domaincount' => ($deleted_domains -1), 'customerid' => $result['customerid'])); + WHERE `customerid` = :customerid"); + Database::pexecute($upd_stmt, array( + 'domaincount' => ($deleted_domains - 1), + 'customerid' => $result['customerid'] + )); $upd_stmt = Database::prepare(" UPDATE `" . TABLE_PANEL_ADMINS . "` SET `domains_used` = `domains_used` - 1 - WHERE `adminid` = :adminid" - ); - Database::pexecute($upd_stmt, array('adminid' => $userinfo['adminid'])); + WHERE `adminid` = :adminid"); + Database::pexecute($upd_stmt, array( + 'adminid' => $userinfo['adminid'] + )); $upd_stmt = Database::prepare(" UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET `standardsubdomain` = '0' - WHERE `standardsubdomain` = :id AND `customerid` = :customerid" - ); - Database::pexecute($upd_stmt, array('id' => $result['id'], 'customerid' => $result['customerid'])); + WHERE `standardsubdomain` = :id AND `customerid` = :customerid"); + Database::pexecute($upd_stmt, array( + 'id' => $result['id'], + 'customerid' => $result['customerid'] + )); $del_stmt = Database::prepare(" DELETE FROM `" . TABLE_DOMAINTOIP . "` - WHERE `id_domain` = :domainid" - ); - Database::pexecute($del_stmt, array('domainid' => $id)); + WHERE `id_domain` = :domainid"); + Database::pexecute($del_stmt, array( + 'domainid' => $id + )); $del_stmt = Database::prepare(" DELETE FROM `" . TABLE_PANEL_DOMAINREDIRECTS . "` - WHERE `did` = :domainid" - ); - Database::pexecute($del_stmt, array('domainid' => $id)); + WHERE `did` = :domainid"); + Database::pexecute($del_stmt, array( + 'domainid' => $id + )); // remove certificate from domain_ssl_settings, fixes #1596 $del_stmt = Database::prepare(" DELETE FROM `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` - WHERE `domainid` = :domainid" - ); - Database::pexecute($del_stmt, array('domainid' => $id)); + WHERE `domainid` = :domainid"); + Database::pexecute($del_stmt, array( + 'domainid' => $id + )); $log->logAction(ADM_ACTION, LOG_INFO, "deleted domain/subdomains (#" . $result['id'] . ")"); updateCounters(); @@ -282,35 +252,38 @@ if ($page == 'domains' // Using nameserver, insert a task which rebuilds the server config inserttask('4'); - redirectTo($filename, array('page' => $page, 's' => $s)); - + redirectTo($filename, array( + 'page' => $page, + 's' => $s + )); } elseif ($alias_check['count'] > 0) { standard_error('domains_cantdeletedomainwithaliases'); - } else { $showcheck = false; if (domainHasMainSubDomains($id)) { $showcheck = true; } - ask_yesno_withcheckbox('admin_domain_reallydelete', 'remove_subbutmain_domains', $filename, array('id' => $id, 'page' => $page, 'action' => $action), $idna_convert->decode($result['domain']), $showcheck); + ask_yesno_withcheckbox('admin_domain_reallydelete', 'remove_subbutmain_domains', $filename, array( + 'id' => $id, + 'page' => $page, + 'action' => $action + ), $idna_convert->decode($result['domain']), $showcheck); } } + } elseif ($action == 'add') { - } elseif($action == 'add') { - - if ($userinfo['domains_used'] < $userinfo['domains'] - || $userinfo['domains'] == '-1' - ) { - if (isset($_POST['send']) - && $_POST['send'] == 'send' - ) { + if ($userinfo['domains_used'] < $userinfo['domains'] || $userinfo['domains'] == '-1') { + if (isset($_POST['send']) && $_POST['send'] == 'send') { if ($_POST['domain'] == Settings::Get('system.hostname')) { standard_error('admin_domain_emailsystemhostname'); } - $domain = $idna_convert->encode(preg_replace(array('/\:(\d)+$/', '/^https?\:\/\//'), '', validate($_POST['domain'], 'domain'))); + $domain = $idna_convert->encode(preg_replace(array( + '/\:(\d)+$/', + '/^https?\:\/\//' + ), '', validate($_POST['domain'], 'domain'))); $subcanemaildomain = intval($_POST['subcanemaildomain']); $isemaildomain = 0; @@ -338,18 +311,16 @@ if ($page == 'domains' $customerid = intval($_POST['customerid']); $customer_stmt = Database::prepare(" SELECT * FROM `" . TABLE_PANEL_CUSTOMERS . "` - WHERE `customerid` = :customerid " . - ($userinfo['customers_see_all'] ? '' : " AND `adminid` = :adminid") + WHERE `customerid` = :customerid " . ($userinfo['customers_see_all'] ? '' : " AND `adminid` = :adminid")); + $params = array( + 'customerid' => $customerid ); - $params = array('customerid' => $customerid); if ($userinfo['customers_see_all'] == '0') { $params['adminid'] = $userinfo['adminid']; } $customer = Database::pexecute_first($customer_stmt, $params); - if (empty($customer) - || $customer['customerid'] != $customerid - ) { + if (empty($customer) || $customer['customerid'] != $customerid) { standard_error('customerdoesntexist'); } @@ -358,16 +329,14 @@ if ($page == 'domains' $adminid = intval($_POST['adminid']); $admin_stmt = Database::prepare(" SELECT * FROM `" . TABLE_PANEL_ADMINS . "` - WHERE `adminid` = :adminid AND (`domains_used` < `domains` OR `domains` = '-1')" - ); - $admin = Database::pexecute_first($admin_stmt, array('adminid' => $adminid)); + WHERE `adminid` = :adminid AND (`domains_used` < `domains` OR `domains` = '-1')"); + $admin = Database::pexecute_first($admin_stmt, array( + 'adminid' => $adminid + )); - if (empty($admin) - || $admin['adminid'] != $adminid - ) { + if (empty($admin) || $admin['adminid'] != $adminid) { standard_error('admindoesntexist'); } - } else { $adminid = $userinfo['adminid']; $admin = $userinfo; @@ -377,15 +346,23 @@ if ($page == 'domains' // need to respect the documentroot_use_default_value - setting $path_suffix = ''; if (Settings::Get('system.documentroot_use_default_value') == 1) { - $path_suffix = '/'.$domain; + $path_suffix = '/' . $domain; } $documentroot = makeCorrectDir($customer['documentroot'] . $path_suffix); $registration_date = trim($_POST['registration_date']); - $registration_date = validate($registration_date, 'registration_date', '/^(19|20)\d\d[-](0[1-9]|1[012])[-](0[1-9]|[12][0-9]|3[01])$/', '', array('0000-00-00', '0', '')); + $registration_date = validate($registration_date, 'registration_date', '/^(19|20)\d\d[-](0[1-9]|1[012])[-](0[1-9]|[12][0-9]|3[01])$/', '', array( + '0000-00-00', + '0', + '' + )); - $termination_date = trim($_POST['termination_date']); - $termination_date = validate($termination_date, 'termination_date', '/^(19|20)\d\d[-](0[1-9]|1[012])[-](0[1-9]|[12][0-9]|3[01])$/', '', array('0000-00-00', '0', '')); + $termination_date = trim($_POST['termination_date']); + $termination_date = validate($termination_date, 'termination_date', '/^(19|20)\d\d[-](0[1-9]|1[012])[-](0[1-9]|[12][0-9]|3[01])$/', '', array( + '0000-00-00', + '0', + '' + )); if ($userinfo['change_serversettings'] == '1') { @@ -411,23 +388,15 @@ if ($page == 'domains' // If path is empty and 'Use domain name as default value for DocumentRoot path' is enabled in settings, // set default path to subdomain or domain name - if (isset($_POST['documentroot']) - && $_POST['documentroot'] != '' - ) { - if (substr($_POST['documentroot'], 0, 1) != '/' - && !preg_match('/^https?\:\/\//', $_POST['documentroot']) - ) { - $documentroot.= '/' . $_POST['documentroot']; + if (isset($_POST['documentroot']) && $_POST['documentroot'] != '') { + if (substr($_POST['documentroot'], 0, 1) != '/' && ! preg_match('/^https?\:\/\//', $_POST['documentroot'])) { + $documentroot .= '/' . $_POST['documentroot']; } else { $documentroot = $_POST['documentroot']; } - } elseif (isset($_POST['documentroot']) - && ($_POST['documentroot'] == '') - && (Settings::Get('system.documentroot_use_default_value') == 1) - ) { + } elseif (isset($_POST['documentroot']) && ($_POST['documentroot'] == '') && (Settings::Get('system.documentroot_use_default_value') == 1)) { $documentroot = makeCorrectDir($customer['documentroot'] . '/' . $domain); } - } else { $isbinddomain = '0'; if (Settings::Get('system.bind_enable') == '1') { @@ -439,40 +408,39 @@ if ($page == 'domains' $specialsettings = ''; } - if ($userinfo['caneditphpsettings'] == '1' - || $userinfo['change_serversettings'] == '1' - ) { + if ($userinfo['caneditphpsettings'] == '1' || $userinfo['change_serversettings'] == '1') { $openbasedir = isset($_POST['openbasedir']) ? intval($_POST['openbasedir']) : 0; - if ((int)Settings::Get('system.mod_fcgid') == 1 - || (int)Settings::Get('phpfpm.enabled') == 1 - ) { - $phpsettingid = (int)$_POST['phpsettingid']; + if ((int) Settings::Get('system.mod_fcgid') == 1 || (int) Settings::Get('phpfpm.enabled') == 1) { + $phpsettingid = (int) $_POST['phpsettingid']; $phpsettingid_check_stmt = Database::prepare(" SELECT * FROM `" . TABLE_PANEL_PHPCONFIGS . "` - WHERE `id` = :phpsettingid" - ); - $phpsettingid_check = Database::pexecute_first($phpsettingid_check_stmt, array('phpsettingid' => $phpsettingid)); + WHERE `id` = :phpsettingid"); + $phpsettingid_check = Database::pexecute_first($phpsettingid_check_stmt, array( + 'phpsettingid' => $phpsettingid + )); - if (!isset($phpsettingid_check['id']) - || $phpsettingid_check['id'] == '0' - || $phpsettingid_check['id'] != $phpsettingid - ) { + if (! isset($phpsettingid_check['id']) || $phpsettingid_check['id'] == '0' || $phpsettingid_check['id'] != $phpsettingid) { standard_error('phpsettingidwrong'); } - if ((int)Settings::Get('system.mod_fcgid') == 1) { - $mod_fcgid_starter = validate($_POST['mod_fcgid_starter'], 'mod_fcgid_starter', '/^[0-9]*$/', '', array('-1', '')); - $mod_fcgid_maxrequests = validate($_POST['mod_fcgid_maxrequests'], 'mod_fcgid_maxrequests', '/^[0-9]*$/', '', array('-1', '')); + if ((int) Settings::Get('system.mod_fcgid') == 1) { + $mod_fcgid_starter = validate($_POST['mod_fcgid_starter'], 'mod_fcgid_starter', '/^[0-9]*$/', '', array( + '-1', + '' + )); + $mod_fcgid_maxrequests = validate($_POST['mod_fcgid_maxrequests'], 'mod_fcgid_maxrequests', '/^[0-9]*$/', '', array( + '-1', + '' + )); } else { $mod_fcgid_starter = '-1'; $mod_fcgid_maxrequests = '-1'; } - } else { - if ((int)Settings::Get('phpfpm.enabled') == 1) { + if ((int) Settings::Get('phpfpm.enabled') == 1) { $phpsettingid = Settings::Get('phpfpm.defaultini'); } else { $phpsettingid = Settings::Get('system.mod_fcgid_defaultini'); @@ -480,11 +448,10 @@ if ($page == 'domains' $mod_fcgid_starter = '-1'; $mod_fcgid_maxrequests = '-1'; } - } else { $openbasedir = '1'; - if ((int)Settings::Get('phpfpm.enabled') == 1) { + if ((int) Settings::Get('phpfpm.enabled') == 1) { $phpsettingid = Settings::Get('phpfpm.defaultini'); } else { $phpsettingid = Settings::Get('system.mod_fcgid_defaultini'); @@ -496,36 +463,37 @@ if ($page == 'domains' if ($userinfo['ip'] != "-1") { $admin_ip_stmt = Database::prepare(" SELECT `id`, `ip`, `port` FROM `" . TABLE_PANEL_IPSANDPORTS . "` - WHERE `id` = :id ORDER BY `ip`, `port` ASC" - ); - $admin_ip = Database::pexecute_first($admin_ip_stmt, array('id' => $userinfo['ip'])); + WHERE `id` = :id ORDER BY `ip`, `port` ASC"); + $admin_ip = Database::pexecute_first($admin_ip_stmt, array( + 'id' => $userinfo['ip'] + )); $additional_ip_condition = " AND `ip` = :adminip "; - $aip_param = array('adminip' => $admin_ip['ip']); + $aip_param = array( + 'adminip' => $admin_ip['ip'] + ); } else { $additional_ip_condition = ''; $aip_param = array(); } $ipandports = array(); - if (isset($_POST['ipandport']) && !is_array($_POST['ipandport'])) { + if (isset($_POST['ipandport']) && ! is_array($_POST['ipandport'])) { $_POST['ipandport'] = unserialize($_POST['ipandport']); } if (isset($_POST['ipandport']) && is_array($_POST['ipandport'])) { - foreach($_POST['ipandport'] as $ipandport) { + foreach ($_POST['ipandport'] as $ipandport) { $ipandport = intval($ipandport); $ipandport_check_stmt = Database::prepare(" SELECT `id`, `ip`, `port` FROM `" . TABLE_PANEL_IPSANDPORTS . "` - WHERE `id` = :id " . $additional_ip_condition - ); + WHERE `id` = :id " . $additional_ip_condition); $ip_params = null; - $ip_params = array_merge(array('id' => $ipandport), $aip_param); + $ip_params = array_merge(array( + 'id' => $ipandport + ), $aip_param); $ipandport_check = Database::pexecute_first($ipandport_check_stmt, $ip_params); - if (!isset($ipandport_check['id']) - || $ipandport_check['id'] == '0' - || $ipandport_check['id'] != $ipandport - ) { + if (! isset($ipandport_check['id']) || $ipandport_check['id'] == '0' || $ipandport_check['id'] != $ipandport) { standard_error('ipportdoesntexist'); } else { $ipandports[] = $ipandport; @@ -533,43 +501,41 @@ if ($page == 'domains' } } - if (Settings::Get('system.use_ssl') == "1" - && isset($_POST['ssl_ipandport']) - ) { + if (Settings::Get('system.use_ssl') == "1" && isset($_POST['ssl_ipandport'])) { $ssl_redirect = 0; if (isset($_POST['ssl_redirect'])) { - $ssl_redirect = (int)$_POST['ssl_redirect']; + $ssl_redirect = (int) $_POST['ssl_redirect']; } $letsencrypt = 0; if (isset($_POST['letsencrypt'])) { - $letsencrypt = (int)$_POST['letsencrypt']; + $letsencrypt = (int) $_POST['letsencrypt']; } $ssl_ipandports = array(); - if (isset($_POST['ssl_ipandport']) && !is_array($_POST['ssl_ipandport'])) { + if (isset($_POST['ssl_ipandport']) && ! is_array($_POST['ssl_ipandport'])) { $_POST['ssl_ipandport'] = unserialize($_POST['ssl_ipandport']); } // Verify SSL-Ports if (isset($_POST['ssl_ipandport']) && is_array($_POST['ssl_ipandport'])) { foreach ($_POST['ssl_ipandport'] as $ssl_ipandport) { - if (trim($ssl_ipandport) == "") continue; - // fix if no ssl-ip/port is checked - if (trim($ssl_ipandport) < 1) continue; + if (trim($ssl_ipandport) == "") + continue; + // fix if no ssl-ip/port is checked + if (trim($ssl_ipandport) < 1) + continue; $ssl_ipandport = intval($ssl_ipandport); $ssl_ipandport_check_stmt = Database::prepare(" SELECT `id`, `ip`, `port` FROM `" . TABLE_PANEL_IPSANDPORTS . "` - WHERE `id` = :id " . $additional_ip_condition - ); + WHERE `id` = :id " . $additional_ip_condition); $ip_params = null; - $ip_params = array_merge(array('id' => $ssl_ipandport), $aip_param); + $ip_params = array_merge(array( + 'id' => $ssl_ipandport + ), $aip_param); $ssl_ipandport_check = Database::pexecute_first($ssl_ipandport_check_stmt, $ip_params); - if (!isset($ssl_ipandport_check['id']) - || $ssl_ipandport_check['id'] == '0' - || $ssl_ipandport_check['id'] != $ssl_ipandport - ) { + if (! isset($ssl_ipandport_check['id']) || $ssl_ipandport_check['id'] == '0' || $ssl_ipandport_check['id'] != $ssl_ipandport) { standard_error('ipportdoesntexist'); } else { $ssl_ipandports[] = $ssl_ipandport; @@ -580,19 +546,19 @@ if ($page == 'domains' $letsencrypt = 0; // we need this for the serialize // if ssl is disabled or no ssl-ip/port exists - $ssl_ipandports[] = -1; + $ssl_ipandports[] = - 1; } } else { $ssl_redirect = 0; $letsencrypt = 0; // we need this for the serialize // if ssl is disabled or no ssl-ip/port exists - $ssl_ipandports[] = -1; + $ssl_ipandports[] = - 1; } // We can't enable let's encrypt for wildcard - domains if ($serveraliasoption == '0' && $letsencrypt == '1') { - standard_error('nowildcardwithletsencrypt'); + standard_error('nowildcardwithletsencrypt'); } // Temporarily deactivate ssl_redirect until Let's Encrypt certificate was generated @@ -600,7 +566,7 @@ if ($page == 'domains' $ssl_redirect = 2; } - if (!preg_match('/^https?\:\/\//', $documentroot)) { + if (! preg_match('/^https?\:\/\//', $documentroot)) { if (strstr($documentroot, ":") !== false) { standard_error('pathmaynotcontaincolon'); } else { @@ -610,9 +576,10 @@ if ($page == 'domains' $domain_check_stmt = Database::prepare(" SELECT `id`, `domain` FROM `" . TABLE_PANEL_DOMAINS . "` - WHERE `domain` = :domain" - ); - $domain_check = Database::pexecute_first($domain_check_stmt, array('domain' => strtolower($domain))); + WHERE `domain` = :domain"); + $domain_check = Database::pexecute_first($domain_check_stmt, array( + 'domain' => strtolower($domain) + )); $aliasdomain_check = array( 'id' => 0 @@ -623,13 +590,16 @@ if ($page == 'domains' $ipandports = array(); $ssl_ipandports = array(); $origipresult_stmt = Database::prepare(" - SELECT `id_ipandports` FROM `" . TABLE_DOMAINTOIP ."` - WHERE `id_domain` = :id" - ); - Database::pexecute($origipresult_stmt, array('id' => $aliasdomain)); - $ipdata_stmt = Database::prepare("SELECT * FROM `".TABLE_PANEL_IPSANDPORTS."` WHERE `id` = :ipid"); + SELECT `id_ipandports` FROM `" . TABLE_DOMAINTOIP . "` + WHERE `id_domain` = :id"); + Database::pexecute($origipresult_stmt, array( + 'id' => $aliasdomain + )); + $ipdata_stmt = Database::prepare("SELECT * FROM `" . TABLE_PANEL_IPSANDPORTS . "` WHERE `id` = :ipid"); while ($origip = $origipresult_stmt->fetch(PDO::FETCH_ASSOC)) { - $_origip_tmp = Database::pexecute_first($ipdata_stmt, array('ipid' => $origip['id_ipandports'])); + $_origip_tmp = Database::pexecute_first($ipdata_stmt, array( + 'ipid' => $origip['id_ipandports'] + )); if ($_origip_tmp['ssl'] == 0) { $ipandports[] = $origip['id_ipandports']; } else { @@ -640,7 +610,7 @@ if ($page == 'domains' if (count($ssl_ipandports) == 0) { // we need this for the serialize // if ssl is disabled or no ssl-ip/port exists - $ssl_ipandports[] = -1; + $ssl_ipandports[] = - 1; } $aliasdomain_check_stmt = Database::prepare(" @@ -648,9 +618,11 @@ if ($page == 'domains' WHERE `d`.`customerid` = :customerid AND `d`.`aliasdomain` IS NULL AND `d`.`id` <> `c`.`standardsubdomain` AND `c`.`customerid` = :customerid - AND `d`.`id` = :aliasdomainid" + AND `d`.`id` = :aliasdomainid"); + $alias_params = array( + 'customerid' => $customerid, + 'aliasdomainid' => $aliasdomain ); - $alias_params = array('customerid' => $customerid, 'aliasdomainid' => $aliasdomain); $aliasdomain_check = Database::pexecute_first($aliasdomain_check_stmt, $alias_params); } @@ -680,10 +652,7 @@ if ($page == 'domains' $email_only = '0'; } - if ($subcanemaildomain != '1' - && $subcanemaildomain != '2' - && $subcanemaildomain != '3' - ) { + if ($subcanemaildomain != '1' && $subcanemaildomain != '2' && $subcanemaildomain != '3') { $subcanemaildomain = '0'; } @@ -703,24 +672,31 @@ if ($page == 'domains' $issubof = '0'; } - if ($aliasdomain != 0 && $letsencrypt != 0) - { + if ($aliasdomain != 0 && $letsencrypt != 0) { standard_error('letsencryptdoesnotworkwithaliasdomains'); } if ($domain == '') { - standard_error(array('stringisempty', 'mydomain')); - } - // Check whether domain validation is enabled and if, validate the domain - elseif (Settings::Get('system.validate_domain') && !validateDomain($domain)) { - standard_error(array('stringiswrong', 'mydomain')); - } elseif($documentroot == '') { - standard_error(array('stringisempty', 'mydocumentroot')); - } elseif($customerid == 0) { + standard_error(array( + 'stringisempty', + 'mydomain' + )); + } // Check whether domain validation is enabled and if, validate the domain +elseif (Settings::Get('system.validate_domain') && ! validateDomain($domain)) { + standard_error(array( + 'stringiswrong', + 'mydomain' + )); + } elseif ($documentroot == '') { + standard_error(array( + 'stringisempty', + 'mydocumentroot' + )); + } elseif ($customerid == 0) { standard_error('adduserfirst'); - } elseif(strtolower($domain_check['domain']) == strtolower($domain)) { + } elseif (strtolower($domain_check['domain']) == strtolower($domain)) { standard_error('domainalreadyexists', $idna_convert->decode($domain)); - } elseif($aliasdomain_check['id'] != $aliasdomain) { + } elseif ($aliasdomain_check['id'] != $aliasdomain) { standard_error('domainisaliasorothercustomer'); } else { $params = array( @@ -756,20 +732,18 @@ if ($page == 'domains' $security_questions = array( 'reallydisablesecuritysetting' => ($openbasedir == '0' && $userinfo['change_serversettings'] == '1'), - 'reallydocrootoutofcustomerroot' => (substr($documentroot, 0, strlen($customer['documentroot'])) != $customer['documentroot'] && !preg_match('/^https?\:\/\//', $documentroot)) + 'reallydocrootoutofcustomerroot' => (substr($documentroot, 0, strlen($customer['documentroot'])) != $customer['documentroot'] && ! preg_match('/^https?\:\/\//', $documentroot)) ); $question_nr = 1; foreach ($security_questions as $question_name => $question_launch) { if ($question_launch !== false) { $params[$question_name] = $question_name; - if (!isset($_POST[$question_name]) - || $_POST[$question_name] != $question_name - ) { + if (! isset($_POST[$question_name]) || $_POST[$question_name] != $question_name) { ask_yesno('admin_domain_' . $question_name, $filename, $params, $question_nr); } } - $question_nr++; + $question_nr ++; } $wwwserveralias = ($serveraliasoption == '1') ? '1' : '0'; @@ -796,7 +770,7 @@ if ($page == 'domains' 'ssl_redirect' => $ssl_redirect, 'add_date' => time(), 'registration_date' => $registration_date, - 'termination_date' => $termination_date, + 'termination_date' => $termination_date, 'phpsettingid' => $phpsettingid, 'mod_fcgid_starter' => $mod_fcgid_starter, 'mod_fcgid_maxrequests' => $mod_fcgid_maxrequests, @@ -841,9 +815,10 @@ if ($page == 'domains' $upd_stmt = Database::prepare(" UPDATE `" . TABLE_PANEL_ADMINS . "` SET `domains_used` = `domains_used` + 1 - WHERE `adminid` = :adminid" - ); - Database::pexecute($upd_stmt, array('adminid' => $adminid)); + WHERE `adminid` = :adminid"); + Database::pexecute($upd_stmt, array( + 'adminid' => $adminid + )); $ins_stmt = Database::prepare(" INSERT INTO `" . TABLE_DOMAINTOIP . "` SET @@ -874,18 +849,17 @@ if ($page == 'domains' // Using nameserver, insert a task which rebuilds the server config inserttask('4'); - redirectTo($filename, array('page' => $page, 's' => $s)); + redirectTo($filename, array( + 'page' => $page, + 's' => $s + )); } - } else { $customers = makeoption($lng['panel']['please_choose'], 0, 0, true); $result_customers_stmt = Database::prepare(" SELECT `customerid`, `loginname`, `name`, `firstname`, `company` - FROM `" . TABLE_PANEL_CUSTOMERS . "` " . - ($userinfo['customers_see_all'] ? '' : " WHERE `adminid` = '" . (int)$userinfo['adminid'] . "' ") . - " ORDER BY COALESCE(NULLIF(`name`,''), `company`) ASC" - ); + FROM `" . TABLE_PANEL_CUSTOMERS . "` " . ($userinfo['customers_see_all'] ? '' : " WHERE `adminid` = '" . (int) $userinfo['adminid'] . "' ") . " ORDER BY COALESCE(NULLIF(`name`,''), `company`) ASC"); $params = array(); if ($userinfo['customers_see_all'] == '0') { $params['adminid'] = $userinfo['adminid']; @@ -893,7 +867,7 @@ if ($page == 'domains' Database::pexecute($result_customers_stmt, $params); while ($row_customer = $result_customers_stmt->fetch(PDO::FETCH_ASSOC)) { - $customers.= makeoption(getCorrectFullUserDetails($row_customer) . ' (' . $row_customer['loginname'] . ')', $row_customer['customerid']); + $customers .= makeoption(getCorrectFullUserDetails($row_customer) . ' (' . $row_customer['loginname'] . ')', $row_customer['customerid']); } $admins = ''; @@ -902,11 +876,10 @@ if ($page == 'domains' $result_admins_stmt = Database::query(" SELECT `adminid`, `loginname`, `name` FROM `" . TABLE_PANEL_ADMINS . "` - WHERE `domains_used` < `domains` OR `domains` = '-1' ORDER BY `name` ASC" - ); + WHERE `domains_used` < `domains` OR `domains` = '-1' ORDER BY `name` ASC"); while ($row_admin = $result_admins_stmt->fetch(PDO::FETCH_ASSOC)) { - $admins.= makeoption(getCorrectFullUserDetails($row_admin) . ' (' . $row_admin['loginname'] . ')', $row_admin['adminid'], $userinfo['adminid']); + $admins .= makeoption(getCorrectFullUserDetails($row_admin) . ' (' . $row_admin['loginname'] . ')', $row_admin['adminid'], $userinfo['adminid']); } } @@ -921,17 +894,23 @@ if ($page == 'domains' $admin_ip_stmt = Database::prepare(" SELECT `id`, `ip`, `port` FROM `" . TABLE_PANEL_IPSANDPORTS . "` WHERE `id` = :ipid ORDER BY `ip`, `port` ASC "); - $admin_ip = Database::pexecute_first($admin_ip_stmt, array('ipid' => $userinfo['ip'])); + $admin_ip = Database::pexecute_first($admin_ip_stmt, array( + 'ipid' => $userinfo['ip'] + )); $result_ipsandports_stmt = Database::prepare(" SELECT `id`, `ip`, `port` FROM `" . TABLE_PANEL_IPSANDPORTS . "` WHERE `ssl`='0' AND `ip` = :ipid ORDER BY `ip`, `port` ASC "); - Database::pexecute($result_ipsandports_stmt, array('ipid' => $admin_ip['ip'])); + Database::pexecute($result_ipsandports_stmt, array( + 'ipid' => $admin_ip['ip'] + )); $result_ssl_ipsandports_stmt = Database::prepare(" SELECT `id`, `ip`, `port` FROM `" . TABLE_PANEL_IPSANDPORTS . "` WHERE `ssl`='1' AND `ip` = :ipid ORDER BY `ip`, `port` ASC "); - Database::pexecute($result_ssl_ipsandports_stmt, array('ipid' => $admin_ip['ip'])); + Database::pexecute($result_ssl_ipsandports_stmt, array( + 'ipid' => $admin_ip['ip'] + )); } // Build array holding all IPs and Ports available to this admin @@ -942,7 +921,10 @@ if ($page == 'domains' $row_ipandport['ip'] = '[' . $row_ipandport['ip'] . ']'; } - $ipsandports[] = array('label' => $row_ipandport['ip'] . ':' . $row_ipandport['port'] . '
', 'value' => $row_ipandport['id']); + $ipsandports[] = array( + 'label' => $row_ipandport['ip'] . ':' . $row_ipandport['port'] . '
', + 'value' => $row_ipandport['id'] + ); } $ssl_ipsandports = array(); @@ -952,7 +934,10 @@ if ($page == 'domains' $row_ssl_ipandport['ip'] = '[' . $row_ssl_ipandport['ip'] . ']'; } - $ssl_ipsandports[] = array('label' => $row_ssl_ipandport['ip'] . ':' . $row_ssl_ipandport['port'] . '
', 'value' => $row_ssl_ipandport['id']); + $ssl_ipsandports[] = array( + 'label' => $row_ssl_ipandport['ip'] . ':' . $row_ssl_ipandport['port'] . '
', + 'value' => $row_ssl_ipandport['id'] + ); } $standardsubdomains = array(); @@ -973,8 +958,7 @@ if ($page == 'domains' $domains = makeoption($lng['domains']['noaliasdomain'], 0, NULL, true); $result_domains_stmt = Database::prepare(" SELECT `d`.`id`, `d`.`domain`, `c`.`loginname` FROM `" . TABLE_PANEL_DOMAINS . "` `d`, `" . TABLE_PANEL_CUSTOMERS . "` `c` - WHERE `d`.`aliasdomain` IS NULL AND `d`.`parentdomainid` = 0" . $standardsubdomains . - ($userinfo['customers_see_all'] ? '' : " AND `d`.`adminid` = :adminid") . " + WHERE `d`.`aliasdomain` IS NULL AND `d`.`parentdomainid` = 0" . $standardsubdomains . ($userinfo['customers_see_all'] ? '' : " AND `d`.`adminid` = :adminid") . " AND `d`.`customerid`=`c`.`customerid` ORDER BY `loginname`, `domain` ASC "); $params = array(); @@ -984,31 +968,30 @@ if ($page == 'domains' Database::pexecute($result_domains_stmt, $params); while ($row_domain = $result_domains_stmt->fetch(PDO::FETCH_ASSOC)) { - $domains.= makeoption($idna_convert->decode($row_domain['domain']) . ' (' . $row_domain['loginname'] . ')', $row_domain['id']); + $domains .= makeoption($idna_convert->decode($row_domain['domain']) . ' (' . $row_domain['loginname'] . ')', $row_domain['id']); } $subtodomains = makeoption($lng['domains']['nosubtomaindomain'], 0, NULL, true); $result_domains_stmt = Database::prepare(" SELECT `d`.`id`, `d`.`domain`, `c`.`loginname` FROM `" . TABLE_PANEL_DOMAINS . "` `d`, `" . TABLE_PANEL_CUSTOMERS . "` `c` - WHERE `d`.`aliasdomain` IS NULL AND `d`.`parentdomainid` = 0 AND `d`.`ismainbutsubto` = 0 " . $standardsubdomains . - ($userinfo['customers_see_all'] ? '' : " AND `d`.`adminid` = :adminid") . " + WHERE `d`.`aliasdomain` IS NULL AND `d`.`parentdomainid` = 0 AND `d`.`ismainbutsubto` = 0 " . $standardsubdomains . ($userinfo['customers_see_all'] ? '' : " AND `d`.`adminid` = :adminid") . " AND `d`.`customerid`=`c`.`customerid` ORDER BY `loginname`, `domain` ASC "); // params from above still valid Database::pexecute($result_domains_stmt, $params); while ($row_domain = $result_domains_stmt->fetch(PDO::FETCH_ASSOC)) { - $subtodomains.= makeoption($idna_convert->decode($row_domain['domain']) . ' (' . $row_domain['loginname'] . ')', $row_domain['id']); + $subtodomains .= makeoption($idna_convert->decode($row_domain['domain']) . ' (' . $row_domain['loginname'] . ')', $row_domain['id']); } $phpconfigs = ''; $configs = Database::query("SELECT * FROM `" . TABLE_PANEL_PHPCONFIGS . "`"); while ($row = $configs->fetch(PDO::FETCH_ASSOC)) { - if ((int)Settings::Get('phpfpm.enabled') == 1) { - $phpconfigs.= makeoption($row['description'], $row['id'], Settings::Get('phpfpm.defaultini'), true, true); + if ((int) Settings::Get('phpfpm.enabled') == 1) { + $phpconfigs .= makeoption($row['description'], $row['id'], Settings::Get('phpfpm.defaultini'), true, true); } else { - $phpconfigs.= makeoption($row['description'], $row['id'], Settings::Get('system.mod_fcgid_defaultini'), true, true); + $phpconfigs .= makeoption($row['description'], $row['id'], Settings::Get('system.mod_fcgid_defaultini'), true, true); } } @@ -1019,13 +1002,13 @@ if ($page == 'domains' $serveraliasoptions .= makeoption($lng['domains']['serveraliasoption_none'], '2', '0', true, true); $subcanemaildomain = makeoption($lng['admin']['subcanemaildomain']['never'], '0', '0', true, true); - $subcanemaildomain.= makeoption($lng['admin']['subcanemaildomain']['choosableno'], '1', '0', true, true); - $subcanemaildomain.= makeoption($lng['admin']['subcanemaildomain']['choosableyes'], '2', '0', true, true); - $subcanemaildomain.= makeoption($lng['admin']['subcanemaildomain']['always'], '3', '0', true, true); + $subcanemaildomain .= makeoption($lng['admin']['subcanemaildomain']['choosableno'], '1', '0', true, true); + $subcanemaildomain .= makeoption($lng['admin']['subcanemaildomain']['choosableyes'], '2', '0', true, true); + $subcanemaildomain .= makeoption($lng['admin']['subcanemaildomain']['always'], '3', '0', true, true); $add_date = date('Y-m-d'); - $domain_add_data = include_once dirname(__FILE__).'/lib/formfields/admin/domains/formfield.domains_add.php'; + $domain_add_data = include_once dirname(__FILE__) . '/lib/formfields/admin/domains/formfield.domains_add.php'; $domain_add_form = htmlform::genHTMLForm($domain_add_data); $title = $domain_add_data['domain_add']['title']; @@ -1034,17 +1017,14 @@ if ($page == 'domains' eval("echo \"" . getTemplate("domains/domains_add") . "\";"); } } - - } elseif($action == 'edit' - && $id != 0 - ) { + } elseif ($action == 'edit' && $id != 0) { $result_stmt = Database::prepare(" SELECT `d`.*, `c`.`customerid` FROM `" . TABLE_PANEL_DOMAINS . "` `d` LEFT JOIN `" . TABLE_PANEL_CUSTOMERS . "` `c` USING(`customerid`) - WHERE `d`.`parentdomainid` = '0' AND `d`.`id` = :id" . - ($userinfo['customers_see_all'] ? '' : " AND `d`.`adminid` = :adminid") + WHERE `d`.`parentdomainid` = '0' AND `d`.`id` = :id" . ($userinfo['customers_see_all'] ? '' : " AND `d`.`adminid` = :adminid")); + $params = array( + 'id' => $id ); - $params = array('id' => $id); if ($userinfo['customers_see_all'] == '0') { $params['adminid'] = $userinfo['adminid']; } @@ -1056,21 +1036,28 @@ if ($page == 'domains' SELECT COUNT(`id`) AS count FROM `" . TABLE_PANEL_DOMAINS . "` WHERE `parentdomainid` = :resultid "); - $subdomains = Database::pexecute_first($subdomains_stmt, array('resultid' => $result['id'])); + $subdomains = Database::pexecute_first($subdomains_stmt, array( + 'resultid' => $result['id'] + )); $subdomains = $subdomains['count']; $alias_check_stmt = Database::prepare(" SELECT COUNT(`id`) AS count FROM `" . TABLE_PANEL_DOMAINS . "` WHERE `aliasdomain` = :resultid "); - $alias_check = Database::pexecute_first($alias_check_stmt, array('resultid' => $result['id'])); + $alias_check = Database::pexecute_first($alias_check_stmt, array( + 'resultid' => $result['id'] + )); $alias_check = $alias_check['count']; $domain_emails_result_stmt = Database::prepare(" SELECT `email`, `email_full`, `destination`, `popaccountid` AS `number_email_forwarders` FROM `" . TABLE_MAIL_VIRTUAL . "` WHERE `customerid` = :customerid AND `domainid` = :id "); - Database::pexecute($domain_emails_result_stmt, array('customerid' => $result['customerid'], 'id' => $result['id'])); + Database::pexecute($domain_emails_result_stmt, array( + 'customerid' => $result['customerid'], + 'id' => $result['id'] + )); $emails = Database::num_rows(); $email_forwarders = 0; @@ -1081,11 +1068,11 @@ if ($page == 'domains' if ($domain_emails_row['destination'] != '') { $domain_emails_row['destination'] = explode(' ', makeCorrectDestination($domain_emails_row['destination'])); - $email_forwarders+= count($domain_emails_row['destination']); + $email_forwarders += count($domain_emails_row['destination']); if (in_array($domain_emails_row['email_full'], $domain_emails_row['destination'])) { - $email_forwarders-= 1; - $email_accounts++; + $email_forwarders -= 1; + $email_accounts ++; } } } @@ -1093,31 +1080,30 @@ if ($page == 'domains' $ipsresult_stmt = Database::prepare(" SELECT `id_ipandports` FROM `" . TABLE_DOMAINTOIP . "` WHERE `id_domain` = :id "); - Database::pexecute($ipsresult_stmt, array('id' => $result['id'])); + Database::pexecute($ipsresult_stmt, array( + 'id' => $result['id'] + )); $usedips = array(); while ($ipsresultrow = $ipsresult_stmt->fetch(PDO::FETCH_ASSOC)) { $usedips[] = $ipsresultrow['id_ipandports']; } - if (isset($_POST['send']) - && $_POST['send'] == 'send' - ) { + if (isset($_POST['send']) && $_POST['send'] == 'send') { $customer_stmt = Database::prepare(" SELECT * FROM " . TABLE_PANEL_CUSTOMERS . " WHERE `customerid` = :customerid "); - $customer = Database::pexecute_first($customer_stmt, array('customerid' => $result['customerid'])); + $customer = Database::pexecute_first($customer_stmt, array( + 'customerid' => $result['customerid'] + )); - $customerid = -1; + $customerid = - 1; if (isset($_POST['customerid'])) { $customerid = intval($_POST['customerid']); } - if ($customerid > 0 - && $customerid != $result['customerid'] - && Settings::Get('panel.allow_domain_change_customer') == '1' - ) { + if ($customerid > 0 && $customerid != $result['customerid'] && Settings::Get('panel.allow_domain_change_customer') == '1') { $customer_stmt = Database::prepare(" SELECT * FROM `" . TABLE_PANEL_CUSTOMERS . "` @@ -1125,9 +1111,7 @@ if ($page == 'domains' AND (`subdomains_used` + :subdomains <= `subdomains` OR `subdomains` = '-1' ) AND (`emails_used` + :emails <= `emails` OR `emails` = '-1' ) AND (`email_forwarders_used` + :forwarders <= `email_forwarders` OR `email_forwarders` = '-1' ) - AND (`email_accounts_used` + :accounts <= `email_accounts` OR `email_accounts` = '-1' ) " . - ($userinfo['customers_see_all'] ? '' : " AND `adminid` = :adminid") - ); + AND (`email_accounts_used` + :accounts <= `email_accounts` OR `email_accounts` = '-1' ) " . ($userinfo['customers_see_all'] ? '' : " AND `adminid` = :adminid")); $params = array( 'customerid' => $customerid, @@ -1141,9 +1125,7 @@ if ($page == 'domains' } $customer = Database::pexecute_first($customer_stmt, $params); - if (empty($customer) - || $customer['customerid'] != $customerid - ) { + if (empty($customer) || $customer['customerid'] != $customerid) { standard_error('customerdoesntexist'); } } else { @@ -1153,29 +1135,28 @@ if ($page == 'domains' $customer_stmt = Database::prepare(" SELECT * FROM " . TABLE_PANEL_ADMINS . " WHERE `adminid` = :adminid "); - $admin = Database::pexecute_first($customer_stmt, array('adminid' => $result['adminid'])); + $admin = Database::pexecute_first($customer_stmt, array( + 'adminid' => $result['adminid'] + )); if ($userinfo['customers_see_all'] == '1') { - $adminid = -1; + $adminid = - 1; if (isset($_POST['adminid'])) { $adminid = intval($_POST['adminid']); } - if ($adminid > 0 - && $adminid != $result['adminid'] - && Settings::Get('panel.allow_domain_change_admin') == '1' - ) { + if ($adminid > 0 && $adminid != $result['adminid'] && Settings::Get('panel.allow_domain_change_admin') == '1') { $admin_stmt = Database::prepare(" SELECT * FROM `" . TABLE_PANEL_ADMINS . "` WHERE `adminid` = :adminid AND ( `domains_used` < `domains` OR `domains` = '-1' ) "); - $admin = Database::pexecute_first($admin_stmt, array('adminid' => $adminid)); + $admin = Database::pexecute_first($admin_stmt, array( + 'adminid' => $adminid + )); - if (empty($admin) - || $admin['adminid'] != $adminid - ) { + if (empty($admin) || $admin['adminid'] != $adminid) { standard_error('admindoesntexist'); } } else { @@ -1190,9 +1171,17 @@ if ($page == 'domains' $subcanemaildomain = intval($_POST['subcanemaildomain']); $caneditdomain = isset($_POST['caneditdomain']) ? intval($_POST['caneditdomain']) : 0; $registration_date = trim($_POST['registration_date']); - $registration_date = validate($registration_date, 'registration_date', '/^(19|20)\d\d[-](0[1-9]|1[012])[-](0[1-9]|[12][0-9]|3[01])$/', '', array('0000-00-00', '0', '')); + $registration_date = validate($registration_date, 'registration_date', '/^(19|20)\d\d[-](0[1-9]|1[012])[-](0[1-9]|[12][0-9]|3[01])$/', '', array( + '0000-00-00', + '0', + '' + )); $termination_date = trim($_POST['termination_date']); - $termination_date = validate($termination_date, 'termination_date', '/^(19|20)\d\d[-](0[1-9]|1[012])[-](0[1-9]|[12][0-9]|3[01])$/', '', array('0000-00-00', '0', '')); + $termination_date = validate($termination_date, 'termination_date', '/^(19|20)\d\d[-](0[1-9]|1[012])[-](0[1-9]|[12][0-9]|3[01])$/', '', array( + '0000-00-00', + '0', + '' + )); $isemaildomain = 0; if (isset($_POST['isemaildomain'])) { @@ -1215,16 +1204,15 @@ if ($page == 'domains' } $speciallogfile = 0; - if(isset($_POST['speciallogfile'])) + if (isset($_POST['speciallogfile'])) $speciallogfile = intval($_POST['speciallogfile']); - if ($userinfo['change_serversettings'] == '1') { $isbinddomain = $result['isbinddomain']; $zonefile = $result['zonefile']; if (Settings::Get('system.bind_enable') == '1') { if (isset($_POST['isbinddomain'])) { - $isbinddomain = (int)$_POST['isbinddomain']; + $isbinddomain = (int) $_POST['isbinddomain']; } else { $isbinddomain = 0; } @@ -1251,12 +1239,9 @@ if ($page == 'domains' } } - if (!preg_match('/^https?\:\/\//', $documentroot) - && strstr($documentroot, ":") !== false - ) { + if (! preg_match('/^https?\:\/\//', $documentroot) && strstr($documentroot, ":") !== false) { standard_error('pathmaynotcontaincolon'); } - } else { $isbinddomain = $result['isbinddomain']; $zonefile = $result['zonefile']; @@ -1266,44 +1251,43 @@ if ($page == 'domains' $documentroot = $result['documentroot']; } - $speciallogverified = (isset($_POST['speciallogverified']) ? (int)$_POST['speciallogverified'] : 0); + $speciallogverified = (isset($_POST['speciallogverified']) ? (int) $_POST['speciallogverified'] : 0); - if ($userinfo['caneditphpsettings'] == '1' - || $userinfo['change_serversettings'] == '1' - ) { + if ($userinfo['caneditphpsettings'] == '1' || $userinfo['change_serversettings'] == '1') { $openbasedir = isset($_POST['openbasedir']) ? intval($_POST['openbasedir']) : 0; - if ((int)Settings::Get('system.mod_fcgid') == 1 - || (int)Settings::Get('phpfpm.enabled') == 1 - ) { - $phpsettingid = (int)$_POST['phpsettingid']; + if ((int) Settings::Get('system.mod_fcgid') == 1 || (int) Settings::Get('phpfpm.enabled') == 1) { + $phpsettingid = (int) $_POST['phpsettingid']; $phpsettingid_check_stmt = Database::prepare(" SELECT * FROM `" . TABLE_PANEL_PHPCONFIGS . "` WHERE `id` = :phpid "); - $phpsettingid_check = Database::pexecute_first($phpsettingid_check_stmt, array('phpid' => $phpsettingid)); + $phpsettingid_check = Database::pexecute_first($phpsettingid_check_stmt, array( + 'phpid' => $phpsettingid + )); - if (!isset($phpsettingid_check['id']) - || $phpsettingid_check['id'] == '0' - || $phpsettingid_check['id'] != $phpsettingid - ) { + if (! isset($phpsettingid_check['id']) || $phpsettingid_check['id'] == '0' || $phpsettingid_check['id'] != $phpsettingid) { standard_error('phpsettingidwrong'); } - if ((int)Settings::Get('system.mod_fcgid') == 1) { - $mod_fcgid_starter = validate($_POST['mod_fcgid_starter'], 'mod_fcgid_starter', '/^[0-9]*$/', '', array('-1', '')); - $mod_fcgid_maxrequests = validate($_POST['mod_fcgid_maxrequests'], 'mod_fcgid_maxrequests', '/^[0-9]*$/', '', array('-1', '')); + if ((int) Settings::Get('system.mod_fcgid') == 1) { + $mod_fcgid_starter = validate($_POST['mod_fcgid_starter'], 'mod_fcgid_starter', '/^[0-9]*$/', '', array( + '-1', + '' + )); + $mod_fcgid_maxrequests = validate($_POST['mod_fcgid_maxrequests'], 'mod_fcgid_maxrequests', '/^[0-9]*$/', '', array( + '-1', + '' + )); } else { $mod_fcgid_starter = $result['mod_fcgid_starter']; $mod_fcgid_maxrequests = $result['mod_fcgid_maxrequests']; } - } else { $phpsettingid = $result['phpsettingid']; $mod_fcgid_starter = $result['mod_fcgid_starter']; $mod_fcgid_maxrequests = $result['mod_fcgid_maxrequests']; } - } else { $openbasedir = $result['openbasedir']; $phpsettingid = $result['phpsettingid']; @@ -1312,7 +1296,7 @@ if ($page == 'domains' } $ipandports = array(); - if (isset($_POST['ipandport']) && !is_array($_POST['ipandport'])) { + if (isset($_POST['ipandport']) && ! is_array($_POST['ipandport'])) { $_POST['ipandport'] = unserialize($_POST['ipandport']); } if (isset($_POST['ipandport']) && is_array($_POST['ipandport'])) { @@ -1321,13 +1305,13 @@ if ($page == 'domains' SELECT `id`, `ip`, `port` FROM `" . TABLE_PANEL_IPSANDPORTS . "` WHERE `id` = :ipandport "); foreach ($_POST['ipandport'] as $ipandport) { - if (trim($ipandport) == "") continue; + if (trim($ipandport) == "") + continue; $ipandport = intval($ipandport); - $ipandport_check = Database::pexecute_first($ipandport_check_stmt, array('ipandport' => $ipandport)); - if (!isset($ipandport_check['id']) - || $ipandport_check['id'] == '0' - || $ipandport_check['id'] != $ipandport - ) { + $ipandport_check = Database::pexecute_first($ipandport_check_stmt, array( + 'ipandport' => $ipandport + )); + if (! isset($ipandport_check['id']) || $ipandport_check['id'] == '0' || $ipandport_check['id'] != $ipandport) { standard_error('ipportdoesntexist'); } else { $ipandports[] = $ipandport; @@ -1335,22 +1319,20 @@ if ($page == 'domains' } } - if (Settings::Get('system.use_ssl') == '1' - && isset($_POST['ssl_ipandport']) - ) { + if (Settings::Get('system.use_ssl') == '1' && isset($_POST['ssl_ipandport'])) { $ssl = 1; // if ssl is set and != 0, it can only be 1 $ssl_redirect = 0; if (isset($_POST['ssl_redirect'])) { - $ssl_redirect = (int)$_POST['ssl_redirect']; + $ssl_redirect = (int) $_POST['ssl_redirect']; } $letsencrypt = 0; if (isset($_POST['letsencrypt'])) { - $letsencrypt = (int)$_POST['letsencrypt']; + $letsencrypt = (int) $_POST['letsencrypt']; } $ssl_ipandports = array(); - if (isset($_POST['ssl_ipandport']) && !is_array($_POST['ssl_ipandport'])) { + if (isset($_POST['ssl_ipandport']) && ! is_array($_POST['ssl_ipandport'])) { $_POST['ssl_ipandport'] = unserialize($_POST['ssl_ipandport']); } if (isset($_POST['ssl_ipandport']) && is_array($_POST['ssl_ipandport'])) { @@ -1359,15 +1341,16 @@ if ($page == 'domains' SELECT `id`, `ip`, `port` FROM `" . TABLE_PANEL_IPSANDPORTS . "` WHERE `id` = :ipandport "); foreach ($_POST['ssl_ipandport'] as $ssl_ipandport) { - if (trim($ssl_ipandport) == "") continue; - // fix if ip/port got de-checked and it was the last one - if (trim($ssl_ipandport) < 1) continue; + if (trim($ssl_ipandport) == "") + continue; + // fix if ip/port got de-checked and it was the last one + if (trim($ssl_ipandport) < 1) + continue; $ssl_ipandport = intval($ssl_ipandport); - $ssl_ipandport_check = Database::pexecute_first($ssl_ipandport_check_stmt, array('ipandport' => $ssl_ipandport)); - if (!isset($ssl_ipandport_check['id']) - || $ssl_ipandport_check['id'] == '0' - || $ssl_ipandport_check['id'] != $ssl_ipandport - ) { + $ssl_ipandport_check = Database::pexecute_first($ssl_ipandport_check_stmt, array( + 'ipandport' => $ssl_ipandport + )); + if (! isset($ssl_ipandport_check['id']) || $ssl_ipandport_check['id'] == '0' || $ssl_ipandport_check['id'] != $ssl_ipandport) { standard_error('ipportdoesntexist'); } else { $ssl_ipandports[] = $ssl_ipandport; @@ -1378,14 +1361,14 @@ if ($page == 'domains' $letsencrypt = 0; // we need this for the serialize // if ssl is disabled or no ssl-ip/port exists - $ssl_ipandports[] = -1; + $ssl_ipandports[] = - 1; } } else { $ssl_redirect = 0; $letsencrypt = 0; // we need this for the serialize // if ssl is disabled or no ssl-ip/port exists - $ssl_ipandports[] = -1; + $ssl_ipandports[] = - 1; } // We can't enable let's encrypt for wildcard domains @@ -1398,7 +1381,7 @@ if ($page == 'domains' $ssl_redirect = 2; } - if (!preg_match('/^https?\:\/\//', $documentroot)) { + if (! preg_match('/^https?\:\/\//', $documentroot)) { $documentroot = makeCorrectDir($documentroot); } @@ -1420,14 +1403,11 @@ if ($page == 'domains' $email_only = '0'; } - if ($subcanemaildomain != '1' - && $subcanemaildomain != '2' - && $subcanemaildomain != '3' - ) { + if ($subcanemaildomain != '1' && $subcanemaildomain != '2' && $subcanemaildomain != '3') { $subcanemaildomain = '0'; } - if ($dkim != '1') { + if ($dkim != '1') { $dkim = '0'; } @@ -1444,12 +1424,16 @@ if ($page == 'domains' $ipandports = array(); $ssl_ipandports = array(); $origipresult_stmt = Database::prepare(" - SELECT `id_ipandports` FROM `" . TABLE_DOMAINTOIP ."` WHERE `id_domain` = :aliasdomain + SELECT `id_ipandports` FROM `" . TABLE_DOMAINTOIP . "` WHERE `id_domain` = :aliasdomain "); - Database::pexecute($origipresult_stmt, array('aliasdomain' => $aliasdomain)); - $ipdata_stmt = Database::prepare("SELECT * FROM `".TABLE_PANEL_IPSANDPORTS."` WHERE `id` = :ipid"); + Database::pexecute($origipresult_stmt, array( + 'aliasdomain' => $aliasdomain + )); + $ipdata_stmt = Database::prepare("SELECT * FROM `" . TABLE_PANEL_IPSANDPORTS . "` WHERE `id` = :ipid"); while ($origip = $origipresult_stmt->fetch(PDO::FETCH_ASSOC)) { - $_origip_tmp = Database::pexecute_first($ipdata_stmt, array('ipid' => $origip['id_ipandports'])); + $_origip_tmp = Database::pexecute_first($ipdata_stmt, array( + 'ipid' => $origip['id_ipandports'] + )); if ($_origip_tmp['ssl'] == 0) { $ipandports[] = $origip['id_ipandports']; } else { @@ -1460,7 +1444,7 @@ if ($page == 'domains' if (count($ssl_ipandports) == 0) { // we need this for the serialize // if ssl is disabled or no ssl-ip/port exists - $ssl_ipandports[] = -1; + $ssl_ipandports[] = - 1; } $aliasdomain_check_stmt = Database::prepare(" @@ -1470,7 +1454,10 @@ if ($page == 'domains' AND `c`.`customerid` = :customerid AND `d`.`id` = :aliasdomain "); - $aliasdomain_check = Database::pexecute_first($aliasdomain_check_stmt, array('customerid' => $customerid, 'aliasdomain' => $aliasdomain)); + $aliasdomain_check = Database::pexecute_first($aliasdomain_check_stmt, array( + 'customerid' => $customerid, + 'aliasdomain' => $aliasdomain + )); } if (count($ipandports) == 0) { @@ -1485,8 +1472,7 @@ if ($page == 'domains' $issubof = '0'; } - if ($aliasdomain != 0 && $letsencrypt != 0) - { + if ($aliasdomain != 0 && $letsencrypt != 0) { standard_error('letsencryptdoesnotworkwithaliasdomains'); } @@ -1529,14 +1515,12 @@ if ($page == 'domains' $security_questions = array( 'reallydisablesecuritysetting' => ($openbasedir == '0' && $userinfo['change_serversettings'] == '1'), - 'reallydocrootoutofcustomerroot' => (substr($documentroot, 0, strlen($customer['documentroot'])) != $customer['documentroot'] && !preg_match('/^https?\:\/\//', $documentroot)) + 'reallydocrootoutofcustomerroot' => (substr($documentroot, 0, strlen($customer['documentroot'])) != $customer['documentroot'] && ! preg_match('/^https?\:\/\//', $documentroot)) ); foreach ($security_questions as $question_name => $question_launch) { if ($question_launch !== false) { $params[$question_name] = $question_name; - if (!isset($_POST[$question_name]) - || $_POST[$question_name] != $question_name - ) { + if (! isset($_POST[$question_name]) || $_POST[$question_name] != $question_name) { ask_yesno('admin_domain_' . $question_name, $filename, $params); } } @@ -1545,21 +1529,7 @@ if ($page == 'domains' $wwwserveralias = ($serveraliasoption == '1') ? '1' : '0'; $iswildcarddomain = ($serveraliasoption == '0') ? '1' : '0'; - if ($documentroot != $result['documentroot'] - || $ssl_redirect != $result['ssl_redirect'] - || $wwwserveralias != $result['wwwserveralias'] - || $iswildcarddomain != $result['iswildcarddomain'] - || $openbasedir != $result['openbasedir'] - || $phpsettingid != $result['phpsettingid'] - || $mod_fcgid_starter != $result['mod_fcgid_starter'] - || $mod_fcgid_maxrequests != $result['mod_fcgid_maxrequests'] - || $specialsettings != $result['specialsettings'] - || $aliasdomain != $result['aliasdomain'] - || $issubof != $result['ismainbutsubto'] - || $email_only != $result['email_only'] - || ($speciallogfile != $result['speciallogfile'] && $speciallogverified == '1') - || $letsencrypt != $result['letsencrypt'] - ) { + if ($documentroot != $result['documentroot'] || $ssl_redirect != $result['ssl_redirect'] || $wwwserveralias != $result['wwwserveralias'] || $iswildcarddomain != $result['iswildcarddomain'] || $openbasedir != $result['openbasedir'] || $phpsettingid != $result['phpsettingid'] || $mod_fcgid_starter != $result['mod_fcgid_starter'] || $mod_fcgid_maxrequests != $result['mod_fcgid_maxrequests'] || $specialsettings != $result['specialsettings'] || $aliasdomain != $result['aliasdomain'] || $issubof != $result['ismainbutsubto'] || $email_only != $result['email_only'] || ($speciallogfile != $result['speciallogfile'] && $speciallogverified == '1') || $letsencrypt != $result['letsencrypt']) { inserttask('1'); } @@ -1567,45 +1537,40 @@ if ($page == 'domains' $speciallogfile = $result['speciallogfile']; } - if ($isbinddomain != $result['isbinddomain'] - || $zonefile != $result['zonefile'] - || $dkim != $result['dkim'] - ) { + if ($isbinddomain != $result['isbinddomain'] || $zonefile != $result['zonefile'] || $dkim != $result['dkim']) { inserttask('4'); } - if ($isemaildomain == '0' - && $result['isemaildomain'] == '1' - ) { + if ($isemaildomain == '0' && $result['isemaildomain'] == '1') { $del_stmt = Database::prepare(" DELETE FROM `" . TABLE_MAIL_USERS . "` WHERE `domainid` = :id "); - Database::pexecute($del_stmt, array('id' => $id)); + Database::pexecute($del_stmt, array( + 'id' => $id + )); $del_stmt = Database::prepare(" DELETE FROM `" . TABLE_MAIL_VIRTUAL . "` WHERE `domainid` = :id "); - Database::pexecute($del_stmt, array('id' => $id)); + Database::pexecute($del_stmt, array( + 'id' => $id + )); $log->logAction(ADM_ACTION, LOG_NOTICE, "deleted domain #" . $id . " from mail-tables"); } $updatechildren = ''; - if ($subcanemaildomain == '0' - && $result['subcanemaildomain'] != '0' - ) { + if ($subcanemaildomain == '0' && $result['subcanemaildomain'] != '0') { $updatechildren = ", `isemaildomain` = '0' "; - - } elseif($subcanemaildomain == '3' - && $result['subcanemaildomain'] != '3' - ) { + } elseif ($subcanemaildomain == '3' && $result['subcanemaildomain'] != '3') { $updatechildren = ", `isemaildomain` = '1' "; } - if ($customerid != $result['customerid'] - && Settings::Get('panel.allow_domain_change_customer') == '1' - ) { - $upd_data = array('customerid' => $customerid, 'domainid' => $result['id']); + if ($customerid != $result['customerid'] && Settings::Get('panel.allow_domain_change_customer') == '1') { + $upd_data = array( + 'customerid' => $customerid, + 'domainid' => $result['id'] + ); $upd_stmt = Database::prepare(" UPDATE `" . TABLE_MAIL_USERS . "` SET `customerid` = :customerid WHERE `domainid` = :domainid "); @@ -1614,7 +1579,12 @@ if ($page == 'domains' UPDATE `" . TABLE_MAIL_VIRTUAL . "` SET `customerid` = :customerid WHERE `domainid` = :domainid "); Database::pexecute($upd_stmt, $upd_data); - $upd_data = array('subdomains' => $subdomains, 'emails' => $emails, 'forwarders' => $email_forwarders, 'accounts' => $email_accounts); + $upd_data = array( + 'subdomains' => $subdomains, + 'emails' => $emails, + 'forwarders' => $email_forwarders, + 'accounts' => $email_accounts + ); $upd_data['customerid'] = $customerid; $upd_stmt = Database::prepare(" UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET @@ -1638,18 +1608,20 @@ if ($page == 'domains' Database::pexecute($upd_stmt, $upd_data); } - if ($adminid != $result['adminid'] - && Settings::Get('panel.allow_domain_change_admin') == '1' - ) { + if ($adminid != $result['adminid'] && Settings::Get('panel.allow_domain_change_admin') == '1') { $upd_stmt = Database::prepare(" UPDATE `" . TABLE_PANEL_ADMINS . "` SET `domains_used` = `domains_used` + 1 WHERE `adminid` = :adminid "); - Database::pexecute($upd_stmt, array('adminid' => $adminid)); + Database::pexecute($upd_stmt, array( + 'adminid' => $adminid + )); $upd_stmt = Database::prepare(" UPDATE `" . TABLE_PANEL_ADMINS . "` SET `domains_used` = `domains_used` - 1 WHERE `adminid` = :adminid "); - Database::pexecute($upd_stmt, array('adminid' => $result['adminid'])); + Database::pexecute($upd_stmt, array( + 'adminid' => $result['adminid'] + )); } $_update_data = array(); @@ -1664,7 +1636,9 @@ if ($page == 'domains' $upd_stmt = Database::prepare(" UPDATE `" . TABLE_PANEL_DOMAINS . "` SET `specialsettings`='' WHERE `parentdomainid` = :id "); - Database::pexecute($upd_stmt, array('id' => $id)); + Database::pexecute($upd_stmt, array( + 'id' => $id + )); $log->logAction(ADM_ACTION, LOG_INFO, "removed specialsettings on all subdomains of domain #" . $id); } @@ -1740,7 +1714,7 @@ if ($page == 'domains' // all its subdomains must have "ssl-redirect = 0" // and disable let's encrypt $update_sslredirect = ''; - if (count($ssl_ipandports) == 1 && $ssl_ipandports[0] == -1) { + if (count($ssl_ipandports) == 1 && $ssl_ipandports[0] == - 1) { $update_sslredirect = ", `ssl_redirect` = '0', `letsencrypt` = '0' "; } @@ -1767,18 +1741,26 @@ if ($page == 'domains' $del_stmt = Database::prepare(" DELETE FROM `" . TABLE_DOMAINTOIP . "` WHERE `id_domain` = :id "); - Database::pexecute($del_stmt, array('id' => $id)); + Database::pexecute($del_stmt, array( + 'id' => $id + )); $ins_stmt = Database::prepare(" INSERT INTO `" . TABLE_DOMAINTOIP . "` SET `id_domain` = :domainid, `id_ipandports` = :ipportid "); foreach ($ipandports as $ipportid) { - Database::pexecute($ins_stmt, array('domainid' => $id, 'ipportid' => $ipportid)); + Database::pexecute($ins_stmt, array( + 'domainid' => $id, + 'ipportid' => $ipportid + )); } foreach ($ssl_ipandports as $ssl_ipportid) { if ($ssl_ipportid > 0) { - Database::pexecute($ins_stmt, array('domainid' => $id, 'ipportid' => $ssl_ipportid)); + Database::pexecute($ins_stmt, array( + 'domainid' => $id, + 'ipportid' => $ssl_ipportid + )); } } @@ -1786,14 +1768,18 @@ if ($page == 'domains' $domainidsresult_stmt = Database::prepare(" SELECT `id` FROM `" . TABLE_PANEL_DOMAINS . "` WHERE `parentdomainid` = :id "); - Database::pexecute($domainidsresult_stmt, array('id' => $id)); + Database::pexecute($domainidsresult_stmt, array( + 'id' => $id + )); while ($row = $domainidsresult_stmt->fetch(PDO::FETCH_ASSOC)) { $del_stmt = Database::prepare(" DELETE FROM `" . TABLE_DOMAINTOIP . "` WHERE `id_domain` = :rowid "); - Database::pexecute($del_stmt, array('rowid' => $row['id'])); + Database::pexecute($del_stmt, array( + 'rowid' => $row['id'] + )); $ins_stmt = Database::prepare(" INSERT INTO `" . TABLE_DOMAINTOIP . "` SET @@ -1802,21 +1788,28 @@ if ($page == 'domains' "); foreach ($ipandports as $ipportid) { - Database::pexecute($ins_stmt, array('rowid' => $row['id'], 'ipportid' => $ipportid)); + Database::pexecute($ins_stmt, array( + 'rowid' => $row['id'], + 'ipportid' => $ipportid + )); } foreach ($ssl_ipandports as $ssl_ipportid) { if ($ssl_ipportid > 0) { - Database::pexecute($ins_stmt, array('rowid' => $row['id'], 'ipportid' => $ssl_ipportid)); + Database::pexecute($ins_stmt, array( + 'rowid' => $row['id'], + 'ipportid' => $ssl_ipportid + )); } } } $log->logAction(ADM_ACTION, LOG_INFO, "edited domain #" . $id); - redirectTo($filename, array('page' => $page, 's' => $s)); - + redirectTo($filename, array( + 'page' => $page, + 's' => $s + )); } else { - if (Settings::Get('panel.allow_domain_change_customer') == '1') { $customers = ''; $result_customers_stmt = Database::prepare(" @@ -1824,8 +1817,7 @@ if ($page == 'domains' WHERE ( (`subdomains_used` + :subdomains <= `subdomains` OR `subdomains` = '-1' ) AND (`emails_used` + :emails <= `emails` OR `emails` = '-1' ) AND (`email_forwarders_used` + :forwarders <= `email_forwarders` OR `email_forwarders` = '-1' ) - AND (`email_accounts_used` + :accounts <= `email_accounts` OR `email_accounts` = '-1' ) " . - ($userinfo['customers_see_all'] ? '' : " AND `adminid` = :adminid ") . ") + AND (`email_accounts_used` + :accounts <= `email_accounts` OR `email_accounts` = '-1' ) " . ($userinfo['customers_see_all'] ? '' : " AND `adminid` = :adminid ") . ") OR `customerid` = :customerid ORDER BY `name` ASC "); $params = array( @@ -1841,15 +1833,16 @@ if ($page == 'domains' Database::pexecute($result_customers_stmt, $params); while ($row_customer = $result_customers_stmt->fetch(PDO::FETCH_ASSOC)) { - $customers.= makeoption(getCorrectFullUserDetails($row_customer) . ' (' . $row_customer['loginname'] . ')', $row_customer['customerid'], $result['customerid']); + $customers .= makeoption(getCorrectFullUserDetails($row_customer) . ' (' . $row_customer['loginname'] . ')', $row_customer['customerid'], $result['customerid']); } - } else { $customer_stmt = Database::prepare(" SELECT `customerid`, `loginname`, `name`, `firstname`, `company` FROM `" . TABLE_PANEL_CUSTOMERS . "` WHERE `customerid` = :customerid "); - $customer = Database::pexecute_first($customer_stmt, array('customerid' => $result['customerid'])); + $customer = Database::pexecute_first($customer_stmt, array( + 'customerid' => $result['customerid'] + )); $result['customername'] = getCorrectFullUserDetails($customer) . ' (' . $customer['loginname'] . ')'; } @@ -1861,16 +1854,20 @@ if ($page == 'domains' SELECT `adminid`, `loginname`, `name` FROM `" . TABLE_PANEL_ADMINS . "` WHERE (`domains_used` < `domains` OR `domains` = '-1') OR `adminid` = :adminid ORDER BY `name` ASC "); - Database::pexecute($result_admins_stmt, array('adminid' => $result['adminid'])); + Database::pexecute($result_admins_stmt, array( + 'adminid' => $result['adminid'] + )); while ($row_admin = $result_admins_stmt->fetch(PDO::FETCH_ASSOC)) { - $admins.= makeoption(getCorrectFullUserDetails($row_admin) . ' (' . $row_admin['loginname'] . ')', $row_admin['adminid'], $result['adminid']); + $admins .= makeoption(getCorrectFullUserDetails($row_admin) . ' (' . $row_admin['loginname'] . ')', $row_admin['adminid'], $result['adminid']); } } else { $admin_stmt = Database::prepare(" SELECT `adminid`, `loginname`, `name` FROM `" . TABLE_PANEL_ADMINS . "` WHERE `adminid` = :adminid "); - $admin = Database::pexecute_first($admin_stmt, array('adminid' => $result['adminid'])); + $admin = Database::pexecute_first($admin_stmt, array( + 'adminid' => $result['adminid'] + )); $result['adminname'] = getCorrectFullUserDetails($admin) . ' (' . $admin['loginname'] . ')'; } } @@ -1884,28 +1881,32 @@ if ($page == 'domains' AND `c`.`standardsubdomain`<>`d`.`id` AND `d`.`customerid` = :customerid AND `c`.`customerid`=`d`.`customerid` ORDER BY `d`.`domain` ASC "); - Database::pexecute($result_domains_stmt, array('id' => $result['id'], 'customerid' => $result['customerid'])); + Database::pexecute($result_domains_stmt, array( + 'id' => $result['id'], + 'customerid' => $result['customerid'] + )); while ($row_domain = $result_domains_stmt->fetch(PDO::FETCH_ASSOC)) { - $domains.= makeoption($idna_convert->decode($row_domain['domain']), $row_domain['id'], $result['aliasdomain']); + $domains .= makeoption($idna_convert->decode($row_domain['domain']), $row_domain['id'], $result['aliasdomain']); } $subtodomains = makeoption($lng['domains']['nosubtomaindomain'], 0, null, true); $result_domains_stmt = Database::prepare(" SELECT `d`.`id`, `d`.`domain` FROM `" . TABLE_PANEL_DOMAINS . "` `d`, `" . TABLE_PANEL_CUSTOMERS . "` `c` WHERE `d`.`aliasdomain` IS NULL AND `d`.`parentdomainid` = '0' AND `d`.`id` <> :id - AND `c`.`standardsubdomain`<>`d`.`id` AND `c`.`customerid`=`d`.`customerid`". - ($userinfo['customers_see_all'] ? '' : " AND `d`.`adminid` = :adminid") . " + AND `c`.`standardsubdomain`<>`d`.`id` AND `c`.`customerid`=`d`.`customerid`" . ($userinfo['customers_see_all'] ? '' : " AND `d`.`adminid` = :adminid") . " ORDER BY `d`.`domain` ASC "); - $params = array('id' => $result['id']); + $params = array( + 'id' => $result['id'] + ); if ($userinfo['customers_see_all'] == '0') { $params['adminid'] = $userinfo['adminid']; } Database::pexecute($result_domains_stmt, $params); while ($row_domain = $result_domains_stmt->fetch(PDO::FETCH_ASSOC)) { - $subtodomains.= makeoption($idna_convert->decode($row_domain['domain']), $row_domain['id'], $result['ismainbutsubto']); + $subtodomains .= makeoption($idna_convert->decode($row_domain['domain']), $row_domain['id'], $result['ismainbutsubto']); } if ($userinfo['ip'] == "-1") { @@ -1919,17 +1920,23 @@ if ($page == 'domains' $admin_ip_stmt = Database::prepare(" SELECT `id`, `ip`, `port` FROM `" . TABLE_PANEL_IPSANDPORTS . "` WHERE `id` = :ipid ORDER BY `ip`, `port` ASC "); - $admin_ip = Database::pexecute_first($admin_ip_stmt, array('ipid' => $userinfo['ip'])); + $admin_ip = Database::pexecute_first($admin_ip_stmt, array( + 'ipid' => $userinfo['ip'] + )); $result_ipsandports_stmt = Database::prepare(" SELECT `id`, `ip`, `port` FROM `" . TABLE_PANEL_IPSANDPORTS . "` WHERE `ssl`='0' AND `ip` = :ipid ORDER BY `ip`, `port` ASC "); - Database::pexecute($result_ipsandports_stmt, array('ipid' => $admin_ip['ip'])); + Database::pexecute($result_ipsandports_stmt, array( + 'ipid' => $admin_ip['ip'] + )); $result_ssl_ipsandports_stmt = Database::prepare(" SELECT `id`, `ip`, `port` FROM `" . TABLE_PANEL_IPSANDPORTS . "` WHERE `ssl`='1' AND `ip` = :ipid ORDER BY `ip`, `port` ASC "); - Database::pexecute($result_ssl_ipsandports_stmt, array('ipid' => $admin_ip['ip'])); + Database::pexecute($result_ssl_ipsandports_stmt, array( + 'ipid' => $admin_ip['ip'] + )); } $ipsandports = array(); @@ -1937,7 +1944,10 @@ if ($page == 'domains' if (filter_var($row_ipandport['ip'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { $row_ipandport['ip'] = '[' . $row_ipandport['ip'] . ']'; } - $ipsandports[] = array('label' => $row_ipandport['ip'] . ':' . $row_ipandport['port'] . '
', 'value' => $row_ipandport['id']); + $ipsandports[] = array( + 'label' => $row_ipandport['ip'] . ':' . $row_ipandport['port'] . '
', + 'value' => $row_ipandport['id'] + ); } $ssl_ipsandports = array(); @@ -1945,7 +1955,10 @@ if ($page == 'domains' if (filter_var($row_ssl_ipandport['ip'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { $row_ssl_ipandport['ip'] = '[' . $row_ssl_ipandport['ip'] . ']'; } - $ssl_ipsandports[] = array('label' => $row_ssl_ipandport['ip'] . ':' . $row_ssl_ipandport['port'] . '
', 'value' => $row_ssl_ipandport['id']); + $ssl_ipsandports[] = array( + 'label' => $row_ssl_ipandport['ip'] . ':' . $row_ssl_ipandport['port'] . '
', + 'value' => $row_ssl_ipandport['id'] + ); } // create serveralias options @@ -1961,15 +1974,15 @@ if ($page == 'domains' // Fudge the result for ssl_redirect to hide the Let's Encrypt steps $result['temporary_ssl_redirect'] = $result['ssl_redirect']; $result['ssl_redirect'] = ($result['ssl_redirect'] == 0 ? 0 : 1); - + $serveraliasoptions .= makeoption($lng['domains']['serveraliasoption_wildcard'], '0', $_value, true, true); $serveraliasoptions .= makeoption($lng['domains']['serveraliasoption_www'], '1', $_value, true, true); $serveraliasoptions .= makeoption($lng['domains']['serveraliasoption_none'], '2', $_value, true, true); $subcanemaildomain = makeoption($lng['admin']['subcanemaildomain']['never'], '0', $result['subcanemaildomain'], true, true); - $subcanemaildomain.= makeoption($lng['admin']['subcanemaildomain']['choosableno'], '1', $result['subcanemaildomain'], true, true); - $subcanemaildomain.= makeoption($lng['admin']['subcanemaildomain']['choosableyes'], '2', $result['subcanemaildomain'], true, true); - $subcanemaildomain.= makeoption($lng['admin']['subcanemaildomain']['always'], '3', $result['subcanemaildomain'], true, true); + $subcanemaildomain .= makeoption($lng['admin']['subcanemaildomain']['choosableno'], '1', $result['subcanemaildomain'], true, true); + $subcanemaildomain .= makeoption($lng['admin']['subcanemaildomain']['choosableyes'], '2', $result['subcanemaildomain'], true, true); + $subcanemaildomain .= makeoption($lng['admin']['subcanemaildomain']['always'], '3', $result['subcanemaildomain'], true, true); $speciallogfile = ($result['speciallogfile'] == 1 ? $lng['panel']['yes'] : $lng['panel']['no']); $result['add_date'] = date('Y-m-d', $result['add_date']); @@ -1977,12 +1990,12 @@ if ($page == 'domains' $phpconfigs_result_stmt = Database::query("SELECT * FROM `" . TABLE_PANEL_PHPCONFIGS . "`"); while ($phpconfigs_row = $phpconfigs_result_stmt->fetch(PDO::FETCH_ASSOC)) { - $phpconfigs.= makeoption($phpconfigs_row['description'], $phpconfigs_row['id'], $result['phpsettingid'], true, true); + $phpconfigs .= makeoption($phpconfigs_row['description'], $phpconfigs_row['id'], $result['phpsettingid'], true, true); } $result = htmlentities_array($result); - $domain_edit_data = include_once dirname(__FILE__).'/lib/formfields/admin/domains/formfield.domains_edit.php'; + $domain_edit_data = include_once dirname(__FILE__) . '/lib/formfields/admin/domains/formfield.domains_edit.php'; $domain_edit_form = htmlform::genHTMLForm($domain_edit_data); $title = $domain_edit_data['domain_edit']['title']; @@ -1993,15 +2006,13 @@ if ($page == 'domains' eval("echo \"" . getTemplate("domains/domains_edit") . "\";"); } } - } elseif($action == 'import') { + } elseif ($action == 'import') { - if (isset($_POST['send']) - && $_POST['send'] == 'send' - ) { + if (isset($_POST['send']) && $_POST['send'] == 'send') { $customerid = intval($_POST['customerid']); $separator = validate($_POST['separator'], 'separator'); - $offset = (int)validate($_POST['offset'], 'offset', "/[0-9]/i"); + $offset = (int) validate($_POST['offset'], 'offset', "/[0-9]/i"); $file_name = $_FILES['file']['tmp_name']; @@ -2015,21 +2026,22 @@ if ($page == 'domains' } // @FIXME find a way to display $result['notice'] here somehow, - // as it might be important if you've reached your maximum allocation of domains + // as it might be important if you've reached your maximum allocation of domains // update customer/admin counters updateCounters(false); $result_str = $result['imported'] . ' / ' . $result['all']; - standard_success('domain_import_successfully', $result_str, array('filename' => $filename, 'action' => '', 'page' => 'domains')); + standard_success('domain_import_successfully', $result_str, array( + 'filename' => $filename, + 'action' => '', + 'page' => 'domains' + )); } else { $customers = makeoption($lng['panel']['please_choose'], 0, 0, true); $result_customers_stmt = Database::prepare(" SELECT `customerid`, `loginname`, `name`, `firstname`, `company` - FROM `" . TABLE_PANEL_CUSTOMERS . "` " . - ($userinfo['customers_see_all'] ? '' : " WHERE `adminid` = '" . (int)$userinfo['adminid'] . "' ") . - " ORDER BY `name` ASC" - ); + FROM `" . TABLE_PANEL_CUSTOMERS . "` " . ($userinfo['customers_see_all'] ? '' : " WHERE `adminid` = '" . (int) $userinfo['adminid'] . "' ") . " ORDER BY `name` ASC"); $params = array(); if ($userinfo['customers_see_all'] == '0') { $params['adminid'] = $userinfo['adminid']; @@ -2037,10 +2049,10 @@ if ($page == 'domains' Database::pexecute($result_customers_stmt, $params); while ($row_customer = $result_customers_stmt->fetch(PDO::FETCH_ASSOC)) { - $customers.= makeoption(getCorrectFullUserDetails($row_customer) . ' (' . $row_customer['loginname'] . ')', $row_customer['customerid']); + $customers .= makeoption(getCorrectFullUserDetails($row_customer) . ' (' . $row_customer['loginname'] . ')', $row_customer['customerid']); } - $domain_import_data = include_once dirname(__FILE__).'/lib/formfields/admin/domains/formfield.domains_import.php'; + $domain_import_data = include_once dirname(__FILE__) . '/lib/formfields/admin/domains/formfield.domains_import.php'; $domain_import_form = htmlform::genHTMLForm($domain_import_data); $title = $domain_import_data['domain_import']['title']; @@ -2050,3 +2062,42 @@ if ($page == 'domains' } } } + +function formatDomainEntry(&$row, &$idna_convert) +{ + $row['domain'] = $idna_convert->decode($row['domain']); + $row['aliasdomain'] = $idna_convert->decode($row['aliasdomain']); + + $resultips_stmt = Database::prepare(" + SELECT `ips`.* FROM `" . TABLE_DOMAINTOIP . "` AS `dti`, `" . TABLE_PANEL_IPSANDPORTS . "` AS `ips` + WHERE `dti`.`id_ipandports` = `ips`.`id` AND `dti`.`id_domain` = :domainid + "); + + Database::pexecute($resultips_stmt, array( + 'domainid' => $row['id'] + )); + + $row['ipandport'] = ''; + while ($rowip = $resultips_stmt->fetch(PDO::FETCH_ASSOC)) { + + if (filter_var($rowip['ip'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { + $row['ipandport'] .= '[' . $rowip['ip'] . ']:' . $rowip['port'] . "\n"; + } else { + $row['ipandport'] .= $rowip['ip'] . ':' . $rowip['port'] . "\n"; + } + } + $row['ipandport'] = substr($row['ipandport'], 0, - 1); + $row['termination_date'] = str_replace("0000-00-00", "", $row['termination_date']); + + $row['termination_css'] = ""; + if ($row['termination_date'] != "") { + $cdate = strtotime($row['termination_date'] . " 23:59:59"); + $today = time(); + + if ($cdate < $today) { + $row['termination_css'] = 'domain-expired'; + } else { + $row['termination_css'] = 'domain-canceled'; + } + } +} diff --git a/templates/Sparkle/admin/domains/domains_domain.tpl b/templates/Sparkle/admin/domains/domains_domain.tpl index fe0dcf4f..4d268100 100644 --- a/templates/Sparkle/admin/domains/domains_domain.tpl +++ b/templates/Sparkle/admin/domains/domains_domain.tpl @@ -1,23 +1,20 @@ - -

+ + - - + + - - + + + + + + + diff --git a/templates/Sparkle/dns_editor/index.tpl b/templates/Sparkle/dns_editor/index.tpl new file mode 100644 index 00000000..92f37970 --- /dev/null +++ b/templates/Sparkle/dns_editor/index.tpl @@ -0,0 +1,28 @@ +$header +
+
+

+   + DNS Editor ({$domain}, {$entriescount} records) +

+
+ + +
+
{$lng['error']['error']}
+
{$errors}
+
+
+ +
+
{$lng['success']['success']}
+
{$success_message}
+
+
+ +
+ {$record_list} +
+ +
+$footer diff --git a/templates/Sparkle/dns_editor/list.tpl b/templates/Sparkle/dns_editor/list.tpl new file mode 100644 index 00000000..3d2e490a --- /dev/null +++ b/templates/Sparkle/dns_editor/list.tpl @@ -0,0 +1,25 @@ + +
'.$text; + return '
' . $text; } else { - return ''.$text.'
' . $text . '
{$row['domain']}  ({$lng['admin']['stdsubdomain']}) - -
({$lng['domains']['termination_date_overview']} {$row['termination_date']})
-
- + +
({$lng['domains']['termination_date_overview']} {$row['termination_date']})
+
{$row['ipandport']} {$row['customername']}  - ({$row['loginname']}) + ({$row['loginname']}) @@ -26,7 +23,7 @@ {$lng['panel']['letsencrypt']} - +   {$lng['panel']['delete']} diff --git a/templates/Sparkle/assets/css/main.css b/templates/Sparkle/assets/css/main.css index 7e532474..38e07046 100644 --- a/templates/Sparkle/assets/css/main.css +++ b/templates/Sparkle/assets/css/main.css @@ -1495,11 +1495,21 @@ fieldset.file { } .domain-canceled { - /* Color copied from .warningcontainer */ - background-color: #fffecc; + /* Color copied from .warningcontainer */ + background-color: #fffecc; } .domain-expired { - /* Color copied from .errorcontainer */ - background-color: rgb(242, 222, 222); -} \ No newline at end of file + /* Color copied from .errorcontainer */ + background-color: rgb(242, 222, 222); +} + +.domain-hostname { + background-color: rgb(53, 106, 160); + color: #ddd; + font-weight: bold; +} + +table.hl tbody tr.domain-hostname:hover { + background-color: rgb(64, 150, 238); +} From 28f0c3eac401b5793c8ca9e9750d99421683cb96 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Fri, 15 Apr 2016 15:09:11 +0200 Subject: [PATCH 0014/1335] only include acme.conf to vhosts if system has ssl and LE enabled (might not exist if not) Signed-off-by: Michael Kaufmann (d00p) --- scripts/jobs/cron_tasks.inc.http.30.nginx.php | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/scripts/jobs/cron_tasks.inc.http.30.nginx.php b/scripts/jobs/cron_tasks.inc.http.30.nginx.php index babc15ea..5069d223 100644 --- a/scripts/jobs/cron_tasks.inc.http.30.nginx.php +++ b/scripts/jobs/cron_tasks.inc.http.30.nginx.php @@ -207,11 +207,11 @@ class nginx extends HttpConfigBase { $this->nginx_data[$vhost_filename] .= "\t\tfastcgi_param SCRIPT_FILENAME \$document_root\$fastcgi_script_name;\n"; $this->nginx_data[$vhost_filename] .= "\t\tfastcgi_param PATH_INFO \$fastcgi_path_info;\n"; $this->nginx_data[$vhost_filename] .= "\t\ttry_files \$fastcgi_script_name =404;\n"; - + if ($row_ipsandports['ssl'] == '1') { $this->nginx_data[$vhost_filename] .= "\t\tfastcgi_param HTTPS on;\n"; } - + if ((int)Settings::Get('phpfpm.enabled') == 1 && (int)Settings::Get('phpfpm.enabled_ownvhost') == 1) { $domain = array( 'id' => 'none', @@ -225,16 +225,16 @@ class nginx extends HttpConfigBase { 'loginname' => 'froxlor.panel', 'documentroot' => $mypath, ); - + $php = new phpinterface($domain); $this->nginx_data[$vhost_filename] .= "\t\tfastcgi_pass unix:".$php->getInterface()->getSocketFile().";\n"; } else { $this->nginx_data[$vhost_filename] .= "\t\tfastcgi_pass ".Settings::Get('system.nginx_php_backend').";\n"; } - + $this->nginx_data[$vhost_filename] .= "\t\tfastcgi_index index.php;\n"; $this->nginx_data[$vhost_filename] .= "\t}\n"; - + $this->nginx_data[$vhost_filename] .= "}\n\n"; // End of Froxlor server{}-part } @@ -422,7 +422,11 @@ class nginx extends HttpConfigBase { ) { $vhost_content.= "\n" . $this->composeSslSettings($domain) . "\n"; } - $vhost_content.= "\t".'include /etc/nginx/acme.conf;'."\n"; + + if (Settings::Get('system.use_ssl') == '1' && Settings::Get('system.leenabled') == '1') + { + $vhost_content.= "\t".'include /etc/nginx/acme.conf;'."\n"; + } // if the documentroot is an URL we just redirect if (preg_match('/^https?\:\/\//', $domain['documentroot'])) { @@ -562,7 +566,7 @@ class nginx extends HttpConfigBase { } if ($domain_or_ip['ssl_cert_file'] != '') { - + // check for existence, #1485 if (!file_exists($domain_or_ip['ssl_cert_file'])) { $this->logger->logAction(CRON_ACTION, LOG_ERR, $domain_or_ip['domain'] . ' :: certificate file "'.$domain_or_ip['ssl_cert_file'].'" does not exist! Cannot create ssl-directives'); @@ -575,7 +579,7 @@ class nginx extends HttpConfigBase { $sslsettings .= "\t" . 'ssl_ecdh_curve secp384r1;' . "\n"; $sslsettings .= "\t" . 'ssl_prefer_server_ciphers on;' . "\n"; $sslsettings .= "\t" . 'ssl_certificate ' . makeCorrectFile($domain_or_ip['ssl_cert_file']) . ';' . "\n"; - + if ($domain_or_ip['ssl_key_file'] != '') { // check for existence, #1485 if (!file_exists($domain_or_ip['ssl_key_file'])) { @@ -585,7 +589,7 @@ class nginx extends HttpConfigBase { $sslsettings .= "\t" . 'ssl_certificate_key ' .makeCorrectFile($domain_or_ip['ssl_key_file']) . ';' . "\n"; } } - + if ($domain_or_ip['ssl_ca_file'] != '') { // check for existence, #1485 if (!file_exists($domain_or_ip['ssl_ca_file'])) { @@ -595,7 +599,7 @@ class nginx extends HttpConfigBase { $sslsettings.= "\t" . 'ssl_client_certificate ' . makeCorrectFile($domain_or_ip['ssl_ca_file']) . ';' . "\n"; } } - + if (isset($domain_or_ip['hsts']) && $domain_or_ip['hsts'] > 0) { $vhost_content .= 'add_header Strict-Transport-Security "max-age=' . $domain_or_ip['hsts']; @@ -835,11 +839,11 @@ class nginx extends HttpConfigBase { $phpopts .= "\t\tfastcgi_param HTTPS on;\n"; } $phpopts .= "\t}\n\n"; - + } return $phpopts; } - + protected function getWebroot($domain, $ssl) { $webroot_text = ''; From f8be36d229b1c6bfaba7b97e755b3814507bcba7 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sun, 17 Apr 2016 13:44:42 +0200 Subject: [PATCH 0015/1335] fix PHP notice #2048 Only variables should be passed by reference Signed-off-by: Michael Kaufmann (d00p) --- admin_customers.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/admin_customers.php b/admin_customers.php index 2b369b7f..dd7add11 100644 --- a/admin_customers.php +++ b/admin_customers.php @@ -939,7 +939,8 @@ if ($page == 'customers' SELECT ip, port FROM `".TABLE_PANEL_IPSANDPORTS."` WHERE `id` = :defaultip "); - $srv_ip = Database::pexecute_first($srv_ip_stmt, array('defaultip' => reset(explode(',', Settings::Get('system.defaultip'))))); + $default_ips = Settings::Get('system.defaultip'); + $srv_ip = Database::pexecute_first($srv_ip_stmt, array('defaultip' => reset(explode(',', $default_ips)))); $replace_arr = array( 'FIRSTNAME' => $firstname, From a485d9f4f9b91ba450e635e027193917baa66946 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 19 Apr 2016 13:31:09 +0200 Subject: [PATCH 0016/1335] TLSECCertificateFile and TLSECCertificateKeyFile for proftpds mod_tls require 1.3.5rc4 and later but Ubuntu 14.04 only provides 1.3.5rc3 Signed-off-by: Michael Kaufmann (d00p) --- lib/configfiles/trusty.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/configfiles/trusty.xml b/lib/configfiles/trusty.xml index c606c0bf..fd961f62 100644 --- a/lib/configfiles/trusty.xml +++ b/lib/configfiles/trusty.xml @@ -1363,8 +1363,8 @@ TLSLog /var/log/proftpd/tls.log TLSProtocol TLSv1 TLSv1.1 TLSv1.2 TLSRSACertificateFile /etc/ssl/certs/proftpd.crt TLSRSACertificateKeyFile /etc/ssl/private/proftpd.key -TLSECCertificateFile /etc/ssl/certs/proftpd_ec.crt -TLSECCertificateKeyFile /etc/ssl/private/proftpd_ec.key +#TLSECCertificateFile /etc/ssl/certs/proftpd_ec.crt +#TLSECCertificateKeyFile /etc/ssl/private/proftpd_ec.key TLSOptions NoCertRequest NoSessionReuseRequired TLSVerifyClient off From c8bbefb2bbba99e612d721a0c4056a43567c7554 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 28 Apr 2016 09:20:35 +0200 Subject: [PATCH 0017/1335] add simple backup-function for customers (testing state) Signed-off-by: Michael Kaufmann (d00p) --- customer_extras.php | 255 ++++++++++++------ install/froxlor.sql | 5 +- .../updates/froxlor/0.9/update_0.9.inc.php | 18 ++ .../admin/domains/formfield.domains_edit.php | 2 +- .../customer/extras/formfield.backup.php | 70 +++++ .../filedir/function.createCustomerBackup.php | 116 ++++++++ lib/functions/froxlor/function.inserttask.php | 5 + lib/navigation/00.froxlor.main.php | 14 +- lng/english.lng.php | 9 + lng/german.lng.php | 9 + scripts/jobs/cron_backup.php | 108 ++++++++ scripts/jobs/cron_tasks.php | 5 +- templates/Sparkle/customer/extras/backup.tpl | 26 ++ 13 files changed, 554 insertions(+), 88 deletions(-) create mode 100644 lib/formfields/customer/extras/formfield.backup.php create mode 100644 lib/functions/filedir/function.createCustomerBackup.php create mode 100644 scripts/jobs/cron_backup.php create mode 100644 templates/Sparkle/customer/extras/backup.tpl diff --git a/customer_extras.php b/customer_extras.php index 06a27a09..96907cb3 100644 --- a/customer_extras.php +++ b/customer_extras.php @@ -16,7 +16,6 @@ * @package Panel * */ - define('AREA', 'customer'); require './lib/init.php'; @@ -38,9 +37,10 @@ if ($page == 'overview') { ); $paging = new paging($userinfo, TABLE_PANEL_HTPASSWDS, $fields); $result_stmt = Database::prepare("SELECT * FROM `" . TABLE_PANEL_HTPASSWDS . "` - WHERE `customerid`= :customerid " . $paging->getSqlWhere(true) . " " . $paging->getSqlOrderBy() . " " . $paging->getSqlLimit() - ); - Database::pexecute($result_stmt, array("customerid" => $userinfo['customerid'])); + WHERE `customerid`= :customerid " . $paging->getSqlWhere(true) . " " . $paging->getSqlOrderBy() . " " . $paging->getSqlLimit()); + Database::pexecute($result_stmt, array( + "customerid" => $userinfo['customerid'] + )); $paging->setEntries(Database::num_rows()); $sortcode = $paging->getHtmlSortCode($lng); $arrowcode = $paging->getHtmlArrowCode($filename . '?page=' . $page . '&s=' . $s); @@ -58,38 +58,49 @@ if ($page == 'overview') { $row['path'] = makeCorrectDir($row['path']); $row = htmlentities_array($row); eval("\$htpasswds.=\"" . getTemplate("extras/htpasswds_htpasswd") . "\";"); - $count++; + $count ++; } - $i++; + $i ++; } eval("echo \"" . getTemplate("extras/htpasswds") . "\";"); } elseif ($action == 'delete' && $id != 0) { $result_stmt = Database::prepare("SELECT * FROM `" . TABLE_PANEL_HTPASSWDS . "` WHERE `customerid`= :customerid - AND `id`= :id" - ); - Database::pexecute($result_stmt, array("customerid" => $userinfo['customerid'], "id" => $id)); + AND `id`= :id"); + Database::pexecute($result_stmt, array( + "customerid" => $userinfo['customerid'], + "id" => $id + )); $result = $result_stmt->fetch(PDO::FETCH_ASSOC); if (isset($result['username']) && $result['username'] != '') { if (isset($_POST['send']) && $_POST['send'] == 'send') { $stmt = Database::prepare("DELETE FROM `" . TABLE_PANEL_HTPASSWDS . "` WHERE `customerid`= :customerid - AND `id`= :id" - ); - Database::pexecute($stmt, array("customerid" => $userinfo['customerid'], "id" => $id)); + AND `id`= :id"); + Database::pexecute($stmt, array( + "customerid" => $userinfo['customerid'], + "id" => $id + )); $log->logAction(USR_ACTION, LOG_INFO, "deleted htpasswd for '" . $result['username'] . " (" . $result['path'] . ")'"); inserttask('1'); - redirectTo($filename, array('page' => $page, 's' => $s)); + redirectTo($filename, array( + 'page' => $page, + 's' => $s + )); } else { if (strpos($result['path'], $userinfo['documentroot']) === 0) { - $result['path'] = str_replace($userinfo['documentroot'], "/", $result['path']); + $result['path'] = str_replace($userinfo['documentroot'], "/", $result['path']); } - ask_yesno('extras_reallydelete', $filename, array('id' => $id, 'page' => $page, 'action' => $action), $result['username'] . ' (' . $result['path'] . ')'); + ask_yesno('extras_reallydelete', $filename, array( + 'id' => $id, + 'page' => $page, + 'action' => $action + ), $result['username'] . ' (' . $result['path'] . ')'); } } } elseif ($action == 'add') { @@ -104,8 +115,7 @@ if ($page == 'overview') { $username_path_check_stmt = Database::prepare("SELECT `id`, `username`, `path` FROM `" . TABLE_PANEL_HTPASSWDS . "` WHERE `username`= :username AND `path`= :path - AND `customerid`= :customerid" - ); + AND `customerid`= :customerid"); $params = array( "username" => $username, "path" => $path, @@ -121,16 +131,22 @@ if ($page == 'overview') { $password = crypt($_POST['directory_password']); } - if (!$_POST['path']) { + if (! $_POST['path']) { standard_error('invalidpath'); } if ($username == '') { - standard_error(array('stringisempty', 'myloginname')); + standard_error(array( + 'stringisempty', + 'myloginname' + )); } elseif ($username_path_check['username'] == $username && $username_path_check['path'] == $path) { standard_error('userpathcombinationdupe'); } elseif ($_POST['directory_password'] == '') { - standard_error(array('stringisempty', 'mypassword')); + standard_error(array( + 'stringisempty', + 'mypassword' + )); } elseif ($path == '') { standard_error('patherror'); } elseif ($_POST['directory_password'] == $username) { @@ -141,8 +157,7 @@ if ($page == 'overview') { `username` = :username, `password` = :password, `path` = :path, - `authname` = :authname" - ); + `authname` = :authname"); $params = array( "customerid" => $userinfo['customerid'], "username" => $username, @@ -153,12 +168,15 @@ if ($page == 'overview') { Database::pexecute($stmt, $params); $log->logAction(USR_ACTION, LOG_INFO, "added htpasswd for '" . $username . " (" . $path . ")'"); inserttask('1'); - redirectTo($filename, array('page' => $page, 's' => $s)); + redirectTo($filename, array( + 'page' => $page, + 's' => $s + )); } } else { $pathSelect = makePathfield($userinfo['documentroot'], $userinfo['guid'], $userinfo['guid']); - $htpasswd_add_data = include_once dirname(__FILE__).'/lib/formfields/customer/extras/formfield.htpasswd_add.php'; + $htpasswd_add_data = include_once dirname(__FILE__) . '/lib/formfields/customer/extras/formfield.htpasswd_add.php'; $htpasswd_add_form = htmlform::genHTMLForm($htpasswd_add_data); $title = $htpasswd_add_data['htpasswd_add']['title']; @@ -169,9 +187,11 @@ if ($page == 'overview') { } elseif ($action == 'edit' && $id != 0) { $result_stmt = Database::prepare("SELECT * FROM `" . TABLE_PANEL_HTPASSWDS . "` WHERE `customerid`= :customerid - AND `id`= :id" - ); - Database::pexecute($result_stmt, array("customerid" => $userinfo['customerid'], "id" => $id)); + AND `id`= :id"); + Database::pexecute($result_stmt, array( + "customerid" => $userinfo['customerid'], + "id" => $id + )); $result = $result_stmt->fetch(PDO::FETCH_ASSOC); if (isset($result['username']) && $result['username'] != '') { @@ -208,19 +228,21 @@ if ($page == 'overview') { } if ($pwd_sql != '' || $auth_sql != '') { - if ($pwd_sql !='' && $auth_sql != '') { - $pwd_sql.= ', '; + if ($pwd_sql != '' && $auth_sql != '') { + $pwd_sql .= ', '; } $stmt = Database::prepare("UPDATE `" . TABLE_PANEL_HTPASSWDS . "` - SET ".$pwd_sql.$auth_sql." + SET " . $pwd_sql . $auth_sql . " WHERE `customerid`= :customerid - AND `id`= :id" - ); + AND `id`= :id"); Database::pexecute($stmt, $params); $log->logAction(USR_ACTION, LOG_INFO, "edited htpasswd for '" . $result['username'] . " (" . $result['path'] . ")'"); inserttask('1'); - redirectTo($filename, array('page' => $page, 's' => $s)); + redirectTo($filename, array( + 'page' => $page, + 's' => $s + )); } } else { if (strpos($result['path'], $userinfo['documentroot']) === 0) { @@ -229,7 +251,7 @@ if ($page == 'overview') { $result = htmlentities_array($result); - $htpasswd_edit_data = include_once dirname(__FILE__).'/lib/formfields/customer/extras/formfield.htpasswd_edit.php'; + $htpasswd_edit_data = include_once dirname(__FILE__) . '/lib/formfields/customer/extras/formfield.htpasswd_edit.php'; $htpasswd_edit_form = htmlform::genHTMLForm($htpasswd_edit_data); $title = $htpasswd_edit_data['htpasswd_edit']['title']; @@ -252,9 +274,10 @@ if ($page == 'overview') { ); $paging = new paging($userinfo, TABLE_PANEL_HTACCESS, $fields); $result_stmt = Database::prepare("SELECT * FROM `" . TABLE_PANEL_HTACCESS . "` - WHERE `customerid`= :customerid " . $paging->getSqlWhere(true) . " " . $paging->getSqlOrderBy() . " " . $paging->getSqlLimit() - ); - Database::pexecute($result_stmt, array("customerid" => $userinfo['customerid'])); + WHERE `customerid`= :customerid " . $paging->getSqlWhere(true) . " " . $paging->getSqlOrderBy() . " " . $paging->getSqlLimit()); + Database::pexecute($result_stmt, array( + "customerid" => $userinfo['customerid'] + )); $paging->setEntries(Database::num_rows()); $sortcode = $paging->getHtmlSortCode($lng); $arrowcode = $paging->getHtmlArrowCode($filename . '?page=' . $page . '&s=' . $s); @@ -278,49 +301,60 @@ if ($page == 'overview') { $row['options_cgi'] = str_replace('0', $lng['panel']['no'], $row['options_cgi']); $row = htmlentities_array($row); eval("\$htaccess.=\"" . getTemplate("extras/htaccess_htaccess") . "\";"); - $count++; + $count ++; } - $i++; + $i ++; } eval("echo \"" . getTemplate("extras/htaccess") . "\";"); } elseif ($action == 'delete' && $id != 0) { $result_stmt = Database::prepare("SELECT * FROM `" . TABLE_PANEL_HTACCESS . "` WHERE `customerid` = :customerid - AND `id` = :id" - ); - Database::pexecute($result_stmt, array("customerid" => $userinfo['customerid'], "id" => $id)); + AND `id` = :id"); + Database::pexecute($result_stmt, array( + "customerid" => $userinfo['customerid'], + "id" => $id + )); $result = $result_stmt->fetch(PDO::FETCH_ASSOC); if (isset($result['customerid']) && $result['customerid'] != '' && $result['customerid'] == $userinfo['customerid']) { if (isset($_POST['send']) && $_POST['send'] == 'send') { // do we have to remove the symlink and folder in suexecpath? - if ((int)Settings::Get('perl.suexecworkaround') == 1) { + if ((int) Settings::Get('perl.suexecworkaround') == 1) { $loginname = getCustomerDetail($result['customerid'], 'loginname'); - $suexecpath = makeCorrectDir(Settings::Get('perl.suexecpath').'/'.$loginname.'/'.md5($result['path']).'/'); - $perlsymlink = makeCorrectFile($result['path'].'/cgi-bin'); + $suexecpath = makeCorrectDir(Settings::Get('perl.suexecpath') . '/' . $loginname . '/' . md5($result['path']) . '/'); + $perlsymlink = makeCorrectFile($result['path'] . '/cgi-bin'); // remove symlink if (file_exists($perlsymlink)) { - safe_exec('rm -f '.escapeshellarg($perlsymlink)); + safe_exec('rm -f ' . escapeshellarg($perlsymlink)); $log->logAction(USR_ACTION, LOG_DEBUG, "deleted suexecworkaround symlink '" . $perlsymlink . "'"); } // remove folder in suexec-path if (file_exists($suexecpath)) { - safe_exec('rm -rf '.escapeshellarg($suexecpath)); + safe_exec('rm -rf ' . escapeshellarg($suexecpath)); $log->logAction(USR_ACTION, LOG_DEBUG, "deleted suexecworkaround path '" . $suexecpath . "'"); } } $stmt = Database::prepare("DELETE FROM `" . TABLE_PANEL_HTACCESS . "` WHERE `customerid`= :customerid - AND `id`= :id" - ); - Database::pexecute($stmt, array("customerid" => $userinfo['customerid'], "id" => $id)); + AND `id`= :id"); + Database::pexecute($stmt, array( + "customerid" => $userinfo['customerid'], + "id" => $id + )); $log->logAction(USR_ACTION, LOG_INFO, "deleted htaccess for '" . str_replace($userinfo['documentroot'], '/', $result['path']) . "'"); inserttask('1'); - redirectTo($filename, array('page' => $page, 's' => $s)); + redirectTo($filename, array( + 'page' => $page, + 's' => $s + )); } else { - ask_yesno('extras_reallydelete_pathoptions', $filename, array('id' => $id, 'page' => $page, 'action' => $action), str_replace($userinfo['documentroot'], '/', $result['path'])); + ask_yesno('extras_reallydelete_pathoptions', $filename, array( + 'id' => $id, + 'page' => $page, + 'action' => $action + ), str_replace($userinfo['documentroot'], '/', $result['path'])); } } } elseif ($action == 'add') { @@ -330,16 +364,18 @@ if ($page == 'overview') { $path = makeCorrectDir($userinfo['documentroot'] . '/' . $path); $path_dupe_check_stmt = Database::prepare("SELECT `id`, `path` FROM `" . TABLE_PANEL_HTACCESS . "` WHERE `path`= :path - AND `customerid`= :customerid" - ); - Database::pexecute($path_dupe_check_stmt, array("path" => $path, "customerid" => $userinfo['customerid'])); + AND `customerid`= :customerid"); + Database::pexecute($path_dupe_check_stmt, array( + "path" => $path, + "customerid" => $userinfo['customerid'] + )); $path_dupe_check = $path_dupe_check_stmt->fetch(PDO::FETCH_ASSOC); - if (!$_POST['path']) { + if (! $_POST['path']) { standard_error('invalidpath'); } - if (isset($_POST['options_cgi']) && (int)$_POST['options_cgi'] != 0) { + if (isset($_POST['options_cgi']) && (int) $_POST['options_cgi'] != 0) { $options_cgi = '1'; } else { $options_cgi = '0'; @@ -372,8 +408,7 @@ if ($page == 'overview') { `error404path` = :error404path, `error403path` = :error403path, `error500path` = :error500path, - `options_cgi` = :options_cgi' - ); + `options_cgi` = :options_cgi'); $params = array( "customerid" => $userinfo['customerid'], "path" => $path, @@ -387,13 +422,16 @@ if ($page == 'overview') { $log->logAction(USR_ACTION, LOG_INFO, "added htaccess for '" . $path . "'"); inserttask('1'); - redirectTo($filename, array('page' => $page, 's' => $s)); + redirectTo($filename, array( + 'page' => $page, + 's' => $s + )); } } else { $pathSelect = makePathfield($userinfo['documentroot'], $userinfo['guid'], $userinfo['guid']); $cperlenabled = customerHasPerlEnabled($userinfo['customerid']); - $htaccess_add_data = include_once dirname(__FILE__).'/lib/formfields/customer/extras/formfield.htaccess_add.php'; + $htaccess_add_data = include_once dirname(__FILE__) . '/lib/formfields/customer/extras/formfield.htaccess_add.php'; $htaccess_add_form = htmlform::genHTMLForm($htaccess_add_data); $title = $htaccess_add_data['htaccess_add']['title']; @@ -404,9 +442,11 @@ if ($page == 'overview') { } elseif (($action == 'edit') && ($id != 0)) { $result_stmt = Database::prepare("SELECT * FROM `" . TABLE_PANEL_HTACCESS . "` WHERE `customerid` = :customerid - AND `id` = :id" - ); - Database::pexecute($result_stmt, array("customerid" => $userinfo['customerid'], "id" => $id)); + AND `id` = :id"); + Database::pexecute($result_stmt, array( + "customerid" => $userinfo['customerid'], + "id" => $id + )); $result = $result_stmt->fetch(PDO::FETCH_ASSOC); if ((isset($result['customerid'])) && ($result['customerid'] != '') && ($result['customerid'] == $userinfo['customerid'])) { @@ -426,12 +466,7 @@ if ($page == 'overview') { $error403path = correctErrorDocument($_POST['error403path']); $error500path = correctErrorDocument($_POST['error500path']); - if (($option_indexes != $result['options_indexes']) - || ($error404path != $result['error404path']) - || ($error403path != $result['error403path']) - || ($error500path != $result['error500path']) - || ($options_cgi != $result['options_cgi']) - ) { + if (($option_indexes != $result['options_indexes']) || ($error404path != $result['error404path']) || ($error403path != $result['error403path']) || ($error500path != $result['error500path']) || ($options_cgi != $result['options_cgi'])) { inserttask('1'); $stmt = Database::prepare("UPDATE `" . TABLE_PANEL_HTACCESS . "` SET `options_indexes` = :options_indexes, @@ -440,8 +475,7 @@ if ($page == 'overview') { `error500path` = :error500path, `options_cgi` = :options_cgi WHERE `customerid` = :customerid - AND `id` = :id" - ); + AND `id` = :id"); $params = array( "customerid" => $userinfo['customerid'], "options_indexes" => $_POST['options_indexes'] == '1' ? '1' : '0', @@ -455,7 +489,10 @@ if ($page == 'overview') { $log->logAction(USR_ACTION, LOG_INFO, "edited htaccess for '" . str_replace($userinfo['documentroot'], '/', $result['path']) . "'"); } - redirectTo($filename, array('page' => $page, 's' => $s)); + redirectTo($filename, array( + 'page' => $page, + 's' => $s + )); } else { if (strpos($result['path'], $userinfo['documentroot']) === 0) { $result['path'] = str_replace($userinfo['documentroot'], "/", $result['path']); @@ -466,12 +503,12 @@ if ($page == 'overview') { $result['error500path'] = $result['error500path']; $cperlenabled = customerHasPerlEnabled($userinfo['customerid']); /* - $options_indexes = makeyesno('options_indexes', '1', '0', $result['options_indexes']); - $options_cgi = makeyesno('options_cgi', '1', '0', $result['options_cgi']); - */ + * $options_indexes = makeyesno('options_indexes', '1', '0', $result['options_indexes']); + * $options_cgi = makeyesno('options_cgi', '1', '0', $result['options_cgi']); + */ $result = htmlentities_array($result); - $htaccess_edit_data = include_once dirname(__FILE__).'/lib/formfields/customer/extras/formfield.htaccess_edit.php'; + $htaccess_edit_data = include_once dirname(__FILE__) . '/lib/formfields/customer/extras/formfield.htaccess_edit.php'; $htaccess_edit_form = htmlform::genHTMLForm($htaccess_edit_data); $title = $htaccess_edit_data['htaccess_edit']['title']; @@ -481,4 +518,66 @@ if ($page == 'overview') { } } } -} +} elseif ($page == 'backup') { + if ($action == '') { + $log->logAction(USR_ACTION, LOG_NOTICE, "viewed customer_extras::backup"); + + if (isset($_POST['send']) && $_POST['send'] == 'send') { + + if (! $_POST['path']) { + standard_error('invalidpath'); + } + + $path = makeCorrectDir(validate($_POST['path'], 'path')); + $path = makeCorrectDir($userinfo['documentroot'] . '/' . $path); + + $backup_dbs = isset($_POST['backup_dbs']) ? intval($_POST['backup_dbs']) : 0; + $backup_mail = isset($_POST['backup_mail']) ? intval($_POST['backup_mail']) : 0; + $backup_web = isset($_POST['backup_web']) ? intval($_POST['backup_web']) : 0; + + if ($backup_dbs != '1') { + $backup_dbs = '0'; + } + + if ($backup_mail != '1') { + $backup_mail = '0'; + } + + if ($backup_web != '1') { + $backup_web = '0'; + } + + $task_data = array( + 'customerid' => $userinfo['customerid'], + 'uid' => $userinfo['guid'], + 'gid' => $userinfo['guid'], + 'loginname' => $userinfo['loginname'], + 'destdir' => $path, + 'backup_dbs' => $backup_dbs, + 'backup_mail' => $backup_mail, + 'backup_web' => $backup_web + ); + inserttask('20', $task_data); + + standard_success('backupscheduled'); + } else { + + // check whether there is a backup-job for this customer + $sel_stmt = Database::prepare("SELECT * FROM `".TABLE_PANEL_TASKS."` WHERE `type` = '20'"); + Database::pexecute($sel_stmt); + while ($entry = $sel_stmt->fetch()) + { + $data = unserialize($entry['data']); + if ($data['customerid'] == $userinfo['customerid']) { + standard_error('customerhasongoingbackupjob'); + } + } + $pathSelect = makePathfield($userinfo['documentroot'], $userinfo['guid'], $userinfo['guid']); + $backup_data = include_once dirname(__FILE__) . '/lib/formfields/customer/extras/formfield.backup.php'; + $backup_form = htmlform::genHTMLForm($backup_data); + $title = $backup_data['backup']['title']; + $image = $backup_data['backup']['image']; + eval("echo \"" . getTemplate("extras/backup") . "\";"); + } + } +} \ No newline at end of file diff --git a/install/froxlor.sql b/install/froxlor.sql index ae2ec259..5bece437 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -556,7 +556,7 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES ('panel', 'password_special_char_required', '0'), ('panel', 'password_special_char', '!?<>§$%+#=@'), ('panel', 'version', '0.9.35.1'), - ('panel', 'db_version', '201603150'); + ('panel', 'db_version', '201604270'); DROP TABLE IF EXISTS `panel_tasks`; @@ -765,7 +765,8 @@ INSERT INTO `cronjobs_run` (`id`, `module`, `cronfile`, `interval`, `isactive`, (4, 'froxlor/ticket', 'ticketarchive', '1 MONTH', '1', 'cron_ticketarchive'), (5, 'froxlor/reports', 'usage_report', '1 DAY', '1', 'cron_usage_report'), (6, 'froxlor/core', 'mailboxsize', '6 HOUR', '1', 'cron_mailboxsize'), - (7, 'froxlor/letsencrypt', 'letsencrypt', '5 MINUTE', '0', 'cron_letsencrypt'); + (7, 'froxlor/letsencrypt', 'letsencrypt', '5 MINUTE', '0', 'cron_letsencrypt'), + (8, 'froxlor/backup', 'backup', '1 DAY', '1', 'cron_backup'); diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index 2b435653..c34c0dc3 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -3292,3 +3292,21 @@ if (isFroxlorVersion('0.9.35')) { updateToVersion('0.9.35.1'); } + +if (isFroxlorVersion('0.9.35.1') && isDatabaseVersion('201603150')) { + + showUpdateStep("Adding new backup-cron entry"); + $stmt = Database::prepare(" + INSERT INTO `" . TABLE_PANEL_CRONRUNS . "` SET + `module` = 'froxlor/backup', + `cronfile` = 'backup', + `interval` = '1 DAY', + `desc_lng_key` = 'cron_backup', + `lastrun` = 0, + `isactive` = 0" + ); + Database::pexecute($stmt); + lastStepStatus(0); + + updateToDbVersion('201604270'); +} diff --git a/lib/formfields/admin/domains/formfield.domains_edit.php b/lib/formfields/admin/domains/formfield.domains_edit.php index 68ffa009..6837da0a 100644 --- a/lib/formfields/admin/domains/formfield.domains_edit.php +++ b/lib/formfields/admin/domains/formfield.domains_edit.php @@ -84,7 +84,7 @@ return array( 'value' => $result['registration_date'], 'size' => 10 ), - 'termination_date' => array( + 'termination_date' => array( 'label' => $lng['domains']['termination_date'], 'desc' => $lng['panel']['dateformat'], 'type' => 'text', diff --git a/lib/formfields/customer/extras/formfield.backup.php b/lib/formfields/customer/extras/formfield.backup.php new file mode 100644 index 00000000..82c9259b --- /dev/null +++ b/lib/formfields/customer/extras/formfield.backup.php @@ -0,0 +1,70 @@ + (2010-) + * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt + * @package Formfields + * + */ +return array( + 'backup' => array( + 'title' => $lng['extras']['backup'], + 'image' => 'icons/backup_big.png', + 'sections' => array( + 'section_a' => array( + 'title' => $lng['extras']['backup'], + 'image' => 'icons/backup_big.png', + 'fields' => array( + 'path' => array( + 'label' => $lng['panel']['path'], + 'desc' => (Settings::Get('panel.pathedit') != 'Dropdown' ? $lng['panel']['pathDescription'] : null).(isset($pathSelect['note']) ? '
'.$pathSelect['value'] : ''), + 'type' => $pathSelect['type'], + 'select_var' => $pathSelect['value'], + 'value' => $pathSelect['value'] + ), + 'backup_web' => array( + 'label' => $lng['extras']['backup_web'], + 'type' => 'checkbox', + 'values' => array( + array( + 'label' => $lng['panel']['yes'], + 'value' => '1' + ) + ), + 'value' => array('1') + ), + 'backup_mail' => array( + 'label' => $lng['extras']['backup_mail'], + 'type' => 'checkbox', + 'values' => array( + array( + 'label' => $lng['panel']['yes'], + 'value' => '1' + ) + ), + 'value' => array('1') + ), + 'backup_dbs' => array( + 'label' => $lng['extras']['backup_dbs'], + 'type' => 'checkbox', + 'values' => array( + array( + 'label' => $lng['panel']['yes'], + 'value' => '1' + ) + ), + 'value' => array('1') + ) + ) + ) + ) + ) +); diff --git a/lib/functions/filedir/function.createCustomerBackup.php b/lib/functions/filedir/function.createCustomerBackup.php new file mode 100644 index 00000000..20cff168 --- /dev/null +++ b/lib/functions/filedir/function.createCustomerBackup.php @@ -0,0 +1,116 @@ + (2010-) + * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt + * @package Functions + * + */ + +/** + * depending on the give choice, the customers web-data, email-data and databases are being backup'ed + * + * @param array $data + * + * @return void + * + */ +function createCustomerBackup($data = null, $customerdocroot = null, &$cronlog) +{ + $cronlog->logAction(CRON_ACTION, LOG_INFO, 'Creating Backup for user "'.$data['loginname'].'"'); + + // create tmp folder + $tmpdir = makeCorrectDir($data['destdir'] . '/.tmp/'); + $cronlog->logAction(CRON_ACTION, LOG_DEBUG, 'Creating tmp-folder "'.$tmpdir.'"'); + $cronlog->logAction(CRON_ACTION, LOG_DEBUG, 'shell> mkdir -p ' . escapeshellarg($tmpdir)); + safe_exec('mkdir -p ' . escapeshellarg($tmpdir)); + $create_backup_tar = false; + + // MySQL databases + if ($data['backup_dbs'] == 1) { + + $cronlog->logAction(CRON_ACTION, LOG_DEBUG, 'Creating mysql-folder "'.makeCorrectDir($tmpdir . '/mysql').'"'); + $cronlog->logAction(CRON_ACTION, LOG_DEBUG, 'shell> mkdir -p ' . escapeshellarg(makeCorrectDir($tmpdir . '/mysql'))); + safe_exec('mkdir -p ' . escapeshellarg(makeCorrectDir($tmpdir . '/mysql'))); + + // get all customer database-names + $sel_stmt = Database::prepare("SELECT `databasename` FROM `" . TABLE_PANEL_DATABASES . "` WHERE `customerid` = :cid"); + Database::pexecute($sel_stmt, array( + 'cid' => $data['customerid'] + )); + + Database::needRoot(true); + Database::needSqlData(); + $sql_root = Database::getSqlData(); + Database::needRoot(false); + + while ($row = $sel_stmt->fetch()) { + $cronlog->logAction(CRON_ACTION, LOG_DEBUG, 'shell> mysqldump -u ' . escapeshellarg($sql_root['user']) . ' -pXXXXX ' . $row['databasename'] . ' > ' . makeCorrectFile($tmpdir . '/mysql/' . $row['databasename'] . '_' . date('YmdHi', time()) . '.sql')); + $bool_false = false; + safe_exec('mysqldump -u ' . escapeshellarg($sql_root['user']) . ' -p' . $sql_root['passwd'] . ' ' . $row['databasename'] . ' > ' . makeCorrectFile($tmpdir . '/mysql/' . $row['databasename'] . '_' . date('YmdHi', time()) . '.sql'), $bool_false, array('>')); + $create_backup_tar = true; + } + + unset($sql_root); + } + + // E-mail data + if ($data['backup_mail'] == 1) { + + $cronlog->logAction(CRON_ACTION, LOG_DEBUG, 'Creating mail-folder "'.makeCorrectDir($tmpdir . '/mail').'"'); + safe_exec('mkdir -p ' . escapeshellarg(makeCorrectDir($tmpdir . '/mail'))); + + // get all customer mail-accounts + $sel_stmt = Database::prepare("SELECT CONCAT(`homedir`, `maildir`) as acc_path FROM `" . TABLE_MAIL_USERS . "` WHERE `customerid` = :cid"); + Database::pexecute($sel_stmt, array( + 'cid' => $data['customerid'] + )); + + $tar_file_list = ""; + while ($row = $sel_stmt->fetch()) { + $tar_file_list .= $row['acc_path'] . " "; + } + + if (! empty($tar_file_list)) { + $cronlog->logAction(CRON_ACTION, LOG_DEBUG, 'shell> tar cfvz ' . escapeshellarg(makeCorrectFile($tmpdir . '/mail/' . $data['loginname'] . '-mail.tar.gz')) . ' ' . escapeshellarg(trim($tar_file_list))); + safe_exec('tar cfz ' . escapeshellarg(makeCorrectFile($tmpdir . '/mail/' . $data['loginname'] . '-mail.tar.gz')) . ' ' . escapeshellarg(trim($tar_file_list))); + $create_backup_tar = true; + } + } + + // Web data + if ($data['backup_web'] == 1) { + + $cronlog->logAction(CRON_ACTION, LOG_DEBUG, 'Creating web-folder "'.makeCorrectDir($tmpdir . '/web').'"'); + safe_exec('mkdir -p ' . escapeshellarg(makeCorrectDir($tmpdir . '/web'))); + $cronlog->logAction(CRON_ACTION, LOG_DEBUG, 'shell> tar cfvz ' . escapeshellarg(makeCorrectFile($tmpdir . '/web/' . $data['loginname'] . '-web.tar.gz')) . ' --exclude ' . escapeshellarg($tmpdir) .' ' . escapeshellarg($customerdocroot)); + safe_exec('tar cfz ' . escapeshellarg(makeCorrectFile($tmpdir . '/web/' . $data['loginname'] . '-web.tar.gz')) . ' --exclude ' . escapeshellarg($tmpdir) .' ' . escapeshellarg($customerdocroot)); + $create_backup_tar = true; + } + + if ($create_backup_tar) + { + $backup_file = makeCorrectFile($tmpdir . '/' . $data['loginname'] . '-backup_' . date('YmdHi', time()) . '.tar.gz'); + $cronlog->logAction(CRON_ACTION, LOG_INFO, 'Creating backup-file "'.$backup_file.'"'); + // pack all archives in tmp-dir to one + $cronlog->logAction(CRON_ACTION, LOG_DEBUG, 'shell> tar cfvz ' . escapeshellarg($backup_file) . ' ' . escapeshellarg($tmpdir)); + safe_exec('tar cfz ' . escapeshellarg($backup_file) . ' ' . escapeshellarg($tmpdir)); + // move to destination directory + $cronlog->logAction(CRON_ACTION, LOG_DEBUG, 'shell> mv ' . escapeshellarg($backup_file) . ' ' . escapeshellarg($data['destdir'])); + safe_exec('mv ' . escapeshellarg($backup_file) . ' ' . escapeshellarg($data['destdir'])); + // remove tmp-files + $cronlog->logAction(CRON_ACTION, LOG_DEBUG, 'shell> rm -rf '.escapeshellarg($tmpdir)); + safe_exec('rm -rf '.escapeshellarg($tmpdir)); + // set owner to customer + $cronlog->logAction(CRON_ACTION, LOG_DEBUG, 'shell> chown -R ' . (int)$data['uid'] . ':' . (int)$data['gid'] . ' ' . escapeshellarg($data['destdir'])); + safe_exec('chown -R ' . (int)$data['uid'] . ':' . (int)$data['gid'] . ' ' . escapeshellarg($data['destdir'])); + } +} diff --git a/lib/functions/froxlor/function.inserttask.php b/lib/functions/froxlor/function.inserttask.php index 41f7750b..3f2cdbde 100644 --- a/lib/functions/froxlor/function.inserttask.php +++ b/lib/functions/froxlor/function.inserttask.php @@ -101,5 +101,10 @@ function inserttask($type, $param1 = '', $param2 = '', $param3 = '', $param4 = ' $data = serialize($data); Database::pexecute($ins_stmt, array('type' => '8', 'data' => $data)); + } elseif ($type == '20' + && is_array($param1) + ) { + $data = serialize($param1); + Database::pexecute($ins_stmt, array('type' => '20', 'data' => $data)); } } diff --git a/lib/navigation/00.froxlor.main.php b/lib/navigation/00.froxlor.main.php index eb7e7d21..cb6f0401 100644 --- a/lib/navigation/00.froxlor.main.php +++ b/lib/navigation/00.froxlor.main.php @@ -124,11 +124,15 @@ return array ( 'url' => 'customer_extras.php?page=htaccess', 'label' => $lng['menue']['extras']['pathoptions'], ), - array ( - 'url' => 'customer_logger.php?page=log', - 'label' => $lng['menue']['logger']['logger'], - 'show_element' => ( Settings::Get('logger.enabled') == true ) - ), + array ( + 'url' => 'customer_logger.php?page=log', + 'label' => $lng['menue']['logger']['logger'], + 'show_element' => ( Settings::Get('logger.enabled') == true ) + ), + array ( + 'url' => 'customer_extras.php?page=backup', + 'label' => $lng['menue']['extras']['backup'], + ), ), ), 'traffic' => array ( diff --git a/lng/english.lng.php b/lng/english.lng.php index 242ff7c5..8e0d72f0 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -1974,3 +1974,12 @@ $lng['domains']['termination_date_overview'] = 'canceled until '; $lng['panel']['set'] = 'Apply'; $lng['customer']['selectserveralias_addinfo'] = 'This option can be set when editing the domain. Its initial value is inherited from the parent-domain.'; $lng['error']['mailaccistobedeleted'] = "Another account with the same name (%s) is currently being deleted and can therefore not be added at this moment."; + +$lng['menue']['extras']['backup'] = 'Backup'; +$lng['extras']['backup'] = 'Create backup'; +$lng['extras']['backup_web'] = 'Backup web-data'; +$lng['extras']['backup_mail'] = 'Backup mail-data'; +$lng['extras']['backup_dbs'] = 'Backup databases'; +$lng['error']['customerhasongoingbackupjob'] = 'There is already a backup job waiting to be processed, please be patient.'; +$lng['success']['backupscheduled'] = 'Your backup job has been scheduled. Please wait for it to be processed'; +$lng['crondesc']['cron_backup'] = 'Process backup jobs'; diff --git a/lng/german.lng.php b/lng/german.lng.php index 38815cc9..6fe024b2 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -1627,3 +1627,12 @@ $lng['domains']['termination_date_overview'] = 'gekündigt zum '; $lng['panel']['set'] = 'Setzen'; $lng['customer']['selectserveralias_addinfo'] = 'Diese Option steht beim Bearbeiten der Domain zur Verfügung. Als Initial-Wert wird die Einstellung der Hauptdomain vererbt.'; $lng['error']['mailaccistobedeleted'] = "Ein vorheriges Konto mit dem gleichen Namen (%s) wird aktuell noch gelöscht und kann daher derzeit nicht angelegt werden"; + +$lng['menue']['extras']['backup'] = 'Sicherung'; +$lng['extras']['backup'] = 'Sicherung erstellen'; +$lng['extras']['backup_web'] = 'Web-Daten sichern'; +$lng['extras']['backup_mail'] = 'E-Mail Daten sichern'; +$lng['extras']['backup_dbs'] = 'Datenbanken sichern'; +$lng['error']['customerhasongoingbackupjob'] = 'Es gibt noch einen austehenden Backup-Job. Bitte haben Sie etwas Geduld.'; +$lng['success']['backupscheduled'] = 'Ihre Sicherung wurde erfolgreich geplant. Bitte warten Sie nun, bis diese abgearbeitet wurde.'; +$lng['crondesc']['cron_backup'] = 'Ausstehende Sicherungen erstellen'; diff --git a/scripts/jobs/cron_backup.php b/scripts/jobs/cron_backup.php new file mode 100644 index 00000000..abecaca4 --- /dev/null +++ b/scripts/jobs/cron_backup.php @@ -0,0 +1,108 @@ + + * @author Froxlor team (2010-) + * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt + * @package Cron + * + * @since 0.9.35.1 + * + */ + +// Check Traffic-Lock +if (function_exists('pcntl_fork')) { + $BackupLock = makeCorrectFile(dirname($lockfile)."/froxlor_cron_backup.lock"); + if (file_exists($BackupLock) + && is_numeric($BackupPid=file_get_contents($BackupLock)) + ) { + if (function_exists('posix_kill')) { + $BackupPidStatus = @posix_kill($BackupPid,0); + } else { + system("kill -CHLD " . $BackupPid . " 1> /dev/null 2> /dev/null", $BackupPidStatus); + $BackupPidStatus = $BackupPidStatus ? false : true; + } + if ($BackupPidStatus) { + $cronlog->logAction(CRON_ACTION, LOG_INFO, 'Backup run already in progress'); + return 1; + } + } + // Create Backup Log and Fork + // We close the database - connection before we fork, so we don't share resources with the child + Database::needRoot(false); // this forces the connection to be set to null + $BackupPid = pcntl_fork(); + // Parent + if ($BackupPid) { + file_put_contents($BackupLock, $BackupPid); + // unnecessary to recreate database connection here + return 0; + + } + //Child + elseif ($BackupPid == 0) { + posix_setsid(); + fclose($debugHandler); + // re-create db + Database::needRoot(false); + } + //Fork failed + else { + return 1; + } + +} else { + if (extension_loaded('pcntl')) { + $msg = "PHP compiled with pcntl but pcntl_fork function is not available."; + } else { + $msg = "PHP compiled without pcntl."; + } + $cronlog->logAction(CRON_ACTION, LOG_WARNING, $msg." Not forking backup-cron, this may take a long time!"); +} + +$cronlog->logAction(CRON_ACTION, LOG_INFO, 'cron_backup: started - creating customer backup'); + +$result_tasks_stmt = Database::query(" + SELECT * FROM `" . TABLE_PANEL_TASKS . "` WHERE `type` = '20' ORDER BY `id` ASC +"); + +$del_stmt = Database::prepare("DELETE FROM `" . TABLE_PANEL_TASKS . "` WHERE `id` = :id"); + +while ($row = $result_tasks_stmt->fetch(PDO::FETCH_ASSOC)) { + + if ($row['data'] != '') { + $row['data'] = unserialize($row['data']); + } + + if (is_array($row['data'])) { + + if (isset($row['data']['customerid']) + && isset($row['data']['loginname']) + && isset($row['data']['destdir']) + ) { + $row['data']['destdir'] = makeCorrectDir($row['data']['destdir']); + $customerdocroot = makeCorrectDir(Settings::Get('system.documentroot_prefix').'/'.$row['data']['loginname'].'/'); + + if (!file_exists($row['data']['destdir']) + && $row['data']['destdir'] != '/' + && $row['data']['destdir'] != Settings::Get('system.documentroot_prefix') + && $row['data']['destdir'] != $customerdocroot + ) { + $cronlog->logAction(CRON_ACTION, LOG_NOTICE, 'Creating backup-destination path for customer: ' . escapeshellarg($row['data']['destdir'])); + safe_exec('mkdir -p '.escapeshellarg($row['data']['destdir'])); + } + + createCustomerBackup($row['data'], $customerdocroot, $cronlog); + } + } + + // remove entry + Database::pexecute($del_stmt, array('id' => $row['id'])); +} diff --git a/scripts/jobs/cron_tasks.php b/scripts/jobs/cron_tasks.php index a614613b..b452c2d4 100644 --- a/scripts/jobs/cron_tasks.php +++ b/scripts/jobs/cron_tasks.php @@ -30,8 +30,9 @@ require_once makeCorrectFile(dirname(__FILE__) . '/cron_tasks.inc.http.35.nginx_ * LOOK INTO TASKS TABLE TO SEE IF THERE ARE ANY UNDONE JOBS */ $cronlog->logAction(CRON_ACTION, LOG_INFO, "cron_tasks: Searching for tasks to do"); +// no type 99 (regenerate cron.d-file) and no type 20 (customer backup) $result_tasks_stmt = Database::query(" - SELECT `id`, `type`, `data` FROM `" . TABLE_PANEL_TASKS . "` WHERE `type` <> '99' ORDER BY `id` ASC + SELECT `id`, `type`, `data` FROM `" . TABLE_PANEL_TASKS . "` WHERE `type` <> '99' AND `type` <> '20' ORDER BY `id` ASC "); $num_results = Database::num_rows(); $resultIDs = array(); @@ -395,7 +396,7 @@ while ($row = $result_tasks_stmt->fetch(PDO::FETCH_ASSOC)) { } } } - } + } } } } diff --git a/templates/Sparkle/customer/extras/backup.tpl b/templates/Sparkle/customer/extras/backup.tpl new file mode 100644 index 00000000..6e71243e --- /dev/null +++ b/templates/Sparkle/customer/extras/backup.tpl @@ -0,0 +1,26 @@ +$header +
+
+

+ {$title}  + {$title} +

+
+ +
+ +
+ + + + + + + {$backup_form} +
+
+ +
+ +
+$footer From a26ebb375bde7c82b48270879b566c83e98af277 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 28 Apr 2016 09:42:16 +0200 Subject: [PATCH 0018/1335] set db version for changes Signed-off-by: Michael Kaufmann (d00p) --- lib/version.inc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/version.inc.php b/lib/version.inc.php index 39283a8f..2d17e6d1 100644 --- a/lib/version.inc.php +++ b/lib/version.inc.php @@ -19,7 +19,7 @@ $version = '0.9.35.1'; // Database version (YYYYMMDDC where C is a daily counter) -$dbversion = '201603150'; +$dbversion = '201604270'; // Distribution branding-tag (used for Debian etc.) $branding = ''; From ef7da5380659c0208600db6a537b744f200a8472 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 28 Apr 2016 10:42:23 +0200 Subject: [PATCH 0019/1335] change directory before creating tarball to avoid having complete paths in it Signed-off-by: Michael Kaufmann (d00p) --- .../filedir/function.createCustomerBackup.php | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/lib/functions/filedir/function.createCustomerBackup.php b/lib/functions/filedir/function.createCustomerBackup.php index 20cff168..64f277e7 100644 --- a/lib/functions/filedir/function.createCustomerBackup.php +++ b/lib/functions/filedir/function.createCustomerBackup.php @@ -32,7 +32,7 @@ function createCustomerBackup($data = null, $customerdocroot = null, &$cronlog) $cronlog->logAction(CRON_ACTION, LOG_DEBUG, 'Creating tmp-folder "'.$tmpdir.'"'); $cronlog->logAction(CRON_ACTION, LOG_DEBUG, 'shell> mkdir -p ' . escapeshellarg($tmpdir)); safe_exec('mkdir -p ' . escapeshellarg($tmpdir)); - $create_backup_tar = false; + $create_backup_tar_data = ""; // MySQL databases if ($data['backup_dbs'] == 1) { @@ -52,11 +52,16 @@ function createCustomerBackup($data = null, $customerdocroot = null, &$cronlog) $sql_root = Database::getSqlData(); Database::needRoot(false); + $has_dbs = false; while ($row = $sel_stmt->fetch()) { $cronlog->logAction(CRON_ACTION, LOG_DEBUG, 'shell> mysqldump -u ' . escapeshellarg($sql_root['user']) . ' -pXXXXX ' . $row['databasename'] . ' > ' . makeCorrectFile($tmpdir . '/mysql/' . $row['databasename'] . '_' . date('YmdHi', time()) . '.sql')); $bool_false = false; safe_exec('mysqldump -u ' . escapeshellarg($sql_root['user']) . ' -p' . $sql_root['passwd'] . ' ' . $row['databasename'] . ' > ' . makeCorrectFile($tmpdir . '/mysql/' . $row['databasename'] . '_' . date('YmdHi', time()) . '.sql'), $bool_false, array('>')); - $create_backup_tar = true; + $has_dbs = true; + } + + if ($has_dbs) { + $create_backup_tar_data .= makeCorrectDir($tmpdir . '/mysql'); } unset($sql_root); @@ -80,9 +85,9 @@ function createCustomerBackup($data = null, $customerdocroot = null, &$cronlog) } if (! empty($tar_file_list)) { - $cronlog->logAction(CRON_ACTION, LOG_DEBUG, 'shell> tar cfvz ' . escapeshellarg(makeCorrectFile($tmpdir . '/mail/' . $data['loginname'] . '-mail.tar.gz')) . ' ' . escapeshellarg(trim($tar_file_list))); - safe_exec('tar cfz ' . escapeshellarg(makeCorrectFile($tmpdir . '/mail/' . $data['loginname'] . '-mail.tar.gz')) . ' ' . escapeshellarg(trim($tar_file_list))); - $create_backup_tar = true; + $cronlog->logAction(CRON_ACTION, LOG_DEBUG, 'shell> tar -C '.escapeshellarg($tmpdir).' cfvz ' . escapeshellarg(makeCorrectFile($tmpdir . '/mail/' . $data['loginname'] . '-mail.tar.gz')) . ' ' . escapeshellarg(trim($tar_file_list))); + safe_exec('tar -C '.escapeshellarg($tmpdir).' cfz ' . escapeshellarg(makeCorrectFile($tmpdir . '/mail/' . $data['loginname'] . '-mail.tar.gz')) . ' ' . escapeshellarg(trim($tar_file_list))); + $create_backup_tar_data .= makeCorrectDir($tmpdir . '/mail'); } } @@ -91,18 +96,18 @@ function createCustomerBackup($data = null, $customerdocroot = null, &$cronlog) $cronlog->logAction(CRON_ACTION, LOG_DEBUG, 'Creating web-folder "'.makeCorrectDir($tmpdir . '/web').'"'); safe_exec('mkdir -p ' . escapeshellarg(makeCorrectDir($tmpdir . '/web'))); - $cronlog->logAction(CRON_ACTION, LOG_DEBUG, 'shell> tar cfvz ' . escapeshellarg(makeCorrectFile($tmpdir . '/web/' . $data['loginname'] . '-web.tar.gz')) . ' --exclude ' . escapeshellarg($tmpdir) .' ' . escapeshellarg($customerdocroot)); - safe_exec('tar cfz ' . escapeshellarg(makeCorrectFile($tmpdir . '/web/' . $data['loginname'] . '-web.tar.gz')) . ' --exclude ' . escapeshellarg($tmpdir) .' ' . escapeshellarg($customerdocroot)); - $create_backup_tar = true; + $cronlog->logAction(CRON_ACTION, LOG_DEBUG, 'shell> tar -C '.escapeshellarg($tmpdir).' cfz ' . escapeshellarg(makeCorrectFile($tmpdir . '/web/' . $data['loginname'] . '-web.tar.gz')) . ' --exclude ' . escapeshellarg($tmpdir) .' ' . escapeshellarg($customerdocroot)); + safe_exec('tar -C '.escapeshellarg($tmpdir).' cfz ' . escapeshellarg(makeCorrectFile($tmpdir . '/web/' . $data['loginname'] . '-web.tar.gz')) . ' --exclude ' . escapeshellarg($tmpdir) .' ' . escapeshellarg($customerdocroot)); + $create_backup_tar_data .= makeCorrectDir($tmpdir . '/web'); } - if ($create_backup_tar) + if (!empty($create_backup_tar_data)) { $backup_file = makeCorrectFile($tmpdir . '/' . $data['loginname'] . '-backup_' . date('YmdHi', time()) . '.tar.gz'); $cronlog->logAction(CRON_ACTION, LOG_INFO, 'Creating backup-file "'.$backup_file.'"'); // pack all archives in tmp-dir to one - $cronlog->logAction(CRON_ACTION, LOG_DEBUG, 'shell> tar cfvz ' . escapeshellarg($backup_file) . ' ' . escapeshellarg($tmpdir)); - safe_exec('tar cfz ' . escapeshellarg($backup_file) . ' ' . escapeshellarg($tmpdir)); + $cronlog->logAction(CRON_ACTION, LOG_DEBUG, 'shell> tar -C '.escapeshellarg($tmpdir).' cfz ' . escapeshellarg($backup_file) . ' ' . escapeshellarg($create_backup_tar_data)); + safe_exec('tar -C '.escapeshellarg($tmpdir).' cfz ' . escapeshellarg($backup_file) . ' ' . escapeshellarg($create_backup_tar_data)); // move to destination directory $cronlog->logAction(CRON_ACTION, LOG_DEBUG, 'shell> mv ' . escapeshellarg($backup_file) . ' ' . escapeshellarg($data['destdir'])); safe_exec('mv ' . escapeshellarg($backup_file) . ' ' . escapeshellarg($data['destdir'])); From 9c9bbb81dee8d9d7a7adc81311ffde1a8fe5a6b3 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 28 Apr 2016 11:11:45 +0200 Subject: [PATCH 0020/1335] fix tar -C parameter and create_backup_tar_data list Signed-off-by: Michael Kaufmann (d00p) --- .../filedir/function.createCustomerBackup.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/functions/filedir/function.createCustomerBackup.php b/lib/functions/filedir/function.createCustomerBackup.php index 64f277e7..03744742 100644 --- a/lib/functions/filedir/function.createCustomerBackup.php +++ b/lib/functions/filedir/function.createCustomerBackup.php @@ -61,7 +61,7 @@ function createCustomerBackup($data = null, $customerdocroot = null, &$cronlog) } if ($has_dbs) { - $create_backup_tar_data .= makeCorrectDir($tmpdir . '/mysql'); + $create_backup_tar_data .= makeCorrectDir($tmpdir . '/mysql')." "; } unset($sql_root); @@ -85,9 +85,9 @@ function createCustomerBackup($data = null, $customerdocroot = null, &$cronlog) } if (! empty($tar_file_list)) { - $cronlog->logAction(CRON_ACTION, LOG_DEBUG, 'shell> tar -C '.escapeshellarg($tmpdir).' cfvz ' . escapeshellarg(makeCorrectFile($tmpdir . '/mail/' . $data['loginname'] . '-mail.tar.gz')) . ' ' . escapeshellarg(trim($tar_file_list))); - safe_exec('tar -C '.escapeshellarg($tmpdir).' cfz ' . escapeshellarg(makeCorrectFile($tmpdir . '/mail/' . $data['loginname'] . '-mail.tar.gz')) . ' ' . escapeshellarg(trim($tar_file_list))); - $create_backup_tar_data .= makeCorrectDir($tmpdir . '/mail'); + $cronlog->logAction(CRON_ACTION, LOG_DEBUG, 'shell> tar cfvz ' . escapeshellarg(makeCorrectFile($tmpdir . '/mail/' . $data['loginname'] . '-mail.tar.gz')) . ' ' . escapeshellarg(trim($tar_file_list)).' -C '.escapeshellarg($tmpdir)); + safe_exec('tar cfz ' . escapeshellarg(makeCorrectFile($tmpdir . '/mail/' . $data['loginname'] . '-mail.tar.gz')) . ' ' . escapeshellarg(trim($tar_file_list)).' -C '.escapeshellarg($tmpdir)); + $create_backup_tar_data .= makeCorrectDir($tmpdir . '/mail')." "; } } @@ -96,9 +96,9 @@ function createCustomerBackup($data = null, $customerdocroot = null, &$cronlog) $cronlog->logAction(CRON_ACTION, LOG_DEBUG, 'Creating web-folder "'.makeCorrectDir($tmpdir . '/web').'"'); safe_exec('mkdir -p ' . escapeshellarg(makeCorrectDir($tmpdir . '/web'))); - $cronlog->logAction(CRON_ACTION, LOG_DEBUG, 'shell> tar -C '.escapeshellarg($tmpdir).' cfz ' . escapeshellarg(makeCorrectFile($tmpdir . '/web/' . $data['loginname'] . '-web.tar.gz')) . ' --exclude ' . escapeshellarg($tmpdir) .' ' . escapeshellarg($customerdocroot)); - safe_exec('tar -C '.escapeshellarg($tmpdir).' cfz ' . escapeshellarg(makeCorrectFile($tmpdir . '/web/' . $data['loginname'] . '-web.tar.gz')) . ' --exclude ' . escapeshellarg($tmpdir) .' ' . escapeshellarg($customerdocroot)); - $create_backup_tar_data .= makeCorrectDir($tmpdir . '/web'); + $cronlog->logAction(CRON_ACTION, LOG_DEBUG, 'shell> tar cfz ' . escapeshellarg(makeCorrectFile($tmpdir . '/web/' . $data['loginname'] . '-web.tar.gz')) . ' --exclude ' . escapeshellarg($tmpdir) .' ' . escapeshellarg($customerdocroot).' -C '.escapeshellarg($tmpdir)); + safe_exec('tar cfz ' . escapeshellarg(makeCorrectFile($tmpdir . '/web/' . $data['loginname'] . '-web.tar.gz')) . ' --exclude ' . escapeshellarg($tmpdir) .' ' . escapeshellarg($customerdocroot).' -C '.escapeshellarg($tmpdir)); + $create_backup_tar_data .= makeCorrectDir($tmpdir . '/web')." "; } if (!empty($create_backup_tar_data)) @@ -106,8 +106,8 @@ function createCustomerBackup($data = null, $customerdocroot = null, &$cronlog) $backup_file = makeCorrectFile($tmpdir . '/' . $data['loginname'] . '-backup_' . date('YmdHi', time()) . '.tar.gz'); $cronlog->logAction(CRON_ACTION, LOG_INFO, 'Creating backup-file "'.$backup_file.'"'); // pack all archives in tmp-dir to one - $cronlog->logAction(CRON_ACTION, LOG_DEBUG, 'shell> tar -C '.escapeshellarg($tmpdir).' cfz ' . escapeshellarg($backup_file) . ' ' . escapeshellarg($create_backup_tar_data)); - safe_exec('tar -C '.escapeshellarg($tmpdir).' cfz ' . escapeshellarg($backup_file) . ' ' . escapeshellarg($create_backup_tar_data)); + $cronlog->logAction(CRON_ACTION, LOG_DEBUG, 'shell> tar cfz ' . escapeshellarg($backup_file) . ' ' . escapeshellarg(trim($create_backup_tar_data)).' -C '.escapeshellarg($tmpdir)); + safe_exec('tar cfz ' . escapeshellarg($backup_file) . ' ' . escapeshellarg(trim($create_backup_tar_data)).' -C '.escapeshellarg($tmpdir)); // move to destination directory $cronlog->logAction(CRON_ACTION, LOG_DEBUG, 'shell> mv ' . escapeshellarg($backup_file) . ' ' . escapeshellarg($data['destdir'])); safe_exec('mv ' . escapeshellarg($backup_file) . ' ' . escapeshellarg($data['destdir'])); From 05974de4d5594d2509a3a566bca7a2a8e1102824 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 28 Apr 2016 11:30:19 +0200 Subject: [PATCH 0021/1335] fix tar -C parameter again... Signed-off-by: Michael Kaufmann (d00p) --- lib/functions/filedir/function.createCustomerBackup.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/functions/filedir/function.createCustomerBackup.php b/lib/functions/filedir/function.createCustomerBackup.php index 03744742..f9ed4fd0 100644 --- a/lib/functions/filedir/function.createCustomerBackup.php +++ b/lib/functions/filedir/function.createCustomerBackup.php @@ -74,14 +74,16 @@ function createCustomerBackup($data = null, $customerdocroot = null, &$cronlog) safe_exec('mkdir -p ' . escapeshellarg(makeCorrectDir($tmpdir . '/mail'))); // get all customer mail-accounts - $sel_stmt = Database::prepare("SELECT CONCAT(`homedir`, `maildir`) as acc_path FROM `" . TABLE_MAIL_USERS . "` WHERE `customerid` = :cid"); + $sel_stmt = Database::prepare("SELECT `homedir`, CONCAT(`homedir`, `maildir`) as acc_path FROM `" . TABLE_MAIL_USERS . "` WHERE `customerid` = :cid"); Database::pexecute($sel_stmt, array( 'cid' => $data['customerid'] )); $tar_file_list = ""; + $mail_homedir = ""; while ($row = $sel_stmt->fetch()) { $tar_file_list .= $row['acc_path'] . " "; + $mail_homedir = $row['homedir']; } if (! empty($tar_file_list)) { @@ -96,8 +98,8 @@ function createCustomerBackup($data = null, $customerdocroot = null, &$cronlog) $cronlog->logAction(CRON_ACTION, LOG_DEBUG, 'Creating web-folder "'.makeCorrectDir($tmpdir . '/web').'"'); safe_exec('mkdir -p ' . escapeshellarg(makeCorrectDir($tmpdir . '/web'))); - $cronlog->logAction(CRON_ACTION, LOG_DEBUG, 'shell> tar cfz ' . escapeshellarg(makeCorrectFile($tmpdir . '/web/' . $data['loginname'] . '-web.tar.gz')) . ' --exclude ' . escapeshellarg($tmpdir) .' ' . escapeshellarg($customerdocroot).' -C '.escapeshellarg($tmpdir)); - safe_exec('tar cfz ' . escapeshellarg(makeCorrectFile($tmpdir . '/web/' . $data['loginname'] . '-web.tar.gz')) . ' --exclude ' . escapeshellarg($tmpdir) .' ' . escapeshellarg($customerdocroot).' -C '.escapeshellarg($tmpdir)); + $cronlog->logAction(CRON_ACTION, LOG_DEBUG, 'shell> tar cfz ' . escapeshellarg(makeCorrectFile($tmpdir . '/web/' . $data['loginname'] . '-web.tar.gz')) . ' --exclude ' . escapeshellarg($tmpdir) .' ' . escapeshellarg($customerdocroot).' -C '.escapeshellarg($customerdocroot)); + safe_exec('tar cfz ' . escapeshellarg(makeCorrectFile($tmpdir . '/web/' . $data['loginname'] . '-web.tar.gz')) . ' --exclude ' . escapeshellarg($tmpdir) .' ' . escapeshellarg($customerdocroot).' -C '.escapeshellarg($customerdocroot)); $create_backup_tar_data .= makeCorrectDir($tmpdir . '/web')." "; } From fb555027fd9c29d7c313583d3c3e5525a41faf6c Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 28 Apr 2016 11:47:48 +0200 Subject: [PATCH 0022/1335] fix -C parameter and --exclude parameter for good now Signed-off-by: Michael Kaufmann (d00p) --- .../filedir/function.createCustomerBackup.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/functions/filedir/function.createCustomerBackup.php b/lib/functions/filedir/function.createCustomerBackup.php index f9ed4fd0..c5c20eb2 100644 --- a/lib/functions/filedir/function.createCustomerBackup.php +++ b/lib/functions/filedir/function.createCustomerBackup.php @@ -61,7 +61,7 @@ function createCustomerBackup($data = null, $customerdocroot = null, &$cronlog) } if ($has_dbs) { - $create_backup_tar_data .= makeCorrectDir($tmpdir . '/mysql')." "; + $create_backup_tar_data .= './mysql '; } unset($sql_root); @@ -87,9 +87,9 @@ function createCustomerBackup($data = null, $customerdocroot = null, &$cronlog) } if (! empty($tar_file_list)) { - $cronlog->logAction(CRON_ACTION, LOG_DEBUG, 'shell> tar cfvz ' . escapeshellarg(makeCorrectFile($tmpdir . '/mail/' . $data['loginname'] . '-mail.tar.gz')) . ' ' . escapeshellarg(trim($tar_file_list)).' -C '.escapeshellarg($tmpdir)); - safe_exec('tar cfz ' . escapeshellarg(makeCorrectFile($tmpdir . '/mail/' . $data['loginname'] . '-mail.tar.gz')) . ' ' . escapeshellarg(trim($tar_file_list)).' -C '.escapeshellarg($tmpdir)); - $create_backup_tar_data .= makeCorrectDir($tmpdir . '/mail')." "; + $cronlog->logAction(CRON_ACTION, LOG_DEBUG, 'shell> tar cfvz ' . escapeshellarg(makeCorrectFile($tmpdir . '/mail/' . $data['loginname'] . '-mail.tar.gz')) . ' ' . escapeshellarg(trim($tar_file_list)).' -C '.escapeshellarg($mail_homedir)); + safe_exec('tar cfz ' . escapeshellarg(makeCorrectFile($tmpdir . '/mail/' . $data['loginname'] . '-mail.tar.gz')) . ' ' . escapeshellarg(trim($tar_file_list)).' -C '.escapeshellarg($mail_homedir)); + $create_backup_tar_data .= './mail '; } } @@ -98,9 +98,9 @@ function createCustomerBackup($data = null, $customerdocroot = null, &$cronlog) $cronlog->logAction(CRON_ACTION, LOG_DEBUG, 'Creating web-folder "'.makeCorrectDir($tmpdir . '/web').'"'); safe_exec('mkdir -p ' . escapeshellarg(makeCorrectDir($tmpdir . '/web'))); - $cronlog->logAction(CRON_ACTION, LOG_DEBUG, 'shell> tar cfz ' . escapeshellarg(makeCorrectFile($tmpdir . '/web/' . $data['loginname'] . '-web.tar.gz')) . ' --exclude ' . escapeshellarg($tmpdir) .' ' . escapeshellarg($customerdocroot).' -C '.escapeshellarg($customerdocroot)); - safe_exec('tar cfz ' . escapeshellarg(makeCorrectFile($tmpdir . '/web/' . $data['loginname'] . '-web.tar.gz')) . ' --exclude ' . escapeshellarg($tmpdir) .' ' . escapeshellarg($customerdocroot).' -C '.escapeshellarg($customerdocroot)); - $create_backup_tar_data .= makeCorrectDir($tmpdir . '/web')." "; + $cronlog->logAction(CRON_ACTION, LOG_DEBUG, 'shell> tar cfz ' . escapeshellarg(makeCorrectFile($tmpdir . '/web/' . $data['loginname'] . '-web.tar.gz')) . ' --exclude=' . escapeshellarg($tmpdir.'/*') .' --exclude=' . escapeshellarg($tmpdir) .' ' . escapeshellarg($customerdocroot).' -C '.escapeshellarg($customerdocroot)); + safe_exec('tar cfz ' . escapeshellarg(makeCorrectFile($tmpdir . '/web/' . $data['loginname'] . '-web.tar.gz')) . ' --exclude=' . escapeshellarg($tmpdir.'/*') .' --exclude=' . escapeshellarg($tmpdir) .' ' . escapeshellarg($customerdocroot).' -C '.escapeshellarg($customerdocroot)); + $create_backup_tar_data .= './web '; } if (!empty($create_backup_tar_data)) @@ -108,8 +108,8 @@ function createCustomerBackup($data = null, $customerdocroot = null, &$cronlog) $backup_file = makeCorrectFile($tmpdir . '/' . $data['loginname'] . '-backup_' . date('YmdHi', time()) . '.tar.gz'); $cronlog->logAction(CRON_ACTION, LOG_INFO, 'Creating backup-file "'.$backup_file.'"'); // pack all archives in tmp-dir to one - $cronlog->logAction(CRON_ACTION, LOG_DEBUG, 'shell> tar cfz ' . escapeshellarg($backup_file) . ' ' . escapeshellarg(trim($create_backup_tar_data)).' -C '.escapeshellarg($tmpdir)); - safe_exec('tar cfz ' . escapeshellarg($backup_file) . ' ' . escapeshellarg(trim($create_backup_tar_data)).' -C '.escapeshellarg($tmpdir)); + $cronlog->logAction(CRON_ACTION, LOG_DEBUG, 'shell> tar cfz ' . escapeshellarg($backup_file) . ' -C '.escapeshellarg($tmpdir).' '.trim($create_backup_tar_data)); + safe_exec('tar cfz ' . escapeshellarg($backup_file) . ' -C '.escapeshellarg($tmpdir).' '.trim($create_backup_tar_data)); // move to destination directory $cronlog->logAction(CRON_ACTION, LOG_DEBUG, 'shell> mv ' . escapeshellarg($backup_file) . ' ' . escapeshellarg($data['destdir'])); safe_exec('mv ' . escapeshellarg($backup_file) . ' ' . escapeshellarg($data['destdir'])); From 9bfd5eb17eb0556658fad7bbd434267a4f42654e Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 28 Apr 2016 12:58:53 +0200 Subject: [PATCH 0023/1335] --exclude is relativ to path given via -C parameter Signed-off-by: Michael Kaufmann (d00p) --- lib/functions/filedir/function.createCustomerBackup.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/functions/filedir/function.createCustomerBackup.php b/lib/functions/filedir/function.createCustomerBackup.php index c5c20eb2..2375081d 100644 --- a/lib/functions/filedir/function.createCustomerBackup.php +++ b/lib/functions/filedir/function.createCustomerBackup.php @@ -74,7 +74,7 @@ function createCustomerBackup($data = null, $customerdocroot = null, &$cronlog) safe_exec('mkdir -p ' . escapeshellarg(makeCorrectDir($tmpdir . '/mail'))); // get all customer mail-accounts - $sel_stmt = Database::prepare("SELECT `homedir`, CONCAT(`homedir`, `maildir`) as acc_path FROM `" . TABLE_MAIL_USERS . "` WHERE `customerid` = :cid"); + $sel_stmt = Database::prepare("SELECT `homedir`, `maildir` FROM `" . TABLE_MAIL_USERS . "` WHERE `customerid` = :cid"); Database::pexecute($sel_stmt, array( 'cid' => $data['customerid'] )); @@ -82,7 +82,7 @@ function createCustomerBackup($data = null, $customerdocroot = null, &$cronlog) $tar_file_list = ""; $mail_homedir = ""; while ($row = $sel_stmt->fetch()) { - $tar_file_list .= $row['acc_path'] . " "; + $tar_file_list .= "./".$row['maildir'] . " "; $mail_homedir = $row['homedir']; } @@ -98,8 +98,8 @@ function createCustomerBackup($data = null, $customerdocroot = null, &$cronlog) $cronlog->logAction(CRON_ACTION, LOG_DEBUG, 'Creating web-folder "'.makeCorrectDir($tmpdir . '/web').'"'); safe_exec('mkdir -p ' . escapeshellarg(makeCorrectDir($tmpdir . '/web'))); - $cronlog->logAction(CRON_ACTION, LOG_DEBUG, 'shell> tar cfz ' . escapeshellarg(makeCorrectFile($tmpdir . '/web/' . $data['loginname'] . '-web.tar.gz')) . ' --exclude=' . escapeshellarg($tmpdir.'/*') .' --exclude=' . escapeshellarg($tmpdir) .' ' . escapeshellarg($customerdocroot).' -C '.escapeshellarg($customerdocroot)); - safe_exec('tar cfz ' . escapeshellarg(makeCorrectFile($tmpdir . '/web/' . $data['loginname'] . '-web.tar.gz')) . ' --exclude=' . escapeshellarg($tmpdir.'/*') .' --exclude=' . escapeshellarg($tmpdir) .' ' . escapeshellarg($customerdocroot).' -C '.escapeshellarg($customerdocroot)); + $cronlog->logAction(CRON_ACTION, LOG_DEBUG, 'shell> tar cfz ' . escapeshellarg(makeCorrectFile($tmpdir . '/web/' . $data['loginname'] . '-web.tar.gz')) . ' --exclude=' . escapeshellarg(str_replace($customerdocroot, "./", makeCorrectFile($tmpdir.'/*'))) .' --exclude=' . escapeshellarg(str_replace($customerdocroot, "./", substr(makeCorrectDir($tmpdir), 0, -1))) .' -C '.escapeshellarg($customerdocroot). ' .'); + safe_exec('tar cfz ' . escapeshellarg(makeCorrectFile($tmpdir . '/web/' . $data['loginname'] . '-web.tar.gz')) . ' --exclude=' . escapeshellarg(str_replace($customerdocroot, "./", makeCorrectFile($tmpdir.'/*'))) .' --exclude=' . escapeshellarg(str_replace($customerdocroot, "./", substr(makeCorrectFile($tmpdir), 0, -1))) .' -C '.escapeshellarg($customerdocroot).' .'); $create_backup_tar_data .= './web '; } From b1446d366e8121c149ccd528cfef99fad272f16c Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 28 Apr 2016 15:27:59 +0200 Subject: [PATCH 0024/1335] add possibility to enable/disable backup function Signed-off-by: Michael Kaufmann (d00p) --- actions/admin/settings/110.accounts.php | 9 ++ customer_extras.php | 118 ++++++++++-------- install/froxlor.sql | 1 + .../updates/froxlor/0.9/update_0.9.inc.php | 8 +- .../preconfig/0.9/preconfig_0.9.inc.php | 8 ++ lib/navigation/00.froxlor.main.php | 1 + lng/english.lng.php | 3 + lng/german.lng.php | 3 + 8 files changed, 93 insertions(+), 58 deletions(-) diff --git a/actions/admin/settings/110.accounts.php b/actions/admin/settings/110.accounts.php index eb2a24a6..95df194e 100644 --- a/actions/admin/settings/110.accounts.php +++ b/actions/admin/settings/110.accounts.php @@ -186,6 +186,15 @@ return array( ) ), ), + 'system_backupenabled' => array( + 'label' => $lng['serversettings']['backupenabled'], + 'settinggroup' => 'system', + 'varname' => 'backupenabled', + 'type' => 'bool', + 'default' => false, + 'cronmodule' => 'froxlor/backup', + 'save_method' => 'storeSettingField' + ), ), ), ); diff --git a/customer_extras.php b/customer_extras.php index 96907cb3..9ed48994 100644 --- a/customer_extras.php +++ b/customer_extras.php @@ -519,65 +519,73 @@ if ($page == 'overview') { } } } elseif ($page == 'backup') { - if ($action == '') { - $log->logAction(USR_ACTION, LOG_NOTICE, "viewed customer_extras::backup"); - if (isset($_POST['send']) && $_POST['send'] == 'send') { + if (Settings::Get('system.backupenabled') == 1) + { + if ($action == '') { + $log->logAction(USR_ACTION, LOG_NOTICE, "viewed customer_extras::backup"); - if (! $_POST['path']) { - standard_error('invalidpath'); - } + if (isset($_POST['send']) && $_POST['send'] == 'send') { - $path = makeCorrectDir(validate($_POST['path'], 'path')); - $path = makeCorrectDir($userinfo['documentroot'] . '/' . $path); - - $backup_dbs = isset($_POST['backup_dbs']) ? intval($_POST['backup_dbs']) : 0; - $backup_mail = isset($_POST['backup_mail']) ? intval($_POST['backup_mail']) : 0; - $backup_web = isset($_POST['backup_web']) ? intval($_POST['backup_web']) : 0; - - if ($backup_dbs != '1') { - $backup_dbs = '0'; - } - - if ($backup_mail != '1') { - $backup_mail = '0'; - } - - if ($backup_web != '1') { - $backup_web = '0'; - } - - $task_data = array( - 'customerid' => $userinfo['customerid'], - 'uid' => $userinfo['guid'], - 'gid' => $userinfo['guid'], - 'loginname' => $userinfo['loginname'], - 'destdir' => $path, - 'backup_dbs' => $backup_dbs, - 'backup_mail' => $backup_mail, - 'backup_web' => $backup_web - ); - inserttask('20', $task_data); - - standard_success('backupscheduled'); - } else { - - // check whether there is a backup-job for this customer - $sel_stmt = Database::prepare("SELECT * FROM `".TABLE_PANEL_TASKS."` WHERE `type` = '20'"); - Database::pexecute($sel_stmt); - while ($entry = $sel_stmt->fetch()) - { - $data = unserialize($entry['data']); - if ($data['customerid'] == $userinfo['customerid']) { - standard_error('customerhasongoingbackupjob'); + if (! $_POST['path']) { + standard_error('invalidpath'); } + + $path = makeCorrectDir(validate($_POST['path'], 'path')); + $path = makeCorrectDir($userinfo['documentroot'] . '/' . $path); + + $backup_dbs = isset($_POST['backup_dbs']) ? intval($_POST['backup_dbs']) : 0; + $backup_mail = isset($_POST['backup_mail']) ? intval($_POST['backup_mail']) : 0; + $backup_web = isset($_POST['backup_web']) ? intval($_POST['backup_web']) : 0; + + if ($backup_dbs != '1') { + $backup_dbs = '0'; + } + + if ($backup_mail != '1') { + $backup_mail = '0'; + } + + if ($backup_web != '1') { + $backup_web = '0'; + } + + $task_data = array( + 'customerid' => $userinfo['customerid'], + 'uid' => $userinfo['guid'], + 'gid' => $userinfo['guid'], + 'loginname' => $userinfo['loginname'], + 'destdir' => $path, + 'backup_dbs' => $backup_dbs, + 'backup_mail' => $backup_mail, + 'backup_web' => $backup_web + ); + inserttask('20', $task_data); + + standard_success('backupscheduled'); + } else { + + // check whether there is a backup-job for this customer + $sel_stmt = Database::prepare("SELECT * FROM `".TABLE_PANEL_TASKS."` WHERE `type` = '20'"); + Database::pexecute($sel_stmt); + while ($entry = $sel_stmt->fetch()) + { + $data = unserialize($entry['data']); + if ($data['customerid'] == $userinfo['customerid']) { + standard_error('customerhasongoingbackupjob'); + } + } + $pathSelect = makePathfield($userinfo['documentroot'], $userinfo['guid'], $userinfo['guid']); + $backup_data = include_once dirname(__FILE__) . '/lib/formfields/customer/extras/formfield.backup.php'; + $backup_form = htmlform::genHTMLForm($backup_data); + $title = $backup_data['backup']['title']; + $image = $backup_data['backup']['image']; + eval("echo \"" . getTemplate("extras/backup") . "\";"); } - $pathSelect = makePathfield($userinfo['documentroot'], $userinfo['guid'], $userinfo['guid']); - $backup_data = include_once dirname(__FILE__) . '/lib/formfields/customer/extras/formfield.backup.php'; - $backup_form = htmlform::genHTMLForm($backup_data); - $title = $backup_data['backup']['title']; - $image = $backup_data['backup']['image']; - eval("echo \"" . getTemplate("extras/backup") . "\";"); } } -} \ No newline at end of file + else + { + standard_error('backupfunctionnotenabled'); + } +} diff --git a/install/froxlor.sql b/install/froxlor.sql index 5bece437..178b9135 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -525,6 +525,7 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES ('system', 'letsencryptkeysize', '4096'), ('system', 'letsencryptreuseold', 0), ('system', 'leenabled', '0'), + ('system', 'backupenabled', '0'), ('panel', 'decimal_places', '4'), ('panel', 'adminmail', 'admin@SERVERNAME'), ('panel', 'phpmyadmin_url', ''), diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index c34c0dc3..5a096f90 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -3295,7 +3295,9 @@ if (isFroxlorVersion('0.9.35')) { if (isFroxlorVersion('0.9.35.1') && isDatabaseVersion('201603150')) { - showUpdateStep("Adding new backup-cron entry"); + showUpdateStep("Adding new backup settings and cron"); + $enable_backup = isset($_POST['enable_backup']) ? (int) $_POST['enable_backup'] : "0"; + Settings::AddNew("system.backupenabled", $enable_backup); $stmt = Database::prepare(" INSERT INTO `" . TABLE_PANEL_CRONRUNS . "` SET `module` = 'froxlor/backup', @@ -3303,9 +3305,9 @@ if (isFroxlorVersion('0.9.35.1') && isDatabaseVersion('201603150')) { `interval` = '1 DAY', `desc_lng_key` = 'cron_backup', `lastrun` = 0, - `isactive` = 0" + `isactive` = :isactive" ); - Database::pexecute($stmt); + Database::pexecute($stmt, array('isactive' => $enable_backup)); lastStepStatus(0); updateToDbVersion('201604270'); diff --git a/install/updates/preconfig/0.9/preconfig_0.9.inc.php b/install/updates/preconfig/0.9/preconfig_0.9.inc.php index 28613aba..0c9d6559 100644 --- a/install/updates/preconfig/0.9/preconfig_0.9.inc.php +++ b/install/updates/preconfig/0.9/preconfig_0.9.inc.php @@ -708,4 +708,12 @@ function parseAndOutputPreconfig(&$has_preconfig, &$return, $current_version, $c $question.= makeyesno('enable_letsencrypt', '1', '0', '1').'
'; eval("\$return.=\"" . getTemplate("update/preconfigitem") . "\";"); } + + if (versionInUpdate($current_db_version, '201604270')) { + $has_preconfig = true; + $description = 'You can chose whether you want to enable or disable our backup function.

'; + $question = 'Do you want to enable Backup? (default: no): '; + $question.= makeyesno('enable_backup', '1', '0', '0').'
'; + eval("\$return.=\"" . getTemplate("update/preconfigitem") . "\";"); + } } diff --git a/lib/navigation/00.froxlor.main.php b/lib/navigation/00.froxlor.main.php index cb6f0401..2a989942 100644 --- a/lib/navigation/00.froxlor.main.php +++ b/lib/navigation/00.froxlor.main.php @@ -132,6 +132,7 @@ return array ( array ( 'url' => 'customer_extras.php?page=backup', 'label' => $lng['menue']['extras']['backup'], + 'show_element' => ( Settings::Get('system.backupenabled') == true ), ), ), ), diff --git a/lng/english.lng.php b/lng/english.lng.php index 8e0d72f0..f35d5437 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -1983,3 +1983,6 @@ $lng['extras']['backup_dbs'] = 'Backup databases'; $lng['error']['customerhasongoingbackupjob'] = 'There is already a backup job waiting to be processed, please be patient.'; $lng['success']['backupscheduled'] = 'Your backup job has been scheduled. Please wait for it to be processed'; $lng['crondesc']['cron_backup'] = 'Process backup jobs'; +$lng['error']['backupfunctionnotenabled'] = 'The backup function is not enabled'; +$lng['serversettings']['backupenabled']['title'] = "Enable backup for customers"; +$lng['serversettings']['backupenabled']['description'] = "If activated, the customer will be able to schedule backup jobs (cron-backup) which generates an archive within his docroot (subdirectory chosable by customer)"; diff --git a/lng/german.lng.php b/lng/german.lng.php index 6fe024b2..76de5ec4 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -1636,3 +1636,6 @@ $lng['extras']['backup_dbs'] = 'Datenbanken sichern'; $lng['error']['customerhasongoingbackupjob'] = 'Es gibt noch einen austehenden Backup-Job. Bitte haben Sie etwas Geduld.'; $lng['success']['backupscheduled'] = 'Ihre Sicherung wurde erfolgreich geplant. Bitte warten Sie nun, bis diese abgearbeitet wurde.'; $lng['crondesc']['cron_backup'] = 'Ausstehende Sicherungen erstellen'; +$lng['error']['backupfunctionnotenabled'] = 'Die Sicherungs-Funktion is nicht aktiviert'; +$lng['serversettings']['backupenabled']['title'] = "Backup für Kunden aktivieren"; +$lng['serversettings']['backupenabled']['description'] = "Wenn dies aktiviert ist, kann der Kunde Sicherungen planen (cron-backup) welche ein Archiv in sein Heimatverzeichnis ablegt (Unterordner vom Kunden wählbar)"; From 7c44c5ea7569d13f43bc26e8cefe14e1bd054a66 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 28 Apr 2016 15:40:43 +0200 Subject: [PATCH 0025/1335] handle exit of forked backup-cronjob correctly Signed-off-by: Michael Kaufmann (d00p) --- scripts/jobs/cron_backup.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/scripts/jobs/cron_backup.php b/scripts/jobs/cron_backup.php index abecaca4..089b95fc 100644 --- a/scripts/jobs/cron_backup.php +++ b/scripts/jobs/cron_backup.php @@ -106,3 +106,8 @@ while ($row = $result_tasks_stmt->fetch(PDO::FETCH_ASSOC)) { // remove entry Database::pexecute($del_stmt, array('id' => $row['id'])); } + +if (function_exists('pcntl_fork')) { + @unlink($BackupLock); + die(); +} From b2cd992f92a1276cd7b2cfa00041db1bfcad7882 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Fri, 29 Apr 2016 08:40:00 +0200 Subject: [PATCH 0026/1335] add important information that the customer should protect the backup directory using htaccess Signed-off-by: Michael Kaufmann (d00p) --- customer_extras.php | 1 + lib/formfields/customer/extras/formfield.backup.php | 5 +++++ lng/english.lng.php | 2 ++ lng/german.lng.php | 2 ++ 4 files changed, 10 insertions(+) diff --git a/customer_extras.php b/customer_extras.php index 9ed48994..6c42196b 100644 --- a/customer_extras.php +++ b/customer_extras.php @@ -560,6 +560,7 @@ if ($page == 'overview') { 'backup_mail' => $backup_mail, 'backup_web' => $backup_web ); + // schedule backup job inserttask('20', $task_data); standard_success('backupscheduled'); diff --git a/lib/formfields/customer/extras/formfield.backup.php b/lib/formfields/customer/extras/formfield.backup.php index 82c9259b..4a808ec0 100644 --- a/lib/formfields/customer/extras/formfield.backup.php +++ b/lib/formfields/customer/extras/formfield.backup.php @@ -30,6 +30,11 @@ return array( 'select_var' => $pathSelect['value'], 'value' => $pathSelect['value'] ), + 'path_protection_info' => array( + 'label' => $lng['extras']['path_protection_label'], + 'type' => 'label', + 'value' => $lng['extras']['path_protection_info'] + ), 'backup_web' => array( 'label' => $lng['extras']['backup_web'], 'type' => 'checkbox', diff --git a/lng/english.lng.php b/lng/english.lng.php index f35d5437..abe69ec0 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -1986,3 +1986,5 @@ $lng['crondesc']['cron_backup'] = 'Process backup jobs'; $lng['error']['backupfunctionnotenabled'] = 'The backup function is not enabled'; $lng['serversettings']['backupenabled']['title'] = "Enable backup for customers"; $lng['serversettings']['backupenabled']['description'] = "If activated, the customer will be able to schedule backup jobs (cron-backup) which generates an archive within his docroot (subdirectory chosable by customer)"; +$lng['extras']['path_protection_label'] = 'Important'; +$lng['extras']['path_protection_info'] = 'We strongly recommend protecting the given path, see "Extras" -> "Directory protection"'; diff --git a/lng/german.lng.php b/lng/german.lng.php index 76de5ec4..cf6f2816 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -1639,3 +1639,5 @@ $lng['crondesc']['cron_backup'] = 'Ausstehende Sicherungen erstellen'; $lng['error']['backupfunctionnotenabled'] = 'Die Sicherungs-Funktion is nicht aktiviert'; $lng['serversettings']['backupenabled']['title'] = "Backup für Kunden aktivieren"; $lng['serversettings']['backupenabled']['description'] = "Wenn dies aktiviert ist, kann der Kunde Sicherungen planen (cron-backup) welche ein Archiv in sein Heimatverzeichnis ablegt (Unterordner vom Kunden wählbar)"; +$lng['extras']['path_protection_label'] = 'Wichtig'; +$lng['extras']['path_protection_info'] = 'Wir raten dringend dazu den angegebenen Pfad zu schützen, siehe "Extras" -> "Verzeichnisschutz"'; From e8e980509f494aa2a033c5296476c7f3d4a1469b Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Fri, 29 Apr 2016 10:10:35 +0200 Subject: [PATCH 0027/1335] fix missing backup-enabled setting entry Signed-off-by: Michael Kaufmann (d00p) --- actions/admin/settings/110.accounts.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/actions/admin/settings/110.accounts.php b/actions/admin/settings/110.accounts.php index 95df194e..73890e4c 100644 --- a/actions/admin/settings/110.accounts.php +++ b/actions/admin/settings/110.accounts.php @@ -183,8 +183,7 @@ return array( 'varname' => 'allow_preset', ), 'onlyif' => 1 - ) - ), + ) ), 'system_backupenabled' => array( 'label' => $lng['serversettings']['backupenabled'], @@ -197,6 +196,6 @@ return array( ), ), ), - ); + ) +); -?> From 2210d3de1239e53b4580b3c64783847b002d1097 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 5 May 2016 08:23:46 +0200 Subject: [PATCH 0028/1335] fix mail-account backup; fix missing job description for admin-dashboard (outstanding tasks); avoid double backup-task insertion when customer presses refresh after scheduling the backup-job Signed-off-by: Michael Kaufmann (d00p) --- customer_extras.php | 21 ++++++++++--------- lib/cron_init.php | 2 +- .../filedir/function.createCustomerBackup.php | 6 +++--- .../froxlor/function.CronjobFunctions.php | 9 ++++++++ lng/english.lng.php | 1 + lng/german.lng.php | 1 + 6 files changed, 26 insertions(+), 14 deletions(-) diff --git a/customer_extras.php b/customer_extras.php index 6c42196b..98fd06cc 100644 --- a/customer_extras.php +++ b/customer_extras.php @@ -525,6 +525,17 @@ if ($page == 'overview') { if ($action == '') { $log->logAction(USR_ACTION, LOG_NOTICE, "viewed customer_extras::backup"); + // check whether there is a backup-job for this customer + $sel_stmt = Database::prepare("SELECT * FROM `".TABLE_PANEL_TASKS."` WHERE `type` = '20'"); + Database::pexecute($sel_stmt); + while ($entry = $sel_stmt->fetch()) + { + $data = unserialize($entry['data']); + if ($data['customerid'] == $userinfo['customerid']) { + standard_error('customerhasongoingbackupjob'); + } + } + if (isset($_POST['send']) && $_POST['send'] == 'send') { if (! $_POST['path']) { @@ -566,16 +577,6 @@ if ($page == 'overview') { standard_success('backupscheduled'); } else { - // check whether there is a backup-job for this customer - $sel_stmt = Database::prepare("SELECT * FROM `".TABLE_PANEL_TASKS."` WHERE `type` = '20'"); - Database::pexecute($sel_stmt); - while ($entry = $sel_stmt->fetch()) - { - $data = unserialize($entry['data']); - if ($data['customerid'] == $userinfo['customerid']) { - standard_error('customerhasongoingbackupjob'); - } - } $pathSelect = makePathfield($userinfo['documentroot'], $userinfo['guid'], $userinfo['guid']); $backup_data = include_once dirname(__FILE__) . '/lib/formfields/customer/extras/formfield.backup.php'; $backup_form = htmlform::genHTMLForm($backup_data); diff --git a/lib/cron_init.php b/lib/cron_init.php index b8dacfa2..68166f5e 100644 --- a/lib/cron_init.php +++ b/lib/cron_init.php @@ -129,7 +129,7 @@ while ($fName = readdir($lockDirHandle)) { // fwrite($debugHandler, 'Previous cronjob didn\'t exit clean. PID: ' . $check_pid . "\n"); fwrite($debugHandler, 'Removing lockfile: ' . $lockdir . $fName . "\n"); - unlink($lockdir . $fName); + @unlink($lockdir . $fName); } else { // Result: A Cronscript with this pid diff --git a/lib/functions/filedir/function.createCustomerBackup.php b/lib/functions/filedir/function.createCustomerBackup.php index 2375081d..9c352439 100644 --- a/lib/functions/filedir/function.createCustomerBackup.php +++ b/lib/functions/filedir/function.createCustomerBackup.php @@ -82,13 +82,13 @@ function createCustomerBackup($data = null, $customerdocroot = null, &$cronlog) $tar_file_list = ""; $mail_homedir = ""; while ($row = $sel_stmt->fetch()) { - $tar_file_list .= "./".$row['maildir'] . " "; + $tar_file_list .= escapeshellarg("./".$row['maildir']) . " "; $mail_homedir = $row['homedir']; } if (! empty($tar_file_list)) { - $cronlog->logAction(CRON_ACTION, LOG_DEBUG, 'shell> tar cfvz ' . escapeshellarg(makeCorrectFile($tmpdir . '/mail/' . $data['loginname'] . '-mail.tar.gz')) . ' ' . escapeshellarg(trim($tar_file_list)).' -C '.escapeshellarg($mail_homedir)); - safe_exec('tar cfz ' . escapeshellarg(makeCorrectFile($tmpdir . '/mail/' . $data['loginname'] . '-mail.tar.gz')) . ' ' . escapeshellarg(trim($tar_file_list)).' -C '.escapeshellarg($mail_homedir)); + $cronlog->logAction(CRON_ACTION, LOG_DEBUG, 'shell> tar cfvz ' . escapeshellarg(makeCorrectFile($tmpdir . '/mail/' . $data['loginname'] . '-mail.tar.gz')) . ' -C '.escapeshellarg($mail_homedir) . ' ' . trim($tar_file_list)); + safe_exec('tar cfz ' . escapeshellarg(makeCorrectFile($tmpdir . '/mail/' . $data['loginname'] . '-mail.tar.gz')) . ' -C '.escapeshellarg($mail_homedir) . ' ' . trim($tar_file_list)); $create_backup_tar_data .= './mail '; } } diff --git a/lib/functions/froxlor/function.CronjobFunctions.php b/lib/functions/froxlor/function.CronjobFunctions.php index 4a670819..a2c09785 100644 --- a/lib/functions/froxlor/function.CronjobFunctions.php +++ b/lib/functions/froxlor/function.CronjobFunctions.php @@ -108,6 +108,15 @@ function getOutstandingTasks() { elseif ($row['type'] == '10') { $task_desc = $lng['tasks']['diskspace_set_quota']; } + // deleting user-files + elseif ($row['type'] == '20') { + $loginname = ''; + if (is_array($row['data'])) { + $loginname = $row['data']['loginname']; + } + $task_desc = $lng['tasks']['backup_customerfiles']; + $task_desc = str_replace('%loginname%', $loginname, $task_desc); + } // re-generating of cron.d-file elseif ($row['type'] == '99') { $task_desc = $lng['tasks']['regenerating_crond']; diff --git a/lng/english.lng.php b/lng/english.lng.php index abe69ec0..6783d8a8 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -1988,3 +1988,4 @@ $lng['serversettings']['backupenabled']['title'] = "Enable backup for customers" $lng['serversettings']['backupenabled']['description'] = "If activated, the customer will be able to schedule backup jobs (cron-backup) which generates an archive within his docroot (subdirectory chosable by customer)"; $lng['extras']['path_protection_label'] = 'Important'; $lng['extras']['path_protection_info'] = 'We strongly recommend protecting the given path, see "Extras" -> "Directory protection"'; +$lng['tasks']['backup_customerfiles'] = 'Backup job for customer %loginname%'; diff --git a/lng/german.lng.php b/lng/german.lng.php index cf6f2816..671ce6c1 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -1641,3 +1641,4 @@ $lng['serversettings']['backupenabled']['title'] = "Backup für Kunden aktiviere $lng['serversettings']['backupenabled']['description'] = "Wenn dies aktiviert ist, kann der Kunde Sicherungen planen (cron-backup) welche ein Archiv in sein Heimatverzeichnis ablegt (Unterordner vom Kunden wählbar)"; $lng['extras']['path_protection_label'] = 'Wichtig'; $lng['extras']['path_protection_info'] = 'Wir raten dringend dazu den angegebenen Pfad zu schützen, siehe "Extras" -> "Verzeichnisschutz"'; +$lng['tasks']['backup_customerfiles'] = 'Datensicherung für Kunde %loginname%'; From 941a25ec9db80faffe7209b49ebc1f4c9a670581 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 5 May 2016 08:43:28 +0200 Subject: [PATCH 0029/1335] avoid possible weird behaviour when resultset changes while running (if possible at all) Signed-off-by: Michael Kaufmann (d00p) --- scripts/jobs/cron_backup.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/jobs/cron_backup.php b/scripts/jobs/cron_backup.php index 089b95fc..b01fe1ed 100644 --- a/scripts/jobs/cron_backup.php +++ b/scripts/jobs/cron_backup.php @@ -75,7 +75,8 @@ $result_tasks_stmt = Database::query(" $del_stmt = Database::prepare("DELETE FROM `" . TABLE_PANEL_TASKS . "` WHERE `id` = :id"); -while ($row = $result_tasks_stmt->fetch(PDO::FETCH_ASSOC)) { +$all_jobs = $result_tasks_stmt->fetchAll(); +foreach ($all_jobs as $row) { if ($row['data'] != '') { $row['data'] = unserialize($row['data']); From 0e79e8d67044b0110584259873160b3a8324d81b Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 9 May 2016 08:08:59 +0200 Subject: [PATCH 0030/1335] fix default_server parameter for listen-statement in nginx since this changed since ngninx-0.8.21; fixes #1621 Signed-off-by: Michael Kaufmann (d00p) --- scripts/jobs/cron_tasks.inc.http.30.nginx.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/jobs/cron_tasks.inc.http.30.nginx.php b/scripts/jobs/cron_tasks.inc.http.30.nginx.php index 5069d223..90ac0dc8 100644 --- a/scripts/jobs/cron_tasks.inc.http.30.nginx.php +++ b/scripts/jobs/cron_tasks.inc.http.30.nginx.php @@ -155,7 +155,7 @@ class nginx extends HttpConfigBase { /** * this HAS to be set for the default host in nginx or else no vhost will work */ - $this->nginx_data[$vhost_filename] .= "\t". 'listen ' . $ip . ':' . $port . ' default'. ($ssl_vhost == true ? ' ssl' : '') . ';' . "\n"; + $this->nginx_data[$vhost_filename] .= "\t". 'listen ' . $ip . ':' . $port . ' default_server'. ($ssl_vhost == true ? ' ssl' : '') . ';' . "\n"; $this->nginx_data[$vhost_filename] .= "\t".'# Froxlor default vhost' . "\n"; $this->nginx_data[$vhost_filename] .= "\t".'server_name ' . Settings::Get('system.hostname') . ';' . "\n"; From 5e09d56871e06e61aaeed6cf584e7f69efb85c3f Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 9 May 2016 08:27:01 +0200 Subject: [PATCH 0031/1335] clear nscd group cache after adding a new customer to avoid permission issues of webserver; fixes #1570 Signed-off-by: Michael Kaufmann (d00p) --- scripts/jobs/cron_tasks.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/scripts/jobs/cron_tasks.php b/scripts/jobs/cron_tasks.php index b452c2d4..d0e260dd 100644 --- a/scripts/jobs/cron_tasks.php +++ b/scripts/jobs/cron_tasks.php @@ -166,6 +166,11 @@ while ($row = $result_tasks_stmt->fetch(PDO::FETCH_ASSOC)) { } $cronlog->logAction(CRON_ACTION, LOG_NOTICE, 'Running: chown -R ' . (int)Settings::Get('system.vmail_uid') . ':' . (int)Settings::Get('system.vmail_gid') . ' ' . escapeshellarg($usermaildir)); safe_exec('chown -R ' . (int)Settings::Get('system.vmail_uid') . ':' . (int)Settings::Get('system.vmail_gid') . ' ' . escapeshellarg($usermaildir)); + + // clear NSCD cache if using fcgid or fpm, #1570 + if (Settings::Get('system.mod_fcgid') == 1 || (int)Settings::Get('phpfpm.enabled') == 1) { + safe_exec('nscd -i group 1> /dev/null', false, array('>')); + } } } From 6369e160b82e8d258e7a2709f11617930802033f Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 9 May 2016 21:33:07 +0200 Subject: [PATCH 0032/1335] fix nscd group cache clearing call; refs #1570 Signed-off-by: Michael Kaufmann (d00p) --- scripts/jobs/cron_tasks.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/jobs/cron_tasks.php b/scripts/jobs/cron_tasks.php index d0e260dd..45020038 100644 --- a/scripts/jobs/cron_tasks.php +++ b/scripts/jobs/cron_tasks.php @@ -169,7 +169,8 @@ while ($row = $result_tasks_stmt->fetch(PDO::FETCH_ASSOC)) { // clear NSCD cache if using fcgid or fpm, #1570 if (Settings::Get('system.mod_fcgid') == 1 || (int)Settings::Get('phpfpm.enabled') == 1) { - safe_exec('nscd -i group 1> /dev/null', false, array('>')); + $false_val = false; + safe_exec('nscd -i group 1> /dev/null', $false_val, array('>')); } } } From 5acd51fdd34c49811d045286c390779508e01755 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 9 May 2016 15:09:09 +0200 Subject: [PATCH 0033/1335] add first part of new dns-editor Signed-off-by: Michael Kaufmann (d00p) --- admin_domains.php | 3 + dns_editor.php | 233 ++++++++++++++++++ install/froxlor.sql | 15 +- .../updates/froxlor/0.9/update_0.9.inc.php | 23 ++ lib/tables.inc.php | 1 + lib/version.inc.php | 2 +- .../Sparkle/admin/domains/domains_edit.tpl | 1 + templates/Sparkle/assets/css/main.css | 22 ++ templates/Sparkle/dns_editor/entry_bit.tpl | 10 + templates/Sparkle/dns_editor/index.tpl | 28 +++ templates/Sparkle/dns_editor/list.tpl | 25 ++ 11 files changed, 361 insertions(+), 2 deletions(-) create mode 100644 dns_editor.php create mode 100644 templates/Sparkle/dns_editor/entry_bit.tpl create mode 100644 templates/Sparkle/dns_editor/index.tpl create mode 100644 templates/Sparkle/dns_editor/list.tpl diff --git a/admin_domains.php b/admin_domains.php index 479e1bc5..7ecc3d5d 100644 --- a/admin_domains.php +++ b/admin_domains.php @@ -2061,6 +2061,9 @@ elseif (Settings::Get('system.validate_domain') && ! validateDomain($domain)) { eval("echo \"" . getTemplate("domains/domains_import") . "\";"); } } +} elseif ($page == 'domaindnseditor') { + + require_once __DIR__.'/dns_editor.php'; } function formatDomainEntry(&$row, &$idna_convert) diff --git a/dns_editor.php b/dns_editor.php new file mode 100644 index 00000000..b6dd43e6 --- /dev/null +++ b/dns_editor.php @@ -0,0 +1,233 @@ + (2016-) + * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt + * @package Panel + * + */ + +// This file is being included in admin_domains and customer_domains + // and therefore does not need to require lib/init.php + +$domain_id = isset($_GET['domain_id']) ? (int) $_GET['domain_id'] : null; + +$record = isset($_POST['record']['record']) ? trim($_POST['record']['record']) : null; +$type = isset($_POST['record']['type']) ? $_POST['record']['type'] : 'A'; +$prio = isset($_POST['record']['prio']) ? (int) $_POST['record']['prio'] : null; +$content = isset($_POST['record']['content']) ? trim($_POST['record']['content']) : null; +$ttl = isset($_POST['record']['ttl']) ? (int) $_POST['record']['ttl'] : 18000; + +// get domain-name +$dom_stmt = Database::prepare("SELECT domain FROM `" . TABLE_PANEL_DOMAINS . "` WHERE id = :did"); +$domain = Database::pexecute_first($dom_stmt, array( + 'did' => $domain_id +)); +$domain = $idna_convert->decode($domain['domain']); + +// select all entries +$sel_stmt = Database::prepare("SELECT * FROM `" . TABLE_DOMAIN_DNS . "` WHERE domain_id = :did"); +Database::pexecute($sel_stmt, array( + 'did' => $domain_id +)); +$dom_entries = $sel_stmt->fetchAll(PDO::FETCH_ASSOC); + +$errors = array(); +$success_message = ""; + +// action for adding a new entry +if ($action == 'add_record' && ! empty($_POST)) { + + // validation + if (empty($record)) { + $record = "@"; + } + + $record = strtolower($record); + + if ($ttl <= 0) { + $ttl = 18000; + } + + if (empty($content)) { + $errors[] = $lng['error']['dns_content_empty']; + } + + // types + if ($type == 'A' && filter_var($content, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) === false) { + $errors[] = $lng['error']['dns_arec_noipv4']; + } elseif ($type == 'AAAA' && filter_var($content, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false) { + $errors[] = $lng['errors']['dns_aaaarec_noipv6']; + } elseif ($type == 'MX' && empty($prio)) { + $errors[] = $lng['error']['dns_mx_prioempty']; + } elseif ($type == 'CNAME') { + // check for trailing dot + if (substr($content, - 1) == '.') { + // remove it for checks + $content = substr($content, 0, - 1); + } + if (! validateDomain($content)) { + $errors[] = $lng['error']['dns_cname_invaliddom']; + } else { + // check whether there are RR-records for the same resource + foreach ($dom_entries as $existing_entries) { + if (($existing_entries['type'] == 'A' || $existing_entries['type'] == 'AAAA' || $existing_entries['type'] == 'MX' || $existing_entries['type'] == 'NS') && $existing_entries['record'] == $record) { + $errors[] = $lng['error']['dns_cname_nomorerr']; + break; + } + } + } + // append trailing dot (again) + $content .= '.'; + } elseif ($type == 'TXT' && ! empty($content)) { + // check that TXT content is enclosed in " " + if (substr($content, 0, 1) != '"') { + $content = '"' . $content; + } + if (substr($content, - 1) != '"') { + $content .= '"'; + } + } elseif ($type == 'SRV') { + // check for trailing dot + if (substr($content, - 1) == '.') { + // remove it for checks + $content = substr($content, 0, - 1); + } + // + if (! validateDomain($content)) { + $errors[] = $lng['error']['dns_srv_needdom']; + } else { + // check whether there is a CNAME-record for the same resource + foreach ($dom_entries as $existing_entries) { + $fqdn = $existing_entries['record'] . '.' . $domain; + if ($existing_entries['type'] == 'CNAME' && $fqdn == $content) { + $errors[] = $lng['error']['dns_srv_noalias']; + break; + } + } + } + // append trailing dot (again) + $content .= '.'; + } + + $new_entry = array( + 'record' => $record, + 'type' => $type, + 'prio' => $prio, + 'content' => $content, + 'ttl' => $ttl, + 'domain_id' => $domain_id + ); + ksort($new_entry); + + // check for duplicate + foreach ($dom_entries as $existing_entry) { + // compare serialized string of array + $check_entry = $existing_entry; + // new entry has no ID yet + unset($check_entry['id']); + // sort by key + ksort($check_entry); + // format integer fields to real integer (as they are read as string from the DB) + $check_entry['prio'] = (int)$check_entry['prio']; + $check_entry['ttl'] = (int)$check_entry['ttl']; + $check_entry['domain_id'] = (int)$check_entry['domain_id']; + // serialize both + $check_entry = serialize($check_entry); + $new = serialize($new_entry); + // compare + if ($check_entry === $new) { + $errors[] = $lng['error']['dns_duplicate_entry']; + unset($check_entry); + break; + } + } + + if (empty($errors)) { + $ins_stmt = Database::prepare(" + INSERT INTO `" . TABLE_DOMAIN_DNS . "` SET + `record` = :record, + `type` = :type, + `prio` = :prio, + `content` = :content, + `ttl` = :ttl, + `domain_id` = :domain_id + "); + + Database::pexecute($ins_stmt, $new_entry); + + $new_entry_id = Database::lastInsertId(); + + // add temporary to the entries-array (no reread of DB necessary) + $new_entry['id'] = $new_entry_id; + $dom_entries[] = $new_entry; + + // success message (inline) + $success_message = $lng['success']['dns_record_added']; + } else { + // show $errors + $errors = implode("
", $errors); + } +} elseif ($action == 'delete') { + // remove entry + $entry_id = isset($_GET['id']) ? (int) $_GET['id'] : 0; + if ($entry_id > 0) { + $del_stmt = Database::prepare("DELETE FROM `" . TABLE_DOMAIN_DNS . "` WHERE `id` = :id"); + Database::pexecute($del_stmt, array( + 'id' => $entry_id + )); + + // remove deleted entry from internal data array (no reread of DB necessary) + $_t = $dom_entries; + foreach ($_t as $idx => $entry) { + if ($entry['id'] == $entry_id) { + unset($dom_entries[$idx]); + break; + } + } + unset($_t); + // success message (inline) + $success_message = $lng['success']['dns_record_deleted']; + } +} + +// show editor +$record_list = ""; +$existing_entries = ""; +$type_select = ""; +$entriescount = 0; + +if (! empty($dom_entries)) { + $entriescount = count($dom_entries); + foreach ($dom_entries as $entry) { + eval("\$existing_entries.=\"" . getTemplate("dns_editor/entry_bit", true) . "\";"); + } +} + +// available types +$type_select_values = array( + 'A', + 'AAAA', + 'NS', + 'MX', + 'SRV', + 'TXT', + 'CNAME' +); +asort($type_select_values); +foreach ($type_select_values as $_type) { + $type_select .= makeoption($_type, $_type, $type); +} + +eval("\$record_list=\"" . getTemplate("dns_editor/list", true) . "\";"); +eval("echo \"" . getTemplate("dns_editor/index", true) . "\";"); diff --git a/install/froxlor.sql b/install/froxlor.sql index 178b9135..7b0d39ca 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -557,7 +557,7 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES ('panel', 'password_special_char_required', '0'), ('panel', 'password_special_char', '!?<>§$%+#=@'), ('panel', 'version', '0.9.35.1'), - ('panel', 'db_version', '201604270'); + ('panel', 'db_version', '201605090'); DROP TABLE IF EXISTS `panel_tasks`; @@ -855,3 +855,16 @@ CREATE TABLE IF NOT EXISTS `panel_domaintoip` ( PRIMARY KEY (`id_domain`,`id_ipandports`) ) ENGINE=MyISAM CHARSET=utf8 COLLATE=utf8_general_ci; + +DROP TABLE IF EXISTS `domain_dns_entries`; +CREATE TABLE `domain_dns_entries` ( + `id` int(20) NOT NULL, + `domain_id` int(15) NOT NULL, + `record` varchar(255) NOT NULL, + `type` varchar(10) NOT NULL DEFAULT 'A', + `content` text NOT NULL, + `ttl` int(11) NOT NULL DEFAULT '18000', + `prio` int(11) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=MyISAM CHARSET=utf8 COLLATE=utf8_general_ci; + diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index 5a096f90..88c7139d 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -3312,3 +3312,26 @@ if (isFroxlorVersion('0.9.35.1') && isDatabaseVersion('201603150')) { updateToDbVersion('201604270'); } + +if (isFroxlorVersion('0.9.35.1') && isDatabaseVersion('201604270')) { + + showUpdateStep("Adding new dns related tables and settings"); + $enable_dns = isset($_POST['enable_dns']) ? (int) $_POST['enable_dns'] : "0"; + Settings::AddNew("system.dnsenabled", $enable_dns); + + Database::query("DROP TABLE IF EXISTS `domain_dns_entries`;"); + $sql = "CREATE TABLE `domain_dns_entries` ( + `id` int(20) NOT NULL, + `domain_id` int(15) NOT NULL, + `record` varchar(255) NOT NULL, + `type` varchar(10) NOT NULL DEFAULT 'A', + `content` text NOT NULL, + `ttl` int(11) NOT NULL DEFAULT '18000', + `prio` int(11) DEFAULT NULL, + PRIMARY KEY (`id`) + ) DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;"; + Database::query($sql); + lastStepStatus(0); + + updateToDbVersion('201605090'); +} diff --git a/lib/tables.inc.php b/lib/tables.inc.php index 65fafc0a..39b37e24 100644 --- a/lib/tables.inc.php +++ b/lib/tables.inc.php @@ -50,5 +50,6 @@ define('TABLE_PANEL_REDIRECTCODES', 'redirect_codes'); define('TABLE_PANEL_DOMAINREDIRECTS', 'domain_redirect_codes'); define('TABLE_PANEL_DOMAIN_SSL_SETTINGS', 'domain_ssl_settings'); define('TABLE_DOMAINTOIP', 'panel_domaintoip'); +define('TABLE_DOMAIN_DNS', 'domain_dns_entries'); require dirname(__FILE__).'/version.inc.php'; diff --git a/lib/version.inc.php b/lib/version.inc.php index 2d17e6d1..5bc79f20 100644 --- a/lib/version.inc.php +++ b/lib/version.inc.php @@ -19,7 +19,7 @@ $version = '0.9.35.1'; // Database version (YYYYMMDDC where C is a daily counter) -$dbversion = '201604270'; +$dbversion = '201605090'; // Distribution branding-tag (used for Debian etc.) $branding = ''; diff --git a/templates/Sparkle/admin/domains/domains_edit.tpl b/templates/Sparkle/admin/domains/domains_edit.tpl index 89b1acc4..fdd9b203 100644 --- a/templates/Sparkle/admin/domains/domains_edit.tpl +++ b/templates/Sparkle/admin/domains/domains_edit.tpl @@ -4,6 +4,7 @@ $header

{$title}  {$title} +  (edit DNS)

diff --git a/templates/Sparkle/assets/css/main.css b/templates/Sparkle/assets/css/main.css index 38e07046..3d3f27bc 100644 --- a/templates/Sparkle/assets/css/main.css +++ b/templates/Sparkle/assets/css/main.css @@ -601,6 +601,12 @@ input[type="password"] { background:#fff url(../img/icons/lock.png) no-repeat 5px 4px; } +input[class="small"] { + width:auto; + margin-top: 5px; +} + + /* * BUTTONS */ @@ -1513,3 +1519,19 @@ fieldset.file { table.hl tbody tr.domain-hostname:hover { background-color: rgb(64, 150, 238); } + +td.size-5 { + width: 5%; +} + +td.size-10 { + width: 10%; +} + +td.size-20 { + width: 20%; +} + +td.size-50 { + width: 50%; +} diff --git a/templates/Sparkle/dns_editor/entry_bit.tpl b/templates/Sparkle/dns_editor/entry_bit.tpl new file mode 100644 index 00000000..1e80ff67 --- /dev/null +++ b/templates/Sparkle/dns_editor/entry_bit.tpl @@ -0,0 +1,10 @@ +
{$entry['record']}{$entry['type']} {$entry['prio']}{$entry['content']}{$entry['ttl']} + {$lng['panel']['delete']} +
+ + + + + + + + + + + + + + + + + + + + {$existing_entries} + +
RecordTypePriorityContentTTL 
+ From 31d08d532c276b10ff308878a2b8b9e4454fc6d9 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 9 May 2016 21:31:02 +0200 Subject: [PATCH 0034/1335] fix missing auto-increment for new table Signed-off-by: Michael Kaufmann (d00p) --- install/froxlor.sql | 2 +- install/updates/froxlor/0.9/update_0.9.inc.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/install/froxlor.sql b/install/froxlor.sql index 7b0d39ca..ad61eec2 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -858,7 +858,7 @@ CREATE TABLE IF NOT EXISTS `panel_domaintoip` ( DROP TABLE IF EXISTS `domain_dns_entries`; CREATE TABLE `domain_dns_entries` ( - `id` int(20) NOT NULL, + `id` int(20) NOT NULL auto_increment, `domain_id` int(15) NOT NULL, `record` varchar(255) NOT NULL, `type` varchar(10) NOT NULL DEFAULT 'A', diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index 88c7139d..4af29fef 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -3321,7 +3321,7 @@ if (isFroxlorVersion('0.9.35.1') && isDatabaseVersion('201604270')) { Database::query("DROP TABLE IF EXISTS `domain_dns_entries`;"); $sql = "CREATE TABLE `domain_dns_entries` ( - `id` int(20) NOT NULL, + `id` int(20) NOT NULL auto_increment, `domain_id` int(15) NOT NULL, `record` varchar(255) NOT NULL, `type` varchar(10) NOT NULL DEFAULT 'A', From 283e272b9983455b807a153fe3b09b382128e2ae Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 9 May 2016 21:52:58 +0200 Subject: [PATCH 0035/1335] enhance MX validation; fix SRV validation Signed-off-by: Michael Kaufmann (d00p) --- dns_editor.php | 34 +++++++++++++++++++++++++++++----- lng/english.lng.php | 13 +++++++++++++ 2 files changed, 42 insertions(+), 5 deletions(-) diff --git a/dns_editor.php b/dns_editor.php index b6dd43e6..b56bb4f1 100644 --- a/dns_editor.php +++ b/dns_editor.php @@ -68,8 +68,29 @@ if ($action == 'add_record' && ! empty($_POST)) { $errors[] = $lng['error']['dns_arec_noipv4']; } elseif ($type == 'AAAA' && filter_var($content, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false) { $errors[] = $lng['errors']['dns_aaaarec_noipv6']; - } elseif ($type == 'MX' && empty($prio)) { - $errors[] = $lng['error']['dns_mx_prioempty']; + } elseif ($type == 'MX') { + if ($prio === null || $prio < 0) { + $errors[] = $lng['error']['dns_mx_prioempty']; + } + // check for trailing dot + if (substr($content, - 1) == '.') { + // remove it for checks + $content = substr($content, 0, - 1); + } + if (! validateDomain($content)) { + $errors[] = $lng['error']['dns_mx_needdom']; + } else { + // check whether there is a CNAME-record for the same resource + foreach ($dom_entries as $existing_entries) { + $fqdn = $existing_entries['record'] . '.' . $domain; + if ($existing_entries['type'] == 'CNAME' && $fqdn == $content) { + $errors[] = $lng['error']['dns_mx_noalias']; + break; + } + } + } + // append trailing dot (again) + $content .= '.'; } elseif ($type == 'CNAME') { // check for trailing dot if (substr($content, - 1) == '.') { @@ -103,14 +124,17 @@ if ($action == 'add_record' && ! empty($_POST)) { // remove it for checks $content = substr($content, 0, - 1); } - // - if (! validateDomain($content)) { + // check only last part of content, as it can look like: + // _service._proto.name. TTL class SRV priority weight port target. + $_split_content = explode(" ", $content); + $target = trim($_split_content[count($_split_content)-1]); + if (! validateDomain($target)) { $errors[] = $lng['error']['dns_srv_needdom']; } else { // check whether there is a CNAME-record for the same resource foreach ($dom_entries as $existing_entries) { $fqdn = $existing_entries['record'] . '.' . $domain; - if ($existing_entries['type'] == 'CNAME' && $fqdn == $content) { + if ($existing_entries['type'] == 'CNAME' && $fqdn == $target) { $errors[] = $lng['error']['dns_srv_noalias']; break; } diff --git a/lng/english.lng.php b/lng/english.lng.php index 6783d8a8..83e7b232 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -1989,3 +1989,16 @@ $lng['serversettings']['backupenabled']['description'] = "If activated, the cust $lng['extras']['path_protection_label'] = 'Important'; $lng['extras']['path_protection_info'] = 'We strongly recommend protecting the given path, see "Extras" -> "Directory protection"'; $lng['tasks']['backup_customerfiles'] = 'Backup job for customer %loginname%'; + +$lng['error']['dns_content_empty'] = 'No content given'; +$lng['error']['dns_arec_noipv4'] = 'No valid IP address for A-record given'; +$lng['error']['dns_mx_prioempty'] = 'Invalid MX priority given'; +$lng['error']['dns_mx_needdom'] = 'The MX content value must be a valid domain-name'; +$lng['error']['dns_mx_noalias'] = 'The MX-content value cannot be an CNAME entry.'; +$lng['error']['dns_cname_invaliddom'] = 'Invalid domain-name for CNAME record'; +$lng['error']['dns_cname_nomorerr'] = 'There already exists a resource-record with the same record-name. It cannot be used as CNAME.'; +$lng['error']['dns_srv_needdom'] = 'The SRV target value must be a valid domain-name'; +$lng['error']['dns_srv_noalias'] = 'The SRV-target value cannot be an CNAME entry.'; +$lng['error']['dns_duplicate_entry'] = 'Record already exists'; +$lng['success']['dns_record_added'] = 'Record added successfully'; +$lng['success']['dns_record_deleted'] = 'Record deleted successfully'; From e33d7b756f953681cb652538a74072ac1f9ae597 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 10 May 2016 07:37:07 +0200 Subject: [PATCH 0036/1335] add missing error-language-string; check whether domain is bind-domain Signed-off-by: Michael Kaufmann (d00p) --- dns_editor.php | 10 +++++++--- lng/english.lng.php | 2 ++ templates/Sparkle/admin/domains/domains_edit.tpl | 4 +++- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/dns_editor.php b/dns_editor.php index b56bb4f1..9fbddf2e 100644 --- a/dns_editor.php +++ b/dns_editor.php @@ -18,7 +18,7 @@ if (! defined('AREA')) */ // This file is being included in admin_domains and customer_domains - // and therefore does not need to require lib/init.php +// and therefore does not need to require lib/init.php $domain_id = isset($_GET['domain_id']) ? (int) $_GET['domain_id'] : null; @@ -29,10 +29,14 @@ $content = isset($_POST['record']['content']) ? trim($_POST['record']['content'] $ttl = isset($_POST['record']['ttl']) ? (int) $_POST['record']['ttl'] : 18000; // get domain-name -$dom_stmt = Database::prepare("SELECT domain FROM `" . TABLE_PANEL_DOMAINS . "` WHERE id = :did"); +$dom_stmt = Database::prepare("SELECT domain, isbinddomain FROM `" . TABLE_PANEL_DOMAINS . "` WHERE id = :did"); $domain = Database::pexecute_first($dom_stmt, array( 'did' => $domain_id )); + +if ($domain['isbinddomain'] != '0') { + standard_error('dns_domain_nodns'); +} $domain = $idna_convert->decode($domain['domain']); // select all entries @@ -67,7 +71,7 @@ if ($action == 'add_record' && ! empty($_POST)) { if ($type == 'A' && filter_var($content, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) === false) { $errors[] = $lng['error']['dns_arec_noipv4']; } elseif ($type == 'AAAA' && filter_var($content, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false) { - $errors[] = $lng['errors']['dns_aaaarec_noipv6']; + $errors[] = $lng['error']['dns_aaaarec_noipv6']; } elseif ($type == 'MX') { if ($prio === null || $prio < 0) { $errors[] = $lng['error']['dns_mx_prioempty']; diff --git a/lng/english.lng.php b/lng/english.lng.php index 83e7b232..976c0ae7 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -1990,8 +1990,10 @@ $lng['extras']['path_protection_label'] = 'Important {$title}  {$title} -  (edit DNS) + +  (edit DNS) + From 64d068659f4df0546a0f841075f118d39ca3f783 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 10 May 2016 10:54:59 +0200 Subject: [PATCH 0037/1335] more validation for NS and SRV records; fix display of long records Signed-off-by: Michael Kaufmann (d00p) --- dns_editor.php | 37 ++++++++++++++++++---- lng/english.lng.php | 3 ++ templates/Sparkle/dns_editor/entry_bit.tpl | 2 +- 3 files changed, 35 insertions(+), 7 deletions(-) diff --git a/dns_editor.php b/dns_editor.php index 9fbddf2e..7d500f32 100644 --- a/dns_editor.php +++ b/dns_editor.php @@ -18,7 +18,7 @@ if (! defined('AREA')) */ // This file is being included in admin_domains and customer_domains -// and therefore does not need to require lib/init.php + // and therefore does not need to require lib/init.php $domain_id = isset($_GET['domain_id']) ? (int) $_GET['domain_id'] : null; @@ -34,7 +34,7 @@ $domain = Database::pexecute_first($dom_stmt, array( 'did' => $domain_id )); -if ($domain['isbinddomain'] != '0') { +if ($domain['isbinddomain'] != '1') { standard_error('dns_domain_nodns'); } $domain = $idna_convert->decode($domain['domain']); @@ -114,6 +114,17 @@ if ($action == 'add_record' && ! empty($_POST)) { } // append trailing dot (again) $content .= '.'; + } elseif ($type == 'NS') { + // check for trailing dot + if (substr($content, - 1) == '.') { + // remove it for checks + $content = substr($content, 0, - 1); + } + if (! validateDomain($content)) { + $errors[] = $lng['error']['dns_ns_invaliddom']; + } + // append trailing dot (again) + $content .= '.'; } elseif ($type == 'TXT' && ! empty($content)) { // check that TXT content is enclosed in " " if (substr($content, 0, 1) != '"') { @@ -123,6 +134,9 @@ if ($action == 'add_record' && ! empty($_POST)) { $content .= '"'; } } elseif ($type == 'SRV') { + if ($prio === null || $prio < 0) { + $errors[] = $lng['error']['dns_srv_prioempty']; + } // check for trailing dot if (substr($content, - 1) == '.') { // remove it for checks @@ -131,7 +145,11 @@ if ($action == 'add_record' && ! empty($_POST)) { // check only last part of content, as it can look like: // _service._proto.name. TTL class SRV priority weight port target. $_split_content = explode(" ", $content); - $target = trim($_split_content[count($_split_content)-1]); + // SRV content must be [weight] [port] [target] + if (count($_split_content) != 3) { + $errors[] = $lng['error']['dns_srv_invalidcontent']; + } + $target = trim($_split_content[count($_split_content) - 1]); if (! validateDomain($target)) { $errors[] = $lng['error']['dns_srv_needdom']; } else { @@ -167,9 +185,9 @@ if ($action == 'add_record' && ! empty($_POST)) { // sort by key ksort($check_entry); // format integer fields to real integer (as they are read as string from the DB) - $check_entry['prio'] = (int)$check_entry['prio']; - $check_entry['ttl'] = (int)$check_entry['ttl']; - $check_entry['domain_id'] = (int)$check_entry['domain_id']; + $check_entry['prio'] = (int) $check_entry['prio']; + $check_entry['ttl'] = (int) $check_entry['ttl']; + $check_entry['domain_id'] = (int) $check_entry['domain_id']; // serialize both $check_entry = serialize($check_entry); $new = serialize($new_entry); @@ -202,6 +220,12 @@ if ($action == 'add_record' && ! empty($_POST)) { // success message (inline) $success_message = $lng['success']['dns_record_added']; + + unset($record); + unset($type); + unset($prio); + unset($content); + unset($ttl); } else { // show $errors $errors = implode("
", $errors); @@ -238,6 +262,7 @@ $entriescount = 0; if (! empty($dom_entries)) { $entriescount = count($dom_entries); foreach ($dom_entries as $entry) { + $entry['content'] = wordwrap($entry['content'], 100, '
', true); eval("\$existing_entries.=\"" . getTemplate("dns_editor/entry_bit", true) . "\";"); } } diff --git a/lng/english.lng.php b/lng/english.lng.php index 976c0ae7..a42edaf9 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -1999,6 +1999,9 @@ $lng['error']['dns_mx_needdom'] = 'The MX content value must be a valid domain-n $lng['error']['dns_mx_noalias'] = 'The MX-content value cannot be an CNAME entry.'; $lng['error']['dns_cname_invaliddom'] = 'Invalid domain-name for CNAME record'; $lng['error']['dns_cname_nomorerr'] = 'There already exists a resource-record with the same record-name. It cannot be used as CNAME.'; +$lng['error']['dns_ns_invaliddom'] = 'Invalid domain-name for NS record'; +$lng['error']['dns_srv_prioempty'] = 'Invalid SRV priority given'; +$lng['error']['dns_srv_invalidcontent'] = 'Invalid SRV content, must contain of fields weight, port and target, e.g.: 5 5060 sipserver.example.com.'; $lng['error']['dns_srv_needdom'] = 'The SRV target value must be a valid domain-name'; $lng['error']['dns_srv_noalias'] = 'The SRV-target value cannot be an CNAME entry.'; $lng['error']['dns_duplicate_entry'] = 'Record already exists'; diff --git a/templates/Sparkle/dns_editor/entry_bit.tpl b/templates/Sparkle/dns_editor/entry_bit.tpl index 1e80ff67..1882b0ba 100644 --- a/templates/Sparkle/dns_editor/entry_bit.tpl +++ b/templates/Sparkle/dns_editor/entry_bit.tpl @@ -1,7 +1,7 @@ {$entry['record']} {$entry['type']} -  {$entry['prio']} +  {$entry['prio']} {$entry['content']} {$entry['ttl']} From bb9331904efa80cae5563f8e57aa1614236c9f4a Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 10 May 2016 11:55:03 +0200 Subject: [PATCH 0038/1335] (really) fix PHP notice #2048 Only variables should be passed by reference, thx to baudetail Signed-off-by: Michael Kaufmann (d00p) --- admin_customers.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/admin_customers.php b/admin_customers.php index dd7add11..0c96941c 100644 --- a/admin_customers.php +++ b/admin_customers.php @@ -940,7 +940,8 @@ if ($page == 'customers' WHERE `id` = :defaultip "); $default_ips = Settings::Get('system.defaultip'); - $srv_ip = Database::pexecute_first($srv_ip_stmt, array('defaultip' => reset(explode(',', $default_ips)))); + $default_ips = explode(',', $default_ips); + $srv_ip = Database::pexecute_first($srv_ip_stmt, array('defaultip' => reset($default_ips))); $replace_arr = array( 'FIRSTNAME' => $firstname, From 407a7c01aa68ed3fb0804ac5bdaae8209691f47e Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 10 May 2016 14:11:01 +0200 Subject: [PATCH 0039/1335] add createDomainZone function for new dns-editor; dump zone below the editor for testing purposes; all required entries that are not custom entered will be auto-generated like the bind-cron does this for now Signed-off-by: Michael Kaufmann (d00p) --- dns_editor.php | 2 + .../dns/function.createDomainZone.php | 213 ++++++++++++++++++ templates/Sparkle/dns_editor/index.tpl | 4 + 3 files changed, 219 insertions(+) create mode 100644 lib/functions/dns/function.createDomainZone.php diff --git a/dns_editor.php b/dns_editor.php index 7d500f32..2ce58c78 100644 --- a/dns_editor.php +++ b/dns_editor.php @@ -283,4 +283,6 @@ foreach ($type_select_values as $_type) { } eval("\$record_list=\"" . getTemplate("dns_editor/list", true) . "\";"); + +$zonefile = createDomainZone($domain_id); eval("echo \"" . getTemplate("dns_editor/index", true) . "\";"); diff --git a/lib/functions/dns/function.createDomainZone.php b/lib/functions/dns/function.createDomainZone.php new file mode 100644 index 00000000..9775ac7a --- /dev/null +++ b/lib/functions/dns/function.createDomainZone.php @@ -0,0 +1,213 @@ + (2016-) + * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt + * @package Functions + * + */ +function createDomainZone($domain_id) +{ + // get domain-name + $dom_stmt = Database::prepare("SELECT * FROM `" . TABLE_PANEL_DOMAINS . "` WHERE id = :did"); + $domain = Database::pexecute_first($dom_stmt, array( + 'did' => $domain_id + )); + + if ($domain['isbinddomain'] != '1') { + return; + } + + // select all entries + $sel_stmt = Database::prepare("SELECT * FROM `" . TABLE_DOMAIN_DNS . "` WHERE domain_id = :did ORDER BY id ASC"); + Database::pexecute($sel_stmt, array( + 'did' => $domain_id + )); + $dom_entries = $sel_stmt->fetchAll(PDO::FETCH_ASSOC); + + // @TODO alias domains + + // TODO for now, dummy time-periods + $soa_content = getPrimaryNs($dom_entries) . " " . str_replace('@', '.', Settings::Get('panel.adminmail')) . ". (" . PHP_EOL; + $soa_content .= $domain['bindserial'] . "\t; serial" . PHP_EOL; + $soa_content .= "1800\t; refresh (30 mins)" . PHP_EOL; + $soa_content .= "900\t; retry (15 mins)" . PHP_EOL; + $soa_content .= "604800\t; expire (7 days)" . PHP_EOL; + $soa_content .= "1200\t)\t; minimum (20 mins)"; + + // create Zone + $zonefile = "\$TTL " . (int) Settings::Get('system.defaultttl') . PHP_EOL; + $zonefile .= "\$ORIGIN " . $domain['domain'] . "." . PHP_EOL; + $zonefile .= formatEntry('@', 'SOA', $soa_content); + + // check for required records + $required_entries = array(); + + addRequiredEntry('@', 'A', $required_entries); + addRequiredEntry('@', 'AAAA', $required_entries); + addRequiredEntry('@', 'NS', $required_entries); + if ($domain['isemaildomain'] === '1') { + addRequiredEntry('@', 'MX', $required_entries); + } + + // additional required records by setting + if ($domain['iswildcarddomain'] == '1') { + addRequiredEntry('*', 'A', $required_entries); + addRequiredEntry('*', 'AAAA', $required_entries); + } else + if ($domain['wwwserveralias'] == '1') { + addRequiredEntry('www', 'A', $required_entries); + addRequiredEntry('www', 'AAAA', $required_entries); + } + + // additional required records for subdomains + $subdomains_stmt = Database::prepare(" + SELECT `domain` FROM `" . TABLE_PANEL_DOMAINS . "` + WHERE `parentdomainid` = :domainid + "); + Database::pexecute($subdomains_stmt, array( + 'domainid' => $domain_id + )); + + while ($subdomain = $subdomains_stmt->fetch(PDO::FETCH_ASSOC)) { + // Listing domains is enough as there currently is no support for choosing + // different ips for a subdomain => use same IPs as toplevel + addRequiredEntry(str_replace('.' . $domain['domain'], '', $subdomain['domain']), 'A', $required_entries); + addRequiredEntry(str_replace('.' . $domain['domain'], '', $subdomain['domain']), 'AAAA', $required_entries); + + // Check whether to add a www.-prefix + if ($domain['iswildcarddomain'] == '1') { + addRequiredEntry('*.' . str_replace('.' . $domain['domain'], '', $subdomain['domain']), 'A', $required_entries); + addRequiredEntry('*.' . str_replace('.' . $domain['domain'], '', $subdomain['domain']), 'AAAA', $required_entries); + } elseif ($domain['wwwserveralias'] == '1') { + addRequiredEntry('www.' . str_replace('.' . $domain['domain'], '', $subdomain['domain']), 'A', $required_entries); + addRequiredEntry('www.' . str_replace('.' . $domain['domain'], '', $subdomain['domain']), 'AAAA', $required_entries); + } + } + + // now generate all records and unset the required entries we have + foreach ($dom_entries as $entry) { + if (array_key_exists($entry['type'], $required_entries) && $required_entries[$entry['type']][md5($entry['record'])] == $entry['record']) { + unset($required_entries[$entry['type']][md5($entry['record'])]); + } + $zonefile .= formatEntry($entry['record'], $entry['type'], $entry['content'], $entry['prio'], $entry['ttl']); + } + + // add missing required entries + if (! empty($required_entries)) { + + // A / AAAA records + if (array_key_exists("A", $required_entries) || array_key_exists("AAAA", $required_entries)) { + $result_ip_stmt = Database::prepare(" + SELECT `p`.`ip` AS `ip` + FROM `" . TABLE_PANEL_IPSANDPORTS . "` `p`, `" . TABLE_DOMAINTOIP . "` `di` + WHERE `di`.`id_domain` = :domainid AND `p`.`id` = `di`.`id_ipandports` + GROUP BY `p`.`ip`; + "); + Database::pexecute($result_ip_stmt, array( + 'domainid' => $domain_id + )); + $all_ips = $result_ip_stmt->fetchAll(PDO::FETCH_ASSOC); + + foreach ($all_ips as $ip) { + foreach ($required_entries as $type => $records) { + foreach ($records as $record) { + if ($type == 'A' && filter_var($ip['ip'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false) { + $zonefile .= formatEntry($record, 'A', $ip['ip']); + } elseif ($type == 'AAAA' && filter_var($ip['ip'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) !== false) { + $zonefile .= formatEntry($record, 'AAAA', $ip['ip']); + } + } + } + unset($required_entries['A']); + unset($required_entries['AAAA']); + } + } + + // NS records + if (array_key_exists("NS", $required_entries)) { + if (Settings::Get('system.nameservers') != '') { + $nameservers = explode(',', Settings::Get('system.nameservers')); + foreach ($nameservers as $nameserver) { + $nameserver = trim($nameserver); + // append dot to hostname + if (substr($nameserver, - 1, 1) != '.') { + $nameserver .= '.'; + } + foreach ($required_entries as $type => $records) { + if ($type == 'NS') { + foreach ($records as $record) { + $zonefile .= formatEntry($record, 'NS', $nameserver); + } + } + } + } + unset($required_entries['NS']); + } + } + + // MX records + if (array_key_exists("MX", $required_entries)) { + if (Settings::Get('system.mxservers') != '') { + $mxservers = explode(',', Settings::Get('system.mxservers')); + foreach ($mxservers as $mxserver) { + if (substr($mxserver, - 1, 1) != '.') { + $mxserver .= '.'; + } + // split in prio and server + $mx_details = explode(" ", $mxserver); + if (count($mx_details) == 1) { + $mx_details[1] = $mx_details[0]; + $mx_details[0] = 10; + } + foreach ($required_entries as $type => $records) { + if ($type == 'MX') { + foreach ($records as $record) { + $zonefile .= formatEntry($record, 'MX', $mx_details[1], $mx_details[0]); + } + } + } + } + unset($required_entries['MX']); + } + } + } + + var_dump($required_entries); + + return $zonefile; +} + +function formatEntry($record = '@', $type = 'A', $content = null, $prio = 0, $ttl = 18000, $class = 'IN') +{ + $result = $record . "\t" . $ttl . "\t" . $class . "\t" . $type . "\t" . (($prio >= 0 && ($type == 'MX' || $type == 'SRV')) ? $prio . "\t" : "") . $content . PHP_EOL; + return $result; +} + +function addRequiredEntry($record = '@', $type = 'A', &$required) +{ + if (!isset($required[$type])) { + $required[$type] = array(); + } + $required[$type][md5($record)] = $record; +} + +function getPrimaryNs($dom_entries) +{ + // go through all records and use the first NS record as primary NS + foreach ($dom_entries as $entry) { + if ($entry['type'] == 'NS') { + return $entry['content']; + } + } + // FIXME use default from settings somehow if none given? + return 'no.dns-server.given.tld.'; +} diff --git a/templates/Sparkle/dns_editor/index.tpl b/templates/Sparkle/dns_editor/index.tpl index 92f37970..737873cb 100644 --- a/templates/Sparkle/dns_editor/index.tpl +++ b/templates/Sparkle/dns_editor/index.tpl @@ -25,4 +25,8 @@ $header + +

+ + $footer From b775c2f60ea15de3c4c6da86705402f911dedf82 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 10 May 2016 19:20:01 +0200 Subject: [PATCH 0040/1335] minor fixes Signed-off-by: Michael Kaufmann (d00p) --- dns_editor.php | 10 +++++----- lib/functions/dns/function.createDomainZone.php | 4 +--- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/dns_editor.php b/dns_editor.php index 2ce58c78..46a57ce7 100644 --- a/dns_editor.php +++ b/dns_editor.php @@ -221,11 +221,11 @@ if ($action == 'add_record' && ! empty($_POST)) { // success message (inline) $success_message = $lng['success']['dns_record_added']; - unset($record); - unset($type); - unset($prio); - unset($content); - unset($ttl); + $record = ""; + $type = 'A'; + $prio = ""; + $content = ""; + $ttl = ""; } else { // show $errors $errors = implode("
", $errors); diff --git a/lib/functions/dns/function.createDomainZone.php b/lib/functions/dns/function.createDomainZone.php index 9775ac7a..dd4e585b 100644 --- a/lib/functions/dns/function.createDomainZone.php +++ b/lib/functions/dns/function.createDomainZone.php @@ -95,7 +95,7 @@ function createDomainZone($domain_id) // now generate all records and unset the required entries we have foreach ($dom_entries as $entry) { - if (array_key_exists($entry['type'], $required_entries) && $required_entries[$entry['type']][md5($entry['record'])] == $entry['record']) { + if (array_key_exists($entry['type'], $required_entries) && array_key_exists(md5($entry['record']), $required_entries[$entry['type']])) { unset($required_entries[$entry['type']][md5($entry['record'])]); } $zonefile .= formatEntry($entry['record'], $entry['type'], $entry['content'], $entry['prio'], $entry['ttl']); @@ -181,8 +181,6 @@ function createDomainZone($domain_id) } } - var_dump($required_entries); - return $zonefile; } From d1106dd9844370670689c05b94e5b6c7f58f8225 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Wed, 11 May 2016 07:44:32 +0200 Subject: [PATCH 0041/1335] fix wrong unset of array-element Signed-off-by: Michael Kaufmann (d00p) --- dns_editor.php | 2 ++ lib/functions/dns/function.createDomainZone.php | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/dns_editor.php b/dns_editor.php index 46a57ce7..ca53b698 100644 --- a/dns_editor.php +++ b/dns_editor.php @@ -59,6 +59,8 @@ if ($action == 'add_record' && ! empty($_POST)) { $record = strtolower($record); + // TODO regex validate record and content for invalid characters + if ($ttl <= 0) { $ttl = 18000; } diff --git a/lib/functions/dns/function.createDomainZone.php b/lib/functions/dns/function.createDomainZone.php index dd4e585b..86ffa75c 100644 --- a/lib/functions/dns/function.createDomainZone.php +++ b/lib/functions/dns/function.createDomainZone.php @@ -127,9 +127,9 @@ function createDomainZone($domain_id) } } } - unset($required_entries['A']); - unset($required_entries['AAAA']); } + unset($required_entries['A']); + unset($required_entries['AAAA']); } // NS records From 7379398d229b07d043768c7364fc1fde693af48f Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Wed, 11 May 2016 13:47:36 +0200 Subject: [PATCH 0042/1335] set correct primary nameserver for SOA record Signed-off-by: Michael Kaufmann (d00p) --- .../dns/function.createDomainZone.php | 56 ++++++++++--------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/lib/functions/dns/function.createDomainZone.php b/lib/functions/dns/function.createDomainZone.php index 86ffa75c..0ddf9e39 100644 --- a/lib/functions/dns/function.createDomainZone.php +++ b/lib/functions/dns/function.createDomainZone.php @@ -33,21 +33,6 @@ function createDomainZone($domain_id) )); $dom_entries = $sel_stmt->fetchAll(PDO::FETCH_ASSOC); - // @TODO alias domains - - // TODO for now, dummy time-periods - $soa_content = getPrimaryNs($dom_entries) . " " . str_replace('@', '.', Settings::Get('panel.adminmail')) . ". (" . PHP_EOL; - $soa_content .= $domain['bindserial'] . "\t; serial" . PHP_EOL; - $soa_content .= "1800\t; refresh (30 mins)" . PHP_EOL; - $soa_content .= "900\t; retry (15 mins)" . PHP_EOL; - $soa_content .= "604800\t; expire (7 days)" . PHP_EOL; - $soa_content .= "1200\t)\t; minimum (20 mins)"; - - // create Zone - $zonefile = "\$TTL " . (int) Settings::Get('system.defaultttl') . PHP_EOL; - $zonefile .= "\$ORIGIN " . $domain['domain'] . "." . PHP_EOL; - $zonefile .= formatEntry('@', 'SOA', $soa_content); - // check for required records $required_entries = array(); @@ -93,11 +78,18 @@ function createDomainZone($domain_id) } } + $primary_ns = null; + $zonefile = ""; + // now generate all records and unset the required entries we have foreach ($dom_entries as $entry) { if (array_key_exists($entry['type'], $required_entries) && array_key_exists(md5($entry['record']), $required_entries[$entry['type']])) { unset($required_entries[$entry['type']][md5($entry['record'])]); } + if (empty($primary_ns) && $entry['type'] == 'NS') { + // use the first NS entry as primary ns + $primary_ns = $entry['content']; + } $zonefile .= formatEntry($entry['record'], $entry['type'], $entry['content'], $entry['prio'], $entry['ttl']); } @@ -145,6 +137,10 @@ function createDomainZone($domain_id) foreach ($required_entries as $type => $records) { if ($type == 'NS') { foreach ($records as $record) { + if (empty($primary_ns)) { + // use the first NS entry as primary ns + $primary_ns = $nameserver; + } $zonefile .= formatEntry($record, 'NS', $nameserver); } } @@ -181,6 +177,24 @@ function createDomainZone($domain_id) } } + if (empty($primary_ns)) { + // TODO log error: no NS given, use system-hostname + $primary_ns = Settings::Get('system.hostname'); + } + + // TODO for now, dummy time-periods + $soa_content = $primary_ns . " " . str_replace('@', '.', Settings::Get('panel.adminmail')) . ". (" . PHP_EOL; + $soa_content .= $domain['bindserial'] . "\t; serial" . PHP_EOL; + $soa_content .= "1800\t; refresh (30 mins)" . PHP_EOL; + $soa_content .= "900\t; retry (15 mins)" . PHP_EOL; + $soa_content .= "604800\t; expire (7 days)" . PHP_EOL; + $soa_content .= "1200\t)\t; minimum (20 mins)"; + + $_zonefile = "\$TTL " . (int) Settings::Get('system.defaultttl') . PHP_EOL; + $_zonefile .= "\$ORIGIN " . $domain['domain'] . "." . PHP_EOL; + $_zonefile .= formatEntry('@', 'SOA', $soa_content); + $zonefile = $_zonefile.$zonefile; + return $zonefile; } @@ -197,15 +211,3 @@ function addRequiredEntry($record = '@', $type = 'A', &$required) } $required[$type][md5($record)] = $record; } - -function getPrimaryNs($dom_entries) -{ - // go through all records and use the first NS record as primary NS - foreach ($dom_entries as $entry) { - if ($entry['type'] == 'NS') { - return $entry['content']; - } - } - // FIXME use default from settings somehow if none given? - return 'no.dns-server.given.tld.'; -} From 9b5ce83e8b4ccbee750866e6cae0d3918dbce3da Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Wed, 11 May 2016 16:11:23 +0200 Subject: [PATCH 0043/1335] add testing-bind-cron for new dns-stuff (not activated); added main-but-subdomain-stuff Signed-off-by: Michael Kaufmann (d00p) --- .../dns/function.createDomainZone.php | 35 ++- scripts/jobs/cron_tasks.inc.dns.15.bind.php | 214 ++++++++++++++++++ 2 files changed, 242 insertions(+), 7 deletions(-) create mode 100644 scripts/jobs/cron_tasks.inc.dns.15.bind.php diff --git a/lib/functions/dns/function.createDomainZone.php b/lib/functions/dns/function.createDomainZone.php index 0ddf9e39..aa0b4cc0 100644 --- a/lib/functions/dns/function.createDomainZone.php +++ b/lib/functions/dns/function.createDomainZone.php @@ -14,7 +14,7 @@ * @package Functions * */ -function createDomainZone($domain_id) +function createDomainZone($domain_id, $froxlorhostname = false) { // get domain-name $dom_stmt = Database::prepare("SELECT * FROM `" . TABLE_PANEL_DOMAINS . "` WHERE id = :did"); @@ -78,6 +78,20 @@ function createDomainZone($domain_id) } } + // additional required records for main-but-subdomain-to + $mainbutsub_stmt = Database::prepare(" + SELECT `domain` FROM `" . TABLE_PANEL_DOMAINS . "` + WHERE `ismainbutsubto` = :domainid + "); + Database::pexecute($mainbutsub_stmt, array( + 'domainid' => $domain_id + )); + + while ($mainbutsubtodomain = $mainbutsub_stmt->fetch(PDO::FETCH_ASSOC)) { + // Add NS entry for subdomain-records of "main-but-subdomain-to"-domains, they get their own Zone + addRequiredEntry(str_replace('.' . $domain['domain'], '', $mainbutsubtodomain['domain']), 'NS', $required_entries); + } + $primary_ns = null; $zonefile = ""; @@ -98,12 +112,19 @@ function createDomainZone($domain_id) // A / AAAA records if (array_key_exists("A", $required_entries) || array_key_exists("AAAA", $required_entries)) { - $result_ip_stmt = Database::prepare(" - SELECT `p`.`ip` AS `ip` - FROM `" . TABLE_PANEL_IPSANDPORTS . "` `p`, `" . TABLE_DOMAINTOIP . "` `di` - WHERE `di`.`id_domain` = :domainid AND `p`.`id` = `di`.`id_ipandports` - GROUP BY `p`.`ip`; - "); + if ($froxlorhostname) { + // use all available IP's for the froxlor-hostname + $result_ip_stmt = Database::prepare(" + SELECT `ip` FROM `".TABLE_PANEL_IPSANDPORTS."` GROUP BY `ip` + "); + } else { + $result_ip_stmt = Database::prepare(" + SELECT `p`.`ip` AS `ip` + FROM `" . TABLE_PANEL_IPSANDPORTS . "` `p`, `" . TABLE_DOMAINTOIP . "` `di` + WHERE `di`.`id_domain` = :domainid AND `p`.`id` = `di`.`id_ipandports` + GROUP BY `p`.`ip`; + "); + } Database::pexecute($result_ip_stmt, array( 'domainid' => $domain_id )); diff --git a/scripts/jobs/cron_tasks.inc.dns.15.bind.php b/scripts/jobs/cron_tasks.inc.dns.15.bind.php new file mode 100644 index 00000000..5918266a --- /dev/null +++ b/scripts/jobs/cron_tasks.inc.dns.15.bind.php @@ -0,0 +1,214 @@ + (2016-) + * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt + * @package Cron + * + */ +class bind2 +{ + + private $_logger = false; + + private $_ns = array(); + + private $_mx = array(); + + private $_axfr = array(); + + public function __construct($logger) + { + $this->_logger = $logger; + + if (Settings::Get('system.nameservers') != '') { + $nameservers = explode(',', Settings::Get('system.nameservers')); + foreach ($nameservers as $nameserver) { + $nameserver = trim($nameserver); + // DNS servers might be multi homed; allow transfer from all ip + // addresses of the DNS server + $nameserver_ips = gethostbynamel($nameserver); + // append dot to hostname + if (substr($nameserver, - 1, 1) != '.') { + $nameserver .= '.'; + } + // ignore invalid responses + if (! is_array($nameserver_ips)) { + // act like gethostbyname() and return unmodified hostname on error + $nameserver_ips = array( + $nameserver + ); + } + $this->_ns[] = array( + 'hostname' => $nameserver, + 'ips' => $nameserver_ips + ); + } + } + + if (Settings::Get('system.mxservers') != '') { + $mxservers = explode(',', Settings::Get('system.mxservers')); + foreach ($mxservers as $mxserver) { + if (substr($mxserver, - 1, 1) != '.') { + $mxserver .= '.'; + } + $this->_mx[] = $mxserver; + } + } + + // AXFR server #100 + if (Settings::Get('system.axfrservers') != '') { + $axfrservers = explode(',', Settings::Get('system.axfrservers')); + foreach ($axfrservers as $axfrserver) { + $this->_axfr[] = trim($axfrserver); + } + } + } + + public function writeConfigs() + { + // tell the world what we are doing + $this->_logger->logAction(CRON_ACTION, LOG_INFO, 'Task4 started - Rebuilding froxlor_bind.conf'); + + // clean up + $this->_cleanZonefiles(); + + // check for subfolder in bind-config-directory + if (! file_exists(makeCorrectDir(Settings::Get('system.bindconf_directory') . '/domains/'))) { + $this->logger->logAction(CRON_ACTION, LOG_NOTICE, 'mkdir ' . escapeshellarg(makeCorrectDir(Settings::Get('system.bindconf_directory') . '/domains/'))); + safe_exec('mkdir -p ' . escapeshellarg(makeCorrectDir(Settings::Get('system.bindconf_directory') . '/domains/'))); + } + + // get all Domains + $result_domains_stmt = Database::query(" + SELECT `d`.`id`, `d`.`domain`, `d`.`customerid`, `d`.`zonefile`, `c`.`loginname`, `c`.`guid` + FROM `" . TABLE_PANEL_DOMAINS . "` `d` LEFT JOIN `" . TABLE_PANEL_CUSTOMERS . "` `c` USING(`customerid`) + WHERE `d`.`isbinddomain` = '1' ORDER BY `d`.`domain` ASC + "); + + $domains = $result_domains_stmt->fetchAll(PDO::FETCH_ASSOC); + + // frolxor-hostname (#1090) + if (Settings::get('system.dns_createhostnameentry') == 1) { + $hostname_arr = array( + 'id' => 'none', + 'domain' => Settings::Get('system.hostname'), + 'isemaildomain' => Settings::Get('system.dns_createmailentry'), + 'customerid' => 'none', + 'loginname' => 'froxlor.panel', + 'bindserial' => date('Ymd') . '00', + 'dkim' => '0', + 'iswildcarddomain' => '1', + 'ismainbutsubto' => '0', + 'zonefile' => '', + 'froxlorhost' => '1' + ); + $domains['none'] = $hostname_arr; + } + + if (empty($domains)) { + $this->logger->logAction(CRON_ACTION, LOG_INFO, 'No domains found for nameserver-config, skipping...'); + return; + } + + $bindconf_file = '# ' . Settings::Get('system.bindconf_directory') . 'froxlor_bind.conf' . "\n" . '# Created ' . date('d.m.Y H:i') . "\n" . '# Do NOT manually edit this file, all changes will be deleted after the next domain change at the panel.' . "\n\n"; + + foreach ($domains as $domain) { + // check for system-hostname + $isFroxlorHostname = false; + if (isset($domain['froxlorhost']) && $domain['froxlorhost'] == 1) { + $isFroxlorHostname = true; + } + // create zone-file + $zonefile = createDomainZone($domain, $isFroxlorHostname); + $domain['zonefile'] = 'domains/' . $domain['domain'] . '.zone'; + $zonefile_name = makeCorrectFile(Settings::Get('system.bindconf_directory') . '/' . $domain['zonefile']); + $zonefile_handler = fopen($zonefile_name, 'w'); + fwrite($zonefile_handler, $zonefile); + fclose($zonefile_handler); + $this->logger->logAction(CRON_ACTION, LOG_INFO, '`' . $zonefile_name . '` zone written'); + + // generate config + $bindconf_file .= $this->_generateDomainConfig($domain); + } + + // write config + $bindconf_file_handler = fopen(makeCorrectFile(Settings::Get('system.bindconf_directory') . '/froxlor_bind.conf'), 'w'); + fwrite($bindconf_file_handler, $bindconf_file); + fclose($bindconf_file_handler); + $this->logger->logAction(CRON_ACTION, LOG_INFO, 'froxlor_bind.conf written'); + + // reload Bind + safe_exec(escapeshellcmd(Settings::Get('system.bindreload_command'))); + $this->logger->logAction(CRON_ACTION, LOG_INFO, 'Bind9 reloaded'); + } + + private function _generateDomainConfig($domain = array()) + { + $bindconf_file = '# Domain ID: ' . $domain['id'] . ' - CustomerID: ' . $domain['customerid'] . ' - CustomerLogin: ' . $domain['loginname'] . "\n"; + $bindconf_file .= 'zone "' . $domain['domain'] . '" in {' . "\n"; + $bindconf_file .= ' type master;' . "\n"; + $bindconf_file .= ' file "' . makeCorrectFile(Settings::Get('system.bindconf_directory') . '/' . $domain['zonefile']) . '";' . "\n"; + $bindconf_file .= ' allow-query { any; };' . "\n"; + + if (count($this->nameservers) > 0 || count($this->axfrservers) > 0) { + // open allow-transfer + $bindconf_file .= ' allow-transfer {' . "\n"; + // put nameservers in allow-transfer + if (count($this->nameservers) > 0) { + foreach ($this->nameservers as $ns) { + foreach ($ns["ips"] as $ip) { + $bindconf_file .= ' ' . $ip . ";\n"; + } + } + } + // AXFR server #100 + if (count($this->axfrservers) > 0) { + foreach ($this->axfrservers as $axfrserver) { + if (validate_ip($axfrserver, true) !== false) { + $bindconf_file .= ' ' . $axfrserver . ';' . "\n"; + } + } + } + // close allow-transfer + $bindconf_file .= ' };' . "\n"; + } + + $bindconf_file .= '};' . "\n"; + $bindconf_file .= "\n"; + + return $bindconf_file; + } + + private function _cleanZonefiles() + { + $config_dir = makeCorrectFile(Settings::Get('system.bindconf_directory') . '/domains/'); + + $this->_logger->logAction(CRON_ACTION, LOG_INFO, 'Cleaning dns zone files from '.$config_dir); + + // check directory + if (@is_dir($config_dir)) { + + // create directory iterator + $its = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($config_dir)); + + // iterate through all subdirs, look for zone files and delete them + foreach ($its as $it) { + if ($it->isFile()) { + // remove file + safe_exec('rm -f ' . escapeshellarg(makeCorrectFile($its->getPathname()))); + } + } + } + } +} From 0404618c24a66dd150c6e672c6b4ef23ccd910b1 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Wed, 11 May 2016 18:10:17 +0200 Subject: [PATCH 0044/1335] add experimental bind-cron for testing purposes Signed-off-by: Michael Kaufmann (d00p) --- scripts/jobs/cron_tasks.inc.dns.15.bind.php | 25 ++++++++++++--------- scripts/jobs/cron_tasks.php | 8 +++++-- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/scripts/jobs/cron_tasks.inc.dns.15.bind.php b/scripts/jobs/cron_tasks.inc.dns.15.bind.php index 5918266a..0e423951 100644 --- a/scripts/jobs/cron_tasks.inc.dns.15.bind.php +++ b/scripts/jobs/cron_tasks.inc.dns.15.bind.php @@ -85,7 +85,7 @@ class bind2 // check for subfolder in bind-config-directory if (! file_exists(makeCorrectDir(Settings::Get('system.bindconf_directory') . '/domains/'))) { - $this->logger->logAction(CRON_ACTION, LOG_NOTICE, 'mkdir ' . escapeshellarg(makeCorrectDir(Settings::Get('system.bindconf_directory') . '/domains/'))); + $this->_logger->logAction(CRON_ACTION, LOG_NOTICE, 'mkdir ' . escapeshellarg(makeCorrectDir(Settings::Get('system.bindconf_directory') . '/domains/'))); safe_exec('mkdir -p ' . escapeshellarg(makeCorrectDir(Settings::Get('system.bindconf_directory') . '/domains/'))); } @@ -117,7 +117,7 @@ class bind2 } if (empty($domains)) { - $this->logger->logAction(CRON_ACTION, LOG_INFO, 'No domains found for nameserver-config, skipping...'); + $this->_logger->logAction(CRON_ACTION, LOG_INFO, 'No domains found for nameserver-config, skipping...'); return; } @@ -130,13 +130,14 @@ class bind2 $isFroxlorHostname = true; } // create zone-file - $zonefile = createDomainZone($domain, $isFroxlorHostname); + $this->_logger->logAction(CRON_ACTION, LOG_DEBUG, 'Generating dns zone for '.$domain['domain']); + $zonefile = createDomainZone($domain['id'], $isFroxlorHostname); $domain['zonefile'] = 'domains/' . $domain['domain'] . '.zone'; $zonefile_name = makeCorrectFile(Settings::Get('system.bindconf_directory') . '/' . $domain['zonefile']); $zonefile_handler = fopen($zonefile_name, 'w'); fwrite($zonefile_handler, $zonefile); fclose($zonefile_handler); - $this->logger->logAction(CRON_ACTION, LOG_INFO, '`' . $zonefile_name . '` zone written'); + $this->_logger->logAction(CRON_ACTION, LOG_INFO, '`' . $zonefile_name . '` zone written'); // generate config $bindconf_file .= $this->_generateDomainConfig($domain); @@ -146,35 +147,37 @@ class bind2 $bindconf_file_handler = fopen(makeCorrectFile(Settings::Get('system.bindconf_directory') . '/froxlor_bind.conf'), 'w'); fwrite($bindconf_file_handler, $bindconf_file); fclose($bindconf_file_handler); - $this->logger->logAction(CRON_ACTION, LOG_INFO, 'froxlor_bind.conf written'); + $this->_logger->logAction(CRON_ACTION, LOG_INFO, 'froxlor_bind.conf written'); // reload Bind safe_exec(escapeshellcmd(Settings::Get('system.bindreload_command'))); - $this->logger->logAction(CRON_ACTION, LOG_INFO, 'Bind9 reloaded'); + $this->_logger->logAction(CRON_ACTION, LOG_INFO, 'Bind9 reloaded'); } private function _generateDomainConfig($domain = array()) { + $this->_logger->logAction(CRON_ACTION, LOG_DEBUG, 'Generating dns config for '.$domain['domain']); + $bindconf_file = '# Domain ID: ' . $domain['id'] . ' - CustomerID: ' . $domain['customerid'] . ' - CustomerLogin: ' . $domain['loginname'] . "\n"; $bindconf_file .= 'zone "' . $domain['domain'] . '" in {' . "\n"; $bindconf_file .= ' type master;' . "\n"; $bindconf_file .= ' file "' . makeCorrectFile(Settings::Get('system.bindconf_directory') . '/' . $domain['zonefile']) . '";' . "\n"; $bindconf_file .= ' allow-query { any; };' . "\n"; - if (count($this->nameservers) > 0 || count($this->axfrservers) > 0) { + if (count($this->_ns) > 0 || count($this->_axfr) > 0) { // open allow-transfer $bindconf_file .= ' allow-transfer {' . "\n"; // put nameservers in allow-transfer - if (count($this->nameservers) > 0) { - foreach ($this->nameservers as $ns) { + if (count($this->_ns) > 0) { + foreach ($this->_ns as $ns) { foreach ($ns["ips"] as $ip) { $bindconf_file .= ' ' . $ip . ";\n"; } } } // AXFR server #100 - if (count($this->axfrservers) > 0) { - foreach ($this->axfrservers as $axfrserver) { + if (count($this->_axfr) > 0) { + foreach ($this->_axfr as $axfrserver) { if (validate_ip($axfrserver, true) !== false) { $bindconf_file .= ' ' . $axfrserver . ';' . "\n"; } diff --git a/scripts/jobs/cron_tasks.php b/scripts/jobs/cron_tasks.php index 45020038..9a175892 100644 --- a/scripts/jobs/cron_tasks.php +++ b/scripts/jobs/cron_tasks.php @@ -19,6 +19,7 @@ // necessary includes require_once makeCorrectFile(dirname(__FILE__) . '/cron_tasks.inc.dns.10.bind.php'); +require_once makeCorrectFile(dirname(__FILE__) . '/cron_tasks.inc.dns.15.bind.php'); require_once makeCorrectFile(dirname(__FILE__) . '/cron_tasks.inc.http.10.apache.php'); require_once makeCorrectFile(dirname(__FILE__) . '/cron_tasks.inc.http.15.apache_fcgid.php'); require_once makeCorrectFile(dirname(__FILE__) . '/cron_tasks.inc.http.20.lighttpd.php'); @@ -179,11 +180,14 @@ while ($row = $result_tasks_stmt->fetch(PDO::FETCH_ASSOC)) { * TYPE=4 MEANS THAT SOMETHING IN THE BIND CONFIG HAS CHANGED. REBUILD froxlor_bind.conf IF BIND IS ENABLED */ elseif ($row['type'] == '4' && (int)Settings::Get('system.bind_enable') != 0) { + + $bindclass ="bind2"; + if (!isset($nameserver)) { - $nameserver = new bind($cronlog); + $nameserver = new $bindclass($cronlog); } - if (Settings::Get('dkim.use_dkim') == '1') { + if (Settings::Get('dkim.use_dkim') == '1' && $bindclass == 'bind') { $nameserver->writeDKIMconfigs(); } From 68d579b6290e959038392a1dbfbc1df249f154ff Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 12 May 2016 09:36:33 +0200 Subject: [PATCH 0045/1335] add SPF and DKIM stuff to DNS Signed-off-by: Michael Kaufmann (d00p) --- dns_editor.php | 7 +- .../dns/function.createDomainZone.php | 143 ++++++++++++++++-- scripts/jobs/cron_tasks.inc.dns.15.bind.php | 86 ++++++++++- scripts/jobs/cron_tasks.php | 2 +- 4 files changed, 219 insertions(+), 19 deletions(-) diff --git a/dns_editor.php b/dns_editor.php index ca53b698..06a68861 100644 --- a/dns_editor.php +++ b/dns_editor.php @@ -129,12 +129,7 @@ if ($action == 'add_record' && ! empty($_POST)) { $content .= '.'; } elseif ($type == 'TXT' && ! empty($content)) { // check that TXT content is enclosed in " " - if (substr($content, 0, 1) != '"') { - $content = '"' . $content; - } - if (substr($content, - 1) != '"') { - $content .= '"'; - } + $content = encloseTXTContent($content); } elseif ($type == 'SRV') { if ($prio === null || $prio < 0) { $errors[] = $lng['error']['dns_srv_prioempty']; diff --git a/lib/functions/dns/function.createDomainZone.php b/lib/functions/dns/function.createDomainZone.php index aa0b4cc0..a1202e1d 100644 --- a/lib/functions/dns/function.createDomainZone.php +++ b/lib/functions/dns/function.createDomainZone.php @@ -47,11 +47,10 @@ function createDomainZone($domain_id, $froxlorhostname = false) if ($domain['iswildcarddomain'] == '1') { addRequiredEntry('*', 'A', $required_entries); addRequiredEntry('*', 'AAAA', $required_entries); - } else - if ($domain['wwwserveralias'] == '1') { - addRequiredEntry('www', 'A', $required_entries); - addRequiredEntry('www', 'AAAA', $required_entries); - } + } elseif ($domain['wwwserveralias'] == '1') { + addRequiredEntry('www', 'A', $required_entries); + addRequiredEntry('www', 'AAAA', $required_entries); + } // additional required records for subdomains $subdomains_stmt = Database::prepare(" @@ -92,13 +91,33 @@ function createDomainZone($domain_id, $froxlorhostname = false) addRequiredEntry(str_replace('.' . $domain['domain'], '', $mainbutsubtodomain['domain']), 'NS', $required_entries); } + // additional required records for SPF and DKIM if activated + if (Settings::Get('spf.use_spf') == '1') { + // check for SPF content later + addRequiredEntry('@SPF@', 'TXT', $required_entries); + } + if (Settings::Get('dkim.use_dkim') == '1') { + // check for DKIM content later + addRequiredEntry('dkim_' . $domain['dkim_id'] . '._domainkey', 'TXT', $required_entries); + // check for ASDP + if (Settings::Get('dkim.dkim_add_adsp') == "1") { + addRequiredEntry('_adsp._domainkey', 'TXT', $required_entries); + } + } + $primary_ns = null; $zonefile = ""; // now generate all records and unset the required entries we have foreach ($dom_entries as $entry) { if (array_key_exists($entry['type'], $required_entries) && array_key_exists(md5($entry['record']), $required_entries[$entry['type']])) { - unset($required_entries[$entry['type']][md5($entry['record'])]); + // check for SPF entry if required + if (Settings::Get('spf.use_spf') == '1' && $entry['type'] == 'TXT' && $entry['record'] == '@' && strtolower(substr($entry['content'], 0, 7)) == '"v=spf1') { + // unset special spf required-entry + unset($required_entries[$entry['type']][md5("@SPF@")]); + } else { + unset($required_entries[$entry['type']][md5($entry['record'])]); + } } if (empty($primary_ns) && $entry['type'] == 'NS') { // use the first NS entry as primary ns @@ -115,7 +134,7 @@ function createDomainZone($domain_id, $froxlorhostname = false) if ($froxlorhostname) { // use all available IP's for the froxlor-hostname $result_ip_stmt = Database::prepare(" - SELECT `ip` FROM `".TABLE_PANEL_IPSANDPORTS."` GROUP BY `ip` + SELECT `ip` FROM `" . TABLE_PANEL_IPSANDPORTS . "` GROUP BY `ip` "); } else { $result_ip_stmt = Database::prepare(" @@ -196,6 +215,29 @@ function createDomainZone($domain_id, $froxlorhostname = false) unset($required_entries['MX']); } } + + // TXT (SPF and DKIM) + if (array_key_exists("TXT", $required_entries)) { + + if (Settings::Get('dkim.use_dkim') == '1') { + $dkim_entries = generateDkimEntries($domain); + } + + foreach ($required_entries as $type => $records) { + if ($type == 'TXT') { + foreach ($records as $record) { + if ($record == '@SPF@') { + $txt_content = Settings::Get('spf.spf_entry'); + $zonefile .= formatEntry('@', 'TXT', encloseTXTContent($txt_content)); + } elseif ($record == 'dkim_' . $domain['dkim_id'] . '._domainkey' && ! empty($dkim_entries)) { + $zonefile .= formatEntry($record, 'TXT', encloseTXTContent($dkim_entries[0])); + } elseif ($record == '_adsp._domainkey' && ! empty($dkim_entries) && isset($dkim_entries[1])) { + $zonefile .= formatEntry($record, 'TXT', encloseTXTContent($dkim_entries[1])); + } + } + } + } + } } if (empty($primary_ns)) { @@ -214,7 +256,7 @@ function createDomainZone($domain_id, $froxlorhostname = false) $_zonefile = "\$TTL " . (int) Settings::Get('system.defaultttl') . PHP_EOL; $_zonefile .= "\$ORIGIN " . $domain['domain'] . "." . PHP_EOL; $_zonefile .= formatEntry('@', 'SOA', $soa_content); - $zonefile = $_zonefile.$zonefile; + $zonefile = $_zonefile . $zonefile; return $zonefile; } @@ -227,8 +269,91 @@ function formatEntry($record = '@', $type = 'A', $content = null, $prio = 0, $tt function addRequiredEntry($record = '@', $type = 'A', &$required) { - if (!isset($required[$type])) { + if (! isset($required[$type])) { $required[$type] = array(); } $required[$type][md5($record)] = $record; } + +function generateDkimEntries($domain) +{ + $zone_dkim = array(); + + if (Settings::Get('dkim.use_dkim') == '1' && $domain['dkim'] == '1' && $domain['dkim_pubkey'] != '') { + // start + $dkim_txt = 'v=DKIM1;'; + + // algorithm + $algorithm = explode(',', Settings::Get('dkim.dkim_algorithm')); + $alg = ''; + foreach ($algorithm as $a) { + if ($a == 'all') { + break; + } else { + $alg .= $a . ':'; + } + } + + if ($alg != '') { + $alg = substr($alg, 0, - 1); + $dkim_txt .= 'h=' . $alg . ';'; + } + + // notes + if (trim(Settings::Get('dkim.dkim_notes') != '')) { + $dkim_txt .= 'n=' . trim(Settings::Get('dkim.dkim_notes')) . ';'; + } + + // key + $dkim_txt .= 'k=rsa;p=' . trim(preg_replace('/-----BEGIN PUBLIC KEY-----(.+)-----END PUBLIC KEY-----/s', '$1', str_replace("\n", '', $domain['dkim_pubkey']))) . ';'; + + // service-type + if (Settings::Get('dkim.dkim_servicetype') == '1') { + $dkim_txt .= 's=email;'; + } + + // end-part + $dkim_txt .= 't=s'; + + // split if necessary + $txt_record_split = ''; + $lbr = 50; + for ($pos = 0; $pos <= strlen($dkim_txt) - 1; $pos += $lbr) { + $txt_record_split .= (($pos == 0) ? '("' : "\t\t\t\t\t \"") . substr($dkim_txt, $pos, $lbr) . (($pos >= strlen($dkim_txt) - $lbr) ? '")' : '"') . "\n"; + } + + // dkim-entry + $zone_dkim[] = $txt_record_split; + + // adsp-entry + if (Settings::Get('dkim.dkim_add_adsp') == "1") { + $adsp = '"dkim='; + switch ((int) Settings::Get('dkim.dkim_add_adsppolicy')) { + case 0: + $adsp .= 'unknown"'; + break; + case 1: + $adsp .= 'all"'; + break; + case 2: + $adsp .= 'discardable"'; + break; + } + $zone_dkim[] = $adsp; + } + } + + return $zone_dkim; +} + +function encloseTXTContent($txt_content) +{ + // check that TXT content is enclosed in " " + if (substr($txt_content, 0, 1) != '"') { + $txt_content = '"' . $txt_content; + } + if (substr($txt_content, - 1) != '"') { + $txt_content .= '"'; + } + return $txt_content; +} diff --git a/scripts/jobs/cron_tasks.inc.dns.15.bind.php b/scripts/jobs/cron_tasks.inc.dns.15.bind.php index 0e423951..55347a2d 100644 --- a/scripts/jobs/cron_tasks.inc.dns.15.bind.php +++ b/scripts/jobs/cron_tasks.inc.dns.15.bind.php @@ -130,7 +130,7 @@ class bind2 $isFroxlorHostname = true; } // create zone-file - $this->_logger->logAction(CRON_ACTION, LOG_DEBUG, 'Generating dns zone for '.$domain['domain']); + $this->_logger->logAction(CRON_ACTION, LOG_DEBUG, 'Generating dns zone for ' . $domain['domain']); $zonefile = createDomainZone($domain['id'], $isFroxlorHostname); $domain['zonefile'] = 'domains/' . $domain['domain'] . '.zone'; $zonefile_name = makeCorrectFile(Settings::Get('system.bindconf_directory') . '/' . $domain['zonefile']); @@ -154,9 +154,89 @@ class bind2 $this->_logger->logAction(CRON_ACTION, LOG_INFO, 'Bind9 reloaded'); } + public function writeDKIMconfigs() + { + if (Settings::Get('dkim.use_dkim') == '1') { + if (! file_exists(makeCorrectDir(Settings::Get('dkim.dkim_prefix')))) { + $this->_logger->logAction(CRON_ACTION, LOG_NOTICE, 'mkdir -p ' . escapeshellarg(makeCorrectDir(Settings::Get('dkim.dkim_prefix')))); + safe_exec('mkdir -p ' . escapeshellarg(makeCorrectDir(Settings::Get('dkim.dkim_prefix')))); + } + + $dkimdomains = ''; + $dkimkeys = ''; + $result_domains_stmt = Database::query(" + SELECT `id`, `domain`, `dkim`, `dkim_id`, `dkim_pubkey`, `dkim_privkey` + FROM `" . TABLE_PANEL_DOMAINS . "` WHERE `dkim` = '1' ORDER BY `id` ASC + "); + + while ($domain = $result_domains_stmt->fetch(PDO::FETCH_ASSOC)) { + + $privkey_filename = makeCorrectFile(Settings::Get('dkim.dkim_prefix') . '/dkim_' . $domain['dkim_id']); + $pubkey_filename = makeCorrectFile(Settings::Get('dkim.dkim_prefix') . '/dkim_' . $domain['dkim_id'] . '.public'); + + if ($domain['dkim_privkey'] == '' || $domain['dkim_pubkey'] == '') { + $max_dkim_id_stmt = Database::query("SELECT MAX(`dkim_id`) as `max_dkim_id` FROM `" . TABLE_PANEL_DOMAINS . "`"); + $max_dkim_id = $max_dkim_id_stmt->fetch(PDO::FETCH_ASSOC); + $domain['dkim_id'] = (int) $max_dkim_id['max_dkim_id'] + 1; + $privkey_filename = makeCorrectFile(Settings::Get('dkim.dkim_prefix') . '/dkim_' . $domain['dkim_id']); + safe_exec('openssl genrsa -out ' . escapeshellarg($privkey_filename) . ' ' . Settings::Get('dkim.dkim_keylength')); + $domain['dkim_privkey'] = file_get_contents($privkey_filename); + safe_exec("chmod 0640 " . escapeshellarg($privkey_filename)); + $pubkey_filename = makeCorrectFile(Settings::Get('dkim.dkim_prefix') . '/dkim_' . $domain['dkim_id'] . '.public'); + safe_exec('openssl rsa -in ' . escapeshellarg($privkey_filename) . ' -pubout -outform pem -out ' . escapeshellarg($pubkey_filename)); + $domain['dkim_pubkey'] = file_get_contents($pubkey_filename); + safe_exec("chmod 0664 " . escapeshellarg($pubkey_filename)); + $upd_stmt = Database::prepare(" + UPDATE `" . TABLE_PANEL_DOMAINS . "` SET + `dkim_id` = :dkimid, + `dkim_privkey` = :privkey, + `dkim_pubkey` = :pubkey + WHERE `id` = :id + "); + $upd_data = array( + 'dkimid' => $domain['dkim_id'], + 'privkey' => $domain['dkim_privkey'], + 'pubkey' => $domain['dkim_pubkey'], + 'id' => $domain['id'] + ); + Database::pexecute($upd_stmt, $upd_data); + } + + if (! file_exists($privkey_filename) && $domain['dkim_privkey'] != '') { + $privkey_file_handler = fopen($privkey_filename, "w"); + fwrite($privkey_file_handler, $domain['dkim_privkey']); + fclose($privkey_file_handler); + safe_exec("chmod 0640 " . escapeshellarg($privkey_filename)); + } + + if (! file_exists($pubkey_filename) && $domain['dkim_pubkey'] != '') { + $pubkey_file_handler = fopen($pubkey_filename, "w"); + fwrite($pubkey_file_handler, $domain['dkim_pubkey']); + fclose($pubkey_file_handler); + safe_exec("chmod 0664 " . escapeshellarg($pubkey_filename)); + } + + $dkimdomains .= $domain['domain'] . "\n"; + $dkimkeys .= "*@" . $domain['domain'] . ":" . $domain['domain'] . ":" . $privkey_filename . "\n"; + } + + $dkimdomains_filename = makeCorrectFile(Settings::Get('dkim.dkim_prefix') . '/' . Settings::Get('dkim.dkim_domains')); + $dkimdomains_file_handler = fopen($dkimdomains_filename, "w"); + fwrite($dkimdomains_file_handler, $dkimdomains); + fclose($dkimdomains_file_handler); + $dkimkeys_filename = makeCorrectFile(Settings::Get('dkim.dkim_prefix') . '/' . Settings::Get('dkim.dkim_dkimkeys')); + $dkimkeys_file_handler = fopen($dkimkeys_filename, "w"); + fwrite($dkimkeys_file_handler, $dkimkeys); + fclose($dkimkeys_file_handler); + + safe_exec(escapeshellcmd(Settings::Get('dkim.dkimrestart_command'))); + $this->_logger->logAction(CRON_ACTION, LOG_INFO, 'Dkim-milter reloaded'); + } + } + private function _generateDomainConfig($domain = array()) { - $this->_logger->logAction(CRON_ACTION, LOG_DEBUG, 'Generating dns config for '.$domain['domain']); + $this->_logger->logAction(CRON_ACTION, LOG_DEBUG, 'Generating dns config for ' . $domain['domain']); $bindconf_file = '# Domain ID: ' . $domain['id'] . ' - CustomerID: ' . $domain['customerid'] . ' - CustomerLogin: ' . $domain['loginname'] . "\n"; $bindconf_file .= 'zone "' . $domain['domain'] . '" in {' . "\n"; @@ -197,7 +277,7 @@ class bind2 { $config_dir = makeCorrectFile(Settings::Get('system.bindconf_directory') . '/domains/'); - $this->_logger->logAction(CRON_ACTION, LOG_INFO, 'Cleaning dns zone files from '.$config_dir); + $this->_logger->logAction(CRON_ACTION, LOG_INFO, 'Cleaning dns zone files from ' . $config_dir); // check directory if (@is_dir($config_dir)) { diff --git a/scripts/jobs/cron_tasks.php b/scripts/jobs/cron_tasks.php index 9a175892..99573e9b 100644 --- a/scripts/jobs/cron_tasks.php +++ b/scripts/jobs/cron_tasks.php @@ -187,7 +187,7 @@ while ($row = $result_tasks_stmt->fetch(PDO::FETCH_ASSOC)) { $nameserver = new $bindclass($cronlog); } - if (Settings::Get('dkim.use_dkim') == '1' && $bindclass == 'bind') { + if (Settings::Get('dkim.use_dkim') == '1') { $nameserver->writeDKIMconfigs(); } From bd9ef50e94bf8a02caff91762e06ead8e485a361 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 12 May 2016 09:47:36 +0200 Subject: [PATCH 0046/1335] correct SPF dns entry for new layout Signed-off-by: Michael Kaufmann (d00p) --- actions/admin/settings/185.spf.php | 2 +- install/froxlor.sql | 4 ++-- install/updates/froxlor/0.9/update_0.9.inc.php | 12 ++++++++++++ lib/version.inc.php | 2 +- 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/actions/admin/settings/185.spf.php b/actions/admin/settings/185.spf.php index c4d14cf5..55a44782 100644 --- a/actions/admin/settings/185.spf.php +++ b/actions/admin/settings/185.spf.php @@ -34,7 +34,7 @@ return array( 'settinggroup' => 'spf', 'varname' => 'spf_entry', 'type' => 'string', - 'default' => '@ IN TXT "v=spf1 a mx -all"', + 'default' => '"v=spf1 a mx -all"', 'save_method' => 'storeSettingField' ) ) diff --git a/install/froxlor.sql b/install/froxlor.sql index ad61eec2..8758e141 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -376,7 +376,7 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES ('admin', 'show_version_login', '0'), ('admin', 'show_version_footer', '0'), ('spf', 'use_spf', '0'), - ('spf', 'spf_entry', '@ IN TXT "v=spf1 a mx -all"'), + ('spf', 'spf_entry', '"v=spf1 a mx -all"'), ('dkim', 'dkim_algorithm', 'all'), ('dkim', 'dkim_add_adsp', '1'), ('dkim', 'dkim_keylength', '1024'), @@ -557,7 +557,7 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES ('panel', 'password_special_char_required', '0'), ('panel', 'password_special_char', '!?<>§$%+#=@'), ('panel', 'version', '0.9.35.1'), - ('panel', 'db_version', '201605090'); + ('panel', 'db_version', '201605120'); DROP TABLE IF EXISTS `panel_tasks`; diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index 4af29fef..ef9dbe27 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -3335,3 +3335,15 @@ if (isFroxlorVersion('0.9.35.1') && isDatabaseVersion('201604270')) { updateToDbVersion('201605090'); } + +if (isFroxlorVersion('0.9.35.1') && isDatabaseVersion('201605090')) { + + showUpdateStep("Adjusting SPF record setting"); + $current_spf = Settings::Get('spf.spf_entry'); + // @ IN TXT "v=spf1 a mx -all" + $new_spf = substr($current_spf, strpos($current_spf, '"')); + Settings::Set('spf.spf_entry', $new_spf, true); + lastStepStatus(0); + + updateToDbVersion('201605120'); +} diff --git a/lib/version.inc.php b/lib/version.inc.php index 5bc79f20..bc203f9b 100644 --- a/lib/version.inc.php +++ b/lib/version.inc.php @@ -19,7 +19,7 @@ $version = '0.9.35.1'; // Database version (YYYYMMDDC where C is a daily counter) -$dbversion = '201605090'; +$dbversion = '201605120'; // Distribution branding-tag (used for Debian etc.) $branding = ''; From cec5f33870f1290376ce69f7dcd828df66e1671e Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 12 May 2016 10:14:04 +0200 Subject: [PATCH 0047/1335] fix checking for existing SPF entry in DNS Signed-off-by: Michael Kaufmann (d00p) --- lib/functions/dns/function.createDomainZone.php | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/lib/functions/dns/function.createDomainZone.php b/lib/functions/dns/function.createDomainZone.php index a1202e1d..6d7b61bb 100644 --- a/lib/functions/dns/function.createDomainZone.php +++ b/lib/functions/dns/function.createDomainZone.php @@ -111,13 +111,11 @@ function createDomainZone($domain_id, $froxlorhostname = false) // now generate all records and unset the required entries we have foreach ($dom_entries as $entry) { if (array_key_exists($entry['type'], $required_entries) && array_key_exists(md5($entry['record']), $required_entries[$entry['type']])) { - // check for SPF entry if required - if (Settings::Get('spf.use_spf') == '1' && $entry['type'] == 'TXT' && $entry['record'] == '@' && strtolower(substr($entry['content'], 0, 7)) == '"v=spf1') { - // unset special spf required-entry - unset($required_entries[$entry['type']][md5("@SPF@")]); - } else { - unset($required_entries[$entry['type']][md5($entry['record'])]); - } + unset($required_entries[$entry['type']][md5($entry['record'])]); + } + if (Settings::Get('spf.use_spf') == '1' && $entry['type'] == 'TXT' && $entry['record'] == '@' && strtolower(substr($entry['content'], 0, 7)) == '"v=spf1') { + // unset special spf required-entry + unset($required_entries[$entry['type']][md5("@SPF@")]); } if (empty($primary_ns) && $entry['type'] == 'NS') { // use the first NS entry as primary ns From 689a1fdbd2c0af00258ab13eaddd3997f3965c97 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 12 May 2016 10:18:45 +0200 Subject: [PATCH 0048/1335] inform cronjob to regenerate bind-configs on changes Signed-off-by: Michael Kaufmann (d00p) --- dns_editor.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/dns_editor.php b/dns_editor.php index 06a68861..0a5747b5 100644 --- a/dns_editor.php +++ b/dns_editor.php @@ -223,6 +223,9 @@ if ($action == 'add_record' && ! empty($_POST)) { $prio = ""; $content = ""; $ttl = ""; + + // re-generate bind configs + inserttask('4'); } else { // show $errors $errors = implode("
", $errors); @@ -247,6 +250,9 @@ if ($action == 'add_record' && ! empty($_POST)) { unset($_t); // success message (inline) $success_message = $lng['success']['dns_record_deleted']; + + // re-generate bind configs + inserttask('4'); } } From 899663350d46a46fb355fbe2abc6e7c9539e46fa Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 12 May 2016 13:36:17 +0200 Subject: [PATCH 0049/1335] fix handling of DKIM multi-line entries; outsource some code to new DnsBase class Signed-off-by: Michael Kaufmann (d00p) --- .../dns/function.createDomainZone.php | 22 +- scripts/classes/class.DnsBase.php | 185 +++++++++++++++ scripts/jobs/cron_tasks.inc.dns.15.bind.php | 222 +++--------------- 3 files changed, 229 insertions(+), 200 deletions(-) create mode 100644 scripts/classes/class.DnsBase.php diff --git a/lib/functions/dns/function.createDomainZone.php b/lib/functions/dns/function.createDomainZone.php index 6d7b61bb..51011107 100644 --- a/lib/functions/dns/function.createDomainZone.php +++ b/lib/functions/dns/function.createDomainZone.php @@ -228,7 +228,12 @@ function createDomainZone($domain_id, $froxlorhostname = false) $txt_content = Settings::Get('spf.spf_entry'); $zonefile .= formatEntry('@', 'TXT', encloseTXTContent($txt_content)); } elseif ($record == 'dkim_' . $domain['dkim_id'] . '._domainkey' && ! empty($dkim_entries)) { - $zonefile .= formatEntry($record, 'TXT', encloseTXTContent($dkim_entries[0])); + // check for multiline entry + $multiline = false; + if (substr($dkim_entries[0], 0, 1) == '(') { + $multiline = true; + } + $zonefile .= formatEntry($record, 'TXT', encloseTXTContent($dkim_entries[0], $multiline)); } elseif ($record == '_adsp._domainkey' && ! empty($dkim_entries) && isset($dkim_entries[1])) { $zonefile .= formatEntry($record, 'TXT', encloseTXTContent($dkim_entries[1])); } @@ -344,14 +349,17 @@ function generateDkimEntries($domain) return $zone_dkim; } -function encloseTXTContent($txt_content) +function encloseTXTContent($txt_content, $isMultiLine = false) { // check that TXT content is enclosed in " " - if (substr($txt_content, 0, 1) != '"') { - $txt_content = '"' . $txt_content; - } - if (substr($txt_content, - 1) != '"') { - $txt_content .= '"'; + if ($isMultiLine == false) + { + if (substr($txt_content, 0, 1) != '"') { + $txt_content = '"' . $txt_content; + } + if (substr($txt_content, - 1) != '"') { + $txt_content .= '"'; + } } return $txt_content; } diff --git a/scripts/classes/class.DnsBase.php b/scripts/classes/class.DnsBase.php new file mode 100644 index 00000000..3a2c138e --- /dev/null +++ b/scripts/classes/class.DnsBase.php @@ -0,0 +1,185 @@ +_logger = $logger; + + if (Settings::Get('system.nameservers') != '') { + $nameservers = explode(',', Settings::Get('system.nameservers')); + foreach ($nameservers as $nameserver) { + $nameserver = trim($nameserver); + // DNS servers might be multi homed; allow transfer from all ip + // addresses of the DNS server + $nameserver_ips = gethostbynamel($nameserver); + // append dot to hostname + if (substr($nameserver, - 1, 1) != '.') { + $nameserver .= '.'; + } + // ignore invalid responses + if (! is_array($nameserver_ips)) { + // act like gethostbyname() and return unmodified hostname on error + $nameserver_ips = array( + $nameserver + ); + } + $this->_ns[] = array( + 'hostname' => $nameserver, + 'ips' => $nameserver_ips + ); + } + } + + if (Settings::Get('system.mxservers') != '') { + $mxservers = explode(',', Settings::Get('system.mxservers')); + foreach ($mxservers as $mxserver) { + if (substr($mxserver, - 1, 1) != '.') { + $mxserver .= '.'; + } + $this->_mx[] = $mxserver; + } + } + + // AXFR server #100 + if (Settings::Get('system.axfrservers') != '') { + $axfrservers = explode(',', Settings::Get('system.axfrservers')); + foreach ($axfrservers as $axfrserver) { + $this->_axfr[] = trim($axfrserver); + } + } + } + + protected function getDomainList() + { + // get all Domains + $result_domains_stmt = Database::query(" + SELECT `d`.`id`, `d`.`domain`, `d`.`customerid`, `d`.`zonefile`, `c`.`loginname`, `c`.`guid` + FROM `" . TABLE_PANEL_DOMAINS . "` `d` LEFT JOIN `" . TABLE_PANEL_CUSTOMERS . "` `c` USING(`customerid`) + WHERE `d`.`isbinddomain` = '1' ORDER BY `d`.`domain` ASC + "); + + $domains = $result_domains_stmt->fetchAll(PDO::FETCH_ASSOC); + + // frolxor-hostname (#1090) + if (Settings::get('system.dns_createhostnameentry') == 1) { + $hostname_arr = array( + 'id' => 'none', + 'domain' => Settings::Get('system.hostname'), + 'isemaildomain' => Settings::Get('system.dns_createmailentry'), + 'customerid' => 'none', + 'loginname' => 'froxlor.panel', + 'bindserial' => date('Ymd') . '00', + 'dkim' => '0', + 'iswildcarddomain' => '1', + 'ismainbutsubto' => '0', + 'zonefile' => '', + 'froxlorhost' => '1' + ); + $domains['none'] = $hostname_arr; + } + + if (empty($domains)) { + $this->_logger->logAction(CRON_ACTION, LOG_INFO, 'No domains found for nameserver-config, skipping...'); + return null; + } + + return $domains; + } + + public function writeDKIMconfigs() + { + if (Settings::Get('dkim.use_dkim') == '1') { + if (! file_exists(makeCorrectDir(Settings::Get('dkim.dkim_prefix')))) { + $this->_logger->logAction(CRON_ACTION, LOG_NOTICE, 'mkdir -p ' . escapeshellarg(makeCorrectDir(Settings::Get('dkim.dkim_prefix')))); + safe_exec('mkdir -p ' . escapeshellarg(makeCorrectDir(Settings::Get('dkim.dkim_prefix')))); + } + + $dkimdomains = ''; + $dkimkeys = ''; + $result_domains_stmt = Database::query(" + SELECT `id`, `domain`, `dkim`, `dkim_id`, `dkim_pubkey`, `dkim_privkey` + FROM `" . TABLE_PANEL_DOMAINS . "` WHERE `dkim` = '1' ORDER BY `id` ASC + "); + + while ($domain = $result_domains_stmt->fetch(PDO::FETCH_ASSOC)) { + + $privkey_filename = makeCorrectFile(Settings::Get('dkim.dkim_prefix') . '/dkim_' . $domain['dkim_id']); + $pubkey_filename = makeCorrectFile(Settings::Get('dkim.dkim_prefix') . '/dkim_' . $domain['dkim_id'] . '.public'); + + if ($domain['dkim_privkey'] == '' || $domain['dkim_pubkey'] == '') { + $max_dkim_id_stmt = Database::query("SELECT MAX(`dkim_id`) as `max_dkim_id` FROM `" . TABLE_PANEL_DOMAINS . "`"); + $max_dkim_id = $max_dkim_id_stmt->fetch(PDO::FETCH_ASSOC); + $domain['dkim_id'] = (int) $max_dkim_id['max_dkim_id'] + 1; + $privkey_filename = makeCorrectFile(Settings::Get('dkim.dkim_prefix') . '/dkim_' . $domain['dkim_id']); + safe_exec('openssl genrsa -out ' . escapeshellarg($privkey_filename) . ' ' . Settings::Get('dkim.dkim_keylength')); + $domain['dkim_privkey'] = file_get_contents($privkey_filename); + safe_exec("chmod 0640 " . escapeshellarg($privkey_filename)); + $pubkey_filename = makeCorrectFile(Settings::Get('dkim.dkim_prefix') . '/dkim_' . $domain['dkim_id'] . '.public'); + safe_exec('openssl rsa -in ' . escapeshellarg($privkey_filename) . ' -pubout -outform pem -out ' . escapeshellarg($pubkey_filename)); + $domain['dkim_pubkey'] = file_get_contents($pubkey_filename); + safe_exec("chmod 0664 " . escapeshellarg($pubkey_filename)); + $upd_stmt = Database::prepare(" + UPDATE `" . TABLE_PANEL_DOMAINS . "` SET + `dkim_id` = :dkimid, + `dkim_privkey` = :privkey, + `dkim_pubkey` = :pubkey + WHERE `id` = :id + "); + $upd_data = array( + 'dkimid' => $domain['dkim_id'], + 'privkey' => $domain['dkim_privkey'], + 'pubkey' => $domain['dkim_pubkey'], + 'id' => $domain['id'] + ); + Database::pexecute($upd_stmt, $upd_data); + } + + if (! file_exists($privkey_filename) && $domain['dkim_privkey'] != '') { + $privkey_file_handler = fopen($privkey_filename, "w"); + fwrite($privkey_file_handler, $domain['dkim_privkey']); + fclose($privkey_file_handler); + safe_exec("chmod 0640 " . escapeshellarg($privkey_filename)); + } + + if (! file_exists($pubkey_filename) && $domain['dkim_pubkey'] != '') { + $pubkey_file_handler = fopen($pubkey_filename, "w"); + fwrite($pubkey_file_handler, $domain['dkim_pubkey']); + fclose($pubkey_file_handler); + safe_exec("chmod 0664 " . escapeshellarg($pubkey_filename)); + } + + $dkimdomains .= $domain['domain'] . "\n"; + $dkimkeys .= "*@" . $domain['domain'] . ":" . $domain['domain'] . ":" . $privkey_filename . "\n"; + } + + $dkimdomains_filename = makeCorrectFile(Settings::Get('dkim.dkim_prefix') . '/' . Settings::Get('dkim.dkim_domains')); + $dkimdomains_file_handler = fopen($dkimdomains_filename, "w"); + fwrite($dkimdomains_file_handler, $dkimdomains); + fclose($dkimdomains_file_handler); + $dkimkeys_filename = makeCorrectFile(Settings::Get('dkim.dkim_prefix') . '/' . Settings::Get('dkim.dkim_dkimkeys')); + $dkimkeys_file_handler = fopen($dkimkeys_filename, "w"); + fwrite($dkimkeys_file_handler, $dkimkeys); + fclose($dkimkeys_file_handler); + + safe_exec(escapeshellcmd(Settings::Get('dkim.dkimrestart_command'))); + $this->_logger->logAction(CRON_ACTION, LOG_INFO, 'Dkim-milter reloaded'); + } + } + +} \ No newline at end of file diff --git a/scripts/jobs/cron_tasks.inc.dns.15.bind.php b/scripts/jobs/cron_tasks.inc.dns.15.bind.php index 55347a2d..1e72166d 100644 --- a/scripts/jobs/cron_tasks.inc.dns.15.bind.php +++ b/scripts/jobs/cron_tasks.inc.dns.15.bind.php @@ -16,65 +16,9 @@ if (! defined('MASTER_CRONJOB')) * @package Cron * */ -class bind2 +class bind2 extends DnsBase { - private $_logger = false; - - private $_ns = array(); - - private $_mx = array(); - - private $_axfr = array(); - - public function __construct($logger) - { - $this->_logger = $logger; - - if (Settings::Get('system.nameservers') != '') { - $nameservers = explode(',', Settings::Get('system.nameservers')); - foreach ($nameservers as $nameserver) { - $nameserver = trim($nameserver); - // DNS servers might be multi homed; allow transfer from all ip - // addresses of the DNS server - $nameserver_ips = gethostbynamel($nameserver); - // append dot to hostname - if (substr($nameserver, - 1, 1) != '.') { - $nameserver .= '.'; - } - // ignore invalid responses - if (! is_array($nameserver_ips)) { - // act like gethostbyname() and return unmodified hostname on error - $nameserver_ips = array( - $nameserver - ); - } - $this->_ns[] = array( - 'hostname' => $nameserver, - 'ips' => $nameserver_ips - ); - } - } - - if (Settings::Get('system.mxservers') != '') { - $mxservers = explode(',', Settings::Get('system.mxservers')); - foreach ($mxservers as $mxserver) { - if (substr($mxserver, - 1, 1) != '.') { - $mxserver .= '.'; - } - $this->_mx[] = $mxserver; - } - } - - // AXFR server #100 - if (Settings::Get('system.axfrservers') != '') { - $axfrservers = explode(',', Settings::Get('system.axfrservers')); - foreach ($axfrservers as $axfrserver) { - $this->_axfr[] = trim($axfrserver); - } - } - } - public function writeConfigs() { // tell the world what we are doing @@ -89,148 +33,40 @@ class bind2 safe_exec('mkdir -p ' . escapeshellarg(makeCorrectDir(Settings::Get('system.bindconf_directory') . '/domains/'))); } - // get all Domains - $result_domains_stmt = Database::query(" - SELECT `d`.`id`, `d`.`domain`, `d`.`customerid`, `d`.`zonefile`, `c`.`loginname`, `c`.`guid` - FROM `" . TABLE_PANEL_DOMAINS . "` `d` LEFT JOIN `" . TABLE_PANEL_CUSTOMERS . "` `c` USING(`customerid`) - WHERE `d`.`isbinddomain` = '1' ORDER BY `d`.`domain` ASC - "); + $domains = $this->getDomainList(); - $domains = $result_domains_stmt->fetchAll(PDO::FETCH_ASSOC); + if (! empty($domains)) { + $bindconf_file = '# ' . Settings::Get('system.bindconf_directory') . 'froxlor_bind.conf' . "\n" . '# Created ' . date('d.m.Y H:i') . "\n" . '# Do NOT manually edit this file, all changes will be deleted after the next domain change at the panel.' . "\n\n"; - // frolxor-hostname (#1090) - if (Settings::get('system.dns_createhostnameentry') == 1) { - $hostname_arr = array( - 'id' => 'none', - 'domain' => Settings::Get('system.hostname'), - 'isemaildomain' => Settings::Get('system.dns_createmailentry'), - 'customerid' => 'none', - 'loginname' => 'froxlor.panel', - 'bindserial' => date('Ymd') . '00', - 'dkim' => '0', - 'iswildcarddomain' => '1', - 'ismainbutsubto' => '0', - 'zonefile' => '', - 'froxlorhost' => '1' - ); - $domains['none'] = $hostname_arr; - } + foreach ($domains as $domain) { + // check for system-hostname + $isFroxlorHostname = false; + if (isset($domain['froxlorhost']) && $domain['froxlorhost'] == 1) { + $isFroxlorHostname = true; + } + // create zone-file + $this->_logger->logAction(CRON_ACTION, LOG_DEBUG, 'Generating dns zone for ' . $domain['domain']); + $zonefile = createDomainZone($domain['id'], $isFroxlorHostname); + $domain['zonefile'] = 'domains/' . $domain['domain'] . '.zone'; + $zonefile_name = makeCorrectFile(Settings::Get('system.bindconf_directory') . '/' . $domain['zonefile']); + $zonefile_handler = fopen($zonefile_name, 'w'); + fwrite($zonefile_handler, $zonefile); + fclose($zonefile_handler); + $this->_logger->logAction(CRON_ACTION, LOG_INFO, '`' . $zonefile_name . '` zone written'); - if (empty($domains)) { - $this->_logger->logAction(CRON_ACTION, LOG_INFO, 'No domains found for nameserver-config, skipping...'); - return; - } - - $bindconf_file = '# ' . Settings::Get('system.bindconf_directory') . 'froxlor_bind.conf' . "\n" . '# Created ' . date('d.m.Y H:i') . "\n" . '# Do NOT manually edit this file, all changes will be deleted after the next domain change at the panel.' . "\n\n"; - - foreach ($domains as $domain) { - // check for system-hostname - $isFroxlorHostname = false; - if (isset($domain['froxlorhost']) && $domain['froxlorhost'] == 1) { - $isFroxlorHostname = true; - } - // create zone-file - $this->_logger->logAction(CRON_ACTION, LOG_DEBUG, 'Generating dns zone for ' . $domain['domain']); - $zonefile = createDomainZone($domain['id'], $isFroxlorHostname); - $domain['zonefile'] = 'domains/' . $domain['domain'] . '.zone'; - $zonefile_name = makeCorrectFile(Settings::Get('system.bindconf_directory') . '/' . $domain['zonefile']); - $zonefile_handler = fopen($zonefile_name, 'w'); - fwrite($zonefile_handler, $zonefile); - fclose($zonefile_handler); - $this->_logger->logAction(CRON_ACTION, LOG_INFO, '`' . $zonefile_name . '` zone written'); - - // generate config - $bindconf_file .= $this->_generateDomainConfig($domain); - } - - // write config - $bindconf_file_handler = fopen(makeCorrectFile(Settings::Get('system.bindconf_directory') . '/froxlor_bind.conf'), 'w'); - fwrite($bindconf_file_handler, $bindconf_file); - fclose($bindconf_file_handler); - $this->_logger->logAction(CRON_ACTION, LOG_INFO, 'froxlor_bind.conf written'); - - // reload Bind - safe_exec(escapeshellcmd(Settings::Get('system.bindreload_command'))); - $this->_logger->logAction(CRON_ACTION, LOG_INFO, 'Bind9 reloaded'); - } - - public function writeDKIMconfigs() - { - if (Settings::Get('dkim.use_dkim') == '1') { - if (! file_exists(makeCorrectDir(Settings::Get('dkim.dkim_prefix')))) { - $this->_logger->logAction(CRON_ACTION, LOG_NOTICE, 'mkdir -p ' . escapeshellarg(makeCorrectDir(Settings::Get('dkim.dkim_prefix')))); - safe_exec('mkdir -p ' . escapeshellarg(makeCorrectDir(Settings::Get('dkim.dkim_prefix')))); + // generate config + $bindconf_file .= $this->_generateDomainConfig($domain); } - $dkimdomains = ''; - $dkimkeys = ''; - $result_domains_stmt = Database::query(" - SELECT `id`, `domain`, `dkim`, `dkim_id`, `dkim_pubkey`, `dkim_privkey` - FROM `" . TABLE_PANEL_DOMAINS . "` WHERE `dkim` = '1' ORDER BY `id` ASC - "); + // write config + $bindconf_file_handler = fopen(makeCorrectFile(Settings::Get('system.bindconf_directory') . '/froxlor_bind.conf'), 'w'); + fwrite($bindconf_file_handler, $bindconf_file); + fclose($bindconf_file_handler); + $this->_logger->logAction(CRON_ACTION, LOG_INFO, 'froxlor_bind.conf written'); - while ($domain = $result_domains_stmt->fetch(PDO::FETCH_ASSOC)) { - - $privkey_filename = makeCorrectFile(Settings::Get('dkim.dkim_prefix') . '/dkim_' . $domain['dkim_id']); - $pubkey_filename = makeCorrectFile(Settings::Get('dkim.dkim_prefix') . '/dkim_' . $domain['dkim_id'] . '.public'); - - if ($domain['dkim_privkey'] == '' || $domain['dkim_pubkey'] == '') { - $max_dkim_id_stmt = Database::query("SELECT MAX(`dkim_id`) as `max_dkim_id` FROM `" . TABLE_PANEL_DOMAINS . "`"); - $max_dkim_id = $max_dkim_id_stmt->fetch(PDO::FETCH_ASSOC); - $domain['dkim_id'] = (int) $max_dkim_id['max_dkim_id'] + 1; - $privkey_filename = makeCorrectFile(Settings::Get('dkim.dkim_prefix') . '/dkim_' . $domain['dkim_id']); - safe_exec('openssl genrsa -out ' . escapeshellarg($privkey_filename) . ' ' . Settings::Get('dkim.dkim_keylength')); - $domain['dkim_privkey'] = file_get_contents($privkey_filename); - safe_exec("chmod 0640 " . escapeshellarg($privkey_filename)); - $pubkey_filename = makeCorrectFile(Settings::Get('dkim.dkim_prefix') . '/dkim_' . $domain['dkim_id'] . '.public'); - safe_exec('openssl rsa -in ' . escapeshellarg($privkey_filename) . ' -pubout -outform pem -out ' . escapeshellarg($pubkey_filename)); - $domain['dkim_pubkey'] = file_get_contents($pubkey_filename); - safe_exec("chmod 0664 " . escapeshellarg($pubkey_filename)); - $upd_stmt = Database::prepare(" - UPDATE `" . TABLE_PANEL_DOMAINS . "` SET - `dkim_id` = :dkimid, - `dkim_privkey` = :privkey, - `dkim_pubkey` = :pubkey - WHERE `id` = :id - "); - $upd_data = array( - 'dkimid' => $domain['dkim_id'], - 'privkey' => $domain['dkim_privkey'], - 'pubkey' => $domain['dkim_pubkey'], - 'id' => $domain['id'] - ); - Database::pexecute($upd_stmt, $upd_data); - } - - if (! file_exists($privkey_filename) && $domain['dkim_privkey'] != '') { - $privkey_file_handler = fopen($privkey_filename, "w"); - fwrite($privkey_file_handler, $domain['dkim_privkey']); - fclose($privkey_file_handler); - safe_exec("chmod 0640 " . escapeshellarg($privkey_filename)); - } - - if (! file_exists($pubkey_filename) && $domain['dkim_pubkey'] != '') { - $pubkey_file_handler = fopen($pubkey_filename, "w"); - fwrite($pubkey_file_handler, $domain['dkim_pubkey']); - fclose($pubkey_file_handler); - safe_exec("chmod 0664 " . escapeshellarg($pubkey_filename)); - } - - $dkimdomains .= $domain['domain'] . "\n"; - $dkimkeys .= "*@" . $domain['domain'] . ":" . $domain['domain'] . ":" . $privkey_filename . "\n"; - } - - $dkimdomains_filename = makeCorrectFile(Settings::Get('dkim.dkim_prefix') . '/' . Settings::Get('dkim.dkim_domains')); - $dkimdomains_file_handler = fopen($dkimdomains_filename, "w"); - fwrite($dkimdomains_file_handler, $dkimdomains); - fclose($dkimdomains_file_handler); - $dkimkeys_filename = makeCorrectFile(Settings::Get('dkim.dkim_prefix') . '/' . Settings::Get('dkim.dkim_dkimkeys')); - $dkimkeys_file_handler = fopen($dkimkeys_filename, "w"); - fwrite($dkimkeys_file_handler, $dkimkeys); - fclose($dkimkeys_file_handler); - - safe_exec(escapeshellcmd(Settings::Get('dkim.dkimrestart_command'))); - $this->_logger->logAction(CRON_ACTION, LOG_INFO, 'Dkim-milter reloaded'); + // reload Bind + safe_exec(escapeshellcmd(Settings::Get('system.bindreload_command'))); + $this->_logger->logAction(CRON_ACTION, LOG_INFO, 'Bind9 reloaded'); } } From 4a4acc5c01b6f5decf731fb7c90d5fd58ff875cf Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 12 May 2016 14:32:41 +0200 Subject: [PATCH 0050/1335] fix constructor of DnsBase --- scripts/classes/class.DnsBase.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/classes/class.DnsBase.php b/scripts/classes/class.DnsBase.php index 3a2c138e..11e06996 100644 --- a/scripts/classes/class.DnsBase.php +++ b/scripts/classes/class.DnsBase.php @@ -17,7 +17,7 @@ abstract class DnsBase { abstract public function writeConfigs(); - protected function __construct($logger) + public function __construct($logger) { $this->_logger = $logger; From 11eb08e03105c7483a5d86c7bcd09fbebe12f89a Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Fri, 13 May 2016 13:27:33 +0200 Subject: [PATCH 0051/1335] add enabled-flag; enable dns-editor for customers; add german translations; few more fixes Signed-off-by: Michael Kaufmann (d00p) --- admin_domains.php | 2 +- customer_domains.php | 3 +++ install/froxlor.sql | 1 + .../preconfig/0.9/preconfig_0.9.inc.php | 8 ++++++++ lng/english.lng.php | 2 ++ lng/german.lng.php | 20 +++++++++++++++++++ scripts/jobs/cron_tasks.php | 6 +++++- .../Sparkle/admin/domains/domains_edit.tpl | 4 ++-- .../Sparkle/customer/domains/domains_edit.tpl | 3 +++ templates/Sparkle/dns_editor/index.tpl | 2 +- 10 files changed, 46 insertions(+), 5 deletions(-) diff --git a/admin_domains.php b/admin_domains.php index 7ecc3d5d..51de4672 100644 --- a/admin_domains.php +++ b/admin_domains.php @@ -2061,7 +2061,7 @@ elseif (Settings::Get('system.validate_domain') && ! validateDomain($domain)) { eval("echo \"" . getTemplate("domains/domains_import") . "\";"); } } -} elseif ($page == 'domaindnseditor') { +} elseif ($page == 'domaindnseditor' && Settings::Get('system.dnsenabled') == '1') { require_once __DIR__.'/dns_editor.php'; } diff --git a/customer_domains.php b/customer_domains.php index de18a20c..643866e6 100644 --- a/customer_domains.php +++ b/customer_domains.php @@ -904,4 +904,7 @@ if ($page == 'overview') { eval("echo \"" . getTemplate("domains/domain_ssleditor") . "\";"); } +} elseif ($page == 'domaindnseditor' && Settings::Get('system.dnsenabled') == '1') { + + require_once __DIR__.'/dns_editor.php'; } diff --git a/install/froxlor.sql b/install/froxlor.sql index 8758e141..b6462902 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -526,6 +526,7 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES ('system', 'letsencryptreuseold', 0), ('system', 'leenabled', '0'), ('system', 'backupenabled', '0'), + ('system', 'dnsenabled', '0'), ('panel', 'decimal_places', '4'), ('panel', 'adminmail', 'admin@SERVERNAME'), ('panel', 'phpmyadmin_url', ''), diff --git a/install/updates/preconfig/0.9/preconfig_0.9.inc.php b/install/updates/preconfig/0.9/preconfig_0.9.inc.php index 0c9d6559..5ebd6bf5 100644 --- a/install/updates/preconfig/0.9/preconfig_0.9.inc.php +++ b/install/updates/preconfig/0.9/preconfig_0.9.inc.php @@ -716,4 +716,12 @@ function parseAndOutputPreconfig(&$has_preconfig, &$return, $current_version, $c $question.= makeyesno('enable_backup', '1', '0', '0').'
'; eval("\$return.=\"" . getTemplate("update/preconfigitem") . "\";"); } + + if (versionInUpdate($current_db_version, '201605090')) { + $has_preconfig = true; + $description = 'You can chose whether you want to enable or disable our DNS editor

'; + $question = 'Do you want to enable the DNS editor? (default: no): '; + $question.= makeyesno('enable_dns', '1', '0', '0').'
'; + eval("\$return.=\"" . getTemplate("update/preconfigitem") . "\";"); + } } diff --git a/lng/english.lng.php b/lng/english.lng.php index a42edaf9..98487720 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -2007,3 +2007,5 @@ $lng['error']['dns_srv_noalias'] = 'The SRV-target value cannot be an CNAME entr $lng['error']['dns_duplicate_entry'] = 'Record already exists'; $lng['success']['dns_record_added'] = 'Record added successfully'; $lng['success']['dns_record_deleted'] = 'Record deleted successfully'; +$lng['dnseditor']['edit'] = 'edit DNS'; +$lng['dnseditor']['records'] = 'records'; diff --git a/lng/german.lng.php b/lng/german.lng.php index 671ce6c1..0584a5b2 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -1642,3 +1642,23 @@ $lng['serversettings']['backupenabled']['description'] = "Wenn dies aktiviert is $lng['extras']['path_protection_label'] = 'Wichtig'; $lng['extras']['path_protection_info'] = 'Wir raten dringend dazu den angegebenen Pfad zu schützen, siehe "Extras" -> "Verzeichnisschutz"'; $lng['tasks']['backup_customerfiles'] = 'Datensicherung für Kunde %loginname%'; + +$lng['error']['dns_domain_nodns'] = 'DNS ist für diese Domain nicht aktiviert'; +$lng['error']['dns_content_empty'] = 'Keinen Inhalt angegeben'; +$lng['error']['dns_arec_noipv4'] = 'Kein gültige IP Adresse für A-Eintrag angegeben'; +$lng['error']['dns_aaaarec_noipv6'] = 'Kein gültige IP Adresse für AAAA-Eintrag angegeben'; +$lng['error']['dns_mx_prioempty'] = 'Ungültige MX Priorität angegeben'; +$lng['error']['dns_mx_needdom'] = 'Der Wert des MX Eintrags muss ein gültiger Domainname sein'; +$lng['error']['dns_mx_noalias'] = 'Der MX Eintrag darf kein CNAME Eintrag sein.'; +$lng['error']['dns_cname_invaliddom'] = 'Ungültiger Domain-Name für CNAME Eintrag'; +$lng['error']['dns_cname_nomorerr'] = 'Es existiert bereits ein Eintrag mit dem gleichen Namen. Dieser Eintrag kann daher nicht für CNAME genutzt werden.'; +$lng['error']['dns_ns_invaliddom'] = 'Ungültiger Domain-Name für NS Eintrag'; +$lng['error']['dns_srv_prioempty'] = 'Ungültige SRV Priorität angegeben'; +$lng['error']['dns_srv_invalidcontent'] = 'Ungültiger Wert des SRV Eintrags, dieser muss aus den Feldern: weight, port und target, bestehen. Bsp.: 5 5060 sipserver.example.com.'; +$lng['error']['dns_srv_needdom'] = 'Der Wert des SRV Eintrags muss ein gültiger Domainname sein'; +$lng['error']['dns_srv_noalias'] = 'Der SRV Eintrag darf kein CNAME Eintrag sein..'; +$lng['error']['dns_duplicate_entry'] = 'Eintrag existiert bereits'; +$lng['success']['dns_record_added'] = 'Eintrag erfolgreich hinzugefügt'; +$lng['success']['dns_record_deleted'] = 'Eintrag erfolgreich entfernt'; +$lng['dnseditor']['edit'] = 'DNS editieren'; +$lng['dnseditor']['records'] = 'Einträge'; diff --git a/scripts/jobs/cron_tasks.php b/scripts/jobs/cron_tasks.php index 99573e9b..1bf460be 100644 --- a/scripts/jobs/cron_tasks.php +++ b/scripts/jobs/cron_tasks.php @@ -181,7 +181,11 @@ while ($row = $result_tasks_stmt->fetch(PDO::FETCH_ASSOC)) { */ elseif ($row['type'] == '4' && (int)Settings::Get('system.bind_enable') != 0) { - $bindclass ="bind2"; + $bindclass ="bind"; + + if (Settings::Get('system.dnsenabled') == '1') { + $bindclass = "bind2"; + } if (!isset($nameserver)) { $nameserver = new $bindclass($cronlog); diff --git a/templates/Sparkle/admin/domains/domains_edit.tpl b/templates/Sparkle/admin/domains/domains_edit.tpl index 275b0077..75f71bfd 100644 --- a/templates/Sparkle/admin/domains/domains_edit.tpl +++ b/templates/Sparkle/admin/domains/domains_edit.tpl @@ -4,8 +4,8 @@ $header

{$title}  {$title} - -  (edit DNS) + +  ({$lng['dnseditor']['edit']})

diff --git a/templates/Sparkle/customer/domains/domains_edit.tpl b/templates/Sparkle/customer/domains/domains_edit.tpl index 3979537e..d7a3c9ea 100644 --- a/templates/Sparkle/customer/domains/domains_edit.tpl +++ b/templates/Sparkle/customer/domains/domains_edit.tpl @@ -4,6 +4,9 @@ $header

{$title}  {$title} + +  ({$lng['dnseditor']['edit']}) +

diff --git a/templates/Sparkle/dns_editor/index.tpl b/templates/Sparkle/dns_editor/index.tpl index 737873cb..bc18f76f 100644 --- a/templates/Sparkle/dns_editor/index.tpl +++ b/templates/Sparkle/dns_editor/index.tpl @@ -3,7 +3,7 @@ $header

  - DNS Editor ({$domain}, {$entriescount} records) + DNS Editor ({$domain}, {$entriescount} {$lng['dnseditor']['records']})

From 86dc57c2ccd2c84c0c48f05343fcd5c827ca35b1 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Fri, 13 May 2016 19:40:37 +0200 Subject: [PATCH 0052/1335] outsource some dns functions to own files; allow opening of dns-editor only for domains that belong to the user (or the user has permission to edit as admin/reseller) Signed-off-by: Michael Kaufmann (d00p) --- dns_editor.php | 10 +-- .../dns/function.createDomainZone.php | 71 --------------- .../dns/function.generateDkimEntries.php | 87 +++++++++++++++++++ .../dns/function.getAllowedDomainEntry.php | 49 +++++++++++ lng/english.lng.php | 1 + lng/german.lng.php | 1 + 6 files changed, 139 insertions(+), 80 deletions(-) create mode 100644 lib/functions/dns/function.generateDkimEntries.php create mode 100644 lib/functions/dns/function.getAllowedDomainEntry.php diff --git a/dns_editor.php b/dns_editor.php index 0a5747b5..777eeeff 100644 --- a/dns_editor.php +++ b/dns_editor.php @@ -29,15 +29,7 @@ $content = isset($_POST['record']['content']) ? trim($_POST['record']['content'] $ttl = isset($_POST['record']['ttl']) ? (int) $_POST['record']['ttl'] : 18000; // get domain-name -$dom_stmt = Database::prepare("SELECT domain, isbinddomain FROM `" . TABLE_PANEL_DOMAINS . "` WHERE id = :did"); -$domain = Database::pexecute_first($dom_stmt, array( - 'did' => $domain_id -)); - -if ($domain['isbinddomain'] != '1') { - standard_error('dns_domain_nodns'); -} -$domain = $idna_convert->decode($domain['domain']); +$domain = getAllowedDomainEntry($domain_id, AREA, $userinfo, $idna_convert); // select all entries $sel_stmt = Database::prepare("SELECT * FROM `" . TABLE_DOMAIN_DNS . "` WHERE domain_id = :did"); diff --git a/lib/functions/dns/function.createDomainZone.php b/lib/functions/dns/function.createDomainZone.php index 51011107..e3d72a62 100644 --- a/lib/functions/dns/function.createDomainZone.php +++ b/lib/functions/dns/function.createDomainZone.php @@ -278,77 +278,6 @@ function addRequiredEntry($record = '@', $type = 'A', &$required) $required[$type][md5($record)] = $record; } -function generateDkimEntries($domain) -{ - $zone_dkim = array(); - - if (Settings::Get('dkim.use_dkim') == '1' && $domain['dkim'] == '1' && $domain['dkim_pubkey'] != '') { - // start - $dkim_txt = 'v=DKIM1;'; - - // algorithm - $algorithm = explode(',', Settings::Get('dkim.dkim_algorithm')); - $alg = ''; - foreach ($algorithm as $a) { - if ($a == 'all') { - break; - } else { - $alg .= $a . ':'; - } - } - - if ($alg != '') { - $alg = substr($alg, 0, - 1); - $dkim_txt .= 'h=' . $alg . ';'; - } - - // notes - if (trim(Settings::Get('dkim.dkim_notes') != '')) { - $dkim_txt .= 'n=' . trim(Settings::Get('dkim.dkim_notes')) . ';'; - } - - // key - $dkim_txt .= 'k=rsa;p=' . trim(preg_replace('/-----BEGIN PUBLIC KEY-----(.+)-----END PUBLIC KEY-----/s', '$1', str_replace("\n", '', $domain['dkim_pubkey']))) . ';'; - - // service-type - if (Settings::Get('dkim.dkim_servicetype') == '1') { - $dkim_txt .= 's=email;'; - } - - // end-part - $dkim_txt .= 't=s'; - - // split if necessary - $txt_record_split = ''; - $lbr = 50; - for ($pos = 0; $pos <= strlen($dkim_txt) - 1; $pos += $lbr) { - $txt_record_split .= (($pos == 0) ? '("' : "\t\t\t\t\t \"") . substr($dkim_txt, $pos, $lbr) . (($pos >= strlen($dkim_txt) - $lbr) ? '")' : '"') . "\n"; - } - - // dkim-entry - $zone_dkim[] = $txt_record_split; - - // adsp-entry - if (Settings::Get('dkim.dkim_add_adsp') == "1") { - $adsp = '"dkim='; - switch ((int) Settings::Get('dkim.dkim_add_adsppolicy')) { - case 0: - $adsp .= 'unknown"'; - break; - case 1: - $adsp .= 'all"'; - break; - case 2: - $adsp .= 'discardable"'; - break; - } - $zone_dkim[] = $adsp; - } - } - - return $zone_dkim; -} - function encloseTXTContent($txt_content, $isMultiLine = false) { // check that TXT content is enclosed in " " diff --git a/lib/functions/dns/function.generateDkimEntries.php b/lib/functions/dns/function.generateDkimEntries.php new file mode 100644 index 00000000..4b53d056 --- /dev/null +++ b/lib/functions/dns/function.generateDkimEntries.php @@ -0,0 +1,87 @@ + (2016-) + * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt + * @package Functions + * + */ + +function generateDkimEntries($domain) +{ + $zone_dkim = array(); + + if (Settings::Get('dkim.use_dkim') == '1' && $domain['dkim'] == '1' && $domain['dkim_pubkey'] != '') { + // start + $dkim_txt = 'v=DKIM1;'; + + // algorithm + $algorithm = explode(',', Settings::Get('dkim.dkim_algorithm')); + $alg = ''; + foreach ($algorithm as $a) { + if ($a == 'all') { + break; + } else { + $alg .= $a . ':'; + } + } + + if ($alg != '') { + $alg = substr($alg, 0, - 1); + $dkim_txt .= 'h=' . $alg . ';'; + } + + // notes + if (trim(Settings::Get('dkim.dkim_notes') != '')) { + $dkim_txt .= 'n=' . trim(Settings::Get('dkim.dkim_notes')) . ';'; + } + + // key + $dkim_txt .= 'k=rsa;p=' . trim(preg_replace('/-----BEGIN PUBLIC KEY-----(.+)-----END PUBLIC KEY-----/s', '$1', str_replace("\n", '', $domain['dkim_pubkey']))) . ';'; + + // service-type + if (Settings::Get('dkim.dkim_servicetype') == '1') { + $dkim_txt .= 's=email;'; + } + + // end-part + $dkim_txt .= 't=s'; + + // split if necessary + $txt_record_split = ''; + $lbr = 50; + for ($pos = 0; $pos <= strlen($dkim_txt) - 1; $pos += $lbr) { + $txt_record_split .= (($pos == 0) ? '("' : "\t\t\t\t\t \"") . substr($dkim_txt, $pos, $lbr) . (($pos >= strlen($dkim_txt) - $lbr) ? '")' : '"') . "\n"; + } + + // dkim-entry + $zone_dkim[] = $txt_record_split; + + // adsp-entry + if (Settings::Get('dkim.dkim_add_adsp') == "1") { + $adsp = '"dkim='; + switch ((int) Settings::Get('dkim.dkim_add_adsppolicy')) { + case 0: + $adsp .= 'unknown"'; + break; + case 1: + $adsp .= 'all"'; + break; + case 2: + $adsp .= 'discardable"'; + break; + } + $zone_dkim[] = $adsp; + } + } + + return $zone_dkim; +} \ No newline at end of file diff --git a/lib/functions/dns/function.getAllowedDomainEntry.php b/lib/functions/dns/function.getAllowedDomainEntry.php new file mode 100644 index 00000000..29ba2a55 --- /dev/null +++ b/lib/functions/dns/function.getAllowedDomainEntry.php @@ -0,0 +1,49 @@ + (2016-) + * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt + * @package Functions + * + */ + +function getAllowedDomainEntry($domain_id, $area = 'customer', $userinfo, &$idna_convert) +{ + $dom_data = array( + 'did' => $domain_id + ); + + $where_clause = ''; + if ($area == 'admin') { + if ($userinfo['domains_see_all'] != '1') { + $where_clause = '`adminid` = :uid'; + $dom_data['uid'] = $userinfo['userid']; + } + } else { + $where_clause = '`customerid` = :uid'; + $dom_data['uid'] = $userinfo['userid']; + } + + $dom_stmt = Database::prepare(" + SELECT domain, isbinddomain + FROM `" . TABLE_PANEL_DOMAINS . "` + WHERE " . $where_clause . " AND id = :did + "); + $domain = Database::pexecute_first($dom_stmt, $dom_data); + + if ($domain) { + if ($domain['isbinddomain'] != '1') { + standard_error('dns_domain_nodns'); + } + return $idna_convert->decode($domain['domain']); + } + standard_error('dns_notfoundorallowed'); +} diff --git a/lng/english.lng.php b/lng/english.lng.php index 98487720..bbd3a7f1 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -2009,3 +2009,4 @@ $lng['success']['dns_record_added'] = 'Record added successfully'; $lng['success']['dns_record_deleted'] = 'Record deleted successfully'; $lng['dnseditor']['edit'] = 'edit DNS'; $lng['dnseditor']['records'] = 'records'; +$lng['error']['dns_notfoundorallowed'] = 'Domain not found or no permission'; diff --git a/lng/german.lng.php b/lng/german.lng.php index 0584a5b2..0079982f 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -1662,3 +1662,4 @@ $lng['success']['dns_record_added'] = 'Eintrag erfolgreich hinzugefügt'; $lng['success']['dns_record_deleted'] = 'Eintrag erfolgreich entfernt'; $lng['dnseditor']['edit'] = 'DNS editieren'; $lng['dnseditor']['records'] = 'Einträge'; +$lng['error']['dns_notfoundorallowed'] = 'Domain nicht gefunden oder keine Berechtigung'; From 552c6e6cf9f76a2c9b9096ad321e0bba5eec6709 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sat, 14 May 2016 08:32:38 +0200 Subject: [PATCH 0053/1335] add 'enable dns editor'-setting; fix missing isbinddomain index in customer_domain Signed-off-by: Michael Kaufmann (d00p) --- actions/admin/settings/160.nameserver.php | 8 ++++++++ customer_domains.php | 2 +- lng/english.lng.php | 2 ++ lng/german.lng.php | 2 ++ 4 files changed, 13 insertions(+), 1 deletion(-) diff --git a/actions/admin/settings/160.nameserver.php b/actions/admin/settings/160.nameserver.php index e738c3f3..030ceaeb 100644 --- a/actions/admin/settings/160.nameserver.php +++ b/actions/admin/settings/160.nameserver.php @@ -31,6 +31,14 @@ return array( 'save_method' => 'storeSettingField', 'overview_option' => true ), + 'system_dnsenabled' => array( + 'label' => $lng['serversettings']['dnseditorenable'], + 'settinggroup' => 'system', + 'varname' => 'dnsenabled', + 'type' => 'bool', + 'default' => false, + 'save_method' => 'storeSettingField' + ), 'system_bindconf_directory' => array( 'label' => $lng['serversettings']['bindconf_directory'], 'settinggroup' => 'system', diff --git a/customer_domains.php b/customer_domains.php index 643866e6..28f4833a 100644 --- a/customer_domains.php +++ b/customer_domains.php @@ -505,7 +505,7 @@ if ($page == 'overview') { } } elseif ($action == 'edit' && $id != 0) { - $stmt = Database::prepare("SELECT `d`.`id`, `d`.`customerid`, `d`.`domain`, `d`.`documentroot`, `d`.`isemaildomain`, `d`.`wwwserveralias`, `d`.`iswildcarddomain`, + $stmt = Database::prepare("SELECT `d`.`id`, `d`.`customerid`, `d`.`domain`, `d`.`documentroot`, `d`.`isemaildomain`, `d`.`isbinddomain`, `d`.`wwwserveralias`, `d`.`iswildcarddomain`, `d`.`parentdomainid`, `d`.`ssl_redirect`, `d`.`aliasdomain`, `d`.`openbasedir`, `d`.`openbasedir_path`, `d`.`letsencrypt`, `pd`.`subcanemaildomain` FROM `" . TABLE_PANEL_DOMAINS . "` `d`, `" . TABLE_PANEL_DOMAINS . "` `pd` WHERE `d`.`customerid` = :customerid diff --git a/lng/english.lng.php b/lng/english.lng.php index bbd3a7f1..47e8d9f3 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -2010,3 +2010,5 @@ $lng['success']['dns_record_deleted'] = 'Record deleted successfully'; $lng['dnseditor']['edit'] = 'edit DNS'; $lng['dnseditor']['records'] = 'records'; $lng['error']['dns_notfoundorallowed'] = 'Domain not found or no permission'; +$lng['serversettings']['dnseditorenable']['title'] = 'Enable DNS editor'; +$lng['serversettings']['dnseditorenable']['description'] = 'Allows admins and customer to manage domain dns entries'; diff --git a/lng/german.lng.php b/lng/german.lng.php index 0079982f..7f62b53b 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -1663,3 +1663,5 @@ $lng['success']['dns_record_deleted'] = 'Eintrag erfolgreich entfernt'; $lng['dnseditor']['edit'] = 'DNS editieren'; $lng['dnseditor']['records'] = 'Einträge'; $lng['error']['dns_notfoundorallowed'] = 'Domain nicht gefunden oder keine Berechtigung'; +$lng['serversettings']['dnseditorenable']['title'] = 'DNS Editor aktivieren'; +$lng['serversettings']['dnseditorenable']['description'] = 'Erlaubt es Admins und Kunden die DNS Einträge Ihrer Domains zu verwalten'; From 02654a256d9a191b2af6302cdb2586d0d1d747f7 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sat, 14 May 2016 08:59:46 +0200 Subject: [PATCH 0054/1335] fix sql query in getAllowedDomainEntry() Signed-off-by: Michael Kaufmann (d00p) --- lib/functions/dns/function.generateDkimEntries.php | 2 +- lib/functions/dns/function.getAllowedDomainEntry.php | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/functions/dns/function.generateDkimEntries.php b/lib/functions/dns/function.generateDkimEntries.php index 4b53d056..e3d5e95a 100644 --- a/lib/functions/dns/function.generateDkimEntries.php +++ b/lib/functions/dns/function.generateDkimEntries.php @@ -84,4 +84,4 @@ function generateDkimEntries($domain) } return $zone_dkim; -} \ No newline at end of file +} diff --git a/lib/functions/dns/function.getAllowedDomainEntry.php b/lib/functions/dns/function.getAllowedDomainEntry.php index 29ba2a55..8026859c 100644 --- a/lib/functions/dns/function.getAllowedDomainEntry.php +++ b/lib/functions/dns/function.getAllowedDomainEntry.php @@ -24,18 +24,18 @@ function getAllowedDomainEntry($domain_id, $area = 'customer', $userinfo, &$idna $where_clause = ''; if ($area == 'admin') { if ($userinfo['domains_see_all'] != '1') { - $where_clause = '`adminid` = :uid'; + $where_clause = '`adminid` = :uid AND '; $dom_data['uid'] = $userinfo['userid']; } } else { - $where_clause = '`customerid` = :uid'; + $where_clause = '`customerid` = :uid AND '; $dom_data['uid'] = $userinfo['userid']; } $dom_stmt = Database::prepare(" SELECT domain, isbinddomain FROM `" . TABLE_PANEL_DOMAINS . "` - WHERE " . $where_clause . " AND id = :did + WHERE " . $where_clause . " id = :did "); $domain = Database::pexecute_first($dom_stmt, $dom_data); From da785500cc14f52017e10523464cac17b4269ed7 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sat, 14 May 2016 18:39:18 +0200 Subject: [PATCH 0055/1335] remove invalid self-closing tag as it produces php-notices and was just added for design reasons Signed-off-by: Michael Kaufmann (d00p) --- lib/configfiles/jessie.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/configfiles/jessie.xml b/lib/configfiles/jessie.xml index 3ab1c2b5..1bbb713a 100644 --- a/lib/configfiles/jessie.xml +++ b/lib/configfiles/jessie.xml @@ -4022,7 +4022,7 @@ aliases: files - + /etc/insserv/overrides From e0e748a0bc98a3c675198bc6048c7564d9527697 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sun, 15 May 2016 08:46:23 +0200 Subject: [PATCH 0056/1335] outsource record-generation and zone-generation to classes for better handling Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/dns/class.DnsEntry.php | 47 +++++++++++++++++++ lib/classes/dns/class.DnsZone.php | 44 +++++++++++++++++ .../dns/function.createDomainZone.php | 32 +++++-------- lng/english.lng.php | 1 + templates/Sparkle/dns_editor/index.tpl | 7 +++ 5 files changed, 112 insertions(+), 19 deletions(-) create mode 100644 lib/classes/dns/class.DnsEntry.php create mode 100644 lib/classes/dns/class.DnsZone.php diff --git a/lib/classes/dns/class.DnsEntry.php b/lib/classes/dns/class.DnsEntry.php new file mode 100644 index 00000000..f1e36ded --- /dev/null +++ b/lib/classes/dns/class.DnsEntry.php @@ -0,0 +1,47 @@ + (2016-) + * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt + * @package Classes + * + */ +class DnsEntry +{ + + public $record; + + public $ttl; + + public $class = 'IN'; + + public $type; + + public $priority; + + public $content; + + public function __construct($record = '', $type = 'A', $content = null, $prio = 0, $ttl = 18000, $class = 'IN') + { + $this->record = $record; + $this->type = $type; + $this->content = $content; + $this->priority = $prio; + $this->ttl = $ttl; + $this->class = $class; + } + + public function __toString() + { + $result = $this->record . "\t" . $this->ttl . "\t" . $this->class . "\t" . $this->type . "\t" . (($this->prio >= 0 && ($this->type == 'MX' || $this->type == 'SRV')) ? $this->prio . "\t" : "") . $this->content . PHP_EOL; + return $result; + } +} diff --git a/lib/classes/dns/class.DnsZone.php b/lib/classes/dns/class.DnsZone.php new file mode 100644 index 00000000..b1b04eb5 --- /dev/null +++ b/lib/classes/dns/class.DnsZone.php @@ -0,0 +1,44 @@ + (2016-) + * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt + * @package Classes + * + */ +class DnsZone +{ + + public $ttl; + + public $origin; + + public $records; + + public function __construct($ttl = 18000, $origin = '', $records = null) + { + $this->ttl = $ttl; + $this->origin = $origin; + $this->records = $records; + } + + public function __toString() + { + $_zonefile = "\$TTL " . $this->ttl . PHP_EOL; + $_zonefile .= "\$ORIGIN " . $this->origin . "." . PHP_EOL; + if (! empty($this->records)) { + foreach ($this->records as $record) { + $_zonefile .= (string) $record; + } + } + return $_zonefile; + } +} diff --git a/lib/functions/dns/function.createDomainZone.php b/lib/functions/dns/function.createDomainZone.php index e3d72a62..4b990127 100644 --- a/lib/functions/dns/function.createDomainZone.php +++ b/lib/functions/dns/function.createDomainZone.php @@ -106,7 +106,7 @@ function createDomainZone($domain_id, $froxlorhostname = false) } $primary_ns = null; - $zonefile = ""; + $zonerecords = array(); // now generate all records and unset the required entries we have foreach ($dom_entries as $entry) { @@ -121,7 +121,7 @@ function createDomainZone($domain_id, $froxlorhostname = false) // use the first NS entry as primary ns $primary_ns = $entry['content']; } - $zonefile .= formatEntry($entry['record'], $entry['type'], $entry['content'], $entry['prio'], $entry['ttl']); + $zonerecords[] = new DnsEntry($entry['record'], $entry['type'], $entry['content'], $entry['prio'], $entry['ttl']); } // add missing required entries @@ -151,9 +151,9 @@ function createDomainZone($domain_id, $froxlorhostname = false) foreach ($required_entries as $type => $records) { foreach ($records as $record) { if ($type == 'A' && filter_var($ip['ip'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false) { - $zonefile .= formatEntry($record, 'A', $ip['ip']); + $zonerecords[] = new DnsEntry($record, 'A', $ip['ip']); } elseif ($type == 'AAAA' && filter_var($ip['ip'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) !== false) { - $zonefile .= formatEntry($record, 'AAAA', $ip['ip']); + $zonerecords[] = new DnsEntry($record, 'AAAA', $ip['ip']); } } } @@ -179,7 +179,7 @@ function createDomainZone($domain_id, $froxlorhostname = false) // use the first NS entry as primary ns $primary_ns = $nameserver; } - $zonefile .= formatEntry($record, 'NS', $nameserver); + $zonerecords[] = new DnsEntry($record, 'NS', $nameserver); } } } @@ -205,7 +205,7 @@ function createDomainZone($domain_id, $froxlorhostname = false) foreach ($required_entries as $type => $records) { if ($type == 'MX') { foreach ($records as $record) { - $zonefile .= formatEntry($record, 'MX', $mx_details[1], $mx_details[0]); + $zonerecords[] = new DnsEntry($record, 'MX', $mx_details[1], $mx_details[0]); } } } @@ -226,16 +226,16 @@ function createDomainZone($domain_id, $froxlorhostname = false) foreach ($records as $record) { if ($record == '@SPF@') { $txt_content = Settings::Get('spf.spf_entry'); - $zonefile .= formatEntry('@', 'TXT', encloseTXTContent($txt_content)); + $zonerecords[] = new DnsEntry('@', 'TXT', encloseTXTContent($txt_content)); } elseif ($record == 'dkim_' . $domain['dkim_id'] . '._domainkey' && ! empty($dkim_entries)) { // check for multiline entry $multiline = false; if (substr($dkim_entries[0], 0, 1) == '(') { $multiline = true; } - $zonefile .= formatEntry($record, 'TXT', encloseTXTContent($dkim_entries[0], $multiline)); + $zonerecords[] = new DnsEntry($record, 'TXT', encloseTXTContent($dkim_entries[0], $multiline)); } elseif ($record == '_adsp._domainkey' && ! empty($dkim_entries) && isset($dkim_entries[1])) { - $zonefile .= formatEntry($record, 'TXT', encloseTXTContent($dkim_entries[1])); + $zonerecords[] = new DnsEntry($record, 'TXT', encloseTXTContent($dkim_entries[1])); } } } @@ -256,18 +256,12 @@ function createDomainZone($domain_id, $froxlorhostname = false) $soa_content .= "604800\t; expire (7 days)" . PHP_EOL; $soa_content .= "1200\t)\t; minimum (20 mins)"; - $_zonefile = "\$TTL " . (int) Settings::Get('system.defaultttl') . PHP_EOL; - $_zonefile .= "\$ORIGIN " . $domain['domain'] . "." . PHP_EOL; - $_zonefile .= formatEntry('@', 'SOA', $soa_content); - $zonefile = $_zonefile . $zonefile; + $soa_record = new DnsEntry('@', 'SOA', $soa_content); + array_unshift($zonerecords, $soa_record); - return $zonefile; -} + $zone = new DnsZone((int) Settings::Get('system.defaultttl'), $domain['domain'], $zonerecords); -function formatEntry($record = '@', $type = 'A', $content = null, $prio = 0, $ttl = 18000, $class = 'IN') -{ - $result = $record . "\t" . $ttl . "\t" . $class . "\t" . $type . "\t" . (($prio >= 0 && ($type == 'MX' || $type == 'SRV')) ? $prio . "\t" : "") . $content . PHP_EOL; - return $result; + return (string)$zone; } function addRequiredEntry($record = '@', $type = 'A', &$required) diff --git a/lng/english.lng.php b/lng/english.lng.php index 47e8d9f3..43538921 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -2012,3 +2012,4 @@ $lng['dnseditor']['records'] = 'records'; $lng['error']['dns_notfoundorallowed'] = 'Domain not found or no permission'; $lng['serversettings']['dnseditorenable']['title'] = 'Enable DNS editor'; $lng['serversettings']['dnseditorenable']['description'] = 'Allows admins and customer to manage domain dns entries'; +$lng['dns']['howitworks'] = 'Here you can manage DNS entries for your domain. Note that froxlor will automatically generate NS/MX/A/AAAA records for you. The custom entries are prefered, only missing entries will be automatically generated.'; diff --git a/templates/Sparkle/dns_editor/index.tpl b/templates/Sparkle/dns_editor/index.tpl index bc18f76f..732c3e90 100644 --- a/templates/Sparkle/dns_editor/index.tpl +++ b/templates/Sparkle/dns_editor/index.tpl @@ -7,6 +7,13 @@ $header +
+
+
{$lng['admin']['warning']}
+
{$lng['dns']['howitworks']}
+
+
+
{$lng['error']['error']}
From 8d8da0986a1a1d69881d89b5a69fbba5bde22470 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sun, 15 May 2016 08:53:47 +0200 Subject: [PATCH 0057/1335] fix typo in DnsEntry class Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/dns/class.DnsEntry.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/classes/dns/class.DnsEntry.php b/lib/classes/dns/class.DnsEntry.php index f1e36ded..c5bcab79 100644 --- a/lib/classes/dns/class.DnsEntry.php +++ b/lib/classes/dns/class.DnsEntry.php @@ -41,7 +41,7 @@ class DnsEntry public function __toString() { - $result = $this->record . "\t" . $this->ttl . "\t" . $this->class . "\t" . $this->type . "\t" . (($this->prio >= 0 && ($this->type == 'MX' || $this->type == 'SRV')) ? $this->prio . "\t" : "") . $this->content . PHP_EOL; + $result = $this->record . "\t" . $this->ttl . "\t" . $this->class . "\t" . $this->type . "\t" . (($this->priority >= 0 && ($this->type == 'MX' || $this->type == 'SRV')) ? $this->priority . "\t" : "") . $this->content . PHP_EOL; return $result; } } From 68fa0e6576c05eb0ac8d0c38feba05d07e989d60 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sun, 15 May 2016 09:56:48 +0200 Subject: [PATCH 0058/1335] let createDomainZone() return the DnsZone object for better use later Signed-off-by: Michael Kaufmann (d00p) --- dns_editor.php | 3 ++- lib/classes/dns/class.DnsZone.php | 5 ++++- lib/functions/dns/function.createDomainZone.php | 4 ++-- scripts/jobs/cron_tasks.inc.dns.15.bind.php | 3 ++- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/dns_editor.php b/dns_editor.php index 777eeeff..bc9726a3 100644 --- a/dns_editor.php +++ b/dns_editor.php @@ -279,5 +279,6 @@ foreach ($type_select_values as $_type) { eval("\$record_list=\"" . getTemplate("dns_editor/list", true) . "\";"); -$zonefile = createDomainZone($domain_id); +$zone = createDomainZone($domain_id); +$zonefile = (string)$zone; eval("echo \"" . getTemplate("dns_editor/index", true) . "\";"); diff --git a/lib/classes/dns/class.DnsZone.php b/lib/classes/dns/class.DnsZone.php index b1b04eb5..9c4a8161 100644 --- a/lib/classes/dns/class.DnsZone.php +++ b/lib/classes/dns/class.DnsZone.php @@ -21,12 +21,15 @@ class DnsZone public $origin; + public $serial; + public $records; - public function __construct($ttl = 18000, $origin = '', $records = null) + public function __construct($ttl = 18000, $origin = '', $serial = '', $records = null) { $this->ttl = $ttl; $this->origin = $origin; + $this->serial = $serial; $this->records = $records; } diff --git a/lib/functions/dns/function.createDomainZone.php b/lib/functions/dns/function.createDomainZone.php index 4b990127..ca1e71dd 100644 --- a/lib/functions/dns/function.createDomainZone.php +++ b/lib/functions/dns/function.createDomainZone.php @@ -259,9 +259,9 @@ function createDomainZone($domain_id, $froxlorhostname = false) $soa_record = new DnsEntry('@', 'SOA', $soa_content); array_unshift($zonerecords, $soa_record); - $zone = new DnsZone((int) Settings::Get('system.defaultttl'), $domain['domain'], $zonerecords); + $zone = new DnsZone((int) Settings::Get('system.defaultttl'), $domain['domain'], $domain['bindserial'], $zonerecords); - return (string)$zone; + return $zone; } function addRequiredEntry($record = '@', $type = 'A', &$required) diff --git a/scripts/jobs/cron_tasks.inc.dns.15.bind.php b/scripts/jobs/cron_tasks.inc.dns.15.bind.php index 1e72166d..4c368be2 100644 --- a/scripts/jobs/cron_tasks.inc.dns.15.bind.php +++ b/scripts/jobs/cron_tasks.inc.dns.15.bind.php @@ -46,7 +46,8 @@ class bind2 extends DnsBase } // create zone-file $this->_logger->logAction(CRON_ACTION, LOG_DEBUG, 'Generating dns zone for ' . $domain['domain']); - $zonefile = createDomainZone($domain['id'], $isFroxlorHostname); + $zone = createDomainZone($domain['id'], $isFroxlorHostname); + $zonefile = (string)$zone; $domain['zonefile'] = 'domains/' . $domain['domain'] . '.zone'; $zonefile_name = makeCorrectFile(Settings::Get('system.bindconf_directory') . '/' . $domain['zonefile']); $zonefile_handler = fopen($zonefile_name, 'w'); From 28115e6b1d40d195fcb748803aed595bcbc88ccd Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sun, 15 May 2016 10:08:42 +0200 Subject: [PATCH 0059/1335] add missing german language string Signed-off-by: Michael Kaufmann (d00p) --- lng/german.lng.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lng/german.lng.php b/lng/german.lng.php index 7f62b53b..70e483ad 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -1665,3 +1665,4 @@ $lng['dnseditor']['records'] = 'Einträge'; $lng['error']['dns_notfoundorallowed'] = 'Domain nicht gefunden oder keine Berechtigung'; $lng['serversettings']['dnseditorenable']['title'] = 'DNS Editor aktivieren'; $lng['serversettings']['dnseditorenable']['description'] = 'Erlaubt es Admins und Kunden die DNS Einträge Ihrer Domains zu verwalten'; +$lng['dns']['howitworks'] = 'Hier können DNS Einträge für die Domain verwaltet werden. Beachte das Froxlor automatisch NS/MX/A/AAAA Einträge generiert. Die benutzerdefinierten Einträge werden bevorzugt, nur fehlende Einträge werden automatisch generiert.'; From 1d4211a5ceb2f70f771fa8b829c19b34fd8a111d Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 16 May 2016 17:20:49 +0200 Subject: [PATCH 0060/1335] remove wrong a2* commands for rhel/centos Signed-off-by: Michael Kaufmann (d00p) --- lib/configfiles/rhel_centos.xml | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/configfiles/rhel_centos.xml b/lib/configfiles/rhel_centos.xml index b43e5a03..9057fba5 100644 --- a/lib/configfiles/rhel_centos.xml +++ b/lib/configfiles/rhel_centos.xml @@ -40,8 +40,6 @@ - - From 0ae0178b4ca13e124f53cb9d07f18da0c5a7bca3 Mon Sep 17 00:00:00 2001 From: Daniel Reichelt Date: Mon, 16 May 2016 16:23:31 +0200 Subject: [PATCH 0061/1335] LE: PSR-2 formatting --- lib/classes/ssl/class.lescript.php | 86 ++++++++++++++++-------------- scripts/jobs/cron_letsencrypt.php | 36 +++++++------ 2 files changed, 66 insertions(+), 56 deletions(-) diff --git a/lib/classes/ssl/class.lescript.php b/lib/classes/ssl/class.lescript.php index 189d2333..9a2b2b94 100644 --- a/lib/classes/ssl/class.lescript.php +++ b/lib/classes/ssl/class.lescript.php @@ -62,14 +62,15 @@ class lescript $keys = $this->generateKey(); // Only store the accountkey in production, in staging always generate a new key if (Settings::Get('system.letsencryptca') == 'production') { - $upd_stmt = Database::prepare(" - UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET `lepublickey` = :public, `leprivatekey` = :private WHERE `customerid` = :customerid; - "); - Database::pexecute($upd_stmt, array( - 'public' => $keys['public'], - 'private' => $keys['private'], - 'customerid' => $certrow['customerid'] - )); + $upd_stmt = Database::prepare( + "UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET `lepublickey` = :public, `leprivatekey` = :private " . + "WHERE `customerid` = :customerid;"); + Database::pexecute($upd_stmt, + array( + 'public' => $keys['public'], + 'private' => $keys['private'], + 'customerid' => $certrow['customerid'] + )); } $this->accountKey = $keys['private']; $this->postNewReg(); @@ -101,13 +102,14 @@ class lescript $this->log("Requesting challenge for $domain"); - $response = $this->signedRequest("/acme/new-authz", array( - "resource" => "new-authz", - "identifier" => array( - "type" => "dns", - "value" => $domain - ) - )); + $response = $this->signedRequest("/acme/new-authz", + array( + "resource" => "new-authz", + "identifier" => array( + "type" => "dns", + "value" => $domain + ) + )); // if response is not an array but a string, it's most likely a server-error, e.g. // ErrorAn error occurred while processing your request. @@ -121,9 +123,10 @@ class lescript } // choose http-01 challenge only - $challenge = array_reduce($response['challenges'], function ($v, $w) { - return $v ? $v : ($w['type'] == 'http-01' ? $w : false); - }); + $challenge = array_reduce($response['challenges'], + function ($v, $w) { + return $v ? $v : ($w['type'] == 'http-01' ? $w : false); + }); if (! $challenge) throw new RuntimeException("HTTP Challenge for $domain is not available. Whole response: " . json_encode($response)); @@ -145,8 +148,7 @@ class lescript "e" => Base64UrlSafeEncoder::encode($accountKeyDetails["rsa"]["e"]), "kty" => "RSA", "n" => Base64UrlSafeEncoder::encode($accountKeyDetails["rsa"]["n"]) - ) - ; + ); $payload = $challenge['token'] . '.' . Base64UrlSafeEncoder::encode(hash('sha256', json_encode($header), true)); file_put_contents($tokenPath, $payload); @@ -174,12 +176,13 @@ class lescript $this->log("Sending request to challenge"); // send request to challenge - $result = $this->signedRequest($challenge['uri'], array( - "resource" => "challenge", - "type" => "http-01", - "keyAuthorization" => $payload, - "token" => $challenge['token'] - )); + $result = $this->signedRequest($challenge['uri'], + array( + "resource" => "challenge", + "type" => "http-01", + "keyAuthorization" => $payload, + "token" => $challenge['token'] + )); // waiting loop // we wait for a maximum of 30 seconds to avoid endless loops @@ -306,7 +309,8 @@ class lescript $tmpConfPath = $tmpConfMeta["uri"]; // workaround to get SAN working - fwrite($tmpConf, 'HOME = . + fwrite($tmpConf, + 'HOME = . RANDFILE = $ENV::HOME/.rnd [ req ] default_bits = ' . Settings::Get('system.letsencryptkeysize') . ' @@ -320,15 +324,16 @@ basicConstraints = CA:FALSE subjectAltName = ' . $san . ' keyUsage = nonRepudiation, digitalSignature, keyEncipherment'); - $csr = openssl_csr_new(array( - "CN" => $domain, - "ST" => Settings::Get('system.letsencryptstate'), - "C" => Settings::Get('system.letsencryptcountrycode'), - "O" => "Unknown" - ), $privateKey, array( - "config" => $tmpConfPath, - "digest_alg" => "sha256" - )); + $csr = openssl_csr_new( + array( + "CN" => $domain, + "ST" => Settings::Get('system.letsencryptstate'), + "C" => Settings::Get('system.letsencryptcountrycode'), + "O" => "Unknown" + ), $privateKey, array( + "config" => $tmpConfPath, + "digest_alg" => "sha256" + )); if (! $csr) throw new \RuntimeException("CSR couldn't be generated! " . openssl_error_string()); @@ -343,10 +348,11 @@ keyUsage = nonRepudiation, digitalSignature, keyEncipherment'); private function generateKey() { - $res = openssl_pkey_new(array( - "private_key_type" => OPENSSL_KEYTYPE_RSA, - "private_key_bits" => (int) Settings::Get('system.letsencryptkeysize') - )); + $res = openssl_pkey_new( + array( + "private_key_type" => OPENSSL_KEYTYPE_RSA, + "private_key_bits" => (int) Settings::Get('system.letsencryptkeysize') + )); if (! openssl_pkey_export($res, $privateKey)) { throw new \RuntimeException("Key export failed!"); diff --git a/scripts/jobs/cron_letsencrypt.php b/scripts/jobs/cron_letsencrypt.php index 1848480f..3c7808d7 100644 --- a/scripts/jobs/cron_letsencrypt.php +++ b/scripts/jobs/cron_letsencrypt.php @@ -1,5 +1,4 @@ logAction(CRON_ACTION, LOG_INFO, "Updating Let's Encrypt certificates" if (! extension_loaded('curl')) { $cronlog->logAction(CRON_ACTION, LOG_ERR, "Let's Encrypt requires the php cURL extension to be installed."); - exit; + exit(); } -$certificates_stmt = Database::query(" +$certificates_stmt = Database::query( + " SELECT domssl.`id`, domssl.`domainid`, domssl.expirationdate, domssl.`ssl_cert_file`, domssl.`ssl_key_file`, domssl.`ssl_ca_file`, domssl.`ssl_csr_file`, dom.`domain`, dom.`iswildcarddomain`, dom.`wwwserveralias`, dom.`documentroot`, dom.`id` as 'domainid', dom.`ssl_redirect`, cust.`leprivatekey`, cust.`lepublickey`, cust.customerid, cust.loginname FROM `" . TABLE_PANEL_CUSTOMERS . "` as cust, `" . TABLE_PANEL_DOMAINS . "` dom LEFT JOIN `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` domssl ON (dom.id = domssl.domainid) WHERE dom.customerid = cust.customerid AND dom.letsencrypt = 1 AND (domssl.expirationdate < DATE_ADD(NOW(), INTERVAL 30 DAY) OR domssl.expirationdate IS NULL) "); -$updcert_stmt = Database::prepare(" +$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, expirationdate = :expirationdate "); @@ -92,16 +93,17 @@ foreach ($certrows as $certrow) { $newcert = openssl_x509_parse($return['crt']); // Store the new data - Database::pexecute($updcert_stmt, array( - 'id' => $certrow['id'], - 'domainid' => $certrow['domainid'], - 'crt' => $return['crt'], - 'key' => $return['key'], - 'ca' => $return['chain'], - 'chain' => $return['chain'], - 'csr' => $return['csr'], - 'expirationdate' => date('Y-m-d H:i:s', $newcert['validTo_time_t']) - )); + Database::pexecute($updcert_stmt, + array( + 'id' => $certrow['id'], + 'domainid' => $certrow['domainid'], + 'crt' => $return['crt'], + 'key' => $return['key'], + 'ca' => $return['chain'], + 'chain' => $return['chain'], + 'csr' => $return['csr'], + 'expirationdate' => date('Y-m-d H:i:s', $newcert['validTo_time_t']) + )); if ($certrow['ssl_redirect'] == 3) { Database::pexecute($upddom_stmt, array( @@ -113,10 +115,12 @@ foreach ($certrows as $certrow) { $changedetected = 1; } catch (Exception $e) { - $cronlog->logAction(CRON_ACTION, LOG_ERR, "Could not get Let's Encrypt certificate for " . $certrow['domain'] . ": " . $e->getMessage()); + $cronlog->logAction(CRON_ACTION, LOG_ERR, + "Could not get Let's Encrypt certificate for " . $certrow['domain'] . ": " . $e->getMessage()); } } else { - $cronlog->logAction(CRON_ACTION, LOG_WARNING, "Skipping Let's Encrypt generation for " . $certrow['domain'] . " due to an enabled ssl_redirect"); + $cronlog->logAction(CRON_ACTION, LOG_WARNING, + "Skipping Let's Encrypt generation for " . $certrow['domain'] . " due to an enabled ssl_redirect"); } } From 712aebb8641877d81135c08e476d3ca3b52182ec Mon Sep 17 00:00:00 2001 From: Daniel Reichelt Date: Sun, 15 May 2016 14:25:37 +0200 Subject: [PATCH 0062/1335] LE: improve SQL readability --- scripts/jobs/cron_letsencrypt.php | 55 +++++++++++++++++++++++++------ 1 file changed, 45 insertions(+), 10 deletions(-) diff --git a/scripts/jobs/cron_letsencrypt.php b/scripts/jobs/cron_letsencrypt.php index 3c7808d7..0ee4722e 100644 --- a/scripts/jobs/cron_letsencrypt.php +++ b/scripts/jobs/cron_letsencrypt.php @@ -29,20 +29,55 @@ if (! extension_loaded('curl')) { $certificates_stmt = Database::query( " - SELECT domssl.`id`, domssl.`domainid`, domssl.expirationdate, domssl.`ssl_cert_file`, domssl.`ssl_key_file`, domssl.`ssl_ca_file`, domssl.`ssl_csr_file`, dom.`domain`, dom.`iswildcarddomain`, dom.`wwwserveralias`, - dom.`documentroot`, dom.`id` as 'domainid', dom.`ssl_redirect`, cust.`leprivatekey`, cust.`lepublickey`, cust.customerid, cust.loginname - FROM `" . TABLE_PANEL_CUSTOMERS . "` as cust, `" . TABLE_PANEL_DOMAINS . "` dom LEFT JOIN `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` domssl ON (dom.id = domssl.domainid) - WHERE dom.customerid = cust.customerid AND dom.letsencrypt = 1 AND (domssl.expirationdate < DATE_ADD(NOW(), INTERVAL 30 DAY) OR domssl.expirationdate IS NULL) -"); + SELECT + domssl.`id`, + domssl.`domainid`, + domssl.expirationdate, + domssl.`ssl_cert_file`, + domssl.`ssl_key_file`, + domssl.`ssl_ca_file`, + domssl.`ssl_csr_file`, + dom.`domain`, + dom.`iswildcarddomain`, + dom.`wwwserveralias`, + dom.`documentroot`, + dom.`id` AS 'domainid', + dom.`ssl_redirect`, + cust.`leprivatekey`, + cust.`lepublickey`, + cust.`customerid`, + 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 dom.`letsencrypt` = 1 + AND ( + domssl.`expirationdate` < DATE_ADD(NOW(), INTERVAL 30 DAY) + OR domssl.`expirationdate` IS NULL + ) + "); $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, expirationdate = :expirationdate -"); + 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, + `expirationdate` = :expirationdate + "); -$upddom_stmt = Database::prepare(" - UPDATE `" . TABLE_PANEL_DOMAINS . "` SET `ssl_redirect` = '1' WHERE `id` = :domainid -"); +$upddom_stmt = Database::prepare("UPDATE `" . TABLE_PANEL_DOMAINS . "` SET `ssl_redirect` = '1' WHERE `id` = :domainid"); $changedetected = 0; $certrows = $certificates_stmt->fetchAll(PDO::FETCH_ASSOC); From 001f10f74e0d7b8c37f152129e4e0916ec329d8b Mon Sep 17 00:00:00 2001 From: Daniel Reichelt Date: Mon, 16 May 2016 16:28:23 +0200 Subject: [PATCH 0063/1335] LE: catch error due to rate-limited account registration and fix bad english in log message --- lib/classes/ssl/class.lescript.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/classes/ssl/class.lescript.php b/lib/classes/ssl/class.lescript.php index 9a2b2b94..4ea258b0 100644 --- a/lib/classes/ssl/class.lescript.php +++ b/lib/classes/ssl/class.lescript.php @@ -73,6 +73,12 @@ class lescript )); } $this->accountKey = $keys['private']; + + $response = $this->postNewReg(); + if ($this->client->getLastCode() != 201) { + throw new \RuntimeException("Account not initialized, probably due to rate limiting. Whole response: " . $response); + } + $this->postNewReg(); $this->log('New account certificate registered'); } else { @@ -84,7 +90,7 @@ class lescript public function signDomains(array $domains, $domainkey = null, $csr = null) { if (! $this->accountKey) { - throw new \RuntimeException("Account not initiated"); + throw new \RuntimeException("Account not initialized"); } $this->log('Starting certificate generation process for domains'); From f3e05742b56a7a903a874f1a3c4df72dc382ed96 Mon Sep 17 00:00:00 2001 From: Daniel Reichelt Date: Mon, 16 May 2016 16:29:59 +0200 Subject: [PATCH 0064/1335] LE: change semantics of setting.letsencryptreuseold Previously setting.letsencryptreuseold determined wheter both a domain's private key and a CSR should be re-generated. Preparing support of alias domains in LE certificates, this is changed to only determine the re-generation of the private key. CSRs now are always re-generated. --- lib/classes/ssl/class.lescript.php | 4 +--- lng/english.lng.php | 4 ++-- lng/german.lng.php | 4 ++-- scripts/jobs/cron_letsencrypt.php | 27 +++++++-------------------- 4 files changed, 12 insertions(+), 27 deletions(-) diff --git a/lib/classes/ssl/class.lescript.php b/lib/classes/ssl/class.lescript.php index 4ea258b0..3bf8b6af 100644 --- a/lib/classes/ssl/class.lescript.php +++ b/lib/classes/ssl/class.lescript.php @@ -227,9 +227,7 @@ class lescript $this->client->getLastLinks(); - if (empty($csrfile) || Settings::Get('system.letsencryptreuseold') == 0) { - $csr = $this->generateCSR($privateDomainKey, $domains); - } + $csr = $this->generateCSR($privateDomainKey, $domains); // request certificates creation $result = $this->signedRequest("/acme/new-cert", array( diff --git a/lng/english.lng.php b/lng/english.lng.php index 6783d8a8..61849a5b 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -1949,8 +1949,8 @@ $lng['serversettings']['letsencryptchallengepath']['title'] = "Path for Let's En $lng['serversettings']['letsencryptchallengepath']['description'] = "Directory where the Let's Encrypt challenges should be offered from via a global alias.
ATTENTION: Let's Encrypt is still in beta"; $lng['serversettings']['letsencryptkeysize']['title'] = "Key size for new Let's Encrypt certificates"; $lng['serversettings']['letsencryptkeysize']['description'] = "Size of the key in Bits for new Let's Encrypt certificates.
ATTENTION: Let's Encrypt is still in beta"; -$lng['serversettings']['letsencryptreuseold']['title'] = "Re-use Let's Encrypt key / CSR"; -$lng['serversettings']['letsencryptreuseold']['description'] = "If activated, the same key and CSR will be used for every renew, otherwise a new key / CSR will be generated every time.
ATTENTION: Let's Encrypt is still in beta"; +$lng['serversettings']['letsencryptreuseold']['title'] = "Re-use Let's Encrypt key"; +$lng['serversettings']['letsencryptreuseold']['description'] = "If activated, the same key will be used for every renew, otherwise a new key will be generated every time.
ATTENTION: Let's Encrypt is still in beta"; $lng['serversettings']['leenabled']['title'] = "Enable Let's Encrypt"; $lng['serversettings']['leenabled']['description'] = "If activated, customers are able to let froxlor automatically generate and renew Let's Encrypt ssl-certificates for domains with a ssl IP/port.

Please remember that you need to go through the webserver-configuration when eabled because this feature needs a special configuration."; $lng['domains']['ssl_redirect_temporarilydisabled'] = "
The SSL redirect is temporarily deactivated while a new Let's Encrypt certificate is generated. It will be activated again after the certificate was generated."; diff --git a/lng/german.lng.php b/lng/german.lng.php index 671ce6c1..dd908b2f 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -1603,8 +1603,8 @@ $lng['serversettings']['letsencryptchallengepath']['title'] = "Verzeichnis für $lng['serversettings']['letsencryptchallengepath']['description'] = "Let's Encrypt challenges werden aus diesem Verzeichnis über einen globalen Alias ausgeliefert.
ACHTUNG: Let's Encrypt befindet sich noch im Test"; $lng['serversettings']['letsencryptkeysize']['title'] = "Schlüsselgröße für neue Let's Encrypt Zertifikate"; $lng['serversettings']['letsencryptkeysize']['description'] = "Größe des Schlüssels in Bit für neue Let's Encrypt Zertifikate.
ACHTUNG: Let's Encrypt befindet sich noch im Test"; -$lng['serversettings']['letsencryptreuseold']['title'] = "Let's Encrypt Schlüssel / CSR wiederverwenden"; -$lng['serversettings']['letsencryptreuseold']['description'] = "Wenn dies aktiviert ist, werden der alte Schlüssel und CSR bei jeder Verlängerung verwendet, andernfalls wird ein neues Paar generiert.
ACHTUNG: Let's Encrypt befindet sich noch im Test"; +$lng['serversettings']['letsencryptreuseold']['title'] = "Let's Encrypt Schlüssel wiederverwenden"; +$lng['serversettings']['letsencryptreuseold']['description'] = "Wenn dies aktiviert ist, wird der alte Schlüssel bei jeder Verlängerung verwendet, andernfalls wird ein neues Paar generiert.
ACHTUNG: Let's Encrypt befindet sich noch im Test"; $lng['serversettings']['leenabled']['title'] = "Let's Encrypt verwenden"; $lng['serversettings']['leenabled']['description'] = "Wenn dies aktiviert ist, können Kunden durch Froxlor automatisch generierte und verlängerbare Let's Encrypt SSL-Zertifikate für Domains mit SSL IP/port nutzen.

Bitte die Webserver-Konfiguration beachten wenn aktiviert, da dieses Feature eine spezielle Konfiguration benötigt."; $lng['domains']['ssl_redirect_temporarilydisabled'] = "
Die SSL-Umleitung ist, während ein neues Let's Encrypt - Zertifikat erstellt wird, temporär deaktiviert. Die Umleitung wird nach der Zertifikatserstellung wieder aktiviert."; diff --git a/scripts/jobs/cron_letsencrypt.php b/scripts/jobs/cron_letsencrypt.php index 0ee4722e..875f8ff5 100644 --- a/scripts/jobs/cron_letsencrypt.php +++ b/scripts/jobs/cron_letsencrypt.php @@ -92,26 +92,13 @@ foreach ($certrows as $certrow) { if ($certrow['ssl_redirect'] != 2) { $cronlog->logAction(CRON_ACTION, LOG_DEBUG, "Updating " . $certrow['domain']); - if ($certrow['ssl_cert_file']) { - $cronlog->logAction(CRON_ACTION, LOG_DEBUG, "letsencrypt using old key / SAN for " . $certrow['domain']); - // Parse the old certificate - $x509data = openssl_x509_parse($certrow['ssl_cert_file']); - - // We are interessted in the old SAN - data - $san = explode(', ', $x509data['extensions']['subjectAltName']); - $domains = array(); - foreach ($san as $dnsname) { - $domains[] = substr($dnsname, 4); - } - } else { - $cronlog->logAction(CRON_ACTION, LOG_DEBUG, "letsencrypt generating new key / SAN for " . $certrow['domain']); - $domains = array( - $certrow['domain'] - ); - // Add www. for SAN - if ($certrow['wwwserveralias'] == 1) { - $domains[] = 'www.' . $certrow['domain']; - } + $cronlog->logAction(CRON_ACTION, LOG_DEBUG, "letsencrypt generating SAN list for " . $certrow['domain']); + $domains = array( + $certrow['domain'] + ); + // Add www. for SAN + if ($certrow['wwwserveralias'] == 1) { + $domains[] = 'www.' . $certrow['domain']; } try { From 6e2b1773a39705e494cbfb5b97673d50c87a6e29 Mon Sep 17 00:00:00 2001 From: Daniel Reichelt Date: Mon, 16 May 2016 16:32:07 +0200 Subject: [PATCH 0065/1335] LE: support alias domains LE CSRs are triggered for the aliasdomain target domain on * domain deletion * domain creation * domain editing when * the aliasdomain target changes (CSR triggered both for old and new target) * wwwalias is disabled or enabled * letsencrypt is disabled or enabled (domain-local) fixes #1597 --- admin_domains.php | 22 +++++++----- customer_domains.php | 26 ++++++++------ ...etsEncryptCSRForAliasDestinationDomain.php | 34 ++++++++++++++++++ lng/english.lng.php | 1 - lng/german.lng.php | 1 - scripts/jobs/cron_letsencrypt.php | 35 +++++++++++++++++-- 6 files changed, 95 insertions(+), 24 deletions(-) create mode 100644 lib/functions/froxlor/function.triggerLetsEncryptCSRForAliasDestinationDomain.php diff --git a/admin_domains.php b/admin_domains.php index 479e1bc5..5fbabda4 100644 --- a/admin_domains.php +++ b/admin_domains.php @@ -245,6 +245,8 @@ if ($page == 'domains' || $page == 'overview') { 'domainid' => $id )); + triggerLetsEncryptCSRForAliasDestinationDomain($result['aliasdomain'], $log); + $log->logAction(ADM_ACTION, LOG_INFO, "deleted domain/subdomains (#" . $result['id'] . ")"); updateCounters(); inserttask('1'); @@ -672,10 +674,6 @@ if ($page == 'domains' || $page == 'overview') { $issubof = '0'; } - if ($aliasdomain != 0 && $letsencrypt != 0) { - standard_error('letsencryptdoesnotworkwithaliasdomains'); - } - if ($domain == '') { standard_error(array( 'stringisempty', @@ -843,6 +841,9 @@ elseif (Settings::Get('system.validate_domain') && ! validateDomain($domain)) { Database::pexecute($ins_stmt, $ins_data); } } + + triggerLetsEncryptCSRForAliasDestinationDomain($aliasdomain, $log); + $log->logAction(ADM_ACTION, LOG_INFO, "added domain '" . $domain . "'"); inserttask('1'); @@ -1472,10 +1473,6 @@ elseif (Settings::Get('system.validate_domain') && ! validateDomain($domain)) { $issubof = '0'; } - if ($aliasdomain != 0 && $letsencrypt != 0) { - standard_error('letsencryptdoesnotworkwithaliasdomains'); - } - if ($serveraliasoption != '1' && $serveraliasoption != '2') { $serveraliasoption = '0'; } @@ -1802,6 +1799,15 @@ elseif (Settings::Get('system.validate_domain') && ! validateDomain($domain)) { } } } + if ($result['aliasdomain'] != $aliasdomain) { + // trigger when domain id for alias destination has changed: both for old and new destination + triggerLetsEncryptCSRForAliasDestinationDomain($result['aliasdomain'], $log); + triggerLetsEncryptCSRForAliasDestinationDomain($aliasdomain, $log); + } else + if ($result['wwwserveralias'] != $wwwserveralias || $result['letsencrypt'] != $letsencrypt) { + // or when wwwserveralias or letsencrypt was changed + triggerLetsEncryptCSRForAliasDestinationDomain($aliasdomain, $log); + } $log->logAction(ADM_ACTION, LOG_INFO, "edited domain #" . $id); redirectTo($filename, array( diff --git a/customer_domains.php b/customer_domains.php index de18a20c..d7bdbc9b 100644 --- a/customer_domains.php +++ b/customer_domains.php @@ -171,7 +171,7 @@ if ($page == 'overview') { eval("echo \"" . getTemplate("domains/domainlist") . "\";"); } elseif ($action == 'delete' && $id != 0) { - $stmt = Database::prepare("SELECT `id`, `customerid`, `domain`, `documentroot`, `isemaildomain`, `parentdomainid` FROM `" . TABLE_PANEL_DOMAINS . "` + $stmt = Database::prepare("SELECT `id`, `customerid`, `domain`, `documentroot`, `isemaildomain`, `parentdomainid`, `aliasdomain` FROM `" . TABLE_PANEL_DOMAINS . "` WHERE `customerid` = :customerid AND `id` = :id" ); @@ -197,6 +197,8 @@ if ($page == 'overview') { } } + triggerLetsEncryptCSRForAliasDestinationDomain($result['aliasdomain'], $log); + $log->logAction(USR_ACTION, LOG_INFO, "deleted subdomain '" . $idna_convert->decode($result['domain']) . "'"); $stmt = Database::prepare("DELETE FROM `" . TABLE_PANEL_DOMAINS . "` WHERE `customerid` = :customerid @@ -290,6 +292,7 @@ if ($page == 'overview') { ORDER BY `d`.`domain` ASC;" ); $aliasdomain_check = Database::pexecute_first($aliasdomain_stmt, array("id" => $aliasdomain, "customerid" => $userinfo['customerid'])); + triggerLetsEncryptCSRForAliasDestinationDomain($aliasdomain, $log); } if (isset($_POST['url']) && $_POST['url'] != '' && validateUrl($idna_convert->encode($_POST['url']))) { @@ -342,11 +345,6 @@ if ($page == 'overview') { } } - if ($aliasdomain != 0 && $letsencrypt != 0) - { - standard_error('letsencryptdoesnotworkwithaliasdomains'); - } - // Temporarily deactivate ssl_redirect until Let's Encrypt certificate was generated if ($ssl_redirect > 0 && $letsencrypt == 1) { $ssl_redirect = 2; @@ -610,11 +608,6 @@ if ($page == 'overview') { $letsencrypt = '0'; } - if ($aliasdomain != 0 && $letsencrypt != 0) - { - standard_error('letsencryptdoesnotworkwithaliasdomains'); - } - // We can't enable let's encrypt for wildcard - domains if ($iswildcarddomain == '1' && $letsencrypt == '1') { standard_error('nowildcardwithletsencrypt'); @@ -677,6 +670,17 @@ if ($page == 'overview') { "id" => $id ); Database::pexecute($stmt, $params); + + if ($result['aliasdomain'] != $aliasdomain) { + // trigger when domain id for alias destination has changed: both for old and new destination + triggerLetsEncryptCSRForAliasDestinationDomain($result['aliasdomain'], $log); + triggerLetsEncryptCSRForAliasDestinationDomain($aliasdomain, $log); + } else + if ($result['wwwserveralias'] != $wwwserveralias || $result['letsencrypt'] != $letsencrypt) { + // or when wwwserveralias or letsencrypt was changed + triggerLetsEncryptCSRForAliasDestinationDomain($aliasdomain, $log); + } + inserttask('1'); // Using nameserver, insert a task which rebuilds the server config diff --git a/lib/functions/froxlor/function.triggerLetsEncryptCSRForAliasDestinationDomain.php b/lib/functions/froxlor/function.triggerLetsEncryptCSRForAliasDestinationDomain.php new file mode 100644 index 00000000..cda8ac98 --- /dev/null +++ b/lib/functions/froxlor/function.triggerLetsEncryptCSRForAliasDestinationDomain.php @@ -0,0 +1,34 @@ + (2016-) + * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt + * @package Functions + * + */ + +function triggerLetsEncryptCSRForAliasDestinationDomain($aliasDestinationDomainID, $log) +{ + if (isset($aliasDestinationDomainID) && $aliasDestinationDomainID > 0) { + $log->logAction(ADM_ACTION, LOG_INFO, "LetsEncrypt CSR triggered for domain ID " . $aliasDestinationDomainID); + $upd_stmt = Database::prepare( + "UPDATE + `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` + SET + `expirationdate` = null + WHERE + domainid = :domainid + "); + Database::pexecute($upd_stmt, array( + 'domainid' => $aliasDestinationDomainID + )); + } +} diff --git a/lng/english.lng.php b/lng/english.lng.php index 61849a5b..2db8a344 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -1936,7 +1936,6 @@ $lng['customer']['letsencrypt']['title'] = 'Use Let\'s Encrypt'; $lng['customer']['letsencrypt']['description'] = 'Get a free certificate from Let\'s Encrypt. The certificate will be created and renewed automatically.
ATTENTION: This feature is still in beta.'; $lng['error']['sslredirectonlypossiblewithsslipport'] = 'Using Let\'s Encrypt is only possible when the domain has at least one ssl-enabled IP/port combination assigned.'; $lng['error']['nowildcardwithletsencrypt'] = 'Let\'s Encrypt cannot (yet) handle wildcard-domains. Please set the ServerAlias to WWW or disable it completely'; -$lng['error']['letsencryptdoesnotworkwithaliasdomains'] = "Usage of Let's Encrypt is not possible for aliasdomains at the moment. Please disable Let's Encrypt or AliasDomain"; $lng['panel']['letsencrypt'] = 'Using Let\'s encrypt'; $lng['crondesc']['cron_letsencrypt'] = 'updating Let\'s Encrypt certificates'; $lng['serversettings']['letsencryptca']['title'] = "Let's Encrypt environment"; diff --git a/lng/german.lng.php b/lng/german.lng.php index dd908b2f..ec8bc35f 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -1590,7 +1590,6 @@ $lng['customer']['letsencrypt']['title'] = 'Benutze Let\'s Encrypt'; $lng['customer']['letsencrypt']['description'] = 'Holt ein kostenloses Zertifikat von Let\'s Encrypt. Das Zertifikat wird automatisch erstellt und verlängert.
ACHTUNG: Dieses Feature befindet sich noch im Test.'; $lng['error']['sslredirectonlypossiblewithsslipport'] = 'Die Nutzung von Let\'s Encrypt ist nur möglich, wenn die Domain mindestens eine IP/Port - Kombination mit aktiviertem SSL zugewiesen hat.'; $lng['error']['nowildcardwithletsencrypt'] = 'Let\'s Encrypt kann (noch) nicht mit Wildcard-Domains umgehen. Bitte den ServerAlias auf WWW setzen oder deaktivieren'; -$lng['error']['letsencryptdoesnotworkwithaliasdomains'] = "Die Nutzung von Let's Encrypt ist mit AliasDomains derzeit nicht möglich. Bitte Let's Encrypt oder AliasDomain deaktivieren"; $lng['panel']['letsencrypt'] = 'Benutzt Let\'s encrypt'; $lng['crondesc']['cron_letsencrypt'] = 'aktualisiert Let\'s Encrypt Zertifikate'; $lng['serversettings']['letsencryptca']['title'] = "Let's Encrypt Umgebung"; diff --git a/scripts/jobs/cron_letsencrypt.php b/scripts/jobs/cron_letsencrypt.php index 875f8ff5..8fe3d5af 100644 --- a/scripts/jobs/cron_letsencrypt.php +++ b/scripts/jobs/cron_letsencrypt.php @@ -38,7 +38,6 @@ $certificates_stmt = Database::query( domssl.`ssl_ca_file`, domssl.`ssl_csr_file`, dom.`domain`, - dom.`iswildcarddomain`, dom.`wwwserveralias`, dom.`documentroot`, dom.`id` AS 'domainid', @@ -56,12 +55,27 @@ $certificates_stmt = Database::query( WHERE dom.`customerid` = cust.`customerid` AND dom.`letsencrypt` = 1 + AND dom.`aliasdomain` IS NULL + AND dom.`iswildcarddomain` = 0 AND ( domssl.`expirationdate` < DATE_ADD(NOW(), INTERVAL 30 DAY) OR domssl.`expirationdate` IS NULL ) "); +$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 + "); + $updcert_stmt = Database::prepare( " REPLACE INTO @@ -92,15 +106,30 @@ foreach ($certrows as $certrow) { if ($certrow['ssl_redirect'] != 2) { $cronlog->logAction(CRON_ACTION, LOG_DEBUG, "Updating " . $certrow['domain']); - $cronlog->logAction(CRON_ACTION, LOG_DEBUG, "letsencrypt generating SAN list for " . $certrow['domain']); + $cronlog->logAction(CRON_ACTION, LOG_DEBUG, "Adding SAN entry: " . $certrow['domain']); $domains = array( $certrow['domain'] ); - // Add www. for SAN + // add www. to SAN list if ($certrow['wwwserveralias'] == 1) { + $cronlog->logAction(CRON_ACTION, LOG_DEBUG, "Adding SAN entry: www." . $certrow['domain']); $domains[] = 'www.' . $certrow['domain']; } + // add alias domains (and possibly www.) to SAN list + Database::pexecute($aliasdomains_stmt, array( + 'id' => $certrow['domainid'] + )); + $aliasdomains = $aliasdomains_stmt->fetchAll(PDO::FETCH_ASSOC); + foreach ($aliasdomains as $aliasdomain) { + $cronlog->logAction(CRON_ACTION, LOG_DEBUG, "Adding SAN entry: " . $aliasdomain['domain']); + $domains[] = $aliasdomain['domain']; + if ($aliasdomain['wwwserveralias'] == 1) { + $cronlog->logAction(CRON_ACTION, LOG_DEBUG, "Adding SAN entry: www." . $aliasdomain['domain']); + $domains[] = 'www.' . $aliasdomain['domain']; + } + } + try { // Initialize Lescript with documentroot $le = new lescript($cronlog); From b03eab897ab95cb5ce7ffe7df9ca3579f5a02612 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 16 May 2016 18:41:01 +0200 Subject: [PATCH 0066/1335] show a2enmod commands only when using apache, thx to d4n13L Signed-off-by: Michael Kaufmann (d00p) --- lib/configfiles/jessie.xml | 4 ++-- lib/configfiles/precise.xml | 4 ++-- lib/configfiles/trusty.xml | 6 ++++-- lib/configfiles/wheezy.xml | 6 ++++-- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/lib/configfiles/jessie.xml b/lib/configfiles/jessie.xml index 1bbb713a..27050f9b 100644 --- a/lib/configfiles/jessie.xml +++ b/lib/configfiles/jessie.xml @@ -40,14 +40,14 @@ - - //service[@type='http']/general/commands + + {{settings.phpfpm.enabled}} diff --git a/lib/configfiles/precise.xml b/lib/configfiles/precise.xml index 54b8fd38..885c3522 100644 --- a/lib/configfiles/precise.xml +++ b/lib/configfiles/precise.xml @@ -40,8 +40,6 @@ - - @@ -49,6 +47,8 @@ default="true"> //service[@type='http']/general/commands + + {{settings.phpfpm.enabled}} diff --git a/lib/configfiles/trusty.xml b/lib/configfiles/trusty.xml index fd961f62..770c93b0 100644 --- a/lib/configfiles/trusty.xml +++ b/lib/configfiles/trusty.xml @@ -40,8 +40,6 @@ - - @@ -49,6 +47,8 @@ default="true"> //service[@type='http']/general/commands + + {{settings.phpfpm.enabled}} @@ -83,6 +83,8 @@ Alias "/.well-known/acme-challenge" "{{settings.system.letsencryptchallengepath} //service[@type='http']/general/commands + + {{settings.phpfpm.enabled}} diff --git a/lib/configfiles/wheezy.xml b/lib/configfiles/wheezy.xml index 782c41fb..c3824096 100644 --- a/lib/configfiles/wheezy.xml +++ b/lib/configfiles/wheezy.xml @@ -40,8 +40,6 @@ - - @@ -49,6 +47,8 @@ default="true"> //service[@type='http']/general/commands + + {{settings.phpfpm.enabled}} @@ -83,6 +83,8 @@ Alias "/.well-known/acme-challenge" "{{settings.system.letsencryptchallengepath} //service[@type='http']/general/commands + + {{settings.phpfpm.enabled}} From 69443d95d5300373cb6fc49a437883b382a7cd38 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 17 May 2016 08:30:02 +0200 Subject: [PATCH 0067/1335] add support for power-dns (untested); most config-templates missing for pdns; create SPF/DKIM entries only if domain is emaildomain Signed-off-by: Michael Kaufmann (d00p) --- actions/admin/settings/160.nameserver.php | 10 + install/froxlor.sql | 3 +- .../updates/froxlor/0.9/update_0.9.inc.php | 14 +- .../preconfig/0.9/preconfig_0.9.inc.php | 515 +++++++------- lib/configfiles/gentoo.xml | 542 ++++++++++++++- .../dns/function.createDomainZone.php | 25 +- lib/version.inc.php | 2 +- lng/english.lng.php | 10 +- lng/german.lng.php | 10 +- scripts/jobs/cron_tasks.inc.dns.10.bind.php | 648 +++--------------- scripts/jobs/cron_tasks.inc.dns.15.bind.php | 134 ---- scripts/jobs/cron_tasks.inc.dns.20.pdns.php | 204 ++++++ scripts/jobs/cron_tasks.php | 8 +- 13 files changed, 1113 insertions(+), 1012 deletions(-) delete mode 100644 scripts/jobs/cron_tasks.inc.dns.15.bind.php create mode 100644 scripts/jobs/cron_tasks.inc.dns.20.pdns.php diff --git a/actions/admin/settings/160.nameserver.php b/actions/admin/settings/160.nameserver.php index 030ceaeb..11d2b6d3 100644 --- a/actions/admin/settings/160.nameserver.php +++ b/actions/admin/settings/160.nameserver.php @@ -39,6 +39,16 @@ return array( 'default' => false, 'save_method' => 'storeSettingField' ), + 'system_dns_server' => array( + 'label' => $lng['serversettings']['dns_server'], + 'settinggroup' => 'system', + 'varname' => 'dns_server', + 'type' => 'option', + 'default' => 'bind', + 'option_mode' => 'one', + 'option_options' => array('bind' => 'Bind9', 'pdns' => 'PowerDNS'), + 'save_method' => 'storeSettingField' + ), 'system_bindconf_directory' => array( 'label' => $lng['serversettings']['bindconf_directory'], 'settinggroup' => 'system', diff --git a/install/froxlor.sql b/install/froxlor.sql index b6462902..dc692f2f 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -527,6 +527,7 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES ('system', 'leenabled', '0'), ('system', 'backupenabled', '0'), ('system', 'dnsenabled', '0'), + ('system', 'dns_server', 'bind'), ('panel', 'decimal_places', '4'), ('panel', 'adminmail', 'admin@SERVERNAME'), ('panel', 'phpmyadmin_url', ''), @@ -558,7 +559,7 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES ('panel', 'password_special_char_required', '0'), ('panel', 'password_special_char', '!?<>§$%+#=@'), ('panel', 'version', '0.9.35.1'), - ('panel', 'db_version', '201605120'); + ('panel', 'db_version', '201605170'); DROP TABLE IF EXISTS `panel_tasks`; diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index ef9dbe27..d0e7b94c 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -3313,7 +3313,7 @@ if (isFroxlorVersion('0.9.35.1') && isDatabaseVersion('201603150')) { updateToDbVersion('201604270'); } -if (isFroxlorVersion('0.9.35.1') && isDatabaseVersion('201604270')) { +if (isDatabaseVersion('201604270')) { showUpdateStep("Adding new dns related tables and settings"); $enable_dns = isset($_POST['enable_dns']) ? (int) $_POST['enable_dns'] : "0"; @@ -3336,7 +3336,7 @@ if (isFroxlorVersion('0.9.35.1') && isDatabaseVersion('201604270')) { updateToDbVersion('201605090'); } -if (isFroxlorVersion('0.9.35.1') && isDatabaseVersion('201605090')) { +if (isDatabaseVersion('201605090')) { showUpdateStep("Adjusting SPF record setting"); $current_spf = Settings::Get('spf.spf_entry'); @@ -3347,3 +3347,13 @@ if (isFroxlorVersion('0.9.35.1') && isDatabaseVersion('201605090')) { updateToDbVersion('201605120'); } + +if (isDatabaseVersion('201605120')) { + + showUpdateStep("Adding new dns-server setting"); + $new_dns_daemon = isset($_POST['new_dns_daemon']) ? $_POST['new_dns_daemon'] : "bind"; + Settings::AddNew("system.dns_server", $new_dns_daemon); + lastStepStatus(0); + + updateToDbVersion('201605170'); +} diff --git a/install/updates/preconfig/0.9/preconfig_0.9.inc.php b/install/updates/preconfig/0.9/preconfig_0.9.inc.php index 5ebd6bf5..403d55d3 100644 --- a/install/updates/preconfig/0.9/preconfig_0.9.inc.php +++ b/install/updates/preconfig/0.9/preconfig_0.9.inc.php @@ -18,47 +18,45 @@ /** * 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 + * @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 parseAndOutputPreconfig(&$has_preconfig, &$return, $current_version, $current_db_version) { - +function parseAndOutputPreconfig(&$has_preconfig, &$return, $current_version, $current_db_version) +{ global $lng; - if(versionInUpdate($current_version, '0.9.4-svn2')) - { + if (versionInUpdate($current_version, '0.9.4-svn2')) { $has_preconfig = true; $description = 'Froxlor now enables the usage of a domain-wildcard entry and subdomains for this domain at the same time (subdomains are parsed before the main-domain vhost container).'; - $description.= 'This makes it possible to catch all non-existing subdomains with the main vhost but also have the ability to use subdomains for that domain.
'; - $description.= 'If you would like Froxlor to do so with your domains, the update script can set the correct values for existing domains for you. Note: future domains will have wildcard-entries enabled by default no matter how you decide here.'; + $description .= 'This makes it possible to catch all non-existing subdomains with the main vhost but also have the ability to use subdomains for that domain.
'; + $description .= 'If you would like Froxlor to do so with your domains, the update script can set the correct values for existing domains for you. Note: future domains will have wildcard-entries enabled by default no matter how you decide here.'; $question = 'Do you want to use wildcard-entries for existing domains?: '; - $question.= makeyesno('update_domainwildcardentry', '1', '0', '1'); + $question .= makeyesno('update_domainwildcardentry', '1', '0', '1'); eval("\$return.=\"" . getTemplate("update/preconfigitem") . "\";"); } - if(versionInUpdate($current_version, '0.9.6-svn2')) - { - if(!PHPMailer::ValidateAddress(Settings::Get('panel.adminmail'))) - { + if (versionInUpdate($current_version, '0.9.6-svn2')) { + if (! PHPMailer::ValidateAddress(Settings::Get('panel.adminmail'))) { $has_preconfig = true; $description = 'Froxlor uses a newer version of the phpMailerClass and determined that your current admin-mail address is invalid.'; - $question = 'Please specify a new admin-email address: '; + $question = 'Please specify a new admin-email address: '; eval("\$return.=\"" . getTemplate("update/preconfigitem") . "\";"); } } - if(versionInUpdate($current_version, '0.9.6-svn3')) - { + if (versionInUpdate($current_version, '0.9.6-svn3')) { $has_preconfig = true; $description = 'You now have the possibility to define default error-documents for your webserver which replace the default webserver error-messages.'; $question = 'Do you want to enable default error-documents?: '; - $question .= makeyesno('update_deferr_enable', '1', '0', '0').'

'; - if(Settings::Get('system.webserver') == 'apache2') - { + $question .= makeyesno('update_deferr_enable', '1', '0', '0') . '

'; + if (Settings::Get('system.webserver') == 'apache2') { $question .= 'Path/URL for error 500: 

'; $question .= 'Path/URL for error 401: 

'; $question .= 'Path/URL for error 403: 

'; @@ -67,37 +65,33 @@ function parseAndOutputPreconfig(&$has_preconfig, &$return, $current_version, $c eval("\$return.=\"" . getTemplate("update/preconfigitem") . "\";"); } - if(versionInUpdate($current_version, '0.9.6-svn4')) - { + if (versionInUpdate($current_version, '0.9.6-svn4')) { $has_preconfig = true; $description = 'You can define a default support-ticket priority level which is pre-selected for new support-tickets.'; $question = 'Which should be the default ticket-priority?: '; $question .= ''; + $priorities .= makeoption($lng['ticket']['normal'], '2', '2'); + $priorities .= makeoption($lng['ticket']['low'], '3', '2'); + $question .= $priorities . ''; eval("\$return.=\"" . getTemplate("update/preconfigitem") . "\";"); } - if(versionInUpdate($current_version, '0.9.6-svn5')) - { + if (versionInUpdate($current_version, '0.9.6-svn5')) { $has_preconfig = true; $description = 'If you have more than one PHP configurations defined in Froxlor you can now set a default one which will be used for every domain.'; $question = 'Select default PHP configuration: '; $question .= ''; + $question .= $configs . ''; eval("\$return.=\"" . getTemplate("update/preconfigitem") . "\";"); } - if(versionInUpdate($current_version, '0.9.6-svn6')) - { + if (versionInUpdate($current_version, '0.9.6-svn6')) { $has_preconfig = true; $description = 'For the new FTP-quota feature, you can now chose the currently used ftpd-software.'; $question = 'Used FTPd-software: '; @@ -108,72 +102,63 @@ function parseAndOutputPreconfig(&$has_preconfig, &$return, $current_version, $c eval("\$return.=\"" . getTemplate("update/preconfigitem") . "\";"); } - if(versionInUpdate($current_version, '0.9.7-svn1')) - { + if (versionInUpdate($current_version, '0.9.7-svn1')) { $has_preconfig = true; $description = 'You can now choose whether customers can select the http-redirect code and which of them acts as default.'; $question = 'Allow customer chosen redirects?: '; - $question.= makeyesno('update_customredirect_enable', '1', '0', '1').'

'; - $question.= 'Select default redirect code (default: empty): '; - $question.= ''; + $question .= makeyesno('update_customredirect_enable', '1', '0', '1') . '

'; + $question .= 'Select default redirect code (default: empty): '; + $question .= ''; eval("\$return.=\"" . getTemplate("update/preconfigitem") . "\";"); } - if(versionInUpdate($current_version, '0.9.7-svn2')) - { + if (versionInUpdate($current_version, '0.9.7-svn2')) { $result = Database::query("SELECT `domain` FROM " . TABLE_PANEL_DOMAINS . " WHERE `documentroot` LIKE '%:%' AND `documentroot` NOT LIKE 'http://%' AND `openbasedir_path` = '0' AND `openbasedir` = '1'"); $wrongOpenBasedirDomain = array(); - while($row = $result->fetch(PDO::FETCH_ASSOC)) { + while ($row = $result->fetch(PDO::FETCH_ASSOC)) { $wrongOpenBasedirDomain[] = $row['domain']; } - if(count($wrongOpenBasedirDomain) > 0) - { + if (count($wrongOpenBasedirDomain) > 0) { $has_preconfig = true; $description = 'Resetting the open_basedir to customer - root'; $question = 'Due to a security - issue regarding open_basedir, Froxlor will set the open_basedir for the following domains to the customers root instead of the chosen documentroot:
 '; - $question.= '
    '; + $question .= '
      '; $idna_convert = new idna_convert_wrapper(); - foreach($wrongOpenBasedirDomain as $domain) - { - $question.= '
    • ' . $idna_convert->decode($domain) . '
    • '; + foreach ($wrongOpenBasedirDomain as $domain) { + $question .= '
    • ' . $idna_convert->decode($domain) . '
    • '; } - $question.= '
    '; + $question .= '
'; eval("\$return.=\"" . getTemplate("update/preconfigitem") . "\";"); } } - if(versionInUpdate($current_version, '0.9.9-svn1')) - { + if (versionInUpdate($current_version, '0.9.9-svn1')) { $has_preconfig = true; $description = 'When entering MX servers to Froxlor there was no mail-, imap-, pop3- and smtp-"A record" created. You can now chose whether this should be done or not.'; $question = 'Do you want these A-records to be created even with MX servers given?: '; - $question.= makeyesno('update_defdns_mailentry', '1', '0', '0'); + $question .= makeyesno('update_defdns_mailentry', '1', '0', '0'); eval("\$return.=\"" . getTemplate("update/preconfigitem") . "\";"); } - if(versionInUpdate($current_version, '0.9.10-svn1')) - { + if (versionInUpdate($current_version, '0.9.10-svn1')) { $has_nouser = false; $has_nogroup = false; $result_stmt = Database::query("SELECT * FROM `" . TABLE_PANEL_SETTINGS . "` WHERE `settinggroup` = 'system' AND `varname` = 'httpuser'"); $result = $result_stmt->fetch(PDO::FETCH_ASSOC); - if(!isset($result) || !isset($result['value'])) - { + if (! isset($result) || ! isset($result['value'])) { $has_preconfig = true; $has_nouser = true; $guessed_user = 'www-data'; - if(function_exists('posix_getuid') - && function_exists('posix_getpwuid') - ) { + if (function_exists('posix_getuid') && function_exists('posix_getpwuid')) { $_httpuser = posix_getpwuid(posix_getuid()); $guessed_user = $_httpuser['name']; } @@ -182,31 +167,24 @@ function parseAndOutputPreconfig(&$has_preconfig, &$return, $current_version, $c $result_stmt = Database::query("SELECT * FROM `" . TABLE_PANEL_SETTINGS . "` WHERE `settinggroup` = 'system' AND `varname` = 'httpgroup'"); $result = $result_stmt->fetch(PDO::FETCH_ASSOC); - if(!isset($result) || !isset($result['value'])) - { + if (! isset($result) || ! isset($result['value'])) { $has_preconfig = true; $has_nogroup = true; $guessed_group = 'www-data'; - if(function_exists('posix_getgid') - && function_exists('posix_getgrgid') - ) { + if (function_exists('posix_getgid') && function_exists('posix_getgrgid')) { $_httpgroup = posix_getgrgid(posix_getgid()); $guessed_group = $_httpgroup['name']; } } - if($has_nouser || $has_nogroup) - { + if ($has_nouser || $has_nogroup) { $description = 'Please enter the correct username/groupname of the webserver on your system We\'re guessing the user but it might not be correct, so please check.'; - if($has_nouser) - { - $question = 'Please enter the webservers username: '; - } - elseif($has_nogroup) - { - $question2 = 'Please enter the webservers groupname: '; - if($has_nouser) { - $question .= '

'.$question2; + if ($has_nouser) { + $question = 'Please enter the webservers username: '; + } elseif ($has_nogroup) { + $question2 = 'Please enter the webservers groupname: '; + if ($has_nouser) { + $question .= '

' . $question2; } else { $question = $question2; } @@ -215,227 +193,202 @@ function parseAndOutputPreconfig(&$has_preconfig, &$return, $current_version, $c } } - if(versionInUpdate($current_version, '0.9.10')) - { + if (versionInUpdate($current_version, '0.9.10')) { $has_preconfig = true; $description = 'you can now decide whether Froxlor should be reached via hostname/froxlor or directly via the hostname.'; $question = 'Do you want Froxlor to be reached directly via the hostname?: '; - $question.= makeyesno('update_directlyviahostname', '1', '0', '0'); + $question .= makeyesno('update_directlyviahostname', '1', '0', '0'); eval("\$return.=\"" . getTemplate("update/preconfigitem") . "\";"); } - if(versionInUpdate($current_version, '0.9.11-svn1')) - { + if (versionInUpdate($current_version, '0.9.11-svn1')) { $has_preconfig = true; $description = 'It is possible to enhance security with setting a regular expression to force your customers to enter more complex passwords.'; $question = 'Enter a regular expression to force a higher password complexity (leave empty for none): '; - $question.= ''; + $question .= ''; eval("\$return.=\"" . getTemplate("update/preconfigitem") . "\";"); } - if(versionInUpdate($current_version, '0.9.11-svn3')) - { + if (versionInUpdate($current_version, '0.9.11-svn3')) { $has_preconfig = true; $description = 'As Froxlor can now handle perl, you have to specify where the perl executable is (only if you\'re running lighttpd, else just leave empty).'; $question = 'Path to perl (default \'/usr/bin/perl\'): '; - $question.= ''; + $question .= ''; eval("\$return.=\"" . getTemplate("update/preconfigitem") . "\";"); } - if(versionInUpdate($current_version, '0.9.12-svn1')) - { - if(Settings::Get('system.mod_fcgid') == 1) - { + if (versionInUpdate($current_version, '0.9.12-svn1')) { + if (Settings::Get('system.mod_fcgid') == 1) { $has_preconfig = true; $description = 'You can chose whether you want Froxlor to use FCGID itself too now.'; $question = 'Use FCGID for the Froxlor Panel?: '; - $question.= makeyesno('update_fcgid_ownvhost', '1', '0', '0').'

'; - $question.= 'If \'yes\', please specify local user/group (have to exist, Froxlor does not add them automatically):

'; - $question.= 'Local user: '; - $question.= '

'; - $question.= 'Local group: '; - $question.= '
'; + $question .= makeyesno('update_fcgid_ownvhost', '1', '0', '0') . '

'; + $question .= 'If \'yes\', please specify local user/group (have to exist, Froxlor does not add them automatically):

'; + $question .= 'Local user: '; + $question .= '

'; + $question .= 'Local group: '; + $question .= '
'; eval("\$return.=\"" . getTemplate("update/preconfigitem") . "\";"); } } - if(versionInUpdate($current_version, '0.9.12-svn2')) - { + if (versionInUpdate($current_version, '0.9.12-svn2')) { $has_preconfig = true; $description = 'Many apache user will have problems using perl/CGI as the customer docroots are not within the suexec path. Froxlor provides a simple workaround for that.'; $question = 'Enable Apache/SuExec/Perl workaround?: '; - $question.= makeyesno('update_perl_suexecworkaround', '1', '0', '0').'

'; - $question.= 'If \'yes\', please specify a path within the suexec path where Froxlor will create symlinks to customer perl-enabled paths:

'; - $question.= 'Path for symlinks (must be within suexec path): '; - $question.= '
'; + $question .= makeyesno('update_perl_suexecworkaround', '1', '0', '0') . '

'; + $question .= 'If \'yes\', please specify a path within the suexec path where Froxlor will create symlinks to customer perl-enabled paths:

'; + $question .= 'Path for symlinks (must be within suexec path): '; + $question .= '
'; eval("\$return.=\"" . getTemplate("update/preconfigitem") . "\";"); } - if(versionInUpdate($current_version, '0.9.12-svn4')) - { - if((int)Settings::Get('system.awstats_enabled') == 1) - { + if (versionInUpdate($current_version, '0.9.12-svn4')) { + if ((int) Settings::Get('system.awstats_enabled') == 1) { $has_preconfig = true; $description = 'Due to different paths of awstats_buildstaticpages.pl and awstats.pl you can set a different path for awstats.pl now.'; $question = 'Path to \'awstats.pl\'?: '; - $question.= '
'; + $question .= '
'; eval("\$return.=\"" . getTemplate("update/preconfigitem") . "\";"); } } - if(versionInUpdate($current_version, '0.9.13-svn1')) - { - if((int)Settings::Get('autoresponder.autoresponder_active') == 1) - { + if (versionInUpdate($current_version, '0.9.13-svn1')) { + if ((int) Settings::Get('autoresponder.autoresponder_active') == 1) { $has_preconfig = true; $description = 'Froxlor can now limit the number of autoresponder-entries for each user. Here you can set the value which will be available for each customer (Of course you can change the value for each customer separately after the update).'; $question = 'How many autoresponders should your customers be able to add?: '; - $question.= ' '.makecheckbox('update_autoresponder_default', $lng['customer']['unlimited'], '-1', false, 0, true, true).'
'; + $question .= ' ' . makecheckbox('update_autoresponder_default', $lng['customer']['unlimited'], '-1', false, 0, true, true) . '
'; eval("\$return.=\"" . getTemplate("update/preconfigitem") . "\";"); } } - if(versionInUpdate($current_version, '0.9.13.1')) - { - if((int)Settings::Get('system.mod_fcgid_ownvhost') == 1) - { + if (versionInUpdate($current_version, '0.9.13.1')) { + if ((int) Settings::Get('system.mod_fcgid_ownvhost') == 1) { $has_preconfig = true; $description = 'You have FCGID for Froxlor itself activated. You can now specify a PHP-configuration for this.'; $question = 'Select Froxlor-vhost PHP configuration: '; $question .= ''; + $question .= $configs . ''; eval("\$return.=\"" . getTemplate("update/preconfigitem") . "\";"); } } - if(versionInUpdate($current_version, '0.9.14-svn3')) - { - if((int)Settings::Get('system.awstats_enabled') == 1) - { + if (versionInUpdate($current_version, '0.9.14-svn3')) { + if ((int) Settings::Get('system.awstats_enabled') == 1) { $has_preconfig = true; $description = 'To have icons in AWStats statistic-pages please enter the path to AWStats icons folder.'; $question = 'Path to AWSTats icons folder: '; - $question.= ''; + $question .= ''; eval("\$return.=\"" . getTemplate("update/preconfigitem") . "\";"); } } - if(versionInUpdate($current_version, '0.9.14-svn4')) - { - if((int)Settings::Get('system.use_ssl') == 1) - { + if (versionInUpdate($current_version, '0.9.14-svn4')) { + if ((int) Settings::Get('system.use_ssl') == 1) { $has_preconfig = true; $description = 'Froxlor now has the possibility to set \'SSLCertificateChainFile\' for the apache webserver.'; $question = 'Enter filename (leave empty for none): '; - $question.= ''; + $question .= ''; eval("\$return.=\"" . getTemplate("update/preconfigitem") . "\";"); } } - if(versionInUpdate($current_version, '0.9.14-svn6')) - { + if (versionInUpdate($current_version, '0.9.14-svn6')) { $has_preconfig = true; $description = 'You can now allow customers to use any of their domains as username for the login.'; $question = 'Do you want to enable domain-login for all customers?: '; - $question.= makeyesno('update_allow_domain_login', '1', '0', '0'); + $question .= makeyesno('update_allow_domain_login', '1', '0', '0'); eval("\$return.=\"" . getTemplate("update/preconfigitem") . "\";"); } - if(versionInUpdate($current_version, '0.9.14-svn10')) - { + if (versionInUpdate($current_version, '0.9.14-svn10')) { $has_preconfig = true; $description = 'This update removes the unsupported real-time option. Additionally the deprecated tables for navigation and cronscripts are removed, any modules using these tables need to be updated to the new structure!'; $question = ''; eval("\$return.=\"" . getTemplate("update/preconfigitem") . "\";"); } - if(versionInUpdate($current_version, '0.9.16-svn1')) - { + if (versionInUpdate($current_version, '0.9.16-svn1')) { $has_preconfig = true; $description = 'Froxlor now features support for php-fpm.'; $question = 'Do you want to enable php-fpm?: '; - $question.= makeyesno('update_phpfpm_enabled', '1', '0', '0').'

'; - $question.= 'If \'yes\', please specify the configuration directory: '; - $question.= '

'; - $question.= 'Please specify the temporary files directory: '; - $question.= '

'; - $question.= 'Please specify the PEAR directory: '; - $question.= '

'; - $question.= 'Please specify the php-fpm restart-command: '; - $question.= '

'; - $question.= 'Please specify the php-fpm rocess manager control: '; - $question.= '

'; + $question .= 'Please specify the temporary files directory: '; + $question .= '

'; + $question .= 'Please specify the PEAR directory: '; + $question .= '

'; + $question .= 'Please specify the php-fpm restart-command: '; + $question .= '

'; + $question .= 'Please specify the php-fpm rocess manager control: '; + $question .= '

'; - $question.= 'Please specify the number of child processes: '; - $question.= '

'; - $question.= 'Please specify the number of requests per child before respawning: '; - $question.= '

'; - $question.= 'The following settings are only required if you chose process manager = dynamic

'; - $question.= 'Please specify the number of child processes created on startup: '; - $question.= '

'; - $question.= 'Please specify the desired minimum number of idle server processes: '; - $question.= '

'; - $question.= 'Please specify the desired maximum number of idle server processes: '; - $question.= '
'; + $redirects .= makeoption('dynamic', 'dynamic', 'static'); + $question .= $redirects . '

'; + $question .= 'Please specify the number of child processes: '; + $question .= '

'; + $question .= 'Please specify the number of requests per child before respawning: '; + $question .= '

'; + $question .= 'The following settings are only required if you chose process manager = dynamic

'; + $question .= 'Please specify the number of child processes created on startup: '; + $question .= '

'; + $question .= 'Please specify the desired minimum number of idle server processes: '; + $question .= '

'; + $question .= 'Please specify the desired maximum number of idle server processes: '; + $question .= '
'; eval("\$return.=\"" . getTemplate("update/preconfigitem") . "\";"); } - if(versionInUpdate($current_version, '0.9.16-svn2')) - { - if((int)Settings::Get('phpfpm.enabled') == 1) - { + if (versionInUpdate($current_version, '0.9.16-svn2')) { + if ((int) Settings::Get('phpfpm.enabled') == 1) { $has_preconfig = true; $description = 'You can chose whether you want Froxlor to use PHP-FPM itself too now.'; $question = 'Use PHP-FPM for the Froxlor Panel?: '; - $question.= makeyesno('update_phpfpm_enabled_ownvhost', '1', '0', '0').'

'; - $question.= 'If \'yes\', please specify local user/group (have to exist, Froxlor does not add them automatically):

'; - $question.= 'Local user: '; - $question.= '

'; - $question.= 'Local group: '; - $question.= '
'; + $question .= makeyesno('update_phpfpm_enabled_ownvhost', '1', '0', '0') . '

'; + $question .= 'If \'yes\', please specify local user/group (have to exist, Froxlor does not add them automatically):

'; + $question .= 'Local user: '; + $question .= '

'; + $question .= 'Local group: '; + $question .= '
'; eval("\$return.=\"" . getTemplate("update/preconfigitem") . "\";"); } } - if(versionInUpdate($current_version, '0.9.17-svn1')) - { + if (versionInUpdate($current_version, '0.9.17-svn1')) { $has_preconfig = true; $description = 'Select if you want to enable the web- and traffic-reports'; $question = 'Enable?: '; - $question.= makeyesno('update_system_report_enable', '1', '0', '1').'

'; - $question.= 'If \'yes\', please specify a percentage value for web- and traffic when reports are to be sent:

'; - $question.= 'Webusage warning level: '; - $question.= '

'; - $question.= 'Traffic warning level: '; - $question.= '
'; + $question .= makeyesno('update_system_report_enable', '1', '0', '1') . '

'; + $question .= 'If \'yes\', please specify a percentage value for web- and traffic when reports are to be sent:

'; + $question .= 'Webusage warning level: '; + $question .= '

'; + $question .= 'Traffic warning level: '; + $question .= '
'; eval("\$return.=\"" . getTemplate("update/preconfigitem") . "\";"); } - if(versionInUpdate($current_version, '0.9.18-svn2')) - { + if (versionInUpdate($current_version, '0.9.18-svn2')) { $has_preconfig = true; $description = 'As you can (obviously) see, Froxlor now comes with a new theme. You also have the possibility to switch back to "Classic" if you want to.'; $question = 'Select default panel theme: '; - $question.= ''; $themes = getThemes(); - foreach($themes as $cur_theme) // $theme is already in use - { - $question.= makeoption($cur_theme, $cur_theme, 'Froxlor'); + foreach ($themes as $cur_theme) // $theme is already in use +{ + $question .= makeoption($cur_theme, $cur_theme, 'Froxlor'); } - $question.= ''; + $question .= ''; eval("\$return.=\"" . getTemplate("update/preconfigitem") . "\";"); } - if(versionInUpdate($current_version, '0.9.28-svn4')) - { + if (versionInUpdate($current_version, '0.9.28-svn4')) { $has_preconfig = true; $description = 'This version introduces a lot of profound changes:'; $description .= '
  • Improving the whole template system
  • Full UTF-8 support
  • Removing support for the former default theme \'Classic\'
'; @@ -444,13 +397,12 @@ function parseAndOutputPreconfig(&$has_preconfig, &$return, $current_version, $c $description .= 'test this update in a testing environment using your existing data.

'; $question = 'Select your preferred Classic Theme replacement: '; - $question.= ''; $themes = getThemes(); - foreach($themes as $cur_theme) - { - $question.= makeoption($cur_theme, $cur_theme, 'Froxlor'); + foreach ($themes as $cur_theme) { + $question .= makeoption($cur_theme, $cur_theme, 'Froxlor'); } - $question.= ''; + $question .= ''; eval("\$return.=\"" . getTemplate("update/preconfigitem") . "\";"); } @@ -460,16 +412,16 @@ function parseAndOutputPreconfig(&$has_preconfig, &$return, $current_version, $c if (Settings::Get('system.webserver') == 'apache2') { $has_preconfig = true; $description = 'Froxlor now supports the new Apache 2.4. Please be aware that you need to load additional apache-modules in ordner to use it.
'; - $description.= '
LoadModule authz_core_module modules/mod_authz_core.so
+			$description .= '
LoadModule authz_core_module modules/mod_authz_core.so
 					LoadModule authz_host_module modules/mod_authz_host.so

'; $question = 'Do you want to enable the Apache-2.4 modification?: '; - $question.= makeyesno('update_system_apache24', '1', '0', '0'); + $question .= makeyesno('update_system_apache24', '1', '0', '0'); eval("\$return.=\"" . getTemplate("update/preconfigitem") . "\";"); } elseif (Settings::Get('system.webserver') == 'nginx') { $has_preconfig = true; $description = 'The path to nginx\'s fastcgi_params file is now customizable.

'; $question = 'Please enter full path to you nginx/fastcgi_params file (including filename): '; - $question.= ''; + $question .= ''; eval("\$return.=\"" . getTemplate("update/preconfigitem") . "\";"); } } @@ -478,11 +430,11 @@ function parseAndOutputPreconfig(&$has_preconfig, &$return, $current_version, $c $has_preconfig = true; - $description = 'This version adds an option to append the domain-name to the document-root for domains and subdomains.
'; + $description = 'This version adds an option to append the domain-name to the document-root for domains and subdomains.
'; $description .= 'You can enable or disable this feature anytime from settings -> system settings.
'; $question = 'Do you want to automatically append the domain-name to the documentroot of newly created domains?: '; - $question.= makeyesno('update_system_documentroot_use_default_value', '1', '0', '0'); + $question .= makeyesno('update_system_documentroot_use_default_value', '1', '0', '0'); eval("\$return.=\"" . getTemplate("update/preconfigitem") . "\";"); } @@ -491,12 +443,10 @@ function parseAndOutputPreconfig(&$has_preconfig, &$return, $current_version, $c $has_preconfig = true; // just an information about the new sendmail parameter (#1134) - $description = 'Froxlor changed the default parameter-set of sendmail (php.ini)
'; + $description = 'Froxlor changed the default parameter-set of sendmail (php.ini)
'; $description .= 'sendmail_path = "/usr/sbin/sendmail -t -i -f {CUSTOMER_EMAIL}"

'; $description .= 'If you don\'t have any problems with sending mails, you don\'t need to change this'; - if (Settings::Get('system.mod_fcgid') == '1' - || Settings::Get('phpfpm.enabled') == '1' - ) { + if (Settings::Get('system.mod_fcgid') == '1' || Settings::Get('phpfpm.enabled') == '1') { // information about removal of php's safe_mode $description .= '

The php safe_mode flag has been removed as current versions of PHP
'; $description .= 'do not support it anymore.

'; @@ -509,45 +459,43 @@ function parseAndOutputPreconfig(&$has_preconfig, &$return, $current_version, $c if (versionInUpdate($current_version, '0.9.29-dev1')) { // we only need to ask if fcgid|php-fpm is enabled - if (Settings::Get('system.mod_fcgid') == '1' - || Settings::Get('phpfpm.enabled') == '1' - ) { + if (Settings::Get('system.mod_fcgid') == '1' || Settings::Get('phpfpm.enabled') == '1') { $has_preconfig = true; - $description = 'Standard-subdomains can now be hidden from the php-configuration overview.
'; + $description = 'Standard-subdomains can now be hidden from the php-configuration overview.
'; $question = 'Do you want to hide the standard-subdomains (this can be changed in the settings any time)?: '; - $question.= makeyesno('hide_stdsubdomains', '1', '0', '0'); + $question .= makeyesno('hide_stdsubdomains', '1', '0', '0'); eval("\$return.=\"" . getTemplate("update/preconfigitem") . "\";"); } } if (versionInUpdate($current_version, '0.9.29-dev2')) { $has_preconfig = true; - $description = 'You can now decide whether admins/customers are able to change the theme
'; + $description = 'You can now decide whether admins/customers are able to change the theme
'; $question = 'If you want to disallow theme-changing, select "no" from the dropdowns: '; - $question.= "Admins: ". makeyesno('allow_themechange_a', '1', '0', '1').'  '; - $question.= "Customers: ".makeyesno('allow_themechange_c', '1', '0', '1'); + $question .= "Admins: " . makeyesno('allow_themechange_a', '1', '0', '1') . '  '; + $question .= "Customers: " . makeyesno('allow_themechange_c', '1', '0', '1'); eval("\$return.=\"" . getTemplate("update/preconfigitem") . "\";"); } if (versionInUpdate($current_version, '0.9.29-dev3')) { $has_preconfig = true; - $description = 'There is now a possibility to specify AXFR servers for your bind zone-configuration
'; + $description = 'There is now a possibility to specify AXFR servers for your bind zone-configuration
'; $question = 'Enter a comma-separated list of AXFR servers or leave empty (default): '; - $question.= ''; + $question .= ''; eval("\$return.=\"" . getTemplate("update/preconfigitem") . "\";"); } if (versionInUpdate($current_version, '0.9.29-dev4')) { $has_preconfig = true; - $description = 'As customers can now specify ssl-certificate data for their domains, you need to specify where the generated files are stored
'; + $description = 'As customers can now specify ssl-certificate data for their domains, you need to specify where the generated files are stored
'; $question = 'Specify the directory for customer ssl-certificates: '; - $question.= ''; + $question .= ''; eval("\$return.=\"" . getTemplate("update/preconfigitem") . "\";"); } if (versionInUpdate($current_version, '0.9.29.1-dev3')) { $has_preconfig = true; - $description = 'The build in logrotation-feature has been removed. Please follow the configuration-instructions for your system to enable logrotating again.'; + $description = 'The build in logrotation-feature has been removed. Please follow the configuration-instructions for your system to enable logrotating again.'; $question = ''; eval("\$return.=\"" . getTemplate("update/preconfigitem") . "\";"); } @@ -555,11 +503,9 @@ function parseAndOutputPreconfig(&$has_preconfig, &$return, $current_version, $c // let the apache+fpm users know that they MUST change their config // for the domains / webserver to work after the update if (versionInUpdate($current_version, '0.9.30-dev1')) { - if (Settings::Get('system.webserver') == 'apache2' - && Settings::Get('phpfpm.enabled') == '1' - ) { + if (Settings::Get('system.webserver') == 'apache2' && Settings::Get('phpfpm.enabled') == '1') { $has_preconfig = true; - $description = 'The PHP-FPM implementation for apache2 has changed. Please look for the "fastcgi.conf" (Debian/Ubuntu) or "70_fastcgi.conf" (Gentoo) within /etc/apache2/ and change it as shown below:

'; + $description = 'The PHP-FPM implementation for apache2 has changed. Please look for the "fastcgi.conf" (Debian/Ubuntu) or "70_fastcgi.conf" (Gentoo) within /etc/apache2/ and change it as shown below:

'; $description .= '
<IfModule mod_fastcgi.c>
     FastCgiIpcDir /var/lib/apache2/fastcgi/
     <Location "/fastcgiphp">
@@ -575,11 +521,9 @@ function parseAndOutputPreconfig(&$has_preconfig, &$return, $current_version, $c
 	}
 
 	if (versionInUpdate($current_version, '0.9.31-dev2')) {
-		if (Settings::Get('system.webserver') == 'apache2'
-				&& Settings::Get('phpfpm.enabled') == '1'
-		) {
+		if (Settings::Get('system.webserver') == 'apache2' && Settings::Get('phpfpm.enabled') == '1') {
 			$has_preconfig = true;
-			$description  = 'The FPM socket directory is now a setting in froxlor. Its default is /var/lib/apache2/fastcgi/.
If you are using /var/run/apache2 in the "fastcgi.conf" (Debian/Ubuntu) or "70_fastcgi.conf" (Gentoo) please correct this path accordingly
'; + $description = 'The FPM socket directory is now a setting in froxlor. Its default is /var/lib/apache2/fastcgi/.
If you are using /var/run/apache2 in the "fastcgi.conf" (Debian/Ubuntu) or "70_fastcgi.conf" (Gentoo) please correct this path accordingly
'; $question = ''; eval("\$return.=\"" . getTemplate("update/preconfigitem") . "\";"); } @@ -587,50 +531,50 @@ function parseAndOutputPreconfig(&$has_preconfig, &$return, $current_version, $c if (versionInUpdate($current_version, '0.9.31-dev4')) { $has_preconfig = true; - $description = 'The template-variable {PASSWORD} has been replaced with {LINK}. Please update your password reset templates!
'; + $description = 'The template-variable {PASSWORD} has been replaced with {LINK}. Please update your password reset templates!
'; $question = ''; eval("\$return.=\"" . getTemplate("update/preconfigitem") . "\";"); } if (versionInUpdate($current_version, '0.9.31-dev5')) { $has_preconfig = true; - $description = 'You can enable/disable error-reporting for admins and customers!

'; + $description = 'You can enable/disable error-reporting for admins and customers!

'; $question = 'Do you want to enable error-reporting for admins? (default: yes): '; - $question.= makeyesno('update_error_report_admin', '1', '0', '1').'
'; - $question.= 'Do you want to enable error-reporting for customers? (default: no): '; - $question.= makeyesno('update_error_report_customer', '1', '0', '0'); + $question .= makeyesno('update_error_report_admin', '1', '0', '1') . '
'; + $question .= 'Do you want to enable error-reporting for customers? (default: no): '; + $question .= makeyesno('update_error_report_customer', '1', '0', '0'); eval("\$return.=\"" . getTemplate("update/preconfigitem") . "\";"); } if (versionInUpdate($current_version, '0.9.31-rc2')) { $has_preconfig = true; - $description = 'You can enable/disable the display/usage of the news-feed for admins

'; + $description = 'You can enable/disable the display/usage of the news-feed for admins

'; $question = 'Do you want to enable the news-feed for admins? (default: yes): '; - $question.= makeyesno('update_admin_news_feed', '1', '0', '1').'
'; + $question .= makeyesno('update_admin_news_feed', '1', '0', '1') . '
'; eval("\$return.=\"" . getTemplate("update/preconfigitem") . "\";"); } if (versionInUpdate($current_version, '0.9.32-dev2')) { $has_preconfig = true; - $description = 'To enable logging of the mail-traffic, you need to set the following settings accordingly

'; + $description = 'To enable logging of the mail-traffic, you need to set the following settings accordingly

'; $question = 'Do you want to enable the traffic collection for mail? (default: yes): '; - $question.= makeyesno('mailtraffic_enabled', '1', '0', '1').'
'; - $question.= 'Mail Transfer Agent
'; - $question.= 'Type of your MTA: '; - $question.= '
'; - $question.= 'Logfile for your MTA: '; - $question.= '
'; - $question.= 'Mail Delivery Agent
'; - $question.= 'Type of your MDA: '; - $question.= '

'; - $question.= 'Logfile for your MDA: '; - $question.= '
'; + $question .= makeyesno('mailtraffic_enabled', '1', '0', '1') . '
'; + $question .= 'Mail Transfer Agent
'; + $question .= 'Type of your MTA: '; + $question .= '
'; + $question .= 'Logfile for your MTA: '; + $question .= '
'; + $question .= 'Mail Delivery Agent
'; + $question .= 'Type of your MDA: '; + $question .= '

'; + $question .= 'Logfile for your MDA: '; + $question .= '
'; eval("\$return.=\"" . getTemplate("update/preconfigitem") . "\";"); } @@ -638,7 +582,7 @@ function parseAndOutputPreconfig(&$has_preconfig, &$return, $current_version, $c $has_preconfig = true; $description = 'Froxlor now generates a cron-configuration file for the cron-daemon. Please set a filename which will be included automatically by your crond (e.g. files in /etc/cron.d/)

'; $question = 'Path to the cron-service configuration-file. This file will be updated regularly and automatically by froxlor.
Note: please be sure to use the same filename as for the main froxlor cronjob (default: /etc/cron.d/froxlor)!
'; - $question.= '
'; + $question .= '
'; eval("\$return.=\"" . getTemplate("update/preconfigitem") . "\";"); } @@ -646,7 +590,7 @@ function parseAndOutputPreconfig(&$has_preconfig, &$return, $current_version, $c $has_preconfig = true; $description = 'In order for the new cron.d file to work properly, we need to know about the cron-service reload command.

'; $question = 'Please specify the reload-command of your cron-daemon (default: /etc/init.d/cron reload)
'; - $question.= '
'; + $question .= '
'; eval("\$return.=\"" . getTemplate("update/preconfigitem") . "\";"); } @@ -654,17 +598,17 @@ function parseAndOutputPreconfig(&$has_preconfig, &$return, $current_version, $c $has_preconfig = true; $description = 'To customize the command which executes the cronjob (php - basically) change the path below according to your system.

'; $question = 'Please specify the command to execute cronscripts (default: "/usr/bin/nice -n 5 /usr/bin/php5 -q")
'; - $question.= '
'; + $question .= '
'; eval("\$return.=\"" . getTemplate("update/preconfigitem") . "\";"); } if (versionInUpdate($current_version, '0.9.33-dev1')) { $has_preconfig = true; - $description = 'You can enable/disable the display/usage of the custom newsfeed for customers.

'; + $description = 'You can enable/disable the display/usage of the custom newsfeed for customers.

'; $question = 'Do you want to enable the custom newsfeed for customer? (default: no): '; - $question.= makeyesno('customer_show_news_feed', '1', '0', '0').'
'; - $question.= 'You have to set the URL for your RSS-feed here, if you have chosen to enable the custom newsfeed on the customer-dashboard: '; - $question.= '
'; + $question .= makeyesno('customer_show_news_feed', '1', '0', '0') . '
'; + $question .= 'You have to set the URL for your RSS-feed here, if you have chosen to enable the custom newsfeed on the customer-dashboard: '; + $question .= '
'; eval("\$return.=\"" . getTemplate("update/preconfigitem") . "\";"); } @@ -672,56 +616,67 @@ function parseAndOutputPreconfig(&$has_preconfig, &$return, $current_version, $c // only if bind is used - if not the default will be set, which is '0' (off) if (Settings::get('system.bind_enable') == 1) { $has_preconfig = true; - $description = 'You can enable/disable the generation of the bind-zone / config for the system hostname.

'; + $description = 'You can enable/disable the generation of the bind-zone / config for the system hostname.

'; $question = 'Do you want to generate a bind-zone for the system-hostname? (default: no): '; - $question.= makeyesno('dns_createhostnameentry', '1', '0', '0').'
'; + $question .= makeyesno('dns_createhostnameentry', '1', '0', '0') . '
'; eval("\$return.=\"" . getTemplate("update/preconfigitem") . "\";"); } } if (versionInUpdate($current_version, '0.9.33-rc2')) { $has_preconfig = true; - $description = 'You can chose whether you want to receive an e-mail on cronjob errors. Keep in mind that this can lead to an e-mail being sent every 5 minutes.

'; + $description = 'You can chose whether you want to receive an e-mail on cronjob errors. Keep in mind that this can lead to an e-mail being sent every 5 minutes.

'; $question = 'Do you want to receive cron-errors via mail? (default: no): '; - $question.= makeyesno('system_send_cron_errors', '1', '0', '0').'
'; + $question .= makeyesno('system_send_cron_errors', '1', '0', '0') . '
'; eval("\$return.=\"" . getTemplate("update/preconfigitem") . "\";"); } if (versionInUpdate($current_version, '0.9.34-dev3')) { - $has_preconfig = true; - $description = 'Froxlor now requires the PHP mbstring-extension as we need to be multibyte-character safe in some cases'; - $question = 'PHP mbstring is currently: '; - if (!extension_loaded('mbstring')) { - $question .= 'not installed/loaded'; - $question .= '
Please install the PHP mbstring extension in order to finish the update'; - } else { - $question .= 'installed/loaded'; - } - $question .= '
'; - eval("\$return.=\"" . getTemplate("update/preconfigitem") . "\";"); + $has_preconfig = true; + $description = 'Froxlor now requires the PHP mbstring-extension as we need to be multibyte-character safe in some cases'; + $question = 'PHP mbstring is currently: '; + if (! extension_loaded('mbstring')) { + $question .= 'not installed/loaded'; + $question .= '
Please install the PHP mbstring extension in order to finish the update'; + } else { + $question .= 'installed/loaded'; + } + $question .= '
'; + eval("\$return.=\"" . getTemplate("update/preconfigitem") . "\";"); } if (versionInUpdate($current_db_version, '201603070')) { - $has_preconfig = true; - $description = 'You can chose whether you want to enable or disable our Let\'s Encrypt implementation.
Please remember that you need to go through the webserver-configuration when enabled because this feature needs a special configuration.

'; - $question = 'Do you want to enable Let\'s Encrypt? (default: yes): '; - $question.= makeyesno('enable_letsencrypt', '1', '0', '1').'
'; - eval("\$return.=\"" . getTemplate("update/preconfigitem") . "\";"); + $has_preconfig = true; + $description = 'You can chose whether you want to enable or disable our Let\'s Encrypt implementation.
Please remember that you need to go through the webserver-configuration when enabled because this feature needs a special configuration.

'; + $question = 'Do you want to enable Let\'s Encrypt? (default: yes): '; + $question .= makeyesno('enable_letsencrypt', '1', '0', '1') . '
'; + eval("\$return.=\"" . getTemplate("update/preconfigitem") . "\";"); } if (versionInUpdate($current_db_version, '201604270')) { $has_preconfig = true; - $description = 'You can chose whether you want to enable or disable our backup function.

'; + $description = 'You can chose whether you want to enable or disable our backup function.

'; $question = 'Do you want to enable Backup? (default: no): '; - $question.= makeyesno('enable_backup', '1', '0', '0').'
'; + $question .= makeyesno('enable_backup', '1', '0', '0') . '
'; eval("\$return.=\"" . getTemplate("update/preconfigitem") . "\";"); } if (versionInUpdate($current_db_version, '201605090')) { $has_preconfig = true; - $description = 'You can chose whether you want to enable or disable our DNS editor

'; + $description = 'You can chose whether you want to enable or disable our DNS editor

'; $question = 'Do you want to enable the DNS editor? (default: no): '; - $question.= makeyesno('enable_dns', '1', '0', '0').'
'; + $question .= makeyesno('enable_dns', '1', '0', '0') . '
'; + eval("\$return.=\"" . getTemplate("update/preconfigitem") . "\";"); + } + + if (versionInUpdate($current_db_version, '201605170')) { + $has_preconfig = true; + $description = 'Froxlor now supports the dns-daemon Power-DNS, you can chose between bind and powerdns now.'; + $question = 'Select dns-daemon you want to use: '; + $question .= ''; eval("\$return.=\"" . getTemplate("update/preconfigitem") . "\";"); } } diff --git a/lib/configfiles/gentoo.xml b/lib/configfiles/gentoo.xml index 1c945572..71374ccb 100644 --- a/lib/configfiles/gentoo.xml +++ b/lib/configfiles/gentoo.xml @@ -388,7 +388,547 @@ mail IN A - + + + + + +################################# +# allow-dnsupdate-from A global setting to allow DNS updates from these IP ranges. +# +# allow-dnsupdate-from=127.0.0.0/8,::1 + +################################# +# allow-recursion List of subnets that are allowed to recurse +# +allow-recursion=127.0.0.1 + +################################# +# also-notify When notifying a domain, also notify these nameservers +# +# also-notify= + +################################# +# any-to-tcp Answer ANY queries with tc=1, shunting to TCP +# +# any-to-tcp=no + +################################# +# cache-ttl Seconds to store packets in the PacketCache +# +# cache-ttl=20 + +################################# +# carbon-interval Number of seconds between carbon (graphite) updates +# +# carbon-interval=30 + +################################# +# carbon-ourname If set, overrides our reported hostname for carbon stats +# +# carbon-ourname= + +################################# +# carbon-server If set, send metrics in carbon (graphite) format to this server +# +# carbon-server= + +################################# +# chroot If set, chroot to this directory for more security +# +# chroot= + +################################# +# config-dir Location of configuration directory (pdns.conf) +# +config-dir=/etc/powerdns + +################################# +# config-name Name of this virtual configuration - will rename the binary image +# +# config-name= + +################################# +# control-console Debugging switch - don't use +# +# control-console=no + +################################# +# daemon Operate as a daemon +# +daemon=yes + +################################# +# default-ksk-algorithms Default KSK algorithms +# +# default-ksk-algorithms=rsasha256 + +################################# +# default-ksk-size Default KSK size (0 means default) +# +# default-ksk-size=0 + +################################# +# default-soa-mail mail address to insert in the SOA record if none set in the backend +# +# default-soa-mail= + +################################# +# default-soa-name name to insert in the SOA record if none set in the backend +# +# default-soa-name=a.misconfigured.powerdns.server + +################################# +# default-ttl Seconds a result is valid if not set otherwise +# +# default-ttl=3600 + +################################# +# default-zsk-algorithms Default ZSK algorithms +# +# default-zsk-algorithms=rsasha256 + +################################# +# default-zsk-size Default ZSK size (0 means default) +# +# default-zsk-size=0 + +################################# +# direct-dnskey Fetch DNSKEY RRs from backend during DNSKEY synthesis +# +# direct-dnskey=no + +################################# +# disable-axfr Disable zonetransfers but do allow TCP queries +# +# disable-axfr=no + +################################# +# disable-axfr-rectify Disable the rectify step during an outgoing AXFR. Only required for regression testing. +# +# disable-axfr-rectify=no + +################################# +# disable-tcp Do not listen to TCP queries +# +# disable-tcp=no + +################################# +# distributor-threads Default number of Distributor (backend) threads to start +# +# distributor-threads=3 + +################################# +# do-ipv6-additional-processing Do AAAA additional processing +# +# do-ipv6-additional-processing=yes + +################################# +# edns-subnet-processing If we should act on EDNS Subnet options +# +# edns-subnet-processing=no + +################################# +# entropy-source If set, read entropy from this file +# +# entropy-source=/dev/urandom + +################################# +# experimental-api-key REST API Static authentication key (required for API use) +# +# experimental-api-key= + +################################# +# experimental-api-readonly If the JSON API should disallow data modification +# +# experimental-api-readonly=no + +################################# +# experimental-dname-processing If we should support DNAME records +# +# experimental-dname-processing=no + +################################# +# experimental-dnsupdate Enable/Disable DNS update (RFC2136) support. Default is no. +# +# experimental-dnsupdate=no + +################################# +# experimental-json-interface If the webserver should serve JSON data +# +# experimental-json-interface=no + +################################# +# experimental-logfile Filename of the log file for JSON parser +# +# experimental-logfile=/var/log/pdns.log + +################################# +# forward-dnsupdate A global setting to allow DNS update packages that are for a Slave domain, to be forwarded to the master. +# +# forward-dnsupdate=yes + +################################# +# guardian Run within a guardian process +# +guardian=yes + +################################# +# include-dir Include *.conf files from this directory +# +# include-dir= + +################################# +# launch Which backends to launch and order to query them in +# +# launch= + +################################# +# load-modules Load this module - supply absolute or relative path +# +# load-modules= + +################################# +# local-address Local IP addresses to which we bind +# +local-address=,127.0.0.1 + +################################# +# local-address-nonexist-fail Fail to start if one or more of the local-address's do not exist on this server +# +# local-address-nonexist-fail=yes + +################################# +# local-ipv6 Local IP address to which we bind +# +# local-ipv6= + +################################# +# local-ipv6-nonexist-fail Fail to start if one or more of the local-ipv6 addresses do not exist on this server +# +# local-ipv6-nonexist-fail=yes + +################################# +# local-port The port on which we listen +# +local-port=53 + +################################# +# log-dns-details If PDNS should log DNS non-erroneous details +# +log-dns-details=yes + +################################# +# log-dns-queries If PDNS should log all incoming DNS queries +# +# log-dns-queries=no + +################################# +# logging-facility Log under a specific facility +# +# logging-facility= + +################################# +# loglevel Amount of logging. Higher is more. Do not set below 3 +# +# loglevel=4 + +################################# +# lua-prequery-script Lua script with prequery handler +# +# lua-prequery-script= + +################################# +# master Act as a master +# +master=yes + +################################# +# max-cache-entries Maximum number of cache entries +# +# max-cache-entries=1000000 + +################################# +# max-ent-entries Maximum number of empty non-terminals in a zone +# +# max-ent-entries=100000 + +################################# +# max-nsec3-iterations Limit the number of NSEC3 hash iterations +# +# max-nsec3-iterations=500 + +################################# +# max-queue-length Maximum queuelength before considering situation lost +# +# max-queue-length=5000 + +################################# +# max-signature-cache-entries Maximum number of signatures cache entries +# +# max-signature-cache-entries= + +################################# +# max-tcp-connections Maximum number of TCP connections +# +# max-tcp-connections=10 + +################################# +# module-dir Default directory for modules +# +module-dir=/usr/lib/powerdns/pdns/ + +################################# +# negquery-cache-ttl Seconds to store negative query results in the QueryCache +# +# negquery-cache-ttl=60 + +################################# +# no-shuffle Set this to prevent random shuffling of answers - for regression testing +# +# no-shuffle=off + +################################# +# only-notify Only send AXFR NOTIFY to these IP addresses or netmasks +# +# only-notify=0.0.0.0/0,::/0 + +################################# +# out-of-zone-additional-processing Do out of zone additional processing +# +# out-of-zone-additional-processing=yes + +################################# +# overload-queue-length Maximum queuelength moving to packetcache only +# +# overload-queue-length=0 + +################################# +# pipebackend-abi-version Version of the pipe backend ABI +# +# pipebackend-abi-version=1 + +################################# +# prevent-self-notification Don't send notifications to what we think is ourself +# +# prevent-self-notification=yes + +################################# +# query-cache-ttl Seconds to store query results in the QueryCache +# +# query-cache-ttl=20 + +################################# +# query-local-address Source IP address for sending queries +# +# query-local-address=0.0.0.0 + +################################# +# query-local-address6 Source IPv6 address for sending queries +# +# query-local-address6=:: + +################################# +# query-logging Hint backends that queries should be logged +# +# query-logging=no + +################################# +# queue-limit Maximum number of milliseconds to queue a query +# +# queue-limit=1500 + +################################# +# receiver-threads Default number of receiver threads to start +# +# receiver-threads=1 + +################################# +# recursive-cache-ttl Seconds to store packets for recursive queries in the PacketCache +# +# recursive-cache-ttl=10 + +################################# +# recursor If recursion is desired, IP address of a recursing nameserver +# +# recursor=no + +################################# +# retrieval-threads Number of AXFR-retrieval threads for slave operation +# +# retrieval-threads=2 + +################################# +# reuseport Enable higher performance on compliant kernels by using SO_REUSEPORT allowing each receiver thread to open its own socket +# +# reuseport=no + +################################# +# security-poll-suffix Domain name from which to query security update notifications +# +# security-poll-suffix=secpoll.powerdns.com. + +################################# +# send-root-referral Send out old-fashioned root-referral instead of ServFail in case of no authority +# +# send-root-referral=no + +################################# +# server-id Returned when queried for 'server.id' TXT or NSID, defaults to hostname - disabled or custom +# +# server-id= + +################################# +# setgid If set, change group id to this gid for more security +# +setgid=pdns + +################################# +# setuid If set, change user id to this uid for more security +# +setuid=pdns + +################################# +# signing-threads Default number of signer threads to start +# +# signing-threads=3 + +################################# +# slave Act as a slave +# +# slave=no + +################################# +# slave-cycle-interval Reschedule failed SOA serial checks once every .. seconds +# +# slave-cycle-interval=60 + +################################# +# slave-renotify If we should send out notifications for slaved updates +# +# slave-renotify=no + +################################# +# soa-expire-default Default SOA expire +# +# soa-expire-default=604800 + +################################# +# soa-minimum-ttl Default SOA minimum ttl +# +# soa-minimum-ttl=3600 + +################################# +# soa-refresh-default Default SOA refresh +# +# soa-refresh-default=10800 + +################################# +# soa-retry-default Default SOA retry +# +# soa-retry-default=3600 + +################################# +# socket-dir Where the controlsocket will live +# +socket-dir=/var/run + +################################# +# tcp-control-address If set, PowerDNS can be controlled over TCP on this address +# +# tcp-control-address= + +################################# +# tcp-control-port If set, PowerDNS can be controlled over TCP on this address +# +# tcp-control-port=53000 + +################################# +# tcp-control-range If set, remote control of PowerDNS is possible over these networks only +# +# tcp-control-range=127.0.0.0/8, 10.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12, ::1/128, fe80::/10 + +################################# +# tcp-control-secret If set, PowerDNS can be controlled over TCP after passing this secret +# +# tcp-control-secret= + +################################# +# traceback-handler Enable the traceback handler (Linux only) +# +# traceback-handler=yes + +################################# +# trusted-notification-proxy IP address of incoming notification proxy +# +# trusted-notification-proxy= + +################################# +# udp-truncation-threshold Maximum UDP response size before we truncate +# +# udp-truncation-threshold=1680 + +################################# +# version-string PowerDNS version in packets - full, anonymous, powerdns or custom +# +version-string=powerdns + +################################# +# webserver Start a webserver for monitoring +# +# webserver=no + +################################# +# webserver-address IP Address of webserver to listen on +# +# webserver-address=127.0.0.1 + +################################# +# webserver-allow-from Webserver access is only allowed from these subnets +# +# webserver-allow-from=0.0.0.0/0,::/0 + +################################# +# webserver-password Password required for accessing the webserver +# +# webserver-password= + +################################# +# webserver-port Port of webserver to listen on +# +# webserver-port=8081 + +################################# +# webserver-print-arguments If the webserver should print arguments +# +# webserver-print-arguments=no + +# include froxlor-bind-specific config +include=/etc/powerdns/pdns_froxlor.conf +]]> + + + + + + + + + (2003-2009) - * @author Froxlor team (2010-) - * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt - * @package Cron + * @copyright (c) the authors + * @author Froxlor team (2016-) + * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt + * @package Cron * */ +class bind extends DnsBase +{ -class bind { - public $logger = false; - public $nameservers = array(); - public $mxservers = array(); - public $axfrservers = array(); + public function writeConfigs() + { + // tell the world what we are doing + $this->_logger->logAction(CRON_ACTION, LOG_INFO, 'Task4 started - Rebuilding froxlor_bind.conf'); - private $_known_filenames = array(); - private $_bindconf_file = ''; + // clean up + $this->_cleanZonefiles(); - public function __construct($logger) { + // check for subfolder in bind-config-directory + if (! file_exists(makeCorrectDir(Settings::Get('system.bindconf_directory') . '/domains/'))) { + $this->_logger->logAction(CRON_ACTION, LOG_NOTICE, 'mkdir ' . escapeshellarg(makeCorrectDir(Settings::Get('system.bindconf_directory') . '/domains/'))); + safe_exec('mkdir -p ' . escapeshellarg(makeCorrectDir(Settings::Get('system.bindconf_directory') . '/domains/'))); + } - $this->logger = $logger; + $domains = $this->getDomainList(); - if (Settings::Get('system.nameservers') != '') { - $nameservers = explode(',', Settings::Get('system.nameservers')); - foreach ($nameservers as $nameserver) { - $nameserver = trim($nameserver); - // DNS servers might be multi homed; allow transfer from all ip - // addresses of the DNS server - $nameserver_ips = gethostbynamel($nameserver); - // append dot to hostname - if (substr($nameserver, -1, 1) != '.') { - $nameserver.= '.'; + if (! empty($domains)) { + $bindconf_file = '# ' . Settings::Get('system.bindconf_directory') . 'froxlor_bind.conf' . "\n" . '# Created ' . date('d.m.Y H:i') . "\n" . '# Do NOT manually edit this file, all changes will be deleted after the next domain change at the panel.' . "\n\n"; + + foreach ($domains as $domain) { + // check for system-hostname + $isFroxlorHostname = false; + if (isset($domain['froxlorhost']) && $domain['froxlorhost'] == 1) { + $isFroxlorHostname = true; } - // ignore invalid responses - if (!is_array($nameserver_ips)) { - // act like gethostbyname() and return unmodified hostname on error - $nameserver_ips = array($nameserver); - } - $this->nameservers[] = array( - 'hostname' => $nameserver, - 'ips' => $nameserver_ips - ); - } - } - - if (Settings::Get('system.mxservers') != '') { - $mxservers = explode(',', Settings::Get('system.mxservers')); - foreach ($mxservers as $mxserver) { - if (substr($mxserver, -1, 1) != '.') { - $mxserver.= '.'; - } - $this->mxservers[] = $mxserver; - } - } - - // AXFR server #100 - if (Settings::Get('system.axfrservers') != '') { - $axfrservers = explode(',', Settings::Get('system.axfrservers')); - foreach ($axfrservers as $axfrserver) { - $this->axfrservers[] = trim($axfrserver); - } - } - } - - - public function writeConfigs() { - $this->logger->logAction(CRON_ACTION, LOG_INFO, 'Task4 started - Rebuilding froxlor_bind.conf'); - - if (!file_exists(makeCorrectDir(Settings::Get('system.bindconf_directory') . '/domains/'))) { - $this->logger->logAction(CRON_ACTION, LOG_NOTICE, 'mkdir ' . escapeshellarg(makeCorrectDir(Settings::Get('system.bindconf_directory') . '/domains/'))); - safe_exec('mkdir ' . escapeshellarg(makeCorrectDir(Settings::Get('system.bindconf_directory') . '/domains/'))); - } - - $this->_known_filenames = array(); - - $this->_bindconf_file = '# ' . Settings::Get('system.bindconf_directory') . 'froxlor_bind.conf' . "\n" . - '# Created ' . date('d.m.Y H:i') . "\n" . - '# Do NOT manually edit this file, all changes will be deleted after the next domain change at the panel.' . "\n" . "\n"; - $result_domains_stmt = Database::query(" - SELECT `d`.`id`, `d`.`domain`, `d`.`isemaildomain`, `d`.`iswildcarddomain`, `d`.`wwwserveralias`, `d`.`customerid`, - `d`.`zonefile`, `d`.`bindserial`, `d`.`dkim`, `d`.`dkim_id`, `d`.`dkim_pubkey`, `d`.`ismainbutsubto`, - `c`.`loginname`, `c`.`guid` - FROM `" . TABLE_PANEL_DOMAINS . "` `d` LEFT JOIN `" . TABLE_PANEL_CUSTOMERS . "` `c` USING(`customerid`) - WHERE `d`.`isbinddomain` = '1' ORDER BY `d`.`domain` ASC - "); - - $domains = array(); - - // don't use fetchall() to be able to set the first column to the domain id and use it later on to set the rows' - // array of direct children without having to search the outer array - while ($domain = $result_domains_stmt->fetch(PDO::FETCH_ASSOC)) { - $domains[$domain["id"]] = $domain; - } - - // frolxor-hostname (#1090) - if (Settings::get('system.dns_createhostnameentry') == 1) { - $hostname_arr = array( - 'id' => 'none', - 'domain' => Settings::Get('system.hostname'), - 'isemaildomain' => Settings::Get('system.dns_createmailentry'), - 'customerid' => 'none', - 'loginname' => 'froxlor.panel', - 'bindserial' => date('Ymd').'00', - 'dkim' => '0', - 'iswildcarddomain' => '1', - 'ismainbutsubto' => '0', - 'zonefile' => '', - 'froxlorhost' => '1' - ); - $domains['none'] = $hostname_arr; - } - - if (empty($domains)) { - $this->logger->logAction(CRON_ACTION, LOG_INFO, 'No domains found for nameserver-config, skipping...'); - return; - } - - // collect domain IDs of direct child domains as arrays in ['children'] column - foreach (array_keys($domains) as $key) { - if (!isset($domains[$key]['children'])) { - $domains[$key]['children'] = array(); - } - if ($domains[$key]['ismainbutsubto'] > 0) { - if (isset($domains[ $domains[$key]['ismainbutsubto'] ])) { - $domains[ $domains[$key]['ismainbutsubto'] ]['children'][] = $domains[$key]['id']; - } else { - $this->logger->logAction(CRON_ACTION, LOG_ERR, - 'Database inconsistency: domain ' . $domain['domain'] . ' (ID #' . $key . - ') is set to to be subdomain to non-existent domain ID #' . - $domains[$key]['ismainbutsubto'] . - '. No DNS record(s) will be created for this domain.'); - } - } - } - - $this->logger->logAction(CRON_ACTION, LOG_DEBUG, - str_pad('domId', 9, ' ') . str_pad('domain', 40, ' ') . - 'ismainbutsubto ' . str_pad('parent domain', 40, ' ') . - "list of child domain ids"); - foreach ($domains as $domain) { - $logLine = - str_pad($domain['id'], 9, ' ') . - str_pad($domain['domain'], 40, ' ') . - str_pad($domain['ismainbutsubto'], 15, ' ') . - str_pad(((isset($domains[ $domain['ismainbutsubto'] ])) ? - $domains[ $domain['ismainbutsubto'] ]['domain'] : - '-'), 40, ' ') . - join(', ', $domain['children']); - $this->logger->logAction(CRON_ACTION, LOG_DEBUG, $logLine); - } - - foreach ($domains as $domain) { - if ($domain['ismainbutsubto'] > 0) { - // domains with ismainbutsubto>0 are handled by recursion within walkDomainList() - continue; - } - $this->walkDomainList($domain, $domains); - } - - $bindconf_file_handler = fopen(makeCorrectFile(Settings::Get('system.bindconf_directory') . '/froxlor_bind.conf'), 'w'); - fwrite($bindconf_file_handler, $this->_bindconf_file); - fclose($bindconf_file_handler); - $this->logger->logAction(CRON_ACTION, LOG_INFO, 'froxlor_bind.conf written'); - safe_exec(escapeshellcmd(Settings::Get('system.bindreload_command'))); - $this->logger->logAction(CRON_ACTION, LOG_INFO, 'Bind9 reloaded'); - $domains_dir = makeCorrectDir(Settings::Get('system.bindconf_directory') . '/domains/'); - - if (file_exists($domains_dir) - && is_dir($domains_dir)) { - $domain_file_dirhandle = opendir($domains_dir); - - while (false !== ($domain_filename = readdir($domain_file_dirhandle))) { - $full_filename = makeCorrectFile($domains_dir . '/' . $domain_filename); - - if ($domain_filename != '.' - && $domain_filename != '..' - && !in_array($domain_filename, $this->_known_filenames) - && is_file($full_filename) - && file_exists($full_filename)) { - $this->logger->logAction(CRON_ACTION, LOG_WARNING, 'Deleting ' . $domain_filename); - unlink(makeCorrectFile($domains_dir . '/' . $domain_filename)); - } - } - } - $this->logger->logAction(CRON_ACTION, LOG_INFO, 'Task4 finished'); - } - - private function walkDomainList($domain, $domains) { - $zonefile = ''; - $subzones = ''; - - foreach($domain['children'] as $child_domain_id) { - $subzones.= $this->walkDomainList($domains[$child_domain_id], $domains); - } - - if ($domain['zonefile'] == '') { - if ($domain['ismainbutsubto'] == 0) { - $zonefile = $this->generateZone($domain); + // create zone-file + $this->_logger->logAction(CRON_ACTION, LOG_DEBUG, 'Generating dns zone for ' . $domain['domain']); + $zone = createDomainZone($domain['id'], $isFroxlorHostname); + $zonefile = (string)$zone; $domain['zonefile'] = 'domains/' . $domain['domain'] . '.zone'; $zonefile_name = makeCorrectFile(Settings::Get('system.bindconf_directory') . '/' . $domain['zonefile']); - $this->_known_filenames[] = basename($zonefile_name); $zonefile_handler = fopen($zonefile_name, 'w'); - fwrite($zonefile_handler, $zonefile.$subzones); + fwrite($zonefile_handler, $zonefile); fclose($zonefile_handler); - $this->logger->logAction(CRON_ACTION, LOG_INFO, '`' . $zonefile_name . '` zone written'); - $this->_bindconf_file .= $this->_generateDomainConfig($domain); - } else { - return $this->generateZone($domain); + $this->_logger->logAction(CRON_ACTION, LOG_INFO, '`' . $zonefile_name . '` zone written'); + + // generate config + $bindconf_file .= $this->_generateDomainConfig($domain); } - } else { - $this->logger->logAction(CRON_ACTION, LOG_INFO, 'Added zonefile ' . $domain['zonefile'] . ' for domain ' . $domain['domain'] . - ' - Note that you will also have to handle ALL records for ALL subdomains.'); - $this->_bindconf_file .= $this->_generateDomainConfig($domain); + + // write config + $bindconf_file_handler = fopen(makeCorrectFile(Settings::Get('system.bindconf_directory') . '/froxlor_bind.conf'), 'w'); + fwrite($bindconf_file_handler, $bindconf_file); + fclose($bindconf_file_handler); + $this->_logger->logAction(CRON_ACTION, LOG_INFO, 'froxlor_bind.conf written'); + + // reload Bind + safe_exec(escapeshellcmd(Settings::Get('system.bindreload_command'))); + $this->_logger->logAction(CRON_ACTION, LOG_INFO, 'Bind9 reloaded'); } } - private function _generateDomainConfig($domain = array()) { - if (isset($domain['froxlorhost']) && $domain['froxlorhost'] === '1') { - $froxlorhost = true; - } else { - $froxlorhost = false; - } + private function _generateDomainConfig($domain = array()) + { + $this->_logger->logAction(CRON_ACTION, LOG_DEBUG, 'Generating dns config for ' . $domain['domain']); $bindconf_file = '# Domain ID: ' . $domain['id'] . ' - CustomerID: ' . $domain['customerid'] . ' - CustomerLogin: ' . $domain['loginname'] . "\n"; - $bindconf_file.= 'zone "' . $domain['domain'] . '" in {' . "\n"; - $bindconf_file.= ' type master;' . "\n"; - $bindconf_file.= ' file "' . makeCorrectFile(Settings::Get('system.bindconf_directory') . '/' . $domain['zonefile']) . '";' . "\n"; - $bindconf_file.= ' allow-query { any; };' . "\n"; + $bindconf_file .= 'zone "' . $domain['domain'] . '" in {' . "\n"; + $bindconf_file .= ' type master;' . "\n"; + $bindconf_file .= ' file "' . makeCorrectFile(Settings::Get('system.bindconf_directory') . '/' . $domain['zonefile']) . '";' . "\n"; + $bindconf_file .= ' allow-query { any; };' . "\n"; - if (count($this->nameservers) > 0 - || count($this->axfrservers) > 0 - ) { + if (count($this->_ns) > 0 || count($this->_axfr) > 0) { // open allow-transfer - $bindconf_file.= ' allow-transfer {' . "\n"; + $bindconf_file .= ' allow-transfer {' . "\n"; // put nameservers in allow-transfer - if (count($this->nameservers) > 0) { - foreach ($this->nameservers as $ns) { - foreach($ns["ips"] as $ip) { - $bindconf_file.= ' ' . $ip . ";\n"; + if (count($this->_ns) > 0) { + foreach ($this->_ns as $ns) { + foreach ($ns["ips"] as $ip) { + $bindconf_file .= ' ' . $ip . ";\n"; } } } // AXFR server #100 - if (count($this->axfrservers) > 0) { - foreach ($this->axfrservers as $axfrserver) { + if (count($this->_axfr) > 0) { + foreach ($this->_axfr as $axfrserver) { if (validate_ip($axfrserver, true) !== false) { - $bindconf_file.= ' ' . $axfrserver . ';' . "\n"; + $bindconf_file .= ' ' . $axfrserver . ';' . "\n"; } } } // close allow-transfer - $bindconf_file.= ' };' . "\n"; + $bindconf_file .= ' };' . "\n"; } - $bindconf_file.= '};' . "\n"; - $bindconf_file.= "\n"; + $bindconf_file .= '};' . "\n"; + $bindconf_file .= "\n"; return $bindconf_file; } - /** - * generate bind zone content. - * - * @param array $domain - * - * @return string - */ - protected function generateZone($domain) { - if (isset($domain['froxlorhost']) && $domain['froxlorhost'] === '1') { - $froxlorhost = true; - } else { - $froxlorhost = false; - } + private function _cleanZonefiles() + { + $config_dir = makeCorrectFile(Settings::Get('system.bindconf_directory') . '/domains/'); - // Array to save all ips needed in the records (already including IN A/AAAA) - $ip_a_records = array(); - // Array to save DNS records - $records = array(); + $this->_logger->logAction(CRON_ACTION, LOG_INFO, 'Cleaning dns zone files from ' . $config_dir); - $domainidquery = ''; - $query_params = array(); - if (!$froxlorhost) { + // check directory + if (@is_dir($config_dir)) { - $domainidquery = "`di`.`id_domain` = :domainid AND "; - $query_params['domainid'] = $domain['id']; + // create directory iterator + $its = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($config_dir)); - $result_ip_stmt = Database::prepare(" - SELECT `p`.`ip` AS `ip` - FROM `".TABLE_PANEL_IPSANDPORTS."` `p`, `".TABLE_DOMAINTOIP."` `di` - WHERE ".$domainidquery."`p`.`id` = `di`.`id_ipandports` - GROUP BY `p`.`ip`; - "); - } else { - // use all available IP's for the froxlor-hostname - $result_ip_stmt = Database::prepare(" - SELECT `ip` FROM `".TABLE_PANEL_IPSANDPORTS."` GROUP BY `ip` - "); - } - Database::pexecute($result_ip_stmt, $query_params); - - while ($ip = $result_ip_stmt->fetch(PDO::FETCH_ASSOC)) { - - if (filter_var($ip['ip'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) { - $ip_a_records[] = "A\t\t" . $ip['ip']; - } - elseif (filter_var($ip['ip'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { - $ip_a_records[] = "AAAA\t\t" . $ip['ip']; - } - else { - return ";Error in at least one IP Address (".$ip['ip']."), could not create zonefile!"; - } - } - - if ($domain['ismainbutsubto'] == 0) { - $date = date('Ymd'); - $bindserial = (preg_match('/^' . $date . '/', $domain['bindserial']) ? $domain['bindserial'] + 1 : $date . '00'); - - if (!$froxlorhost) { - $upd_stmt = Database::prepare(" - UPDATE `" . TABLE_PANEL_DOMAINS . "` SET - `bindserial` = :serial - WHERE `id` = :id - "); - Database::pexecute($upd_stmt, array('serial' => $bindserial, 'id' => $domain['id'])); - } - - $zonefile = '$TTL ' . (int)Settings::Get('system.defaultttl') . "\n"; - if (count($this->nameservers) == 0) { - $zonefile.= '@ IN SOA ns ' . str_replace('@', '.', Settings::Get('panel.adminmail')) . '. (' . "\n"; - } else { - $zonefile.= '@ IN SOA ' . $this->nameservers[0]['hostname'] . ' ' . str_replace('@', '.', Settings::Get('panel.adminmail')) . '. (' . "\n"; - } - - $zonefile.= ' ' . $bindserial . ' ; serial' . "\n" . ' 8H ; refresh' . "\n" . ' 2H ; retry' . "\n" . ' 1W ; expiry' . "\n" . ' 11h) ; minimum' . "\n"; - - // no nameservers given, use all of the A/AAAA entries - if (count($this->nameservers) == 0) { - $zonefile .= '@ IN NS ns' . "\n"; - foreach ($ip_a_records as $ip_a_record) { - $zonefile .= 'ns IN ' . $ip_a_record . "\n"; - } - } else { - foreach ($this->nameservers as $nameserver) { - $zonefile.= '@ IN NS ' . trim($nameserver['hostname']) . "\n"; + // iterate through all subdirs, look for zone files and delete them + foreach ($its as $it) { + if ($it->isFile()) { + // remove file + safe_exec('rm -f ' . escapeshellarg(makeCorrectFile($its->getPathname()))); } } - } else { - $zonefile = '$ORIGIN ' . $domain["domain"] . ".\n"; - } - - if ($domain['isemaildomain'] === '1') { - if (count($this->mxservers) == 0) { - $zonefile.= '@ IN MX 10 mail' . "\n"; - $records[] = 'mail'; - if ($domain['iswildcarddomain'] != '1') { - $records[] = 'imap'; - $records[] = 'smtp'; - $records[] = 'pop3'; - } - } else { - foreach ($this->mxservers as $mxserver) { - $zonefile.= '@ IN MX ' . trim($mxserver) . "\n"; - } - - if (Settings::Get('system.dns_createmailentry') == '1') { - $records[] = 'mail'; - if ($domain['iswildcarddomain'] != '1') { - $records[] = 'imap'; - $records[] = 'smtp'; - $records[] = 'pop3'; - } - } - } - - /* - * @TODO domain-based spf-settings - */ - if (Settings::Get('spf.use_spf') == '1' - /*&& $domain['spf'] == '1' */ - ) { - $zonefile.= Settings::Get('spf.spf_entry') . "\n"; - if (in_array('mail', $records)) { - $zonefile.= str_replace('@', 'mail', Settings::Get('spf.spf_entry')) . "\n"; - } - } - } - - /** - * generate dkim-zone-entries - */ - $zonefile.= $this->generateDkim($domain); - - if (!$froxlorhost) { - $nssubdomains_stmt = Database::prepare(" - SELECT `domain` FROM `" . TABLE_PANEL_DOMAINS . "` - WHERE `isbinddomain` = '1' AND `ismainbutsubto` = '0' AND `domain` LIKE :domain - "); - Database::pexecute($nssubdomains_stmt, array('domain' => '%.' . $domain['domain'])); - - while ($nssubdomain = $nssubdomains_stmt->fetch(PDO::FETCH_ASSOC)) { - - if (preg_match('/^[^\.]+\.' . preg_quote($domain['domain'], '/') . '/', $nssubdomain['domain'])) { - - $nssubdomain = str_replace('.' . $domain['domain'], '', $nssubdomain['domain']); - - if (count($this->nameservers) == 0) { - $zonefile.= $nssubdomain . ' IN NS ns.' . $nssubdomain . "\n"; - } else { - foreach ($this->nameservers as $nameserver) { - $zonefile.= $nssubdomain . ' IN NS ' . trim($nameserver['hostname']) . "\n"; - } - } - } - } - } - - $records[] = '@'; - - if ($domain['iswildcarddomain'] == '1') { - $records[] = '*'; - } else if ($domain['wwwserveralias'] == '1') { - $records[] = 'www'; - } - - if (!$froxlorhost) { - $subdomains_stmt = Database::prepare(" - SELECT `domain` FROM `".TABLE_PANEL_DOMAINS."` - WHERE `parentdomainid` = :domainid - "); - Database::pexecute($subdomains_stmt, array('domainid' => $domain['id'])); - - while ($subdomain = $subdomains_stmt->fetch(PDO::FETCH_ASSOC)) { - // Listing domains is enough as there currently is no support for choosing - // different ips for a subdomain => use same IPs as toplevel - $records[] = str_replace('.' . $domain['domain'], '', $subdomain['domain']); - - // Check whether to add a www.-prefix - if ($domain['wwwserveralias'] == '1') { - $records[] = 'www.'.str_replace('.' . $domain['domain'], '', $subdomain['domain']); - } - } - } - - // Create DNS-Records for every name we have saved - foreach ($records as $record) { - // we create an entry for every ip we have saved - foreach ($ip_a_records as $ip_a_record) { - $zonefile.= $record . "\tIN\t" . $ip_a_record . "\n"; - } - } - - return $zonefile; - } - - - private function generateDkim($domain) { - $zone_dkim = ''; - - if (Settings::Get('dkim.use_dkim') == '1' - && $domain['dkim'] == '1' - && $domain['dkim_pubkey'] != '') { - // start - $dkim_txt = 'v=DKIM1;'; - - // algorithm - $algorithm = explode(',', Settings::Get('dkim.dkim_algorithm')); - $alg = ''; - foreach ($algorithm as $a) { - if ($a == 'all') { - break; - } else { - $alg.=$a.':'; - } - } - - if ($alg != '') { - $alg = substr($alg, 0, -1); - $dkim_txt.= 'h='.$alg.';'; - } - - // notes - if (trim(Settings::Get('dkim.dkim_notes') != '')) { - $dkim_txt.= 'n='.trim(Settings::Get('dkim.dkim_notes')).';'; - } - - // key - $dkim_txt.= 'k=rsa;p='.trim(preg_replace('/-----BEGIN PUBLIC KEY-----(.+)-----END PUBLIC KEY-----/s', '$1', str_replace("\n", '', $domain['dkim_pubkey']))).';'; - - // service-type - if (Settings::Get('dkim.dkim_servicetype') == '1') { - $dkim_txt.= 's=email;'; - } - - // end-part - $dkim_txt.='t=s'; - - // split if necessary - $txt_record_split=''; - $lbr=50; - for ($pos=0; $pos<=strlen($dkim_txt)-1; $pos+=$lbr) { - $txt_record_split.= (($pos==0) ? '("' : "\t\t\t\t\t \"") . substr($dkim_txt, $pos, $lbr) . (($pos>=strlen($dkim_txt)-$lbr) ? '")' : '"' ) ."\n"; - } - - // dkim-entry - $zone_dkim .= 'dkim_' . $domain['dkim_id'] . '._domainkey IN TXT ' . $txt_record_split; - - // adsp-entry - if (Settings::Get('dkim.dkim_add_adsp') == "1") { - - $zone_dkim .= '_adsp._domainkey IN TXT "dkim='; - switch ((int)Settings::Get('dkim.dkim_add_adsppolicy')) { - case 0: - $zone_dkim .= 'unknown"'. "\n"; - break; - case 1: - $zone_dkim .= 'all"'. "\n"; - break; - case 2: - $zone_dkim .= 'discardable"'. "\n"; - break; - } - } - } - - return $zone_dkim; - } - - - public function writeDKIMconfigs() { - if (Settings::Get('dkim.use_dkim') == '1') { - if (!file_exists(makeCorrectDir(Settings::Get('dkim.dkim_prefix')))) { - $this->logger->logAction(CRON_ACTION, LOG_NOTICE, 'mkdir -p ' . escapeshellarg(makeCorrectDir(Settings::Get('dkim.dkim_prefix')))); - safe_exec('mkdir -p ' . escapeshellarg(makeCorrectDir(Settings::Get('dkim.dkim_prefix')))); - } - - $dkimdomains = ''; - $dkimkeys = ''; - $result_domains_stmt = Database::query(" - SELECT `id`, `domain`, `dkim`, `dkim_id`, `dkim_pubkey`, `dkim_privkey` - FROM `" . TABLE_PANEL_DOMAINS . "` WHERE `dkim` = '1' ORDER BY `id` ASC - "); - - while ($domain = $result_domains_stmt->fetch(PDO::FETCH_ASSOC)) { - - $privkey_filename = makeCorrectFile(Settings::Get('dkim.dkim_prefix') . '/dkim_' . $domain['dkim_id']); - $pubkey_filename = makeCorrectFile(Settings::Get('dkim.dkim_prefix') . '/dkim_' . $domain['dkim_id'] . '.public'); - - if ($domain['dkim_privkey'] == '' - || $domain['dkim_pubkey'] == '') { - $max_dkim_id_stmt = Database::query("SELECT MAX(`dkim_id`) as `max_dkim_id` FROM `" . TABLE_PANEL_DOMAINS . "`"); - $max_dkim_id = $max_dkim_id_stmt->fetch(PDO::FETCH_ASSOC); - $domain['dkim_id'] = (int)$max_dkim_id['max_dkim_id'] + 1; - $privkey_filename = makeCorrectFile(Settings::Get('dkim.dkim_prefix') . '/dkim_' . $domain['dkim_id']); - safe_exec('openssl genrsa -out ' . escapeshellarg($privkey_filename) . ' ' . Settings::Get('dkim.dkim_keylength')); - $domain['dkim_privkey'] = file_get_contents($privkey_filename); - safe_exec("chmod 0640 " . escapeshellarg($privkey_filename)); - $pubkey_filename = makeCorrectFile(Settings::Get('dkim.dkim_prefix') . '/dkim_' . $domain['dkim_id'] . '.public'); - safe_exec('openssl rsa -in ' . escapeshellarg($privkey_filename) . ' -pubout -outform pem -out ' . escapeshellarg($pubkey_filename)); - $domain['dkim_pubkey'] = file_get_contents($pubkey_filename); - safe_exec("chmod 0664 " . escapeshellarg($pubkey_filename)); - $upd_stmt = Database::prepare(" - UPDATE `" . TABLE_PANEL_DOMAINS . "` SET - `dkim_id` = :dkimid, - `dkim_privkey` = :privkey, - `dkim_pubkey` = :pubkey - WHERE `id` = :id - "); - $upd_data = array( - 'dkimid' => $domain['dkim_id'], - 'privkey' => $domain['dkim_privkey'], - 'pubkey' => $domain['dkim_pubkey'], - 'id' => $domain['id'] - ); - Database::pexecute($upd_stmt, $upd_data); - } - - if (!file_exists($privkey_filename) - && $domain['dkim_privkey'] != '') { - $privkey_file_handler = fopen($privkey_filename, "w"); - fwrite($privkey_file_handler, $domain['dkim_privkey']); - fclose($privkey_file_handler); - safe_exec("chmod 0640 " . escapeshellarg($privkey_filename)); - } - - if (!file_exists($pubkey_filename) - && $domain['dkim_pubkey'] != '') { - $pubkey_file_handler = fopen($pubkey_filename, "w"); - fwrite($pubkey_file_handler, $domain['dkim_pubkey']); - fclose($pubkey_file_handler); - safe_exec("chmod 0664 " . escapeshellarg($pubkey_filename)); - } - - $dkimdomains.= $domain['domain'] . "\n"; - $dkimkeys.= "*@" . $domain['domain'] . ":" . $domain['domain'] . ":" . $privkey_filename . "\n"; - } - - $dkimdomains_filename = makeCorrectFile(Settings::Get('dkim.dkim_prefix') . '/' . Settings::Get('dkim.dkim_domains')); - $dkimdomains_file_handler = fopen($dkimdomains_filename, "w"); - fwrite($dkimdomains_file_handler, $dkimdomains); - fclose($dkimdomains_file_handler); - $dkimkeys_filename = makeCorrectFile(Settings::Get('dkim.dkim_prefix') . '/' . Settings::Get('dkim.dkim_dkimkeys')); - $dkimkeys_file_handler = fopen($dkimkeys_filename, "w"); - fwrite($dkimkeys_file_handler, $dkimkeys); - fclose($dkimkeys_file_handler); - - safe_exec(escapeshellcmd(Settings::Get('dkim.dkimrestart_command'))); - $this->logger->logAction(CRON_ACTION, LOG_INFO, 'Dkim-milter reloaded'); } } } diff --git a/scripts/jobs/cron_tasks.inc.dns.15.bind.php b/scripts/jobs/cron_tasks.inc.dns.15.bind.php deleted file mode 100644 index 4c368be2..00000000 --- a/scripts/jobs/cron_tasks.inc.dns.15.bind.php +++ /dev/null @@ -1,134 +0,0 @@ - (2016-) - * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt - * @package Cron - * - */ -class bind2 extends DnsBase -{ - - public function writeConfigs() - { - // tell the world what we are doing - $this->_logger->logAction(CRON_ACTION, LOG_INFO, 'Task4 started - Rebuilding froxlor_bind.conf'); - - // clean up - $this->_cleanZonefiles(); - - // check for subfolder in bind-config-directory - if (! file_exists(makeCorrectDir(Settings::Get('system.bindconf_directory') . '/domains/'))) { - $this->_logger->logAction(CRON_ACTION, LOG_NOTICE, 'mkdir ' . escapeshellarg(makeCorrectDir(Settings::Get('system.bindconf_directory') . '/domains/'))); - safe_exec('mkdir -p ' . escapeshellarg(makeCorrectDir(Settings::Get('system.bindconf_directory') . '/domains/'))); - } - - $domains = $this->getDomainList(); - - if (! empty($domains)) { - $bindconf_file = '# ' . Settings::Get('system.bindconf_directory') . 'froxlor_bind.conf' . "\n" . '# Created ' . date('d.m.Y H:i') . "\n" . '# Do NOT manually edit this file, all changes will be deleted after the next domain change at the panel.' . "\n\n"; - - foreach ($domains as $domain) { - // check for system-hostname - $isFroxlorHostname = false; - if (isset($domain['froxlorhost']) && $domain['froxlorhost'] == 1) { - $isFroxlorHostname = true; - } - // create zone-file - $this->_logger->logAction(CRON_ACTION, LOG_DEBUG, 'Generating dns zone for ' . $domain['domain']); - $zone = createDomainZone($domain['id'], $isFroxlorHostname); - $zonefile = (string)$zone; - $domain['zonefile'] = 'domains/' . $domain['domain'] . '.zone'; - $zonefile_name = makeCorrectFile(Settings::Get('system.bindconf_directory') . '/' . $domain['zonefile']); - $zonefile_handler = fopen($zonefile_name, 'w'); - fwrite($zonefile_handler, $zonefile); - fclose($zonefile_handler); - $this->_logger->logAction(CRON_ACTION, LOG_INFO, '`' . $zonefile_name . '` zone written'); - - // generate config - $bindconf_file .= $this->_generateDomainConfig($domain); - } - - // write config - $bindconf_file_handler = fopen(makeCorrectFile(Settings::Get('system.bindconf_directory') . '/froxlor_bind.conf'), 'w'); - fwrite($bindconf_file_handler, $bindconf_file); - fclose($bindconf_file_handler); - $this->_logger->logAction(CRON_ACTION, LOG_INFO, 'froxlor_bind.conf written'); - - // reload Bind - safe_exec(escapeshellcmd(Settings::Get('system.bindreload_command'))); - $this->_logger->logAction(CRON_ACTION, LOG_INFO, 'Bind9 reloaded'); - } - } - - private function _generateDomainConfig($domain = array()) - { - $this->_logger->logAction(CRON_ACTION, LOG_DEBUG, 'Generating dns config for ' . $domain['domain']); - - $bindconf_file = '# Domain ID: ' . $domain['id'] . ' - CustomerID: ' . $domain['customerid'] . ' - CustomerLogin: ' . $domain['loginname'] . "\n"; - $bindconf_file .= 'zone "' . $domain['domain'] . '" in {' . "\n"; - $bindconf_file .= ' type master;' . "\n"; - $bindconf_file .= ' file "' . makeCorrectFile(Settings::Get('system.bindconf_directory') . '/' . $domain['zonefile']) . '";' . "\n"; - $bindconf_file .= ' allow-query { any; };' . "\n"; - - if (count($this->_ns) > 0 || count($this->_axfr) > 0) { - // open allow-transfer - $bindconf_file .= ' allow-transfer {' . "\n"; - // put nameservers in allow-transfer - if (count($this->_ns) > 0) { - foreach ($this->_ns as $ns) { - foreach ($ns["ips"] as $ip) { - $bindconf_file .= ' ' . $ip . ";\n"; - } - } - } - // AXFR server #100 - if (count($this->_axfr) > 0) { - foreach ($this->_axfr as $axfrserver) { - if (validate_ip($axfrserver, true) !== false) { - $bindconf_file .= ' ' . $axfrserver . ';' . "\n"; - } - } - } - // close allow-transfer - $bindconf_file .= ' };' . "\n"; - } - - $bindconf_file .= '};' . "\n"; - $bindconf_file .= "\n"; - - return $bindconf_file; - } - - private function _cleanZonefiles() - { - $config_dir = makeCorrectFile(Settings::Get('system.bindconf_directory') . '/domains/'); - - $this->_logger->logAction(CRON_ACTION, LOG_INFO, 'Cleaning dns zone files from ' . $config_dir); - - // check directory - if (@is_dir($config_dir)) { - - // create directory iterator - $its = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($config_dir)); - - // iterate through all subdirs, look for zone files and delete them - foreach ($its as $it) { - if ($it->isFile()) { - // remove file - safe_exec('rm -f ' . escapeshellarg(makeCorrectFile($its->getPathname()))); - } - } - } - } -} diff --git a/scripts/jobs/cron_tasks.inc.dns.20.pdns.php b/scripts/jobs/cron_tasks.inc.dns.20.pdns.php new file mode 100644 index 00000000..61b11ec1 --- /dev/null +++ b/scripts/jobs/cron_tasks.inc.dns.20.pdns.php @@ -0,0 +1,204 @@ + (2016-) + * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt + * @package Cron + * + */ +class pdns extends DnsBase +{ + + private $pdns_db = null; + + public function writeConfigs() + { + // tell the world what we are doing + $this->_logger->logAction(CRON_ACTION, LOG_INFO, 'Task4 started - Refreshing DNS database'); + + // connect to db + $this->_connectToPdnsDb(); + + // clean up + $this->_cleanZonefiles(); + + $domains = $this->getDomainList(); + + if (! empty($domains)) { + + foreach ($domains as $domain) { + // check for system-hostname + $isFroxlorHostname = false; + if (isset($domain['froxlorhost']) && $domain['froxlorhost'] == 1) { + $isFroxlorHostname = true; + } + // create zone-file + $this->_logger->logAction(CRON_ACTION, LOG_DEBUG, 'Generating dns zone for ' . $domain['domain']); + $zone = createDomainZone($domain['id'], $isFroxlorHostname); + + $dom_id = $this->_insertZone($zone->origin, $zone->serial); + $this->_insertRecords($dom_id, $zone->records); + $this->_insertAllowedTransfers($dom_id); + + $this->_logger->logAction(CRON_ACTION, LOG_INFO, '`' . $domain['domain'] . '` zone written'); + } + + $this->_logger->logAction(CRON_ACTION, LOG_INFO, 'Database updated'); + + // reload Bind + safe_exec(escapeshellcmd(Settings::Get('system.bindreload_command'))); + $this->_logger->logAction(CRON_ACTION, LOG_INFO, 'pdns reloaded'); + } + } + + private function _cleanZonefiles() + { + $this->_logger->logAction(CRON_ACTION, LOG_INFO, 'Cleaning dns zone entries from database'); + + $this->pdns_db->query("TRUNCATE TABLE `records`"); + $this->pdns_db->query("TRUNCATE TABLE `domains`"); + $this->pdns_db->query("TRUNCATE TABLE `domainmetadata`"); + } + + private function _insertZone($domainname, $serial = 0) + { + $ins_stmt = $this->pdns_db->prepare(" + INSERT INTO domains set `name` = :domainname, `notified_serial` = :serial, `type` = 'NATIVE' + "); + $ins_stmt->execute(array('domainname' => $domainname, 'serial' => $serial)); + $lastid = $this->pdns_db->lastInsertId(); + return $lastid; + } + + private function _insertRecords($domainid = 0, $records) + { + $ins_stmt = $this->pdns_db->prepare(" + INSERT INTO records set + `domain_id` = :did, + `name` = :rec, + `type` = :type, + `content` = :content, + `ttl` = :ttl, + `prio` = :prio, + `disabled` = '0' + "); + + foreach ($records as $record) + { + $ins_data = array( + 'did' => $domainid, + 'rec' => $record->record, + 'type' => $record->type, + 'content' => $record->content, + 'ttl' => $record->ttl, + 'prio' => $record->priority + ); + $ins_stmt->execute($ins_data); + } + } + + private function _insertAllowedTransfers($domainid) + { + $ins_stmt = $this->pdns_db->prepare(" + INSERT INTO domainmetadata set `domain_id` = :did, `kind` = 'ALLOW-AXFR-FROM', `content` = :value + "); + + $ins_data = array( + 'did' => $domainid + ); + + if (count($this->_ns) > 0 || count($this->_axfr) > 0) { + // put nameservers in allow-transfer + if (count($this->_ns) > 0) { + foreach ($this->_ns as $ns) { + foreach ($ns["ips"] as $ip) { + $ins_data['value'] = $ip; + $ins_stmt->execute($ins_data); + } + } + } + // AXFR server #100 + if (count($this->_axfr) > 0) { + foreach ($this->_axfr as $axfrserver) { + if (validate_ip($axfrserver, true) !== false) { + $ins_data['value'] = $axfrserver; + $ins_stmt->execute($ins_data); + } + } + } + } + } + + private function _connectToPdnsDb() + { + // get froxlor pdns config + $cf = Settings::Get('system.bindconf_directory').'/pdns_froxlor.conf'; + $config = makeCorrectFile($cf); + + if (!file_exists($config)) + { + $this->_logger->logAction(CRON_ACTION, LOG_ERROR, 'PowerDNS configuration file not found. Did you go through the configuration templates?'); + die('PowerDNS configuration file not found. Did you go through the configuration templates?'); + } + $lines = file($config); + $mysql_data = array(); + foreach ($lines as $line) + { + $line = trim($line); + if (strtolower(substr($line, 0, 6)) == 'gmysql') + { + $namevalue = explode("=", $line); + $mysql_data[$namevalue[0]] = $namevalue[1]; + } + } + + // build up connection string + $driver = 'mysql'; + $dsn = $driver.":"; + $options = array(PDO::MYSQL_ATTR_INIT_COMMAND => 'set names utf8'); + $attributes = array('ATTR_ERRMODE' => 'ERRMODE_EXCEPTION'); + $dbconf = array(); + + $dbconf["dsn"] = array( + 'dbname' => $mysql_data["gmysql-dbname"], + 'charset' => 'utf8' + ); + + if (isset($mysql_data['gmysql-socket']) && !empty($mysql_data['gmysql-socket'])) { + $dbconf["dsn"]['unix_socket'] = makeCorrectFile($mysql_data['gmysql-socket']); + } else { + $dbconf["dsn"]['host'] = $mysql_data['gmysql-host']; + $dbconf["dsn"]['port'] = $mysql_data['gmysql-port']; + } + + // add options to dsn-string + foreach ($dbconf["dsn"] as $k => $v) { + $dsn .= $k."=".$v.";"; + } + + // clean up + unset($dbconf); + + // try to connect + try { + $this->pdns_db = new PDO($dsn, $mysql_data['gmysql-user'], $mysql_data['gmysql-password'], $options); + } catch (PDOException $e) { + die($e->getMessage()); + } + + // set attributes + foreach ($attributes as $k => $v) { + $this->pdns_db->setAttribute(constant("PDO::".$k), constant("PDO::".$v)); + } + } +} diff --git a/scripts/jobs/cron_tasks.php b/scripts/jobs/cron_tasks.php index 1bf460be..e69a0192 100644 --- a/scripts/jobs/cron_tasks.php +++ b/scripts/jobs/cron_tasks.php @@ -181,14 +181,10 @@ while ($row = $result_tasks_stmt->fetch(PDO::FETCH_ASSOC)) { */ elseif ($row['type'] == '4' && (int)Settings::Get('system.bind_enable') != 0) { - $bindclass ="bind"; - - if (Settings::Get('system.dnsenabled') == '1') { - $bindclass = "bind2"; - } + $dnssrv = Settings::Get('system.dns_server'); if (!isset($nameserver)) { - $nameserver = new $bindclass($cronlog); + $nameserver = new $dnssrv($cronlog); } if (Settings::Get('dkim.use_dkim') == '1') { From f7441df89543c2d09b534ee4f42bccb34016ebf3 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 17 May 2016 08:43:02 +0200 Subject: [PATCH 0068/1335] add missing file inclusion Signed-off-by: Michael Kaufmann (d00p) --- scripts/jobs/cron_tasks.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/jobs/cron_tasks.php b/scripts/jobs/cron_tasks.php index e69a0192..dd7a754f 100644 --- a/scripts/jobs/cron_tasks.php +++ b/scripts/jobs/cron_tasks.php @@ -19,7 +19,7 @@ // necessary includes require_once makeCorrectFile(dirname(__FILE__) . '/cron_tasks.inc.dns.10.bind.php'); -require_once makeCorrectFile(dirname(__FILE__) . '/cron_tasks.inc.dns.15.bind.php'); +require_once makeCorrectFile(dirname(__FILE__) . '/cron_tasks.inc.dns.20.pdns.php'); require_once makeCorrectFile(dirname(__FILE__) . '/cron_tasks.inc.http.10.apache.php'); require_once makeCorrectFile(dirname(__FILE__) . '/cron_tasks.inc.http.15.apache_fcgid.php'); require_once makeCorrectFile(dirname(__FILE__) . '/cron_tasks.inc.http.20.lighttpd.php'); From 1ce5cf6c0010d22bb02f40d014eb9688bc2a9875 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 17 May 2016 09:12:39 +0200 Subject: [PATCH 0069/1335] various fixes for dns with froxlor-hostname Signed-off-by: Michael Kaufmann (d00p) --- .../dns/function.createDomainZone.php | 107 ++++++++++-------- scripts/classes/class.DnsBase.php | 8 +- scripts/jobs/cron_tasks.inc.dns.10.bind.php | 2 +- scripts/jobs/cron_tasks.inc.dns.20.pdns.php | 2 +- 4 files changed, 68 insertions(+), 51 deletions(-) diff --git a/lib/functions/dns/function.createDomainZone.php b/lib/functions/dns/function.createDomainZone.php index 6d271c02..8a42efb8 100644 --- a/lib/functions/dns/function.createDomainZone.php +++ b/lib/functions/dns/function.createDomainZone.php @@ -16,22 +16,33 @@ */ function createDomainZone($domain_id, $froxlorhostname = false) { - // get domain-name - $dom_stmt = Database::prepare("SELECT * FROM `" . TABLE_PANEL_DOMAINS . "` WHERE id = :did"); - $domain = Database::pexecute_first($dom_stmt, array( - 'did' => $domain_id - )); + if (!$froxlorhostname) + { + // get domain-name + $dom_stmt = Database::prepare("SELECT * FROM `" . TABLE_PANEL_DOMAINS . "` WHERE id = :did"); + $domain = Database::pexecute_first($dom_stmt, array( + 'did' => $domain_id + )); + } + else + { + $domain = $domain_id; + } if ($domain['isbinddomain'] != '1') { return; } - // select all entries - $sel_stmt = Database::prepare("SELECT * FROM `" . TABLE_DOMAIN_DNS . "` WHERE domain_id = :did ORDER BY id ASC"); - Database::pexecute($sel_stmt, array( - 'did' => $domain_id - )); - $dom_entries = $sel_stmt->fetchAll(PDO::FETCH_ASSOC); + $dom_entries = array(); + if (!$froxlorhostname) + { + // select all entries + $sel_stmt = Database::prepare("SELECT * FROM `" . TABLE_DOMAIN_DNS . "` WHERE domain_id = :did ORDER BY id ASC"); + Database::pexecute($sel_stmt, array( + 'did' => $domain_id + )); + $dom_entries = $sel_stmt->fetchAll(PDO::FETCH_ASSOC); + } // check for required records $required_entries = array(); @@ -52,43 +63,46 @@ function createDomainZone($domain_id, $froxlorhostname = false) addRequiredEntry('www', 'AAAA', $required_entries); } - // additional required records for subdomains - $subdomains_stmt = Database::prepare(" - SELECT `domain` FROM `" . TABLE_PANEL_DOMAINS . "` - WHERE `parentdomainid` = :domainid - "); - Database::pexecute($subdomains_stmt, array( - 'domainid' => $domain_id - )); + if (!$froxlorhostname) + { + // additional required records for subdomains + $subdomains_stmt = Database::prepare(" + SELECT `domain` FROM `" . TABLE_PANEL_DOMAINS . "` + WHERE `parentdomainid` = :domainid + "); + Database::pexecute($subdomains_stmt, array( + 'domainid' => $domain_id + )); - while ($subdomain = $subdomains_stmt->fetch(PDO::FETCH_ASSOC)) { - // Listing domains is enough as there currently is no support for choosing - // different ips for a subdomain => use same IPs as toplevel - addRequiredEntry(str_replace('.' . $domain['domain'], '', $subdomain['domain']), 'A', $required_entries); - addRequiredEntry(str_replace('.' . $domain['domain'], '', $subdomain['domain']), 'AAAA', $required_entries); + while ($subdomain = $subdomains_stmt->fetch(PDO::FETCH_ASSOC)) { + // Listing domains is enough as there currently is no support for choosing + // different ips for a subdomain => use same IPs as toplevel + addRequiredEntry(str_replace('.' . $domain['domain'], '', $subdomain['domain']), 'A', $required_entries); + addRequiredEntry(str_replace('.' . $domain['domain'], '', $subdomain['domain']), 'AAAA', $required_entries); - // Check whether to add a www.-prefix - if ($domain['iswildcarddomain'] == '1') { - addRequiredEntry('*.' . str_replace('.' . $domain['domain'], '', $subdomain['domain']), 'A', $required_entries); - addRequiredEntry('*.' . str_replace('.' . $domain['domain'], '', $subdomain['domain']), 'AAAA', $required_entries); - } elseif ($domain['wwwserveralias'] == '1') { - addRequiredEntry('www.' . str_replace('.' . $domain['domain'], '', $subdomain['domain']), 'A', $required_entries); - addRequiredEntry('www.' . str_replace('.' . $domain['domain'], '', $subdomain['domain']), 'AAAA', $required_entries); + // Check whether to add a www.-prefix + if ($domain['iswildcarddomain'] == '1') { + addRequiredEntry('*.' . str_replace('.' . $domain['domain'], '', $subdomain['domain']), 'A', $required_entries); + addRequiredEntry('*.' . str_replace('.' . $domain['domain'], '', $subdomain['domain']), 'AAAA', $required_entries); + } elseif ($domain['wwwserveralias'] == '1') { + addRequiredEntry('www.' . str_replace('.' . $domain['domain'], '', $subdomain['domain']), 'A', $required_entries); + addRequiredEntry('www.' . str_replace('.' . $domain['domain'], '', $subdomain['domain']), 'AAAA', $required_entries); + } } - } - // additional required records for main-but-subdomain-to - $mainbutsub_stmt = Database::prepare(" - SELECT `domain` FROM `" . TABLE_PANEL_DOMAINS . "` - WHERE `ismainbutsubto` = :domainid - "); - Database::pexecute($mainbutsub_stmt, array( - 'domainid' => $domain_id - )); + // additional required records for main-but-subdomain-to + $mainbutsub_stmt = Database::prepare(" + SELECT `domain` FROM `" . TABLE_PANEL_DOMAINS . "` + WHERE `ismainbutsubto` = :domainid + "); + Database::pexecute($mainbutsub_stmt, array( + 'domainid' => $domain_id + )); - while ($mainbutsubtodomain = $mainbutsub_stmt->fetch(PDO::FETCH_ASSOC)) { - // Add NS entry for subdomain-records of "main-but-subdomain-to"-domains, they get their own Zone - addRequiredEntry(str_replace('.' . $domain['domain'], '', $mainbutsubtodomain['domain']), 'NS', $required_entries); + while ($mainbutsubtodomain = $mainbutsub_stmt->fetch(PDO::FETCH_ASSOC)) { + // Add NS entry for subdomain-records of "main-but-subdomain-to"-domains, they get their own Zone + addRequiredEntry(str_replace('.' . $domain['domain'], '', $mainbutsubtodomain['domain']), 'NS', $required_entries); + } } // additional required records for SPF and DKIM if activated @@ -136,6 +150,7 @@ function createDomainZone($domain_id, $froxlorhostname = false) $result_ip_stmt = Database::prepare(" SELECT `ip` FROM `" . TABLE_PANEL_IPSANDPORTS . "` GROUP BY `ip` "); + Database::pexecute($result_ip_stmt); } else { $result_ip_stmt = Database::prepare(" SELECT `p`.`ip` AS `ip` @@ -143,10 +158,10 @@ function createDomainZone($domain_id, $froxlorhostname = false) WHERE `di`.`id_domain` = :domainid AND `p`.`id` = `di`.`id_ipandports` GROUP BY `p`.`ip`; "); + Database::pexecute($result_ip_stmt, array( + 'domainid' => $domain_id + )); } - Database::pexecute($result_ip_stmt, array( - 'domainid' => $domain_id - )); $all_ips = $result_ip_stmt->fetchAll(PDO::FETCH_ASSOC); foreach ($all_ips as $ip) { diff --git a/scripts/classes/class.DnsBase.php b/scripts/classes/class.DnsBase.php index 11e06996..ba7a8506 100644 --- a/scripts/classes/class.DnsBase.php +++ b/scripts/classes/class.DnsBase.php @@ -1,11 +1,13 @@ 'none', 'domain' => Settings::Get('system.hostname'), + 'isbinddomain' => '1', 'isemaildomain' => Settings::Get('system.dns_createmailentry'), 'customerid' => 'none', 'loginname' => 'froxlor.panel', @@ -181,5 +184,4 @@ abstract class DnsBase { $this->_logger->logAction(CRON_ACTION, LOG_INFO, 'Dkim-milter reloaded'); } } - -} \ No newline at end of file +} diff --git a/scripts/jobs/cron_tasks.inc.dns.10.bind.php b/scripts/jobs/cron_tasks.inc.dns.10.bind.php index 3e9cff1d..89120e98 100644 --- a/scripts/jobs/cron_tasks.inc.dns.10.bind.php +++ b/scripts/jobs/cron_tasks.inc.dns.10.bind.php @@ -46,7 +46,7 @@ class bind extends DnsBase } // create zone-file $this->_logger->logAction(CRON_ACTION, LOG_DEBUG, 'Generating dns zone for ' . $domain['domain']); - $zone = createDomainZone($domain['id'], $isFroxlorHostname); + $zone = createDomainZone(($domain['id'] == 'none') ? $domain : $domain['id'], $isFroxlorHostname); $zonefile = (string)$zone; $domain['zonefile'] = 'domains/' . $domain['domain'] . '.zone'; $zonefile_name = makeCorrectFile(Settings::Get('system.bindconf_directory') . '/' . $domain['zonefile']); diff --git a/scripts/jobs/cron_tasks.inc.dns.20.pdns.php b/scripts/jobs/cron_tasks.inc.dns.20.pdns.php index 61b11ec1..8352c095 100644 --- a/scripts/jobs/cron_tasks.inc.dns.20.pdns.php +++ b/scripts/jobs/cron_tasks.inc.dns.20.pdns.php @@ -44,7 +44,7 @@ class pdns extends DnsBase } // create zone-file $this->_logger->logAction(CRON_ACTION, LOG_DEBUG, 'Generating dns zone for ' . $domain['domain']); - $zone = createDomainZone($domain['id'], $isFroxlorHostname); + $zone = createDomainZone(($domain['id'] == 'none') ? $domain : $domain['id'], $isFroxlorHostname); $dom_id = $this->_insertZone($zone->origin, $zone->serial); $this->_insertRecords($dom_id, $zone->records); From 41e4135f71d21075e015408aa83e1b5dde4434f7 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Wed, 18 May 2016 09:03:14 +0200 Subject: [PATCH 0070/1335] do not allow punycode input in domain-names Signed-off-by: Michael Kaufmann (d00p) --- admin_domains.php | 19 +++++++++++++------ install/froxlor.sql | 2 +- lng/english.lng.php | 2 ++ lng/german.lng.php | 2 ++ 4 files changed, 18 insertions(+), 7 deletions(-) diff --git a/admin_domains.php b/admin_domains.php index 5fbabda4..26dcf406 100644 --- a/admin_domains.php +++ b/admin_domains.php @@ -282,10 +282,23 @@ if ($page == 'domains' || $page == 'overview') { standard_error('admin_domain_emailsystemhostname'); } + if (strpos($_POST['domain'], '--') !== false) { + standard_error('domain_nopunycode'); + } + $domain = $idna_convert->encode(preg_replace(array( '/\:(\d)+$/', '/^https?\:\/\//' ), '', validate($_POST['domain'], 'domain'))); + + // Check whether domain validation is enabled and if, validate the domain + if (Settings::Get('system.validate_domain') && ! validateDomain($domain)) { + standard_error(array( + 'stringiswrong', + 'mydomain' + )); + } + $subcanemaildomain = intval($_POST['subcanemaildomain']); $isemaildomain = 0; @@ -679,12 +692,6 @@ if ($page == 'domains' || $page == 'overview') { 'stringisempty', 'mydomain' )); - } // Check whether domain validation is enabled and if, validate the domain -elseif (Settings::Get('system.validate_domain') && ! validateDomain($domain)) { - standard_error(array( - 'stringiswrong', - 'mydomain' - )); } elseif ($documentroot == '') { standard_error(array( 'stringisempty', diff --git a/install/froxlor.sql b/install/froxlor.sql index 178b9135..38747109 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -556,7 +556,7 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES ('panel', 'password_numeric', '0'), ('panel', 'password_special_char_required', '0'), ('panel', 'password_special_char', '!?<>§$%+#=@'), - ('panel', 'version', '0.9.35.1'), + ('panel', 'version', '0.9.36'), ('panel', 'db_version', '201604270'); diff --git a/lng/english.lng.php b/lng/english.lng.php index 2db8a344..9b8f4b1c 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -1988,3 +1988,5 @@ $lng['serversettings']['backupenabled']['description'] = "If activated, the cust $lng['extras']['path_protection_label'] = 'Important'; $lng['extras']['path_protection_info'] = 'We strongly recommend protecting the given path, see "Extras" -> "Directory protection"'; $lng['tasks']['backup_customerfiles'] = 'Backup job for customer %loginname%'; + +$lng['error']['domain_nopunycode'] = 'You must not specify punycode (IDNA). The domain will automatically be converted'; diff --git a/lng/german.lng.php b/lng/german.lng.php index ec8bc35f..956afd80 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -1641,3 +1641,5 @@ $lng['serversettings']['backupenabled']['description'] = "Wenn dies aktiviert is $lng['extras']['path_protection_label'] = 'Wichtig'; $lng['extras']['path_protection_info'] = 'Wir raten dringend dazu den angegebenen Pfad zu schützen, siehe "Extras" -> "Verzeichnisschutz"'; $lng['tasks']['backup_customerfiles'] = 'Datensicherung für Kunde %loginname%'; + +$lng['error']['domain_nopunycode'] = 'Die Eingabe von Punycode (IDNA) ist nicht notwendig. Die Domain wird automatisch konvertiert.'; From eb70e619c9dbffc6d837fc80f169e930ead57464 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Wed, 18 May 2016 10:28:13 +0200 Subject: [PATCH 0071/1335] also check for punycode when customer adds a subdomain Signed-off-by: Michael Kaufmann (d00p) --- customer_domains.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/customer_domains.php b/customer_domains.php index d7bdbc9b..1464d512 100644 --- a/customer_domains.php +++ b/customer_domains.php @@ -247,6 +247,11 @@ if ($page == 'overview') { } elseif ($action == 'add') { if ($userinfo['subdomains_used'] < $userinfo['subdomains'] || $userinfo['subdomains'] == '-1') { if (isset($_POST['send']) && $_POST['send'] == 'send') { + + if (strpos($_POST['subdomain'], '--') !== false) { + standard_error('domain_nopunycode'); + } + $subdomain = $idna_convert->encode(preg_replace(array('/\:(\d)+$/', '/^https?\:\/\//'), '', validate($_POST['subdomain'], 'subdomain', '', 'subdomainiswrong'))); $domain = $idna_convert->encode($_POST['domain']); $domain_stmt = Database::prepare("SELECT * FROM `" . TABLE_PANEL_DOMAINS . "` From 6ea4655fd8ed5d03a83d10904f39202582435118 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Wed, 18 May 2016 10:32:52 +0200 Subject: [PATCH 0072/1335] set version to 0.9.36 for upcoming release Signed-off-by: Michael Kaufmann (d00p) --- install/updates/froxlor/0.9/update_0.9.inc.php | 8 ++++++++ lib/version.inc.php | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index 5a096f90..5b8c4851 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -3312,3 +3312,11 @@ if (isFroxlorVersion('0.9.35.1') && isDatabaseVersion('201603150')) { updateToDbVersion('201604270'); } + +if (isFroxlorVersion('0.9.35.1')) { + + showUpdateStep("Updating from 0.9.35.1 to 0.9.36 final"); + lastStepStatus(0); + + updateToVersion('0.9.36'); +} diff --git a/lib/version.inc.php b/lib/version.inc.php index 2d17e6d1..655221e6 100644 --- a/lib/version.inc.php +++ b/lib/version.inc.php @@ -16,7 +16,7 @@ */ // Main version variable -$version = '0.9.35.1'; +$version = '0.9.36'; // Database version (YYYYMMDDC where C is a daily counter) $dbversion = '201604270'; From b14ab6b1c1dcb4b3ca2995a498600d81527a04f6 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Wed, 18 May 2016 10:35:47 +0200 Subject: [PATCH 0073/1335] validate record/label in dns-editor; better escaping for soa-admin mail Signed-off-by: Michael Kaufmann (d00p) --- dns_editor.php | 19 ++++++++++++++++++- .../dns/function.createDomainZone.php | 9 ++++++++- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/dns_editor.php b/dns_editor.php index bc9726a3..6dd50fb7 100644 --- a/dns_editor.php +++ b/dns_editor.php @@ -51,7 +51,24 @@ if ($action == 'add_record' && ! empty($_POST)) { $record = strtolower($record); - // TODO regex validate record and content for invalid characters + if ($record != '@' && $record != '*') + { + // validate record + if (strpos($record, '--') !== false) { + $errors[] = $lng['error']['domain_nopunycode']; + } + else + { + $record = $idna_convert->encode($record); + $check_dom = $record.'.example.com'; + if (!validateDomain($check_dom)) + { + $errors[] = sprintf($lng['error']['subdomainiswrong'], $idna_convert->decode($record)); + } + } + } + + // TODO regex validate content for invalid characters if ($ttl <= 0) { $ttl = 18000; diff --git a/lib/functions/dns/function.createDomainZone.php b/lib/functions/dns/function.createDomainZone.php index 8a42efb8..f7baed6e 100644 --- a/lib/functions/dns/function.createDomainZone.php +++ b/lib/functions/dns/function.createDomainZone.php @@ -266,7 +266,7 @@ function createDomainZone($domain_id, $froxlorhostname = false) } // TODO for now, dummy time-periods - $soa_content = $primary_ns . " " . str_replace('@', '.', Settings::Get('panel.adminmail')) . ". (" . PHP_EOL; + $soa_content = $primary_ns . " " . escapeSoaAdminMail(Settings::Get('panel.adminmail')) . " (" . PHP_EOL; $soa_content .= $domain['bindserial'] . "\t; serial" . PHP_EOL; $soa_content .= "1800\t; refresh (30 mins)" . PHP_EOL; $soa_content .= "900\t; retry (15 mins)" . PHP_EOL; @@ -302,3 +302,10 @@ function encloseTXTContent($txt_content, $isMultiLine = false) } return $txt_content; } + +function escapeSoaAdminMail($email) +{ + $mail_parts = explode("@", $email); + $escpd_mail = str_replace(".", "\.", $mail_parts[0]).".".$mail_parts[1]."."; + return $escpd_mail; +} From 9d16790f5b67d9975f4e3039189036493c7c2f1d Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Wed, 18 May 2016 10:43:16 +0200 Subject: [PATCH 0074/1335] Update phpMailer to 5.2.15 Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/phpmailer/class.PHPMailer.php | 85 ++++++++++++++++------- lib/classes/phpmailer/class.SMTP.php | 19 +++-- 2 files changed, 73 insertions(+), 31 deletions(-) diff --git a/lib/classes/phpmailer/class.PHPMailer.php b/lib/classes/phpmailer/class.PHPMailer.php index e4dd00bf..89d97316 100644 --- a/lib/classes/phpmailer/class.PHPMailer.php +++ b/lib/classes/phpmailer/class.PHPMailer.php @@ -31,7 +31,7 @@ class PHPMailer * The PHPMailer Version number. * @var string */ - public $Version = '5.2.14'; + public $Version = '5.2.15'; /** * Email priority. @@ -352,6 +352,7 @@ class PHPMailer /** * Whether to split multiple to addresses into multiple messages * or send them all in one message. + * Only supported in `mail` and `sendmail` transports, not in SMTP. * @var boolean */ public $SingleTo = false; @@ -446,6 +447,15 @@ class PHPMailer */ public $XMailer = ''; + /** + * Which validator to use by default when validating email addresses. + * May be a callable to inject your own validator, but there are several built-in validators. + * @see PHPMailer::validateAddress() + * @var string|callable + * @static + */ + public static $validator = 'auto'; + /** * An instance of the SMTP sender class. * @var SMTP @@ -634,9 +644,11 @@ class PHPMailer * Constructor. * @param boolean $exceptions Should we throw external exceptions? */ - public function __construct($exceptions = false) + public function __construct($exceptions = null) { - $this->exceptions = (boolean)$exceptions; + if ($exceptions !== null) { + $this->exceptions = (boolean)$exceptions; + } } /** @@ -645,9 +657,7 @@ class PHPMailer public function __destruct() { //Close any open SMTP connection nicely - if ($this->Mailer == 'smtp') { - $this->smtpClose(); - } + $this->smtpClose(); } /** @@ -713,7 +723,7 @@ class PHPMailer case 'echo': default: //Normalize line breaks - $str = preg_replace('/(\r\n|\r|\n)/ms', "\n", $str); + $str = preg_replace('/\r\n?/ms', "\n", $str); echo gmdate('Y-m-d H:i:s') . "\t" . str_replace( "\n", "\n \t ", @@ -850,7 +860,7 @@ class PHPMailer $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim if (($pos = strrpos($address, '@')) === false) { // At-sign is misssing. - $error_message = $this->lang('invalid_address') . $address; + $error_message = $this->lang('invalid_address') . " (addAnAddress $kind): $address"; $this->setError($error_message); $this->edebug($error_message); if ($this->exceptions) { @@ -900,7 +910,7 @@ class PHPMailer return false; } if (!$this->validateAddress($address)) { - $error_message = $this->lang('invalid_address') . $address; + $error_message = $this->lang('invalid_address') . " (addAnAddress $kind): $address"; $this->setError($error_message); $this->edebug($error_message); if ($this->exceptions) { @@ -994,7 +1004,7 @@ class PHPMailer if (($pos = strrpos($address, '@')) === false or (!$this->has8bitChars(substr($address, ++$pos)) or !$this->idnSupported()) and !$this->validateAddress($address)) { - $error_message = $this->lang('invalid_address') . $address; + $error_message = $this->lang('invalid_address') . " (setFrom) $address"; $this->setError($error_message); $this->edebug($error_message); if ($this->exceptions) { @@ -1027,19 +1037,30 @@ class PHPMailer /** * Check that a string looks like an email address. * @param string $address The email address to check - * @param string $patternselect A selector for the validation pattern to use : + * @param string|callable $patternselect A selector for the validation pattern to use : * * `auto` Pick best pattern automatically; * * `pcre8` Use the squiloople.com pattern, requires PCRE > 8.0, PHP >= 5.3.2, 5.2.14; * * `pcre` Use old PCRE implementation; * * `php` Use PHP built-in FILTER_VALIDATE_EMAIL; * * `html5` Use the pattern given by the HTML5 spec for 'email' type form input elements. * * `noregex` Don't use a regex: super fast, really dumb. + * Alternatively you may pass in a callable to inject your own validator, for example: + * PHPMailer::validateAddress('user@example.com', function($address) { + * return (strpos($address, '@') !== false); + * }); + * You can also set the PHPMailer::$validator static to a callable, allowing built-in methods to use your validator. * @return boolean * @static * @access public */ - public static function validateAddress($address, $patternselect = 'auto') + public static function validateAddress($address, $patternselect = null) { + if (is_null($patternselect)) { + $patternselect = self::$validator; + } + if (is_callable($patternselect)) { + return call_user_func($patternselect, $address); + } //Reject line breaks in addresses; it's valid RFC5322, but not RFC5321 if (strpos($address, "\n") !== false or strpos($address, "\r") !== false) { return false; @@ -1216,7 +1237,7 @@ class PHPMailer } $this->$address_kind = $this->punyencodeAddress($this->$address_kind); if (!$this->validateAddress($this->$address_kind)) { - $error_message = $this->lang('invalid_address') . $this->$address_kind; + $error_message = $this->lang('invalid_address') . ' (punyEncode) ' . $this->$address_kind; $this->setError($error_message); $this->edebug($error_message); if ($this->exceptions) { @@ -1227,7 +1248,7 @@ class PHPMailer } // Set whether the message is multipart/alternative - if (!empty($this->AltBody)) { + if ($this->alternativeExists()) { $this->ContentType = 'multipart/alternative'; } @@ -1634,7 +1655,7 @@ class PHPMailer */ public function smtpClose() { - if ($this->smtp !== null) { + if (is_a($this->smtp, 'SMTP')) { if ($this->smtp->connected()) { $this->smtp->quit(); $this->smtp->close(); @@ -1972,7 +1993,7 @@ class PHPMailer $result .= $this->headerLine('Subject', $this->encodeHeader($this->secureHeader($this->Subject))); } - if ($this->MessageID != '') { + if ('' != $this->MessageID and preg_match('/^<.*@.*>$/', $this->MessageID)) { $this->lastMessageID = $this->MessageID; } else { $this->lastMessageID = sprintf('<%s@%s>', $this->uniqueid, $this->serverHostname()); @@ -2074,7 +2095,7 @@ class PHPMailer */ public function getSentMIMEMessage() { - return $this->MIMEHeader . $this->mailHeader . self::CRLF . $this->MIMEBody; + return rtrim($this->MIMEHeader . $this->mailHeader, "\n\r") . self::CRLF . self::CRLF . $this->MIMEBody; } /** @@ -2120,8 +2141,9 @@ class PHPMailer $altBodyEncoding = '7bit'; $altBodyCharSet = 'us-ascii'; } - //If lines are too long, change to quoted-printable transfer encoding - if (self::hasLineLongerThanMax($this->AltBody)) { + //If lines are too long, and we're not already using an encoding that will shorten them, + //change to quoted-printable transfer encoding + if ('base64' != $altBodyEncoding and self::hasLineLongerThanMax($this->AltBody)) { $altBodyEncoding = 'quoted-printable'; } //Use this as a preamble in all multipart message types @@ -3296,7 +3318,7 @@ class PHPMailer $message ); } - } elseif (substr($url, 0, 4) !== 'cid:' && !preg_match('#^[A-z]+://#', $url)) { + } elseif (substr($url, 0, 4) !== 'cid:' && !preg_match('#^[a-z][a-z0-9+.-]*://#i', $url)) { // Do not change urls for absolute images (thanks to corvuscorax) // Do not change urls that are already inline images $filename = basename($url); @@ -3332,7 +3354,7 @@ class PHPMailer // Convert all message body line breaks to CRLF, makes quoted-printable encoding work much better $this->Body = $this->normalizeBreaks($message); $this->AltBody = $this->normalizeBreaks($this->html2text($message, $advanced)); - if (empty($this->AltBody)) { + if (!$this->alternativeExists()) { $this->AltBody = 'To view this email message, open it in a program that understands HTML!' . self::CRLF . self::CRLF; } @@ -3657,11 +3679,13 @@ class PHPMailer if ($this->DKIM_passphrase != '') { $privKey = openssl_pkey_get_private($privKeyStr, $this->DKIM_passphrase); } else { - $privKey = $privKeyStr; + $privKey = openssl_pkey_get_private($privKeyStr); } - if (openssl_sign($signHeader, $signature, $privKey)) { + if (openssl_sign($signHeader, $signature, $privKey, 'sha256WithRSAEncryption')) { //sha1WithRSAEncryption + openssl_pkey_free($privKey); return base64_encode($signature); } + openssl_pkey_free($privKey); return ''; } @@ -3678,7 +3702,7 @@ class PHPMailer foreach ($lines as $key => $line) { list($heading, $value) = explode(':', $line, 2); $heading = strtolower($heading); - $value = preg_replace('/\s+/', ' ', $value); // Compress useless spaces + $value = preg_replace('/\s{2,}/', ' ', $value); // Compress useless spaces $lines[$key] = $heading . ':' . trim($value); // Don't forget to remove WSP around the value } $signHeader = implode("\r\n", $lines); @@ -3716,7 +3740,7 @@ class PHPMailer */ public function DKIM_Add($headers_line, $subject, $body) { - $DKIMsignatureType = 'rsa-sha1'; // Signature & hash algorithms + $DKIMsignatureType = 'rsa-sha256'; // Signature & hash algorithms $DKIMcanonicalization = 'relaxed/simple'; // Canonicalization of header/body $DKIMquery = 'dns/txt'; // Query method $DKIMtime = time(); // Signature Timestamp = seconds since 00:00:00 - Jan 1, 1970 (UTC time zone) @@ -3724,6 +3748,7 @@ class PHPMailer $headers = explode($this->LE, $headers_line); $from_header = ''; $to_header = ''; + $date_header = ''; $current = ''; foreach ($headers as $header) { if (strpos($header, 'From:') === 0) { @@ -3732,6 +3757,9 @@ class PHPMailer } elseif (strpos($header, 'To:') === 0) { $to_header = $header; $current = 'to_header'; + } elseif (strpos($header, 'Date:') === 0) { + $date_header = $header; + $current = 'date_header'; } else { if (!empty($$current) && strpos($header, ' =?') === 0) { $$current .= $header; @@ -3742,6 +3770,7 @@ class PHPMailer } $from = str_replace('|', '=7C', $this->DKIM_QP($from_header)); $to = str_replace('|', '=7C', $this->DKIM_QP($to_header)); + $date = str_replace('|', '=7C', $this->DKIM_QP($date_header)); $subject = str_replace( '|', '=7C', @@ -3749,7 +3778,7 @@ class PHPMailer ); // Copied header fields (dkim-quoted-printable) $body = $this->DKIM_BodyC($body); $DKIMlen = strlen($body); // Length of body - $DKIMb64 = base64_encode(pack('H*', sha1($body))); // Base64 of packed binary SHA-1 hash of body + $DKIMb64 = base64_encode(pack('H*', hash('sha256', $body))); // Base64 of packed binary SHA-256 hash of body if ('' == $this->DKIM_identity) { $ident = ''; } else { @@ -3762,16 +3791,18 @@ class PHPMailer $this->DKIM_selector . ";\r\n" . "\tt=" . $DKIMtime . '; c=' . $DKIMcanonicalization . ";\r\n" . - "\th=From:To:Subject;\r\n" . + "\th=From:To:Date:Subject;\r\n" . "\td=" . $this->DKIM_domain . ';' . $ident . "\r\n" . "\tz=$from\r\n" . "\t|$to\r\n" . + "\t|$date\r\n" . "\t|$subject;\r\n" . "\tbh=" . $DKIMb64 . ";\r\n" . "\tb="; $toSign = $this->DKIM_HeaderC( $from_header . "\r\n" . $to_header . "\r\n" . + $date_header . "\r\n" . $subject_header . "\r\n" . $dkimhdrs ); diff --git a/lib/classes/phpmailer/class.SMTP.php b/lib/classes/phpmailer/class.SMTP.php index 2e32e2fc..039f8beb 100644 --- a/lib/classes/phpmailer/class.SMTP.php +++ b/lib/classes/phpmailer/class.SMTP.php @@ -30,7 +30,7 @@ class SMTP * The PHPMailer SMTP version number. * @var string */ - const VERSION = '5.2.14'; + const VERSION = '5.2.15'; /** * SMTP line break constant. @@ -81,7 +81,7 @@ class SMTP * @deprecated Use the `VERSION` constant instead * @see SMTP::VERSION */ - public $Version = '5.2.14'; + public $Version = '5.2.15'; /** * SMTP server port number. @@ -336,11 +336,22 @@ class SMTP if (!$this->sendCommand('STARTTLS', 'STARTTLS', 220)) { return false; } + + //Allow the best TLS version(s) we can + $crypto_method = STREAM_CRYPTO_METHOD_TLS_CLIENT; + + //PHP 5.6.7 dropped inclusion of TLS 1.1 and 1.2 in STREAM_CRYPTO_METHOD_TLS_CLIENT + //so add them back in manually if we can + if (defined('STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT')) { + $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT; + $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT; + } + // Begin encrypted connection if (!stream_socket_enable_crypto( $this->smtp_conn, true, - STREAM_CRYPTO_METHOD_TLS_CLIENT + $crypto_method )) { return false; } @@ -736,7 +747,7 @@ class SMTP protected function parseHelloFields($type) { $this->server_caps = array(); - $lines = explode("\n", $this->last_reply); + $lines = explode("\n", $this->helo_rply); foreach ($lines as $n => $s) { //First 4 chars contain response code followed by - or space From d97957e5586941b585358f03664b7bc561aa9e3c Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Wed, 18 May 2016 12:12:34 +0200 Subject: [PATCH 0075/1335] array short-syntax is only PHP >= 5.4 Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/integrity/class.IntegrityCheck.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/classes/integrity/class.IntegrityCheck.php b/lib/classes/integrity/class.IntegrityCheck.php index c7fea0f0..bae1c2b3 100644 --- a/lib/classes/integrity/class.IntegrityCheck.php +++ b/lib/classes/integrity/class.IntegrityCheck.php @@ -132,7 +132,7 @@ class IntegrityCheck { // Admin uses default-IP $admips[$row['adminid']] = explode(',', Settings::Get('system.defaultip')); } else { - $admips[$row['adminid']] = [ $row['ip'] ]; + $admips[$row['adminid']] = array($row['ip']); } } } @@ -292,7 +292,7 @@ class IntegrityCheck { while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) { $ips[$row['id']] = $row['ip'] . ':' . $row['port']; } - + // Cache all configured domains $result_stmt = Database::prepare("SELECT `id`, `parentdomainid`, `letsencrypt` FROM `" . TABLE_PANEL_DOMAINS . "` ORDER BY `id` ASC"); $ip_stmt = Database::prepare("SELECT `id_domain`, `id_ipandports` FROM `" . TABLE_DOMAINTOIP . "` WHERE `id_domain` = :domainid"); @@ -304,7 +304,7 @@ class IntegrityCheck { Database::pexecute($ip_stmt, array('domainid' => $row['id'])); while ($iprow = $ip_stmt->fetch(PDO::FETCH_ASSOC)) { // If the parentdomain has an ip/port assigned which we know is SSL enabled, set the parentdomain to "true" - if (array_key_exists($iprow['id_ipandports'], $ips)) { $parentdomains[$row['id']] = true; } + if (array_key_exists($iprow['id_ipandports'], $ips)) { $parentdomains[$row['id']] = true; } } } elseif ($row['letsencrypt'] == 1) { // All subdomains with enabled letsencrypt enabled are stored @@ -312,14 +312,14 @@ class IntegrityCheck { $subdomains[$row['parentdomainid']][] = $row['id']; } } - + // Check if every parentdomain with enabled letsencrypt as SSL enabled foreach ($parentdomains as $id => $sslavailable) { // This parentdomain has no subdomains if (!isset($subdomains[$id])) { continue; } // This parentdomain has SSL enabled, doesn't matter what status the subdomains have if ($sslavailable) { continue; } - + // At this point only parentdomains reside which have letsencrypt enabled subdomains if ($fix) { // We make a blanket update to all subdomains of this parentdomain, doesn't matter which one is wrong, all have to be disabled @@ -331,7 +331,7 @@ class IntegrityCheck { return false; } } - + if ($fix) { return $this->SubdomainLetsencrypt(); } else { From 83fa0059deacfe88b1ab0fa675f6775e53b4a9ef Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Wed, 18 May 2016 13:25:31 +0200 Subject: [PATCH 0076/1335] add flag to allow access to dns-editor per customer; clean up dns-entries when domain gets deleted Signed-off-by: Michael Kaufmann (d00p) --- admin_customers.php | 27 ++++++++++++++++++- admin_domains.php | 9 +++++++ customer_domains.php | 7 +++++ .../updates/froxlor/0.9/update_0.9.inc.php | 9 +++++++ .../admin/customer/formfield.customer_add.php | 10 ++++++- .../customer/formfield.customer_edit.php | 11 +++++++- lng/english.lng.php | 1 + lng/german.lng.php | 1 + 8 files changed, 72 insertions(+), 3 deletions(-) diff --git a/admin_customers.php b/admin_customers.php index 0c96941c..406439e6 100644 --- a/admin_customers.php +++ b/admin_customers.php @@ -278,12 +278,14 @@ if ($page == 'customers' Database::pexecute($stmt, array('id' => $id)); $stmt = Database::prepare("DELETE FROM `" . TABLE_PANEL_DATABASES . "` WHERE `customerid` = :id"); Database::pexecute($stmt, array('id' => $id)); - // first gather all domain-id's to clean up panel_domaintoip accordingly + // first gather all domain-id's to clean up panel_domaintoip and dns-entries accordingly $did_stmt = Database::prepare("SELECT `id` FROM `".TABLE_PANEL_DOMAINS."` WHERE `customerid` = :id"); Database::pexecute($did_stmt, array('id' => $id)); while ($row = $did_stmt->fetch(PDO::FETCH_ASSOC)) { $stmt = Database::prepare("DELETE FROM `" . TABLE_DOMAINTOIP . "` WHERE `id_domain` = :did"); Database::pexecute($stmt, array('did' => $row['id'])); + $stmt = Database::prepare("DELETE FROM `" . TABLE_DOMAIN_DNS . "` WHERE `domain_id` = :did"); + Database::pexecute($stmt, array('did' => $row['id'])); } $stmt = Database::prepare("DELETE FROM `" . TABLE_PANEL_DOMAINS . "` WHERE `customerid` = :id"); Database::pexecute($stmt, array('id' => $id)); @@ -526,6 +528,11 @@ if ($page == 'customers' $perlenabled = intval($_POST['perlenabled']); } + $dnsenabled = 0; + if (isset($_POST['dnsenabled'])) { + $dnsenabled = intval($_POST['dnsenabled']); + } + $store_defaultindex = 0; if (isset($_POST['store_defaultindex'])) { $store_defaultindex = intval($_POST['store_defaultindex']); @@ -638,6 +645,10 @@ if ($page == 'customers' $perlenabled = '1'; } + if ($dnsenabled != '0') { + $dnsenabled = '1'; + } + if ($password == '') { $password = generatePassword(); } @@ -676,6 +687,7 @@ if ($page == 'customers' 'imap' => $email_imap, 'pop3' => $email_pop3, 'perlenabled' => $perlenabled, + 'dnsenabled' => $dnsenabled, 'theme' => $_theme, 'custom_notes' => $custom_notes, 'custom_notes_show' => $custom_notes_show @@ -712,9 +724,11 @@ if ($page == 'customers' `mysqls` = :mysqls, `standardsubdomain` = '0', `phpenabled` = :phpenabled, + `dnsenabled` = :dnsenabled, `imap` = :imap, `pop3` = :pop3, `perlenabled` = :perlenabled, + `dnsenabled` = :dnsenabled, `theme` = :theme, `custom_notes` = :custom_notes, `custom_notes_show` = :custom_notes_show" @@ -1186,6 +1200,11 @@ if ($page == 'customers' $perlenabled = intval($_POST['perlenabled']); } + $dnsenabled = 0; + if (isset($_POST['dnsenabled'])) { + $dnsenabled = intval($_POST['dnsenabled']); + } + $diskspace = $diskspace * 1024; $traffic = $traffic * 1024 * 1024; @@ -1317,6 +1336,10 @@ if ($page == 'customers' $perlenabled = '1'; } + if ($dnsenabled != '0') { + $dnsenabled = '1'; + } + if ($phpenabled != $result['phpenabled'] || $perlenabled != $result['perlenabled'] ) { @@ -1427,6 +1450,7 @@ if ($page == 'customers' 'imap' => $email_imap, 'pop3' => $email_pop3, 'perlenabled' => $perlenabled, + 'dnsenabled' => $dnsenabled, 'custom_notes' => $custom_notes, 'custom_notes_show' => $custom_notes_show ); @@ -1460,6 +1484,7 @@ if ($page == 'customers' `imap` = :imap, `pop3` = :pop3, `perlenabled` = :perlenabled, + `dnsenabled` = :dnsenabled `custom_notes` = :custom_notes, `custom_notes_show` = :custom_notes_show WHERE `customerid` = :customerid" diff --git a/admin_domains.php b/admin_domains.php index af3c18a1..c840b277 100644 --- a/admin_domains.php +++ b/admin_domains.php @@ -245,6 +245,15 @@ if ($page == 'domains' || $page == 'overview') { 'domainid' => $id )); + // remove possible existing DNS entries + $del_stmt = Database::prepare(" + DELETE FROM `" . TABLE_DOMAIN_DNS . "` + WHERE `domain_id` = :domainid + "); + Database::pexecute($del_stmt, array( + 'domainid' => $id + )); + triggerLetsEncryptCSRForAliasDestinationDomain($result['aliasdomain'], $log); $log->logAction(ADM_ACTION, LOG_INFO, "deleted domain/subdomains (#" . $result['id'] . ")"); diff --git a/customer_domains.php b/customer_domains.php index c34f296a..f9432f1b 100644 --- a/customer_domains.php +++ b/customer_domains.php @@ -232,6 +232,13 @@ if ($page == 'overview') { ); Database::pexecute($del_stmt, array('domainid' => $id)); + // remove possible existing DNS entries + $del_stmt = Database::prepare(" + DELETE FROM `" . TABLE_DOMAIN_DNS . "` + WHERE `domain_id` = :domainid + "); + Database::pexecute($del_stmt, array('domainid' => $id)); + inserttask('1'); // Using nameserver, insert a task which rebuilds the server config diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index a7ed3ef1..507add57 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -3365,3 +3365,12 @@ if (isDatabaseVersion('201605120')) { updateToDbVersion('201605170'); } + +if (isDatabaseVersion('201605170')) { + + showUpdateStep("Adding new dns-editor setting for customers"); + Database::query("ALTER TABLE `panel_customers` ADD `dnsenabled` tinyint(1) NOT NULL default '0' AFTER `perlenabled`;"); + lastStepStatus(0); + + updateToDbVersion('201605170'); +} diff --git a/lib/formfields/admin/customer/formfield.customer_add.php b/lib/formfields/admin/customer/formfield.customer_add.php index 54a7e863..eaa408aa 100644 --- a/lib/formfields/admin/customer/formfield.customer_add.php +++ b/lib/formfields/admin/customer/formfield.customer_add.php @@ -257,7 +257,15 @@ return array( 'values' => array( array ('label' => $lng['panel']['yes'], 'value' => '1') ) - ) + ), + 'dnsenabled' => array( + 'label' => $lng['admin']['dnsenabled'].'?', + 'type' => 'checkbox', + 'values' => array( + array ('label' => $lng['panel']['yes'], 'value' => '1') + ), + 'visible' => (Settings::Get('system.dnsenabled') == '1' ? true : false) + ), ) ) ) diff --git a/lib/formfields/admin/customer/formfield.customer_edit.php b/lib/formfields/admin/customer/formfield.customer_edit.php index b7e23476..b099651f 100644 --- a/lib/formfields/admin/customer/formfield.customer_edit.php +++ b/lib/formfields/admin/customer/formfield.customer_edit.php @@ -267,7 +267,16 @@ return array( array ('label' => $lng['panel']['yes'], 'value' => '1') ), 'value' => array($result['perlenabled']) - ) + ), + 'dnsenabled' => array( + 'label' => $lng['admin']['dnsenabled'].'?', + 'type' => 'checkbox', + 'values' => array( + array ('label' => $lng['panel']['yes'], 'value' => '1') + ), + 'value' => array($result['dnsenabled']), + 'visible' => (Settings::Get('system.dnsenabled') == '1' ? true : false) + ), ) ), 'section_d' => array( diff --git a/lng/english.lng.php b/lng/english.lng.php index 72873745..da5a5a66 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -2016,3 +2016,4 @@ $lng['serversettings']['dns_server']['title'] = 'DNS server daemon'; $lng['serversettings']['dns_server']['description'] = 'Remember that daemons have to be configured using froxlors configuration templates'; $lng['error']['domain_nopunycode'] = 'You must not specify punycode (IDNA). The domain will automatically be converted'; +$lng['admin']['dnsenabled'] = 'Enable DNS editor'; diff --git a/lng/german.lng.php b/lng/german.lng.php index ee44bec4..9b26a8ff 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -1669,3 +1669,4 @@ $lng['serversettings']['dns_server']['title'] = 'DNS Server Dienst'; $lng['serversettings']['dns_server']['description'] = 'Dienste müssen mit den froxlor Konfigurationstemplates konfiguriert werden'; $lng['error']['domain_nopunycode'] = 'Die Eingabe von Punycode (IDNA) ist nicht notwendig. Die Domain wird automatisch konvertiert.'; +$lng['admin']['dnsenabled'] = 'Zugriff auf DNS Editor'; From b4c7fb574c0823067c55396e061d1a71384b286c Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Wed, 18 May 2016 13:27:56 +0200 Subject: [PATCH 0077/1335] increase DB version for db-updates Signed-off-by: Michael Kaufmann (d00p) --- install/updates/froxlor/0.9/update_0.9.inc.php | 2 +- lib/version.inc.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index 507add57..cf1cdec5 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -3372,5 +3372,5 @@ if (isDatabaseVersion('201605170')) { Database::query("ALTER TABLE `panel_customers` ADD `dnsenabled` tinyint(1) NOT NULL default '0' AFTER `perlenabled`;"); lastStepStatus(0); - updateToDbVersion('201605170'); + updateToDbVersion('201605180'); } diff --git a/lib/version.inc.php b/lib/version.inc.php index a8bcacfa..924d7cf1 100644 --- a/lib/version.inc.php +++ b/lib/version.inc.php @@ -19,7 +19,7 @@ $version = '0.9.36'; // Database version (YYYYMMDDC where C is a daily counter) -$dbversion = '201605170'; +$dbversion = '201605180'; // Distribution branding-tag (used for Debian etc.) $branding = ''; From fa60c17dbcac637d6feacd0cf8f71c445f760d0e Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Wed, 18 May 2016 13:32:44 +0200 Subject: [PATCH 0078/1335] fix sql-query when editing a customer; check for dnsenabled flag in customer-domain-view template Signed-off-by: Michael Kaufmann (d00p) --- admin_customers.php | 3 +-- templates/Sparkle/customer/domains/domains_edit.tpl | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/admin_customers.php b/admin_customers.php index 406439e6..7c41f687 100644 --- a/admin_customers.php +++ b/admin_customers.php @@ -724,7 +724,6 @@ if ($page == 'customers' `mysqls` = :mysqls, `standardsubdomain` = '0', `phpenabled` = :phpenabled, - `dnsenabled` = :dnsenabled, `imap` = :imap, `pop3` = :pop3, `perlenabled` = :perlenabled, @@ -1484,7 +1483,7 @@ if ($page == 'customers' `imap` = :imap, `pop3` = :pop3, `perlenabled` = :perlenabled, - `dnsenabled` = :dnsenabled + `dnsenabled` = :dnsenabled, `custom_notes` = :custom_notes, `custom_notes_show` = :custom_notes_show WHERE `customerid` = :customerid" diff --git a/templates/Sparkle/customer/domains/domains_edit.tpl b/templates/Sparkle/customer/domains/domains_edit.tpl index d7a3c9ea..1ce66b1b 100644 --- a/templates/Sparkle/customer/domains/domains_edit.tpl +++ b/templates/Sparkle/customer/domains/domains_edit.tpl @@ -4,7 +4,7 @@ $header

{$title}  {$title} - +  ({$lng['dnseditor']['edit']})

From 1f63ea10a0ce0b8be051b7f59532e8a2380f25d3 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Wed, 18 May 2016 13:39:46 +0200 Subject: [PATCH 0079/1335] adjust install sql file for dnsenabled flag and db-version Signed-off-by: Michael Kaufmann (d00p) --- install/froxlor.sql | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/install/froxlor.sql b/install/froxlor.sql index 202d4d5a..77644c5a 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -191,6 +191,7 @@ CREATE TABLE `panel_customers` ( `pop3` tinyint(1) NOT NULL default '1', `imap` tinyint(1) NOT NULL default '1', `perlenabled` tinyint(1) NOT NULL default '0', + `dnsenabled` tinyint(1) NOT NULL default '0' `theme` varchar(255) NOT NULL default 'Sparkle', `custom_notes` text, `custom_notes_show` tinyint(1) NOT NULL default '0', @@ -559,7 +560,7 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES ('panel', 'password_special_char_required', '0'), ('panel', 'password_special_char', '!?<>§$%+#=@'), ('panel', 'version', '0.9.36'), - ('panel', 'db_version', '201605170'); + ('panel', 'db_version', '201605180'); DROP TABLE IF EXISTS `panel_tasks`; From 1e3262d6911737077837d12fb51f9da0ed6eb355 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Wed, 18 May 2016 13:54:21 +0200 Subject: [PATCH 0080/1335] do not show dns-editor to customers if not allowed and they enter the URL manually Signed-off-by: Michael Kaufmann (d00p) --- customer_domains.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/customer_domains.php b/customer_domains.php index f9432f1b..fd8a0425 100644 --- a/customer_domains.php +++ b/customer_domains.php @@ -920,7 +920,7 @@ if ($page == 'overview') { eval("echo \"" . getTemplate("domains/domain_ssleditor") . "\";"); } -} elseif ($page == 'domaindnseditor' && Settings::Get('system.dnsenabled') == '1') { +} elseif ($page == 'domaindnseditor' && $userinfo['dnsenabled'] == '1' && Settings::Get('system.dnsenabled') == '1') { require_once __DIR__.'/dns_editor.php'; } From 076b6143ce75e6811ca00634eaa243c8fc9d2817 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Wed, 18 May 2016 15:55:20 +0200 Subject: [PATCH 0081/1335] limit record length Signed-off-by: Michael Kaufmann (d00p) --- dns_editor.php | 4 ++++ lng/english.lng.php | 1 + lng/german.lng.php | 1 + 3 files changed, 6 insertions(+) diff --git a/dns_editor.php b/dns_editor.php index 6dd50fb7..222a412b 100644 --- a/dns_editor.php +++ b/dns_editor.php @@ -65,6 +65,10 @@ if ($action == 'add_record' && ! empty($_POST)) { { $errors[] = sprintf($lng['error']['subdomainiswrong'], $idna_convert->decode($record)); } + if (strlen($record) > 63) + { + $errors[] = $lng['error']['dns_record_toolong']; + } } } diff --git a/lng/english.lng.php b/lng/english.lng.php index da5a5a66..8d84df25 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -2017,3 +2017,4 @@ $lng['serversettings']['dns_server']['description'] = 'Remember that daemons hav $lng['error']['domain_nopunycode'] = 'You must not specify punycode (IDNA). The domain will automatically be converted'; $lng['admin']['dnsenabled'] = 'Enable DNS editor'; +$lng['error']['dns_record_toolong'] = 'Records/labels can only be up to 63 characters'; diff --git a/lng/german.lng.php b/lng/german.lng.php index 9b26a8ff..056712a3 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -1670,3 +1670,4 @@ $lng['serversettings']['dns_server']['description'] = 'Dienste müssen mit den f $lng['error']['domain_nopunycode'] = 'Die Eingabe von Punycode (IDNA) ist nicht notwendig. Die Domain wird automatisch konvertiert.'; $lng['admin']['dnsenabled'] = 'Zugriff auf DNS Editor'; +$lng['error']['dns_record_toolong'] = 'Records/Labels können maximal 63 Zeichen lang sein'; From 96ff346e546630fadbfe53ba91dbf5316fde26c3 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 19 May 2016 09:28:46 +0200 Subject: [PATCH 0082/1335] fix powerdns-config template Signed-off-by: Michael Kaufmann (d00p) --- lib/configfiles/gentoo.xml | 17 ++++++++++------- scripts/jobs/cron_tasks.inc.dns.20.pdns.php | 2 +- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/lib/configfiles/gentoo.xml b/lib/configfiles/gentoo.xml index 71374ccb..e86c2478 100644 --- a/lib/configfiles/gentoo.xml +++ b/lib/configfiles/gentoo.xml @@ -396,7 +396,7 @@ mail IN A ################################# # allow-axfr-ips Allow zonetransfers only to these subnets # -allow-axfr-ips= +# allow-axfr-ips=127.0.0.0/8,::1, ################################# # allow-dnsupdate-from A global setting to allow DNS updates from these IP ranges. @@ -908,15 +908,17 @@ version-string=powerdns # # webserver-print-arguments=no -# include froxlor-bind-specific config -include=/etc/powerdns/pdns_froxlor.conf +# include froxlor-specific config +include-dir=/etc/powerdns/froxlor/ ]]>
- + +# allow-axfr-ips=127.0.0.0/8,::1, ################################# # allow-dnsupdate-from A global setting to allow DNS updates from these IP ranges. @@ -1449,11 +1451,12 @@ version-string=powerdns # webserver-print-arguments=no # include froxlor-bind-specific config -include=/etc/powerdns/pdns_froxlor.conf +include-dir=/etc/powerdns/froxlor/ ]]> - + Date: Thu, 19 May 2016 10:29:17 +0200 Subject: [PATCH 0083/1335] add powerdns config-templates for distros Signed-off-by: Michael Kaufmann (d00p) --- lib/configfiles/jessie.xml | 547 +++++++++++++++++++- lib/configfiles/precise.xml | 38 +- lib/configfiles/trusty.xml | 39 +- lib/configfiles/wheezy.xml | 340 +++++++++++- scripts/jobs/cron_tasks.inc.dns.20.pdns.php | 4 +- 5 files changed, 959 insertions(+), 9 deletions(-) diff --git a/lib/configfiles/jessie.xml b/lib/configfiles/jessie.xml index 27050f9b..f60a1b9f 100644 --- a/lib/configfiles/jessie.xml +++ b/lib/configfiles/jessie.xml @@ -373,7 +373,548 @@ exit "$RETVAL"
- + + + + + +################################# +# allow-dnsupdate-from A global setting to allow DNS updates from these IP ranges. +# +# allow-dnsupdate-from=127.0.0.0/8,::1 + +################################# +# allow-recursion List of subnets that are allowed to recurse +# +allow-recursion=127.0.0.1 + +################################# +# also-notify When notifying a domain, also notify these nameservers +# +# also-notify= + +################################# +# any-to-tcp Answer ANY queries with tc=1, shunting to TCP +# +# any-to-tcp=no + +################################# +# cache-ttl Seconds to store packets in the PacketCache +# +# cache-ttl=20 + +################################# +# carbon-interval Number of seconds between carbon (graphite) updates +# +# carbon-interval=30 + +################################# +# carbon-ourname If set, overrides our reported hostname for carbon stats +# +# carbon-ourname= + +################################# +# carbon-server If set, send metrics in carbon (graphite) format to this server +# +# carbon-server= + +################################# +# chroot If set, chroot to this directory for more security +# +# chroot= + +################################# +# config-dir Location of configuration directory (pdns.conf) +# +config-dir=/etc/powerdns + +################################# +# config-name Name of this virtual configuration - will rename the binary image +# +# config-name= + +################################# +# control-console Debugging switch - don't use +# +# control-console=no + +################################# +# daemon Operate as a daemon +# +daemon=yes + +################################# +# default-ksk-algorithms Default KSK algorithms +# +# default-ksk-algorithms=rsasha256 + +################################# +# default-ksk-size Default KSK size (0 means default) +# +# default-ksk-size=0 + +################################# +# default-soa-mail mail address to insert in the SOA record if none set in the backend +# +# default-soa-mail= + +################################# +# default-soa-name name to insert in the SOA record if none set in the backend +# +# default-soa-name=a.misconfigured.powerdns.server + +################################# +# default-ttl Seconds a result is valid if not set otherwise +# +# default-ttl=3600 + +################################# +# default-zsk-algorithms Default ZSK algorithms +# +# default-zsk-algorithms=rsasha256 + +################################# +# default-zsk-size Default ZSK size (0 means default) +# +# default-zsk-size=0 + +################################# +# direct-dnskey Fetch DNSKEY RRs from backend during DNSKEY synthesis +# +# direct-dnskey=no + +################################# +# disable-axfr Disable zonetransfers but do allow TCP queries +# +# disable-axfr=no + +################################# +# disable-axfr-rectify Disable the rectify step during an outgoing AXFR. Only required for regression testing. +# +# disable-axfr-rectify=no + +################################# +# disable-tcp Do not listen to TCP queries +# +# disable-tcp=no + +################################# +# distributor-threads Default number of Distributor (backend) threads to start +# +# distributor-threads=3 + +################################# +# do-ipv6-additional-processing Do AAAA additional processing +# +# do-ipv6-additional-processing=yes + +################################# +# edns-subnet-processing If we should act on EDNS Subnet options +# +# edns-subnet-processing=no + +################################# +# entropy-source If set, read entropy from this file +# +# entropy-source=/dev/urandom + +################################# +# experimental-api-key REST API Static authentication key (required for API use) +# +# experimental-api-key= + +################################# +# experimental-api-readonly If the JSON API should disallow data modification +# +# experimental-api-readonly=no + +################################# +# experimental-dname-processing If we should support DNAME records +# +# experimental-dname-processing=no + +################################# +# experimental-dnsupdate Enable/Disable DNS update (RFC2136) support. Default is no. +# +# experimental-dnsupdate=no + +################################# +# experimental-json-interface If the webserver should serve JSON data +# +# experimental-json-interface=no + +################################# +# experimental-logfile Filename of the log file for JSON parser +# +# experimental-logfile=/var/log/pdns.log + +################################# +# forward-dnsupdate A global setting to allow DNS update packages that are for a Slave domain, to be forwarded to the master. +# +# forward-dnsupdate=yes + +################################# +# guardian Run within a guardian process +# +guardian=yes + +################################# +# include-dir Include *.conf files from this directory +# +# include-dir= +include-dir=/etc/powerdns/pdns.d + +################################# +# launch Which backends to launch and order to query them in +# +# launch= + +################################# +# load-modules Load this module - supply absolute or relative path +# +# load-modules= + +################################# +# local-address Local IP addresses to which we bind +# +local-address=,127.0.0.1 + +################################# +# local-address-nonexist-fail Fail to start if one or more of the local-address's do not exist on this server +# +# local-address-nonexist-fail=yes + +################################# +# local-ipv6 Local IP address to which we bind +# +# local-ipv6= + +################################# +# local-ipv6-nonexist-fail Fail to start if one or more of the local-ipv6 addresses do not exist on this server +# +# local-ipv6-nonexist-fail=yes + +################################# +# local-port The port on which we listen +# +# local-port=53 + +################################# +# log-dns-details If PDNS should log DNS non-erroneous details +# +# log-dns-details=no + +################################# +# log-dns-queries If PDNS should log all incoming DNS queries +# +# log-dns-queries=no + +################################# +# logging-facility Log under a specific facility +# +# logging-facility= + +################################# +# loglevel Amount of logging. Higher is more. Do not set below 3 +# +# loglevel=4 + +################################# +# lua-prequery-script Lua script with prequery handler +# +# lua-prequery-script= + +################################# +# master Act as a master +# +master=yes + +################################# +# max-cache-entries Maximum number of cache entries +# +# max-cache-entries=1000000 + +################################# +# max-ent-entries Maximum number of empty non-terminals in a zone +# +# max-ent-entries=100000 + +################################# +# max-nsec3-iterations Limit the number of NSEC3 hash iterations +# +# max-nsec3-iterations=500 + +################################# +# max-queue-length Maximum queuelength before considering situation lost +# +# max-queue-length=5000 + +################################# +# max-signature-cache-entries Maximum number of signatures cache entries +# +# max-signature-cache-entries= + +################################# +# max-tcp-connections Maximum number of TCP connections +# +# max-tcp-connections=10 + +################################# +# module-dir Default directory for modules +# +# module-dir=/usr/lib/TRIPLET/pdns + +################################# +# negquery-cache-ttl Seconds to store negative query results in the QueryCache +# +# negquery-cache-ttl=60 + +################################# +# no-shuffle Set this to prevent random shuffling of answers - for regression testing +# +# no-shuffle=off + +################################# +# only-notify Only send AXFR NOTIFY to these IP addresses or netmasks +# +# only-notify=0.0.0.0/0,::/0 + +################################# +# out-of-zone-additional-processing Do out of zone additional processing +# +# out-of-zone-additional-processing=yes + +################################# +# overload-queue-length Maximum queuelength moving to packetcache only +# +# overload-queue-length=0 + +################################# +# pipebackend-abi-version Version of the pipe backend ABI +# +# pipebackend-abi-version=1 + +################################# +# prevent-self-notification Don't send notifications to what we think is ourself +# +# prevent-self-notification=yes + +################################# +# query-cache-ttl Seconds to store query results in the QueryCache +# +# query-cache-ttl=20 + +################################# +# query-local-address Source IP address for sending queries +# +# query-local-address=0.0.0.0 + +################################# +# query-local-address6 Source IPv6 address for sending queries +# +# query-local-address6=:: + +################################# +# query-logging Hint backends that queries should be logged +# +# query-logging=no + +################################# +# queue-limit Maximum number of milliseconds to queue a query +# +# queue-limit=1500 + +################################# +# receiver-threads Default number of receiver threads to start +# +# receiver-threads=1 + +################################# +# recursive-cache-ttl Seconds to store packets for recursive queries in the PacketCache +# +# recursive-cache-ttl=10 + +################################# +# recursor If recursion is desired, IP address of a recursing nameserver +# +# recursor=no + +################################# +# retrieval-threads Number of AXFR-retrieval threads for slave operation +# +# retrieval-threads=2 + +################################# +# reuseport Enable higher performance on compliant kernels by using SO_REUSEPORT allowing each receiver thread to open its own socket +# +# reuseport=no + +################################# +# security-poll-suffix Domain name from which to query security update notifications +# +# security-poll-suffix=secpoll.powerdns.com. + +################################# +# send-root-referral Send out old-fashioned root-referral instead of ServFail in case of no authority +# +# send-root-referral=no + +################################# +# server-id Returned when queried for 'server.id' TXT or NSID, defaults to hostname - disabled or custom +# +# server-id= + +################################# +# setgid If set, change group id to this gid for more security +# +setgid=pdns + +################################# +# setuid If set, change user id to this uid for more security +# +setuid=pdns + +################################# +# signing-threads Default number of signer threads to start +# +# signing-threads=3 + +################################# +# slave Act as a slave +# +# slave=no + +################################# +# slave-cycle-interval Reschedule failed SOA serial checks once every .. seconds +# +# slave-cycle-interval=60 + +################################# +# slave-renotify If we should send out notifications for slaved updates +# +# slave-renotify=no + +################################# +# soa-expire-default Default SOA expire +# +# soa-expire-default=604800 + +################################# +# soa-minimum-ttl Default SOA minimum ttl +# +# soa-minimum-ttl=3600 + +################################# +# soa-refresh-default Default SOA refresh +# +# soa-refresh-default=10800 + +################################# +# soa-retry-default Default SOA retry +# +# soa-retry-default=3600 + +################################# +# socket-dir Where the controlsocket will live +# +# socket-dir=/var/run + +################################# +# tcp-control-address If set, PowerDNS can be controlled over TCP on this address +# +# tcp-control-address= + +################################# +# tcp-control-port If set, PowerDNS can be controlled over TCP on this address +# +# tcp-control-port=53000 + +################################# +# tcp-control-range If set, remote control of PowerDNS is possible over these networks only +# +# tcp-control-range=127.0.0.0/8, 10.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12, ::1/128, fe80::/10 + +################################# +# tcp-control-secret If set, PowerDNS can be controlled over TCP after passing this secret +# +# tcp-control-secret= + +################################# +# traceback-handler Enable the traceback handler (Linux only) +# +# traceback-handler=yes + +################################# +# trusted-notification-proxy IP address of incoming notification proxy +# +# trusted-notification-proxy= + +################################# +# udp-truncation-threshold Maximum UDP response size before we truncate +# +# udp-truncation-threshold=1680 + +################################# +# version-string PowerDNS version in packets - full, anonymous, powerdns or custom +# + +version-string=powerdns +################################# +# webserver Start a webserver for monitoring +# +# webserver=no + +################################# +# webserver-address IP Address of webserver to listen on +# +# webserver-address=127.0.0.1 + +################################# +# webserver-allow-from Webserver access is only allowed from these subnets +# +# webserver-allow-from=0.0.0.0/0,::/0 + +################################# +# webserver-password Password required for accessing the webserver +# +# webserver-password= + +################################# +# webserver-port Port of webserver to listen on +# +# webserver-port=8081 + +################################# +# webserver-print-arguments If the webserver should print arguments +# +# webserver-print-arguments=no + +include-dir=/etc/powerdns/froxlor +]]> + + + + + + + + + + - + - + + + + + + + + + + + + + - + + + + + + + + + + + + + + - + + + + + +################################# +# allow-recursion List of netmasks that are allowed to recurse +# +allow-recursion=127.0.0.1 + +################################# +# allow-recursion-override Local data even about hosts that don't exist will +# override the internet. (on/off) +# +# allow-recursion-override= + +################################# +# cache-ttl Seconds to store packets in the PacketCache +# +# cache-ttl=20 + +################################# +# chroot If set, chroot to this directory for more security +# +# chroot=/var/spool/powerdns + +################################# +# config-dir Location of configuration directory (pdns.conf) +# +config-dir=/etc/powerdns + +################################# +# config-name Name of this virtual configuration - will rename the binary image +# +# config-name= + +################################# +# control-console Debugging switch - don't use +# +# control-console=no + +################################# +# daemon Operate as a daemon +# +daemon=yes + +################################# +# default-soa-name name to insert in the SOA record if none set in the backend +# +# default-soa-name=a.misconfigured.powerdns.server + +################################# +# disable-axfr Disable zonetransfers but do allow TCP queries +# +disable-axfr=yes + +################################# +# disable-tcp Do not listen to TCP queries +# +# disable-tcp=no + +################################# +# distributor-threads Default number of Distributor (backend) threads to start +# +# distributor-threads=3 + +################################# +# fancy-records Process URL and MBOXFW records +# +# fancy-records=no + +################################# +# guardian Run within a guardian process +# +guardian=yes + +################################# +# launch Which backends to launch and order to query them in +# +# launch= + +################################# +# lazy-recursion Only recurse if question cannot be answered locally +# +lazy-recursion=yes + +################################# +# load-modules Load this module - supply absolute or relative path +# +# load-modules= + +################################# +# local-address Local IP address to which we bind +# +local-address=,127.0.0.1 + +################################# +# local-ipv6 Local IP address to which we bind +# +# local-ipv6= + +################################# +# local-port The port on which we listen +# +local-port=53 + +################################# +# log-dns-details If PDNS should log failed update requests +# +log-dns-details=yes + +################################# +# log-failed-updates If PDNS should log failed update requests +# +# log-failed-updates= + +################################# +# logfile Logfile to use +# +# logfile=/var/log/pdns.log + +################################# +# logging-facility Log under a specific facility +# +# logging-facility= + +################################# +# loglevel Amount of logging. Higher is more. Do not set below 3 +# +# loglevel=4 + +################################# +# master Act as a master +# +master=yes + +################################# +# max-queue-length Maximum queuelength before considering situation lost +# +# max-queue-length=5000 + +################################# +# max-tcp-connections Maximum number of TCP connections +# +# max-tcp-connections=10 + +################################# +# module-dir Default directory for modules +# +module-dir=/usr/lib/powerdns + +################################# +# negquery-cache-ttl Seconds to store packets in the PacketCache +# +# negquery-cache-ttl=60 + +################################# +# out-of-zone-additional-processing Do out of zone additional processing +# +# out-of-zone-additional-processing=no + +################################# +# query-cache-ttl Seconds to store packets in the PacketCache +# +# query-cache-ttl=20 + +################################# +# query-logging Hint backends that queries should be logged +# +# query-logging=no + +################################# +# queue-limit Maximum number of milliseconds to queue a query +# +# queue-limit=1500 + +################################# +# query-local-address The IP address to use as a source address for sending +# queries. +# query-local-address= + +################################# +# receiver-threads Number of receiver threads to launch +# +# receiver-threads=1 + +################################# +# recursive-cache-ttl Seconds to store packets in the PacketCache +# +# recursive-cache-ttl=10 + +################################# +# recursor If recursion is desired, IP address of a recursing nameserver +# +# recursor= + +################################# +# setgid If set, change group id to this gid for more security +# +setgid=pdns + +################################# +# setuid If set, change user id to this uid for more security +# +setuid=pdns + +################################# +# skip-cname Do not perform CNAME indirection for each query +# +# skip-cname=no + +################################# +# slave Act as a slave +# +# slave=no + +################################# +# slave-cycle-interval Reschedule failed SOA serial checks once every .. seconds +# +# slave-cycle-interval=60 + +################################# +# smtpredirector Our smtpredir MX host +# +# smtpredirector=a.misconfigured.powerdns.smtp.server + +################################# +# soa-minimum-ttl Default SOA mininum ttl +# +# soa-minimum-ttl=3600 + +################################# +# soa-refresh-default Default SOA refresh +# +# soa-refresh-default=10800 + +################################# +# soa-retry-default Default SOA retry +# +# soa-retry-default=3600 + +################################# +# soa-expire-default Default SOA expire +# +# soa-expire-default=604800 + +################################# +# soa-serial-offset Make sure that no SOA serial is less than this number +# +# soa-serial-offset=0 + +################################# +# socket-dir Where the controlsocket will live +# +socket-dir=/var/run + +################################# +# strict-rfc-axfrs Perform strictly rfc compliant axfrs (very slow) +# +# strict-rfc-axfrs=no + +################################# +# urlredirector Where we send hosts to that need to be url redirected +# +# urlredirector=127.0.0.1 + +################################# +# use-logfile Use a log file +# +# use-logfile=yes + +################################# +# webserver Start a webserver for monitoring +# +# webserver=no + +################################# +# webserver-address IP Address of webserver to listen on +# +# webserver-address=127.0.0.1 + +################################# +# webserver-password Password required for accessing the webserver +# +# webserver-password= + +################################# +# webserver-port Port of webserver to listen on +# +# webserver-port=8081 + +################################# +# webserver-print-arguments If the webserver should print arguments +# +# webserver-print-arguments=no + +################################# +# wildcard-url Process URL and MBOXFW records +# +# wildcard-url=no + +################################# +# wildcards Honor wildcards in the database +# +# wildcards= + +################################# +# version-string What should PowerDNS return for version +# allowed methods are anonymous / powerdns / full / custom +version-string=powerdns + +include-dir=/etc/powerdns/froxlor +]]> + + + + + + + + + + _logger->logAction(CRON_ACTION, LOG_ERROR, 'PowerDNS configuration file not found. Did you go through the configuration templates?'); - die('PowerDNS configuration file not found. Did you go through the configuration templates?'); + $this->_logger->logAction(CRON_ACTION, LOG_ERROR, 'PowerDNS configuration file ('.$config.') not found. Did you go through the configuration templates?'); + die('PowerDNS configuration file ('.$config.') not found. Did you go through the configuration templates?'.PHP_EOL); } $lines = file($config); $mysql_data = array(); From 5c1079e04bf01027593ecd3a1637d916c8e92fac Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 19 May 2016 12:09:04 +0200 Subject: [PATCH 0084/1335] Add change_date value to the pdns-record entries Signed-off-by: Michael Kaufmann (d00p) --- scripts/jobs/cron_tasks.inc.dns.20.pdns.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/jobs/cron_tasks.inc.dns.20.pdns.php b/scripts/jobs/cron_tasks.inc.dns.20.pdns.php index b6b609ed..bf892554 100644 --- a/scripts/jobs/cron_tasks.inc.dns.20.pdns.php +++ b/scripts/jobs/cron_tasks.inc.dns.20.pdns.php @@ -90,7 +90,8 @@ class pdns extends DnsBase `content` = :content, `ttl` = :ttl, `prio` = :prio, - `disabled` = '0' + `disabled` = '0', + `change_date` = UNIX_TIMESTAMP() "); foreach ($records as $record) From c2b0714b4a38b3f5ba6d9a5dc37b8bef1bfced82 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 19 May 2016 13:49:22 +0200 Subject: [PATCH 0085/1335] powerdns needs the 'whole' record (., e.g. sub.example.com) Signed-off-by: Michael Kaufmann (d00p) --- scripts/jobs/cron_tasks.inc.dns.20.pdns.php | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/scripts/jobs/cron_tasks.inc.dns.20.pdns.php b/scripts/jobs/cron_tasks.inc.dns.20.pdns.php index bf892554..3847657d 100644 --- a/scripts/jobs/cron_tasks.inc.dns.20.pdns.php +++ b/scripts/jobs/cron_tasks.inc.dns.20.pdns.php @@ -47,7 +47,7 @@ class pdns extends DnsBase $zone = createDomainZone(($domain['id'] == 'none') ? $domain : $domain['id'], $isFroxlorHostname); $dom_id = $this->_insertZone($zone->origin, $zone->serial); - $this->_insertRecords($dom_id, $zone->records); + $this->_insertRecords($dom_id, $zone->records, $zone->origin); $this->_insertAllowedTransfers($dom_id); $this->_logger->logAction(CRON_ACTION, LOG_INFO, '`' . $domain['domain'] . '` zone written'); @@ -80,7 +80,7 @@ class pdns extends DnsBase return $lastid; } - private function _insertRecords($domainid = 0, $records) + private function _insertRecords($domainid = 0, $records, $origin) { $ins_stmt = $this->pdns_db->prepare(" INSERT INTO records set @@ -96,9 +96,17 @@ class pdns extends DnsBase foreach ($records as $record) { + if ($record->record == '@') { + $_record = $origin; + } + else + { + $_record = $record->record.".".$origin; + } + $ins_data = array( 'did' => $domainid, - 'rec' => $record->record, + 'rec' => $_record, 'type' => $record->type, 'content' => $record->content, 'ttl' => $record->ttl, From 61eab6fd9370eb916b0b38e6ac2eb86b994c97e1 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Fri, 20 May 2016 15:56:06 +0200 Subject: [PATCH 0086/1335] do not validate fqdn of SRV and TXT entries, as they might use underscores Signed-off-by: Michael Kaufmann (d00p) --- dns_editor.php | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/dns_editor.php b/dns_editor.php index 222a412b..adebd536 100644 --- a/dns_editor.php +++ b/dns_editor.php @@ -51,22 +51,19 @@ if ($action == 'add_record' && ! empty($_POST)) { $record = strtolower($record); - if ($record != '@' && $record != '*') - { + if ($record != '@' && $record != '*') { // validate record if (strpos($record, '--') !== false) { $errors[] = $lng['error']['domain_nopunycode']; - } - else - { + } else { $record = $idna_convert->encode($record); - $check_dom = $record.'.example.com'; - if (!validateDomain($check_dom)) - { - $errors[] = sprintf($lng['error']['subdomainiswrong'], $idna_convert->decode($record)); + if ($type != 'SRV' && $type != 'TXT') { + $check_dom = $record . '.example.com'; + if (! validateDomain($check_dom)) { + $errors[] = sprintf($lng['error']['subdomainiswrong'], $idna_convert->decode($record)); + } } - if (strlen($record) > 63) - { + if (strlen($record) > 63) { $errors[] = $lng['error']['dns_record_toolong']; } } @@ -301,5 +298,5 @@ foreach ($type_select_values as $_type) { eval("\$record_list=\"" . getTemplate("dns_editor/list", true) . "\";"); $zone = createDomainZone($domain_id); -$zonefile = (string)$zone; +$zonefile = (string) $zone; eval("echo \"" . getTemplate("dns_editor/index", true) . "\";"); From be373e278fcff764e4b99d4b6f65e33344a37cdd Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Fri, 20 May 2016 16:17:33 +0200 Subject: [PATCH 0087/1335] allow defined non-existing entry for SRV target-value Signed-off-by: Michael Kaufmann (d00p) --- dns_editor.php | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/dns_editor.php b/dns_editor.php index adebd536..325fe1d7 100644 --- a/dns_editor.php +++ b/dns_editor.php @@ -144,11 +144,6 @@ if ($action == 'add_record' && ! empty($_POST)) { if ($prio === null || $prio < 0) { $errors[] = $lng['error']['dns_srv_prioempty']; } - // check for trailing dot - if (substr($content, - 1) == '.') { - // remove it for checks - $content = substr($content, 0, - 1); - } // check only last part of content, as it can look like: // _service._proto.name. TTL class SRV priority weight port target. $_split_content = explode(" ", $content); @@ -157,6 +152,13 @@ if ($action == 'add_record' && ! empty($_POST)) { $errors[] = $lng['error']['dns_srv_invalidcontent']; } $target = trim($_split_content[count($_split_content) - 1]); + if ($target != '.') { + // check for trailing dot + if (substr($target, - 1) == '.') { + // remove it for checks + $target = substr($target, 0, - 1); + } + } if (! validateDomain($target)) { $errors[] = $lng['error']['dns_srv_needdom']; } else { @@ -170,7 +172,9 @@ if ($action == 'add_record' && ! empty($_POST)) { } } // append trailing dot (again) - $content .= '.'; + if ($target != '.') { + $content .= '.'; + } } $new_entry = array( From 7ea1de2a9221d2c30d19e492fe8a456d56a652d8 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Fri, 20 May 2016 16:21:55 +0200 Subject: [PATCH 0088/1335] allow defined non-existing entry for SRV target-value for real now, i guess Signed-off-by: Michael Kaufmann (d00p) --- dns_editor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dns_editor.php b/dns_editor.php index 325fe1d7..15687b10 100644 --- a/dns_editor.php +++ b/dns_editor.php @@ -159,7 +159,7 @@ if ($action == 'add_record' && ! empty($_POST)) { $target = substr($target, 0, - 1); } } - if (! validateDomain($target)) { + if ($target != '.' && ! validateDomain($target)) { $errors[] = $lng['error']['dns_srv_needdom']; } else { // check whether there is a CNAME-record for the same resource From 339d84736e5f353473c1119c50e5e38d3a054f57 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sat, 21 May 2016 09:03:21 +0200 Subject: [PATCH 0089/1335] no quotation of dns data for powerdns Signed-off-by: Michael Kaufmann (d00p) --- lib/functions/dns/function.createDomainZone.php | 11 ++++++++++- .../dns/function.generateDkimEntries.php | 16 ++++++++++------ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/lib/functions/dns/function.createDomainZone.php b/lib/functions/dns/function.createDomainZone.php index f7baed6e..424c6ceb 100644 --- a/lib/functions/dns/function.createDomainZone.php +++ b/lib/functions/dns/function.createDomainZone.php @@ -292,7 +292,7 @@ function addRequiredEntry($record = '@', $type = 'A', &$required) function encloseTXTContent($txt_content, $isMultiLine = false) { // check that TXT content is enclosed in " " - if ($isMultiLine == false) { + if ($isMultiLine == false && Settings::Get('system.dns_server') != 'pdns') { if (substr($txt_content, 0, 1) != '"') { $txt_content = '"' . $txt_content; } @@ -300,6 +300,15 @@ function encloseTXTContent($txt_content, $isMultiLine = false) $txt_content .= '"'; } } + if (Settings::Get('system.dns_server') == 'pdns') { + // no quotation for PowerDNS + if (substr($txt_content, 0, 1) == '"') { + $txt_content = substr($txt_content, 1); + } + if (substr($txt_content, - 1) == '"') { + $txt_content = substr($txt_content, 0, -1); + } + } return $txt_content; } diff --git a/lib/functions/dns/function.generateDkimEntries.php b/lib/functions/dns/function.generateDkimEntries.php index e3d5e95a..622af765 100644 --- a/lib/functions/dns/function.generateDkimEntries.php +++ b/lib/functions/dns/function.generateDkimEntries.php @@ -14,7 +14,6 @@ * @package Functions * */ - function generateDkimEntries($domain) { $zone_dkim = array(); @@ -55,11 +54,16 @@ function generateDkimEntries($domain) // end-part $dkim_txt .= 't=s'; - // split if necessary - $txt_record_split = ''; - $lbr = 50; - for ($pos = 0; $pos <= strlen($dkim_txt) - 1; $pos += $lbr) { - $txt_record_split .= (($pos == 0) ? '("' : "\t\t\t\t\t \"") . substr($dkim_txt, $pos, $lbr) . (($pos >= strlen($dkim_txt) - $lbr) ? '")' : '"') . "\n"; + if (Settings::Get('system.dns_server') == 'pdns') { + // PowerDNS does not need/want splitted content + $txt_record_split = $dkim_txt; + } else { + // split if necessary + $txt_record_split = ''; + $lbr = 50; + for ($pos = 0; $pos <= strlen($dkim_txt) - 1; $pos += $lbr) { + $txt_record_split .= (($pos == 0) ? '("' : "\t\t\t\t\t \"") . substr($dkim_txt, $pos, $lbr) . (($pos >= strlen($dkim_txt) - $lbr) ? '")' : '"') . "\n"; + } } // dkim-entry From b51f0821cba7819d0db11021aa5c61feca2e71c3 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sun, 22 May 2016 11:08:44 +0200 Subject: [PATCH 0090/1335] show scheduled backup and give customer the possiblity to abort the job Signed-off-by: Michael Kaufmann (d00p) --- customer_extras.php | 27 ++++++++++++++++++- lng/english.lng.php | 1 + lng/german.lng.php | 1 + templates/Sparkle/customer/extras/backup.tpl | 9 +++++++ .../customer/extras/backup_listexisting.tpl | 26 ++++++++++++++++++ 5 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 templates/Sparkle/customer/extras/backup_listexisting.tpl diff --git a/customer_extras.php b/customer_extras.php index 98fd06cc..9294863c 100644 --- a/customer_extras.php +++ b/customer_extras.php @@ -522,17 +522,29 @@ if ($page == 'overview') { if (Settings::Get('system.backupenabled') == 1) { + if ($action == 'abort' && isset($_POST['send']) && $_POST['send'] == 'send') { + $log->logAction(USR_ACTION, LOG_NOTICE, "customer_extras::backup - aborted scheduled backupjob"); + $entry = isset($_POST['backup_job_entry']) ? (int)$_POST['backup_job_entry'] : 0; + if ($entry > 0) { + $del_stmt = Database::prepare("DELETE FROM `".TABLE_PANEL_TASKS."` WHERE `id` = :tid"); + Database::pexecute($del_stmt, array('tid' => $entry)); + standard_success('backupaborted'); + } + redirectTo($filename, array('page' => $page, 'action' => '', 's' => $s)); + } if ($action == '') { $log->logAction(USR_ACTION, LOG_NOTICE, "viewed customer_extras::backup"); // check whether there is a backup-job for this customer $sel_stmt = Database::prepare("SELECT * FROM `".TABLE_PANEL_TASKS."` WHERE `type` = '20'"); Database::pexecute($sel_stmt); + $existing_backupJob = null; while ($entry = $sel_stmt->fetch()) { $data = unserialize($entry['data']); if ($data['customerid'] == $userinfo['customerid']) { - standard_error('customerhasongoingbackupjob'); + $existing_backupJob = $entry; + break; } } @@ -577,11 +589,24 @@ if ($page == 'overview') { standard_success('backupscheduled'); } else { + if (!empty($existing_backupJob)) { + $action = "abort"; + $row = unserialize($entry['data']); + $row['path'] = makeCorrectDir(str_replace($userinfo['documentroot'], "/", $row['destdir'])); + $row['backup_web'] = ($row['backup_web'] == '1') ? $lng['panel']['yes'] : $lng['panel']['no']; + $row['backup_mail'] = ($row['backup_mail'] == '1') ? $lng['panel']['yes'] : $lng['panel']['no']; + $row['backup_dbs'] = ($row['backup_dbs'] == '1') ? $lng['panel']['yes'] : $lng['panel']['no']; + } $pathSelect = makePathfield($userinfo['documentroot'], $userinfo['guid'], $userinfo['guid']); $backup_data = include_once dirname(__FILE__) . '/lib/formfields/customer/extras/formfield.backup.php'; $backup_form = htmlform::genHTMLForm($backup_data); $title = $backup_data['backup']['title']; $image = $backup_data['backup']['image']; + + if (!empty($existing_backupJob)) { + // overwrite backup_form after we took everything from it we needed + eval("\$backup_form = \"" . getTemplate("extras/backup_listexisting") . "\";"); + } eval("echo \"" . getTemplate("extras/backup") . "\";"); } } diff --git a/lng/english.lng.php b/lng/english.lng.php index 9b8f4b1c..0b0d8075 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -1981,6 +1981,7 @@ $lng['extras']['backup_mail'] = 'Backup mail-data'; $lng['extras']['backup_dbs'] = 'Backup databases'; $lng['error']['customerhasongoingbackupjob'] = 'There is already a backup job waiting to be processed, please be patient.'; $lng['success']['backupscheduled'] = 'Your backup job has been scheduled. Please wait for it to be processed'; +$lng['success']['backupaborted'] = 'Your scheduled backup has been cancelled'; $lng['crondesc']['cron_backup'] = 'Process backup jobs'; $lng['error']['backupfunctionnotenabled'] = 'The backup function is not enabled'; $lng['serversettings']['backupenabled']['title'] = "Enable backup for customers"; diff --git a/lng/german.lng.php b/lng/german.lng.php index 956afd80..9a3b0837 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -1634,6 +1634,7 @@ $lng['extras']['backup_mail'] = 'E-Mail Daten sichern'; $lng['extras']['backup_dbs'] = 'Datenbanken sichern'; $lng['error']['customerhasongoingbackupjob'] = 'Es gibt noch einen austehenden Backup-Job. Bitte haben Sie etwas Geduld.'; $lng['success']['backupscheduled'] = 'Ihre Sicherung wurde erfolgreich geplant. Bitte warten Sie nun, bis diese abgearbeitet wurde.'; +$lng['success']['backupaborted'] = 'Die geplante Sicherung wurde abgebrochen'; $lng['crondesc']['cron_backup'] = 'Ausstehende Sicherungen erstellen'; $lng['error']['backupfunctionnotenabled'] = 'Die Sicherungs-Funktion is nicht aktiviert'; $lng['serversettings']['backupenabled']['title'] = "Backup für Kunden aktivieren"; diff --git a/templates/Sparkle/customer/extras/backup.tpl b/templates/Sparkle/customer/extras/backup.tpl index 6e71243e..09faee56 100644 --- a/templates/Sparkle/customer/extras/backup.tpl +++ b/templates/Sparkle/customer/extras/backup.tpl @@ -7,6 +7,15 @@ $header + +
+
+
{$lng['admin']['warning']}
+
{$lng['error']['customerhasongoingbackupjob']}
+
+
+
+
diff --git a/templates/Sparkle/customer/extras/backup_listexisting.tpl b/templates/Sparkle/customer/extras/backup_listexisting.tpl new file mode 100644 index 00000000..4afa8676 --- /dev/null +++ b/templates/Sparkle/customer/extras/backup_listexisting.tpl @@ -0,0 +1,26 @@ + + + {$lng['panel']['path']} + {$lng['extras']['backup_web']} + {$lng['extras']['backup_mail']} + {$lng['extras']['backup_dbs']} + + + + + {$row['path']} + {$row['backup_web']} + {$row['backup_mail']} + {$row['backup_dbs']} + + + + + +

+ + +

+ + + From 08563e92985163016a054a5b6c203ccbfd4360f9 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 23 May 2016 13:01:05 +0200 Subject: [PATCH 0091/1335] add more opcache-php.ini directives for php-fpm, fixes #1624 Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/phpinterface/class.phpinterface_fpm.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/classes/phpinterface/class.phpinterface_fpm.php b/lib/classes/phpinterface/class.phpinterface_fpm.php index 211ee4a8..156940ba 100644 --- a/lib/classes/phpinterface/class.phpinterface_fpm.php +++ b/lib/classes/phpinterface/class.phpinterface_fpm.php @@ -113,7 +113,11 @@ class phpinterface_fpm { 'session.gc_probability', 'variables_order', 'opcache.log_verbosity_level', - 'opcache.restrict_api' + 'opcache.restrict_api', + 'opcache.revalidate_freq', + 'opcache.max_accelerated_files', + 'opcache.memory_consumption', + 'opcache.interned_strings_buffer' ), 'php_admin_flag' => array( 'allow_call_time_pass_reference', @@ -137,7 +141,8 @@ class phpinterface_fpm { 'opcache.revalidate_path', 'opcache.save_comments', 'opcache.use_cwd', - 'opcache.validate_timestamps' + 'opcache.validate_timestamps', + 'opcache.fast_shutdown' ) ); From e5c16439e1b5f47573eb867958044572d477345c Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sun, 29 May 2016 09:41:38 +0200 Subject: [PATCH 0092/1335] replace config-template-variables also in commands to be executed, e.g. used in proftpd ssl-cert generation command Signed-off-by: Michael Kaufmann (d00p) --- admin_configfiles.php | 368 +++++++++++++++++++++--------------------- 1 file changed, 184 insertions(+), 184 deletions(-) diff --git a/admin_configfiles.php b/admin_configfiles.php index 4dbccbba..a1c626be 100644 --- a/admin_configfiles.php +++ b/admin_configfiles.php @@ -19,199 +19,199 @@ define('AREA', 'admin'); require './lib/init.php'; if ($userinfo['change_serversettings'] == '1') { - - $replace_arr = Array( - '' => $sql['user'], - '' => 'MYSQL_PASSWORD', - '' => $sql['db'], - '' => $sql['host'], - '' => isset($sql['socket']) ? $sql['socket'] : null, - '' => Settings::Get('system.hostname'), - '' => Settings::Get('system.ipaddress'), - '' => Settings::Get('system.nameservers'), - '' => Settings::Get('system.vmail_homedir'), - '' => Settings::Get('system.vmail_uid'), - '' => Settings::Get('system.vmail_gid'), - '' => (Settings::Get('system.use_ssl') == '1') ? 'imaps pop3s' : '', - '' => (Settings::Get('system.mod_fcgid_tmpdir') != '') ? makeCorrectDir(Settings::Get('system.mod_fcgid_tmpdir')) : '/tmp/', - '' => makeCorrectDir(FROXLOR_INSTALL_DIR), - '' => makeCorrectDir(Settings::Get('system.bindconf_directory')), - '' => Settings::Get('system.apachereload_command'), - '' => makeCorrectDir(Settings::Get('system.logfiles_directory')), - '' => makeCorrectDir(Settings::Get('phpfpm.fastcgi_ipcdir')), - '' => Settings::Get('system.httpgroup') - ); - - // get distro from URL param - $distribution = (isset($_GET['distribution']) && $_GET['distribution'] != 'choose') ? $_GET['distribution'] : ""; - $service = (isset($_GET['service']) && $_GET['service'] != 'choose') ? $_GET['service'] : ""; - $daemon = (isset($_GET['daemon']) && $_GET['daemon'] != 'choose') ? $_GET['daemon'] : ""; - $distributions_select = ""; - $services_select = ""; - $daemons_select = ""; - - $configfiles = ""; - $services = ""; - $daemons = ""; - - $config_dir = makeCorrectDir(FROXLOR_INSTALL_DIR . '/lib/configfiles/'); - - if ($distribution != "") { - // create configparser object - $configfiles = new ConfigParser($config_dir . '/' . $distribution . ".xml"); - - // get distro-info - $dist_display = getCompleteDistroName($configfiles); - - // get all the services from the distro - $services = $configfiles->getServices(); - - if ($service != "") { - - $daemons = $services[$service]->getDaemons(); - - if ($daemon == "") { - foreach ($daemons as $di => $dd) { - $title = $dd->title; - if ($dd->default) { - $title = $title." (".strtolower($lng['panel']['default']).")"; - } - $daemons_select .= makeoption($title, $di); - } - } - } else { - foreach ($services as $si => $sd) { - $services_select .= makeoption($sd->title, $si); - } - } - } else { - - // show list of available distro's - $distros = glob($config_dir . '*.xml'); - // tmp array - $distributions_select_data = array(); - // read in all the distros - foreach ($distros as $_distribution) { - // get configparser object - $dist = new ConfigParser($_distribution); - // get distro-info - $dist_display = getCompleteDistroName($dist); - // store in tmp array - $distributions_select_data[$dist_display] = str_replace(".xml", "", strtolower(basename($_distribution))); - } - - // sort by distribution name - ksort($distributions_select_data); - - foreach ($distributions_select_data as $dist_display => $dist_index) { - // create select-box-option - $distributions_select .= makeoption($dist_display, $dist_index); - } - } - - if ($distribution != "" && $service != "" && $daemon != "") { - - $confarr = $daemons[$daemon]->getConfig(); - - $configpage = ''; - - $distro_editor = $configfiles->distributionEditor; - $commands_pre = ""; - $commands_file = ""; - $commands_post = ""; - - $lasttype = ''; - $commands = ''; - foreach ($confarr as $idx => $action) { - if ($lasttype != '' && $lasttype != $action['type']) { - $commands = trim($commands); - $numbrows = count(explode("\n", $commands)); - eval("\$configpage.=\"" . getTemplate("configfiles/configfiles_commands") . "\";"); - $lasttype = ''; - $commands = ''; - } - switch ($action['type']) { - case "install": - $commands .= $action['content'] . "\n"; - $lasttype = "install"; - break; - case "command": - $commands .= $action['content'] . "\n"; - $lasttype = "command"; - break; - case "file": - if (array_key_exists('content', $action)) { - $commands_file = getFileContentContainer($action['content'], $replace_arr, $action['name'], $distro_editor); - } elseif (array_key_exists('subcommands', $action)) { - foreach ($action['subcommands'] as $fileaction) { - if (array_key_exists('execute', $fileaction) && $fileaction['execute'] == "pre") { - $commands_pre .= $fileaction['content'] . "\n"; - } elseif (array_key_exists('execute', $fileaction) && $fileaction['execute'] == "post") { - $commands_post .= $fileaction['content'] . "\n"; - } elseif ($fileaction['type'] == 'file') { - $commands_file = getFileContentContainer($fileaction['content'], $replace_arr, $action['name'], $distro_editor); - } - } - } - $realname = $action['name']; - $commands = trim($commands_pre); - if ($commands != "") { - $numbrows = count(explode("\n", $commands)); - eval("\$commands_pre=\"" . getTemplate("configfiles/configfiles_commands") . "\";"); - } - $commands = trim($commands_post); - if ($commands != "") { - $numbrows = count(explode("\n", $commands)); - eval("\$commands_post=\"" . getTemplate("configfiles/configfiles_commands") . "\";"); - } - eval("\$configpage.=\"" . getTemplate("configfiles/configfiles_subfileblock") . "\";"); - $commands = ''; - $commands_pre = ''; - $commands_post = ''; - break; - } - } - $commands = trim($commands); - if ($commands != '') { - $numbrows = count(explode("\n", $commands)); - eval("\$configpage.=\"" . getTemplate("configfiles/configfiles_commands") . "\";"); - } - eval("echo \"" . getTemplate("configfiles/configfiles") . "\";"); - } else { - eval("echo \"" . getTemplate("configfiles/wizard") . "\";"); - } + $replace_arr = Array( + '' => $sql['user'], + '' => 'MYSQL_PASSWORD', + '' => $sql['db'], + '' => $sql['host'], + '' => isset($sql['socket']) ? $sql['socket'] : null, + '' => Settings::Get('system.hostname'), + '' => Settings::Get('system.ipaddress'), + '' => Settings::Get('system.nameservers'), + '' => Settings::Get('system.vmail_homedir'), + '' => Settings::Get('system.vmail_uid'), + '' => Settings::Get('system.vmail_gid'), + '' => (Settings::Get('system.use_ssl') == '1') ? 'imaps pop3s' : '', + '' => (Settings::Get('system.mod_fcgid_tmpdir') != '') ? makeCorrectDir(Settings::Get('system.mod_fcgid_tmpdir')) : '/tmp/', + '' => makeCorrectDir(FROXLOR_INSTALL_DIR), + '' => makeCorrectDir(Settings::Get('system.bindconf_directory')), + '' => Settings::Get('system.apachereload_command'), + '' => makeCorrectDir(Settings::Get('system.logfiles_directory')), + '' => makeCorrectDir(Settings::Get('phpfpm.fastcgi_ipcdir')), + '' => Settings::Get('system.httpgroup') + ); + + // get distro from URL param + $distribution = (isset($_GET['distribution']) && $_GET['distribution'] != 'choose') ? $_GET['distribution'] : ""; + $service = (isset($_GET['service']) && $_GET['service'] != 'choose') ? $_GET['service'] : ""; + $daemon = (isset($_GET['daemon']) && $_GET['daemon'] != 'choose') ? $_GET['daemon'] : ""; + $distributions_select = ""; + $services_select = ""; + $daemons_select = ""; + + $configfiles = ""; + $services = ""; + $daemons = ""; + + $config_dir = makeCorrectDir(FROXLOR_INSTALL_DIR . '/lib/configfiles/'); + + if ($distribution != "") { + // create configparser object + $configfiles = new ConfigParser($config_dir . '/' . $distribution . ".xml"); + + // get distro-info + $dist_display = getCompleteDistroName($configfiles); + + // get all the services from the distro + $services = $configfiles->getServices(); + + if ($service != "") { + + $daemons = $services[$service]->getDaemons(); + + if ($daemon == "") { + foreach ($daemons as $di => $dd) { + $title = $dd->title; + if ($dd->default) { + $title = $title . " (" . strtolower($lng['panel']['default']) . ")"; + } + $daemons_select .= makeoption($title, $di); + } + } + } else { + foreach ($services as $si => $sd) { + $services_select .= makeoption($sd->title, $si); + } + } + } else { + + // show list of available distro's + $distros = glob($config_dir . '*.xml'); + // tmp array + $distributions_select_data = array(); + // read in all the distros + foreach ($distros as $_distribution) { + // get configparser object + $dist = new ConfigParser($_distribution); + // get distro-info + $dist_display = getCompleteDistroName($dist); + // store in tmp array + $distributions_select_data[$dist_display] = str_replace(".xml", "", strtolower(basename($_distribution))); + } + + // sort by distribution name + ksort($distributions_select_data); + + foreach ($distributions_select_data as $dist_display => $dist_index) { + // create select-box-option + $distributions_select .= makeoption($dist_display, $dist_index); + } + } + + if ($distribution != "" && $service != "" && $daemon != "") { + + $confarr = $daemons[$daemon]->getConfig(); + + $configpage = ''; + + $distro_editor = $configfiles->distributionEditor; + + $commands_pre = ""; + $commands_file = ""; + $commands_post = ""; + + $lasttype = ''; + $commands = ''; + foreach ($confarr as $idx => $action) { + if ($lasttype != '' && $lasttype != $action['type']) { + $commands = trim($commands); + $numbrows = count(explode("\n", $commands)); + eval("\$configpage.=\"" . getTemplate("configfiles/configfiles_commands") . "\";"); + $lasttype = ''; + $commands = ''; + } + switch ($action['type']) { + case "install": + $commands .= strtr($action['content'], $replace_arr) . "\n"; + $lasttype = "install"; + break; + case "command": + $commands .= strtr($action['content'], $replace_arr) . "\n"; + $lasttype = "command"; + break; + case "file": + if (array_key_exists('content', $action)) { + $commands_file = getFileContentContainer($action['content'], $replace_arr, $action['name'], $distro_editor); + } elseif (array_key_exists('subcommands', $action)) { + foreach ($action['subcommands'] as $fileaction) { + if (array_key_exists('execute', $fileaction) && $fileaction['execute'] == "pre") { + $commands_pre .= $fileaction['content'] . "\n"; + } elseif (array_key_exists('execute', $fileaction) && $fileaction['execute'] == "post") { + $commands_post .= $fileaction['content'] . "\n"; + } elseif ($fileaction['type'] == 'file') { + $commands_file = getFileContentContainer($fileaction['content'], $replace_arr, $action['name'], $distro_editor); + } + } + } + $realname = $action['name']; + $commands = trim($commands_pre); + if ($commands != "") { + $numbrows = count(explode("\n", $commands)); + eval("\$commands_pre=\"" . getTemplate("configfiles/configfiles_commands") . "\";"); + } + $commands = trim($commands_post); + if ($commands != "") { + $numbrows = count(explode("\n", $commands)); + eval("\$commands_post=\"" . getTemplate("configfiles/configfiles_commands") . "\";"); + } + eval("\$configpage.=\"" . getTemplate("configfiles/configfiles_subfileblock") . "\";"); + $commands = ''; + $commands_pre = ''; + $commands_post = ''; + break; + } + } + $commands = trim($commands); + if ($commands != '') { + $numbrows = count(explode("\n", $commands)); + eval("\$configpage.=\"" . getTemplate("configfiles/configfiles_commands") . "\";"); + } + eval("echo \"" . getTemplate("configfiles/configfiles") . "\";"); + } else { + eval("echo \"" . getTemplate("configfiles/wizard") . "\";"); + } } else { - die('not allowed to see this page'); - // redirect or similar here + die('not allowed to see this page'); + // redirect or similar here } // helper functions function getFileContentContainer($file_content, &$replace_arr, $realname, $distro_editor) { - $files = ""; - $file_content = trim($file_content); - if ($file_content != '') { - $file_content = strtr($file_content, $replace_arr); - $file_content = htmlspecialchars($file_content); - $numbrows = count(explode("\n", $file_content)); - eval("\$files=\"" . getTemplate("configfiles/configfiles_file") . "\";"); - } - return $files; + $files = ""; + $file_content = trim($file_content); + if ($file_content != '') { + $file_content = strtr($file_content, $replace_arr); + $file_content = htmlspecialchars($file_content); + $numbrows = count(explode("\n", $file_content)); + eval("\$files=\"" . getTemplate("configfiles/configfiles_file") . "\";"); + } + return $files; } function getCompleteDistroName($cparser) { - // get distro-info - $dist_display = $cparser->distributionName; - if ($cparser->distributionCodename != '') { - $dist_display .= " ".$cparser->distributionCodename; - } - if ($cparser->distributionVersion != '') { - $dist_display .= " (" . $cparser->distributionVersion . ")"; - } - if ($cparser->deprecated) { - $dist_display .= " [deprecated]"; - } - return $dist_display; + // get distro-info + $dist_display = $cparser->distributionName; + if ($cparser->distributionCodename != '') { + $dist_display .= " " . $cparser->distributionCodename; + } + if ($cparser->distributionVersion != '') { + $dist_display .= " (" . $cparser->distributionVersion . ")"; + } + if ($cparser->deprecated) { + $dist_display .= " [deprecated]"; + } + return $dist_display; } From 1b0649d0cfcbaf7cd3cce2d89ff90489323659eb Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sun, 29 May 2016 09:56:33 +0200 Subject: [PATCH 0093/1335] replace CUSTOMER_TMP with the correct tmp-folder, depending what php-interface is being used Signed-off-by: Michael Kaufmann (d00p) --- admin_configfiles.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/admin_configfiles.php b/admin_configfiles.php index a1c626be..efbeba7a 100644 --- a/admin_configfiles.php +++ b/admin_configfiles.php @@ -20,6 +20,16 @@ require './lib/init.php'; if ($userinfo['change_serversettings'] == '1') { + $customer_tmpdir = '/tmp/'; + if (Settings::Get('system.mod_fcgid') == '1' && Settings::Get('system.mod_fcgid_tmpdir') != '') + { + $customer_tmpdir = Settings::Get('system.mod_fcgid_tmpdir'); + } + elseif (Settings::Get('phpfpm.enabled') == '1' && Settings::Get('phpfpm.tmpdir') != '') + { + $customer_tmpdir = Settings::Get('phpfpm.tmpdir'); + } + $replace_arr = Array( '' => $sql['user'], '' => 'MYSQL_PASSWORD', @@ -33,7 +43,7 @@ if ($userinfo['change_serversettings'] == '1') { '' => Settings::Get('system.vmail_uid'), '' => Settings::Get('system.vmail_gid'), '' => (Settings::Get('system.use_ssl') == '1') ? 'imaps pop3s' : '', - '' => (Settings::Get('system.mod_fcgid_tmpdir') != '') ? makeCorrectDir(Settings::Get('system.mod_fcgid_tmpdir')) : '/tmp/', + '' => makeCorrectDir($customer_tmpdir), '' => makeCorrectDir(FROXLOR_INSTALL_DIR), '' => makeCorrectDir(Settings::Get('system.bindconf_directory')), '' => Settings::Get('system.apachereload_command'), From 7faebbb197ac1b6a61e70bf619bf3eeafc9dd681 Mon Sep 17 00:00:00 2001 From: Stefan Heid Date: Thu, 2 Jun 2016 21:21:40 +0200 Subject: [PATCH 0094/1335] BUGFIX: Cleaninstall fails due syntax error in froxlor.sql (missing comma) (#356) --- install/froxlor.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/froxlor.sql b/install/froxlor.sql index 77644c5a..7de09553 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -191,7 +191,7 @@ CREATE TABLE `panel_customers` ( `pop3` tinyint(1) NOT NULL default '1', `imap` tinyint(1) NOT NULL default '1', `perlenabled` tinyint(1) NOT NULL default '0', - `dnsenabled` tinyint(1) NOT NULL default '0' + `dnsenabled` tinyint(1) NOT NULL default '0', `theme` varchar(255) NOT NULL default 'Sparkle', `custom_notes` text, `custom_notes_show` tinyint(1) NOT NULL default '0', From 970a119f235d18e117c3acbb41d8bc8d7a43990c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Zapletal?= Date: Fri, 3 Jun 2016 16:20:34 +0200 Subject: [PATCH 0095/1335] fix non-persistent XSS due inproper content escaping --- index.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.php b/index.php index da6bba42..423f8fd8 100644 --- a/index.php +++ b/index.php @@ -302,7 +302,7 @@ if ($action == 'login') { } $lastqrystr = ""; if (isset($_REQUEST['qrystr']) && $_REQUEST['qrystr'] != "") { - $lastqrystr = strip_tags($_REQUEST['qrystr']); + $lastqrystr = htmlspecialchars($_REQUEST['qrystr'], ENT_QUOTES); } eval("echo \"" . getTemplate('login') . "\";"); From 2e7133d619faf85437e27806cbacd8ed7f77aa32 Mon Sep 17 00:00:00 2001 From: Oliver Hader Date: Sat, 4 Jun 2016 20:18:19 +0200 Subject: [PATCH 0096/1335] [SECURITY] Information disclosure on database failures In case the database is not responding, e.g. due to "too many connections" cut-off database credentials might be shown and system path be revealed. In terms of security this is considered as information disclosure. --- install/lib/class.FroxlorInstall.php | 4 +- lib/classes/database/class.Database.php | 68 +++++++++++++++++++++++-- 2 files changed, 67 insertions(+), 5 deletions(-) diff --git a/install/lib/class.FroxlorInstall.php b/install/lib/class.FroxlorInstall.php index 89b6e853..2a94ba0b 100644 --- a/install/lib/class.FroxlorInstall.php +++ b/install/lib/class.FroxlorInstall.php @@ -296,7 +296,7 @@ class FroxlorInstall $content .= $this->_status_message('begin', $this->_lng['install']['creating_configfile']); $userdata = "_data['mysql_host'], "'\\") . "';\n"; $userdata .= "\$sql['user']='" . addcslashes($this->_data['mysql_unpriv_user'], "'\\") . "';\n"; $userdata .= "\$sql['password']='" . addcslashes($this->_data['mysql_unpriv_pass'], "'\\") . "';\n"; @@ -305,6 +305,8 @@ class FroxlorInstall $userdata .= "\$sql_root[0]['host']='" . addcslashes($this->_data['mysql_host'], "'\\") . "';\n"; $userdata .= "\$sql_root[0]['user']='" . addcslashes($this->_data['mysql_root_user'], "'\\") . "';\n"; $userdata .= "\$sql_root[0]['password']='" . addcslashes($this->_data['mysql_root_pass'], "'\\") . "';\n"; + $userdata .= "// enable debugging to browser in case of SQL errors\n"; + $userdata .= "\$sql['debug'] = false;\n"; $userdata .= "?>"; // test if we can store the userdata.inc.php in ../lib diff --git a/lib/classes/database/class.Database.php b/lib/classes/database/class.Database.php index 12c459ca..74a655d7 100644 --- a/lib/classes/database/class.Database.php +++ b/lib/classes/database/class.Database.php @@ -323,15 +323,18 @@ class Database { $sql_root = array(0 => array('caption' => 'Default', 'host' => $sql['host'], 'socket' => (isset($sql['socket']) ? $sql['socket'] : null), 'user' => $sql['root_user'], 'password' => $sql['root_password'])); } + $substitutions = array( + $sql['password'] => 'DB_UNPRIV_PWD', + $sql_root[0]['password'] => 'DB_ROOT_PWD', + ); + // hide username/password in messages $error_message = $error->getMessage(); $error_trace = $error->getTraceAsString(); // error-message - $error_message = str_replace($sql['password'], 'DB_UNPRIV_PWD', $error_message); - $error_message = str_replace($sql_root[0]['password'], 'DB_ROOT_PWD', $error_message); + $error_message = self::_substitute($error_message, $substitutions); // error-trace - $error_trace = str_replace($sql['password'], 'DB_UNPRIV_PWD', $error_trace); - $error_trace = str_replace($sql_root[0]['password'], 'DB_ROOT_PWD', $error_trace); + $error_trace = self::_substitute($error_trace, $substitutions); if ($error->getCode() == 2003) { $error_message = "Unable to connect to database. Either the mysql-server is not running or your user/password is wrong."; @@ -365,6 +368,9 @@ class Database { @fclose($errlog); if ($showerror) { + if (empty($sql['debug'])) { + $error_trace = ''; + } // fallback $theme = 'Sparkle'; @@ -402,4 +408,58 @@ class Database { die("We are sorry, but a MySQL - error occurred. The administrator may find more information in the syslog"); } } + + /** + * Substitutes patterns in content. + * + * @param string $content + * @param array $substitutions + * @param int $minLength + * @return string + */ + private static function _substitute($content, array $substitutions, $minLength = 6) { + $replacements = array(); + + foreach ($substitutions as $search => $replace) { + $replacements = $replacements + self::_createShiftedSubstitutions($search, $replace, $minLength); + } + + $content = str_replace( + array_keys($replacements), + array_values($replacements), + $content + ); + + return $content; + } + + /** + * Creates substitutions, shifted by length, e.g. + * + * _createShiftedSubstitutions('abcdefgh', 'value', 4): + * array( + * 'abcdefgh' => 'value', + * 'abcdefg' => 'value', + * 'abcdef' => 'value', + * 'abcde' => 'value', + * 'abcd' => 'value', + * ) + * + * @param string $search + * @param string $replace + * @param int $minLength + * @return array + */ + private static function _createShiftedSubstitutions($search, $replace, $minLength) { + $substitutions = array(); + $length = strlen($search); + + if ($length > $minLength) { + for ($shiftedLength = $length; $shiftedLength >= $minLength; $shiftedLength--) { + $substitutions[substr($search, 0, $shiftedLength)] = $replace; + } + } + + return $substitutions; + } } From 7a36f5edac2b7430b70d4a38ad6d38a79bf5d09b Mon Sep 17 00:00:00 2001 From: Florian Klink Date: Tue, 7 Jun 2016 21:56:09 +0200 Subject: [PATCH 0097/1335] lng: fix typo decending -> descending --- lib/classes/output/class.paging.php | 2 +- lng/dutch.lng.php | 2 +- lng/english.lng.php | 2 +- lng/french.lng.php | 2 +- lng/german.lng.php | 2 +- lng/italian.lng.php | 2 +- lng/portugues.lng.php | 2 +- lng/swedish.lng.php | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/classes/output/class.paging.php b/lib/classes/output/class.paging.php index 54fb352f..a903622c 100644 --- a/lib/classes/output/class.paging.php +++ b/lib/classes/output/class.paging.php @@ -399,7 +399,7 @@ class paging { } $breakorws = ($break ? '
' : ' '); - foreach (array('asc' => $lng['panel']['ascending'], 'desc' => $lng['panel']['decending']) as $sortordertype => $sortorderdescription) { + foreach (array('asc' => $lng['panel']['ascending'], 'desc' => $lng['panel']['descending']) as $sortordertype => $sortorderdescription) { $orderoptions.= makeoption($sortorderdescription, $sortordertype, $this->sortorder, true, true); } diff --git a/lng/dutch.lng.php b/lng/dutch.lng.php index 2ad74afe..fa5d87d3 100644 --- a/lng/dutch.lng.php +++ b/lng/dutch.lng.php @@ -394,7 +394,7 @@ $lng['serversettings']['defaultip']['title'] = 'Standaard IP/Poort'; $lng['serversettings']['defaultip']['description'] = 'Wat is de standaard IP/Poort combinatie?'; $lng['domains']['statstics'] = 'Gebruiks Statistieken'; $lng['panel']['ascending'] = 'oplopend'; -$lng['panel']['decending'] = 'aflopend'; +$lng['panel']['descending'] = 'aflopend'; $lng['panel']['search'] = 'Zoeken'; $lng['panel']['used'] = 'gebruikt'; diff --git a/lng/english.lng.php b/lng/english.lng.php index 5fdd316d..c06d1e03 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -437,7 +437,7 @@ $lng['serversettings']['defaultip']['title'] = 'Default IP/Port'; $lng['serversettings']['defaultip']['description'] = 'Select all IP-addresses you want to use as default for new domains'; $lng['domains']['statstics'] = 'Usage Statistics'; $lng['panel']['ascending'] = 'ascending'; -$lng['panel']['decending'] = 'decending'; +$lng['panel']['descending'] = 'descending'; $lng['panel']['search'] = 'Search'; $lng['panel']['used'] = 'used'; diff --git a/lng/french.lng.php b/lng/french.lng.php index e55f885f..aea6d93c 100644 --- a/lng/french.lng.php +++ b/lng/french.lng.php @@ -427,7 +427,7 @@ $lng['serversettings']['defaultip']['title'] = 'IP / Port par défaut'; $lng['serversettings']['defaultip']['description'] = 'Quel est l\'IP / Port par défaut ?'; $lng['domains']['statstics'] = 'Fréquentation'; $lng['panel']['ascending'] = 'ascendant'; -$lng['panel']['decending'] = 'descendant'; +$lng['panel']['descending'] = 'descendant'; $lng['panel']['search'] = 'Rechercher'; $lng['panel']['used'] = 'utilisé'; diff --git a/lng/german.lng.php b/lng/german.lng.php index 64593a5a..f9397b1f 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -433,7 +433,7 @@ $lng['serversettings']['defaultip']['title'] = 'Standard-IP/Port'; $lng['serversettings']['defaultip']['description'] = 'Welche IP/Port-Kombination sollen standardmäßig verwendet werden?'; $lng['domains']['statstics'] = 'Statistiken'; $lng['panel']['ascending'] = 'aufsteigend'; -$lng['panel']['decending'] = 'absteigend'; +$lng['panel']['descending'] = 'absteigend'; $lng['panel']['search'] = 'Suche'; $lng['panel']['used'] = 'genutzt'; diff --git a/lng/italian.lng.php b/lng/italian.lng.php index 021be071..c636e70e 100644 --- a/lng/italian.lng.php +++ b/lng/italian.lng.php @@ -422,7 +422,7 @@ $lng['serversettings']['defaultip']['title'] = 'IP/Porta default'; $lng['serversettings']['defaultip']['description'] = 'Qual\'è la combinazione IP/Porta default?'; $lng['domains']['statstics'] = 'Statistiche d\'utilizzo'; $lng['panel']['ascending'] = 'ascendente'; -$lng['panel']['decending'] = 'discendente'; +$lng['panel']['descending'] = 'discendente'; $lng['panel']['search'] = 'Cerca'; $lng['panel']['used'] = 'utilizzato'; diff --git a/lng/portugues.lng.php b/lng/portugues.lng.php index f0122bf8..8bca7709 100644 --- a/lng/portugues.lng.php +++ b/lng/portugues.lng.php @@ -419,7 +419,7 @@ $lng['serversettings']['defaultip']['title'] = 'IP/Porta Padrão'; $lng['serversettings']['defaultip']['description'] = 'Qual é a IP/Porta Padrão?'; $lng['domains']['statstics'] = 'Estatísticas de Uso'; $lng['panel']['ascending'] = 'Crescente'; -$lng['panel']['decending'] = 'Decrescente'; +$lng['panel']['descending'] = 'Decrescente'; $lng['panel']['search'] = 'Procurar'; $lng['panel']['used'] = 'Usado'; $lng['panel']['translator'] = 'Tradutor'; diff --git a/lng/swedish.lng.php b/lng/swedish.lng.php index e0bf3ba7..eaf8f1f0 100644 --- a/lng/swedish.lng.php +++ b/lng/swedish.lng.php @@ -412,7 +412,7 @@ $lng['serversettings']['defaultip']['title'] = 'Förvald IP/Port'; $lng['serversettings']['defaultip']['description'] = 'Vilken är den förvalda IP/Port kombinationen?'; $lng['domains']['statstics'] = 'Användarstatistik'; $lng['panel']['ascending'] = 'Stigande'; -$lng['panel']['decending'] = 'Fallande'; +$lng['panel']['descending'] = 'Fallande'; $lng['panel']['search'] = 'Sök'; $lng['panel']['used'] = 'använd'; From 82af43f598f9444f118e7381f370a14e8e6eba02 Mon Sep 17 00:00:00 2001 From: Florian Klink Date: Tue, 7 Jun 2016 21:46:27 +0200 Subject: [PATCH 0098/1335] lng/english: unify upper/lowercase string in $lng['panel'] Some where lowercase, some uppercase. Especially in the Cancel/save case this looked weird. This patch changes all button labels to their uppercase form. --- lng/english.lng.php | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/lng/english.lng.php b/lng/english.lng.php index c06d1e03..98b58f1a 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -22,17 +22,17 @@ */ $lng['translator'] = ''; -$lng['panel']['edit'] = 'edit'; -$lng['panel']['delete'] = 'delete'; -$lng['panel']['create'] = 'create'; -$lng['panel']['save'] = 'save'; -$lng['panel']['yes'] = 'yes'; -$lng['panel']['no'] = 'no'; +$lng['panel']['edit'] = 'Edit'; +$lng['panel']['delete'] = 'Delete'; +$lng['panel']['create'] = 'Create'; +$lng['panel']['save'] = 'Save'; +$lng['panel']['yes'] = 'Yes'; +$lng['panel']['no'] = 'No'; $lng['panel']['emptyfornochanges'] = 'empty for no changes'; $lng['panel']['emptyfordefault'] = 'empty for defaults'; $lng['panel']['path'] = 'Path'; $lng['panel']['toggle'] = 'Toggle'; -$lng['panel']['next'] = 'next'; +$lng['panel']['next'] = 'Next'; $lng['panel']['dirsmissing'] = 'Can not find or read the directory!'; /** @@ -477,7 +477,7 @@ $lng['serversettings']['deactivateddocroot']['description'] = 'When a user is de // ADDED IN 1.2.16-svn4 -$lng['panel']['reset'] = 'discard changes'; +$lng['panel']['reset'] = 'Discard changes'; $lng['admin']['accountsettings'] = 'Account settings'; $lng['admin']['panelsettings'] = 'Panel settings'; $lng['admin']['systemsettings'] = 'System settings'; @@ -1273,7 +1273,7 @@ $lng['admin']['ipsandports']['ssl_cert_chainfile']['description'] = 'Mostly CA_B $lng['admin']['ipsandports']['docroot']['title'] = 'Custom docroot (empty = point to Froxlor)'; $lng['admin']['ipsandports']['docroot']['description'] = 'You can define a custom document-root (the destination for a request) for this ip/port combination here.
ATTENTION: Please be careful with what you enter here!'; $lng['serversettings']['login_domain_login'] = 'Allow login with domains'; -$lng['panel']['unlock'] = 'unlock'; +$lng['panel']['unlock'] = 'Unlock'; $lng['question']['customer_reallyunlock'] = 'Do you really want to unlock customer %s?'; // ADDED IN FROXLOR 0.9.15 @@ -1733,8 +1733,8 @@ $lng['serversettings']['ssl']['ssl_cipher_list']['description'] = 'This is a lis // Added in Froxlor 0.9.31 $lng['panel']['dashboard'] = 'Dashboard'; -$lng['panel']['assigned'] = 'assigned'; -$lng['panel']['available'] = 'available'; +$lng['panel']['assigned'] = 'Assigned'; +$lng['panel']['available'] = 'Available'; $lng['customer']['services'] = 'Services'; $lng['serversettings']['phpfpm_settings']['ipcdir']['title'] = 'FastCGI IPC directory'; $lng['serversettings']['phpfpm_settings']['ipcdir']['description'] = 'The directory where the php-fpm sockets will be stored by the webserver.
This directory has to be readable for the webserver'; From 5866293a254ed4a6947c005527ca800bf13b9287 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 13 Jun 2016 13:45:23 +0200 Subject: [PATCH 0099/1335] add script to change ip-addresses in froxlor from CLI and updated corresponding settings. Helpful for VM-template installations or similar. Use at own risk; feedback is appreciated Signed-off-by: Michael Kaufmann (d00p) --- install/scripts/switch-server-ip.php | 412 +++++++++++++++++++++++++++ 1 file changed, 412 insertions(+) create mode 100644 install/scripts/switch-server-ip.php diff --git a/install/scripts/switch-server-ip.php b/install/scripts/switch-server-ip.php new file mode 100644 index 00000000..506cca9f --- /dev/null +++ b/install/scripts/switch-server-ip.php @@ -0,0 +1,412 @@ +#!/usr/bin/php + (2016-) + * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt + * @package Cron + * + */ + +// give control to command line handler +try { + CmdLineHandler::processParameters($argc, $argv); +} catch (Exception $e) { + CmdLineHandler::printerr($e->getMessage()); +} + +class CmdLineHandler +{ + + /** + * internal variable for passed arguments + * + * @var array + */ + private static $args = null; + + /** + * Action object read from commandline/config + * + * @var Action + */ + private $_action = null; + + /** + * list of valid parameters/switches + */ + public static $switches = array( + /* 'd', // debug / output information for everything */ + 'h' + ); + // same as --help + public static $params = array( + 'switch', + 'list', + 'froxlor-dir', + 'help' + ); + + /** + * Returns a CmdLineHandler object with given + * arguments from command line + * + * @param int $argc + * @param array $argv + * + * @return CmdLineHandler + */ + public static function processParameters($argc, $argv) + { + return new CmdLineHandler($argc, $argv); + } + + /** + * returns the Action object generated in + * the class constructor + * + * @return Action + */ + public function getAction() + { + return $this->_action; + } + + /** + * class constructor, validates the command line parameters + * and sets the Action-object if valid + * + * @param int $argc + * @param string[] $argv + * + * @return null + * @throws Exception + */ + private function __construct($argc, $argv) + { + self::$args = $this->_parseArgs($argv); + $this->_action = $this->_createAction(); + } + + /** + * Parses the arguments given via the command line; + * three types are supported: + * 1. + * --parm1 or --parm2=value + * 2. -xyz (multiple switches in one) or -a=value + * 3. parm1 parm2 + * + * The 1. will be mapped as + * ["parm1"] => true, ["parm2"] => "value" + * The 2. as + * ["x"] => true, ["y"] => true, ["z"] => true, ["a"] => "value" + * And the 3. as + * [0] => "parm1", [1] => "parm2" + * + * @param array $argv + * + * @return array + */ + private function _parseArgs($argv) + { + array_shift($argv); + $o = array(); + foreach ($argv as $a) { + if (substr($a, 0, 2) == '--') { + $eq = strpos($a, '='); + if ($eq !== false) { + $o[substr($a, 2, $eq - 2)] = substr($a, $eq + 1); + } else { + $k = substr($a, 2); + if (! isset($o[$k])) { + $o[$k] = true; + } + } + } else + if (substr($a, 0, 1) == '-') { + if (substr($a, 2, 1) == '=') { + $o[substr($a, 1, 1)] = substr($a, 3); + } else { + foreach (str_split(substr($a, 1)) as $k) { + if (! isset($o[$k])) { + $o[$k] = true; + } + } + } + } else { + $o[] = $a; + } + } + return $o; + } + + /** + * Creates an Action-Object for the Action-Handler + * + * @return Action + * @throws Exception + */ + private function _createAction() + { + + // Test for help-switch + if (empty(self::$args) || array_key_exists("help", self::$args) || array_key_exists("h", self::$args)) { + self::printHelp(); + // end of execution + } + // check if no unknown parameters are present + foreach (self::$args as $arg => $value) { + + if (is_numeric($arg)) { + throw new Exception("Unknown parameter '" . $value . "' in argument list"); + } elseif (! in_array($arg, self::$params) && ! in_array($arg, self::$switches)) { + throw new Exception("Unknown parameter '" . $arg . "' in argument list"); + } + } + + // set debugger switch + if (isset(self::$args["d"]) && self::$args["d"] == true) { + // Debugger::getInstance()->setEnabled(true); + // Debugger::getInstance()->debug("debug output enabled"); + } + + return new Action(self::$args); + } + + public static function printHelp() + { + self::println(""); + self::println("Help / command line parameters:"); + self::println(""); + // commands + self::println("--switch\t\tlets you switch ip-address A with ip-address B"); + self::println("\t\t\tExample: --switch=A,B"); + self::println("\t\t\tExample: --switch=\"A1,B1 A2,B2 A3,B3 ...\""); + self::println(""); + self::println("--list\t\t\tshow all currently used ip-addresses in froxlor"); + self::println(""); + self::println("--froxlor-dir\t\tpath to froxlor installation"); + self::println("\t\t\tExample: --froxlor-dir=/var/www/froxlor/"); + self::println(""); + self::println("--help\t\t\tshow help screen (this)"); + self::println(""); + // switches + // self::println("-d\t\t\tenable debug output"); + self::println("-h\t\t\tsame as --help"); + self::println(""); + + die(); // end of execution + } + + public static function println($msg = "") + { + print $msg . PHP_EOL; + } + + private static function _printcolor($msg = "", $color = "0") + { + print "\033[" . $color . "m" . $msg . "\033[0m" . PHP_EOL; + } + + public static function printerr($msg = "") + { + self::_printcolor($msg, "31"); + } + + public static function printsucc($msg = "") + { + self::_printcolor($msg, "32"); + } + + public static function printwarn($msg = "") + { + self::_printcolor($msg, "33"); + } +} + +class Action +{ + + private $_args = null; + + private $_name = null; + + private $_db = null; + + public function __construct($args) + { + $this->_args = $args; + $this->_validate(); + } + + public function getActionName() + { + return $this->_name; + } + + /** + * validates the parsed command line parameters + * + * @throws Exception + */ + private function _validate() + { + $need_config = false; + if (array_key_exists("list", $this->_args) || array_key_exists("switch", $this->_args)) { + $need_config = true; + } + + $this->_checkConfigParam($need_config); + + $this->_parseConfig(); + + if (array_key_exists("list", $this->_args)) { + $this->_listIPs(); + } + if (array_key_exists("switch", $this->_args)) { + $this->_switchIPs(); + } + } + + private function _listIPs() + { + $sel_stmt = Database::prepare("SELECT * FROM panel_ipsandports ORDER BY ip ASC, port ASC"); + Database::pexecute($sel_stmt); + $ips = $sel_stmt->fetchAll(PDO::FETCH_ASSOC); + $mask = "|%-10.10s |%-50.50s | %10.10s |\n"; + printf($mask, str_repeat("-", 10), str_repeat("-", 50), str_repeat("-", 10)); + printf($mask, 'id', 'IP address', 'port'); + printf($mask, str_repeat("-", 10), str_repeat("-", 50), str_repeat("-", 10)); + foreach ($ips as $ipdata) { + printf($mask, $ipdata['id'], $ipdata['ip'], $ipdata['port']); + } + printf($mask, str_repeat("-", 10), str_repeat("-", 50), str_repeat("-", 10)); + echo PHP_EOL . PHP_EOL; + } + + private function _switchIPs() + { + $ip_list = $this->_args['switch']; + + if (empty($ip_list) || is_bool($ip_list)) { + throw new Exception("No paramters given for --switch action."); + } + + $ips_to_switch = array(); + $ip_list = explode(" ", $ip_list); + foreach ($ip_list as $ips_combo) { + $ip_pair = explode(",", $ips_combo); + if (count($ip_pair) != 2) { + throw new Exception("Invalid parameter given for --switch"); + } else { + if (filter_var($ip_pair[0], FILTER_VALIDATE_IP) == false) { + throw new Exception("Invalid source ip address: " . $ip_pair[0]); + } + if (filter_var($ip_pair[1], FILTER_VALIDATE_IP) == false) { + throw new Exception("Invalid target ip address: " . $ip_pair[1]); + } + if ($ip_pair[0] == $ip_pair[1]) { + throw new Exception("Source and target ip address are equal"); + } + } + $ips_to_switch[] = $ip_pair; + } + + if (count($ips_to_switch) > 0) { + $upd_stmt = Database::prepare("UPDATE panel_ipsandports SET `ip` = :newip WHERE `ip` = :oldip"); + + // system.ipaddress + $check_sysip_stmt = Database::prepare("SELECT `value` FROM `panel_settings` WHERE `settinggroup` = 'system' and `varname` = 'ipaddress'"); + $check_sysip = Database::pexecute_first($check_sysip_stmt); + + // system.mysql_access_host + $check_mysqlip_stmt = Database::prepare("SELECT `value` FROM `panel_settings` WHERE `settinggroup` = 'system' and `varname` = 'mysql_access_host'"); + $check_mysqlip = Database::pexecute_first($check_mysqlip_stmt); + + // system.axfrservers + $check_axfrip_stmt = Database::prepare("SELECT `value` FROM `panel_settings` WHERE `settinggroup` = 'system' and `varname` = 'axfrservers'"); + $check_axfrip = Database::pexecute_first($check_axfrip_stmt); + + foreach ($ips_to_switch as $ip_pair) { + echo "Switching IP \033[1m" . $ip_pair[0] . "\033[0m to IP \033[1m" . $ip_pair[1] . "\033[0m" . PHP_EOL; + Database::pexecute($upd_stmt, array( + 'newip' => $ip_pair[1], + 'oldip' => $ip_pair[0] + )); + $rows_updated = $upd_stmt->rowCount(); + + if ($rows_updated == 0) { + CmdLineHandler::printwarn("Note: " . $ip_pair[0] . " not updated to " . $ip_pair[1] . " (possibly no entry found in froxlor database. Use --list to see what IP addresses are added in froxlor"); + } + + // check whether the system.ipaddress needs updating + if ($check_sysip['value'] == $ip_pair[0]) { + $upd2_stmt = Database::prepare("UPDATE `panel_settings` SET `value` = :newip WHERE `settinggroup` = 'system' and `varname` = 'ipaddress'"); + Database::pexecute($upd2_stmt, array( + 'newip' => $ip_pair[1] + )); + CmdLineHandler::printsucc("Updated system-ipaddress from '" . $ip_pair[0] . "' to '" . $ip_pair[1] . "'"); + } + + // check whether the system.mysql_access_host needs updating + if (strstr($check_mysqlip['value'], $ip_pair[0]) !== false) { + $new_mysqlip = str_replace($ip_pair[0], $ip_pair[1], $check_mysqlip['value']); + $upd2_stmt = Database::prepare("UPDATE `panel_settings` SET `value` = :newmysql WHERE `settinggroup` = 'system' and `varname` = 'mysql_access_host'"); + Database::pexecute($upd2_stmt, array( + 'newmysql' => $new_mysqlip + )); + CmdLineHandler::printsucc("Updated mysql_access_host from '" . $check_mysqlip['value'] . "' to '" . $new_mysqlip . "'"); + } + + // check whether the system.axfrservers needs updating + if (strstr($check_axfrip['value'], $ip_pair[0]) !== false) { + $new_axfrip = str_replace($ip_pair[0], $ip_pair[1], $check_axfrip['value']); + $upd2_stmt = Database::prepare("UPDATE `panel_settings` SET `value` = :newaxfr WHERE `settinggroup` = 'system' and `varname` = 'axfrservers'"); + Database::pexecute($upd2_stmt, array( + 'newaxfr' => $new_axfrip + )); + CmdLineHandler::printsucc("Updated axfrservers from '" . $check_axfrip['value'] . "' to '" . $new_axfrip . "'"); + } + } + } + + echo PHP_EOL; + CmdLineHandler::printwarn("*** ATTENTION *** Remember to replace IP addresses in configuration files if used anywhere."); + CmdLineHandler::printsucc("IP addresses updated"); + } + + private function _parseConfig() + { + define('FROXLOR_INSTALL_DIR', $this->_args['froxlor-dir']); + if (!file_exists(FROXLOR_INSTALL_DIR . '/lib/classes/database/class.Database.php')) { + throw new Exception("Could not find froxlor's Database class. Is froxlor really installed to '".FROXLOR_INSTALL_DIR."'?"); + } + if (!file_exists(FROXLOR_INSTALL_DIR . '/lib/userdata.inc.php')) { + throw new Exception("Could not find froxlor's userdata.inc.php file. You should use this script only with a fully installed and setup froxlor system."); + } + require FROXLOR_INSTALL_DIR . '/lib/classes/database/class.Database.php'; + } + + private function _checkConfigParam($needed = false) + { + if ($needed) { + if (! isset($this->_args["froxlor-dir"])) { + throw new Exception("No configuration given (missing --froxlor-dir parameter?)"); + } elseif (! is_dir($this->_args["froxlor-dir"])) { + throw new Exception("Given --froxlor-dir parameter is not a directory"); + } elseif (! file_exists($this->_args["froxlor-dir"])) { + throw new Exception("Given froxlor directory cannot be found ('" . $this->_args["froxlor-dir"] . "')"); + } elseif (! is_readable($this->_args["froxlor-dir"])) { + throw new Exception("Given froxlor direcotry cannot be read ('" . $this->_args["froxlor-dir"] . "')"); + } + } + } +} From 88ccf5b86980f21bd88b77e15b9947c20dec3b45 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 14 Jun 2016 07:29:38 +0200 Subject: [PATCH 0100/1335] don't generate unnecessary php-related vhost-entries when php is disabled, thx to karstenk Signed-off-by: Michael Kaufmann (d00p) --- scripts/jobs/cron_tasks.inc.http.30.nginx.php | 28 +++++++++++++++---- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/scripts/jobs/cron_tasks.inc.http.30.nginx.php b/scripts/jobs/cron_tasks.inc.http.30.nginx.php index 90ac0dc8..3f723b69 100644 --- a/scripts/jobs/cron_tasks.inc.http.30.nginx.php +++ b/scripts/jobs/cron_tasks.inc.http.30.nginx.php @@ -859,9 +859,24 @@ class nginx extends HttpConfigBase { $this->_deactivated = false; } - $webroot_text .= "\t" . 'index index.php index.html index.htm;'."\n"; + if ($domain['phpenabled'] == '1') + { + $webroot_text .= "\t" . 'index index.php index.html index.htm;'."\n"; + } + else + { + $webroot_text .= "\t" . 'index index.html index.htm;'."\n"; + } $webroot_text .= "\n\t".'location / {'."\n"; - $webroot_text .= "\t\t" . 'try_files $uri $uri/ @rewrites;'."\n"; + $webroot_text .= "\t\t" . 'try_files $uri $uri/'; + if ($domain['phpenabled'] == '1') + { + $webroot_text .= ' @rewrites;'."\n"; + } + else + { + $webroot_text .= ";\n"; + } if ($this->vhost_root_autoindex) { $webroot_text .= "\t\t".'autoindex on;'."\n"; @@ -869,9 +884,12 @@ class nginx extends HttpConfigBase { } $webroot_text .= "\t".'}'."\n\n"; - $webroot_text .= "\tlocation @rewrites {\n"; - $webroot_text .= "\t\trewrite ^ /index.php last;\n"; - $webroot_text .= "\t}\n\n"; + if ($domain['phpenabled'] == '1') + { + $webroot_text .= "\tlocation @rewrites {\n"; + $webroot_text .= "\t\trewrite ^ /index.php last;\n"; + $webroot_text .= "\t}\n\n"; + } return $webroot_text; } From bd36145ad6e91301efeaec73ea5db995c93a2358 Mon Sep 17 00:00:00 2001 From: Daniel Reichelt Date: Fri, 20 May 2016 23:30:18 +0200 Subject: [PATCH 0101/1335] cron_traffic: replace echo by log message --- scripts/jobs/cron_traffic.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/jobs/cron_traffic.php b/scripts/jobs/cron_traffic.php index 4477fab9..282985e5 100644 --- a/scripts/jobs/cron_traffic.php +++ b/scripts/jobs/cron_traffic.php @@ -139,7 +139,7 @@ while ($row_database = $databases_stmt->fetch(PDO::FETCH_ASSOC)) { // sum up result $mysqlusage_all[$row_database['customerid']] += floatval($mysql_usage_row['customerusage']); } else { - echo "Seems like the database " . $row_database['databasename'] . " had been removed manually.\n"; + $cronlog->logAction(CRON_ACTION, LOG_WARNING, "Seems like the database " . $row_database['databasename'] . " had been removed manually."); } } From 843845a825ecd1ccbf7498bad3ba4d8d581d783a Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 16 Jun 2016 11:03:02 +0200 Subject: [PATCH 0102/1335] Update Idna-Converter to version 1.0.2 (default IDNA standard is now 2008) Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/idna/class.idna_convert.php | 3463 ----------------- .../idna/class.idna_convert_wrapper.php | 55 +- lib/classes/idna/ext/EncodingHelper.php | 186 + lib/classes/idna/ext/IdnaConvert.php | 409 ++ lib/classes/idna/ext/NamePrepData.php | 1921 +++++++++ lib/classes/idna/ext/NamePrepData2003.php | 501 +++ .../idna/ext/NamePrepDataInterface.php | 7 + lib/classes/idna/ext/Punycode.php | 542 +++ lib/classes/idna/ext/PunycodeInterface.php | 20 + lib/classes/idna/ext/UnicodeTranscoder.php | 343 ++ .../idna/ext/UnicodeTranscoderInterface.php | 40 + lib/functions.php | 6 + 12 files changed, 4024 insertions(+), 3469 deletions(-) delete mode 100644 lib/classes/idna/class.idna_convert.php create mode 100644 lib/classes/idna/ext/EncodingHelper.php create mode 100644 lib/classes/idna/ext/IdnaConvert.php create mode 100644 lib/classes/idna/ext/NamePrepData.php create mode 100644 lib/classes/idna/ext/NamePrepData2003.php create mode 100644 lib/classes/idna/ext/NamePrepDataInterface.php create mode 100644 lib/classes/idna/ext/Punycode.php create mode 100644 lib/classes/idna/ext/PunycodeInterface.php create mode 100644 lib/classes/idna/ext/UnicodeTranscoder.php create mode 100644 lib/classes/idna/ext/UnicodeTranscoderInterface.php diff --git a/lib/classes/idna/class.idna_convert.php b/lib/classes/idna/class.idna_convert.php deleted file mode 100644 index a244b63f..00000000 --- a/lib/classes/idna/class.idna_convert.php +++ /dev/null @@ -1,3463 +0,0 @@ - - * @copyright 2004-2014 phlyLabs Berlin, http://phlylabs.de - * @version 0.9.0 2014-12-12 - */ -class idna_convert { - - private $version = '0.9.0'; - protected $sub_version = 'main'; - - // NP See below - // Internal settings, do not mess with them - protected $_punycode_prefix = 'xn--'; - protected $_invalid_ucs = 0x80000000; - protected $_max_ucs = 0x10FFFF; - protected $_base = 36; - protected $_tmin = 1; - protected $_tmax = 26; - protected $_skew = 38; - protected $_damp = 700; - protected $_initial_bias = 72; - protected $_initial_n = 0x80; - protected $_sbase = 0xAC00; - protected $_lbase = 0x1100; - protected $_vbase = 0x1161; - protected $_tbase = 0x11A7; - protected $_lcount = 19; - protected $_vcount = 21; - protected $_tcount = 28; - protected $_ncount = 588; // _vcount * _tcount - protected $_scount = 11172; // _lcount * _tcount * _vcount - protected $_error = false; - protected static $_mb_string_overload = null; - // See {@link set_parameter()} for details of how to change the following - // settings from within your script / application - protected $_api_encoding = 'utf8'; // Default input charset is UTF-8 - protected $_allow_overlong = false; // Overlong UTF-8 encodings are forbidden - protected $_strict_mode = false; // Behave strict or not - protected $_idn_version = 2003; // Can be either 2003 (old, default) or 2008 - - /** - * the constructor - * - * @param array $options - * @return boolean - * @since 0.5.2 - */ - public function __construct($options = false) - { - $this->slast = $this->_sbase + $this->_lcount * $this->_vcount * $this->_tcount; - // If parameters are given, pass these to the respective method - if (is_array($options)) { - $this->set_parameter($options); - } - - // populate mbstring overloading cache if not set - if (self::$_mb_string_overload === null) { - self::$_mb_string_overload = (extension_loaded('mbstring') && (ini_get('mbstring.func_overload') & 0x02) === 0x02); - } - } - - public function get_version() - { - return $this->version.'-'.$this->sub_version; - } - - /** - * Sets a new option value. Available options and values: - * [encoding - Use either UTF-8, UCS4 as array or UCS4 as string as input ('utf8' for UTF-8, - * 'ucs4_string' and 'ucs4_array' respectively for UCS4); The output is always UTF-8] - * [overlong - Unicode does not allow unnecessarily long encodings of chars, - * to allow this, set this parameter to true, else to false; - * default is false.] - * [strict - true: strict mode, good for registration purposes - Causes errors - * on failures; false: loose mode, ideal for "wildlife" applications - * by silently ignoring errors and returning the original input instead - * - * @param mixed Parameter to set (string: single parameter; array of Parameter => Value pairs) - * @param string Value to use (if parameter 1 is a string) - * @return boolean true on success, false otherwise - */ - public function set_parameter($option, $value = false) - { - if (!is_array($option)) { - $option = array($option => $value); - } - foreach ($option as $k => $v) { - switch ($k) { - case 'encoding': - switch ($v) { - case 'utf8': - case 'ucs4_string': - case 'ucs4_array': - $this->_api_encoding = $v; - break; - default: - $this->_error('Set Parameter: Unknown parameter ' . $v . ' for option ' . $k); - return false; - } - break; - case 'overlong': - $this->_allow_overlong = ($v) ? true : false; - break; - case 'strict': - $this->_strict_mode = ($v) ? true : false; - break; - case 'idn_version': - if (in_array($v, array('2003', '2008'))) { - $this->_idn_version = $v; - } else { - $this->_error('Set Parameter: Unknown parameter ' . $v . ' for option ' . $k); - } - break; - case 'encode_german_sz': // Deprecated - if (!$v) { - self::$NP['replacemaps'][0xDF] = array(0x73, 0x73); - } else { - unset(self::$NP['replacemaps'][0xDF]); - } - break; - default: - $this->_error('Set Parameter: Unknown option ' . $k); - return false; - } - } - return true; - } - - /** - * Decode a given ACE domain name - * @param string Domain name (ACE string) - * [@param string Desired output encoding, see {@link set_parameter}] - * @return string Decoded Domain name (UTF-8 or UCS-4) - */ - public function decode($input, $one_time_encoding = false) - { - // Optionally set - if ($one_time_encoding) { - switch ($one_time_encoding) { - case 'utf8': - case 'ucs4_string': - case 'ucs4_array': - break; - default: - $this->_error('Unknown encoding ' . $one_time_encoding); - return false; - } - } - // Make sure to drop any newline characters around - $input = trim($input); - - // Negotiate input and try to determine, whether it is a plain string, - // an email address or something like a complete URL - if (strpos($input, '@')) { // Maybe it is an email address - // No no in strict mode - if ($this->_strict_mode) { - $this->_error('Only simple domain name parts can be handled in strict mode'); - return false; - } - list ($email_pref, $input) = explode('@', $input, 2); - $arr = explode('.', $input); - foreach ($arr as $k => $v) { - if (preg_match('!^' . preg_quote($this->_punycode_prefix, '!') . '!', $v)) { - $conv = $this->_decode($v); - if ($conv) { - $arr[$k] = $conv; - } - } - } - $input = join('.', $arr); - $arr = explode('.', $email_pref); - foreach ($arr as $k => $v) { - if (preg_match('!^' . preg_quote($this->_punycode_prefix, '!') . '!', $v)) { - $conv = $this->_decode($v); - if ($conv) { - $arr[$k] = $conv; - } - } - } - $email_pref = join('.', $arr); - $return = $email_pref . '@' . $input; - } elseif (preg_match('![:\./]!', $input)) { // Or a complete domain name (with or without paths / parameters) - // No no in strict mode - if ($this->_strict_mode) { - $this->_error('Only simple domain name parts can be handled in strict mode'); - return false; - } - $parsed = parse_url($input); - if (isset($parsed['host'])) { - $arr = explode('.', $parsed['host']); - foreach ($arr as $k => $v) { - $conv = $this->_decode($v); - if ($conv) { - $arr[$k] = $conv; - } - } - $parsed['host'] = join('.', $arr); - $return = (empty($parsed['scheme']) ? '' : $parsed['scheme'] . (strtolower($parsed['scheme']) == 'mailto' ? ':' : '://')). - (empty($parsed['user']) ? '' : $parsed['user'] . (empty($parsed['pass']) ? '' : ':' . $parsed['pass']) . '@'). - $parsed['host']. - (empty($parsed['port']) ? '' : ':' . $parsed['port']). - (empty($parsed['path']) ? '' : $parsed['path']). - (empty($parsed['query']) ? '' : '?' . $parsed['query']). - (empty($parsed['fragment']) ? '' : '#' . $parsed['fragment']); - } else { // parse_url seems to have failed, try without it - $arr = explode('.', $input); - foreach ($arr as $k => $v) { - $conv = $this->_decode($v); - $arr[$k] = ($conv) ? $conv : $v; - } - $return = join('.', $arr); - } - } else { // Otherwise we consider it being a pure domain name string - $return = $this->_decode($input); - if (!$return) { - $return = $input; - } - } - // The output is UTF-8 by default, other output formats need conversion here - // If one time encoding is given, use this, else the objects property - switch (($one_time_encoding) ? $one_time_encoding : $this->_api_encoding) { - case 'utf8': return $return; // break; - case 'ucs4_string': return $this->_ucs4_to_ucs4_string($this->_utf8_to_ucs4($return)); // break; - case 'ucs4_array': return $this->_utf8_to_ucs4($return); // break; - default: $this->_error('Unsupported output format'); return false; - } - } - - /** - * Encode a given UTF-8 domain name - * @param string Domain name (UTF-8 or UCS-4) - * [@param string Desired input encoding, see {@link set_parameter}] - * @return string Encoded Domain name (ACE string) - */ - public function encode($decoded, $one_time_encoding = false) - { - // Forcing conversion of input to UCS4 array - // If one time encoding is given, use this, else the objects property - switch ($one_time_encoding ? $one_time_encoding : $this->_api_encoding) { - case 'utf8': - $decoded = $this->_utf8_to_ucs4($decoded); - break; - case 'ucs4_string': - $decoded = $this->_ucs4_string_to_ucs4($decoded); - case 'ucs4_array': - break; - default: - $this->_error('Unsupported input format: ' . ($one_time_encoding ? $one_time_encoding : $this->_api_encoding)); - return false; - } - - // No input, no output, what else did you expect? - if (empty($decoded)) { - return ''; - } - - // Anchors for iteration - $last_begin = 0; - // Output string - $output = ''; - foreach ($decoded as $k => $v) { - // Make sure to use just the plain dot - switch ($v) { - case 0x3002: - case 0xFF0E: - case 0xFF61: - $decoded[$k] = 0x2E; - // Right, no break here, the above are converted to dots anyway - // Stumbling across an anchoring character - case 0x2E: - case 0x2F: - case 0x3A: - case 0x3F: - case 0x40: - // Neither email addresses nor URLs allowed in strict mode - if ($this->_strict_mode) { - $this->_error('Neither email addresses nor URLs are allowed in strict mode.'); - return false; - } else { - // Skip first char - if ($k) { - $encoded = ''; - $encoded = $this->_encode(array_slice($decoded, $last_begin, (($k) - $last_begin))); - if ($encoded) { - $output .= $encoded; - } else { - $output .= $this->_ucs4_to_utf8(array_slice($decoded, $last_begin, (($k) - $last_begin))); - } - $output .= chr($decoded[$k]); - } - $last_begin = $k + 1; - } - } - } - // Catch the rest of the string - if ($last_begin) { - $inp_len = sizeof($decoded); - $encoded = ''; - $encoded = $this->_encode(array_slice($decoded, $last_begin, (($inp_len) - $last_begin))); - if ($encoded) { - $output .= $encoded; - } else { - $output .= $this->_ucs4_to_utf8(array_slice($decoded, $last_begin, (($inp_len) - $last_begin))); - } - return $output; - } else { - if (false !== ($output = $this->_encode($decoded))) { - return $output; - } else { - return $this->_ucs4_to_utf8($decoded); - } - } - } - - /** - * Removes a weakness of encode(), which cannot properly handle URIs but instead encodes their - * path or query components, too. - * @param string $uri Expects the URI as a UTF-8 (or ASCII) string - * @return string The URI encoded to Punycode, everything but the host component is left alone - * @since 0.6.4 - */ - public function encode_uri($uri) - { - $parsed = parse_url($uri); - if (!isset($parsed['host'])) { - $this->_error('The given string does not look like a URI'); - return false; - } - $arr = explode('.', $parsed['host']); - foreach ($arr as $k => $v) { - $conv = $this->encode($v, 'utf8'); - if ($conv) { - $arr[$k] = $conv; - } - } - $parsed['host'] = join('.', $arr); - $return = (empty($parsed['scheme']) ? '' : $parsed['scheme'] . (strtolower($parsed['scheme']) == 'mailto' ? ':' : '://')). - (empty($parsed['user']) ? '' : $parsed['user'] . (empty($parsed['pass']) ? '' : ':' . $parsed['pass']) . '@'). - $parsed['host']. - (empty($parsed['port']) ? '' : ':' . $parsed['port']). - (empty($parsed['path']) ? '' : $parsed['path']). - (empty($parsed['query']) ? '' : '?' . $parsed['query']). - (empty($parsed['fragment']) ? '' : '#' . $parsed['fragment']); - return $return; - } - - /** - * Use this method to get the last error occurred - * @param void - * @return string The last error, that occurred - */ - public function get_last_error() - { - return $this->_error; - } - - /** - * The actual decoding algorithm - * @param string - * @return mixed - */ - protected function _decode($encoded) - { - $decoded = array(); - // find the Punycode prefix - if (!preg_match('!^' . preg_quote($this->_punycode_prefix, '!') . '!', $encoded)) { - $this->_error('This is not a punycode string'); - return false; - } - $encode_test = preg_replace('!^' . preg_quote($this->_punycode_prefix, '!') . '!', '', $encoded); - // If nothing left after removing the prefix, it is hopeless - if (!$encode_test) { - $this->_error('The given encoded string was empty'); - return false; - } - // Find last occurrence of the delimiter - $delim_pos = strrpos($encoded, '-'); - if ($delim_pos > self::byteLength($this->_punycode_prefix)) { - for ($k = self::byteLength($this->_punycode_prefix); $k < $delim_pos; ++$k) { - $decoded[] = ord($encoded{$k}); - } - } - $deco_len = count($decoded); - $enco_len = self::byteLength($encoded); - - // Wandering through the strings; init - $is_first = true; - $bias = $this->_initial_bias; - $idx = 0; - $char = $this->_initial_n; - - for ($enco_idx = ($delim_pos) ? ($delim_pos + 1) : 0; $enco_idx < $enco_len; ++$deco_len) { - for ($old_idx = $idx, $w = 1, $k = $this->_base; 1; $k += $this->_base) { - $digit = $this->_decode_digit($encoded{$enco_idx++}); - $idx += $digit * $w; - $t = ($k <= $bias) ? $this->_tmin : - (($k >= $bias + $this->_tmax) ? $this->_tmax : ($k - $bias)); - if ($digit < $t) { - break; - } - $w = (int) ($w * ($this->_base - $t)); - } - $bias = $this->_adapt($idx - $old_idx, $deco_len + 1, $is_first); - $is_first = false; - $char += (int) ($idx / ($deco_len + 1)); - $idx %= ($deco_len + 1); - if ($deco_len > 0) { - // Make room for the decoded char - for ($i = $deco_len; $i > $idx; $i--) { - $decoded[$i] = $decoded[($i - 1)]; - } - } - $decoded[$idx++] = $char; - } - return $this->_ucs4_to_utf8($decoded); - } - - /** - * The actual encoding algorithm - * @param string - * @return mixed - */ - protected function _encode($decoded) - { - // We cannot encode a domain name containing the Punycode prefix - $extract = self::byteLength($this->_punycode_prefix); - $check_pref = $this->_utf8_to_ucs4($this->_punycode_prefix); - $check_deco = array_slice($decoded, 0, $extract); - - if ($check_pref == $check_deco) { - $this->_error('This is already a punycode string'); - return false; - } - // We will not try to encode strings consisting of basic code points only - $encodable = false; - foreach ($decoded as $k => $v) { - if ($v > 0x7a) { - $encodable = true; - break; - } - } - if (!$encodable) { - $this->_error('The given string does not contain encodable chars'); - return false; - } - // Do NAMEPREP - $decoded = $this->_nameprep($decoded); - if (!$decoded || !is_array($decoded)) { - return false; // NAMEPREP failed - } - $deco_len = count($decoded); - if (!$deco_len) { - return false; // Empty array - } - $codecount = 0; // How many chars have been consumed - $encoded = ''; - // Copy all basic code points to output - for ($i = 0; $i < $deco_len; ++$i) { - $test = $decoded[$i]; - // Will match [-0-9a-zA-Z] - if ((0x2F < $test && $test < 0x40) || (0x40 < $test && $test < 0x5B) || (0x60 < $test && $test <= 0x7B) || (0x2D == $test)) { - $encoded .= chr($decoded[$i]); - $codecount++; - } - } - if ($codecount == $deco_len) { - return $encoded; // All codepoints were basic ones - } - // Start with the prefix; copy it to output - $encoded = $this->_punycode_prefix . $encoded; - // If we have basic code points in output, add an hyphen to the end - if ($codecount) { - $encoded .= '-'; - } - // Now find and encode all non-basic code points - $is_first = true; - $cur_code = $this->_initial_n; - $bias = $this->_initial_bias; - $delta = 0; - while ($codecount < $deco_len) { - // Find the smallest code point >= the current code point and - // remember the last ouccrence of it in the input - for ($i = 0, $next_code = $this->_max_ucs; $i < $deco_len; $i++) { - if ($decoded[$i] >= $cur_code && $decoded[$i] <= $next_code) { - $next_code = $decoded[$i]; - } - } - $delta += ($next_code - $cur_code) * ($codecount + 1); - $cur_code = $next_code; - - // Scan input again and encode all characters whose code point is $cur_code - for ($i = 0; $i < $deco_len; $i++) { - if ($decoded[$i] < $cur_code) { - $delta++; - } elseif ($decoded[$i] == $cur_code) { - for ($q = $delta, $k = $this->_base; 1; $k += $this->_base) { - $t = ($k <= $bias) ? $this->_tmin : - (($k >= $bias + $this->_tmax) ? $this->_tmax : $k - $bias); - if ($q < $t) { - break; - } - $encoded .= $this->_encode_digit(intval($t + (($q - $t) % ($this->_base - $t)))); //v0.4.5 Changed from ceil() to intval() - $q = (int) (($q - $t) / ($this->_base - $t)); - } - $encoded .= $this->_encode_digit($q); - $bias = $this->_adapt($delta, $codecount + 1, $is_first); - $codecount++; - $delta = 0; - $is_first = false; - } - } - $delta++; - $cur_code++; - } - return $encoded; - } - - /** - * Adapt the bias according to the current code point and position - * @param int $delta - * @param int $npoints - * @param int $is_first - * @return int - */ - protected function _adapt($delta, $npoints, $is_first) - { - $delta = intval($is_first ? ($delta / $this->_damp) : ($delta / 2)); - $delta += intval($delta / $npoints); - for ($k = 0; $delta > (($this->_base - $this->_tmin) * $this->_tmax) / 2; $k += $this->_base) { - $delta = intval($delta / ($this->_base - $this->_tmin)); - } - return intval($k + ($this->_base - $this->_tmin + 1) * $delta / ($delta + $this->_skew)); - } - - /** - * Encoding a certain digit - * @param int $d - * @return string - */ - protected function _encode_digit($d) - { - return chr($d + 22 + 75 * ($d < 26)); - } - - /** - * Decode a certain digit - * @param int $cp - * @return int - */ - protected function _decode_digit($cp) - { - $cp = ord($cp); - return ($cp - 48 < 10) ? $cp - 22 : (($cp - 65 < 26) ? $cp - 65 : (($cp - 97 < 26) ? $cp - 97 : $this->_base)); - } - - /** - * Internal error handling method - * @param string $error - */ - protected function _error($error = '') - { - $this->_error = $error; - } - - /** - * Do Nameprep according to RFC3491 and RFC3454 - * @param array Unicode Characters - * @return string Unicode Characters, Nameprep'd - */ - protected function _nameprep($input) - { - $output = array(); - // - // Mapping - // Walking through the input array, performing the required steps on each of - // the input chars and putting the result into the output array - // While mapping required chars we apply the canonical ordering - foreach ($input as $v) { - // Map to nothing == skip that code point - if (in_array($v, self::$NP['map_nothing'])) { - continue; - } - // Try to find prohibited input - if (in_array($v, self::$NP['prohibit']) || in_array($v, self::$NP['general_prohibited'])) { - $this->_error('NAMEPREP: Prohibited input U+' . sprintf('%08X', $v)); - return false; - } - foreach (self::$NP['prohibit_ranges'] as $range) { - if ($range[0] <= $v && $v <= $range[1]) { - $this->_error('NAMEPREP: Prohibited input U+' . sprintf('%08X', $v)); - return false; - } - } - - if (0xAC00 <= $v && $v <= 0xD7AF) { - // Hangul syllable decomposition - foreach ($this->_hangul_decompose($v) as $out) { - $output[] = (int) $out; - } - } elseif (($this->_idn_version == '2003') && isset(self::$NP['replacemaps_2003'][$v])) { - foreach ($this->_apply_canonical_ordering(self::$NP['replacemaps_2003'][$v]) as $out) { - $output[] = (int) $out; - } - } elseif (($this->_idn_version == '2008') && isset(self::$NP['replacemaps'][$v])) { - foreach ($this->_apply_canonical_ordering(self::$NP['replacemaps'][$v]) as $out) { - $output[] = (int) $out; - } - } else { - $output[] = (int) $v; - } - } - // Before applying any Combining, try to rearrange any Hangul syllables - $output = $this->_hangul_compose($output); - // - // Combine code points - // - $last_class = 0; - $last_starter = 0; - $out_len = count($output); - for ($i = 0; $i < $out_len; ++$i) { - $class = $this->_get_combining_class($output[$i]); - if ((!$last_class || $last_class > $class) && $class) { - // Try to match - $seq_len = $i - $last_starter; - $out = $this->_combine(array_slice($output, $last_starter, $seq_len)); - // On match: Replace the last starter with the composed character and remove - // the now redundant non-starter(s) - if ($out) { - $output[$last_starter] = $out; - if (count($out) != $seq_len) { - for ($j = $i + 1; $j < $out_len; ++$j) { - $output[$j - 1] = $output[$j]; - } - unset($output[$out_len]); - } - // Rewind the for loop by one, since there can be more possible compositions - $i--; - $out_len--; - $last_class = ($i == $last_starter) ? 0 : $this->_get_combining_class($output[$i - 1]); - continue; - } - } - // The current class is 0 - if (!$class) { - $last_starter = $i; - } - $last_class = $class; - } - return $output; - } - - /** - * Decomposes a Hangul syllable - * (see http://www.unicode.org/unicode/reports/tr15/#Hangul - * @param integer 32bit UCS4 code point - * @return array Either Hangul Syllable decomposed or original 32bit value as one value array - */ - protected function _hangul_decompose($char) - { - $sindex = (int) $char - $this->_sbase; - if ($sindex < 0 || $sindex >= $this->_scount) { - return array($char); - } - $result = array(); - $result[] = (int) $this->_lbase + $sindex / $this->_ncount; - $result[] = (int) $this->_vbase + ($sindex % $this->_ncount) / $this->_tcount; - $T = intval($this->_tbase + $sindex % $this->_tcount); - if ($T != $this->_tbase) { - $result[] = $T; - } - return $result; - } - - /** - * Ccomposes a Hangul syllable - * (see http://www.unicode.org/unicode/reports/tr15/#Hangul - * @param array Decomposed UCS4 sequence - * @return array UCS4 sequence with syllables composed - */ - protected function _hangul_compose($input) - { - $inp_len = count($input); - if (!$inp_len) { - return array(); - } - $result = array(); - $last = (int) $input[0]; - $result[] = $last; // copy first char from input to output - - for ($i = 1; $i < $inp_len; ++$i) { - $char = (int) $input[$i]; - $sindex = $last - $this->_sbase; - $lindex = $last - $this->_lbase; - $vindex = $char - $this->_vbase; - $tindex = $char - $this->_tbase; - // Find out, whether two current characters are LV and T - if (0 <= $sindex && $sindex < $this->_scount && ($sindex % $this->_tcount == 0) && 0 <= $tindex && $tindex <= $this->_tcount) { - // create syllable of form LVT - $last += $tindex; - $result[(count($result) - 1)] = $last; // reset last - continue; // discard char - } - // Find out, whether two current characters form L and V - if (0 <= $lindex && $lindex < $this->_lcount && 0 <= $vindex && $vindex < $this->_vcount) { - // create syllable of form LV - $last = (int) $this->_sbase + ($lindex * $this->_vcount + $vindex) * $this->_tcount; - $result[(count($result) - 1)] = $last; // reset last - continue; // discard char - } - // if neither case was true, just add the character - $last = $char; - $result[] = $char; - } - return $result; - } - - /** - * Returns the combining class of a certain wide char - * @param integer Wide char to check (32bit integer) - * @return integer Combining class if found, else 0 - */ - protected function _get_combining_class($char) - { - return isset(self::$NP['norm_combcls'][$char]) ? self::$NP['norm_combcls'][$char] : 0; - } - - /** - * Applies the canonical ordering of a decomposed UCS4 sequence - * @param array Decomposed UCS4 sequence - * @return array Ordered USC4 sequence - */ - protected function _apply_canonical_ordering($input) - { - $swap = true; - $size = count($input); - while ($swap) { - $swap = false; - $last = $this->_get_combining_class(intval($input[0])); - for ($i = 0; $i < $size - 1; ++$i) { - $next = $this->_get_combining_class(intval($input[$i + 1])); - if ($next != 0 && $last > $next) { - // Move item leftward until it fits - for ($j = $i + 1; $j > 0; --$j) { - if ($this->_get_combining_class(intval($input[$j - 1])) <= $next) { - break; - } - $t = intval($input[$j]); - $input[$j] = intval($input[$j - 1]); - $input[$j - 1] = $t; - $swap = true; - } - // Reentering the loop looking at the old character again - $next = $last; - } - $last = $next; - } - } - return $input; - } - - /** - * Do composition of a sequence of starter and non-starter - * @param array UCS4 Decomposed sequence - * @return array Ordered USC4 sequence - */ - protected function _combine($input) - { - $inp_len = count($input); - if (0 == $inp_len) { - return false; - } - foreach (self::$NP['replacemaps'] as $np_src => $np_target) { - if ($np_target[0] != $input[0]) { - continue; - } - if (count($np_target) != $inp_len) { - continue; - } - $hit = false; - foreach ($input as $k2 => $v2) { - if ($v2 == $np_target[$k2]) { - $hit = true; - } else { - $hit = false; - break; - } - } - if ($hit) { - return $np_src; - } - } - return false; - } - - /** - * This converts an UTF-8 encoded string to its UCS-4 representation - * By talking about UCS-4 "strings" we mean arrays of 32bit integers representing - * each of the "chars". This is due to PHP not being able to handle strings with - * bit depth different from 8. This apllies to the reverse method _ucs4_to_utf8(), too. - * The following UTF-8 encodings are supported: - * bytes bits representation - * 1 7 0xxxxxxx - * 2 11 110xxxxx 10xxxxxx - * 3 16 1110xxxx 10xxxxxx 10xxxxxx - * 4 21 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx - * 5 26 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx - * 6 31 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx - * Each x represents a bit that can be used to store character data. - * The five and six byte sequences are part of Annex D of ISO/IEC 10646-1:2000 - * @param string $input - * @return string - */ - protected function _utf8_to_ucs4($input) - { - $output = array(); - $out_len = 0; - $inp_len = self::byteLength($input); - $mode = 'next'; - $test = 'none'; - for ($k = 0; $k < $inp_len; ++$k) { - $v = ord($input{$k}); // Extract byte from input string - if ($v < 128) { // We found an ASCII char - put into stirng as is - $output[$out_len] = $v; - ++$out_len; - if ('add' == $mode) { - $this->_error('Conversion from UTF-8 to UCS-4 failed: malformed input at byte ' . $k); - return false; - } - continue; - } - if ('next' == $mode) { // Try to find the next start byte; determine the width of the Unicode char - $start_byte = $v; - $mode = 'add'; - $test = 'range'; - if ($v >> 5 == 6) { // &110xxxxx 10xxxxx - $next_byte = 0; // Tells, how many times subsequent bitmasks must rotate 6bits to the left - $v = ($v - 192) << 6; - } elseif ($v >> 4 == 14) { // &1110xxxx 10xxxxxx 10xxxxxx - $next_byte = 1; - $v = ($v - 224) << 12; - } elseif ($v >> 3 == 30) { // &11110xxx 10xxxxxx 10xxxxxx 10xxxxxx - $next_byte = 2; - $v = ($v - 240) << 18; - } elseif ($v >> 2 == 62) { // &111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx - $next_byte = 3; - $v = ($v - 248) << 24; - } elseif ($v >> 1 == 126) { // &1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx - $next_byte = 4; - $v = ($v - 252) << 30; - } else { - $this->_error('This might be UTF-8, but I don\'t understand it at byte ' . $k); - return false; - } - if ('add' == $mode) { - $output[$out_len] = (int) $v; - ++$out_len; - continue; - } - } - if ('add' == $mode) { - if (!$this->_allow_overlong && $test == 'range') { - $test = 'none'; - if (($v < 0xA0 && $start_byte == 0xE0) || ($v < 0x90 && $start_byte == 0xF0) || ($v > 0x8F && $start_byte == 0xF4)) { - $this->_error('Bogus UTF-8 character detected (out of legal range) at byte ' . $k); - return false; - } - } - if ($v >> 6 == 2) { // Bit mask must be 10xxxxxx - $v = ($v - 128) << ($next_byte * 6); - $output[($out_len - 1)] += $v; - --$next_byte; - } else { - $this->_error('Conversion from UTF-8 to UCS-4 failed: malformed input at byte ' . $k); - return false; - } - if ($next_byte < 0) { - $mode = 'next'; - } - } - } // for - return $output; - } - - /** - * Convert UCS-4 string into UTF-8 string - * See _utf8_to_ucs4() for details - * @param string $input - * @return string - */ - protected function _ucs4_to_utf8($input) - { - $output = ''; - foreach ($input as $k => $v) { - if ($v < 128) { // 7bit are transferred literally - $output .= chr($v); - } elseif ($v < (1 << 11)) { // 2 bytes - $output .= chr(192 + ($v >> 6)) . chr(128 + ($v & 63)); - } elseif ($v < (1 << 16)) { // 3 bytes - $output .= chr(224 + ($v >> 12)) . chr(128 + (($v >> 6) & 63)) . chr(128 + ($v & 63)); - } elseif ($v < (1 << 21)) { // 4 bytes - $output .= chr(240 + ($v >> 18)) . chr(128 + (($v >> 12) & 63)) . chr(128 + (($v >> 6) & 63)) . chr(128 + ($v & 63)); - } else { - $this->_error('Conversion from UCS-4 to UTF-8 failed: malformed input at byte ' . $k); - return false; - } - } - return $output; - } - - /** - * Convert UCS-4 array into UCS-4 string - * - * @param array $input - * @return string - */ - protected function _ucs4_to_ucs4_string($input) - { - $output = ''; - // Take array values and split output to 4 bytes per value - // The bit mask is 255, which reads &11111111 - foreach ($input as $v) { - $output .= chr(($v >> 24) & 255) . chr(($v >> 16) & 255) . chr(($v >> 8) & 255) . chr($v & 255); - } - return $output; - } - - /** - * Convert UCS-4 strin into UCS-4 garray - * - * @param string $input - * @return array - */ - protected function _ucs4_string_to_ucs4($input) - { - $output = array(); - $inp_len = self::byteLength($input); - // Input length must be dividable by 4 - if ($inp_len % 4) { - $this->_error('Input UCS4 string is broken'); - return false; - } - // Empty input - return empty output - if (!$inp_len) { - return $output; - } - for ($i = 0, $out_len = -1; $i < $inp_len; ++$i) { - // Increment output position every 4 input bytes - if (!($i % 4)) { - $out_len++; - $output[$out_len] = 0; - } - $output[$out_len] += ord($input{$i}) << (8 * (3 - ($i % 4) ) ); - } - return $output; - } - - /** - * Gets the length of a string in bytes even if mbstring function - * overloading is turned on - * - * @param string $string the string for which to get the length. - * @return integer the length of the string in bytes. - */ - protected static function byteLength($string) - { - if (self::$_mb_string_overload) { - return mb_strlen($string, '8bit'); - } - return strlen((binary) $string); - } - - /** - * Attempts to return a concrete IDNA instance. - * - * @param array $params Set of paramaters - * @return idna_convert - * @access public - */ - public function getInstance($params = array()) - { - return new idna_convert($params); - } - - /** - * Attempts to return a concrete IDNA instance for either php4 or php5, - * only creating a new instance if no IDNA instance with the same - * parameters currently exists. - * - * @param array $params Set of paramaters - * - * @return object idna_convert - * @access public - */ - public function singleton($params = array()) - { - static $instances = array(); - - $signature = serialize($params); - if (!isset($instances[$signature])) { - $instances[$signature] = idna_convert::getInstance($params); - } - return $instances[$signature]; - } - - /** - * Holds all relevant mapping tables - * See RFC3454 for details - * - * @private array - * @since 0.5.2 - */ - protected static $NP = array( - 'map_nothing' => array(0xAD, 0x34F, 0x1806, 0x180B, 0x180C, 0x180D, 0x200B, 0x200C, - 0x200D, 0x2060, 0xFE00, 0xFE01, 0xFE02, 0xFE03, 0xFE04, 0xFE05, 0xFE06, 0xFE07, - 0xFE08, 0xFE09, 0xFE0A, 0xFE0B, 0xFE0C, 0xFE0D, 0xFE0E, 0xFE0F, 0xFEFF - ), - 'general_prohibited' => array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, - 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, - 43, 44, 47, 59, 60, 61, 62, 63, 64, 91, 92, 93, 94, 95, 96, 123, 124, 125, 126, 127, 0x3002 - ), - 'prohibit' => array(0xA0, 0x340, 0x341, 0x6DD, 0x70F, 0x1680, 0x180E, 0x2000, 0x2001, 0x2002, 0x2003, - 0x2004, 0x2005, 0x2006, 0x2007, 0x2008, 0x2009, 0x200A, 0x200B, 0x200C, 0x200D, 0x200E, 0x200F, - 0x2028, 0x2029, 0x202A, 0x202B, 0x202C, 0x202D, 0x202E, 0x202F, 0x205F, 0x206A, 0x206B, 0x206C, - 0x206D, 0x206E, 0x206F, 0x3000, 0x33C2, 0xFEFF, 0xFFF9, 0xFFFA, 0xFFFB, 0xFFFC, 0xFFFD, 0xFFFE, 0xFFFF, - 0x1FFFE, 0x1FFFF, 0x2FFFE, 0x2FFFF, 0x3FFFE, 0x3FFFF, 0x4FFFE, 0x4FFFF, 0x5FFFE, 0x5FFFF, 0x6FFFE, - 0x6FFFF, 0x7FFFE, 0x7FFFF, 0x8FFFE, 0x8FFFF, 0x9FFFE, 0x9FFFF, 0xAFFFE, 0xAFFFF, 0xBFFFE, 0xBFFFF, - 0xCFFFE, 0xCFFFF, 0xDFFFE, 0xDFFFF, 0xE0001, 0xEFFFE, 0xEFFFF, 0xFFFFE, 0xFFFFF, 0x10FFFE, 0x10FFFF - ), - 'prohibit_ranges' => array(array(0x80, 0x9F), array(0x2060, 0x206F), array(0x1D173, 0x1D17A), - array(0xE000, 0xF8FF), array(0xF0000, 0xFFFFD), array(0x100000, 0x10FFFD), - array(0xFDD0, 0xFDEF), array(0xD800, 0xDFFF), array(0x2FF0, 0x2FFB), array(0xE0020, 0xE007F) - ), - 'replacemaps_2003' => array(0x41 => array(0x61), 0x42 => array(0x62), 0x43 => array(0x63), - 0x44 => array(0x64), 0x45 => array(0x65), 0x46 => array(0x66), 0x47 => array(0x67), - 0x48 => array(0x68), 0x49 => array(0x69), 0x4A => array(0x6A), 0x4B => array(0x6B), - 0x4C => array(0x6C), 0x4D => array(0x6D), 0x4E => array(0x6E), 0x4F => array(0x6F), - 0x50 => array(0x70), 0x51 => array(0x71), 0x52 => array(0x72), 0x53 => array(0x73), - 0x54 => array(0x74), 0x55 => array(0x75), 0x56 => array(0x76), 0x57 => array(0x77), - 0x58 => array(0x78), 0x59 => array(0x79), 0x5A => array(0x7A), 0xB5 => array(0x3BC), - 0xC0 => array(0xE0), 0xC1 => array(0xE1), 0xC2 => array(0xE2), 0xC3 => array(0xE3), - 0xC4 => array(0xE4), 0xC5 => array(0xE5), 0xC6 => array(0xE6), 0xC7 => array(0xE7), - 0xC8 => array(0xE8), 0xC9 => array(0xE9), 0xCA => array(0xEA), 0xCB => array(0xEB), - 0xCC => array(0xEC), 0xCD => array(0xED), 0xCE => array(0xEE), 0xCF => array(0xEF), - 0xD0 => array(0xF0), 0xD1 => array(0xF1), 0xD2 => array(0xF2), 0xD3 => array(0xF3), - 0xD4 => array(0xF4), 0xD5 => array(0xF5), 0xD6 => array(0xF6), 0xD8 => array(0xF8), - 0xD9 => array(0xF9), 0xDA => array(0xFA), 0xDB => array(0xFB), 0xDC => array(0xFC), - 0xDD => array(0xFD), 0xDE => array(0xFE), 0xDF => array(0x73, 0x73), - 0x100 => array(0x101), 0x102 => array(0x103), 0x104 => array(0x105), - 0x106 => array(0x107), 0x108 => array(0x109), 0x10A => array(0x10B), - 0x10C => array(0x10D), 0x10E => array(0x10F), 0x110 => array(0x111), - 0x112 => array(0x113), 0x114 => array(0x115), 0x116 => array(0x117), - 0x118 => array(0x119), 0x11A => array(0x11B), 0x11C => array(0x11D), - 0x11E => array(0x11F), 0x120 => array(0x121), 0x122 => array(0x123), - 0x124 => array(0x125), 0x126 => array(0x127), 0x128 => array(0x129), - 0x12A => array(0x12B), 0x12C => array(0x12D), 0x12E => array(0x12F), - 0x130 => array(0x69, 0x307), 0x132 => array(0x133), 0x134 => array(0x135), - 0x136 => array(0x137), 0x139 => array(0x13A), 0x13B => array(0x13C), - 0x13D => array(0x13E), 0x13F => array(0x140), 0x141 => array(0x142), - 0x143 => array(0x144), 0x145 => array(0x146), 0x147 => array(0x148), - 0x149 => array(0x2BC, 0x6E), 0x14A => array(0x14B), 0x14C => array(0x14D), - 0x14E => array(0x14F), 0x150 => array(0x151), 0x152 => array(0x153), - 0x154 => array(0x155), 0x156 => array(0x157), 0x158 => array(0x159), - 0x15A => array(0x15B), 0x15C => array(0x15D), 0x15E => array(0x15F), - 0x160 => array(0x161), 0x162 => array(0x163), 0x164 => array(0x165), - 0x166 => array(0x167), 0x168 => array(0x169), 0x16A => array(0x16B), - 0x16C => array(0x16D), 0x16E => array(0x16F), 0x170 => array(0x171), - 0x172 => array(0x173), 0x174 => array(0x175), 0x176 => array(0x177), - 0x178 => array(0xFF), 0x179 => array(0x17A), 0x17B => array(0x17C), - 0x17D => array(0x17E), 0x17F => array(0x73), 0x181 => array(0x253), - 0x182 => array(0x183), 0x184 => array(0x185), 0x186 => array(0x254), - 0x187 => array(0x188), 0x189 => array(0x256), 0x18A => array(0x257), - 0x18B => array(0x18C), 0x18E => array(0x1DD), 0x18F => array(0x259), - 0x190 => array(0x25B), 0x191 => array(0x192), 0x193 => array(0x260), - 0x194 => array(0x263), 0x196 => array(0x269), 0x197 => array(0x268), - 0x198 => array(0x199), 0x19C => array(0x26F), 0x19D => array(0x272), - 0x19F => array(0x275), 0x1A0 => array(0x1A1), 0x1A2 => array(0x1A3), - 0x1A4 => array(0x1A5), 0x1A6 => array(0x280), 0x1A7 => array(0x1A8), - 0x1A9 => array(0x283), 0x1AC => array(0x1AD), 0x1AE => array(0x288), - 0x1AF => array(0x1B0), 0x1B1 => array(0x28A), 0x1B2 => array(0x28B), - 0x1B3 => array(0x1B4), 0x1B5 => array(0x1B6), 0x1B7 => array(0x292), - 0x1B8 => array(0x1B9), 0x1BC => array(0x1BD), 0x1C4 => array(0x1C6), - 0x1C5 => array(0x1C6), 0x1C7 => array(0x1C9), 0x1C8 => array(0x1C9), - 0x1CA => array(0x1CC), 0x1CB => array(0x1CC), 0x1CD => array(0x1CE), - 0x1CF => array(0x1D0), 0x1D1 => array(0x1D2), 0x1D3 => array(0x1D4), - 0x1D5 => array(0x1D6), 0x1D7 => array(0x1D8), 0x1D9 => array(0x1DA), - 0x1DB => array(0x1DC), 0x1DE => array(0x1DF), 0x1E0 => array(0x1E1), - 0x1E2 => array(0x1E3), 0x1E4 => array(0x1E5), 0x1E6 => array(0x1E7), - 0x1E8 => array(0x1E9), 0x1EA => array(0x1EB), 0x1EC => array(0x1ED), - 0x1EE => array(0x1EF), 0x1F0 => array(0x6A, 0x30C), 0x1F1 => array(0x1F3), - 0x1F2 => array(0x1F3), 0x1F4 => array(0x1F5), 0x1F6 => array(0x195), - 0x1F7 => array(0x1BF), 0x1F8 => array(0x1F9), 0x1FA => array(0x1FB), - 0x1FC => array(0x1FD), 0x1FE => array(0x1FF), 0x200 => array(0x201), - 0x202 => array(0x203), 0x204 => array(0x205), 0x206 => array(0x207), - 0x208 => array(0x209), 0x20A => array(0x20B), 0x20C => array(0x20D), - 0x20E => array(0x20F), 0x210 => array(0x211), 0x212 => array(0x213), - 0x214 => array(0x215), 0x216 => array(0x217), 0x218 => array(0x219), - 0x21A => array(0x21B), 0x21C => array(0x21D), 0x21E => array(0x21F), - 0x220 => array(0x19E), 0x222 => array(0x223), 0x224 => array(0x225), - 0x226 => array(0x227), 0x228 => array(0x229), 0x22A => array(0x22B), - 0x22C => array(0x22D), 0x22E => array(0x22F), 0x230 => array(0x231), - 0x232 => array(0x233), 0x345 => array(0x3B9), 0x37A => array(0x20, 0x3B9), - 0x386 => array(0x3AC), 0x388 => array(0x3AD), 0x389 => array(0x3AE), - 0x38A => array(0x3AF), 0x38C => array(0x3CC), 0x38E => array(0x3CD), - 0x38F => array(0x3CE), 0x390 => array(0x3B9, 0x308, 0x301), - 0x391 => array(0x3B1), 0x392 => array(0x3B2), 0x393 => array(0x3B3), - 0x394 => array(0x3B4), 0x395 => array(0x3B5), 0x396 => array(0x3B6), - 0x397 => array(0x3B7), 0x398 => array(0x3B8), 0x399 => array(0x3B9), - 0x39A => array(0x3BA), 0x39B => array(0x3BB), 0x39C => array(0x3BC), - 0x39D => array(0x3BD), 0x39E => array(0x3BE), 0x39F => array(0x3BF), - 0x3A0 => array(0x3C0), 0x3A1 => array(0x3C1), 0x3A3 => array(0x3C3), - 0x3A4 => array(0x3C4), 0x3A5 => array(0x3C5), 0x3A6 => array(0x3C6), - 0x3A7 => array(0x3C7), 0x3A8 => array(0x3C8), 0x3A9 => array(0x3C9), - 0x3AA => array(0x3CA), 0x3AB => array(0x3CB), 0x3B0 => array(0x3C5, 0x308, 0x301), - 0x3C2 => array(0x3C3), 0x3D0 => array(0x3B2), 0x3D1 => array(0x3B8), - 0x3D2 => array(0x3C5), 0x3D3 => array(0x3CD), 0x3D4 => array(0x3CB), - 0x3D5 => array(0x3C6), 0x3D6 => array(0x3C0), 0x3D8 => array(0x3D9), - 0x3DA => array(0x3DB), 0x3DC => array(0x3DD), 0x3DE => array(0x3DF), - 0x3E0 => array(0x3E1), 0x3E2 => array(0x3E3), 0x3E4 => array(0x3E5), - 0x3E6 => array(0x3E7), 0x3E8 => array(0x3E9), 0x3EA => array(0x3EB), - 0x3EC => array(0x3ED), 0x3EE => array(0x3EF), 0x3F0 => array(0x3BA), - 0x3F1 => array(0x3C1), 0x3F2 => array(0x3C3), 0x3F4 => array(0x3B8), - 0x3F5 => array(0x3B5), 0x400 => array(0x450), 0x401 => array(0x451), - 0x402 => array(0x452), 0x403 => array(0x453), 0x404 => array(0x454), - 0x405 => array(0x455), 0x406 => array(0x456), 0x407 => array(0x457), - 0x408 => array(0x458), 0x409 => array(0x459), 0x40A => array(0x45A), - 0x40B => array(0x45B), 0x40C => array(0x45C), 0x40D => array(0x45D), - 0x40E => array(0x45E), 0x40F => array(0x45F), 0x410 => array(0x430), - 0x411 => array(0x431), 0x412 => array(0x432), 0x413 => array(0x433), - 0x414 => array(0x434), 0x415 => array(0x435), 0x416 => array(0x436), - 0x417 => array(0x437), 0x418 => array(0x438), 0x419 => array(0x439), - 0x41A => array(0x43A), 0x41B => array(0x43B), 0x41C => array(0x43C), - 0x41D => array(0x43D), 0x41E => array(0x43E), 0x41F => array(0x43F), - 0x420 => array(0x440), 0x421 => array(0x441), 0x422 => array(0x442), - 0x423 => array(0x443), 0x424 => array(0x444), 0x425 => array(0x445), - 0x426 => array(0x446), 0x427 => array(0x447), 0x428 => array(0x448), - 0x429 => array(0x449), 0x42A => array(0x44A), 0x42B => array(0x44B), - 0x42C => array(0x44C), 0x42D => array(0x44D), 0x42E => array(0x44E), - 0x42F => array(0x44F), 0x460 => array(0x461), 0x462 => array(0x463), - 0x464 => array(0x465), 0x466 => array(0x467), 0x468 => array(0x469), - 0x46A => array(0x46B), 0x46C => array(0x46D), 0x46E => array(0x46F), - 0x470 => array(0x471), 0x472 => array(0x473), 0x474 => array(0x475), - 0x476 => array(0x477), 0x478 => array(0x479), 0x47A => array(0x47B), - 0x47C => array(0x47D), 0x47E => array(0x47F), 0x480 => array(0x481), - 0x48A => array(0x48B), 0x48C => array(0x48D), 0x48E => array(0x48F), - 0x490 => array(0x491), 0x492 => array(0x493), 0x494 => array(0x495), - 0x496 => array(0x497), 0x498 => array(0x499), 0x49A => array(0x49B), - 0x49C => array(0x49D), 0x49E => array(0x49F), 0x4A0 => array(0x4A1), - 0x4A2 => array(0x4A3), 0x4A4 => array(0x4A5), 0x4A6 => array(0x4A7), - 0x4A8 => array(0x4A9), 0x4AA => array(0x4AB), 0x4AC => array(0x4AD), - 0x4AE => array(0x4AF), 0x4B0 => array(0x4B1), 0x4B2 => array(0x4B3), - 0x4B4 => array(0x4B5), 0x4B6 => array(0x4B7), 0x4B8 => array(0x4B9), - 0x4BA => array(0x4BB), 0x4BC => array(0x4BD), 0x4BE => array(0x4BF), - 0x4C1 => array(0x4C2), 0x4C3 => array(0x4C4), 0x4C5 => array(0x4C6), - 0x4C7 => array(0x4C8), 0x4C9 => array(0x4CA), 0x4CB => array(0x4CC), - 0x4CD => array(0x4CE), 0x4D0 => array(0x4D1), 0x4D2 => array(0x4D3), - 0x4D4 => array(0x4D5), 0x4D6 => array(0x4D7), 0x4D8 => array(0x4D9), - 0x4DA => array(0x4DB), 0x4DC => array(0x4DD), 0x4DE => array(0x4DF), - 0x4E0 => array(0x4E1), 0x4E2 => array(0x4E3), 0x4E4 => array(0x4E5), - 0x4E6 => array(0x4E7), 0x4E8 => array(0x4E9), 0x4EA => array(0x4EB), - 0x4EC => array(0x4ED), 0x4EE => array(0x4EF), 0x4F0 => array(0x4F1), - 0x4F2 => array(0x4F3), 0x4F4 => array(0x4F5), 0x4F8 => array(0x4F9), - 0x500 => array(0x501), 0x502 => array(0x503), 0x504 => array(0x505), - 0x506 => array(0x507), 0x508 => array(0x509), 0x50A => array(0x50B), - 0x50C => array(0x50D), 0x50E => array(0x50F), 0x531 => array(0x561), - 0x532 => array(0x562), 0x533 => array(0x563), 0x534 => array(0x564), - 0x535 => array(0x565), 0x536 => array(0x566), 0x537 => array(0x567), - 0x538 => array(0x568), 0x539 => array(0x569), 0x53A => array(0x56A), - 0x53B => array(0x56B), 0x53C => array(0x56C), 0x53D => array(0x56D), - 0x53E => array(0x56E), 0x53F => array(0x56F), 0x540 => array(0x570), - 0x541 => array(0x571), 0x542 => array(0x572), 0x543 => array(0x573), - 0x544 => array(0x574), 0x545 => array(0x575), 0x546 => array(0x576), - 0x547 => array(0x577), 0x548 => array(0x578), 0x549 => array(0x579), - 0x54A => array(0x57A), 0x54B => array(0x57B), 0x54C => array(0x57C), - 0x54D => array(0x57D), 0x54E => array(0x57E), 0x54F => array(0x57F), - 0x550 => array(0x580), 0x551 => array(0x581), 0x552 => array(0x582), - 0x553 => array(0x583), 0x554 => array(0x584), 0x555 => array(0x585), - 0x556 => array(0x586), 0x587 => array(0x565, 0x582), 0xE33 => array(0xE4D, 0xE32), - 0x1E00 => array(0x1E01), 0x1E02 => array(0x1E03), 0x1E04 => array(0x1E05), - 0x1E06 => array(0x1E07), 0x1E08 => array(0x1E09), 0x1E0A => array(0x1E0B), - 0x1E0C => array(0x1E0D), 0x1E0E => array(0x1E0F), 0x1E10 => array(0x1E11), - 0x1E12 => array(0x1E13), 0x1E14 => array(0x1E15), 0x1E16 => array(0x1E17), - 0x1E18 => array(0x1E19), 0x1E1A => array(0x1E1B), 0x1E1C => array(0x1E1D), - 0x1E1E => array(0x1E1F), 0x1E20 => array(0x1E21), 0x1E22 => array(0x1E23), - 0x1E24 => array(0x1E25), 0x1E26 => array(0x1E27), 0x1E28 => array(0x1E29), - 0x1E2A => array(0x1E2B), 0x1E2C => array(0x1E2D), 0x1E2E => array(0x1E2F), - 0x1E30 => array(0x1E31), 0x1E32 => array(0x1E33), 0x1E34 => array(0x1E35), - 0x1E36 => array(0x1E37), 0x1E38 => array(0x1E39), 0x1E3A => array(0x1E3B), - 0x1E3C => array(0x1E3D), 0x1E3E => array(0x1E3F), 0x1E40 => array(0x1E41), - 0x1E42 => array(0x1E43), 0x1E44 => array(0x1E45), 0x1E46 => array(0x1E47), - 0x1E48 => array(0x1E49), 0x1E4A => array(0x1E4B), 0x1E4C => array(0x1E4D), - 0x1E4E => array(0x1E4F), 0x1E50 => array(0x1E51), 0x1E52 => array(0x1E53), - 0x1E54 => array(0x1E55), 0x1E56 => array(0x1E57), 0x1E58 => array(0x1E59), - 0x1E5A => array(0x1E5B), 0x1E5C => array(0x1E5D), 0x1E5E => array(0x1E5F), - 0x1E60 => array(0x1E61), 0x1E62 => array(0x1E63), 0x1E64 => array(0x1E65), - 0x1E66 => array(0x1E67), 0x1E68 => array(0x1E69), 0x1E6A => array(0x1E6B), - 0x1E6C => array(0x1E6D), 0x1E6E => array(0x1E6F), 0x1E70 => array(0x1E71), - 0x1E72 => array(0x1E73), 0x1E74 => array(0x1E75), 0x1E76 => array(0x1E77), - 0x1E78 => array(0x1E79), 0x1E7A => array(0x1E7B), 0x1E7C => array(0x1E7D), - 0x1E7E => array(0x1E7F), 0x1E80 => array(0x1E81), 0x1E82 => array(0x1E83), - 0x1E84 => array(0x1E85), 0x1E86 => array(0x1E87), 0x1E88 => array(0x1E89), - 0x1E8A => array(0x1E8B), 0x1E8C => array(0x1E8D), 0x1E8E => array(0x1E8F), - 0x1E90 => array(0x1E91), 0x1E92 => array(0x1E93), 0x1E94 => array(0x1E95), - 0x1E96 => array(0x68, 0x331), 0x1E97 => array(0x74, 0x308), 0x1E98 => array(0x77, 0x30A), - 0x1E99 => array(0x79, 0x30A), 0x1E9A => array(0x61, 0x2BE), 0x1E9B => array(0x1E61), - 0x1EA0 => array(0x1EA1), 0x1EA2 => array(0x1EA3), 0x1EA4 => array(0x1EA5), - 0x1EA6 => array(0x1EA7), 0x1EA8 => array(0x1EA9), 0x1EAA => array(0x1EAB), - 0x1EAC => array(0x1EAD), 0x1EAE => array(0x1EAF), 0x1EB0 => array(0x1EB1), - 0x1EB2 => array(0x1EB3), 0x1EB4 => array(0x1EB5), 0x1EB6 => array(0x1EB7), - 0x1EB8 => array(0x1EB9), 0x1EBA => array(0x1EBB), 0x1EBC => array(0x1EBD), - 0x1EBE => array(0x1EBF), 0x1EC0 => array(0x1EC1), 0x1EC2 => array(0x1EC3), - 0x1EC4 => array(0x1EC5), 0x1EC6 => array(0x1EC7), 0x1EC8 => array(0x1EC9), - 0x1ECA => array(0x1ECB), 0x1ECC => array(0x1ECD), 0x1ECE => array(0x1ECF), - 0x1ED0 => array(0x1ED1), 0x1ED2 => array(0x1ED3), 0x1ED4 => array(0x1ED5), - 0x1ED6 => array(0x1ED7), 0x1ED8 => array(0x1ED9), 0x1EDA => array(0x1EDB), - 0x1EDC => array(0x1EDD), 0x1EDE => array(0x1EDF), 0x1EE0 => array(0x1EE1), - 0x1EE2 => array(0x1EE3), 0x1EE4 => array(0x1EE5), 0x1EE6 => array(0x1EE7), - 0x1EE8 => array(0x1EE9), 0x1EEA => array(0x1EEB), 0x1EEC => array(0x1EED), - 0x1EEE => array(0x1EEF), 0x1EF0 => array(0x1EF1), 0x1EF2 => array(0x1EF3), - 0x1EF4 => array(0x1EF5), 0x1EF6 => array(0x1EF7), 0x1EF8 => array(0x1EF9), - 0x1F08 => array(0x1F00), 0x1F09 => array(0x1F01), 0x1F0A => array(0x1F02), - 0x1F0B => array(0x1F03), 0x1F0C => array(0x1F04), 0x1F0D => array(0x1F05), - 0x1F0E => array(0x1F06), 0x1F0F => array(0x1F07), 0x1F18 => array(0x1F10), - 0x1F19 => array(0x1F11), 0x1F1A => array(0x1F12), 0x1F1B => array(0x1F13), - 0x1F1C => array(0x1F14), 0x1F1D => array(0x1F15), 0x1F28 => array(0x1F20), - 0x1F29 => array(0x1F21), 0x1F2A => array(0x1F22), 0x1F2B => array(0x1F23), - 0x1F2C => array(0x1F24), 0x1F2D => array(0x1F25), 0x1F2E => array(0x1F26), - 0x1F2F => array(0x1F27), 0x1F38 => array(0x1F30), 0x1F39 => array(0x1F31), - 0x1F3A => array(0x1F32), 0x1F3B => array(0x1F33), 0x1F3C => array(0x1F34), - 0x1F3D => array(0x1F35), 0x1F3E => array(0x1F36), 0x1F3F => array(0x1F37), - 0x1F48 => array(0x1F40), 0x1F49 => array(0x1F41), 0x1F4A => array(0x1F42), - 0x1F4B => array(0x1F43), 0x1F4C => array(0x1F44), 0x1F4D => array(0x1F45), - 0x1F50 => array(0x3C5, 0x313), 0x1F52 => array(0x3C5, 0x313, 0x300), - 0x1F54 => array(0x3C5, 0x313, 0x301), 0x1F56 => array(0x3C5, 0x313, 0x342), - 0x1F59 => array(0x1F51), 0x1F5B => array(0x1F53), 0x1F5D => array(0x1F55), - 0x1F5F => array(0x1F57), 0x1F68 => array(0x1F60), 0x1F69 => array(0x1F61), - 0x1F6A => array(0x1F62), 0x1F6B => array(0x1F63), 0x1F6C => array(0x1F64), - 0x1F6D => array(0x1F65), 0x1F6E => array(0x1F66), 0x1F6F => array(0x1F67), - 0x1F80 => array(0x1F00, 0x3B9), 0x1F81 => array(0x1F01, 0x3B9), - 0x1F82 => array(0x1F02, 0x3B9), 0x1F83 => array(0x1F03, 0x3B9), - 0x1F84 => array(0x1F04, 0x3B9), 0x1F85 => array(0x1F05, 0x3B9), - 0x1F86 => array(0x1F06, 0x3B9), 0x1F87 => array(0x1F07, 0x3B9), - 0x1F88 => array(0x1F00, 0x3B9), 0x1F89 => array(0x1F01, 0x3B9), - 0x1F8A => array(0x1F02, 0x3B9), 0x1F8B => array(0x1F03, 0x3B9), - 0x1F8C => array(0x1F04, 0x3B9), 0x1F8D => array(0x1F05, 0x3B9), - 0x1F8E => array(0x1F06, 0x3B9), 0x1F8F => array(0x1F07, 0x3B9), - 0x1F90 => array(0x1F20, 0x3B9), 0x1F91 => array(0x1F21, 0x3B9), - 0x1F92 => array(0x1F22, 0x3B9), 0x1F93 => array(0x1F23, 0x3B9), - 0x1F94 => array(0x1F24, 0x3B9), 0x1F95 => array(0x1F25, 0x3B9), - 0x1F96 => array(0x1F26, 0x3B9), 0x1F97 => array(0x1F27, 0x3B9), - 0x1F98 => array(0x1F20, 0x3B9), 0x1F99 => array(0x1F21, 0x3B9), - 0x1F9A => array(0x1F22, 0x3B9), 0x1F9B => array(0x1F23, 0x3B9), - 0x1F9C => array(0x1F24, 0x3B9), 0x1F9D => array(0x1F25, 0x3B9), - 0x1F9E => array(0x1F26, 0x3B9), 0x1F9F => array(0x1F27, 0x3B9), - 0x1FA0 => array(0x1F60, 0x3B9), 0x1FA1 => array(0x1F61, 0x3B9), - 0x1FA2 => array(0x1F62, 0x3B9), 0x1FA3 => array(0x1F63, 0x3B9), - 0x1FA4 => array(0x1F64, 0x3B9), 0x1FA5 => array(0x1F65, 0x3B9), - 0x1FA6 => array(0x1F66, 0x3B9), 0x1FA7 => array(0x1F67, 0x3B9), - 0x1FA8 => array(0x1F60, 0x3B9), 0x1FA9 => array(0x1F61, 0x3B9), - 0x1FAA => array(0x1F62, 0x3B9), 0x1FAB => array(0x1F63, 0x3B9), - 0x1FAC => array(0x1F64, 0x3B9), 0x1FAD => array(0x1F65, 0x3B9), - 0x1FAE => array(0x1F66, 0x3B9), 0x1FAF => array(0x1F67, 0x3B9), - 0x1FB2 => array(0x1F70, 0x3B9), 0x1FB3 => array(0x3B1, 0x3B9), - 0x1FB4 => array(0x3AC, 0x3B9), 0x1FB6 => array(0x3B1, 0x342), - 0x1FB7 => array(0x3B1, 0x342, 0x3B9), 0x1FB8 => array(0x1FB0), - 0x1FB9 => array(0x1FB1), 0x1FBA => array(0x1F70), 0x1FBB => array(0x1F71), - 0x1FBC => array(0x3B1, 0x3B9), 0x1FBE => array(0x3B9), - 0x1FC2 => array(0x1F74, 0x3B9), 0x1FC3 => array(0x3B7, 0x3B9), - 0x1FC4 => array(0x3AE, 0x3B9), 0x1FC6 => array(0x3B7, 0x342), - 0x1FC7 => array(0x3B7, 0x342, 0x3B9), 0x1FC8 => array(0x1F72), - 0x1FC9 => array(0x1F73), 0x1FCA => array(0x1F74), 0x1FCB => array(0x1F75), - 0x1FCC => array(0x3B7, 0x3B9), 0x1FD2 => array(0x3B9, 0x308, 0x300), - 0x1FD3 => array(0x3B9, 0x308, 0x301), 0x1FD6 => array(0x3B9, 0x342), - 0x1FD7 => array(0x3B9, 0x308, 0x342), 0x1FD8 => array(0x1FD0), - 0x1FD9 => array(0x1FD1), 0x1FDA => array(0x1F76), - 0x1FDB => array(0x1F77), 0x1FE2 => array(0x3C5, 0x308, 0x300), - 0x1FE3 => array(0x3C5, 0x308, 0x301), 0x1FE4 => array(0x3C1, 0x313), - 0x1FE6 => array(0x3C5, 0x342), 0x1FE7 => array(0x3C5, 0x308, 0x342), - 0x1FE8 => array(0x1FE0), 0x1FE9 => array(0x1FE1), - 0x1FEA => array(0x1F7A), 0x1FEB => array(0x1F7B), - 0x1FEC => array(0x1FE5), 0x1FF2 => array(0x1F7C, 0x3B9), - 0x1FF3 => array(0x3C9, 0x3B9), 0x1FF4 => array(0x3CE, 0x3B9), - 0x1FF6 => array(0x3C9, 0x342), 0x1FF7 => array(0x3C9, 0x342, 0x3B9), - 0x1FF8 => array(0x1F78), 0x1FF9 => array(0x1F79), 0x1FFA => array(0x1F7C), - 0x1FFB => array(0x1F7D), 0x1FFC => array(0x3C9, 0x3B9), - 0x20A8 => array(0x72, 0x73), 0x2102 => array(0x63), 0x2103 => array(0xB0, 0x63), - 0x2107 => array(0x25B), 0x2109 => array(0xB0, 0x66), 0x210B => array(0x68), - 0x210C => array(0x68), 0x210D => array(0x68), 0x2110 => array(0x69), - 0x2111 => array(0x69), 0x2112 => array(0x6C), 0x2115 => array(0x6E), - 0x2116 => array(0x6E, 0x6F), 0x2119 => array(0x70), 0x211A => array(0x71), - 0x211B => array(0x72), 0x211C => array(0x72), 0x211D => array(0x72), - 0x2120 => array(0x73, 0x6D), 0x2121 => array(0x74, 0x65, 0x6C), - 0x2122 => array(0x74, 0x6D), 0x2124 => array(0x7A), 0x2126 => array(0x3C9), - 0x2128 => array(0x7A), 0x212A => array(0x6B), 0x212B => array(0xE5), - 0x212C => array(0x62), 0x212D => array(0x63), 0x2130 => array(0x65), - 0x2131 => array(0x66), 0x2133 => array(0x6D), 0x213E => array(0x3B3), - 0x213F => array(0x3C0), 0x2145 => array(0x64), 0x2160 => array(0x2170), - 0x2161 => array(0x2171), 0x2162 => array(0x2172), 0x2163 => array(0x2173), - 0x2164 => array(0x2174), 0x2165 => array(0x2175), 0x2166 => array(0x2176), - 0x2167 => array(0x2177), 0x2168 => array(0x2178), 0x2169 => array(0x2179), - 0x216A => array(0x217A), 0x216B => array(0x217B), 0x216C => array(0x217C), - 0x216D => array(0x217D), 0x216E => array(0x217E), 0x216F => array(0x217F), - 0x24B6 => array(0x24D0), 0x24B7 => array(0x24D1), 0x24B8 => array(0x24D2), - 0x24B9 => array(0x24D3), 0x24BA => array(0x24D4), 0x24BB => array(0x24D5), - 0x24BC => array(0x24D6), 0x24BD => array(0x24D7), 0x24BE => array(0x24D8), - 0x24BF => array(0x24D9), 0x24C0 => array(0x24DA), 0x24C1 => array(0x24DB), - 0x24C2 => array(0x24DC), 0x24C3 => array(0x24DD), 0x24C4 => array(0x24DE), - 0x24C5 => array(0x24DF), 0x24C6 => array(0x24E0), 0x24C7 => array(0x24E1), - 0x24C8 => array(0x24E2), 0x24C9 => array(0x24E3), 0x24CA => array(0x24E4), - 0x24CB => array(0x24E5), 0x24CC => array(0x24E6), 0x24CD => array(0x24E7), - 0x24CE => array(0x24E8), 0x24CF => array(0x24E9), 0x3371 => array(0x68, 0x70, 0x61), - 0x3373 => array(0x61, 0x75), 0x3375 => array(0x6F, 0x76), - 0x3380 => array(0x70, 0x61), 0x3381 => array(0x6E, 0x61), - 0x3382 => array(0x3BC, 0x61), 0x3383 => array(0x6D, 0x61), - 0x3384 => array(0x6B, 0x61), 0x3385 => array(0x6B, 0x62), - 0x3386 => array(0x6D, 0x62), 0x3387 => array(0x67, 0x62), - 0x338A => array(0x70, 0x66), 0x338B => array(0x6E, 0x66), - 0x338C => array(0x3BC, 0x66), 0x3390 => array(0x68, 0x7A), - 0x3391 => array(0x6B, 0x68, 0x7A), 0x3392 => array(0x6D, 0x68, 0x7A), - 0x3393 => array(0x67, 0x68, 0x7A), 0x3394 => array(0x74, 0x68, 0x7A), - 0x33A9 => array(0x70, 0x61), 0x33AA => array(0x6B, 0x70, 0x61), - 0x33AB => array(0x6D, 0x70, 0x61), 0x33AC => array(0x67, 0x70, 0x61), - 0x33B4 => array(0x70, 0x76), 0x33B5 => array(0x6E, 0x76), - 0x33B6 => array(0x3BC, 0x76), 0x33B7 => array(0x6D, 0x76), - 0x33B8 => array(0x6B, 0x76), 0x33B9 => array(0x6D, 0x76), - 0x33BA => array(0x70, 0x77), 0x33BB => array(0x6E, 0x77), - 0x33BC => array(0x3BC, 0x77), 0x33BD => array(0x6D, 0x77), - 0x33BE => array(0x6B, 0x77), 0x33BF => array(0x6D, 0x77), - 0x33C0 => array(0x6B, 0x3C9), 0x33C1 => array(0x6D, 0x3C9), /* - 0x33C2 => array(0x61, 0x2E, 0x6D, 0x2E), */ - 0x33C3 => array(0x62, 0x71), 0x33C6 => array(0x63, 0x2215, 0x6B, 0x67), - 0x33C7 => array(0x63, 0x6F, 0x2E), 0x33C8 => array(0x64, 0x62), - 0x33C9 => array(0x67, 0x79), 0x33CB => array(0x68, 0x70), - 0x33CD => array(0x6B, 0x6B), 0x33CE => array(0x6B, 0x6D), - 0x33D7 => array(0x70, 0x68), 0x33D9 => array(0x70, 0x70, 0x6D), - 0x33DA => array(0x70, 0x72), 0x33DC => array(0x73, 0x76), - 0x33DD => array(0x77, 0x62), 0xFB00 => array(0x66, 0x66), - 0xFB01 => array(0x66, 0x69), 0xFB02 => array(0x66, 0x6C), - 0xFB03 => array(0x66, 0x66, 0x69), 0xFB04 => array(0x66, 0x66, 0x6C), - 0xFB05 => array(0x73, 0x74), 0xFB06 => array(0x73, 0x74), - 0xFB13 => array(0x574, 0x576), 0xFB14 => array(0x574, 0x565), - 0xFB15 => array(0x574, 0x56B), 0xFB16 => array(0x57E, 0x576), - 0xFB17 => array(0x574, 0x56D), 0xFF21 => array(0xFF41), - 0xFF22 => array(0xFF42), 0xFF23 => array(0xFF43), 0xFF24 => array(0xFF44), - 0xFF25 => array(0xFF45), 0xFF26 => array(0xFF46), 0xFF27 => array(0xFF47), - 0xFF28 => array(0xFF48), 0xFF29 => array(0xFF49), 0xFF2A => array(0xFF4A), - 0xFF2B => array(0xFF4B), 0xFF2C => array(0xFF4C), 0xFF2D => array(0xFF4D), - 0xFF2E => array(0xFF4E), 0xFF2F => array(0xFF4F), 0xFF30 => array(0xFF50), - 0xFF31 => array(0xFF51), 0xFF32 => array(0xFF52), 0xFF33 => array(0xFF53), - 0xFF34 => array(0xFF54), 0xFF35 => array(0xFF55), 0xFF36 => array(0xFF56), - 0xFF37 => array(0xFF57), 0xFF38 => array(0xFF58), 0xFF39 => array(0xFF59), - 0xFF3A => array(0xFF5A), 0x10400 => array(0x10428), 0x10401 => array(0x10429), - 0x10402 => array(0x1042A), 0x10403 => array(0x1042B), 0x10404 => array(0x1042C), - 0x10405 => array(0x1042D), 0x10406 => array(0x1042E), 0x10407 => array(0x1042F), - 0x10408 => array(0x10430), 0x10409 => array(0x10431), 0x1040A => array(0x10432), - 0x1040B => array(0x10433), 0x1040C => array(0x10434), 0x1040D => array(0x10435), - 0x1040E => array(0x10436), 0x1040F => array(0x10437), 0x10410 => array(0x10438), - 0x10411 => array(0x10439), 0x10412 => array(0x1043A), 0x10413 => array(0x1043B), - 0x10414 => array(0x1043C), 0x10415 => array(0x1043D), 0x10416 => array(0x1043E), - 0x10417 => array(0x1043F), 0x10418 => array(0x10440), 0x10419 => array(0x10441), - 0x1041A => array(0x10442), 0x1041B => array(0x10443), 0x1041C => array(0x10444), - 0x1041D => array(0x10445), 0x1041E => array(0x10446), 0x1041F => array(0x10447), - 0x10420 => array(0x10448), 0x10421 => array(0x10449), 0x10422 => array(0x1044A), - 0x10423 => array(0x1044B), 0x10424 => array(0x1044C), 0x10425 => array(0x1044D), - 0x1D400 => array(0x61), 0x1D401 => array(0x62), 0x1D402 => array(0x63), - 0x1D403 => array(0x64), 0x1D404 => array(0x65), 0x1D405 => array(0x66), - 0x1D406 => array(0x67), 0x1D407 => array(0x68), 0x1D408 => array(0x69), - 0x1D409 => array(0x6A), 0x1D40A => array(0x6B), 0x1D40B => array(0x6C), - 0x1D40C => array(0x6D), 0x1D40D => array(0x6E), 0x1D40E => array(0x6F), - 0x1D40F => array(0x70), 0x1D410 => array(0x71), 0x1D411 => array(0x72), - 0x1D412 => array(0x73), 0x1D413 => array(0x74), 0x1D414 => array(0x75), - 0x1D415 => array(0x76), 0x1D416 => array(0x77), 0x1D417 => array(0x78), - 0x1D418 => array(0x79), 0x1D419 => array(0x7A), 0x1D434 => array(0x61), - 0x1D435 => array(0x62), 0x1D436 => array(0x63), 0x1D437 => array(0x64), - 0x1D438 => array(0x65), 0x1D439 => array(0x66), 0x1D43A => array(0x67), - 0x1D43B => array(0x68), 0x1D43C => array(0x69), 0x1D43D => array(0x6A), - 0x1D43E => array(0x6B), 0x1D43F => array(0x6C), 0x1D440 => array(0x6D), - 0x1D441 => array(0x6E), 0x1D442 => array(0x6F), 0x1D443 => array(0x70), - 0x1D444 => array(0x71), 0x1D445 => array(0x72), 0x1D446 => array(0x73), - 0x1D447 => array(0x74), 0x1D448 => array(0x75), 0x1D449 => array(0x76), - 0x1D44A => array(0x77), 0x1D44B => array(0x78), 0x1D44C => array(0x79), - 0x1D44D => array(0x7A), 0x1D468 => array(0x61), 0x1D469 => array(0x62), - 0x1D46A => array(0x63), 0x1D46B => array(0x64), 0x1D46C => array(0x65), - 0x1D46D => array(0x66), 0x1D46E => array(0x67), 0x1D46F => array(0x68), - 0x1D470 => array(0x69), 0x1D471 => array(0x6A), 0x1D472 => array(0x6B), - 0x1D473 => array(0x6C), 0x1D474 => array(0x6D), 0x1D475 => array(0x6E), - 0x1D476 => array(0x6F), 0x1D477 => array(0x70), 0x1D478 => array(0x71), - 0x1D479 => array(0x72), 0x1D47A => array(0x73), 0x1D47B => array(0x74), - 0x1D47C => array(0x75), 0x1D47D => array(0x76), 0x1D47E => array(0x77), - 0x1D47F => array(0x78), 0x1D480 => array(0x79), 0x1D481 => array(0x7A), - 0x1D49C => array(0x61), 0x1D49E => array(0x63), 0x1D49F => array(0x64), - 0x1D4A2 => array(0x67), 0x1D4A5 => array(0x6A), 0x1D4A6 => array(0x6B), - 0x1D4A9 => array(0x6E), 0x1D4AA => array(0x6F), 0x1D4AB => array(0x70), - 0x1D4AC => array(0x71), 0x1D4AE => array(0x73), 0x1D4AF => array(0x74), - 0x1D4B0 => array(0x75), 0x1D4B1 => array(0x76), 0x1D4B2 => array(0x77), - 0x1D4B3 => array(0x78), 0x1D4B4 => array(0x79), 0x1D4B5 => array(0x7A), - 0x1D4D0 => array(0x61), 0x1D4D1 => array(0x62), 0x1D4D2 => array(0x63), - 0x1D4D3 => array(0x64), 0x1D4D4 => array(0x65), 0x1D4D5 => array(0x66), - 0x1D4D6 => array(0x67), 0x1D4D7 => array(0x68), 0x1D4D8 => array(0x69), - 0x1D4D9 => array(0x6A), 0x1D4DA => array(0x6B), 0x1D4DB => array(0x6C), - 0x1D4DC => array(0x6D), 0x1D4DD => array(0x6E), 0x1D4DE => array(0x6F), - 0x1D4DF => array(0x70), 0x1D4E0 => array(0x71), 0x1D4E1 => array(0x72), - 0x1D4E2 => array(0x73), 0x1D4E3 => array(0x74), 0x1D4E4 => array(0x75), - 0x1D4E5 => array(0x76), 0x1D4E6 => array(0x77), 0x1D4E7 => array(0x78), - 0x1D4E8 => array(0x79), 0x1D4E9 => array(0x7A), 0x1D504 => array(0x61), - 0x1D505 => array(0x62), 0x1D507 => array(0x64), 0x1D508 => array(0x65), - 0x1D509 => array(0x66), 0x1D50A => array(0x67), 0x1D50D => array(0x6A), - 0x1D50E => array(0x6B), 0x1D50F => array(0x6C), 0x1D510 => array(0x6D), - 0x1D511 => array(0x6E), 0x1D512 => array(0x6F), 0x1D513 => array(0x70), - 0x1D514 => array(0x71), 0x1D516 => array(0x73), 0x1D517 => array(0x74), - 0x1D518 => array(0x75), 0x1D519 => array(0x76), 0x1D51A => array(0x77), - 0x1D51B => array(0x78), 0x1D51C => array(0x79), 0x1D538 => array(0x61), - 0x1D539 => array(0x62), 0x1D53B => array(0x64), 0x1D53C => array(0x65), - 0x1D53D => array(0x66), 0x1D53E => array(0x67), 0x1D540 => array(0x69), - 0x1D541 => array(0x6A), 0x1D542 => array(0x6B), 0x1D543 => array(0x6C), - 0x1D544 => array(0x6D), 0x1D546 => array(0x6F), 0x1D54A => array(0x73), - 0x1D54B => array(0x74), 0x1D54C => array(0x75), 0x1D54D => array(0x76), - 0x1D54E => array(0x77), 0x1D54F => array(0x78), 0x1D550 => array(0x79), - 0x1D56C => array(0x61), 0x1D56D => array(0x62), 0x1D56E => array(0x63), - 0x1D56F => array(0x64), 0x1D570 => array(0x65), 0x1D571 => array(0x66), - 0x1D572 => array(0x67), 0x1D573 => array(0x68), 0x1D574 => array(0x69), - 0x1D575 => array(0x6A), 0x1D576 => array(0x6B), 0x1D577 => array(0x6C), - 0x1D578 => array(0x6D), 0x1D579 => array(0x6E), 0x1D57A => array(0x6F), - 0x1D57B => array(0x70), 0x1D57C => array(0x71), 0x1D57D => array(0x72), - 0x1D57E => array(0x73), 0x1D57F => array(0x74), 0x1D580 => array(0x75), - 0x1D581 => array(0x76), 0x1D582 => array(0x77), 0x1D583 => array(0x78), - 0x1D584 => array(0x79), 0x1D585 => array(0x7A), 0x1D5A0 => array(0x61), - 0x1D5A1 => array(0x62), 0x1D5A2 => array(0x63), 0x1D5A3 => array(0x64), - 0x1D5A4 => array(0x65), 0x1D5A5 => array(0x66), 0x1D5A6 => array(0x67), - 0x1D5A7 => array(0x68), 0x1D5A8 => array(0x69), 0x1D5A9 => array(0x6A), - 0x1D5AA => array(0x6B), 0x1D5AB => array(0x6C), 0x1D5AC => array(0x6D), - 0x1D5AD => array(0x6E), 0x1D5AE => array(0x6F), 0x1D5AF => array(0x70), - 0x1D5B0 => array(0x71), 0x1D5B1 => array(0x72), 0x1D5B2 => array(0x73), - 0x1D5B3 => array(0x74), 0x1D5B4 => array(0x75), 0x1D5B5 => array(0x76), - 0x1D5B6 => array(0x77), 0x1D5B7 => array(0x78), 0x1D5B8 => array(0x79), - 0x1D5B9 => array(0x7A), 0x1D5D4 => array(0x61), 0x1D5D5 => array(0x62), - 0x1D5D6 => array(0x63), 0x1D5D7 => array(0x64), 0x1D5D8 => array(0x65), - 0x1D5D9 => array(0x66), 0x1D5DA => array(0x67), 0x1D5DB => array(0x68), - 0x1D5DC => array(0x69), 0x1D5DD => array(0x6A), 0x1D5DE => array(0x6B), - 0x1D5DF => array(0x6C), 0x1D5E0 => array(0x6D), 0x1D5E1 => array(0x6E), - 0x1D5E2 => array(0x6F), 0x1D5E3 => array(0x70), 0x1D5E4 => array(0x71), - 0x1D5E5 => array(0x72), 0x1D5E6 => array(0x73), 0x1D5E7 => array(0x74), - 0x1D5E8 => array(0x75), 0x1D5E9 => array(0x76), 0x1D5EA => array(0x77), - 0x1D5EB => array(0x78), 0x1D5EC => array(0x79), 0x1D5ED => array(0x7A), - 0x1D608 => array(0x61), 0x1D609 => array(0x62), 0x1D60A => array(0x63), - 0x1D60B => array(0x64), 0x1D60C => array(0x65), 0x1D60D => array(0x66), - 0x1D60E => array(0x67), 0x1D60F => array(0x68), 0x1D610 => array(0x69), - 0x1D611 => array(0x6A), 0x1D612 => array(0x6B), 0x1D613 => array(0x6C), - 0x1D614 => array(0x6D), 0x1D615 => array(0x6E), 0x1D616 => array(0x6F), - 0x1D617 => array(0x70), 0x1D618 => array(0x71), 0x1D619 => array(0x72), - 0x1D61A => array(0x73), 0x1D61B => array(0x74), 0x1D61C => array(0x75), - 0x1D61D => array(0x76), 0x1D61E => array(0x77), 0x1D61F => array(0x78), - 0x1D620 => array(0x79), 0x1D621 => array(0x7A), 0x1D63C => array(0x61), - 0x1D63D => array(0x62), 0x1D63E => array(0x63), 0x1D63F => array(0x64), - 0x1D640 => array(0x65), 0x1D641 => array(0x66), 0x1D642 => array(0x67), - 0x1D643 => array(0x68), 0x1D644 => array(0x69), 0x1D645 => array(0x6A), - 0x1D646 => array(0x6B), 0x1D647 => array(0x6C), 0x1D648 => array(0x6D), - 0x1D649 => array(0x6E), 0x1D64A => array(0x6F), 0x1D64B => array(0x70), - 0x1D64C => array(0x71), 0x1D64D => array(0x72), 0x1D64E => array(0x73), - 0x1D64F => array(0x74), 0x1D650 => array(0x75), 0x1D651 => array(0x76), - 0x1D652 => array(0x77), 0x1D653 => array(0x78), 0x1D654 => array(0x79), - 0x1D655 => array(0x7A), 0x1D670 => array(0x61), 0x1D671 => array(0x62), - 0x1D672 => array(0x63), 0x1D673 => array(0x64), 0x1D674 => array(0x65), - 0x1D675 => array(0x66), 0x1D676 => array(0x67), 0x1D677 => array(0x68), - 0x1D678 => array(0x69), 0x1D679 => array(0x6A), 0x1D67A => array(0x6B), - 0x1D67B => array(0x6C), 0x1D67C => array(0x6D), 0x1D67D => array(0x6E), - 0x1D67E => array(0x6F), 0x1D67F => array(0x70), 0x1D680 => array(0x71), - 0x1D681 => array(0x72), 0x1D682 => array(0x73), 0x1D683 => array(0x74), - 0x1D684 => array(0x75), 0x1D685 => array(0x76), 0x1D686 => array(0x77), - 0x1D687 => array(0x78), 0x1D688 => array(0x79), 0x1D689 => array(0x7A), - 0x1D6A8 => array(0x3B1), 0x1D6A9 => array(0x3B2), 0x1D6AA => array(0x3B3), - 0x1D6AB => array(0x3B4), 0x1D6AC => array(0x3B5), 0x1D6AD => array(0x3B6), - 0x1D6AE => array(0x3B7), 0x1D6AF => array(0x3B8), 0x1D6B0 => array(0x3B9), - 0x1D6B1 => array(0x3BA), 0x1D6B2 => array(0x3BB), 0x1D6B3 => array(0x3BC), - 0x1D6B4 => array(0x3BD), 0x1D6B5 => array(0x3BE), 0x1D6B6 => array(0x3BF), - 0x1D6B7 => array(0x3C0), 0x1D6B8 => array(0x3C1), 0x1D6B9 => array(0x3B8), - 0x1D6BA => array(0x3C3), 0x1D6BB => array(0x3C4), 0x1D6BC => array(0x3C5), - 0x1D6BD => array(0x3C6), 0x1D6BE => array(0x3C7), 0x1D6BF => array(0x3C8), - 0x1D6C0 => array(0x3C9), 0x1D6D3 => array(0x3C3), 0x1D6E2 => array(0x3B1), - 0x1D6E3 => array(0x3B2), 0x1D6E4 => array(0x3B3), 0x1D6E5 => array(0x3B4), - 0x1D6E6 => array(0x3B5), 0x1D6E7 => array(0x3B6), 0x1D6E8 => array(0x3B7), - 0x1D6E9 => array(0x3B8), 0x1D6EA => array(0x3B9), 0x1D6EB => array(0x3BA), - 0x1D6EC => array(0x3BB), 0x1D6ED => array(0x3BC), 0x1D6EE => array(0x3BD), - 0x1D6EF => array(0x3BE), 0x1D6F0 => array(0x3BF), 0x1D6F1 => array(0x3C0), - 0x1D6F2 => array(0x3C1), 0x1D6F3 => array(0x3B8), 0x1D6F4 => array(0x3C3), - 0x1D6F5 => array(0x3C4), 0x1D6F6 => array(0x3C5), 0x1D6F7 => array(0x3C6), - 0x1D6F8 => array(0x3C7), 0x1D6F9 => array(0x3C8), 0x1D6FA => array(0x3C9), - 0x1D70D => array(0x3C3), 0x1D71C => array(0x3B1), 0x1D71D => array(0x3B2), - 0x1D71E => array(0x3B3), 0x1D71F => array(0x3B4), 0x1D720 => array(0x3B5), - 0x1D721 => array(0x3B6), 0x1D722 => array(0x3B7), 0x1D723 => array(0x3B8), - 0x1D724 => array(0x3B9), 0x1D725 => array(0x3BA), 0x1D726 => array(0x3BB), - 0x1D727 => array(0x3BC), 0x1D728 => array(0x3BD), 0x1D729 => array(0x3BE), - 0x1D72A => array(0x3BF), 0x1D72B => array(0x3C0), 0x1D72C => array(0x3C1), - 0x1D72D => array(0x3B8), 0x1D72E => array(0x3C3), 0x1D72F => array(0x3C4), - 0x1D730 => array(0x3C5), 0x1D731 => array(0x3C6), 0x1D732 => array(0x3C7), - 0x1D733 => array(0x3C8), 0x1D734 => array(0x3C9), 0x1D747 => array(0x3C3), - 0x1D756 => array(0x3B1), 0x1D757 => array(0x3B2), 0x1D758 => array(0x3B3), - 0x1D759 => array(0x3B4), 0x1D75A => array(0x3B5), 0x1D75B => array(0x3B6), - 0x1D75C => array(0x3B7), 0x1D75D => array(0x3B8), 0x1D75E => array(0x3B9), - 0x1D75F => array(0x3BA), 0x1D760 => array(0x3BB), 0x1D761 => array(0x3BC), - 0x1D762 => array(0x3BD), 0x1D763 => array(0x3BE), 0x1D764 => array(0x3BF), - 0x1D765 => array(0x3C0), 0x1D766 => array(0x3C1), 0x1D767 => array(0x3B8), - 0x1D768 => array(0x3C3), 0x1D769 => array(0x3C4), 0x1D76A => array(0x3C5), - 0x1D76B => array(0x3C6), 0x1D76C => array(0x3C7), 0x1D76D => array(0x3C8), - 0x1D76E => array(0x3C9), 0x1D781 => array(0x3C3), 0x1D790 => array(0x3B1), - 0x1D791 => array(0x3B2), 0x1D792 => array(0x3B3), 0x1D793 => array(0x3B4), - 0x1D794 => array(0x3B5), 0x1D795 => array(0x3B6), 0x1D796 => array(0x3B7), - 0x1D797 => array(0x3B8), 0x1D798 => array(0x3B9), 0x1D799 => array(0x3BA), - 0x1D79A => array(0x3BB), 0x1D79B => array(0x3BC), 0x1D79C => array(0x3BD), - 0x1D79D => array(0x3BE), 0x1D79E => array(0x3BF), 0x1D79F => array(0x3C0), - 0x1D7A0 => array(0x3C1), 0x1D7A1 => array(0x3B8), 0x1D7A2 => array(0x3C3), - 0x1D7A3 => array(0x3C4), 0x1D7A4 => array(0x3C5), 0x1D7A5 => array(0x3C6), - 0x1D7A6 => array(0x3C7), 0x1D7A7 => array(0x3C8), 0x1D7A8 => array(0x3C9), - 0x1D7BB => array(0x3C3), 0x3F9 => array(0x3C3), 0x1D2C => array(0x61), - 0x1D2D => array(0xE6), 0x1D2E => array(0x62), 0x1D30 => array(0x64), - 0x1D31 => array(0x65), 0x1D32 => array(0x1DD), 0x1D33 => array(0x67), - 0x1D34 => array(0x68), 0x1D35 => array(0x69), 0x1D36 => array(0x6A), - 0x1D37 => array(0x6B), 0x1D38 => array(0x6C), 0x1D39 => array(0x6D), - 0x1D3A => array(0x6E), 0x1D3C => array(0x6F), 0x1D3D => array(0x223), - 0x1D3E => array(0x70), 0x1D3F => array(0x72), 0x1D40 => array(0x74), - 0x1D41 => array(0x75), 0x1D42 => array(0x77), 0x213B => array(0x66, 0x61, 0x78), - 0x3250 => array(0x70, 0x74, 0x65), 0x32CC => array(0x68, 0x67), - 0x32CE => array(0x65, 0x76), 0x32CF => array(0x6C, 0x74, 0x64), - 0x337A => array(0x69, 0x75), 0x33DE => array(0x76, 0x2215, 0x6D), - 0x33DF => array(0x61, 0x2215, 0x6D) - ), - 'replacemaps' => array(0x41 => array(0x61), 0x42 => array(0x62), 0x43 => array(0x63), - 0x44 => array(0x64), 0x45 => array(0x65), 0x46 => array(0x66), - 0x47 => array(0x67), 0x48 => array(0x68), 0x49 => array(0x69), - 0x4A => array(0x6A), 0x4B => array(0x6B), 0x4C => array(0x6C), - 0x4D => array(0x6D), 0x4E => array(0x6E), 0x4F => array(0x6F), - 0x50 => array(0x70), 0x51 => array(0x71), 0x52 => array(0x72), - 0x53 => array(0x73), 0x54 => array(0x74), 0x55 => array(0x75), - 0x56 => array(0x76), 0x57 => array(0x77), 0x58 => array(0x78), - 0x59 => array(0x79), 0x5A => array(0x7A), 0xAA => array(0x61), - 0xB2 => array(0x32), 0xB3 => array(0x33), 0xB5 => array(0x3BC), - 0xB9 => array(0x31), 0xBA => array(0x6F), 0xBC => array(0x31, 0x2044, 0x34), - 0xBD => array(0x31, 0x2044, 0x32), 0xBE => array(0x33, 0x2044, 0x34), 0xC0 => array(0xE0), - 0xC1 => array(0xE1), 0xC2 => array(0xE2), 0xC3 => array(0xE3), - 0xC4 => array(0xE4), 0xC5 => array(0xE5), 0xC6 => array(0xE6), - 0xC7 => array(0xE7), 0xC8 => array(0xE8), 0xC9 => array(0xE9), - 0xCA => array(0xEA), 0xCB => array(0xEB), 0xCC => array(0xEC), - 0xCD => array(0xED), 0xCE => array(0xEE), 0xCF => array(0xEF), - 0xD0 => array(0xF0), 0xD1 => array(0xF1), 0xD2 => array(0xF2), - 0xD3 => array(0xF3), 0xD4 => array(0xF4), 0xD5 => array(0xF5), - 0xD6 => array(0xF6), 0xD8 => array(0xF8), 0xD9 => array(0xF9), - 0xDA => array(0xFA), 0xDB => array(0xFB), 0xDC => array(0xFC), - 0xDD => array(0xFD), 0xDE => array(0xFE), 0x100 => array(0x101), - 0x102 => array(0x103), 0x104 => array(0x105), 0x106 => array(0x107), - 0x108 => array(0x109), 0x10A => array(0x10B), 0x10C => array(0x10D), - 0x10E => array(0x10F), 0x110 => array(0x111), 0x112 => array(0x113), - 0x114 => array(0x115), 0x116 => array(0x117), 0x118 => array(0x119), - 0x11A => array(0x11B), 0x11C => array(0x11D), 0x11E => array(0x11F), - 0x120 => array(0x121), 0x122 => array(0x123), 0x124 => array(0x125), - 0x126 => array(0x127), 0x128 => array(0x129), 0x12A => array(0x12B), - 0x12C => array(0x12D), 0x12E => array(0x12F), 0x130 => array(0x69, 0x307), - 0x132 => array(0x69, 0x6A), 0x133 => array(0x69, 0x6A), 0x134 => array(0x135), - 0x136 => array(0x137), 0x139 => array(0x13A), 0x13B => array(0x13C), - 0x13D => array(0x13E), 0x13F => array(0x6C, 0xB7), 0x140 => array(0x6C, 0xB7), - 0x141 => array(0x142), 0x143 => array(0x144), 0x145 => array(0x146), - 0x147 => array(0x148), 0x149 => array(0x2BC, 0x6E), 0x14A => array(0x14B), - 0x14C => array(0x14D), 0x14E => array(0x14F), 0x150 => array(0x151), - 0x152 => array(0x153), 0x154 => array(0x155), 0x156 => array(0x157), - 0x158 => array(0x159), 0x15A => array(0x15B), 0x15C => array(0x15D), - 0x15E => array(0x15F), 0x160 => array(0x161), 0x162 => array(0x163), - 0x164 => array(0x165), 0x166 => array(0x167), 0x168 => array(0x169), - 0x16A => array(0x16B), 0x16C => array(0x16D), 0x16E => array(0x16F), - 0x170 => array(0x171), 0x172 => array(0x173), 0x174 => array(0x175), - 0x176 => array(0x177), 0x178 => array(0xFF), 0x179 => array(0x17A), - 0x17B => array(0x17C), 0x17D => array(0x17E), 0x17F => array(0x73), - 0x181 => array(0x253), 0x182 => array(0x183), 0x184 => array(0x185), - 0x186 => array(0x254), 0x187 => array(0x188), 0x189 => array(0x256), - 0x18A => array(0x257), 0x18B => array(0x18C), 0x18E => array(0x1DD), - 0x18F => array(0x259), 0x190 => array(0x25B), 0x191 => array(0x192), - 0x193 => array(0x260), 0x194 => array(0x263), 0x196 => array(0x269), - 0x197 => array(0x268), 0x198 => array(0x199), 0x19C => array(0x26F), - 0x19D => array(0x272), 0x19F => array(0x275), 0x1A0 => array(0x1A1), - 0x1A2 => array(0x1A3), 0x1A4 => array(0x1A5), 0x1A6 => array(0x280), - 0x1A7 => array(0x1A8), 0x1A9 => array(0x283), 0x1AC => array(0x1AD), - 0x1AE => array(0x288), 0x1AF => array(0x1B0), 0x1B1 => array(0x28A), - 0x1B2 => array(0x28B), 0x1B3 => array(0x1B4), 0x1B5 => array(0x1B6), - 0x1B7 => array(0x292), 0x1B8 => array(0x1B9), 0x1BC => array(0x1BD), - 0x1C4 => array(0x64, 0x17E), 0x1C5 => array(0x64, 0x17E), 0x1C6 => array(0x64, 0x17E), - 0x1C7 => array(0x6C, 0x6A), 0x1C8 => array(0x6C, 0x6A), 0x1C9 => array(0x6C, 0x6A), - 0x1CA => array(0x6E, 0x6A), 0x1CB => array(0x6E, 0x6A), 0x1CC => array(0x6E, 0x6A), - 0x1CD => array(0x1CE), 0x1CF => array(0x1D0), 0x1D1 => array(0x1D2), - 0x1D3 => array(0x1D4), 0x1D5 => array(0x1D6), 0x1D7 => array(0x1D8), - 0x1D9 => array(0x1DA), 0x1DB => array(0x1DC), 0x1DE => array(0x1DF), - 0x1E0 => array(0x1E1), 0x1E2 => array(0x1E3), 0x1E4 => array(0x1E5), - 0x1E6 => array(0x1E7), 0x1E8 => array(0x1E9), 0x1EA => array(0x1EB), - 0x1EC => array(0x1ED), 0x1EE => array(0x1EF), 0x1F1 => array(0x64, 0x7A), - 0x1F2 => array(0x64, 0x7A), 0x1F3 => array(0x64, 0x7A), 0x1F4 => array(0x1F5), - 0x1F6 => array(0x195), 0x1F7 => array(0x1BF), 0x1F8 => array(0x1F9), - 0x1FA => array(0x1FB), 0x1FC => array(0x1FD), 0x1FE => array(0x1FF), - 0x200 => array(0x201), 0x202 => array(0x203), 0x204 => array(0x205), - 0x206 => array(0x207), 0x208 => array(0x209), 0x20A => array(0x20B), - 0x20C => array(0x20D), 0x20E => array(0x20F), 0x210 => array(0x211), - 0x212 => array(0x213), 0x214 => array(0x215), 0x216 => array(0x217), - 0x218 => array(0x219), 0x21A => array(0x21B), 0x21C => array(0x21D), - 0x21E => array(0x21F), 0x220 => array(0x19E), 0x222 => array(0x223), - 0x224 => array(0x225), 0x226 => array(0x227), 0x228 => array(0x229), - 0x22A => array(0x22B), 0x22C => array(0x22D), 0x22E => array(0x22F), - 0x230 => array(0x231), 0x232 => array(0x233), 0x23A => array(0x2C65), - 0x23B => array(0x23C), 0x23D => array(0x19A), 0x23E => array(0x2C66), - 0x241 => array(0x242), 0x243 => array(0x180), 0x244 => array(0x289), - 0x245 => array(0x28C), 0x246 => array(0x247), 0x248 => array(0x249), - 0x24A => array(0x24B), 0x24C => array(0x24D), 0x24E => array(0x24F), - 0x2B0 => array(0x68), 0x2B1 => array(0x266), 0x2B2 => array(0x6A), - 0x2B3 => array(0x72), 0x2B4 => array(0x279), 0x2B5 => array(0x27B), - 0x2B6 => array(0x281), 0x2B7 => array(0x77), 0x2B8 => array(0x79), - 0x2E0 => array(0x263), 0x2E1 => array(0x6C), 0x2E2 => array(0x73), - 0x2E3 => array(0x78), 0x2E4 => array(0x295), 0x340 => array(0x300), - 0x341 => array(0x301), 0x343 => array(0x313), 0x344 => array(0x308, 0x301), - 0x345 => array(0x3B9), 0x370 => array(0x371), 0x372 => array(0x373), - 0x374 => array(0x2B9), 0x376 => array(0x377), 0x37F => array(0x3F3), - 0x386 => array(0x3AC), 0x387 => array(0xB7), 0x388 => array(0x3AD), - 0x389 => array(0x3AE), 0x38A => array(0x3AF), 0x38C => array(0x3CC), - 0x38E => array(0x3CD), 0x38F => array(0x3CE), 0x391 => array(0x3B1), - 0x392 => array(0x3B2), 0x393 => array(0x3B3), 0x394 => array(0x3B4), - 0x395 => array(0x3B5), 0x396 => array(0x3B6), 0x397 => array(0x3B7), - 0x398 => array(0x3B8), 0x399 => array(0x3B9), 0x39A => array(0x3BA), - 0x39B => array(0x3BB), 0x39C => array(0x3BC), 0x39D => array(0x3BD), - 0x39E => array(0x3BE), 0x39F => array(0x3BF), 0x3A0 => array(0x3C0), - 0x3A1 => array(0x3C1), 0x3A3 => array(0x3C3), 0x3A4 => array(0x3C4), - 0x3A5 => array(0x3C5), 0x3A6 => array(0x3C6), 0x3A7 => array(0x3C7), - 0x3A8 => array(0x3C8), 0x3A9 => array(0x3C9), 0x3AA => array(0x3CA), - 0x3AB => array(0x3CB), 0x3CF => array(0x3D7), 0x3D0 => array(0x3B2), - 0x3D1 => array(0x3B8), 0x3D2 => array(0x3C5), 0x3D3 => array(0x3CD), - 0x3D4 => array(0x3CB), 0x3D5 => array(0x3C6), 0x3D6 => array(0x3C0), - 0x3D8 => array(0x3D9), 0x3DA => array(0x3DB), 0x3DC => array(0x3DD), - 0x3DE => array(0x3DF), 0x3E0 => array(0x3E1), 0x3E2 => array(0x3E3), - 0x3E4 => array(0x3E5), 0x3E6 => array(0x3E7), 0x3E8 => array(0x3E9), - 0x3EA => array(0x3EB), 0x3EC => array(0x3ED), 0x3EE => array(0x3EF), - 0x3F0 => array(0x3BA), 0x3F1 => array(0x3C1), 0x3F2 => array(0x3C3), - 0x3F4 => array(0x3B8), 0x3F5 => array(0x3B5), 0x3F7 => array(0x3F8), - 0x3F9 => array(0x3C3), 0x3FA => array(0x3FB), 0x3FD => array(0x37B), - 0x3FE => array(0x37C), 0x3FF => array(0x37D), 0x400 => array(0x450), - 0x401 => array(0x451), 0x402 => array(0x452), 0x403 => array(0x453), - 0x404 => array(0x454), 0x405 => array(0x455), 0x406 => array(0x456), - 0x407 => array(0x457), 0x408 => array(0x458), 0x409 => array(0x459), - 0x40A => array(0x45A), 0x40B => array(0x45B), 0x40C => array(0x45C), - 0x40D => array(0x45D), 0x40E => array(0x45E), 0x40F => array(0x45F), - 0x410 => array(0x430), 0x411 => array(0x431), 0x412 => array(0x432), - 0x413 => array(0x433), 0x414 => array(0x434), 0x415 => array(0x435), - 0x416 => array(0x436), 0x417 => array(0x437), 0x418 => array(0x438), - 0x419 => array(0x439), 0x41A => array(0x43A), 0x41B => array(0x43B), - 0x41C => array(0x43C), 0x41D => array(0x43D), 0x41E => array(0x43E), - 0x41F => array(0x43F), 0x420 => array(0x440), 0x421 => array(0x441), - 0x422 => array(0x442), 0x423 => array(0x443), 0x424 => array(0x444), - 0x425 => array(0x445), 0x426 => array(0x446), 0x427 => array(0x447), - 0x428 => array(0x448), 0x429 => array(0x449), 0x42A => array(0x44A), - 0x42B => array(0x44B), 0x42C => array(0x44C), 0x42D => array(0x44D), - 0x42E => array(0x44E), 0x42F => array(0x44F), 0x460 => array(0x461), - 0x462 => array(0x463), 0x464 => array(0x465), 0x466 => array(0x467), - 0x468 => array(0x469), 0x46A => array(0x46B), 0x46C => array(0x46D), - 0x46E => array(0x46F), 0x470 => array(0x471), 0x472 => array(0x473), - 0x474 => array(0x475), 0x476 => array(0x477), 0x478 => array(0x479), - 0x47A => array(0x47B), 0x47C => array(0x47D), 0x47E => array(0x47F), - 0x480 => array(0x481), 0x48A => array(0x48B), 0x48C => array(0x48D), - 0x48E => array(0x48F), 0x490 => array(0x491), 0x492 => array(0x493), - 0x494 => array(0x495), 0x496 => array(0x497), 0x498 => array(0x499), - 0x49A => array(0x49B), 0x49C => array(0x49D), 0x49E => array(0x49F), - 0x4A0 => array(0x4A1), 0x4A2 => array(0x4A3), 0x4A4 => array(0x4A5), - 0x4A6 => array(0x4A7), 0x4A8 => array(0x4A9), 0x4AA => array(0x4AB), - 0x4AC => array(0x4AD), 0x4AE => array(0x4AF), 0x4B0 => array(0x4B1), - 0x4B2 => array(0x4B3), 0x4B4 => array(0x4B5), 0x4B6 => array(0x4B7), - 0x4B8 => array(0x4B9), 0x4BA => array(0x4BB), 0x4BC => array(0x4BD), - 0x4BE => array(0x4BF), 0x4C1 => array(0x4C2), 0x4C3 => array(0x4C4), - 0x4C5 => array(0x4C6), 0x4C7 => array(0x4C8), 0x4C9 => array(0x4CA), - 0x4CB => array(0x4CC), 0x4CD => array(0x4CE), 0x4D0 => array(0x4D1), - 0x4D2 => array(0x4D3), 0x4D4 => array(0x4D5), 0x4D6 => array(0x4D7), - 0x4D8 => array(0x4D9), 0x4DA => array(0x4DB), 0x4DC => array(0x4DD), - 0x4DE => array(0x4DF), 0x4E0 => array(0x4E1), 0x4E2 => array(0x4E3), - 0x4E4 => array(0x4E5), 0x4E6 => array(0x4E7), 0x4E8 => array(0x4E9), - 0x4EA => array(0x4EB), 0x4EC => array(0x4ED), 0x4EE => array(0x4EF), - 0x4F0 => array(0x4F1), 0x4F2 => array(0x4F3), 0x4F4 => array(0x4F5), - 0x4F6 => array(0x4F7), 0x4F8 => array(0x4F9), 0x4FA => array(0x4FB), - 0x4FC => array(0x4FD), 0x4FE => array(0x4FF), 0x500 => array(0x501), - 0x502 => array(0x503), 0x504 => array(0x505), 0x506 => array(0x507), - 0x508 => array(0x509), 0x50A => array(0x50B), 0x50C => array(0x50D), - 0x50E => array(0x50F), 0x510 => array(0x511), 0x512 => array(0x513), - 0x514 => array(0x515), 0x516 => array(0x517), 0x518 => array(0x519), - 0x51A => array(0x51B), 0x51C => array(0x51D), 0x51E => array(0x51F), - 0x520 => array(0x521), 0x522 => array(0x523), 0x524 => array(0x525), - 0x526 => array(0x527), 0x528 => array(0x529), 0x52A => array(0x52B), - 0x52C => array(0x52D), 0x52E => array(0x52F), 0x531 => array(0x561), - 0x532 => array(0x562), 0x533 => array(0x563), 0x534 => array(0x564), - 0x535 => array(0x565), 0x536 => array(0x566), 0x537 => array(0x567), - 0x538 => array(0x568), 0x539 => array(0x569), 0x53A => array(0x56A), - 0x53B => array(0x56B), 0x53C => array(0x56C), 0x53D => array(0x56D), - 0x53E => array(0x56E), 0x53F => array(0x56F), 0x540 => array(0x570), - 0x541 => array(0x571), 0x542 => array(0x572), 0x543 => array(0x573), - 0x544 => array(0x574), 0x545 => array(0x575), 0x546 => array(0x576), - 0x547 => array(0x577), 0x548 => array(0x578), 0x549 => array(0x579), - 0x54A => array(0x57A), 0x54B => array(0x57B), 0x54C => array(0x57C), - 0x54D => array(0x57D), 0x54E => array(0x57E), 0x54F => array(0x57F), - 0x550 => array(0x580), 0x551 => array(0x581), 0x552 => array(0x582), - 0x553 => array(0x583), 0x554 => array(0x584), 0x555 => array(0x585), - 0x556 => array(0x586), 0x587 => array(0x565, 0x582), 0x675 => array(0x627, 0x674), - 0x676 => array(0x648, 0x674), 0x677 => array(0x6C7, 0x674), 0x678 => array(0x64A, 0x674), - 0x958 => array(0x915, 0x93C), 0x959 => array(0x916, 0x93C), 0x95A => array(0x917, 0x93C), - 0x95B => array(0x91C, 0x93C), 0x95C => array(0x921, 0x93C), 0x95D => array(0x922, 0x93C), - 0x95E => array(0x92B, 0x93C), 0x95F => array(0x92F, 0x93C), 0x9DC => array(0x9A1, 0x9BC), - 0x9DD => array(0x9A2, 0x9BC), 0x9DF => array(0x9AF, 0x9BC), 0xA33 => array(0xA32, 0xA3C), - 0xA36 => array(0xA38, 0xA3C), 0xA59 => array(0xA16, 0xA3C), 0xA5A => array(0xA17, 0xA3C), - 0xA5B => array(0xA1C, 0xA3C), 0xA5E => array(0xA2B, 0xA3C), 0xB5C => array(0xB21, 0xB3C), - 0xB5D => array(0xB22, 0xB3C), 0xE33 => array(0xE4D, 0xE32), 0xEB3 => array(0xECD, 0xEB2), - 0xEDC => array(0xEAB, 0xE99), 0xEDD => array(0xEAB, 0xEA1), 0xF0C => array(0xF0B), - 0xF43 => array(0xF42, 0xFB7), 0xF4D => array(0xF4C, 0xFB7), 0xF52 => array(0xF51, 0xFB7), - 0xF57 => array(0xF56, 0xFB7), 0xF5C => array(0xF5B, 0xFB7), 0xF69 => array(0xF40, 0xFB5), - 0xF73 => array(0xF71, 0xF72), 0xF75 => array(0xF71, 0xF74), 0xF76 => array(0xFB2, 0xF80), - 0xF77 => array(0xFB2, 0xF71, 0xF80), 0xF78 => array(0xFB3, 0xF80), 0xF79 => array(0xFB3, 0xF71, 0xF80), - 0xF81 => array(0xF71, 0xF80), 0xF93 => array(0xF92, 0xFB7), 0xF9D => array(0xF9C, 0xFB7), - 0xFA2 => array(0xFA1, 0xFB7), 0xFA7 => array(0xFA6, 0xFB7), 0xFAC => array(0xFAB, 0xFB7), - 0xFB9 => array(0xF90, 0xFB5), 0x10C7 => array(0x2D27), 0x10CD => array(0x2D2D), - 0x10FC => array(0x10DC), 0x1D2C => array(0x61), 0x1D2D => array(0xE6), - 0x1D2E => array(0x62), 0x1D30 => array(0x64), 0x1D31 => array(0x65), - 0x1D32 => array(0x1DD), 0x1D33 => array(0x67), 0x1D34 => array(0x68), - 0x1D35 => array(0x69), 0x1D36 => array(0x6A), 0x1D37 => array(0x6B), - 0x1D38 => array(0x6C), 0x1D39 => array(0x6D), 0x1D3A => array(0x6E), - 0x1D3C => array(0x6F), 0x1D3D => array(0x223), 0x1D3E => array(0x70), - 0x1D3F => array(0x72), 0x1D40 => array(0x74), 0x1D41 => array(0x75), - 0x1D42 => array(0x77), 0x1D43 => array(0x61), 0x1D44 => array(0x250), - 0x1D45 => array(0x251), 0x1D46 => array(0x1D02), 0x1D47 => array(0x62), - 0x1D48 => array(0x64), 0x1D49 => array(0x65), 0x1D4A => array(0x259), - 0x1D4B => array(0x25B), 0x1D4C => array(0x25C), 0x1D4D => array(0x67), - 0x1D4F => array(0x6B), 0x1D50 => array(0x6D), 0x1D51 => array(0x14B), - 0x1D52 => array(0x6F), 0x1D53 => array(0x254), 0x1D54 => array(0x1D16), - 0x1D55 => array(0x1D17), 0x1D56 => array(0x70), 0x1D57 => array(0x74), - 0x1D58 => array(0x75), 0x1D59 => array(0x1D1D), 0x1D5A => array(0x26F), - 0x1D5B => array(0x76), 0x1D5C => array(0x1D25), 0x1D5D => array(0x3B2), - 0x1D5E => array(0x3B3), 0x1D5F => array(0x3B4), 0x1D60 => array(0x3C6), - 0x1D61 => array(0x3C7), 0x1D62 => array(0x69), 0x1D63 => array(0x72), - 0x1D64 => array(0x75), 0x1D65 => array(0x76), 0x1D66 => array(0x3B2), - 0x1D67 => array(0x3B3), 0x1D68 => array(0x3C1), 0x1D69 => array(0x3C6), - 0x1D6A => array(0x3C7), 0x1D78 => array(0x43D), 0x1D9B => array(0x252), - 0x1D9C => array(0x63), 0x1D9D => array(0x255), 0x1D9E => array(0xF0), - 0x1D9F => array(0x25C), 0x1DA0 => array(0x66), 0x1DA1 => array(0x25F), - 0x1DA2 => array(0x261), 0x1DA3 => array(0x265), 0x1DA4 => array(0x268), - 0x1DA5 => array(0x269), 0x1DA6 => array(0x26A), 0x1DA7 => array(0x1D7B), - 0x1DA8 => array(0x29D), 0x1DA9 => array(0x26D), 0x1DAA => array(0x1D85), - 0x1DAB => array(0x29F), 0x1DAC => array(0x271), 0x1DAD => array(0x270), - 0x1DAE => array(0x272), 0x1DAF => array(0x273), 0x1DB0 => array(0x274), - 0x1DB1 => array(0x275), 0x1DB2 => array(0x278), 0x1DB3 => array(0x282), - 0x1DB4 => array(0x283), 0x1DB5 => array(0x1AB), 0x1DB6 => array(0x289), - 0x1DB7 => array(0x28A), 0x1DB8 => array(0x1D1C), 0x1DB9 => array(0x28B), - 0x1DBA => array(0x28C), 0x1DBB => array(0x7A), 0x1DBC => array(0x290), - 0x1DBD => array(0x291), 0x1DBE => array(0x292), 0x1DBF => array(0x3B8), - 0x1E00 => array(0x1E01), 0x1E02 => array(0x1E03), 0x1E04 => array(0x1E05), - 0x1E06 => array(0x1E07), 0x1E08 => array(0x1E09), 0x1E0A => array(0x1E0B), - 0x1E0C => array(0x1E0D), 0x1E0E => array(0x1E0F), 0x1E10 => array(0x1E11), - 0x1E12 => array(0x1E13), 0x1E14 => array(0x1E15), 0x1E16 => array(0x1E17), - 0x1E18 => array(0x1E19), 0x1E1A => array(0x1E1B), 0x1E1C => array(0x1E1D), - 0x1E1E => array(0x1E1F), 0x1E20 => array(0x1E21), 0x1E22 => array(0x1E23), - 0x1E24 => array(0x1E25), 0x1E26 => array(0x1E27), 0x1E28 => array(0x1E29), - 0x1E2A => array(0x1E2B), 0x1E2C => array(0x1E2D), 0x1E2E => array(0x1E2F), - 0x1E30 => array(0x1E31), 0x1E32 => array(0x1E33), 0x1E34 => array(0x1E35), - 0x1E36 => array(0x1E37), 0x1E38 => array(0x1E39), 0x1E3A => array(0x1E3B), - 0x1E3C => array(0x1E3D), 0x1E3E => array(0x1E3F), 0x1E40 => array(0x1E41), - 0x1E42 => array(0x1E43), 0x1E44 => array(0x1E45), 0x1E46 => array(0x1E47), - 0x1E48 => array(0x1E49), 0x1E4A => array(0x1E4B), 0x1E4C => array(0x1E4D), - 0x1E4E => array(0x1E4F), 0x1E50 => array(0x1E51), 0x1E52 => array(0x1E53), - 0x1E54 => array(0x1E55), 0x1E56 => array(0x1E57), 0x1E58 => array(0x1E59), - 0x1E5A => array(0x1E5B), 0x1E5C => array(0x1E5D), 0x1E5E => array(0x1E5F), - 0x1E60 => array(0x1E61), 0x1E62 => array(0x1E63), 0x1E64 => array(0x1E65), - 0x1E66 => array(0x1E67), 0x1E68 => array(0x1E69), 0x1E6A => array(0x1E6B), - 0x1E6C => array(0x1E6D), 0x1E6E => array(0x1E6F), 0x1E70 => array(0x1E71), - 0x1E72 => array(0x1E73), 0x1E74 => array(0x1E75), 0x1E76 => array(0x1E77), - 0x1E78 => array(0x1E79), 0x1E7A => array(0x1E7B), 0x1E7C => array(0x1E7D), - 0x1E7E => array(0x1E7F), 0x1E80 => array(0x1E81), 0x1E82 => array(0x1E83), - 0x1E84 => array(0x1E85), 0x1E86 => array(0x1E87), 0x1E88 => array(0x1E89), - 0x1E8A => array(0x1E8B), 0x1E8C => array(0x1E8D), 0x1E8E => array(0x1E8F), - 0x1E90 => array(0x1E91), 0x1E92 => array(0x1E93), 0x1E94 => array(0x1E95), - 0x1E9A => array(0x61, 0x2BE), 0x1E9B => array(0x1E61), 0x1E9E => array(0x73, 0x73), - 0x1EA0 => array(0x1EA1), 0x1EA2 => array(0x1EA3), 0x1EA4 => array(0x1EA5), - 0x1EA6 => array(0x1EA7), 0x1EA8 => array(0x1EA9), 0x1EAA => array(0x1EAB), - 0x1EAC => array(0x1EAD), 0x1EAE => array(0x1EAF), 0x1EB0 => array(0x1EB1), - 0x1EB2 => array(0x1EB3), 0x1EB4 => array(0x1EB5), 0x1EB6 => array(0x1EB7), - 0x1EB8 => array(0x1EB9), 0x1EBA => array(0x1EBB), 0x1EBC => array(0x1EBD), - 0x1EBE => array(0x1EBF), 0x1EC0 => array(0x1EC1), 0x1EC2 => array(0x1EC3), - 0x1EC4 => array(0x1EC5), 0x1EC6 => array(0x1EC7), 0x1EC8 => array(0x1EC9), - 0x1ECA => array(0x1ECB), 0x1ECC => array(0x1ECD), 0x1ECE => array(0x1ECF), - 0x1ED0 => array(0x1ED1), 0x1ED2 => array(0x1ED3), 0x1ED4 => array(0x1ED5), - 0x1ED6 => array(0x1ED7), 0x1ED8 => array(0x1ED9), 0x1EDA => array(0x1EDB), - 0x1EDC => array(0x1EDD), 0x1EDE => array(0x1EDF), 0x1EE0 => array(0x1EE1), - 0x1EE2 => array(0x1EE3), 0x1EE4 => array(0x1EE5), 0x1EE6 => array(0x1EE7), - 0x1EE8 => array(0x1EE9), 0x1EEA => array(0x1EEB), 0x1EEC => array(0x1EED), - 0x1EEE => array(0x1EEF), 0x1EF0 => array(0x1EF1), 0x1EF2 => array(0x1EF3), - 0x1EF4 => array(0x1EF5), 0x1EF6 => array(0x1EF7), 0x1EF8 => array(0x1EF9), - 0x1EFA => array(0x1EFB), 0x1EFC => array(0x1EFD), 0x1EFE => array(0x1EFF), - 0x1F08 => array(0x1F00), 0x1F09 => array(0x1F01), 0x1F0A => array(0x1F02), - 0x1F0B => array(0x1F03), 0x1F0C => array(0x1F04), 0x1F0D => array(0x1F05), - 0x1F0E => array(0x1F06), 0x1F0F => array(0x1F07), 0x1F18 => array(0x1F10), - 0x1F19 => array(0x1F11), 0x1F1A => array(0x1F12), 0x1F1B => array(0x1F13), - 0x1F1C => array(0x1F14), 0x1F1D => array(0x1F15), 0x1F28 => array(0x1F20), - 0x1F29 => array(0x1F21), 0x1F2A => array(0x1F22), 0x1F2B => array(0x1F23), - 0x1F2C => array(0x1F24), 0x1F2D => array(0x1F25), 0x1F2E => array(0x1F26), - 0x1F2F => array(0x1F27), 0x1F38 => array(0x1F30), 0x1F39 => array(0x1F31), - 0x1F3A => array(0x1F32), 0x1F3B => array(0x1F33), 0x1F3C => array(0x1F34), - 0x1F3D => array(0x1F35), 0x1F3E => array(0x1F36), 0x1F3F => array(0x1F37), - 0x1F48 => array(0x1F40), 0x1F49 => array(0x1F41), 0x1F4A => array(0x1F42), - 0x1F4B => array(0x1F43), 0x1F4C => array(0x1F44), 0x1F4D => array(0x1F45), - 0x1F59 => array(0x1F51), 0x1F5B => array(0x1F53), 0x1F5D => array(0x1F55), - 0x1F5F => array(0x1F57), 0x1F68 => array(0x1F60), 0x1F69 => array(0x1F61), - 0x1F6A => array(0x1F62), 0x1F6B => array(0x1F63), 0x1F6C => array(0x1F64), - 0x1F6D => array(0x1F65), 0x1F6E => array(0x1F66), 0x1F6F => array(0x1F67), - 0x1F71 => array(0x3AC), 0x1F73 => array(0x3AD), 0x1F75 => array(0x3AE), - 0x1F77 => array(0x3AF), 0x1F79 => array(0x3CC), 0x1F7B => array(0x3CD), - 0x1F7D => array(0x3CE), 0x1F80 => array(0x1F00, 0x3B9), 0x1F81 => array(0x1F01, 0x3B9), - 0x1F82 => array(0x1F02, 0x3B9), 0x1F83 => array(0x1F03, 0x3B9), 0x1F84 => array(0x1F04, 0x3B9), - 0x1F85 => array(0x1F05, 0x3B9), 0x1F86 => array(0x1F06, 0x3B9), 0x1F87 => array(0x1F07, 0x3B9), - 0x1F88 => array(0x1F00, 0x3B9), 0x1F89 => array(0x1F01, 0x3B9), 0x1F8A => array(0x1F02, 0x3B9), - 0x1F8B => array(0x1F03, 0x3B9), 0x1F8C => array(0x1F04, 0x3B9), 0x1F8D => array(0x1F05, 0x3B9), - 0x1F8E => array(0x1F06, 0x3B9), 0x1F8F => array(0x1F07, 0x3B9), 0x1F90 => array(0x1F20, 0x3B9), - 0x1F91 => array(0x1F21, 0x3B9), 0x1F92 => array(0x1F22, 0x3B9), 0x1F93 => array(0x1F23, 0x3B9), - 0x1F94 => array(0x1F24, 0x3B9), 0x1F95 => array(0x1F25, 0x3B9), 0x1F96 => array(0x1F26, 0x3B9), - 0x1F97 => array(0x1F27, 0x3B9), 0x1F98 => array(0x1F20, 0x3B9), 0x1F99 => array(0x1F21, 0x3B9), - 0x1F9A => array(0x1F22, 0x3B9), 0x1F9B => array(0x1F23, 0x3B9), 0x1F9C => array(0x1F24, 0x3B9), - 0x1F9D => array(0x1F25, 0x3B9), 0x1F9E => array(0x1F26, 0x3B9), 0x1F9F => array(0x1F27, 0x3B9), - 0x1FA0 => array(0x1F60, 0x3B9), 0x1FA1 => array(0x1F61, 0x3B9), 0x1FA2 => array(0x1F62, 0x3B9), - 0x1FA3 => array(0x1F63, 0x3B9), 0x1FA4 => array(0x1F64, 0x3B9), 0x1FA5 => array(0x1F65, 0x3B9), - 0x1FA6 => array(0x1F66, 0x3B9), 0x1FA7 => array(0x1F67, 0x3B9), 0x1FA8 => array(0x1F60, 0x3B9), - 0x1FA9 => array(0x1F61, 0x3B9), 0x1FAA => array(0x1F62, 0x3B9), 0x1FAB => array(0x1F63, 0x3B9), - 0x1FAC => array(0x1F64, 0x3B9), 0x1FAD => array(0x1F65, 0x3B9), 0x1FAE => array(0x1F66, 0x3B9), - 0x1FAF => array(0x1F67, 0x3B9), 0x1FB2 => array(0x1F70, 0x3B9), 0x1FB3 => array(0x3B1, 0x3B9), - 0x1FB4 => array(0x3AC, 0x3B9), 0x1FB7 => array(0x1FB6, 0x3B9), 0x1FB8 => array(0x1FB0), - 0x1FB9 => array(0x1FB1), 0x1FBA => array(0x1F70), 0x1FBB => array(0x3AC), - 0x1FBC => array(0x3B1, 0x3B9), 0x1FBE => array(0x3B9), 0x1FC2 => array(0x1F74, 0x3B9), - 0x1FC3 => array(0x3B7, 0x3B9), 0x1FC4 => array(0x3AE, 0x3B9), 0x1FC7 => array(0x1FC6, 0x3B9), - 0x1FC8 => array(0x1F72), 0x1FC9 => array(0x3AD), 0x1FCA => array(0x1F74), - 0x1FCB => array(0x3AE), 0x1FCC => array(0x3B7, 0x3B9), 0x1FD3 => array(0x390), - 0x1FD8 => array(0x1FD0), 0x1FD9 => array(0x1FD1), 0x1FDA => array(0x1F76), - 0x1FDB => array(0x3AF), 0x1FE3 => array(0x3B0), 0x1FE8 => array(0x1FE0), - 0x1FE9 => array(0x1FE1), 0x1FEA => array(0x1F7A), 0x1FEB => array(0x3CD), - 0x1FEC => array(0x1FE5), 0x1FF2 => array(0x1F7C, 0x3B9), 0x1FF3 => array(0x3C9, 0x3B9), - 0x1FF4 => array(0x3CE, 0x3B9), 0x1FF7 => array(0x1FF6, 0x3B9), 0x1FF8 => array(0x1F78), - 0x1FF9 => array(0x3CC), 0x1FFA => array(0x1F7C), 0x1FFB => array(0x3CE), - 0x1FFC => array(0x3C9, 0x3B9), 0x2011 => array(0x2010), 0x2033 => array(0x2032, 0x2032), - 0x2034 => array(0x2032, 0x2032, 0x2032), 0x2036 => array(0x2035, 0x2035), 0x2037 => array(0x2035, 0x2035, 0x2035), - 0x2057 => array(0x2032, 0x2032, 0x2032, 0x2032), 0x2070 => array(0x30), 0x2071 => array(0x69), - 0x2074 => array(0x34), 0x2075 => array(0x35), 0x2076 => array(0x36), - 0x2077 => array(0x37), 0x2078 => array(0x38), 0x2079 => array(0x39), - 0x207B => array(0x2212), 0x207F => array(0x6E), 0x2080 => array(0x30), - 0x2081 => array(0x31), 0x2082 => array(0x32), 0x2083 => array(0x33), - 0x2084 => array(0x34), 0x2085 => array(0x35), 0x2086 => array(0x36), - 0x2087 => array(0x37), 0x2088 => array(0x38), 0x2089 => array(0x39), - 0x208B => array(0x2212), 0x2090 => array(0x61), 0x2091 => array(0x65), - 0x2092 => array(0x6F), 0x2093 => array(0x78), 0x2094 => array(0x259), - 0x2095 => array(0x68), 0x2096 => array(0x6B), 0x2097 => array(0x6C), - 0x2098 => array(0x6D), 0x2099 => array(0x6E), 0x209A => array(0x70), - 0x209B => array(0x73), 0x209C => array(0x74), 0x20A8 => array(0x72, 0x73), - 0x2102 => array(0x63), 0x2103 => array(0xB0, 0x63), 0x2107 => array(0x25B), - 0x2109 => array(0xB0, 0x66), 0x210A => array(0x67), 0x210B => array(0x68), - 0x210C => array(0x68), 0x210D => array(0x68), 0x210E => array(0x68), - 0x210F => array(0x127), 0x2110 => array(0x69), 0x2111 => array(0x69), - 0x2112 => array(0x6C), 0x2113 => array(0x6C), 0x2115 => array(0x6E), - 0x2116 => array(0x6E, 0x6F), 0x2119 => array(0x70), 0x211A => array(0x71), - 0x211B => array(0x72), 0x211C => array(0x72), 0x211D => array(0x72), - 0x2120 => array(0x73, 0x6D), 0x2121 => array(0x74, 0x65, 0x6C), 0x2122 => array(0x74, 0x6D), - 0x2124 => array(0x7A), 0x2126 => array(0x3C9), 0x2128 => array(0x7A), - 0x212A => array(0x6B), 0x212B => array(0xE5), 0x212C => array(0x62), - 0x212D => array(0x63), 0x212F => array(0x65), 0x2130 => array(0x65), - 0x2131 => array(0x66), 0x2133 => array(0x6D), 0x2134 => array(0x6F), - 0x2135 => array(0x5D0), 0x2136 => array(0x5D1), 0x2137 => array(0x5D2), - 0x2138 => array(0x5D3), 0x2139 => array(0x69), 0x213B => array(0x66, 0x61, 0x78), - 0x213C => array(0x3C0), 0x213D => array(0x3B3), 0x213E => array(0x3B3), - 0x213F => array(0x3C0), 0x2140 => array(0x2211), 0x2145 => array(0x64), - 0x2146 => array(0x64), 0x2147 => array(0x65), 0x2148 => array(0x69), - 0x2149 => array(0x6A), 0x2150 => array(0x31, 0x2044, 0x37), 0x2151 => array(0x31, 0x2044, 0x39), - 0x2152 => array(0x31, 0x2044, 0x31, 0x30), 0x2153 => array(0x31, 0x2044, 0x33), 0x2154 => array(0x32, 0x2044, 0x33), - 0x2155 => array(0x31, 0x2044, 0x35), 0x2156 => array(0x32, 0x2044, 0x35), 0x2157 => array(0x33, 0x2044, 0x35), - 0x2158 => array(0x34, 0x2044, 0x35), 0x2159 => array(0x31, 0x2044, 0x36), 0x215A => array(0x35, 0x2044, 0x36), - 0x215B => array(0x31, 0x2044, 0x38), 0x215C => array(0x33, 0x2044, 0x38), 0x215D => array(0x35, 0x2044, 0x38), - 0x215E => array(0x37, 0x2044, 0x38), 0x215F => array(0x31, 0x2044), 0x2160 => array(0x69), - 0x2161 => array(0x69, 0x69), 0x2162 => array(0x69, 0x69, 0x69), 0x2163 => array(0x69, 0x76), - 0x2164 => array(0x76), 0x2165 => array(0x76, 0x69), 0x2166 => array(0x76, 0x69, 0x69), - 0x2167 => array(0x76, 0x69, 0x69, 0x69), 0x2168 => array(0x69, 0x78), 0x2169 => array(0x78), - 0x216A => array(0x78, 0x69), 0x216B => array(0x78, 0x69, 0x69), 0x216C => array(0x6C), - 0x216D => array(0x63), 0x216E => array(0x64), 0x216F => array(0x6D), - 0x2170 => array(0x69), 0x2171 => array(0x69, 0x69), 0x2172 => array(0x69, 0x69, 0x69), - 0x2173 => array(0x69, 0x76), 0x2174 => array(0x76), 0x2175 => array(0x76, 0x69), - 0x2176 => array(0x76, 0x69, 0x69), 0x2177 => array(0x76, 0x69, 0x69, 0x69), 0x2178 => array(0x69, 0x78), - 0x2179 => array(0x78), 0x217A => array(0x78, 0x69), 0x217B => array(0x78, 0x69, 0x69), - 0x217C => array(0x6C), 0x217D => array(0x63), 0x217E => array(0x64), - 0x217F => array(0x6D), 0x2189 => array(0x30, 0x2044, 0x33), 0x222C => array(0x222B, 0x222B), - 0x222D => array(0x222B, 0x222B, 0x222B), 0x222F => array(0x222E, 0x222E), 0x2230 => array(0x222E, 0x222E, 0x222E), - 0x2329 => array(0x3008), 0x232A => array(0x3009), 0x2460 => array(0x31), - 0x2461 => array(0x32), 0x2462 => array(0x33), 0x2463 => array(0x34), - 0x2464 => array(0x35), 0x2465 => array(0x36), 0x2466 => array(0x37), - 0x2467 => array(0x38), 0x2468 => array(0x39), 0x2469 => array(0x31, 0x30), - 0x246A => array(0x31, 0x31), 0x246B => array(0x31, 0x32), 0x246C => array(0x31, 0x33), - 0x246D => array(0x31, 0x34), 0x246E => array(0x31, 0x35), 0x246F => array(0x31, 0x36), - 0x2470 => array(0x31, 0x37), 0x2471 => array(0x31, 0x38), 0x2472 => array(0x31, 0x39), - 0x2473 => array(0x32, 0x30), 0x24B6 => array(0x61), 0x24B7 => array(0x62), - 0x24B8 => array(0x63), 0x24B9 => array(0x64), 0x24BA => array(0x65), - 0x24BB => array(0x66), 0x24BC => array(0x67), 0x24BD => array(0x68), - 0x24BE => array(0x69), 0x24BF => array(0x6A), 0x24C0 => array(0x6B), - 0x24C1 => array(0x6C), 0x24C2 => array(0x6D), 0x24C3 => array(0x6E), - 0x24C4 => array(0x6F), 0x24C5 => array(0x70), 0x24C6 => array(0x71), - 0x24C7 => array(0x72), 0x24C8 => array(0x73), 0x24C9 => array(0x74), - 0x24CA => array(0x75), 0x24CB => array(0x76), 0x24CC => array(0x77), - 0x24CD => array(0x78), 0x24CE => array(0x79), 0x24CF => array(0x7A), - 0x24D0 => array(0x61), 0x24D1 => array(0x62), 0x24D2 => array(0x63), - 0x24D3 => array(0x64), 0x24D4 => array(0x65), 0x24D5 => array(0x66), - 0x24D6 => array(0x67), 0x24D7 => array(0x68), 0x24D8 => array(0x69), - 0x24D9 => array(0x6A), 0x24DA => array(0x6B), 0x24DB => array(0x6C), - 0x24DC => array(0x6D), 0x24DD => array(0x6E), 0x24DE => array(0x6F), - 0x24DF => array(0x70), 0x24E0 => array(0x71), 0x24E1 => array(0x72), - 0x24E2 => array(0x73), 0x24E3 => array(0x74), 0x24E4 => array(0x75), - 0x24E5 => array(0x76), 0x24E6 => array(0x77), 0x24E7 => array(0x78), - 0x24E8 => array(0x79), 0x24E9 => array(0x7A), 0x24EA => array(0x30), - 0x2A0C => array(0x222B, 0x222B, 0x222B, 0x222B), 0x2ADC => array(0x2ADD, 0x338), 0x2C00 => array(0x2C30), - 0x2C01 => array(0x2C31), 0x2C02 => array(0x2C32), 0x2C03 => array(0x2C33), - 0x2C04 => array(0x2C34), 0x2C05 => array(0x2C35), 0x2C06 => array(0x2C36), - 0x2C07 => array(0x2C37), 0x2C08 => array(0x2C38), 0x2C09 => array(0x2C39), - 0x2C0A => array(0x2C3A), 0x2C0B => array(0x2C3B), 0x2C0C => array(0x2C3C), - 0x2C0D => array(0x2C3D), 0x2C0E => array(0x2C3E), 0x2C0F => array(0x2C3F), - 0x2C10 => array(0x2C40), 0x2C11 => array(0x2C41), 0x2C12 => array(0x2C42), - 0x2C13 => array(0x2C43), 0x2C14 => array(0x2C44), 0x2C15 => array(0x2C45), - 0x2C16 => array(0x2C46), 0x2C17 => array(0x2C47), 0x2C18 => array(0x2C48), - 0x2C19 => array(0x2C49), 0x2C1A => array(0x2C4A), 0x2C1B => array(0x2C4B), - 0x2C1C => array(0x2C4C), 0x2C1D => array(0x2C4D), 0x2C1E => array(0x2C4E), - 0x2C1F => array(0x2C4F), 0x2C20 => array(0x2C50), 0x2C21 => array(0x2C51), - 0x2C22 => array(0x2C52), 0x2C23 => array(0x2C53), 0x2C24 => array(0x2C54), - 0x2C25 => array(0x2C55), 0x2C26 => array(0x2C56), 0x2C27 => array(0x2C57), - 0x2C28 => array(0x2C58), 0x2C29 => array(0x2C59), 0x2C2A => array(0x2C5A), - 0x2C2B => array(0x2C5B), 0x2C2C => array(0x2C5C), 0x2C2D => array(0x2C5D), - 0x2C2E => array(0x2C5E), 0x2C60 => array(0x2C61), 0x2C62 => array(0x26B), - 0x2C63 => array(0x1D7D), 0x2C64 => array(0x27D), 0x2C67 => array(0x2C68), - 0x2C69 => array(0x2C6A), 0x2C6B => array(0x2C6C), 0x2C6D => array(0x251), - 0x2C6E => array(0x271), 0x2C6F => array(0x250), 0x2C70 => array(0x252), - 0x2C72 => array(0x2C73), 0x2C75 => array(0x2C76), 0x2C7C => array(0x6A), - 0x2C7D => array(0x76), 0x2C7E => array(0x23F), 0x2C7F => array(0x240), - 0x2C80 => array(0x2C81), 0x2C82 => array(0x2C83), 0x2C84 => array(0x2C85), - 0x2C86 => array(0x2C87), 0x2C88 => array(0x2C89), 0x2C8A => array(0x2C8B), - 0x2C8C => array(0x2C8D), 0x2C8E => array(0x2C8F), 0x2C90 => array(0x2C91), - 0x2C92 => array(0x2C93), 0x2C94 => array(0x2C95), 0x2C96 => array(0x2C97), - 0x2C98 => array(0x2C99), 0x2C9A => array(0x2C9B), 0x2C9C => array(0x2C9D), - 0x2C9E => array(0x2C9F), 0x2CA0 => array(0x2CA1), 0x2CA2 => array(0x2CA3), - 0x2CA4 => array(0x2CA5), 0x2CA6 => array(0x2CA7), 0x2CA8 => array(0x2CA9), - 0x2CAA => array(0x2CAB), 0x2CAC => array(0x2CAD), 0x2CAE => array(0x2CAF), - 0x2CB0 => array(0x2CB1), 0x2CB2 => array(0x2CB3), 0x2CB4 => array(0x2CB5), - 0x2CB6 => array(0x2CB7), 0x2CB8 => array(0x2CB9), 0x2CBA => array(0x2CBB), - 0x2CBC => array(0x2CBD), 0x2CBE => array(0x2CBF), 0x2CC0 => array(0x2CC1), - 0x2CC2 => array(0x2CC3), 0x2CC4 => array(0x2CC5), 0x2CC6 => array(0x2CC7), - 0x2CC8 => array(0x2CC9), 0x2CCA => array(0x2CCB), 0x2CCC => array(0x2CCD), - 0x2CCE => array(0x2CCF), 0x2CD0 => array(0x2CD1), 0x2CD2 => array(0x2CD3), - 0x2CD4 => array(0x2CD5), 0x2CD6 => array(0x2CD7), 0x2CD8 => array(0x2CD9), - 0x2CDA => array(0x2CDB), 0x2CDC => array(0x2CDD), 0x2CDE => array(0x2CDF), - 0x2CE0 => array(0x2CE1), 0x2CE2 => array(0x2CE3), 0x2CEB => array(0x2CEC), - 0x2CED => array(0x2CEE), 0x2CF2 => array(0x2CF3), 0x2D6F => array(0x2D61), - 0x2E9F => array(0x6BCD), 0x2EF3 => array(0x9F9F), 0x2F00 => array(0x4E00), - 0x2F01 => array(0x4E28), 0x2F02 => array(0x4E36), 0x2F03 => array(0x4E3F), - 0x2F04 => array(0x4E59), 0x2F05 => array(0x4E85), 0x2F06 => array(0x4E8C), - 0x2F07 => array(0x4EA0), 0x2F08 => array(0x4EBA), 0x2F09 => array(0x513F), - 0x2F0A => array(0x5165), 0x2F0B => array(0x516B), 0x2F0C => array(0x5182), - 0x2F0D => array(0x5196), 0x2F0E => array(0x51AB), 0x2F0F => array(0x51E0), - 0x2F10 => array(0x51F5), 0x2F11 => array(0x5200), 0x2F12 => array(0x529B), - 0x2F13 => array(0x52F9), 0x2F14 => array(0x5315), 0x2F15 => array(0x531A), - 0x2F16 => array(0x5338), 0x2F17 => array(0x5341), 0x2F18 => array(0x535C), - 0x2F19 => array(0x5369), 0x2F1A => array(0x5382), 0x2F1B => array(0x53B6), - 0x2F1C => array(0x53C8), 0x2F1D => array(0x53E3), 0x2F1E => array(0x56D7), - 0x2F1F => array(0x571F), 0x2F20 => array(0x58EB), 0x2F21 => array(0x5902), - 0x2F22 => array(0x590A), 0x2F23 => array(0x5915), 0x2F24 => array(0x5927), - 0x2F25 => array(0x5973), 0x2F26 => array(0x5B50), 0x2F27 => array(0x5B80), - 0x2F28 => array(0x5BF8), 0x2F29 => array(0x5C0F), 0x2F2A => array(0x5C22), - 0x2F2B => array(0x5C38), 0x2F2C => array(0x5C6E), 0x2F2D => array(0x5C71), - 0x2F2E => array(0x5DDB), 0x2F2F => array(0x5DE5), 0x2F30 => array(0x5DF1), - 0x2F31 => array(0x5DFE), 0x2F32 => array(0x5E72), 0x2F33 => array(0x5E7A), - 0x2F34 => array(0x5E7F), 0x2F35 => array(0x5EF4), 0x2F36 => array(0x5EFE), - 0x2F37 => array(0x5F0B), 0x2F38 => array(0x5F13), 0x2F39 => array(0x5F50), - 0x2F3A => array(0x5F61), 0x2F3B => array(0x5F73), 0x2F3C => array(0x5FC3), - 0x2F3D => array(0x6208), 0x2F3E => array(0x6236), 0x2F3F => array(0x624B), - 0x2F40 => array(0x652F), 0x2F41 => array(0x6534), 0x2F42 => array(0x6587), - 0x2F43 => array(0x6597), 0x2F44 => array(0x65A4), 0x2F45 => array(0x65B9), - 0x2F46 => array(0x65E0), 0x2F47 => array(0x65E5), 0x2F48 => array(0x66F0), - 0x2F49 => array(0x6708), 0x2F4A => array(0x6728), 0x2F4B => array(0x6B20), - 0x2F4C => array(0x6B62), 0x2F4D => array(0x6B79), 0x2F4E => array(0x6BB3), - 0x2F4F => array(0x6BCB), 0x2F50 => array(0x6BD4), 0x2F51 => array(0x6BDB), - 0x2F52 => array(0x6C0F), 0x2F53 => array(0x6C14), 0x2F54 => array(0x6C34), - 0x2F55 => array(0x706B), 0x2F56 => array(0x722A), 0x2F57 => array(0x7236), - 0x2F58 => array(0x723B), 0x2F59 => array(0x723F), 0x2F5A => array(0x7247), - 0x2F5B => array(0x7259), 0x2F5C => array(0x725B), 0x2F5D => array(0x72AC), - 0x2F5E => array(0x7384), 0x2F5F => array(0x7389), 0x2F60 => array(0x74DC), - 0x2F61 => array(0x74E6), 0x2F62 => array(0x7518), 0x2F63 => array(0x751F), - 0x2F64 => array(0x7528), 0x2F65 => array(0x7530), 0x2F66 => array(0x758B), - 0x2F67 => array(0x7592), 0x2F68 => array(0x7676), 0x2F69 => array(0x767D), - 0x2F6A => array(0x76AE), 0x2F6B => array(0x76BF), 0x2F6C => array(0x76EE), - 0x2F6D => array(0x77DB), 0x2F6E => array(0x77E2), 0x2F6F => array(0x77F3), - 0x2F70 => array(0x793A), 0x2F71 => array(0x79B8), 0x2F72 => array(0x79BE), - 0x2F73 => array(0x7A74), 0x2F74 => array(0x7ACB), 0x2F75 => array(0x7AF9), - 0x2F76 => array(0x7C73), 0x2F77 => array(0x7CF8), 0x2F78 => array(0x7F36), - 0x2F79 => array(0x7F51), 0x2F7A => array(0x7F8A), 0x2F7B => array(0x7FBD), - 0x2F7C => array(0x8001), 0x2F7D => array(0x800C), 0x2F7E => array(0x8012), - 0x2F7F => array(0x8033), 0x2F80 => array(0x807F), 0x2F81 => array(0x8089), - 0x2F82 => array(0x81E3), 0x2F83 => array(0x81EA), 0x2F84 => array(0x81F3), - 0x2F85 => array(0x81FC), 0x2F86 => array(0x820C), 0x2F87 => array(0x821B), - 0x2F88 => array(0x821F), 0x2F89 => array(0x826E), 0x2F8A => array(0x8272), - 0x2F8B => array(0x8278), 0x2F8C => array(0x864D), 0x2F8D => array(0x866B), - 0x2F8E => array(0x8840), 0x2F8F => array(0x884C), 0x2F90 => array(0x8863), - 0x2F91 => array(0x897E), 0x2F92 => array(0x898B), 0x2F93 => array(0x89D2), - 0x2F94 => array(0x8A00), 0x2F95 => array(0x8C37), 0x2F96 => array(0x8C46), - 0x2F97 => array(0x8C55), 0x2F98 => array(0x8C78), 0x2F99 => array(0x8C9D), - 0x2F9A => array(0x8D64), 0x2F9B => array(0x8D70), 0x2F9C => array(0x8DB3), - 0x2F9D => array(0x8EAB), 0x2F9E => array(0x8ECA), 0x2F9F => array(0x8F9B), - 0x2FA0 => array(0x8FB0), 0x2FA1 => array(0x8FB5), 0x2FA2 => array(0x9091), - 0x2FA3 => array(0x9149), 0x2FA4 => array(0x91C6), 0x2FA5 => array(0x91CC), - 0x2FA6 => array(0x91D1), 0x2FA7 => array(0x9577), 0x2FA8 => array(0x9580), - 0x2FA9 => array(0x961C), 0x2FAA => array(0x96B6), 0x2FAB => array(0x96B9), - 0x2FAC => array(0x96E8), 0x2FAD => array(0x9751), 0x2FAE => array(0x975E), - 0x2FAF => array(0x9762), 0x2FB0 => array(0x9769), 0x2FB1 => array(0x97CB), - 0x2FB2 => array(0x97ED), 0x2FB3 => array(0x97F3), 0x2FB4 => array(0x9801), - 0x2FB5 => array(0x98A8), 0x2FB6 => array(0x98DB), 0x2FB7 => array(0x98DF), - 0x2FB8 => array(0x9996), 0x2FB9 => array(0x9999), 0x2FBA => array(0x99AC), - 0x2FBB => array(0x9AA8), 0x2FBC => array(0x9AD8), 0x2FBD => array(0x9ADF), - 0x2FBE => array(0x9B25), 0x2FBF => array(0x9B2F), 0x2FC0 => array(0x9B32), - 0x2FC1 => array(0x9B3C), 0x2FC2 => array(0x9B5A), 0x2FC3 => array(0x9CE5), - 0x2FC4 => array(0x9E75), 0x2FC5 => array(0x9E7F), 0x2FC6 => array(0x9EA5), - 0x2FC7 => array(0x9EBB), 0x2FC8 => array(0x9EC3), 0x2FC9 => array(0x9ECD), - 0x2FCA => array(0x9ED1), 0x2FCB => array(0x9EF9), 0x2FCC => array(0x9EFD), - 0x2FCD => array(0x9F0E), 0x2FCE => array(0x9F13), 0x2FCF => array(0x9F20), - 0x2FD0 => array(0x9F3B), 0x2FD1 => array(0x9F4A), 0x2FD2 => array(0x9F52), - 0x2FD3 => array(0x9F8D), 0x2FD4 => array(0x9F9C), 0x2FD5 => array(0x9FA0), - 0x3002 => array(0x2E), 0x3036 => array(0x3012), 0x3038 => array(0x5341), - 0x3039 => array(0x5344), 0x303A => array(0x5345), 0x309F => array(0x3088, 0x308A), - 0x30FF => array(0x30B3, 0x30C8), 0x3131 => array(0x1100), 0x3132 => array(0x1101), - 0x3133 => array(0x11AA), 0x3134 => array(0x1102), 0x3135 => array(0x11AC), - 0x3136 => array(0x11AD), 0x3137 => array(0x1103), 0x3138 => array(0x1104), - 0x3139 => array(0x1105), 0x313A => array(0x11B0), 0x313B => array(0x11B1), - 0x313C => array(0x11B2), 0x313D => array(0x11B3), 0x313E => array(0x11B4), - 0x313F => array(0x11B5), 0x3140 => array(0x111A), 0x3141 => array(0x1106), - 0x3142 => array(0x1107), 0x3143 => array(0x1108), 0x3144 => array(0x1121), - 0x3145 => array(0x1109), 0x3146 => array(0x110A), 0x3147 => array(0x110B), - 0x3148 => array(0x110C), 0x3149 => array(0x110D), 0x314A => array(0x110E), - 0x314B => array(0x110F), 0x314C => array(0x1110), 0x314D => array(0x1111), - 0x314E => array(0x1112), 0x314F => array(0x1161), 0x3150 => array(0x1162), - 0x3151 => array(0x1163), 0x3152 => array(0x1164), 0x3153 => array(0x1165), - 0x3154 => array(0x1166), 0x3155 => array(0x1167), 0x3156 => array(0x1168), - 0x3157 => array(0x1169), 0x3158 => array(0x116A), 0x3159 => array(0x116B), - 0x315A => array(0x116C), 0x315B => array(0x116D), 0x315C => array(0x116E), - 0x315D => array(0x116F), 0x315E => array(0x1170), 0x315F => array(0x1171), - 0x3160 => array(0x1172), 0x3161 => array(0x1173), 0x3162 => array(0x1174), - 0x3163 => array(0x1175), 0x3165 => array(0x1114), 0x3166 => array(0x1115), - 0x3167 => array(0x11C7), 0x3168 => array(0x11C8), 0x3169 => array(0x11CC), - 0x316A => array(0x11CE), 0x316B => array(0x11D3), 0x316C => array(0x11D7), - 0x316D => array(0x11D9), 0x316E => array(0x111C), 0x316F => array(0x11DD), - 0x3170 => array(0x11DF), 0x3171 => array(0x111D), 0x3172 => array(0x111E), - 0x3173 => array(0x1120), 0x3174 => array(0x1122), 0x3175 => array(0x1123), - 0x3176 => array(0x1127), 0x3177 => array(0x1129), 0x3178 => array(0x112B), - 0x3179 => array(0x112C), 0x317A => array(0x112D), 0x317B => array(0x112E), - 0x317C => array(0x112F), 0x317D => array(0x1132), 0x317E => array(0x1136), - 0x317F => array(0x1140), 0x3180 => array(0x1147), 0x3181 => array(0x114C), - 0x3182 => array(0x11F1), 0x3183 => array(0x11F2), 0x3184 => array(0x1157), - 0x3185 => array(0x1158), 0x3186 => array(0x1159), 0x3187 => array(0x1184), - 0x3188 => array(0x1185), 0x3189 => array(0x1188), 0x318A => array(0x1191), - 0x318B => array(0x1192), 0x318C => array(0x1194), 0x318D => array(0x119E), - 0x318E => array(0x11A1), 0x3192 => array(0x4E00), 0x3193 => array(0x4E8C), - 0x3194 => array(0x4E09), 0x3195 => array(0x56DB), 0x3196 => array(0x4E0A), - 0x3197 => array(0x4E2D), 0x3198 => array(0x4E0B), 0x3199 => array(0x7532), - 0x319A => array(0x4E59), 0x319B => array(0x4E19), 0x319C => array(0x4E01), - 0x319D => array(0x5929), 0x319E => array(0x5730), 0x319F => array(0x4EBA), - 0x3244 => array(0x554F), 0x3245 => array(0x5E7C), 0x3246 => array(0x6587), - 0x3247 => array(0x7B8F), 0x3250 => array(0x70, 0x74, 0x65), 0x3251 => array(0x32, 0x31), - 0x3252 => array(0x32, 0x32), 0x3253 => array(0x32, 0x33), 0x3254 => array(0x32, 0x34), - 0x3255 => array(0x32, 0x35), 0x3256 => array(0x32, 0x36), 0x3257 => array(0x32, 0x37), - 0x3258 => array(0x32, 0x38), 0x3259 => array(0x32, 0x39), 0x325A => array(0x33, 0x30), - 0x325B => array(0x33, 0x31), 0x325C => array(0x33, 0x32), 0x325D => array(0x33, 0x33), - 0x325E => array(0x33, 0x34), 0x325F => array(0x33, 0x35), 0x3260 => array(0x1100), - 0x3261 => array(0x1102), 0x3262 => array(0x1103), 0x3263 => array(0x1105), - 0x3264 => array(0x1106), 0x3265 => array(0x1107), 0x3266 => array(0x1109), - 0x3267 => array(0x110B), 0x3268 => array(0x110C), 0x3269 => array(0x110E), - 0x326A => array(0x110F), 0x326B => array(0x1110), 0x326C => array(0x1111), - 0x326D => array(0x1112), 0x326E => array(0xAC00), 0x326F => array(0xB098), - 0x3270 => array(0xB2E4), 0x3271 => array(0xB77C), 0x3272 => array(0xB9C8), - 0x3273 => array(0xBC14), 0x3274 => array(0xC0AC), 0x3275 => array(0xC544), - 0x3276 => array(0xC790), 0x3277 => array(0xCC28), 0x3278 => array(0xCE74), - 0x3279 => array(0xD0C0), 0x327A => array(0xD30C), 0x327B => array(0xD558), - 0x327C => array(0xCC38, 0xACE0), 0x327D => array(0xC8FC, 0xC758), 0x327E => array(0xC6B0), - 0x3280 => array(0x4E00), 0x3281 => array(0x4E8C), 0x3282 => array(0x4E09), - 0x3283 => array(0x56DB), 0x3284 => array(0x4E94), 0x3285 => array(0x516D), - 0x3286 => array(0x4E03), 0x3287 => array(0x516B), 0x3288 => array(0x4E5D), - 0x3289 => array(0x5341), 0x328A => array(0x6708), 0x328B => array(0x706B), - 0x328C => array(0x6C34), 0x328D => array(0x6728), 0x328E => array(0x91D1), - 0x328F => array(0x571F), 0x3290 => array(0x65E5), 0x3291 => array(0x682A), - 0x3292 => array(0x6709), 0x3293 => array(0x793E), 0x3294 => array(0x540D), - 0x3295 => array(0x7279), 0x3296 => array(0x8CA1), 0x3297 => array(0x795D), - 0x3298 => array(0x52B4), 0x3299 => array(0x79D8), 0x329A => array(0x7537), - 0x329B => array(0x5973), 0x329C => array(0x9069), 0x329D => array(0x512A), - 0x329E => array(0x5370), 0x329F => array(0x6CE8), 0x32A0 => array(0x9805), - 0x32A1 => array(0x4F11), 0x32A2 => array(0x5199), 0x32A3 => array(0x6B63), - 0x32A4 => array(0x4E0A), 0x32A5 => array(0x4E2D), 0x32A6 => array(0x4E0B), - 0x32A7 => array(0x5DE6), 0x32A8 => array(0x53F3), 0x32A9 => array(0x533B), - 0x32AA => array(0x5B97), 0x32AB => array(0x5B66), 0x32AC => array(0x76E3), - 0x32AD => array(0x4F01), 0x32AE => array(0x8CC7), 0x32AF => array(0x5354), - 0x32B0 => array(0x591C), 0x32B1 => array(0x33, 0x36), 0x32B2 => array(0x33, 0x37), - 0x32B3 => array(0x33, 0x38), 0x32B4 => array(0x33, 0x39), 0x32B5 => array(0x34, 0x30), - 0x32B6 => array(0x34, 0x31), 0x32B7 => array(0x34, 0x32), 0x32B8 => array(0x34, 0x33), - 0x32B9 => array(0x34, 0x34), 0x32BA => array(0x34, 0x35), 0x32BB => array(0x34, 0x36), - 0x32BC => array(0x34, 0x37), 0x32BD => array(0x34, 0x38), 0x32BE => array(0x34, 0x39), - 0x32BF => array(0x35, 0x30), 0x32C0 => array(0x31, 0x6708), 0x32C1 => array(0x32, 0x6708), - 0x32C2 => array(0x33, 0x6708), 0x32C3 => array(0x34, 0x6708), 0x32C4 => array(0x35, 0x6708), - 0x32C5 => array(0x36, 0x6708), 0x32C6 => array(0x37, 0x6708), 0x32C7 => array(0x38, 0x6708), - 0x32C8 => array(0x39, 0x6708), 0x32C9 => array(0x31, 0x30, 0x6708), 0x32CA => array(0x31, 0x31, 0x6708), - 0x32CB => array(0x31, 0x32, 0x6708), 0x32CC => array(0x68, 0x67), 0x32CD => array(0x65, 0x72, 0x67), - 0x32CE => array(0x65, 0x76), 0x32CF => array(0x6C, 0x74, 0x64), 0x32D0 => array(0x30A2), - 0x32D1 => array(0x30A4), 0x32D2 => array(0x30A6), 0x32D3 => array(0x30A8), - 0x32D4 => array(0x30AA), 0x32D5 => array(0x30AB), 0x32D6 => array(0x30AD), - 0x32D7 => array(0x30AF), 0x32D8 => array(0x30B1), 0x32D9 => array(0x30B3), - 0x32DA => array(0x30B5), 0x32DB => array(0x30B7), 0x32DC => array(0x30B9), - 0x32DD => array(0x30BB), 0x32DE => array(0x30BD), 0x32DF => array(0x30BF), - 0x32E0 => array(0x30C1), 0x32E1 => array(0x30C4), 0x32E2 => array(0x30C6), - 0x32E3 => array(0x30C8), 0x32E4 => array(0x30CA), 0x32E5 => array(0x30CB), - 0x32E6 => array(0x30CC), 0x32E7 => array(0x30CD), 0x32E8 => array(0x30CE), - 0x32E9 => array(0x30CF), 0x32EA => array(0x30D2), 0x32EB => array(0x30D5), - 0x32EC => array(0x30D8), 0x32ED => array(0x30DB), 0x32EE => array(0x30DE), - 0x32EF => array(0x30DF), 0x32F0 => array(0x30E0), 0x32F1 => array(0x30E1), - 0x32F2 => array(0x30E2), 0x32F3 => array(0x30E4), 0x32F4 => array(0x30E6), - 0x32F5 => array(0x30E8), 0x32F6 => array(0x30E9), 0x32F7 => array(0x30EA), - 0x32F8 => array(0x30EB), 0x32F9 => array(0x30EC), 0x32FA => array(0x30ED), - 0x32FB => array(0x30EF), 0x32FC => array(0x30F0), 0x32FD => array(0x30F1), - 0x32FE => array(0x30F2), 0x3300 => array(0x30A2, 0x30D1, 0x30FC, 0x30C8), 0x3301 => array(0x30A2, 0x30EB, 0x30D5, 0x30A1), - 0x3302 => array(0x30A2, 0x30F3, 0x30DA, 0x30A2), 0x3303 => array(0x30A2, 0x30FC, 0x30EB), 0x3304 => array(0x30A4, 0x30CB, 0x30F3, 0x30B0), - 0x3305 => array(0x30A4, 0x30F3, 0x30C1), 0x3306 => array(0x30A6, 0x30A9, 0x30F3), 0x3307 => array(0x30A8, 0x30B9, 0x30AF, 0x30FC, 0x30C9), - 0x3308 => array(0x30A8, 0x30FC, 0x30AB, 0x30FC), 0x3309 => array(0x30AA, 0x30F3, 0x30B9), 0x330A => array(0x30AA, 0x30FC, 0x30E0), - 0x330B => array(0x30AB, 0x30A4, 0x30EA), 0x330C => array(0x30AB, 0x30E9, 0x30C3, 0x30C8), 0x330D => array(0x30AB, 0x30ED, 0x30EA, 0x30FC), - 0x330E => array(0x30AC, 0x30ED, 0x30F3), 0x330F => array(0x30AC, 0x30F3, 0x30DE), 0x3310 => array(0x30AE, 0x30AC), - 0x3311 => array(0x30AE, 0x30CB, 0x30FC), 0x3312 => array(0x30AD, 0x30E5, 0x30EA, 0x30FC), 0x3313 => array(0x30AE, 0x30EB, 0x30C0, 0x30FC), - 0x3314 => array(0x30AD, 0x30ED), 0x3315 => array(0x30AD, 0x30ED, 0x30B0, 0x30E9, 0x30E0), 0x3316 => array(0x30AD, 0x30ED, 0x30E1, 0x30FC, 0x30C8, 0x30EB), - 0x3317 => array(0x30AD, 0x30ED, 0x30EF, 0x30C3, 0x30C8), 0x3318 => array(0x30B0, 0x30E9, 0x30E0), 0x3319 => array(0x30B0, 0x30E9, 0x30E0, 0x30C8, 0x30F3), - 0x331A => array(0x30AF, 0x30EB, 0x30BC, 0x30A4, 0x30ED), 0x331B => array(0x30AF, 0x30ED, 0x30FC, 0x30CD), 0x331C => array(0x30B1, 0x30FC, 0x30B9), - 0x331D => array(0x30B3, 0x30EB, 0x30CA), 0x331E => array(0x30B3, 0x30FC, 0x30DD), 0x331F => array(0x30B5, 0x30A4, 0x30AF, 0x30EB), - 0x3320 => array(0x30B5, 0x30F3, 0x30C1, 0x30FC, 0x30E0), 0x3321 => array(0x30B7, 0x30EA, 0x30F3, 0x30B0), 0x3322 => array(0x30BB, 0x30F3, 0x30C1), - 0x3323 => array(0x30BB, 0x30F3, 0x30C8), 0x3324 => array(0x30C0, 0x30FC, 0x30B9), 0x3325 => array(0x30C7, 0x30B7), - 0x3326 => array(0x30C9, 0x30EB), 0x3327 => array(0x30C8, 0x30F3), 0x3328 => array(0x30CA, 0x30CE), - 0x3329 => array(0x30CE, 0x30C3, 0x30C8), 0x332A => array(0x30CF, 0x30A4, 0x30C4), 0x332B => array(0x30D1, 0x30FC, 0x30BB, 0x30F3, 0x30C8), - 0x332C => array(0x30D1, 0x30FC, 0x30C4), 0x332D => array(0x30D0, 0x30FC, 0x30EC, 0x30EB), 0x332E => array(0x30D4, 0x30A2, 0x30B9, 0x30C8, 0x30EB), - 0x332F => array(0x30D4, 0x30AF, 0x30EB), 0x3330 => array(0x30D4, 0x30B3), 0x3331 => array(0x30D3, 0x30EB), - 0x3332 => array(0x30D5, 0x30A1, 0x30E9, 0x30C3, 0x30C9), 0x3333 => array(0x30D5, 0x30A3, 0x30FC, 0x30C8), 0x3334 => array(0x30D6, 0x30C3, 0x30B7, 0x30A7, 0x30EB), - 0x3335 => array(0x30D5, 0x30E9, 0x30F3), 0x3336 => array(0x30D8, 0x30AF, 0x30BF, 0x30FC, 0x30EB), 0x3337 => array(0x30DA, 0x30BD), - 0x3338 => array(0x30DA, 0x30CB, 0x30D2), 0x3339 => array(0x30D8, 0x30EB, 0x30C4), 0x333A => array(0x30DA, 0x30F3, 0x30B9), - 0x333B => array(0x30DA, 0x30FC, 0x30B8), 0x333C => array(0x30D9, 0x30FC, 0x30BF), 0x333D => array(0x30DD, 0x30A4, 0x30F3, 0x30C8), - 0x333E => array(0x30DC, 0x30EB, 0x30C8), 0x333F => array(0x30DB, 0x30F3), 0x3340 => array(0x30DD, 0x30F3, 0x30C9), - 0x3341 => array(0x30DB, 0x30FC, 0x30EB), 0x3342 => array(0x30DB, 0x30FC, 0x30F3), 0x3343 => array(0x30DE, 0x30A4, 0x30AF, 0x30ED), - 0x3344 => array(0x30DE, 0x30A4, 0x30EB), 0x3345 => array(0x30DE, 0x30C3, 0x30CF), 0x3346 => array(0x30DE, 0x30EB, 0x30AF), - 0x3347 => array(0x30DE, 0x30F3, 0x30B7, 0x30E7, 0x30F3), 0x3348 => array(0x30DF, 0x30AF, 0x30ED, 0x30F3), 0x3349 => array(0x30DF, 0x30EA), - 0x334A => array(0x30DF, 0x30EA, 0x30D0, 0x30FC, 0x30EB), 0x334B => array(0x30E1, 0x30AC), 0x334C => array(0x30E1, 0x30AC, 0x30C8, 0x30F3), - 0x334D => array(0x30E1, 0x30FC, 0x30C8, 0x30EB), 0x334E => array(0x30E4, 0x30FC, 0x30C9), 0x334F => array(0x30E4, 0x30FC, 0x30EB), - 0x3350 => array(0x30E6, 0x30A2, 0x30F3), 0x3351 => array(0x30EA, 0x30C3, 0x30C8, 0x30EB), 0x3352 => array(0x30EA, 0x30E9), - 0x3353 => array(0x30EB, 0x30D4, 0x30FC), 0x3354 => array(0x30EB, 0x30FC, 0x30D6, 0x30EB), 0x3355 => array(0x30EC, 0x30E0), - 0x3356 => array(0x30EC, 0x30F3, 0x30C8, 0x30B2, 0x30F3), 0x3357 => array(0x30EF, 0x30C3, 0x30C8), 0x3358 => array(0x30, 0x70B9), - 0x3359 => array(0x31, 0x70B9), 0x335A => array(0x32, 0x70B9), 0x335B => array(0x33, 0x70B9), - 0x335C => array(0x34, 0x70B9), 0x335D => array(0x35, 0x70B9), 0x335E => array(0x36, 0x70B9), - 0x335F => array(0x37, 0x70B9), 0x3360 => array(0x38, 0x70B9), 0x3361 => array(0x39, 0x70B9), - 0x3362 => array(0x31, 0x30, 0x70B9), 0x3363 => array(0x31, 0x31, 0x70B9), 0x3364 => array(0x31, 0x32, 0x70B9), - 0x3365 => array(0x31, 0x33, 0x70B9), 0x3366 => array(0x31, 0x34, 0x70B9), 0x3367 => array(0x31, 0x35, 0x70B9), - 0x3368 => array(0x31, 0x36, 0x70B9), 0x3369 => array(0x31, 0x37, 0x70B9), 0x336A => array(0x31, 0x38, 0x70B9), - 0x336B => array(0x31, 0x39, 0x70B9), 0x336C => array(0x32, 0x30, 0x70B9), 0x336D => array(0x32, 0x31, 0x70B9), - 0x336E => array(0x32, 0x32, 0x70B9), 0x336F => array(0x32, 0x33, 0x70B9), 0x3370 => array(0x32, 0x34, 0x70B9), - 0x3371 => array(0x68, 0x70, 0x61), 0x3372 => array(0x64, 0x61), 0x3373 => array(0x61, 0x75), - 0x3374 => array(0x62, 0x61, 0x72), 0x3375 => array(0x6F, 0x76), 0x3376 => array(0x70, 0x63), - 0x3377 => array(0x64, 0x6D), 0x3378 => array(0x64, 0x6D, 0x32), 0x3379 => array(0x64, 0x6D, 0x33), - 0x337A => array(0x69, 0x75), 0x337B => array(0x5E73, 0x6210), 0x337C => array(0x662D, 0x548C), - 0x337D => array(0x5927, 0x6B63), 0x337E => array(0x660E, 0x6CBB), 0x337F => array(0x682A, 0x5F0F, 0x4F1A, 0x793E), - 0x3380 => array(0x70, 0x61), 0x3381 => array(0x6E, 0x61), 0x3382 => array(0x3BC, 0x61), - 0x3383 => array(0x6D, 0x61), 0x3384 => array(0x6B, 0x61), 0x3385 => array(0x6B, 0x62), - 0x3386 => array(0x6D, 0x62), 0x3387 => array(0x67, 0x62), 0x3388 => array(0x63, 0x61, 0x6C), - 0x3389 => array(0x6B, 0x63, 0x61, 0x6C), 0x338A => array(0x70, 0x66), 0x338B => array(0x6E, 0x66), - 0x338C => array(0x3BC, 0x66), 0x338D => array(0x3BC, 0x67), 0x338E => array(0x6D, 0x67), - 0x338F => array(0x6B, 0x67), 0x3390 => array(0x68, 0x7A), 0x3391 => array(0x6B, 0x68, 0x7A), - 0x3392 => array(0x6D, 0x68, 0x7A), 0x3393 => array(0x67, 0x68, 0x7A), 0x3394 => array(0x74, 0x68, 0x7A), - 0x3395 => array(0x3BC, 0x6C), 0x3396 => array(0x6D, 0x6C), 0x3397 => array(0x64, 0x6C), - 0x3398 => array(0x6B, 0x6C), 0x3399 => array(0x66, 0x6D), 0x339A => array(0x6E, 0x6D), - 0x339B => array(0x3BC, 0x6D), 0x339C => array(0x6D, 0x6D), 0x339D => array(0x63, 0x6D), - 0x339E => array(0x6B, 0x6D), 0x339F => array(0x6D, 0x6D, 0x32), 0x33A0 => array(0x63, 0x6D, 0x32), - 0x33A1 => array(0x6D, 0x32), 0x33A2 => array(0x6B, 0x6D, 0x32), 0x33A3 => array(0x6D, 0x6D, 0x33), - 0x33A4 => array(0x63, 0x6D, 0x33), 0x33A5 => array(0x6D, 0x33), 0x33A6 => array(0x6B, 0x6D, 0x33), - 0x33A7 => array(0x6D, 0x2215, 0x73), 0x33A8 => array(0x6D, 0x2215, 0x73, 0x32), 0x33A9 => array(0x70, 0x61), - 0x33AA => array(0x6B, 0x70, 0x61), 0x33AB => array(0x6D, 0x70, 0x61), 0x33AC => array(0x67, 0x70, 0x61), - 0x33AD => array(0x72, 0x61, 0x64), 0x33AE => array(0x72, 0x61, 0x64, 0x2215, 0x73), 0x33AF => array(0x72, 0x61, 0x64, 0x2215, 0x73, 0x32), - 0x33B0 => array(0x70, 0x73), 0x33B1 => array(0x6E, 0x73), 0x33B2 => array(0x3BC, 0x73), - 0x33B3 => array(0x6D, 0x73), 0x33B4 => array(0x70, 0x76), 0x33B5 => array(0x6E, 0x76), - 0x33B6 => array(0x3BC, 0x76), 0x33B7 => array(0x6D, 0x76), 0x33B8 => array(0x6B, 0x76), - 0x33B9 => array(0x6D, 0x76), 0x33BA => array(0x70, 0x77), 0x33BB => array(0x6E, 0x77), - 0x33BC => array(0x3BC, 0x77), 0x33BD => array(0x6D, 0x77), 0x33BE => array(0x6B, 0x77), - 0x33BF => array(0x6D, 0x77), 0x33C0 => array(0x6B, 0x3C9), 0x33C1 => array(0x6D, 0x3C9), - 0x33C3 => array(0x62, 0x71), 0x33C4 => array(0x63, 0x63), 0x33C5 => array(0x63, 0x64), - 0x33C6 => array(0x63, 0x2215, 0x6B, 0x67), 0x33C8 => array(0x64, 0x62), 0x33C9 => array(0x67, 0x79), - 0x33CA => array(0x68, 0x61), 0x33CB => array(0x68, 0x70), 0x33CC => array(0x69, 0x6E), - 0x33CD => array(0x6B, 0x6B), 0x33CE => array(0x6B, 0x6D), 0x33CF => array(0x6B, 0x74), - 0x33D0 => array(0x6C, 0x6D), 0x33D1 => array(0x6C, 0x6E), 0x33D2 => array(0x6C, 0x6F, 0x67), - 0x33D3 => array(0x6C, 0x78), 0x33D4 => array(0x6D, 0x62), 0x33D5 => array(0x6D, 0x69, 0x6C), - 0x33D6 => array(0x6D, 0x6F, 0x6C), 0x33D7 => array(0x70, 0x68), 0x33D9 => array(0x70, 0x70, 0x6D), - 0x33DA => array(0x70, 0x72), 0x33DB => array(0x73, 0x72), 0x33DC => array(0x73, 0x76), - 0x33DD => array(0x77, 0x62), 0x33DE => array(0x76, 0x2215, 0x6D), 0x33DF => array(0x61, 0x2215, 0x6D), - 0x33E0 => array(0x31, 0x65E5), 0x33E1 => array(0x32, 0x65E5), 0x33E2 => array(0x33, 0x65E5), - 0x33E3 => array(0x34, 0x65E5), 0x33E4 => array(0x35, 0x65E5), 0x33E5 => array(0x36, 0x65E5), - 0x33E6 => array(0x37, 0x65E5), 0x33E7 => array(0x38, 0x65E5), 0x33E8 => array(0x39, 0x65E5), - 0x33E9 => array(0x31, 0x30, 0x65E5), 0x33EA => array(0x31, 0x31, 0x65E5), 0x33EB => array(0x31, 0x32, 0x65E5), - 0x33EC => array(0x31, 0x33, 0x65E5), 0x33ED => array(0x31, 0x34, 0x65E5), 0x33EE => array(0x31, 0x35, 0x65E5), - 0x33EF => array(0x31, 0x36, 0x65E5), 0x33F0 => array(0x31, 0x37, 0x65E5), 0x33F1 => array(0x31, 0x38, 0x65E5), - 0x33F2 => array(0x31, 0x39, 0x65E5), 0x33F3 => array(0x32, 0x30, 0x65E5), 0x33F4 => array(0x32, 0x31, 0x65E5), - 0x33F5 => array(0x32, 0x32, 0x65E5), 0x33F6 => array(0x32, 0x33, 0x65E5), 0x33F7 => array(0x32, 0x34, 0x65E5), - 0x33F8 => array(0x32, 0x35, 0x65E5), 0x33F9 => array(0x32, 0x36, 0x65E5), 0x33FA => array(0x32, 0x37, 0x65E5), - 0x33FB => array(0x32, 0x38, 0x65E5), 0x33FC => array(0x32, 0x39, 0x65E5), 0x33FD => array(0x33, 0x30, 0x65E5), - 0x33FE => array(0x33, 0x31, 0x65E5), 0x33FF => array(0x67, 0x61, 0x6C), 0xA640 => array(0xA641), - 0xA642 => array(0xA643), 0xA644 => array(0xA645), 0xA646 => array(0xA647), - 0xA648 => array(0xA649), 0xA64A => array(0xA64B), 0xA64C => array(0xA64D), - 0xA64E => array(0xA64F), 0xA650 => array(0xA651), 0xA652 => array(0xA653), - 0xA654 => array(0xA655), 0xA656 => array(0xA657), 0xA658 => array(0xA659), - 0xA65A => array(0xA65B), 0xA65C => array(0xA65D), 0xA65E => array(0xA65F), - 0xA660 => array(0xA661), 0xA662 => array(0xA663), 0xA664 => array(0xA665), - 0xA666 => array(0xA667), 0xA668 => array(0xA669), 0xA66A => array(0xA66B), - 0xA66C => array(0xA66D), 0xA680 => array(0xA681), 0xA682 => array(0xA683), - 0xA684 => array(0xA685), 0xA686 => array(0xA687), 0xA688 => array(0xA689), - 0xA68A => array(0xA68B), 0xA68C => array(0xA68D), 0xA68E => array(0xA68F), - 0xA690 => array(0xA691), 0xA692 => array(0xA693), 0xA694 => array(0xA695), - 0xA696 => array(0xA697), 0xA698 => array(0xA699), 0xA69A => array(0xA69B), - 0xA69C => array(0x44A), 0xA69D => array(0x44C), 0xA722 => array(0xA723), - 0xA724 => array(0xA725), 0xA726 => array(0xA727), 0xA728 => array(0xA729), - 0xA72A => array(0xA72B), 0xA72C => array(0xA72D), 0xA72E => array(0xA72F), - 0xA732 => array(0xA733), 0xA734 => array(0xA735), 0xA736 => array(0xA737), - 0xA738 => array(0xA739), 0xA73A => array(0xA73B), 0xA73C => array(0xA73D), - 0xA73E => array(0xA73F), 0xA740 => array(0xA741), 0xA742 => array(0xA743), - 0xA744 => array(0xA745), 0xA746 => array(0xA747), 0xA748 => array(0xA749), - 0xA74A => array(0xA74B), 0xA74C => array(0xA74D), 0xA74E => array(0xA74F), - 0xA750 => array(0xA751), 0xA752 => array(0xA753), 0xA754 => array(0xA755), - 0xA756 => array(0xA757), 0xA758 => array(0xA759), 0xA75A => array(0xA75B), - 0xA75C => array(0xA75D), 0xA75E => array(0xA75F), 0xA760 => array(0xA761), - 0xA762 => array(0xA763), 0xA764 => array(0xA765), 0xA766 => array(0xA767), - 0xA768 => array(0xA769), 0xA76A => array(0xA76B), 0xA76C => array(0xA76D), - 0xA76E => array(0xA76F), 0xA770 => array(0xA76F), 0xA779 => array(0xA77A), - 0xA77B => array(0xA77C), 0xA77D => array(0x1D79), 0xA77E => array(0xA77F), - 0xA780 => array(0xA781), 0xA782 => array(0xA783), 0xA784 => array(0xA785), - 0xA786 => array(0xA787), 0xA78B => array(0xA78C), 0xA78D => array(0x265), - 0xA790 => array(0xA791), 0xA792 => array(0xA793), 0xA796 => array(0xA797), - 0xA798 => array(0xA799), 0xA79A => array(0xA79B), 0xA79C => array(0xA79D), - 0xA79E => array(0xA79F), 0xA7A0 => array(0xA7A1), 0xA7A2 => array(0xA7A3), - 0xA7A4 => array(0xA7A5), 0xA7A6 => array(0xA7A7), 0xA7A8 => array(0xA7A9), - 0xA7AA => array(0x266), 0xA7AB => array(0x25C), 0xA7AC => array(0x261), - 0xA7AD => array(0x26C), 0xA7B0 => array(0x29E), 0xA7B1 => array(0x287), - 0xA7F8 => array(0x127), 0xA7F9 => array(0x153), 0xAB5C => array(0xA727), - 0xAB5D => array(0xAB37), 0xAB5E => array(0x26B), 0xAB5F => array(0xAB52), - 0xF900 => array(0x8C48), 0xF901 => array(0x66F4), 0xF902 => array(0x8ECA), - 0xF903 => array(0x8CC8), 0xF904 => array(0x6ED1), 0xF905 => array(0x4E32), - 0xF906 => array(0x53E5), 0xF907 => array(0x9F9C), 0xF908 => array(0x9F9C), - 0xF909 => array(0x5951), 0xF90A => array(0x91D1), 0xF90B => array(0x5587), - 0xF90C => array(0x5948), 0xF90D => array(0x61F6), 0xF90E => array(0x7669), - 0xF90F => array(0x7F85), 0xF910 => array(0x863F), 0xF911 => array(0x87BA), - 0xF912 => array(0x88F8), 0xF913 => array(0x908F), 0xF914 => array(0x6A02), - 0xF915 => array(0x6D1B), 0xF916 => array(0x70D9), 0xF917 => array(0x73DE), - 0xF918 => array(0x843D), 0xF919 => array(0x916A), 0xF91A => array(0x99F1), - 0xF91B => array(0x4E82), 0xF91C => array(0x5375), 0xF91D => array(0x6B04), - 0xF91E => array(0x721B), 0xF91F => array(0x862D), 0xF920 => array(0x9E1E), - 0xF921 => array(0x5D50), 0xF922 => array(0x6FEB), 0xF923 => array(0x85CD), - 0xF924 => array(0x8964), 0xF925 => array(0x62C9), 0xF926 => array(0x81D8), - 0xF927 => array(0x881F), 0xF928 => array(0x5ECA), 0xF929 => array(0x6717), - 0xF92A => array(0x6D6A), 0xF92B => array(0x72FC), 0xF92C => array(0x90CE), - 0xF92D => array(0x4F86), 0xF92E => array(0x51B7), 0xF92F => array(0x52DE), - 0xF930 => array(0x64C4), 0xF931 => array(0x6AD3), 0xF932 => array(0x7210), - 0xF933 => array(0x76E7), 0xF934 => array(0x8001), 0xF935 => array(0x8606), - 0xF936 => array(0x865C), 0xF937 => array(0x8DEF), 0xF938 => array(0x9732), - 0xF939 => array(0x9B6F), 0xF93A => array(0x9DFA), 0xF93B => array(0x788C), - 0xF93C => array(0x797F), 0xF93D => array(0x7DA0), 0xF93E => array(0x83C9), - 0xF93F => array(0x9304), 0xF940 => array(0x9E7F), 0xF941 => array(0x8AD6), - 0xF942 => array(0x58DF), 0xF943 => array(0x5F04), 0xF944 => array(0x7C60), - 0xF945 => array(0x807E), 0xF946 => array(0x7262), 0xF947 => array(0x78CA), - 0xF948 => array(0x8CC2), 0xF949 => array(0x96F7), 0xF94A => array(0x58D8), - 0xF94B => array(0x5C62), 0xF94C => array(0x6A13), 0xF94D => array(0x6DDA), - 0xF94E => array(0x6F0F), 0xF94F => array(0x7D2F), 0xF950 => array(0x7E37), - 0xF951 => array(0x964B), 0xF952 => array(0x52D2), 0xF953 => array(0x808B), - 0xF954 => array(0x51DC), 0xF955 => array(0x51CC), 0xF956 => array(0x7A1C), - 0xF957 => array(0x7DBE), 0xF958 => array(0x83F1), 0xF959 => array(0x9675), - 0xF95A => array(0x8B80), 0xF95B => array(0x62CF), 0xF95C => array(0x6A02), - 0xF95D => array(0x8AFE), 0xF95E => array(0x4E39), 0xF95F => array(0x5BE7), - 0xF960 => array(0x6012), 0xF961 => array(0x7387), 0xF962 => array(0x7570), - 0xF963 => array(0x5317), 0xF964 => array(0x78FB), 0xF965 => array(0x4FBF), - 0xF966 => array(0x5FA9), 0xF967 => array(0x4E0D), 0xF968 => array(0x6CCC), - 0xF969 => array(0x6578), 0xF96A => array(0x7D22), 0xF96B => array(0x53C3), - 0xF96C => array(0x585E), 0xF96D => array(0x7701), 0xF96E => array(0x8449), - 0xF96F => array(0x8AAA), 0xF970 => array(0x6BBA), 0xF971 => array(0x8FB0), - 0xF972 => array(0x6C88), 0xF973 => array(0x62FE), 0xF974 => array(0x82E5), - 0xF975 => array(0x63A0), 0xF976 => array(0x7565), 0xF977 => array(0x4EAE), - 0xF978 => array(0x5169), 0xF979 => array(0x51C9), 0xF97A => array(0x6881), - 0xF97B => array(0x7CE7), 0xF97C => array(0x826F), 0xF97D => array(0x8AD2), - 0xF97E => array(0x91CF), 0xF97F => array(0x52F5), 0xF980 => array(0x5442), - 0xF981 => array(0x5973), 0xF982 => array(0x5EEC), 0xF983 => array(0x65C5), - 0xF984 => array(0x6FFE), 0xF985 => array(0x792A), 0xF986 => array(0x95AD), - 0xF987 => array(0x9A6A), 0xF988 => array(0x9E97), 0xF989 => array(0x9ECE), - 0xF98A => array(0x529B), 0xF98B => array(0x66C6), 0xF98C => array(0x6B77), - 0xF98D => array(0x8F62), 0xF98E => array(0x5E74), 0xF98F => array(0x6190), - 0xF990 => array(0x6200), 0xF991 => array(0x649A), 0xF992 => array(0x6F23), - 0xF993 => array(0x7149), 0xF994 => array(0x7489), 0xF995 => array(0x79CA), - 0xF996 => array(0x7DF4), 0xF997 => array(0x806F), 0xF998 => array(0x8F26), - 0xF999 => array(0x84EE), 0xF99A => array(0x9023), 0xF99B => array(0x934A), - 0xF99C => array(0x5217), 0xF99D => array(0x52A3), 0xF99E => array(0x54BD), - 0xF99F => array(0x70C8), 0xF9A0 => array(0x88C2), 0xF9A1 => array(0x8AAA), - 0xF9A2 => array(0x5EC9), 0xF9A3 => array(0x5FF5), 0xF9A4 => array(0x637B), - 0xF9A5 => array(0x6BAE), 0xF9A6 => array(0x7C3E), 0xF9A7 => array(0x7375), - 0xF9A8 => array(0x4EE4), 0xF9A9 => array(0x56F9), 0xF9AA => array(0x5BE7), - 0xF9AB => array(0x5DBA), 0xF9AC => array(0x601C), 0xF9AD => array(0x73B2), - 0xF9AE => array(0x7469), 0xF9AF => array(0x7F9A), 0xF9B0 => array(0x8046), - 0xF9B1 => array(0x9234), 0xF9B2 => array(0x96F6), 0xF9B3 => array(0x9748), - 0xF9B4 => array(0x9818), 0xF9B5 => array(0x4F8B), 0xF9B6 => array(0x79AE), - 0xF9B7 => array(0x91B4), 0xF9B8 => array(0x96B8), 0xF9B9 => array(0x60E1), - 0xF9BA => array(0x4E86), 0xF9BB => array(0x50DA), 0xF9BC => array(0x5BEE), - 0xF9BD => array(0x5C3F), 0xF9BE => array(0x6599), 0xF9BF => array(0x6A02), - 0xF9C0 => array(0x71CE), 0xF9C1 => array(0x7642), 0xF9C2 => array(0x84FC), - 0xF9C3 => array(0x907C), 0xF9C4 => array(0x9F8D), 0xF9C5 => array(0x6688), - 0xF9C6 => array(0x962E), 0xF9C7 => array(0x5289), 0xF9C8 => array(0x677B), - 0xF9C9 => array(0x67F3), 0xF9CA => array(0x6D41), 0xF9CB => array(0x6E9C), - 0xF9CC => array(0x7409), 0xF9CD => array(0x7559), 0xF9CE => array(0x786B), - 0xF9CF => array(0x7D10), 0xF9D0 => array(0x985E), 0xF9D1 => array(0x516D), - 0xF9D2 => array(0x622E), 0xF9D3 => array(0x9678), 0xF9D4 => array(0x502B), - 0xF9D5 => array(0x5D19), 0xF9D6 => array(0x6DEA), 0xF9D7 => array(0x8F2A), - 0xF9D8 => array(0x5F8B), 0xF9D9 => array(0x6144), 0xF9DA => array(0x6817), - 0xF9DB => array(0x7387), 0xF9DC => array(0x9686), 0xF9DD => array(0x5229), - 0xF9DE => array(0x540F), 0xF9DF => array(0x5C65), 0xF9E0 => array(0x6613), - 0xF9E1 => array(0x674E), 0xF9E2 => array(0x68A8), 0xF9E3 => array(0x6CE5), - 0xF9E4 => array(0x7406), 0xF9E5 => array(0x75E2), 0xF9E6 => array(0x7F79), - 0xF9E7 => array(0x88CF), 0xF9E8 => array(0x88E1), 0xF9E9 => array(0x91CC), - 0xF9EA => array(0x96E2), 0xF9EB => array(0x533F), 0xF9EC => array(0x6EBA), - 0xF9ED => array(0x541D), 0xF9EE => array(0x71D0), 0xF9EF => array(0x7498), - 0xF9F0 => array(0x85FA), 0xF9F1 => array(0x96A3), 0xF9F2 => array(0x9C57), - 0xF9F3 => array(0x9E9F), 0xF9F4 => array(0x6797), 0xF9F5 => array(0x6DCB), - 0xF9F6 => array(0x81E8), 0xF9F7 => array(0x7ACB), 0xF9F8 => array(0x7B20), - 0xF9F9 => array(0x7C92), 0xF9FA => array(0x72C0), 0xF9FB => array(0x7099), - 0xF9FC => array(0x8B58), 0xF9FD => array(0x4EC0), 0xF9FE => array(0x8336), - 0xF9FF => array(0x523A), 0xFA00 => array(0x5207), 0xFA01 => array(0x5EA6), - 0xFA02 => array(0x62D3), 0xFA03 => array(0x7CD6), 0xFA04 => array(0x5B85), - 0xFA05 => array(0x6D1E), 0xFA06 => array(0x66B4), 0xFA07 => array(0x8F3B), - 0xFA08 => array(0x884C), 0xFA09 => array(0x964D), 0xFA0A => array(0x898B), - 0xFA0B => array(0x5ED3), 0xFA0C => array(0x5140), 0xFA0D => array(0x55C0), - 0xFA10 => array(0x585A), 0xFA12 => array(0x6674), 0xFA15 => array(0x51DE), - 0xFA16 => array(0x732A), 0xFA17 => array(0x76CA), 0xFA18 => array(0x793C), - 0xFA19 => array(0x795E), 0xFA1A => array(0x7965), 0xFA1B => array(0x798F), - 0xFA1C => array(0x9756), 0xFA1D => array(0x7CBE), 0xFA1E => array(0x7FBD), - 0xFA20 => array(0x8612), 0xFA22 => array(0x8AF8), 0xFA25 => array(0x9038), - 0xFA26 => array(0x90FD), 0xFA2A => array(0x98EF), 0xFA2B => array(0x98FC), - 0xFA2C => array(0x9928), 0xFA2D => array(0x9DB4), 0xFA2E => array(0x90DE), - 0xFA2F => array(0x96B7), 0xFA30 => array(0x4FAE), 0xFA31 => array(0x50E7), - 0xFA32 => array(0x514D), 0xFA33 => array(0x52C9), 0xFA34 => array(0x52E4), - 0xFA35 => array(0x5351), 0xFA36 => array(0x559D), 0xFA37 => array(0x5606), - 0xFA38 => array(0x5668), 0xFA39 => array(0x5840), 0xFA3A => array(0x58A8), - 0xFA3B => array(0x5C64), 0xFA3C => array(0x5C6E), 0xFA3D => array(0x6094), - 0xFA3E => array(0x6168), 0xFA3F => array(0x618E), 0xFA40 => array(0x61F2), - 0xFA41 => array(0x654F), 0xFA42 => array(0x65E2), 0xFA43 => array(0x6691), - 0xFA44 => array(0x6885), 0xFA45 => array(0x6D77), 0xFA46 => array(0x6E1A), - 0xFA47 => array(0x6F22), 0xFA48 => array(0x716E), 0xFA49 => array(0x722B), - 0xFA4A => array(0x7422), 0xFA4B => array(0x7891), 0xFA4C => array(0x793E), - 0xFA4D => array(0x7949), 0xFA4E => array(0x7948), 0xFA4F => array(0x7950), - 0xFA50 => array(0x7956), 0xFA51 => array(0x795D), 0xFA52 => array(0x798D), - 0xFA53 => array(0x798E), 0xFA54 => array(0x7A40), 0xFA55 => array(0x7A81), - 0xFA56 => array(0x7BC0), 0xFA57 => array(0x7DF4), 0xFA58 => array(0x7E09), - 0xFA59 => array(0x7E41), 0xFA5A => array(0x7F72), 0xFA5B => array(0x8005), - 0xFA5C => array(0x81ED), 0xFA5D => array(0x8279), 0xFA5E => array(0x8279), - 0xFA5F => array(0x8457), 0xFA60 => array(0x8910), 0xFA61 => array(0x8996), - 0xFA62 => array(0x8B01), 0xFA63 => array(0x8B39), 0xFA64 => array(0x8CD3), - 0xFA65 => array(0x8D08), 0xFA66 => array(0x8FB6), 0xFA67 => array(0x9038), - 0xFA68 => array(0x96E3), 0xFA69 => array(0x97FF), 0xFA6A => array(0x983B), - 0xFA6B => array(0x6075), 0xFA6C => array(0x242EE), 0xFA6D => array(0x8218), - 0xFA70 => array(0x4E26), 0xFA71 => array(0x51B5), 0xFA72 => array(0x5168), - 0xFA73 => array(0x4F80), 0xFA74 => array(0x5145), 0xFA75 => array(0x5180), - 0xFA76 => array(0x52C7), 0xFA77 => array(0x52FA), 0xFA78 => array(0x559D), - 0xFA79 => array(0x5555), 0xFA7A => array(0x5599), 0xFA7B => array(0x55E2), - 0xFA7C => array(0x585A), 0xFA7D => array(0x58B3), 0xFA7E => array(0x5944), - 0xFA7F => array(0x5954), 0xFA80 => array(0x5A62), 0xFA81 => array(0x5B28), - 0xFA82 => array(0x5ED2), 0xFA83 => array(0x5ED9), 0xFA84 => array(0x5F69), - 0xFA85 => array(0x5FAD), 0xFA86 => array(0x60D8), 0xFA87 => array(0x614E), - 0xFA88 => array(0x6108), 0xFA89 => array(0x618E), 0xFA8A => array(0x6160), - 0xFA8B => array(0x61F2), 0xFA8C => array(0x6234), 0xFA8D => array(0x63C4), - 0xFA8E => array(0x641C), 0xFA8F => array(0x6452), 0xFA90 => array(0x6556), - 0xFA91 => array(0x6674), 0xFA92 => array(0x6717), 0xFA93 => array(0x671B), - 0xFA94 => array(0x6756), 0xFA95 => array(0x6B79), 0xFA96 => array(0x6BBA), - 0xFA97 => array(0x6D41), 0xFA98 => array(0x6EDB), 0xFA99 => array(0x6ECB), - 0xFA9A => array(0x6F22), 0xFA9B => array(0x701E), 0xFA9C => array(0x716E), - 0xFA9D => array(0x77A7), 0xFA9E => array(0x7235), 0xFA9F => array(0x72AF), - 0xFAA0 => array(0x732A), 0xFAA1 => array(0x7471), 0xFAA2 => array(0x7506), - 0xFAA3 => array(0x753B), 0xFAA4 => array(0x761D), 0xFAA5 => array(0x761F), - 0xFAA6 => array(0x76CA), 0xFAA7 => array(0x76DB), 0xFAA8 => array(0x76F4), - 0xFAA9 => array(0x774A), 0xFAAA => array(0x7740), 0xFAAB => array(0x78CC), - 0xFAAC => array(0x7AB1), 0xFAAD => array(0x7BC0), 0xFAAE => array(0x7C7B), - 0xFAAF => array(0x7D5B), 0xFAB0 => array(0x7DF4), 0xFAB1 => array(0x7F3E), - 0xFAB2 => array(0x8005), 0xFAB3 => array(0x8352), 0xFAB4 => array(0x83EF), - 0xFAB5 => array(0x8779), 0xFAB6 => array(0x8941), 0xFAB7 => array(0x8986), - 0xFAB8 => array(0x8996), 0xFAB9 => array(0x8ABF), 0xFABA => array(0x8AF8), - 0xFABB => array(0x8ACB), 0xFABC => array(0x8B01), 0xFABD => array(0x8AFE), - 0xFABE => array(0x8AED), 0xFABF => array(0x8B39), 0xFAC0 => array(0x8B8A), - 0xFAC1 => array(0x8D08), 0xFAC2 => array(0x8F38), 0xFAC3 => array(0x9072), - 0xFAC4 => array(0x9199), 0xFAC5 => array(0x9276), 0xFAC6 => array(0x967C), - 0xFAC7 => array(0x96E3), 0xFAC8 => array(0x9756), 0xFAC9 => array(0x97DB), - 0xFACA => array(0x97FF), 0xFACB => array(0x980B), 0xFACC => array(0x983B), - 0xFACD => array(0x9B12), 0xFACE => array(0x9F9C), 0xFACF => array(0x2284A), - 0xFAD0 => array(0x22844), 0xFAD1 => array(0x233D5), 0xFAD2 => array(0x3B9D), - 0xFAD3 => array(0x4018), 0xFAD4 => array(0x4039), 0xFAD5 => array(0x25249), - 0xFAD6 => array(0x25CD0), 0xFAD7 => array(0x27ED3), 0xFAD8 => array(0x9F43), - 0xFAD9 => array(0x9F8E), 0xFB00 => array(0x66, 0x66), 0xFB01 => array(0x66, 0x69), - 0xFB02 => array(0x66, 0x6C), 0xFB03 => array(0x66, 0x66, 0x69), 0xFB04 => array(0x66, 0x66, 0x6C), - 0xFB05 => array(0x73, 0x74), 0xFB06 => array(0x73, 0x74), 0xFB13 => array(0x574, 0x576), - 0xFB14 => array(0x574, 0x565), 0xFB15 => array(0x574, 0x56B), 0xFB16 => array(0x57E, 0x576), - 0xFB17 => array(0x574, 0x56D), 0xFB1D => array(0x5D9, 0x5B4), 0xFB1F => array(0x5F2, 0x5B7), - 0xFB20 => array(0x5E2), 0xFB21 => array(0x5D0), 0xFB22 => array(0x5D3), - 0xFB23 => array(0x5D4), 0xFB24 => array(0x5DB), 0xFB25 => array(0x5DC), - 0xFB26 => array(0x5DD), 0xFB27 => array(0x5E8), 0xFB28 => array(0x5EA), - 0xFB2A => array(0x5E9, 0x5C1), 0xFB2B => array(0x5E9, 0x5C2), 0xFB2C => array(0x5E9, 0x5BC, 0x5C1), - 0xFB2D => array(0x5E9, 0x5BC, 0x5C2), 0xFB2E => array(0x5D0, 0x5B7), 0xFB2F => array(0x5D0, 0x5B8), - 0xFB30 => array(0x5D0, 0x5BC), 0xFB31 => array(0x5D1, 0x5BC), 0xFB32 => array(0x5D2, 0x5BC), - 0xFB33 => array(0x5D3, 0x5BC), 0xFB34 => array(0x5D4, 0x5BC), 0xFB35 => array(0x5D5, 0x5BC), - 0xFB36 => array(0x5D6, 0x5BC), 0xFB38 => array(0x5D8, 0x5BC), 0xFB39 => array(0x5D9, 0x5BC), - 0xFB3A => array(0x5DA, 0x5BC), 0xFB3B => array(0x5DB, 0x5BC), 0xFB3C => array(0x5DC, 0x5BC), - 0xFB3E => array(0x5DE, 0x5BC), 0xFB40 => array(0x5E0, 0x5BC), 0xFB41 => array(0x5E1, 0x5BC), - 0xFB43 => array(0x5E3, 0x5BC), 0xFB44 => array(0x5E4, 0x5BC), 0xFB46 => array(0x5E6, 0x5BC), - 0xFB47 => array(0x5E7, 0x5BC), 0xFB48 => array(0x5E8, 0x5BC), 0xFB49 => array(0x5E9, 0x5BC), - 0xFB4A => array(0x5EA, 0x5BC), 0xFB4B => array(0x5D5, 0x5B9), 0xFB4C => array(0x5D1, 0x5BF), - 0xFB4D => array(0x5DB, 0x5BF), 0xFB4E => array(0x5E4, 0x5BF), 0xFB4F => array(0x5D0, 0x5DC), - 0xFB50 => array(0x671), 0xFB51 => array(0x671), 0xFB52 => array(0x67B), - 0xFB53 => array(0x67B), 0xFB54 => array(0x67B), 0xFB55 => array(0x67B), - 0xFB56 => array(0x67E), 0xFB57 => array(0x67E), 0xFB58 => array(0x67E), - 0xFB59 => array(0x67E), 0xFB5A => array(0x680), 0xFB5B => array(0x680), - 0xFB5C => array(0x680), 0xFB5D => array(0x680), 0xFB5E => array(0x67A), - 0xFB5F => array(0x67A), 0xFB60 => array(0x67A), 0xFB61 => array(0x67A), - 0xFB62 => array(0x67F), 0xFB63 => array(0x67F), 0xFB64 => array(0x67F), - 0xFB65 => array(0x67F), 0xFB66 => array(0x679), 0xFB67 => array(0x679), - 0xFB68 => array(0x679), 0xFB69 => array(0x679), 0xFB6A => array(0x6A4), - 0xFB6B => array(0x6A4), 0xFB6C => array(0x6A4), 0xFB6D => array(0x6A4), - 0xFB6E => array(0x6A6), 0xFB6F => array(0x6A6), 0xFB70 => array(0x6A6), - 0xFB71 => array(0x6A6), 0xFB72 => array(0x684), 0xFB73 => array(0x684), - 0xFB74 => array(0x684), 0xFB75 => array(0x684), 0xFB76 => array(0x683), - 0xFB77 => array(0x683), 0xFB78 => array(0x683), 0xFB79 => array(0x683), - 0xFB7A => array(0x686), 0xFB7B => array(0x686), 0xFB7C => array(0x686), - 0xFB7D => array(0x686), 0xFB7E => array(0x687), 0xFB7F => array(0x687), - 0xFB80 => array(0x687), 0xFB81 => array(0x687), 0xFB82 => array(0x68D), - 0xFB83 => array(0x68D), 0xFB84 => array(0x68C), 0xFB85 => array(0x68C), - 0xFB86 => array(0x68E), 0xFB87 => array(0x68E), 0xFB88 => array(0x688), - 0xFB89 => array(0x688), 0xFB8A => array(0x698), 0xFB8B => array(0x698), - 0xFB8C => array(0x691), 0xFB8D => array(0x691), 0xFB8E => array(0x6A9), - 0xFB8F => array(0x6A9), 0xFB90 => array(0x6A9), 0xFB91 => array(0x6A9), - 0xFB92 => array(0x6AF), 0xFB93 => array(0x6AF), 0xFB94 => array(0x6AF), - 0xFB95 => array(0x6AF), 0xFB96 => array(0x6B3), 0xFB97 => array(0x6B3), - 0xFB98 => array(0x6B3), 0xFB99 => array(0x6B3), 0xFB9A => array(0x6B1), - 0xFB9B => array(0x6B1), 0xFB9C => array(0x6B1), 0xFB9D => array(0x6B1), - 0xFB9E => array(0x6BA), 0xFB9F => array(0x6BA), 0xFBA0 => array(0x6BB), - 0xFBA1 => array(0x6BB), 0xFBA2 => array(0x6BB), 0xFBA3 => array(0x6BB), - 0xFBA4 => array(0x6C0), 0xFBA5 => array(0x6C0), 0xFBA6 => array(0x6C1), - 0xFBA7 => array(0x6C1), 0xFBA8 => array(0x6C1), 0xFBA9 => array(0x6C1), - 0xFBAA => array(0x6BE), 0xFBAB => array(0x6BE), 0xFBAC => array(0x6BE), - 0xFBAD => array(0x6BE), 0xFBAE => array(0x6D2), 0xFBAF => array(0x6D2), - 0xFBB0 => array(0x6D3), 0xFBB1 => array(0x6D3), 0xFBD3 => array(0x6AD), - 0xFBD4 => array(0x6AD), 0xFBD5 => array(0x6AD), 0xFBD6 => array(0x6AD), - 0xFBD7 => array(0x6C7), 0xFBD8 => array(0x6C7), 0xFBD9 => array(0x6C6), - 0xFBDA => array(0x6C6), 0xFBDB => array(0x6C8), 0xFBDC => array(0x6C8), - 0xFBDD => array(0x6C7, 0x674), 0xFBDE => array(0x6CB), 0xFBDF => array(0x6CB), - 0xFBE0 => array(0x6C5), 0xFBE1 => array(0x6C5), 0xFBE2 => array(0x6C9), - 0xFBE3 => array(0x6C9), 0xFBE4 => array(0x6D0), 0xFBE5 => array(0x6D0), - 0xFBE6 => array(0x6D0), 0xFBE7 => array(0x6D0), 0xFBE8 => array(0x649), - 0xFBE9 => array(0x649), 0xFBEA => array(0x626, 0x627), 0xFBEB => array(0x626, 0x627), - 0xFBEC => array(0x626, 0x6D5), 0xFBED => array(0x626, 0x6D5), 0xFBEE => array(0x626, 0x648), - 0xFBEF => array(0x626, 0x648), 0xFBF0 => array(0x626, 0x6C7), 0xFBF1 => array(0x626, 0x6C7), - 0xFBF2 => array(0x626, 0x6C6), 0xFBF3 => array(0x626, 0x6C6), 0xFBF4 => array(0x626, 0x6C8), - 0xFBF5 => array(0x626, 0x6C8), 0xFBF6 => array(0x626, 0x6D0), 0xFBF7 => array(0x626, 0x6D0), - 0xFBF8 => array(0x626, 0x6D0), 0xFBF9 => array(0x626, 0x649), 0xFBFA => array(0x626, 0x649), - 0xFBFB => array(0x626, 0x649), 0xFBFC => array(0x6CC), 0xFBFD => array(0x6CC), - 0xFBFE => array(0x6CC), 0xFBFF => array(0x6CC), 0xFC00 => array(0x626, 0x62C), - 0xFC01 => array(0x626, 0x62D), 0xFC02 => array(0x626, 0x645), 0xFC03 => array(0x626, 0x649), - 0xFC04 => array(0x626, 0x64A), 0xFC05 => array(0x628, 0x62C), 0xFC06 => array(0x628, 0x62D), - 0xFC07 => array(0x628, 0x62E), 0xFC08 => array(0x628, 0x645), 0xFC09 => array(0x628, 0x649), - 0xFC0A => array(0x628, 0x64A), 0xFC0B => array(0x62A, 0x62C), 0xFC0C => array(0x62A, 0x62D), - 0xFC0D => array(0x62A, 0x62E), 0xFC0E => array(0x62A, 0x645), 0xFC0F => array(0x62A, 0x649), - 0xFC10 => array(0x62A, 0x64A), 0xFC11 => array(0x62B, 0x62C), 0xFC12 => array(0x62B, 0x645), - 0xFC13 => array(0x62B, 0x649), 0xFC14 => array(0x62B, 0x64A), 0xFC15 => array(0x62C, 0x62D), - 0xFC16 => array(0x62C, 0x645), 0xFC17 => array(0x62D, 0x62C), 0xFC18 => array(0x62D, 0x645), - 0xFC19 => array(0x62E, 0x62C), 0xFC1A => array(0x62E, 0x62D), 0xFC1B => array(0x62E, 0x645), - 0xFC1C => array(0x633, 0x62C), 0xFC1D => array(0x633, 0x62D), 0xFC1E => array(0x633, 0x62E), - 0xFC1F => array(0x633, 0x645), 0xFC20 => array(0x635, 0x62D), 0xFC21 => array(0x635, 0x645), - 0xFC22 => array(0x636, 0x62C), 0xFC23 => array(0x636, 0x62D), 0xFC24 => array(0x636, 0x62E), - 0xFC25 => array(0x636, 0x645), 0xFC26 => array(0x637, 0x62D), 0xFC27 => array(0x637, 0x645), - 0xFC28 => array(0x638, 0x645), 0xFC29 => array(0x639, 0x62C), 0xFC2A => array(0x639, 0x645), - 0xFC2B => array(0x63A, 0x62C), 0xFC2C => array(0x63A, 0x645), 0xFC2D => array(0x641, 0x62C), - 0xFC2E => array(0x641, 0x62D), 0xFC2F => array(0x641, 0x62E), 0xFC30 => array(0x641, 0x645), - 0xFC31 => array(0x641, 0x649), 0xFC32 => array(0x641, 0x64A), 0xFC33 => array(0x642, 0x62D), - 0xFC34 => array(0x642, 0x645), 0xFC35 => array(0x642, 0x649), 0xFC36 => array(0x642, 0x64A), - 0xFC37 => array(0x643, 0x627), 0xFC38 => array(0x643, 0x62C), 0xFC39 => array(0x643, 0x62D), - 0xFC3A => array(0x643, 0x62E), 0xFC3B => array(0x643, 0x644), 0xFC3C => array(0x643, 0x645), - 0xFC3D => array(0x643, 0x649), 0xFC3E => array(0x643, 0x64A), 0xFC3F => array(0x644, 0x62C), - 0xFC40 => array(0x644, 0x62D), 0xFC41 => array(0x644, 0x62E), 0xFC42 => array(0x644, 0x645), - 0xFC43 => array(0x644, 0x649), 0xFC44 => array(0x644, 0x64A), 0xFC45 => array(0x645, 0x62C), - 0xFC46 => array(0x645, 0x62D), 0xFC47 => array(0x645, 0x62E), 0xFC48 => array(0x645, 0x645), - 0xFC49 => array(0x645, 0x649), 0xFC4A => array(0x645, 0x64A), 0xFC4B => array(0x646, 0x62C), - 0xFC4C => array(0x646, 0x62D), 0xFC4D => array(0x646, 0x62E), 0xFC4E => array(0x646, 0x645), - 0xFC4F => array(0x646, 0x649), 0xFC50 => array(0x646, 0x64A), 0xFC51 => array(0x647, 0x62C), - 0xFC52 => array(0x647, 0x645), 0xFC53 => array(0x647, 0x649), 0xFC54 => array(0x647, 0x64A), - 0xFC55 => array(0x64A, 0x62C), 0xFC56 => array(0x64A, 0x62D), 0xFC57 => array(0x64A, 0x62E), - 0xFC58 => array(0x64A, 0x645), 0xFC59 => array(0x64A, 0x649), 0xFC5A => array(0x64A, 0x64A), - 0xFC5B => array(0x630, 0x670), 0xFC5C => array(0x631, 0x670), 0xFC5D => array(0x649, 0x670), - 0xFC64 => array(0x626, 0x631), 0xFC65 => array(0x626, 0x632), 0xFC66 => array(0x626, 0x645), - 0xFC67 => array(0x626, 0x646), 0xFC68 => array(0x626, 0x649), 0xFC69 => array(0x626, 0x64A), - 0xFC6A => array(0x628, 0x631), 0xFC6B => array(0x628, 0x632), 0xFC6C => array(0x628, 0x645), - 0xFC6D => array(0x628, 0x646), 0xFC6E => array(0x628, 0x649), 0xFC6F => array(0x628, 0x64A), - 0xFC70 => array(0x62A, 0x631), 0xFC71 => array(0x62A, 0x632), 0xFC72 => array(0x62A, 0x645), - 0xFC73 => array(0x62A, 0x646), 0xFC74 => array(0x62A, 0x649), 0xFC75 => array(0x62A, 0x64A), - 0xFC76 => array(0x62B, 0x631), 0xFC77 => array(0x62B, 0x632), 0xFC78 => array(0x62B, 0x645), - 0xFC79 => array(0x62B, 0x646), 0xFC7A => array(0x62B, 0x649), 0xFC7B => array(0x62B, 0x64A), - 0xFC7C => array(0x641, 0x649), 0xFC7D => array(0x641, 0x64A), 0xFC7E => array(0x642, 0x649), - 0xFC7F => array(0x642, 0x64A), 0xFC80 => array(0x643, 0x627), 0xFC81 => array(0x643, 0x644), - 0xFC82 => array(0x643, 0x645), 0xFC83 => array(0x643, 0x649), 0xFC84 => array(0x643, 0x64A), - 0xFC85 => array(0x644, 0x645), 0xFC86 => array(0x644, 0x649), 0xFC87 => array(0x644, 0x64A), - 0xFC88 => array(0x645, 0x627), 0xFC89 => array(0x645, 0x645), 0xFC8A => array(0x646, 0x631), - 0xFC8B => array(0x646, 0x632), 0xFC8C => array(0x646, 0x645), 0xFC8D => array(0x646, 0x646), - 0xFC8E => array(0x646, 0x649), 0xFC8F => array(0x646, 0x64A), 0xFC90 => array(0x649, 0x670), - 0xFC91 => array(0x64A, 0x631), 0xFC92 => array(0x64A, 0x632), 0xFC93 => array(0x64A, 0x645), - 0xFC94 => array(0x64A, 0x646), 0xFC95 => array(0x64A, 0x649), 0xFC96 => array(0x64A, 0x64A), - 0xFC97 => array(0x626, 0x62C), 0xFC98 => array(0x626, 0x62D), 0xFC99 => array(0x626, 0x62E), - 0xFC9A => array(0x626, 0x645), 0xFC9B => array(0x626, 0x647), 0xFC9C => array(0x628, 0x62C), - 0xFC9D => array(0x628, 0x62D), 0xFC9E => array(0x628, 0x62E), 0xFC9F => array(0x628, 0x645), - 0xFCA0 => array(0x628, 0x647), 0xFCA1 => array(0x62A, 0x62C), 0xFCA2 => array(0x62A, 0x62D), - 0xFCA3 => array(0x62A, 0x62E), 0xFCA4 => array(0x62A, 0x645), 0xFCA5 => array(0x62A, 0x647), - 0xFCA6 => array(0x62B, 0x645), 0xFCA7 => array(0x62C, 0x62D), 0xFCA8 => array(0x62C, 0x645), - 0xFCA9 => array(0x62D, 0x62C), 0xFCAA => array(0x62D, 0x645), 0xFCAB => array(0x62E, 0x62C), - 0xFCAC => array(0x62E, 0x645), 0xFCAD => array(0x633, 0x62C), 0xFCAE => array(0x633, 0x62D), - 0xFCAF => array(0x633, 0x62E), 0xFCB0 => array(0x633, 0x645), 0xFCB1 => array(0x635, 0x62D), - 0xFCB2 => array(0x635, 0x62E), 0xFCB3 => array(0x635, 0x645), 0xFCB4 => array(0x636, 0x62C), - 0xFCB5 => array(0x636, 0x62D), 0xFCB6 => array(0x636, 0x62E), 0xFCB7 => array(0x636, 0x645), - 0xFCB8 => array(0x637, 0x62D), 0xFCB9 => array(0x638, 0x645), 0xFCBA => array(0x639, 0x62C), - 0xFCBB => array(0x639, 0x645), 0xFCBC => array(0x63A, 0x62C), 0xFCBD => array(0x63A, 0x645), - 0xFCBE => array(0x641, 0x62C), 0xFCBF => array(0x641, 0x62D), 0xFCC0 => array(0x641, 0x62E), - 0xFCC1 => array(0x641, 0x645), 0xFCC2 => array(0x642, 0x62D), 0xFCC3 => array(0x642, 0x645), - 0xFCC4 => array(0x643, 0x62C), 0xFCC5 => array(0x643, 0x62D), 0xFCC6 => array(0x643, 0x62E), - 0xFCC7 => array(0x643, 0x644), 0xFCC8 => array(0x643, 0x645), 0xFCC9 => array(0x644, 0x62C), - 0xFCCA => array(0x644, 0x62D), 0xFCCB => array(0x644, 0x62E), 0xFCCC => array(0x644, 0x645), - 0xFCCD => array(0x644, 0x647), 0xFCCE => array(0x645, 0x62C), 0xFCCF => array(0x645, 0x62D), - 0xFCD0 => array(0x645, 0x62E), 0xFCD1 => array(0x645, 0x645), 0xFCD2 => array(0x646, 0x62C), - 0xFCD3 => array(0x646, 0x62D), 0xFCD4 => array(0x646, 0x62E), 0xFCD5 => array(0x646, 0x645), - 0xFCD6 => array(0x646, 0x647), 0xFCD7 => array(0x647, 0x62C), 0xFCD8 => array(0x647, 0x645), - 0xFCD9 => array(0x647, 0x670), 0xFCDA => array(0x64A, 0x62C), 0xFCDB => array(0x64A, 0x62D), - 0xFCDC => array(0x64A, 0x62E), 0xFCDD => array(0x64A, 0x645), 0xFCDE => array(0x64A, 0x647), - 0xFCDF => array(0x626, 0x645), 0xFCE0 => array(0x626, 0x647), 0xFCE1 => array(0x628, 0x645), - 0xFCE2 => array(0x628, 0x647), 0xFCE3 => array(0x62A, 0x645), 0xFCE4 => array(0x62A, 0x647), - 0xFCE5 => array(0x62B, 0x645), 0xFCE6 => array(0x62B, 0x647), 0xFCE7 => array(0x633, 0x645), - 0xFCE8 => array(0x633, 0x647), 0xFCE9 => array(0x634, 0x645), 0xFCEA => array(0x634, 0x647), - 0xFCEB => array(0x643, 0x644), 0xFCEC => array(0x643, 0x645), 0xFCED => array(0x644, 0x645), - 0xFCEE => array(0x646, 0x645), 0xFCEF => array(0x646, 0x647), 0xFCF0 => array(0x64A, 0x645), - 0xFCF1 => array(0x64A, 0x647), 0xFCF2 => array(0x640, 0x64E, 0x651), 0xFCF3 => array(0x640, 0x64F, 0x651), - 0xFCF4 => array(0x640, 0x650, 0x651), 0xFCF5 => array(0x637, 0x649), 0xFCF6 => array(0x637, 0x64A), - 0xFCF7 => array(0x639, 0x649), 0xFCF8 => array(0x639, 0x64A), 0xFCF9 => array(0x63A, 0x649), - 0xFCFA => array(0x63A, 0x64A), 0xFCFB => array(0x633, 0x649), 0xFCFC => array(0x633, 0x64A), - 0xFCFD => array(0x634, 0x649), 0xFCFE => array(0x634, 0x64A), 0xFCFF => array(0x62D, 0x649), - 0xFD00 => array(0x62D, 0x64A), 0xFD01 => array(0x62C, 0x649), 0xFD02 => array(0x62C, 0x64A), - 0xFD03 => array(0x62E, 0x649), 0xFD04 => array(0x62E, 0x64A), 0xFD05 => array(0x635, 0x649), - 0xFD06 => array(0x635, 0x64A), 0xFD07 => array(0x636, 0x649), 0xFD08 => array(0x636, 0x64A), - 0xFD09 => array(0x634, 0x62C), 0xFD0A => array(0x634, 0x62D), 0xFD0B => array(0x634, 0x62E), - 0xFD0C => array(0x634, 0x645), 0xFD0D => array(0x634, 0x631), 0xFD0E => array(0x633, 0x631), - 0xFD0F => array(0x635, 0x631), 0xFD10 => array(0x636, 0x631), 0xFD11 => array(0x637, 0x649), - 0xFD12 => array(0x637, 0x64A), 0xFD13 => array(0x639, 0x649), 0xFD14 => array(0x639, 0x64A), - 0xFD15 => array(0x63A, 0x649), 0xFD16 => array(0x63A, 0x64A), 0xFD17 => array(0x633, 0x649), - 0xFD18 => array(0x633, 0x64A), 0xFD19 => array(0x634, 0x649), 0xFD1A => array(0x634, 0x64A), - 0xFD1B => array(0x62D, 0x649), 0xFD1C => array(0x62D, 0x64A), 0xFD1D => array(0x62C, 0x649), - 0xFD1E => array(0x62C, 0x64A), 0xFD1F => array(0x62E, 0x649), 0xFD20 => array(0x62E, 0x64A), - 0xFD21 => array(0x635, 0x649), 0xFD22 => array(0x635, 0x64A), 0xFD23 => array(0x636, 0x649), - 0xFD24 => array(0x636, 0x64A), 0xFD25 => array(0x634, 0x62C), 0xFD26 => array(0x634, 0x62D), - 0xFD27 => array(0x634, 0x62E), 0xFD28 => array(0x634, 0x645), 0xFD29 => array(0x634, 0x631), - 0xFD2A => array(0x633, 0x631), 0xFD2B => array(0x635, 0x631), 0xFD2C => array(0x636, 0x631), - 0xFD2D => array(0x634, 0x62C), 0xFD2E => array(0x634, 0x62D), 0xFD2F => array(0x634, 0x62E), - 0xFD30 => array(0x634, 0x645), 0xFD31 => array(0x633, 0x647), 0xFD32 => array(0x634, 0x647), - 0xFD33 => array(0x637, 0x645), 0xFD34 => array(0x633, 0x62C), 0xFD35 => array(0x633, 0x62D), - 0xFD36 => array(0x633, 0x62E), 0xFD37 => array(0x634, 0x62C), 0xFD38 => array(0x634, 0x62D), - 0xFD39 => array(0x634, 0x62E), 0xFD3A => array(0x637, 0x645), 0xFD3B => array(0x638, 0x645), - 0xFD3C => array(0x627, 0x64B), 0xFD3D => array(0x627, 0x64B), 0xFD50 => array(0x62A, 0x62C, 0x645), - 0xFD51 => array(0x62A, 0x62D, 0x62C), 0xFD52 => array(0x62A, 0x62D, 0x62C), 0xFD53 => array(0x62A, 0x62D, 0x645), - 0xFD54 => array(0x62A, 0x62E, 0x645), 0xFD55 => array(0x62A, 0x645, 0x62C), 0xFD56 => array(0x62A, 0x645, 0x62D), - 0xFD57 => array(0x62A, 0x645, 0x62E), 0xFD58 => array(0x62C, 0x645, 0x62D), 0xFD59 => array(0x62C, 0x645, 0x62D), - 0xFD5A => array(0x62D, 0x645, 0x64A), 0xFD5B => array(0x62D, 0x645, 0x649), 0xFD5C => array(0x633, 0x62D, 0x62C), - 0xFD5D => array(0x633, 0x62C, 0x62D), 0xFD5E => array(0x633, 0x62C, 0x649), 0xFD5F => array(0x633, 0x645, 0x62D), - 0xFD60 => array(0x633, 0x645, 0x62D), 0xFD61 => array(0x633, 0x645, 0x62C), 0xFD62 => array(0x633, 0x645, 0x645), - 0xFD63 => array(0x633, 0x645, 0x645), 0xFD64 => array(0x635, 0x62D, 0x62D), 0xFD65 => array(0x635, 0x62D, 0x62D), - 0xFD66 => array(0x635, 0x645, 0x645), 0xFD67 => array(0x634, 0x62D, 0x645), 0xFD68 => array(0x634, 0x62D, 0x645), - 0xFD69 => array(0x634, 0x62C, 0x64A), 0xFD6A => array(0x634, 0x645, 0x62E), 0xFD6B => array(0x634, 0x645, 0x62E), - 0xFD6C => array(0x634, 0x645, 0x645), 0xFD6D => array(0x634, 0x645, 0x645), 0xFD6E => array(0x636, 0x62D, 0x649), - 0xFD6F => array(0x636, 0x62E, 0x645), 0xFD70 => array(0x636, 0x62E, 0x645), 0xFD71 => array(0x637, 0x645, 0x62D), - 0xFD72 => array(0x637, 0x645, 0x62D), 0xFD73 => array(0x637, 0x645, 0x645), 0xFD74 => array(0x637, 0x645, 0x64A), - 0xFD75 => array(0x639, 0x62C, 0x645), 0xFD76 => array(0x639, 0x645, 0x645), 0xFD77 => array(0x639, 0x645, 0x645), - 0xFD78 => array(0x639, 0x645, 0x649), 0xFD79 => array(0x63A, 0x645, 0x645), 0xFD7A => array(0x63A, 0x645, 0x64A), - 0xFD7B => array(0x63A, 0x645, 0x649), 0xFD7C => array(0x641, 0x62E, 0x645), 0xFD7D => array(0x641, 0x62E, 0x645), - 0xFD7E => array(0x642, 0x645, 0x62D), 0xFD7F => array(0x642, 0x645, 0x645), 0xFD80 => array(0x644, 0x62D, 0x645), - 0xFD81 => array(0x644, 0x62D, 0x64A), 0xFD82 => array(0x644, 0x62D, 0x649), 0xFD83 => array(0x644, 0x62C, 0x62C), - 0xFD84 => array(0x644, 0x62C, 0x62C), 0xFD85 => array(0x644, 0x62E, 0x645), 0xFD86 => array(0x644, 0x62E, 0x645), - 0xFD87 => array(0x644, 0x645, 0x62D), 0xFD88 => array(0x644, 0x645, 0x62D), 0xFD89 => array(0x645, 0x62D, 0x62C), - 0xFD8A => array(0x645, 0x62D, 0x645), 0xFD8B => array(0x645, 0x62D, 0x64A), 0xFD8C => array(0x645, 0x62C, 0x62D), - 0xFD8D => array(0x645, 0x62C, 0x645), 0xFD8E => array(0x645, 0x62E, 0x62C), 0xFD8F => array(0x645, 0x62E, 0x645), - 0xFD92 => array(0x645, 0x62C, 0x62E), 0xFD93 => array(0x647, 0x645, 0x62C), 0xFD94 => array(0x647, 0x645, 0x645), - 0xFD95 => array(0x646, 0x62D, 0x645), 0xFD96 => array(0x646, 0x62D, 0x649), 0xFD97 => array(0x646, 0x62C, 0x645), - 0xFD98 => array(0x646, 0x62C, 0x645), 0xFD99 => array(0x646, 0x62C, 0x649), 0xFD9A => array(0x646, 0x645, 0x64A), - 0xFD9B => array(0x646, 0x645, 0x649), 0xFD9C => array(0x64A, 0x645, 0x645), 0xFD9D => array(0x64A, 0x645, 0x645), - 0xFD9E => array(0x628, 0x62E, 0x64A), 0xFD9F => array(0x62A, 0x62C, 0x64A), 0xFDA0 => array(0x62A, 0x62C, 0x649), - 0xFDA1 => array(0x62A, 0x62E, 0x64A), 0xFDA2 => array(0x62A, 0x62E, 0x649), 0xFDA3 => array(0x62A, 0x645, 0x64A), - 0xFDA4 => array(0x62A, 0x645, 0x649), 0xFDA5 => array(0x62C, 0x645, 0x64A), 0xFDA6 => array(0x62C, 0x62D, 0x649), - 0xFDA7 => array(0x62C, 0x645, 0x649), 0xFDA8 => array(0x633, 0x62E, 0x649), 0xFDA9 => array(0x635, 0x62D, 0x64A), - 0xFDAA => array(0x634, 0x62D, 0x64A), 0xFDAB => array(0x636, 0x62D, 0x64A), 0xFDAC => array(0x644, 0x62C, 0x64A), - 0xFDAD => array(0x644, 0x645, 0x64A), 0xFDAE => array(0x64A, 0x62D, 0x64A), 0xFDAF => array(0x64A, 0x62C, 0x64A), - 0xFDB0 => array(0x64A, 0x645, 0x64A), 0xFDB1 => array(0x645, 0x645, 0x64A), 0xFDB2 => array(0x642, 0x645, 0x64A), - 0xFDB3 => array(0x646, 0x62D, 0x64A), 0xFDB4 => array(0x642, 0x645, 0x62D), 0xFDB5 => array(0x644, 0x62D, 0x645), - 0xFDB6 => array(0x639, 0x645, 0x64A), 0xFDB7 => array(0x643, 0x645, 0x64A), 0xFDB8 => array(0x646, 0x62C, 0x62D), - 0xFDB9 => array(0x645, 0x62E, 0x64A), 0xFDBA => array(0x644, 0x62C, 0x645), 0xFDBB => array(0x643, 0x645, 0x645), - 0xFDBC => array(0x644, 0x62C, 0x645), 0xFDBD => array(0x646, 0x62C, 0x62D), 0xFDBE => array(0x62C, 0x62D, 0x64A), - 0xFDBF => array(0x62D, 0x62C, 0x64A), 0xFDC0 => array(0x645, 0x62C, 0x64A), 0xFDC1 => array(0x641, 0x645, 0x64A), - 0xFDC2 => array(0x628, 0x62D, 0x64A), 0xFDC3 => array(0x643, 0x645, 0x645), 0xFDC4 => array(0x639, 0x62C, 0x645), - 0xFDC5 => array(0x635, 0x645, 0x645), 0xFDC6 => array(0x633, 0x62E, 0x64A), 0xFDC7 => array(0x646, 0x62C, 0x64A), - 0xFDF0 => array(0x635, 0x644, 0x6D2), 0xFDF1 => array(0x642, 0x644, 0x6D2), 0xFDF2 => array(0x627, 0x644, 0x644, 0x647), - 0xFDF3 => array(0x627, 0x643, 0x628, 0x631), 0xFDF4 => array(0x645, 0x62D, 0x645, 0x62F), 0xFDF5 => array(0x635, 0x644, 0x639, 0x645), - 0xFDF6 => array(0x631, 0x633, 0x648, 0x644), 0xFDF7 => array(0x639, 0x644, 0x64A, 0x647), 0xFDF8 => array(0x648, 0x633, 0x644, 0x645), - 0xFDF9 => array(0x635, 0x644, 0x649), 0xFDFC => array(0x631, 0x6CC, 0x627, 0x644), 0xFE11 => array(0x3001), - 0xFE17 => array(0x3016), 0xFE18 => array(0x3017), 0xFE31 => array(0x2014), - 0xFE32 => array(0x2013), 0xFE39 => array(0x3014), 0xFE3A => array(0x3015), - 0xFE3B => array(0x3010), 0xFE3C => array(0x3011), 0xFE3D => array(0x300A), - 0xFE3E => array(0x300B), 0xFE3F => array(0x3008), 0xFE40 => array(0x3009), - 0xFE41 => array(0x300C), 0xFE42 => array(0x300D), 0xFE43 => array(0x300E), - 0xFE44 => array(0x300F), 0xFE51 => array(0x3001), 0xFE58 => array(0x2014), - 0xFE5D => array(0x3014), 0xFE5E => array(0x3015), 0xFE63 => array(0x2D), - 0xFE71 => array(0x640, 0x64B), 0xFE77 => array(0x640, 0x64E), 0xFE79 => array(0x640, 0x64F), - 0xFE7B => array(0x640, 0x650), 0xFE7D => array(0x640, 0x651), 0xFE7F => array(0x640, 0x652), - 0xFE80 => array(0x621), 0xFE81 => array(0x622), 0xFE82 => array(0x622), - 0xFE83 => array(0x623), 0xFE84 => array(0x623), 0xFE85 => array(0x624), - 0xFE86 => array(0x624), 0xFE87 => array(0x625), 0xFE88 => array(0x625), - 0xFE89 => array(0x626), 0xFE8A => array(0x626), 0xFE8B => array(0x626), - 0xFE8C => array(0x626), 0xFE8D => array(0x627), 0xFE8E => array(0x627), - 0xFE8F => array(0x628), 0xFE90 => array(0x628), 0xFE91 => array(0x628), - 0xFE92 => array(0x628), 0xFE93 => array(0x629), 0xFE94 => array(0x629), - 0xFE95 => array(0x62A), 0xFE96 => array(0x62A), 0xFE97 => array(0x62A), - 0xFE98 => array(0x62A), 0xFE99 => array(0x62B), 0xFE9A => array(0x62B), - 0xFE9B => array(0x62B), 0xFE9C => array(0x62B), 0xFE9D => array(0x62C), - 0xFE9E => array(0x62C), 0xFE9F => array(0x62C), 0xFEA0 => array(0x62C), - 0xFEA1 => array(0x62D), 0xFEA2 => array(0x62D), 0xFEA3 => array(0x62D), - 0xFEA4 => array(0x62D), 0xFEA5 => array(0x62E), 0xFEA6 => array(0x62E), - 0xFEA7 => array(0x62E), 0xFEA8 => array(0x62E), 0xFEA9 => array(0x62F), - 0xFEAA => array(0x62F), 0xFEAB => array(0x630), 0xFEAC => array(0x630), - 0xFEAD => array(0x631), 0xFEAE => array(0x631), 0xFEAF => array(0x632), - 0xFEB0 => array(0x632), 0xFEB1 => array(0x633), 0xFEB2 => array(0x633), - 0xFEB3 => array(0x633), 0xFEB4 => array(0x633), 0xFEB5 => array(0x634), - 0xFEB6 => array(0x634), 0xFEB7 => array(0x634), 0xFEB8 => array(0x634), - 0xFEB9 => array(0x635), 0xFEBA => array(0x635), 0xFEBB => array(0x635), - 0xFEBC => array(0x635), 0xFEBD => array(0x636), 0xFEBE => array(0x636), - 0xFEBF => array(0x636), 0xFEC0 => array(0x636), 0xFEC1 => array(0x637), - 0xFEC2 => array(0x637), 0xFEC3 => array(0x637), 0xFEC4 => array(0x637), - 0xFEC5 => array(0x638), 0xFEC6 => array(0x638), 0xFEC7 => array(0x638), - 0xFEC8 => array(0x638), 0xFEC9 => array(0x639), 0xFECA => array(0x639), - 0xFECB => array(0x639), 0xFECC => array(0x639), 0xFECD => array(0x63A), - 0xFECE => array(0x63A), 0xFECF => array(0x63A), 0xFED0 => array(0x63A), - 0xFED1 => array(0x641), 0xFED2 => array(0x641), 0xFED3 => array(0x641), - 0xFED4 => array(0x641), 0xFED5 => array(0x642), 0xFED6 => array(0x642), - 0xFED7 => array(0x642), 0xFED8 => array(0x642), 0xFED9 => array(0x643), - 0xFEDA => array(0x643), 0xFEDB => array(0x643), 0xFEDC => array(0x643), - 0xFEDD => array(0x644), 0xFEDE => array(0x644), 0xFEDF => array(0x644), - 0xFEE0 => array(0x644), 0xFEE1 => array(0x645), 0xFEE2 => array(0x645), - 0xFEE3 => array(0x645), 0xFEE4 => array(0x645), 0xFEE5 => array(0x646), - 0xFEE6 => array(0x646), 0xFEE7 => array(0x646), 0xFEE8 => array(0x646), - 0xFEE9 => array(0x647), 0xFEEA => array(0x647), 0xFEEB => array(0x647), - 0xFEEC => array(0x647), 0xFEED => array(0x648), 0xFEEE => array(0x648), - 0xFEEF => array(0x649), 0xFEF0 => array(0x649), 0xFEF1 => array(0x64A), - 0xFEF2 => array(0x64A), 0xFEF3 => array(0x64A), 0xFEF4 => array(0x64A), - 0xFEF5 => array(0x644, 0x622), 0xFEF6 => array(0x644, 0x622), 0xFEF7 => array(0x644, 0x623), - 0xFEF8 => array(0x644, 0x623), 0xFEF9 => array(0x644, 0x625), 0xFEFA => array(0x644, 0x625), - 0xFEFB => array(0x644, 0x627), 0xFEFC => array(0x644, 0x627), 0xFF0D => array(0x2D), - 0xFF0E => array(0x2E), 0xFF10 => array(0x30), 0xFF11 => array(0x31), - 0xFF12 => array(0x32), 0xFF13 => array(0x33), 0xFF14 => array(0x34), - 0xFF15 => array(0x35), 0xFF16 => array(0x36), 0xFF17 => array(0x37), - 0xFF18 => array(0x38), 0xFF19 => array(0x39), 0xFF21 => array(0x61), - 0xFF22 => array(0x62), 0xFF23 => array(0x63), 0xFF24 => array(0x64), - 0xFF25 => array(0x65), 0xFF26 => array(0x66), 0xFF27 => array(0x67), - 0xFF28 => array(0x68), 0xFF29 => array(0x69), 0xFF2A => array(0x6A), - 0xFF2B => array(0x6B), 0xFF2C => array(0x6C), 0xFF2D => array(0x6D), - 0xFF2E => array(0x6E), 0xFF2F => array(0x6F), 0xFF30 => array(0x70), - 0xFF31 => array(0x71), 0xFF32 => array(0x72), 0xFF33 => array(0x73), - 0xFF34 => array(0x74), 0xFF35 => array(0x75), 0xFF36 => array(0x76), - 0xFF37 => array(0x77), 0xFF38 => array(0x78), 0xFF39 => array(0x79), - 0xFF3A => array(0x7A), 0xFF41 => array(0x61), 0xFF42 => array(0x62), - 0xFF43 => array(0x63), 0xFF44 => array(0x64), 0xFF45 => array(0x65), - 0xFF46 => array(0x66), 0xFF47 => array(0x67), 0xFF48 => array(0x68), - 0xFF49 => array(0x69), 0xFF4A => array(0x6A), 0xFF4B => array(0x6B), - 0xFF4C => array(0x6C), 0xFF4D => array(0x6D), 0xFF4E => array(0x6E), - 0xFF4F => array(0x6F), 0xFF50 => array(0x70), 0xFF51 => array(0x71), - 0xFF52 => array(0x72), 0xFF53 => array(0x73), 0xFF54 => array(0x74), - 0xFF55 => array(0x75), 0xFF56 => array(0x76), 0xFF57 => array(0x77), - 0xFF58 => array(0x78), 0xFF59 => array(0x79), 0xFF5A => array(0x7A), - 0xFF5F => array(0x2985), 0xFF60 => array(0x2986), 0xFF61 => array(0x2E), - 0xFF62 => array(0x300C), 0xFF63 => array(0x300D), 0xFF64 => array(0x3001), - 0xFF65 => array(0x30FB), 0xFF66 => array(0x30F2), 0xFF67 => array(0x30A1), - 0xFF68 => array(0x30A3), 0xFF69 => array(0x30A5), 0xFF6A => array(0x30A7), - 0xFF6B => array(0x30A9), 0xFF6C => array(0x30E3), 0xFF6D => array(0x30E5), - 0xFF6E => array(0x30E7), 0xFF6F => array(0x30C3), 0xFF70 => array(0x30FC), - 0xFF71 => array(0x30A2), 0xFF72 => array(0x30A4), 0xFF73 => array(0x30A6), - 0xFF74 => array(0x30A8), 0xFF75 => array(0x30AA), 0xFF76 => array(0x30AB), - 0xFF77 => array(0x30AD), 0xFF78 => array(0x30AF), 0xFF79 => array(0x30B1), - 0xFF7A => array(0x30B3), 0xFF7B => array(0x30B5), 0xFF7C => array(0x30B7), - 0xFF7D => array(0x30B9), 0xFF7E => array(0x30BB), 0xFF7F => array(0x30BD), - 0xFF80 => array(0x30BF), 0xFF81 => array(0x30C1), 0xFF82 => array(0x30C4), - 0xFF83 => array(0x30C6), 0xFF84 => array(0x30C8), 0xFF85 => array(0x30CA), - 0xFF86 => array(0x30CB), 0xFF87 => array(0x30CC), 0xFF88 => array(0x30CD), - 0xFF89 => array(0x30CE), 0xFF8A => array(0x30CF), 0xFF8B => array(0x30D2), - 0xFF8C => array(0x30D5), 0xFF8D => array(0x30D8), 0xFF8E => array(0x30DB), - 0xFF8F => array(0x30DE), 0xFF90 => array(0x30DF), 0xFF91 => array(0x30E0), - 0xFF92 => array(0x30E1), 0xFF93 => array(0x30E2), 0xFF94 => array(0x30E4), - 0xFF95 => array(0x30E6), 0xFF96 => array(0x30E8), 0xFF97 => array(0x30E9), - 0xFF98 => array(0x30EA), 0xFF99 => array(0x30EB), 0xFF9A => array(0x30EC), - 0xFF9B => array(0x30ED), 0xFF9C => array(0x30EF), 0xFF9D => array(0x30F3), - 0xFF9E => array(0x3099), 0xFF9F => array(0x309A), 0xFFA1 => array(0x1100), - 0xFFA2 => array(0x1101), 0xFFA3 => array(0x11AA), 0xFFA4 => array(0x1102), - 0xFFA5 => array(0x11AC), 0xFFA6 => array(0x11AD), 0xFFA7 => array(0x1103), - 0xFFA8 => array(0x1104), 0xFFA9 => array(0x1105), 0xFFAA => array(0x11B0), - 0xFFAB => array(0x11B1), 0xFFAC => array(0x11B2), 0xFFAD => array(0x11B3), - 0xFFAE => array(0x11B4), 0xFFAF => array(0x11B5), 0xFFB0 => array(0x111A), - 0xFFB1 => array(0x1106), 0xFFB2 => array(0x1107), 0xFFB3 => array(0x1108), - 0xFFB4 => array(0x1121), 0xFFB5 => array(0x1109), 0xFFB6 => array(0x110A), - 0xFFB7 => array(0x110B), 0xFFB8 => array(0x110C), 0xFFB9 => array(0x110D), - 0xFFBA => array(0x110E), 0xFFBB => array(0x110F), 0xFFBC => array(0x1110), - 0xFFBD => array(0x1111), 0xFFBE => array(0x1112), 0xFFC2 => array(0x1161), - 0xFFC3 => array(0x1162), 0xFFC4 => array(0x1163), 0xFFC5 => array(0x1164), - 0xFFC6 => array(0x1165), 0xFFC7 => array(0x1166), 0xFFCA => array(0x1167), - 0xFFCB => array(0x1168), 0xFFCC => array(0x1169), 0xFFCD => array(0x116A), - 0xFFCE => array(0x116B), 0xFFCF => array(0x116C), 0xFFD2 => array(0x116D), - 0xFFD3 => array(0x116E), 0xFFD4 => array(0x116F), 0xFFD5 => array(0x1170), - 0xFFD6 => array(0x1171), 0xFFD7 => array(0x1172), 0xFFDA => array(0x1173), - 0xFFDB => array(0x1174), 0xFFDC => array(0x1175), 0xFFE0 => array(0xA2), - 0xFFE1 => array(0xA3), 0xFFE2 => array(0xAC), 0xFFE4 => array(0xA6), - 0xFFE5 => array(0xA5), 0xFFE6 => array(0x20A9), 0xFFE8 => array(0x2502), - 0xFFE9 => array(0x2190), 0xFFEA => array(0x2191), 0xFFEB => array(0x2192), - 0xFFEC => array(0x2193), 0xFFED => array(0x25A0), 0xFFEE => array(0x25CB), - 0x10400 => array(0x10428), 0x10401 => array(0x10429), 0x10402 => array(0x1042A), - 0x10403 => array(0x1042B), 0x10404 => array(0x1042C), 0x10405 => array(0x1042D), - 0x10406 => array(0x1042E), 0x10407 => array(0x1042F), 0x10408 => array(0x10430), - 0x10409 => array(0x10431), 0x1040A => array(0x10432), 0x1040B => array(0x10433), - 0x1040C => array(0x10434), 0x1040D => array(0x10435), 0x1040E => array(0x10436), - 0x1040F => array(0x10437), 0x10410 => array(0x10438), 0x10411 => array(0x10439), - 0x10412 => array(0x1043A), 0x10413 => array(0x1043B), 0x10414 => array(0x1043C), - 0x10415 => array(0x1043D), 0x10416 => array(0x1043E), 0x10417 => array(0x1043F), - 0x10418 => array(0x10440), 0x10419 => array(0x10441), 0x1041A => array(0x10442), - 0x1041B => array(0x10443), 0x1041C => array(0x10444), 0x1041D => array(0x10445), - 0x1041E => array(0x10446), 0x1041F => array(0x10447), 0x10420 => array(0x10448), - 0x10421 => array(0x10449), 0x10422 => array(0x1044A), 0x10423 => array(0x1044B), - 0x10424 => array(0x1044C), 0x10425 => array(0x1044D), 0x10426 => array(0x1044E), - 0x10427 => array(0x1044F), 0x118A0 => array(0x118C0), 0x118A1 => array(0x118C1), - 0x118A2 => array(0x118C2), 0x118A3 => array(0x118C3), 0x118A4 => array(0x118C4), - 0x118A5 => array(0x118C5), 0x118A6 => array(0x118C6), 0x118A7 => array(0x118C7), - 0x118A8 => array(0x118C8), 0x118A9 => array(0x118C9), 0x118AA => array(0x118CA), - 0x118AB => array(0x118CB), 0x118AC => array(0x118CC), 0x118AD => array(0x118CD), - 0x118AE => array(0x118CE), 0x118AF => array(0x118CF), 0x118B0 => array(0x118D0), - 0x118B1 => array(0x118D1), 0x118B2 => array(0x118D2), 0x118B3 => array(0x118D3), - 0x118B4 => array(0x118D4), 0x118B5 => array(0x118D5), 0x118B6 => array(0x118D6), - 0x118B7 => array(0x118D7), 0x118B8 => array(0x118D8), 0x118B9 => array(0x118D9), - 0x118BA => array(0x118DA), 0x118BB => array(0x118DB), 0x118BC => array(0x118DC), - 0x118BD => array(0x118DD), 0x118BE => array(0x118DE), 0x118BF => array(0x118DF), - 0x1D15E => array(0x1D157, 0x1D165), 0x1D15F => array(0x1D158, 0x1D165), 0x1D160 => array(0x1D158, 0x1D165, 0x1D16E), - 0x1D161 => array(0x1D158, 0x1D165, 0x1D16F), 0x1D162 => array(0x1D158, 0x1D165, 0x1D170), 0x1D163 => array(0x1D158, 0x1D165, 0x1D171), - 0x1D164 => array(0x1D158, 0x1D165, 0x1D172), 0x1D1BB => array(0x1D1B9, 0x1D165), 0x1D1BC => array(0x1D1BA, 0x1D165), - 0x1D1BD => array(0x1D1B9, 0x1D165, 0x1D16E), 0x1D1BE => array(0x1D1BA, 0x1D165, 0x1D16E), 0x1D1BF => array(0x1D1B9, 0x1D165, 0x1D16F), - 0x1D1C0 => array(0x1D1BA, 0x1D165, 0x1D16F), 0x1D400 => array(0x61), 0x1D401 => array(0x62), - 0x1D402 => array(0x63), 0x1D403 => array(0x64), 0x1D404 => array(0x65), - 0x1D405 => array(0x66), 0x1D406 => array(0x67), 0x1D407 => array(0x68), - 0x1D408 => array(0x69), 0x1D409 => array(0x6A), 0x1D40A => array(0x6B), - 0x1D40B => array(0x6C), 0x1D40C => array(0x6D), 0x1D40D => array(0x6E), - 0x1D40E => array(0x6F), 0x1D40F => array(0x70), 0x1D410 => array(0x71), - 0x1D411 => array(0x72), 0x1D412 => array(0x73), 0x1D413 => array(0x74), - 0x1D414 => array(0x75), 0x1D415 => array(0x76), 0x1D416 => array(0x77), - 0x1D417 => array(0x78), 0x1D418 => array(0x79), 0x1D419 => array(0x7A), - 0x1D41A => array(0x61), 0x1D41B => array(0x62), 0x1D41C => array(0x63), - 0x1D41D => array(0x64), 0x1D41E => array(0x65), 0x1D41F => array(0x66), - 0x1D420 => array(0x67), 0x1D421 => array(0x68), 0x1D422 => array(0x69), - 0x1D423 => array(0x6A), 0x1D424 => array(0x6B), 0x1D425 => array(0x6C), - 0x1D426 => array(0x6D), 0x1D427 => array(0x6E), 0x1D428 => array(0x6F), - 0x1D429 => array(0x70), 0x1D42A => array(0x71), 0x1D42B => array(0x72), - 0x1D42C => array(0x73), 0x1D42D => array(0x74), 0x1D42E => array(0x75), - 0x1D42F => array(0x76), 0x1D430 => array(0x77), 0x1D431 => array(0x78), - 0x1D432 => array(0x79), 0x1D433 => array(0x7A), 0x1D434 => array(0x61), - 0x1D435 => array(0x62), 0x1D436 => array(0x63), 0x1D437 => array(0x64), - 0x1D438 => array(0x65), 0x1D439 => array(0x66), 0x1D43A => array(0x67), - 0x1D43B => array(0x68), 0x1D43C => array(0x69), 0x1D43D => array(0x6A), - 0x1D43E => array(0x6B), 0x1D43F => array(0x6C), 0x1D440 => array(0x6D), - 0x1D441 => array(0x6E), 0x1D442 => array(0x6F), 0x1D443 => array(0x70), - 0x1D444 => array(0x71), 0x1D445 => array(0x72), 0x1D446 => array(0x73), - 0x1D447 => array(0x74), 0x1D448 => array(0x75), 0x1D449 => array(0x76), - 0x1D44A => array(0x77), 0x1D44B => array(0x78), 0x1D44C => array(0x79), - 0x1D44D => array(0x7A), 0x1D44E => array(0x61), 0x1D44F => array(0x62), - 0x1D450 => array(0x63), 0x1D451 => array(0x64), 0x1D452 => array(0x65), - 0x1D453 => array(0x66), 0x1D454 => array(0x67), 0x1D456 => array(0x69), - 0x1D457 => array(0x6A), 0x1D458 => array(0x6B), 0x1D459 => array(0x6C), - 0x1D45A => array(0x6D), 0x1D45B => array(0x6E), 0x1D45C => array(0x6F), - 0x1D45D => array(0x70), 0x1D45E => array(0x71), 0x1D45F => array(0x72), - 0x1D460 => array(0x73), 0x1D461 => array(0x74), 0x1D462 => array(0x75), - 0x1D463 => array(0x76), 0x1D464 => array(0x77), 0x1D465 => array(0x78), - 0x1D466 => array(0x79), 0x1D467 => array(0x7A), 0x1D468 => array(0x61), - 0x1D469 => array(0x62), 0x1D46A => array(0x63), 0x1D46B => array(0x64), - 0x1D46C => array(0x65), 0x1D46D => array(0x66), 0x1D46E => array(0x67), - 0x1D46F => array(0x68), 0x1D470 => array(0x69), 0x1D471 => array(0x6A), - 0x1D472 => array(0x6B), 0x1D473 => array(0x6C), 0x1D474 => array(0x6D), - 0x1D475 => array(0x6E), 0x1D476 => array(0x6F), 0x1D477 => array(0x70), - 0x1D478 => array(0x71), 0x1D479 => array(0x72), 0x1D47A => array(0x73), - 0x1D47B => array(0x74), 0x1D47C => array(0x75), 0x1D47D => array(0x76), - 0x1D47E => array(0x77), 0x1D47F => array(0x78), 0x1D480 => array(0x79), - 0x1D481 => array(0x7A), 0x1D482 => array(0x61), 0x1D483 => array(0x62), - 0x1D484 => array(0x63), 0x1D485 => array(0x64), 0x1D486 => array(0x65), - 0x1D487 => array(0x66), 0x1D488 => array(0x67), 0x1D489 => array(0x68), - 0x1D48A => array(0x69), 0x1D48B => array(0x6A), 0x1D48C => array(0x6B), - 0x1D48D => array(0x6C), 0x1D48E => array(0x6D), 0x1D48F => array(0x6E), - 0x1D490 => array(0x6F), 0x1D491 => array(0x70), 0x1D492 => array(0x71), - 0x1D493 => array(0x72), 0x1D494 => array(0x73), 0x1D495 => array(0x74), - 0x1D496 => array(0x75), 0x1D497 => array(0x76), 0x1D498 => array(0x77), - 0x1D499 => array(0x78), 0x1D49A => array(0x79), 0x1D49B => array(0x7A), - 0x1D49C => array(0x61), 0x1D49E => array(0x63), 0x1D49F => array(0x64), - 0x1D4A2 => array(0x67), 0x1D4A5 => array(0x6A), 0x1D4A6 => array(0x6B), - 0x1D4A9 => array(0x6E), 0x1D4AA => array(0x6F), 0x1D4AB => array(0x70), - 0x1D4AC => array(0x71), 0x1D4AE => array(0x73), 0x1D4AF => array(0x74), - 0x1D4B0 => array(0x75), 0x1D4B1 => array(0x76), 0x1D4B2 => array(0x77), - 0x1D4B3 => array(0x78), 0x1D4B4 => array(0x79), 0x1D4B5 => array(0x7A), - 0x1D4B6 => array(0x61), 0x1D4B7 => array(0x62), 0x1D4B8 => array(0x63), - 0x1D4B9 => array(0x64), 0x1D4BB => array(0x66), 0x1D4BD => array(0x68), - 0x1D4BE => array(0x69), 0x1D4BF => array(0x6A), 0x1D4C0 => array(0x6B), - 0x1D4C1 => array(0x6C), 0x1D4C2 => array(0x6D), 0x1D4C3 => array(0x6E), - 0x1D4C5 => array(0x70), 0x1D4C6 => array(0x71), 0x1D4C7 => array(0x72), - 0x1D4C8 => array(0x73), 0x1D4C9 => array(0x74), 0x1D4CA => array(0x75), - 0x1D4CB => array(0x76), 0x1D4CC => array(0x77), 0x1D4CD => array(0x78), - 0x1D4CE => array(0x79), 0x1D4CF => array(0x7A), 0x1D4D0 => array(0x61), - 0x1D4D1 => array(0x62), 0x1D4D2 => array(0x63), 0x1D4D3 => array(0x64), - 0x1D4D4 => array(0x65), 0x1D4D5 => array(0x66), 0x1D4D6 => array(0x67), - 0x1D4D7 => array(0x68), 0x1D4D8 => array(0x69), 0x1D4D9 => array(0x6A), - 0x1D4DA => array(0x6B), 0x1D4DB => array(0x6C), 0x1D4DC => array(0x6D), - 0x1D4DD => array(0x6E), 0x1D4DE => array(0x6F), 0x1D4DF => array(0x70), - 0x1D4E0 => array(0x71), 0x1D4E1 => array(0x72), 0x1D4E2 => array(0x73), - 0x1D4E3 => array(0x74), 0x1D4E4 => array(0x75), 0x1D4E5 => array(0x76), - 0x1D4E6 => array(0x77), 0x1D4E7 => array(0x78), 0x1D4E8 => array(0x79), - 0x1D4E9 => array(0x7A), 0x1D4EA => array(0x61), 0x1D4EB => array(0x62), - 0x1D4EC => array(0x63), 0x1D4ED => array(0x64), 0x1D4EE => array(0x65), - 0x1D4EF => array(0x66), 0x1D4F0 => array(0x67), 0x1D4F1 => array(0x68), - 0x1D4F2 => array(0x69), 0x1D4F3 => array(0x6A), 0x1D4F4 => array(0x6B), - 0x1D4F5 => array(0x6C), 0x1D4F6 => array(0x6D), 0x1D4F7 => array(0x6E), - 0x1D4F8 => array(0x6F), 0x1D4F9 => array(0x70), 0x1D4FA => array(0x71), - 0x1D4FB => array(0x72), 0x1D4FC => array(0x73), 0x1D4FD => array(0x74), - 0x1D4FE => array(0x75), 0x1D4FF => array(0x76), 0x1D500 => array(0x77), - 0x1D501 => array(0x78), 0x1D502 => array(0x79), 0x1D503 => array(0x7A), - 0x1D504 => array(0x61), 0x1D505 => array(0x62), 0x1D507 => array(0x64), - 0x1D508 => array(0x65), 0x1D509 => array(0x66), 0x1D50A => array(0x67), - 0x1D50D => array(0x6A), 0x1D50E => array(0x6B), 0x1D50F => array(0x6C), - 0x1D510 => array(0x6D), 0x1D511 => array(0x6E), 0x1D512 => array(0x6F), - 0x1D513 => array(0x70), 0x1D514 => array(0x71), 0x1D516 => array(0x73), - 0x1D517 => array(0x74), 0x1D518 => array(0x75), 0x1D519 => array(0x76), - 0x1D51A => array(0x77), 0x1D51B => array(0x78), 0x1D51C => array(0x79), - 0x1D51E => array(0x61), 0x1D51F => array(0x62), 0x1D520 => array(0x63), - 0x1D521 => array(0x64), 0x1D522 => array(0x65), 0x1D523 => array(0x66), - 0x1D524 => array(0x67), 0x1D525 => array(0x68), 0x1D526 => array(0x69), - 0x1D527 => array(0x6A), 0x1D528 => array(0x6B), 0x1D529 => array(0x6C), - 0x1D52A => array(0x6D), 0x1D52B => array(0x6E), 0x1D52C => array(0x6F), - 0x1D52D => array(0x70), 0x1D52E => array(0x71), 0x1D52F => array(0x72), - 0x1D530 => array(0x73), 0x1D531 => array(0x74), 0x1D532 => array(0x75), - 0x1D533 => array(0x76), 0x1D534 => array(0x77), 0x1D535 => array(0x78), - 0x1D536 => array(0x79), 0x1D537 => array(0x7A), 0x1D538 => array(0x61), - 0x1D539 => array(0x62), 0x1D53B => array(0x64), 0x1D53C => array(0x65), - 0x1D53D => array(0x66), 0x1D53E => array(0x67), 0x1D540 => array(0x69), - 0x1D541 => array(0x6A), 0x1D542 => array(0x6B), 0x1D543 => array(0x6C), - 0x1D544 => array(0x6D), 0x1D546 => array(0x6F), 0x1D54A => array(0x73), - 0x1D54B => array(0x74), 0x1D54C => array(0x75), 0x1D54D => array(0x76), - 0x1D54E => array(0x77), 0x1D54F => array(0x78), 0x1D550 => array(0x79), - 0x1D552 => array(0x61), 0x1D553 => array(0x62), 0x1D554 => array(0x63), - 0x1D555 => array(0x64), 0x1D556 => array(0x65), 0x1D557 => array(0x66), - 0x1D558 => array(0x67), 0x1D559 => array(0x68), 0x1D55A => array(0x69), - 0x1D55B => array(0x6A), 0x1D55C => array(0x6B), 0x1D55D => array(0x6C), - 0x1D55E => array(0x6D), 0x1D55F => array(0x6E), 0x1D560 => array(0x6F), - 0x1D561 => array(0x70), 0x1D562 => array(0x71), 0x1D563 => array(0x72), - 0x1D564 => array(0x73), 0x1D565 => array(0x74), 0x1D566 => array(0x75), - 0x1D567 => array(0x76), 0x1D568 => array(0x77), 0x1D569 => array(0x78), - 0x1D56A => array(0x79), 0x1D56B => array(0x7A), 0x1D56C => array(0x61), - 0x1D56D => array(0x62), 0x1D56E => array(0x63), 0x1D56F => array(0x64), - 0x1D570 => array(0x65), 0x1D571 => array(0x66), 0x1D572 => array(0x67), - 0x1D573 => array(0x68), 0x1D574 => array(0x69), 0x1D575 => array(0x6A), - 0x1D576 => array(0x6B), 0x1D577 => array(0x6C), 0x1D578 => array(0x6D), - 0x1D579 => array(0x6E), 0x1D57A => array(0x6F), 0x1D57B => array(0x70), - 0x1D57C => array(0x71), 0x1D57D => array(0x72), 0x1D57E => array(0x73), - 0x1D57F => array(0x74), 0x1D580 => array(0x75), 0x1D581 => array(0x76), - 0x1D582 => array(0x77), 0x1D583 => array(0x78), 0x1D584 => array(0x79), - 0x1D585 => array(0x7A), 0x1D586 => array(0x61), 0x1D587 => array(0x62), - 0x1D588 => array(0x63), 0x1D589 => array(0x64), 0x1D58A => array(0x65), - 0x1D58B => array(0x66), 0x1D58C => array(0x67), 0x1D58D => array(0x68), - 0x1D58E => array(0x69), 0x1D58F => array(0x6A), 0x1D590 => array(0x6B), - 0x1D591 => array(0x6C), 0x1D592 => array(0x6D), 0x1D593 => array(0x6E), - 0x1D594 => array(0x6F), 0x1D595 => array(0x70), 0x1D596 => array(0x71), - 0x1D597 => array(0x72), 0x1D598 => array(0x73), 0x1D599 => array(0x74), - 0x1D59A => array(0x75), 0x1D59B => array(0x76), 0x1D59C => array(0x77), - 0x1D59D => array(0x78), 0x1D59E => array(0x79), 0x1D59F => array(0x7A), - 0x1D5A0 => array(0x61), 0x1D5A1 => array(0x62), 0x1D5A2 => array(0x63), - 0x1D5A3 => array(0x64), 0x1D5A4 => array(0x65), 0x1D5A5 => array(0x66), - 0x1D5A6 => array(0x67), 0x1D5A7 => array(0x68), 0x1D5A8 => array(0x69), - 0x1D5A9 => array(0x6A), 0x1D5AA => array(0x6B), 0x1D5AB => array(0x6C), - 0x1D5AC => array(0x6D), 0x1D5AD => array(0x6E), 0x1D5AE => array(0x6F), - 0x1D5AF => array(0x70), 0x1D5B0 => array(0x71), 0x1D5B1 => array(0x72), - 0x1D5B2 => array(0x73), 0x1D5B3 => array(0x74), 0x1D5B4 => array(0x75), - 0x1D5B5 => array(0x76), 0x1D5B6 => array(0x77), 0x1D5B7 => array(0x78), - 0x1D5B8 => array(0x79), 0x1D5B9 => array(0x7A), 0x1D5BA => array(0x61), - 0x1D5BB => array(0x62), 0x1D5BC => array(0x63), 0x1D5BD => array(0x64), - 0x1D5BE => array(0x65), 0x1D5BF => array(0x66), 0x1D5C0 => array(0x67), - 0x1D5C1 => array(0x68), 0x1D5C2 => array(0x69), 0x1D5C3 => array(0x6A), - 0x1D5C4 => array(0x6B), 0x1D5C5 => array(0x6C), 0x1D5C6 => array(0x6D), - 0x1D5C7 => array(0x6E), 0x1D5C8 => array(0x6F), 0x1D5C9 => array(0x70), - 0x1D5CA => array(0x71), 0x1D5CB => array(0x72), 0x1D5CC => array(0x73), - 0x1D5CD => array(0x74), 0x1D5CE => array(0x75), 0x1D5CF => array(0x76), - 0x1D5D0 => array(0x77), 0x1D5D1 => array(0x78), 0x1D5D2 => array(0x79), - 0x1D5D3 => array(0x7A), 0x1D5D4 => array(0x61), 0x1D5D5 => array(0x62), - 0x1D5D6 => array(0x63), 0x1D5D7 => array(0x64), 0x1D5D8 => array(0x65), - 0x1D5D9 => array(0x66), 0x1D5DA => array(0x67), 0x1D5DB => array(0x68), - 0x1D5DC => array(0x69), 0x1D5DD => array(0x6A), 0x1D5DE => array(0x6B), - 0x1D5DF => array(0x6C), 0x1D5E0 => array(0x6D), 0x1D5E1 => array(0x6E), - 0x1D5E2 => array(0x6F), 0x1D5E3 => array(0x70), 0x1D5E4 => array(0x71), - 0x1D5E5 => array(0x72), 0x1D5E6 => array(0x73), 0x1D5E7 => array(0x74), - 0x1D5E8 => array(0x75), 0x1D5E9 => array(0x76), 0x1D5EA => array(0x77), - 0x1D5EB => array(0x78), 0x1D5EC => array(0x79), 0x1D5ED => array(0x7A), - 0x1D5EE => array(0x61), 0x1D5EF => array(0x62), 0x1D5F0 => array(0x63), - 0x1D5F1 => array(0x64), 0x1D5F2 => array(0x65), 0x1D5F3 => array(0x66), - 0x1D5F4 => array(0x67), 0x1D5F5 => array(0x68), 0x1D5F6 => array(0x69), - 0x1D5F7 => array(0x6A), 0x1D5F8 => array(0x6B), 0x1D5F9 => array(0x6C), - 0x1D5FA => array(0x6D), 0x1D5FB => array(0x6E), 0x1D5FC => array(0x6F), - 0x1D5FD => array(0x70), 0x1D5FE => array(0x71), 0x1D5FF => array(0x72), - 0x1D600 => array(0x73), 0x1D601 => array(0x74), 0x1D602 => array(0x75), - 0x1D603 => array(0x76), 0x1D604 => array(0x77), 0x1D605 => array(0x78), - 0x1D606 => array(0x79), 0x1D607 => array(0x7A), 0x1D608 => array(0x61), - 0x1D609 => array(0x62), 0x1D60A => array(0x63), 0x1D60B => array(0x64), - 0x1D60C => array(0x65), 0x1D60D => array(0x66), 0x1D60E => array(0x67), - 0x1D60F => array(0x68), 0x1D610 => array(0x69), 0x1D611 => array(0x6A), - 0x1D612 => array(0x6B), 0x1D613 => array(0x6C), 0x1D614 => array(0x6D), - 0x1D615 => array(0x6E), 0x1D616 => array(0x6F), 0x1D617 => array(0x70), - 0x1D618 => array(0x71), 0x1D619 => array(0x72), 0x1D61A => array(0x73), - 0x1D61B => array(0x74), 0x1D61C => array(0x75), 0x1D61D => array(0x76), - 0x1D61E => array(0x77), 0x1D61F => array(0x78), 0x1D620 => array(0x79), - 0x1D621 => array(0x7A), 0x1D622 => array(0x61), 0x1D623 => array(0x62), - 0x1D624 => array(0x63), 0x1D625 => array(0x64), 0x1D626 => array(0x65), - 0x1D627 => array(0x66), 0x1D628 => array(0x67), 0x1D629 => array(0x68), - 0x1D62A => array(0x69), 0x1D62B => array(0x6A), 0x1D62C => array(0x6B), - 0x1D62D => array(0x6C), 0x1D62E => array(0x6D), 0x1D62F => array(0x6E), - 0x1D630 => array(0x6F), 0x1D631 => array(0x70), 0x1D632 => array(0x71), - 0x1D633 => array(0x72), 0x1D634 => array(0x73), 0x1D635 => array(0x74), - 0x1D636 => array(0x75), 0x1D637 => array(0x76), 0x1D638 => array(0x77), - 0x1D639 => array(0x78), 0x1D63A => array(0x79), 0x1D63B => array(0x7A), - 0x1D63C => array(0x61), 0x1D63D => array(0x62), 0x1D63E => array(0x63), - 0x1D63F => array(0x64), 0x1D640 => array(0x65), 0x1D641 => array(0x66), - 0x1D642 => array(0x67), 0x1D643 => array(0x68), 0x1D644 => array(0x69), - 0x1D645 => array(0x6A), 0x1D646 => array(0x6B), 0x1D647 => array(0x6C), - 0x1D648 => array(0x6D), 0x1D649 => array(0x6E), 0x1D64A => array(0x6F), - 0x1D64B => array(0x70), 0x1D64C => array(0x71), 0x1D64D => array(0x72), - 0x1D64E => array(0x73), 0x1D64F => array(0x74), 0x1D650 => array(0x75), - 0x1D651 => array(0x76), 0x1D652 => array(0x77), 0x1D653 => array(0x78), - 0x1D654 => array(0x79), 0x1D655 => array(0x7A), 0x1D656 => array(0x61), - 0x1D657 => array(0x62), 0x1D658 => array(0x63), 0x1D659 => array(0x64), - 0x1D65A => array(0x65), 0x1D65B => array(0x66), 0x1D65C => array(0x67), - 0x1D65D => array(0x68), 0x1D65E => array(0x69), 0x1D65F => array(0x6A), - 0x1D660 => array(0x6B), 0x1D661 => array(0x6C), 0x1D662 => array(0x6D), - 0x1D663 => array(0x6E), 0x1D664 => array(0x6F), 0x1D665 => array(0x70), - 0x1D666 => array(0x71), 0x1D667 => array(0x72), 0x1D668 => array(0x73), - 0x1D669 => array(0x74), 0x1D66A => array(0x75), 0x1D66B => array(0x76), - 0x1D66C => array(0x77), 0x1D66D => array(0x78), 0x1D66E => array(0x79), - 0x1D66F => array(0x7A), 0x1D670 => array(0x61), 0x1D671 => array(0x62), - 0x1D672 => array(0x63), 0x1D673 => array(0x64), 0x1D674 => array(0x65), - 0x1D675 => array(0x66), 0x1D676 => array(0x67), 0x1D677 => array(0x68), - 0x1D678 => array(0x69), 0x1D679 => array(0x6A), 0x1D67A => array(0x6B), - 0x1D67B => array(0x6C), 0x1D67C => array(0x6D), 0x1D67D => array(0x6E), - 0x1D67E => array(0x6F), 0x1D67F => array(0x70), 0x1D680 => array(0x71), - 0x1D681 => array(0x72), 0x1D682 => array(0x73), 0x1D683 => array(0x74), - 0x1D684 => array(0x75), 0x1D685 => array(0x76), 0x1D686 => array(0x77), - 0x1D687 => array(0x78), 0x1D688 => array(0x79), 0x1D689 => array(0x7A), - 0x1D68A => array(0x61), 0x1D68B => array(0x62), 0x1D68C => array(0x63), - 0x1D68D => array(0x64), 0x1D68E => array(0x65), 0x1D68F => array(0x66), - 0x1D690 => array(0x67), 0x1D691 => array(0x68), 0x1D692 => array(0x69), - 0x1D693 => array(0x6A), 0x1D694 => array(0x6B), 0x1D695 => array(0x6C), - 0x1D696 => array(0x6D), 0x1D697 => array(0x6E), 0x1D698 => array(0x6F), - 0x1D699 => array(0x70), 0x1D69A => array(0x71), 0x1D69B => array(0x72), - 0x1D69C => array(0x73), 0x1D69D => array(0x74), 0x1D69E => array(0x75), - 0x1D69F => array(0x76), 0x1D6A0 => array(0x77), 0x1D6A1 => array(0x78), - 0x1D6A2 => array(0x79), 0x1D6A3 => array(0x7A), 0x1D6A4 => array(0x131), - 0x1D6A5 => array(0x237), 0x1D6A8 => array(0x3B1), 0x1D6A9 => array(0x3B2), - 0x1D6AA => array(0x3B3), 0x1D6AB => array(0x3B4), 0x1D6AC => array(0x3B5), - 0x1D6AD => array(0x3B6), 0x1D6AE => array(0x3B7), 0x1D6AF => array(0x3B8), - 0x1D6B0 => array(0x3B9), 0x1D6B1 => array(0x3BA), 0x1D6B2 => array(0x3BB), - 0x1D6B3 => array(0x3BC), 0x1D6B4 => array(0x3BD), 0x1D6B5 => array(0x3BE), - 0x1D6B6 => array(0x3BF), 0x1D6B7 => array(0x3C0), 0x1D6B8 => array(0x3C1), - 0x1D6B9 => array(0x3B8), 0x1D6BA => array(0x3C3), 0x1D6BB => array(0x3C4), - 0x1D6BC => array(0x3C5), 0x1D6BD => array(0x3C6), 0x1D6BE => array(0x3C7), - 0x1D6BF => array(0x3C8), 0x1D6C0 => array(0x3C9), 0x1D6C1 => array(0x2207), - 0x1D6C2 => array(0x3B1), 0x1D6C3 => array(0x3B2), 0x1D6C4 => array(0x3B3), - 0x1D6C5 => array(0x3B4), 0x1D6C6 => array(0x3B5), 0x1D6C7 => array(0x3B6), - 0x1D6C8 => array(0x3B7), 0x1D6C9 => array(0x3B8), 0x1D6CA => array(0x3B9), - 0x1D6CB => array(0x3BA), 0x1D6CC => array(0x3BB), 0x1D6CD => array(0x3BC), - 0x1D6CE => array(0x3BD), 0x1D6CF => array(0x3BE), 0x1D6D0 => array(0x3BF), - 0x1D6D1 => array(0x3C0), 0x1D6D2 => array(0x3C1), 0x1D6D3 => array(0x3C3), - 0x1D6D4 => array(0x3C3), 0x1D6D5 => array(0x3C4), 0x1D6D6 => array(0x3C5), - 0x1D6D7 => array(0x3C6), 0x1D6D8 => array(0x3C7), 0x1D6D9 => array(0x3C8), - 0x1D6DA => array(0x3C9), 0x1D6DB => array(0x2202), 0x1D6DC => array(0x3B5), - 0x1D6DD => array(0x3B8), 0x1D6DE => array(0x3BA), 0x1D6DF => array(0x3C6), - 0x1D6E0 => array(0x3C1), 0x1D6E1 => array(0x3C0), 0x1D6E2 => array(0x3B1), - 0x1D6E3 => array(0x3B2), 0x1D6E4 => array(0x3B3), 0x1D6E5 => array(0x3B4), - 0x1D6E6 => array(0x3B5), 0x1D6E7 => array(0x3B6), 0x1D6E8 => array(0x3B7), - 0x1D6E9 => array(0x3B8), 0x1D6EA => array(0x3B9), 0x1D6EB => array(0x3BA), - 0x1D6EC => array(0x3BB), 0x1D6ED => array(0x3BC), 0x1D6EE => array(0x3BD), - 0x1D6EF => array(0x3BE), 0x1D6F0 => array(0x3BF), 0x1D6F1 => array(0x3C0), - 0x1D6F2 => array(0x3C1), 0x1D6F3 => array(0x3B8), 0x1D6F4 => array(0x3C3), - 0x1D6F5 => array(0x3C4), 0x1D6F6 => array(0x3C5), 0x1D6F7 => array(0x3C6), - 0x1D6F8 => array(0x3C7), 0x1D6F9 => array(0x3C8), 0x1D6FA => array(0x3C9), - 0x1D6FB => array(0x2207), 0x1D6FC => array(0x3B1), 0x1D6FD => array(0x3B2), - 0x1D6FE => array(0x3B3), 0x1D6FF => array(0x3B4), 0x1D700 => array(0x3B5), - 0x1D701 => array(0x3B6), 0x1D702 => array(0x3B7), 0x1D703 => array(0x3B8), - 0x1D704 => array(0x3B9), 0x1D705 => array(0x3BA), 0x1D706 => array(0x3BB), - 0x1D707 => array(0x3BC), 0x1D708 => array(0x3BD), 0x1D709 => array(0x3BE), - 0x1D70A => array(0x3BF), 0x1D70B => array(0x3C0), 0x1D70C => array(0x3C1), - 0x1D70D => array(0x3C3), 0x1D70E => array(0x3C3), 0x1D70F => array(0x3C4), - 0x1D710 => array(0x3C5), 0x1D711 => array(0x3C6), 0x1D712 => array(0x3C7), - 0x1D713 => array(0x3C8), 0x1D714 => array(0x3C9), 0x1D715 => array(0x2202), - 0x1D716 => array(0x3B5), 0x1D717 => array(0x3B8), 0x1D718 => array(0x3BA), - 0x1D719 => array(0x3C6), 0x1D71A => array(0x3C1), 0x1D71B => array(0x3C0), - 0x1D71C => array(0x3B1), 0x1D71D => array(0x3B2), 0x1D71E => array(0x3B3), - 0x1D71F => array(0x3B4), 0x1D720 => array(0x3B5), 0x1D721 => array(0x3B6), - 0x1D722 => array(0x3B7), 0x1D723 => array(0x3B8), 0x1D724 => array(0x3B9), - 0x1D725 => array(0x3BA), 0x1D726 => array(0x3BB), 0x1D727 => array(0x3BC), - 0x1D728 => array(0x3BD), 0x1D729 => array(0x3BE), 0x1D72A => array(0x3BF), - 0x1D72B => array(0x3C0), 0x1D72C => array(0x3C1), 0x1D72D => array(0x3B8), - 0x1D72E => array(0x3C3), 0x1D72F => array(0x3C4), 0x1D730 => array(0x3C5), - 0x1D731 => array(0x3C6), 0x1D732 => array(0x3C7), 0x1D733 => array(0x3C8), - 0x1D734 => array(0x3C9), 0x1D735 => array(0x2207), 0x1D736 => array(0x3B1), - 0x1D737 => array(0x3B2), 0x1D738 => array(0x3B3), 0x1D739 => array(0x3B4), - 0x1D73A => array(0x3B5), 0x1D73B => array(0x3B6), 0x1D73C => array(0x3B7), - 0x1D73D => array(0x3B8), 0x1D73E => array(0x3B9), 0x1D73F => array(0x3BA), - 0x1D740 => array(0x3BB), 0x1D741 => array(0x3BC), 0x1D742 => array(0x3BD), - 0x1D743 => array(0x3BE), 0x1D744 => array(0x3BF), 0x1D745 => array(0x3C0), - 0x1D746 => array(0x3C1), 0x1D747 => array(0x3C3), 0x1D748 => array(0x3C3), - 0x1D749 => array(0x3C4), 0x1D74A => array(0x3C5), 0x1D74B => array(0x3C6), - 0x1D74C => array(0x3C7), 0x1D74D => array(0x3C8), 0x1D74E => array(0x3C9), - 0x1D74F => array(0x2202), 0x1D750 => array(0x3B5), 0x1D751 => array(0x3B8), - 0x1D752 => array(0x3BA), 0x1D753 => array(0x3C6), 0x1D754 => array(0x3C1), - 0x1D755 => array(0x3C0), 0x1D756 => array(0x3B1), 0x1D757 => array(0x3B2), - 0x1D758 => array(0x3B3), 0x1D759 => array(0x3B4), 0x1D75A => array(0x3B5), - 0x1D75B => array(0x3B6), 0x1D75C => array(0x3B7), 0x1D75D => array(0x3B8), - 0x1D75E => array(0x3B9), 0x1D75F => array(0x3BA), 0x1D760 => array(0x3BB), - 0x1D761 => array(0x3BC), 0x1D762 => array(0x3BD), 0x1D763 => array(0x3BE), - 0x1D764 => array(0x3BF), 0x1D765 => array(0x3C0), 0x1D766 => array(0x3C1), - 0x1D767 => array(0x3B8), 0x1D768 => array(0x3C3), 0x1D769 => array(0x3C4), - 0x1D76A => array(0x3C5), 0x1D76B => array(0x3C6), 0x1D76C => array(0x3C7), - 0x1D76D => array(0x3C8), 0x1D76E => array(0x3C9), 0x1D76F => array(0x2207), - 0x1D770 => array(0x3B1), 0x1D771 => array(0x3B2), 0x1D772 => array(0x3B3), - 0x1D773 => array(0x3B4), 0x1D774 => array(0x3B5), 0x1D775 => array(0x3B6), - 0x1D776 => array(0x3B7), 0x1D777 => array(0x3B8), 0x1D778 => array(0x3B9), - 0x1D779 => array(0x3BA), 0x1D77A => array(0x3BB), 0x1D77B => array(0x3BC), - 0x1D77C => array(0x3BD), 0x1D77D => array(0x3BE), 0x1D77E => array(0x3BF), - 0x1D77F => array(0x3C0), 0x1D780 => array(0x3C1), 0x1D781 => array(0x3C3), - 0x1D782 => array(0x3C3), 0x1D783 => array(0x3C4), 0x1D784 => array(0x3C5), - 0x1D785 => array(0x3C6), 0x1D786 => array(0x3C7), 0x1D787 => array(0x3C8), - 0x1D788 => array(0x3C9), 0x1D789 => array(0x2202), 0x1D78A => array(0x3B5), - 0x1D78B => array(0x3B8), 0x1D78C => array(0x3BA), 0x1D78D => array(0x3C6), - 0x1D78E => array(0x3C1), 0x1D78F => array(0x3C0), 0x1D790 => array(0x3B1), - 0x1D791 => array(0x3B2), 0x1D792 => array(0x3B3), 0x1D793 => array(0x3B4), - 0x1D794 => array(0x3B5), 0x1D795 => array(0x3B6), 0x1D796 => array(0x3B7), - 0x1D797 => array(0x3B8), 0x1D798 => array(0x3B9), 0x1D799 => array(0x3BA), - 0x1D79A => array(0x3BB), 0x1D79B => array(0x3BC), 0x1D79C => array(0x3BD), - 0x1D79D => array(0x3BE), 0x1D79E => array(0x3BF), 0x1D79F => array(0x3C0), - 0x1D7A0 => array(0x3C1), 0x1D7A1 => array(0x3B8), 0x1D7A2 => array(0x3C3), - 0x1D7A3 => array(0x3C4), 0x1D7A4 => array(0x3C5), 0x1D7A5 => array(0x3C6), - 0x1D7A6 => array(0x3C7), 0x1D7A7 => array(0x3C8), 0x1D7A8 => array(0x3C9), - 0x1D7A9 => array(0x2207), 0x1D7AA => array(0x3B1), 0x1D7AB => array(0x3B2), - 0x1D7AC => array(0x3B3), 0x1D7AD => array(0x3B4), 0x1D7AE => array(0x3B5), - 0x1D7AF => array(0x3B6), 0x1D7B0 => array(0x3B7), 0x1D7B1 => array(0x3B8), - 0x1D7B2 => array(0x3B9), 0x1D7B3 => array(0x3BA), 0x1D7B4 => array(0x3BB), - 0x1D7B5 => array(0x3BC), 0x1D7B6 => array(0x3BD), 0x1D7B7 => array(0x3BE), - 0x1D7B8 => array(0x3BF), 0x1D7B9 => array(0x3C0), 0x1D7BA => array(0x3C1), - 0x1D7BB => array(0x3C3), 0x1D7BC => array(0x3C3), 0x1D7BD => array(0x3C4), - 0x1D7BE => array(0x3C5), 0x1D7BF => array(0x3C6), 0x1D7C0 => array(0x3C7), - 0x1D7C1 => array(0x3C8), 0x1D7C2 => array(0x3C9), 0x1D7C3 => array(0x2202), - 0x1D7C4 => array(0x3B5), 0x1D7C5 => array(0x3B8), 0x1D7C6 => array(0x3BA), - 0x1D7C7 => array(0x3C6), 0x1D7C8 => array(0x3C1), 0x1D7C9 => array(0x3C0), - 0x1D7CA => array(0x3DD), 0x1D7CB => array(0x3DD), 0x1D7CE => array(0x30), - 0x1D7CF => array(0x31), 0x1D7D0 => array(0x32), 0x1D7D1 => array(0x33), - 0x1D7D2 => array(0x34), 0x1D7D3 => array(0x35), 0x1D7D4 => array(0x36), - 0x1D7D5 => array(0x37), 0x1D7D6 => array(0x38), 0x1D7D7 => array(0x39), - 0x1D7D8 => array(0x30), 0x1D7D9 => array(0x31), 0x1D7DA => array(0x32), - 0x1D7DB => array(0x33), 0x1D7DC => array(0x34), 0x1D7DD => array(0x35), - 0x1D7DE => array(0x36), 0x1D7DF => array(0x37), 0x1D7E0 => array(0x38), - 0x1D7E1 => array(0x39), 0x1D7E2 => array(0x30), 0x1D7E3 => array(0x31), - 0x1D7E4 => array(0x32), 0x1D7E5 => array(0x33), 0x1D7E6 => array(0x34), - 0x1D7E7 => array(0x35), 0x1D7E8 => array(0x36), 0x1D7E9 => array(0x37), - 0x1D7EA => array(0x38), 0x1D7EB => array(0x39), 0x1D7EC => array(0x30), - 0x1D7ED => array(0x31), 0x1D7EE => array(0x32), 0x1D7EF => array(0x33), - 0x1D7F0 => array(0x34), 0x1D7F1 => array(0x35), 0x1D7F2 => array(0x36), - 0x1D7F3 => array(0x37), 0x1D7F4 => array(0x38), 0x1D7F5 => array(0x39), - 0x1D7F6 => array(0x30), 0x1D7F7 => array(0x31), 0x1D7F8 => array(0x32), - 0x1D7F9 => array(0x33), 0x1D7FA => array(0x34), 0x1D7FB => array(0x35), - 0x1D7FC => array(0x36), 0x1D7FD => array(0x37), 0x1D7FE => array(0x38), - 0x1D7FF => array(0x39), 0x1EE00 => array(0x627), 0x1EE01 => array(0x628), - 0x1EE02 => array(0x62C), 0x1EE03 => array(0x62F), 0x1EE05 => array(0x648), - 0x1EE06 => array(0x632), 0x1EE07 => array(0x62D), 0x1EE08 => array(0x637), - 0x1EE09 => array(0x64A), 0x1EE0A => array(0x643), 0x1EE0B => array(0x644), - 0x1EE0C => array(0x645), 0x1EE0D => array(0x646), 0x1EE0E => array(0x633), - 0x1EE0F => array(0x639), 0x1EE10 => array(0x641), 0x1EE11 => array(0x635), - 0x1EE12 => array(0x642), 0x1EE13 => array(0x631), 0x1EE14 => array(0x634), - 0x1EE15 => array(0x62A), 0x1EE16 => array(0x62B), 0x1EE17 => array(0x62E), - 0x1EE18 => array(0x630), 0x1EE19 => array(0x636), 0x1EE1A => array(0x638), - 0x1EE1B => array(0x63A), 0x1EE1C => array(0x66E), 0x1EE1D => array(0x6BA), - 0x1EE1E => array(0x6A1), 0x1EE1F => array(0x66F), 0x1EE21 => array(0x628), - 0x1EE22 => array(0x62C), 0x1EE24 => array(0x647), 0x1EE27 => array(0x62D), - 0x1EE29 => array(0x64A), 0x1EE2A => array(0x643), 0x1EE2B => array(0x644), - 0x1EE2C => array(0x645), 0x1EE2D => array(0x646), 0x1EE2E => array(0x633), - 0x1EE2F => array(0x639), 0x1EE30 => array(0x641), 0x1EE31 => array(0x635), - 0x1EE32 => array(0x642), 0x1EE34 => array(0x634), 0x1EE35 => array(0x62A), - 0x1EE36 => array(0x62B), 0x1EE37 => array(0x62E), 0x1EE39 => array(0x636), - 0x1EE3B => array(0x63A), 0x1EE42 => array(0x62C), 0x1EE47 => array(0x62D), - 0x1EE49 => array(0x64A), 0x1EE4B => array(0x644), 0x1EE4D => array(0x646), - 0x1EE4E => array(0x633), 0x1EE4F => array(0x639), 0x1EE51 => array(0x635), - 0x1EE52 => array(0x642), 0x1EE54 => array(0x634), 0x1EE57 => array(0x62E), - 0x1EE59 => array(0x636), 0x1EE5B => array(0x63A), 0x1EE5D => array(0x6BA), - 0x1EE5F => array(0x66F), 0x1EE61 => array(0x628), 0x1EE62 => array(0x62C), - 0x1EE64 => array(0x647), 0x1EE67 => array(0x62D), 0x1EE68 => array(0x637), - 0x1EE69 => array(0x64A), 0x1EE6A => array(0x643), 0x1EE6C => array(0x645), - 0x1EE6D => array(0x646), 0x1EE6E => array(0x633), 0x1EE6F => array(0x639), - 0x1EE70 => array(0x641), 0x1EE71 => array(0x635), 0x1EE72 => array(0x642), - 0x1EE74 => array(0x634), 0x1EE75 => array(0x62A), 0x1EE76 => array(0x62B), - 0x1EE77 => array(0x62E), 0x1EE79 => array(0x636), 0x1EE7A => array(0x638), - 0x1EE7B => array(0x63A), 0x1EE7C => array(0x66E), 0x1EE7E => array(0x6A1), - 0x1EE80 => array(0x627), 0x1EE81 => array(0x628), 0x1EE82 => array(0x62C), - 0x1EE83 => array(0x62F), 0x1EE84 => array(0x647), 0x1EE85 => array(0x648), - 0x1EE86 => array(0x632), 0x1EE87 => array(0x62D), 0x1EE88 => array(0x637), - 0x1EE89 => array(0x64A), 0x1EE8B => array(0x644), 0x1EE8C => array(0x645), - 0x1EE8D => array(0x646), 0x1EE8E => array(0x633), 0x1EE8F => array(0x639), - 0x1EE90 => array(0x641), 0x1EE91 => array(0x635), 0x1EE92 => array(0x642), - 0x1EE93 => array(0x631), 0x1EE94 => array(0x634), 0x1EE95 => array(0x62A), - 0x1EE96 => array(0x62B), 0x1EE97 => array(0x62E), 0x1EE98 => array(0x630), - 0x1EE99 => array(0x636), 0x1EE9A => array(0x638), 0x1EE9B => array(0x63A), - 0x1EEA1 => array(0x628), 0x1EEA2 => array(0x62C), 0x1EEA3 => array(0x62F), - 0x1EEA5 => array(0x648), 0x1EEA6 => array(0x632), 0x1EEA7 => array(0x62D), - 0x1EEA8 => array(0x637), 0x1EEA9 => array(0x64A), 0x1EEAB => array(0x644), - 0x1EEAC => array(0x645), 0x1EEAD => array(0x646), 0x1EEAE => array(0x633), - 0x1EEAF => array(0x639), 0x1EEB0 => array(0x641), 0x1EEB1 => array(0x635), - 0x1EEB2 => array(0x642), 0x1EEB3 => array(0x631), 0x1EEB4 => array(0x634), - 0x1EEB5 => array(0x62A), 0x1EEB6 => array(0x62B), 0x1EEB7 => array(0x62E), - 0x1EEB8 => array(0x630), 0x1EEB9 => array(0x636), 0x1EEBA => array(0x638), - 0x1EEBB => array(0x63A), 0x1F12A => array(0x3014, 0x73, 0x3015), 0x1F12B => array(0x63), - 0x1F12C => array(0x72), 0x1F12D => array(0x63, 0x64), 0x1F12E => array(0x77, 0x7A), - 0x1F130 => array(0x61), 0x1F131 => array(0x62), 0x1F132 => array(0x63), - 0x1F133 => array(0x64), 0x1F134 => array(0x65), 0x1F135 => array(0x66), - 0x1F136 => array(0x67), 0x1F137 => array(0x68), 0x1F138 => array(0x69), - 0x1F139 => array(0x6A), 0x1F13A => array(0x6B), 0x1F13B => array(0x6C), - 0x1F13C => array(0x6D), 0x1F13D => array(0x6E), 0x1F13E => array(0x6F), - 0x1F13F => array(0x70), 0x1F140 => array(0x71), 0x1F141 => array(0x72), - 0x1F142 => array(0x73), 0x1F143 => array(0x74), 0x1F144 => array(0x75), - 0x1F145 => array(0x76), 0x1F146 => array(0x77), 0x1F147 => array(0x78), - 0x1F148 => array(0x79), 0x1F149 => array(0x7A), 0x1F14A => array(0x68, 0x76), - 0x1F14B => array(0x6D, 0x76), 0x1F14C => array(0x73, 0x64), 0x1F14D => array(0x73, 0x73), - 0x1F14E => array(0x70, 0x70, 0x76), 0x1F14F => array(0x77, 0x63), 0x1F16A => array(0x6D, 0x63), - 0x1F16B => array(0x6D, 0x64), 0x1F190 => array(0x64, 0x6A), 0x1F200 => array(0x307B, 0x304B), - 0x1F201 => array(0x30B3, 0x30B3), 0x1F202 => array(0x30B5), 0x1F210 => array(0x624B), - 0x1F211 => array(0x5B57), 0x1F212 => array(0x53CC), 0x1F213 => array(0x30C7), - 0x1F214 => array(0x4E8C), 0x1F215 => array(0x591A), 0x1F216 => array(0x89E3), - 0x1F217 => array(0x5929), 0x1F218 => array(0x4EA4), 0x1F219 => array(0x6620), - 0x1F21A => array(0x7121), 0x1F21B => array(0x6599), 0x1F21C => array(0x524D), - 0x1F21D => array(0x5F8C), 0x1F21E => array(0x518D), 0x1F21F => array(0x65B0), - 0x1F220 => array(0x521D), 0x1F221 => array(0x7D42), 0x1F222 => array(0x751F), - 0x1F223 => array(0x8CA9), 0x1F224 => array(0x58F0), 0x1F225 => array(0x5439), - 0x1F226 => array(0x6F14), 0x1F227 => array(0x6295), 0x1F228 => array(0x6355), - 0x1F229 => array(0x4E00), 0x1F22A => array(0x4E09), 0x1F22B => array(0x904A), - 0x1F22C => array(0x5DE6), 0x1F22D => array(0x4E2D), 0x1F22E => array(0x53F3), - 0x1F22F => array(0x6307), 0x1F230 => array(0x8D70), 0x1F231 => array(0x6253), - 0x1F232 => array(0x7981), 0x1F233 => array(0x7A7A), 0x1F234 => array(0x5408), - 0x1F235 => array(0x6E80), 0x1F236 => array(0x6709), 0x1F237 => array(0x6708), - 0x1F238 => array(0x7533), 0x1F239 => array(0x5272), 0x1F23A => array(0x55B6), - 0x1F240 => array(0x3014, 0x672C, 0x3015), 0x1F241 => array(0x3014, 0x4E09, 0x3015), 0x1F242 => array(0x3014, 0x4E8C, 0x3015), - 0x1F243 => array(0x3014, 0x5B89, 0x3015), 0x1F244 => array(0x3014, 0x70B9, 0x3015), 0x1F245 => array(0x3014, 0x6253, 0x3015), - 0x1F246 => array(0x3014, 0x76D7, 0x3015), 0x1F247 => array(0x3014, 0x52DD, 0x3015), 0x1F248 => array(0x3014, 0x6557, 0x3015), - 0x1F250 => array(0x5F97), 0x1F251 => array(0x53EF), 0x2F800 => array(0x4E3D), - 0x2F801 => array(0x4E38), 0x2F802 => array(0x4E41), 0x2F803 => array(0x20122), - 0x2F804 => array(0x4F60), 0x2F805 => array(0x4FAE), 0x2F806 => array(0x4FBB), - 0x2F807 => array(0x5002), 0x2F808 => array(0x507A), 0x2F809 => array(0x5099), - 0x2F80A => array(0x50E7), 0x2F80B => array(0x50CF), 0x2F80C => array(0x349E), - 0x2F80D => array(0x2063A), 0x2F80E => array(0x514D), 0x2F80F => array(0x5154), - 0x2F810 => array(0x5164), 0x2F811 => array(0x5177), 0x2F812 => array(0x2051C), - 0x2F813 => array(0x34B9), 0x2F814 => array(0x5167), 0x2F815 => array(0x518D), - 0x2F816 => array(0x2054B), 0x2F817 => array(0x5197), 0x2F818 => array(0x51A4), - 0x2F819 => array(0x4ECC), 0x2F81A => array(0x51AC), 0x2F81B => array(0x51B5), - 0x2F81C => array(0x291DF), 0x2F81D => array(0x51F5), 0x2F81E => array(0x5203), - 0x2F81F => array(0x34DF), 0x2F820 => array(0x523B), 0x2F821 => array(0x5246), - 0x2F822 => array(0x5272), 0x2F823 => array(0x5277), 0x2F824 => array(0x3515), - 0x2F825 => array(0x52C7), 0x2F826 => array(0x52C9), 0x2F827 => array(0x52E4), - 0x2F828 => array(0x52FA), 0x2F829 => array(0x5305), 0x2F82A => array(0x5306), - 0x2F82B => array(0x5317), 0x2F82C => array(0x5349), 0x2F82D => array(0x5351), - 0x2F82E => array(0x535A), 0x2F82F => array(0x5373), 0x2F830 => array(0x537D), - 0x2F831 => array(0x537F), 0x2F832 => array(0x537F), 0x2F833 => array(0x537F), - 0x2F834 => array(0x20A2C), 0x2F835 => array(0x7070), 0x2F836 => array(0x53CA), - 0x2F837 => array(0x53DF), 0x2F838 => array(0x20B63), 0x2F839 => array(0x53EB), - 0x2F83A => array(0x53F1), 0x2F83B => array(0x5406), 0x2F83C => array(0x549E), - 0x2F83D => array(0x5438), 0x2F83E => array(0x5448), 0x2F83F => array(0x5468), - 0x2F840 => array(0x54A2), 0x2F841 => array(0x54F6), 0x2F842 => array(0x5510), - 0x2F843 => array(0x5553), 0x2F844 => array(0x5563), 0x2F845 => array(0x5584), - 0x2F846 => array(0x5584), 0x2F847 => array(0x5599), 0x2F848 => array(0x55AB), - 0x2F849 => array(0x55B3), 0x2F84A => array(0x55C2), 0x2F84B => array(0x5716), - 0x2F84C => array(0x5606), 0x2F84D => array(0x5717), 0x2F84E => array(0x5651), - 0x2F84F => array(0x5674), 0x2F850 => array(0x5207), 0x2F851 => array(0x58EE), - 0x2F852 => array(0x57CE), 0x2F853 => array(0x57F4), 0x2F854 => array(0x580D), - 0x2F855 => array(0x578B), 0x2F856 => array(0x5832), 0x2F857 => array(0x5831), - 0x2F858 => array(0x58AC), 0x2F859 => array(0x214E4), 0x2F85A => array(0x58F2), - 0x2F85B => array(0x58F7), 0x2F85C => array(0x5906), 0x2F85D => array(0x591A), - 0x2F85E => array(0x5922), 0x2F85F => array(0x5962), 0x2F860 => array(0x216A8), - 0x2F861 => array(0x216EA), 0x2F862 => array(0x59EC), 0x2F863 => array(0x5A1B), - 0x2F864 => array(0x5A27), 0x2F865 => array(0x59D8), 0x2F866 => array(0x5A66), - 0x2F867 => array(0x36EE), 0x2F869 => array(0x5B08), 0x2F86A => array(0x5B3E), - 0x2F86B => array(0x5B3E), 0x2F86C => array(0x219C8), 0x2F86D => array(0x5BC3), - 0x2F86E => array(0x5BD8), 0x2F86F => array(0x5BE7), 0x2F870 => array(0x5BF3), - 0x2F871 => array(0x21B18), 0x2F872 => array(0x5BFF), 0x2F873 => array(0x5C06), - 0x2F875 => array(0x5C22), 0x2F876 => array(0x3781), 0x2F877 => array(0x5C60), - 0x2F878 => array(0x5C6E), 0x2F879 => array(0x5CC0), 0x2F87A => array(0x5C8D), - 0x2F87B => array(0x21DE4), 0x2F87C => array(0x5D43), 0x2F87D => array(0x21DE6), - 0x2F87E => array(0x5D6E), 0x2F87F => array(0x5D6B), 0x2F880 => array(0x5D7C), - 0x2F881 => array(0x5DE1), 0x2F882 => array(0x5DE2), 0x2F883 => array(0x382F), - 0x2F884 => array(0x5DFD), 0x2F885 => array(0x5E28), 0x2F886 => array(0x5E3D), - 0x2F887 => array(0x5E69), 0x2F888 => array(0x3862), 0x2F889 => array(0x22183), - 0x2F88A => array(0x387C), 0x2F88B => array(0x5EB0), 0x2F88C => array(0x5EB3), - 0x2F88D => array(0x5EB6), 0x2F88E => array(0x5ECA), 0x2F88F => array(0x2A392), - 0x2F890 => array(0x5EFE), 0x2F891 => array(0x22331), 0x2F892 => array(0x22331), - 0x2F893 => array(0x8201), 0x2F894 => array(0x5F22), 0x2F895 => array(0x5F22), - 0x2F896 => array(0x38C7), 0x2F897 => array(0x232B8), 0x2F898 => array(0x261DA), - 0x2F899 => array(0x5F62), 0x2F89A => array(0x5F6B), 0x2F89B => array(0x38E3), - 0x2F89C => array(0x5F9A), 0x2F89D => array(0x5FCD), 0x2F89E => array(0x5FD7), - 0x2F89F => array(0x5FF9), 0x2F8A0 => array(0x6081), 0x2F8A1 => array(0x393A), - 0x2F8A2 => array(0x391C), 0x2F8A3 => array(0x6094), 0x2F8A4 => array(0x226D4), - 0x2F8A5 => array(0x60C7), 0x2F8A6 => array(0x6148), 0x2F8A7 => array(0x614C), - 0x2F8A8 => array(0x614E), 0x2F8A9 => array(0x614C), 0x2F8AA => array(0x617A), - 0x2F8AB => array(0x618E), 0x2F8AC => array(0x61B2), 0x2F8AD => array(0x61A4), - 0x2F8AE => array(0x61AF), 0x2F8AF => array(0x61DE), 0x2F8B0 => array(0x61F2), - 0x2F8B1 => array(0x61F6), 0x2F8B2 => array(0x6210), 0x2F8B3 => array(0x621B), - 0x2F8B4 => array(0x625D), 0x2F8B5 => array(0x62B1), 0x2F8B6 => array(0x62D4), - 0x2F8B7 => array(0x6350), 0x2F8B8 => array(0x22B0C), 0x2F8B9 => array(0x633D), - 0x2F8BA => array(0x62FC), 0x2F8BB => array(0x6368), 0x2F8BC => array(0x6383), - 0x2F8BD => array(0x63E4), 0x2F8BE => array(0x22BF1), 0x2F8BF => array(0x6422), - 0x2F8C0 => array(0x63C5), 0x2F8C1 => array(0x63A9), 0x2F8C2 => array(0x3A2E), - 0x2F8C3 => array(0x6469), 0x2F8C4 => array(0x647E), 0x2F8C5 => array(0x649D), - 0x2F8C6 => array(0x6477), 0x2F8C7 => array(0x3A6C), 0x2F8C8 => array(0x654F), - 0x2F8C9 => array(0x656C), 0x2F8CA => array(0x2300A), 0x2F8CB => array(0x65E3), - 0x2F8CC => array(0x66F8), 0x2F8CD => array(0x6649), 0x2F8CE => array(0x3B19), - 0x2F8CF => array(0x6691), 0x2F8D0 => array(0x3B08), 0x2F8D1 => array(0x3AE4), - 0x2F8D2 => array(0x5192), 0x2F8D3 => array(0x5195), 0x2F8D4 => array(0x6700), - 0x2F8D5 => array(0x669C), 0x2F8D6 => array(0x80AD), 0x2F8D7 => array(0x43D9), - 0x2F8D8 => array(0x6717), 0x2F8D9 => array(0x671B), 0x2F8DA => array(0x6721), - 0x2F8DB => array(0x675E), 0x2F8DC => array(0x6753), 0x2F8DD => array(0x233C3), - 0x2F8DE => array(0x3B49), 0x2F8DF => array(0x67FA), 0x2F8E0 => array(0x6785), - 0x2F8E1 => array(0x6852), 0x2F8E2 => array(0x6885), 0x2F8E3 => array(0x2346D), - 0x2F8E4 => array(0x688E), 0x2F8E5 => array(0x681F), 0x2F8E6 => array(0x6914), - 0x2F8E7 => array(0x3B9D), 0x2F8E8 => array(0x6942), 0x2F8E9 => array(0x69A3), - 0x2F8EA => array(0x69EA), 0x2F8EB => array(0x6AA8), 0x2F8EC => array(0x236A3), - 0x2F8ED => array(0x6ADB), 0x2F8EE => array(0x3C18), 0x2F8EF => array(0x6B21), - 0x2F8F0 => array(0x238A7), 0x2F8F1 => array(0x6B54), 0x2F8F2 => array(0x3C4E), - 0x2F8F3 => array(0x6B72), 0x2F8F4 => array(0x6B9F), 0x2F8F5 => array(0x6BBA), - 0x2F8F6 => array(0x6BBB), 0x2F8F7 => array(0x23A8D), 0x2F8F8 => array(0x21D0B), - 0x2F8F9 => array(0x23AFA), 0x2F8FA => array(0x6C4E), 0x2F8FB => array(0x23CBC), - 0x2F8FC => array(0x6CBF), 0x2F8FD => array(0x6CCD), 0x2F8FE => array(0x6C67), - 0x2F8FF => array(0x6D16), 0x2F900 => array(0x6D3E), 0x2F901 => array(0x6D77), - 0x2F902 => array(0x6D41), 0x2F903 => array(0x6D69), 0x2F904 => array(0x6D78), - 0x2F905 => array(0x6D85), 0x2F906 => array(0x23D1E), 0x2F907 => array(0x6D34), - 0x2F908 => array(0x6E2F), 0x2F909 => array(0x6E6E), 0x2F90A => array(0x3D33), - 0x2F90B => array(0x6ECB), 0x2F90C => array(0x6EC7), 0x2F90D => array(0x23ED1), - 0x2F90E => array(0x6DF9), 0x2F90F => array(0x6F6E), 0x2F910 => array(0x23F5E), - 0x2F911 => array(0x23F8E), 0x2F912 => array(0x6FC6), 0x2F913 => array(0x7039), - 0x2F914 => array(0x701E), 0x2F915 => array(0x701B), 0x2F916 => array(0x3D96), - 0x2F917 => array(0x704A), 0x2F918 => array(0x707D), 0x2F919 => array(0x7077), - 0x2F91A => array(0x70AD), 0x2F91B => array(0x20525), 0x2F91C => array(0x7145), - 0x2F91D => array(0x24263), 0x2F91E => array(0x719C), 0x2F920 => array(0x7228), - 0x2F921 => array(0x7235), 0x2F922 => array(0x7250), 0x2F923 => array(0x24608), - 0x2F924 => array(0x7280), 0x2F925 => array(0x7295), 0x2F926 => array(0x24735), - 0x2F927 => array(0x24814), 0x2F928 => array(0x737A), 0x2F929 => array(0x738B), - 0x2F92A => array(0x3EAC), 0x2F92B => array(0x73A5), 0x2F92C => array(0x3EB8), - 0x2F92D => array(0x3EB8), 0x2F92E => array(0x7447), 0x2F92F => array(0x745C), - 0x2F930 => array(0x7471), 0x2F931 => array(0x7485), 0x2F932 => array(0x74CA), - 0x2F933 => array(0x3F1B), 0x2F934 => array(0x7524), 0x2F935 => array(0x24C36), - 0x2F936 => array(0x753E), 0x2F937 => array(0x24C92), 0x2F938 => array(0x7570), - 0x2F939 => array(0x2219F), 0x2F93A => array(0x7610), 0x2F93B => array(0x24FA1), - 0x2F93C => array(0x24FB8), 0x2F93D => array(0x25044), 0x2F93E => array(0x3FFC), - 0x2F93F => array(0x4008), 0x2F940 => array(0x76F4), 0x2F941 => array(0x250F3), - 0x2F942 => array(0x250F2), 0x2F943 => array(0x25119), 0x2F944 => array(0x25133), - 0x2F945 => array(0x771E), 0x2F946 => array(0x771F), 0x2F947 => array(0x771F), - 0x2F948 => array(0x774A), 0x2F949 => array(0x4039), 0x2F94A => array(0x778B), - 0x2F94B => array(0x4046), 0x2F94C => array(0x4096), 0x2F94D => array(0x2541D), - 0x2F94E => array(0x784E), 0x2F94F => array(0x788C), 0x2F950 => array(0x78CC), - 0x2F951 => array(0x40E3), 0x2F952 => array(0x25626), 0x2F953 => array(0x7956), - 0x2F954 => array(0x2569A), 0x2F955 => array(0x256C5), 0x2F956 => array(0x798F), - 0x2F957 => array(0x79EB), 0x2F958 => array(0x412F), 0x2F959 => array(0x7A40), - 0x2F95A => array(0x7A4A), 0x2F95B => array(0x7A4F), 0x2F95C => array(0x2597C), - 0x2F95D => array(0x25AA7), 0x2F95E => array(0x25AA7), 0x2F960 => array(0x4202), - 0x2F961 => array(0x25BAB), 0x2F962 => array(0x7BC6), 0x2F963 => array(0x7BC9), - 0x2F964 => array(0x4227), 0x2F965 => array(0x25C80), 0x2F966 => array(0x7CD2), - 0x2F967 => array(0x42A0), 0x2F968 => array(0x7CE8), 0x2F969 => array(0x7CE3), - 0x2F96A => array(0x7D00), 0x2F96B => array(0x25F86), 0x2F96C => array(0x7D63), - 0x2F96D => array(0x4301), 0x2F96E => array(0x7DC7), 0x2F96F => array(0x7E02), - 0x2F970 => array(0x7E45), 0x2F971 => array(0x4334), 0x2F972 => array(0x26228), - 0x2F973 => array(0x26247), 0x2F974 => array(0x4359), 0x2F975 => array(0x262D9), - 0x2F976 => array(0x7F7A), 0x2F977 => array(0x2633E), 0x2F978 => array(0x7F95), - 0x2F979 => array(0x7FFA), 0x2F97A => array(0x8005), 0x2F97B => array(0x264DA), - 0x2F97C => array(0x26523), 0x2F97D => array(0x8060), 0x2F97E => array(0x265A8), - 0x2F97F => array(0x8070), 0x2F980 => array(0x2335F), 0x2F981 => array(0x43D5), - 0x2F982 => array(0x80B2), 0x2F983 => array(0x8103), 0x2F984 => array(0x440B), - 0x2F985 => array(0x813E), 0x2F986 => array(0x5AB5), 0x2F987 => array(0x267A7), - 0x2F988 => array(0x267B5), 0x2F989 => array(0x23393), 0x2F98A => array(0x2339C), - 0x2F98B => array(0x8201), 0x2F98C => array(0x8204), 0x2F98D => array(0x8F9E), - 0x2F98E => array(0x446B), 0x2F98F => array(0x8291), 0x2F990 => array(0x828B), - 0x2F991 => array(0x829D), 0x2F992 => array(0x52B3), 0x2F993 => array(0x82B1), - 0x2F994 => array(0x82B3), 0x2F995 => array(0x82BD), 0x2F996 => array(0x82E6), - 0x2F997 => array(0x26B3C), 0x2F998 => array(0x82E5), 0x2F999 => array(0x831D), - 0x2F99A => array(0x8363), 0x2F99B => array(0x83AD), 0x2F99C => array(0x8323), - 0x2F99D => array(0x83BD), 0x2F99E => array(0x83E7), 0x2F99F => array(0x8457), - 0x2F9A0 => array(0x8353), 0x2F9A1 => array(0x83CA), 0x2F9A2 => array(0x83CC), - 0x2F9A3 => array(0x83DC), 0x2F9A4 => array(0x26C36), 0x2F9A5 => array(0x26D6B), - 0x2F9A6 => array(0x26CD5), 0x2F9A7 => array(0x452B), 0x2F9A8 => array(0x84F1), - 0x2F9A9 => array(0x84F3), 0x2F9AA => array(0x8516), 0x2F9AB => array(0x273CA), - 0x2F9AC => array(0x8564), 0x2F9AD => array(0x26F2C), 0x2F9AE => array(0x455D), - 0x2F9AF => array(0x4561), 0x2F9B0 => array(0x26FB1), 0x2F9B1 => array(0x270D2), - 0x2F9B2 => array(0x456B), 0x2F9B3 => array(0x8650), 0x2F9B4 => array(0x865C), - 0x2F9B5 => array(0x8667), 0x2F9B6 => array(0x8669), 0x2F9B7 => array(0x86A9), - 0x2F9B8 => array(0x8688), 0x2F9B9 => array(0x870E), 0x2F9BA => array(0x86E2), - 0x2F9BB => array(0x8779), 0x2F9BC => array(0x8728), 0x2F9BD => array(0x876B), - 0x2F9BE => array(0x8786), 0x2F9C0 => array(0x87E1), 0x2F9C1 => array(0x8801), - 0x2F9C2 => array(0x45F9), 0x2F9C3 => array(0x8860), 0x2F9C4 => array(0x8863), - 0x2F9C5 => array(0x27667), 0x2F9C6 => array(0x88D7), 0x2F9C7 => array(0x88DE), - 0x2F9C8 => array(0x4635), 0x2F9C9 => array(0x88FA), 0x2F9CA => array(0x34BB), - 0x2F9CB => array(0x278AE), 0x2F9CC => array(0x27966), 0x2F9CD => array(0x46BE), - 0x2F9CE => array(0x46C7), 0x2F9CF => array(0x8AA0), 0x2F9D0 => array(0x8AED), - 0x2F9D1 => array(0x8B8A), 0x2F9D2 => array(0x8C55), 0x2F9D3 => array(0x27CA8), - 0x2F9D4 => array(0x8CAB), 0x2F9D5 => array(0x8CC1), 0x2F9D6 => array(0x8D1B), - 0x2F9D7 => array(0x8D77), 0x2F9D8 => array(0x27F2F), 0x2F9D9 => array(0x20804), - 0x2F9DA => array(0x8DCB), 0x2F9DB => array(0x8DBC), 0x2F9DC => array(0x8DF0), - 0x2F9DD => array(0x208DE), 0x2F9DE => array(0x8ED4), 0x2F9DF => array(0x8F38), - 0x2F9E0 => array(0x285D2), 0x2F9E1 => array(0x285ED), 0x2F9E2 => array(0x9094), - 0x2F9E3 => array(0x90F1), 0x2F9E4 => array(0x9111), 0x2F9E5 => array(0x2872E), - 0x2F9E6 => array(0x911B), 0x2F9E7 => array(0x9238), 0x2F9E8 => array(0x92D7), - 0x2F9E9 => array(0x92D8), 0x2F9EA => array(0x927C), 0x2F9EB => array(0x93F9), - 0x2F9EC => array(0x9415), 0x2F9ED => array(0x28BFA), 0x2F9EE => array(0x958B), - 0x2F9EF => array(0x4995), 0x2F9F0 => array(0x95B7), 0x2F9F1 => array(0x28D77), - 0x2F9F2 => array(0x49E6), 0x2F9F3 => array(0x96C3), 0x2F9F4 => array(0x5DB2), - 0x2F9F5 => array(0x9723), 0x2F9F6 => array(0x29145), 0x2F9F7 => array(0x2921A), - 0x2F9F8 => array(0x4A6E), 0x2F9F9 => array(0x4A76), 0x2F9FA => array(0x97E0), - 0x2F9FB => array(0x2940A), 0x2F9FC => array(0x4AB2), 0x2F9FD => array(0x29496), - 0x2F9FE => array(0x980B), 0x2F9FF => array(0x980B), 0x2FA00 => array(0x9829), - 0x2FA01 => array(0x295B6), 0x2FA02 => array(0x98E2), 0x2FA03 => array(0x4B33), - 0x2FA04 => array(0x9929), 0x2FA05 => array(0x99A7), 0x2FA06 => array(0x99C2), - 0x2FA07 => array(0x99FE), 0x2FA08 => array(0x4BCE), 0x2FA09 => array(0x29B30), - 0x2FA0A => array(0x9B12), 0x2FA0B => array(0x9C40), 0x2FA0C => array(0x9CFD), - 0x2FA0D => array(0x4CCE), 0x2FA0E => array(0x4CED), 0x2FA0F => array(0x9D67), - 0x2FA10 => array(0x2A0CE), 0x2FA11 => array(0x4CF8), 0x2FA12 => array(0x2A105), - 0x2FA13 => array(0x2A20E), 0x2FA14 => array(0x2A291), 0x2FA15 => array(0x9EBB), - 0x2FA16 => array(0x4D56), 0x2FA17 => array(0x9EF9), 0x2FA18 => array(0x9EFE), - 0x2FA19 => array(0x9F05), 0x2FA1A => array(0x9F0F), 0x2FA1B => array(0x9F16), - 0x2FA1C => array(0x9F3B), 0x2FA1D => array(0x2A600) - ), - 'norm_combcls' => array(0x334 => 1, 0x335 => 1, 0x336 => 1, 0x337 => 1, - 0x338 => 1, 0x93C => 7, 0x9BC => 7, 0xA3C => 7, 0xABC => 7, - 0xB3C => 7, 0xCBC => 7, 0x1037 => 7, 0x3099 => 8, 0x309A => 8, - 0x94D => 9, 0x9CD => 9, 0xA4D => 9, 0xACD => 9, 0xB4D => 9, - 0xBCD => 9, 0xC4D => 9, 0xCCD => 9, 0xD4D => 9, 0xDCA => 9, - 0xE3A => 9, 0xF84 => 9, 0x1039 => 9, 0x1714 => 9, 0x1734 => 9, - 0x17D2 => 9, 0x5B0 => 10, 0x5B1 => 11, 0x5B2 => 12, 0x5B3 => 13, - 0x5B4 => 14, 0x5B5 => 15, 0x5B6 => 16, 0x5B7 => 17, 0x5B8 => 18, - 0x5B9 => 19, 0x5BB => 20, 0x5Bc => 21, 0x5BD => 22, 0x5BF => 23, - 0x5C1 => 24, 0x5C2 => 25, 0xFB1E => 26, 0x64B => 27, 0x64C => 28, - 0x64D => 29, 0x64E => 30, 0x64F => 31, 0x650 => 32, 0x651 => 33, - 0x652 => 34, 0x670 => 35, 0x711 => 36, 0xC55 => 84, 0xC56 => 91, - 0xE38 => 103, 0xE39 => 103, 0xE48 => 107, 0xE49 => 107, 0xE4A => 107, - 0xE4B => 107, 0xEB8 => 118, 0xEB9 => 118, 0xEC8 => 122, 0xEC9 => 122, - 0xECA => 122, 0xECB => 122, 0xF71 => 129, 0xF72 => 130, 0xF7A => 130, - 0xF7B => 130, 0xF7C => 130, 0xF7D => 130, 0xF80 => 130, 0xF74 => 132, - 0x321 => 202, 0x322 => 202, 0x327 => 202, 0x328 => 202, 0x31B => 216, - 0xF39 => 216, 0x1D165 => 216, 0x1D166 => 216, 0x1D16E => 216, 0x1D16F => 216, - 0x1D170 => 216, 0x1D171 => 216, 0x1D172 => 216, 0x302A => 218, 0x316 => 220, - 0x317 => 220, 0x318 => 220, 0x319 => 220, 0x31C => 220, 0x31D => 220, - 0x31E => 220, 0x31F => 220, 0x320 => 220, 0x323 => 220, 0x324 => 220, - 0x325 => 220, 0x326 => 220, 0x329 => 220, 0x32A => 220, 0x32B => 220, - 0x32C => 220, 0x32D => 220, 0x32E => 220, 0x32F => 220, 0x330 => 220, - 0x331 => 220, 0x332 => 220, 0x333 => 220, 0x339 => 220, 0x33A => 220, - 0x33B => 220, 0x33C => 220, 0x347 => 220, 0x348 => 220, 0x349 => 220, - 0x34D => 220, 0x34E => 220, 0x353 => 220, 0x354 => 220, 0x355 => 220, - 0x356 => 220, 0x591 => 220, 0x596 => 220, 0x59B => 220, 0x5A3 => 220, - 0x5A4 => 220, 0x5A5 => 220, 0x5A6 => 220, 0x5A7 => 220, 0x5AA => 220, - 0x655 => 220, 0x656 => 220, 0x6E3 => 220, 0x6EA => 220, 0x6ED => 220, - 0x731 => 220, 0x734 => 220, 0x737 => 220, 0x738 => 220, 0x739 => 220, - 0x73B => 220, 0x73C => 220, 0x73E => 220, 0x742 => 220, 0x744 => 220, - 0x746 => 220, 0x748 => 220, 0x952 => 220, 0xF18 => 220, 0xF19 => 220, - 0xF35 => 220, 0xF37 => 220, 0xFC6 => 220, 0x193B => 220, 0x20E8 => 220, - 0x1D17B => 220, 0x1D17C => 220, 0x1D17D => 220, 0x1D17E => 220, 0x1D17F => 220, - 0x1D180 => 220, 0x1D181 => 220, 0x1D182 => 220, 0x1D18A => 220, 0x1D18B => 220, - 0x59A => 222, 0x5AD => 222, 0x1929 => 222, 0x302D => 222, 0x302E => 224, - 0x302F => 224, 0x1D16D => 226, 0x5AE => 228, 0x18A9 => 228, 0x302B => 228, - 0x300 => 230, 0x301 => 230, 0x302 => 230, 0x303 => 230, 0x304 => 230, - 0x305 => 230, 0x306 => 230, 0x307 => 230, 0x308 => 230, 0x309 => 230, - 0x30A => 230, 0x30B => 230, 0x30C => 230, 0x30D => 230, 0x30E => 230, - 0x30F => 230, 0x310 => 230, 0x311 => 230, 0x312 => 230, 0x313 => 230, - 0x314 => 230, 0x33D => 230, 0x33E => 230, 0x33F => 230, 0x340 => 230, - 0x341 => 230, 0x342 => 230, 0x343 => 230, 0x344 => 230, 0x346 => 230, - 0x34A => 230, 0x34B => 230, 0x34C => 230, 0x350 => 230, 0x351 => 230, - 0x352 => 230, 0x357 => 230, 0x363 => 230, 0x364 => 230, 0x365 => 230, - 0x366 => 230, 0x367 => 230, 0x368 => 230, 0x369 => 230, 0x36A => 230, - 0x36B => 230, 0x36C => 230, 0x36D => 230, 0x36E => 230, 0x36F => 230, - 0x483 => 230, 0x484 => 230, 0x485 => 230, 0x486 => 230, 0x592 => 230, - 0x593 => 230, 0x594 => 230, 0x595 => 230, 0x597 => 230, 0x598 => 230, - 0x599 => 230, 0x59C => 230, 0x59D => 230, 0x59E => 230, 0x59F => 230, - 0x5A0 => 230, 0x5A1 => 230, 0x5A8 => 230, 0x5A9 => 230, 0x5AB => 230, - 0x5AC => 230, 0x5AF => 230, 0x5C4 => 230, 0x610 => 230, 0x611 => 230, - 0x612 => 230, 0x613 => 230, 0x614 => 230, 0x615 => 230, 0x653 => 230, - 0x654 => 230, 0x657 => 230, 0x658 => 230, 0x6D6 => 230, 0x6D7 => 230, - 0x6D8 => 230, 0x6D9 => 230, 0x6DA => 230, 0x6DB => 230, 0x6DC => 230, - 0x6DF => 230, 0x6E0 => 230, 0x6E1 => 230, 0x6E2 => 230, 0x6E4 => 230, - 0x6E7 => 230, 0x6E8 => 230, 0x6EB => 230, 0x6EC => 230, 0x730 => 230, - 0x732 => 230, 0x733 => 230, 0x735 => 230, 0x736 => 230, 0x73A => 230, - 0x73D => 230, 0x73F => 230, 0x740 => 230, 0x741 => 230, 0x743 => 230, - 0x745 => 230, 0x747 => 230, 0x749 => 230, 0x74A => 230, 0x951 => 230, - 0x953 => 230, 0x954 => 230, 0xF82 => 230, 0xF83 => 230, 0xF86 => 230, - 0xF87 => 230, 0x170D => 230, 0x193A => 230, 0x20D0 => 230, 0x20D1 => 230, - 0x20D4 => 230, 0x20D5 => 230, 0x20D6 => 230, 0x20D7 => 230, 0x20DB => 230, - 0x20DC => 230, 0x20E1 => 230, 0x20E7 => 230, 0x20E9 => 230, 0xFE20 => 230, - 0xFE21 => 230, 0xFE22 => 230, 0xFE23 => 230, 0x1D185 => 230, 0x1D186 => 230, - 0x1D187 => 230, 0x1D189 => 230, 0x1D188 => 230, 0x1D1AA => 230, 0x1D1AB => 230, - 0x1D1AC => 230, 0x1D1AD => 230, 0x315 => 232, 0x31A => 232, 0x302C => 232, - 0x35F => 233, 0x362 => 233, 0x35D => 234, 0x35E => 234, 0x360 => 234, - 0x361 => 234, 0x345 => 240 - ) - ); -} diff --git a/lib/classes/idna/class.idna_convert_wrapper.php b/lib/classes/idna/class.idna_convert_wrapper.php index 696f098f..efba7fb1 100644 --- a/lib/classes/idna/class.idna_convert_wrapper.php +++ b/lib/classes/idna/class.idna_convert_wrapper.php @@ -17,11 +17,12 @@ * */ +// Source for updates: https://github.com/phlylabs/idna-convert.git + /** * Class for wrapping a specific idna conversion class and offering a standard interface * @package Functions */ - class idna_convert_wrapper { /** @@ -37,7 +38,11 @@ class idna_convert_wrapper public function __construct() { - $this->idna_converter = new idna_convert(); + // Instantiate it + //$this->idna_converter = new idna_convert(array('idn_version' => '2008', 'encode_german_sz' => false)); + + // use this when using new version of IdnaConverter (which does not work yet) + $this->idna_converter = new Mso\IdnaConvert\IdnaConvert(); } /** @@ -52,7 +57,9 @@ class idna_convert_wrapper public function encode($to_encode) { - return $this->_do_action('encode', $to_encode); + $to_encode = $this->is_utf8($to_encode) ? $to_encode : utf8_encode($to_encode); + return $this->idna_converter->encode($to_encode); + //return $this->_do_action('encode', $to_encode); } /** @@ -67,7 +74,45 @@ class idna_convert_wrapper public function decode($to_decode) { - return $this->_do_action('decode', $to_decode); + return $this->idna_converter->decode($to_decode); + //return $this->_do_action('decode', $to_decode); + } + + /** + * check whether a string is utf-8 encoded or not + * + * @param string $string + * + * @return boolean + */ + public function is_utf8($string = null) { + + if (function_exists("mb_detect_encoding")) { + if (mb_detect_encoding($string, 'UTF-8, ISO-8859-1') === 'UTF-8') { + return true; + } + return false; + } + $strlen = strlen($string); + for ($i = 0; $i < $strlen; $i ++) { + $ord = ord($string[$i]); + if ($ord < 0x80) + continue; // 0bbbbbbb + elseif (($ord & 0xE0) === 0xC0 && $ord > 0xC1) + $n = 1; // 110bbbbb (exkl C0-C1) + elseif (($ord & 0xF0) === 0xE0) + $n = 2; // 1110bbbb + elseif (($ord & 0xF8) === 0xF0 && $ord < 0xF5) + $n = 3; // 11110bbb (exkl F5-FF) + else + return false; // ungültiges UTF-8-Zeichen + for ($c = 0; $c < $n; $c ++) // $n Folgebytes? // 10bbbbbb + if (++ $i === $strlen || (ord($string[$i]) & 0xC0) !== 0x80) + // ungültiges UTF-8-Zeichen + return false; + } + // kein ungültiges UTF-8-Zeichen gefunden + return true; } /** @@ -141,5 +186,3 @@ class idna_convert_wrapper return implode($sepchar, $strings); } } - -?> diff --git a/lib/classes/idna/ext/EncodingHelper.php b/lib/classes/idna/ext/EncodingHelper.php new file mode 100644 index 00000000..41b50209 --- /dev/null +++ b/lib/classes/idna/ext/EncodingHelper.php @@ -0,0 +1,186 @@ + + * @copyright 2003-2016 phlyLabs Berlin, http://phlylabs.de + * @version 1.0.0 2016-01-08 + */ + +namespace Mso\IdnaConvert; + +class EncodingHelper +{ + /** + * Convert a string from any of various encodings to UTF-8 + * + * @param string $string String to encode + *[@param string $encoding Encoding; Default: ISO-8859-1] + *[@param bool $safe_mode Safe Mode: if set to TRUE, the original string is retunred on errors] + * @return string|false The encoded string or false on failure + * @since 0.0.1 + */ + public static function toUtf8($string = '', $encoding = 'iso-8859-1', $safe_mode = false) + { + $safe = ($safe_mode) ? $string : false; + if (strtoupper($encoding) == 'UTF-8' || strtoupper($encoding) == 'UTF8') { + + return $string; + } + if (strtoupper($encoding) == 'ISO-8859-1') { + + return \utf8_encode($string); + + } if (strtoupper($encoding) == 'WINDOWS-1252') { + + return \utf8_encode(self::map_w1252_iso8859_1($string)); + } + + if (strtoupper($encoding) == 'UNICODE-1-1-UTF-7') { + $encoding = 'utf-7'; + } + if (function_exists('mb_convert_encoding')) { + $conv = @mb_convert_encoding($string, 'UTF-8', strtoupper($encoding)); + if ($conv) { + + return $conv; + } + } + if (function_exists('iconv')) { + $conv = @iconv(strtoupper($encoding), 'UTF-8', $string); + if ($conv) { + + return $conv; + } + } + if (function_exists('libiconv')) { + $conv = @libiconv(strtoupper($encoding), 'UTF-8', $string); + if ($conv) { + + return $conv; + } + } + + return $safe; + } + + /** + * Convert a string from UTF-8 to any of various encodings + * + * @param string $string String to decode + *[@param string $encoding Encoding; Default: ISO-8859-1] + *[@param bool $safe_mode Safe Mode: if set to TRUE, the original string is retunred on errors] + * @return string|false The decoded string or false on failure + * @since 0.0.1 + */ + public static function fromUtf8($string = '', $encoding = 'iso-8859-1', $safe_mode = false) + { + $safe = ($safe_mode) ? $string : false; + if (!$encoding) $encoding = 'ISO-8859-1'; + if (strtoupper($encoding) == 'UTF-8' || strtoupper($encoding) == 'UTF8') { + + return $string; + } + if (strtoupper($encoding) == 'ISO-8859-1') { + + return utf8_decode($string); + } + if (strtoupper($encoding) == 'WINDOWS-1252') { + + return self::map_iso8859_1_w1252(utf8_decode($string)); + } + + if (strtoupper($encoding) == 'UNICODE-1-1-UTF-7') { + $encoding = 'utf-7'; + } + if (function_exists('mb_convert_encoding')) { + $conv = @mb_convert_encoding($string, strtoupper($encoding), 'UTF-8'); + if ($conv) { + + return $conv; + } + } + if (function_exists('iconv')) { + $conv = @iconv('UTF-8', strtoupper($encoding), $string); + if ($conv) { + + return $conv; + } + } + if (function_exists('libiconv')) { + $conv = @libiconv('UTF-8', strtoupper($encoding), $string); + if ($conv) { + + return $conv; + } + } + + return $safe; + } + + /** + * Special treatment for our guys in Redmond + * Windows-1252 is basically ISO-8859-1 -- with some exceptions, which get accounted for here + * + * @param string $string Your input in Win1252 + * @return string The resulting ISO-8859-1 string + * @since 0.0.1 + */ + protected static function map_w1252_iso8859_1($string = '') + { + if ($string == '') { + + return ''; + } + $return = ''; + + for ($i = 0; $i < strlen($string); ++$i) { + $c = ord($string{$i}); + switch ($c) { + case 129: $return .= chr(252); break; + case 132: $return .= chr(228); break; + case 142: $return .= chr(196); break; + case 148: $return .= chr(246); break; + case 153: $return .= chr(214); break; + case 154: $return .= chr(220); break; + case 225: $return .= chr(223); break; + default: $return .= chr($c); + } + } + + return $return; + } + + /** + * Special treatment for our guys in Redmond + * Windows-1252 is basically ISO-8859-1 -- with some exceptions, which get accounted for here + * + * @param string $string Your input in ISO-8859-1 + * @return string The resulting Win1252 string + * @since 0.0.1 + */ + protected static function map_iso8859_1_w1252($string = '') + { + if ($string == '') { + return ''; + } + + $return = ''; + for ($i = 0; $i < strlen($string); ++$i) { + $c = ord($string{$i}); + switch ($c) { + case 196: $return .= chr(142); break; + case 214: $return .= chr(153); break; + case 220: $return .= chr(154); break; + case 223: $return .= chr(225); break; + case 228: $return .= chr(132); break; + case 246: $return .= chr(148); break; + case 252: $return .= chr(129); break; + default: $return .= chr($c); + } + } + + return $return; + } +} diff --git a/lib/classes/idna/ext/IdnaConvert.php b/lib/classes/idna/ext/IdnaConvert.php new file mode 100644 index 00000000..868ac2a3 --- /dev/null +++ b/lib/classes/idna/ext/IdnaConvert.php @@ -0,0 +1,409 @@ + + * @copyright 2004-2016 phlyLabs Berlin, http://phlylabs.de + * @version 1.0.1-dev 2016-01-12 + */ + +namespace Mso\IdnaConvert; + +class IdnaConvert { + + const Version = '1.0.2'; + const SubVersion = 'main'; + + // Internal settings, do not touch! + const PunycodePrefix = 'xn--'; + + protected $encoding = 'utf8'; // Default input charset is UTF-8 + protected $strictMode = false; // Behave strict or not + protected $idnVersion = '2008'; // Can be either 2003 (old) or 2008 (default) + + protected $NamePrepData = null; + protected $UnicodeTranscoder = null; + + /** + * the constructor + * + * @param array|null $params Parameters to control the class' behaviour + * @since 0.5.2 + */ + public function __construct($params = null) + { + $this->UnicodeTranscoder = new UnicodeTranscoder(); + + // Kept for backwarsds compatibility. Consider using the setter methods instead. + if (!empty($params) && is_array($params)) { + if (isset($params['encoding'])) { + $this->setEncoding($params['encoding']); + } + + if (isset($params['idn_version'])) { + $this->setIdnVersion($params['idn_version']); + } + + if (isset($params['strict_mode'])) { + $this->setStrictMode($params['strict_mode']); + } + } + + $this->setIdnVersion($this->idnVersion); + } + + public function getClassVersion() + { + return self::Version.'-'.self::SubVersion; + } + + /** + * @return string + */ + public function getEncoding() + { + return $this->encoding; + } + + /** + * @param string $encoding + */ + public function setEncoding($encoding) + { + switch ($encoding) { + case 'utf8': + case 'ucs4_string': + case 'ucs4_array': + $this->encoding = $encoding; + break; + default: + throw new \InvalidArgumentException(sprintf('Invalid encoding %s', $encoding)); + } + } + + /** + * @return boolean + */ + public function isStrictMode() + { + return $this->strictMode; + } + + /** + * @param boolean $strictMode + */ + public function setStrictMode($strictMode) + { + $this->strictMode = ($strictMode) ? true : false; + } + + /** + * @return int + */ + public function getIdnVersion() + { + return $this->idnVersion; + } + + /** + * @param int $idnVersion + */ + public function setIdnVersion($idnVersion) + { + if (in_array($idnVersion, ['2003', '2008'])) { + if (is_null($this->NamePrepData) || $idnVersion != $this->idnVersion) { + $this->NamePrepData = null; // Ought to destroy the object's reference + // Re-instantiate with different data set + $this->NamePrepData = ($idnVersion == 2003) + ? new NamePrepData2003() + : new NamePrepData(); + } + + $this->idnVersion = $idnVersion; + + } else { + throw new \InvalidArgumentException(sprintf('Invalid IDN version %d', $idnVersion)); + } + } + + /** + * Decode a given ACE domain name + * @param string $input Domain name (ACE string) + * [@param string $one_time_encoding Desired output encoding] + * @return string Decoded Domain name (UTF-8 or UCS-4) + */ + public function decode($input, $one_time_encoding = null) + { + $punyCode = $this->punycodeFactory(); + + // Optionally set + if ($one_time_encoding) { + switch ($one_time_encoding) { + case 'utf8': + case 'ucs4_string': + case 'ucs4_array': + break; + default: + throw new \InvalidArgumentException(sprintf('Invalid encoding %s', $one_time_encoding)); + } + } + // Make sure to drop any newline characters around + $input = trim($input); + + // Negotiate input and try to determine, whether it is a plain string, + // an email address or something like a complete URL + if (strpos($input, '@')) { // Maybe it is an email address + // No no in strict mode + if ($this->strictMode) { + throw new \InvalidArgumentException('Only individual domain name parts can be handled in strict mode'); + } + list ($email_pref, $input) = explode('@', $input, 2); + $arr = explode('.', $input); + foreach ($arr as $k => $v) { + if (preg_match('!^' . preg_quote(self::PunycodePrefix, '!') . '!', $v)) { + $conv = $punyCode->decode($v); + if ($conv) { + $arr[$k] = $conv; + } + } + } + $input = join('.', $arr); + $arr = explode('.', $email_pref); + foreach ($arr as $k => $v) { + if (preg_match('!^' . preg_quote(self::PunycodePrefix, '!') . '!', $v)) { + $conv = $punyCode->decode($v); + if ($conv) { + $arr[$k] = $conv; + } + } + } + $email_pref = join('.', $arr); + $return = $email_pref . '@' . $input; + } elseif (preg_match('![:\./]!', $input)) { // Or a complete domain name (with or without paths / parameters) + // No no in strict mode + if ($this->strictMode) { + throw new \InvalidArgumentException('Only individual domain name parts can be handled in strict mode'); + } + $parsed = parse_url($input); + if (isset($parsed['host'])) { + $arr = explode('.', $parsed['host']); + foreach ($arr as $k => $v) { + $conv = $punyCode->decode($v); + if ($conv) { + $arr[$k] = $conv; + } + } + $parsed['host'] = join('.', $arr); + $return = (empty($parsed['scheme']) ? '' : $parsed['scheme'] . (strtolower($parsed['scheme']) == 'mailto' ? ':' : '://')). + (empty($parsed['user']) ? '' : $parsed['user'] . (empty($parsed['pass']) ? '' : ':' . $parsed['pass']) . '@'). + $parsed['host']. + (empty($parsed['port']) ? '' : ':' . $parsed['port']). + (empty($parsed['path']) ? '' : $parsed['path']). + (empty($parsed['query']) ? '' : '?' . $parsed['query']). + (empty($parsed['fragment']) ? '' : '#' . $parsed['fragment']); + } else { // parse_url seems to have failed, try without it + $arr = explode('.', $input); + foreach ($arr as $k => $v) { + $conv = $punyCode->decode($v); + $arr[$k] = ($conv) ? $conv : $v; + } + $return = join('.', $arr); + } + } else { // Otherwise we consider it being a pure domain name string + $return = $punyCode->decode($input); + if (!$return) { + $return = $input; + } + } + // The output is UTF-8 by default, other output formats need conversion here + // If one time encoding is given, use this, else the objects property + $outputEncoding = ($one_time_encoding) ? $one_time_encoding : $this->encoding; + switch ($outputEncoding) { + case 'utf8': + return $return; // break; + case 'ucs4_string': + return $this->UnicodeTranscoder->convert($return, 'utf8', 'ucs4'); // break; + case 'ucs4_array': + return $this->UnicodeTranscoder->convert($return, 'utf8', 'ucs4array'); // break; + default: + throw new \InvalidArgumentException(sprintf('Unsupported output encoding %s', $outputEncoding)); + } + } + + /** + * Encode a given UTF-8 domain name + * @param string $decoded Domain name (UTF-8 or UCS-4) + * [@param boolean $one_time_encoding Desired input encoding, see {@link set_parameter}] + * @return string Encoded Domain name (ACE string) + */ + public function encode($decoded, $one_time_encoding = false) + { + // Forcing conversion of input to UCS4 array + // If one time encoding is given, use this, else the objects property + $inputEncoding = $one_time_encoding ? $one_time_encoding : $this->encoding; + switch ($inputEncoding) { + case 'utf8': + $decoded = $this->UnicodeTranscoder->convert($decoded, 'utf8', 'ucs4array'); + break; + case 'ucs4_string': + $decoded = $this->UnicodeTranscoder->convert($decoded, 'ucs4', 'ucs4array'); + break; + case 'ucs4_array': + break; + default: + throw new \InvalidArgumentException(sprintf('Unsupported input encoding %s', $inputEncoding)); + } + + // No input, no output, what else did you expect? + if (empty($decoded)) { + return ''; + } + + $punyCode = $this->punycodeFactory(); + + // Anchors for iteration + $last_begin = 0; + // Output string + $output = ''; + foreach ($decoded as $k => $v) { + // Make sure to use just the plain dot + switch ($v) { + case 0x3002: + case 0xFF0E: + case 0xFF61: + $decoded[$k] = 0x2E; + // Right, no break here, the above are converted to dots anyway + // Stumbling across an anchoring character + case 0x2E: + case 0x2F: + case 0x3A: + case 0x3F: + case 0x40: + // Neither email addresses nor URLs allowed in strict mode + if ($this->strictMode) { + throw new \InvalidArgumentException('Neither email addresses nor URLs are allowed in strict mode.'); + } else { + // Skip first char + if ($k) { + $encoded = $punyCode->encode(array_slice($decoded, $last_begin, (($k) - $last_begin))); + if ($encoded) { + $output .= $encoded; + } else { + $output .= $this->UnicodeTranscoder->convert(array_slice($decoded, $last_begin, (($k) - $last_begin)), 'ucs4array', 'utf8'); + } + $output .= chr($decoded[$k]); + } + $last_begin = $k + 1; + } + } + } + // Catch the rest of the string + if ($last_begin) { + $inp_len = sizeof($decoded); + $encoded = $punyCode->encode(array_slice($decoded, $last_begin, (($inp_len) - $last_begin))); + if ($encoded) { + $output .= $encoded; + } else { + $output .= $this->UnicodeTranscoder->convert(array_slice($decoded, $last_begin, (($inp_len) - $last_begin)), 'ucs4array', 'utf8'); + } + return $output; + } else { + if (false !== ($output = $punyCode->encode($decoded))) { + return $output; + } else { + return $this->UnicodeTranscoder->convert($decoded, 'ucs4array', 'utf8'); + } + } + } + + /** + * Mitigates a weakness of encode(), which cannot properly handle URIs but instead encodes their + * path or query components, too. + * @param string $uri Expects the URI as a UTF-8 (or ASCII) string + * @return string The URI encoded to Punycode, everything but the host component is left alone + * @since 0.6.4 + */ + public function encodeUri($uri) + { + $parsed = parse_url($uri); + if (!isset($parsed['host'])) { + throw new \InvalidArgumentException('The given string does not look like a URI'); + } + $arr = explode('.', $parsed['host']); + foreach ($arr as $k => $v) { + $conv = $this->encode($v, 'utf8'); + if ($conv) { + $arr[$k] = $conv; + } + } + $parsed['host'] = join('.', $arr); + $return = (empty($parsed['scheme']) ? '' : $parsed['scheme'] . (strtolower($parsed['scheme']) == 'mailto' ? ':' : '://')). + (empty($parsed['user']) ? '' : $parsed['user'] . (empty($parsed['pass']) ? '' : ':' . $parsed['pass']) . '@'). + $parsed['host']. + (empty($parsed['port']) ? '' : ':' . $parsed['port']). + (empty($parsed['path']) ? '' : $parsed['path']). + (empty($parsed['query']) ? '' : '?' . $parsed['query']). + (empty($parsed['fragment']) ? '' : '#' . $parsed['fragment']); + return $return; + } + + /** + * The actual punycode class is rather costly, as well as passing the huge nameprep database around. + * This factory method allows to ease the burden when dealing with multiple IDN versions. + * + * @return \Mso\IdnaConvert\Punycode + */ + protected function punycodeFactory() + { + static $instances = []; + + if (!isset($instances[$this->idnVersion])) { + $instances[$this->idnVersion] = new Punycode($this->NamePrepData, $this->UnicodeTranscoder); + } + return $instances[$this->idnVersion]; + } + +} diff --git a/lib/classes/idna/ext/NamePrepData.php b/lib/classes/idna/ext/NamePrepData.php new file mode 100644 index 00000000..2de76af8 --- /dev/null +++ b/lib/classes/idna/ext/NamePrepData.php @@ -0,0 +1,1921 @@ + [0x61], 0x42 => [0x62], 0x43 => [0x63], + 0x44 => [0x64], 0x45 => [0x65], 0x46 => [0x66], + 0x47 => [0x67], 0x48 => [0x68], 0x49 => [0x69], + 0x4A => [0x6A], 0x4B => [0x6B], 0x4C => [0x6C], + 0x4D => [0x6D], 0x4E => [0x6E], 0x4F => [0x6F], + 0x50 => [0x70], 0x51 => [0x71], 0x52 => [0x72], + 0x53 => [0x73], 0x54 => [0x74], 0x55 => [0x75], + 0x56 => [0x76], 0x57 => [0x77], 0x58 => [0x78], + 0x59 => [0x79], 0x5A => [0x7A], 0xAA => [0x61], + 0xB2 => [0x32], 0xB3 => [0x33], 0xB5 => [0x3BC], + 0xB9 => [0x31], 0xBA => [0x6F], 0xBC => [0x31, 0x2044, 0x34], + 0xBD => [0x31, 0x2044, 0x32], 0xBE => [0x33, 0x2044, 0x34], 0xC0 => [0xE0], + 0xC1 => [0xE1], 0xC2 => [0xE2], 0xC3 => [0xE3], + 0xC4 => [0xE4], 0xC5 => [0xE5], 0xC6 => [0xE6], + 0xC7 => [0xE7], 0xC8 => [0xE8], 0xC9 => [0xE9], + 0xCA => [0xEA], 0xCB => [0xEB], 0xCC => [0xEC], + 0xCD => [0xED], 0xCE => [0xEE], 0xCF => [0xEF], + 0xD0 => [0xF0], 0xD1 => [0xF1], 0xD2 => [0xF2], + 0xD3 => [0xF3], 0xD4 => [0xF4], 0xD5 => [0xF5], + 0xD6 => [0xF6], 0xD8 => [0xF8], 0xD9 => [0xF9], + 0xDA => [0xFA], 0xDB => [0xFB], 0xDC => [0xFC], + 0xDD => [0xFD], 0xDE => [0xFE], 0x100 => [0x101], + 0x102 => [0x103], 0x104 => [0x105], 0x106 => [0x107], + 0x108 => [0x109], 0x10A => [0x10B], 0x10C => [0x10D], + 0x10E => [0x10F], 0x110 => [0x111], 0x112 => [0x113], + 0x114 => [0x115], 0x116 => [0x117], 0x118 => [0x119], + 0x11A => [0x11B], 0x11C => [0x11D], 0x11E => [0x11F], + 0x120 => [0x121], 0x122 => [0x123], 0x124 => [0x125], + 0x126 => [0x127], 0x128 => [0x129], 0x12A => [0x12B], + 0x12C => [0x12D], 0x12E => [0x12F], 0x130 => [0x69, 0x307], + 0x132 => [0x69, 0x6A], 0x133 => [0x69, 0x6A], 0x134 => [0x135], + 0x136 => [0x137], 0x139 => [0x13A], 0x13B => [0x13C], + 0x13D => [0x13E], 0x13F => [0x6C, 0xB7], 0x140 => [0x6C, 0xB7], + 0x141 => [0x142], 0x143 => [0x144], 0x145 => [0x146], + 0x147 => [0x148], 0x149 => [0x2BC, 0x6E], 0x14A => [0x14B], + 0x14C => [0x14D], 0x14E => [0x14F], 0x150 => [0x151], + 0x152 => [0x153], 0x154 => [0x155], 0x156 => [0x157], + 0x158 => [0x159], 0x15A => [0x15B], 0x15C => [0x15D], + 0x15E => [0x15F], 0x160 => [0x161], 0x162 => [0x163], + 0x164 => [0x165], 0x166 => [0x167], 0x168 => [0x169], + 0x16A => [0x16B], 0x16C => [0x16D], 0x16E => [0x16F], + 0x170 => [0x171], 0x172 => [0x173], 0x174 => [0x175], + 0x176 => [0x177], 0x178 => [0xFF], 0x179 => [0x17A], + 0x17B => [0x17C], 0x17D => [0x17E], 0x17F => [0x73], + 0x181 => [0x253], 0x182 => [0x183], 0x184 => [0x185], + 0x186 => [0x254], 0x187 => [0x188], 0x189 => [0x256], + 0x18A => [0x257], 0x18B => [0x18C], 0x18E => [0x1DD], + 0x18F => [0x259], 0x190 => [0x25B], 0x191 => [0x192], + 0x193 => [0x260], 0x194 => [0x263], 0x196 => [0x269], + 0x197 => [0x268], 0x198 => [0x199], 0x19C => [0x26F], + 0x19D => [0x272], 0x19F => [0x275], 0x1A0 => [0x1A1], + 0x1A2 => [0x1A3], 0x1A4 => [0x1A5], 0x1A6 => [0x280], + 0x1A7 => [0x1A8], 0x1A9 => [0x283], 0x1AC => [0x1AD], + 0x1AE => [0x288], 0x1AF => [0x1B0], 0x1B1 => [0x28A], + 0x1B2 => [0x28B], 0x1B3 => [0x1B4], 0x1B5 => [0x1B6], + 0x1B7 => [0x292], 0x1B8 => [0x1B9], 0x1BC => [0x1BD], + 0x1C4 => [0x64, 0x17E], 0x1C5 => [0x64, 0x17E], 0x1C6 => [0x64, 0x17E], + 0x1C7 => [0x6C, 0x6A], 0x1C8 => [0x6C, 0x6A], 0x1C9 => [0x6C, 0x6A], + 0x1CA => [0x6E, 0x6A], 0x1CB => [0x6E, 0x6A], 0x1CC => [0x6E, 0x6A], + 0x1CD => [0x1CE], 0x1CF => [0x1D0], 0x1D1 => [0x1D2], + 0x1D3 => [0x1D4], 0x1D5 => [0x1D6], 0x1D7 => [0x1D8], + 0x1D9 => [0x1DA], 0x1DB => [0x1DC], 0x1DE => [0x1DF], + 0x1E0 => [0x1E1], 0x1E2 => [0x1E3], 0x1E4 => [0x1E5], + 0x1E6 => [0x1E7], 0x1E8 => [0x1E9], 0x1EA => [0x1EB], + 0x1EC => [0x1ED], 0x1EE => [0x1EF], 0x1F1 => [0x64, 0x7A], + 0x1F2 => [0x64, 0x7A], 0x1F3 => [0x64, 0x7A], 0x1F4 => [0x1F5], + 0x1F6 => [0x195], 0x1F7 => [0x1BF], 0x1F8 => [0x1F9], + 0x1FA => [0x1FB], 0x1FC => [0x1FD], 0x1FE => [0x1FF], + 0x200 => [0x201], 0x202 => [0x203], 0x204 => [0x205], + 0x206 => [0x207], 0x208 => [0x209], 0x20A => [0x20B], + 0x20C => [0x20D], 0x20E => [0x20F], 0x210 => [0x211], + 0x212 => [0x213], 0x214 => [0x215], 0x216 => [0x217], + 0x218 => [0x219], 0x21A => [0x21B], 0x21C => [0x21D], + 0x21E => [0x21F], 0x220 => [0x19E], 0x222 => [0x223], + 0x224 => [0x225], 0x226 => [0x227], 0x228 => [0x229], + 0x22A => [0x22B], 0x22C => [0x22D], 0x22E => [0x22F], + 0x230 => [0x231], 0x232 => [0x233], 0x23A => [0x2C65], + 0x23B => [0x23C], 0x23D => [0x19A], 0x23E => [0x2C66], + 0x241 => [0x242], 0x243 => [0x180], 0x244 => [0x289], + 0x245 => [0x28C], 0x246 => [0x247], 0x248 => [0x249], + 0x24A => [0x24B], 0x24C => [0x24D], 0x24E => [0x24F], + 0x2B0 => [0x68], 0x2B1 => [0x266], 0x2B2 => [0x6A], + 0x2B3 => [0x72], 0x2B4 => [0x279], 0x2B5 => [0x27B], + 0x2B6 => [0x281], 0x2B7 => [0x77], 0x2B8 => [0x79], + 0x2E0 => [0x263], 0x2E1 => [0x6C], 0x2E2 => [0x73], + 0x2E3 => [0x78], 0x2E4 => [0x295], 0x340 => [0x300], + 0x341 => [0x301], 0x343 => [0x313], 0x344 => [0x308, 0x301], + 0x345 => [0x3B9], 0x370 => [0x371], 0x372 => [0x373], + 0x374 => [0x2B9], 0x376 => [0x377], 0x37F => [0x3F3], + 0x386 => [0x3AC], 0x387 => [0xB7], 0x388 => [0x3AD], + 0x389 => [0x3AE], 0x38A => [0x3AF], 0x38C => [0x3CC], + 0x38E => [0x3CD], 0x38F => [0x3CE], 0x391 => [0x3B1], + 0x392 => [0x3B2], 0x393 => [0x3B3], 0x394 => [0x3B4], + 0x395 => [0x3B5], 0x396 => [0x3B6], 0x397 => [0x3B7], + 0x398 => [0x3B8], 0x399 => [0x3B9], 0x39A => [0x3BA], + 0x39B => [0x3BB], 0x39C => [0x3BC], 0x39D => [0x3BD], + 0x39E => [0x3BE], 0x39F => [0x3BF], 0x3A0 => [0x3C0], + 0x3A1 => [0x3C1], 0x3A3 => [0x3C3], 0x3A4 => [0x3C4], + 0x3A5 => [0x3C5], 0x3A6 => [0x3C6], 0x3A7 => [0x3C7], + 0x3A8 => [0x3C8], 0x3A9 => [0x3C9], 0x3AA => [0x3CA], + 0x3AB => [0x3CB], 0x3CF => [0x3D7], 0x3D0 => [0x3B2], + 0x3D1 => [0x3B8], 0x3D2 => [0x3C5], 0x3D3 => [0x3CD], + 0x3D4 => [0x3CB], 0x3D5 => [0x3C6], 0x3D6 => [0x3C0], + 0x3D8 => [0x3D9], 0x3DA => [0x3DB], 0x3DC => [0x3DD], + 0x3DE => [0x3DF], 0x3E0 => [0x3E1], 0x3E2 => [0x3E3], + 0x3E4 => [0x3E5], 0x3E6 => [0x3E7], 0x3E8 => [0x3E9], + 0x3EA => [0x3EB], 0x3EC => [0x3ED], 0x3EE => [0x3EF], + 0x3F0 => [0x3BA], 0x3F1 => [0x3C1], 0x3F2 => [0x3C3], + 0x3F4 => [0x3B8], 0x3F5 => [0x3B5], 0x3F7 => [0x3F8], + 0x3F9 => [0x3C3], 0x3FA => [0x3FB], 0x3FD => [0x37B], + 0x3FE => [0x37C], 0x3FF => [0x37D], 0x400 => [0x450], + 0x401 => [0x451], 0x402 => [0x452], 0x403 => [0x453], + 0x404 => [0x454], 0x405 => [0x455], 0x406 => [0x456], + 0x407 => [0x457], 0x408 => [0x458], 0x409 => [0x459], + 0x40A => [0x45A], 0x40B => [0x45B], 0x40C => [0x45C], + 0x40D => [0x45D], 0x40E => [0x45E], 0x40F => [0x45F], + 0x410 => [0x430], 0x411 => [0x431], 0x412 => [0x432], + 0x413 => [0x433], 0x414 => [0x434], 0x415 => [0x435], + 0x416 => [0x436], 0x417 => [0x437], 0x418 => [0x438], + 0x419 => [0x439], 0x41A => [0x43A], 0x41B => [0x43B], + 0x41C => [0x43C], 0x41D => [0x43D], 0x41E => [0x43E], + 0x41F => [0x43F], 0x420 => [0x440], 0x421 => [0x441], + 0x422 => [0x442], 0x423 => [0x443], 0x424 => [0x444], + 0x425 => [0x445], 0x426 => [0x446], 0x427 => [0x447], + 0x428 => [0x448], 0x429 => [0x449], 0x42A => [0x44A], + 0x42B => [0x44B], 0x42C => [0x44C], 0x42D => [0x44D], + 0x42E => [0x44E], 0x42F => [0x44F], 0x460 => [0x461], + 0x462 => [0x463], 0x464 => [0x465], 0x466 => [0x467], + 0x468 => [0x469], 0x46A => [0x46B], 0x46C => [0x46D], + 0x46E => [0x46F], 0x470 => [0x471], 0x472 => [0x473], + 0x474 => [0x475], 0x476 => [0x477], 0x478 => [0x479], + 0x47A => [0x47B], 0x47C => [0x47D], 0x47E => [0x47F], + 0x480 => [0x481], 0x48A => [0x48B], 0x48C => [0x48D], + 0x48E => [0x48F], 0x490 => [0x491], 0x492 => [0x493], + 0x494 => [0x495], 0x496 => [0x497], 0x498 => [0x499], + 0x49A => [0x49B], 0x49C => [0x49D], 0x49E => [0x49F], + 0x4A0 => [0x4A1], 0x4A2 => [0x4A3], 0x4A4 => [0x4A5], + 0x4A6 => [0x4A7], 0x4A8 => [0x4A9], 0x4AA => [0x4AB], + 0x4AC => [0x4AD], 0x4AE => [0x4AF], 0x4B0 => [0x4B1], + 0x4B2 => [0x4B3], 0x4B4 => [0x4B5], 0x4B6 => [0x4B7], + 0x4B8 => [0x4B9], 0x4BA => [0x4BB], 0x4BC => [0x4BD], + 0x4BE => [0x4BF], 0x4C1 => [0x4C2], 0x4C3 => [0x4C4], + 0x4C5 => [0x4C6], 0x4C7 => [0x4C8], 0x4C9 => [0x4CA], + 0x4CB => [0x4CC], 0x4CD => [0x4CE], 0x4D0 => [0x4D1], + 0x4D2 => [0x4D3], 0x4D4 => [0x4D5], 0x4D6 => [0x4D7], + 0x4D8 => [0x4D9], 0x4DA => [0x4DB], 0x4DC => [0x4DD], + 0x4DE => [0x4DF], 0x4E0 => [0x4E1], 0x4E2 => [0x4E3], + 0x4E4 => [0x4E5], 0x4E6 => [0x4E7], 0x4E8 => [0x4E9], + 0x4EA => [0x4EB], 0x4EC => [0x4ED], 0x4EE => [0x4EF], + 0x4F0 => [0x4F1], 0x4F2 => [0x4F3], 0x4F4 => [0x4F5], + 0x4F6 => [0x4F7], 0x4F8 => [0x4F9], 0x4FA => [0x4FB], + 0x4FC => [0x4FD], 0x4FE => [0x4FF], 0x500 => [0x501], + 0x502 => [0x503], 0x504 => [0x505], 0x506 => [0x507], + 0x508 => [0x509], 0x50A => [0x50B], 0x50C => [0x50D], + 0x50E => [0x50F], 0x510 => [0x511], 0x512 => [0x513], + 0x514 => [0x515], 0x516 => [0x517], 0x518 => [0x519], + 0x51A => [0x51B], 0x51C => [0x51D], 0x51E => [0x51F], + 0x520 => [0x521], 0x522 => [0x523], 0x524 => [0x525], + 0x526 => [0x527], 0x528 => [0x529], 0x52A => [0x52B], + 0x52C => [0x52D], 0x52E => [0x52F], 0x531 => [0x561], + 0x532 => [0x562], 0x533 => [0x563], 0x534 => [0x564], + 0x535 => [0x565], 0x536 => [0x566], 0x537 => [0x567], + 0x538 => [0x568], 0x539 => [0x569], 0x53A => [0x56A], + 0x53B => [0x56B], 0x53C => [0x56C], 0x53D => [0x56D], + 0x53E => [0x56E], 0x53F => [0x56F], 0x540 => [0x570], + 0x541 => [0x571], 0x542 => [0x572], 0x543 => [0x573], + 0x544 => [0x574], 0x545 => [0x575], 0x546 => [0x576], + 0x547 => [0x577], 0x548 => [0x578], 0x549 => [0x579], + 0x54A => [0x57A], 0x54B => [0x57B], 0x54C => [0x57C], + 0x54D => [0x57D], 0x54E => [0x57E], 0x54F => [0x57F], + 0x550 => [0x580], 0x551 => [0x581], 0x552 => [0x582], + 0x553 => [0x583], 0x554 => [0x584], 0x555 => [0x585], + 0x556 => [0x586], 0x587 => [0x565, 0x582], 0x675 => [0x627, 0x674], + 0x676 => [0x648, 0x674], 0x677 => [0x6C7, 0x674], 0x678 => [0x64A, 0x674], + 0x958 => [0x915, 0x93C], 0x959 => [0x916, 0x93C], 0x95A => [0x917, 0x93C], + 0x95B => [0x91C, 0x93C], 0x95C => [0x921, 0x93C], 0x95D => [0x922, 0x93C], + 0x95E => [0x92B, 0x93C], 0x95F => [0x92F, 0x93C], 0x9DC => [0x9A1, 0x9BC], + 0x9DD => [0x9A2, 0x9BC], 0x9DF => [0x9AF, 0x9BC], 0xA33 => [0xA32, 0xA3C], + 0xA36 => [0xA38, 0xA3C], 0xA59 => [0xA16, 0xA3C], 0xA5A => [0xA17, 0xA3C], + 0xA5B => [0xA1C, 0xA3C], 0xA5E => [0xA2B, 0xA3C], 0xB5C => [0xB21, 0xB3C], + 0xB5D => [0xB22, 0xB3C], 0xE33 => [0xE4D, 0xE32], 0xEB3 => [0xECD, 0xEB2], + 0xEDC => [0xEAB, 0xE99], 0xEDD => [0xEAB, 0xEA1], 0xF0C => [0xF0B], + 0xF43 => [0xF42, 0xFB7], 0xF4D => [0xF4C, 0xFB7], 0xF52 => [0xF51, 0xFB7], + 0xF57 => [0xF56, 0xFB7], 0xF5C => [0xF5B, 0xFB7], 0xF69 => [0xF40, 0xFB5], + 0xF73 => [0xF71, 0xF72], 0xF75 => [0xF71, 0xF74], 0xF76 => [0xFB2, 0xF80], + 0xF77 => [0xFB2, 0xF71, 0xF80], 0xF78 => [0xFB3, 0xF80], 0xF79 => [0xFB3, 0xF71, 0xF80], + 0xF81 => [0xF71, 0xF80], 0xF93 => [0xF92, 0xFB7], 0xF9D => [0xF9C, 0xFB7], + 0xFA2 => [0xFA1, 0xFB7], 0xFA7 => [0xFA6, 0xFB7], 0xFAC => [0xFAB, 0xFB7], + 0xFB9 => [0xF90, 0xFB5], 0x10C7 => [0x2D27], 0x10CD => [0x2D2D], + 0x10FC => [0x10DC], 0x1D2C => [0x61], 0x1D2D => [0xE6], + 0x1D2E => [0x62], 0x1D30 => [0x64], 0x1D31 => [0x65], + 0x1D32 => [0x1DD], 0x1D33 => [0x67], 0x1D34 => [0x68], + 0x1D35 => [0x69], 0x1D36 => [0x6A], 0x1D37 => [0x6B], + 0x1D38 => [0x6C], 0x1D39 => [0x6D], 0x1D3A => [0x6E], + 0x1D3C => [0x6F], 0x1D3D => [0x223], 0x1D3E => [0x70], + 0x1D3F => [0x72], 0x1D40 => [0x74], 0x1D41 => [0x75], + 0x1D42 => [0x77], 0x1D43 => [0x61], 0x1D44 => [0x250], + 0x1D45 => [0x251], 0x1D46 => [0x1D02], 0x1D47 => [0x62], + 0x1D48 => [0x64], 0x1D49 => [0x65], 0x1D4A => [0x259], + 0x1D4B => [0x25B], 0x1D4C => [0x25C], 0x1D4D => [0x67], + 0x1D4F => [0x6B], 0x1D50 => [0x6D], 0x1D51 => [0x14B], + 0x1D52 => [0x6F], 0x1D53 => [0x254], 0x1D54 => [0x1D16], + 0x1D55 => [0x1D17], 0x1D56 => [0x70], 0x1D57 => [0x74], + 0x1D58 => [0x75], 0x1D59 => [0x1D1D], 0x1D5A => [0x26F], + 0x1D5B => [0x76], 0x1D5C => [0x1D25], 0x1D5D => [0x3B2], + 0x1D5E => [0x3B3], 0x1D5F => [0x3B4], 0x1D60 => [0x3C6], + 0x1D61 => [0x3C7], 0x1D62 => [0x69], 0x1D63 => [0x72], + 0x1D64 => [0x75], 0x1D65 => [0x76], 0x1D66 => [0x3B2], + 0x1D67 => [0x3B3], 0x1D68 => [0x3C1], 0x1D69 => [0x3C6], + 0x1D6A => [0x3C7], 0x1D78 => [0x43D], 0x1D9B => [0x252], + 0x1D9C => [0x63], 0x1D9D => [0x255], 0x1D9E => [0xF0], + 0x1D9F => [0x25C], 0x1DA0 => [0x66], 0x1DA1 => [0x25F], + 0x1DA2 => [0x261], 0x1DA3 => [0x265], 0x1DA4 => [0x268], + 0x1DA5 => [0x269], 0x1DA6 => [0x26A], 0x1DA7 => [0x1D7B], + 0x1DA8 => [0x29D], 0x1DA9 => [0x26D], 0x1DAA => [0x1D85], + 0x1DAB => [0x29F], 0x1DAC => [0x271], 0x1DAD => [0x270], + 0x1DAE => [0x272], 0x1DAF => [0x273], 0x1DB0 => [0x274], + 0x1DB1 => [0x275], 0x1DB2 => [0x278], 0x1DB3 => [0x282], + 0x1DB4 => [0x283], 0x1DB5 => [0x1AB], 0x1DB6 => [0x289], + 0x1DB7 => [0x28A], 0x1DB8 => [0x1D1C], 0x1DB9 => [0x28B], + 0x1DBA => [0x28C], 0x1DBB => [0x7A], 0x1DBC => [0x290], + 0x1DBD => [0x291], 0x1DBE => [0x292], 0x1DBF => [0x3B8], + 0x1E00 => [0x1E01], 0x1E02 => [0x1E03], 0x1E04 => [0x1E05], + 0x1E06 => [0x1E07], 0x1E08 => [0x1E09], 0x1E0A => [0x1E0B], + 0x1E0C => [0x1E0D], 0x1E0E => [0x1E0F], 0x1E10 => [0x1E11], + 0x1E12 => [0x1E13], 0x1E14 => [0x1E15], 0x1E16 => [0x1E17], + 0x1E18 => [0x1E19], 0x1E1A => [0x1E1B], 0x1E1C => [0x1E1D], + 0x1E1E => [0x1E1F], 0x1E20 => [0x1E21], 0x1E22 => [0x1E23], + 0x1E24 => [0x1E25], 0x1E26 => [0x1E27], 0x1E28 => [0x1E29], + 0x1E2A => [0x1E2B], 0x1E2C => [0x1E2D], 0x1E2E => [0x1E2F], + 0x1E30 => [0x1E31], 0x1E32 => [0x1E33], 0x1E34 => [0x1E35], + 0x1E36 => [0x1E37], 0x1E38 => [0x1E39], 0x1E3A => [0x1E3B], + 0x1E3C => [0x1E3D], 0x1E3E => [0x1E3F], 0x1E40 => [0x1E41], + 0x1E42 => [0x1E43], 0x1E44 => [0x1E45], 0x1E46 => [0x1E47], + 0x1E48 => [0x1E49], 0x1E4A => [0x1E4B], 0x1E4C => [0x1E4D], + 0x1E4E => [0x1E4F], 0x1E50 => [0x1E51], 0x1E52 => [0x1E53], + 0x1E54 => [0x1E55], 0x1E56 => [0x1E57], 0x1E58 => [0x1E59], + 0x1E5A => [0x1E5B], 0x1E5C => [0x1E5D], 0x1E5E => [0x1E5F], + 0x1E60 => [0x1E61], 0x1E62 => [0x1E63], 0x1E64 => [0x1E65], + 0x1E66 => [0x1E67], 0x1E68 => [0x1E69], 0x1E6A => [0x1E6B], + 0x1E6C => [0x1E6D], 0x1E6E => [0x1E6F], 0x1E70 => [0x1E71], + 0x1E72 => [0x1E73], 0x1E74 => [0x1E75], 0x1E76 => [0x1E77], + 0x1E78 => [0x1E79], 0x1E7A => [0x1E7B], 0x1E7C => [0x1E7D], + 0x1E7E => [0x1E7F], 0x1E80 => [0x1E81], 0x1E82 => [0x1E83], + 0x1E84 => [0x1E85], 0x1E86 => [0x1E87], 0x1E88 => [0x1E89], + 0x1E8A => [0x1E8B], 0x1E8C => [0x1E8D], 0x1E8E => [0x1E8F], + 0x1E90 => [0x1E91], 0x1E92 => [0x1E93], 0x1E94 => [0x1E95], + 0x1E9A => [0x61, 0x2BE], 0x1E9B => [0x1E61], 0x1E9E => [0x73, 0x73], + 0x1EA0 => [0x1EA1], 0x1EA2 => [0x1EA3], 0x1EA4 => [0x1EA5], + 0x1EA6 => [0x1EA7], 0x1EA8 => [0x1EA9], 0x1EAA => [0x1EAB], + 0x1EAC => [0x1EAD], 0x1EAE => [0x1EAF], 0x1EB0 => [0x1EB1], + 0x1EB2 => [0x1EB3], 0x1EB4 => [0x1EB5], 0x1EB6 => [0x1EB7], + 0x1EB8 => [0x1EB9], 0x1EBA => [0x1EBB], 0x1EBC => [0x1EBD], + 0x1EBE => [0x1EBF], 0x1EC0 => [0x1EC1], 0x1EC2 => [0x1EC3], + 0x1EC4 => [0x1EC5], 0x1EC6 => [0x1EC7], 0x1EC8 => [0x1EC9], + 0x1ECA => [0x1ECB], 0x1ECC => [0x1ECD], 0x1ECE => [0x1ECF], + 0x1ED0 => [0x1ED1], 0x1ED2 => [0x1ED3], 0x1ED4 => [0x1ED5], + 0x1ED6 => [0x1ED7], 0x1ED8 => [0x1ED9], 0x1EDA => [0x1EDB], + 0x1EDC => [0x1EDD], 0x1EDE => [0x1EDF], 0x1EE0 => [0x1EE1], + 0x1EE2 => [0x1EE3], 0x1EE4 => [0x1EE5], 0x1EE6 => [0x1EE7], + 0x1EE8 => [0x1EE9], 0x1EEA => [0x1EEB], 0x1EEC => [0x1EED], + 0x1EEE => [0x1EEF], 0x1EF0 => [0x1EF1], 0x1EF2 => [0x1EF3], + 0x1EF4 => [0x1EF5], 0x1EF6 => [0x1EF7], 0x1EF8 => [0x1EF9], + 0x1EFA => [0x1EFB], 0x1EFC => [0x1EFD], 0x1EFE => [0x1EFF], + 0x1F08 => [0x1F00], 0x1F09 => [0x1F01], 0x1F0A => [0x1F02], + 0x1F0B => [0x1F03], 0x1F0C => [0x1F04], 0x1F0D => [0x1F05], + 0x1F0E => [0x1F06], 0x1F0F => [0x1F07], 0x1F18 => [0x1F10], + 0x1F19 => [0x1F11], 0x1F1A => [0x1F12], 0x1F1B => [0x1F13], + 0x1F1C => [0x1F14], 0x1F1D => [0x1F15], 0x1F28 => [0x1F20], + 0x1F29 => [0x1F21], 0x1F2A => [0x1F22], 0x1F2B => [0x1F23], + 0x1F2C => [0x1F24], 0x1F2D => [0x1F25], 0x1F2E => [0x1F26], + 0x1F2F => [0x1F27], 0x1F38 => [0x1F30], 0x1F39 => [0x1F31], + 0x1F3A => [0x1F32], 0x1F3B => [0x1F33], 0x1F3C => [0x1F34], + 0x1F3D => [0x1F35], 0x1F3E => [0x1F36], 0x1F3F => [0x1F37], + 0x1F48 => [0x1F40], 0x1F49 => [0x1F41], 0x1F4A => [0x1F42], + 0x1F4B => [0x1F43], 0x1F4C => [0x1F44], 0x1F4D => [0x1F45], + 0x1F59 => [0x1F51], 0x1F5B => [0x1F53], 0x1F5D => [0x1F55], + 0x1F5F => [0x1F57], 0x1F68 => [0x1F60], 0x1F69 => [0x1F61], + 0x1F6A => [0x1F62], 0x1F6B => [0x1F63], 0x1F6C => [0x1F64], + 0x1F6D => [0x1F65], 0x1F6E => [0x1F66], 0x1F6F => [0x1F67], + 0x1F71 => [0x3AC], 0x1F73 => [0x3AD], 0x1F75 => [0x3AE], + 0x1F77 => [0x3AF], 0x1F79 => [0x3CC], 0x1F7B => [0x3CD], + 0x1F7D => [0x3CE], 0x1F80 => [0x1F00, 0x3B9], 0x1F81 => [0x1F01, 0x3B9], + 0x1F82 => [0x1F02, 0x3B9], 0x1F83 => [0x1F03, 0x3B9], 0x1F84 => [0x1F04, 0x3B9], + 0x1F85 => [0x1F05, 0x3B9], 0x1F86 => [0x1F06, 0x3B9], 0x1F87 => [0x1F07, 0x3B9], + 0x1F88 => [0x1F00, 0x3B9], 0x1F89 => [0x1F01, 0x3B9], 0x1F8A => [0x1F02, 0x3B9], + 0x1F8B => [0x1F03, 0x3B9], 0x1F8C => [0x1F04, 0x3B9], 0x1F8D => [0x1F05, 0x3B9], + 0x1F8E => [0x1F06, 0x3B9], 0x1F8F => [0x1F07, 0x3B9], 0x1F90 => [0x1F20, 0x3B9], + 0x1F91 => [0x1F21, 0x3B9], 0x1F92 => [0x1F22, 0x3B9], 0x1F93 => [0x1F23, 0x3B9], + 0x1F94 => [0x1F24, 0x3B9], 0x1F95 => [0x1F25, 0x3B9], 0x1F96 => [0x1F26, 0x3B9], + 0x1F97 => [0x1F27, 0x3B9], 0x1F98 => [0x1F20, 0x3B9], 0x1F99 => [0x1F21, 0x3B9], + 0x1F9A => [0x1F22, 0x3B9], 0x1F9B => [0x1F23, 0x3B9], 0x1F9C => [0x1F24, 0x3B9], + 0x1F9D => [0x1F25, 0x3B9], 0x1F9E => [0x1F26, 0x3B9], 0x1F9F => [0x1F27, 0x3B9], + 0x1FA0 => [0x1F60, 0x3B9], 0x1FA1 => [0x1F61, 0x3B9], 0x1FA2 => [0x1F62, 0x3B9], + 0x1FA3 => [0x1F63, 0x3B9], 0x1FA4 => [0x1F64, 0x3B9], 0x1FA5 => [0x1F65, 0x3B9], + 0x1FA6 => [0x1F66, 0x3B9], 0x1FA7 => [0x1F67, 0x3B9], 0x1FA8 => [0x1F60, 0x3B9], + 0x1FA9 => [0x1F61, 0x3B9], 0x1FAA => [0x1F62, 0x3B9], 0x1FAB => [0x1F63, 0x3B9], + 0x1FAC => [0x1F64, 0x3B9], 0x1FAD => [0x1F65, 0x3B9], 0x1FAE => [0x1F66, 0x3B9], + 0x1FAF => [0x1F67, 0x3B9], 0x1FB2 => [0x1F70, 0x3B9], 0x1FB3 => [0x3B1, 0x3B9], + 0x1FB4 => [0x3AC, 0x3B9], 0x1FB7 => [0x1FB6, 0x3B9], 0x1FB8 => [0x1FB0], + 0x1FB9 => [0x1FB1], 0x1FBA => [0x1F70], 0x1FBB => [0x3AC], + 0x1FBC => [0x3B1, 0x3B9], 0x1FBE => [0x3B9], 0x1FC2 => [0x1F74, 0x3B9], + 0x1FC3 => [0x3B7, 0x3B9], 0x1FC4 => [0x3AE, 0x3B9], 0x1FC7 => [0x1FC6, 0x3B9], + 0x1FC8 => [0x1F72], 0x1FC9 => [0x3AD], 0x1FCA => [0x1F74], + 0x1FCB => [0x3AE], 0x1FCC => [0x3B7, 0x3B9], 0x1FD3 => [0x390], + 0x1FD8 => [0x1FD0], 0x1FD9 => [0x1FD1], 0x1FDA => [0x1F76], + 0x1FDB => [0x3AF], 0x1FE3 => [0x3B0], 0x1FE8 => [0x1FE0], + 0x1FE9 => [0x1FE1], 0x1FEA => [0x1F7A], 0x1FEB => [0x3CD], + 0x1FEC => [0x1FE5], 0x1FF2 => [0x1F7C, 0x3B9], 0x1FF3 => [0x3C9, 0x3B9], + 0x1FF4 => [0x3CE, 0x3B9], 0x1FF7 => [0x1FF6, 0x3B9], 0x1FF8 => [0x1F78], + 0x1FF9 => [0x3CC], 0x1FFA => [0x1F7C], 0x1FFB => [0x3CE], + 0x1FFC => [0x3C9, 0x3B9], 0x2011 => [0x2010], 0x2033 => [0x2032, 0x2032], + 0x2034 => [0x2032, 0x2032, 0x2032], 0x2036 => [0x2035, 0x2035], 0x2037 => [0x2035, 0x2035, 0x2035], + 0x2057 => [0x2032, 0x2032, 0x2032, 0x2032], 0x2070 => [0x30], 0x2071 => [0x69], + 0x2074 => [0x34], 0x2075 => [0x35], 0x2076 => [0x36], + 0x2077 => [0x37], 0x2078 => [0x38], 0x2079 => [0x39], + 0x207B => [0x2212], 0x207F => [0x6E], 0x2080 => [0x30], + 0x2081 => [0x31], 0x2082 => [0x32], 0x2083 => [0x33], + 0x2084 => [0x34], 0x2085 => [0x35], 0x2086 => [0x36], + 0x2087 => [0x37], 0x2088 => [0x38], 0x2089 => [0x39], + 0x208B => [0x2212], 0x2090 => [0x61], 0x2091 => [0x65], + 0x2092 => [0x6F], 0x2093 => [0x78], 0x2094 => [0x259], + 0x2095 => [0x68], 0x2096 => [0x6B], 0x2097 => [0x6C], + 0x2098 => [0x6D], 0x2099 => [0x6E], 0x209A => [0x70], + 0x209B => [0x73], 0x209C => [0x74], 0x20A8 => [0x72, 0x73], + 0x2102 => [0x63], 0x2103 => [0xB0, 0x63], 0x2107 => [0x25B], + 0x2109 => [0xB0, 0x66], 0x210A => [0x67], 0x210B => [0x68], + 0x210C => [0x68], 0x210D => [0x68], 0x210E => [0x68], + 0x210F => [0x127], 0x2110 => [0x69], 0x2111 => [0x69], + 0x2112 => [0x6C], 0x2113 => [0x6C], 0x2115 => [0x6E], + 0x2116 => [0x6E, 0x6F], 0x2119 => [0x70], 0x211A => [0x71], + 0x211B => [0x72], 0x211C => [0x72], 0x211D => [0x72], + 0x2120 => [0x73, 0x6D], 0x2121 => [0x74, 0x65, 0x6C], 0x2122 => [0x74, 0x6D], + 0x2124 => [0x7A], 0x2126 => [0x3C9], 0x2128 => [0x7A], + 0x212A => [0x6B], 0x212B => [0xE5], 0x212C => [0x62], + 0x212D => [0x63], 0x212F => [0x65], 0x2130 => [0x65], + 0x2131 => [0x66], 0x2133 => [0x6D], 0x2134 => [0x6F], + 0x2135 => [0x5D0], 0x2136 => [0x5D1], 0x2137 => [0x5D2], + 0x2138 => [0x5D3], 0x2139 => [0x69], 0x213B => [0x66, 0x61, 0x78], + 0x213C => [0x3C0], 0x213D => [0x3B3], 0x213E => [0x3B3], + 0x213F => [0x3C0], 0x2140 => [0x2211], 0x2145 => [0x64], + 0x2146 => [0x64], 0x2147 => [0x65], 0x2148 => [0x69], + 0x2149 => [0x6A], 0x2150 => [0x31, 0x2044, 0x37], 0x2151 => [0x31, 0x2044, 0x39], + 0x2152 => [0x31, 0x2044, 0x31, 0x30], 0x2153 => [0x31, 0x2044, 0x33], 0x2154 => [0x32, 0x2044, 0x33], + 0x2155 => [0x31, 0x2044, 0x35], 0x2156 => [0x32, 0x2044, 0x35], 0x2157 => [0x33, 0x2044, 0x35], + 0x2158 => [0x34, 0x2044, 0x35], 0x2159 => [0x31, 0x2044, 0x36], 0x215A => [0x35, 0x2044, 0x36], + 0x215B => [0x31, 0x2044, 0x38], 0x215C => [0x33, 0x2044, 0x38], 0x215D => [0x35, 0x2044, 0x38], + 0x215E => [0x37, 0x2044, 0x38], 0x215F => [0x31, 0x2044], 0x2160 => [0x69], + 0x2161 => [0x69, 0x69], 0x2162 => [0x69, 0x69, 0x69], 0x2163 => [0x69, 0x76], + 0x2164 => [0x76], 0x2165 => [0x76, 0x69], 0x2166 => [0x76, 0x69, 0x69], + 0x2167 => [0x76, 0x69, 0x69, 0x69], 0x2168 => [0x69, 0x78], 0x2169 => [0x78], + 0x216A => [0x78, 0x69], 0x216B => [0x78, 0x69, 0x69], 0x216C => [0x6C], + 0x216D => [0x63], 0x216E => [0x64], 0x216F => [0x6D], + 0x2170 => [0x69], 0x2171 => [0x69, 0x69], 0x2172 => [0x69, 0x69, 0x69], + 0x2173 => [0x69, 0x76], 0x2174 => [0x76], 0x2175 => [0x76, 0x69], + 0x2176 => [0x76, 0x69, 0x69], 0x2177 => [0x76, 0x69, 0x69, 0x69], 0x2178 => [0x69, 0x78], + 0x2179 => [0x78], 0x217A => [0x78, 0x69], 0x217B => [0x78, 0x69, 0x69], + 0x217C => [0x6C], 0x217D => [0x63], 0x217E => [0x64], + 0x217F => [0x6D], 0x2189 => [0x30, 0x2044, 0x33], 0x222C => [0x222B, 0x222B], + 0x222D => [0x222B, 0x222B, 0x222B], 0x222F => [0x222E, 0x222E], 0x2230 => [0x222E, 0x222E, 0x222E], + 0x2329 => [0x3008], 0x232A => [0x3009], 0x2460 => [0x31], + 0x2461 => [0x32], 0x2462 => [0x33], 0x2463 => [0x34], + 0x2464 => [0x35], 0x2465 => [0x36], 0x2466 => [0x37], + 0x2467 => [0x38], 0x2468 => [0x39], 0x2469 => [0x31, 0x30], + 0x246A => [0x31, 0x31], 0x246B => [0x31, 0x32], 0x246C => [0x31, 0x33], + 0x246D => [0x31, 0x34], 0x246E => [0x31, 0x35], 0x246F => [0x31, 0x36], + 0x2470 => [0x31, 0x37], 0x2471 => [0x31, 0x38], 0x2472 => [0x31, 0x39], + 0x2473 => [0x32, 0x30], 0x24B6 => [0x61], 0x24B7 => [0x62], + 0x24B8 => [0x63], 0x24B9 => [0x64], 0x24BA => [0x65], + 0x24BB => [0x66], 0x24BC => [0x67], 0x24BD => [0x68], + 0x24BE => [0x69], 0x24BF => [0x6A], 0x24C0 => [0x6B], + 0x24C1 => [0x6C], 0x24C2 => [0x6D], 0x24C3 => [0x6E], + 0x24C4 => [0x6F], 0x24C5 => [0x70], 0x24C6 => [0x71], + 0x24C7 => [0x72], 0x24C8 => [0x73], 0x24C9 => [0x74], + 0x24CA => [0x75], 0x24CB => [0x76], 0x24CC => [0x77], + 0x24CD => [0x78], 0x24CE => [0x79], 0x24CF => [0x7A], + 0x24D0 => [0x61], 0x24D1 => [0x62], 0x24D2 => [0x63], + 0x24D3 => [0x64], 0x24D4 => [0x65], 0x24D5 => [0x66], + 0x24D6 => [0x67], 0x24D7 => [0x68], 0x24D8 => [0x69], + 0x24D9 => [0x6A], 0x24DA => [0x6B], 0x24DB => [0x6C], + 0x24DC => [0x6D], 0x24DD => [0x6E], 0x24DE => [0x6F], + 0x24DF => [0x70], 0x24E0 => [0x71], 0x24E1 => [0x72], + 0x24E2 => [0x73], 0x24E3 => [0x74], 0x24E4 => [0x75], + 0x24E5 => [0x76], 0x24E6 => [0x77], 0x24E7 => [0x78], + 0x24E8 => [0x79], 0x24E9 => [0x7A], 0x24EA => [0x30], + 0x2A0C => [0x222B, 0x222B, 0x222B, 0x222B], 0x2ADC => [0x2ADD, 0x338], 0x2C00 => [0x2C30], + 0x2C01 => [0x2C31], 0x2C02 => [0x2C32], 0x2C03 => [0x2C33], + 0x2C04 => [0x2C34], 0x2C05 => [0x2C35], 0x2C06 => [0x2C36], + 0x2C07 => [0x2C37], 0x2C08 => [0x2C38], 0x2C09 => [0x2C39], + 0x2C0A => [0x2C3A], 0x2C0B => [0x2C3B], 0x2C0C => [0x2C3C], + 0x2C0D => [0x2C3D], 0x2C0E => [0x2C3E], 0x2C0F => [0x2C3F], + 0x2C10 => [0x2C40], 0x2C11 => [0x2C41], 0x2C12 => [0x2C42], + 0x2C13 => [0x2C43], 0x2C14 => [0x2C44], 0x2C15 => [0x2C45], + 0x2C16 => [0x2C46], 0x2C17 => [0x2C47], 0x2C18 => [0x2C48], + 0x2C19 => [0x2C49], 0x2C1A => [0x2C4A], 0x2C1B => [0x2C4B], + 0x2C1C => [0x2C4C], 0x2C1D => [0x2C4D], 0x2C1E => [0x2C4E], + 0x2C1F => [0x2C4F], 0x2C20 => [0x2C50], 0x2C21 => [0x2C51], + 0x2C22 => [0x2C52], 0x2C23 => [0x2C53], 0x2C24 => [0x2C54], + 0x2C25 => [0x2C55], 0x2C26 => [0x2C56], 0x2C27 => [0x2C57], + 0x2C28 => [0x2C58], 0x2C29 => [0x2C59], 0x2C2A => [0x2C5A], + 0x2C2B => [0x2C5B], 0x2C2C => [0x2C5C], 0x2C2D => [0x2C5D], + 0x2C2E => [0x2C5E], 0x2C60 => [0x2C61], 0x2C62 => [0x26B], + 0x2C63 => [0x1D7D], 0x2C64 => [0x27D], 0x2C67 => [0x2C68], + 0x2C69 => [0x2C6A], 0x2C6B => [0x2C6C], 0x2C6D => [0x251], + 0x2C6E => [0x271], 0x2C6F => [0x250], 0x2C70 => [0x252], + 0x2C72 => [0x2C73], 0x2C75 => [0x2C76], 0x2C7C => [0x6A], + 0x2C7D => [0x76], 0x2C7E => [0x23F], 0x2C7F => [0x240], + 0x2C80 => [0x2C81], 0x2C82 => [0x2C83], 0x2C84 => [0x2C85], + 0x2C86 => [0x2C87], 0x2C88 => [0x2C89], 0x2C8A => [0x2C8B], + 0x2C8C => [0x2C8D], 0x2C8E => [0x2C8F], 0x2C90 => [0x2C91], + 0x2C92 => [0x2C93], 0x2C94 => [0x2C95], 0x2C96 => [0x2C97], + 0x2C98 => [0x2C99], 0x2C9A => [0x2C9B], 0x2C9C => [0x2C9D], + 0x2C9E => [0x2C9F], 0x2CA0 => [0x2CA1], 0x2CA2 => [0x2CA3], + 0x2CA4 => [0x2CA5], 0x2CA6 => [0x2CA7], 0x2CA8 => [0x2CA9], + 0x2CAA => [0x2CAB], 0x2CAC => [0x2CAD], 0x2CAE => [0x2CAF], + 0x2CB0 => [0x2CB1], 0x2CB2 => [0x2CB3], 0x2CB4 => [0x2CB5], + 0x2CB6 => [0x2CB7], 0x2CB8 => [0x2CB9], 0x2CBA => [0x2CBB], + 0x2CBC => [0x2CBD], 0x2CBE => [0x2CBF], 0x2CC0 => [0x2CC1], + 0x2CC2 => [0x2CC3], 0x2CC4 => [0x2CC5], 0x2CC6 => [0x2CC7], + 0x2CC8 => [0x2CC9], 0x2CCA => [0x2CCB], 0x2CCC => [0x2CCD], + 0x2CCE => [0x2CCF], 0x2CD0 => [0x2CD1], 0x2CD2 => [0x2CD3], + 0x2CD4 => [0x2CD5], 0x2CD6 => [0x2CD7], 0x2CD8 => [0x2CD9], + 0x2CDA => [0x2CDB], 0x2CDC => [0x2CDD], 0x2CDE => [0x2CDF], + 0x2CE0 => [0x2CE1], 0x2CE2 => [0x2CE3], 0x2CEB => [0x2CEC], + 0x2CED => [0x2CEE], 0x2CF2 => [0x2CF3], 0x2D6F => [0x2D61], + 0x2E9F => [0x6BCD], 0x2EF3 => [0x9F9F], 0x2F00 => [0x4E00], + 0x2F01 => [0x4E28], 0x2F02 => [0x4E36], 0x2F03 => [0x4E3F], + 0x2F04 => [0x4E59], 0x2F05 => [0x4E85], 0x2F06 => [0x4E8C], + 0x2F07 => [0x4EA0], 0x2F08 => [0x4EBA], 0x2F09 => [0x513F], + 0x2F0A => [0x5165], 0x2F0B => [0x516B], 0x2F0C => [0x5182], + 0x2F0D => [0x5196], 0x2F0E => [0x51AB], 0x2F0F => [0x51E0], + 0x2F10 => [0x51F5], 0x2F11 => [0x5200], 0x2F12 => [0x529B], + 0x2F13 => [0x52F9], 0x2F14 => [0x5315], 0x2F15 => [0x531A], + 0x2F16 => [0x5338], 0x2F17 => [0x5341], 0x2F18 => [0x535C], + 0x2F19 => [0x5369], 0x2F1A => [0x5382], 0x2F1B => [0x53B6], + 0x2F1C => [0x53C8], 0x2F1D => [0x53E3], 0x2F1E => [0x56D7], + 0x2F1F => [0x571F], 0x2F20 => [0x58EB], 0x2F21 => [0x5902], + 0x2F22 => [0x590A], 0x2F23 => [0x5915], 0x2F24 => [0x5927], + 0x2F25 => [0x5973], 0x2F26 => [0x5B50], 0x2F27 => [0x5B80], + 0x2F28 => [0x5BF8], 0x2F29 => [0x5C0F], 0x2F2A => [0x5C22], + 0x2F2B => [0x5C38], 0x2F2C => [0x5C6E], 0x2F2D => [0x5C71], + 0x2F2E => [0x5DDB], 0x2F2F => [0x5DE5], 0x2F30 => [0x5DF1], + 0x2F31 => [0x5DFE], 0x2F32 => [0x5E72], 0x2F33 => [0x5E7A], + 0x2F34 => [0x5E7F], 0x2F35 => [0x5EF4], 0x2F36 => [0x5EFE], + 0x2F37 => [0x5F0B], 0x2F38 => [0x5F13], 0x2F39 => [0x5F50], + 0x2F3A => [0x5F61], 0x2F3B => [0x5F73], 0x2F3C => [0x5FC3], + 0x2F3D => [0x6208], 0x2F3E => [0x6236], 0x2F3F => [0x624B], + 0x2F40 => [0x652F], 0x2F41 => [0x6534], 0x2F42 => [0x6587], + 0x2F43 => [0x6597], 0x2F44 => [0x65A4], 0x2F45 => [0x65B9], + 0x2F46 => [0x65E0], 0x2F47 => [0x65E5], 0x2F48 => [0x66F0], + 0x2F49 => [0x6708], 0x2F4A => [0x6728], 0x2F4B => [0x6B20], + 0x2F4C => [0x6B62], 0x2F4D => [0x6B79], 0x2F4E => [0x6BB3], + 0x2F4F => [0x6BCB], 0x2F50 => [0x6BD4], 0x2F51 => [0x6BDB], + 0x2F52 => [0x6C0F], 0x2F53 => [0x6C14], 0x2F54 => [0x6C34], + 0x2F55 => [0x706B], 0x2F56 => [0x722A], 0x2F57 => [0x7236], + 0x2F58 => [0x723B], 0x2F59 => [0x723F], 0x2F5A => [0x7247], + 0x2F5B => [0x7259], 0x2F5C => [0x725B], 0x2F5D => [0x72AC], + 0x2F5E => [0x7384], 0x2F5F => [0x7389], 0x2F60 => [0x74DC], + 0x2F61 => [0x74E6], 0x2F62 => [0x7518], 0x2F63 => [0x751F], + 0x2F64 => [0x7528], 0x2F65 => [0x7530], 0x2F66 => [0x758B], + 0x2F67 => [0x7592], 0x2F68 => [0x7676], 0x2F69 => [0x767D], + 0x2F6A => [0x76AE], 0x2F6B => [0x76BF], 0x2F6C => [0x76EE], + 0x2F6D => [0x77DB], 0x2F6E => [0x77E2], 0x2F6F => [0x77F3], + 0x2F70 => [0x793A], 0x2F71 => [0x79B8], 0x2F72 => [0x79BE], + 0x2F73 => [0x7A74], 0x2F74 => [0x7ACB], 0x2F75 => [0x7AF9], + 0x2F76 => [0x7C73], 0x2F77 => [0x7CF8], 0x2F78 => [0x7F36], + 0x2F79 => [0x7F51], 0x2F7A => [0x7F8A], 0x2F7B => [0x7FBD], + 0x2F7C => [0x8001], 0x2F7D => [0x800C], 0x2F7E => [0x8012], + 0x2F7F => [0x8033], 0x2F80 => [0x807F], 0x2F81 => [0x8089], + 0x2F82 => [0x81E3], 0x2F83 => [0x81EA], 0x2F84 => [0x81F3], + 0x2F85 => [0x81FC], 0x2F86 => [0x820C], 0x2F87 => [0x821B], + 0x2F88 => [0x821F], 0x2F89 => [0x826E], 0x2F8A => [0x8272], + 0x2F8B => [0x8278], 0x2F8C => [0x864D], 0x2F8D => [0x866B], + 0x2F8E => [0x8840], 0x2F8F => [0x884C], 0x2F90 => [0x8863], + 0x2F91 => [0x897E], 0x2F92 => [0x898B], 0x2F93 => [0x89D2], + 0x2F94 => [0x8A00], 0x2F95 => [0x8C37], 0x2F96 => [0x8C46], + 0x2F97 => [0x8C55], 0x2F98 => [0x8C78], 0x2F99 => [0x8C9D], + 0x2F9A => [0x8D64], 0x2F9B => [0x8D70], 0x2F9C => [0x8DB3], + 0x2F9D => [0x8EAB], 0x2F9E => [0x8ECA], 0x2F9F => [0x8F9B], + 0x2FA0 => [0x8FB0], 0x2FA1 => [0x8FB5], 0x2FA2 => [0x9091], + 0x2FA3 => [0x9149], 0x2FA4 => [0x91C6], 0x2FA5 => [0x91CC], + 0x2FA6 => [0x91D1], 0x2FA7 => [0x9577], 0x2FA8 => [0x9580], + 0x2FA9 => [0x961C], 0x2FAA => [0x96B6], 0x2FAB => [0x96B9], + 0x2FAC => [0x96E8], 0x2FAD => [0x9751], 0x2FAE => [0x975E], + 0x2FAF => [0x9762], 0x2FB0 => [0x9769], 0x2FB1 => [0x97CB], + 0x2FB2 => [0x97ED], 0x2FB3 => [0x97F3], 0x2FB4 => [0x9801], + 0x2FB5 => [0x98A8], 0x2FB6 => [0x98DB], 0x2FB7 => [0x98DF], + 0x2FB8 => [0x9996], 0x2FB9 => [0x9999], 0x2FBA => [0x99AC], + 0x2FBB => [0x9AA8], 0x2FBC => [0x9AD8], 0x2FBD => [0x9ADF], + 0x2FBE => [0x9B25], 0x2FBF => [0x9B2F], 0x2FC0 => [0x9B32], + 0x2FC1 => [0x9B3C], 0x2FC2 => [0x9B5A], 0x2FC3 => [0x9CE5], + 0x2FC4 => [0x9E75], 0x2FC5 => [0x9E7F], 0x2FC6 => [0x9EA5], + 0x2FC7 => [0x9EBB], 0x2FC8 => [0x9EC3], 0x2FC9 => [0x9ECD], + 0x2FCA => [0x9ED1], 0x2FCB => [0x9EF9], 0x2FCC => [0x9EFD], + 0x2FCD => [0x9F0E], 0x2FCE => [0x9F13], 0x2FCF => [0x9F20], + 0x2FD0 => [0x9F3B], 0x2FD1 => [0x9F4A], 0x2FD2 => [0x9F52], + 0x2FD3 => [0x9F8D], 0x2FD4 => [0x9F9C], 0x2FD5 => [0x9FA0], + 0x3002 => [0x2E], 0x3036 => [0x3012], 0x3038 => [0x5341], + 0x3039 => [0x5344], 0x303A => [0x5345], 0x309F => [0x3088, 0x308A], + 0x30FF => [0x30B3, 0x30C8], 0x3131 => [0x1100], 0x3132 => [0x1101], + 0x3133 => [0x11AA], 0x3134 => [0x1102], 0x3135 => [0x11AC], + 0x3136 => [0x11AD], 0x3137 => [0x1103], 0x3138 => [0x1104], + 0x3139 => [0x1105], 0x313A => [0x11B0], 0x313B => [0x11B1], + 0x313C => [0x11B2], 0x313D => [0x11B3], 0x313E => [0x11B4], + 0x313F => [0x11B5], 0x3140 => [0x111A], 0x3141 => [0x1106], + 0x3142 => [0x1107], 0x3143 => [0x1108], 0x3144 => [0x1121], + 0x3145 => [0x1109], 0x3146 => [0x110A], 0x3147 => [0x110B], + 0x3148 => [0x110C], 0x3149 => [0x110D], 0x314A => [0x110E], + 0x314B => [0x110F], 0x314C => [0x1110], 0x314D => [0x1111], + 0x314E => [0x1112], 0x314F => [0x1161], 0x3150 => [0x1162], + 0x3151 => [0x1163], 0x3152 => [0x1164], 0x3153 => [0x1165], + 0x3154 => [0x1166], 0x3155 => [0x1167], 0x3156 => [0x1168], + 0x3157 => [0x1169], 0x3158 => [0x116A], 0x3159 => [0x116B], + 0x315A => [0x116C], 0x315B => [0x116D], 0x315C => [0x116E], + 0x315D => [0x116F], 0x315E => [0x1170], 0x315F => [0x1171], + 0x3160 => [0x1172], 0x3161 => [0x1173], 0x3162 => [0x1174], + 0x3163 => [0x1175], 0x3165 => [0x1114], 0x3166 => [0x1115], + 0x3167 => [0x11C7], 0x3168 => [0x11C8], 0x3169 => [0x11CC], + 0x316A => [0x11CE], 0x316B => [0x11D3], 0x316C => [0x11D7], + 0x316D => [0x11D9], 0x316E => [0x111C], 0x316F => [0x11DD], + 0x3170 => [0x11DF], 0x3171 => [0x111D], 0x3172 => [0x111E], + 0x3173 => [0x1120], 0x3174 => [0x1122], 0x3175 => [0x1123], + 0x3176 => [0x1127], 0x3177 => [0x1129], 0x3178 => [0x112B], + 0x3179 => [0x112C], 0x317A => [0x112D], 0x317B => [0x112E], + 0x317C => [0x112F], 0x317D => [0x1132], 0x317E => [0x1136], + 0x317F => [0x1140], 0x3180 => [0x1147], 0x3181 => [0x114C], + 0x3182 => [0x11F1], 0x3183 => [0x11F2], 0x3184 => [0x1157], + 0x3185 => [0x1158], 0x3186 => [0x1159], 0x3187 => [0x1184], + 0x3188 => [0x1185], 0x3189 => [0x1188], 0x318A => [0x1191], + 0x318B => [0x1192], 0x318C => [0x1194], 0x318D => [0x119E], + 0x318E => [0x11A1], 0x3192 => [0x4E00], 0x3193 => [0x4E8C], + 0x3194 => [0x4E09], 0x3195 => [0x56DB], 0x3196 => [0x4E0A], + 0x3197 => [0x4E2D], 0x3198 => [0x4E0B], 0x3199 => [0x7532], + 0x319A => [0x4E59], 0x319B => [0x4E19], 0x319C => [0x4E01], + 0x319D => [0x5929], 0x319E => [0x5730], 0x319F => [0x4EBA], + 0x3244 => [0x554F], 0x3245 => [0x5E7C], 0x3246 => [0x6587], + 0x3247 => [0x7B8F], 0x3250 => [0x70, 0x74, 0x65], 0x3251 => [0x32, 0x31], + 0x3252 => [0x32, 0x32], 0x3253 => [0x32, 0x33], 0x3254 => [0x32, 0x34], + 0x3255 => [0x32, 0x35], 0x3256 => [0x32, 0x36], 0x3257 => [0x32, 0x37], + 0x3258 => [0x32, 0x38], 0x3259 => [0x32, 0x39], 0x325A => [0x33, 0x30], + 0x325B => [0x33, 0x31], 0x325C => [0x33, 0x32], 0x325D => [0x33, 0x33], + 0x325E => [0x33, 0x34], 0x325F => [0x33, 0x35], 0x3260 => [0x1100], + 0x3261 => [0x1102], 0x3262 => [0x1103], 0x3263 => [0x1105], + 0x3264 => [0x1106], 0x3265 => [0x1107], 0x3266 => [0x1109], + 0x3267 => [0x110B], 0x3268 => [0x110C], 0x3269 => [0x110E], + 0x326A => [0x110F], 0x326B => [0x1110], 0x326C => [0x1111], + 0x326D => [0x1112], 0x326E => [0xAC00], 0x326F => [0xB098], + 0x3270 => [0xB2E4], 0x3271 => [0xB77C], 0x3272 => [0xB9C8], + 0x3273 => [0xBC14], 0x3274 => [0xC0AC], 0x3275 => [0xC544], + 0x3276 => [0xC790], 0x3277 => [0xCC28], 0x3278 => [0xCE74], + 0x3279 => [0xD0C0], 0x327A => [0xD30C], 0x327B => [0xD558], + 0x327C => [0xCC38, 0xACE0], 0x327D => [0xC8FC, 0xC758], 0x327E => [0xC6B0], + 0x3280 => [0x4E00], 0x3281 => [0x4E8C], 0x3282 => [0x4E09], + 0x3283 => [0x56DB], 0x3284 => [0x4E94], 0x3285 => [0x516D], + 0x3286 => [0x4E03], 0x3287 => [0x516B], 0x3288 => [0x4E5D], + 0x3289 => [0x5341], 0x328A => [0x6708], 0x328B => [0x706B], + 0x328C => [0x6C34], 0x328D => [0x6728], 0x328E => [0x91D1], + 0x328F => [0x571F], 0x3290 => [0x65E5], 0x3291 => [0x682A], + 0x3292 => [0x6709], 0x3293 => [0x793E], 0x3294 => [0x540D], + 0x3295 => [0x7279], 0x3296 => [0x8CA1], 0x3297 => [0x795D], + 0x3298 => [0x52B4], 0x3299 => [0x79D8], 0x329A => [0x7537], + 0x329B => [0x5973], 0x329C => [0x9069], 0x329D => [0x512A], + 0x329E => [0x5370], 0x329F => [0x6CE8], 0x32A0 => [0x9805], + 0x32A1 => [0x4F11], 0x32A2 => [0x5199], 0x32A3 => [0x6B63], + 0x32A4 => [0x4E0A], 0x32A5 => [0x4E2D], 0x32A6 => [0x4E0B], + 0x32A7 => [0x5DE6], 0x32A8 => [0x53F3], 0x32A9 => [0x533B], + 0x32AA => [0x5B97], 0x32AB => [0x5B66], 0x32AC => [0x76E3], + 0x32AD => [0x4F01], 0x32AE => [0x8CC7], 0x32AF => [0x5354], + 0x32B0 => [0x591C], 0x32B1 => [0x33, 0x36], 0x32B2 => [0x33, 0x37], + 0x32B3 => [0x33, 0x38], 0x32B4 => [0x33, 0x39], 0x32B5 => [0x34, 0x30], + 0x32B6 => [0x34, 0x31], 0x32B7 => [0x34, 0x32], 0x32B8 => [0x34, 0x33], + 0x32B9 => [0x34, 0x34], 0x32BA => [0x34, 0x35], 0x32BB => [0x34, 0x36], + 0x32BC => [0x34, 0x37], 0x32BD => [0x34, 0x38], 0x32BE => [0x34, 0x39], + 0x32BF => [0x35, 0x30], 0x32C0 => [0x31, 0x6708], 0x32C1 => [0x32, 0x6708], + 0x32C2 => [0x33, 0x6708], 0x32C3 => [0x34, 0x6708], 0x32C4 => [0x35, 0x6708], + 0x32C5 => [0x36, 0x6708], 0x32C6 => [0x37, 0x6708], 0x32C7 => [0x38, 0x6708], + 0x32C8 => [0x39, 0x6708], 0x32C9 => [0x31, 0x30, 0x6708], 0x32CA => [0x31, 0x31, 0x6708], + 0x32CB => [0x31, 0x32, 0x6708], 0x32CC => [0x68, 0x67], 0x32CD => [0x65, 0x72, 0x67], + 0x32CE => [0x65, 0x76], 0x32CF => [0x6C, 0x74, 0x64], 0x32D0 => [0x30A2], + 0x32D1 => [0x30A4], 0x32D2 => [0x30A6], 0x32D3 => [0x30A8], + 0x32D4 => [0x30AA], 0x32D5 => [0x30AB], 0x32D6 => [0x30AD], + 0x32D7 => [0x30AF], 0x32D8 => [0x30B1], 0x32D9 => [0x30B3], + 0x32DA => [0x30B5], 0x32DB => [0x30B7], 0x32DC => [0x30B9], + 0x32DD => [0x30BB], 0x32DE => [0x30BD], 0x32DF => [0x30BF], + 0x32E0 => [0x30C1], 0x32E1 => [0x30C4], 0x32E2 => [0x30C6], + 0x32E3 => [0x30C8], 0x32E4 => [0x30CA], 0x32E5 => [0x30CB], + 0x32E6 => [0x30CC], 0x32E7 => [0x30CD], 0x32E8 => [0x30CE], + 0x32E9 => [0x30CF], 0x32EA => [0x30D2], 0x32EB => [0x30D5], + 0x32EC => [0x30D8], 0x32ED => [0x30DB], 0x32EE => [0x30DE], + 0x32EF => [0x30DF], 0x32F0 => [0x30E0], 0x32F1 => [0x30E1], + 0x32F2 => [0x30E2], 0x32F3 => [0x30E4], 0x32F4 => [0x30E6], + 0x32F5 => [0x30E8], 0x32F6 => [0x30E9], 0x32F7 => [0x30EA], + 0x32F8 => [0x30EB], 0x32F9 => [0x30EC], 0x32FA => [0x30ED], + 0x32FB => [0x30EF], 0x32FC => [0x30F0], 0x32FD => [0x30F1], + 0x32FE => [0x30F2], 0x3300 => [0x30A2, 0x30D1, 0x30FC, 0x30C8], 0x3301 => [0x30A2, 0x30EB, 0x30D5, 0x30A1], + 0x3302 => [0x30A2, 0x30F3, 0x30DA, 0x30A2], 0x3303 => [0x30A2, 0x30FC, 0x30EB], 0x3304 => [0x30A4, 0x30CB, 0x30F3, 0x30B0], + 0x3305 => [0x30A4, 0x30F3, 0x30C1], 0x3306 => [0x30A6, 0x30A9, 0x30F3], 0x3307 => [0x30A8, 0x30B9, 0x30AF, 0x30FC, 0x30C9], + 0x3308 => [0x30A8, 0x30FC, 0x30AB, 0x30FC], 0x3309 => [0x30AA, 0x30F3, 0x30B9], 0x330A => [0x30AA, 0x30FC, 0x30E0], + 0x330B => [0x30AB, 0x30A4, 0x30EA], 0x330C => [0x30AB, 0x30E9, 0x30C3, 0x30C8], 0x330D => [0x30AB, 0x30ED, 0x30EA, 0x30FC], + 0x330E => [0x30AC, 0x30ED, 0x30F3], 0x330F => [0x30AC, 0x30F3, 0x30DE], 0x3310 => [0x30AE, 0x30AC], + 0x3311 => [0x30AE, 0x30CB, 0x30FC], 0x3312 => [0x30AD, 0x30E5, 0x30EA, 0x30FC], 0x3313 => [0x30AE, 0x30EB, 0x30C0, 0x30FC], + 0x3314 => [0x30AD, 0x30ED], 0x3315 => [0x30AD, 0x30ED, 0x30B0, 0x30E9, 0x30E0], 0x3316 => [0x30AD, 0x30ED, 0x30E1, 0x30FC, 0x30C8, 0x30EB], + 0x3317 => [0x30AD, 0x30ED, 0x30EF, 0x30C3, 0x30C8], 0x3318 => [0x30B0, 0x30E9, 0x30E0], 0x3319 => [0x30B0, 0x30E9, 0x30E0, 0x30C8, 0x30F3], + 0x331A => [0x30AF, 0x30EB, 0x30BC, 0x30A4, 0x30ED], 0x331B => [0x30AF, 0x30ED, 0x30FC, 0x30CD], 0x331C => [0x30B1, 0x30FC, 0x30B9], + 0x331D => [0x30B3, 0x30EB, 0x30CA], 0x331E => [0x30B3, 0x30FC, 0x30DD], 0x331F => [0x30B5, 0x30A4, 0x30AF, 0x30EB], + 0x3320 => [0x30B5, 0x30F3, 0x30C1, 0x30FC, 0x30E0], 0x3321 => [0x30B7, 0x30EA, 0x30F3, 0x30B0], 0x3322 => [0x30BB, 0x30F3, 0x30C1], + 0x3323 => [0x30BB, 0x30F3, 0x30C8], 0x3324 => [0x30C0, 0x30FC, 0x30B9], 0x3325 => [0x30C7, 0x30B7], + 0x3326 => [0x30C9, 0x30EB], 0x3327 => [0x30C8, 0x30F3], 0x3328 => [0x30CA, 0x30CE], + 0x3329 => [0x30CE, 0x30C3, 0x30C8], 0x332A => [0x30CF, 0x30A4, 0x30C4], 0x332B => [0x30D1, 0x30FC, 0x30BB, 0x30F3, 0x30C8], + 0x332C => [0x30D1, 0x30FC, 0x30C4], 0x332D => [0x30D0, 0x30FC, 0x30EC, 0x30EB], 0x332E => [0x30D4, 0x30A2, 0x30B9, 0x30C8, 0x30EB], + 0x332F => [0x30D4, 0x30AF, 0x30EB], 0x3330 => [0x30D4, 0x30B3], 0x3331 => [0x30D3, 0x30EB], + 0x3332 => [0x30D5, 0x30A1, 0x30E9, 0x30C3, 0x30C9], 0x3333 => [0x30D5, 0x30A3, 0x30FC, 0x30C8], 0x3334 => [0x30D6, 0x30C3, 0x30B7, 0x30A7, 0x30EB], + 0x3335 => [0x30D5, 0x30E9, 0x30F3], 0x3336 => [0x30D8, 0x30AF, 0x30BF, 0x30FC, 0x30EB], 0x3337 => [0x30DA, 0x30BD], + 0x3338 => [0x30DA, 0x30CB, 0x30D2], 0x3339 => [0x30D8, 0x30EB, 0x30C4], 0x333A => [0x30DA, 0x30F3, 0x30B9], + 0x333B => [0x30DA, 0x30FC, 0x30B8], 0x333C => [0x30D9, 0x30FC, 0x30BF], 0x333D => [0x30DD, 0x30A4, 0x30F3, 0x30C8], + 0x333E => [0x30DC, 0x30EB, 0x30C8], 0x333F => [0x30DB, 0x30F3], 0x3340 => [0x30DD, 0x30F3, 0x30C9], + 0x3341 => [0x30DB, 0x30FC, 0x30EB], 0x3342 => [0x30DB, 0x30FC, 0x30F3], 0x3343 => [0x30DE, 0x30A4, 0x30AF, 0x30ED], + 0x3344 => [0x30DE, 0x30A4, 0x30EB], 0x3345 => [0x30DE, 0x30C3, 0x30CF], 0x3346 => [0x30DE, 0x30EB, 0x30AF], + 0x3347 => [0x30DE, 0x30F3, 0x30B7, 0x30E7, 0x30F3], 0x3348 => [0x30DF, 0x30AF, 0x30ED, 0x30F3], 0x3349 => [0x30DF, 0x30EA], + 0x334A => [0x30DF, 0x30EA, 0x30D0, 0x30FC, 0x30EB], 0x334B => [0x30E1, 0x30AC], 0x334C => [0x30E1, 0x30AC, 0x30C8, 0x30F3], + 0x334D => [0x30E1, 0x30FC, 0x30C8, 0x30EB], 0x334E => [0x30E4, 0x30FC, 0x30C9], 0x334F => [0x30E4, 0x30FC, 0x30EB], + 0x3350 => [0x30E6, 0x30A2, 0x30F3], 0x3351 => [0x30EA, 0x30C3, 0x30C8, 0x30EB], 0x3352 => [0x30EA, 0x30E9], + 0x3353 => [0x30EB, 0x30D4, 0x30FC], 0x3354 => [0x30EB, 0x30FC, 0x30D6, 0x30EB], 0x3355 => [0x30EC, 0x30E0], + 0x3356 => [0x30EC, 0x30F3, 0x30C8, 0x30B2, 0x30F3], 0x3357 => [0x30EF, 0x30C3, 0x30C8], 0x3358 => [0x30, 0x70B9], + 0x3359 => [0x31, 0x70B9], 0x335A => [0x32, 0x70B9], 0x335B => [0x33, 0x70B9], + 0x335C => [0x34, 0x70B9], 0x335D => [0x35, 0x70B9], 0x335E => [0x36, 0x70B9], + 0x335F => [0x37, 0x70B9], 0x3360 => [0x38, 0x70B9], 0x3361 => [0x39, 0x70B9], + 0x3362 => [0x31, 0x30, 0x70B9], 0x3363 => [0x31, 0x31, 0x70B9], 0x3364 => [0x31, 0x32, 0x70B9], + 0x3365 => [0x31, 0x33, 0x70B9], 0x3366 => [0x31, 0x34, 0x70B9], 0x3367 => [0x31, 0x35, 0x70B9], + 0x3368 => [0x31, 0x36, 0x70B9], 0x3369 => [0x31, 0x37, 0x70B9], 0x336A => [0x31, 0x38, 0x70B9], + 0x336B => [0x31, 0x39, 0x70B9], 0x336C => [0x32, 0x30, 0x70B9], 0x336D => [0x32, 0x31, 0x70B9], + 0x336E => [0x32, 0x32, 0x70B9], 0x336F => [0x32, 0x33, 0x70B9], 0x3370 => [0x32, 0x34, 0x70B9], + 0x3371 => [0x68, 0x70, 0x61], 0x3372 => [0x64, 0x61], 0x3373 => [0x61, 0x75], + 0x3374 => [0x62, 0x61, 0x72], 0x3375 => [0x6F, 0x76], 0x3376 => [0x70, 0x63], + 0x3377 => [0x64, 0x6D], 0x3378 => [0x64, 0x6D, 0x32], 0x3379 => [0x64, 0x6D, 0x33], + 0x337A => [0x69, 0x75], 0x337B => [0x5E73, 0x6210], 0x337C => [0x662D, 0x548C], + 0x337D => [0x5927, 0x6B63], 0x337E => [0x660E, 0x6CBB], 0x337F => [0x682A, 0x5F0F, 0x4F1A, 0x793E], + 0x3380 => [0x70, 0x61], 0x3381 => [0x6E, 0x61], 0x3382 => [0x3BC, 0x61], + 0x3383 => [0x6D, 0x61], 0x3384 => [0x6B, 0x61], 0x3385 => [0x6B, 0x62], + 0x3386 => [0x6D, 0x62], 0x3387 => [0x67, 0x62], 0x3388 => [0x63, 0x61, 0x6C], + 0x3389 => [0x6B, 0x63, 0x61, 0x6C], 0x338A => [0x70, 0x66], 0x338B => [0x6E, 0x66], + 0x338C => [0x3BC, 0x66], 0x338D => [0x3BC, 0x67], 0x338E => [0x6D, 0x67], + 0x338F => [0x6B, 0x67], 0x3390 => [0x68, 0x7A], 0x3391 => [0x6B, 0x68, 0x7A], + 0x3392 => [0x6D, 0x68, 0x7A], 0x3393 => [0x67, 0x68, 0x7A], 0x3394 => [0x74, 0x68, 0x7A], + 0x3395 => [0x3BC, 0x6C], 0x3396 => [0x6D, 0x6C], 0x3397 => [0x64, 0x6C], + 0x3398 => [0x6B, 0x6C], 0x3399 => [0x66, 0x6D], 0x339A => [0x6E, 0x6D], + 0x339B => [0x3BC, 0x6D], 0x339C => [0x6D, 0x6D], 0x339D => [0x63, 0x6D], + 0x339E => [0x6B, 0x6D], 0x339F => [0x6D, 0x6D, 0x32], 0x33A0 => [0x63, 0x6D, 0x32], + 0x33A1 => [0x6D, 0x32], 0x33A2 => [0x6B, 0x6D, 0x32], 0x33A3 => [0x6D, 0x6D, 0x33], + 0x33A4 => [0x63, 0x6D, 0x33], 0x33A5 => [0x6D, 0x33], 0x33A6 => [0x6B, 0x6D, 0x33], + 0x33A7 => [0x6D, 0x2215, 0x73], 0x33A8 => [0x6D, 0x2215, 0x73, 0x32], 0x33A9 => [0x70, 0x61], + 0x33AA => [0x6B, 0x70, 0x61], 0x33AB => [0x6D, 0x70, 0x61], 0x33AC => [0x67, 0x70, 0x61], + 0x33AD => [0x72, 0x61, 0x64], 0x33AE => [0x72, 0x61, 0x64, 0x2215, 0x73], 0x33AF => [0x72, 0x61, 0x64, 0x2215, 0x73, 0x32], + 0x33B0 => [0x70, 0x73], 0x33B1 => [0x6E, 0x73], 0x33B2 => [0x3BC, 0x73], + 0x33B3 => [0x6D, 0x73], 0x33B4 => [0x70, 0x76], 0x33B5 => [0x6E, 0x76], + 0x33B6 => [0x3BC, 0x76], 0x33B7 => [0x6D, 0x76], 0x33B8 => [0x6B, 0x76], + 0x33B9 => [0x6D, 0x76], 0x33BA => [0x70, 0x77], 0x33BB => [0x6E, 0x77], + 0x33BC => [0x3BC, 0x77], 0x33BD => [0x6D, 0x77], 0x33BE => [0x6B, 0x77], + 0x33BF => [0x6D, 0x77], 0x33C0 => [0x6B, 0x3C9], 0x33C1 => [0x6D, 0x3C9], + 0x33C3 => [0x62, 0x71], 0x33C4 => [0x63, 0x63], 0x33C5 => [0x63, 0x64], + 0x33C6 => [0x63, 0x2215, 0x6B, 0x67], 0x33C8 => [0x64, 0x62], 0x33C9 => [0x67, 0x79], + 0x33CA => [0x68, 0x61], 0x33CB => [0x68, 0x70], 0x33CC => [0x69, 0x6E], + 0x33CD => [0x6B, 0x6B], 0x33CE => [0x6B, 0x6D], 0x33CF => [0x6B, 0x74], + 0x33D0 => [0x6C, 0x6D], 0x33D1 => [0x6C, 0x6E], 0x33D2 => [0x6C, 0x6F, 0x67], + 0x33D3 => [0x6C, 0x78], 0x33D4 => [0x6D, 0x62], 0x33D5 => [0x6D, 0x69, 0x6C], + 0x33D6 => [0x6D, 0x6F, 0x6C], 0x33D7 => [0x70, 0x68], 0x33D9 => [0x70, 0x70, 0x6D], + 0x33DA => [0x70, 0x72], 0x33DB => [0x73, 0x72], 0x33DC => [0x73, 0x76], + 0x33DD => [0x77, 0x62], 0x33DE => [0x76, 0x2215, 0x6D], 0x33DF => [0x61, 0x2215, 0x6D], + 0x33E0 => [0x31, 0x65E5], 0x33E1 => [0x32, 0x65E5], 0x33E2 => [0x33, 0x65E5], + 0x33E3 => [0x34, 0x65E5], 0x33E4 => [0x35, 0x65E5], 0x33E5 => [0x36, 0x65E5], + 0x33E6 => [0x37, 0x65E5], 0x33E7 => [0x38, 0x65E5], 0x33E8 => [0x39, 0x65E5], + 0x33E9 => [0x31, 0x30, 0x65E5], 0x33EA => [0x31, 0x31, 0x65E5], 0x33EB => [0x31, 0x32, 0x65E5], + 0x33EC => [0x31, 0x33, 0x65E5], 0x33ED => [0x31, 0x34, 0x65E5], 0x33EE => [0x31, 0x35, 0x65E5], + 0x33EF => [0x31, 0x36, 0x65E5], 0x33F0 => [0x31, 0x37, 0x65E5], 0x33F1 => [0x31, 0x38, 0x65E5], + 0x33F2 => [0x31, 0x39, 0x65E5], 0x33F3 => [0x32, 0x30, 0x65E5], 0x33F4 => [0x32, 0x31, 0x65E5], + 0x33F5 => [0x32, 0x32, 0x65E5], 0x33F6 => [0x32, 0x33, 0x65E5], 0x33F7 => [0x32, 0x34, 0x65E5], + 0x33F8 => [0x32, 0x35, 0x65E5], 0x33F9 => [0x32, 0x36, 0x65E5], 0x33FA => [0x32, 0x37, 0x65E5], + 0x33FB => [0x32, 0x38, 0x65E5], 0x33FC => [0x32, 0x39, 0x65E5], 0x33FD => [0x33, 0x30, 0x65E5], + 0x33FE => [0x33, 0x31, 0x65E5], 0x33FF => [0x67, 0x61, 0x6C], 0xA640 => [0xA641], + 0xA642 => [0xA643], 0xA644 => [0xA645], 0xA646 => [0xA647], + 0xA648 => [0xA649], 0xA64A => [0xA64B], 0xA64C => [0xA64D], + 0xA64E => [0xA64F], 0xA650 => [0xA651], 0xA652 => [0xA653], + 0xA654 => [0xA655], 0xA656 => [0xA657], 0xA658 => [0xA659], + 0xA65A => [0xA65B], 0xA65C => [0xA65D], 0xA65E => [0xA65F], + 0xA660 => [0xA661], 0xA662 => [0xA663], 0xA664 => [0xA665], + 0xA666 => [0xA667], 0xA668 => [0xA669], 0xA66A => [0xA66B], + 0xA66C => [0xA66D], 0xA680 => [0xA681], 0xA682 => [0xA683], + 0xA684 => [0xA685], 0xA686 => [0xA687], 0xA688 => [0xA689], + 0xA68A => [0xA68B], 0xA68C => [0xA68D], 0xA68E => [0xA68F], + 0xA690 => [0xA691], 0xA692 => [0xA693], 0xA694 => [0xA695], + 0xA696 => [0xA697], 0xA698 => [0xA699], 0xA69A => [0xA69B], + 0xA69C => [0x44A], 0xA69D => [0x44C], 0xA722 => [0xA723], + 0xA724 => [0xA725], 0xA726 => [0xA727], 0xA728 => [0xA729], + 0xA72A => [0xA72B], 0xA72C => [0xA72D], 0xA72E => [0xA72F], + 0xA732 => [0xA733], 0xA734 => [0xA735], 0xA736 => [0xA737], + 0xA738 => [0xA739], 0xA73A => [0xA73B], 0xA73C => [0xA73D], + 0xA73E => [0xA73F], 0xA740 => [0xA741], 0xA742 => [0xA743], + 0xA744 => [0xA745], 0xA746 => [0xA747], 0xA748 => [0xA749], + 0xA74A => [0xA74B], 0xA74C => [0xA74D], 0xA74E => [0xA74F], + 0xA750 => [0xA751], 0xA752 => [0xA753], 0xA754 => [0xA755], + 0xA756 => [0xA757], 0xA758 => [0xA759], 0xA75A => [0xA75B], + 0xA75C => [0xA75D], 0xA75E => [0xA75F], 0xA760 => [0xA761], + 0xA762 => [0xA763], 0xA764 => [0xA765], 0xA766 => [0xA767], + 0xA768 => [0xA769], 0xA76A => [0xA76B], 0xA76C => [0xA76D], + 0xA76E => [0xA76F], 0xA770 => [0xA76F], 0xA779 => [0xA77A], + 0xA77B => [0xA77C], 0xA77D => [0x1D79], 0xA77E => [0xA77F], + 0xA780 => [0xA781], 0xA782 => [0xA783], 0xA784 => [0xA785], + 0xA786 => [0xA787], 0xA78B => [0xA78C], 0xA78D => [0x265], + 0xA790 => [0xA791], 0xA792 => [0xA793], 0xA796 => [0xA797], + 0xA798 => [0xA799], 0xA79A => [0xA79B], 0xA79C => [0xA79D], + 0xA79E => [0xA79F], 0xA7A0 => [0xA7A1], 0xA7A2 => [0xA7A3], + 0xA7A4 => [0xA7A5], 0xA7A6 => [0xA7A7], 0xA7A8 => [0xA7A9], + 0xA7AA => [0x266], 0xA7AB => [0x25C], 0xA7AC => [0x261], + 0xA7AD => [0x26C], 0xA7B0 => [0x29E], 0xA7B1 => [0x287], + 0xA7F8 => [0x127], 0xA7F9 => [0x153], 0xAB5C => [0xA727], + 0xAB5D => [0xAB37], 0xAB5E => [0x26B], 0xAB5F => [0xAB52], + 0xF900 => [0x8C48], 0xF901 => [0x66F4], 0xF902 => [0x8ECA], + 0xF903 => [0x8CC8], 0xF904 => [0x6ED1], 0xF905 => [0x4E32], + 0xF906 => [0x53E5], 0xF907 => [0x9F9C], 0xF908 => [0x9F9C], + 0xF909 => [0x5951], 0xF90A => [0x91D1], 0xF90B => [0x5587], + 0xF90C => [0x5948], 0xF90D => [0x61F6], 0xF90E => [0x7669], + 0xF90F => [0x7F85], 0xF910 => [0x863F], 0xF911 => [0x87BA], + 0xF912 => [0x88F8], 0xF913 => [0x908F], 0xF914 => [0x6A02], + 0xF915 => [0x6D1B], 0xF916 => [0x70D9], 0xF917 => [0x73DE], + 0xF918 => [0x843D], 0xF919 => [0x916A], 0xF91A => [0x99F1], + 0xF91B => [0x4E82], 0xF91C => [0x5375], 0xF91D => [0x6B04], + 0xF91E => [0x721B], 0xF91F => [0x862D], 0xF920 => [0x9E1E], + 0xF921 => [0x5D50], 0xF922 => [0x6FEB], 0xF923 => [0x85CD], + 0xF924 => [0x8964], 0xF925 => [0x62C9], 0xF926 => [0x81D8], + 0xF927 => [0x881F], 0xF928 => [0x5ECA], 0xF929 => [0x6717], + 0xF92A => [0x6D6A], 0xF92B => [0x72FC], 0xF92C => [0x90CE], + 0xF92D => [0x4F86], 0xF92E => [0x51B7], 0xF92F => [0x52DE], + 0xF930 => [0x64C4], 0xF931 => [0x6AD3], 0xF932 => [0x7210], + 0xF933 => [0x76E7], 0xF934 => [0x8001], 0xF935 => [0x8606], + 0xF936 => [0x865C], 0xF937 => [0x8DEF], 0xF938 => [0x9732], + 0xF939 => [0x9B6F], 0xF93A => [0x9DFA], 0xF93B => [0x788C], + 0xF93C => [0x797F], 0xF93D => [0x7DA0], 0xF93E => [0x83C9], + 0xF93F => [0x9304], 0xF940 => [0x9E7F], 0xF941 => [0x8AD6], + 0xF942 => [0x58DF], 0xF943 => [0x5F04], 0xF944 => [0x7C60], + 0xF945 => [0x807E], 0xF946 => [0x7262], 0xF947 => [0x78CA], + 0xF948 => [0x8CC2], 0xF949 => [0x96F7], 0xF94A => [0x58D8], + 0xF94B => [0x5C62], 0xF94C => [0x6A13], 0xF94D => [0x6DDA], + 0xF94E => [0x6F0F], 0xF94F => [0x7D2F], 0xF950 => [0x7E37], + 0xF951 => [0x964B], 0xF952 => [0x52D2], 0xF953 => [0x808B], + 0xF954 => [0x51DC], 0xF955 => [0x51CC], 0xF956 => [0x7A1C], + 0xF957 => [0x7DBE], 0xF958 => [0x83F1], 0xF959 => [0x9675], + 0xF95A => [0x8B80], 0xF95B => [0x62CF], 0xF95C => [0x6A02], + 0xF95D => [0x8AFE], 0xF95E => [0x4E39], 0xF95F => [0x5BE7], + 0xF960 => [0x6012], 0xF961 => [0x7387], 0xF962 => [0x7570], + 0xF963 => [0x5317], 0xF964 => [0x78FB], 0xF965 => [0x4FBF], + 0xF966 => [0x5FA9], 0xF967 => [0x4E0D], 0xF968 => [0x6CCC], + 0xF969 => [0x6578], 0xF96A => [0x7D22], 0xF96B => [0x53C3], + 0xF96C => [0x585E], 0xF96D => [0x7701], 0xF96E => [0x8449], + 0xF96F => [0x8AAA], 0xF970 => [0x6BBA], 0xF971 => [0x8FB0], + 0xF972 => [0x6C88], 0xF973 => [0x62FE], 0xF974 => [0x82E5], + 0xF975 => [0x63A0], 0xF976 => [0x7565], 0xF977 => [0x4EAE], + 0xF978 => [0x5169], 0xF979 => [0x51C9], 0xF97A => [0x6881], + 0xF97B => [0x7CE7], 0xF97C => [0x826F], 0xF97D => [0x8AD2], + 0xF97E => [0x91CF], 0xF97F => [0x52F5], 0xF980 => [0x5442], + 0xF981 => [0x5973], 0xF982 => [0x5EEC], 0xF983 => [0x65C5], + 0xF984 => [0x6FFE], 0xF985 => [0x792A], 0xF986 => [0x95AD], + 0xF987 => [0x9A6A], 0xF988 => [0x9E97], 0xF989 => [0x9ECE], + 0xF98A => [0x529B], 0xF98B => [0x66C6], 0xF98C => [0x6B77], + 0xF98D => [0x8F62], 0xF98E => [0x5E74], 0xF98F => [0x6190], + 0xF990 => [0x6200], 0xF991 => [0x649A], 0xF992 => [0x6F23], + 0xF993 => [0x7149], 0xF994 => [0x7489], 0xF995 => [0x79CA], + 0xF996 => [0x7DF4], 0xF997 => [0x806F], 0xF998 => [0x8F26], + 0xF999 => [0x84EE], 0xF99A => [0x9023], 0xF99B => [0x934A], + 0xF99C => [0x5217], 0xF99D => [0x52A3], 0xF99E => [0x54BD], + 0xF99F => [0x70C8], 0xF9A0 => [0x88C2], 0xF9A1 => [0x8AAA], + 0xF9A2 => [0x5EC9], 0xF9A3 => [0x5FF5], 0xF9A4 => [0x637B], + 0xF9A5 => [0x6BAE], 0xF9A6 => [0x7C3E], 0xF9A7 => [0x7375], + 0xF9A8 => [0x4EE4], 0xF9A9 => [0x56F9], 0xF9AA => [0x5BE7], + 0xF9AB => [0x5DBA], 0xF9AC => [0x601C], 0xF9AD => [0x73B2], + 0xF9AE => [0x7469], 0xF9AF => [0x7F9A], 0xF9B0 => [0x8046], + 0xF9B1 => [0x9234], 0xF9B2 => [0x96F6], 0xF9B3 => [0x9748], + 0xF9B4 => [0x9818], 0xF9B5 => [0x4F8B], 0xF9B6 => [0x79AE], + 0xF9B7 => [0x91B4], 0xF9B8 => [0x96B8], 0xF9B9 => [0x60E1], + 0xF9BA => [0x4E86], 0xF9BB => [0x50DA], 0xF9BC => [0x5BEE], + 0xF9BD => [0x5C3F], 0xF9BE => [0x6599], 0xF9BF => [0x6A02], + 0xF9C0 => [0x71CE], 0xF9C1 => [0x7642], 0xF9C2 => [0x84FC], + 0xF9C3 => [0x907C], 0xF9C4 => [0x9F8D], 0xF9C5 => [0x6688], + 0xF9C6 => [0x962E], 0xF9C7 => [0x5289], 0xF9C8 => [0x677B], + 0xF9C9 => [0x67F3], 0xF9CA => [0x6D41], 0xF9CB => [0x6E9C], + 0xF9CC => [0x7409], 0xF9CD => [0x7559], 0xF9CE => [0x786B], + 0xF9CF => [0x7D10], 0xF9D0 => [0x985E], 0xF9D1 => [0x516D], + 0xF9D2 => [0x622E], 0xF9D3 => [0x9678], 0xF9D4 => [0x502B], + 0xF9D5 => [0x5D19], 0xF9D6 => [0x6DEA], 0xF9D7 => [0x8F2A], + 0xF9D8 => [0x5F8B], 0xF9D9 => [0x6144], 0xF9DA => [0x6817], + 0xF9DB => [0x7387], 0xF9DC => [0x9686], 0xF9DD => [0x5229], + 0xF9DE => [0x540F], 0xF9DF => [0x5C65], 0xF9E0 => [0x6613], + 0xF9E1 => [0x674E], 0xF9E2 => [0x68A8], 0xF9E3 => [0x6CE5], + 0xF9E4 => [0x7406], 0xF9E5 => [0x75E2], 0xF9E6 => [0x7F79], + 0xF9E7 => [0x88CF], 0xF9E8 => [0x88E1], 0xF9E9 => [0x91CC], + 0xF9EA => [0x96E2], 0xF9EB => [0x533F], 0xF9EC => [0x6EBA], + 0xF9ED => [0x541D], 0xF9EE => [0x71D0], 0xF9EF => [0x7498], + 0xF9F0 => [0x85FA], 0xF9F1 => [0x96A3], 0xF9F2 => [0x9C57], + 0xF9F3 => [0x9E9F], 0xF9F4 => [0x6797], 0xF9F5 => [0x6DCB], + 0xF9F6 => [0x81E8], 0xF9F7 => [0x7ACB], 0xF9F8 => [0x7B20], + 0xF9F9 => [0x7C92], 0xF9FA => [0x72C0], 0xF9FB => [0x7099], + 0xF9FC => [0x8B58], 0xF9FD => [0x4EC0], 0xF9FE => [0x8336], + 0xF9FF => [0x523A], 0xFA00 => [0x5207], 0xFA01 => [0x5EA6], + 0xFA02 => [0x62D3], 0xFA03 => [0x7CD6], 0xFA04 => [0x5B85], + 0xFA05 => [0x6D1E], 0xFA06 => [0x66B4], 0xFA07 => [0x8F3B], + 0xFA08 => [0x884C], 0xFA09 => [0x964D], 0xFA0A => [0x898B], + 0xFA0B => [0x5ED3], 0xFA0C => [0x5140], 0xFA0D => [0x55C0], + 0xFA10 => [0x585A], 0xFA12 => [0x6674], 0xFA15 => [0x51DE], + 0xFA16 => [0x732A], 0xFA17 => [0x76CA], 0xFA18 => [0x793C], + 0xFA19 => [0x795E], 0xFA1A => [0x7965], 0xFA1B => [0x798F], + 0xFA1C => [0x9756], 0xFA1D => [0x7CBE], 0xFA1E => [0x7FBD], + 0xFA20 => [0x8612], 0xFA22 => [0x8AF8], 0xFA25 => [0x9038], + 0xFA26 => [0x90FD], 0xFA2A => [0x98EF], 0xFA2B => [0x98FC], + 0xFA2C => [0x9928], 0xFA2D => [0x9DB4], 0xFA2E => [0x90DE], + 0xFA2F => [0x96B7], 0xFA30 => [0x4FAE], 0xFA31 => [0x50E7], + 0xFA32 => [0x514D], 0xFA33 => [0x52C9], 0xFA34 => [0x52E4], + 0xFA35 => [0x5351], 0xFA36 => [0x559D], 0xFA37 => [0x5606], + 0xFA38 => [0x5668], 0xFA39 => [0x5840], 0xFA3A => [0x58A8], + 0xFA3B => [0x5C64], 0xFA3C => [0x5C6E], 0xFA3D => [0x6094], + 0xFA3E => [0x6168], 0xFA3F => [0x618E], 0xFA40 => [0x61F2], + 0xFA41 => [0x654F], 0xFA42 => [0x65E2], 0xFA43 => [0x6691], + 0xFA44 => [0x6885], 0xFA45 => [0x6D77], 0xFA46 => [0x6E1A], + 0xFA47 => [0x6F22], 0xFA48 => [0x716E], 0xFA49 => [0x722B], + 0xFA4A => [0x7422], 0xFA4B => [0x7891], 0xFA4C => [0x793E], + 0xFA4D => [0x7949], 0xFA4E => [0x7948], 0xFA4F => [0x7950], + 0xFA50 => [0x7956], 0xFA51 => [0x795D], 0xFA52 => [0x798D], + 0xFA53 => [0x798E], 0xFA54 => [0x7A40], 0xFA55 => [0x7A81], + 0xFA56 => [0x7BC0], 0xFA57 => [0x7DF4], 0xFA58 => [0x7E09], + 0xFA59 => [0x7E41], 0xFA5A => [0x7F72], 0xFA5B => [0x8005], + 0xFA5C => [0x81ED], 0xFA5D => [0x8279], 0xFA5E => [0x8279], + 0xFA5F => [0x8457], 0xFA60 => [0x8910], 0xFA61 => [0x8996], + 0xFA62 => [0x8B01], 0xFA63 => [0x8B39], 0xFA64 => [0x8CD3], + 0xFA65 => [0x8D08], 0xFA66 => [0x8FB6], 0xFA67 => [0x9038], + 0xFA68 => [0x96E3], 0xFA69 => [0x97FF], 0xFA6A => [0x983B], + 0xFA6B => [0x6075], 0xFA6C => [0x242EE], 0xFA6D => [0x8218], + 0xFA70 => [0x4E26], 0xFA71 => [0x51B5], 0xFA72 => [0x5168], + 0xFA73 => [0x4F80], 0xFA74 => [0x5145], 0xFA75 => [0x5180], + 0xFA76 => [0x52C7], 0xFA77 => [0x52FA], 0xFA78 => [0x559D], + 0xFA79 => [0x5555], 0xFA7A => [0x5599], 0xFA7B => [0x55E2], + 0xFA7C => [0x585A], 0xFA7D => [0x58B3], 0xFA7E => [0x5944], + 0xFA7F => [0x5954], 0xFA80 => [0x5A62], 0xFA81 => [0x5B28], + 0xFA82 => [0x5ED2], 0xFA83 => [0x5ED9], 0xFA84 => [0x5F69], + 0xFA85 => [0x5FAD], 0xFA86 => [0x60D8], 0xFA87 => [0x614E], + 0xFA88 => [0x6108], 0xFA89 => [0x618E], 0xFA8A => [0x6160], + 0xFA8B => [0x61F2], 0xFA8C => [0x6234], 0xFA8D => [0x63C4], + 0xFA8E => [0x641C], 0xFA8F => [0x6452], 0xFA90 => [0x6556], + 0xFA91 => [0x6674], 0xFA92 => [0x6717], 0xFA93 => [0x671B], + 0xFA94 => [0x6756], 0xFA95 => [0x6B79], 0xFA96 => [0x6BBA], + 0xFA97 => [0x6D41], 0xFA98 => [0x6EDB], 0xFA99 => [0x6ECB], + 0xFA9A => [0x6F22], 0xFA9B => [0x701E], 0xFA9C => [0x716E], + 0xFA9D => [0x77A7], 0xFA9E => [0x7235], 0xFA9F => [0x72AF], + 0xFAA0 => [0x732A], 0xFAA1 => [0x7471], 0xFAA2 => [0x7506], + 0xFAA3 => [0x753B], 0xFAA4 => [0x761D], 0xFAA5 => [0x761F], + 0xFAA6 => [0x76CA], 0xFAA7 => [0x76DB], 0xFAA8 => [0x76F4], + 0xFAA9 => [0x774A], 0xFAAA => [0x7740], 0xFAAB => [0x78CC], + 0xFAAC => [0x7AB1], 0xFAAD => [0x7BC0], 0xFAAE => [0x7C7B], + 0xFAAF => [0x7D5B], 0xFAB0 => [0x7DF4], 0xFAB1 => [0x7F3E], + 0xFAB2 => [0x8005], 0xFAB3 => [0x8352], 0xFAB4 => [0x83EF], + 0xFAB5 => [0x8779], 0xFAB6 => [0x8941], 0xFAB7 => [0x8986], + 0xFAB8 => [0x8996], 0xFAB9 => [0x8ABF], 0xFABA => [0x8AF8], + 0xFABB => [0x8ACB], 0xFABC => [0x8B01], 0xFABD => [0x8AFE], + 0xFABE => [0x8AED], 0xFABF => [0x8B39], 0xFAC0 => [0x8B8A], + 0xFAC1 => [0x8D08], 0xFAC2 => [0x8F38], 0xFAC3 => [0x9072], + 0xFAC4 => [0x9199], 0xFAC5 => [0x9276], 0xFAC6 => [0x967C], + 0xFAC7 => [0x96E3], 0xFAC8 => [0x9756], 0xFAC9 => [0x97DB], + 0xFACA => [0x97FF], 0xFACB => [0x980B], 0xFACC => [0x983B], + 0xFACD => [0x9B12], 0xFACE => [0x9F9C], 0xFACF => [0x2284A], + 0xFAD0 => [0x22844], 0xFAD1 => [0x233D5], 0xFAD2 => [0x3B9D], + 0xFAD3 => [0x4018], 0xFAD4 => [0x4039], 0xFAD5 => [0x25249], + 0xFAD6 => [0x25CD0], 0xFAD7 => [0x27ED3], 0xFAD8 => [0x9F43], + 0xFAD9 => [0x9F8E], 0xFB00 => [0x66, 0x66], 0xFB01 => [0x66, 0x69], + 0xFB02 => [0x66, 0x6C], 0xFB03 => [0x66, 0x66, 0x69], 0xFB04 => [0x66, 0x66, 0x6C], + 0xFB05 => [0x73, 0x74], 0xFB06 => [0x73, 0x74], 0xFB13 => [0x574, 0x576], + 0xFB14 => [0x574, 0x565], 0xFB15 => [0x574, 0x56B], 0xFB16 => [0x57E, 0x576], + 0xFB17 => [0x574, 0x56D], 0xFB1D => [0x5D9, 0x5B4], 0xFB1F => [0x5F2, 0x5B7], + 0xFB20 => [0x5E2], 0xFB21 => [0x5D0], 0xFB22 => [0x5D3], + 0xFB23 => [0x5D4], 0xFB24 => [0x5DB], 0xFB25 => [0x5DC], + 0xFB26 => [0x5DD], 0xFB27 => [0x5E8], 0xFB28 => [0x5EA], + 0xFB2A => [0x5E9, 0x5C1], 0xFB2B => [0x5E9, 0x5C2], 0xFB2C => [0x5E9, 0x5BC, 0x5C1], + 0xFB2D => [0x5E9, 0x5BC, 0x5C2], 0xFB2E => [0x5D0, 0x5B7], 0xFB2F => [0x5D0, 0x5B8], + 0xFB30 => [0x5D0, 0x5BC], 0xFB31 => [0x5D1, 0x5BC], 0xFB32 => [0x5D2, 0x5BC], + 0xFB33 => [0x5D3, 0x5BC], 0xFB34 => [0x5D4, 0x5BC], 0xFB35 => [0x5D5, 0x5BC], + 0xFB36 => [0x5D6, 0x5BC], 0xFB38 => [0x5D8, 0x5BC], 0xFB39 => [0x5D9, 0x5BC], + 0xFB3A => [0x5DA, 0x5BC], 0xFB3B => [0x5DB, 0x5BC], 0xFB3C => [0x5DC, 0x5BC], + 0xFB3E => [0x5DE, 0x5BC], 0xFB40 => [0x5E0, 0x5BC], 0xFB41 => [0x5E1, 0x5BC], + 0xFB43 => [0x5E3, 0x5BC], 0xFB44 => [0x5E4, 0x5BC], 0xFB46 => [0x5E6, 0x5BC], + 0xFB47 => [0x5E7, 0x5BC], 0xFB48 => [0x5E8, 0x5BC], 0xFB49 => [0x5E9, 0x5BC], + 0xFB4A => [0x5EA, 0x5BC], 0xFB4B => [0x5D5, 0x5B9], 0xFB4C => [0x5D1, 0x5BF], + 0xFB4D => [0x5DB, 0x5BF], 0xFB4E => [0x5E4, 0x5BF], 0xFB4F => [0x5D0, 0x5DC], + 0xFB50 => [0x671], 0xFB51 => [0x671], 0xFB52 => [0x67B], + 0xFB53 => [0x67B], 0xFB54 => [0x67B], 0xFB55 => [0x67B], + 0xFB56 => [0x67E], 0xFB57 => [0x67E], 0xFB58 => [0x67E], + 0xFB59 => [0x67E], 0xFB5A => [0x680], 0xFB5B => [0x680], + 0xFB5C => [0x680], 0xFB5D => [0x680], 0xFB5E => [0x67A], + 0xFB5F => [0x67A], 0xFB60 => [0x67A], 0xFB61 => [0x67A], + 0xFB62 => [0x67F], 0xFB63 => [0x67F], 0xFB64 => [0x67F], + 0xFB65 => [0x67F], 0xFB66 => [0x679], 0xFB67 => [0x679], + 0xFB68 => [0x679], 0xFB69 => [0x679], 0xFB6A => [0x6A4], + 0xFB6B => [0x6A4], 0xFB6C => [0x6A4], 0xFB6D => [0x6A4], + 0xFB6E => [0x6A6], 0xFB6F => [0x6A6], 0xFB70 => [0x6A6], + 0xFB71 => [0x6A6], 0xFB72 => [0x684], 0xFB73 => [0x684], + 0xFB74 => [0x684], 0xFB75 => [0x684], 0xFB76 => [0x683], + 0xFB77 => [0x683], 0xFB78 => [0x683], 0xFB79 => [0x683], + 0xFB7A => [0x686], 0xFB7B => [0x686], 0xFB7C => [0x686], + 0xFB7D => [0x686], 0xFB7E => [0x687], 0xFB7F => [0x687], + 0xFB80 => [0x687], 0xFB81 => [0x687], 0xFB82 => [0x68D], + 0xFB83 => [0x68D], 0xFB84 => [0x68C], 0xFB85 => [0x68C], + 0xFB86 => [0x68E], 0xFB87 => [0x68E], 0xFB88 => [0x688], + 0xFB89 => [0x688], 0xFB8A => [0x698], 0xFB8B => [0x698], + 0xFB8C => [0x691], 0xFB8D => [0x691], 0xFB8E => [0x6A9], + 0xFB8F => [0x6A9], 0xFB90 => [0x6A9], 0xFB91 => [0x6A9], + 0xFB92 => [0x6AF], 0xFB93 => [0x6AF], 0xFB94 => [0x6AF], + 0xFB95 => [0x6AF], 0xFB96 => [0x6B3], 0xFB97 => [0x6B3], + 0xFB98 => [0x6B3], 0xFB99 => [0x6B3], 0xFB9A => [0x6B1], + 0xFB9B => [0x6B1], 0xFB9C => [0x6B1], 0xFB9D => [0x6B1], + 0xFB9E => [0x6BA], 0xFB9F => [0x6BA], 0xFBA0 => [0x6BB], + 0xFBA1 => [0x6BB], 0xFBA2 => [0x6BB], 0xFBA3 => [0x6BB], + 0xFBA4 => [0x6C0], 0xFBA5 => [0x6C0], 0xFBA6 => [0x6C1], + 0xFBA7 => [0x6C1], 0xFBA8 => [0x6C1], 0xFBA9 => [0x6C1], + 0xFBAA => [0x6BE], 0xFBAB => [0x6BE], 0xFBAC => [0x6BE], + 0xFBAD => [0x6BE], 0xFBAE => [0x6D2], 0xFBAF => [0x6D2], + 0xFBB0 => [0x6D3], 0xFBB1 => [0x6D3], 0xFBD3 => [0x6AD], + 0xFBD4 => [0x6AD], 0xFBD5 => [0x6AD], 0xFBD6 => [0x6AD], + 0xFBD7 => [0x6C7], 0xFBD8 => [0x6C7], 0xFBD9 => [0x6C6], + 0xFBDA => [0x6C6], 0xFBDB => [0x6C8], 0xFBDC => [0x6C8], + 0xFBDD => [0x6C7, 0x674], 0xFBDE => [0x6CB], 0xFBDF => [0x6CB], + 0xFBE0 => [0x6C5], 0xFBE1 => [0x6C5], 0xFBE2 => [0x6C9], + 0xFBE3 => [0x6C9], 0xFBE4 => [0x6D0], 0xFBE5 => [0x6D0], + 0xFBE6 => [0x6D0], 0xFBE7 => [0x6D0], 0xFBE8 => [0x649], + 0xFBE9 => [0x649], 0xFBEA => [0x626, 0x627], 0xFBEB => [0x626, 0x627], + 0xFBEC => [0x626, 0x6D5], 0xFBED => [0x626, 0x6D5], 0xFBEE => [0x626, 0x648], + 0xFBEF => [0x626, 0x648], 0xFBF0 => [0x626, 0x6C7], 0xFBF1 => [0x626, 0x6C7], + 0xFBF2 => [0x626, 0x6C6], 0xFBF3 => [0x626, 0x6C6], 0xFBF4 => [0x626, 0x6C8], + 0xFBF5 => [0x626, 0x6C8], 0xFBF6 => [0x626, 0x6D0], 0xFBF7 => [0x626, 0x6D0], + 0xFBF8 => [0x626, 0x6D0], 0xFBF9 => [0x626, 0x649], 0xFBFA => [0x626, 0x649], + 0xFBFB => [0x626, 0x649], 0xFBFC => [0x6CC], 0xFBFD => [0x6CC], + 0xFBFE => [0x6CC], 0xFBFF => [0x6CC], 0xFC00 => [0x626, 0x62C], + 0xFC01 => [0x626, 0x62D], 0xFC02 => [0x626, 0x645], 0xFC03 => [0x626, 0x649], + 0xFC04 => [0x626, 0x64A], 0xFC05 => [0x628, 0x62C], 0xFC06 => [0x628, 0x62D], + 0xFC07 => [0x628, 0x62E], 0xFC08 => [0x628, 0x645], 0xFC09 => [0x628, 0x649], + 0xFC0A => [0x628, 0x64A], 0xFC0B => [0x62A, 0x62C], 0xFC0C => [0x62A, 0x62D], + 0xFC0D => [0x62A, 0x62E], 0xFC0E => [0x62A, 0x645], 0xFC0F => [0x62A, 0x649], + 0xFC10 => [0x62A, 0x64A], 0xFC11 => [0x62B, 0x62C], 0xFC12 => [0x62B, 0x645], + 0xFC13 => [0x62B, 0x649], 0xFC14 => [0x62B, 0x64A], 0xFC15 => [0x62C, 0x62D], + 0xFC16 => [0x62C, 0x645], 0xFC17 => [0x62D, 0x62C], 0xFC18 => [0x62D, 0x645], + 0xFC19 => [0x62E, 0x62C], 0xFC1A => [0x62E, 0x62D], 0xFC1B => [0x62E, 0x645], + 0xFC1C => [0x633, 0x62C], 0xFC1D => [0x633, 0x62D], 0xFC1E => [0x633, 0x62E], + 0xFC1F => [0x633, 0x645], 0xFC20 => [0x635, 0x62D], 0xFC21 => [0x635, 0x645], + 0xFC22 => [0x636, 0x62C], 0xFC23 => [0x636, 0x62D], 0xFC24 => [0x636, 0x62E], + 0xFC25 => [0x636, 0x645], 0xFC26 => [0x637, 0x62D], 0xFC27 => [0x637, 0x645], + 0xFC28 => [0x638, 0x645], 0xFC29 => [0x639, 0x62C], 0xFC2A => [0x639, 0x645], + 0xFC2B => [0x63A, 0x62C], 0xFC2C => [0x63A, 0x645], 0xFC2D => [0x641, 0x62C], + 0xFC2E => [0x641, 0x62D], 0xFC2F => [0x641, 0x62E], 0xFC30 => [0x641, 0x645], + 0xFC31 => [0x641, 0x649], 0xFC32 => [0x641, 0x64A], 0xFC33 => [0x642, 0x62D], + 0xFC34 => [0x642, 0x645], 0xFC35 => [0x642, 0x649], 0xFC36 => [0x642, 0x64A], + 0xFC37 => [0x643, 0x627], 0xFC38 => [0x643, 0x62C], 0xFC39 => [0x643, 0x62D], + 0xFC3A => [0x643, 0x62E], 0xFC3B => [0x643, 0x644], 0xFC3C => [0x643, 0x645], + 0xFC3D => [0x643, 0x649], 0xFC3E => [0x643, 0x64A], 0xFC3F => [0x644, 0x62C], + 0xFC40 => [0x644, 0x62D], 0xFC41 => [0x644, 0x62E], 0xFC42 => [0x644, 0x645], + 0xFC43 => [0x644, 0x649], 0xFC44 => [0x644, 0x64A], 0xFC45 => [0x645, 0x62C], + 0xFC46 => [0x645, 0x62D], 0xFC47 => [0x645, 0x62E], 0xFC48 => [0x645, 0x645], + 0xFC49 => [0x645, 0x649], 0xFC4A => [0x645, 0x64A], 0xFC4B => [0x646, 0x62C], + 0xFC4C => [0x646, 0x62D], 0xFC4D => [0x646, 0x62E], 0xFC4E => [0x646, 0x645], + 0xFC4F => [0x646, 0x649], 0xFC50 => [0x646, 0x64A], 0xFC51 => [0x647, 0x62C], + 0xFC52 => [0x647, 0x645], 0xFC53 => [0x647, 0x649], 0xFC54 => [0x647, 0x64A], + 0xFC55 => [0x64A, 0x62C], 0xFC56 => [0x64A, 0x62D], 0xFC57 => [0x64A, 0x62E], + 0xFC58 => [0x64A, 0x645], 0xFC59 => [0x64A, 0x649], 0xFC5A => [0x64A, 0x64A], + 0xFC5B => [0x630, 0x670], 0xFC5C => [0x631, 0x670], 0xFC5D => [0x649, 0x670], + 0xFC64 => [0x626, 0x631], 0xFC65 => [0x626, 0x632], 0xFC66 => [0x626, 0x645], + 0xFC67 => [0x626, 0x646], 0xFC68 => [0x626, 0x649], 0xFC69 => [0x626, 0x64A], + 0xFC6A => [0x628, 0x631], 0xFC6B => [0x628, 0x632], 0xFC6C => [0x628, 0x645], + 0xFC6D => [0x628, 0x646], 0xFC6E => [0x628, 0x649], 0xFC6F => [0x628, 0x64A], + 0xFC70 => [0x62A, 0x631], 0xFC71 => [0x62A, 0x632], 0xFC72 => [0x62A, 0x645], + 0xFC73 => [0x62A, 0x646], 0xFC74 => [0x62A, 0x649], 0xFC75 => [0x62A, 0x64A], + 0xFC76 => [0x62B, 0x631], 0xFC77 => [0x62B, 0x632], 0xFC78 => [0x62B, 0x645], + 0xFC79 => [0x62B, 0x646], 0xFC7A => [0x62B, 0x649], 0xFC7B => [0x62B, 0x64A], + 0xFC7C => [0x641, 0x649], 0xFC7D => [0x641, 0x64A], 0xFC7E => [0x642, 0x649], + 0xFC7F => [0x642, 0x64A], 0xFC80 => [0x643, 0x627], 0xFC81 => [0x643, 0x644], + 0xFC82 => [0x643, 0x645], 0xFC83 => [0x643, 0x649], 0xFC84 => [0x643, 0x64A], + 0xFC85 => [0x644, 0x645], 0xFC86 => [0x644, 0x649], 0xFC87 => [0x644, 0x64A], + 0xFC88 => [0x645, 0x627], 0xFC89 => [0x645, 0x645], 0xFC8A => [0x646, 0x631], + 0xFC8B => [0x646, 0x632], 0xFC8C => [0x646, 0x645], 0xFC8D => [0x646, 0x646], + 0xFC8E => [0x646, 0x649], 0xFC8F => [0x646, 0x64A], 0xFC90 => [0x649, 0x670], + 0xFC91 => [0x64A, 0x631], 0xFC92 => [0x64A, 0x632], 0xFC93 => [0x64A, 0x645], + 0xFC94 => [0x64A, 0x646], 0xFC95 => [0x64A, 0x649], 0xFC96 => [0x64A, 0x64A], + 0xFC97 => [0x626, 0x62C], 0xFC98 => [0x626, 0x62D], 0xFC99 => [0x626, 0x62E], + 0xFC9A => [0x626, 0x645], 0xFC9B => [0x626, 0x647], 0xFC9C => [0x628, 0x62C], + 0xFC9D => [0x628, 0x62D], 0xFC9E => [0x628, 0x62E], 0xFC9F => [0x628, 0x645], + 0xFCA0 => [0x628, 0x647], 0xFCA1 => [0x62A, 0x62C], 0xFCA2 => [0x62A, 0x62D], + 0xFCA3 => [0x62A, 0x62E], 0xFCA4 => [0x62A, 0x645], 0xFCA5 => [0x62A, 0x647], + 0xFCA6 => [0x62B, 0x645], 0xFCA7 => [0x62C, 0x62D], 0xFCA8 => [0x62C, 0x645], + 0xFCA9 => [0x62D, 0x62C], 0xFCAA => [0x62D, 0x645], 0xFCAB => [0x62E, 0x62C], + 0xFCAC => [0x62E, 0x645], 0xFCAD => [0x633, 0x62C], 0xFCAE => [0x633, 0x62D], + 0xFCAF => [0x633, 0x62E], 0xFCB0 => [0x633, 0x645], 0xFCB1 => [0x635, 0x62D], + 0xFCB2 => [0x635, 0x62E], 0xFCB3 => [0x635, 0x645], 0xFCB4 => [0x636, 0x62C], + 0xFCB5 => [0x636, 0x62D], 0xFCB6 => [0x636, 0x62E], 0xFCB7 => [0x636, 0x645], + 0xFCB8 => [0x637, 0x62D], 0xFCB9 => [0x638, 0x645], 0xFCBA => [0x639, 0x62C], + 0xFCBB => [0x639, 0x645], 0xFCBC => [0x63A, 0x62C], 0xFCBD => [0x63A, 0x645], + 0xFCBE => [0x641, 0x62C], 0xFCBF => [0x641, 0x62D], 0xFCC0 => [0x641, 0x62E], + 0xFCC1 => [0x641, 0x645], 0xFCC2 => [0x642, 0x62D], 0xFCC3 => [0x642, 0x645], + 0xFCC4 => [0x643, 0x62C], 0xFCC5 => [0x643, 0x62D], 0xFCC6 => [0x643, 0x62E], + 0xFCC7 => [0x643, 0x644], 0xFCC8 => [0x643, 0x645], 0xFCC9 => [0x644, 0x62C], + 0xFCCA => [0x644, 0x62D], 0xFCCB => [0x644, 0x62E], 0xFCCC => [0x644, 0x645], + 0xFCCD => [0x644, 0x647], 0xFCCE => [0x645, 0x62C], 0xFCCF => [0x645, 0x62D], + 0xFCD0 => [0x645, 0x62E], 0xFCD1 => [0x645, 0x645], 0xFCD2 => [0x646, 0x62C], + 0xFCD3 => [0x646, 0x62D], 0xFCD4 => [0x646, 0x62E], 0xFCD5 => [0x646, 0x645], + 0xFCD6 => [0x646, 0x647], 0xFCD7 => [0x647, 0x62C], 0xFCD8 => [0x647, 0x645], + 0xFCD9 => [0x647, 0x670], 0xFCDA => [0x64A, 0x62C], 0xFCDB => [0x64A, 0x62D], + 0xFCDC => [0x64A, 0x62E], 0xFCDD => [0x64A, 0x645], 0xFCDE => [0x64A, 0x647], + 0xFCDF => [0x626, 0x645], 0xFCE0 => [0x626, 0x647], 0xFCE1 => [0x628, 0x645], + 0xFCE2 => [0x628, 0x647], 0xFCE3 => [0x62A, 0x645], 0xFCE4 => [0x62A, 0x647], + 0xFCE5 => [0x62B, 0x645], 0xFCE6 => [0x62B, 0x647], 0xFCE7 => [0x633, 0x645], + 0xFCE8 => [0x633, 0x647], 0xFCE9 => [0x634, 0x645], 0xFCEA => [0x634, 0x647], + 0xFCEB => [0x643, 0x644], 0xFCEC => [0x643, 0x645], 0xFCED => [0x644, 0x645], + 0xFCEE => [0x646, 0x645], 0xFCEF => [0x646, 0x647], 0xFCF0 => [0x64A, 0x645], + 0xFCF1 => [0x64A, 0x647], 0xFCF2 => [0x640, 0x64E, 0x651], 0xFCF3 => [0x640, 0x64F, 0x651], + 0xFCF4 => [0x640, 0x650, 0x651], 0xFCF5 => [0x637, 0x649], 0xFCF6 => [0x637, 0x64A], + 0xFCF7 => [0x639, 0x649], 0xFCF8 => [0x639, 0x64A], 0xFCF9 => [0x63A, 0x649], + 0xFCFA => [0x63A, 0x64A], 0xFCFB => [0x633, 0x649], 0xFCFC => [0x633, 0x64A], + 0xFCFD => [0x634, 0x649], 0xFCFE => [0x634, 0x64A], 0xFCFF => [0x62D, 0x649], + 0xFD00 => [0x62D, 0x64A], 0xFD01 => [0x62C, 0x649], 0xFD02 => [0x62C, 0x64A], + 0xFD03 => [0x62E, 0x649], 0xFD04 => [0x62E, 0x64A], 0xFD05 => [0x635, 0x649], + 0xFD06 => [0x635, 0x64A], 0xFD07 => [0x636, 0x649], 0xFD08 => [0x636, 0x64A], + 0xFD09 => [0x634, 0x62C], 0xFD0A => [0x634, 0x62D], 0xFD0B => [0x634, 0x62E], + 0xFD0C => [0x634, 0x645], 0xFD0D => [0x634, 0x631], 0xFD0E => [0x633, 0x631], + 0xFD0F => [0x635, 0x631], 0xFD10 => [0x636, 0x631], 0xFD11 => [0x637, 0x649], + 0xFD12 => [0x637, 0x64A], 0xFD13 => [0x639, 0x649], 0xFD14 => [0x639, 0x64A], + 0xFD15 => [0x63A, 0x649], 0xFD16 => [0x63A, 0x64A], 0xFD17 => [0x633, 0x649], + 0xFD18 => [0x633, 0x64A], 0xFD19 => [0x634, 0x649], 0xFD1A => [0x634, 0x64A], + 0xFD1B => [0x62D, 0x649], 0xFD1C => [0x62D, 0x64A], 0xFD1D => [0x62C, 0x649], + 0xFD1E => [0x62C, 0x64A], 0xFD1F => [0x62E, 0x649], 0xFD20 => [0x62E, 0x64A], + 0xFD21 => [0x635, 0x649], 0xFD22 => [0x635, 0x64A], 0xFD23 => [0x636, 0x649], + 0xFD24 => [0x636, 0x64A], 0xFD25 => [0x634, 0x62C], 0xFD26 => [0x634, 0x62D], + 0xFD27 => [0x634, 0x62E], 0xFD28 => [0x634, 0x645], 0xFD29 => [0x634, 0x631], + 0xFD2A => [0x633, 0x631], 0xFD2B => [0x635, 0x631], 0xFD2C => [0x636, 0x631], + 0xFD2D => [0x634, 0x62C], 0xFD2E => [0x634, 0x62D], 0xFD2F => [0x634, 0x62E], + 0xFD30 => [0x634, 0x645], 0xFD31 => [0x633, 0x647], 0xFD32 => [0x634, 0x647], + 0xFD33 => [0x637, 0x645], 0xFD34 => [0x633, 0x62C], 0xFD35 => [0x633, 0x62D], + 0xFD36 => [0x633, 0x62E], 0xFD37 => [0x634, 0x62C], 0xFD38 => [0x634, 0x62D], + 0xFD39 => [0x634, 0x62E], 0xFD3A => [0x637, 0x645], 0xFD3B => [0x638, 0x645], + 0xFD3C => [0x627, 0x64B], 0xFD3D => [0x627, 0x64B], 0xFD50 => [0x62A, 0x62C, 0x645], + 0xFD51 => [0x62A, 0x62D, 0x62C], 0xFD52 => [0x62A, 0x62D, 0x62C], 0xFD53 => [0x62A, 0x62D, 0x645], + 0xFD54 => [0x62A, 0x62E, 0x645], 0xFD55 => [0x62A, 0x645, 0x62C], 0xFD56 => [0x62A, 0x645, 0x62D], + 0xFD57 => [0x62A, 0x645, 0x62E], 0xFD58 => [0x62C, 0x645, 0x62D], 0xFD59 => [0x62C, 0x645, 0x62D], + 0xFD5A => [0x62D, 0x645, 0x64A], 0xFD5B => [0x62D, 0x645, 0x649], 0xFD5C => [0x633, 0x62D, 0x62C], + 0xFD5D => [0x633, 0x62C, 0x62D], 0xFD5E => [0x633, 0x62C, 0x649], 0xFD5F => [0x633, 0x645, 0x62D], + 0xFD60 => [0x633, 0x645, 0x62D], 0xFD61 => [0x633, 0x645, 0x62C], 0xFD62 => [0x633, 0x645, 0x645], + 0xFD63 => [0x633, 0x645, 0x645], 0xFD64 => [0x635, 0x62D, 0x62D], 0xFD65 => [0x635, 0x62D, 0x62D], + 0xFD66 => [0x635, 0x645, 0x645], 0xFD67 => [0x634, 0x62D, 0x645], 0xFD68 => [0x634, 0x62D, 0x645], + 0xFD69 => [0x634, 0x62C, 0x64A], 0xFD6A => [0x634, 0x645, 0x62E], 0xFD6B => [0x634, 0x645, 0x62E], + 0xFD6C => [0x634, 0x645, 0x645], 0xFD6D => [0x634, 0x645, 0x645], 0xFD6E => [0x636, 0x62D, 0x649], + 0xFD6F => [0x636, 0x62E, 0x645], 0xFD70 => [0x636, 0x62E, 0x645], 0xFD71 => [0x637, 0x645, 0x62D], + 0xFD72 => [0x637, 0x645, 0x62D], 0xFD73 => [0x637, 0x645, 0x645], 0xFD74 => [0x637, 0x645, 0x64A], + 0xFD75 => [0x639, 0x62C, 0x645], 0xFD76 => [0x639, 0x645, 0x645], 0xFD77 => [0x639, 0x645, 0x645], + 0xFD78 => [0x639, 0x645, 0x649], 0xFD79 => [0x63A, 0x645, 0x645], 0xFD7A => [0x63A, 0x645, 0x64A], + 0xFD7B => [0x63A, 0x645, 0x649], 0xFD7C => [0x641, 0x62E, 0x645], 0xFD7D => [0x641, 0x62E, 0x645], + 0xFD7E => [0x642, 0x645, 0x62D], 0xFD7F => [0x642, 0x645, 0x645], 0xFD80 => [0x644, 0x62D, 0x645], + 0xFD81 => [0x644, 0x62D, 0x64A], 0xFD82 => [0x644, 0x62D, 0x649], 0xFD83 => [0x644, 0x62C, 0x62C], + 0xFD84 => [0x644, 0x62C, 0x62C], 0xFD85 => [0x644, 0x62E, 0x645], 0xFD86 => [0x644, 0x62E, 0x645], + 0xFD87 => [0x644, 0x645, 0x62D], 0xFD88 => [0x644, 0x645, 0x62D], 0xFD89 => [0x645, 0x62D, 0x62C], + 0xFD8A => [0x645, 0x62D, 0x645], 0xFD8B => [0x645, 0x62D, 0x64A], 0xFD8C => [0x645, 0x62C, 0x62D], + 0xFD8D => [0x645, 0x62C, 0x645], 0xFD8E => [0x645, 0x62E, 0x62C], 0xFD8F => [0x645, 0x62E, 0x645], + 0xFD92 => [0x645, 0x62C, 0x62E], 0xFD93 => [0x647, 0x645, 0x62C], 0xFD94 => [0x647, 0x645, 0x645], + 0xFD95 => [0x646, 0x62D, 0x645], 0xFD96 => [0x646, 0x62D, 0x649], 0xFD97 => [0x646, 0x62C, 0x645], + 0xFD98 => [0x646, 0x62C, 0x645], 0xFD99 => [0x646, 0x62C, 0x649], 0xFD9A => [0x646, 0x645, 0x64A], + 0xFD9B => [0x646, 0x645, 0x649], 0xFD9C => [0x64A, 0x645, 0x645], 0xFD9D => [0x64A, 0x645, 0x645], + 0xFD9E => [0x628, 0x62E, 0x64A], 0xFD9F => [0x62A, 0x62C, 0x64A], 0xFDA0 => [0x62A, 0x62C, 0x649], + 0xFDA1 => [0x62A, 0x62E, 0x64A], 0xFDA2 => [0x62A, 0x62E, 0x649], 0xFDA3 => [0x62A, 0x645, 0x64A], + 0xFDA4 => [0x62A, 0x645, 0x649], 0xFDA5 => [0x62C, 0x645, 0x64A], 0xFDA6 => [0x62C, 0x62D, 0x649], + 0xFDA7 => [0x62C, 0x645, 0x649], 0xFDA8 => [0x633, 0x62E, 0x649], 0xFDA9 => [0x635, 0x62D, 0x64A], + 0xFDAA => [0x634, 0x62D, 0x64A], 0xFDAB => [0x636, 0x62D, 0x64A], 0xFDAC => [0x644, 0x62C, 0x64A], + 0xFDAD => [0x644, 0x645, 0x64A], 0xFDAE => [0x64A, 0x62D, 0x64A], 0xFDAF => [0x64A, 0x62C, 0x64A], + 0xFDB0 => [0x64A, 0x645, 0x64A], 0xFDB1 => [0x645, 0x645, 0x64A], 0xFDB2 => [0x642, 0x645, 0x64A], + 0xFDB3 => [0x646, 0x62D, 0x64A], 0xFDB4 => [0x642, 0x645, 0x62D], 0xFDB5 => [0x644, 0x62D, 0x645], + 0xFDB6 => [0x639, 0x645, 0x64A], 0xFDB7 => [0x643, 0x645, 0x64A], 0xFDB8 => [0x646, 0x62C, 0x62D], + 0xFDB9 => [0x645, 0x62E, 0x64A], 0xFDBA => [0x644, 0x62C, 0x645], 0xFDBB => [0x643, 0x645, 0x645], + 0xFDBC => [0x644, 0x62C, 0x645], 0xFDBD => [0x646, 0x62C, 0x62D], 0xFDBE => [0x62C, 0x62D, 0x64A], + 0xFDBF => [0x62D, 0x62C, 0x64A], 0xFDC0 => [0x645, 0x62C, 0x64A], 0xFDC1 => [0x641, 0x645, 0x64A], + 0xFDC2 => [0x628, 0x62D, 0x64A], 0xFDC3 => [0x643, 0x645, 0x645], 0xFDC4 => [0x639, 0x62C, 0x645], + 0xFDC5 => [0x635, 0x645, 0x645], 0xFDC6 => [0x633, 0x62E, 0x64A], 0xFDC7 => [0x646, 0x62C, 0x64A], + 0xFDF0 => [0x635, 0x644, 0x6D2], 0xFDF1 => [0x642, 0x644, 0x6D2], 0xFDF2 => [0x627, 0x644, 0x644, 0x647], + 0xFDF3 => [0x627, 0x643, 0x628, 0x631], 0xFDF4 => [0x645, 0x62D, 0x645, 0x62F], 0xFDF5 => [0x635, 0x644, 0x639, 0x645], + 0xFDF6 => [0x631, 0x633, 0x648, 0x644], 0xFDF7 => [0x639, 0x644, 0x64A, 0x647], 0xFDF8 => [0x648, 0x633, 0x644, 0x645], + 0xFDF9 => [0x635, 0x644, 0x649], 0xFDFC => [0x631, 0x6CC, 0x627, 0x644], 0xFE11 => [0x3001], + 0xFE17 => [0x3016], 0xFE18 => [0x3017], 0xFE31 => [0x2014], + 0xFE32 => [0x2013], 0xFE39 => [0x3014], 0xFE3A => [0x3015], + 0xFE3B => [0x3010], 0xFE3C => [0x3011], 0xFE3D => [0x300A], + 0xFE3E => [0x300B], 0xFE3F => [0x3008], 0xFE40 => [0x3009], + 0xFE41 => [0x300C], 0xFE42 => [0x300D], 0xFE43 => [0x300E], + 0xFE44 => [0x300F], 0xFE51 => [0x3001], 0xFE58 => [0x2014], + 0xFE5D => [0x3014], 0xFE5E => [0x3015], 0xFE63 => [0x2D], + 0xFE71 => [0x640, 0x64B], 0xFE77 => [0x640, 0x64E], 0xFE79 => [0x640, 0x64F], + 0xFE7B => [0x640, 0x650], 0xFE7D => [0x640, 0x651], 0xFE7F => [0x640, 0x652], + 0xFE80 => [0x621], 0xFE81 => [0x622], 0xFE82 => [0x622], + 0xFE83 => [0x623], 0xFE84 => [0x623], 0xFE85 => [0x624], + 0xFE86 => [0x624], 0xFE87 => [0x625], 0xFE88 => [0x625], + 0xFE89 => [0x626], 0xFE8A => [0x626], 0xFE8B => [0x626], + 0xFE8C => [0x626], 0xFE8D => [0x627], 0xFE8E => [0x627], + 0xFE8F => [0x628], 0xFE90 => [0x628], 0xFE91 => [0x628], + 0xFE92 => [0x628], 0xFE93 => [0x629], 0xFE94 => [0x629], + 0xFE95 => [0x62A], 0xFE96 => [0x62A], 0xFE97 => [0x62A], + 0xFE98 => [0x62A], 0xFE99 => [0x62B], 0xFE9A => [0x62B], + 0xFE9B => [0x62B], 0xFE9C => [0x62B], 0xFE9D => [0x62C], + 0xFE9E => [0x62C], 0xFE9F => [0x62C], 0xFEA0 => [0x62C], + 0xFEA1 => [0x62D], 0xFEA2 => [0x62D], 0xFEA3 => [0x62D], + 0xFEA4 => [0x62D], 0xFEA5 => [0x62E], 0xFEA6 => [0x62E], + 0xFEA7 => [0x62E], 0xFEA8 => [0x62E], 0xFEA9 => [0x62F], + 0xFEAA => [0x62F], 0xFEAB => [0x630], 0xFEAC => [0x630], + 0xFEAD => [0x631], 0xFEAE => [0x631], 0xFEAF => [0x632], + 0xFEB0 => [0x632], 0xFEB1 => [0x633], 0xFEB2 => [0x633], + 0xFEB3 => [0x633], 0xFEB4 => [0x633], 0xFEB5 => [0x634], + 0xFEB6 => [0x634], 0xFEB7 => [0x634], 0xFEB8 => [0x634], + 0xFEB9 => [0x635], 0xFEBA => [0x635], 0xFEBB => [0x635], + 0xFEBC => [0x635], 0xFEBD => [0x636], 0xFEBE => [0x636], + 0xFEBF => [0x636], 0xFEC0 => [0x636], 0xFEC1 => [0x637], + 0xFEC2 => [0x637], 0xFEC3 => [0x637], 0xFEC4 => [0x637], + 0xFEC5 => [0x638], 0xFEC6 => [0x638], 0xFEC7 => [0x638], + 0xFEC8 => [0x638], 0xFEC9 => [0x639], 0xFECA => [0x639], + 0xFECB => [0x639], 0xFECC => [0x639], 0xFECD => [0x63A], + 0xFECE => [0x63A], 0xFECF => [0x63A], 0xFED0 => [0x63A], + 0xFED1 => [0x641], 0xFED2 => [0x641], 0xFED3 => [0x641], + 0xFED4 => [0x641], 0xFED5 => [0x642], 0xFED6 => [0x642], + 0xFED7 => [0x642], 0xFED8 => [0x642], 0xFED9 => [0x643], + 0xFEDA => [0x643], 0xFEDB => [0x643], 0xFEDC => [0x643], + 0xFEDD => [0x644], 0xFEDE => [0x644], 0xFEDF => [0x644], + 0xFEE0 => [0x644], 0xFEE1 => [0x645], 0xFEE2 => [0x645], + 0xFEE3 => [0x645], 0xFEE4 => [0x645], 0xFEE5 => [0x646], + 0xFEE6 => [0x646], 0xFEE7 => [0x646], 0xFEE8 => [0x646], + 0xFEE9 => [0x647], 0xFEEA => [0x647], 0xFEEB => [0x647], + 0xFEEC => [0x647], 0xFEED => [0x648], 0xFEEE => [0x648], + 0xFEEF => [0x649], 0xFEF0 => [0x649], 0xFEF1 => [0x64A], + 0xFEF2 => [0x64A], 0xFEF3 => [0x64A], 0xFEF4 => [0x64A], + 0xFEF5 => [0x644, 0x622], 0xFEF6 => [0x644, 0x622], 0xFEF7 => [0x644, 0x623], + 0xFEF8 => [0x644, 0x623], 0xFEF9 => [0x644, 0x625], 0xFEFA => [0x644, 0x625], + 0xFEFB => [0x644, 0x627], 0xFEFC => [0x644, 0x627], 0xFF0D => [0x2D], + 0xFF0E => [0x2E], 0xFF10 => [0x30], 0xFF11 => [0x31], + 0xFF12 => [0x32], 0xFF13 => [0x33], 0xFF14 => [0x34], + 0xFF15 => [0x35], 0xFF16 => [0x36], 0xFF17 => [0x37], + 0xFF18 => [0x38], 0xFF19 => [0x39], 0xFF21 => [0x61], + 0xFF22 => [0x62], 0xFF23 => [0x63], 0xFF24 => [0x64], + 0xFF25 => [0x65], 0xFF26 => [0x66], 0xFF27 => [0x67], + 0xFF28 => [0x68], 0xFF29 => [0x69], 0xFF2A => [0x6A], + 0xFF2B => [0x6B], 0xFF2C => [0x6C], 0xFF2D => [0x6D], + 0xFF2E => [0x6E], 0xFF2F => [0x6F], 0xFF30 => [0x70], + 0xFF31 => [0x71], 0xFF32 => [0x72], 0xFF33 => [0x73], + 0xFF34 => [0x74], 0xFF35 => [0x75], 0xFF36 => [0x76], + 0xFF37 => [0x77], 0xFF38 => [0x78], 0xFF39 => [0x79], + 0xFF3A => [0x7A], 0xFF41 => [0x61], 0xFF42 => [0x62], + 0xFF43 => [0x63], 0xFF44 => [0x64], 0xFF45 => [0x65], + 0xFF46 => [0x66], 0xFF47 => [0x67], 0xFF48 => [0x68], + 0xFF49 => [0x69], 0xFF4A => [0x6A], 0xFF4B => [0x6B], + 0xFF4C => [0x6C], 0xFF4D => [0x6D], 0xFF4E => [0x6E], + 0xFF4F => [0x6F], 0xFF50 => [0x70], 0xFF51 => [0x71], + 0xFF52 => [0x72], 0xFF53 => [0x73], 0xFF54 => [0x74], + 0xFF55 => [0x75], 0xFF56 => [0x76], 0xFF57 => [0x77], + 0xFF58 => [0x78], 0xFF59 => [0x79], 0xFF5A => [0x7A], + 0xFF5F => [0x2985], 0xFF60 => [0x2986], 0xFF61 => [0x2E], + 0xFF62 => [0x300C], 0xFF63 => [0x300D], 0xFF64 => [0x3001], + 0xFF65 => [0x30FB], 0xFF66 => [0x30F2], 0xFF67 => [0x30A1], + 0xFF68 => [0x30A3], 0xFF69 => [0x30A5], 0xFF6A => [0x30A7], + 0xFF6B => [0x30A9], 0xFF6C => [0x30E3], 0xFF6D => [0x30E5], + 0xFF6E => [0x30E7], 0xFF6F => [0x30C3], 0xFF70 => [0x30FC], + 0xFF71 => [0x30A2], 0xFF72 => [0x30A4], 0xFF73 => [0x30A6], + 0xFF74 => [0x30A8], 0xFF75 => [0x30AA], 0xFF76 => [0x30AB], + 0xFF77 => [0x30AD], 0xFF78 => [0x30AF], 0xFF79 => [0x30B1], + 0xFF7A => [0x30B3], 0xFF7B => [0x30B5], 0xFF7C => [0x30B7], + 0xFF7D => [0x30B9], 0xFF7E => [0x30BB], 0xFF7F => [0x30BD], + 0xFF80 => [0x30BF], 0xFF81 => [0x30C1], 0xFF82 => [0x30C4], + 0xFF83 => [0x30C6], 0xFF84 => [0x30C8], 0xFF85 => [0x30CA], + 0xFF86 => [0x30CB], 0xFF87 => [0x30CC], 0xFF88 => [0x30CD], + 0xFF89 => [0x30CE], 0xFF8A => [0x30CF], 0xFF8B => [0x30D2], + 0xFF8C => [0x30D5], 0xFF8D => [0x30D8], 0xFF8E => [0x30DB], + 0xFF8F => [0x30DE], 0xFF90 => [0x30DF], 0xFF91 => [0x30E0], + 0xFF92 => [0x30E1], 0xFF93 => [0x30E2], 0xFF94 => [0x30E4], + 0xFF95 => [0x30E6], 0xFF96 => [0x30E8], 0xFF97 => [0x30E9], + 0xFF98 => [0x30EA], 0xFF99 => [0x30EB], 0xFF9A => [0x30EC], + 0xFF9B => [0x30ED], 0xFF9C => [0x30EF], 0xFF9D => [0x30F3], + 0xFF9E => [0x3099], 0xFF9F => [0x309A], 0xFFA1 => [0x1100], + 0xFFA2 => [0x1101], 0xFFA3 => [0x11AA], 0xFFA4 => [0x1102], + 0xFFA5 => [0x11AC], 0xFFA6 => [0x11AD], 0xFFA7 => [0x1103], + 0xFFA8 => [0x1104], 0xFFA9 => [0x1105], 0xFFAA => [0x11B0], + 0xFFAB => [0x11B1], 0xFFAC => [0x11B2], 0xFFAD => [0x11B3], + 0xFFAE => [0x11B4], 0xFFAF => [0x11B5], 0xFFB0 => [0x111A], + 0xFFB1 => [0x1106], 0xFFB2 => [0x1107], 0xFFB3 => [0x1108], + 0xFFB4 => [0x1121], 0xFFB5 => [0x1109], 0xFFB6 => [0x110A], + 0xFFB7 => [0x110B], 0xFFB8 => [0x110C], 0xFFB9 => [0x110D], + 0xFFBA => [0x110E], 0xFFBB => [0x110F], 0xFFBC => [0x1110], + 0xFFBD => [0x1111], 0xFFBE => [0x1112], 0xFFC2 => [0x1161], + 0xFFC3 => [0x1162], 0xFFC4 => [0x1163], 0xFFC5 => [0x1164], + 0xFFC6 => [0x1165], 0xFFC7 => [0x1166], 0xFFCA => [0x1167], + 0xFFCB => [0x1168], 0xFFCC => [0x1169], 0xFFCD => [0x116A], + 0xFFCE => [0x116B], 0xFFCF => [0x116C], 0xFFD2 => [0x116D], + 0xFFD3 => [0x116E], 0xFFD4 => [0x116F], 0xFFD5 => [0x1170], + 0xFFD6 => [0x1171], 0xFFD7 => [0x1172], 0xFFDA => [0x1173], + 0xFFDB => [0x1174], 0xFFDC => [0x1175], 0xFFE0 => [0xA2], + 0xFFE1 => [0xA3], 0xFFE2 => [0xAC], 0xFFE4 => [0xA6], + 0xFFE5 => [0xA5], 0xFFE6 => [0x20A9], 0xFFE8 => [0x2502], + 0xFFE9 => [0x2190], 0xFFEA => [0x2191], 0xFFEB => [0x2192], + 0xFFEC => [0x2193], 0xFFED => [0x25A0], 0xFFEE => [0x25CB], + 0x10400 => [0x10428], 0x10401 => [0x10429], 0x10402 => [0x1042A], + 0x10403 => [0x1042B], 0x10404 => [0x1042C], 0x10405 => [0x1042D], + 0x10406 => [0x1042E], 0x10407 => [0x1042F], 0x10408 => [0x10430], + 0x10409 => [0x10431], 0x1040A => [0x10432], 0x1040B => [0x10433], + 0x1040C => [0x10434], 0x1040D => [0x10435], 0x1040E => [0x10436], + 0x1040F => [0x10437], 0x10410 => [0x10438], 0x10411 => [0x10439], + 0x10412 => [0x1043A], 0x10413 => [0x1043B], 0x10414 => [0x1043C], + 0x10415 => [0x1043D], 0x10416 => [0x1043E], 0x10417 => [0x1043F], + 0x10418 => [0x10440], 0x10419 => [0x10441], 0x1041A => [0x10442], + 0x1041B => [0x10443], 0x1041C => [0x10444], 0x1041D => [0x10445], + 0x1041E => [0x10446], 0x1041F => [0x10447], 0x10420 => [0x10448], + 0x10421 => [0x10449], 0x10422 => [0x1044A], 0x10423 => [0x1044B], + 0x10424 => [0x1044C], 0x10425 => [0x1044D], 0x10426 => [0x1044E], + 0x10427 => [0x1044F], 0x118A0 => [0x118C0], 0x118A1 => [0x118C1], + 0x118A2 => [0x118C2], 0x118A3 => [0x118C3], 0x118A4 => [0x118C4], + 0x118A5 => [0x118C5], 0x118A6 => [0x118C6], 0x118A7 => [0x118C7], + 0x118A8 => [0x118C8], 0x118A9 => [0x118C9], 0x118AA => [0x118CA], + 0x118AB => [0x118CB], 0x118AC => [0x118CC], 0x118AD => [0x118CD], + 0x118AE => [0x118CE], 0x118AF => [0x118CF], 0x118B0 => [0x118D0], + 0x118B1 => [0x118D1], 0x118B2 => [0x118D2], 0x118B3 => [0x118D3], + 0x118B4 => [0x118D4], 0x118B5 => [0x118D5], 0x118B6 => [0x118D6], + 0x118B7 => [0x118D7], 0x118B8 => [0x118D8], 0x118B9 => [0x118D9], + 0x118BA => [0x118DA], 0x118BB => [0x118DB], 0x118BC => [0x118DC], + 0x118BD => [0x118DD], 0x118BE => [0x118DE], 0x118BF => [0x118DF], + 0x1D15E => [0x1D157, 0x1D165], 0x1D15F => [0x1D158, 0x1D165], 0x1D160 => [0x1D158, 0x1D165, 0x1D16E], + 0x1D161 => [0x1D158, 0x1D165, 0x1D16F], 0x1D162 => [0x1D158, 0x1D165, 0x1D170], 0x1D163 => [0x1D158, 0x1D165, 0x1D171], + 0x1D164 => [0x1D158, 0x1D165, 0x1D172], 0x1D1BB => [0x1D1B9, 0x1D165], 0x1D1BC => [0x1D1BA, 0x1D165], + 0x1D1BD => [0x1D1B9, 0x1D165, 0x1D16E], 0x1D1BE => [0x1D1BA, 0x1D165, 0x1D16E], 0x1D1BF => [0x1D1B9, 0x1D165, 0x1D16F], + 0x1D1C0 => [0x1D1BA, 0x1D165, 0x1D16F], 0x1D400 => [0x61], 0x1D401 => [0x62], + 0x1D402 => [0x63], 0x1D403 => [0x64], 0x1D404 => [0x65], + 0x1D405 => [0x66], 0x1D406 => [0x67], 0x1D407 => [0x68], + 0x1D408 => [0x69], 0x1D409 => [0x6A], 0x1D40A => [0x6B], + 0x1D40B => [0x6C], 0x1D40C => [0x6D], 0x1D40D => [0x6E], + 0x1D40E => [0x6F], 0x1D40F => [0x70], 0x1D410 => [0x71], + 0x1D411 => [0x72], 0x1D412 => [0x73], 0x1D413 => [0x74], + 0x1D414 => [0x75], 0x1D415 => [0x76], 0x1D416 => [0x77], + 0x1D417 => [0x78], 0x1D418 => [0x79], 0x1D419 => [0x7A], + 0x1D41A => [0x61], 0x1D41B => [0x62], 0x1D41C => [0x63], + 0x1D41D => [0x64], 0x1D41E => [0x65], 0x1D41F => [0x66], + 0x1D420 => [0x67], 0x1D421 => [0x68], 0x1D422 => [0x69], + 0x1D423 => [0x6A], 0x1D424 => [0x6B], 0x1D425 => [0x6C], + 0x1D426 => [0x6D], 0x1D427 => [0x6E], 0x1D428 => [0x6F], + 0x1D429 => [0x70], 0x1D42A => [0x71], 0x1D42B => [0x72], + 0x1D42C => [0x73], 0x1D42D => [0x74], 0x1D42E => [0x75], + 0x1D42F => [0x76], 0x1D430 => [0x77], 0x1D431 => [0x78], + 0x1D432 => [0x79], 0x1D433 => [0x7A], 0x1D434 => [0x61], + 0x1D435 => [0x62], 0x1D436 => [0x63], 0x1D437 => [0x64], + 0x1D438 => [0x65], 0x1D439 => [0x66], 0x1D43A => [0x67], + 0x1D43B => [0x68], 0x1D43C => [0x69], 0x1D43D => [0x6A], + 0x1D43E => [0x6B], 0x1D43F => [0x6C], 0x1D440 => [0x6D], + 0x1D441 => [0x6E], 0x1D442 => [0x6F], 0x1D443 => [0x70], + 0x1D444 => [0x71], 0x1D445 => [0x72], 0x1D446 => [0x73], + 0x1D447 => [0x74], 0x1D448 => [0x75], 0x1D449 => [0x76], + 0x1D44A => [0x77], 0x1D44B => [0x78], 0x1D44C => [0x79], + 0x1D44D => [0x7A], 0x1D44E => [0x61], 0x1D44F => [0x62], + 0x1D450 => [0x63], 0x1D451 => [0x64], 0x1D452 => [0x65], + 0x1D453 => [0x66], 0x1D454 => [0x67], 0x1D456 => [0x69], + 0x1D457 => [0x6A], 0x1D458 => [0x6B], 0x1D459 => [0x6C], + 0x1D45A => [0x6D], 0x1D45B => [0x6E], 0x1D45C => [0x6F], + 0x1D45D => [0x70], 0x1D45E => [0x71], 0x1D45F => [0x72], + 0x1D460 => [0x73], 0x1D461 => [0x74], 0x1D462 => [0x75], + 0x1D463 => [0x76], 0x1D464 => [0x77], 0x1D465 => [0x78], + 0x1D466 => [0x79], 0x1D467 => [0x7A], 0x1D468 => [0x61], + 0x1D469 => [0x62], 0x1D46A => [0x63], 0x1D46B => [0x64], + 0x1D46C => [0x65], 0x1D46D => [0x66], 0x1D46E => [0x67], + 0x1D46F => [0x68], 0x1D470 => [0x69], 0x1D471 => [0x6A], + 0x1D472 => [0x6B], 0x1D473 => [0x6C], 0x1D474 => [0x6D], + 0x1D475 => [0x6E], 0x1D476 => [0x6F], 0x1D477 => [0x70], + 0x1D478 => [0x71], 0x1D479 => [0x72], 0x1D47A => [0x73], + 0x1D47B => [0x74], 0x1D47C => [0x75], 0x1D47D => [0x76], + 0x1D47E => [0x77], 0x1D47F => [0x78], 0x1D480 => [0x79], + 0x1D481 => [0x7A], 0x1D482 => [0x61], 0x1D483 => [0x62], + 0x1D484 => [0x63], 0x1D485 => [0x64], 0x1D486 => [0x65], + 0x1D487 => [0x66], 0x1D488 => [0x67], 0x1D489 => [0x68], + 0x1D48A => [0x69], 0x1D48B => [0x6A], 0x1D48C => [0x6B], + 0x1D48D => [0x6C], 0x1D48E => [0x6D], 0x1D48F => [0x6E], + 0x1D490 => [0x6F], 0x1D491 => [0x70], 0x1D492 => [0x71], + 0x1D493 => [0x72], 0x1D494 => [0x73], 0x1D495 => [0x74], + 0x1D496 => [0x75], 0x1D497 => [0x76], 0x1D498 => [0x77], + 0x1D499 => [0x78], 0x1D49A => [0x79], 0x1D49B => [0x7A], + 0x1D49C => [0x61], 0x1D49E => [0x63], 0x1D49F => [0x64], + 0x1D4A2 => [0x67], 0x1D4A5 => [0x6A], 0x1D4A6 => [0x6B], + 0x1D4A9 => [0x6E], 0x1D4AA => [0x6F], 0x1D4AB => [0x70], + 0x1D4AC => [0x71], 0x1D4AE => [0x73], 0x1D4AF => [0x74], + 0x1D4B0 => [0x75], 0x1D4B1 => [0x76], 0x1D4B2 => [0x77], + 0x1D4B3 => [0x78], 0x1D4B4 => [0x79], 0x1D4B5 => [0x7A], + 0x1D4B6 => [0x61], 0x1D4B7 => [0x62], 0x1D4B8 => [0x63], + 0x1D4B9 => [0x64], 0x1D4BB => [0x66], 0x1D4BD => [0x68], + 0x1D4BE => [0x69], 0x1D4BF => [0x6A], 0x1D4C0 => [0x6B], + 0x1D4C1 => [0x6C], 0x1D4C2 => [0x6D], 0x1D4C3 => [0x6E], + 0x1D4C5 => [0x70], 0x1D4C6 => [0x71], 0x1D4C7 => [0x72], + 0x1D4C8 => [0x73], 0x1D4C9 => [0x74], 0x1D4CA => [0x75], + 0x1D4CB => [0x76], 0x1D4CC => [0x77], 0x1D4CD => [0x78], + 0x1D4CE => [0x79], 0x1D4CF => [0x7A], 0x1D4D0 => [0x61], + 0x1D4D1 => [0x62], 0x1D4D2 => [0x63], 0x1D4D3 => [0x64], + 0x1D4D4 => [0x65], 0x1D4D5 => [0x66], 0x1D4D6 => [0x67], + 0x1D4D7 => [0x68], 0x1D4D8 => [0x69], 0x1D4D9 => [0x6A], + 0x1D4DA => [0x6B], 0x1D4DB => [0x6C], 0x1D4DC => [0x6D], + 0x1D4DD => [0x6E], 0x1D4DE => [0x6F], 0x1D4DF => [0x70], + 0x1D4E0 => [0x71], 0x1D4E1 => [0x72], 0x1D4E2 => [0x73], + 0x1D4E3 => [0x74], 0x1D4E4 => [0x75], 0x1D4E5 => [0x76], + 0x1D4E6 => [0x77], 0x1D4E7 => [0x78], 0x1D4E8 => [0x79], + 0x1D4E9 => [0x7A], 0x1D4EA => [0x61], 0x1D4EB => [0x62], + 0x1D4EC => [0x63], 0x1D4ED => [0x64], 0x1D4EE => [0x65], + 0x1D4EF => [0x66], 0x1D4F0 => [0x67], 0x1D4F1 => [0x68], + 0x1D4F2 => [0x69], 0x1D4F3 => [0x6A], 0x1D4F4 => [0x6B], + 0x1D4F5 => [0x6C], 0x1D4F6 => [0x6D], 0x1D4F7 => [0x6E], + 0x1D4F8 => [0x6F], 0x1D4F9 => [0x70], 0x1D4FA => [0x71], + 0x1D4FB => [0x72], 0x1D4FC => [0x73], 0x1D4FD => [0x74], + 0x1D4FE => [0x75], 0x1D4FF => [0x76], 0x1D500 => [0x77], + 0x1D501 => [0x78], 0x1D502 => [0x79], 0x1D503 => [0x7A], + 0x1D504 => [0x61], 0x1D505 => [0x62], 0x1D507 => [0x64], + 0x1D508 => [0x65], 0x1D509 => [0x66], 0x1D50A => [0x67], + 0x1D50D => [0x6A], 0x1D50E => [0x6B], 0x1D50F => [0x6C], + 0x1D510 => [0x6D], 0x1D511 => [0x6E], 0x1D512 => [0x6F], + 0x1D513 => [0x70], 0x1D514 => [0x71], 0x1D516 => [0x73], + 0x1D517 => [0x74], 0x1D518 => [0x75], 0x1D519 => [0x76], + 0x1D51A => [0x77], 0x1D51B => [0x78], 0x1D51C => [0x79], + 0x1D51E => [0x61], 0x1D51F => [0x62], 0x1D520 => [0x63], + 0x1D521 => [0x64], 0x1D522 => [0x65], 0x1D523 => [0x66], + 0x1D524 => [0x67], 0x1D525 => [0x68], 0x1D526 => [0x69], + 0x1D527 => [0x6A], 0x1D528 => [0x6B], 0x1D529 => [0x6C], + 0x1D52A => [0x6D], 0x1D52B => [0x6E], 0x1D52C => [0x6F], + 0x1D52D => [0x70], 0x1D52E => [0x71], 0x1D52F => [0x72], + 0x1D530 => [0x73], 0x1D531 => [0x74], 0x1D532 => [0x75], + 0x1D533 => [0x76], 0x1D534 => [0x77], 0x1D535 => [0x78], + 0x1D536 => [0x79], 0x1D537 => [0x7A], 0x1D538 => [0x61], + 0x1D539 => [0x62], 0x1D53B => [0x64], 0x1D53C => [0x65], + 0x1D53D => [0x66], 0x1D53E => [0x67], 0x1D540 => [0x69], + 0x1D541 => [0x6A], 0x1D542 => [0x6B], 0x1D543 => [0x6C], + 0x1D544 => [0x6D], 0x1D546 => [0x6F], 0x1D54A => [0x73], + 0x1D54B => [0x74], 0x1D54C => [0x75], 0x1D54D => [0x76], + 0x1D54E => [0x77], 0x1D54F => [0x78], 0x1D550 => [0x79], + 0x1D552 => [0x61], 0x1D553 => [0x62], 0x1D554 => [0x63], + 0x1D555 => [0x64], 0x1D556 => [0x65], 0x1D557 => [0x66], + 0x1D558 => [0x67], 0x1D559 => [0x68], 0x1D55A => [0x69], + 0x1D55B => [0x6A], 0x1D55C => [0x6B], 0x1D55D => [0x6C], + 0x1D55E => [0x6D], 0x1D55F => [0x6E], 0x1D560 => [0x6F], + 0x1D561 => [0x70], 0x1D562 => [0x71], 0x1D563 => [0x72], + 0x1D564 => [0x73], 0x1D565 => [0x74], 0x1D566 => [0x75], + 0x1D567 => [0x76], 0x1D568 => [0x77], 0x1D569 => [0x78], + 0x1D56A => [0x79], 0x1D56B => [0x7A], 0x1D56C => [0x61], + 0x1D56D => [0x62], 0x1D56E => [0x63], 0x1D56F => [0x64], + 0x1D570 => [0x65], 0x1D571 => [0x66], 0x1D572 => [0x67], + 0x1D573 => [0x68], 0x1D574 => [0x69], 0x1D575 => [0x6A], + 0x1D576 => [0x6B], 0x1D577 => [0x6C], 0x1D578 => [0x6D], + 0x1D579 => [0x6E], 0x1D57A => [0x6F], 0x1D57B => [0x70], + 0x1D57C => [0x71], 0x1D57D => [0x72], 0x1D57E => [0x73], + 0x1D57F => [0x74], 0x1D580 => [0x75], 0x1D581 => [0x76], + 0x1D582 => [0x77], 0x1D583 => [0x78], 0x1D584 => [0x79], + 0x1D585 => [0x7A], 0x1D586 => [0x61], 0x1D587 => [0x62], + 0x1D588 => [0x63], 0x1D589 => [0x64], 0x1D58A => [0x65], + 0x1D58B => [0x66], 0x1D58C => [0x67], 0x1D58D => [0x68], + 0x1D58E => [0x69], 0x1D58F => [0x6A], 0x1D590 => [0x6B], + 0x1D591 => [0x6C], 0x1D592 => [0x6D], 0x1D593 => [0x6E], + 0x1D594 => [0x6F], 0x1D595 => [0x70], 0x1D596 => [0x71], + 0x1D597 => [0x72], 0x1D598 => [0x73], 0x1D599 => [0x74], + 0x1D59A => [0x75], 0x1D59B => [0x76], 0x1D59C => [0x77], + 0x1D59D => [0x78], 0x1D59E => [0x79], 0x1D59F => [0x7A], + 0x1D5A0 => [0x61], 0x1D5A1 => [0x62], 0x1D5A2 => [0x63], + 0x1D5A3 => [0x64], 0x1D5A4 => [0x65], 0x1D5A5 => [0x66], + 0x1D5A6 => [0x67], 0x1D5A7 => [0x68], 0x1D5A8 => [0x69], + 0x1D5A9 => [0x6A], 0x1D5AA => [0x6B], 0x1D5AB => [0x6C], + 0x1D5AC => [0x6D], 0x1D5AD => [0x6E], 0x1D5AE => [0x6F], + 0x1D5AF => [0x70], 0x1D5B0 => [0x71], 0x1D5B1 => [0x72], + 0x1D5B2 => [0x73], 0x1D5B3 => [0x74], 0x1D5B4 => [0x75], + 0x1D5B5 => [0x76], 0x1D5B6 => [0x77], 0x1D5B7 => [0x78], + 0x1D5B8 => [0x79], 0x1D5B9 => [0x7A], 0x1D5BA => [0x61], + 0x1D5BB => [0x62], 0x1D5BC => [0x63], 0x1D5BD => [0x64], + 0x1D5BE => [0x65], 0x1D5BF => [0x66], 0x1D5C0 => [0x67], + 0x1D5C1 => [0x68], 0x1D5C2 => [0x69], 0x1D5C3 => [0x6A], + 0x1D5C4 => [0x6B], 0x1D5C5 => [0x6C], 0x1D5C6 => [0x6D], + 0x1D5C7 => [0x6E], 0x1D5C8 => [0x6F], 0x1D5C9 => [0x70], + 0x1D5CA => [0x71], 0x1D5CB => [0x72], 0x1D5CC => [0x73], + 0x1D5CD => [0x74], 0x1D5CE => [0x75], 0x1D5CF => [0x76], + 0x1D5D0 => [0x77], 0x1D5D1 => [0x78], 0x1D5D2 => [0x79], + 0x1D5D3 => [0x7A], 0x1D5D4 => [0x61], 0x1D5D5 => [0x62], + 0x1D5D6 => [0x63], 0x1D5D7 => [0x64], 0x1D5D8 => [0x65], + 0x1D5D9 => [0x66], 0x1D5DA => [0x67], 0x1D5DB => [0x68], + 0x1D5DC => [0x69], 0x1D5DD => [0x6A], 0x1D5DE => [0x6B], + 0x1D5DF => [0x6C], 0x1D5E0 => [0x6D], 0x1D5E1 => [0x6E], + 0x1D5E2 => [0x6F], 0x1D5E3 => [0x70], 0x1D5E4 => [0x71], + 0x1D5E5 => [0x72], 0x1D5E6 => [0x73], 0x1D5E7 => [0x74], + 0x1D5E8 => [0x75], 0x1D5E9 => [0x76], 0x1D5EA => [0x77], + 0x1D5EB => [0x78], 0x1D5EC => [0x79], 0x1D5ED => [0x7A], + 0x1D5EE => [0x61], 0x1D5EF => [0x62], 0x1D5F0 => [0x63], + 0x1D5F1 => [0x64], 0x1D5F2 => [0x65], 0x1D5F3 => [0x66], + 0x1D5F4 => [0x67], 0x1D5F5 => [0x68], 0x1D5F6 => [0x69], + 0x1D5F7 => [0x6A], 0x1D5F8 => [0x6B], 0x1D5F9 => [0x6C], + 0x1D5FA => [0x6D], 0x1D5FB => [0x6E], 0x1D5FC => [0x6F], + 0x1D5FD => [0x70], 0x1D5FE => [0x71], 0x1D5FF => [0x72], + 0x1D600 => [0x73], 0x1D601 => [0x74], 0x1D602 => [0x75], + 0x1D603 => [0x76], 0x1D604 => [0x77], 0x1D605 => [0x78], + 0x1D606 => [0x79], 0x1D607 => [0x7A], 0x1D608 => [0x61], + 0x1D609 => [0x62], 0x1D60A => [0x63], 0x1D60B => [0x64], + 0x1D60C => [0x65], 0x1D60D => [0x66], 0x1D60E => [0x67], + 0x1D60F => [0x68], 0x1D610 => [0x69], 0x1D611 => [0x6A], + 0x1D612 => [0x6B], 0x1D613 => [0x6C], 0x1D614 => [0x6D], + 0x1D615 => [0x6E], 0x1D616 => [0x6F], 0x1D617 => [0x70], + 0x1D618 => [0x71], 0x1D619 => [0x72], 0x1D61A => [0x73], + 0x1D61B => [0x74], 0x1D61C => [0x75], 0x1D61D => [0x76], + 0x1D61E => [0x77], 0x1D61F => [0x78], 0x1D620 => [0x79], + 0x1D621 => [0x7A], 0x1D622 => [0x61], 0x1D623 => [0x62], + 0x1D624 => [0x63], 0x1D625 => [0x64], 0x1D626 => [0x65], + 0x1D627 => [0x66], 0x1D628 => [0x67], 0x1D629 => [0x68], + 0x1D62A => [0x69], 0x1D62B => [0x6A], 0x1D62C => [0x6B], + 0x1D62D => [0x6C], 0x1D62E => [0x6D], 0x1D62F => [0x6E], + 0x1D630 => [0x6F], 0x1D631 => [0x70], 0x1D632 => [0x71], + 0x1D633 => [0x72], 0x1D634 => [0x73], 0x1D635 => [0x74], + 0x1D636 => [0x75], 0x1D637 => [0x76], 0x1D638 => [0x77], + 0x1D639 => [0x78], 0x1D63A => [0x79], 0x1D63B => [0x7A], + 0x1D63C => [0x61], 0x1D63D => [0x62], 0x1D63E => [0x63], + 0x1D63F => [0x64], 0x1D640 => [0x65], 0x1D641 => [0x66], + 0x1D642 => [0x67], 0x1D643 => [0x68], 0x1D644 => [0x69], + 0x1D645 => [0x6A], 0x1D646 => [0x6B], 0x1D647 => [0x6C], + 0x1D648 => [0x6D], 0x1D649 => [0x6E], 0x1D64A => [0x6F], + 0x1D64B => [0x70], 0x1D64C => [0x71], 0x1D64D => [0x72], + 0x1D64E => [0x73], 0x1D64F => [0x74], 0x1D650 => [0x75], + 0x1D651 => [0x76], 0x1D652 => [0x77], 0x1D653 => [0x78], + 0x1D654 => [0x79], 0x1D655 => [0x7A], 0x1D656 => [0x61], + 0x1D657 => [0x62], 0x1D658 => [0x63], 0x1D659 => [0x64], + 0x1D65A => [0x65], 0x1D65B => [0x66], 0x1D65C => [0x67], + 0x1D65D => [0x68], 0x1D65E => [0x69], 0x1D65F => [0x6A], + 0x1D660 => [0x6B], 0x1D661 => [0x6C], 0x1D662 => [0x6D], + 0x1D663 => [0x6E], 0x1D664 => [0x6F], 0x1D665 => [0x70], + 0x1D666 => [0x71], 0x1D667 => [0x72], 0x1D668 => [0x73], + 0x1D669 => [0x74], 0x1D66A => [0x75], 0x1D66B => [0x76], + 0x1D66C => [0x77], 0x1D66D => [0x78], 0x1D66E => [0x79], + 0x1D66F => [0x7A], 0x1D670 => [0x61], 0x1D671 => [0x62], + 0x1D672 => [0x63], 0x1D673 => [0x64], 0x1D674 => [0x65], + 0x1D675 => [0x66], 0x1D676 => [0x67], 0x1D677 => [0x68], + 0x1D678 => [0x69], 0x1D679 => [0x6A], 0x1D67A => [0x6B], + 0x1D67B => [0x6C], 0x1D67C => [0x6D], 0x1D67D => [0x6E], + 0x1D67E => [0x6F], 0x1D67F => [0x70], 0x1D680 => [0x71], + 0x1D681 => [0x72], 0x1D682 => [0x73], 0x1D683 => [0x74], + 0x1D684 => [0x75], 0x1D685 => [0x76], 0x1D686 => [0x77], + 0x1D687 => [0x78], 0x1D688 => [0x79], 0x1D689 => [0x7A], + 0x1D68A => [0x61], 0x1D68B => [0x62], 0x1D68C => [0x63], + 0x1D68D => [0x64], 0x1D68E => [0x65], 0x1D68F => [0x66], + 0x1D690 => [0x67], 0x1D691 => [0x68], 0x1D692 => [0x69], + 0x1D693 => [0x6A], 0x1D694 => [0x6B], 0x1D695 => [0x6C], + 0x1D696 => [0x6D], 0x1D697 => [0x6E], 0x1D698 => [0x6F], + 0x1D699 => [0x70], 0x1D69A => [0x71], 0x1D69B => [0x72], + 0x1D69C => [0x73], 0x1D69D => [0x74], 0x1D69E => [0x75], + 0x1D69F => [0x76], 0x1D6A0 => [0x77], 0x1D6A1 => [0x78], + 0x1D6A2 => [0x79], 0x1D6A3 => [0x7A], 0x1D6A4 => [0x131], + 0x1D6A5 => [0x237], 0x1D6A8 => [0x3B1], 0x1D6A9 => [0x3B2], + 0x1D6AA => [0x3B3], 0x1D6AB => [0x3B4], 0x1D6AC => [0x3B5], + 0x1D6AD => [0x3B6], 0x1D6AE => [0x3B7], 0x1D6AF => [0x3B8], + 0x1D6B0 => [0x3B9], 0x1D6B1 => [0x3BA], 0x1D6B2 => [0x3BB], + 0x1D6B3 => [0x3BC], 0x1D6B4 => [0x3BD], 0x1D6B5 => [0x3BE], + 0x1D6B6 => [0x3BF], 0x1D6B7 => [0x3C0], 0x1D6B8 => [0x3C1], + 0x1D6B9 => [0x3B8], 0x1D6BA => [0x3C3], 0x1D6BB => [0x3C4], + 0x1D6BC => [0x3C5], 0x1D6BD => [0x3C6], 0x1D6BE => [0x3C7], + 0x1D6BF => [0x3C8], 0x1D6C0 => [0x3C9], 0x1D6C1 => [0x2207], + 0x1D6C2 => [0x3B1], 0x1D6C3 => [0x3B2], 0x1D6C4 => [0x3B3], + 0x1D6C5 => [0x3B4], 0x1D6C6 => [0x3B5], 0x1D6C7 => [0x3B6], + 0x1D6C8 => [0x3B7], 0x1D6C9 => [0x3B8], 0x1D6CA => [0x3B9], + 0x1D6CB => [0x3BA], 0x1D6CC => [0x3BB], 0x1D6CD => [0x3BC], + 0x1D6CE => [0x3BD], 0x1D6CF => [0x3BE], 0x1D6D0 => [0x3BF], + 0x1D6D1 => [0x3C0], 0x1D6D2 => [0x3C1], 0x1D6D3 => [0x3C3], + 0x1D6D4 => [0x3C3], 0x1D6D5 => [0x3C4], 0x1D6D6 => [0x3C5], + 0x1D6D7 => [0x3C6], 0x1D6D8 => [0x3C7], 0x1D6D9 => [0x3C8], + 0x1D6DA => [0x3C9], 0x1D6DB => [0x2202], 0x1D6DC => [0x3B5], + 0x1D6DD => [0x3B8], 0x1D6DE => [0x3BA], 0x1D6DF => [0x3C6], + 0x1D6E0 => [0x3C1], 0x1D6E1 => [0x3C0], 0x1D6E2 => [0x3B1], + 0x1D6E3 => [0x3B2], 0x1D6E4 => [0x3B3], 0x1D6E5 => [0x3B4], + 0x1D6E6 => [0x3B5], 0x1D6E7 => [0x3B6], 0x1D6E8 => [0x3B7], + 0x1D6E9 => [0x3B8], 0x1D6EA => [0x3B9], 0x1D6EB => [0x3BA], + 0x1D6EC => [0x3BB], 0x1D6ED => [0x3BC], 0x1D6EE => [0x3BD], + 0x1D6EF => [0x3BE], 0x1D6F0 => [0x3BF], 0x1D6F1 => [0x3C0], + 0x1D6F2 => [0x3C1], 0x1D6F3 => [0x3B8], 0x1D6F4 => [0x3C3], + 0x1D6F5 => [0x3C4], 0x1D6F6 => [0x3C5], 0x1D6F7 => [0x3C6], + 0x1D6F8 => [0x3C7], 0x1D6F9 => [0x3C8], 0x1D6FA => [0x3C9], + 0x1D6FB => [0x2207], 0x1D6FC => [0x3B1], 0x1D6FD => [0x3B2], + 0x1D6FE => [0x3B3], 0x1D6FF => [0x3B4], 0x1D700 => [0x3B5], + 0x1D701 => [0x3B6], 0x1D702 => [0x3B7], 0x1D703 => [0x3B8], + 0x1D704 => [0x3B9], 0x1D705 => [0x3BA], 0x1D706 => [0x3BB], + 0x1D707 => [0x3BC], 0x1D708 => [0x3BD], 0x1D709 => [0x3BE], + 0x1D70A => [0x3BF], 0x1D70B => [0x3C0], 0x1D70C => [0x3C1], + 0x1D70D => [0x3C3], 0x1D70E => [0x3C3], 0x1D70F => [0x3C4], + 0x1D710 => [0x3C5], 0x1D711 => [0x3C6], 0x1D712 => [0x3C7], + 0x1D713 => [0x3C8], 0x1D714 => [0x3C9], 0x1D715 => [0x2202], + 0x1D716 => [0x3B5], 0x1D717 => [0x3B8], 0x1D718 => [0x3BA], + 0x1D719 => [0x3C6], 0x1D71A => [0x3C1], 0x1D71B => [0x3C0], + 0x1D71C => [0x3B1], 0x1D71D => [0x3B2], 0x1D71E => [0x3B3], + 0x1D71F => [0x3B4], 0x1D720 => [0x3B5], 0x1D721 => [0x3B6], + 0x1D722 => [0x3B7], 0x1D723 => [0x3B8], 0x1D724 => [0x3B9], + 0x1D725 => [0x3BA], 0x1D726 => [0x3BB], 0x1D727 => [0x3BC], + 0x1D728 => [0x3BD], 0x1D729 => [0x3BE], 0x1D72A => [0x3BF], + 0x1D72B => [0x3C0], 0x1D72C => [0x3C1], 0x1D72D => [0x3B8], + 0x1D72E => [0x3C3], 0x1D72F => [0x3C4], 0x1D730 => [0x3C5], + 0x1D731 => [0x3C6], 0x1D732 => [0x3C7], 0x1D733 => [0x3C8], + 0x1D734 => [0x3C9], 0x1D735 => [0x2207], 0x1D736 => [0x3B1], + 0x1D737 => [0x3B2], 0x1D738 => [0x3B3], 0x1D739 => [0x3B4], + 0x1D73A => [0x3B5], 0x1D73B => [0x3B6], 0x1D73C => [0x3B7], + 0x1D73D => [0x3B8], 0x1D73E => [0x3B9], 0x1D73F => [0x3BA], + 0x1D740 => [0x3BB], 0x1D741 => [0x3BC], 0x1D742 => [0x3BD], + 0x1D743 => [0x3BE], 0x1D744 => [0x3BF], 0x1D745 => [0x3C0], + 0x1D746 => [0x3C1], 0x1D747 => [0x3C3], 0x1D748 => [0x3C3], + 0x1D749 => [0x3C4], 0x1D74A => [0x3C5], 0x1D74B => [0x3C6], + 0x1D74C => [0x3C7], 0x1D74D => [0x3C8], 0x1D74E => [0x3C9], + 0x1D74F => [0x2202], 0x1D750 => [0x3B5], 0x1D751 => [0x3B8], + 0x1D752 => [0x3BA], 0x1D753 => [0x3C6], 0x1D754 => [0x3C1], + 0x1D755 => [0x3C0], 0x1D756 => [0x3B1], 0x1D757 => [0x3B2], + 0x1D758 => [0x3B3], 0x1D759 => [0x3B4], 0x1D75A => [0x3B5], + 0x1D75B => [0x3B6], 0x1D75C => [0x3B7], 0x1D75D => [0x3B8], + 0x1D75E => [0x3B9], 0x1D75F => [0x3BA], 0x1D760 => [0x3BB], + 0x1D761 => [0x3BC], 0x1D762 => [0x3BD], 0x1D763 => [0x3BE], + 0x1D764 => [0x3BF], 0x1D765 => [0x3C0], 0x1D766 => [0x3C1], + 0x1D767 => [0x3B8], 0x1D768 => [0x3C3], 0x1D769 => [0x3C4], + 0x1D76A => [0x3C5], 0x1D76B => [0x3C6], 0x1D76C => [0x3C7], + 0x1D76D => [0x3C8], 0x1D76E => [0x3C9], 0x1D76F => [0x2207], + 0x1D770 => [0x3B1], 0x1D771 => [0x3B2], 0x1D772 => [0x3B3], + 0x1D773 => [0x3B4], 0x1D774 => [0x3B5], 0x1D775 => [0x3B6], + 0x1D776 => [0x3B7], 0x1D777 => [0x3B8], 0x1D778 => [0x3B9], + 0x1D779 => [0x3BA], 0x1D77A => [0x3BB], 0x1D77B => [0x3BC], + 0x1D77C => [0x3BD], 0x1D77D => [0x3BE], 0x1D77E => [0x3BF], + 0x1D77F => [0x3C0], 0x1D780 => [0x3C1], 0x1D781 => [0x3C3], + 0x1D782 => [0x3C3], 0x1D783 => [0x3C4], 0x1D784 => [0x3C5], + 0x1D785 => [0x3C6], 0x1D786 => [0x3C7], 0x1D787 => [0x3C8], + 0x1D788 => [0x3C9], 0x1D789 => [0x2202], 0x1D78A => [0x3B5], + 0x1D78B => [0x3B8], 0x1D78C => [0x3BA], 0x1D78D => [0x3C6], + 0x1D78E => [0x3C1], 0x1D78F => [0x3C0], 0x1D790 => [0x3B1], + 0x1D791 => [0x3B2], 0x1D792 => [0x3B3], 0x1D793 => [0x3B4], + 0x1D794 => [0x3B5], 0x1D795 => [0x3B6], 0x1D796 => [0x3B7], + 0x1D797 => [0x3B8], 0x1D798 => [0x3B9], 0x1D799 => [0x3BA], + 0x1D79A => [0x3BB], 0x1D79B => [0x3BC], 0x1D79C => [0x3BD], + 0x1D79D => [0x3BE], 0x1D79E => [0x3BF], 0x1D79F => [0x3C0], + 0x1D7A0 => [0x3C1], 0x1D7A1 => [0x3B8], 0x1D7A2 => [0x3C3], + 0x1D7A3 => [0x3C4], 0x1D7A4 => [0x3C5], 0x1D7A5 => [0x3C6], + 0x1D7A6 => [0x3C7], 0x1D7A7 => [0x3C8], 0x1D7A8 => [0x3C9], + 0x1D7A9 => [0x2207], 0x1D7AA => [0x3B1], 0x1D7AB => [0x3B2], + 0x1D7AC => [0x3B3], 0x1D7AD => [0x3B4], 0x1D7AE => [0x3B5], + 0x1D7AF => [0x3B6], 0x1D7B0 => [0x3B7], 0x1D7B1 => [0x3B8], + 0x1D7B2 => [0x3B9], 0x1D7B3 => [0x3BA], 0x1D7B4 => [0x3BB], + 0x1D7B5 => [0x3BC], 0x1D7B6 => [0x3BD], 0x1D7B7 => [0x3BE], + 0x1D7B8 => [0x3BF], 0x1D7B9 => [0x3C0], 0x1D7BA => [0x3C1], + 0x1D7BB => [0x3C3], 0x1D7BC => [0x3C3], 0x1D7BD => [0x3C4], + 0x1D7BE => [0x3C5], 0x1D7BF => [0x3C6], 0x1D7C0 => [0x3C7], + 0x1D7C1 => [0x3C8], 0x1D7C2 => [0x3C9], 0x1D7C3 => [0x2202], + 0x1D7C4 => [0x3B5], 0x1D7C5 => [0x3B8], 0x1D7C6 => [0x3BA], + 0x1D7C7 => [0x3C6], 0x1D7C8 => [0x3C1], 0x1D7C9 => [0x3C0], + 0x1D7CA => [0x3DD], 0x1D7CB => [0x3DD], 0x1D7CE => [0x30], + 0x1D7CF => [0x31], 0x1D7D0 => [0x32], 0x1D7D1 => [0x33], + 0x1D7D2 => [0x34], 0x1D7D3 => [0x35], 0x1D7D4 => [0x36], + 0x1D7D5 => [0x37], 0x1D7D6 => [0x38], 0x1D7D7 => [0x39], + 0x1D7D8 => [0x30], 0x1D7D9 => [0x31], 0x1D7DA => [0x32], + 0x1D7DB => [0x33], 0x1D7DC => [0x34], 0x1D7DD => [0x35], + 0x1D7DE => [0x36], 0x1D7DF => [0x37], 0x1D7E0 => [0x38], + 0x1D7E1 => [0x39], 0x1D7E2 => [0x30], 0x1D7E3 => [0x31], + 0x1D7E4 => [0x32], 0x1D7E5 => [0x33], 0x1D7E6 => [0x34], + 0x1D7E7 => [0x35], 0x1D7E8 => [0x36], 0x1D7E9 => [0x37], + 0x1D7EA => [0x38], 0x1D7EB => [0x39], 0x1D7EC => [0x30], + 0x1D7ED => [0x31], 0x1D7EE => [0x32], 0x1D7EF => [0x33], + 0x1D7F0 => [0x34], 0x1D7F1 => [0x35], 0x1D7F2 => [0x36], + 0x1D7F3 => [0x37], 0x1D7F4 => [0x38], 0x1D7F5 => [0x39], + 0x1D7F6 => [0x30], 0x1D7F7 => [0x31], 0x1D7F8 => [0x32], + 0x1D7F9 => [0x33], 0x1D7FA => [0x34], 0x1D7FB => [0x35], + 0x1D7FC => [0x36], 0x1D7FD => [0x37], 0x1D7FE => [0x38], + 0x1D7FF => [0x39], 0x1EE00 => [0x627], 0x1EE01 => [0x628], + 0x1EE02 => [0x62C], 0x1EE03 => [0x62F], 0x1EE05 => [0x648], + 0x1EE06 => [0x632], 0x1EE07 => [0x62D], 0x1EE08 => [0x637], + 0x1EE09 => [0x64A], 0x1EE0A => [0x643], 0x1EE0B => [0x644], + 0x1EE0C => [0x645], 0x1EE0D => [0x646], 0x1EE0E => [0x633], + 0x1EE0F => [0x639], 0x1EE10 => [0x641], 0x1EE11 => [0x635], + 0x1EE12 => [0x642], 0x1EE13 => [0x631], 0x1EE14 => [0x634], + 0x1EE15 => [0x62A], 0x1EE16 => [0x62B], 0x1EE17 => [0x62E], + 0x1EE18 => [0x630], 0x1EE19 => [0x636], 0x1EE1A => [0x638], + 0x1EE1B => [0x63A], 0x1EE1C => [0x66E], 0x1EE1D => [0x6BA], + 0x1EE1E => [0x6A1], 0x1EE1F => [0x66F], 0x1EE21 => [0x628], + 0x1EE22 => [0x62C], 0x1EE24 => [0x647], 0x1EE27 => [0x62D], + 0x1EE29 => [0x64A], 0x1EE2A => [0x643], 0x1EE2B => [0x644], + 0x1EE2C => [0x645], 0x1EE2D => [0x646], 0x1EE2E => [0x633], + 0x1EE2F => [0x639], 0x1EE30 => [0x641], 0x1EE31 => [0x635], + 0x1EE32 => [0x642], 0x1EE34 => [0x634], 0x1EE35 => [0x62A], + 0x1EE36 => [0x62B], 0x1EE37 => [0x62E], 0x1EE39 => [0x636], + 0x1EE3B => [0x63A], 0x1EE42 => [0x62C], 0x1EE47 => [0x62D], + 0x1EE49 => [0x64A], 0x1EE4B => [0x644], 0x1EE4D => [0x646], + 0x1EE4E => [0x633], 0x1EE4F => [0x639], 0x1EE51 => [0x635], + 0x1EE52 => [0x642], 0x1EE54 => [0x634], 0x1EE57 => [0x62E], + 0x1EE59 => [0x636], 0x1EE5B => [0x63A], 0x1EE5D => [0x6BA], + 0x1EE5F => [0x66F], 0x1EE61 => [0x628], 0x1EE62 => [0x62C], + 0x1EE64 => [0x647], 0x1EE67 => [0x62D], 0x1EE68 => [0x637], + 0x1EE69 => [0x64A], 0x1EE6A => [0x643], 0x1EE6C => [0x645], + 0x1EE6D => [0x646], 0x1EE6E => [0x633], 0x1EE6F => [0x639], + 0x1EE70 => [0x641], 0x1EE71 => [0x635], 0x1EE72 => [0x642], + 0x1EE74 => [0x634], 0x1EE75 => [0x62A], 0x1EE76 => [0x62B], + 0x1EE77 => [0x62E], 0x1EE79 => [0x636], 0x1EE7A => [0x638], + 0x1EE7B => [0x63A], 0x1EE7C => [0x66E], 0x1EE7E => [0x6A1], + 0x1EE80 => [0x627], 0x1EE81 => [0x628], 0x1EE82 => [0x62C], + 0x1EE83 => [0x62F], 0x1EE84 => [0x647], 0x1EE85 => [0x648], + 0x1EE86 => [0x632], 0x1EE87 => [0x62D], 0x1EE88 => [0x637], + 0x1EE89 => [0x64A], 0x1EE8B => [0x644], 0x1EE8C => [0x645], + 0x1EE8D => [0x646], 0x1EE8E => [0x633], 0x1EE8F => [0x639], + 0x1EE90 => [0x641], 0x1EE91 => [0x635], 0x1EE92 => [0x642], + 0x1EE93 => [0x631], 0x1EE94 => [0x634], 0x1EE95 => [0x62A], + 0x1EE96 => [0x62B], 0x1EE97 => [0x62E], 0x1EE98 => [0x630], + 0x1EE99 => [0x636], 0x1EE9A => [0x638], 0x1EE9B => [0x63A], + 0x1EEA1 => [0x628], 0x1EEA2 => [0x62C], 0x1EEA3 => [0x62F], + 0x1EEA5 => [0x648], 0x1EEA6 => [0x632], 0x1EEA7 => [0x62D], + 0x1EEA8 => [0x637], 0x1EEA9 => [0x64A], 0x1EEAB => [0x644], + 0x1EEAC => [0x645], 0x1EEAD => [0x646], 0x1EEAE => [0x633], + 0x1EEAF => [0x639], 0x1EEB0 => [0x641], 0x1EEB1 => [0x635], + 0x1EEB2 => [0x642], 0x1EEB3 => [0x631], 0x1EEB4 => [0x634], + 0x1EEB5 => [0x62A], 0x1EEB6 => [0x62B], 0x1EEB7 => [0x62E], + 0x1EEB8 => [0x630], 0x1EEB9 => [0x636], 0x1EEBA => [0x638], + 0x1EEBB => [0x63A], 0x1F12A => [0x3014, 0x73, 0x3015], 0x1F12B => [0x63], + 0x1F12C => [0x72], 0x1F12D => [0x63, 0x64], 0x1F12E => [0x77, 0x7A], + 0x1F130 => [0x61], 0x1F131 => [0x62], 0x1F132 => [0x63], + 0x1F133 => [0x64], 0x1F134 => [0x65], 0x1F135 => [0x66], + 0x1F136 => [0x67], 0x1F137 => [0x68], 0x1F138 => [0x69], + 0x1F139 => [0x6A], 0x1F13A => [0x6B], 0x1F13B => [0x6C], + 0x1F13C => [0x6D], 0x1F13D => [0x6E], 0x1F13E => [0x6F], + 0x1F13F => [0x70], 0x1F140 => [0x71], 0x1F141 => [0x72], + 0x1F142 => [0x73], 0x1F143 => [0x74], 0x1F144 => [0x75], + 0x1F145 => [0x76], 0x1F146 => [0x77], 0x1F147 => [0x78], + 0x1F148 => [0x79], 0x1F149 => [0x7A], 0x1F14A => [0x68, 0x76], + 0x1F14B => [0x6D, 0x76], 0x1F14C => [0x73, 0x64], 0x1F14D => [0x73, 0x73], + 0x1F14E => [0x70, 0x70, 0x76], 0x1F14F => [0x77, 0x63], 0x1F16A => [0x6D, 0x63], + 0x1F16B => [0x6D, 0x64], 0x1F190 => [0x64, 0x6A], 0x1F200 => [0x307B, 0x304B], + 0x1F201 => [0x30B3, 0x30B3], 0x1F202 => [0x30B5], 0x1F210 => [0x624B], + 0x1F211 => [0x5B57], 0x1F212 => [0x53CC], 0x1F213 => [0x30C7], + 0x1F214 => [0x4E8C], 0x1F215 => [0x591A], 0x1F216 => [0x89E3], + 0x1F217 => [0x5929], 0x1F218 => [0x4EA4], 0x1F219 => [0x6620], + 0x1F21A => [0x7121], 0x1F21B => [0x6599], 0x1F21C => [0x524D], + 0x1F21D => [0x5F8C], 0x1F21E => [0x518D], 0x1F21F => [0x65B0], + 0x1F220 => [0x521D], 0x1F221 => [0x7D42], 0x1F222 => [0x751F], + 0x1F223 => [0x8CA9], 0x1F224 => [0x58F0], 0x1F225 => [0x5439], + 0x1F226 => [0x6F14], 0x1F227 => [0x6295], 0x1F228 => [0x6355], + 0x1F229 => [0x4E00], 0x1F22A => [0x4E09], 0x1F22B => [0x904A], + 0x1F22C => [0x5DE6], 0x1F22D => [0x4E2D], 0x1F22E => [0x53F3], + 0x1F22F => [0x6307], 0x1F230 => [0x8D70], 0x1F231 => [0x6253], + 0x1F232 => [0x7981], 0x1F233 => [0x7A7A], 0x1F234 => [0x5408], + 0x1F235 => [0x6E80], 0x1F236 => [0x6709], 0x1F237 => [0x6708], + 0x1F238 => [0x7533], 0x1F239 => [0x5272], 0x1F23A => [0x55B6], + 0x1F240 => [0x3014, 0x672C, 0x3015], 0x1F241 => [0x3014, 0x4E09, 0x3015], 0x1F242 => [0x3014, 0x4E8C, 0x3015], + 0x1F243 => [0x3014, 0x5B89, 0x3015], 0x1F244 => [0x3014, 0x70B9, 0x3015], 0x1F245 => [0x3014, 0x6253, 0x3015], + 0x1F246 => [0x3014, 0x76D7, 0x3015], 0x1F247 => [0x3014, 0x52DD, 0x3015], 0x1F248 => [0x3014, 0x6557, 0x3015], + 0x1F250 => [0x5F97], 0x1F251 => [0x53EF], 0x2F800 => [0x4E3D], + 0x2F801 => [0x4E38], 0x2F802 => [0x4E41], 0x2F803 => [0x20122], + 0x2F804 => [0x4F60], 0x2F805 => [0x4FAE], 0x2F806 => [0x4FBB], + 0x2F807 => [0x5002], 0x2F808 => [0x507A], 0x2F809 => [0x5099], + 0x2F80A => [0x50E7], 0x2F80B => [0x50CF], 0x2F80C => [0x349E], + 0x2F80D => [0x2063A], 0x2F80E => [0x514D], 0x2F80F => [0x5154], + 0x2F810 => [0x5164], 0x2F811 => [0x5177], 0x2F812 => [0x2051C], + 0x2F813 => [0x34B9], 0x2F814 => [0x5167], 0x2F815 => [0x518D], + 0x2F816 => [0x2054B], 0x2F817 => [0x5197], 0x2F818 => [0x51A4], + 0x2F819 => [0x4ECC], 0x2F81A => [0x51AC], 0x2F81B => [0x51B5], + 0x2F81C => [0x291DF], 0x2F81D => [0x51F5], 0x2F81E => [0x5203], + 0x2F81F => [0x34DF], 0x2F820 => [0x523B], 0x2F821 => [0x5246], + 0x2F822 => [0x5272], 0x2F823 => [0x5277], 0x2F824 => [0x3515], + 0x2F825 => [0x52C7], 0x2F826 => [0x52C9], 0x2F827 => [0x52E4], + 0x2F828 => [0x52FA], 0x2F829 => [0x5305], 0x2F82A => [0x5306], + 0x2F82B => [0x5317], 0x2F82C => [0x5349], 0x2F82D => [0x5351], + 0x2F82E => [0x535A], 0x2F82F => [0x5373], 0x2F830 => [0x537D], + 0x2F831 => [0x537F], 0x2F832 => [0x537F], 0x2F833 => [0x537F], + 0x2F834 => [0x20A2C], 0x2F835 => [0x7070], 0x2F836 => [0x53CA], + 0x2F837 => [0x53DF], 0x2F838 => [0x20B63], 0x2F839 => [0x53EB], + 0x2F83A => [0x53F1], 0x2F83B => [0x5406], 0x2F83C => [0x549E], + 0x2F83D => [0x5438], 0x2F83E => [0x5448], 0x2F83F => [0x5468], + 0x2F840 => [0x54A2], 0x2F841 => [0x54F6], 0x2F842 => [0x5510], + 0x2F843 => [0x5553], 0x2F844 => [0x5563], 0x2F845 => [0x5584], + 0x2F846 => [0x5584], 0x2F847 => [0x5599], 0x2F848 => [0x55AB], + 0x2F849 => [0x55B3], 0x2F84A => [0x55C2], 0x2F84B => [0x5716], + 0x2F84C => [0x5606], 0x2F84D => [0x5717], 0x2F84E => [0x5651], + 0x2F84F => [0x5674], 0x2F850 => [0x5207], 0x2F851 => [0x58EE], + 0x2F852 => [0x57CE], 0x2F853 => [0x57F4], 0x2F854 => [0x580D], + 0x2F855 => [0x578B], 0x2F856 => [0x5832], 0x2F857 => [0x5831], + 0x2F858 => [0x58AC], 0x2F859 => [0x214E4], 0x2F85A => [0x58F2], + 0x2F85B => [0x58F7], 0x2F85C => [0x5906], 0x2F85D => [0x591A], + 0x2F85E => [0x5922], 0x2F85F => [0x5962], 0x2F860 => [0x216A8], + 0x2F861 => [0x216EA], 0x2F862 => [0x59EC], 0x2F863 => [0x5A1B], + 0x2F864 => [0x5A27], 0x2F865 => [0x59D8], 0x2F866 => [0x5A66], + 0x2F867 => [0x36EE], 0x2F869 => [0x5B08], 0x2F86A => [0x5B3E], + 0x2F86B => [0x5B3E], 0x2F86C => [0x219C8], 0x2F86D => [0x5BC3], + 0x2F86E => [0x5BD8], 0x2F86F => [0x5BE7], 0x2F870 => [0x5BF3], + 0x2F871 => [0x21B18], 0x2F872 => [0x5BFF], 0x2F873 => [0x5C06], + 0x2F875 => [0x5C22], 0x2F876 => [0x3781], 0x2F877 => [0x5C60], + 0x2F878 => [0x5C6E], 0x2F879 => [0x5CC0], 0x2F87A => [0x5C8D], + 0x2F87B => [0x21DE4], 0x2F87C => [0x5D43], 0x2F87D => [0x21DE6], + 0x2F87E => [0x5D6E], 0x2F87F => [0x5D6B], 0x2F880 => [0x5D7C], + 0x2F881 => [0x5DE1], 0x2F882 => [0x5DE2], 0x2F883 => [0x382F], + 0x2F884 => [0x5DFD], 0x2F885 => [0x5E28], 0x2F886 => [0x5E3D], + 0x2F887 => [0x5E69], 0x2F888 => [0x3862], 0x2F889 => [0x22183], + 0x2F88A => [0x387C], 0x2F88B => [0x5EB0], 0x2F88C => [0x5EB3], + 0x2F88D => [0x5EB6], 0x2F88E => [0x5ECA], 0x2F88F => [0x2A392], + 0x2F890 => [0x5EFE], 0x2F891 => [0x22331], 0x2F892 => [0x22331], + 0x2F893 => [0x8201], 0x2F894 => [0x5F22], 0x2F895 => [0x5F22], + 0x2F896 => [0x38C7], 0x2F897 => [0x232B8], 0x2F898 => [0x261DA], + 0x2F899 => [0x5F62], 0x2F89A => [0x5F6B], 0x2F89B => [0x38E3], + 0x2F89C => [0x5F9A], 0x2F89D => [0x5FCD], 0x2F89E => [0x5FD7], + 0x2F89F => [0x5FF9], 0x2F8A0 => [0x6081], 0x2F8A1 => [0x393A], + 0x2F8A2 => [0x391C], 0x2F8A3 => [0x6094], 0x2F8A4 => [0x226D4], + 0x2F8A5 => [0x60C7], 0x2F8A6 => [0x6148], 0x2F8A7 => [0x614C], + 0x2F8A8 => [0x614E], 0x2F8A9 => [0x614C], 0x2F8AA => [0x617A], + 0x2F8AB => [0x618E], 0x2F8AC => [0x61B2], 0x2F8AD => [0x61A4], + 0x2F8AE => [0x61AF], 0x2F8AF => [0x61DE], 0x2F8B0 => [0x61F2], + 0x2F8B1 => [0x61F6], 0x2F8B2 => [0x6210], 0x2F8B3 => [0x621B], + 0x2F8B4 => [0x625D], 0x2F8B5 => [0x62B1], 0x2F8B6 => [0x62D4], + 0x2F8B7 => [0x6350], 0x2F8B8 => [0x22B0C], 0x2F8B9 => [0x633D], + 0x2F8BA => [0x62FC], 0x2F8BB => [0x6368], 0x2F8BC => [0x6383], + 0x2F8BD => [0x63E4], 0x2F8BE => [0x22BF1], 0x2F8BF => [0x6422], + 0x2F8C0 => [0x63C5], 0x2F8C1 => [0x63A9], 0x2F8C2 => [0x3A2E], + 0x2F8C3 => [0x6469], 0x2F8C4 => [0x647E], 0x2F8C5 => [0x649D], + 0x2F8C6 => [0x6477], 0x2F8C7 => [0x3A6C], 0x2F8C8 => [0x654F], + 0x2F8C9 => [0x656C], 0x2F8CA => [0x2300A], 0x2F8CB => [0x65E3], + 0x2F8CC => [0x66F8], 0x2F8CD => [0x6649], 0x2F8CE => [0x3B19], + 0x2F8CF => [0x6691], 0x2F8D0 => [0x3B08], 0x2F8D1 => [0x3AE4], + 0x2F8D2 => [0x5192], 0x2F8D3 => [0x5195], 0x2F8D4 => [0x6700], + 0x2F8D5 => [0x669C], 0x2F8D6 => [0x80AD], 0x2F8D7 => [0x43D9], + 0x2F8D8 => [0x6717], 0x2F8D9 => [0x671B], 0x2F8DA => [0x6721], + 0x2F8DB => [0x675E], 0x2F8DC => [0x6753], 0x2F8DD => [0x233C3], + 0x2F8DE => [0x3B49], 0x2F8DF => [0x67FA], 0x2F8E0 => [0x6785], + 0x2F8E1 => [0x6852], 0x2F8E2 => [0x6885], 0x2F8E3 => [0x2346D], + 0x2F8E4 => [0x688E], 0x2F8E5 => [0x681F], 0x2F8E6 => [0x6914], + 0x2F8E7 => [0x3B9D], 0x2F8E8 => [0x6942], 0x2F8E9 => [0x69A3], + 0x2F8EA => [0x69EA], 0x2F8EB => [0x6AA8], 0x2F8EC => [0x236A3], + 0x2F8ED => [0x6ADB], 0x2F8EE => [0x3C18], 0x2F8EF => [0x6B21], + 0x2F8F0 => [0x238A7], 0x2F8F1 => [0x6B54], 0x2F8F2 => [0x3C4E], + 0x2F8F3 => [0x6B72], 0x2F8F4 => [0x6B9F], 0x2F8F5 => [0x6BBA], + 0x2F8F6 => [0x6BBB], 0x2F8F7 => [0x23A8D], 0x2F8F8 => [0x21D0B], + 0x2F8F9 => [0x23AFA], 0x2F8FA => [0x6C4E], 0x2F8FB => [0x23CBC], + 0x2F8FC => [0x6CBF], 0x2F8FD => [0x6CCD], 0x2F8FE => [0x6C67], + 0x2F8FF => [0x6D16], 0x2F900 => [0x6D3E], 0x2F901 => [0x6D77], + 0x2F902 => [0x6D41], 0x2F903 => [0x6D69], 0x2F904 => [0x6D78], + 0x2F905 => [0x6D85], 0x2F906 => [0x23D1E], 0x2F907 => [0x6D34], + 0x2F908 => [0x6E2F], 0x2F909 => [0x6E6E], 0x2F90A => [0x3D33], + 0x2F90B => [0x6ECB], 0x2F90C => [0x6EC7], 0x2F90D => [0x23ED1], + 0x2F90E => [0x6DF9], 0x2F90F => [0x6F6E], 0x2F910 => [0x23F5E], + 0x2F911 => [0x23F8E], 0x2F912 => [0x6FC6], 0x2F913 => [0x7039], + 0x2F914 => [0x701E], 0x2F915 => [0x701B], 0x2F916 => [0x3D96], + 0x2F917 => [0x704A], 0x2F918 => [0x707D], 0x2F919 => [0x7077], + 0x2F91A => [0x70AD], 0x2F91B => [0x20525], 0x2F91C => [0x7145], + 0x2F91D => [0x24263], 0x2F91E => [0x719C], 0x2F920 => [0x7228], + 0x2F921 => [0x7235], 0x2F922 => [0x7250], 0x2F923 => [0x24608], + 0x2F924 => [0x7280], 0x2F925 => [0x7295], 0x2F926 => [0x24735], + 0x2F927 => [0x24814], 0x2F928 => [0x737A], 0x2F929 => [0x738B], + 0x2F92A => [0x3EAC], 0x2F92B => [0x73A5], 0x2F92C => [0x3EB8], + 0x2F92D => [0x3EB8], 0x2F92E => [0x7447], 0x2F92F => [0x745C], + 0x2F930 => [0x7471], 0x2F931 => [0x7485], 0x2F932 => [0x74CA], + 0x2F933 => [0x3F1B], 0x2F934 => [0x7524], 0x2F935 => [0x24C36], + 0x2F936 => [0x753E], 0x2F937 => [0x24C92], 0x2F938 => [0x7570], + 0x2F939 => [0x2219F], 0x2F93A => [0x7610], 0x2F93B => [0x24FA1], + 0x2F93C => [0x24FB8], 0x2F93D => [0x25044], 0x2F93E => [0x3FFC], + 0x2F93F => [0x4008], 0x2F940 => [0x76F4], 0x2F941 => [0x250F3], + 0x2F942 => [0x250F2], 0x2F943 => [0x25119], 0x2F944 => [0x25133], + 0x2F945 => [0x771E], 0x2F946 => [0x771F], 0x2F947 => [0x771F], + 0x2F948 => [0x774A], 0x2F949 => [0x4039], 0x2F94A => [0x778B], + 0x2F94B => [0x4046], 0x2F94C => [0x4096], 0x2F94D => [0x2541D], + 0x2F94E => [0x784E], 0x2F94F => [0x788C], 0x2F950 => [0x78CC], + 0x2F951 => [0x40E3], 0x2F952 => [0x25626], 0x2F953 => [0x7956], + 0x2F954 => [0x2569A], 0x2F955 => [0x256C5], 0x2F956 => [0x798F], + 0x2F957 => [0x79EB], 0x2F958 => [0x412F], 0x2F959 => [0x7A40], + 0x2F95A => [0x7A4A], 0x2F95B => [0x7A4F], 0x2F95C => [0x2597C], + 0x2F95D => [0x25AA7], 0x2F95E => [0x25AA7], 0x2F960 => [0x4202], + 0x2F961 => [0x25BAB], 0x2F962 => [0x7BC6], 0x2F963 => [0x7BC9], + 0x2F964 => [0x4227], 0x2F965 => [0x25C80], 0x2F966 => [0x7CD2], + 0x2F967 => [0x42A0], 0x2F968 => [0x7CE8], 0x2F969 => [0x7CE3], + 0x2F96A => [0x7D00], 0x2F96B => [0x25F86], 0x2F96C => [0x7D63], + 0x2F96D => [0x4301], 0x2F96E => [0x7DC7], 0x2F96F => [0x7E02], + 0x2F970 => [0x7E45], 0x2F971 => [0x4334], 0x2F972 => [0x26228], + 0x2F973 => [0x26247], 0x2F974 => [0x4359], 0x2F975 => [0x262D9], + 0x2F976 => [0x7F7A], 0x2F977 => [0x2633E], 0x2F978 => [0x7F95], + 0x2F979 => [0x7FFA], 0x2F97A => [0x8005], 0x2F97B => [0x264DA], + 0x2F97C => [0x26523], 0x2F97D => [0x8060], 0x2F97E => [0x265A8], + 0x2F97F => [0x8070], 0x2F980 => [0x2335F], 0x2F981 => [0x43D5], + 0x2F982 => [0x80B2], 0x2F983 => [0x8103], 0x2F984 => [0x440B], + 0x2F985 => [0x813E], 0x2F986 => [0x5AB5], 0x2F987 => [0x267A7], + 0x2F988 => [0x267B5], 0x2F989 => [0x23393], 0x2F98A => [0x2339C], + 0x2F98B => [0x8201], 0x2F98C => [0x8204], 0x2F98D => [0x8F9E], + 0x2F98E => [0x446B], 0x2F98F => [0x8291], 0x2F990 => [0x828B], + 0x2F991 => [0x829D], 0x2F992 => [0x52B3], 0x2F993 => [0x82B1], + 0x2F994 => [0x82B3], 0x2F995 => [0x82BD], 0x2F996 => [0x82E6], + 0x2F997 => [0x26B3C], 0x2F998 => [0x82E5], 0x2F999 => [0x831D], + 0x2F99A => [0x8363], 0x2F99B => [0x83AD], 0x2F99C => [0x8323], + 0x2F99D => [0x83BD], 0x2F99E => [0x83E7], 0x2F99F => [0x8457], + 0x2F9A0 => [0x8353], 0x2F9A1 => [0x83CA], 0x2F9A2 => [0x83CC], + 0x2F9A3 => [0x83DC], 0x2F9A4 => [0x26C36], 0x2F9A5 => [0x26D6B], + 0x2F9A6 => [0x26CD5], 0x2F9A7 => [0x452B], 0x2F9A8 => [0x84F1], + 0x2F9A9 => [0x84F3], 0x2F9AA => [0x8516], 0x2F9AB => [0x273CA], + 0x2F9AC => [0x8564], 0x2F9AD => [0x26F2C], 0x2F9AE => [0x455D], + 0x2F9AF => [0x4561], 0x2F9B0 => [0x26FB1], 0x2F9B1 => [0x270D2], + 0x2F9B2 => [0x456B], 0x2F9B3 => [0x8650], 0x2F9B4 => [0x865C], + 0x2F9B5 => [0x8667], 0x2F9B6 => [0x8669], 0x2F9B7 => [0x86A9], + 0x2F9B8 => [0x8688], 0x2F9B9 => [0x870E], 0x2F9BA => [0x86E2], + 0x2F9BB => [0x8779], 0x2F9BC => [0x8728], 0x2F9BD => [0x876B], + 0x2F9BE => [0x8786], 0x2F9C0 => [0x87E1], 0x2F9C1 => [0x8801], + 0x2F9C2 => [0x45F9], 0x2F9C3 => [0x8860], 0x2F9C4 => [0x8863], + 0x2F9C5 => [0x27667], 0x2F9C6 => [0x88D7], 0x2F9C7 => [0x88DE], + 0x2F9C8 => [0x4635], 0x2F9C9 => [0x88FA], 0x2F9CA => [0x34BB], + 0x2F9CB => [0x278AE], 0x2F9CC => [0x27966], 0x2F9CD => [0x46BE], + 0x2F9CE => [0x46C7], 0x2F9CF => [0x8AA0], 0x2F9D0 => [0x8AED], + 0x2F9D1 => [0x8B8A], 0x2F9D2 => [0x8C55], 0x2F9D3 => [0x27CA8], + 0x2F9D4 => [0x8CAB], 0x2F9D5 => [0x8CC1], 0x2F9D6 => [0x8D1B], + 0x2F9D7 => [0x8D77], 0x2F9D8 => [0x27F2F], 0x2F9D9 => [0x20804], + 0x2F9DA => [0x8DCB], 0x2F9DB => [0x8DBC], 0x2F9DC => [0x8DF0], + 0x2F9DD => [0x208DE], 0x2F9DE => [0x8ED4], 0x2F9DF => [0x8F38], + 0x2F9E0 => [0x285D2], 0x2F9E1 => [0x285ED], 0x2F9E2 => [0x9094], + 0x2F9E3 => [0x90F1], 0x2F9E4 => [0x9111], 0x2F9E5 => [0x2872E], + 0x2F9E6 => [0x911B], 0x2F9E7 => [0x9238], 0x2F9E8 => [0x92D7], + 0x2F9E9 => [0x92D8], 0x2F9EA => [0x927C], 0x2F9EB => [0x93F9], + 0x2F9EC => [0x9415], 0x2F9ED => [0x28BFA], 0x2F9EE => [0x958B], + 0x2F9EF => [0x4995], 0x2F9F0 => [0x95B7], 0x2F9F1 => [0x28D77], + 0x2F9F2 => [0x49E6], 0x2F9F3 => [0x96C3], 0x2F9F4 => [0x5DB2], + 0x2F9F5 => [0x9723], 0x2F9F6 => [0x29145], 0x2F9F7 => [0x2921A], + 0x2F9F8 => [0x4A6E], 0x2F9F9 => [0x4A76], 0x2F9FA => [0x97E0], + 0x2F9FB => [0x2940A], 0x2F9FC => [0x4AB2], 0x2F9FD => [0x29496], + 0x2F9FE => [0x980B], 0x2F9FF => [0x980B], 0x2FA00 => [0x9829], + 0x2FA01 => [0x295B6], 0x2FA02 => [0x98E2], 0x2FA03 => [0x4B33], + 0x2FA04 => [0x9929], 0x2FA05 => [0x99A7], 0x2FA06 => [0x99C2], + 0x2FA07 => [0x99FE], 0x2FA08 => [0x4BCE], 0x2FA09 => [0x29B30], + 0x2FA0A => [0x9B12], 0x2FA0B => [0x9C40], 0x2FA0C => [0x9CFD], + 0x2FA0D => [0x4CCE], 0x2FA0E => [0x4CED], 0x2FA0F => [0x9D67], + 0x2FA10 => [0x2A0CE], 0x2FA11 => [0x4CF8], 0x2FA12 => [0x2A105], + 0x2FA13 => [0x2A20E], 0x2FA14 => [0x2A291], 0x2FA15 => [0x9EBB], + 0x2FA16 => [0x4D56], 0x2FA17 => [0x9EF9], 0x2FA18 => [0x9EFE], + 0x2FA19 => [0x9F05], 0x2FA1A => [0x9F0F], 0x2FA1B => [0x9F16], + 0x2FA1C => [0x9F3B], 0x2FA1D => [0x2A600] + ]; + public $normalizeCombiningClasses = [ + 0x334 => 1, 0x335 => 1, 0x336 => 1, 0x337 => 1, + 0x338 => 1, 0x93C => 7, 0x9BC => 7, 0xA3C => 7, 0xABC => 7, + 0xB3C => 7, 0xCBC => 7, 0x1037 => 7, 0x3099 => 8, 0x309A => 8, + 0x94D => 9, 0x9CD => 9, 0xA4D => 9, 0xACD => 9, 0xB4D => 9, + 0xBCD => 9, 0xC4D => 9, 0xCCD => 9, 0xD4D => 9, 0xDCA => 9, + 0xE3A => 9, 0xF84 => 9, 0x1039 => 9, 0x1714 => 9, 0x1734 => 9, + 0x17D2 => 9, 0x5B0 => 10, 0x5B1 => 11, 0x5B2 => 12, 0x5B3 => 13, + 0x5B4 => 14, 0x5B5 => 15, 0x5B6 => 16, 0x5B7 => 17, 0x5B8 => 18, + 0x5B9 => 19, 0x5BB => 20, 0x5Bc => 21, 0x5BD => 22, 0x5BF => 23, + 0x5C1 => 24, 0x5C2 => 25, 0xFB1E => 26, 0x64B => 27, 0x64C => 28, + 0x64D => 29, 0x64E => 30, 0x64F => 31, 0x650 => 32, 0x651 => 33, + 0x652 => 34, 0x670 => 35, 0x711 => 36, 0xC55 => 84, 0xC56 => 91, + 0xE38 => 103, 0xE39 => 103, 0xE48 => 107, 0xE49 => 107, 0xE4A => 107, + 0xE4B => 107, 0xEB8 => 118, 0xEB9 => 118, 0xEC8 => 122, 0xEC9 => 122, + 0xECA => 122, 0xECB => 122, 0xF71 => 129, 0xF72 => 130, 0xF7A => 130, + 0xF7B => 130, 0xF7C => 130, 0xF7D => 130, 0xF80 => 130, 0xF74 => 132, + 0x321 => 202, 0x322 => 202, 0x327 => 202, 0x328 => 202, 0x31B => 216, + 0xF39 => 216, 0x1D165 => 216, 0x1D166 => 216, 0x1D16E => 216, 0x1D16F => 216, + 0x1D170 => 216, 0x1D171 => 216, 0x1D172 => 216, 0x302A => 218, 0x316 => 220, + 0x317 => 220, 0x318 => 220, 0x319 => 220, 0x31C => 220, 0x31D => 220, + 0x31E => 220, 0x31F => 220, 0x320 => 220, 0x323 => 220, 0x324 => 220, + 0x325 => 220, 0x326 => 220, 0x329 => 220, 0x32A => 220, 0x32B => 220, + 0x32C => 220, 0x32D => 220, 0x32E => 220, 0x32F => 220, 0x330 => 220, + 0x331 => 220, 0x332 => 220, 0x333 => 220, 0x339 => 220, 0x33A => 220, + 0x33B => 220, 0x33C => 220, 0x347 => 220, 0x348 => 220, 0x349 => 220, + 0x34D => 220, 0x34E => 220, 0x353 => 220, 0x354 => 220, 0x355 => 220, + 0x356 => 220, 0x591 => 220, 0x596 => 220, 0x59B => 220, 0x5A3 => 220, + 0x5A4 => 220, 0x5A5 => 220, 0x5A6 => 220, 0x5A7 => 220, 0x5AA => 220, + 0x655 => 220, 0x656 => 220, 0x6E3 => 220, 0x6EA => 220, 0x6ED => 220, + 0x731 => 220, 0x734 => 220, 0x737 => 220, 0x738 => 220, 0x739 => 220, + 0x73B => 220, 0x73C => 220, 0x73E => 220, 0x742 => 220, 0x744 => 220, + 0x746 => 220, 0x748 => 220, 0x952 => 220, 0xF18 => 220, 0xF19 => 220, + 0xF35 => 220, 0xF37 => 220, 0xFC6 => 220, 0x193B => 220, 0x20E8 => 220, + 0x1D17B => 220, 0x1D17C => 220, 0x1D17D => 220, 0x1D17E => 220, 0x1D17F => 220, + 0x1D180 => 220, 0x1D181 => 220, 0x1D182 => 220, 0x1D18A => 220, 0x1D18B => 220, + 0x59A => 222, 0x5AD => 222, 0x1929 => 222, 0x302D => 222, 0x302E => 224, + 0x302F => 224, 0x1D16D => 226, 0x5AE => 228, 0x18A9 => 228, 0x302B => 228, + 0x300 => 230, 0x301 => 230, 0x302 => 230, 0x303 => 230, 0x304 => 230, + 0x305 => 230, 0x306 => 230, 0x307 => 230, 0x308 => 230, 0x309 => 230, + 0x30A => 230, 0x30B => 230, 0x30C => 230, 0x30D => 230, 0x30E => 230, + 0x30F => 230, 0x310 => 230, 0x311 => 230, 0x312 => 230, 0x313 => 230, + 0x314 => 230, 0x33D => 230, 0x33E => 230, 0x33F => 230, 0x340 => 230, + 0x341 => 230, 0x342 => 230, 0x343 => 230, 0x344 => 230, 0x346 => 230, + 0x34A => 230, 0x34B => 230, 0x34C => 230, 0x350 => 230, 0x351 => 230, + 0x352 => 230, 0x357 => 230, 0x363 => 230, 0x364 => 230, 0x365 => 230, + 0x366 => 230, 0x367 => 230, 0x368 => 230, 0x369 => 230, 0x36A => 230, + 0x36B => 230, 0x36C => 230, 0x36D => 230, 0x36E => 230, 0x36F => 230, + 0x483 => 230, 0x484 => 230, 0x485 => 230, 0x486 => 230, 0x592 => 230, + 0x593 => 230, 0x594 => 230, 0x595 => 230, 0x597 => 230, 0x598 => 230, + 0x599 => 230, 0x59C => 230, 0x59D => 230, 0x59E => 230, 0x59F => 230, + 0x5A0 => 230, 0x5A1 => 230, 0x5A8 => 230, 0x5A9 => 230, 0x5AB => 230, + 0x5AC => 230, 0x5AF => 230, 0x5C4 => 230, 0x610 => 230, 0x611 => 230, + 0x612 => 230, 0x613 => 230, 0x614 => 230, 0x615 => 230, 0x653 => 230, + 0x654 => 230, 0x657 => 230, 0x658 => 230, 0x6D6 => 230, 0x6D7 => 230, + 0x6D8 => 230, 0x6D9 => 230, 0x6DA => 230, 0x6DB => 230, 0x6DC => 230, + 0x6DF => 230, 0x6E0 => 230, 0x6E1 => 230, 0x6E2 => 230, 0x6E4 => 230, + 0x6E7 => 230, 0x6E8 => 230, 0x6EB => 230, 0x6EC => 230, 0x730 => 230, + 0x732 => 230, 0x733 => 230, 0x735 => 230, 0x736 => 230, 0x73A => 230, + 0x73D => 230, 0x73F => 230, 0x740 => 230, 0x741 => 230, 0x743 => 230, + 0x745 => 230, 0x747 => 230, 0x749 => 230, 0x74A => 230, 0x951 => 230, + 0x953 => 230, 0x954 => 230, 0xF82 => 230, 0xF83 => 230, 0xF86 => 230, + 0xF87 => 230, 0x170D => 230, 0x193A => 230, 0x20D0 => 230, 0x20D1 => 230, + 0x20D4 => 230, 0x20D5 => 230, 0x20D6 => 230, 0x20D7 => 230, 0x20DB => 230, + 0x20DC => 230, 0x20E1 => 230, 0x20E7 => 230, 0x20E9 => 230, 0xFE20 => 230, + 0xFE21 => 230, 0xFE22 => 230, 0xFE23 => 230, 0x1D185 => 230, 0x1D186 => 230, + 0x1D187 => 230, 0x1D189 => 230, 0x1D188 => 230, 0x1D1AA => 230, 0x1D1AB => 230, + 0x1D1AC => 230, 0x1D1AD => 230, 0x315 => 232, 0x31A => 232, 0x302C => 232, + 0x35F => 233, 0x362 => 233, 0x35D => 234, 0x35E => 234, 0x360 => 234, + 0x361 => 234, 0x345 => 240 + ]; +} diff --git a/lib/classes/idna/ext/NamePrepData2003.php b/lib/classes/idna/ext/NamePrepData2003.php new file mode 100644 index 00000000..baa05152 --- /dev/null +++ b/lib/classes/idna/ext/NamePrepData2003.php @@ -0,0 +1,501 @@ + [0x61], 0x42 => [0x62], 0x43 => [0x63], + 0x44 => [0x64], 0x45 => [0x65], 0x46 => [0x66], 0x47 => [0x67], + 0x48 => [0x68], 0x49 => [0x69], 0x4A => [0x6A], 0x4B => [0x6B], + 0x4C => [0x6C], 0x4D => [0x6D], 0x4E => [0x6E], 0x4F => [0x6F], + 0x50 => [0x70], 0x51 => [0x71], 0x52 => [0x72], 0x53 => [0x73], + 0x54 => [0x74], 0x55 => [0x75], 0x56 => [0x76], 0x57 => [0x77], + 0x58 => [0x78], 0x59 => [0x79], 0x5A => [0x7A], 0xB5 => [0x3BC], + 0xC0 => [0xE0], 0xC1 => [0xE1], 0xC2 => [0xE2], 0xC3 => [0xE3], + 0xC4 => [0xE4], 0xC5 => [0xE5], 0xC6 => [0xE6], 0xC7 => [0xE7], + 0xC8 => [0xE8], 0xC9 => [0xE9], 0xCA => [0xEA], 0xCB => [0xEB], + 0xCC => [0xEC], 0xCD => [0xED], 0xCE => [0xEE], 0xCF => [0xEF], + 0xD0 => [0xF0], 0xD1 => [0xF1], 0xD2 => [0xF2], 0xD3 => [0xF3], + 0xD4 => [0xF4], 0xD5 => [0xF5], 0xD6 => [0xF6], 0xD8 => [0xF8], + 0xD9 => [0xF9], 0xDA => [0xFA], 0xDB => [0xFB], 0xDC => [0xFC], + 0xDD => [0xFD], 0xDE => [0xFE], 0xDF => [0x73, 0x73], + 0x100 => [0x101], 0x102 => [0x103], 0x104 => [0x105], + 0x106 => [0x107], 0x108 => [0x109], 0x10A => [0x10B], + 0x10C => [0x10D], 0x10E => [0x10F], 0x110 => [0x111], + 0x112 => [0x113], 0x114 => [0x115], 0x116 => [0x117], + 0x118 => [0x119], 0x11A => [0x11B], 0x11C => [0x11D], + 0x11E => [0x11F], 0x120 => [0x121], 0x122 => [0x123], + 0x124 => [0x125], 0x126 => [0x127], 0x128 => [0x129], + 0x12A => [0x12B], 0x12C => [0x12D], 0x12E => [0x12F], + 0x130 => [0x69, 0x307], 0x132 => [0x133], 0x134 => [0x135], + 0x136 => [0x137], 0x139 => [0x13A], 0x13B => [0x13C], + 0x13D => [0x13E], 0x13F => [0x140], 0x141 => [0x142], + 0x143 => [0x144], 0x145 => [0x146], 0x147 => [0x148], + 0x149 => [0x2BC, 0x6E], 0x14A => [0x14B], 0x14C => [0x14D], + 0x14E => [0x14F], 0x150 => [0x151], 0x152 => [0x153], + 0x154 => [0x155], 0x156 => [0x157], 0x158 => [0x159], + 0x15A => [0x15B], 0x15C => [0x15D], 0x15E => [0x15F], + 0x160 => [0x161], 0x162 => [0x163], 0x164 => [0x165], + 0x166 => [0x167], 0x168 => [0x169], 0x16A => [0x16B], + 0x16C => [0x16D], 0x16E => [0x16F], 0x170 => [0x171], + 0x172 => [0x173], 0x174 => [0x175], 0x176 => [0x177], + 0x178 => [0xFF], 0x179 => [0x17A], 0x17B => [0x17C], + 0x17D => [0x17E], 0x17F => [0x73], 0x181 => [0x253], + 0x182 => [0x183], 0x184 => [0x185], 0x186 => [0x254], + 0x187 => [0x188], 0x189 => [0x256], 0x18A => [0x257], + 0x18B => [0x18C], 0x18E => [0x1DD], 0x18F => [0x259], + 0x190 => [0x25B], 0x191 => [0x192], 0x193 => [0x260], + 0x194 => [0x263], 0x196 => [0x269], 0x197 => [0x268], + 0x198 => [0x199], 0x19C => [0x26F], 0x19D => [0x272], + 0x19F => [0x275], 0x1A0 => [0x1A1], 0x1A2 => [0x1A3], + 0x1A4 => [0x1A5], 0x1A6 => [0x280], 0x1A7 => [0x1A8], + 0x1A9 => [0x283], 0x1AC => [0x1AD], 0x1AE => [0x288], + 0x1AF => [0x1B0], 0x1B1 => [0x28A], 0x1B2 => [0x28B], + 0x1B3 => [0x1B4], 0x1B5 => [0x1B6], 0x1B7 => [0x292], + 0x1B8 => [0x1B9], 0x1BC => [0x1BD], 0x1C4 => [0x1C6], + 0x1C5 => [0x1C6], 0x1C7 => [0x1C9], 0x1C8 => [0x1C9], + 0x1CA => [0x1CC], 0x1CB => [0x1CC], 0x1CD => [0x1CE], + 0x1CF => [0x1D0], 0x1D1 => [0x1D2], 0x1D3 => [0x1D4], + 0x1D5 => [0x1D6], 0x1D7 => [0x1D8], 0x1D9 => [0x1DA], + 0x1DB => [0x1DC], 0x1DE => [0x1DF], 0x1E0 => [0x1E1], + 0x1E2 => [0x1E3], 0x1E4 => [0x1E5], 0x1E6 => [0x1E7], + 0x1E8 => [0x1E9], 0x1EA => [0x1EB], 0x1EC => [0x1ED], + 0x1EE => [0x1EF], 0x1F0 => [0x6A, 0x30C], 0x1F1 => [0x1F3], + 0x1F2 => [0x1F3], 0x1F4 => [0x1F5], 0x1F6 => [0x195], + 0x1F7 => [0x1BF], 0x1F8 => [0x1F9], 0x1FA => [0x1FB], + 0x1FC => [0x1FD], 0x1FE => [0x1FF], 0x200 => [0x201], + 0x202 => [0x203], 0x204 => [0x205], 0x206 => [0x207], + 0x208 => [0x209], 0x20A => [0x20B], 0x20C => [0x20D], + 0x20E => [0x20F], 0x210 => [0x211], 0x212 => [0x213], + 0x214 => [0x215], 0x216 => [0x217], 0x218 => [0x219], + 0x21A => [0x21B], 0x21C => [0x21D], 0x21E => [0x21F], + 0x220 => [0x19E], 0x222 => [0x223], 0x224 => [0x225], + 0x226 => [0x227], 0x228 => [0x229], 0x22A => [0x22B], + 0x22C => [0x22D], 0x22E => [0x22F], 0x230 => [0x231], + 0x232 => [0x233], 0x345 => [0x3B9], 0x37A => [0x20, 0x3B9], + 0x386 => [0x3AC], 0x388 => [0x3AD], 0x389 => [0x3AE], + 0x38A => [0x3AF], 0x38C => [0x3CC], 0x38E => [0x3CD], + 0x38F => [0x3CE], 0x390 => [0x3B9, 0x308, 0x301], + 0x391 => [0x3B1], 0x392 => [0x3B2], 0x393 => [0x3B3], + 0x394 => [0x3B4], 0x395 => [0x3B5], 0x396 => [0x3B6], + 0x397 => [0x3B7], 0x398 => [0x3B8], 0x399 => [0x3B9], + 0x39A => [0x3BA], 0x39B => [0x3BB], 0x39C => [0x3BC], + 0x39D => [0x3BD], 0x39E => [0x3BE], 0x39F => [0x3BF], + 0x3A0 => [0x3C0], 0x3A1 => [0x3C1], 0x3A3 => [0x3C3], + 0x3A4 => [0x3C4], 0x3A5 => [0x3C5], 0x3A6 => [0x3C6], + 0x3A7 => [0x3C7], 0x3A8 => [0x3C8], 0x3A9 => [0x3C9], + 0x3AA => [0x3CA], 0x3AB => [0x3CB], 0x3B0 => [0x3C5, 0x308, 0x301], + 0x3C2 => [0x3C3], 0x3D0 => [0x3B2], 0x3D1 => [0x3B8], + 0x3D2 => [0x3C5], 0x3D3 => [0x3CD], 0x3D4 => [0x3CB], + 0x3D5 => [0x3C6], 0x3D6 => [0x3C0], 0x3D8 => [0x3D9], + 0x3DA => [0x3DB], 0x3DC => [0x3DD], 0x3DE => [0x3DF], + 0x3E0 => [0x3E1], 0x3E2 => [0x3E3], 0x3E4 => [0x3E5], + 0x3E6 => [0x3E7], 0x3E8 => [0x3E9], 0x3EA => [0x3EB], + 0x3EC => [0x3ED], 0x3EE => [0x3EF], 0x3F0 => [0x3BA], + 0x3F1 => [0x3C1], 0x3F2 => [0x3C3], 0x3F4 => [0x3B8], + 0x3F5 => [0x3B5], 0x400 => [0x450], 0x401 => [0x451], + 0x402 => [0x452], 0x403 => [0x453], 0x404 => [0x454], + 0x405 => [0x455], 0x406 => [0x456], 0x407 => [0x457], + 0x408 => [0x458], 0x409 => [0x459], 0x40A => [0x45A], + 0x40B => [0x45B], 0x40C => [0x45C], 0x40D => [0x45D], + 0x40E => [0x45E], 0x40F => [0x45F], 0x410 => [0x430], + 0x411 => [0x431], 0x412 => [0x432], 0x413 => [0x433], + 0x414 => [0x434], 0x415 => [0x435], 0x416 => [0x436], + 0x417 => [0x437], 0x418 => [0x438], 0x419 => [0x439], + 0x41A => [0x43A], 0x41B => [0x43B], 0x41C => [0x43C], + 0x41D => [0x43D], 0x41E => [0x43E], 0x41F => [0x43F], + 0x420 => [0x440], 0x421 => [0x441], 0x422 => [0x442], + 0x423 => [0x443], 0x424 => [0x444], 0x425 => [0x445], + 0x426 => [0x446], 0x427 => [0x447], 0x428 => [0x448], + 0x429 => [0x449], 0x42A => [0x44A], 0x42B => [0x44B], + 0x42C => [0x44C], 0x42D => [0x44D], 0x42E => [0x44E], + 0x42F => [0x44F], 0x460 => [0x461], 0x462 => [0x463], + 0x464 => [0x465], 0x466 => [0x467], 0x468 => [0x469], + 0x46A => [0x46B], 0x46C => [0x46D], 0x46E => [0x46F], + 0x470 => [0x471], 0x472 => [0x473], 0x474 => [0x475], + 0x476 => [0x477], 0x478 => [0x479], 0x47A => [0x47B], + 0x47C => [0x47D], 0x47E => [0x47F], 0x480 => [0x481], + 0x48A => [0x48B], 0x48C => [0x48D], 0x48E => [0x48F], + 0x490 => [0x491], 0x492 => [0x493], 0x494 => [0x495], + 0x496 => [0x497], 0x498 => [0x499], 0x49A => [0x49B], + 0x49C => [0x49D], 0x49E => [0x49F], 0x4A0 => [0x4A1], + 0x4A2 => [0x4A3], 0x4A4 => [0x4A5], 0x4A6 => [0x4A7], + 0x4A8 => [0x4A9], 0x4AA => [0x4AB], 0x4AC => [0x4AD], + 0x4AE => [0x4AF], 0x4B0 => [0x4B1], 0x4B2 => [0x4B3], + 0x4B4 => [0x4B5], 0x4B6 => [0x4B7], 0x4B8 => [0x4B9], + 0x4BA => [0x4BB], 0x4BC => [0x4BD], 0x4BE => [0x4BF], + 0x4C1 => [0x4C2], 0x4C3 => [0x4C4], 0x4C5 => [0x4C6], + 0x4C7 => [0x4C8], 0x4C9 => [0x4CA], 0x4CB => [0x4CC], + 0x4CD => [0x4CE], 0x4D0 => [0x4D1], 0x4D2 => [0x4D3], + 0x4D4 => [0x4D5], 0x4D6 => [0x4D7], 0x4D8 => [0x4D9], + 0x4DA => [0x4DB], 0x4DC => [0x4DD], 0x4DE => [0x4DF], + 0x4E0 => [0x4E1], 0x4E2 => [0x4E3], 0x4E4 => [0x4E5], + 0x4E6 => [0x4E7], 0x4E8 => [0x4E9], 0x4EA => [0x4EB], + 0x4EC => [0x4ED], 0x4EE => [0x4EF], 0x4F0 => [0x4F1], + 0x4F2 => [0x4F3], 0x4F4 => [0x4F5], 0x4F8 => [0x4F9], + 0x500 => [0x501], 0x502 => [0x503], 0x504 => [0x505], + 0x506 => [0x507], 0x508 => [0x509], 0x50A => [0x50B], + 0x50C => [0x50D], 0x50E => [0x50F], 0x531 => [0x561], + 0x532 => [0x562], 0x533 => [0x563], 0x534 => [0x564], + 0x535 => [0x565], 0x536 => [0x566], 0x537 => [0x567], + 0x538 => [0x568], 0x539 => [0x569], 0x53A => [0x56A], + 0x53B => [0x56B], 0x53C => [0x56C], 0x53D => [0x56D], + 0x53E => [0x56E], 0x53F => [0x56F], 0x540 => [0x570], + 0x541 => [0x571], 0x542 => [0x572], 0x543 => [0x573], + 0x544 => [0x574], 0x545 => [0x575], 0x546 => [0x576], + 0x547 => [0x577], 0x548 => [0x578], 0x549 => [0x579], + 0x54A => [0x57A], 0x54B => [0x57B], 0x54C => [0x57C], + 0x54D => [0x57D], 0x54E => [0x57E], 0x54F => [0x57F], + 0x550 => [0x580], 0x551 => [0x581], 0x552 => [0x582], + 0x553 => [0x583], 0x554 => [0x584], 0x555 => [0x585], + 0x556 => [0x586], 0x587 => [0x565, 0x582], 0xE33 => [0xE4D, 0xE32], + 0x1E00 => [0x1E01], 0x1E02 => [0x1E03], 0x1E04 => [0x1E05], + 0x1E06 => [0x1E07], 0x1E08 => [0x1E09], 0x1E0A => [0x1E0B], + 0x1E0C => [0x1E0D], 0x1E0E => [0x1E0F], 0x1E10 => [0x1E11], + 0x1E12 => [0x1E13], 0x1E14 => [0x1E15], 0x1E16 => [0x1E17], + 0x1E18 => [0x1E19], 0x1E1A => [0x1E1B], 0x1E1C => [0x1E1D], + 0x1E1E => [0x1E1F], 0x1E20 => [0x1E21], 0x1E22 => [0x1E23], + 0x1E24 => [0x1E25], 0x1E26 => [0x1E27], 0x1E28 => [0x1E29], + 0x1E2A => [0x1E2B], 0x1E2C => [0x1E2D], 0x1E2E => [0x1E2F], + 0x1E30 => [0x1E31], 0x1E32 => [0x1E33], 0x1E34 => [0x1E35], + 0x1E36 => [0x1E37], 0x1E38 => [0x1E39], 0x1E3A => [0x1E3B], + 0x1E3C => [0x1E3D], 0x1E3E => [0x1E3F], 0x1E40 => [0x1E41], + 0x1E42 => [0x1E43], 0x1E44 => [0x1E45], 0x1E46 => [0x1E47], + 0x1E48 => [0x1E49], 0x1E4A => [0x1E4B], 0x1E4C => [0x1E4D], + 0x1E4E => [0x1E4F], 0x1E50 => [0x1E51], 0x1E52 => [0x1E53], + 0x1E54 => [0x1E55], 0x1E56 => [0x1E57], 0x1E58 => [0x1E59], + 0x1E5A => [0x1E5B], 0x1E5C => [0x1E5D], 0x1E5E => [0x1E5F], + 0x1E60 => [0x1E61], 0x1E62 => [0x1E63], 0x1E64 => [0x1E65], + 0x1E66 => [0x1E67], 0x1E68 => [0x1E69], 0x1E6A => [0x1E6B], + 0x1E6C => [0x1E6D], 0x1E6E => [0x1E6F], 0x1E70 => [0x1E71], + 0x1E72 => [0x1E73], 0x1E74 => [0x1E75], 0x1E76 => [0x1E77], + 0x1E78 => [0x1E79], 0x1E7A => [0x1E7B], 0x1E7C => [0x1E7D], + 0x1E7E => [0x1E7F], 0x1E80 => [0x1E81], 0x1E82 => [0x1E83], + 0x1E84 => [0x1E85], 0x1E86 => [0x1E87], 0x1E88 => [0x1E89], + 0x1E8A => [0x1E8B], 0x1E8C => [0x1E8D], 0x1E8E => [0x1E8F], + 0x1E90 => [0x1E91], 0x1E92 => [0x1E93], 0x1E94 => [0x1E95], + 0x1E96 => [0x68, 0x331], 0x1E97 => [0x74, 0x308], 0x1E98 => [0x77, 0x30A], + 0x1E99 => [0x79, 0x30A], 0x1E9A => [0x61, 0x2BE], 0x1E9B => [0x1E61], + 0x1EA0 => [0x1EA1], 0x1EA2 => [0x1EA3], 0x1EA4 => [0x1EA5], + 0x1EA6 => [0x1EA7], 0x1EA8 => [0x1EA9], 0x1EAA => [0x1EAB], + 0x1EAC => [0x1EAD], 0x1EAE => [0x1EAF], 0x1EB0 => [0x1EB1], + 0x1EB2 => [0x1EB3], 0x1EB4 => [0x1EB5], 0x1EB6 => [0x1EB7], + 0x1EB8 => [0x1EB9], 0x1EBA => [0x1EBB], 0x1EBC => [0x1EBD], + 0x1EBE => [0x1EBF], 0x1EC0 => [0x1EC1], 0x1EC2 => [0x1EC3], + 0x1EC4 => [0x1EC5], 0x1EC6 => [0x1EC7], 0x1EC8 => [0x1EC9], + 0x1ECA => [0x1ECB], 0x1ECC => [0x1ECD], 0x1ECE => [0x1ECF], + 0x1ED0 => [0x1ED1], 0x1ED2 => [0x1ED3], 0x1ED4 => [0x1ED5], + 0x1ED6 => [0x1ED7], 0x1ED8 => [0x1ED9], 0x1EDA => [0x1EDB], + 0x1EDC => [0x1EDD], 0x1EDE => [0x1EDF], 0x1EE0 => [0x1EE1], + 0x1EE2 => [0x1EE3], 0x1EE4 => [0x1EE5], 0x1EE6 => [0x1EE7], + 0x1EE8 => [0x1EE9], 0x1EEA => [0x1EEB], 0x1EEC => [0x1EED], + 0x1EEE => [0x1EEF], 0x1EF0 => [0x1EF1], 0x1EF2 => [0x1EF3], + 0x1EF4 => [0x1EF5], 0x1EF6 => [0x1EF7], 0x1EF8 => [0x1EF9], + 0x1F08 => [0x1F00], 0x1F09 => [0x1F01], 0x1F0A => [0x1F02], + 0x1F0B => [0x1F03], 0x1F0C => [0x1F04], 0x1F0D => [0x1F05], + 0x1F0E => [0x1F06], 0x1F0F => [0x1F07], 0x1F18 => [0x1F10], + 0x1F19 => [0x1F11], 0x1F1A => [0x1F12], 0x1F1B => [0x1F13], + 0x1F1C => [0x1F14], 0x1F1D => [0x1F15], 0x1F28 => [0x1F20], + 0x1F29 => [0x1F21], 0x1F2A => [0x1F22], 0x1F2B => [0x1F23], + 0x1F2C => [0x1F24], 0x1F2D => [0x1F25], 0x1F2E => [0x1F26], + 0x1F2F => [0x1F27], 0x1F38 => [0x1F30], 0x1F39 => [0x1F31], + 0x1F3A => [0x1F32], 0x1F3B => [0x1F33], 0x1F3C => [0x1F34], + 0x1F3D => [0x1F35], 0x1F3E => [0x1F36], 0x1F3F => [0x1F37], + 0x1F48 => [0x1F40], 0x1F49 => [0x1F41], 0x1F4A => [0x1F42], + 0x1F4B => [0x1F43], 0x1F4C => [0x1F44], 0x1F4D => [0x1F45], + 0x1F50 => [0x3C5, 0x313], 0x1F52 => [0x3C5, 0x313, 0x300], + 0x1F54 => [0x3C5, 0x313, 0x301], 0x1F56 => [0x3C5, 0x313, 0x342], + 0x1F59 => [0x1F51], 0x1F5B => [0x1F53], 0x1F5D => [0x1F55], + 0x1F5F => [0x1F57], 0x1F68 => [0x1F60], 0x1F69 => [0x1F61], + 0x1F6A => [0x1F62], 0x1F6B => [0x1F63], 0x1F6C => [0x1F64], + 0x1F6D => [0x1F65], 0x1F6E => [0x1F66], 0x1F6F => [0x1F67], + 0x1F80 => [0x1F00, 0x3B9], 0x1F81 => [0x1F01, 0x3B9], + 0x1F82 => [0x1F02, 0x3B9], 0x1F83 => [0x1F03, 0x3B9], + 0x1F84 => [0x1F04, 0x3B9], 0x1F85 => [0x1F05, 0x3B9], + 0x1F86 => [0x1F06, 0x3B9], 0x1F87 => [0x1F07, 0x3B9], + 0x1F88 => [0x1F00, 0x3B9], 0x1F89 => [0x1F01, 0x3B9], + 0x1F8A => [0x1F02, 0x3B9], 0x1F8B => [0x1F03, 0x3B9], + 0x1F8C => [0x1F04, 0x3B9], 0x1F8D => [0x1F05, 0x3B9], + 0x1F8E => [0x1F06, 0x3B9], 0x1F8F => [0x1F07, 0x3B9], + 0x1F90 => [0x1F20, 0x3B9], 0x1F91 => [0x1F21, 0x3B9], + 0x1F92 => [0x1F22, 0x3B9], 0x1F93 => [0x1F23, 0x3B9], + 0x1F94 => [0x1F24, 0x3B9], 0x1F95 => [0x1F25, 0x3B9], + 0x1F96 => [0x1F26, 0x3B9], 0x1F97 => [0x1F27, 0x3B9], + 0x1F98 => [0x1F20, 0x3B9], 0x1F99 => [0x1F21, 0x3B9], + 0x1F9A => [0x1F22, 0x3B9], 0x1F9B => [0x1F23, 0x3B9], + 0x1F9C => [0x1F24, 0x3B9], 0x1F9D => [0x1F25, 0x3B9], + 0x1F9E => [0x1F26, 0x3B9], 0x1F9F => [0x1F27, 0x3B9], + 0x1FA0 => [0x1F60, 0x3B9], 0x1FA1 => [0x1F61, 0x3B9], + 0x1FA2 => [0x1F62, 0x3B9], 0x1FA3 => [0x1F63, 0x3B9], + 0x1FA4 => [0x1F64, 0x3B9], 0x1FA5 => [0x1F65, 0x3B9], + 0x1FA6 => [0x1F66, 0x3B9], 0x1FA7 => [0x1F67, 0x3B9], + 0x1FA8 => [0x1F60, 0x3B9], 0x1FA9 => [0x1F61, 0x3B9], + 0x1FAA => [0x1F62, 0x3B9], 0x1FAB => [0x1F63, 0x3B9], + 0x1FAC => [0x1F64, 0x3B9], 0x1FAD => [0x1F65, 0x3B9], + 0x1FAE => [0x1F66, 0x3B9], 0x1FAF => [0x1F67, 0x3B9], + 0x1FB2 => [0x1F70, 0x3B9], 0x1FB3 => [0x3B1, 0x3B9], + 0x1FB4 => [0x3AC, 0x3B9], 0x1FB6 => [0x3B1, 0x342], + 0x1FB7 => [0x3B1, 0x342, 0x3B9], 0x1FB8 => [0x1FB0], + 0x1FB9 => [0x1FB1], 0x1FBA => [0x1F70], 0x1FBB => [0x1F71], + 0x1FBC => [0x3B1, 0x3B9], 0x1FBE => [0x3B9], + 0x1FC2 => [0x1F74, 0x3B9], 0x1FC3 => [0x3B7, 0x3B9], + 0x1FC4 => [0x3AE, 0x3B9], 0x1FC6 => [0x3B7, 0x342], + 0x1FC7 => [0x3B7, 0x342, 0x3B9], 0x1FC8 => [0x1F72], + 0x1FC9 => [0x1F73], 0x1FCA => [0x1F74], 0x1FCB => [0x1F75], + 0x1FCC => [0x3B7, 0x3B9], 0x1FD2 => [0x3B9, 0x308, 0x300], + 0x1FD3 => [0x3B9, 0x308, 0x301], 0x1FD6 => [0x3B9, 0x342], + 0x1FD7 => [0x3B9, 0x308, 0x342], 0x1FD8 => [0x1FD0], + 0x1FD9 => [0x1FD1], 0x1FDA => [0x1F76], + 0x1FDB => [0x1F77], 0x1FE2 => [0x3C5, 0x308, 0x300], + 0x1FE3 => [0x3C5, 0x308, 0x301], 0x1FE4 => [0x3C1, 0x313], + 0x1FE6 => [0x3C5, 0x342], 0x1FE7 => [0x3C5, 0x308, 0x342], + 0x1FE8 => [0x1FE0], 0x1FE9 => [0x1FE1], + 0x1FEA => [0x1F7A], 0x1FEB => [0x1F7B], + 0x1FEC => [0x1FE5], 0x1FF2 => [0x1F7C, 0x3B9], + 0x1FF3 => [0x3C9, 0x3B9], 0x1FF4 => [0x3CE, 0x3B9], + 0x1FF6 => [0x3C9, 0x342], 0x1FF7 => [0x3C9, 0x342, 0x3B9], + 0x1FF8 => [0x1F78], 0x1FF9 => [0x1F79], 0x1FFA => [0x1F7C], + 0x1FFB => [0x1F7D], 0x1FFC => [0x3C9, 0x3B9], + 0x20A8 => [0x72, 0x73], 0x2102 => [0x63], 0x2103 => [0xB0, 0x63], + 0x2107 => [0x25B], 0x2109 => [0xB0, 0x66], 0x210B => [0x68], + 0x210C => [0x68], 0x210D => [0x68], 0x2110 => [0x69], + 0x2111 => [0x69], 0x2112 => [0x6C], 0x2115 => [0x6E], + 0x2116 => [0x6E, 0x6F], 0x2119 => [0x70], 0x211A => [0x71], + 0x211B => [0x72], 0x211C => [0x72], 0x211D => [0x72], + 0x2120 => [0x73, 0x6D], 0x2121 => [0x74, 0x65, 0x6C], + 0x2122 => [0x74, 0x6D], 0x2124 => [0x7A], 0x2126 => [0x3C9], + 0x2128 => [0x7A], 0x212A => [0x6B], 0x212B => [0xE5], + 0x212C => [0x62], 0x212D => [0x63], 0x2130 => [0x65], + 0x2131 => [0x66], 0x2133 => [0x6D], 0x213E => [0x3B3], + 0x213F => [0x3C0], 0x2145 => [0x64], 0x2160 => [0x2170], + 0x2161 => [0x2171], 0x2162 => [0x2172], 0x2163 => [0x2173], + 0x2164 => [0x2174], 0x2165 => [0x2175], 0x2166 => [0x2176], + 0x2167 => [0x2177], 0x2168 => [0x2178], 0x2169 => [0x2179], + 0x216A => [0x217A], 0x216B => [0x217B], 0x216C => [0x217C], + 0x216D => [0x217D], 0x216E => [0x217E], 0x216F => [0x217F], + 0x24B6 => [0x24D0], 0x24B7 => [0x24D1], 0x24B8 => [0x24D2], + 0x24B9 => [0x24D3], 0x24BA => [0x24D4], 0x24BB => [0x24D5], + 0x24BC => [0x24D6], 0x24BD => [0x24D7], 0x24BE => [0x24D8], + 0x24BF => [0x24D9], 0x24C0 => [0x24DA], 0x24C1 => [0x24DB], + 0x24C2 => [0x24DC], 0x24C3 => [0x24DD], 0x24C4 => [0x24DE], + 0x24C5 => [0x24DF], 0x24C6 => [0x24E0], 0x24C7 => [0x24E1], + 0x24C8 => [0x24E2], 0x24C9 => [0x24E3], 0x24CA => [0x24E4], + 0x24CB => [0x24E5], 0x24CC => [0x24E6], 0x24CD => [0x24E7], + 0x24CE => [0x24E8], 0x24CF => [0x24E9], 0x3371 => [0x68, 0x70, 0x61], + 0x3373 => [0x61, 0x75], 0x3375 => [0x6F, 0x76], + 0x3380 => [0x70, 0x61], 0x3381 => [0x6E, 0x61], + 0x3382 => [0x3BC, 0x61], 0x3383 => [0x6D, 0x61], + 0x3384 => [0x6B, 0x61], 0x3385 => [0x6B, 0x62], + 0x3386 => [0x6D, 0x62], 0x3387 => [0x67, 0x62], + 0x338A => [0x70, 0x66], 0x338B => [0x6E, 0x66], + 0x338C => [0x3BC, 0x66], 0x3390 => [0x68, 0x7A], + 0x3391 => [0x6B, 0x68, 0x7A], 0x3392 => [0x6D, 0x68, 0x7A], + 0x3393 => [0x67, 0x68, 0x7A], 0x3394 => [0x74, 0x68, 0x7A], + 0x33A9 => [0x70, 0x61], 0x33AA => [0x6B, 0x70, 0x61], + 0x33AB => [0x6D, 0x70, 0x61], 0x33AC => [0x67, 0x70, 0x61], + 0x33B4 => [0x70, 0x76], 0x33B5 => [0x6E, 0x76], + 0x33B6 => [0x3BC, 0x76], 0x33B7 => [0x6D, 0x76], + 0x33B8 => [0x6B, 0x76], 0x33B9 => [0x6D, 0x76], + 0x33BA => [0x70, 0x77], 0x33BB => [0x6E, 0x77], + 0x33BC => [0x3BC, 0x77], 0x33BD => [0x6D, 0x77], + 0x33BE => [0x6B, 0x77], 0x33BF => [0x6D, 0x77], + 0x33C0 => [0x6B, 0x3C9], 0x33C1 => [0x6D, 0x3C9], /* + 0x33C2 => array(0x61, 0x2E, 0x6D, 0x2E), */ + 0x33C3 => [0x62, 0x71], 0x33C6 => [0x63, 0x2215, 0x6B, 0x67], + 0x33C7 => [0x63, 0x6F, 0x2E], 0x33C8 => [0x64, 0x62], + 0x33C9 => [0x67, 0x79], 0x33CB => [0x68, 0x70], + 0x33CD => [0x6B, 0x6B], 0x33CE => [0x6B, 0x6D], + 0x33D7 => [0x70, 0x68], 0x33D9 => [0x70, 0x70, 0x6D], + 0x33DA => [0x70, 0x72], 0x33DC => [0x73, 0x76], + 0x33DD => [0x77, 0x62], 0xFB00 => [0x66, 0x66], + 0xFB01 => [0x66, 0x69], 0xFB02 => [0x66, 0x6C], + 0xFB03 => [0x66, 0x66, 0x69], 0xFB04 => [0x66, 0x66, 0x6C], + 0xFB05 => [0x73, 0x74], 0xFB06 => [0x73, 0x74], + 0xFB13 => [0x574, 0x576], 0xFB14 => [0x574, 0x565], + 0xFB15 => [0x574, 0x56B], 0xFB16 => [0x57E, 0x576], + 0xFB17 => [0x574, 0x56D], 0xFF21 => [0xFF41], + 0xFF22 => [0xFF42], 0xFF23 => [0xFF43], 0xFF24 => [0xFF44], + 0xFF25 => [0xFF45], 0xFF26 => [0xFF46], 0xFF27 => [0xFF47], + 0xFF28 => [0xFF48], 0xFF29 => [0xFF49], 0xFF2A => [0xFF4A], + 0xFF2B => [0xFF4B], 0xFF2C => [0xFF4C], 0xFF2D => [0xFF4D], + 0xFF2E => [0xFF4E], 0xFF2F => [0xFF4F], 0xFF30 => [0xFF50], + 0xFF31 => [0xFF51], 0xFF32 => [0xFF52], 0xFF33 => [0xFF53], + 0xFF34 => [0xFF54], 0xFF35 => [0xFF55], 0xFF36 => [0xFF56], + 0xFF37 => [0xFF57], 0xFF38 => [0xFF58], 0xFF39 => [0xFF59], + 0xFF3A => [0xFF5A], 0x10400 => [0x10428], 0x10401 => [0x10429], + 0x10402 => [0x1042A], 0x10403 => [0x1042B], 0x10404 => [0x1042C], + 0x10405 => [0x1042D], 0x10406 => [0x1042E], 0x10407 => [0x1042F], + 0x10408 => [0x10430], 0x10409 => [0x10431], 0x1040A => [0x10432], + 0x1040B => [0x10433], 0x1040C => [0x10434], 0x1040D => [0x10435], + 0x1040E => [0x10436], 0x1040F => [0x10437], 0x10410 => [0x10438], + 0x10411 => [0x10439], 0x10412 => [0x1043A], 0x10413 => [0x1043B], + 0x10414 => [0x1043C], 0x10415 => [0x1043D], 0x10416 => [0x1043E], + 0x10417 => [0x1043F], 0x10418 => [0x10440], 0x10419 => [0x10441], + 0x1041A => [0x10442], 0x1041B => [0x10443], 0x1041C => [0x10444], + 0x1041D => [0x10445], 0x1041E => [0x10446], 0x1041F => [0x10447], + 0x10420 => [0x10448], 0x10421 => [0x10449], 0x10422 => [0x1044A], + 0x10423 => [0x1044B], 0x10424 => [0x1044C], 0x10425 => [0x1044D], + 0x1D400 => [0x61], 0x1D401 => [0x62], 0x1D402 => [0x63], + 0x1D403 => [0x64], 0x1D404 => [0x65], 0x1D405 => [0x66], + 0x1D406 => [0x67], 0x1D407 => [0x68], 0x1D408 => [0x69], + 0x1D409 => [0x6A], 0x1D40A => [0x6B], 0x1D40B => [0x6C], + 0x1D40C => [0x6D], 0x1D40D => [0x6E], 0x1D40E => [0x6F], + 0x1D40F => [0x70], 0x1D410 => [0x71], 0x1D411 => [0x72], + 0x1D412 => [0x73], 0x1D413 => [0x74], 0x1D414 => [0x75], + 0x1D415 => [0x76], 0x1D416 => [0x77], 0x1D417 => [0x78], + 0x1D418 => [0x79], 0x1D419 => [0x7A], 0x1D434 => [0x61], + 0x1D435 => [0x62], 0x1D436 => [0x63], 0x1D437 => [0x64], + 0x1D438 => [0x65], 0x1D439 => [0x66], 0x1D43A => [0x67], + 0x1D43B => [0x68], 0x1D43C => [0x69], 0x1D43D => [0x6A], + 0x1D43E => [0x6B], 0x1D43F => [0x6C], 0x1D440 => [0x6D], + 0x1D441 => [0x6E], 0x1D442 => [0x6F], 0x1D443 => [0x70], + 0x1D444 => [0x71], 0x1D445 => [0x72], 0x1D446 => [0x73], + 0x1D447 => [0x74], 0x1D448 => [0x75], 0x1D449 => [0x76], + 0x1D44A => [0x77], 0x1D44B => [0x78], 0x1D44C => [0x79], + 0x1D44D => [0x7A], 0x1D468 => [0x61], 0x1D469 => [0x62], + 0x1D46A => [0x63], 0x1D46B => [0x64], 0x1D46C => [0x65], + 0x1D46D => [0x66], 0x1D46E => [0x67], 0x1D46F => [0x68], + 0x1D470 => [0x69], 0x1D471 => [0x6A], 0x1D472 => [0x6B], + 0x1D473 => [0x6C], 0x1D474 => [0x6D], 0x1D475 => [0x6E], + 0x1D476 => [0x6F], 0x1D477 => [0x70], 0x1D478 => [0x71], + 0x1D479 => [0x72], 0x1D47A => [0x73], 0x1D47B => [0x74], + 0x1D47C => [0x75], 0x1D47D => [0x76], 0x1D47E => [0x77], + 0x1D47F => [0x78], 0x1D480 => [0x79], 0x1D481 => [0x7A], + 0x1D49C => [0x61], 0x1D49E => [0x63], 0x1D49F => [0x64], + 0x1D4A2 => [0x67], 0x1D4A5 => [0x6A], 0x1D4A6 => [0x6B], + 0x1D4A9 => [0x6E], 0x1D4AA => [0x6F], 0x1D4AB => [0x70], + 0x1D4AC => [0x71], 0x1D4AE => [0x73], 0x1D4AF => [0x74], + 0x1D4B0 => [0x75], 0x1D4B1 => [0x76], 0x1D4B2 => [0x77], + 0x1D4B3 => [0x78], 0x1D4B4 => [0x79], 0x1D4B5 => [0x7A], + 0x1D4D0 => [0x61], 0x1D4D1 => [0x62], 0x1D4D2 => [0x63], + 0x1D4D3 => [0x64], 0x1D4D4 => [0x65], 0x1D4D5 => [0x66], + 0x1D4D6 => [0x67], 0x1D4D7 => [0x68], 0x1D4D8 => [0x69], + 0x1D4D9 => [0x6A], 0x1D4DA => [0x6B], 0x1D4DB => [0x6C], + 0x1D4DC => [0x6D], 0x1D4DD => [0x6E], 0x1D4DE => [0x6F], + 0x1D4DF => [0x70], 0x1D4E0 => [0x71], 0x1D4E1 => [0x72], + 0x1D4E2 => [0x73], 0x1D4E3 => [0x74], 0x1D4E4 => [0x75], + 0x1D4E5 => [0x76], 0x1D4E6 => [0x77], 0x1D4E7 => [0x78], + 0x1D4E8 => [0x79], 0x1D4E9 => [0x7A], 0x1D504 => [0x61], + 0x1D505 => [0x62], 0x1D507 => [0x64], 0x1D508 => [0x65], + 0x1D509 => [0x66], 0x1D50A => [0x67], 0x1D50D => [0x6A], + 0x1D50E => [0x6B], 0x1D50F => [0x6C], 0x1D510 => [0x6D], + 0x1D511 => [0x6E], 0x1D512 => [0x6F], 0x1D513 => [0x70], + 0x1D514 => [0x71], 0x1D516 => [0x73], 0x1D517 => [0x74], + 0x1D518 => [0x75], 0x1D519 => [0x76], 0x1D51A => [0x77], + 0x1D51B => [0x78], 0x1D51C => [0x79], 0x1D538 => [0x61], + 0x1D539 => [0x62], 0x1D53B => [0x64], 0x1D53C => [0x65], + 0x1D53D => [0x66], 0x1D53E => [0x67], 0x1D540 => [0x69], + 0x1D541 => [0x6A], 0x1D542 => [0x6B], 0x1D543 => [0x6C], + 0x1D544 => [0x6D], 0x1D546 => [0x6F], 0x1D54A => [0x73], + 0x1D54B => [0x74], 0x1D54C => [0x75], 0x1D54D => [0x76], + 0x1D54E => [0x77], 0x1D54F => [0x78], 0x1D550 => [0x79], + 0x1D56C => [0x61], 0x1D56D => [0x62], 0x1D56E => [0x63], + 0x1D56F => [0x64], 0x1D570 => [0x65], 0x1D571 => [0x66], + 0x1D572 => [0x67], 0x1D573 => [0x68], 0x1D574 => [0x69], + 0x1D575 => [0x6A], 0x1D576 => [0x6B], 0x1D577 => [0x6C], + 0x1D578 => [0x6D], 0x1D579 => [0x6E], 0x1D57A => [0x6F], + 0x1D57B => [0x70], 0x1D57C => [0x71], 0x1D57D => [0x72], + 0x1D57E => [0x73], 0x1D57F => [0x74], 0x1D580 => [0x75], + 0x1D581 => [0x76], 0x1D582 => [0x77], 0x1D583 => [0x78], + 0x1D584 => [0x79], 0x1D585 => [0x7A], 0x1D5A0 => [0x61], + 0x1D5A1 => [0x62], 0x1D5A2 => [0x63], 0x1D5A3 => [0x64], + 0x1D5A4 => [0x65], 0x1D5A5 => [0x66], 0x1D5A6 => [0x67], + 0x1D5A7 => [0x68], 0x1D5A8 => [0x69], 0x1D5A9 => [0x6A], + 0x1D5AA => [0x6B], 0x1D5AB => [0x6C], 0x1D5AC => [0x6D], + 0x1D5AD => [0x6E], 0x1D5AE => [0x6F], 0x1D5AF => [0x70], + 0x1D5B0 => [0x71], 0x1D5B1 => [0x72], 0x1D5B2 => [0x73], + 0x1D5B3 => [0x74], 0x1D5B4 => [0x75], 0x1D5B5 => [0x76], + 0x1D5B6 => [0x77], 0x1D5B7 => [0x78], 0x1D5B8 => [0x79], + 0x1D5B9 => [0x7A], 0x1D5D4 => [0x61], 0x1D5D5 => [0x62], + 0x1D5D6 => [0x63], 0x1D5D7 => [0x64], 0x1D5D8 => [0x65], + 0x1D5D9 => [0x66], 0x1D5DA => [0x67], 0x1D5DB => [0x68], + 0x1D5DC => [0x69], 0x1D5DD => [0x6A], 0x1D5DE => [0x6B], + 0x1D5DF => [0x6C], 0x1D5E0 => [0x6D], 0x1D5E1 => [0x6E], + 0x1D5E2 => [0x6F], 0x1D5E3 => [0x70], 0x1D5E4 => [0x71], + 0x1D5E5 => [0x72], 0x1D5E6 => [0x73], 0x1D5E7 => [0x74], + 0x1D5E8 => [0x75], 0x1D5E9 => [0x76], 0x1D5EA => [0x77], + 0x1D5EB => [0x78], 0x1D5EC => [0x79], 0x1D5ED => [0x7A], + 0x1D608 => [0x61], 0x1D609 => [0x62], 0x1D60A => [0x63], + 0x1D60B => [0x64], 0x1D60C => [0x65], 0x1D60D => [0x66], + 0x1D60E => [0x67], 0x1D60F => [0x68], 0x1D610 => [0x69], + 0x1D611 => [0x6A], 0x1D612 => [0x6B], 0x1D613 => [0x6C], + 0x1D614 => [0x6D], 0x1D615 => [0x6E], 0x1D616 => [0x6F], + 0x1D617 => [0x70], 0x1D618 => [0x71], 0x1D619 => [0x72], + 0x1D61A => [0x73], 0x1D61B => [0x74], 0x1D61C => [0x75], + 0x1D61D => [0x76], 0x1D61E => [0x77], 0x1D61F => [0x78], + 0x1D620 => [0x79], 0x1D621 => [0x7A], 0x1D63C => [0x61], + 0x1D63D => [0x62], 0x1D63E => [0x63], 0x1D63F => [0x64], + 0x1D640 => [0x65], 0x1D641 => [0x66], 0x1D642 => [0x67], + 0x1D643 => [0x68], 0x1D644 => [0x69], 0x1D645 => [0x6A], + 0x1D646 => [0x6B], 0x1D647 => [0x6C], 0x1D648 => [0x6D], + 0x1D649 => [0x6E], 0x1D64A => [0x6F], 0x1D64B => [0x70], + 0x1D64C => [0x71], 0x1D64D => [0x72], 0x1D64E => [0x73], + 0x1D64F => [0x74], 0x1D650 => [0x75], 0x1D651 => [0x76], + 0x1D652 => [0x77], 0x1D653 => [0x78], 0x1D654 => [0x79], + 0x1D655 => [0x7A], 0x1D670 => [0x61], 0x1D671 => [0x62], + 0x1D672 => [0x63], 0x1D673 => [0x64], 0x1D674 => [0x65], + 0x1D675 => [0x66], 0x1D676 => [0x67], 0x1D677 => [0x68], + 0x1D678 => [0x69], 0x1D679 => [0x6A], 0x1D67A => [0x6B], + 0x1D67B => [0x6C], 0x1D67C => [0x6D], 0x1D67D => [0x6E], + 0x1D67E => [0x6F], 0x1D67F => [0x70], 0x1D680 => [0x71], + 0x1D681 => [0x72], 0x1D682 => [0x73], 0x1D683 => [0x74], + 0x1D684 => [0x75], 0x1D685 => [0x76], 0x1D686 => [0x77], + 0x1D687 => [0x78], 0x1D688 => [0x79], 0x1D689 => [0x7A], + 0x1D6A8 => [0x3B1], 0x1D6A9 => [0x3B2], 0x1D6AA => [0x3B3], + 0x1D6AB => [0x3B4], 0x1D6AC => [0x3B5], 0x1D6AD => [0x3B6], + 0x1D6AE => [0x3B7], 0x1D6AF => [0x3B8], 0x1D6B0 => [0x3B9], + 0x1D6B1 => [0x3BA], 0x1D6B2 => [0x3BB], 0x1D6B3 => [0x3BC], + 0x1D6B4 => [0x3BD], 0x1D6B5 => [0x3BE], 0x1D6B6 => [0x3BF], + 0x1D6B7 => [0x3C0], 0x1D6B8 => [0x3C1], 0x1D6B9 => [0x3B8], + 0x1D6BA => [0x3C3], 0x1D6BB => [0x3C4], 0x1D6BC => [0x3C5], + 0x1D6BD => [0x3C6], 0x1D6BE => [0x3C7], 0x1D6BF => [0x3C8], + 0x1D6C0 => [0x3C9], 0x1D6D3 => [0x3C3], 0x1D6E2 => [0x3B1], + 0x1D6E3 => [0x3B2], 0x1D6E4 => [0x3B3], 0x1D6E5 => [0x3B4], + 0x1D6E6 => [0x3B5], 0x1D6E7 => [0x3B6], 0x1D6E8 => [0x3B7], + 0x1D6E9 => [0x3B8], 0x1D6EA => [0x3B9], 0x1D6EB => [0x3BA], + 0x1D6EC => [0x3BB], 0x1D6ED => [0x3BC], 0x1D6EE => [0x3BD], + 0x1D6EF => [0x3BE], 0x1D6F0 => [0x3BF], 0x1D6F1 => [0x3C0], + 0x1D6F2 => [0x3C1], 0x1D6F3 => [0x3B8], 0x1D6F4 => [0x3C3], + 0x1D6F5 => [0x3C4], 0x1D6F6 => [0x3C5], 0x1D6F7 => [0x3C6], + 0x1D6F8 => [0x3C7], 0x1D6F9 => [0x3C8], 0x1D6FA => [0x3C9], + 0x1D70D => [0x3C3], 0x1D71C => [0x3B1], 0x1D71D => [0x3B2], + 0x1D71E => [0x3B3], 0x1D71F => [0x3B4], 0x1D720 => [0x3B5], + 0x1D721 => [0x3B6], 0x1D722 => [0x3B7], 0x1D723 => [0x3B8], + 0x1D724 => [0x3B9], 0x1D725 => [0x3BA], 0x1D726 => [0x3BB], + 0x1D727 => [0x3BC], 0x1D728 => [0x3BD], 0x1D729 => [0x3BE], + 0x1D72A => [0x3BF], 0x1D72B => [0x3C0], 0x1D72C => [0x3C1], + 0x1D72D => [0x3B8], 0x1D72E => [0x3C3], 0x1D72F => [0x3C4], + 0x1D730 => [0x3C5], 0x1D731 => [0x3C6], 0x1D732 => [0x3C7], + 0x1D733 => [0x3C8], 0x1D734 => [0x3C9], 0x1D747 => [0x3C3], + 0x1D756 => [0x3B1], 0x1D757 => [0x3B2], 0x1D758 => [0x3B3], + 0x1D759 => [0x3B4], 0x1D75A => [0x3B5], 0x1D75B => [0x3B6], + 0x1D75C => [0x3B7], 0x1D75D => [0x3B8], 0x1D75E => [0x3B9], + 0x1D75F => [0x3BA], 0x1D760 => [0x3BB], 0x1D761 => [0x3BC], + 0x1D762 => [0x3BD], 0x1D763 => [0x3BE], 0x1D764 => [0x3BF], + 0x1D765 => [0x3C0], 0x1D766 => [0x3C1], 0x1D767 => [0x3B8], + 0x1D768 => [0x3C3], 0x1D769 => [0x3C4], 0x1D76A => [0x3C5], + 0x1D76B => [0x3C6], 0x1D76C => [0x3C7], 0x1D76D => [0x3C8], + 0x1D76E => [0x3C9], 0x1D781 => [0x3C3], 0x1D790 => [0x3B1], + 0x1D791 => [0x3B2], 0x1D792 => [0x3B3], 0x1D793 => [0x3B4], + 0x1D794 => [0x3B5], 0x1D795 => [0x3B6], 0x1D796 => [0x3B7], + 0x1D797 => [0x3B8], 0x1D798 => [0x3B9], 0x1D799 => [0x3BA], + 0x1D79A => [0x3BB], 0x1D79B => [0x3BC], 0x1D79C => [0x3BD], + 0x1D79D => [0x3BE], 0x1D79E => [0x3BF], 0x1D79F => [0x3C0], + 0x1D7A0 => [0x3C1], 0x1D7A1 => [0x3B8], 0x1D7A2 => [0x3C3], + 0x1D7A3 => [0x3C4], 0x1D7A4 => [0x3C5], 0x1D7A5 => [0x3C6], + 0x1D7A6 => [0x3C7], 0x1D7A7 => [0x3C8], 0x1D7A8 => [0x3C9], + 0x1D7BB => [0x3C3], 0x3F9 => [0x3C3], 0x1D2C => [0x61], + 0x1D2D => [0xE6], 0x1D2E => [0x62], 0x1D30 => [0x64], + 0x1D31 => [0x65], 0x1D32 => [0x1DD], 0x1D33 => [0x67], + 0x1D34 => [0x68], 0x1D35 => [0x69], 0x1D36 => [0x6A], + 0x1D37 => [0x6B], 0x1D38 => [0x6C], 0x1D39 => [0x6D], + 0x1D3A => [0x6E], 0x1D3C => [0x6F], 0x1D3D => [0x223], + 0x1D3E => [0x70], 0x1D3F => [0x72], 0x1D40 => [0x74], + 0x1D41 => [0x75], 0x1D42 => [0x77], 0x213B => [0x66, 0x61, 0x78], + 0x3250 => [0x70, 0x74, 0x65], 0x32CC => [0x68, 0x67], + 0x32CE => [0x65, 0x76], 0x32CF => [0x6C, 0x74, 0x64], + 0x337A => [0x69, 0x75], 0x33DE => [0x76, 0x2215, 0x6D], + 0x33DF => [0x61, 0x2215, 0x6D] + ]; +} diff --git a/lib/classes/idna/ext/NamePrepDataInterface.php b/lib/classes/idna/ext/NamePrepDataInterface.php new file mode 100644 index 00000000..11d791eb --- /dev/null +++ b/lib/classes/idna/ext/NamePrepDataInterface.php @@ -0,0 +1,7 @@ + + * @copyright 2004-2016 phlyLabs Berlin, http://phlylabs.de + * @version 1.0.1 2016-01-24 + */ + +namespace Mso\IdnaConvert; + +class Punycode implements PunycodeInterface +{ + // Internal settings, do not touch! + const punycodePrefix = 'xn--'; + const invalidUcs = 0x80000000; + const maxUcs = 0x10FFFF; + const base = 36; + const tMin = 1; + const tMax = 26; + const skew = 38; + const damp = 700; + const initialBias = 72; + const initialN = 0x80; + const sBase = 0xAC00; + const lBase = 0x1100; + const vBase = 0x1161; + const tBase = 0x11A7; + const lCount = 19; + const vCount = 21; + const tCount = 28; + const nCount = 588; // vCount * tCount + const sCount = 11172; // lCount * tCount * vCount + const sLast = self::sBase + self::lCount * self::vCount * self::tCount; + + protected static $isMbStringOverload = null; + + protected $NamePrepData; + protected $UnicodeTranscoder; + + /** + * the constructor + * + * @param $NamePrepData NamePrepDataInterface inject NamePrepData object + * @param $UnicodeTranscoder UnicodeTranscoderInterface inject Unicode Transcoder + * @since 0.5.2 + */ + public function __construct(NamePrepDataInterface $NamePrepData, UnicodeTranscoderInterface $UnicodeTranscoder) + { + // populate mbstring overloading cache if not set + if (self::$isMbStringOverload === null) { + self::$isMbStringOverload = (extension_loaded('mbstring') && (ini_get('mbstring.func_overload') & 0x02) === 0x02); + } + + $this->NamePrepData = $NamePrepData; + $this->UnicodeTranscoder = $UnicodeTranscoder; + } + + public function getPunycodePrefix() + { + return self::punycodePrefix; + } + + /** + * The actual decoding algorithm + * @param string + * @return mixed + */ + public function decode($encoded) + { + $decoded = []; + // find the Punycode prefix + if (!preg_match('!^' . preg_quote(self::punycodePrefix, '!') . '!', $encoded)) { + // *** froxlor patch *** + return $encoded; + // *** end froxlor patch *** + throw new \InvalidArgumentException('This is not a punycode string'); + } + $encode_test = preg_replace('!^' . preg_quote(self::punycodePrefix, '!') . '!', '', $encoded); + // If nothing left after removing the prefix, it is hopeless + if (!$encode_test) { + return false; + } + // Find last occurence of the delimiter + $delim_pos = strrpos($encoded, '-'); + if ($delim_pos > self::byteLength(self::punycodePrefix)) { + for ($k = self::byteLength(self::punycodePrefix); $k < $delim_pos; ++$k) { + $decoded[] = ord($encoded{$k}); + } + } + $deco_len = count($decoded); + $enco_len = self::byteLength($encoded); + + // Wandering through the strings; init + $is_first = true; + $bias = self::initialBias; + $idx = 0; + $char = self::initialN; + + for ($enco_idx = ($delim_pos) ? ($delim_pos + 1) : 0; $enco_idx < $enco_len; ++$deco_len) { + for ($old_idx = $idx, $w = 1, $k = self::base; 1; $k += self::base) { + $digit = $this->decodeDigit($encoded{$enco_idx++}); + $idx += $digit * $w; + $t = ($k <= $bias) ? self::tMin : + (($k >= $bias + self::tMax) ? self::tMax : ($k - $bias)); + if ($digit < $t) { + break; + } + $w = (int) ($w * (self::base - $t)); + } + $bias = $this->adapt($idx - $old_idx, $deco_len + 1, $is_first); + $is_first = false; + $char += (int) ($idx / ($deco_len + 1)); + $idx %= ($deco_len + 1); + if ($deco_len > 0) { + // Make room for the decoded char + for ($i = $deco_len; $i > $idx; $i--) { + $decoded[$i] = $decoded[($i - 1)]; + } + } + $decoded[$idx++] = $char; + } + return $this->UnicodeTranscoder->ucs4array_utf8($decoded); + } + + /** + * The actual encoding algorithm + * @param array $decoded + * @return mixed + */ + public function encode($decoded) + { + // We cannot encode a domain name containing the Punycode prefix + $extract = self::byteLength(self::punycodePrefix); + $check_pref = $this->UnicodeTranscoder->utf8_ucs4array(self::punycodePrefix); + $check_deco = array_slice($decoded, 0, $extract); + + if ($check_pref == $check_deco) { + throw new \InvalidArgumentException('This is already a Punycode string'); + } + // We will not try to encode strings consisting of basic code points only + $encodable = false; + foreach ($decoded as $k => $v) { + if ($v > 0x7a) { + $encodable = true; + break; + } + } + if (!$encodable) { + return false; + } + // Do NAMEPREP + $decoded = $this->namePrep($decoded); + if (!$decoded || !is_array($decoded)) { + return false; // NAMEPREP failed + } + $deco_len = count($decoded); + if (!$deco_len) { + return false; // Empty array + } + $codecount = 0; // How many chars have been consumed + $encoded = ''; + // Copy all basic code points to output + for ($i = 0; $i < $deco_len; ++$i) { + $test = $decoded[$i]; + // Will match [-0-9a-zA-Z] + if ((0x2F < $test && $test < 0x40) + || (0x40 < $test && $test < 0x5B) + || (0x60 < $test && $test <= 0x7B) + || (0x2D == $test)) { + $encoded .= chr($decoded[$i]); + $codecount++; + } + } + if ($codecount == $deco_len) { + return $encoded; // All codepoints were basic ones + } + // Start with the prefix; copy it to output + $encoded = self::punycodePrefix . $encoded; + // If we have basic code points in output, add an hyphen to the end + if ($codecount) { + $encoded .= '-'; + } + // Now find and encode all non-basic code points + $is_first = true; + $cur_code = self::initialN; + $bias = self::initialBias; + $delta = 0; + while ($codecount < $deco_len) { + // Find the smallest code point >= the current code point and + // remember the last ouccrence of it in the input + for ($i = 0, $next_code = self::maxUcs; $i < $deco_len; $i++) { + if ($decoded[$i] >= $cur_code && $decoded[$i] <= $next_code) { + $next_code = $decoded[$i]; + } + } + $delta += ($next_code - $cur_code) * ($codecount + 1); + $cur_code = $next_code; + + // Scan input again and encode all characters whose code point is $cur_code + for ($i = 0; $i < $deco_len; $i++) { + if ($decoded[$i] < $cur_code) { + $delta++; + } elseif ($decoded[$i] == $cur_code) { + for ($q = $delta, $k = self::base; 1; $k += self::base) { + $t = ($k <= $bias) + ? self::tMin + : (($k >= $bias + self::tMax) ? self::tMax : $k - $bias); + if ($q < $t) { + break; + } + + $encoded .= $this->encodeDigit(intval($t + (($q - $t) % (self::base - $t)))); + $q = (int) (($q - $t) / (self::base - $t)); + } + $encoded .= $this->encodeDigit($q); + $bias = $this->adapt($delta, $codecount + 1, $is_first); + $codecount++; + $delta = 0; + $is_first = false; + } + } + $delta++; + $cur_code++; + } + return $encoded; + } + + /** + * Adapt the bias according to the current code point and position + * @param int $delta + * @param int $npoints + * @param int $is_first + * @return int + */ + protected function adapt($delta, $npoints, $is_first) + { + $delta = intval($is_first ? ($delta / self::damp) : ($delta / 2)); + $delta += intval($delta / $npoints); + for ($k = 0; $delta > ((self::base - self::tMin) * self::tMax) / 2; $k += self::base) { + $delta = intval($delta / (self::base - self::tMin)); + } + return intval($k + (self::base - self::tMin + 1) * $delta / ($delta + self::skew)); + } + + /** + * Encoding a certain digit + * @param int $d + * @return string + */ + protected function encodeDigit($d) + { + return chr($d + 22 + 75 * ($d < 26)); + } + + /** + * Decode a certain digit + * @param int $cp + * @return int + */ + protected function decodeDigit($cp) + { + $cp = ord($cp); + if ($cp - 48 < 10) { + + return $cp - 22; + } + + if ($cp - 65 < 26) { + + return $cp - 65; + } + if ($cp - 97 < 26) { + + return $cp - 97; + } + + return self::base; + } + + /** + * Do Nameprep according to RFC3491 and RFC3454 + * @param array $input Unicode Characters + * @return string Unicode Characters, Nameprep'd + */ + protected function namePrep($input) + { + $output = []; + // + // Mapping + // Walking through the input array, performing the required steps on each of + // the input chars and putting the result into the output array + // While mapping required chars we apply the canonical ordering + foreach ($input as $v) { + // Map to nothing == skip that code point + if (in_array($v, $this->NamePrepData->mapToNothing)) { + continue; + } + // Try to find prohibited input + if (in_array($v, $this->NamePrepData->prohibit) || in_array($v, $this->NamePrepData->generalProhibited)) { + throw new \InvalidArgumentException(sprintf('NAMEPREP: Prohibited input U+%08X', $v)); + } + foreach ($this->NamePrepData->prohibitRanges as $range) { + if ($range[0] <= $v && $v <= $range[1]) { + throw new \InvalidArgumentException(sprintf('NAMEPREP: Prohibited input U+%08X', $v)); + } + } + + if (0xAC00 <= $v && $v <= 0xD7AF) { + // Hangul syllable decomposition + foreach ($this->hangulDecompose($v) as $out) { + $output[] = (int) $out; + } + } elseif (isset($this->NamePrepData->replaceMaps[$v])) { + foreach ($this->applyCanonicalOrdering($this->NamePrepData->replaceMaps[$v]) as $out) { + $output[] = (int) $out; + } + } else { + $output[] = (int) $v; + } + } + // Before applying any Combining, try to rearrange any Hangul syllables + $output = $this->hangulCompose($output); + // + // Combine code points + // + $last_class = 0; + $last_starter = 0; + $out_len = count($output); + for ($i = 0; $i < $out_len; ++$i) { + $class = $this->getCombiningClass($output[$i]); + if ((!$last_class || $last_class > $class) && $class) { + // Try to match + $seq_len = $i - $last_starter; + $out = $this->combine(array_slice($output, $last_starter, $seq_len)); + // On match: Replace the last starter with the composed character and remove + // the now redundant non-starter(s) + if ($out) { + $output[$last_starter] = $out; + if (count($out) != $seq_len) { + for ($j = $i + 1; $j < $out_len; ++$j) { + $output[$j - 1] = $output[$j]; + } + unset($output[$out_len]); + } + // Rewind the for loop by one, since there can be more possible compositions + $i--; + $out_len--; + $last_class = ($i == $last_starter) ? 0 : $this->getCombiningClass($output[$i - 1]); + continue; + } + } + // The current class is 0 + if (!$class) { + $last_starter = $i; + } + $last_class = $class; + } + return $output; + } + + /** + * Decomposes a Hangul syllable + * (see http://www.unicode.org/unicode/reports/tr15/#Hangul + * @param integer 32bit UCS4 code point + * @return array Either Hangul Syllable decomposed or original 32bit value as one value array + */ + protected function hangulDecompose($char) + { + $sindex = (int) $char - self::sBase; + if ($sindex < 0 || $sindex >= self::sCount) { + return [$char]; + } + $result = []; + $result[] = (int) self::lBase + $sindex / self::nCount; + $result[] = (int) self::vBase + ($sindex % self::nCount) / self::tCount; + $T = intval(self::tBase + $sindex % self::tCount); + if ($T != self::tBase) { + $result[] = $T; + } + return $result; + } + + /** + * Ccomposes a Hangul syllable + * (see http://www.unicode.org/unicode/reports/tr15/#Hangul + * @param array $input Decomposed UCS4 sequence + * @return array UCS4 sequence with syllables composed + */ + protected function hangulCompose($input) + { + $inp_len = count($input); + if (!$inp_len) { + return []; + } + $result = []; + $last = (int) $input[0]; + $result[] = $last; // copy first char from input to output + + for ($i = 1; $i < $inp_len; ++$i) { + $char = (int) $input[$i]; + $sindex = $last - self::sBase; + $lindex = $last - self::lBase; + $vindex = $char - self::vBase; + $tindex = $char - self::tBase; + // Find out, whether two current characters are LV and T + if (0 <= $sindex && $sindex < self::sCount && ($sindex % self::tCount == 0) && 0 <= $tindex && $tindex <= self::tCount) { + // create syllable of form LVT + $last += $tindex; + $result[(count($result) - 1)] = $last; // reset last + continue; // discard char + } + // Find out, whether two current characters form L and V + if (0 <= $lindex && $lindex < self::lCount && 0 <= $vindex && $vindex < self::vCount) { + // create syllable of form LV + $last = (int) self::sBase + ($lindex * self::vCount + $vindex) * self::tCount; + $result[(count($result) - 1)] = $last; // reset last + continue; // discard char + } + // if neither case was true, just add the character + $last = $char; + $result[] = $char; + } + return $result; + } + + /** + * Returns the combining class of a certain wide char + * @param integer $char Wide char to check (32bit integer) + * @return integer Combining class if found, else 0 + */ + protected function getCombiningClass($char) + { + return isset($this->NamePrepData->normalizeCombiningClasses[$char]) + ? $this->NamePrepData->normalizeCombiningClasses[$char] + : 0; + } + + /** + * Applies the canonical ordering of a decomposed UCS4 sequence + * @param array $input Decomposed UCS4 sequence + * @return array Ordered USC4 sequence + */ + protected function applyCanonicalOrdering($input) + { + $swap = true; + $size = count($input); + while ($swap) { + $swap = false; + $last = $this->getCombiningClass(intval($input[0])); + for ($i = 0; $i < $size - 1; ++$i) { + $next = $this->getCombiningClass(intval($input[$i + 1])); + if ($next != 0 && $last > $next) { + // Move item leftward until it fits + for ($j = $i + 1; $j > 0; --$j) { + if ($this->getCombiningClass(intval($input[$j - 1])) <= $next) { + break; + } + $t = intval($input[$j]); + $input[$j] = intval($input[$j - 1]); + $input[$j - 1] = $t; + $swap = true; + } + // Reentering the loop looking at the old character again + $next = $last; + } + $last = $next; + } + } + return $input; + } + + /** + * Do composition of a sequence of starter and non-starter + * @param array $input UCS4 Decomposed sequence + * @return array Ordered USC4 sequence + */ + protected function combine($input) + { + $inp_len = count($input); + if (0 == $inp_len) { + return false; + } + foreach ($this->NamePrepData->replaceMaps as $np_src => $np_target) { + if ($np_target[0] != $input[0]) { + continue; + } + if (count($np_target) != $inp_len) { + continue; + } + $hit = false; + foreach ($input as $k2 => $v2) { + if ($v2 == $np_target[$k2]) { + $hit = true; + } else { + $hit = false; + break; + } + } + if ($hit) { + return $np_src; + } + } + return false; + } + + /** + * Gets the length of a string in bytes even if mbstring function + * overloading is turned on + * + * @param string $string the string for which to get the length. + * @return integer the length of the string in bytes. + */ + protected static function byteLength($string) + { + if (self::$isMbStringOverload) { + return mb_strlen($string, '8bit'); + } + return strlen((binary) $string); + } +} diff --git a/lib/classes/idna/ext/PunycodeInterface.php b/lib/classes/idna/ext/PunycodeInterface.php new file mode 100644 index 00000000..c64ad368 --- /dev/null +++ b/lib/classes/idna/ext/PunycodeInterface.php @@ -0,0 +1,20 @@ + + * @copyright 2004-2016 phlyLabs Berlin, http://phlylabs.de + */ + +namespace Mso\IdnaConvert; + +interface PunycodeInterface +{ + + public function __construct(NamePrepDataInterface $NamePrepData, UnicodeTranscoderInterface $UCTC); + + public function getPunycodePrefix(); + + public function decode($encoded); + + public function encode($decoded); + +} diff --git a/lib/classes/idna/ext/UnicodeTranscoder.php b/lib/classes/idna/ext/UnicodeTranscoder.php new file mode 100644 index 00000000..3d9118fb --- /dev/null +++ b/lib/classes/idna/ext/UnicodeTranscoder.php @@ -0,0 +1,343 @@ + + * @copyright 2003-2016 phlyLabs Berlin, http://phlylabs.de + * @version 0.1.1 2016-01-24 + */ + +namespace Mso\IdnaConvert; + +class UnicodeTranscoder implements UnicodeTranscoderInterface +{ + private static $mechs = ['ucs4', 'ucs4array', 'utf8', 'utf7', 'utf7imap']; + // unsupported yet: 'ucs4le', 'ucs4be', 'utf16', 'utf16le', 'utf16be' + + private static $allow_overlong = false; + private static $safe_mode; + private static $safe_char; + + /** + * The actual conversion routine + * + * @param mixed $data The data to convert, usually a string, array when converting from UCS-4 array + * @param string $from Original encoding of the data + * @param string $to Target encoding of the data + * @param bool $safe_mode SafeMode tries to correct invalid codepoints + * @param int $safe_char Unicode Codepoint as placeholder for all otherwise broken characters + * @return mixed False on failure, String or array on success, depending on target encoding + * @access public + * @throws \InvalidArgumentException + * @since 0.0.1 + */ + public static function convert($data, $from, $to, $safe_mode = false, $safe_char = 0xFFFC) + { + self::$safe_mode = ($safe_mode) ? true : false; + self::$safe_char = ($safe_char) ? $safe_char : 0xFFFC; + + if (self::$safe_mode) { + self::$allow_overlong = true; + } + if (!in_array($from, self::$mechs)) { + throw new \InvalidArgumentException(sprintf('Invalid input format %s', $from)); + } + if (!in_array($to, self::$mechs)) { + throw new \InvalidArgumentException(sprintf('Invalid output format %s', $to)); + } + if ($from != 'ucs4array') { + $methodName = $from.'_ucs4array'; + $data = self::$methodName($data); + } + if ($to != 'ucs4array') { + $methodName = 'ucs4array_'.$to; + $data = self::$methodName($data); + } + + return $data; + } + + /** + * This converts an UTF-8 encoded string to its UCS-4 representation + * + * @param string $input The UTF-8 string to convert + * @return array Array of 32bit values representing each codepoint + * @throws \InvalidArgumentException + * @access public + */ + public static function utf8_ucs4array($input) + { + $start_byte = $next_byte = 0; + + $output = []; + $out_len = 0; + $inp_len = strlen($input); + $mode = 'next'; + $test = 'none'; + for ($k = 0; $k < $inp_len; ++$k) { + $v = ord($input{$k}); // Extract byte from input string + + if ($v < 128) { // We found an ASCII char - put into stirng as is + $output[$out_len] = $v; + ++$out_len; + if ('add' == $mode) { + if (self::$safe_mode) { + $output[$out_len - 2] = self::$safe_char; + $mode = 'next'; + } else { + throw new \InvalidArgumentException(sprintf('Conversion from UTF-8 to UCS-4 failed: malformed input at byte %d', $k)); + } + } + continue; + } + if ('next' == $mode) { // Try to find the next start byte; determine the width of the Unicode char + $start_byte = $v; + $mode = 'add'; + $test = 'range'; + if ($v >> 5 == 6) { // &110xxxxx 10xxxxx + $next_byte = 0; // Tells, how many times subsequent bitmasks must rotate 6bits to the left + $v = ($v - 192) << 6; + } elseif ($v >> 4 == 14) { // &1110xxxx 10xxxxxx 10xxxxxx + $next_byte = 1; + $v = ($v - 224) << 12; + } elseif ($v >> 3 == 30) { // &11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + $next_byte = 2; + $v = ($v - 240) << 18; + } elseif (self::$safe_mode) { + $mode = 'next'; + $output[$out_len] = self::$safe_char; + ++$out_len; + continue; + } else { + throw new \InvalidArgumentException(sprintf('This might be UTF-8, but I don\'t understand it at byte %d', $k)); + } + if ($inp_len - $k - $next_byte < 2) { + $output[$out_len] = self::$safe_char; + $mode = 'no'; + continue; + } + + if ('add' == $mode) { + $output[$out_len] = (int)$v; + ++$out_len; + continue; + } + } + if ('add' == $mode) { + if (!self::$allow_overlong && $test == 'range') { + $test = 'none'; + if (($v < 0xA0 && $start_byte == 0xE0) || ($v < 0x90 && $start_byte == 0xF0) || ($v > 0x8F && $start_byte == 0xF4)) { + throw new \InvalidArgumentException(sprintf('Bogus UTF-8 character detected (out of legal range) at byte %d', $k)); + } + } + if ($v >> 6 == 2) { // Bit mask must be 10xxxxxx + $v = ($v - 128) << ($next_byte * 6); + $output[($out_len - 1)] += $v; + --$next_byte; + } else { + if (self::$safe_mode) { + $output[$out_len - 1] = ord(self::$safe_char); + $k--; + $mode = 'next'; + continue; + } else { + throw new \InvalidArgumentException(sprintf('Conversion from UTF-8 to UCS-4 failed: malformed input at byte %d', $k)); + } + } + if ($next_byte < 0) { + $mode = 'next'; + } + } + } // for + + return $output; + } + + /** + * Convert UCS-4 arary into UTF-8 string + * See utf8_ucs4array() for details + * @param $input array Array of UCS-4 codepoints + * @return string + * @access public + */ + public static function ucs4array_utf8($input) + { + $output = ''; + foreach ($input as $k => $v) { + if ($v < 128) { // 7bit are transferred literally + $output .= chr($v); + } elseif ($v < (1 << 11)) { // 2 bytes + $output .= chr(192 + ($v >> 6)) . chr(128 + ($v & 63)); + } elseif ($v < (1 << 16)) { // 3 bytes + $output .= chr(224 + ($v >> 12)) . chr(128 + (($v >> 6) & 63)) . chr(128 + ($v & 63)); + } elseif ($v < (1 << 21)) { // 4 bytes + $output .= chr(240 + ($v >> 18)) . chr(128 + (($v >> 12) & 63)) . chr(128 + (($v >> 6) & 63)) . chr(128 + ($v & 63)); + } elseif (self::$safe_mode) { + $output .= self::$safe_char; + } else { + throw new \InvalidArgumentException(sprintf('Conversion from UCS-4 to UTF-8 failed: malformed input at byte %d', $k)); + } + } + + return $output; + } + + public static function utf7imap_ucs4array($input) + { + return self::utf7_ucs4array(str_replace(',', '/', $input), '&'); + } + + public static function utf7_ucs4array($input, $sc = '+') + { + $output = []; + $out_len = 0; + $inp_len = strlen($input); + $mode = 'd'; + $b64 = ''; + + for ($k = 0; $k < $inp_len; ++$k) { + $c = $input{$k}; + + // Ignore zero bytes + if (0 == ord($c)) { + continue; + } + if ('b' == $mode) { + // Sequence got terminated + if (!preg_match('![A-Za-z0-9/' . preg_quote($sc, '!') . ']!', $c)) { + if ('-' == $c) { + if ($b64 == '') { + $output[$out_len] = ord($sc); + $out_len++; + $mode = 'd'; + + continue; + } + } + $tmp = base64_decode($b64); + $tmp = substr($tmp, -1 * (strlen($tmp) % 2)); + for ($i = 0; $i < strlen($tmp); $i++) { + if ($i % 2) { + $output[$out_len] += ord($tmp{$i}); + $out_len++; + } else { + $output[$out_len] = ord($tmp{$i}) << 8; + } + } + $mode = 'd'; + $b64 = ''; + + continue; + } else { + $b64 .= $c; + } + } + if ('d' == $mode) { + if ($sc == $c) { + $mode = 'b'; + + continue; + } + $output[$out_len] = ord($c); + $out_len++; + } + } + + return $output; + } + + public static function ucs4array_utf7imap($input) + { + return str_replace('/', ',', self::ucs4array_utf7($input, '&')); + } + + public static function ucs4array_utf7($input, $sc = '+') + { + $output = ''; + $mode = 'd'; + $b64 = ''; + while (true) { + $v = (!empty($input)) ? array_shift($input) : false; + $is_direct = (false !== $v) ? (0x20 <= $v && $v <= 0x7e && $v != ord($sc)) : true; + if ($mode == 'b') { + if ($is_direct) { + if ($b64 == chr(0) . $sc) { + $output .= $sc . '-'; + $b64 = ''; + } elseif ($b64) { + $output .= $sc . str_replace('=', '', base64_encode($b64)) . '-'; + $b64 = ''; + } + $mode = 'd'; + } elseif (false !== $v) { + $b64 .= chr(($v >> 8) & 255) . chr($v & 255); + } + } + if ($mode == 'd' && false !== $v) { + if ($is_direct) { + $output .= chr($v); + } else { + $b64 = chr(($v >> 8) & 255) . chr($v & 255); + $mode = 'b'; + } + } + if (false === $v && $b64 == '') break; + } + + return $output; + } + + /** + * Convert UCS-4 array into UCS-4 string (Little Endian at the moment) + * @param $input array UCS-4 code points + * @return string + * @access public + */ + public static function ucs4array_ucs4($input) + { + $output = ''; + foreach ($input as $v) { + $output .= chr(($v >> 24) & 255) . chr(($v >> 16) & 255) . chr(($v >> 8) & 255) . chr($v & 255); + } + + return $output; + } + + /** + * Convert UCS-4 string (LE ar the moment) into UCS-4 array + * @param $input string UCS-4 LE string + * @return array + * @access public + */ + public static function ucs4_ucs4array($input) + { + $output = []; + + $inp_len = strlen($input); + // Input length must be dividable by 4 + if ($inp_len % 4) { + throw new \InvalidArgumentException('Input UCS4 string is broken'); + } + // Empty input - return empty output + if (!$inp_len) return $output; + + for ($i = 0, $out_len = -1; $i < $inp_len; ++$i) { + if (!($i % 4)) { // Increment output position every 4 input bytes + $out_len++; + $output[$out_len] = 0; + } + $output[$out_len] += ord($input{$i}) << (8 * (3 - ($i % 4))); + } + + return $output; + } +} diff --git a/lib/classes/idna/ext/UnicodeTranscoderInterface.php b/lib/classes/idna/ext/UnicodeTranscoderInterface.php new file mode 100644 index 00000000..b8b1b505 --- /dev/null +++ b/lib/classes/idna/ext/UnicodeTranscoderInterface.php @@ -0,0 +1,40 @@ + + * @copyright 2003-2016 phlyLabs Berlin, http://phlylabs.de + * @version 0.1.0 2016-01-08 + */ + +namespace Mso\IdnaConvert; + +interface UnicodeTranscoderInterface +{ + public static function convert($data, $from, $to, $safe_mode = false, $safe_char = 0xFFFC); + + public static function utf8_ucs4array($input); + + public static function ucs4array_utf8($input); + + public static function utf7imap_ucs4array($input); + + public static function utf7_ucs4array($input, $sc = '+'); + + public static function ucs4array_utf7imap($input); + + public static function ucs4array_utf7($input, $sc = '+'); + + public static function ucs4array_ucs4($input); + + public static function ucs4_ucs4array($input); +} diff --git a/lib/functions.php b/lib/functions.php index b69dc5b6..ebfd5bb6 100644 --- a/lib/functions.php +++ b/lib/functions.php @@ -94,6 +94,12 @@ class Autoloader { dirname(dirname(__FILE__)) . '/install/', ); + if (substr($class, 0, 15) == "Mso\IdnaConvert") { + $class = substr($class, 16); + include_once __DIR__.'/classes/idna/ext/'.$class.'.php'; + return true; + } + // now iterate through the paths foreach ($paths as $path) { // valid directory? From b6f99958fda6b2492e8fdeabed45dc8ab6435265 Mon Sep 17 00:00:00 2001 From: Daniel Reichelt Date: Fri, 17 Jun 2016 14:32:06 +0200 Subject: [PATCH 0103/1335] dns: fix generation of alias records for subdomains The checks whether or not to create wildcard/www records for subdomains wrongly depended on the setting of the parent domain instead of the subdomain in question. --- lib/functions/dns/function.createDomainZone.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/functions/dns/function.createDomainZone.php b/lib/functions/dns/function.createDomainZone.php index 424c6ceb..3fadb2ec 100644 --- a/lib/functions/dns/function.createDomainZone.php +++ b/lib/functions/dns/function.createDomainZone.php @@ -67,7 +67,7 @@ function createDomainZone($domain_id, $froxlorhostname = false) { // additional required records for subdomains $subdomains_stmt = Database::prepare(" - SELECT `domain` FROM `" . TABLE_PANEL_DOMAINS . "` + SELECT `domain`, `iswildcarddomain`, `wwwserveralias` FROM `" . TABLE_PANEL_DOMAINS . "` WHERE `parentdomainid` = :domainid "); Database::pexecute($subdomains_stmt, array( @@ -81,10 +81,10 @@ function createDomainZone($domain_id, $froxlorhostname = false) addRequiredEntry(str_replace('.' . $domain['domain'], '', $subdomain['domain']), 'AAAA', $required_entries); // Check whether to add a www.-prefix - if ($domain['iswildcarddomain'] == '1') { + if ($subdomain['iswildcarddomain'] == '1') { addRequiredEntry('*.' . str_replace('.' . $domain['domain'], '', $subdomain['domain']), 'A', $required_entries); addRequiredEntry('*.' . str_replace('.' . $domain['domain'], '', $subdomain['domain']), 'AAAA', $required_entries); - } elseif ($domain['wwwserveralias'] == '1') { + } elseif ($subdomain['wwwserveralias'] == '1') { addRequiredEntry('www.' . str_replace('.' . $domain['domain'], '', $subdomain['domain']), 'A', $required_entries); addRequiredEntry('www.' . str_replace('.' . $domain['domain'], '', $subdomain['domain']), 'AAAA', $required_entries); } From d6dc71436abcfb94d97bffecfa381ce521b1eaef Mon Sep 17 00:00:00 2001 From: Daniel Reichelt Date: Fri, 17 Jun 2016 15:32:58 +0200 Subject: [PATCH 0104/1335] dns: fix generation of imap/pop3/mail/smtp A/AAAA-records The dns-editor introduced a regression where imap/pop3/mail/smtp A-records would not get created, even tough the setting system.dns_createmailentry was enabled. fix A fix --- lib/functions/dns/function.createDomainZone.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/functions/dns/function.createDomainZone.php b/lib/functions/dns/function.createDomainZone.php index 3fadb2ec..7eb878a8 100644 --- a/lib/functions/dns/function.createDomainZone.php +++ b/lib/functions/dns/function.createDomainZone.php @@ -52,6 +52,13 @@ function createDomainZone($domain_id, $froxlorhostname = false) addRequiredEntry('@', 'NS', $required_entries); if ($domain['isemaildomain'] === '1') { addRequiredEntry('@', 'MX', $required_entries); + if (Settings::Get('system.dns_createmailentry')) { + foreach(['imap', 'pop3', 'mail', 'smtp'] as $record) { + foreach(['AAAA', 'A'] as $type) { + addRequiredEntry($record, $type, $required_entries); + } + } + } } // additional required records by setting From 5f9962b6ba401ca386eb9b1e76269cc8a3c5b45e Mon Sep 17 00:00:00 2001 From: Daniel Reichelt Date: Fri, 17 Jun 2016 19:55:00 +0200 Subject: [PATCH 0105/1335] dns: fix generation of zone serials --- lib/functions/dns/function.createDomainZone.php | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/lib/functions/dns/function.createDomainZone.php b/lib/functions/dns/function.createDomainZone.php index 7eb878a8..29076004 100644 --- a/lib/functions/dns/function.createDomainZone.php +++ b/lib/functions/dns/function.createDomainZone.php @@ -272,9 +272,22 @@ function createDomainZone($domain_id, $froxlorhostname = false) $primary_ns = Settings::Get('system.hostname'); } - // TODO for now, dummy time-periods + $date = date('Ymd'); + $domain['bindserial'] = (preg_match('/^' . $date . '/', $domain['bindserial']) ? + $domain['bindserial'] + 1 : + $date . '00'); + if (!$froxlorhostname) { + $upd_stmt = Database::prepare(" + UPDATE `" . TABLE_PANEL_DOMAINS . "` SET + `bindserial` = :serial + WHERE `id` = :id + "); + Database::pexecute($upd_stmt, array('serial' => $domain['bindserial'], 'id' => $domain['id'])); + } + $soa_content = $primary_ns . " " . escapeSoaAdminMail(Settings::Get('panel.adminmail')) . " (" . PHP_EOL; $soa_content .= $domain['bindserial'] . "\t; serial" . PHP_EOL; + // TODO for now, dummy time-periods $soa_content .= "1800\t; refresh (30 mins)" . PHP_EOL; $soa_content .= "900\t; retry (15 mins)" . PHP_EOL; $soa_content .= "604800\t; expire (7 days)" . PHP_EOL; From a400fc9c659e48cff0a893e48a3dc7718a40d246 Mon Sep 17 00:00:00 2001 From: Daniel Reichelt Date: Fri, 17 Jun 2016 19:22:45 +0200 Subject: [PATCH 0106/1335] dns: no separate zonefiles for ismainbutsubto domains 1/3 move log message --- scripts/classes/class.DnsBase.php | 1 - scripts/jobs/cron_tasks.inc.dns.10.bind.php | 69 +++++++++++---------- scripts/jobs/cron_tasks.inc.dns.20.pdns.php | 51 ++++++++------- 3 files changed, 63 insertions(+), 58 deletions(-) diff --git a/scripts/classes/class.DnsBase.php b/scripts/classes/class.DnsBase.php index ba7a8506..ea63b3c4 100644 --- a/scripts/classes/class.DnsBase.php +++ b/scripts/classes/class.DnsBase.php @@ -98,7 +98,6 @@ abstract class DnsBase } if (empty($domains)) { - $this->_logger->logAction(CRON_ACTION, LOG_INFO, 'No domains found for nameserver-config, skipping...'); return null; } diff --git a/scripts/jobs/cron_tasks.inc.dns.10.bind.php b/scripts/jobs/cron_tasks.inc.dns.10.bind.php index 89120e98..330676df 100644 --- a/scripts/jobs/cron_tasks.inc.dns.10.bind.php +++ b/scripts/jobs/cron_tasks.inc.dns.10.bind.php @@ -35,40 +35,43 @@ class bind extends DnsBase $domains = $this->getDomainList(); - if (! empty($domains)) { - $bindconf_file = '# ' . Settings::Get('system.bindconf_directory') . 'froxlor_bind.conf' . "\n" . '# Created ' . date('d.m.Y H:i') . "\n" . '# Do NOT manually edit this file, all changes will be deleted after the next domain change at the panel.' . "\n\n"; - - foreach ($domains as $domain) { - // check for system-hostname - $isFroxlorHostname = false; - if (isset($domain['froxlorhost']) && $domain['froxlorhost'] == 1) { - $isFroxlorHostname = true; - } - // create zone-file - $this->_logger->logAction(CRON_ACTION, LOG_DEBUG, 'Generating dns zone for ' . $domain['domain']); - $zone = createDomainZone(($domain['id'] == 'none') ? $domain : $domain['id'], $isFroxlorHostname); - $zonefile = (string)$zone; - $domain['zonefile'] = 'domains/' . $domain['domain'] . '.zone'; - $zonefile_name = makeCorrectFile(Settings::Get('system.bindconf_directory') . '/' . $domain['zonefile']); - $zonefile_handler = fopen($zonefile_name, 'w'); - fwrite($zonefile_handler, $zonefile); - fclose($zonefile_handler); - $this->_logger->logAction(CRON_ACTION, LOG_INFO, '`' . $zonefile_name . '` zone written'); - - // generate config - $bindconf_file .= $this->_generateDomainConfig($domain); - } - - // write config - $bindconf_file_handler = fopen(makeCorrectFile(Settings::Get('system.bindconf_directory') . '/froxlor_bind.conf'), 'w'); - fwrite($bindconf_file_handler, $bindconf_file); - fclose($bindconf_file_handler); - $this->_logger->logAction(CRON_ACTION, LOG_INFO, 'froxlor_bind.conf written'); - - // reload Bind - safe_exec(escapeshellcmd(Settings::Get('system.bindreload_command'))); - $this->_logger->logAction(CRON_ACTION, LOG_INFO, 'Bind9 reloaded'); + if (empty($domains)) { + $this->_logger->logAction(CRON_ACTION, LOG_INFO, 'No domains found for nameserver-config, skipping...'); + return; } + + $bindconf_file = '# ' . Settings::Get('system.bindconf_directory') . 'froxlor_bind.conf' . "\n" . '# Created ' . date('d.m.Y H:i') . "\n" . '# Do NOT manually edit this file, all changes will be deleted after the next domain change at the panel.' . "\n\n"; + + foreach ($domains as $domain) { + // check for system-hostname + $isFroxlorHostname = false; + if (isset($domain['froxlorhost']) && $domain['froxlorhost'] == 1) { + $isFroxlorHostname = true; + } + // create zone-file + $this->_logger->logAction(CRON_ACTION, LOG_DEBUG, 'Generating dns zone for ' . $domain['domain']); + $zone = createDomainZone(($domain['id'] == 'none') ? $domain : $domain['id'], $isFroxlorHostname); + $zonefile = (string)$zone; + $domain['zonefile'] = 'domains/' . $domain['domain'] . '.zone'; + $zonefile_name = makeCorrectFile(Settings::Get('system.bindconf_directory') . '/' . $domain['zonefile']); + $zonefile_handler = fopen($zonefile_name, 'w'); + fwrite($zonefile_handler, $zonefile); + fclose($zonefile_handler); + $this->_logger->logAction(CRON_ACTION, LOG_INFO, '`' . $zonefile_name . '` zone written'); + + // generate config + $bindconf_file .= $this->_generateDomainConfig($domain); + } + + // write config + $bindconf_file_handler = fopen(makeCorrectFile(Settings::Get('system.bindconf_directory') . '/froxlor_bind.conf'), 'w'); + fwrite($bindconf_file_handler, $bindconf_file); + fclose($bindconf_file_handler); + $this->_logger->logAction(CRON_ACTION, LOG_INFO, 'froxlor_bind.conf written'); + + // reload Bind + safe_exec(escapeshellcmd(Settings::Get('system.bindreload_command'))); + $this->_logger->logAction(CRON_ACTION, LOG_INFO, 'Bind9 reloaded'); } private function _generateDomainConfig($domain = array()) diff --git a/scripts/jobs/cron_tasks.inc.dns.20.pdns.php b/scripts/jobs/cron_tasks.inc.dns.20.pdns.php index 3847657d..efc50b31 100644 --- a/scripts/jobs/cron_tasks.inc.dns.20.pdns.php +++ b/scripts/jobs/cron_tasks.inc.dns.20.pdns.php @@ -34,31 +34,34 @@ class pdns extends DnsBase $domains = $this->getDomainList(); - if (! empty($domains)) { - - foreach ($domains as $domain) { - // check for system-hostname - $isFroxlorHostname = false; - if (isset($domain['froxlorhost']) && $domain['froxlorhost'] == 1) { - $isFroxlorHostname = true; - } - // create zone-file - $this->_logger->logAction(CRON_ACTION, LOG_DEBUG, 'Generating dns zone for ' . $domain['domain']); - $zone = createDomainZone(($domain['id'] == 'none') ? $domain : $domain['id'], $isFroxlorHostname); - - $dom_id = $this->_insertZone($zone->origin, $zone->serial); - $this->_insertRecords($dom_id, $zone->records, $zone->origin); - $this->_insertAllowedTransfers($dom_id); - - $this->_logger->logAction(CRON_ACTION, LOG_INFO, '`' . $domain['domain'] . '` zone written'); - } - - $this->_logger->logAction(CRON_ACTION, LOG_INFO, 'Database updated'); - - // reload Bind - safe_exec(escapeshellcmd(Settings::Get('system.bindreload_command'))); - $this->_logger->logAction(CRON_ACTION, LOG_INFO, 'pdns reloaded'); + if (empty($domains)) { + $this->_logger->logAction(CRON_ACTION, LOG_INFO, 'No domains found for nameserver-config, skipping...'); + return; } + + + foreach ($domains as $domain) { + // check for system-hostname + $isFroxlorHostname = false; + if (isset($domain['froxlorhost']) && $domain['froxlorhost'] == 1) { + $isFroxlorHostname = true; + } + // create zone-file + $this->_logger->logAction(CRON_ACTION, LOG_DEBUG, 'Generating dns zone for ' . $domain['domain']); + $zone = createDomainZone(($domain['id'] == 'none') ? $domain : $domain['id'], $isFroxlorHostname); + + $dom_id = $this->_insertZone($zone->origin, $zone->serial); + $this->_insertRecords($dom_id, $zone->records, $zone->origin); + $this->_insertAllowedTransfers($dom_id); + + $this->_logger->logAction(CRON_ACTION, LOG_INFO, '`' . $domain['domain'] . '` zone written'); + } + + $this->_logger->logAction(CRON_ACTION, LOG_INFO, 'Database updated'); + + // reload Bind + safe_exec(escapeshellcmd(Settings::Get('system.bindreload_command'))); + $this->_logger->logAction(CRON_ACTION, LOG_INFO, 'pdns reloaded'); } private function _cleanZonefiles() From 631e36f4d5bb28e18691f7642f8c782609188627 Mon Sep 17 00:00:00 2001 From: Daniel Reichelt Date: Fri, 17 Jun 2016 19:21:10 +0200 Subject: [PATCH 0107/1335] dns: no separate zonefiles for ismainbutsubto domains 2/3 rewrite getDomainList() to contain parent-relations --- scripts/classes/class.DnsBase.php | 72 ++++++++++++++++++++++++++++--- 1 file changed, 65 insertions(+), 7 deletions(-) diff --git a/scripts/classes/class.DnsBase.php b/scripts/classes/class.DnsBase.php index ea63b3c4..5103ee0d 100644 --- a/scripts/classes/class.DnsBase.php +++ b/scripts/classes/class.DnsBase.php @@ -69,14 +69,38 @@ abstract class DnsBase protected function getDomainList() { - // get all Domains - $result_domains_stmt = Database::query(" - SELECT `d`.`id`, `d`.`domain`, `d`.`customerid`, `d`.`zonefile`, `c`.`loginname`, `c`.`guid` - FROM `" . TABLE_PANEL_DOMAINS . "` `d` LEFT JOIN `" . TABLE_PANEL_CUSTOMERS . "` `c` USING(`customerid`) - WHERE `d`.`isbinddomain` = '1' ORDER BY `d`.`domain` ASC + $result_domains_stmt = Database::query( + " + SELECT + `d`.`id`, + `d`.`domain`, + `d`.`isemaildomain`, + `d`.`iswildcarddomain`, + `d`.`wwwserveralias`, + `d`.`customerid`, + `d`.`zonefile`, + `d`.`bindserial`, + `d`.`dkim`, + `d`.`dkim_id`, + `d`.`dkim_pubkey`, + `d`.`ismainbutsubto`, + `c`.`loginname`, + `c`.`guid` + FROM + `" . TABLE_PANEL_DOMAINS . "` `d` + LEFT JOIN `" . TABLE_PANEL_CUSTOMERS . "` `c` USING(`customerid`) + WHERE + `d`.`isbinddomain` = '1' + ORDER BY + `d`.`domain` ASC "); - $domains = $result_domains_stmt->fetchAll(PDO::FETCH_ASSOC); + $domains = array(); + // don't use fetchall() to be able to set the first column to the domain id and use it later on to set the rows' + // array of direct children without having to search the outer array + while ($domain = $result_domains_stmt->fetch(PDO::FETCH_ASSOC)) { + $domains[$domain["id"]] = $domain; + } // frolxor-hostname (#1090) if (Settings::get('system.dns_createhostnameentry') == 1) { @@ -101,7 +125,41 @@ abstract class DnsBase return null; } - return $domains; + // collect domain IDs of direct child domains as arrays in ['children'] column + foreach (array_keys($domains) as $key) { + if (! isset($domains[$key]['children'])) { + $domains[$key]['children'] = array(); + } + if ($domains[$key]['ismainbutsubto'] > 0) { + if (isset($domains[ $domains[$key]['ismainbutsubto'] ])) { + $domains[ $domains[$key]['ismainbutsubto'] ]['children'][] = $domains[$key]['id']; + } else { + $this->_logger->logAction(CRON_ACTION, LOG_ERR, + 'Database inconsistency: domain ' . $domain['domain'] . ' (ID #' . $key . + ') is set to to be subdomain to non-existent domain ID #' . + $domains[$key]['ismainbutsubto'] . + '. No DNS record(s) will be created for this domain.'); + } + } + } + + $this->_logger->logAction(CRON_ACTION, LOG_DEBUG, + str_pad('domId', 9, ' ') . str_pad('domain', 40, ' ') . + 'ismainbutsubto ' . str_pad('parent domain', 40, ' ') . + "list of child domain ids"); + foreach ($domains as $domain) { + $logLine = + str_pad($domain['id'], 9, ' ') . + str_pad($domain['domain'], 40, ' ') . + str_pad($domain['ismainbutsubto'], 15, ' ') . + str_pad(((isset($domains[ $domain['ismainbutsubto'] ])) ? + $domains[ $domain['ismainbutsubto'] ]['domain'] : + '-'), 40, ' ') . + join(', ', $domain['children']); + $this->_logger->logAction(CRON_ACTION, LOG_DEBUG, $logLine); + } + + return $domains; } public function writeDKIMconfigs() From 50317da18597d873129a196d3f097ed12851a479 Mon Sep 17 00:00:00 2001 From: Daniel Reichelt Date: Fri, 17 Jun 2016 20:15:44 +0200 Subject: [PATCH 0108/1335] dns: no separate zonefiles for ismainbutsubto domains 3/3 replace iteration over $domains array by recursive walkDomainList() --- .../dns/function.createDomainZone.php | 66 +++++++--------- scripts/jobs/cron_tasks.inc.dns.10.bind.php | 78 +++++++++++++------ scripts/jobs/cron_tasks.inc.dns.20.pdns.php | 70 +++++++++++++---- 3 files changed, 138 insertions(+), 76 deletions(-) diff --git a/lib/functions/dns/function.createDomainZone.php b/lib/functions/dns/function.createDomainZone.php index 29076004..b99f193a 100644 --- a/lib/functions/dns/function.createDomainZone.php +++ b/lib/functions/dns/function.createDomainZone.php @@ -14,7 +14,7 @@ * @package Functions * */ -function createDomainZone($domain_id, $froxlorhostname = false) +function createDomainZone($domain_id, $froxlorhostname = false, $isMainButSubTo = false) { if (!$froxlorhostname) { @@ -49,7 +49,9 @@ function createDomainZone($domain_id, $froxlorhostname = false) addRequiredEntry('@', 'A', $required_entries); addRequiredEntry('@', 'AAAA', $required_entries); - addRequiredEntry('@', 'NS', $required_entries); + if (! $isMainButSubTo) { + addRequiredEntry('@', 'NS', $required_entries); + } if ($domain['isemaildomain'] === '1') { addRequiredEntry('@', 'MX', $required_entries); if (Settings::Get('system.dns_createmailentry')) { @@ -96,20 +98,6 @@ function createDomainZone($domain_id, $froxlorhostname = false) addRequiredEntry('www.' . str_replace('.' . $domain['domain'], '', $subdomain['domain']), 'AAAA', $required_entries); } } - - // additional required records for main-but-subdomain-to - $mainbutsub_stmt = Database::prepare(" - SELECT `domain` FROM `" . TABLE_PANEL_DOMAINS . "` - WHERE `ismainbutsubto` = :domainid - "); - Database::pexecute($mainbutsub_stmt, array( - 'domainid' => $domain_id - )); - - while ($mainbutsubtodomain = $mainbutsub_stmt->fetch(PDO::FETCH_ASSOC)) { - // Add NS entry for subdomain-records of "main-but-subdomain-to"-domains, they get their own Zone - addRequiredEntry(str_replace('.' . $domain['domain'], '', $mainbutsubtodomain['domain']), 'NS', $required_entries); - } } // additional required records for SPF and DKIM if activated @@ -272,30 +260,32 @@ function createDomainZone($domain_id, $froxlorhostname = false) $primary_ns = Settings::Get('system.hostname'); } - $date = date('Ymd'); - $domain['bindserial'] = (preg_match('/^' . $date . '/', $domain['bindserial']) ? - $domain['bindserial'] + 1 : - $date . '00'); - if (!$froxlorhostname) { - $upd_stmt = Database::prepare(" - UPDATE `" . TABLE_PANEL_DOMAINS . "` SET - `bindserial` = :serial - WHERE `id` = :id - "); - Database::pexecute($upd_stmt, array('serial' => $domain['bindserial'], 'id' => $domain['id'])); + if (! $isMainButSubTo) { + $date = date('Ymd'); + $domain['bindserial'] = (preg_match('/^' . $date . '/', $domain['bindserial']) ? + $domain['bindserial'] + 1 : + $date . '00'); + if (!$froxlorhostname) { + $upd_stmt = Database::prepare(" + UPDATE `" . TABLE_PANEL_DOMAINS . "` SET + `bindserial` = :serial + WHERE `id` = :id + "); + Database::pexecute($upd_stmt, array('serial' => $domain['bindserial'], 'id' => $domain['id'])); + } + + $soa_content = $primary_ns . " " . escapeSoaAdminMail(Settings::Get('panel.adminmail')) . " (" . PHP_EOL; + $soa_content .= $domain['bindserial'] . "\t; serial" . PHP_EOL; + // TODO for now, dummy time-periods + $soa_content .= "1800\t; refresh (30 mins)" . PHP_EOL; + $soa_content .= "900\t; retry (15 mins)" . PHP_EOL; + $soa_content .= "604800\t; expire (7 days)" . PHP_EOL; + $soa_content .= "1200\t)\t; minimum (20 mins)"; + + $soa_record = new DnsEntry('@', 'SOA', $soa_content); + array_unshift($zonerecords, $soa_record); } - $soa_content = $primary_ns . " " . escapeSoaAdminMail(Settings::Get('panel.adminmail')) . " (" . PHP_EOL; - $soa_content .= $domain['bindserial'] . "\t; serial" . PHP_EOL; - // TODO for now, dummy time-periods - $soa_content .= "1800\t; refresh (30 mins)" . PHP_EOL; - $soa_content .= "900\t; retry (15 mins)" . PHP_EOL; - $soa_content .= "604800\t; expire (7 days)" . PHP_EOL; - $soa_content .= "1200\t)\t; minimum (20 mins)"; - - $soa_record = new DnsEntry('@', 'SOA', $soa_content); - array_unshift($zonerecords, $soa_record); - $zone = new DnsZone((int) Settings::Get('system.defaultttl'), $domain['domain'], $domain['bindserial'], $zonerecords); return $zone; diff --git a/scripts/jobs/cron_tasks.inc.dns.10.bind.php b/scripts/jobs/cron_tasks.inc.dns.10.bind.php index 330676df..091cc6ce 100644 --- a/scripts/jobs/cron_tasks.inc.dns.10.bind.php +++ b/scripts/jobs/cron_tasks.inc.dns.10.bind.php @@ -19,6 +19,8 @@ if (! defined('MASTER_CRONJOB')) class bind extends DnsBase { + private $_bindconf_file = ""; + public function writeConfigs() { // tell the world what we are doing @@ -43,35 +45,67 @@ class bind extends DnsBase $bindconf_file = '# ' . Settings::Get('system.bindconf_directory') . 'froxlor_bind.conf' . "\n" . '# Created ' . date('d.m.Y H:i') . "\n" . '# Do NOT manually edit this file, all changes will be deleted after the next domain change at the panel.' . "\n\n"; foreach ($domains as $domain) { + if ($domain['ismainbutsubto'] > 0) { + // domains with ismainbutsubto>0 are handled by recursion within walkDomainList() + continue; + } + $this->walkDomainList($domain, $domains); + } + + $bindconf_file_handler = fopen(makeCorrectFile(Settings::Get('system.bindconf_directory') . '/froxlor_bind.conf'), 'w'); + fwrite($bindconf_file_handler, $this->_bindconf_file); + fclose($bindconf_file_handler); + $this->_logger->logAction(CRON_ACTION, LOG_INFO, 'froxlor_bind.conf written'); + safe_exec(escapeshellcmd(Settings::Get('system.bindreload_command'))); + $this->_logger->logAction(CRON_ACTION, LOG_INFO, 'Bind9 reloaded'); + $domains_dir = makeCorrectDir(Settings::Get('system.bindconf_directory') . '/domains/'); + + $this->_logger->logAction(CRON_ACTION, LOG_INFO, 'Task4 finished'); + } + + + private function walkDomainList($domain, $domains) + { + $zoneContent = ''; + $subzones = ''; + + foreach ($domain['children'] as $child_domain_id) { + $subzones .= $this->walkDomainList($domains[$child_domain_id], $domains); + } + + if ($domain['zonefile'] == '') { // check for system-hostname $isFroxlorHostname = false; if (isset($domain['froxlorhost']) && $domain['froxlorhost'] == 1) { $isFroxlorHostname = true; } - // create zone-file - $this->_logger->logAction(CRON_ACTION, LOG_DEBUG, 'Generating dns zone for ' . $domain['domain']); - $zone = createDomainZone(($domain['id'] == 'none') ? $domain : $domain['id'], $isFroxlorHostname); - $zonefile = (string)$zone; - $domain['zonefile'] = 'domains/' . $domain['domain'] . '.zone'; - $zonefile_name = makeCorrectFile(Settings::Get('system.bindconf_directory') . '/' . $domain['zonefile']); - $zonefile_handler = fopen($zonefile_name, 'w'); - fwrite($zonefile_handler, $zonefile); - fclose($zonefile_handler); - $this->_logger->logAction(CRON_ACTION, LOG_INFO, '`' . $zonefile_name . '` zone written'); - // generate config - $bindconf_file .= $this->_generateDomainConfig($domain); + if ($domain['ismainbutsubto'] == 0) { + $zoneContent = (string) createDomainZone(($domain['id'] == 'none') ? + $domain : + $domain['id'], + $isFroxlorHostname); + $domain['zonefile'] = 'domains/' . $domain['domain'] . '.zone'; + $zonefile_name = makeCorrectFile(Settings::Get('system.bindconf_directory') . '/' . + $domain['zonefile']); + $zonefile_handler = fopen($zonefile_name, 'w'); + fwrite($zonefile_handler, $zoneContent . $subzones); + fclose($zonefile_handler); + $this->_logger->logAction(CRON_ACTION, LOG_INFO, '`' . $zonefile_name . '` written'); + $this->_bindconf_file .= $this->_generateDomainConfig($domain); + } else { + return (string) createDomainZone(($domain['id'] == 'none') ? + $domain : + $domain['id'], + $isFroxlorHostname, + true); + } + } else { + $this->_logger->logAction(CRON_ACTION, LOG_INFO, + 'Added zonefile ' . $domain['zonefile'] . ' for domain ' . $domain['domain'] . + ' - Note that you will also have to handle ALL records for ALL subdomains.'); + $this->_bindconf_file .= $this->_generateDomainConfig($domain); } - - // write config - $bindconf_file_handler = fopen(makeCorrectFile(Settings::Get('system.bindconf_directory') . '/froxlor_bind.conf'), 'w'); - fwrite($bindconf_file_handler, $bindconf_file); - fclose($bindconf_file_handler); - $this->_logger->logAction(CRON_ACTION, LOG_INFO, 'froxlor_bind.conf written'); - - // reload Bind - safe_exec(escapeshellcmd(Settings::Get('system.bindreload_command'))); - $this->_logger->logAction(CRON_ACTION, LOG_INFO, 'Bind9 reloaded'); } private function _generateDomainConfig($domain = array()) diff --git a/scripts/jobs/cron_tasks.inc.dns.20.pdns.php b/scripts/jobs/cron_tasks.inc.dns.20.pdns.php index efc50b31..d0d59ee0 100644 --- a/scripts/jobs/cron_tasks.inc.dns.20.pdns.php +++ b/scripts/jobs/cron_tasks.inc.dns.20.pdns.php @@ -30,7 +30,7 @@ class pdns extends DnsBase $this->_connectToPdnsDb(); // clean up - $this->_cleanZonefiles(); + $this->_clearZoneTables(); $domains = $this->getDomainList(); @@ -39,22 +39,12 @@ class pdns extends DnsBase return; } - foreach ($domains as $domain) { - // check for system-hostname - $isFroxlorHostname = false; - if (isset($domain['froxlorhost']) && $domain['froxlorhost'] == 1) { - $isFroxlorHostname = true; + if ($domain['ismainbutsubto'] > 0) { + // domains with ismainbutsubto>0 are handled by recursion within walkDomainList() + continue; } - // create zone-file - $this->_logger->logAction(CRON_ACTION, LOG_DEBUG, 'Generating dns zone for ' . $domain['domain']); - $zone = createDomainZone(($domain['id'] == 'none') ? $domain : $domain['id'], $isFroxlorHostname); - - $dom_id = $this->_insertZone($zone->origin, $zone->serial); - $this->_insertRecords($dom_id, $zone->records, $zone->origin); - $this->_insertAllowedTransfers($dom_id); - - $this->_logger->logAction(CRON_ACTION, LOG_INFO, '`' . $domain['domain'] . '` zone written'); + $this->walkDomainList($domain, $domains); } $this->_logger->logAction(CRON_ACTION, LOG_INFO, 'Database updated'); @@ -64,7 +54,50 @@ class pdns extends DnsBase $this->_logger->logAction(CRON_ACTION, LOG_INFO, 'pdns reloaded'); } - private function _cleanZonefiles() + private function walkDomainList($domain, $domains) + { + $zoneContent = ''; + $subzones = array(); + + foreach ($domain['children'] as $child_domain_id) { + $subzones[] = $this->walkDomainList($domains[$child_domain_id], $domains); + } + + if ($domain['zonefile'] == '') { + // check for system-hostname + $isFroxlorHostname = false; + if (isset($domain['froxlorhost']) && $domain['froxlorhost'] == 1) { + $isFroxlorHostname = true; + } + + if ($domain['ismainbutsubto'] == 0) { + $zoneContent = createDomainZone(($domain['id'] == 'none') ? + $domain : + $domain['id'], + $isFroxlorHostname); + if (count($subzones)) { + array_push($zoneContent->records, ...$subzones); + } + $pdnsDomId = $this->_insertZone($zoneContent->origin, $zoneContent->serial); + $this->_insertRecords($pdnsDomId, $zoneContent->records, $zoneContent->origin); + $this->_insertAllowedTransfers($pdnsDomId); + $this->_logger->logAction(CRON_ACTION, LOG_INFO, 'DB entries stored for zone `' . $domain['domain'] . '`'); + } else { + return createDomainZone(($domain['id'] == 'none') ? + $domain : + $domain['id'], + $isFroxlorHostname, + true); + } + } else { + $this->_logger->logAction(CRON_ACTION, LOG_ERROR, + 'Zonefiles are NOT supported when PowerDNS is selected as DNS daemon (triggered by: ' . + $domain['domain'] . ')'); + $this->_bindconf_file .= $this->_generateDomainConfig($domain); + } + } + + private function _clearZoneTables() { $this->_logger->logAction(CRON_ACTION, LOG_INFO, 'Cleaning dns zone entries from database'); @@ -99,6 +132,11 @@ class pdns extends DnsBase foreach ($records as $record) { + if ($record instanceof DnsZone) { + $this->_insertRecords($domainid, $record->records, $record->origin); + continue; + } + if ($record->record == '@') { $_record = $origin; } From b4f90730cc32d008e5d2ad61e69ca042ba02f084 Mon Sep 17 00:00:00 2001 From: Daniel Reichelt Date: Fri, 17 Jun 2016 23:01:37 +0200 Subject: [PATCH 0109/1335] unify "reloading" the dns daemon --- scripts/classes/class.DnsBase.php | 14 ++++++++++++++ scripts/jobs/cron_tasks.inc.dns.10.bind.php | 5 +---- scripts/jobs/cron_tasks.inc.dns.20.pdns.php | 8 +++----- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/scripts/classes/class.DnsBase.php b/scripts/classes/class.DnsBase.php index 5103ee0d..65753b59 100644 --- a/scripts/classes/class.DnsBase.php +++ b/scripts/classes/class.DnsBase.php @@ -162,6 +162,20 @@ abstract class DnsBase return $domains; } + public function reloadDaemon() + { + // reload DNS daemon + $cmd = Settings::Get('system.bindreload_command'); + $cmdStatus = 1; + safe_exec(escapeshellcmd($cmd), $cmdStatus); + if ($cmdStatus === 0) { + $this->_logger->logAction(CRON_ACTION, LOG_INFO, Settings::Get('system.dns_server') . ' daemon reloaded'); + } else { + $this->_logger->logAction(CRON_ACTION, LOG_ERR, 'Error while running `' . $cmd . + '`: exit code (' . $cmdStatus . ') - please check your system logs'); + } + } + public function writeDKIMconfigs() { if (Settings::Get('dkim.use_dkim') == '1') { diff --git a/scripts/jobs/cron_tasks.inc.dns.10.bind.php b/scripts/jobs/cron_tasks.inc.dns.10.bind.php index 091cc6ce..06fbc9c8 100644 --- a/scripts/jobs/cron_tasks.inc.dns.10.bind.php +++ b/scripts/jobs/cron_tasks.inc.dns.10.bind.php @@ -56,10 +56,7 @@ class bind extends DnsBase fwrite($bindconf_file_handler, $this->_bindconf_file); fclose($bindconf_file_handler); $this->_logger->logAction(CRON_ACTION, LOG_INFO, 'froxlor_bind.conf written'); - safe_exec(escapeshellcmd(Settings::Get('system.bindreload_command'))); - $this->_logger->logAction(CRON_ACTION, LOG_INFO, 'Bind9 reloaded'); - $domains_dir = makeCorrectDir(Settings::Get('system.bindconf_directory') . '/domains/'); - + $this->reloadDaemon(); $this->_logger->logAction(CRON_ACTION, LOG_INFO, 'Task4 finished'); } diff --git a/scripts/jobs/cron_tasks.inc.dns.20.pdns.php b/scripts/jobs/cron_tasks.inc.dns.20.pdns.php index d0d59ee0..15844c71 100644 --- a/scripts/jobs/cron_tasks.inc.dns.20.pdns.php +++ b/scripts/jobs/cron_tasks.inc.dns.20.pdns.php @@ -47,11 +47,9 @@ class pdns extends DnsBase $this->walkDomainList($domain, $domains); } - $this->_logger->logAction(CRON_ACTION, LOG_INFO, 'Database updated'); - - // reload Bind - safe_exec(escapeshellcmd(Settings::Get('system.bindreload_command'))); - $this->_logger->logAction(CRON_ACTION, LOG_INFO, 'pdns reloaded'); + $this->_logger->logAction(CRON_ACTION, LOG_INFO, 'PowerDNS database updated'); + $this->reloadDaemon(); + $this->_logger->logAction(CRON_ACTION, LOG_INFO, 'Task4 finished'); } private function walkDomainList($domain, $domains) From 5789e9a8a4608ad32574ea00d6e89a5d2253380a Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sun, 19 Jun 2016 18:59:44 +0200 Subject: [PATCH 0110/1335] re-add old IDNA class so we do not have to force the php-5.6 requirement for froxlor Signed-off-by: Michael Kaufmann (d00p) --- actions/admin/settings/131.ssl.php | 2 +- install/lib/class.FroxlorInstall.php | 6 +- install/lng/english.lng.php | 1 + install/lng/german.lng.php | 1 + lib/classes/idna/class.idna_convert.php | 3464 +++++++++++++++++ .../idna/class.idna_convert_wrapper.php | 26 +- 6 files changed, 3489 insertions(+), 11 deletions(-) create mode 100644 lib/classes/idna/class.idna_convert.php diff --git a/actions/admin/settings/131.ssl.php b/actions/admin/settings/131.ssl.php index 92077c01..07d28ced 100644 --- a/actions/admin/settings/131.ssl.php +++ b/actions/admin/settings/131.ssl.php @@ -114,7 +114,7 @@ return array( 'varname' => 'letsencryptstate', 'type' => 'string', 'string_emptyallowed' => false, - 'default' => 'Germany', + 'default' => 'Hessen', 'save_method' => 'storeSettingField', ), 'system_letsencryptchallengepath' => array( diff --git a/install/lib/class.FroxlorInstall.php b/install/lib/class.FroxlorInstall.php index 2a94ba0b..545a58be 100644 --- a/install/lib/class.FroxlorInstall.php +++ b/install/lib/class.FroxlorInstall.php @@ -908,7 +908,11 @@ class FroxlorInstall $content .= $this->_status_message('red', $this->_lng['requirements']['notfound'] . ' (' . PHP_VERSION . ')'); $_die = true; } else { - $content .= $this->_status_message('green', PHP_VERSION); + if (version_compare("5.6.0", PHP_VERSION, ">=")) { + $content .= $this->_status_message('orange', $this->_lng['requirements']['newerphpprefered'] . ' (' .PHP_VERSION . ')'); + } else { + $content .= $this->_status_message('green', PHP_VERSION); + } } // Check if magic_quotes_runtime is active | get_magic_quotes_runtime() is always FALSE since 5.4 diff --git a/install/lng/english.lng.php b/install/lng/english.lng.php index 4166c54f..2d107cde 100644 --- a/install/lng/english.lng.php +++ b/install/lng/english.lng.php @@ -24,6 +24,7 @@ $lng['requirements']['notfound'] = 'not found'; $lng['requirements']['notinstalled'] = 'not installed'; $lng['requirements']['activated'] = 'enabled'; $lng['requirements']['phpversion'] = 'PHP version >= 5.3'; +$lng['requirements']['newerphpprefered'] = 'Good, but php-5.6 is prefered.'; $lng['requirements']['phpmagic_quotes_runtime'] = 'magic_quotes_runtime...'; $lng['requirements']['phpmagic_quotes_runtime_description'] = 'PHP setting "magic_quotes_runtime" must be set to "Off". We have disabled it temporary for now please fix the coresponding php.ini.'; $lng['requirements']['phppdo'] = 'PHP PDO extension and PDO-MySQL driver...'; diff --git a/install/lng/german.lng.php b/install/lng/german.lng.php index e87142cb..3bcfdcd6 100644 --- a/install/lng/german.lng.php +++ b/install/lng/german.lng.php @@ -24,6 +24,7 @@ $lng['requirements']['notfound'] = 'nicht gefunden'; $lng['requirements']['notinstalled'] = 'nicht installiert'; $lng['requirements']['activated'] = 'ist aktiviert.'; $lng['requirements']['phpversion'] = 'PHP Version >= 5.3'; +$lng['requirements']['newerphpprefered'] = 'Passt, aber php-5.6 wird bevorzugt.'; $lng['requirements']['phpmagic_quotes_runtime'] = 'magic_quotes_runtime'; $lng['requirements']['phpmagic_quotes_runtime_description'] = 'Die PHP Einstellung "magic_quotes_runtime" muss deaktiviert sein ("Off"). Die Einstellung wurde temporär deaktiviert, bitte ändern Sie diese in der entsprechenden php.ini.'; $lng['requirements']['phppdo'] = 'PHP PDO Erweiterung und PDO-MySQL Treiber...'; diff --git a/lib/classes/idna/class.idna_convert.php b/lib/classes/idna/class.idna_convert.php new file mode 100644 index 00000000..f128b6f3 --- /dev/null +++ b/lib/classes/idna/class.idna_convert.php @@ -0,0 +1,3464 @@ + + * @copyright 2004-2014 phlyLabs Berlin, http://phlylabs.de + * @version 0.9.0 2014-12-12 + */ +class idna_convert { + + private $version = '0.9.0'; + protected $sub_version = 'main'; + + // NP See below + // Internal settings, do not mess with them + protected $_punycode_prefix = 'xn--'; + protected $_invalid_ucs = 0x80000000; + protected $_max_ucs = 0x10FFFF; + protected $_base = 36; + protected $_tmin = 1; + protected $_tmax = 26; + protected $_skew = 38; + protected $_damp = 700; + protected $_initial_bias = 72; + protected $_initial_n = 0x80; + protected $_sbase = 0xAC00; + protected $_lbase = 0x1100; + protected $_vbase = 0x1161; + protected $_tbase = 0x11A7; + protected $_lcount = 19; + protected $_vcount = 21; + protected $_tcount = 28; + protected $_ncount = 588; // _vcount * _tcount + protected $_scount = 11172; // _lcount * _tcount * _vcount + protected $_error = false; + protected static $_mb_string_overload = null; + // See {@link set_parameter()} for details of how to change the following + // settings from within your script / application + protected $_api_encoding = 'utf8'; // Default input charset is UTF-8 + protected $_allow_overlong = false; // Overlong UTF-8 encodings are forbidden + protected $_strict_mode = false; // Behave strict or not + protected $_idn_version = 2003; // Can be either 2003 (old, default) or 2008 + + /** + * the constructor + * + * @param array $options + * @return boolean + * @since 0.5.2 + */ + public function __construct($options = false) + { + $this->slast = $this->_sbase + $this->_lcount * $this->_vcount * $this->_tcount; + // If parameters are given, pass these to the respective method + if (is_array($options)) { + $this->set_parameter($options); + } + + // populate mbstring overloading cache if not set + if (self::$_mb_string_overload === null) { + self::$_mb_string_overload = (extension_loaded('mbstring') && (ini_get('mbstring.func_overload') & 0x02) === 0x02); + } + } + + public function get_version() + { + return $this->version.'-'.$this->sub_version; + } + + /** + * Sets a new option value. Available options and values: + * [encoding - Use either UTF-8, UCS4 as array or UCS4 as string as input ('utf8' for UTF-8, + * 'ucs4_string' and 'ucs4_array' respectively for UCS4); The output is always UTF-8] + * [overlong - Unicode does not allow unnecessarily long encodings of chars, + * to allow this, set this parameter to true, else to false; + * default is false.] + * [strict - true: strict mode, good for registration purposes - Causes errors + * on failures; false: loose mode, ideal for "wildlife" applications + * by silently ignoring errors and returning the original input instead + * + * @param mixed Parameter to set (string: single parameter; array of Parameter => Value pairs) + * @param string Value to use (if parameter 1 is a string) + * @return boolean true on success, false otherwise + */ + public function set_parameter($option, $value = false) + { + if (!is_array($option)) { + $option = array($option => $value); + } + foreach ($option as $k => $v) { + switch ($k) { + case 'encoding': + switch ($v) { + case 'utf8': + case 'ucs4_string': + case 'ucs4_array': + $this->_api_encoding = $v; + break; + default: + $this->_error('Set Parameter: Unknown parameter ' . $v . ' for option ' . $k); + return false; + } + break; + case 'overlong': + $this->_allow_overlong = ($v) ? true : false; + break; + case 'strict': + $this->_strict_mode = ($v) ? true : false; + break; + case 'idn_version': + if (in_array($v, array('2003', '2008'))) { + $this->_idn_version = $v; + } else { + $this->_error('Set Parameter: Unknown parameter ' . $v . ' for option ' . $k); + } + break; + case 'encode_german_sz': // Deprecated + if (!$v) { + self::$NP['replacemaps'][0xDF] = array(0x73, 0x73); + } else { + unset(self::$NP['replacemaps'][0xDF]); + } + break; + default: + $this->_error('Set Parameter: Unknown option ' . $k); + return false; + } + } + return true; + } + + /** + * Decode a given ACE domain name + * @param string Domain name (ACE string) + * [@param string Desired output encoding, see {@link set_parameter}] + * @return string Decoded Domain name (UTF-8 or UCS-4) + */ + public function decode($input, $one_time_encoding = false) + { + // Optionally set + if ($one_time_encoding) { + switch ($one_time_encoding) { + case 'utf8': + case 'ucs4_string': + case 'ucs4_array': + break; + default: + $this->_error('Unknown encoding ' . $one_time_encoding); + return false; + } + } + // Make sure to drop any newline characters around + $input = trim($input); + + // Negotiate input and try to determine, whether it is a plain string, + // an email address or something like a complete URL + if (strpos($input, '@')) { // Maybe it is an email address + // No no in strict mode + if ($this->_strict_mode) { + $this->_error('Only simple domain name parts can be handled in strict mode'); + return false; + } + list ($email_pref, $input) = explode('@', $input, 2); + $arr = explode('.', $input); + foreach ($arr as $k => $v) { + if (preg_match('!^' . preg_quote($this->_punycode_prefix, '!') . '!', $v)) { + $conv = $this->_decode($v); + if ($conv) { + $arr[$k] = $conv; + } + } + } + $input = join('.', $arr); + $arr = explode('.', $email_pref); + foreach ($arr as $k => $v) { + if (preg_match('!^' . preg_quote($this->_punycode_prefix, '!') . '!', $v)) { + $conv = $this->_decode($v); + if ($conv) { + $arr[$k] = $conv; + } + } + } + $email_pref = join('.', $arr); + $return = $email_pref . '@' . $input; + } elseif (preg_match('![:\./]!', $input)) { // Or a complete domain name (with or without paths / parameters) + // No no in strict mode + if ($this->_strict_mode) { + $this->_error('Only simple domain name parts can be handled in strict mode'); + return false; + } + $parsed = parse_url($input); + if (isset($parsed['host'])) { + $arr = explode('.', $parsed['host']); + foreach ($arr as $k => $v) { + $conv = $this->_decode($v); + if ($conv) { + $arr[$k] = $conv; + } + } + $parsed['host'] = join('.', $arr); + $return = (empty($parsed['scheme']) ? '' : $parsed['scheme'] . (strtolower($parsed['scheme']) == 'mailto' ? ':' : '://')). + (empty($parsed['user']) ? '' : $parsed['user'] . (empty($parsed['pass']) ? '' : ':' . $parsed['pass']) . '@'). + $parsed['host']. + (empty($parsed['port']) ? '' : ':' . $parsed['port']). + (empty($parsed['path']) ? '' : $parsed['path']). + (empty($parsed['query']) ? '' : '?' . $parsed['query']). + (empty($parsed['fragment']) ? '' : '#' . $parsed['fragment']); + } else { // parse_url seems to have failed, try without it + $arr = explode('.', $input); + foreach ($arr as $k => $v) { + $conv = $this->_decode($v); + $arr[$k] = ($conv) ? $conv : $v; + } + $return = join('.', $arr); + } + } else { // Otherwise we consider it being a pure domain name string + $return = $this->_decode($input); + if (!$return) { + $return = $input; + } + } + // The output is UTF-8 by default, other output formats need conversion here + // If one time encoding is given, use this, else the objects property + switch (($one_time_encoding) ? $one_time_encoding : $this->_api_encoding) { + case 'utf8': return $return; // break; + case 'ucs4_string': return $this->_ucs4_to_ucs4_string($this->_utf8_to_ucs4($return)); // break; + case 'ucs4_array': return $this->_utf8_to_ucs4($return); // break; + default: $this->_error('Unsupported output format'); return false; + } + } + + /** + * Encode a given UTF-8 domain name + * @param string Domain name (UTF-8 or UCS-4) + * [@param string Desired input encoding, see {@link set_parameter}] + * @return string Encoded Domain name (ACE string) + */ + public function encode($decoded, $one_time_encoding = false) + { + // Forcing conversion of input to UCS4 array + // If one time encoding is given, use this, else the objects property + switch ($one_time_encoding ? $one_time_encoding : $this->_api_encoding) { + case 'utf8': + $decoded = $this->_utf8_to_ucs4($decoded); + break; + case 'ucs4_string': + $decoded = $this->_ucs4_string_to_ucs4($decoded); + case 'ucs4_array': + break; + default: + $this->_error('Unsupported input format: ' . ($one_time_encoding ? $one_time_encoding : $this->_api_encoding)); + return false; + } + + // No input, no output, what else did you expect? + if (empty($decoded)) { + return ''; + } + + // Anchors for iteration + $last_begin = 0; + // Output string + $output = ''; + foreach ($decoded as $k => $v) { + // Make sure to use just the plain dot + switch ($v) { + case 0x3002: + case 0xFF0E: + case 0xFF61: + $decoded[$k] = 0x2E; + // Right, no break here, the above are converted to dots anyway + // Stumbling across an anchoring character + case 0x2E: + case 0x2F: + case 0x3A: + case 0x3F: + case 0x40: + // Neither email addresses nor URLs allowed in strict mode + if ($this->_strict_mode) { + $this->_error('Neither email addresses nor URLs are allowed in strict mode.'); + return false; + } else { + // Skip first char + if ($k) { + $encoded = ''; + $encoded = $this->_encode(array_slice($decoded, $last_begin, (($k) - $last_begin))); + if ($encoded) { + $output .= $encoded; + } else { + $output .= $this->_ucs4_to_utf8(array_slice($decoded, $last_begin, (($k) - $last_begin))); + } + $output .= chr($decoded[$k]); + } + $last_begin = $k + 1; + } + } + } + // Catch the rest of the string + if ($last_begin) { + $inp_len = sizeof($decoded); + $encoded = ''; + $encoded = $this->_encode(array_slice($decoded, $last_begin, (($inp_len) - $last_begin))); + if ($encoded) { + $output .= $encoded; + } else { + $output .= $this->_ucs4_to_utf8(array_slice($decoded, $last_begin, (($inp_len) - $last_begin))); + } + return $output; + } else { + if (false !== ($output = $this->_encode($decoded))) { + return $output; + } else { + return $this->_ucs4_to_utf8($decoded); + } + } + } + + /** + * Removes a weakness of encode(), which cannot properly handle URIs but instead encodes their + * path or query components, too. + * @param string $uri Expects the URI as a UTF-8 (or ASCII) string + * @return string The URI encoded to Punycode, everything but the host component is left alone + * @since 0.6.4 + */ + public function encode_uri($uri) + { + $parsed = parse_url($uri); + if (!isset($parsed['host'])) { + $this->_error('The given string does not look like a URI'); + return false; + } + $arr = explode('.', $parsed['host']); + foreach ($arr as $k => $v) { + $conv = $this->encode($v, 'utf8'); + if ($conv) { + $arr[$k] = $conv; + } + } + $parsed['host'] = join('.', $arr); + $return = (empty($parsed['scheme']) ? '' : $parsed['scheme'] . (strtolower($parsed['scheme']) == 'mailto' ? ':' : '://')). + (empty($parsed['user']) ? '' : $parsed['user'] . (empty($parsed['pass']) ? '' : ':' . $parsed['pass']) . '@'). + $parsed['host']. + (empty($parsed['port']) ? '' : ':' . $parsed['port']). + (empty($parsed['path']) ? '' : $parsed['path']). + (empty($parsed['query']) ? '' : '?' . $parsed['query']). + (empty($parsed['fragment']) ? '' : '#' . $parsed['fragment']); + return $return; + } + + /** + * Use this method to get the last error occurred + * @param void + * @return string The last error, that occurred + */ + public function get_last_error() + { + return $this->_error; + } + + /** + * The actual decoding algorithm + * @param string + * @return mixed + */ + protected function _decode($encoded) + { + $decoded = array(); + // find the Punycode prefix + if (!preg_match('!^' . preg_quote($this->_punycode_prefix, '!') . '!', $encoded)) { + $this->_error('This is not a punycode string'); + return false; + } + $encode_test = preg_replace('!^' . preg_quote($this->_punycode_prefix, '!') . '!', '', $encoded); + // If nothing left after removing the prefix, it is hopeless + if (!$encode_test) { + $this->_error('The given encoded string was empty'); + return false; + } + // Find last occurrence of the delimiter + $delim_pos = strrpos($encoded, '-'); + if ($delim_pos > self::byteLength($this->_punycode_prefix)) { + for ($k = self::byteLength($this->_punycode_prefix); $k < $delim_pos; ++$k) { + $decoded[] = ord($encoded{$k}); + } + } + $deco_len = count($decoded); + $enco_len = self::byteLength($encoded); + + // Wandering through the strings; init + $is_first = true; + $bias = $this->_initial_bias; + $idx = 0; + $char = $this->_initial_n; + + for ($enco_idx = ($delim_pos) ? ($delim_pos + 1) : 0; $enco_idx < $enco_len; ++$deco_len) { + for ($old_idx = $idx, $w = 1, $k = $this->_base; 1; $k += $this->_base) { + $digit = $this->_decode_digit($encoded{$enco_idx++}); + $idx += $digit * $w; + $t = ($k <= $bias) ? $this->_tmin : + (($k >= $bias + $this->_tmax) ? $this->_tmax : ($k - $bias)); + if ($digit < $t) { + break; + } + $w = (int) ($w * ($this->_base - $t)); + } + $bias = $this->_adapt($idx - $old_idx, $deco_len + 1, $is_first); + $is_first = false; + $char += (int) ($idx / ($deco_len + 1)); + $idx %= ($deco_len + 1); + if ($deco_len > 0) { + // Make room for the decoded char + for ($i = $deco_len; $i > $idx; $i--) { + $decoded[$i] = $decoded[($i - 1)]; + } + } + $decoded[$idx++] = $char; + } + return $this->_ucs4_to_utf8($decoded); + } + + /** + * The actual encoding algorithm + * @param string + * @return mixed + */ + protected function _encode($decoded) + { + // We cannot encode a domain name containing the Punycode prefix + $extract = self::byteLength($this->_punycode_prefix); + $check_pref = $this->_utf8_to_ucs4($this->_punycode_prefix); + $check_deco = array_slice($decoded, 0, $extract); + + if ($check_pref == $check_deco) { + $this->_error('This is already a punycode string'); + return false; + } + // We will not try to encode strings consisting of basic code points only + $encodable = false; + foreach ($decoded as $k => $v) { + if ($v > 0x7a) { + $encodable = true; + break; + } + } + if (!$encodable) { + $this->_error('The given string does not contain encodable chars'); + return false; + } + // Do NAMEPREP + $decoded = $this->_nameprep($decoded); + if (!$decoded || !is_array($decoded)) { + return false; // NAMEPREP failed + } + $deco_len = count($decoded); + if (!$deco_len) { + return false; // Empty array + } + $codecount = 0; // How many chars have been consumed + $encoded = ''; + // Copy all basic code points to output + for ($i = 0; $i < $deco_len; ++$i) { + $test = $decoded[$i]; + // Will match [-0-9a-zA-Z] + if ((0x2F < $test && $test < 0x40) || (0x40 < $test && $test < 0x5B) || (0x60 < $test && $test <= 0x7B) || (0x2D == $test)) { + $encoded .= chr($decoded[$i]); + $codecount++; + } + } + if ($codecount == $deco_len) { + return $encoded; // All codepoints were basic ones + } + // Start with the prefix; copy it to output + $encoded = $this->_punycode_prefix . $encoded; + // If we have basic code points in output, add an hyphen to the end + if ($codecount) { + $encoded .= '-'; + } + // Now find and encode all non-basic code points + $is_first = true; + $cur_code = $this->_initial_n; + $bias = $this->_initial_bias; + $delta = 0; + while ($codecount < $deco_len) { + // Find the smallest code point >= the current code point and + // remember the last ouccrence of it in the input + for ($i = 0, $next_code = $this->_max_ucs; $i < $deco_len; $i++) { + if ($decoded[$i] >= $cur_code && $decoded[$i] <= $next_code) { + $next_code = $decoded[$i]; + } + } + $delta += ($next_code - $cur_code) * ($codecount + 1); + $cur_code = $next_code; + + // Scan input again and encode all characters whose code point is $cur_code + for ($i = 0; $i < $deco_len; $i++) { + if ($decoded[$i] < $cur_code) { + $delta++; + } elseif ($decoded[$i] == $cur_code) { + for ($q = $delta, $k = $this->_base; 1; $k += $this->_base) { + $t = ($k <= $bias) ? $this->_tmin : + (($k >= $bias + $this->_tmax) ? $this->_tmax : $k - $bias); + if ($q < $t) { + break; + } + $encoded .= $this->_encode_digit(intval($t + (($q - $t) % ($this->_base - $t)))); //v0.4.5 Changed from ceil() to intval() + $q = (int) (($q - $t) / ($this->_base - $t)); + } + $encoded .= $this->_encode_digit($q); + $bias = $this->_adapt($delta, $codecount + 1, $is_first); + $codecount++; + $delta = 0; + $is_first = false; + } + } + $delta++; + $cur_code++; + } + return $encoded; + } + + /** + * Adapt the bias according to the current code point and position + * @param int $delta + * @param int $npoints + * @param int $is_first + * @return int + */ + protected function _adapt($delta, $npoints, $is_first) + { + $delta = intval($is_first ? ($delta / $this->_damp) : ($delta / 2)); + $delta += intval($delta / $npoints); + for ($k = 0; $delta > (($this->_base - $this->_tmin) * $this->_tmax) / 2; $k += $this->_base) { + $delta = intval($delta / ($this->_base - $this->_tmin)); + } + return intval($k + ($this->_base - $this->_tmin + 1) * $delta / ($delta + $this->_skew)); + } + + /** + * Encoding a certain digit + * @param int $d + * @return string + */ + protected function _encode_digit($d) + { + return chr($d + 22 + 75 * ($d < 26)); + } + + /** + * Decode a certain digit + * @param int $cp + * @return int + */ + protected function _decode_digit($cp) + { + $cp = ord($cp); + return ($cp - 48 < 10) ? $cp - 22 : (($cp - 65 < 26) ? $cp - 65 : (($cp - 97 < 26) ? $cp - 97 : $this->_base)); + } + + /** + * Internal error handling method + * @param string $error + */ + protected function _error($error = '') + { + $this->_error = $error; + } + + /** + * Do Nameprep according to RFC3491 and RFC3454 + * @param array Unicode Characters + * @return string Unicode Characters, Nameprep'd + */ + protected function _nameprep($input) + { + $output = array(); + // + // Mapping + // Walking through the input array, performing the required steps on each of + // the input chars and putting the result into the output array + // While mapping required chars we apply the canonical ordering + foreach ($input as $v) { + // Map to nothing == skip that code point + if (in_array($v, self::$NP['map_nothing'])) { + continue; + } + // Try to find prohibited input + if (in_array($v, self::$NP['prohibit']) || in_array($v, self::$NP['general_prohibited'])) { + $this->_error('NAMEPREP: Prohibited input U+' . sprintf('%08X', $v)); + return false; + } + foreach (self::$NP['prohibit_ranges'] as $range) { + if ($range[0] <= $v && $v <= $range[1]) { + $this->_error('NAMEPREP: Prohibited input U+' . sprintf('%08X', $v)); + return false; + } + } + + if (0xAC00 <= $v && $v <= 0xD7AF) { + // Hangul syllable decomposition + foreach ($this->_hangul_decompose($v) as $out) { + $output[] = (int) $out; + } + } elseif (($this->_idn_version == '2003') && isset(self::$NP['replacemaps_2003'][$v])) { + foreach ($this->_apply_canonical_ordering(self::$NP['replacemaps_2003'][$v]) as $out) { + $output[] = (int) $out; + } + } elseif (($this->_idn_version == '2008') && isset(self::$NP['replacemaps'][$v])) { + foreach ($this->_apply_canonical_ordering(self::$NP['replacemaps'][$v]) as $out) { + $output[] = (int) $out; + } + } else { + $output[] = (int) $v; + } + } + // Before applying any Combining, try to rearrange any Hangul syllables + $output = $this->_hangul_compose($output); + // + // Combine code points + // + $last_class = 0; + $last_starter = 0; + $out_len = count($output); + for ($i = 0; $i < $out_len; ++$i) { + $class = $this->_get_combining_class($output[$i]); + if ((!$last_class || $last_class > $class) && $class) { + // Try to match + $seq_len = $i - $last_starter; + $out = $this->_combine(array_slice($output, $last_starter, $seq_len)); + // On match: Replace the last starter with the composed character and remove + // the now redundant non-starter(s) + if ($out) { + $output[$last_starter] = $out; + if (count($out) != $seq_len) { + for ($j = $i + 1; $j < $out_len; ++$j) { + $output[$j - 1] = $output[$j]; + } + unset($output[$out_len]); + } + // Rewind the for loop by one, since there can be more possible compositions + $i--; + $out_len--; + $last_class = ($i == $last_starter) ? 0 : $this->_get_combining_class($output[$i - 1]); + continue; + } + } + // The current class is 0 + if (!$class) { + $last_starter = $i; + } + $last_class = $class; + } + return $output; + } + + /** + * Decomposes a Hangul syllable + * (see http://www.unicode.org/unicode/reports/tr15/#Hangul + * @param integer 32bit UCS4 code point + * @return array Either Hangul Syllable decomposed or original 32bit value as one value array + */ + protected function _hangul_decompose($char) + { + $sindex = (int) $char - $this->_sbase; + if ($sindex < 0 || $sindex >= $this->_scount) { + return array($char); + } + $result = array(); + $result[] = (int) $this->_lbase + $sindex / $this->_ncount; + $result[] = (int) $this->_vbase + ($sindex % $this->_ncount) / $this->_tcount; + $T = intval($this->_tbase + $sindex % $this->_tcount); + if ($T != $this->_tbase) { + $result[] = $T; + } + return $result; + } + + /** + * Ccomposes a Hangul syllable + * (see http://www.unicode.org/unicode/reports/tr15/#Hangul + * @param array Decomposed UCS4 sequence + * @return array UCS4 sequence with syllables composed + */ + protected function _hangul_compose($input) + { + $inp_len = count($input); + if (!$inp_len) { + return array(); + } + $result = array(); + $last = (int) $input[0]; + $result[] = $last; // copy first char from input to output + + for ($i = 1; $i < $inp_len; ++$i) { + $char = (int) $input[$i]; + $sindex = $last - $this->_sbase; + $lindex = $last - $this->_lbase; + $vindex = $char - $this->_vbase; + $tindex = $char - $this->_tbase; + // Find out, whether two current characters are LV and T + if (0 <= $sindex && $sindex < $this->_scount && ($sindex % $this->_tcount == 0) && 0 <= $tindex && $tindex <= $this->_tcount) { + // create syllable of form LVT + $last += $tindex; + $result[(count($result) - 1)] = $last; // reset last + continue; // discard char + } + // Find out, whether two current characters form L and V + if (0 <= $lindex && $lindex < $this->_lcount && 0 <= $vindex && $vindex < $this->_vcount) { + // create syllable of form LV + $last = (int) $this->_sbase + ($lindex * $this->_vcount + $vindex) * $this->_tcount; + $result[(count($result) - 1)] = $last; // reset last + continue; // discard char + } + // if neither case was true, just add the character + $last = $char; + $result[] = $char; + } + return $result; + } + + /** + * Returns the combining class of a certain wide char + * @param integer Wide char to check (32bit integer) + * @return integer Combining class if found, else 0 + */ + protected function _get_combining_class($char) + { + return isset(self::$NP['norm_combcls'][$char]) ? self::$NP['norm_combcls'][$char] : 0; + } + + /** + * Applies the canonical ordering of a decomposed UCS4 sequence + * @param array Decomposed UCS4 sequence + * @return array Ordered USC4 sequence + */ + protected function _apply_canonical_ordering($input) + { + $swap = true; + $size = count($input); + while ($swap) { + $swap = false; + $last = $this->_get_combining_class(intval($input[0])); + for ($i = 0; $i < $size - 1; ++$i) { + $next = $this->_get_combining_class(intval($input[$i + 1])); + if ($next != 0 && $last > $next) { + // Move item leftward until it fits + for ($j = $i + 1; $j > 0; --$j) { + if ($this->_get_combining_class(intval($input[$j - 1])) <= $next) { + break; + } + $t = intval($input[$j]); + $input[$j] = intval($input[$j - 1]); + $input[$j - 1] = $t; + $swap = true; + } + // Reentering the loop looking at the old character again + $next = $last; + } + $last = $next; + } + } + return $input; + } + + /** + * Do composition of a sequence of starter and non-starter + * @param array UCS4 Decomposed sequence + * @return array Ordered USC4 sequence + */ + protected function _combine($input) + { + $inp_len = count($input); + if (0 == $inp_len) { + return false; + } + foreach (self::$NP['replacemaps'] as $np_src => $np_target) { + if ($np_target[0] != $input[0]) { + continue; + } + if (count($np_target) != $inp_len) { + continue; + } + $hit = false; + foreach ($input as $k2 => $v2) { + if ($v2 == $np_target[$k2]) { + $hit = true; + } else { + $hit = false; + break; + } + } + if ($hit) { + return $np_src; + } + } + return false; + } + + /** + * This converts an UTF-8 encoded string to its UCS-4 representation + * By talking about UCS-4 "strings" we mean arrays of 32bit integers representing + * each of the "chars". This is due to PHP not being able to handle strings with + * bit depth different from 8. This apllies to the reverse method _ucs4_to_utf8(), too. + * The following UTF-8 encodings are supported: + * bytes bits representation + * 1 7 0xxxxxxx + * 2 11 110xxxxx 10xxxxxx + * 3 16 1110xxxx 10xxxxxx 10xxxxxx + * 4 21 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + * 5 26 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx + * 6 31 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx + * Each x represents a bit that can be used to store character data. + * The five and six byte sequences are part of Annex D of ISO/IEC 10646-1:2000 + * @param string $input + * @return string + */ + protected function _utf8_to_ucs4($input) + { + $output = array(); + $out_len = 0; + $inp_len = self::byteLength($input); + $mode = 'next'; + $test = 'none'; + for ($k = 0; $k < $inp_len; ++$k) { + $v = ord($input{$k}); // Extract byte from input string + if ($v < 128) { // We found an ASCII char - put into stirng as is + $output[$out_len] = $v; + ++$out_len; + if ('add' == $mode) { + $this->_error('Conversion from UTF-8 to UCS-4 failed: malformed input at byte ' . $k); + return false; + } + continue; + } + if ('next' == $mode) { // Try to find the next start byte; determine the width of the Unicode char + $start_byte = $v; + $mode = 'add'; + $test = 'range'; + if ($v >> 5 == 6) { // &110xxxxx 10xxxxx + $next_byte = 0; // Tells, how many times subsequent bitmasks must rotate 6bits to the left + $v = ($v - 192) << 6; + } elseif ($v >> 4 == 14) { // &1110xxxx 10xxxxxx 10xxxxxx + $next_byte = 1; + $v = ($v - 224) << 12; + } elseif ($v >> 3 == 30) { // &11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + $next_byte = 2; + $v = ($v - 240) << 18; + } elseif ($v >> 2 == 62) { // &111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx + $next_byte = 3; + $v = ($v - 248) << 24; + } elseif ($v >> 1 == 126) { // &1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx + $next_byte = 4; + $v = ($v - 252) << 30; + } else { + $this->_error('This might be UTF-8, but I don\'t understand it at byte ' . $k); + return false; + } + if ('add' == $mode) { + $output[$out_len] = (int) $v; + ++$out_len; + continue; + } + } + if ('add' == $mode) { + if (!$this->_allow_overlong && $test == 'range') { + $test = 'none'; + if (($v < 0xA0 && $start_byte == 0xE0) || ($v < 0x90 && $start_byte == 0xF0) || ($v > 0x8F && $start_byte == 0xF4)) { + $this->_error('Bogus UTF-8 character detected (out of legal range) at byte ' . $k); + return false; + } + } + if ($v >> 6 == 2) { // Bit mask must be 10xxxxxx + $v = ($v - 128) << ($next_byte * 6); + $output[($out_len - 1)] += $v; + --$next_byte; + } else { + $this->_error('Conversion from UTF-8 to UCS-4 failed: malformed input at byte ' . $k); + return false; + } + if ($next_byte < 0) { + $mode = 'next'; + } + } + } // for + return $output; + } + + /** + * Convert UCS-4 string into UTF-8 string + * See _utf8_to_ucs4() for details + * @param string $input + * @return string + */ + protected function _ucs4_to_utf8($input) + { + $output = ''; + foreach ($input as $k => $v) { + if ($v < 128) { // 7bit are transferred literally + $output .= chr($v); + } elseif ($v < (1 << 11)) { // 2 bytes + $output .= chr(192 + ($v >> 6)) . chr(128 + ($v & 63)); + } elseif ($v < (1 << 16)) { // 3 bytes + $output .= chr(224 + ($v >> 12)) . chr(128 + (($v >> 6) & 63)) . chr(128 + ($v & 63)); + } elseif ($v < (1 << 21)) { // 4 bytes + $output .= chr(240 + ($v >> 18)) . chr(128 + (($v >> 12) & 63)) . chr(128 + (($v >> 6) & 63)) . chr(128 + ($v & 63)); + } else { + $this->_error('Conversion from UCS-4 to UTF-8 failed: malformed input at byte ' . $k); + return false; + } + } + return $output; + } + + /** + * Convert UCS-4 array into UCS-4 string + * + * @param array $input + * @return string + */ + protected function _ucs4_to_ucs4_string($input) + { + $output = ''; + // Take array values and split output to 4 bytes per value + // The bit mask is 255, which reads &11111111 + foreach ($input as $v) { + $output .= chr(($v >> 24) & 255) . chr(($v >> 16) & 255) . chr(($v >> 8) & 255) . chr($v & 255); + } + return $output; + } + + /** + * Convert UCS-4 strin into UCS-4 garray + * + * @param string $input + * @return array + */ + protected function _ucs4_string_to_ucs4($input) + { + $output = array(); + $inp_len = self::byteLength($input); + // Input length must be dividable by 4 + if ($inp_len % 4) { + $this->_error('Input UCS4 string is broken'); + return false; + } + // Empty input - return empty output + if (!$inp_len) { + return $output; + } + for ($i = 0, $out_len = -1; $i < $inp_len; ++$i) { + // Increment output position every 4 input bytes + if (!($i % 4)) { + $out_len++; + $output[$out_len] = 0; + } + $output[$out_len] += ord($input{$i}) << (8 * (3 - ($i % 4) ) ); + } + return $output; + } + + /** + * Gets the length of a string in bytes even if mbstring function + * overloading is turned on + * + * @param string $string the string for which to get the length. + * @return integer the length of the string in bytes. + */ + protected static function byteLength($string) + { + if (self::$_mb_string_overload) { + return mb_strlen($string, '8bit'); + } + return strlen((binary) $string); + } + + /** + * Attempts to return a concrete IDNA instance. + * + * @param array $params Set of paramaters + * @return idna_convert + * @access public + */ + public function getInstance($params = array()) + { + return new idna_convert($params); + } + + /** + * Attempts to return a concrete IDNA instance for either php4 or php5, + * only creating a new instance if no IDNA instance with the same + * parameters currently exists. + * + * @param array $params Set of paramaters + * + * @return object idna_convert + * @access public + */ + public function singleton($params = array()) + { + static $instances = array(); + + $signature = serialize($params); + if (!isset($instances[$signature])) { + $instances[$signature] = idna_convert::getInstance($params); + } + return $instances[$signature]; + } + + /** + * Holds all relevant mapping tables + * See RFC3454 for details + * + * @private array + * @since 0.5.2 + */ + protected static $NP = array( + 'map_nothing' => array(0xAD, 0x34F, 0x1806, 0x180B, 0x180C, 0x180D, 0x200B, 0x200C, + 0x200D, 0x2060, 0xFE00, 0xFE01, 0xFE02, 0xFE03, 0xFE04, 0xFE05, 0xFE06, 0xFE07, + 0xFE08, 0xFE09, 0xFE0A, 0xFE0B, 0xFE0C, 0xFE0D, 0xFE0E, 0xFE0F, 0xFEFF + ), + 'general_prohibited' => array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, + 43, 44, 47, 59, 60, 61, 62, 63, 64, 91, 92, 93, 94, 95, 96, 123, 124, 125, 126, 127, 0x3002 + ), + 'prohibit' => array(0xA0, 0x340, 0x341, 0x6DD, 0x70F, 0x1680, 0x180E, 0x2000, 0x2001, 0x2002, 0x2003, + 0x2004, 0x2005, 0x2006, 0x2007, 0x2008, 0x2009, 0x200A, 0x200B, 0x200C, 0x200D, 0x200E, 0x200F, + 0x2028, 0x2029, 0x202A, 0x202B, 0x202C, 0x202D, 0x202E, 0x202F, 0x205F, 0x206A, 0x206B, 0x206C, + 0x206D, 0x206E, 0x206F, 0x3000, 0x33C2, 0xFEFF, 0xFFF9, 0xFFFA, 0xFFFB, 0xFFFC, 0xFFFD, 0xFFFE, 0xFFFF, + 0x1FFFE, 0x1FFFF, 0x2FFFE, 0x2FFFF, 0x3FFFE, 0x3FFFF, 0x4FFFE, 0x4FFFF, 0x5FFFE, 0x5FFFF, 0x6FFFE, + 0x6FFFF, 0x7FFFE, 0x7FFFF, 0x8FFFE, 0x8FFFF, 0x9FFFE, 0x9FFFF, 0xAFFFE, 0xAFFFF, 0xBFFFE, 0xBFFFF, + 0xCFFFE, 0xCFFFF, 0xDFFFE, 0xDFFFF, 0xE0001, 0xEFFFE, 0xEFFFF, 0xFFFFE, 0xFFFFF, 0x10FFFE, 0x10FFFF + ), + 'prohibit_ranges' => array(array(0x80, 0x9F), array(0x2060, 0x206F), array(0x1D173, 0x1D17A), + array(0xE000, 0xF8FF), array(0xF0000, 0xFFFFD), array(0x100000, 0x10FFFD), + array(0xFDD0, 0xFDEF), array(0xD800, 0xDFFF), array(0x2FF0, 0x2FFB), array(0xE0020, 0xE007F) + ), + 'replacemaps_2003' => array(0x41 => array(0x61), 0x42 => array(0x62), 0x43 => array(0x63), + 0x44 => array(0x64), 0x45 => array(0x65), 0x46 => array(0x66), 0x47 => array(0x67), + 0x48 => array(0x68), 0x49 => array(0x69), 0x4A => array(0x6A), 0x4B => array(0x6B), + 0x4C => array(0x6C), 0x4D => array(0x6D), 0x4E => array(0x6E), 0x4F => array(0x6F), + 0x50 => array(0x70), 0x51 => array(0x71), 0x52 => array(0x72), 0x53 => array(0x73), + 0x54 => array(0x74), 0x55 => array(0x75), 0x56 => array(0x76), 0x57 => array(0x77), + 0x58 => array(0x78), 0x59 => array(0x79), 0x5A => array(0x7A), 0xB5 => array(0x3BC), + 0xC0 => array(0xE0), 0xC1 => array(0xE1), 0xC2 => array(0xE2), 0xC3 => array(0xE3), + 0xC4 => array(0xE4), 0xC5 => array(0xE5), 0xC6 => array(0xE6), 0xC7 => array(0xE7), + 0xC8 => array(0xE8), 0xC9 => array(0xE9), 0xCA => array(0xEA), 0xCB => array(0xEB), + 0xCC => array(0xEC), 0xCD => array(0xED), 0xCE => array(0xEE), 0xCF => array(0xEF), + 0xD0 => array(0xF0), 0xD1 => array(0xF1), 0xD2 => array(0xF2), 0xD3 => array(0xF3), + 0xD4 => array(0xF4), 0xD5 => array(0xF5), 0xD6 => array(0xF6), 0xD8 => array(0xF8), + 0xD9 => array(0xF9), 0xDA => array(0xFA), 0xDB => array(0xFB), 0xDC => array(0xFC), + 0xDD => array(0xFD), 0xDE => array(0xFE), 0xDF => array(0x73, 0x73), + 0x100 => array(0x101), 0x102 => array(0x103), 0x104 => array(0x105), + 0x106 => array(0x107), 0x108 => array(0x109), 0x10A => array(0x10B), + 0x10C => array(0x10D), 0x10E => array(0x10F), 0x110 => array(0x111), + 0x112 => array(0x113), 0x114 => array(0x115), 0x116 => array(0x117), + 0x118 => array(0x119), 0x11A => array(0x11B), 0x11C => array(0x11D), + 0x11E => array(0x11F), 0x120 => array(0x121), 0x122 => array(0x123), + 0x124 => array(0x125), 0x126 => array(0x127), 0x128 => array(0x129), + 0x12A => array(0x12B), 0x12C => array(0x12D), 0x12E => array(0x12F), + 0x130 => array(0x69, 0x307), 0x132 => array(0x133), 0x134 => array(0x135), + 0x136 => array(0x137), 0x139 => array(0x13A), 0x13B => array(0x13C), + 0x13D => array(0x13E), 0x13F => array(0x140), 0x141 => array(0x142), + 0x143 => array(0x144), 0x145 => array(0x146), 0x147 => array(0x148), + 0x149 => array(0x2BC, 0x6E), 0x14A => array(0x14B), 0x14C => array(0x14D), + 0x14E => array(0x14F), 0x150 => array(0x151), 0x152 => array(0x153), + 0x154 => array(0x155), 0x156 => array(0x157), 0x158 => array(0x159), + 0x15A => array(0x15B), 0x15C => array(0x15D), 0x15E => array(0x15F), + 0x160 => array(0x161), 0x162 => array(0x163), 0x164 => array(0x165), + 0x166 => array(0x167), 0x168 => array(0x169), 0x16A => array(0x16B), + 0x16C => array(0x16D), 0x16E => array(0x16F), 0x170 => array(0x171), + 0x172 => array(0x173), 0x174 => array(0x175), 0x176 => array(0x177), + 0x178 => array(0xFF), 0x179 => array(0x17A), 0x17B => array(0x17C), + 0x17D => array(0x17E), 0x17F => array(0x73), 0x181 => array(0x253), + 0x182 => array(0x183), 0x184 => array(0x185), 0x186 => array(0x254), + 0x187 => array(0x188), 0x189 => array(0x256), 0x18A => array(0x257), + 0x18B => array(0x18C), 0x18E => array(0x1DD), 0x18F => array(0x259), + 0x190 => array(0x25B), 0x191 => array(0x192), 0x193 => array(0x260), + 0x194 => array(0x263), 0x196 => array(0x269), 0x197 => array(0x268), + 0x198 => array(0x199), 0x19C => array(0x26F), 0x19D => array(0x272), + 0x19F => array(0x275), 0x1A0 => array(0x1A1), 0x1A2 => array(0x1A3), + 0x1A4 => array(0x1A5), 0x1A6 => array(0x280), 0x1A7 => array(0x1A8), + 0x1A9 => array(0x283), 0x1AC => array(0x1AD), 0x1AE => array(0x288), + 0x1AF => array(0x1B0), 0x1B1 => array(0x28A), 0x1B2 => array(0x28B), + 0x1B3 => array(0x1B4), 0x1B5 => array(0x1B6), 0x1B7 => array(0x292), + 0x1B8 => array(0x1B9), 0x1BC => array(0x1BD), 0x1C4 => array(0x1C6), + 0x1C5 => array(0x1C6), 0x1C7 => array(0x1C9), 0x1C8 => array(0x1C9), + 0x1CA => array(0x1CC), 0x1CB => array(0x1CC), 0x1CD => array(0x1CE), + 0x1CF => array(0x1D0), 0x1D1 => array(0x1D2), 0x1D3 => array(0x1D4), + 0x1D5 => array(0x1D6), 0x1D7 => array(0x1D8), 0x1D9 => array(0x1DA), + 0x1DB => array(0x1DC), 0x1DE => array(0x1DF), 0x1E0 => array(0x1E1), + 0x1E2 => array(0x1E3), 0x1E4 => array(0x1E5), 0x1E6 => array(0x1E7), + 0x1E8 => array(0x1E9), 0x1EA => array(0x1EB), 0x1EC => array(0x1ED), + 0x1EE => array(0x1EF), 0x1F0 => array(0x6A, 0x30C), 0x1F1 => array(0x1F3), + 0x1F2 => array(0x1F3), 0x1F4 => array(0x1F5), 0x1F6 => array(0x195), + 0x1F7 => array(0x1BF), 0x1F8 => array(0x1F9), 0x1FA => array(0x1FB), + 0x1FC => array(0x1FD), 0x1FE => array(0x1FF), 0x200 => array(0x201), + 0x202 => array(0x203), 0x204 => array(0x205), 0x206 => array(0x207), + 0x208 => array(0x209), 0x20A => array(0x20B), 0x20C => array(0x20D), + 0x20E => array(0x20F), 0x210 => array(0x211), 0x212 => array(0x213), + 0x214 => array(0x215), 0x216 => array(0x217), 0x218 => array(0x219), + 0x21A => array(0x21B), 0x21C => array(0x21D), 0x21E => array(0x21F), + 0x220 => array(0x19E), 0x222 => array(0x223), 0x224 => array(0x225), + 0x226 => array(0x227), 0x228 => array(0x229), 0x22A => array(0x22B), + 0x22C => array(0x22D), 0x22E => array(0x22F), 0x230 => array(0x231), + 0x232 => array(0x233), 0x345 => array(0x3B9), 0x37A => array(0x20, 0x3B9), + 0x386 => array(0x3AC), 0x388 => array(0x3AD), 0x389 => array(0x3AE), + 0x38A => array(0x3AF), 0x38C => array(0x3CC), 0x38E => array(0x3CD), + 0x38F => array(0x3CE), 0x390 => array(0x3B9, 0x308, 0x301), + 0x391 => array(0x3B1), 0x392 => array(0x3B2), 0x393 => array(0x3B3), + 0x394 => array(0x3B4), 0x395 => array(0x3B5), 0x396 => array(0x3B6), + 0x397 => array(0x3B7), 0x398 => array(0x3B8), 0x399 => array(0x3B9), + 0x39A => array(0x3BA), 0x39B => array(0x3BB), 0x39C => array(0x3BC), + 0x39D => array(0x3BD), 0x39E => array(0x3BE), 0x39F => array(0x3BF), + 0x3A0 => array(0x3C0), 0x3A1 => array(0x3C1), 0x3A3 => array(0x3C3), + 0x3A4 => array(0x3C4), 0x3A5 => array(0x3C5), 0x3A6 => array(0x3C6), + 0x3A7 => array(0x3C7), 0x3A8 => array(0x3C8), 0x3A9 => array(0x3C9), + 0x3AA => array(0x3CA), 0x3AB => array(0x3CB), 0x3B0 => array(0x3C5, 0x308, 0x301), + 0x3C2 => array(0x3C3), 0x3D0 => array(0x3B2), 0x3D1 => array(0x3B8), + 0x3D2 => array(0x3C5), 0x3D3 => array(0x3CD), 0x3D4 => array(0x3CB), + 0x3D5 => array(0x3C6), 0x3D6 => array(0x3C0), 0x3D8 => array(0x3D9), + 0x3DA => array(0x3DB), 0x3DC => array(0x3DD), 0x3DE => array(0x3DF), + 0x3E0 => array(0x3E1), 0x3E2 => array(0x3E3), 0x3E4 => array(0x3E5), + 0x3E6 => array(0x3E7), 0x3E8 => array(0x3E9), 0x3EA => array(0x3EB), + 0x3EC => array(0x3ED), 0x3EE => array(0x3EF), 0x3F0 => array(0x3BA), + 0x3F1 => array(0x3C1), 0x3F2 => array(0x3C3), 0x3F4 => array(0x3B8), + 0x3F5 => array(0x3B5), 0x400 => array(0x450), 0x401 => array(0x451), + 0x402 => array(0x452), 0x403 => array(0x453), 0x404 => array(0x454), + 0x405 => array(0x455), 0x406 => array(0x456), 0x407 => array(0x457), + 0x408 => array(0x458), 0x409 => array(0x459), 0x40A => array(0x45A), + 0x40B => array(0x45B), 0x40C => array(0x45C), 0x40D => array(0x45D), + 0x40E => array(0x45E), 0x40F => array(0x45F), 0x410 => array(0x430), + 0x411 => array(0x431), 0x412 => array(0x432), 0x413 => array(0x433), + 0x414 => array(0x434), 0x415 => array(0x435), 0x416 => array(0x436), + 0x417 => array(0x437), 0x418 => array(0x438), 0x419 => array(0x439), + 0x41A => array(0x43A), 0x41B => array(0x43B), 0x41C => array(0x43C), + 0x41D => array(0x43D), 0x41E => array(0x43E), 0x41F => array(0x43F), + 0x420 => array(0x440), 0x421 => array(0x441), 0x422 => array(0x442), + 0x423 => array(0x443), 0x424 => array(0x444), 0x425 => array(0x445), + 0x426 => array(0x446), 0x427 => array(0x447), 0x428 => array(0x448), + 0x429 => array(0x449), 0x42A => array(0x44A), 0x42B => array(0x44B), + 0x42C => array(0x44C), 0x42D => array(0x44D), 0x42E => array(0x44E), + 0x42F => array(0x44F), 0x460 => array(0x461), 0x462 => array(0x463), + 0x464 => array(0x465), 0x466 => array(0x467), 0x468 => array(0x469), + 0x46A => array(0x46B), 0x46C => array(0x46D), 0x46E => array(0x46F), + 0x470 => array(0x471), 0x472 => array(0x473), 0x474 => array(0x475), + 0x476 => array(0x477), 0x478 => array(0x479), 0x47A => array(0x47B), + 0x47C => array(0x47D), 0x47E => array(0x47F), 0x480 => array(0x481), + 0x48A => array(0x48B), 0x48C => array(0x48D), 0x48E => array(0x48F), + 0x490 => array(0x491), 0x492 => array(0x493), 0x494 => array(0x495), + 0x496 => array(0x497), 0x498 => array(0x499), 0x49A => array(0x49B), + 0x49C => array(0x49D), 0x49E => array(0x49F), 0x4A0 => array(0x4A1), + 0x4A2 => array(0x4A3), 0x4A4 => array(0x4A5), 0x4A6 => array(0x4A7), + 0x4A8 => array(0x4A9), 0x4AA => array(0x4AB), 0x4AC => array(0x4AD), + 0x4AE => array(0x4AF), 0x4B0 => array(0x4B1), 0x4B2 => array(0x4B3), + 0x4B4 => array(0x4B5), 0x4B6 => array(0x4B7), 0x4B8 => array(0x4B9), + 0x4BA => array(0x4BB), 0x4BC => array(0x4BD), 0x4BE => array(0x4BF), + 0x4C1 => array(0x4C2), 0x4C3 => array(0x4C4), 0x4C5 => array(0x4C6), + 0x4C7 => array(0x4C8), 0x4C9 => array(0x4CA), 0x4CB => array(0x4CC), + 0x4CD => array(0x4CE), 0x4D0 => array(0x4D1), 0x4D2 => array(0x4D3), + 0x4D4 => array(0x4D5), 0x4D6 => array(0x4D7), 0x4D8 => array(0x4D9), + 0x4DA => array(0x4DB), 0x4DC => array(0x4DD), 0x4DE => array(0x4DF), + 0x4E0 => array(0x4E1), 0x4E2 => array(0x4E3), 0x4E4 => array(0x4E5), + 0x4E6 => array(0x4E7), 0x4E8 => array(0x4E9), 0x4EA => array(0x4EB), + 0x4EC => array(0x4ED), 0x4EE => array(0x4EF), 0x4F0 => array(0x4F1), + 0x4F2 => array(0x4F3), 0x4F4 => array(0x4F5), 0x4F8 => array(0x4F9), + 0x500 => array(0x501), 0x502 => array(0x503), 0x504 => array(0x505), + 0x506 => array(0x507), 0x508 => array(0x509), 0x50A => array(0x50B), + 0x50C => array(0x50D), 0x50E => array(0x50F), 0x531 => array(0x561), + 0x532 => array(0x562), 0x533 => array(0x563), 0x534 => array(0x564), + 0x535 => array(0x565), 0x536 => array(0x566), 0x537 => array(0x567), + 0x538 => array(0x568), 0x539 => array(0x569), 0x53A => array(0x56A), + 0x53B => array(0x56B), 0x53C => array(0x56C), 0x53D => array(0x56D), + 0x53E => array(0x56E), 0x53F => array(0x56F), 0x540 => array(0x570), + 0x541 => array(0x571), 0x542 => array(0x572), 0x543 => array(0x573), + 0x544 => array(0x574), 0x545 => array(0x575), 0x546 => array(0x576), + 0x547 => array(0x577), 0x548 => array(0x578), 0x549 => array(0x579), + 0x54A => array(0x57A), 0x54B => array(0x57B), 0x54C => array(0x57C), + 0x54D => array(0x57D), 0x54E => array(0x57E), 0x54F => array(0x57F), + 0x550 => array(0x580), 0x551 => array(0x581), 0x552 => array(0x582), + 0x553 => array(0x583), 0x554 => array(0x584), 0x555 => array(0x585), + 0x556 => array(0x586), 0x587 => array(0x565, 0x582), 0xE33 => array(0xE4D, 0xE32), + 0x1E00 => array(0x1E01), 0x1E02 => array(0x1E03), 0x1E04 => array(0x1E05), + 0x1E06 => array(0x1E07), 0x1E08 => array(0x1E09), 0x1E0A => array(0x1E0B), + 0x1E0C => array(0x1E0D), 0x1E0E => array(0x1E0F), 0x1E10 => array(0x1E11), + 0x1E12 => array(0x1E13), 0x1E14 => array(0x1E15), 0x1E16 => array(0x1E17), + 0x1E18 => array(0x1E19), 0x1E1A => array(0x1E1B), 0x1E1C => array(0x1E1D), + 0x1E1E => array(0x1E1F), 0x1E20 => array(0x1E21), 0x1E22 => array(0x1E23), + 0x1E24 => array(0x1E25), 0x1E26 => array(0x1E27), 0x1E28 => array(0x1E29), + 0x1E2A => array(0x1E2B), 0x1E2C => array(0x1E2D), 0x1E2E => array(0x1E2F), + 0x1E30 => array(0x1E31), 0x1E32 => array(0x1E33), 0x1E34 => array(0x1E35), + 0x1E36 => array(0x1E37), 0x1E38 => array(0x1E39), 0x1E3A => array(0x1E3B), + 0x1E3C => array(0x1E3D), 0x1E3E => array(0x1E3F), 0x1E40 => array(0x1E41), + 0x1E42 => array(0x1E43), 0x1E44 => array(0x1E45), 0x1E46 => array(0x1E47), + 0x1E48 => array(0x1E49), 0x1E4A => array(0x1E4B), 0x1E4C => array(0x1E4D), + 0x1E4E => array(0x1E4F), 0x1E50 => array(0x1E51), 0x1E52 => array(0x1E53), + 0x1E54 => array(0x1E55), 0x1E56 => array(0x1E57), 0x1E58 => array(0x1E59), + 0x1E5A => array(0x1E5B), 0x1E5C => array(0x1E5D), 0x1E5E => array(0x1E5F), + 0x1E60 => array(0x1E61), 0x1E62 => array(0x1E63), 0x1E64 => array(0x1E65), + 0x1E66 => array(0x1E67), 0x1E68 => array(0x1E69), 0x1E6A => array(0x1E6B), + 0x1E6C => array(0x1E6D), 0x1E6E => array(0x1E6F), 0x1E70 => array(0x1E71), + 0x1E72 => array(0x1E73), 0x1E74 => array(0x1E75), 0x1E76 => array(0x1E77), + 0x1E78 => array(0x1E79), 0x1E7A => array(0x1E7B), 0x1E7C => array(0x1E7D), + 0x1E7E => array(0x1E7F), 0x1E80 => array(0x1E81), 0x1E82 => array(0x1E83), + 0x1E84 => array(0x1E85), 0x1E86 => array(0x1E87), 0x1E88 => array(0x1E89), + 0x1E8A => array(0x1E8B), 0x1E8C => array(0x1E8D), 0x1E8E => array(0x1E8F), + 0x1E90 => array(0x1E91), 0x1E92 => array(0x1E93), 0x1E94 => array(0x1E95), + 0x1E96 => array(0x68, 0x331), 0x1E97 => array(0x74, 0x308), 0x1E98 => array(0x77, 0x30A), + 0x1E99 => array(0x79, 0x30A), 0x1E9A => array(0x61, 0x2BE), 0x1E9B => array(0x1E61), + 0x1EA0 => array(0x1EA1), 0x1EA2 => array(0x1EA3), 0x1EA4 => array(0x1EA5), + 0x1EA6 => array(0x1EA7), 0x1EA8 => array(0x1EA9), 0x1EAA => array(0x1EAB), + 0x1EAC => array(0x1EAD), 0x1EAE => array(0x1EAF), 0x1EB0 => array(0x1EB1), + 0x1EB2 => array(0x1EB3), 0x1EB4 => array(0x1EB5), 0x1EB6 => array(0x1EB7), + 0x1EB8 => array(0x1EB9), 0x1EBA => array(0x1EBB), 0x1EBC => array(0x1EBD), + 0x1EBE => array(0x1EBF), 0x1EC0 => array(0x1EC1), 0x1EC2 => array(0x1EC3), + 0x1EC4 => array(0x1EC5), 0x1EC6 => array(0x1EC7), 0x1EC8 => array(0x1EC9), + 0x1ECA => array(0x1ECB), 0x1ECC => array(0x1ECD), 0x1ECE => array(0x1ECF), + 0x1ED0 => array(0x1ED1), 0x1ED2 => array(0x1ED3), 0x1ED4 => array(0x1ED5), + 0x1ED6 => array(0x1ED7), 0x1ED8 => array(0x1ED9), 0x1EDA => array(0x1EDB), + 0x1EDC => array(0x1EDD), 0x1EDE => array(0x1EDF), 0x1EE0 => array(0x1EE1), + 0x1EE2 => array(0x1EE3), 0x1EE4 => array(0x1EE5), 0x1EE6 => array(0x1EE7), + 0x1EE8 => array(0x1EE9), 0x1EEA => array(0x1EEB), 0x1EEC => array(0x1EED), + 0x1EEE => array(0x1EEF), 0x1EF0 => array(0x1EF1), 0x1EF2 => array(0x1EF3), + 0x1EF4 => array(0x1EF5), 0x1EF6 => array(0x1EF7), 0x1EF8 => array(0x1EF9), + 0x1F08 => array(0x1F00), 0x1F09 => array(0x1F01), 0x1F0A => array(0x1F02), + 0x1F0B => array(0x1F03), 0x1F0C => array(0x1F04), 0x1F0D => array(0x1F05), + 0x1F0E => array(0x1F06), 0x1F0F => array(0x1F07), 0x1F18 => array(0x1F10), + 0x1F19 => array(0x1F11), 0x1F1A => array(0x1F12), 0x1F1B => array(0x1F13), + 0x1F1C => array(0x1F14), 0x1F1D => array(0x1F15), 0x1F28 => array(0x1F20), + 0x1F29 => array(0x1F21), 0x1F2A => array(0x1F22), 0x1F2B => array(0x1F23), + 0x1F2C => array(0x1F24), 0x1F2D => array(0x1F25), 0x1F2E => array(0x1F26), + 0x1F2F => array(0x1F27), 0x1F38 => array(0x1F30), 0x1F39 => array(0x1F31), + 0x1F3A => array(0x1F32), 0x1F3B => array(0x1F33), 0x1F3C => array(0x1F34), + 0x1F3D => array(0x1F35), 0x1F3E => array(0x1F36), 0x1F3F => array(0x1F37), + 0x1F48 => array(0x1F40), 0x1F49 => array(0x1F41), 0x1F4A => array(0x1F42), + 0x1F4B => array(0x1F43), 0x1F4C => array(0x1F44), 0x1F4D => array(0x1F45), + 0x1F50 => array(0x3C5, 0x313), 0x1F52 => array(0x3C5, 0x313, 0x300), + 0x1F54 => array(0x3C5, 0x313, 0x301), 0x1F56 => array(0x3C5, 0x313, 0x342), + 0x1F59 => array(0x1F51), 0x1F5B => array(0x1F53), 0x1F5D => array(0x1F55), + 0x1F5F => array(0x1F57), 0x1F68 => array(0x1F60), 0x1F69 => array(0x1F61), + 0x1F6A => array(0x1F62), 0x1F6B => array(0x1F63), 0x1F6C => array(0x1F64), + 0x1F6D => array(0x1F65), 0x1F6E => array(0x1F66), 0x1F6F => array(0x1F67), + 0x1F80 => array(0x1F00, 0x3B9), 0x1F81 => array(0x1F01, 0x3B9), + 0x1F82 => array(0x1F02, 0x3B9), 0x1F83 => array(0x1F03, 0x3B9), + 0x1F84 => array(0x1F04, 0x3B9), 0x1F85 => array(0x1F05, 0x3B9), + 0x1F86 => array(0x1F06, 0x3B9), 0x1F87 => array(0x1F07, 0x3B9), + 0x1F88 => array(0x1F00, 0x3B9), 0x1F89 => array(0x1F01, 0x3B9), + 0x1F8A => array(0x1F02, 0x3B9), 0x1F8B => array(0x1F03, 0x3B9), + 0x1F8C => array(0x1F04, 0x3B9), 0x1F8D => array(0x1F05, 0x3B9), + 0x1F8E => array(0x1F06, 0x3B9), 0x1F8F => array(0x1F07, 0x3B9), + 0x1F90 => array(0x1F20, 0x3B9), 0x1F91 => array(0x1F21, 0x3B9), + 0x1F92 => array(0x1F22, 0x3B9), 0x1F93 => array(0x1F23, 0x3B9), + 0x1F94 => array(0x1F24, 0x3B9), 0x1F95 => array(0x1F25, 0x3B9), + 0x1F96 => array(0x1F26, 0x3B9), 0x1F97 => array(0x1F27, 0x3B9), + 0x1F98 => array(0x1F20, 0x3B9), 0x1F99 => array(0x1F21, 0x3B9), + 0x1F9A => array(0x1F22, 0x3B9), 0x1F9B => array(0x1F23, 0x3B9), + 0x1F9C => array(0x1F24, 0x3B9), 0x1F9D => array(0x1F25, 0x3B9), + 0x1F9E => array(0x1F26, 0x3B9), 0x1F9F => array(0x1F27, 0x3B9), + 0x1FA0 => array(0x1F60, 0x3B9), 0x1FA1 => array(0x1F61, 0x3B9), + 0x1FA2 => array(0x1F62, 0x3B9), 0x1FA3 => array(0x1F63, 0x3B9), + 0x1FA4 => array(0x1F64, 0x3B9), 0x1FA5 => array(0x1F65, 0x3B9), + 0x1FA6 => array(0x1F66, 0x3B9), 0x1FA7 => array(0x1F67, 0x3B9), + 0x1FA8 => array(0x1F60, 0x3B9), 0x1FA9 => array(0x1F61, 0x3B9), + 0x1FAA => array(0x1F62, 0x3B9), 0x1FAB => array(0x1F63, 0x3B9), + 0x1FAC => array(0x1F64, 0x3B9), 0x1FAD => array(0x1F65, 0x3B9), + 0x1FAE => array(0x1F66, 0x3B9), 0x1FAF => array(0x1F67, 0x3B9), + 0x1FB2 => array(0x1F70, 0x3B9), 0x1FB3 => array(0x3B1, 0x3B9), + 0x1FB4 => array(0x3AC, 0x3B9), 0x1FB6 => array(0x3B1, 0x342), + 0x1FB7 => array(0x3B1, 0x342, 0x3B9), 0x1FB8 => array(0x1FB0), + 0x1FB9 => array(0x1FB1), 0x1FBA => array(0x1F70), 0x1FBB => array(0x1F71), + 0x1FBC => array(0x3B1, 0x3B9), 0x1FBE => array(0x3B9), + 0x1FC2 => array(0x1F74, 0x3B9), 0x1FC3 => array(0x3B7, 0x3B9), + 0x1FC4 => array(0x3AE, 0x3B9), 0x1FC6 => array(0x3B7, 0x342), + 0x1FC7 => array(0x3B7, 0x342, 0x3B9), 0x1FC8 => array(0x1F72), + 0x1FC9 => array(0x1F73), 0x1FCA => array(0x1F74), 0x1FCB => array(0x1F75), + 0x1FCC => array(0x3B7, 0x3B9), 0x1FD2 => array(0x3B9, 0x308, 0x300), + 0x1FD3 => array(0x3B9, 0x308, 0x301), 0x1FD6 => array(0x3B9, 0x342), + 0x1FD7 => array(0x3B9, 0x308, 0x342), 0x1FD8 => array(0x1FD0), + 0x1FD9 => array(0x1FD1), 0x1FDA => array(0x1F76), + 0x1FDB => array(0x1F77), 0x1FE2 => array(0x3C5, 0x308, 0x300), + 0x1FE3 => array(0x3C5, 0x308, 0x301), 0x1FE4 => array(0x3C1, 0x313), + 0x1FE6 => array(0x3C5, 0x342), 0x1FE7 => array(0x3C5, 0x308, 0x342), + 0x1FE8 => array(0x1FE0), 0x1FE9 => array(0x1FE1), + 0x1FEA => array(0x1F7A), 0x1FEB => array(0x1F7B), + 0x1FEC => array(0x1FE5), 0x1FF2 => array(0x1F7C, 0x3B9), + 0x1FF3 => array(0x3C9, 0x3B9), 0x1FF4 => array(0x3CE, 0x3B9), + 0x1FF6 => array(0x3C9, 0x342), 0x1FF7 => array(0x3C9, 0x342, 0x3B9), + 0x1FF8 => array(0x1F78), 0x1FF9 => array(0x1F79), 0x1FFA => array(0x1F7C), + 0x1FFB => array(0x1F7D), 0x1FFC => array(0x3C9, 0x3B9), + 0x20A8 => array(0x72, 0x73), 0x2102 => array(0x63), 0x2103 => array(0xB0, 0x63), + 0x2107 => array(0x25B), 0x2109 => array(0xB0, 0x66), 0x210B => array(0x68), + 0x210C => array(0x68), 0x210D => array(0x68), 0x2110 => array(0x69), + 0x2111 => array(0x69), 0x2112 => array(0x6C), 0x2115 => array(0x6E), + 0x2116 => array(0x6E, 0x6F), 0x2119 => array(0x70), 0x211A => array(0x71), + 0x211B => array(0x72), 0x211C => array(0x72), 0x211D => array(0x72), + 0x2120 => array(0x73, 0x6D), 0x2121 => array(0x74, 0x65, 0x6C), + 0x2122 => array(0x74, 0x6D), 0x2124 => array(0x7A), 0x2126 => array(0x3C9), + 0x2128 => array(0x7A), 0x212A => array(0x6B), 0x212B => array(0xE5), + 0x212C => array(0x62), 0x212D => array(0x63), 0x2130 => array(0x65), + 0x2131 => array(0x66), 0x2133 => array(0x6D), 0x213E => array(0x3B3), + 0x213F => array(0x3C0), 0x2145 => array(0x64), 0x2160 => array(0x2170), + 0x2161 => array(0x2171), 0x2162 => array(0x2172), 0x2163 => array(0x2173), + 0x2164 => array(0x2174), 0x2165 => array(0x2175), 0x2166 => array(0x2176), + 0x2167 => array(0x2177), 0x2168 => array(0x2178), 0x2169 => array(0x2179), + 0x216A => array(0x217A), 0x216B => array(0x217B), 0x216C => array(0x217C), + 0x216D => array(0x217D), 0x216E => array(0x217E), 0x216F => array(0x217F), + 0x24B6 => array(0x24D0), 0x24B7 => array(0x24D1), 0x24B8 => array(0x24D2), + 0x24B9 => array(0x24D3), 0x24BA => array(0x24D4), 0x24BB => array(0x24D5), + 0x24BC => array(0x24D6), 0x24BD => array(0x24D7), 0x24BE => array(0x24D8), + 0x24BF => array(0x24D9), 0x24C0 => array(0x24DA), 0x24C1 => array(0x24DB), + 0x24C2 => array(0x24DC), 0x24C3 => array(0x24DD), 0x24C4 => array(0x24DE), + 0x24C5 => array(0x24DF), 0x24C6 => array(0x24E0), 0x24C7 => array(0x24E1), + 0x24C8 => array(0x24E2), 0x24C9 => array(0x24E3), 0x24CA => array(0x24E4), + 0x24CB => array(0x24E5), 0x24CC => array(0x24E6), 0x24CD => array(0x24E7), + 0x24CE => array(0x24E8), 0x24CF => array(0x24E9), 0x3371 => array(0x68, 0x70, 0x61), + 0x3373 => array(0x61, 0x75), 0x3375 => array(0x6F, 0x76), + 0x3380 => array(0x70, 0x61), 0x3381 => array(0x6E, 0x61), + 0x3382 => array(0x3BC, 0x61), 0x3383 => array(0x6D, 0x61), + 0x3384 => array(0x6B, 0x61), 0x3385 => array(0x6B, 0x62), + 0x3386 => array(0x6D, 0x62), 0x3387 => array(0x67, 0x62), + 0x338A => array(0x70, 0x66), 0x338B => array(0x6E, 0x66), + 0x338C => array(0x3BC, 0x66), 0x3390 => array(0x68, 0x7A), + 0x3391 => array(0x6B, 0x68, 0x7A), 0x3392 => array(0x6D, 0x68, 0x7A), + 0x3393 => array(0x67, 0x68, 0x7A), 0x3394 => array(0x74, 0x68, 0x7A), + 0x33A9 => array(0x70, 0x61), 0x33AA => array(0x6B, 0x70, 0x61), + 0x33AB => array(0x6D, 0x70, 0x61), 0x33AC => array(0x67, 0x70, 0x61), + 0x33B4 => array(0x70, 0x76), 0x33B5 => array(0x6E, 0x76), + 0x33B6 => array(0x3BC, 0x76), 0x33B7 => array(0x6D, 0x76), + 0x33B8 => array(0x6B, 0x76), 0x33B9 => array(0x6D, 0x76), + 0x33BA => array(0x70, 0x77), 0x33BB => array(0x6E, 0x77), + 0x33BC => array(0x3BC, 0x77), 0x33BD => array(0x6D, 0x77), + 0x33BE => array(0x6B, 0x77), 0x33BF => array(0x6D, 0x77), + 0x33C0 => array(0x6B, 0x3C9), 0x33C1 => array(0x6D, 0x3C9), /* + 0x33C2 => array(0x61, 0x2E, 0x6D, 0x2E), */ + 0x33C3 => array(0x62, 0x71), 0x33C6 => array(0x63, 0x2215, 0x6B, 0x67), + 0x33C7 => array(0x63, 0x6F, 0x2E), 0x33C8 => array(0x64, 0x62), + 0x33C9 => array(0x67, 0x79), 0x33CB => array(0x68, 0x70), + 0x33CD => array(0x6B, 0x6B), 0x33CE => array(0x6B, 0x6D), + 0x33D7 => array(0x70, 0x68), 0x33D9 => array(0x70, 0x70, 0x6D), + 0x33DA => array(0x70, 0x72), 0x33DC => array(0x73, 0x76), + 0x33DD => array(0x77, 0x62), 0xFB00 => array(0x66, 0x66), + 0xFB01 => array(0x66, 0x69), 0xFB02 => array(0x66, 0x6C), + 0xFB03 => array(0x66, 0x66, 0x69), 0xFB04 => array(0x66, 0x66, 0x6C), + 0xFB05 => array(0x73, 0x74), 0xFB06 => array(0x73, 0x74), + 0xFB13 => array(0x574, 0x576), 0xFB14 => array(0x574, 0x565), + 0xFB15 => array(0x574, 0x56B), 0xFB16 => array(0x57E, 0x576), + 0xFB17 => array(0x574, 0x56D), 0xFF21 => array(0xFF41), + 0xFF22 => array(0xFF42), 0xFF23 => array(0xFF43), 0xFF24 => array(0xFF44), + 0xFF25 => array(0xFF45), 0xFF26 => array(0xFF46), 0xFF27 => array(0xFF47), + 0xFF28 => array(0xFF48), 0xFF29 => array(0xFF49), 0xFF2A => array(0xFF4A), + 0xFF2B => array(0xFF4B), 0xFF2C => array(0xFF4C), 0xFF2D => array(0xFF4D), + 0xFF2E => array(0xFF4E), 0xFF2F => array(0xFF4F), 0xFF30 => array(0xFF50), + 0xFF31 => array(0xFF51), 0xFF32 => array(0xFF52), 0xFF33 => array(0xFF53), + 0xFF34 => array(0xFF54), 0xFF35 => array(0xFF55), 0xFF36 => array(0xFF56), + 0xFF37 => array(0xFF57), 0xFF38 => array(0xFF58), 0xFF39 => array(0xFF59), + 0xFF3A => array(0xFF5A), 0x10400 => array(0x10428), 0x10401 => array(0x10429), + 0x10402 => array(0x1042A), 0x10403 => array(0x1042B), 0x10404 => array(0x1042C), + 0x10405 => array(0x1042D), 0x10406 => array(0x1042E), 0x10407 => array(0x1042F), + 0x10408 => array(0x10430), 0x10409 => array(0x10431), 0x1040A => array(0x10432), + 0x1040B => array(0x10433), 0x1040C => array(0x10434), 0x1040D => array(0x10435), + 0x1040E => array(0x10436), 0x1040F => array(0x10437), 0x10410 => array(0x10438), + 0x10411 => array(0x10439), 0x10412 => array(0x1043A), 0x10413 => array(0x1043B), + 0x10414 => array(0x1043C), 0x10415 => array(0x1043D), 0x10416 => array(0x1043E), + 0x10417 => array(0x1043F), 0x10418 => array(0x10440), 0x10419 => array(0x10441), + 0x1041A => array(0x10442), 0x1041B => array(0x10443), 0x1041C => array(0x10444), + 0x1041D => array(0x10445), 0x1041E => array(0x10446), 0x1041F => array(0x10447), + 0x10420 => array(0x10448), 0x10421 => array(0x10449), 0x10422 => array(0x1044A), + 0x10423 => array(0x1044B), 0x10424 => array(0x1044C), 0x10425 => array(0x1044D), + 0x1D400 => array(0x61), 0x1D401 => array(0x62), 0x1D402 => array(0x63), + 0x1D403 => array(0x64), 0x1D404 => array(0x65), 0x1D405 => array(0x66), + 0x1D406 => array(0x67), 0x1D407 => array(0x68), 0x1D408 => array(0x69), + 0x1D409 => array(0x6A), 0x1D40A => array(0x6B), 0x1D40B => array(0x6C), + 0x1D40C => array(0x6D), 0x1D40D => array(0x6E), 0x1D40E => array(0x6F), + 0x1D40F => array(0x70), 0x1D410 => array(0x71), 0x1D411 => array(0x72), + 0x1D412 => array(0x73), 0x1D413 => array(0x74), 0x1D414 => array(0x75), + 0x1D415 => array(0x76), 0x1D416 => array(0x77), 0x1D417 => array(0x78), + 0x1D418 => array(0x79), 0x1D419 => array(0x7A), 0x1D434 => array(0x61), + 0x1D435 => array(0x62), 0x1D436 => array(0x63), 0x1D437 => array(0x64), + 0x1D438 => array(0x65), 0x1D439 => array(0x66), 0x1D43A => array(0x67), + 0x1D43B => array(0x68), 0x1D43C => array(0x69), 0x1D43D => array(0x6A), + 0x1D43E => array(0x6B), 0x1D43F => array(0x6C), 0x1D440 => array(0x6D), + 0x1D441 => array(0x6E), 0x1D442 => array(0x6F), 0x1D443 => array(0x70), + 0x1D444 => array(0x71), 0x1D445 => array(0x72), 0x1D446 => array(0x73), + 0x1D447 => array(0x74), 0x1D448 => array(0x75), 0x1D449 => array(0x76), + 0x1D44A => array(0x77), 0x1D44B => array(0x78), 0x1D44C => array(0x79), + 0x1D44D => array(0x7A), 0x1D468 => array(0x61), 0x1D469 => array(0x62), + 0x1D46A => array(0x63), 0x1D46B => array(0x64), 0x1D46C => array(0x65), + 0x1D46D => array(0x66), 0x1D46E => array(0x67), 0x1D46F => array(0x68), + 0x1D470 => array(0x69), 0x1D471 => array(0x6A), 0x1D472 => array(0x6B), + 0x1D473 => array(0x6C), 0x1D474 => array(0x6D), 0x1D475 => array(0x6E), + 0x1D476 => array(0x6F), 0x1D477 => array(0x70), 0x1D478 => array(0x71), + 0x1D479 => array(0x72), 0x1D47A => array(0x73), 0x1D47B => array(0x74), + 0x1D47C => array(0x75), 0x1D47D => array(0x76), 0x1D47E => array(0x77), + 0x1D47F => array(0x78), 0x1D480 => array(0x79), 0x1D481 => array(0x7A), + 0x1D49C => array(0x61), 0x1D49E => array(0x63), 0x1D49F => array(0x64), + 0x1D4A2 => array(0x67), 0x1D4A5 => array(0x6A), 0x1D4A6 => array(0x6B), + 0x1D4A9 => array(0x6E), 0x1D4AA => array(0x6F), 0x1D4AB => array(0x70), + 0x1D4AC => array(0x71), 0x1D4AE => array(0x73), 0x1D4AF => array(0x74), + 0x1D4B0 => array(0x75), 0x1D4B1 => array(0x76), 0x1D4B2 => array(0x77), + 0x1D4B3 => array(0x78), 0x1D4B4 => array(0x79), 0x1D4B5 => array(0x7A), + 0x1D4D0 => array(0x61), 0x1D4D1 => array(0x62), 0x1D4D2 => array(0x63), + 0x1D4D3 => array(0x64), 0x1D4D4 => array(0x65), 0x1D4D5 => array(0x66), + 0x1D4D6 => array(0x67), 0x1D4D7 => array(0x68), 0x1D4D8 => array(0x69), + 0x1D4D9 => array(0x6A), 0x1D4DA => array(0x6B), 0x1D4DB => array(0x6C), + 0x1D4DC => array(0x6D), 0x1D4DD => array(0x6E), 0x1D4DE => array(0x6F), + 0x1D4DF => array(0x70), 0x1D4E0 => array(0x71), 0x1D4E1 => array(0x72), + 0x1D4E2 => array(0x73), 0x1D4E3 => array(0x74), 0x1D4E4 => array(0x75), + 0x1D4E5 => array(0x76), 0x1D4E6 => array(0x77), 0x1D4E7 => array(0x78), + 0x1D4E8 => array(0x79), 0x1D4E9 => array(0x7A), 0x1D504 => array(0x61), + 0x1D505 => array(0x62), 0x1D507 => array(0x64), 0x1D508 => array(0x65), + 0x1D509 => array(0x66), 0x1D50A => array(0x67), 0x1D50D => array(0x6A), + 0x1D50E => array(0x6B), 0x1D50F => array(0x6C), 0x1D510 => array(0x6D), + 0x1D511 => array(0x6E), 0x1D512 => array(0x6F), 0x1D513 => array(0x70), + 0x1D514 => array(0x71), 0x1D516 => array(0x73), 0x1D517 => array(0x74), + 0x1D518 => array(0x75), 0x1D519 => array(0x76), 0x1D51A => array(0x77), + 0x1D51B => array(0x78), 0x1D51C => array(0x79), 0x1D538 => array(0x61), + 0x1D539 => array(0x62), 0x1D53B => array(0x64), 0x1D53C => array(0x65), + 0x1D53D => array(0x66), 0x1D53E => array(0x67), 0x1D540 => array(0x69), + 0x1D541 => array(0x6A), 0x1D542 => array(0x6B), 0x1D543 => array(0x6C), + 0x1D544 => array(0x6D), 0x1D546 => array(0x6F), 0x1D54A => array(0x73), + 0x1D54B => array(0x74), 0x1D54C => array(0x75), 0x1D54D => array(0x76), + 0x1D54E => array(0x77), 0x1D54F => array(0x78), 0x1D550 => array(0x79), + 0x1D56C => array(0x61), 0x1D56D => array(0x62), 0x1D56E => array(0x63), + 0x1D56F => array(0x64), 0x1D570 => array(0x65), 0x1D571 => array(0x66), + 0x1D572 => array(0x67), 0x1D573 => array(0x68), 0x1D574 => array(0x69), + 0x1D575 => array(0x6A), 0x1D576 => array(0x6B), 0x1D577 => array(0x6C), + 0x1D578 => array(0x6D), 0x1D579 => array(0x6E), 0x1D57A => array(0x6F), + 0x1D57B => array(0x70), 0x1D57C => array(0x71), 0x1D57D => array(0x72), + 0x1D57E => array(0x73), 0x1D57F => array(0x74), 0x1D580 => array(0x75), + 0x1D581 => array(0x76), 0x1D582 => array(0x77), 0x1D583 => array(0x78), + 0x1D584 => array(0x79), 0x1D585 => array(0x7A), 0x1D5A0 => array(0x61), + 0x1D5A1 => array(0x62), 0x1D5A2 => array(0x63), 0x1D5A3 => array(0x64), + 0x1D5A4 => array(0x65), 0x1D5A5 => array(0x66), 0x1D5A6 => array(0x67), + 0x1D5A7 => array(0x68), 0x1D5A8 => array(0x69), 0x1D5A9 => array(0x6A), + 0x1D5AA => array(0x6B), 0x1D5AB => array(0x6C), 0x1D5AC => array(0x6D), + 0x1D5AD => array(0x6E), 0x1D5AE => array(0x6F), 0x1D5AF => array(0x70), + 0x1D5B0 => array(0x71), 0x1D5B1 => array(0x72), 0x1D5B2 => array(0x73), + 0x1D5B3 => array(0x74), 0x1D5B4 => array(0x75), 0x1D5B5 => array(0x76), + 0x1D5B6 => array(0x77), 0x1D5B7 => array(0x78), 0x1D5B8 => array(0x79), + 0x1D5B9 => array(0x7A), 0x1D5D4 => array(0x61), 0x1D5D5 => array(0x62), + 0x1D5D6 => array(0x63), 0x1D5D7 => array(0x64), 0x1D5D8 => array(0x65), + 0x1D5D9 => array(0x66), 0x1D5DA => array(0x67), 0x1D5DB => array(0x68), + 0x1D5DC => array(0x69), 0x1D5DD => array(0x6A), 0x1D5DE => array(0x6B), + 0x1D5DF => array(0x6C), 0x1D5E0 => array(0x6D), 0x1D5E1 => array(0x6E), + 0x1D5E2 => array(0x6F), 0x1D5E3 => array(0x70), 0x1D5E4 => array(0x71), + 0x1D5E5 => array(0x72), 0x1D5E6 => array(0x73), 0x1D5E7 => array(0x74), + 0x1D5E8 => array(0x75), 0x1D5E9 => array(0x76), 0x1D5EA => array(0x77), + 0x1D5EB => array(0x78), 0x1D5EC => array(0x79), 0x1D5ED => array(0x7A), + 0x1D608 => array(0x61), 0x1D609 => array(0x62), 0x1D60A => array(0x63), + 0x1D60B => array(0x64), 0x1D60C => array(0x65), 0x1D60D => array(0x66), + 0x1D60E => array(0x67), 0x1D60F => array(0x68), 0x1D610 => array(0x69), + 0x1D611 => array(0x6A), 0x1D612 => array(0x6B), 0x1D613 => array(0x6C), + 0x1D614 => array(0x6D), 0x1D615 => array(0x6E), 0x1D616 => array(0x6F), + 0x1D617 => array(0x70), 0x1D618 => array(0x71), 0x1D619 => array(0x72), + 0x1D61A => array(0x73), 0x1D61B => array(0x74), 0x1D61C => array(0x75), + 0x1D61D => array(0x76), 0x1D61E => array(0x77), 0x1D61F => array(0x78), + 0x1D620 => array(0x79), 0x1D621 => array(0x7A), 0x1D63C => array(0x61), + 0x1D63D => array(0x62), 0x1D63E => array(0x63), 0x1D63F => array(0x64), + 0x1D640 => array(0x65), 0x1D641 => array(0x66), 0x1D642 => array(0x67), + 0x1D643 => array(0x68), 0x1D644 => array(0x69), 0x1D645 => array(0x6A), + 0x1D646 => array(0x6B), 0x1D647 => array(0x6C), 0x1D648 => array(0x6D), + 0x1D649 => array(0x6E), 0x1D64A => array(0x6F), 0x1D64B => array(0x70), + 0x1D64C => array(0x71), 0x1D64D => array(0x72), 0x1D64E => array(0x73), + 0x1D64F => array(0x74), 0x1D650 => array(0x75), 0x1D651 => array(0x76), + 0x1D652 => array(0x77), 0x1D653 => array(0x78), 0x1D654 => array(0x79), + 0x1D655 => array(0x7A), 0x1D670 => array(0x61), 0x1D671 => array(0x62), + 0x1D672 => array(0x63), 0x1D673 => array(0x64), 0x1D674 => array(0x65), + 0x1D675 => array(0x66), 0x1D676 => array(0x67), 0x1D677 => array(0x68), + 0x1D678 => array(0x69), 0x1D679 => array(0x6A), 0x1D67A => array(0x6B), + 0x1D67B => array(0x6C), 0x1D67C => array(0x6D), 0x1D67D => array(0x6E), + 0x1D67E => array(0x6F), 0x1D67F => array(0x70), 0x1D680 => array(0x71), + 0x1D681 => array(0x72), 0x1D682 => array(0x73), 0x1D683 => array(0x74), + 0x1D684 => array(0x75), 0x1D685 => array(0x76), 0x1D686 => array(0x77), + 0x1D687 => array(0x78), 0x1D688 => array(0x79), 0x1D689 => array(0x7A), + 0x1D6A8 => array(0x3B1), 0x1D6A9 => array(0x3B2), 0x1D6AA => array(0x3B3), + 0x1D6AB => array(0x3B4), 0x1D6AC => array(0x3B5), 0x1D6AD => array(0x3B6), + 0x1D6AE => array(0x3B7), 0x1D6AF => array(0x3B8), 0x1D6B0 => array(0x3B9), + 0x1D6B1 => array(0x3BA), 0x1D6B2 => array(0x3BB), 0x1D6B3 => array(0x3BC), + 0x1D6B4 => array(0x3BD), 0x1D6B5 => array(0x3BE), 0x1D6B6 => array(0x3BF), + 0x1D6B7 => array(0x3C0), 0x1D6B8 => array(0x3C1), 0x1D6B9 => array(0x3B8), + 0x1D6BA => array(0x3C3), 0x1D6BB => array(0x3C4), 0x1D6BC => array(0x3C5), + 0x1D6BD => array(0x3C6), 0x1D6BE => array(0x3C7), 0x1D6BF => array(0x3C8), + 0x1D6C0 => array(0x3C9), 0x1D6D3 => array(0x3C3), 0x1D6E2 => array(0x3B1), + 0x1D6E3 => array(0x3B2), 0x1D6E4 => array(0x3B3), 0x1D6E5 => array(0x3B4), + 0x1D6E6 => array(0x3B5), 0x1D6E7 => array(0x3B6), 0x1D6E8 => array(0x3B7), + 0x1D6E9 => array(0x3B8), 0x1D6EA => array(0x3B9), 0x1D6EB => array(0x3BA), + 0x1D6EC => array(0x3BB), 0x1D6ED => array(0x3BC), 0x1D6EE => array(0x3BD), + 0x1D6EF => array(0x3BE), 0x1D6F0 => array(0x3BF), 0x1D6F1 => array(0x3C0), + 0x1D6F2 => array(0x3C1), 0x1D6F3 => array(0x3B8), 0x1D6F4 => array(0x3C3), + 0x1D6F5 => array(0x3C4), 0x1D6F6 => array(0x3C5), 0x1D6F7 => array(0x3C6), + 0x1D6F8 => array(0x3C7), 0x1D6F9 => array(0x3C8), 0x1D6FA => array(0x3C9), + 0x1D70D => array(0x3C3), 0x1D71C => array(0x3B1), 0x1D71D => array(0x3B2), + 0x1D71E => array(0x3B3), 0x1D71F => array(0x3B4), 0x1D720 => array(0x3B5), + 0x1D721 => array(0x3B6), 0x1D722 => array(0x3B7), 0x1D723 => array(0x3B8), + 0x1D724 => array(0x3B9), 0x1D725 => array(0x3BA), 0x1D726 => array(0x3BB), + 0x1D727 => array(0x3BC), 0x1D728 => array(0x3BD), 0x1D729 => array(0x3BE), + 0x1D72A => array(0x3BF), 0x1D72B => array(0x3C0), 0x1D72C => array(0x3C1), + 0x1D72D => array(0x3B8), 0x1D72E => array(0x3C3), 0x1D72F => array(0x3C4), + 0x1D730 => array(0x3C5), 0x1D731 => array(0x3C6), 0x1D732 => array(0x3C7), + 0x1D733 => array(0x3C8), 0x1D734 => array(0x3C9), 0x1D747 => array(0x3C3), + 0x1D756 => array(0x3B1), 0x1D757 => array(0x3B2), 0x1D758 => array(0x3B3), + 0x1D759 => array(0x3B4), 0x1D75A => array(0x3B5), 0x1D75B => array(0x3B6), + 0x1D75C => array(0x3B7), 0x1D75D => array(0x3B8), 0x1D75E => array(0x3B9), + 0x1D75F => array(0x3BA), 0x1D760 => array(0x3BB), 0x1D761 => array(0x3BC), + 0x1D762 => array(0x3BD), 0x1D763 => array(0x3BE), 0x1D764 => array(0x3BF), + 0x1D765 => array(0x3C0), 0x1D766 => array(0x3C1), 0x1D767 => array(0x3B8), + 0x1D768 => array(0x3C3), 0x1D769 => array(0x3C4), 0x1D76A => array(0x3C5), + 0x1D76B => array(0x3C6), 0x1D76C => array(0x3C7), 0x1D76D => array(0x3C8), + 0x1D76E => array(0x3C9), 0x1D781 => array(0x3C3), 0x1D790 => array(0x3B1), + 0x1D791 => array(0x3B2), 0x1D792 => array(0x3B3), 0x1D793 => array(0x3B4), + 0x1D794 => array(0x3B5), 0x1D795 => array(0x3B6), 0x1D796 => array(0x3B7), + 0x1D797 => array(0x3B8), 0x1D798 => array(0x3B9), 0x1D799 => array(0x3BA), + 0x1D79A => array(0x3BB), 0x1D79B => array(0x3BC), 0x1D79C => array(0x3BD), + 0x1D79D => array(0x3BE), 0x1D79E => array(0x3BF), 0x1D79F => array(0x3C0), + 0x1D7A0 => array(0x3C1), 0x1D7A1 => array(0x3B8), 0x1D7A2 => array(0x3C3), + 0x1D7A3 => array(0x3C4), 0x1D7A4 => array(0x3C5), 0x1D7A5 => array(0x3C6), + 0x1D7A6 => array(0x3C7), 0x1D7A7 => array(0x3C8), 0x1D7A8 => array(0x3C9), + 0x1D7BB => array(0x3C3), 0x3F9 => array(0x3C3), 0x1D2C => array(0x61), + 0x1D2D => array(0xE6), 0x1D2E => array(0x62), 0x1D30 => array(0x64), + 0x1D31 => array(0x65), 0x1D32 => array(0x1DD), 0x1D33 => array(0x67), + 0x1D34 => array(0x68), 0x1D35 => array(0x69), 0x1D36 => array(0x6A), + 0x1D37 => array(0x6B), 0x1D38 => array(0x6C), 0x1D39 => array(0x6D), + 0x1D3A => array(0x6E), 0x1D3C => array(0x6F), 0x1D3D => array(0x223), + 0x1D3E => array(0x70), 0x1D3F => array(0x72), 0x1D40 => array(0x74), + 0x1D41 => array(0x75), 0x1D42 => array(0x77), 0x213B => array(0x66, 0x61, 0x78), + 0x3250 => array(0x70, 0x74, 0x65), 0x32CC => array(0x68, 0x67), + 0x32CE => array(0x65, 0x76), 0x32CF => array(0x6C, 0x74, 0x64), + 0x337A => array(0x69, 0x75), 0x33DE => array(0x76, 0x2215, 0x6D), + 0x33DF => array(0x61, 0x2215, 0x6D) + ), + 'replacemaps' => array(0x41 => array(0x61), 0x42 => array(0x62), 0x43 => array(0x63), + 0x44 => array(0x64), 0x45 => array(0x65), 0x46 => array(0x66), + 0x47 => array(0x67), 0x48 => array(0x68), 0x49 => array(0x69), + 0x4A => array(0x6A), 0x4B => array(0x6B), 0x4C => array(0x6C), + 0x4D => array(0x6D), 0x4E => array(0x6E), 0x4F => array(0x6F), + 0x50 => array(0x70), 0x51 => array(0x71), 0x52 => array(0x72), + 0x53 => array(0x73), 0x54 => array(0x74), 0x55 => array(0x75), + 0x56 => array(0x76), 0x57 => array(0x77), 0x58 => array(0x78), + 0x59 => array(0x79), 0x5A => array(0x7A), 0xAA => array(0x61), + 0xB2 => array(0x32), 0xB3 => array(0x33), 0xB5 => array(0x3BC), + 0xB9 => array(0x31), 0xBA => array(0x6F), 0xBC => array(0x31, 0x2044, 0x34), + 0xBD => array(0x31, 0x2044, 0x32), 0xBE => array(0x33, 0x2044, 0x34), 0xC0 => array(0xE0), + 0xC1 => array(0xE1), 0xC2 => array(0xE2), 0xC3 => array(0xE3), + 0xC4 => array(0xE4), 0xC5 => array(0xE5), 0xC6 => array(0xE6), + 0xC7 => array(0xE7), 0xC8 => array(0xE8), 0xC9 => array(0xE9), + 0xCA => array(0xEA), 0xCB => array(0xEB), 0xCC => array(0xEC), + 0xCD => array(0xED), 0xCE => array(0xEE), 0xCF => array(0xEF), + 0xD0 => array(0xF0), 0xD1 => array(0xF1), 0xD2 => array(0xF2), + 0xD3 => array(0xF3), 0xD4 => array(0xF4), 0xD5 => array(0xF5), + 0xD6 => array(0xF6), 0xD8 => array(0xF8), 0xD9 => array(0xF9), + 0xDA => array(0xFA), 0xDB => array(0xFB), 0xDC => array(0xFC), + 0xDD => array(0xFD), 0xDE => array(0xFE), 0x100 => array(0x101), + 0x102 => array(0x103), 0x104 => array(0x105), 0x106 => array(0x107), + 0x108 => array(0x109), 0x10A => array(0x10B), 0x10C => array(0x10D), + 0x10E => array(0x10F), 0x110 => array(0x111), 0x112 => array(0x113), + 0x114 => array(0x115), 0x116 => array(0x117), 0x118 => array(0x119), + 0x11A => array(0x11B), 0x11C => array(0x11D), 0x11E => array(0x11F), + 0x120 => array(0x121), 0x122 => array(0x123), 0x124 => array(0x125), + 0x126 => array(0x127), 0x128 => array(0x129), 0x12A => array(0x12B), + 0x12C => array(0x12D), 0x12E => array(0x12F), 0x130 => array(0x69, 0x307), + 0x132 => array(0x69, 0x6A), 0x133 => array(0x69, 0x6A), 0x134 => array(0x135), + 0x136 => array(0x137), 0x139 => array(0x13A), 0x13B => array(0x13C), + 0x13D => array(0x13E), 0x13F => array(0x6C, 0xB7), 0x140 => array(0x6C, 0xB7), + 0x141 => array(0x142), 0x143 => array(0x144), 0x145 => array(0x146), + 0x147 => array(0x148), 0x149 => array(0x2BC, 0x6E), 0x14A => array(0x14B), + 0x14C => array(0x14D), 0x14E => array(0x14F), 0x150 => array(0x151), + 0x152 => array(0x153), 0x154 => array(0x155), 0x156 => array(0x157), + 0x158 => array(0x159), 0x15A => array(0x15B), 0x15C => array(0x15D), + 0x15E => array(0x15F), 0x160 => array(0x161), 0x162 => array(0x163), + 0x164 => array(0x165), 0x166 => array(0x167), 0x168 => array(0x169), + 0x16A => array(0x16B), 0x16C => array(0x16D), 0x16E => array(0x16F), + 0x170 => array(0x171), 0x172 => array(0x173), 0x174 => array(0x175), + 0x176 => array(0x177), 0x178 => array(0xFF), 0x179 => array(0x17A), + 0x17B => array(0x17C), 0x17D => array(0x17E), 0x17F => array(0x73), + 0x181 => array(0x253), 0x182 => array(0x183), 0x184 => array(0x185), + 0x186 => array(0x254), 0x187 => array(0x188), 0x189 => array(0x256), + 0x18A => array(0x257), 0x18B => array(0x18C), 0x18E => array(0x1DD), + 0x18F => array(0x259), 0x190 => array(0x25B), 0x191 => array(0x192), + 0x193 => array(0x260), 0x194 => array(0x263), 0x196 => array(0x269), + 0x197 => array(0x268), 0x198 => array(0x199), 0x19C => array(0x26F), + 0x19D => array(0x272), 0x19F => array(0x275), 0x1A0 => array(0x1A1), + 0x1A2 => array(0x1A3), 0x1A4 => array(0x1A5), 0x1A6 => array(0x280), + 0x1A7 => array(0x1A8), 0x1A9 => array(0x283), 0x1AC => array(0x1AD), + 0x1AE => array(0x288), 0x1AF => array(0x1B0), 0x1B1 => array(0x28A), + 0x1B2 => array(0x28B), 0x1B3 => array(0x1B4), 0x1B5 => array(0x1B6), + 0x1B7 => array(0x292), 0x1B8 => array(0x1B9), 0x1BC => array(0x1BD), + 0x1C4 => array(0x64, 0x17E), 0x1C5 => array(0x64, 0x17E), 0x1C6 => array(0x64, 0x17E), + 0x1C7 => array(0x6C, 0x6A), 0x1C8 => array(0x6C, 0x6A), 0x1C9 => array(0x6C, 0x6A), + 0x1CA => array(0x6E, 0x6A), 0x1CB => array(0x6E, 0x6A), 0x1CC => array(0x6E, 0x6A), + 0x1CD => array(0x1CE), 0x1CF => array(0x1D0), 0x1D1 => array(0x1D2), + 0x1D3 => array(0x1D4), 0x1D5 => array(0x1D6), 0x1D7 => array(0x1D8), + 0x1D9 => array(0x1DA), 0x1DB => array(0x1DC), 0x1DE => array(0x1DF), + 0x1E0 => array(0x1E1), 0x1E2 => array(0x1E3), 0x1E4 => array(0x1E5), + 0x1E6 => array(0x1E7), 0x1E8 => array(0x1E9), 0x1EA => array(0x1EB), + 0x1EC => array(0x1ED), 0x1EE => array(0x1EF), 0x1F1 => array(0x64, 0x7A), + 0x1F2 => array(0x64, 0x7A), 0x1F3 => array(0x64, 0x7A), 0x1F4 => array(0x1F5), + 0x1F6 => array(0x195), 0x1F7 => array(0x1BF), 0x1F8 => array(0x1F9), + 0x1FA => array(0x1FB), 0x1FC => array(0x1FD), 0x1FE => array(0x1FF), + 0x200 => array(0x201), 0x202 => array(0x203), 0x204 => array(0x205), + 0x206 => array(0x207), 0x208 => array(0x209), 0x20A => array(0x20B), + 0x20C => array(0x20D), 0x20E => array(0x20F), 0x210 => array(0x211), + 0x212 => array(0x213), 0x214 => array(0x215), 0x216 => array(0x217), + 0x218 => array(0x219), 0x21A => array(0x21B), 0x21C => array(0x21D), + 0x21E => array(0x21F), 0x220 => array(0x19E), 0x222 => array(0x223), + 0x224 => array(0x225), 0x226 => array(0x227), 0x228 => array(0x229), + 0x22A => array(0x22B), 0x22C => array(0x22D), 0x22E => array(0x22F), + 0x230 => array(0x231), 0x232 => array(0x233), 0x23A => array(0x2C65), + 0x23B => array(0x23C), 0x23D => array(0x19A), 0x23E => array(0x2C66), + 0x241 => array(0x242), 0x243 => array(0x180), 0x244 => array(0x289), + 0x245 => array(0x28C), 0x246 => array(0x247), 0x248 => array(0x249), + 0x24A => array(0x24B), 0x24C => array(0x24D), 0x24E => array(0x24F), + 0x2B0 => array(0x68), 0x2B1 => array(0x266), 0x2B2 => array(0x6A), + 0x2B3 => array(0x72), 0x2B4 => array(0x279), 0x2B5 => array(0x27B), + 0x2B6 => array(0x281), 0x2B7 => array(0x77), 0x2B8 => array(0x79), + 0x2E0 => array(0x263), 0x2E1 => array(0x6C), 0x2E2 => array(0x73), + 0x2E3 => array(0x78), 0x2E4 => array(0x295), 0x340 => array(0x300), + 0x341 => array(0x301), 0x343 => array(0x313), 0x344 => array(0x308, 0x301), + 0x345 => array(0x3B9), 0x370 => array(0x371), 0x372 => array(0x373), + 0x374 => array(0x2B9), 0x376 => array(0x377), 0x37F => array(0x3F3), + 0x386 => array(0x3AC), 0x387 => array(0xB7), 0x388 => array(0x3AD), + 0x389 => array(0x3AE), 0x38A => array(0x3AF), 0x38C => array(0x3CC), + 0x38E => array(0x3CD), 0x38F => array(0x3CE), 0x391 => array(0x3B1), + 0x392 => array(0x3B2), 0x393 => array(0x3B3), 0x394 => array(0x3B4), + 0x395 => array(0x3B5), 0x396 => array(0x3B6), 0x397 => array(0x3B7), + 0x398 => array(0x3B8), 0x399 => array(0x3B9), 0x39A => array(0x3BA), + 0x39B => array(0x3BB), 0x39C => array(0x3BC), 0x39D => array(0x3BD), + 0x39E => array(0x3BE), 0x39F => array(0x3BF), 0x3A0 => array(0x3C0), + 0x3A1 => array(0x3C1), 0x3A3 => array(0x3C3), 0x3A4 => array(0x3C4), + 0x3A5 => array(0x3C5), 0x3A6 => array(0x3C6), 0x3A7 => array(0x3C7), + 0x3A8 => array(0x3C8), 0x3A9 => array(0x3C9), 0x3AA => array(0x3CA), + 0x3AB => array(0x3CB), 0x3CF => array(0x3D7), 0x3D0 => array(0x3B2), + 0x3D1 => array(0x3B8), 0x3D2 => array(0x3C5), 0x3D3 => array(0x3CD), + 0x3D4 => array(0x3CB), 0x3D5 => array(0x3C6), 0x3D6 => array(0x3C0), + 0x3D8 => array(0x3D9), 0x3DA => array(0x3DB), 0x3DC => array(0x3DD), + 0x3DE => array(0x3DF), 0x3E0 => array(0x3E1), 0x3E2 => array(0x3E3), + 0x3E4 => array(0x3E5), 0x3E6 => array(0x3E7), 0x3E8 => array(0x3E9), + 0x3EA => array(0x3EB), 0x3EC => array(0x3ED), 0x3EE => array(0x3EF), + 0x3F0 => array(0x3BA), 0x3F1 => array(0x3C1), 0x3F2 => array(0x3C3), + 0x3F4 => array(0x3B8), 0x3F5 => array(0x3B5), 0x3F7 => array(0x3F8), + 0x3F9 => array(0x3C3), 0x3FA => array(0x3FB), 0x3FD => array(0x37B), + 0x3FE => array(0x37C), 0x3FF => array(0x37D), 0x400 => array(0x450), + 0x401 => array(0x451), 0x402 => array(0x452), 0x403 => array(0x453), + 0x404 => array(0x454), 0x405 => array(0x455), 0x406 => array(0x456), + 0x407 => array(0x457), 0x408 => array(0x458), 0x409 => array(0x459), + 0x40A => array(0x45A), 0x40B => array(0x45B), 0x40C => array(0x45C), + 0x40D => array(0x45D), 0x40E => array(0x45E), 0x40F => array(0x45F), + 0x410 => array(0x430), 0x411 => array(0x431), 0x412 => array(0x432), + 0x413 => array(0x433), 0x414 => array(0x434), 0x415 => array(0x435), + 0x416 => array(0x436), 0x417 => array(0x437), 0x418 => array(0x438), + 0x419 => array(0x439), 0x41A => array(0x43A), 0x41B => array(0x43B), + 0x41C => array(0x43C), 0x41D => array(0x43D), 0x41E => array(0x43E), + 0x41F => array(0x43F), 0x420 => array(0x440), 0x421 => array(0x441), + 0x422 => array(0x442), 0x423 => array(0x443), 0x424 => array(0x444), + 0x425 => array(0x445), 0x426 => array(0x446), 0x427 => array(0x447), + 0x428 => array(0x448), 0x429 => array(0x449), 0x42A => array(0x44A), + 0x42B => array(0x44B), 0x42C => array(0x44C), 0x42D => array(0x44D), + 0x42E => array(0x44E), 0x42F => array(0x44F), 0x460 => array(0x461), + 0x462 => array(0x463), 0x464 => array(0x465), 0x466 => array(0x467), + 0x468 => array(0x469), 0x46A => array(0x46B), 0x46C => array(0x46D), + 0x46E => array(0x46F), 0x470 => array(0x471), 0x472 => array(0x473), + 0x474 => array(0x475), 0x476 => array(0x477), 0x478 => array(0x479), + 0x47A => array(0x47B), 0x47C => array(0x47D), 0x47E => array(0x47F), + 0x480 => array(0x481), 0x48A => array(0x48B), 0x48C => array(0x48D), + 0x48E => array(0x48F), 0x490 => array(0x491), 0x492 => array(0x493), + 0x494 => array(0x495), 0x496 => array(0x497), 0x498 => array(0x499), + 0x49A => array(0x49B), 0x49C => array(0x49D), 0x49E => array(0x49F), + 0x4A0 => array(0x4A1), 0x4A2 => array(0x4A3), 0x4A4 => array(0x4A5), + 0x4A6 => array(0x4A7), 0x4A8 => array(0x4A9), 0x4AA => array(0x4AB), + 0x4AC => array(0x4AD), 0x4AE => array(0x4AF), 0x4B0 => array(0x4B1), + 0x4B2 => array(0x4B3), 0x4B4 => array(0x4B5), 0x4B6 => array(0x4B7), + 0x4B8 => array(0x4B9), 0x4BA => array(0x4BB), 0x4BC => array(0x4BD), + 0x4BE => array(0x4BF), 0x4C1 => array(0x4C2), 0x4C3 => array(0x4C4), + 0x4C5 => array(0x4C6), 0x4C7 => array(0x4C8), 0x4C9 => array(0x4CA), + 0x4CB => array(0x4CC), 0x4CD => array(0x4CE), 0x4D0 => array(0x4D1), + 0x4D2 => array(0x4D3), 0x4D4 => array(0x4D5), 0x4D6 => array(0x4D7), + 0x4D8 => array(0x4D9), 0x4DA => array(0x4DB), 0x4DC => array(0x4DD), + 0x4DE => array(0x4DF), 0x4E0 => array(0x4E1), 0x4E2 => array(0x4E3), + 0x4E4 => array(0x4E5), 0x4E6 => array(0x4E7), 0x4E8 => array(0x4E9), + 0x4EA => array(0x4EB), 0x4EC => array(0x4ED), 0x4EE => array(0x4EF), + 0x4F0 => array(0x4F1), 0x4F2 => array(0x4F3), 0x4F4 => array(0x4F5), + 0x4F6 => array(0x4F7), 0x4F8 => array(0x4F9), 0x4FA => array(0x4FB), + 0x4FC => array(0x4FD), 0x4FE => array(0x4FF), 0x500 => array(0x501), + 0x502 => array(0x503), 0x504 => array(0x505), 0x506 => array(0x507), + 0x508 => array(0x509), 0x50A => array(0x50B), 0x50C => array(0x50D), + 0x50E => array(0x50F), 0x510 => array(0x511), 0x512 => array(0x513), + 0x514 => array(0x515), 0x516 => array(0x517), 0x518 => array(0x519), + 0x51A => array(0x51B), 0x51C => array(0x51D), 0x51E => array(0x51F), + 0x520 => array(0x521), 0x522 => array(0x523), 0x524 => array(0x525), + 0x526 => array(0x527), 0x528 => array(0x529), 0x52A => array(0x52B), + 0x52C => array(0x52D), 0x52E => array(0x52F), 0x531 => array(0x561), + 0x532 => array(0x562), 0x533 => array(0x563), 0x534 => array(0x564), + 0x535 => array(0x565), 0x536 => array(0x566), 0x537 => array(0x567), + 0x538 => array(0x568), 0x539 => array(0x569), 0x53A => array(0x56A), + 0x53B => array(0x56B), 0x53C => array(0x56C), 0x53D => array(0x56D), + 0x53E => array(0x56E), 0x53F => array(0x56F), 0x540 => array(0x570), + 0x541 => array(0x571), 0x542 => array(0x572), 0x543 => array(0x573), + 0x544 => array(0x574), 0x545 => array(0x575), 0x546 => array(0x576), + 0x547 => array(0x577), 0x548 => array(0x578), 0x549 => array(0x579), + 0x54A => array(0x57A), 0x54B => array(0x57B), 0x54C => array(0x57C), + 0x54D => array(0x57D), 0x54E => array(0x57E), 0x54F => array(0x57F), + 0x550 => array(0x580), 0x551 => array(0x581), 0x552 => array(0x582), + 0x553 => array(0x583), 0x554 => array(0x584), 0x555 => array(0x585), + 0x556 => array(0x586), 0x587 => array(0x565, 0x582), 0x675 => array(0x627, 0x674), + 0x676 => array(0x648, 0x674), 0x677 => array(0x6C7, 0x674), 0x678 => array(0x64A, 0x674), + 0x958 => array(0x915, 0x93C), 0x959 => array(0x916, 0x93C), 0x95A => array(0x917, 0x93C), + 0x95B => array(0x91C, 0x93C), 0x95C => array(0x921, 0x93C), 0x95D => array(0x922, 0x93C), + 0x95E => array(0x92B, 0x93C), 0x95F => array(0x92F, 0x93C), 0x9DC => array(0x9A1, 0x9BC), + 0x9DD => array(0x9A2, 0x9BC), 0x9DF => array(0x9AF, 0x9BC), 0xA33 => array(0xA32, 0xA3C), + 0xA36 => array(0xA38, 0xA3C), 0xA59 => array(0xA16, 0xA3C), 0xA5A => array(0xA17, 0xA3C), + 0xA5B => array(0xA1C, 0xA3C), 0xA5E => array(0xA2B, 0xA3C), 0xB5C => array(0xB21, 0xB3C), + 0xB5D => array(0xB22, 0xB3C), 0xE33 => array(0xE4D, 0xE32), 0xEB3 => array(0xECD, 0xEB2), + 0xEDC => array(0xEAB, 0xE99), 0xEDD => array(0xEAB, 0xEA1), 0xF0C => array(0xF0B), + 0xF43 => array(0xF42, 0xFB7), 0xF4D => array(0xF4C, 0xFB7), 0xF52 => array(0xF51, 0xFB7), + 0xF57 => array(0xF56, 0xFB7), 0xF5C => array(0xF5B, 0xFB7), 0xF69 => array(0xF40, 0xFB5), + 0xF73 => array(0xF71, 0xF72), 0xF75 => array(0xF71, 0xF74), 0xF76 => array(0xFB2, 0xF80), + 0xF77 => array(0xFB2, 0xF71, 0xF80), 0xF78 => array(0xFB3, 0xF80), 0xF79 => array(0xFB3, 0xF71, 0xF80), + 0xF81 => array(0xF71, 0xF80), 0xF93 => array(0xF92, 0xFB7), 0xF9D => array(0xF9C, 0xFB7), + 0xFA2 => array(0xFA1, 0xFB7), 0xFA7 => array(0xFA6, 0xFB7), 0xFAC => array(0xFAB, 0xFB7), + 0xFB9 => array(0xF90, 0xFB5), 0x10C7 => array(0x2D27), 0x10CD => array(0x2D2D), + 0x10FC => array(0x10DC), 0x1D2C => array(0x61), 0x1D2D => array(0xE6), + 0x1D2E => array(0x62), 0x1D30 => array(0x64), 0x1D31 => array(0x65), + 0x1D32 => array(0x1DD), 0x1D33 => array(0x67), 0x1D34 => array(0x68), + 0x1D35 => array(0x69), 0x1D36 => array(0x6A), 0x1D37 => array(0x6B), + 0x1D38 => array(0x6C), 0x1D39 => array(0x6D), 0x1D3A => array(0x6E), + 0x1D3C => array(0x6F), 0x1D3D => array(0x223), 0x1D3E => array(0x70), + 0x1D3F => array(0x72), 0x1D40 => array(0x74), 0x1D41 => array(0x75), + 0x1D42 => array(0x77), 0x1D43 => array(0x61), 0x1D44 => array(0x250), + 0x1D45 => array(0x251), 0x1D46 => array(0x1D02), 0x1D47 => array(0x62), + 0x1D48 => array(0x64), 0x1D49 => array(0x65), 0x1D4A => array(0x259), + 0x1D4B => array(0x25B), 0x1D4C => array(0x25C), 0x1D4D => array(0x67), + 0x1D4F => array(0x6B), 0x1D50 => array(0x6D), 0x1D51 => array(0x14B), + 0x1D52 => array(0x6F), 0x1D53 => array(0x254), 0x1D54 => array(0x1D16), + 0x1D55 => array(0x1D17), 0x1D56 => array(0x70), 0x1D57 => array(0x74), + 0x1D58 => array(0x75), 0x1D59 => array(0x1D1D), 0x1D5A => array(0x26F), + 0x1D5B => array(0x76), 0x1D5C => array(0x1D25), 0x1D5D => array(0x3B2), + 0x1D5E => array(0x3B3), 0x1D5F => array(0x3B4), 0x1D60 => array(0x3C6), + 0x1D61 => array(0x3C7), 0x1D62 => array(0x69), 0x1D63 => array(0x72), + 0x1D64 => array(0x75), 0x1D65 => array(0x76), 0x1D66 => array(0x3B2), + 0x1D67 => array(0x3B3), 0x1D68 => array(0x3C1), 0x1D69 => array(0x3C6), + 0x1D6A => array(0x3C7), 0x1D78 => array(0x43D), 0x1D9B => array(0x252), + 0x1D9C => array(0x63), 0x1D9D => array(0x255), 0x1D9E => array(0xF0), + 0x1D9F => array(0x25C), 0x1DA0 => array(0x66), 0x1DA1 => array(0x25F), + 0x1DA2 => array(0x261), 0x1DA3 => array(0x265), 0x1DA4 => array(0x268), + 0x1DA5 => array(0x269), 0x1DA6 => array(0x26A), 0x1DA7 => array(0x1D7B), + 0x1DA8 => array(0x29D), 0x1DA9 => array(0x26D), 0x1DAA => array(0x1D85), + 0x1DAB => array(0x29F), 0x1DAC => array(0x271), 0x1DAD => array(0x270), + 0x1DAE => array(0x272), 0x1DAF => array(0x273), 0x1DB0 => array(0x274), + 0x1DB1 => array(0x275), 0x1DB2 => array(0x278), 0x1DB3 => array(0x282), + 0x1DB4 => array(0x283), 0x1DB5 => array(0x1AB), 0x1DB6 => array(0x289), + 0x1DB7 => array(0x28A), 0x1DB8 => array(0x1D1C), 0x1DB9 => array(0x28B), + 0x1DBA => array(0x28C), 0x1DBB => array(0x7A), 0x1DBC => array(0x290), + 0x1DBD => array(0x291), 0x1DBE => array(0x292), 0x1DBF => array(0x3B8), + 0x1E00 => array(0x1E01), 0x1E02 => array(0x1E03), 0x1E04 => array(0x1E05), + 0x1E06 => array(0x1E07), 0x1E08 => array(0x1E09), 0x1E0A => array(0x1E0B), + 0x1E0C => array(0x1E0D), 0x1E0E => array(0x1E0F), 0x1E10 => array(0x1E11), + 0x1E12 => array(0x1E13), 0x1E14 => array(0x1E15), 0x1E16 => array(0x1E17), + 0x1E18 => array(0x1E19), 0x1E1A => array(0x1E1B), 0x1E1C => array(0x1E1D), + 0x1E1E => array(0x1E1F), 0x1E20 => array(0x1E21), 0x1E22 => array(0x1E23), + 0x1E24 => array(0x1E25), 0x1E26 => array(0x1E27), 0x1E28 => array(0x1E29), + 0x1E2A => array(0x1E2B), 0x1E2C => array(0x1E2D), 0x1E2E => array(0x1E2F), + 0x1E30 => array(0x1E31), 0x1E32 => array(0x1E33), 0x1E34 => array(0x1E35), + 0x1E36 => array(0x1E37), 0x1E38 => array(0x1E39), 0x1E3A => array(0x1E3B), + 0x1E3C => array(0x1E3D), 0x1E3E => array(0x1E3F), 0x1E40 => array(0x1E41), + 0x1E42 => array(0x1E43), 0x1E44 => array(0x1E45), 0x1E46 => array(0x1E47), + 0x1E48 => array(0x1E49), 0x1E4A => array(0x1E4B), 0x1E4C => array(0x1E4D), + 0x1E4E => array(0x1E4F), 0x1E50 => array(0x1E51), 0x1E52 => array(0x1E53), + 0x1E54 => array(0x1E55), 0x1E56 => array(0x1E57), 0x1E58 => array(0x1E59), + 0x1E5A => array(0x1E5B), 0x1E5C => array(0x1E5D), 0x1E5E => array(0x1E5F), + 0x1E60 => array(0x1E61), 0x1E62 => array(0x1E63), 0x1E64 => array(0x1E65), + 0x1E66 => array(0x1E67), 0x1E68 => array(0x1E69), 0x1E6A => array(0x1E6B), + 0x1E6C => array(0x1E6D), 0x1E6E => array(0x1E6F), 0x1E70 => array(0x1E71), + 0x1E72 => array(0x1E73), 0x1E74 => array(0x1E75), 0x1E76 => array(0x1E77), + 0x1E78 => array(0x1E79), 0x1E7A => array(0x1E7B), 0x1E7C => array(0x1E7D), + 0x1E7E => array(0x1E7F), 0x1E80 => array(0x1E81), 0x1E82 => array(0x1E83), + 0x1E84 => array(0x1E85), 0x1E86 => array(0x1E87), 0x1E88 => array(0x1E89), + 0x1E8A => array(0x1E8B), 0x1E8C => array(0x1E8D), 0x1E8E => array(0x1E8F), + 0x1E90 => array(0x1E91), 0x1E92 => array(0x1E93), 0x1E94 => array(0x1E95), + 0x1E9A => array(0x61, 0x2BE), 0x1E9B => array(0x1E61), 0x1E9E => array(0x73, 0x73), + 0x1EA0 => array(0x1EA1), 0x1EA2 => array(0x1EA3), 0x1EA4 => array(0x1EA5), + 0x1EA6 => array(0x1EA7), 0x1EA8 => array(0x1EA9), 0x1EAA => array(0x1EAB), + 0x1EAC => array(0x1EAD), 0x1EAE => array(0x1EAF), 0x1EB0 => array(0x1EB1), + 0x1EB2 => array(0x1EB3), 0x1EB4 => array(0x1EB5), 0x1EB6 => array(0x1EB7), + 0x1EB8 => array(0x1EB9), 0x1EBA => array(0x1EBB), 0x1EBC => array(0x1EBD), + 0x1EBE => array(0x1EBF), 0x1EC0 => array(0x1EC1), 0x1EC2 => array(0x1EC3), + 0x1EC4 => array(0x1EC5), 0x1EC6 => array(0x1EC7), 0x1EC8 => array(0x1EC9), + 0x1ECA => array(0x1ECB), 0x1ECC => array(0x1ECD), 0x1ECE => array(0x1ECF), + 0x1ED0 => array(0x1ED1), 0x1ED2 => array(0x1ED3), 0x1ED4 => array(0x1ED5), + 0x1ED6 => array(0x1ED7), 0x1ED8 => array(0x1ED9), 0x1EDA => array(0x1EDB), + 0x1EDC => array(0x1EDD), 0x1EDE => array(0x1EDF), 0x1EE0 => array(0x1EE1), + 0x1EE2 => array(0x1EE3), 0x1EE4 => array(0x1EE5), 0x1EE6 => array(0x1EE7), + 0x1EE8 => array(0x1EE9), 0x1EEA => array(0x1EEB), 0x1EEC => array(0x1EED), + 0x1EEE => array(0x1EEF), 0x1EF0 => array(0x1EF1), 0x1EF2 => array(0x1EF3), + 0x1EF4 => array(0x1EF5), 0x1EF6 => array(0x1EF7), 0x1EF8 => array(0x1EF9), + 0x1EFA => array(0x1EFB), 0x1EFC => array(0x1EFD), 0x1EFE => array(0x1EFF), + 0x1F08 => array(0x1F00), 0x1F09 => array(0x1F01), 0x1F0A => array(0x1F02), + 0x1F0B => array(0x1F03), 0x1F0C => array(0x1F04), 0x1F0D => array(0x1F05), + 0x1F0E => array(0x1F06), 0x1F0F => array(0x1F07), 0x1F18 => array(0x1F10), + 0x1F19 => array(0x1F11), 0x1F1A => array(0x1F12), 0x1F1B => array(0x1F13), + 0x1F1C => array(0x1F14), 0x1F1D => array(0x1F15), 0x1F28 => array(0x1F20), + 0x1F29 => array(0x1F21), 0x1F2A => array(0x1F22), 0x1F2B => array(0x1F23), + 0x1F2C => array(0x1F24), 0x1F2D => array(0x1F25), 0x1F2E => array(0x1F26), + 0x1F2F => array(0x1F27), 0x1F38 => array(0x1F30), 0x1F39 => array(0x1F31), + 0x1F3A => array(0x1F32), 0x1F3B => array(0x1F33), 0x1F3C => array(0x1F34), + 0x1F3D => array(0x1F35), 0x1F3E => array(0x1F36), 0x1F3F => array(0x1F37), + 0x1F48 => array(0x1F40), 0x1F49 => array(0x1F41), 0x1F4A => array(0x1F42), + 0x1F4B => array(0x1F43), 0x1F4C => array(0x1F44), 0x1F4D => array(0x1F45), + 0x1F59 => array(0x1F51), 0x1F5B => array(0x1F53), 0x1F5D => array(0x1F55), + 0x1F5F => array(0x1F57), 0x1F68 => array(0x1F60), 0x1F69 => array(0x1F61), + 0x1F6A => array(0x1F62), 0x1F6B => array(0x1F63), 0x1F6C => array(0x1F64), + 0x1F6D => array(0x1F65), 0x1F6E => array(0x1F66), 0x1F6F => array(0x1F67), + 0x1F71 => array(0x3AC), 0x1F73 => array(0x3AD), 0x1F75 => array(0x3AE), + 0x1F77 => array(0x3AF), 0x1F79 => array(0x3CC), 0x1F7B => array(0x3CD), + 0x1F7D => array(0x3CE), 0x1F80 => array(0x1F00, 0x3B9), 0x1F81 => array(0x1F01, 0x3B9), + 0x1F82 => array(0x1F02, 0x3B9), 0x1F83 => array(0x1F03, 0x3B9), 0x1F84 => array(0x1F04, 0x3B9), + 0x1F85 => array(0x1F05, 0x3B9), 0x1F86 => array(0x1F06, 0x3B9), 0x1F87 => array(0x1F07, 0x3B9), + 0x1F88 => array(0x1F00, 0x3B9), 0x1F89 => array(0x1F01, 0x3B9), 0x1F8A => array(0x1F02, 0x3B9), + 0x1F8B => array(0x1F03, 0x3B9), 0x1F8C => array(0x1F04, 0x3B9), 0x1F8D => array(0x1F05, 0x3B9), + 0x1F8E => array(0x1F06, 0x3B9), 0x1F8F => array(0x1F07, 0x3B9), 0x1F90 => array(0x1F20, 0x3B9), + 0x1F91 => array(0x1F21, 0x3B9), 0x1F92 => array(0x1F22, 0x3B9), 0x1F93 => array(0x1F23, 0x3B9), + 0x1F94 => array(0x1F24, 0x3B9), 0x1F95 => array(0x1F25, 0x3B9), 0x1F96 => array(0x1F26, 0x3B9), + 0x1F97 => array(0x1F27, 0x3B9), 0x1F98 => array(0x1F20, 0x3B9), 0x1F99 => array(0x1F21, 0x3B9), + 0x1F9A => array(0x1F22, 0x3B9), 0x1F9B => array(0x1F23, 0x3B9), 0x1F9C => array(0x1F24, 0x3B9), + 0x1F9D => array(0x1F25, 0x3B9), 0x1F9E => array(0x1F26, 0x3B9), 0x1F9F => array(0x1F27, 0x3B9), + 0x1FA0 => array(0x1F60, 0x3B9), 0x1FA1 => array(0x1F61, 0x3B9), 0x1FA2 => array(0x1F62, 0x3B9), + 0x1FA3 => array(0x1F63, 0x3B9), 0x1FA4 => array(0x1F64, 0x3B9), 0x1FA5 => array(0x1F65, 0x3B9), + 0x1FA6 => array(0x1F66, 0x3B9), 0x1FA7 => array(0x1F67, 0x3B9), 0x1FA8 => array(0x1F60, 0x3B9), + 0x1FA9 => array(0x1F61, 0x3B9), 0x1FAA => array(0x1F62, 0x3B9), 0x1FAB => array(0x1F63, 0x3B9), + 0x1FAC => array(0x1F64, 0x3B9), 0x1FAD => array(0x1F65, 0x3B9), 0x1FAE => array(0x1F66, 0x3B9), + 0x1FAF => array(0x1F67, 0x3B9), 0x1FB2 => array(0x1F70, 0x3B9), 0x1FB3 => array(0x3B1, 0x3B9), + 0x1FB4 => array(0x3AC, 0x3B9), 0x1FB7 => array(0x1FB6, 0x3B9), 0x1FB8 => array(0x1FB0), + 0x1FB9 => array(0x1FB1), 0x1FBA => array(0x1F70), 0x1FBB => array(0x3AC), + 0x1FBC => array(0x3B1, 0x3B9), 0x1FBE => array(0x3B9), 0x1FC2 => array(0x1F74, 0x3B9), + 0x1FC3 => array(0x3B7, 0x3B9), 0x1FC4 => array(0x3AE, 0x3B9), 0x1FC7 => array(0x1FC6, 0x3B9), + 0x1FC8 => array(0x1F72), 0x1FC9 => array(0x3AD), 0x1FCA => array(0x1F74), + 0x1FCB => array(0x3AE), 0x1FCC => array(0x3B7, 0x3B9), 0x1FD3 => array(0x390), + 0x1FD8 => array(0x1FD0), 0x1FD9 => array(0x1FD1), 0x1FDA => array(0x1F76), + 0x1FDB => array(0x3AF), 0x1FE3 => array(0x3B0), 0x1FE8 => array(0x1FE0), + 0x1FE9 => array(0x1FE1), 0x1FEA => array(0x1F7A), 0x1FEB => array(0x3CD), + 0x1FEC => array(0x1FE5), 0x1FF2 => array(0x1F7C, 0x3B9), 0x1FF3 => array(0x3C9, 0x3B9), + 0x1FF4 => array(0x3CE, 0x3B9), 0x1FF7 => array(0x1FF6, 0x3B9), 0x1FF8 => array(0x1F78), + 0x1FF9 => array(0x3CC), 0x1FFA => array(0x1F7C), 0x1FFB => array(0x3CE), + 0x1FFC => array(0x3C9, 0x3B9), 0x2011 => array(0x2010), 0x2033 => array(0x2032, 0x2032), + 0x2034 => array(0x2032, 0x2032, 0x2032), 0x2036 => array(0x2035, 0x2035), 0x2037 => array(0x2035, 0x2035, 0x2035), + 0x2057 => array(0x2032, 0x2032, 0x2032, 0x2032), 0x2070 => array(0x30), 0x2071 => array(0x69), + 0x2074 => array(0x34), 0x2075 => array(0x35), 0x2076 => array(0x36), + 0x2077 => array(0x37), 0x2078 => array(0x38), 0x2079 => array(0x39), + 0x207B => array(0x2212), 0x207F => array(0x6E), 0x2080 => array(0x30), + 0x2081 => array(0x31), 0x2082 => array(0x32), 0x2083 => array(0x33), + 0x2084 => array(0x34), 0x2085 => array(0x35), 0x2086 => array(0x36), + 0x2087 => array(0x37), 0x2088 => array(0x38), 0x2089 => array(0x39), + 0x208B => array(0x2212), 0x2090 => array(0x61), 0x2091 => array(0x65), + 0x2092 => array(0x6F), 0x2093 => array(0x78), 0x2094 => array(0x259), + 0x2095 => array(0x68), 0x2096 => array(0x6B), 0x2097 => array(0x6C), + 0x2098 => array(0x6D), 0x2099 => array(0x6E), 0x209A => array(0x70), + 0x209B => array(0x73), 0x209C => array(0x74), 0x20A8 => array(0x72, 0x73), + 0x2102 => array(0x63), 0x2103 => array(0xB0, 0x63), 0x2107 => array(0x25B), + 0x2109 => array(0xB0, 0x66), 0x210A => array(0x67), 0x210B => array(0x68), + 0x210C => array(0x68), 0x210D => array(0x68), 0x210E => array(0x68), + 0x210F => array(0x127), 0x2110 => array(0x69), 0x2111 => array(0x69), + 0x2112 => array(0x6C), 0x2113 => array(0x6C), 0x2115 => array(0x6E), + 0x2116 => array(0x6E, 0x6F), 0x2119 => array(0x70), 0x211A => array(0x71), + 0x211B => array(0x72), 0x211C => array(0x72), 0x211D => array(0x72), + 0x2120 => array(0x73, 0x6D), 0x2121 => array(0x74, 0x65, 0x6C), 0x2122 => array(0x74, 0x6D), + 0x2124 => array(0x7A), 0x2126 => array(0x3C9), 0x2128 => array(0x7A), + 0x212A => array(0x6B), 0x212B => array(0xE5), 0x212C => array(0x62), + 0x212D => array(0x63), 0x212F => array(0x65), 0x2130 => array(0x65), + 0x2131 => array(0x66), 0x2133 => array(0x6D), 0x2134 => array(0x6F), + 0x2135 => array(0x5D0), 0x2136 => array(0x5D1), 0x2137 => array(0x5D2), + 0x2138 => array(0x5D3), 0x2139 => array(0x69), 0x213B => array(0x66, 0x61, 0x78), + 0x213C => array(0x3C0), 0x213D => array(0x3B3), 0x213E => array(0x3B3), + 0x213F => array(0x3C0), 0x2140 => array(0x2211), 0x2145 => array(0x64), + 0x2146 => array(0x64), 0x2147 => array(0x65), 0x2148 => array(0x69), + 0x2149 => array(0x6A), 0x2150 => array(0x31, 0x2044, 0x37), 0x2151 => array(0x31, 0x2044, 0x39), + 0x2152 => array(0x31, 0x2044, 0x31, 0x30), 0x2153 => array(0x31, 0x2044, 0x33), 0x2154 => array(0x32, 0x2044, 0x33), + 0x2155 => array(0x31, 0x2044, 0x35), 0x2156 => array(0x32, 0x2044, 0x35), 0x2157 => array(0x33, 0x2044, 0x35), + 0x2158 => array(0x34, 0x2044, 0x35), 0x2159 => array(0x31, 0x2044, 0x36), 0x215A => array(0x35, 0x2044, 0x36), + 0x215B => array(0x31, 0x2044, 0x38), 0x215C => array(0x33, 0x2044, 0x38), 0x215D => array(0x35, 0x2044, 0x38), + 0x215E => array(0x37, 0x2044, 0x38), 0x215F => array(0x31, 0x2044), 0x2160 => array(0x69), + 0x2161 => array(0x69, 0x69), 0x2162 => array(0x69, 0x69, 0x69), 0x2163 => array(0x69, 0x76), + 0x2164 => array(0x76), 0x2165 => array(0x76, 0x69), 0x2166 => array(0x76, 0x69, 0x69), + 0x2167 => array(0x76, 0x69, 0x69, 0x69), 0x2168 => array(0x69, 0x78), 0x2169 => array(0x78), + 0x216A => array(0x78, 0x69), 0x216B => array(0x78, 0x69, 0x69), 0x216C => array(0x6C), + 0x216D => array(0x63), 0x216E => array(0x64), 0x216F => array(0x6D), + 0x2170 => array(0x69), 0x2171 => array(0x69, 0x69), 0x2172 => array(0x69, 0x69, 0x69), + 0x2173 => array(0x69, 0x76), 0x2174 => array(0x76), 0x2175 => array(0x76, 0x69), + 0x2176 => array(0x76, 0x69, 0x69), 0x2177 => array(0x76, 0x69, 0x69, 0x69), 0x2178 => array(0x69, 0x78), + 0x2179 => array(0x78), 0x217A => array(0x78, 0x69), 0x217B => array(0x78, 0x69, 0x69), + 0x217C => array(0x6C), 0x217D => array(0x63), 0x217E => array(0x64), + 0x217F => array(0x6D), 0x2189 => array(0x30, 0x2044, 0x33), 0x222C => array(0x222B, 0x222B), + 0x222D => array(0x222B, 0x222B, 0x222B), 0x222F => array(0x222E, 0x222E), 0x2230 => array(0x222E, 0x222E, 0x222E), + 0x2329 => array(0x3008), 0x232A => array(0x3009), 0x2460 => array(0x31), + 0x2461 => array(0x32), 0x2462 => array(0x33), 0x2463 => array(0x34), + 0x2464 => array(0x35), 0x2465 => array(0x36), 0x2466 => array(0x37), + 0x2467 => array(0x38), 0x2468 => array(0x39), 0x2469 => array(0x31, 0x30), + 0x246A => array(0x31, 0x31), 0x246B => array(0x31, 0x32), 0x246C => array(0x31, 0x33), + 0x246D => array(0x31, 0x34), 0x246E => array(0x31, 0x35), 0x246F => array(0x31, 0x36), + 0x2470 => array(0x31, 0x37), 0x2471 => array(0x31, 0x38), 0x2472 => array(0x31, 0x39), + 0x2473 => array(0x32, 0x30), 0x24B6 => array(0x61), 0x24B7 => array(0x62), + 0x24B8 => array(0x63), 0x24B9 => array(0x64), 0x24BA => array(0x65), + 0x24BB => array(0x66), 0x24BC => array(0x67), 0x24BD => array(0x68), + 0x24BE => array(0x69), 0x24BF => array(0x6A), 0x24C0 => array(0x6B), + 0x24C1 => array(0x6C), 0x24C2 => array(0x6D), 0x24C3 => array(0x6E), + 0x24C4 => array(0x6F), 0x24C5 => array(0x70), 0x24C6 => array(0x71), + 0x24C7 => array(0x72), 0x24C8 => array(0x73), 0x24C9 => array(0x74), + 0x24CA => array(0x75), 0x24CB => array(0x76), 0x24CC => array(0x77), + 0x24CD => array(0x78), 0x24CE => array(0x79), 0x24CF => array(0x7A), + 0x24D0 => array(0x61), 0x24D1 => array(0x62), 0x24D2 => array(0x63), + 0x24D3 => array(0x64), 0x24D4 => array(0x65), 0x24D5 => array(0x66), + 0x24D6 => array(0x67), 0x24D7 => array(0x68), 0x24D8 => array(0x69), + 0x24D9 => array(0x6A), 0x24DA => array(0x6B), 0x24DB => array(0x6C), + 0x24DC => array(0x6D), 0x24DD => array(0x6E), 0x24DE => array(0x6F), + 0x24DF => array(0x70), 0x24E0 => array(0x71), 0x24E1 => array(0x72), + 0x24E2 => array(0x73), 0x24E3 => array(0x74), 0x24E4 => array(0x75), + 0x24E5 => array(0x76), 0x24E6 => array(0x77), 0x24E7 => array(0x78), + 0x24E8 => array(0x79), 0x24E9 => array(0x7A), 0x24EA => array(0x30), + 0x2A0C => array(0x222B, 0x222B, 0x222B, 0x222B), 0x2ADC => array(0x2ADD, 0x338), 0x2C00 => array(0x2C30), + 0x2C01 => array(0x2C31), 0x2C02 => array(0x2C32), 0x2C03 => array(0x2C33), + 0x2C04 => array(0x2C34), 0x2C05 => array(0x2C35), 0x2C06 => array(0x2C36), + 0x2C07 => array(0x2C37), 0x2C08 => array(0x2C38), 0x2C09 => array(0x2C39), + 0x2C0A => array(0x2C3A), 0x2C0B => array(0x2C3B), 0x2C0C => array(0x2C3C), + 0x2C0D => array(0x2C3D), 0x2C0E => array(0x2C3E), 0x2C0F => array(0x2C3F), + 0x2C10 => array(0x2C40), 0x2C11 => array(0x2C41), 0x2C12 => array(0x2C42), + 0x2C13 => array(0x2C43), 0x2C14 => array(0x2C44), 0x2C15 => array(0x2C45), + 0x2C16 => array(0x2C46), 0x2C17 => array(0x2C47), 0x2C18 => array(0x2C48), + 0x2C19 => array(0x2C49), 0x2C1A => array(0x2C4A), 0x2C1B => array(0x2C4B), + 0x2C1C => array(0x2C4C), 0x2C1D => array(0x2C4D), 0x2C1E => array(0x2C4E), + 0x2C1F => array(0x2C4F), 0x2C20 => array(0x2C50), 0x2C21 => array(0x2C51), + 0x2C22 => array(0x2C52), 0x2C23 => array(0x2C53), 0x2C24 => array(0x2C54), + 0x2C25 => array(0x2C55), 0x2C26 => array(0x2C56), 0x2C27 => array(0x2C57), + 0x2C28 => array(0x2C58), 0x2C29 => array(0x2C59), 0x2C2A => array(0x2C5A), + 0x2C2B => array(0x2C5B), 0x2C2C => array(0x2C5C), 0x2C2D => array(0x2C5D), + 0x2C2E => array(0x2C5E), 0x2C60 => array(0x2C61), 0x2C62 => array(0x26B), + 0x2C63 => array(0x1D7D), 0x2C64 => array(0x27D), 0x2C67 => array(0x2C68), + 0x2C69 => array(0x2C6A), 0x2C6B => array(0x2C6C), 0x2C6D => array(0x251), + 0x2C6E => array(0x271), 0x2C6F => array(0x250), 0x2C70 => array(0x252), + 0x2C72 => array(0x2C73), 0x2C75 => array(0x2C76), 0x2C7C => array(0x6A), + 0x2C7D => array(0x76), 0x2C7E => array(0x23F), 0x2C7F => array(0x240), + 0x2C80 => array(0x2C81), 0x2C82 => array(0x2C83), 0x2C84 => array(0x2C85), + 0x2C86 => array(0x2C87), 0x2C88 => array(0x2C89), 0x2C8A => array(0x2C8B), + 0x2C8C => array(0x2C8D), 0x2C8E => array(0x2C8F), 0x2C90 => array(0x2C91), + 0x2C92 => array(0x2C93), 0x2C94 => array(0x2C95), 0x2C96 => array(0x2C97), + 0x2C98 => array(0x2C99), 0x2C9A => array(0x2C9B), 0x2C9C => array(0x2C9D), + 0x2C9E => array(0x2C9F), 0x2CA0 => array(0x2CA1), 0x2CA2 => array(0x2CA3), + 0x2CA4 => array(0x2CA5), 0x2CA6 => array(0x2CA7), 0x2CA8 => array(0x2CA9), + 0x2CAA => array(0x2CAB), 0x2CAC => array(0x2CAD), 0x2CAE => array(0x2CAF), + 0x2CB0 => array(0x2CB1), 0x2CB2 => array(0x2CB3), 0x2CB4 => array(0x2CB5), + 0x2CB6 => array(0x2CB7), 0x2CB8 => array(0x2CB9), 0x2CBA => array(0x2CBB), + 0x2CBC => array(0x2CBD), 0x2CBE => array(0x2CBF), 0x2CC0 => array(0x2CC1), + 0x2CC2 => array(0x2CC3), 0x2CC4 => array(0x2CC5), 0x2CC6 => array(0x2CC7), + 0x2CC8 => array(0x2CC9), 0x2CCA => array(0x2CCB), 0x2CCC => array(0x2CCD), + 0x2CCE => array(0x2CCF), 0x2CD0 => array(0x2CD1), 0x2CD2 => array(0x2CD3), + 0x2CD4 => array(0x2CD5), 0x2CD6 => array(0x2CD7), 0x2CD8 => array(0x2CD9), + 0x2CDA => array(0x2CDB), 0x2CDC => array(0x2CDD), 0x2CDE => array(0x2CDF), + 0x2CE0 => array(0x2CE1), 0x2CE2 => array(0x2CE3), 0x2CEB => array(0x2CEC), + 0x2CED => array(0x2CEE), 0x2CF2 => array(0x2CF3), 0x2D6F => array(0x2D61), + 0x2E9F => array(0x6BCD), 0x2EF3 => array(0x9F9F), 0x2F00 => array(0x4E00), + 0x2F01 => array(0x4E28), 0x2F02 => array(0x4E36), 0x2F03 => array(0x4E3F), + 0x2F04 => array(0x4E59), 0x2F05 => array(0x4E85), 0x2F06 => array(0x4E8C), + 0x2F07 => array(0x4EA0), 0x2F08 => array(0x4EBA), 0x2F09 => array(0x513F), + 0x2F0A => array(0x5165), 0x2F0B => array(0x516B), 0x2F0C => array(0x5182), + 0x2F0D => array(0x5196), 0x2F0E => array(0x51AB), 0x2F0F => array(0x51E0), + 0x2F10 => array(0x51F5), 0x2F11 => array(0x5200), 0x2F12 => array(0x529B), + 0x2F13 => array(0x52F9), 0x2F14 => array(0x5315), 0x2F15 => array(0x531A), + 0x2F16 => array(0x5338), 0x2F17 => array(0x5341), 0x2F18 => array(0x535C), + 0x2F19 => array(0x5369), 0x2F1A => array(0x5382), 0x2F1B => array(0x53B6), + 0x2F1C => array(0x53C8), 0x2F1D => array(0x53E3), 0x2F1E => array(0x56D7), + 0x2F1F => array(0x571F), 0x2F20 => array(0x58EB), 0x2F21 => array(0x5902), + 0x2F22 => array(0x590A), 0x2F23 => array(0x5915), 0x2F24 => array(0x5927), + 0x2F25 => array(0x5973), 0x2F26 => array(0x5B50), 0x2F27 => array(0x5B80), + 0x2F28 => array(0x5BF8), 0x2F29 => array(0x5C0F), 0x2F2A => array(0x5C22), + 0x2F2B => array(0x5C38), 0x2F2C => array(0x5C6E), 0x2F2D => array(0x5C71), + 0x2F2E => array(0x5DDB), 0x2F2F => array(0x5DE5), 0x2F30 => array(0x5DF1), + 0x2F31 => array(0x5DFE), 0x2F32 => array(0x5E72), 0x2F33 => array(0x5E7A), + 0x2F34 => array(0x5E7F), 0x2F35 => array(0x5EF4), 0x2F36 => array(0x5EFE), + 0x2F37 => array(0x5F0B), 0x2F38 => array(0x5F13), 0x2F39 => array(0x5F50), + 0x2F3A => array(0x5F61), 0x2F3B => array(0x5F73), 0x2F3C => array(0x5FC3), + 0x2F3D => array(0x6208), 0x2F3E => array(0x6236), 0x2F3F => array(0x624B), + 0x2F40 => array(0x652F), 0x2F41 => array(0x6534), 0x2F42 => array(0x6587), + 0x2F43 => array(0x6597), 0x2F44 => array(0x65A4), 0x2F45 => array(0x65B9), + 0x2F46 => array(0x65E0), 0x2F47 => array(0x65E5), 0x2F48 => array(0x66F0), + 0x2F49 => array(0x6708), 0x2F4A => array(0x6728), 0x2F4B => array(0x6B20), + 0x2F4C => array(0x6B62), 0x2F4D => array(0x6B79), 0x2F4E => array(0x6BB3), + 0x2F4F => array(0x6BCB), 0x2F50 => array(0x6BD4), 0x2F51 => array(0x6BDB), + 0x2F52 => array(0x6C0F), 0x2F53 => array(0x6C14), 0x2F54 => array(0x6C34), + 0x2F55 => array(0x706B), 0x2F56 => array(0x722A), 0x2F57 => array(0x7236), + 0x2F58 => array(0x723B), 0x2F59 => array(0x723F), 0x2F5A => array(0x7247), + 0x2F5B => array(0x7259), 0x2F5C => array(0x725B), 0x2F5D => array(0x72AC), + 0x2F5E => array(0x7384), 0x2F5F => array(0x7389), 0x2F60 => array(0x74DC), + 0x2F61 => array(0x74E6), 0x2F62 => array(0x7518), 0x2F63 => array(0x751F), + 0x2F64 => array(0x7528), 0x2F65 => array(0x7530), 0x2F66 => array(0x758B), + 0x2F67 => array(0x7592), 0x2F68 => array(0x7676), 0x2F69 => array(0x767D), + 0x2F6A => array(0x76AE), 0x2F6B => array(0x76BF), 0x2F6C => array(0x76EE), + 0x2F6D => array(0x77DB), 0x2F6E => array(0x77E2), 0x2F6F => array(0x77F3), + 0x2F70 => array(0x793A), 0x2F71 => array(0x79B8), 0x2F72 => array(0x79BE), + 0x2F73 => array(0x7A74), 0x2F74 => array(0x7ACB), 0x2F75 => array(0x7AF9), + 0x2F76 => array(0x7C73), 0x2F77 => array(0x7CF8), 0x2F78 => array(0x7F36), + 0x2F79 => array(0x7F51), 0x2F7A => array(0x7F8A), 0x2F7B => array(0x7FBD), + 0x2F7C => array(0x8001), 0x2F7D => array(0x800C), 0x2F7E => array(0x8012), + 0x2F7F => array(0x8033), 0x2F80 => array(0x807F), 0x2F81 => array(0x8089), + 0x2F82 => array(0x81E3), 0x2F83 => array(0x81EA), 0x2F84 => array(0x81F3), + 0x2F85 => array(0x81FC), 0x2F86 => array(0x820C), 0x2F87 => array(0x821B), + 0x2F88 => array(0x821F), 0x2F89 => array(0x826E), 0x2F8A => array(0x8272), + 0x2F8B => array(0x8278), 0x2F8C => array(0x864D), 0x2F8D => array(0x866B), + 0x2F8E => array(0x8840), 0x2F8F => array(0x884C), 0x2F90 => array(0x8863), + 0x2F91 => array(0x897E), 0x2F92 => array(0x898B), 0x2F93 => array(0x89D2), + 0x2F94 => array(0x8A00), 0x2F95 => array(0x8C37), 0x2F96 => array(0x8C46), + 0x2F97 => array(0x8C55), 0x2F98 => array(0x8C78), 0x2F99 => array(0x8C9D), + 0x2F9A => array(0x8D64), 0x2F9B => array(0x8D70), 0x2F9C => array(0x8DB3), + 0x2F9D => array(0x8EAB), 0x2F9E => array(0x8ECA), 0x2F9F => array(0x8F9B), + 0x2FA0 => array(0x8FB0), 0x2FA1 => array(0x8FB5), 0x2FA2 => array(0x9091), + 0x2FA3 => array(0x9149), 0x2FA4 => array(0x91C6), 0x2FA5 => array(0x91CC), + 0x2FA6 => array(0x91D1), 0x2FA7 => array(0x9577), 0x2FA8 => array(0x9580), + 0x2FA9 => array(0x961C), 0x2FAA => array(0x96B6), 0x2FAB => array(0x96B9), + 0x2FAC => array(0x96E8), 0x2FAD => array(0x9751), 0x2FAE => array(0x975E), + 0x2FAF => array(0x9762), 0x2FB0 => array(0x9769), 0x2FB1 => array(0x97CB), + 0x2FB2 => array(0x97ED), 0x2FB3 => array(0x97F3), 0x2FB4 => array(0x9801), + 0x2FB5 => array(0x98A8), 0x2FB6 => array(0x98DB), 0x2FB7 => array(0x98DF), + 0x2FB8 => array(0x9996), 0x2FB9 => array(0x9999), 0x2FBA => array(0x99AC), + 0x2FBB => array(0x9AA8), 0x2FBC => array(0x9AD8), 0x2FBD => array(0x9ADF), + 0x2FBE => array(0x9B25), 0x2FBF => array(0x9B2F), 0x2FC0 => array(0x9B32), + 0x2FC1 => array(0x9B3C), 0x2FC2 => array(0x9B5A), 0x2FC3 => array(0x9CE5), + 0x2FC4 => array(0x9E75), 0x2FC5 => array(0x9E7F), 0x2FC6 => array(0x9EA5), + 0x2FC7 => array(0x9EBB), 0x2FC8 => array(0x9EC3), 0x2FC9 => array(0x9ECD), + 0x2FCA => array(0x9ED1), 0x2FCB => array(0x9EF9), 0x2FCC => array(0x9EFD), + 0x2FCD => array(0x9F0E), 0x2FCE => array(0x9F13), 0x2FCF => array(0x9F20), + 0x2FD0 => array(0x9F3B), 0x2FD1 => array(0x9F4A), 0x2FD2 => array(0x9F52), + 0x2FD3 => array(0x9F8D), 0x2FD4 => array(0x9F9C), 0x2FD5 => array(0x9FA0), + 0x3002 => array(0x2E), 0x3036 => array(0x3012), 0x3038 => array(0x5341), + 0x3039 => array(0x5344), 0x303A => array(0x5345), 0x309F => array(0x3088, 0x308A), + 0x30FF => array(0x30B3, 0x30C8), 0x3131 => array(0x1100), 0x3132 => array(0x1101), + 0x3133 => array(0x11AA), 0x3134 => array(0x1102), 0x3135 => array(0x11AC), + 0x3136 => array(0x11AD), 0x3137 => array(0x1103), 0x3138 => array(0x1104), + 0x3139 => array(0x1105), 0x313A => array(0x11B0), 0x313B => array(0x11B1), + 0x313C => array(0x11B2), 0x313D => array(0x11B3), 0x313E => array(0x11B4), + 0x313F => array(0x11B5), 0x3140 => array(0x111A), 0x3141 => array(0x1106), + 0x3142 => array(0x1107), 0x3143 => array(0x1108), 0x3144 => array(0x1121), + 0x3145 => array(0x1109), 0x3146 => array(0x110A), 0x3147 => array(0x110B), + 0x3148 => array(0x110C), 0x3149 => array(0x110D), 0x314A => array(0x110E), + 0x314B => array(0x110F), 0x314C => array(0x1110), 0x314D => array(0x1111), + 0x314E => array(0x1112), 0x314F => array(0x1161), 0x3150 => array(0x1162), + 0x3151 => array(0x1163), 0x3152 => array(0x1164), 0x3153 => array(0x1165), + 0x3154 => array(0x1166), 0x3155 => array(0x1167), 0x3156 => array(0x1168), + 0x3157 => array(0x1169), 0x3158 => array(0x116A), 0x3159 => array(0x116B), + 0x315A => array(0x116C), 0x315B => array(0x116D), 0x315C => array(0x116E), + 0x315D => array(0x116F), 0x315E => array(0x1170), 0x315F => array(0x1171), + 0x3160 => array(0x1172), 0x3161 => array(0x1173), 0x3162 => array(0x1174), + 0x3163 => array(0x1175), 0x3165 => array(0x1114), 0x3166 => array(0x1115), + 0x3167 => array(0x11C7), 0x3168 => array(0x11C8), 0x3169 => array(0x11CC), + 0x316A => array(0x11CE), 0x316B => array(0x11D3), 0x316C => array(0x11D7), + 0x316D => array(0x11D9), 0x316E => array(0x111C), 0x316F => array(0x11DD), + 0x3170 => array(0x11DF), 0x3171 => array(0x111D), 0x3172 => array(0x111E), + 0x3173 => array(0x1120), 0x3174 => array(0x1122), 0x3175 => array(0x1123), + 0x3176 => array(0x1127), 0x3177 => array(0x1129), 0x3178 => array(0x112B), + 0x3179 => array(0x112C), 0x317A => array(0x112D), 0x317B => array(0x112E), + 0x317C => array(0x112F), 0x317D => array(0x1132), 0x317E => array(0x1136), + 0x317F => array(0x1140), 0x3180 => array(0x1147), 0x3181 => array(0x114C), + 0x3182 => array(0x11F1), 0x3183 => array(0x11F2), 0x3184 => array(0x1157), + 0x3185 => array(0x1158), 0x3186 => array(0x1159), 0x3187 => array(0x1184), + 0x3188 => array(0x1185), 0x3189 => array(0x1188), 0x318A => array(0x1191), + 0x318B => array(0x1192), 0x318C => array(0x1194), 0x318D => array(0x119E), + 0x318E => array(0x11A1), 0x3192 => array(0x4E00), 0x3193 => array(0x4E8C), + 0x3194 => array(0x4E09), 0x3195 => array(0x56DB), 0x3196 => array(0x4E0A), + 0x3197 => array(0x4E2D), 0x3198 => array(0x4E0B), 0x3199 => array(0x7532), + 0x319A => array(0x4E59), 0x319B => array(0x4E19), 0x319C => array(0x4E01), + 0x319D => array(0x5929), 0x319E => array(0x5730), 0x319F => array(0x4EBA), + 0x3244 => array(0x554F), 0x3245 => array(0x5E7C), 0x3246 => array(0x6587), + 0x3247 => array(0x7B8F), 0x3250 => array(0x70, 0x74, 0x65), 0x3251 => array(0x32, 0x31), + 0x3252 => array(0x32, 0x32), 0x3253 => array(0x32, 0x33), 0x3254 => array(0x32, 0x34), + 0x3255 => array(0x32, 0x35), 0x3256 => array(0x32, 0x36), 0x3257 => array(0x32, 0x37), + 0x3258 => array(0x32, 0x38), 0x3259 => array(0x32, 0x39), 0x325A => array(0x33, 0x30), + 0x325B => array(0x33, 0x31), 0x325C => array(0x33, 0x32), 0x325D => array(0x33, 0x33), + 0x325E => array(0x33, 0x34), 0x325F => array(0x33, 0x35), 0x3260 => array(0x1100), + 0x3261 => array(0x1102), 0x3262 => array(0x1103), 0x3263 => array(0x1105), + 0x3264 => array(0x1106), 0x3265 => array(0x1107), 0x3266 => array(0x1109), + 0x3267 => array(0x110B), 0x3268 => array(0x110C), 0x3269 => array(0x110E), + 0x326A => array(0x110F), 0x326B => array(0x1110), 0x326C => array(0x1111), + 0x326D => array(0x1112), 0x326E => array(0xAC00), 0x326F => array(0xB098), + 0x3270 => array(0xB2E4), 0x3271 => array(0xB77C), 0x3272 => array(0xB9C8), + 0x3273 => array(0xBC14), 0x3274 => array(0xC0AC), 0x3275 => array(0xC544), + 0x3276 => array(0xC790), 0x3277 => array(0xCC28), 0x3278 => array(0xCE74), + 0x3279 => array(0xD0C0), 0x327A => array(0xD30C), 0x327B => array(0xD558), + 0x327C => array(0xCC38, 0xACE0), 0x327D => array(0xC8FC, 0xC758), 0x327E => array(0xC6B0), + 0x3280 => array(0x4E00), 0x3281 => array(0x4E8C), 0x3282 => array(0x4E09), + 0x3283 => array(0x56DB), 0x3284 => array(0x4E94), 0x3285 => array(0x516D), + 0x3286 => array(0x4E03), 0x3287 => array(0x516B), 0x3288 => array(0x4E5D), + 0x3289 => array(0x5341), 0x328A => array(0x6708), 0x328B => array(0x706B), + 0x328C => array(0x6C34), 0x328D => array(0x6728), 0x328E => array(0x91D1), + 0x328F => array(0x571F), 0x3290 => array(0x65E5), 0x3291 => array(0x682A), + 0x3292 => array(0x6709), 0x3293 => array(0x793E), 0x3294 => array(0x540D), + 0x3295 => array(0x7279), 0x3296 => array(0x8CA1), 0x3297 => array(0x795D), + 0x3298 => array(0x52B4), 0x3299 => array(0x79D8), 0x329A => array(0x7537), + 0x329B => array(0x5973), 0x329C => array(0x9069), 0x329D => array(0x512A), + 0x329E => array(0x5370), 0x329F => array(0x6CE8), 0x32A0 => array(0x9805), + 0x32A1 => array(0x4F11), 0x32A2 => array(0x5199), 0x32A3 => array(0x6B63), + 0x32A4 => array(0x4E0A), 0x32A5 => array(0x4E2D), 0x32A6 => array(0x4E0B), + 0x32A7 => array(0x5DE6), 0x32A8 => array(0x53F3), 0x32A9 => array(0x533B), + 0x32AA => array(0x5B97), 0x32AB => array(0x5B66), 0x32AC => array(0x76E3), + 0x32AD => array(0x4F01), 0x32AE => array(0x8CC7), 0x32AF => array(0x5354), + 0x32B0 => array(0x591C), 0x32B1 => array(0x33, 0x36), 0x32B2 => array(0x33, 0x37), + 0x32B3 => array(0x33, 0x38), 0x32B4 => array(0x33, 0x39), 0x32B5 => array(0x34, 0x30), + 0x32B6 => array(0x34, 0x31), 0x32B7 => array(0x34, 0x32), 0x32B8 => array(0x34, 0x33), + 0x32B9 => array(0x34, 0x34), 0x32BA => array(0x34, 0x35), 0x32BB => array(0x34, 0x36), + 0x32BC => array(0x34, 0x37), 0x32BD => array(0x34, 0x38), 0x32BE => array(0x34, 0x39), + 0x32BF => array(0x35, 0x30), 0x32C0 => array(0x31, 0x6708), 0x32C1 => array(0x32, 0x6708), + 0x32C2 => array(0x33, 0x6708), 0x32C3 => array(0x34, 0x6708), 0x32C4 => array(0x35, 0x6708), + 0x32C5 => array(0x36, 0x6708), 0x32C6 => array(0x37, 0x6708), 0x32C7 => array(0x38, 0x6708), + 0x32C8 => array(0x39, 0x6708), 0x32C9 => array(0x31, 0x30, 0x6708), 0x32CA => array(0x31, 0x31, 0x6708), + 0x32CB => array(0x31, 0x32, 0x6708), 0x32CC => array(0x68, 0x67), 0x32CD => array(0x65, 0x72, 0x67), + 0x32CE => array(0x65, 0x76), 0x32CF => array(0x6C, 0x74, 0x64), 0x32D0 => array(0x30A2), + 0x32D1 => array(0x30A4), 0x32D2 => array(0x30A6), 0x32D3 => array(0x30A8), + 0x32D4 => array(0x30AA), 0x32D5 => array(0x30AB), 0x32D6 => array(0x30AD), + 0x32D7 => array(0x30AF), 0x32D8 => array(0x30B1), 0x32D9 => array(0x30B3), + 0x32DA => array(0x30B5), 0x32DB => array(0x30B7), 0x32DC => array(0x30B9), + 0x32DD => array(0x30BB), 0x32DE => array(0x30BD), 0x32DF => array(0x30BF), + 0x32E0 => array(0x30C1), 0x32E1 => array(0x30C4), 0x32E2 => array(0x30C6), + 0x32E3 => array(0x30C8), 0x32E4 => array(0x30CA), 0x32E5 => array(0x30CB), + 0x32E6 => array(0x30CC), 0x32E7 => array(0x30CD), 0x32E8 => array(0x30CE), + 0x32E9 => array(0x30CF), 0x32EA => array(0x30D2), 0x32EB => array(0x30D5), + 0x32EC => array(0x30D8), 0x32ED => array(0x30DB), 0x32EE => array(0x30DE), + 0x32EF => array(0x30DF), 0x32F0 => array(0x30E0), 0x32F1 => array(0x30E1), + 0x32F2 => array(0x30E2), 0x32F3 => array(0x30E4), 0x32F4 => array(0x30E6), + 0x32F5 => array(0x30E8), 0x32F6 => array(0x30E9), 0x32F7 => array(0x30EA), + 0x32F8 => array(0x30EB), 0x32F9 => array(0x30EC), 0x32FA => array(0x30ED), + 0x32FB => array(0x30EF), 0x32FC => array(0x30F0), 0x32FD => array(0x30F1), + 0x32FE => array(0x30F2), 0x3300 => array(0x30A2, 0x30D1, 0x30FC, 0x30C8), 0x3301 => array(0x30A2, 0x30EB, 0x30D5, 0x30A1), + 0x3302 => array(0x30A2, 0x30F3, 0x30DA, 0x30A2), 0x3303 => array(0x30A2, 0x30FC, 0x30EB), 0x3304 => array(0x30A4, 0x30CB, 0x30F3, 0x30B0), + 0x3305 => array(0x30A4, 0x30F3, 0x30C1), 0x3306 => array(0x30A6, 0x30A9, 0x30F3), 0x3307 => array(0x30A8, 0x30B9, 0x30AF, 0x30FC, 0x30C9), + 0x3308 => array(0x30A8, 0x30FC, 0x30AB, 0x30FC), 0x3309 => array(0x30AA, 0x30F3, 0x30B9), 0x330A => array(0x30AA, 0x30FC, 0x30E0), + 0x330B => array(0x30AB, 0x30A4, 0x30EA), 0x330C => array(0x30AB, 0x30E9, 0x30C3, 0x30C8), 0x330D => array(0x30AB, 0x30ED, 0x30EA, 0x30FC), + 0x330E => array(0x30AC, 0x30ED, 0x30F3), 0x330F => array(0x30AC, 0x30F3, 0x30DE), 0x3310 => array(0x30AE, 0x30AC), + 0x3311 => array(0x30AE, 0x30CB, 0x30FC), 0x3312 => array(0x30AD, 0x30E5, 0x30EA, 0x30FC), 0x3313 => array(0x30AE, 0x30EB, 0x30C0, 0x30FC), + 0x3314 => array(0x30AD, 0x30ED), 0x3315 => array(0x30AD, 0x30ED, 0x30B0, 0x30E9, 0x30E0), 0x3316 => array(0x30AD, 0x30ED, 0x30E1, 0x30FC, 0x30C8, 0x30EB), + 0x3317 => array(0x30AD, 0x30ED, 0x30EF, 0x30C3, 0x30C8), 0x3318 => array(0x30B0, 0x30E9, 0x30E0), 0x3319 => array(0x30B0, 0x30E9, 0x30E0, 0x30C8, 0x30F3), + 0x331A => array(0x30AF, 0x30EB, 0x30BC, 0x30A4, 0x30ED), 0x331B => array(0x30AF, 0x30ED, 0x30FC, 0x30CD), 0x331C => array(0x30B1, 0x30FC, 0x30B9), + 0x331D => array(0x30B3, 0x30EB, 0x30CA), 0x331E => array(0x30B3, 0x30FC, 0x30DD), 0x331F => array(0x30B5, 0x30A4, 0x30AF, 0x30EB), + 0x3320 => array(0x30B5, 0x30F3, 0x30C1, 0x30FC, 0x30E0), 0x3321 => array(0x30B7, 0x30EA, 0x30F3, 0x30B0), 0x3322 => array(0x30BB, 0x30F3, 0x30C1), + 0x3323 => array(0x30BB, 0x30F3, 0x30C8), 0x3324 => array(0x30C0, 0x30FC, 0x30B9), 0x3325 => array(0x30C7, 0x30B7), + 0x3326 => array(0x30C9, 0x30EB), 0x3327 => array(0x30C8, 0x30F3), 0x3328 => array(0x30CA, 0x30CE), + 0x3329 => array(0x30CE, 0x30C3, 0x30C8), 0x332A => array(0x30CF, 0x30A4, 0x30C4), 0x332B => array(0x30D1, 0x30FC, 0x30BB, 0x30F3, 0x30C8), + 0x332C => array(0x30D1, 0x30FC, 0x30C4), 0x332D => array(0x30D0, 0x30FC, 0x30EC, 0x30EB), 0x332E => array(0x30D4, 0x30A2, 0x30B9, 0x30C8, 0x30EB), + 0x332F => array(0x30D4, 0x30AF, 0x30EB), 0x3330 => array(0x30D4, 0x30B3), 0x3331 => array(0x30D3, 0x30EB), + 0x3332 => array(0x30D5, 0x30A1, 0x30E9, 0x30C3, 0x30C9), 0x3333 => array(0x30D5, 0x30A3, 0x30FC, 0x30C8), 0x3334 => array(0x30D6, 0x30C3, 0x30B7, 0x30A7, 0x30EB), + 0x3335 => array(0x30D5, 0x30E9, 0x30F3), 0x3336 => array(0x30D8, 0x30AF, 0x30BF, 0x30FC, 0x30EB), 0x3337 => array(0x30DA, 0x30BD), + 0x3338 => array(0x30DA, 0x30CB, 0x30D2), 0x3339 => array(0x30D8, 0x30EB, 0x30C4), 0x333A => array(0x30DA, 0x30F3, 0x30B9), + 0x333B => array(0x30DA, 0x30FC, 0x30B8), 0x333C => array(0x30D9, 0x30FC, 0x30BF), 0x333D => array(0x30DD, 0x30A4, 0x30F3, 0x30C8), + 0x333E => array(0x30DC, 0x30EB, 0x30C8), 0x333F => array(0x30DB, 0x30F3), 0x3340 => array(0x30DD, 0x30F3, 0x30C9), + 0x3341 => array(0x30DB, 0x30FC, 0x30EB), 0x3342 => array(0x30DB, 0x30FC, 0x30F3), 0x3343 => array(0x30DE, 0x30A4, 0x30AF, 0x30ED), + 0x3344 => array(0x30DE, 0x30A4, 0x30EB), 0x3345 => array(0x30DE, 0x30C3, 0x30CF), 0x3346 => array(0x30DE, 0x30EB, 0x30AF), + 0x3347 => array(0x30DE, 0x30F3, 0x30B7, 0x30E7, 0x30F3), 0x3348 => array(0x30DF, 0x30AF, 0x30ED, 0x30F3), 0x3349 => array(0x30DF, 0x30EA), + 0x334A => array(0x30DF, 0x30EA, 0x30D0, 0x30FC, 0x30EB), 0x334B => array(0x30E1, 0x30AC), 0x334C => array(0x30E1, 0x30AC, 0x30C8, 0x30F3), + 0x334D => array(0x30E1, 0x30FC, 0x30C8, 0x30EB), 0x334E => array(0x30E4, 0x30FC, 0x30C9), 0x334F => array(0x30E4, 0x30FC, 0x30EB), + 0x3350 => array(0x30E6, 0x30A2, 0x30F3), 0x3351 => array(0x30EA, 0x30C3, 0x30C8, 0x30EB), 0x3352 => array(0x30EA, 0x30E9), + 0x3353 => array(0x30EB, 0x30D4, 0x30FC), 0x3354 => array(0x30EB, 0x30FC, 0x30D6, 0x30EB), 0x3355 => array(0x30EC, 0x30E0), + 0x3356 => array(0x30EC, 0x30F3, 0x30C8, 0x30B2, 0x30F3), 0x3357 => array(0x30EF, 0x30C3, 0x30C8), 0x3358 => array(0x30, 0x70B9), + 0x3359 => array(0x31, 0x70B9), 0x335A => array(0x32, 0x70B9), 0x335B => array(0x33, 0x70B9), + 0x335C => array(0x34, 0x70B9), 0x335D => array(0x35, 0x70B9), 0x335E => array(0x36, 0x70B9), + 0x335F => array(0x37, 0x70B9), 0x3360 => array(0x38, 0x70B9), 0x3361 => array(0x39, 0x70B9), + 0x3362 => array(0x31, 0x30, 0x70B9), 0x3363 => array(0x31, 0x31, 0x70B9), 0x3364 => array(0x31, 0x32, 0x70B9), + 0x3365 => array(0x31, 0x33, 0x70B9), 0x3366 => array(0x31, 0x34, 0x70B9), 0x3367 => array(0x31, 0x35, 0x70B9), + 0x3368 => array(0x31, 0x36, 0x70B9), 0x3369 => array(0x31, 0x37, 0x70B9), 0x336A => array(0x31, 0x38, 0x70B9), + 0x336B => array(0x31, 0x39, 0x70B9), 0x336C => array(0x32, 0x30, 0x70B9), 0x336D => array(0x32, 0x31, 0x70B9), + 0x336E => array(0x32, 0x32, 0x70B9), 0x336F => array(0x32, 0x33, 0x70B9), 0x3370 => array(0x32, 0x34, 0x70B9), + 0x3371 => array(0x68, 0x70, 0x61), 0x3372 => array(0x64, 0x61), 0x3373 => array(0x61, 0x75), + 0x3374 => array(0x62, 0x61, 0x72), 0x3375 => array(0x6F, 0x76), 0x3376 => array(0x70, 0x63), + 0x3377 => array(0x64, 0x6D), 0x3378 => array(0x64, 0x6D, 0x32), 0x3379 => array(0x64, 0x6D, 0x33), + 0x337A => array(0x69, 0x75), 0x337B => array(0x5E73, 0x6210), 0x337C => array(0x662D, 0x548C), + 0x337D => array(0x5927, 0x6B63), 0x337E => array(0x660E, 0x6CBB), 0x337F => array(0x682A, 0x5F0F, 0x4F1A, 0x793E), + 0x3380 => array(0x70, 0x61), 0x3381 => array(0x6E, 0x61), 0x3382 => array(0x3BC, 0x61), + 0x3383 => array(0x6D, 0x61), 0x3384 => array(0x6B, 0x61), 0x3385 => array(0x6B, 0x62), + 0x3386 => array(0x6D, 0x62), 0x3387 => array(0x67, 0x62), 0x3388 => array(0x63, 0x61, 0x6C), + 0x3389 => array(0x6B, 0x63, 0x61, 0x6C), 0x338A => array(0x70, 0x66), 0x338B => array(0x6E, 0x66), + 0x338C => array(0x3BC, 0x66), 0x338D => array(0x3BC, 0x67), 0x338E => array(0x6D, 0x67), + 0x338F => array(0x6B, 0x67), 0x3390 => array(0x68, 0x7A), 0x3391 => array(0x6B, 0x68, 0x7A), + 0x3392 => array(0x6D, 0x68, 0x7A), 0x3393 => array(0x67, 0x68, 0x7A), 0x3394 => array(0x74, 0x68, 0x7A), + 0x3395 => array(0x3BC, 0x6C), 0x3396 => array(0x6D, 0x6C), 0x3397 => array(0x64, 0x6C), + 0x3398 => array(0x6B, 0x6C), 0x3399 => array(0x66, 0x6D), 0x339A => array(0x6E, 0x6D), + 0x339B => array(0x3BC, 0x6D), 0x339C => array(0x6D, 0x6D), 0x339D => array(0x63, 0x6D), + 0x339E => array(0x6B, 0x6D), 0x339F => array(0x6D, 0x6D, 0x32), 0x33A0 => array(0x63, 0x6D, 0x32), + 0x33A1 => array(0x6D, 0x32), 0x33A2 => array(0x6B, 0x6D, 0x32), 0x33A3 => array(0x6D, 0x6D, 0x33), + 0x33A4 => array(0x63, 0x6D, 0x33), 0x33A5 => array(0x6D, 0x33), 0x33A6 => array(0x6B, 0x6D, 0x33), + 0x33A7 => array(0x6D, 0x2215, 0x73), 0x33A8 => array(0x6D, 0x2215, 0x73, 0x32), 0x33A9 => array(0x70, 0x61), + 0x33AA => array(0x6B, 0x70, 0x61), 0x33AB => array(0x6D, 0x70, 0x61), 0x33AC => array(0x67, 0x70, 0x61), + 0x33AD => array(0x72, 0x61, 0x64), 0x33AE => array(0x72, 0x61, 0x64, 0x2215, 0x73), 0x33AF => array(0x72, 0x61, 0x64, 0x2215, 0x73, 0x32), + 0x33B0 => array(0x70, 0x73), 0x33B1 => array(0x6E, 0x73), 0x33B2 => array(0x3BC, 0x73), + 0x33B3 => array(0x6D, 0x73), 0x33B4 => array(0x70, 0x76), 0x33B5 => array(0x6E, 0x76), + 0x33B6 => array(0x3BC, 0x76), 0x33B7 => array(0x6D, 0x76), 0x33B8 => array(0x6B, 0x76), + 0x33B9 => array(0x6D, 0x76), 0x33BA => array(0x70, 0x77), 0x33BB => array(0x6E, 0x77), + 0x33BC => array(0x3BC, 0x77), 0x33BD => array(0x6D, 0x77), 0x33BE => array(0x6B, 0x77), + 0x33BF => array(0x6D, 0x77), 0x33C0 => array(0x6B, 0x3C9), 0x33C1 => array(0x6D, 0x3C9), + 0x33C3 => array(0x62, 0x71), 0x33C4 => array(0x63, 0x63), 0x33C5 => array(0x63, 0x64), + 0x33C6 => array(0x63, 0x2215, 0x6B, 0x67), 0x33C8 => array(0x64, 0x62), 0x33C9 => array(0x67, 0x79), + 0x33CA => array(0x68, 0x61), 0x33CB => array(0x68, 0x70), 0x33CC => array(0x69, 0x6E), + 0x33CD => array(0x6B, 0x6B), 0x33CE => array(0x6B, 0x6D), 0x33CF => array(0x6B, 0x74), + 0x33D0 => array(0x6C, 0x6D), 0x33D1 => array(0x6C, 0x6E), 0x33D2 => array(0x6C, 0x6F, 0x67), + 0x33D3 => array(0x6C, 0x78), 0x33D4 => array(0x6D, 0x62), 0x33D5 => array(0x6D, 0x69, 0x6C), + 0x33D6 => array(0x6D, 0x6F, 0x6C), 0x33D7 => array(0x70, 0x68), 0x33D9 => array(0x70, 0x70, 0x6D), + 0x33DA => array(0x70, 0x72), 0x33DB => array(0x73, 0x72), 0x33DC => array(0x73, 0x76), + 0x33DD => array(0x77, 0x62), 0x33DE => array(0x76, 0x2215, 0x6D), 0x33DF => array(0x61, 0x2215, 0x6D), + 0x33E0 => array(0x31, 0x65E5), 0x33E1 => array(0x32, 0x65E5), 0x33E2 => array(0x33, 0x65E5), + 0x33E3 => array(0x34, 0x65E5), 0x33E4 => array(0x35, 0x65E5), 0x33E5 => array(0x36, 0x65E5), + 0x33E6 => array(0x37, 0x65E5), 0x33E7 => array(0x38, 0x65E5), 0x33E8 => array(0x39, 0x65E5), + 0x33E9 => array(0x31, 0x30, 0x65E5), 0x33EA => array(0x31, 0x31, 0x65E5), 0x33EB => array(0x31, 0x32, 0x65E5), + 0x33EC => array(0x31, 0x33, 0x65E5), 0x33ED => array(0x31, 0x34, 0x65E5), 0x33EE => array(0x31, 0x35, 0x65E5), + 0x33EF => array(0x31, 0x36, 0x65E5), 0x33F0 => array(0x31, 0x37, 0x65E5), 0x33F1 => array(0x31, 0x38, 0x65E5), + 0x33F2 => array(0x31, 0x39, 0x65E5), 0x33F3 => array(0x32, 0x30, 0x65E5), 0x33F4 => array(0x32, 0x31, 0x65E5), + 0x33F5 => array(0x32, 0x32, 0x65E5), 0x33F6 => array(0x32, 0x33, 0x65E5), 0x33F7 => array(0x32, 0x34, 0x65E5), + 0x33F8 => array(0x32, 0x35, 0x65E5), 0x33F9 => array(0x32, 0x36, 0x65E5), 0x33FA => array(0x32, 0x37, 0x65E5), + 0x33FB => array(0x32, 0x38, 0x65E5), 0x33FC => array(0x32, 0x39, 0x65E5), 0x33FD => array(0x33, 0x30, 0x65E5), + 0x33FE => array(0x33, 0x31, 0x65E5), 0x33FF => array(0x67, 0x61, 0x6C), 0xA640 => array(0xA641), + 0xA642 => array(0xA643), 0xA644 => array(0xA645), 0xA646 => array(0xA647), + 0xA648 => array(0xA649), 0xA64A => array(0xA64B), 0xA64C => array(0xA64D), + 0xA64E => array(0xA64F), 0xA650 => array(0xA651), 0xA652 => array(0xA653), + 0xA654 => array(0xA655), 0xA656 => array(0xA657), 0xA658 => array(0xA659), + 0xA65A => array(0xA65B), 0xA65C => array(0xA65D), 0xA65E => array(0xA65F), + 0xA660 => array(0xA661), 0xA662 => array(0xA663), 0xA664 => array(0xA665), + 0xA666 => array(0xA667), 0xA668 => array(0xA669), 0xA66A => array(0xA66B), + 0xA66C => array(0xA66D), 0xA680 => array(0xA681), 0xA682 => array(0xA683), + 0xA684 => array(0xA685), 0xA686 => array(0xA687), 0xA688 => array(0xA689), + 0xA68A => array(0xA68B), 0xA68C => array(0xA68D), 0xA68E => array(0xA68F), + 0xA690 => array(0xA691), 0xA692 => array(0xA693), 0xA694 => array(0xA695), + 0xA696 => array(0xA697), 0xA698 => array(0xA699), 0xA69A => array(0xA69B), + 0xA69C => array(0x44A), 0xA69D => array(0x44C), 0xA722 => array(0xA723), + 0xA724 => array(0xA725), 0xA726 => array(0xA727), 0xA728 => array(0xA729), + 0xA72A => array(0xA72B), 0xA72C => array(0xA72D), 0xA72E => array(0xA72F), + 0xA732 => array(0xA733), 0xA734 => array(0xA735), 0xA736 => array(0xA737), + 0xA738 => array(0xA739), 0xA73A => array(0xA73B), 0xA73C => array(0xA73D), + 0xA73E => array(0xA73F), 0xA740 => array(0xA741), 0xA742 => array(0xA743), + 0xA744 => array(0xA745), 0xA746 => array(0xA747), 0xA748 => array(0xA749), + 0xA74A => array(0xA74B), 0xA74C => array(0xA74D), 0xA74E => array(0xA74F), + 0xA750 => array(0xA751), 0xA752 => array(0xA753), 0xA754 => array(0xA755), + 0xA756 => array(0xA757), 0xA758 => array(0xA759), 0xA75A => array(0xA75B), + 0xA75C => array(0xA75D), 0xA75E => array(0xA75F), 0xA760 => array(0xA761), + 0xA762 => array(0xA763), 0xA764 => array(0xA765), 0xA766 => array(0xA767), + 0xA768 => array(0xA769), 0xA76A => array(0xA76B), 0xA76C => array(0xA76D), + 0xA76E => array(0xA76F), 0xA770 => array(0xA76F), 0xA779 => array(0xA77A), + 0xA77B => array(0xA77C), 0xA77D => array(0x1D79), 0xA77E => array(0xA77F), + 0xA780 => array(0xA781), 0xA782 => array(0xA783), 0xA784 => array(0xA785), + 0xA786 => array(0xA787), 0xA78B => array(0xA78C), 0xA78D => array(0x265), + 0xA790 => array(0xA791), 0xA792 => array(0xA793), 0xA796 => array(0xA797), + 0xA798 => array(0xA799), 0xA79A => array(0xA79B), 0xA79C => array(0xA79D), + 0xA79E => array(0xA79F), 0xA7A0 => array(0xA7A1), 0xA7A2 => array(0xA7A3), + 0xA7A4 => array(0xA7A5), 0xA7A6 => array(0xA7A7), 0xA7A8 => array(0xA7A9), + 0xA7AA => array(0x266), 0xA7AB => array(0x25C), 0xA7AC => array(0x261), + 0xA7AD => array(0x26C), 0xA7B0 => array(0x29E), 0xA7B1 => array(0x287), + 0xA7F8 => array(0x127), 0xA7F9 => array(0x153), 0xAB5C => array(0xA727), + 0xAB5D => array(0xAB37), 0xAB5E => array(0x26B), 0xAB5F => array(0xAB52), + 0xF900 => array(0x8C48), 0xF901 => array(0x66F4), 0xF902 => array(0x8ECA), + 0xF903 => array(0x8CC8), 0xF904 => array(0x6ED1), 0xF905 => array(0x4E32), + 0xF906 => array(0x53E5), 0xF907 => array(0x9F9C), 0xF908 => array(0x9F9C), + 0xF909 => array(0x5951), 0xF90A => array(0x91D1), 0xF90B => array(0x5587), + 0xF90C => array(0x5948), 0xF90D => array(0x61F6), 0xF90E => array(0x7669), + 0xF90F => array(0x7F85), 0xF910 => array(0x863F), 0xF911 => array(0x87BA), + 0xF912 => array(0x88F8), 0xF913 => array(0x908F), 0xF914 => array(0x6A02), + 0xF915 => array(0x6D1B), 0xF916 => array(0x70D9), 0xF917 => array(0x73DE), + 0xF918 => array(0x843D), 0xF919 => array(0x916A), 0xF91A => array(0x99F1), + 0xF91B => array(0x4E82), 0xF91C => array(0x5375), 0xF91D => array(0x6B04), + 0xF91E => array(0x721B), 0xF91F => array(0x862D), 0xF920 => array(0x9E1E), + 0xF921 => array(0x5D50), 0xF922 => array(0x6FEB), 0xF923 => array(0x85CD), + 0xF924 => array(0x8964), 0xF925 => array(0x62C9), 0xF926 => array(0x81D8), + 0xF927 => array(0x881F), 0xF928 => array(0x5ECA), 0xF929 => array(0x6717), + 0xF92A => array(0x6D6A), 0xF92B => array(0x72FC), 0xF92C => array(0x90CE), + 0xF92D => array(0x4F86), 0xF92E => array(0x51B7), 0xF92F => array(0x52DE), + 0xF930 => array(0x64C4), 0xF931 => array(0x6AD3), 0xF932 => array(0x7210), + 0xF933 => array(0x76E7), 0xF934 => array(0x8001), 0xF935 => array(0x8606), + 0xF936 => array(0x865C), 0xF937 => array(0x8DEF), 0xF938 => array(0x9732), + 0xF939 => array(0x9B6F), 0xF93A => array(0x9DFA), 0xF93B => array(0x788C), + 0xF93C => array(0x797F), 0xF93D => array(0x7DA0), 0xF93E => array(0x83C9), + 0xF93F => array(0x9304), 0xF940 => array(0x9E7F), 0xF941 => array(0x8AD6), + 0xF942 => array(0x58DF), 0xF943 => array(0x5F04), 0xF944 => array(0x7C60), + 0xF945 => array(0x807E), 0xF946 => array(0x7262), 0xF947 => array(0x78CA), + 0xF948 => array(0x8CC2), 0xF949 => array(0x96F7), 0xF94A => array(0x58D8), + 0xF94B => array(0x5C62), 0xF94C => array(0x6A13), 0xF94D => array(0x6DDA), + 0xF94E => array(0x6F0F), 0xF94F => array(0x7D2F), 0xF950 => array(0x7E37), + 0xF951 => array(0x964B), 0xF952 => array(0x52D2), 0xF953 => array(0x808B), + 0xF954 => array(0x51DC), 0xF955 => array(0x51CC), 0xF956 => array(0x7A1C), + 0xF957 => array(0x7DBE), 0xF958 => array(0x83F1), 0xF959 => array(0x9675), + 0xF95A => array(0x8B80), 0xF95B => array(0x62CF), 0xF95C => array(0x6A02), + 0xF95D => array(0x8AFE), 0xF95E => array(0x4E39), 0xF95F => array(0x5BE7), + 0xF960 => array(0x6012), 0xF961 => array(0x7387), 0xF962 => array(0x7570), + 0xF963 => array(0x5317), 0xF964 => array(0x78FB), 0xF965 => array(0x4FBF), + 0xF966 => array(0x5FA9), 0xF967 => array(0x4E0D), 0xF968 => array(0x6CCC), + 0xF969 => array(0x6578), 0xF96A => array(0x7D22), 0xF96B => array(0x53C3), + 0xF96C => array(0x585E), 0xF96D => array(0x7701), 0xF96E => array(0x8449), + 0xF96F => array(0x8AAA), 0xF970 => array(0x6BBA), 0xF971 => array(0x8FB0), + 0xF972 => array(0x6C88), 0xF973 => array(0x62FE), 0xF974 => array(0x82E5), + 0xF975 => array(0x63A0), 0xF976 => array(0x7565), 0xF977 => array(0x4EAE), + 0xF978 => array(0x5169), 0xF979 => array(0x51C9), 0xF97A => array(0x6881), + 0xF97B => array(0x7CE7), 0xF97C => array(0x826F), 0xF97D => array(0x8AD2), + 0xF97E => array(0x91CF), 0xF97F => array(0x52F5), 0xF980 => array(0x5442), + 0xF981 => array(0x5973), 0xF982 => array(0x5EEC), 0xF983 => array(0x65C5), + 0xF984 => array(0x6FFE), 0xF985 => array(0x792A), 0xF986 => array(0x95AD), + 0xF987 => array(0x9A6A), 0xF988 => array(0x9E97), 0xF989 => array(0x9ECE), + 0xF98A => array(0x529B), 0xF98B => array(0x66C6), 0xF98C => array(0x6B77), + 0xF98D => array(0x8F62), 0xF98E => array(0x5E74), 0xF98F => array(0x6190), + 0xF990 => array(0x6200), 0xF991 => array(0x649A), 0xF992 => array(0x6F23), + 0xF993 => array(0x7149), 0xF994 => array(0x7489), 0xF995 => array(0x79CA), + 0xF996 => array(0x7DF4), 0xF997 => array(0x806F), 0xF998 => array(0x8F26), + 0xF999 => array(0x84EE), 0xF99A => array(0x9023), 0xF99B => array(0x934A), + 0xF99C => array(0x5217), 0xF99D => array(0x52A3), 0xF99E => array(0x54BD), + 0xF99F => array(0x70C8), 0xF9A0 => array(0x88C2), 0xF9A1 => array(0x8AAA), + 0xF9A2 => array(0x5EC9), 0xF9A3 => array(0x5FF5), 0xF9A4 => array(0x637B), + 0xF9A5 => array(0x6BAE), 0xF9A6 => array(0x7C3E), 0xF9A7 => array(0x7375), + 0xF9A8 => array(0x4EE4), 0xF9A9 => array(0x56F9), 0xF9AA => array(0x5BE7), + 0xF9AB => array(0x5DBA), 0xF9AC => array(0x601C), 0xF9AD => array(0x73B2), + 0xF9AE => array(0x7469), 0xF9AF => array(0x7F9A), 0xF9B0 => array(0x8046), + 0xF9B1 => array(0x9234), 0xF9B2 => array(0x96F6), 0xF9B3 => array(0x9748), + 0xF9B4 => array(0x9818), 0xF9B5 => array(0x4F8B), 0xF9B6 => array(0x79AE), + 0xF9B7 => array(0x91B4), 0xF9B8 => array(0x96B8), 0xF9B9 => array(0x60E1), + 0xF9BA => array(0x4E86), 0xF9BB => array(0x50DA), 0xF9BC => array(0x5BEE), + 0xF9BD => array(0x5C3F), 0xF9BE => array(0x6599), 0xF9BF => array(0x6A02), + 0xF9C0 => array(0x71CE), 0xF9C1 => array(0x7642), 0xF9C2 => array(0x84FC), + 0xF9C3 => array(0x907C), 0xF9C4 => array(0x9F8D), 0xF9C5 => array(0x6688), + 0xF9C6 => array(0x962E), 0xF9C7 => array(0x5289), 0xF9C8 => array(0x677B), + 0xF9C9 => array(0x67F3), 0xF9CA => array(0x6D41), 0xF9CB => array(0x6E9C), + 0xF9CC => array(0x7409), 0xF9CD => array(0x7559), 0xF9CE => array(0x786B), + 0xF9CF => array(0x7D10), 0xF9D0 => array(0x985E), 0xF9D1 => array(0x516D), + 0xF9D2 => array(0x622E), 0xF9D3 => array(0x9678), 0xF9D4 => array(0x502B), + 0xF9D5 => array(0x5D19), 0xF9D6 => array(0x6DEA), 0xF9D7 => array(0x8F2A), + 0xF9D8 => array(0x5F8B), 0xF9D9 => array(0x6144), 0xF9DA => array(0x6817), + 0xF9DB => array(0x7387), 0xF9DC => array(0x9686), 0xF9DD => array(0x5229), + 0xF9DE => array(0x540F), 0xF9DF => array(0x5C65), 0xF9E0 => array(0x6613), + 0xF9E1 => array(0x674E), 0xF9E2 => array(0x68A8), 0xF9E3 => array(0x6CE5), + 0xF9E4 => array(0x7406), 0xF9E5 => array(0x75E2), 0xF9E6 => array(0x7F79), + 0xF9E7 => array(0x88CF), 0xF9E8 => array(0x88E1), 0xF9E9 => array(0x91CC), + 0xF9EA => array(0x96E2), 0xF9EB => array(0x533F), 0xF9EC => array(0x6EBA), + 0xF9ED => array(0x541D), 0xF9EE => array(0x71D0), 0xF9EF => array(0x7498), + 0xF9F0 => array(0x85FA), 0xF9F1 => array(0x96A3), 0xF9F2 => array(0x9C57), + 0xF9F3 => array(0x9E9F), 0xF9F4 => array(0x6797), 0xF9F5 => array(0x6DCB), + 0xF9F6 => array(0x81E8), 0xF9F7 => array(0x7ACB), 0xF9F8 => array(0x7B20), + 0xF9F9 => array(0x7C92), 0xF9FA => array(0x72C0), 0xF9FB => array(0x7099), + 0xF9FC => array(0x8B58), 0xF9FD => array(0x4EC0), 0xF9FE => array(0x8336), + 0xF9FF => array(0x523A), 0xFA00 => array(0x5207), 0xFA01 => array(0x5EA6), + 0xFA02 => array(0x62D3), 0xFA03 => array(0x7CD6), 0xFA04 => array(0x5B85), + 0xFA05 => array(0x6D1E), 0xFA06 => array(0x66B4), 0xFA07 => array(0x8F3B), + 0xFA08 => array(0x884C), 0xFA09 => array(0x964D), 0xFA0A => array(0x898B), + 0xFA0B => array(0x5ED3), 0xFA0C => array(0x5140), 0xFA0D => array(0x55C0), + 0xFA10 => array(0x585A), 0xFA12 => array(0x6674), 0xFA15 => array(0x51DE), + 0xFA16 => array(0x732A), 0xFA17 => array(0x76CA), 0xFA18 => array(0x793C), + 0xFA19 => array(0x795E), 0xFA1A => array(0x7965), 0xFA1B => array(0x798F), + 0xFA1C => array(0x9756), 0xFA1D => array(0x7CBE), 0xFA1E => array(0x7FBD), + 0xFA20 => array(0x8612), 0xFA22 => array(0x8AF8), 0xFA25 => array(0x9038), + 0xFA26 => array(0x90FD), 0xFA2A => array(0x98EF), 0xFA2B => array(0x98FC), + 0xFA2C => array(0x9928), 0xFA2D => array(0x9DB4), 0xFA2E => array(0x90DE), + 0xFA2F => array(0x96B7), 0xFA30 => array(0x4FAE), 0xFA31 => array(0x50E7), + 0xFA32 => array(0x514D), 0xFA33 => array(0x52C9), 0xFA34 => array(0x52E4), + 0xFA35 => array(0x5351), 0xFA36 => array(0x559D), 0xFA37 => array(0x5606), + 0xFA38 => array(0x5668), 0xFA39 => array(0x5840), 0xFA3A => array(0x58A8), + 0xFA3B => array(0x5C64), 0xFA3C => array(0x5C6E), 0xFA3D => array(0x6094), + 0xFA3E => array(0x6168), 0xFA3F => array(0x618E), 0xFA40 => array(0x61F2), + 0xFA41 => array(0x654F), 0xFA42 => array(0x65E2), 0xFA43 => array(0x6691), + 0xFA44 => array(0x6885), 0xFA45 => array(0x6D77), 0xFA46 => array(0x6E1A), + 0xFA47 => array(0x6F22), 0xFA48 => array(0x716E), 0xFA49 => array(0x722B), + 0xFA4A => array(0x7422), 0xFA4B => array(0x7891), 0xFA4C => array(0x793E), + 0xFA4D => array(0x7949), 0xFA4E => array(0x7948), 0xFA4F => array(0x7950), + 0xFA50 => array(0x7956), 0xFA51 => array(0x795D), 0xFA52 => array(0x798D), + 0xFA53 => array(0x798E), 0xFA54 => array(0x7A40), 0xFA55 => array(0x7A81), + 0xFA56 => array(0x7BC0), 0xFA57 => array(0x7DF4), 0xFA58 => array(0x7E09), + 0xFA59 => array(0x7E41), 0xFA5A => array(0x7F72), 0xFA5B => array(0x8005), + 0xFA5C => array(0x81ED), 0xFA5D => array(0x8279), 0xFA5E => array(0x8279), + 0xFA5F => array(0x8457), 0xFA60 => array(0x8910), 0xFA61 => array(0x8996), + 0xFA62 => array(0x8B01), 0xFA63 => array(0x8B39), 0xFA64 => array(0x8CD3), + 0xFA65 => array(0x8D08), 0xFA66 => array(0x8FB6), 0xFA67 => array(0x9038), + 0xFA68 => array(0x96E3), 0xFA69 => array(0x97FF), 0xFA6A => array(0x983B), + 0xFA6B => array(0x6075), 0xFA6C => array(0x242EE), 0xFA6D => array(0x8218), + 0xFA70 => array(0x4E26), 0xFA71 => array(0x51B5), 0xFA72 => array(0x5168), + 0xFA73 => array(0x4F80), 0xFA74 => array(0x5145), 0xFA75 => array(0x5180), + 0xFA76 => array(0x52C7), 0xFA77 => array(0x52FA), 0xFA78 => array(0x559D), + 0xFA79 => array(0x5555), 0xFA7A => array(0x5599), 0xFA7B => array(0x55E2), + 0xFA7C => array(0x585A), 0xFA7D => array(0x58B3), 0xFA7E => array(0x5944), + 0xFA7F => array(0x5954), 0xFA80 => array(0x5A62), 0xFA81 => array(0x5B28), + 0xFA82 => array(0x5ED2), 0xFA83 => array(0x5ED9), 0xFA84 => array(0x5F69), + 0xFA85 => array(0x5FAD), 0xFA86 => array(0x60D8), 0xFA87 => array(0x614E), + 0xFA88 => array(0x6108), 0xFA89 => array(0x618E), 0xFA8A => array(0x6160), + 0xFA8B => array(0x61F2), 0xFA8C => array(0x6234), 0xFA8D => array(0x63C4), + 0xFA8E => array(0x641C), 0xFA8F => array(0x6452), 0xFA90 => array(0x6556), + 0xFA91 => array(0x6674), 0xFA92 => array(0x6717), 0xFA93 => array(0x671B), + 0xFA94 => array(0x6756), 0xFA95 => array(0x6B79), 0xFA96 => array(0x6BBA), + 0xFA97 => array(0x6D41), 0xFA98 => array(0x6EDB), 0xFA99 => array(0x6ECB), + 0xFA9A => array(0x6F22), 0xFA9B => array(0x701E), 0xFA9C => array(0x716E), + 0xFA9D => array(0x77A7), 0xFA9E => array(0x7235), 0xFA9F => array(0x72AF), + 0xFAA0 => array(0x732A), 0xFAA1 => array(0x7471), 0xFAA2 => array(0x7506), + 0xFAA3 => array(0x753B), 0xFAA4 => array(0x761D), 0xFAA5 => array(0x761F), + 0xFAA6 => array(0x76CA), 0xFAA7 => array(0x76DB), 0xFAA8 => array(0x76F4), + 0xFAA9 => array(0x774A), 0xFAAA => array(0x7740), 0xFAAB => array(0x78CC), + 0xFAAC => array(0x7AB1), 0xFAAD => array(0x7BC0), 0xFAAE => array(0x7C7B), + 0xFAAF => array(0x7D5B), 0xFAB0 => array(0x7DF4), 0xFAB1 => array(0x7F3E), + 0xFAB2 => array(0x8005), 0xFAB3 => array(0x8352), 0xFAB4 => array(0x83EF), + 0xFAB5 => array(0x8779), 0xFAB6 => array(0x8941), 0xFAB7 => array(0x8986), + 0xFAB8 => array(0x8996), 0xFAB9 => array(0x8ABF), 0xFABA => array(0x8AF8), + 0xFABB => array(0x8ACB), 0xFABC => array(0x8B01), 0xFABD => array(0x8AFE), + 0xFABE => array(0x8AED), 0xFABF => array(0x8B39), 0xFAC0 => array(0x8B8A), + 0xFAC1 => array(0x8D08), 0xFAC2 => array(0x8F38), 0xFAC3 => array(0x9072), + 0xFAC4 => array(0x9199), 0xFAC5 => array(0x9276), 0xFAC6 => array(0x967C), + 0xFAC7 => array(0x96E3), 0xFAC8 => array(0x9756), 0xFAC9 => array(0x97DB), + 0xFACA => array(0x97FF), 0xFACB => array(0x980B), 0xFACC => array(0x983B), + 0xFACD => array(0x9B12), 0xFACE => array(0x9F9C), 0xFACF => array(0x2284A), + 0xFAD0 => array(0x22844), 0xFAD1 => array(0x233D5), 0xFAD2 => array(0x3B9D), + 0xFAD3 => array(0x4018), 0xFAD4 => array(0x4039), 0xFAD5 => array(0x25249), + 0xFAD6 => array(0x25CD0), 0xFAD7 => array(0x27ED3), 0xFAD8 => array(0x9F43), + 0xFAD9 => array(0x9F8E), 0xFB00 => array(0x66, 0x66), 0xFB01 => array(0x66, 0x69), + 0xFB02 => array(0x66, 0x6C), 0xFB03 => array(0x66, 0x66, 0x69), 0xFB04 => array(0x66, 0x66, 0x6C), + 0xFB05 => array(0x73, 0x74), 0xFB06 => array(0x73, 0x74), 0xFB13 => array(0x574, 0x576), + 0xFB14 => array(0x574, 0x565), 0xFB15 => array(0x574, 0x56B), 0xFB16 => array(0x57E, 0x576), + 0xFB17 => array(0x574, 0x56D), 0xFB1D => array(0x5D9, 0x5B4), 0xFB1F => array(0x5F2, 0x5B7), + 0xFB20 => array(0x5E2), 0xFB21 => array(0x5D0), 0xFB22 => array(0x5D3), + 0xFB23 => array(0x5D4), 0xFB24 => array(0x5DB), 0xFB25 => array(0x5DC), + 0xFB26 => array(0x5DD), 0xFB27 => array(0x5E8), 0xFB28 => array(0x5EA), + 0xFB2A => array(0x5E9, 0x5C1), 0xFB2B => array(0x5E9, 0x5C2), 0xFB2C => array(0x5E9, 0x5BC, 0x5C1), + 0xFB2D => array(0x5E9, 0x5BC, 0x5C2), 0xFB2E => array(0x5D0, 0x5B7), 0xFB2F => array(0x5D0, 0x5B8), + 0xFB30 => array(0x5D0, 0x5BC), 0xFB31 => array(0x5D1, 0x5BC), 0xFB32 => array(0x5D2, 0x5BC), + 0xFB33 => array(0x5D3, 0x5BC), 0xFB34 => array(0x5D4, 0x5BC), 0xFB35 => array(0x5D5, 0x5BC), + 0xFB36 => array(0x5D6, 0x5BC), 0xFB38 => array(0x5D8, 0x5BC), 0xFB39 => array(0x5D9, 0x5BC), + 0xFB3A => array(0x5DA, 0x5BC), 0xFB3B => array(0x5DB, 0x5BC), 0xFB3C => array(0x5DC, 0x5BC), + 0xFB3E => array(0x5DE, 0x5BC), 0xFB40 => array(0x5E0, 0x5BC), 0xFB41 => array(0x5E1, 0x5BC), + 0xFB43 => array(0x5E3, 0x5BC), 0xFB44 => array(0x5E4, 0x5BC), 0xFB46 => array(0x5E6, 0x5BC), + 0xFB47 => array(0x5E7, 0x5BC), 0xFB48 => array(0x5E8, 0x5BC), 0xFB49 => array(0x5E9, 0x5BC), + 0xFB4A => array(0x5EA, 0x5BC), 0xFB4B => array(0x5D5, 0x5B9), 0xFB4C => array(0x5D1, 0x5BF), + 0xFB4D => array(0x5DB, 0x5BF), 0xFB4E => array(0x5E4, 0x5BF), 0xFB4F => array(0x5D0, 0x5DC), + 0xFB50 => array(0x671), 0xFB51 => array(0x671), 0xFB52 => array(0x67B), + 0xFB53 => array(0x67B), 0xFB54 => array(0x67B), 0xFB55 => array(0x67B), + 0xFB56 => array(0x67E), 0xFB57 => array(0x67E), 0xFB58 => array(0x67E), + 0xFB59 => array(0x67E), 0xFB5A => array(0x680), 0xFB5B => array(0x680), + 0xFB5C => array(0x680), 0xFB5D => array(0x680), 0xFB5E => array(0x67A), + 0xFB5F => array(0x67A), 0xFB60 => array(0x67A), 0xFB61 => array(0x67A), + 0xFB62 => array(0x67F), 0xFB63 => array(0x67F), 0xFB64 => array(0x67F), + 0xFB65 => array(0x67F), 0xFB66 => array(0x679), 0xFB67 => array(0x679), + 0xFB68 => array(0x679), 0xFB69 => array(0x679), 0xFB6A => array(0x6A4), + 0xFB6B => array(0x6A4), 0xFB6C => array(0x6A4), 0xFB6D => array(0x6A4), + 0xFB6E => array(0x6A6), 0xFB6F => array(0x6A6), 0xFB70 => array(0x6A6), + 0xFB71 => array(0x6A6), 0xFB72 => array(0x684), 0xFB73 => array(0x684), + 0xFB74 => array(0x684), 0xFB75 => array(0x684), 0xFB76 => array(0x683), + 0xFB77 => array(0x683), 0xFB78 => array(0x683), 0xFB79 => array(0x683), + 0xFB7A => array(0x686), 0xFB7B => array(0x686), 0xFB7C => array(0x686), + 0xFB7D => array(0x686), 0xFB7E => array(0x687), 0xFB7F => array(0x687), + 0xFB80 => array(0x687), 0xFB81 => array(0x687), 0xFB82 => array(0x68D), + 0xFB83 => array(0x68D), 0xFB84 => array(0x68C), 0xFB85 => array(0x68C), + 0xFB86 => array(0x68E), 0xFB87 => array(0x68E), 0xFB88 => array(0x688), + 0xFB89 => array(0x688), 0xFB8A => array(0x698), 0xFB8B => array(0x698), + 0xFB8C => array(0x691), 0xFB8D => array(0x691), 0xFB8E => array(0x6A9), + 0xFB8F => array(0x6A9), 0xFB90 => array(0x6A9), 0xFB91 => array(0x6A9), + 0xFB92 => array(0x6AF), 0xFB93 => array(0x6AF), 0xFB94 => array(0x6AF), + 0xFB95 => array(0x6AF), 0xFB96 => array(0x6B3), 0xFB97 => array(0x6B3), + 0xFB98 => array(0x6B3), 0xFB99 => array(0x6B3), 0xFB9A => array(0x6B1), + 0xFB9B => array(0x6B1), 0xFB9C => array(0x6B1), 0xFB9D => array(0x6B1), + 0xFB9E => array(0x6BA), 0xFB9F => array(0x6BA), 0xFBA0 => array(0x6BB), + 0xFBA1 => array(0x6BB), 0xFBA2 => array(0x6BB), 0xFBA3 => array(0x6BB), + 0xFBA4 => array(0x6C0), 0xFBA5 => array(0x6C0), 0xFBA6 => array(0x6C1), + 0xFBA7 => array(0x6C1), 0xFBA8 => array(0x6C1), 0xFBA9 => array(0x6C1), + 0xFBAA => array(0x6BE), 0xFBAB => array(0x6BE), 0xFBAC => array(0x6BE), + 0xFBAD => array(0x6BE), 0xFBAE => array(0x6D2), 0xFBAF => array(0x6D2), + 0xFBB0 => array(0x6D3), 0xFBB1 => array(0x6D3), 0xFBD3 => array(0x6AD), + 0xFBD4 => array(0x6AD), 0xFBD5 => array(0x6AD), 0xFBD6 => array(0x6AD), + 0xFBD7 => array(0x6C7), 0xFBD8 => array(0x6C7), 0xFBD9 => array(0x6C6), + 0xFBDA => array(0x6C6), 0xFBDB => array(0x6C8), 0xFBDC => array(0x6C8), + 0xFBDD => array(0x6C7, 0x674), 0xFBDE => array(0x6CB), 0xFBDF => array(0x6CB), + 0xFBE0 => array(0x6C5), 0xFBE1 => array(0x6C5), 0xFBE2 => array(0x6C9), + 0xFBE3 => array(0x6C9), 0xFBE4 => array(0x6D0), 0xFBE5 => array(0x6D0), + 0xFBE6 => array(0x6D0), 0xFBE7 => array(0x6D0), 0xFBE8 => array(0x649), + 0xFBE9 => array(0x649), 0xFBEA => array(0x626, 0x627), 0xFBEB => array(0x626, 0x627), + 0xFBEC => array(0x626, 0x6D5), 0xFBED => array(0x626, 0x6D5), 0xFBEE => array(0x626, 0x648), + 0xFBEF => array(0x626, 0x648), 0xFBF0 => array(0x626, 0x6C7), 0xFBF1 => array(0x626, 0x6C7), + 0xFBF2 => array(0x626, 0x6C6), 0xFBF3 => array(0x626, 0x6C6), 0xFBF4 => array(0x626, 0x6C8), + 0xFBF5 => array(0x626, 0x6C8), 0xFBF6 => array(0x626, 0x6D0), 0xFBF7 => array(0x626, 0x6D0), + 0xFBF8 => array(0x626, 0x6D0), 0xFBF9 => array(0x626, 0x649), 0xFBFA => array(0x626, 0x649), + 0xFBFB => array(0x626, 0x649), 0xFBFC => array(0x6CC), 0xFBFD => array(0x6CC), + 0xFBFE => array(0x6CC), 0xFBFF => array(0x6CC), 0xFC00 => array(0x626, 0x62C), + 0xFC01 => array(0x626, 0x62D), 0xFC02 => array(0x626, 0x645), 0xFC03 => array(0x626, 0x649), + 0xFC04 => array(0x626, 0x64A), 0xFC05 => array(0x628, 0x62C), 0xFC06 => array(0x628, 0x62D), + 0xFC07 => array(0x628, 0x62E), 0xFC08 => array(0x628, 0x645), 0xFC09 => array(0x628, 0x649), + 0xFC0A => array(0x628, 0x64A), 0xFC0B => array(0x62A, 0x62C), 0xFC0C => array(0x62A, 0x62D), + 0xFC0D => array(0x62A, 0x62E), 0xFC0E => array(0x62A, 0x645), 0xFC0F => array(0x62A, 0x649), + 0xFC10 => array(0x62A, 0x64A), 0xFC11 => array(0x62B, 0x62C), 0xFC12 => array(0x62B, 0x645), + 0xFC13 => array(0x62B, 0x649), 0xFC14 => array(0x62B, 0x64A), 0xFC15 => array(0x62C, 0x62D), + 0xFC16 => array(0x62C, 0x645), 0xFC17 => array(0x62D, 0x62C), 0xFC18 => array(0x62D, 0x645), + 0xFC19 => array(0x62E, 0x62C), 0xFC1A => array(0x62E, 0x62D), 0xFC1B => array(0x62E, 0x645), + 0xFC1C => array(0x633, 0x62C), 0xFC1D => array(0x633, 0x62D), 0xFC1E => array(0x633, 0x62E), + 0xFC1F => array(0x633, 0x645), 0xFC20 => array(0x635, 0x62D), 0xFC21 => array(0x635, 0x645), + 0xFC22 => array(0x636, 0x62C), 0xFC23 => array(0x636, 0x62D), 0xFC24 => array(0x636, 0x62E), + 0xFC25 => array(0x636, 0x645), 0xFC26 => array(0x637, 0x62D), 0xFC27 => array(0x637, 0x645), + 0xFC28 => array(0x638, 0x645), 0xFC29 => array(0x639, 0x62C), 0xFC2A => array(0x639, 0x645), + 0xFC2B => array(0x63A, 0x62C), 0xFC2C => array(0x63A, 0x645), 0xFC2D => array(0x641, 0x62C), + 0xFC2E => array(0x641, 0x62D), 0xFC2F => array(0x641, 0x62E), 0xFC30 => array(0x641, 0x645), + 0xFC31 => array(0x641, 0x649), 0xFC32 => array(0x641, 0x64A), 0xFC33 => array(0x642, 0x62D), + 0xFC34 => array(0x642, 0x645), 0xFC35 => array(0x642, 0x649), 0xFC36 => array(0x642, 0x64A), + 0xFC37 => array(0x643, 0x627), 0xFC38 => array(0x643, 0x62C), 0xFC39 => array(0x643, 0x62D), + 0xFC3A => array(0x643, 0x62E), 0xFC3B => array(0x643, 0x644), 0xFC3C => array(0x643, 0x645), + 0xFC3D => array(0x643, 0x649), 0xFC3E => array(0x643, 0x64A), 0xFC3F => array(0x644, 0x62C), + 0xFC40 => array(0x644, 0x62D), 0xFC41 => array(0x644, 0x62E), 0xFC42 => array(0x644, 0x645), + 0xFC43 => array(0x644, 0x649), 0xFC44 => array(0x644, 0x64A), 0xFC45 => array(0x645, 0x62C), + 0xFC46 => array(0x645, 0x62D), 0xFC47 => array(0x645, 0x62E), 0xFC48 => array(0x645, 0x645), + 0xFC49 => array(0x645, 0x649), 0xFC4A => array(0x645, 0x64A), 0xFC4B => array(0x646, 0x62C), + 0xFC4C => array(0x646, 0x62D), 0xFC4D => array(0x646, 0x62E), 0xFC4E => array(0x646, 0x645), + 0xFC4F => array(0x646, 0x649), 0xFC50 => array(0x646, 0x64A), 0xFC51 => array(0x647, 0x62C), + 0xFC52 => array(0x647, 0x645), 0xFC53 => array(0x647, 0x649), 0xFC54 => array(0x647, 0x64A), + 0xFC55 => array(0x64A, 0x62C), 0xFC56 => array(0x64A, 0x62D), 0xFC57 => array(0x64A, 0x62E), + 0xFC58 => array(0x64A, 0x645), 0xFC59 => array(0x64A, 0x649), 0xFC5A => array(0x64A, 0x64A), + 0xFC5B => array(0x630, 0x670), 0xFC5C => array(0x631, 0x670), 0xFC5D => array(0x649, 0x670), + 0xFC64 => array(0x626, 0x631), 0xFC65 => array(0x626, 0x632), 0xFC66 => array(0x626, 0x645), + 0xFC67 => array(0x626, 0x646), 0xFC68 => array(0x626, 0x649), 0xFC69 => array(0x626, 0x64A), + 0xFC6A => array(0x628, 0x631), 0xFC6B => array(0x628, 0x632), 0xFC6C => array(0x628, 0x645), + 0xFC6D => array(0x628, 0x646), 0xFC6E => array(0x628, 0x649), 0xFC6F => array(0x628, 0x64A), + 0xFC70 => array(0x62A, 0x631), 0xFC71 => array(0x62A, 0x632), 0xFC72 => array(0x62A, 0x645), + 0xFC73 => array(0x62A, 0x646), 0xFC74 => array(0x62A, 0x649), 0xFC75 => array(0x62A, 0x64A), + 0xFC76 => array(0x62B, 0x631), 0xFC77 => array(0x62B, 0x632), 0xFC78 => array(0x62B, 0x645), + 0xFC79 => array(0x62B, 0x646), 0xFC7A => array(0x62B, 0x649), 0xFC7B => array(0x62B, 0x64A), + 0xFC7C => array(0x641, 0x649), 0xFC7D => array(0x641, 0x64A), 0xFC7E => array(0x642, 0x649), + 0xFC7F => array(0x642, 0x64A), 0xFC80 => array(0x643, 0x627), 0xFC81 => array(0x643, 0x644), + 0xFC82 => array(0x643, 0x645), 0xFC83 => array(0x643, 0x649), 0xFC84 => array(0x643, 0x64A), + 0xFC85 => array(0x644, 0x645), 0xFC86 => array(0x644, 0x649), 0xFC87 => array(0x644, 0x64A), + 0xFC88 => array(0x645, 0x627), 0xFC89 => array(0x645, 0x645), 0xFC8A => array(0x646, 0x631), + 0xFC8B => array(0x646, 0x632), 0xFC8C => array(0x646, 0x645), 0xFC8D => array(0x646, 0x646), + 0xFC8E => array(0x646, 0x649), 0xFC8F => array(0x646, 0x64A), 0xFC90 => array(0x649, 0x670), + 0xFC91 => array(0x64A, 0x631), 0xFC92 => array(0x64A, 0x632), 0xFC93 => array(0x64A, 0x645), + 0xFC94 => array(0x64A, 0x646), 0xFC95 => array(0x64A, 0x649), 0xFC96 => array(0x64A, 0x64A), + 0xFC97 => array(0x626, 0x62C), 0xFC98 => array(0x626, 0x62D), 0xFC99 => array(0x626, 0x62E), + 0xFC9A => array(0x626, 0x645), 0xFC9B => array(0x626, 0x647), 0xFC9C => array(0x628, 0x62C), + 0xFC9D => array(0x628, 0x62D), 0xFC9E => array(0x628, 0x62E), 0xFC9F => array(0x628, 0x645), + 0xFCA0 => array(0x628, 0x647), 0xFCA1 => array(0x62A, 0x62C), 0xFCA2 => array(0x62A, 0x62D), + 0xFCA3 => array(0x62A, 0x62E), 0xFCA4 => array(0x62A, 0x645), 0xFCA5 => array(0x62A, 0x647), + 0xFCA6 => array(0x62B, 0x645), 0xFCA7 => array(0x62C, 0x62D), 0xFCA8 => array(0x62C, 0x645), + 0xFCA9 => array(0x62D, 0x62C), 0xFCAA => array(0x62D, 0x645), 0xFCAB => array(0x62E, 0x62C), + 0xFCAC => array(0x62E, 0x645), 0xFCAD => array(0x633, 0x62C), 0xFCAE => array(0x633, 0x62D), + 0xFCAF => array(0x633, 0x62E), 0xFCB0 => array(0x633, 0x645), 0xFCB1 => array(0x635, 0x62D), + 0xFCB2 => array(0x635, 0x62E), 0xFCB3 => array(0x635, 0x645), 0xFCB4 => array(0x636, 0x62C), + 0xFCB5 => array(0x636, 0x62D), 0xFCB6 => array(0x636, 0x62E), 0xFCB7 => array(0x636, 0x645), + 0xFCB8 => array(0x637, 0x62D), 0xFCB9 => array(0x638, 0x645), 0xFCBA => array(0x639, 0x62C), + 0xFCBB => array(0x639, 0x645), 0xFCBC => array(0x63A, 0x62C), 0xFCBD => array(0x63A, 0x645), + 0xFCBE => array(0x641, 0x62C), 0xFCBF => array(0x641, 0x62D), 0xFCC0 => array(0x641, 0x62E), + 0xFCC1 => array(0x641, 0x645), 0xFCC2 => array(0x642, 0x62D), 0xFCC3 => array(0x642, 0x645), + 0xFCC4 => array(0x643, 0x62C), 0xFCC5 => array(0x643, 0x62D), 0xFCC6 => array(0x643, 0x62E), + 0xFCC7 => array(0x643, 0x644), 0xFCC8 => array(0x643, 0x645), 0xFCC9 => array(0x644, 0x62C), + 0xFCCA => array(0x644, 0x62D), 0xFCCB => array(0x644, 0x62E), 0xFCCC => array(0x644, 0x645), + 0xFCCD => array(0x644, 0x647), 0xFCCE => array(0x645, 0x62C), 0xFCCF => array(0x645, 0x62D), + 0xFCD0 => array(0x645, 0x62E), 0xFCD1 => array(0x645, 0x645), 0xFCD2 => array(0x646, 0x62C), + 0xFCD3 => array(0x646, 0x62D), 0xFCD4 => array(0x646, 0x62E), 0xFCD5 => array(0x646, 0x645), + 0xFCD6 => array(0x646, 0x647), 0xFCD7 => array(0x647, 0x62C), 0xFCD8 => array(0x647, 0x645), + 0xFCD9 => array(0x647, 0x670), 0xFCDA => array(0x64A, 0x62C), 0xFCDB => array(0x64A, 0x62D), + 0xFCDC => array(0x64A, 0x62E), 0xFCDD => array(0x64A, 0x645), 0xFCDE => array(0x64A, 0x647), + 0xFCDF => array(0x626, 0x645), 0xFCE0 => array(0x626, 0x647), 0xFCE1 => array(0x628, 0x645), + 0xFCE2 => array(0x628, 0x647), 0xFCE3 => array(0x62A, 0x645), 0xFCE4 => array(0x62A, 0x647), + 0xFCE5 => array(0x62B, 0x645), 0xFCE6 => array(0x62B, 0x647), 0xFCE7 => array(0x633, 0x645), + 0xFCE8 => array(0x633, 0x647), 0xFCE9 => array(0x634, 0x645), 0xFCEA => array(0x634, 0x647), + 0xFCEB => array(0x643, 0x644), 0xFCEC => array(0x643, 0x645), 0xFCED => array(0x644, 0x645), + 0xFCEE => array(0x646, 0x645), 0xFCEF => array(0x646, 0x647), 0xFCF0 => array(0x64A, 0x645), + 0xFCF1 => array(0x64A, 0x647), 0xFCF2 => array(0x640, 0x64E, 0x651), 0xFCF3 => array(0x640, 0x64F, 0x651), + 0xFCF4 => array(0x640, 0x650, 0x651), 0xFCF5 => array(0x637, 0x649), 0xFCF6 => array(0x637, 0x64A), + 0xFCF7 => array(0x639, 0x649), 0xFCF8 => array(0x639, 0x64A), 0xFCF9 => array(0x63A, 0x649), + 0xFCFA => array(0x63A, 0x64A), 0xFCFB => array(0x633, 0x649), 0xFCFC => array(0x633, 0x64A), + 0xFCFD => array(0x634, 0x649), 0xFCFE => array(0x634, 0x64A), 0xFCFF => array(0x62D, 0x649), + 0xFD00 => array(0x62D, 0x64A), 0xFD01 => array(0x62C, 0x649), 0xFD02 => array(0x62C, 0x64A), + 0xFD03 => array(0x62E, 0x649), 0xFD04 => array(0x62E, 0x64A), 0xFD05 => array(0x635, 0x649), + 0xFD06 => array(0x635, 0x64A), 0xFD07 => array(0x636, 0x649), 0xFD08 => array(0x636, 0x64A), + 0xFD09 => array(0x634, 0x62C), 0xFD0A => array(0x634, 0x62D), 0xFD0B => array(0x634, 0x62E), + 0xFD0C => array(0x634, 0x645), 0xFD0D => array(0x634, 0x631), 0xFD0E => array(0x633, 0x631), + 0xFD0F => array(0x635, 0x631), 0xFD10 => array(0x636, 0x631), 0xFD11 => array(0x637, 0x649), + 0xFD12 => array(0x637, 0x64A), 0xFD13 => array(0x639, 0x649), 0xFD14 => array(0x639, 0x64A), + 0xFD15 => array(0x63A, 0x649), 0xFD16 => array(0x63A, 0x64A), 0xFD17 => array(0x633, 0x649), + 0xFD18 => array(0x633, 0x64A), 0xFD19 => array(0x634, 0x649), 0xFD1A => array(0x634, 0x64A), + 0xFD1B => array(0x62D, 0x649), 0xFD1C => array(0x62D, 0x64A), 0xFD1D => array(0x62C, 0x649), + 0xFD1E => array(0x62C, 0x64A), 0xFD1F => array(0x62E, 0x649), 0xFD20 => array(0x62E, 0x64A), + 0xFD21 => array(0x635, 0x649), 0xFD22 => array(0x635, 0x64A), 0xFD23 => array(0x636, 0x649), + 0xFD24 => array(0x636, 0x64A), 0xFD25 => array(0x634, 0x62C), 0xFD26 => array(0x634, 0x62D), + 0xFD27 => array(0x634, 0x62E), 0xFD28 => array(0x634, 0x645), 0xFD29 => array(0x634, 0x631), + 0xFD2A => array(0x633, 0x631), 0xFD2B => array(0x635, 0x631), 0xFD2C => array(0x636, 0x631), + 0xFD2D => array(0x634, 0x62C), 0xFD2E => array(0x634, 0x62D), 0xFD2F => array(0x634, 0x62E), + 0xFD30 => array(0x634, 0x645), 0xFD31 => array(0x633, 0x647), 0xFD32 => array(0x634, 0x647), + 0xFD33 => array(0x637, 0x645), 0xFD34 => array(0x633, 0x62C), 0xFD35 => array(0x633, 0x62D), + 0xFD36 => array(0x633, 0x62E), 0xFD37 => array(0x634, 0x62C), 0xFD38 => array(0x634, 0x62D), + 0xFD39 => array(0x634, 0x62E), 0xFD3A => array(0x637, 0x645), 0xFD3B => array(0x638, 0x645), + 0xFD3C => array(0x627, 0x64B), 0xFD3D => array(0x627, 0x64B), 0xFD50 => array(0x62A, 0x62C, 0x645), + 0xFD51 => array(0x62A, 0x62D, 0x62C), 0xFD52 => array(0x62A, 0x62D, 0x62C), 0xFD53 => array(0x62A, 0x62D, 0x645), + 0xFD54 => array(0x62A, 0x62E, 0x645), 0xFD55 => array(0x62A, 0x645, 0x62C), 0xFD56 => array(0x62A, 0x645, 0x62D), + 0xFD57 => array(0x62A, 0x645, 0x62E), 0xFD58 => array(0x62C, 0x645, 0x62D), 0xFD59 => array(0x62C, 0x645, 0x62D), + 0xFD5A => array(0x62D, 0x645, 0x64A), 0xFD5B => array(0x62D, 0x645, 0x649), 0xFD5C => array(0x633, 0x62D, 0x62C), + 0xFD5D => array(0x633, 0x62C, 0x62D), 0xFD5E => array(0x633, 0x62C, 0x649), 0xFD5F => array(0x633, 0x645, 0x62D), + 0xFD60 => array(0x633, 0x645, 0x62D), 0xFD61 => array(0x633, 0x645, 0x62C), 0xFD62 => array(0x633, 0x645, 0x645), + 0xFD63 => array(0x633, 0x645, 0x645), 0xFD64 => array(0x635, 0x62D, 0x62D), 0xFD65 => array(0x635, 0x62D, 0x62D), + 0xFD66 => array(0x635, 0x645, 0x645), 0xFD67 => array(0x634, 0x62D, 0x645), 0xFD68 => array(0x634, 0x62D, 0x645), + 0xFD69 => array(0x634, 0x62C, 0x64A), 0xFD6A => array(0x634, 0x645, 0x62E), 0xFD6B => array(0x634, 0x645, 0x62E), + 0xFD6C => array(0x634, 0x645, 0x645), 0xFD6D => array(0x634, 0x645, 0x645), 0xFD6E => array(0x636, 0x62D, 0x649), + 0xFD6F => array(0x636, 0x62E, 0x645), 0xFD70 => array(0x636, 0x62E, 0x645), 0xFD71 => array(0x637, 0x645, 0x62D), + 0xFD72 => array(0x637, 0x645, 0x62D), 0xFD73 => array(0x637, 0x645, 0x645), 0xFD74 => array(0x637, 0x645, 0x64A), + 0xFD75 => array(0x639, 0x62C, 0x645), 0xFD76 => array(0x639, 0x645, 0x645), 0xFD77 => array(0x639, 0x645, 0x645), + 0xFD78 => array(0x639, 0x645, 0x649), 0xFD79 => array(0x63A, 0x645, 0x645), 0xFD7A => array(0x63A, 0x645, 0x64A), + 0xFD7B => array(0x63A, 0x645, 0x649), 0xFD7C => array(0x641, 0x62E, 0x645), 0xFD7D => array(0x641, 0x62E, 0x645), + 0xFD7E => array(0x642, 0x645, 0x62D), 0xFD7F => array(0x642, 0x645, 0x645), 0xFD80 => array(0x644, 0x62D, 0x645), + 0xFD81 => array(0x644, 0x62D, 0x64A), 0xFD82 => array(0x644, 0x62D, 0x649), 0xFD83 => array(0x644, 0x62C, 0x62C), + 0xFD84 => array(0x644, 0x62C, 0x62C), 0xFD85 => array(0x644, 0x62E, 0x645), 0xFD86 => array(0x644, 0x62E, 0x645), + 0xFD87 => array(0x644, 0x645, 0x62D), 0xFD88 => array(0x644, 0x645, 0x62D), 0xFD89 => array(0x645, 0x62D, 0x62C), + 0xFD8A => array(0x645, 0x62D, 0x645), 0xFD8B => array(0x645, 0x62D, 0x64A), 0xFD8C => array(0x645, 0x62C, 0x62D), + 0xFD8D => array(0x645, 0x62C, 0x645), 0xFD8E => array(0x645, 0x62E, 0x62C), 0xFD8F => array(0x645, 0x62E, 0x645), + 0xFD92 => array(0x645, 0x62C, 0x62E), 0xFD93 => array(0x647, 0x645, 0x62C), 0xFD94 => array(0x647, 0x645, 0x645), + 0xFD95 => array(0x646, 0x62D, 0x645), 0xFD96 => array(0x646, 0x62D, 0x649), 0xFD97 => array(0x646, 0x62C, 0x645), + 0xFD98 => array(0x646, 0x62C, 0x645), 0xFD99 => array(0x646, 0x62C, 0x649), 0xFD9A => array(0x646, 0x645, 0x64A), + 0xFD9B => array(0x646, 0x645, 0x649), 0xFD9C => array(0x64A, 0x645, 0x645), 0xFD9D => array(0x64A, 0x645, 0x645), + 0xFD9E => array(0x628, 0x62E, 0x64A), 0xFD9F => array(0x62A, 0x62C, 0x64A), 0xFDA0 => array(0x62A, 0x62C, 0x649), + 0xFDA1 => array(0x62A, 0x62E, 0x64A), 0xFDA2 => array(0x62A, 0x62E, 0x649), 0xFDA3 => array(0x62A, 0x645, 0x64A), + 0xFDA4 => array(0x62A, 0x645, 0x649), 0xFDA5 => array(0x62C, 0x645, 0x64A), 0xFDA6 => array(0x62C, 0x62D, 0x649), + 0xFDA7 => array(0x62C, 0x645, 0x649), 0xFDA8 => array(0x633, 0x62E, 0x649), 0xFDA9 => array(0x635, 0x62D, 0x64A), + 0xFDAA => array(0x634, 0x62D, 0x64A), 0xFDAB => array(0x636, 0x62D, 0x64A), 0xFDAC => array(0x644, 0x62C, 0x64A), + 0xFDAD => array(0x644, 0x645, 0x64A), 0xFDAE => array(0x64A, 0x62D, 0x64A), 0xFDAF => array(0x64A, 0x62C, 0x64A), + 0xFDB0 => array(0x64A, 0x645, 0x64A), 0xFDB1 => array(0x645, 0x645, 0x64A), 0xFDB2 => array(0x642, 0x645, 0x64A), + 0xFDB3 => array(0x646, 0x62D, 0x64A), 0xFDB4 => array(0x642, 0x645, 0x62D), 0xFDB5 => array(0x644, 0x62D, 0x645), + 0xFDB6 => array(0x639, 0x645, 0x64A), 0xFDB7 => array(0x643, 0x645, 0x64A), 0xFDB8 => array(0x646, 0x62C, 0x62D), + 0xFDB9 => array(0x645, 0x62E, 0x64A), 0xFDBA => array(0x644, 0x62C, 0x645), 0xFDBB => array(0x643, 0x645, 0x645), + 0xFDBC => array(0x644, 0x62C, 0x645), 0xFDBD => array(0x646, 0x62C, 0x62D), 0xFDBE => array(0x62C, 0x62D, 0x64A), + 0xFDBF => array(0x62D, 0x62C, 0x64A), 0xFDC0 => array(0x645, 0x62C, 0x64A), 0xFDC1 => array(0x641, 0x645, 0x64A), + 0xFDC2 => array(0x628, 0x62D, 0x64A), 0xFDC3 => array(0x643, 0x645, 0x645), 0xFDC4 => array(0x639, 0x62C, 0x645), + 0xFDC5 => array(0x635, 0x645, 0x645), 0xFDC6 => array(0x633, 0x62E, 0x64A), 0xFDC7 => array(0x646, 0x62C, 0x64A), + 0xFDF0 => array(0x635, 0x644, 0x6D2), 0xFDF1 => array(0x642, 0x644, 0x6D2), 0xFDF2 => array(0x627, 0x644, 0x644, 0x647), + 0xFDF3 => array(0x627, 0x643, 0x628, 0x631), 0xFDF4 => array(0x645, 0x62D, 0x645, 0x62F), 0xFDF5 => array(0x635, 0x644, 0x639, 0x645), + 0xFDF6 => array(0x631, 0x633, 0x648, 0x644), 0xFDF7 => array(0x639, 0x644, 0x64A, 0x647), 0xFDF8 => array(0x648, 0x633, 0x644, 0x645), + 0xFDF9 => array(0x635, 0x644, 0x649), 0xFDFC => array(0x631, 0x6CC, 0x627, 0x644), 0xFE11 => array(0x3001), + 0xFE17 => array(0x3016), 0xFE18 => array(0x3017), 0xFE31 => array(0x2014), + 0xFE32 => array(0x2013), 0xFE39 => array(0x3014), 0xFE3A => array(0x3015), + 0xFE3B => array(0x3010), 0xFE3C => array(0x3011), 0xFE3D => array(0x300A), + 0xFE3E => array(0x300B), 0xFE3F => array(0x3008), 0xFE40 => array(0x3009), + 0xFE41 => array(0x300C), 0xFE42 => array(0x300D), 0xFE43 => array(0x300E), + 0xFE44 => array(0x300F), 0xFE51 => array(0x3001), 0xFE58 => array(0x2014), + 0xFE5D => array(0x3014), 0xFE5E => array(0x3015), 0xFE63 => array(0x2D), + 0xFE71 => array(0x640, 0x64B), 0xFE77 => array(0x640, 0x64E), 0xFE79 => array(0x640, 0x64F), + 0xFE7B => array(0x640, 0x650), 0xFE7D => array(0x640, 0x651), 0xFE7F => array(0x640, 0x652), + 0xFE80 => array(0x621), 0xFE81 => array(0x622), 0xFE82 => array(0x622), + 0xFE83 => array(0x623), 0xFE84 => array(0x623), 0xFE85 => array(0x624), + 0xFE86 => array(0x624), 0xFE87 => array(0x625), 0xFE88 => array(0x625), + 0xFE89 => array(0x626), 0xFE8A => array(0x626), 0xFE8B => array(0x626), + 0xFE8C => array(0x626), 0xFE8D => array(0x627), 0xFE8E => array(0x627), + 0xFE8F => array(0x628), 0xFE90 => array(0x628), 0xFE91 => array(0x628), + 0xFE92 => array(0x628), 0xFE93 => array(0x629), 0xFE94 => array(0x629), + 0xFE95 => array(0x62A), 0xFE96 => array(0x62A), 0xFE97 => array(0x62A), + 0xFE98 => array(0x62A), 0xFE99 => array(0x62B), 0xFE9A => array(0x62B), + 0xFE9B => array(0x62B), 0xFE9C => array(0x62B), 0xFE9D => array(0x62C), + 0xFE9E => array(0x62C), 0xFE9F => array(0x62C), 0xFEA0 => array(0x62C), + 0xFEA1 => array(0x62D), 0xFEA2 => array(0x62D), 0xFEA3 => array(0x62D), + 0xFEA4 => array(0x62D), 0xFEA5 => array(0x62E), 0xFEA6 => array(0x62E), + 0xFEA7 => array(0x62E), 0xFEA8 => array(0x62E), 0xFEA9 => array(0x62F), + 0xFEAA => array(0x62F), 0xFEAB => array(0x630), 0xFEAC => array(0x630), + 0xFEAD => array(0x631), 0xFEAE => array(0x631), 0xFEAF => array(0x632), + 0xFEB0 => array(0x632), 0xFEB1 => array(0x633), 0xFEB2 => array(0x633), + 0xFEB3 => array(0x633), 0xFEB4 => array(0x633), 0xFEB5 => array(0x634), + 0xFEB6 => array(0x634), 0xFEB7 => array(0x634), 0xFEB8 => array(0x634), + 0xFEB9 => array(0x635), 0xFEBA => array(0x635), 0xFEBB => array(0x635), + 0xFEBC => array(0x635), 0xFEBD => array(0x636), 0xFEBE => array(0x636), + 0xFEBF => array(0x636), 0xFEC0 => array(0x636), 0xFEC1 => array(0x637), + 0xFEC2 => array(0x637), 0xFEC3 => array(0x637), 0xFEC4 => array(0x637), + 0xFEC5 => array(0x638), 0xFEC6 => array(0x638), 0xFEC7 => array(0x638), + 0xFEC8 => array(0x638), 0xFEC9 => array(0x639), 0xFECA => array(0x639), + 0xFECB => array(0x639), 0xFECC => array(0x639), 0xFECD => array(0x63A), + 0xFECE => array(0x63A), 0xFECF => array(0x63A), 0xFED0 => array(0x63A), + 0xFED1 => array(0x641), 0xFED2 => array(0x641), 0xFED3 => array(0x641), + 0xFED4 => array(0x641), 0xFED5 => array(0x642), 0xFED6 => array(0x642), + 0xFED7 => array(0x642), 0xFED8 => array(0x642), 0xFED9 => array(0x643), + 0xFEDA => array(0x643), 0xFEDB => array(0x643), 0xFEDC => array(0x643), + 0xFEDD => array(0x644), 0xFEDE => array(0x644), 0xFEDF => array(0x644), + 0xFEE0 => array(0x644), 0xFEE1 => array(0x645), 0xFEE2 => array(0x645), + 0xFEE3 => array(0x645), 0xFEE4 => array(0x645), 0xFEE5 => array(0x646), + 0xFEE6 => array(0x646), 0xFEE7 => array(0x646), 0xFEE8 => array(0x646), + 0xFEE9 => array(0x647), 0xFEEA => array(0x647), 0xFEEB => array(0x647), + 0xFEEC => array(0x647), 0xFEED => array(0x648), 0xFEEE => array(0x648), + 0xFEEF => array(0x649), 0xFEF0 => array(0x649), 0xFEF1 => array(0x64A), + 0xFEF2 => array(0x64A), 0xFEF3 => array(0x64A), 0xFEF4 => array(0x64A), + 0xFEF5 => array(0x644, 0x622), 0xFEF6 => array(0x644, 0x622), 0xFEF7 => array(0x644, 0x623), + 0xFEF8 => array(0x644, 0x623), 0xFEF9 => array(0x644, 0x625), 0xFEFA => array(0x644, 0x625), + 0xFEFB => array(0x644, 0x627), 0xFEFC => array(0x644, 0x627), 0xFF0D => array(0x2D), + 0xFF0E => array(0x2E), 0xFF10 => array(0x30), 0xFF11 => array(0x31), + 0xFF12 => array(0x32), 0xFF13 => array(0x33), 0xFF14 => array(0x34), + 0xFF15 => array(0x35), 0xFF16 => array(0x36), 0xFF17 => array(0x37), + 0xFF18 => array(0x38), 0xFF19 => array(0x39), 0xFF21 => array(0x61), + 0xFF22 => array(0x62), 0xFF23 => array(0x63), 0xFF24 => array(0x64), + 0xFF25 => array(0x65), 0xFF26 => array(0x66), 0xFF27 => array(0x67), + 0xFF28 => array(0x68), 0xFF29 => array(0x69), 0xFF2A => array(0x6A), + 0xFF2B => array(0x6B), 0xFF2C => array(0x6C), 0xFF2D => array(0x6D), + 0xFF2E => array(0x6E), 0xFF2F => array(0x6F), 0xFF30 => array(0x70), + 0xFF31 => array(0x71), 0xFF32 => array(0x72), 0xFF33 => array(0x73), + 0xFF34 => array(0x74), 0xFF35 => array(0x75), 0xFF36 => array(0x76), + 0xFF37 => array(0x77), 0xFF38 => array(0x78), 0xFF39 => array(0x79), + 0xFF3A => array(0x7A), 0xFF41 => array(0x61), 0xFF42 => array(0x62), + 0xFF43 => array(0x63), 0xFF44 => array(0x64), 0xFF45 => array(0x65), + 0xFF46 => array(0x66), 0xFF47 => array(0x67), 0xFF48 => array(0x68), + 0xFF49 => array(0x69), 0xFF4A => array(0x6A), 0xFF4B => array(0x6B), + 0xFF4C => array(0x6C), 0xFF4D => array(0x6D), 0xFF4E => array(0x6E), + 0xFF4F => array(0x6F), 0xFF50 => array(0x70), 0xFF51 => array(0x71), + 0xFF52 => array(0x72), 0xFF53 => array(0x73), 0xFF54 => array(0x74), + 0xFF55 => array(0x75), 0xFF56 => array(0x76), 0xFF57 => array(0x77), + 0xFF58 => array(0x78), 0xFF59 => array(0x79), 0xFF5A => array(0x7A), + 0xFF5F => array(0x2985), 0xFF60 => array(0x2986), 0xFF61 => array(0x2E), + 0xFF62 => array(0x300C), 0xFF63 => array(0x300D), 0xFF64 => array(0x3001), + 0xFF65 => array(0x30FB), 0xFF66 => array(0x30F2), 0xFF67 => array(0x30A1), + 0xFF68 => array(0x30A3), 0xFF69 => array(0x30A5), 0xFF6A => array(0x30A7), + 0xFF6B => array(0x30A9), 0xFF6C => array(0x30E3), 0xFF6D => array(0x30E5), + 0xFF6E => array(0x30E7), 0xFF6F => array(0x30C3), 0xFF70 => array(0x30FC), + 0xFF71 => array(0x30A2), 0xFF72 => array(0x30A4), 0xFF73 => array(0x30A6), + 0xFF74 => array(0x30A8), 0xFF75 => array(0x30AA), 0xFF76 => array(0x30AB), + 0xFF77 => array(0x30AD), 0xFF78 => array(0x30AF), 0xFF79 => array(0x30B1), + 0xFF7A => array(0x30B3), 0xFF7B => array(0x30B5), 0xFF7C => array(0x30B7), + 0xFF7D => array(0x30B9), 0xFF7E => array(0x30BB), 0xFF7F => array(0x30BD), + 0xFF80 => array(0x30BF), 0xFF81 => array(0x30C1), 0xFF82 => array(0x30C4), + 0xFF83 => array(0x30C6), 0xFF84 => array(0x30C8), 0xFF85 => array(0x30CA), + 0xFF86 => array(0x30CB), 0xFF87 => array(0x30CC), 0xFF88 => array(0x30CD), + 0xFF89 => array(0x30CE), 0xFF8A => array(0x30CF), 0xFF8B => array(0x30D2), + 0xFF8C => array(0x30D5), 0xFF8D => array(0x30D8), 0xFF8E => array(0x30DB), + 0xFF8F => array(0x30DE), 0xFF90 => array(0x30DF), 0xFF91 => array(0x30E0), + 0xFF92 => array(0x30E1), 0xFF93 => array(0x30E2), 0xFF94 => array(0x30E4), + 0xFF95 => array(0x30E6), 0xFF96 => array(0x30E8), 0xFF97 => array(0x30E9), + 0xFF98 => array(0x30EA), 0xFF99 => array(0x30EB), 0xFF9A => array(0x30EC), + 0xFF9B => array(0x30ED), 0xFF9C => array(0x30EF), 0xFF9D => array(0x30F3), + 0xFF9E => array(0x3099), 0xFF9F => array(0x309A), 0xFFA1 => array(0x1100), + 0xFFA2 => array(0x1101), 0xFFA3 => array(0x11AA), 0xFFA4 => array(0x1102), + 0xFFA5 => array(0x11AC), 0xFFA6 => array(0x11AD), 0xFFA7 => array(0x1103), + 0xFFA8 => array(0x1104), 0xFFA9 => array(0x1105), 0xFFAA => array(0x11B0), + 0xFFAB => array(0x11B1), 0xFFAC => array(0x11B2), 0xFFAD => array(0x11B3), + 0xFFAE => array(0x11B4), 0xFFAF => array(0x11B5), 0xFFB0 => array(0x111A), + 0xFFB1 => array(0x1106), 0xFFB2 => array(0x1107), 0xFFB3 => array(0x1108), + 0xFFB4 => array(0x1121), 0xFFB5 => array(0x1109), 0xFFB6 => array(0x110A), + 0xFFB7 => array(0x110B), 0xFFB8 => array(0x110C), 0xFFB9 => array(0x110D), + 0xFFBA => array(0x110E), 0xFFBB => array(0x110F), 0xFFBC => array(0x1110), + 0xFFBD => array(0x1111), 0xFFBE => array(0x1112), 0xFFC2 => array(0x1161), + 0xFFC3 => array(0x1162), 0xFFC4 => array(0x1163), 0xFFC5 => array(0x1164), + 0xFFC6 => array(0x1165), 0xFFC7 => array(0x1166), 0xFFCA => array(0x1167), + 0xFFCB => array(0x1168), 0xFFCC => array(0x1169), 0xFFCD => array(0x116A), + 0xFFCE => array(0x116B), 0xFFCF => array(0x116C), 0xFFD2 => array(0x116D), + 0xFFD3 => array(0x116E), 0xFFD4 => array(0x116F), 0xFFD5 => array(0x1170), + 0xFFD6 => array(0x1171), 0xFFD7 => array(0x1172), 0xFFDA => array(0x1173), + 0xFFDB => array(0x1174), 0xFFDC => array(0x1175), 0xFFE0 => array(0xA2), + 0xFFE1 => array(0xA3), 0xFFE2 => array(0xAC), 0xFFE4 => array(0xA6), + 0xFFE5 => array(0xA5), 0xFFE6 => array(0x20A9), 0xFFE8 => array(0x2502), + 0xFFE9 => array(0x2190), 0xFFEA => array(0x2191), 0xFFEB => array(0x2192), + 0xFFEC => array(0x2193), 0xFFED => array(0x25A0), 0xFFEE => array(0x25CB), + 0x10400 => array(0x10428), 0x10401 => array(0x10429), 0x10402 => array(0x1042A), + 0x10403 => array(0x1042B), 0x10404 => array(0x1042C), 0x10405 => array(0x1042D), + 0x10406 => array(0x1042E), 0x10407 => array(0x1042F), 0x10408 => array(0x10430), + 0x10409 => array(0x10431), 0x1040A => array(0x10432), 0x1040B => array(0x10433), + 0x1040C => array(0x10434), 0x1040D => array(0x10435), 0x1040E => array(0x10436), + 0x1040F => array(0x10437), 0x10410 => array(0x10438), 0x10411 => array(0x10439), + 0x10412 => array(0x1043A), 0x10413 => array(0x1043B), 0x10414 => array(0x1043C), + 0x10415 => array(0x1043D), 0x10416 => array(0x1043E), 0x10417 => array(0x1043F), + 0x10418 => array(0x10440), 0x10419 => array(0x10441), 0x1041A => array(0x10442), + 0x1041B => array(0x10443), 0x1041C => array(0x10444), 0x1041D => array(0x10445), + 0x1041E => array(0x10446), 0x1041F => array(0x10447), 0x10420 => array(0x10448), + 0x10421 => array(0x10449), 0x10422 => array(0x1044A), 0x10423 => array(0x1044B), + 0x10424 => array(0x1044C), 0x10425 => array(0x1044D), 0x10426 => array(0x1044E), + 0x10427 => array(0x1044F), 0x118A0 => array(0x118C0), 0x118A1 => array(0x118C1), + 0x118A2 => array(0x118C2), 0x118A3 => array(0x118C3), 0x118A4 => array(0x118C4), + 0x118A5 => array(0x118C5), 0x118A6 => array(0x118C6), 0x118A7 => array(0x118C7), + 0x118A8 => array(0x118C8), 0x118A9 => array(0x118C9), 0x118AA => array(0x118CA), + 0x118AB => array(0x118CB), 0x118AC => array(0x118CC), 0x118AD => array(0x118CD), + 0x118AE => array(0x118CE), 0x118AF => array(0x118CF), 0x118B0 => array(0x118D0), + 0x118B1 => array(0x118D1), 0x118B2 => array(0x118D2), 0x118B3 => array(0x118D3), + 0x118B4 => array(0x118D4), 0x118B5 => array(0x118D5), 0x118B6 => array(0x118D6), + 0x118B7 => array(0x118D7), 0x118B8 => array(0x118D8), 0x118B9 => array(0x118D9), + 0x118BA => array(0x118DA), 0x118BB => array(0x118DB), 0x118BC => array(0x118DC), + 0x118BD => array(0x118DD), 0x118BE => array(0x118DE), 0x118BF => array(0x118DF), + 0x1D15E => array(0x1D157, 0x1D165), 0x1D15F => array(0x1D158, 0x1D165), 0x1D160 => array(0x1D158, 0x1D165, 0x1D16E), + 0x1D161 => array(0x1D158, 0x1D165, 0x1D16F), 0x1D162 => array(0x1D158, 0x1D165, 0x1D170), 0x1D163 => array(0x1D158, 0x1D165, 0x1D171), + 0x1D164 => array(0x1D158, 0x1D165, 0x1D172), 0x1D1BB => array(0x1D1B9, 0x1D165), 0x1D1BC => array(0x1D1BA, 0x1D165), + 0x1D1BD => array(0x1D1B9, 0x1D165, 0x1D16E), 0x1D1BE => array(0x1D1BA, 0x1D165, 0x1D16E), 0x1D1BF => array(0x1D1B9, 0x1D165, 0x1D16F), + 0x1D1C0 => array(0x1D1BA, 0x1D165, 0x1D16F), 0x1D400 => array(0x61), 0x1D401 => array(0x62), + 0x1D402 => array(0x63), 0x1D403 => array(0x64), 0x1D404 => array(0x65), + 0x1D405 => array(0x66), 0x1D406 => array(0x67), 0x1D407 => array(0x68), + 0x1D408 => array(0x69), 0x1D409 => array(0x6A), 0x1D40A => array(0x6B), + 0x1D40B => array(0x6C), 0x1D40C => array(0x6D), 0x1D40D => array(0x6E), + 0x1D40E => array(0x6F), 0x1D40F => array(0x70), 0x1D410 => array(0x71), + 0x1D411 => array(0x72), 0x1D412 => array(0x73), 0x1D413 => array(0x74), + 0x1D414 => array(0x75), 0x1D415 => array(0x76), 0x1D416 => array(0x77), + 0x1D417 => array(0x78), 0x1D418 => array(0x79), 0x1D419 => array(0x7A), + 0x1D41A => array(0x61), 0x1D41B => array(0x62), 0x1D41C => array(0x63), + 0x1D41D => array(0x64), 0x1D41E => array(0x65), 0x1D41F => array(0x66), + 0x1D420 => array(0x67), 0x1D421 => array(0x68), 0x1D422 => array(0x69), + 0x1D423 => array(0x6A), 0x1D424 => array(0x6B), 0x1D425 => array(0x6C), + 0x1D426 => array(0x6D), 0x1D427 => array(0x6E), 0x1D428 => array(0x6F), + 0x1D429 => array(0x70), 0x1D42A => array(0x71), 0x1D42B => array(0x72), + 0x1D42C => array(0x73), 0x1D42D => array(0x74), 0x1D42E => array(0x75), + 0x1D42F => array(0x76), 0x1D430 => array(0x77), 0x1D431 => array(0x78), + 0x1D432 => array(0x79), 0x1D433 => array(0x7A), 0x1D434 => array(0x61), + 0x1D435 => array(0x62), 0x1D436 => array(0x63), 0x1D437 => array(0x64), + 0x1D438 => array(0x65), 0x1D439 => array(0x66), 0x1D43A => array(0x67), + 0x1D43B => array(0x68), 0x1D43C => array(0x69), 0x1D43D => array(0x6A), + 0x1D43E => array(0x6B), 0x1D43F => array(0x6C), 0x1D440 => array(0x6D), + 0x1D441 => array(0x6E), 0x1D442 => array(0x6F), 0x1D443 => array(0x70), + 0x1D444 => array(0x71), 0x1D445 => array(0x72), 0x1D446 => array(0x73), + 0x1D447 => array(0x74), 0x1D448 => array(0x75), 0x1D449 => array(0x76), + 0x1D44A => array(0x77), 0x1D44B => array(0x78), 0x1D44C => array(0x79), + 0x1D44D => array(0x7A), 0x1D44E => array(0x61), 0x1D44F => array(0x62), + 0x1D450 => array(0x63), 0x1D451 => array(0x64), 0x1D452 => array(0x65), + 0x1D453 => array(0x66), 0x1D454 => array(0x67), 0x1D456 => array(0x69), + 0x1D457 => array(0x6A), 0x1D458 => array(0x6B), 0x1D459 => array(0x6C), + 0x1D45A => array(0x6D), 0x1D45B => array(0x6E), 0x1D45C => array(0x6F), + 0x1D45D => array(0x70), 0x1D45E => array(0x71), 0x1D45F => array(0x72), + 0x1D460 => array(0x73), 0x1D461 => array(0x74), 0x1D462 => array(0x75), + 0x1D463 => array(0x76), 0x1D464 => array(0x77), 0x1D465 => array(0x78), + 0x1D466 => array(0x79), 0x1D467 => array(0x7A), 0x1D468 => array(0x61), + 0x1D469 => array(0x62), 0x1D46A => array(0x63), 0x1D46B => array(0x64), + 0x1D46C => array(0x65), 0x1D46D => array(0x66), 0x1D46E => array(0x67), + 0x1D46F => array(0x68), 0x1D470 => array(0x69), 0x1D471 => array(0x6A), + 0x1D472 => array(0x6B), 0x1D473 => array(0x6C), 0x1D474 => array(0x6D), + 0x1D475 => array(0x6E), 0x1D476 => array(0x6F), 0x1D477 => array(0x70), + 0x1D478 => array(0x71), 0x1D479 => array(0x72), 0x1D47A => array(0x73), + 0x1D47B => array(0x74), 0x1D47C => array(0x75), 0x1D47D => array(0x76), + 0x1D47E => array(0x77), 0x1D47F => array(0x78), 0x1D480 => array(0x79), + 0x1D481 => array(0x7A), 0x1D482 => array(0x61), 0x1D483 => array(0x62), + 0x1D484 => array(0x63), 0x1D485 => array(0x64), 0x1D486 => array(0x65), + 0x1D487 => array(0x66), 0x1D488 => array(0x67), 0x1D489 => array(0x68), + 0x1D48A => array(0x69), 0x1D48B => array(0x6A), 0x1D48C => array(0x6B), + 0x1D48D => array(0x6C), 0x1D48E => array(0x6D), 0x1D48F => array(0x6E), + 0x1D490 => array(0x6F), 0x1D491 => array(0x70), 0x1D492 => array(0x71), + 0x1D493 => array(0x72), 0x1D494 => array(0x73), 0x1D495 => array(0x74), + 0x1D496 => array(0x75), 0x1D497 => array(0x76), 0x1D498 => array(0x77), + 0x1D499 => array(0x78), 0x1D49A => array(0x79), 0x1D49B => array(0x7A), + 0x1D49C => array(0x61), 0x1D49E => array(0x63), 0x1D49F => array(0x64), + 0x1D4A2 => array(0x67), 0x1D4A5 => array(0x6A), 0x1D4A6 => array(0x6B), + 0x1D4A9 => array(0x6E), 0x1D4AA => array(0x6F), 0x1D4AB => array(0x70), + 0x1D4AC => array(0x71), 0x1D4AE => array(0x73), 0x1D4AF => array(0x74), + 0x1D4B0 => array(0x75), 0x1D4B1 => array(0x76), 0x1D4B2 => array(0x77), + 0x1D4B3 => array(0x78), 0x1D4B4 => array(0x79), 0x1D4B5 => array(0x7A), + 0x1D4B6 => array(0x61), 0x1D4B7 => array(0x62), 0x1D4B8 => array(0x63), + 0x1D4B9 => array(0x64), 0x1D4BB => array(0x66), 0x1D4BD => array(0x68), + 0x1D4BE => array(0x69), 0x1D4BF => array(0x6A), 0x1D4C0 => array(0x6B), + 0x1D4C1 => array(0x6C), 0x1D4C2 => array(0x6D), 0x1D4C3 => array(0x6E), + 0x1D4C5 => array(0x70), 0x1D4C6 => array(0x71), 0x1D4C7 => array(0x72), + 0x1D4C8 => array(0x73), 0x1D4C9 => array(0x74), 0x1D4CA => array(0x75), + 0x1D4CB => array(0x76), 0x1D4CC => array(0x77), 0x1D4CD => array(0x78), + 0x1D4CE => array(0x79), 0x1D4CF => array(0x7A), 0x1D4D0 => array(0x61), + 0x1D4D1 => array(0x62), 0x1D4D2 => array(0x63), 0x1D4D3 => array(0x64), + 0x1D4D4 => array(0x65), 0x1D4D5 => array(0x66), 0x1D4D6 => array(0x67), + 0x1D4D7 => array(0x68), 0x1D4D8 => array(0x69), 0x1D4D9 => array(0x6A), + 0x1D4DA => array(0x6B), 0x1D4DB => array(0x6C), 0x1D4DC => array(0x6D), + 0x1D4DD => array(0x6E), 0x1D4DE => array(0x6F), 0x1D4DF => array(0x70), + 0x1D4E0 => array(0x71), 0x1D4E1 => array(0x72), 0x1D4E2 => array(0x73), + 0x1D4E3 => array(0x74), 0x1D4E4 => array(0x75), 0x1D4E5 => array(0x76), + 0x1D4E6 => array(0x77), 0x1D4E7 => array(0x78), 0x1D4E8 => array(0x79), + 0x1D4E9 => array(0x7A), 0x1D4EA => array(0x61), 0x1D4EB => array(0x62), + 0x1D4EC => array(0x63), 0x1D4ED => array(0x64), 0x1D4EE => array(0x65), + 0x1D4EF => array(0x66), 0x1D4F0 => array(0x67), 0x1D4F1 => array(0x68), + 0x1D4F2 => array(0x69), 0x1D4F3 => array(0x6A), 0x1D4F4 => array(0x6B), + 0x1D4F5 => array(0x6C), 0x1D4F6 => array(0x6D), 0x1D4F7 => array(0x6E), + 0x1D4F8 => array(0x6F), 0x1D4F9 => array(0x70), 0x1D4FA => array(0x71), + 0x1D4FB => array(0x72), 0x1D4FC => array(0x73), 0x1D4FD => array(0x74), + 0x1D4FE => array(0x75), 0x1D4FF => array(0x76), 0x1D500 => array(0x77), + 0x1D501 => array(0x78), 0x1D502 => array(0x79), 0x1D503 => array(0x7A), + 0x1D504 => array(0x61), 0x1D505 => array(0x62), 0x1D507 => array(0x64), + 0x1D508 => array(0x65), 0x1D509 => array(0x66), 0x1D50A => array(0x67), + 0x1D50D => array(0x6A), 0x1D50E => array(0x6B), 0x1D50F => array(0x6C), + 0x1D510 => array(0x6D), 0x1D511 => array(0x6E), 0x1D512 => array(0x6F), + 0x1D513 => array(0x70), 0x1D514 => array(0x71), 0x1D516 => array(0x73), + 0x1D517 => array(0x74), 0x1D518 => array(0x75), 0x1D519 => array(0x76), + 0x1D51A => array(0x77), 0x1D51B => array(0x78), 0x1D51C => array(0x79), + 0x1D51E => array(0x61), 0x1D51F => array(0x62), 0x1D520 => array(0x63), + 0x1D521 => array(0x64), 0x1D522 => array(0x65), 0x1D523 => array(0x66), + 0x1D524 => array(0x67), 0x1D525 => array(0x68), 0x1D526 => array(0x69), + 0x1D527 => array(0x6A), 0x1D528 => array(0x6B), 0x1D529 => array(0x6C), + 0x1D52A => array(0x6D), 0x1D52B => array(0x6E), 0x1D52C => array(0x6F), + 0x1D52D => array(0x70), 0x1D52E => array(0x71), 0x1D52F => array(0x72), + 0x1D530 => array(0x73), 0x1D531 => array(0x74), 0x1D532 => array(0x75), + 0x1D533 => array(0x76), 0x1D534 => array(0x77), 0x1D535 => array(0x78), + 0x1D536 => array(0x79), 0x1D537 => array(0x7A), 0x1D538 => array(0x61), + 0x1D539 => array(0x62), 0x1D53B => array(0x64), 0x1D53C => array(0x65), + 0x1D53D => array(0x66), 0x1D53E => array(0x67), 0x1D540 => array(0x69), + 0x1D541 => array(0x6A), 0x1D542 => array(0x6B), 0x1D543 => array(0x6C), + 0x1D544 => array(0x6D), 0x1D546 => array(0x6F), 0x1D54A => array(0x73), + 0x1D54B => array(0x74), 0x1D54C => array(0x75), 0x1D54D => array(0x76), + 0x1D54E => array(0x77), 0x1D54F => array(0x78), 0x1D550 => array(0x79), + 0x1D552 => array(0x61), 0x1D553 => array(0x62), 0x1D554 => array(0x63), + 0x1D555 => array(0x64), 0x1D556 => array(0x65), 0x1D557 => array(0x66), + 0x1D558 => array(0x67), 0x1D559 => array(0x68), 0x1D55A => array(0x69), + 0x1D55B => array(0x6A), 0x1D55C => array(0x6B), 0x1D55D => array(0x6C), + 0x1D55E => array(0x6D), 0x1D55F => array(0x6E), 0x1D560 => array(0x6F), + 0x1D561 => array(0x70), 0x1D562 => array(0x71), 0x1D563 => array(0x72), + 0x1D564 => array(0x73), 0x1D565 => array(0x74), 0x1D566 => array(0x75), + 0x1D567 => array(0x76), 0x1D568 => array(0x77), 0x1D569 => array(0x78), + 0x1D56A => array(0x79), 0x1D56B => array(0x7A), 0x1D56C => array(0x61), + 0x1D56D => array(0x62), 0x1D56E => array(0x63), 0x1D56F => array(0x64), + 0x1D570 => array(0x65), 0x1D571 => array(0x66), 0x1D572 => array(0x67), + 0x1D573 => array(0x68), 0x1D574 => array(0x69), 0x1D575 => array(0x6A), + 0x1D576 => array(0x6B), 0x1D577 => array(0x6C), 0x1D578 => array(0x6D), + 0x1D579 => array(0x6E), 0x1D57A => array(0x6F), 0x1D57B => array(0x70), + 0x1D57C => array(0x71), 0x1D57D => array(0x72), 0x1D57E => array(0x73), + 0x1D57F => array(0x74), 0x1D580 => array(0x75), 0x1D581 => array(0x76), + 0x1D582 => array(0x77), 0x1D583 => array(0x78), 0x1D584 => array(0x79), + 0x1D585 => array(0x7A), 0x1D586 => array(0x61), 0x1D587 => array(0x62), + 0x1D588 => array(0x63), 0x1D589 => array(0x64), 0x1D58A => array(0x65), + 0x1D58B => array(0x66), 0x1D58C => array(0x67), 0x1D58D => array(0x68), + 0x1D58E => array(0x69), 0x1D58F => array(0x6A), 0x1D590 => array(0x6B), + 0x1D591 => array(0x6C), 0x1D592 => array(0x6D), 0x1D593 => array(0x6E), + 0x1D594 => array(0x6F), 0x1D595 => array(0x70), 0x1D596 => array(0x71), + 0x1D597 => array(0x72), 0x1D598 => array(0x73), 0x1D599 => array(0x74), + 0x1D59A => array(0x75), 0x1D59B => array(0x76), 0x1D59C => array(0x77), + 0x1D59D => array(0x78), 0x1D59E => array(0x79), 0x1D59F => array(0x7A), + 0x1D5A0 => array(0x61), 0x1D5A1 => array(0x62), 0x1D5A2 => array(0x63), + 0x1D5A3 => array(0x64), 0x1D5A4 => array(0x65), 0x1D5A5 => array(0x66), + 0x1D5A6 => array(0x67), 0x1D5A7 => array(0x68), 0x1D5A8 => array(0x69), + 0x1D5A9 => array(0x6A), 0x1D5AA => array(0x6B), 0x1D5AB => array(0x6C), + 0x1D5AC => array(0x6D), 0x1D5AD => array(0x6E), 0x1D5AE => array(0x6F), + 0x1D5AF => array(0x70), 0x1D5B0 => array(0x71), 0x1D5B1 => array(0x72), + 0x1D5B2 => array(0x73), 0x1D5B3 => array(0x74), 0x1D5B4 => array(0x75), + 0x1D5B5 => array(0x76), 0x1D5B6 => array(0x77), 0x1D5B7 => array(0x78), + 0x1D5B8 => array(0x79), 0x1D5B9 => array(0x7A), 0x1D5BA => array(0x61), + 0x1D5BB => array(0x62), 0x1D5BC => array(0x63), 0x1D5BD => array(0x64), + 0x1D5BE => array(0x65), 0x1D5BF => array(0x66), 0x1D5C0 => array(0x67), + 0x1D5C1 => array(0x68), 0x1D5C2 => array(0x69), 0x1D5C3 => array(0x6A), + 0x1D5C4 => array(0x6B), 0x1D5C5 => array(0x6C), 0x1D5C6 => array(0x6D), + 0x1D5C7 => array(0x6E), 0x1D5C8 => array(0x6F), 0x1D5C9 => array(0x70), + 0x1D5CA => array(0x71), 0x1D5CB => array(0x72), 0x1D5CC => array(0x73), + 0x1D5CD => array(0x74), 0x1D5CE => array(0x75), 0x1D5CF => array(0x76), + 0x1D5D0 => array(0x77), 0x1D5D1 => array(0x78), 0x1D5D2 => array(0x79), + 0x1D5D3 => array(0x7A), 0x1D5D4 => array(0x61), 0x1D5D5 => array(0x62), + 0x1D5D6 => array(0x63), 0x1D5D7 => array(0x64), 0x1D5D8 => array(0x65), + 0x1D5D9 => array(0x66), 0x1D5DA => array(0x67), 0x1D5DB => array(0x68), + 0x1D5DC => array(0x69), 0x1D5DD => array(0x6A), 0x1D5DE => array(0x6B), + 0x1D5DF => array(0x6C), 0x1D5E0 => array(0x6D), 0x1D5E1 => array(0x6E), + 0x1D5E2 => array(0x6F), 0x1D5E3 => array(0x70), 0x1D5E4 => array(0x71), + 0x1D5E5 => array(0x72), 0x1D5E6 => array(0x73), 0x1D5E7 => array(0x74), + 0x1D5E8 => array(0x75), 0x1D5E9 => array(0x76), 0x1D5EA => array(0x77), + 0x1D5EB => array(0x78), 0x1D5EC => array(0x79), 0x1D5ED => array(0x7A), + 0x1D5EE => array(0x61), 0x1D5EF => array(0x62), 0x1D5F0 => array(0x63), + 0x1D5F1 => array(0x64), 0x1D5F2 => array(0x65), 0x1D5F3 => array(0x66), + 0x1D5F4 => array(0x67), 0x1D5F5 => array(0x68), 0x1D5F6 => array(0x69), + 0x1D5F7 => array(0x6A), 0x1D5F8 => array(0x6B), 0x1D5F9 => array(0x6C), + 0x1D5FA => array(0x6D), 0x1D5FB => array(0x6E), 0x1D5FC => array(0x6F), + 0x1D5FD => array(0x70), 0x1D5FE => array(0x71), 0x1D5FF => array(0x72), + 0x1D600 => array(0x73), 0x1D601 => array(0x74), 0x1D602 => array(0x75), + 0x1D603 => array(0x76), 0x1D604 => array(0x77), 0x1D605 => array(0x78), + 0x1D606 => array(0x79), 0x1D607 => array(0x7A), 0x1D608 => array(0x61), + 0x1D609 => array(0x62), 0x1D60A => array(0x63), 0x1D60B => array(0x64), + 0x1D60C => array(0x65), 0x1D60D => array(0x66), 0x1D60E => array(0x67), + 0x1D60F => array(0x68), 0x1D610 => array(0x69), 0x1D611 => array(0x6A), + 0x1D612 => array(0x6B), 0x1D613 => array(0x6C), 0x1D614 => array(0x6D), + 0x1D615 => array(0x6E), 0x1D616 => array(0x6F), 0x1D617 => array(0x70), + 0x1D618 => array(0x71), 0x1D619 => array(0x72), 0x1D61A => array(0x73), + 0x1D61B => array(0x74), 0x1D61C => array(0x75), 0x1D61D => array(0x76), + 0x1D61E => array(0x77), 0x1D61F => array(0x78), 0x1D620 => array(0x79), + 0x1D621 => array(0x7A), 0x1D622 => array(0x61), 0x1D623 => array(0x62), + 0x1D624 => array(0x63), 0x1D625 => array(0x64), 0x1D626 => array(0x65), + 0x1D627 => array(0x66), 0x1D628 => array(0x67), 0x1D629 => array(0x68), + 0x1D62A => array(0x69), 0x1D62B => array(0x6A), 0x1D62C => array(0x6B), + 0x1D62D => array(0x6C), 0x1D62E => array(0x6D), 0x1D62F => array(0x6E), + 0x1D630 => array(0x6F), 0x1D631 => array(0x70), 0x1D632 => array(0x71), + 0x1D633 => array(0x72), 0x1D634 => array(0x73), 0x1D635 => array(0x74), + 0x1D636 => array(0x75), 0x1D637 => array(0x76), 0x1D638 => array(0x77), + 0x1D639 => array(0x78), 0x1D63A => array(0x79), 0x1D63B => array(0x7A), + 0x1D63C => array(0x61), 0x1D63D => array(0x62), 0x1D63E => array(0x63), + 0x1D63F => array(0x64), 0x1D640 => array(0x65), 0x1D641 => array(0x66), + 0x1D642 => array(0x67), 0x1D643 => array(0x68), 0x1D644 => array(0x69), + 0x1D645 => array(0x6A), 0x1D646 => array(0x6B), 0x1D647 => array(0x6C), + 0x1D648 => array(0x6D), 0x1D649 => array(0x6E), 0x1D64A => array(0x6F), + 0x1D64B => array(0x70), 0x1D64C => array(0x71), 0x1D64D => array(0x72), + 0x1D64E => array(0x73), 0x1D64F => array(0x74), 0x1D650 => array(0x75), + 0x1D651 => array(0x76), 0x1D652 => array(0x77), 0x1D653 => array(0x78), + 0x1D654 => array(0x79), 0x1D655 => array(0x7A), 0x1D656 => array(0x61), + 0x1D657 => array(0x62), 0x1D658 => array(0x63), 0x1D659 => array(0x64), + 0x1D65A => array(0x65), 0x1D65B => array(0x66), 0x1D65C => array(0x67), + 0x1D65D => array(0x68), 0x1D65E => array(0x69), 0x1D65F => array(0x6A), + 0x1D660 => array(0x6B), 0x1D661 => array(0x6C), 0x1D662 => array(0x6D), + 0x1D663 => array(0x6E), 0x1D664 => array(0x6F), 0x1D665 => array(0x70), + 0x1D666 => array(0x71), 0x1D667 => array(0x72), 0x1D668 => array(0x73), + 0x1D669 => array(0x74), 0x1D66A => array(0x75), 0x1D66B => array(0x76), + 0x1D66C => array(0x77), 0x1D66D => array(0x78), 0x1D66E => array(0x79), + 0x1D66F => array(0x7A), 0x1D670 => array(0x61), 0x1D671 => array(0x62), + 0x1D672 => array(0x63), 0x1D673 => array(0x64), 0x1D674 => array(0x65), + 0x1D675 => array(0x66), 0x1D676 => array(0x67), 0x1D677 => array(0x68), + 0x1D678 => array(0x69), 0x1D679 => array(0x6A), 0x1D67A => array(0x6B), + 0x1D67B => array(0x6C), 0x1D67C => array(0x6D), 0x1D67D => array(0x6E), + 0x1D67E => array(0x6F), 0x1D67F => array(0x70), 0x1D680 => array(0x71), + 0x1D681 => array(0x72), 0x1D682 => array(0x73), 0x1D683 => array(0x74), + 0x1D684 => array(0x75), 0x1D685 => array(0x76), 0x1D686 => array(0x77), + 0x1D687 => array(0x78), 0x1D688 => array(0x79), 0x1D689 => array(0x7A), + 0x1D68A => array(0x61), 0x1D68B => array(0x62), 0x1D68C => array(0x63), + 0x1D68D => array(0x64), 0x1D68E => array(0x65), 0x1D68F => array(0x66), + 0x1D690 => array(0x67), 0x1D691 => array(0x68), 0x1D692 => array(0x69), + 0x1D693 => array(0x6A), 0x1D694 => array(0x6B), 0x1D695 => array(0x6C), + 0x1D696 => array(0x6D), 0x1D697 => array(0x6E), 0x1D698 => array(0x6F), + 0x1D699 => array(0x70), 0x1D69A => array(0x71), 0x1D69B => array(0x72), + 0x1D69C => array(0x73), 0x1D69D => array(0x74), 0x1D69E => array(0x75), + 0x1D69F => array(0x76), 0x1D6A0 => array(0x77), 0x1D6A1 => array(0x78), + 0x1D6A2 => array(0x79), 0x1D6A3 => array(0x7A), 0x1D6A4 => array(0x131), + 0x1D6A5 => array(0x237), 0x1D6A8 => array(0x3B1), 0x1D6A9 => array(0x3B2), + 0x1D6AA => array(0x3B3), 0x1D6AB => array(0x3B4), 0x1D6AC => array(0x3B5), + 0x1D6AD => array(0x3B6), 0x1D6AE => array(0x3B7), 0x1D6AF => array(0x3B8), + 0x1D6B0 => array(0x3B9), 0x1D6B1 => array(0x3BA), 0x1D6B2 => array(0x3BB), + 0x1D6B3 => array(0x3BC), 0x1D6B4 => array(0x3BD), 0x1D6B5 => array(0x3BE), + 0x1D6B6 => array(0x3BF), 0x1D6B7 => array(0x3C0), 0x1D6B8 => array(0x3C1), + 0x1D6B9 => array(0x3B8), 0x1D6BA => array(0x3C3), 0x1D6BB => array(0x3C4), + 0x1D6BC => array(0x3C5), 0x1D6BD => array(0x3C6), 0x1D6BE => array(0x3C7), + 0x1D6BF => array(0x3C8), 0x1D6C0 => array(0x3C9), 0x1D6C1 => array(0x2207), + 0x1D6C2 => array(0x3B1), 0x1D6C3 => array(0x3B2), 0x1D6C4 => array(0x3B3), + 0x1D6C5 => array(0x3B4), 0x1D6C6 => array(0x3B5), 0x1D6C7 => array(0x3B6), + 0x1D6C8 => array(0x3B7), 0x1D6C9 => array(0x3B8), 0x1D6CA => array(0x3B9), + 0x1D6CB => array(0x3BA), 0x1D6CC => array(0x3BB), 0x1D6CD => array(0x3BC), + 0x1D6CE => array(0x3BD), 0x1D6CF => array(0x3BE), 0x1D6D0 => array(0x3BF), + 0x1D6D1 => array(0x3C0), 0x1D6D2 => array(0x3C1), 0x1D6D3 => array(0x3C3), + 0x1D6D4 => array(0x3C3), 0x1D6D5 => array(0x3C4), 0x1D6D6 => array(0x3C5), + 0x1D6D7 => array(0x3C6), 0x1D6D8 => array(0x3C7), 0x1D6D9 => array(0x3C8), + 0x1D6DA => array(0x3C9), 0x1D6DB => array(0x2202), 0x1D6DC => array(0x3B5), + 0x1D6DD => array(0x3B8), 0x1D6DE => array(0x3BA), 0x1D6DF => array(0x3C6), + 0x1D6E0 => array(0x3C1), 0x1D6E1 => array(0x3C0), 0x1D6E2 => array(0x3B1), + 0x1D6E3 => array(0x3B2), 0x1D6E4 => array(0x3B3), 0x1D6E5 => array(0x3B4), + 0x1D6E6 => array(0x3B5), 0x1D6E7 => array(0x3B6), 0x1D6E8 => array(0x3B7), + 0x1D6E9 => array(0x3B8), 0x1D6EA => array(0x3B9), 0x1D6EB => array(0x3BA), + 0x1D6EC => array(0x3BB), 0x1D6ED => array(0x3BC), 0x1D6EE => array(0x3BD), + 0x1D6EF => array(0x3BE), 0x1D6F0 => array(0x3BF), 0x1D6F1 => array(0x3C0), + 0x1D6F2 => array(0x3C1), 0x1D6F3 => array(0x3B8), 0x1D6F4 => array(0x3C3), + 0x1D6F5 => array(0x3C4), 0x1D6F6 => array(0x3C5), 0x1D6F7 => array(0x3C6), + 0x1D6F8 => array(0x3C7), 0x1D6F9 => array(0x3C8), 0x1D6FA => array(0x3C9), + 0x1D6FB => array(0x2207), 0x1D6FC => array(0x3B1), 0x1D6FD => array(0x3B2), + 0x1D6FE => array(0x3B3), 0x1D6FF => array(0x3B4), 0x1D700 => array(0x3B5), + 0x1D701 => array(0x3B6), 0x1D702 => array(0x3B7), 0x1D703 => array(0x3B8), + 0x1D704 => array(0x3B9), 0x1D705 => array(0x3BA), 0x1D706 => array(0x3BB), + 0x1D707 => array(0x3BC), 0x1D708 => array(0x3BD), 0x1D709 => array(0x3BE), + 0x1D70A => array(0x3BF), 0x1D70B => array(0x3C0), 0x1D70C => array(0x3C1), + 0x1D70D => array(0x3C3), 0x1D70E => array(0x3C3), 0x1D70F => array(0x3C4), + 0x1D710 => array(0x3C5), 0x1D711 => array(0x3C6), 0x1D712 => array(0x3C7), + 0x1D713 => array(0x3C8), 0x1D714 => array(0x3C9), 0x1D715 => array(0x2202), + 0x1D716 => array(0x3B5), 0x1D717 => array(0x3B8), 0x1D718 => array(0x3BA), + 0x1D719 => array(0x3C6), 0x1D71A => array(0x3C1), 0x1D71B => array(0x3C0), + 0x1D71C => array(0x3B1), 0x1D71D => array(0x3B2), 0x1D71E => array(0x3B3), + 0x1D71F => array(0x3B4), 0x1D720 => array(0x3B5), 0x1D721 => array(0x3B6), + 0x1D722 => array(0x3B7), 0x1D723 => array(0x3B8), 0x1D724 => array(0x3B9), + 0x1D725 => array(0x3BA), 0x1D726 => array(0x3BB), 0x1D727 => array(0x3BC), + 0x1D728 => array(0x3BD), 0x1D729 => array(0x3BE), 0x1D72A => array(0x3BF), + 0x1D72B => array(0x3C0), 0x1D72C => array(0x3C1), 0x1D72D => array(0x3B8), + 0x1D72E => array(0x3C3), 0x1D72F => array(0x3C4), 0x1D730 => array(0x3C5), + 0x1D731 => array(0x3C6), 0x1D732 => array(0x3C7), 0x1D733 => array(0x3C8), + 0x1D734 => array(0x3C9), 0x1D735 => array(0x2207), 0x1D736 => array(0x3B1), + 0x1D737 => array(0x3B2), 0x1D738 => array(0x3B3), 0x1D739 => array(0x3B4), + 0x1D73A => array(0x3B5), 0x1D73B => array(0x3B6), 0x1D73C => array(0x3B7), + 0x1D73D => array(0x3B8), 0x1D73E => array(0x3B9), 0x1D73F => array(0x3BA), + 0x1D740 => array(0x3BB), 0x1D741 => array(0x3BC), 0x1D742 => array(0x3BD), + 0x1D743 => array(0x3BE), 0x1D744 => array(0x3BF), 0x1D745 => array(0x3C0), + 0x1D746 => array(0x3C1), 0x1D747 => array(0x3C3), 0x1D748 => array(0x3C3), + 0x1D749 => array(0x3C4), 0x1D74A => array(0x3C5), 0x1D74B => array(0x3C6), + 0x1D74C => array(0x3C7), 0x1D74D => array(0x3C8), 0x1D74E => array(0x3C9), + 0x1D74F => array(0x2202), 0x1D750 => array(0x3B5), 0x1D751 => array(0x3B8), + 0x1D752 => array(0x3BA), 0x1D753 => array(0x3C6), 0x1D754 => array(0x3C1), + 0x1D755 => array(0x3C0), 0x1D756 => array(0x3B1), 0x1D757 => array(0x3B2), + 0x1D758 => array(0x3B3), 0x1D759 => array(0x3B4), 0x1D75A => array(0x3B5), + 0x1D75B => array(0x3B6), 0x1D75C => array(0x3B7), 0x1D75D => array(0x3B8), + 0x1D75E => array(0x3B9), 0x1D75F => array(0x3BA), 0x1D760 => array(0x3BB), + 0x1D761 => array(0x3BC), 0x1D762 => array(0x3BD), 0x1D763 => array(0x3BE), + 0x1D764 => array(0x3BF), 0x1D765 => array(0x3C0), 0x1D766 => array(0x3C1), + 0x1D767 => array(0x3B8), 0x1D768 => array(0x3C3), 0x1D769 => array(0x3C4), + 0x1D76A => array(0x3C5), 0x1D76B => array(0x3C6), 0x1D76C => array(0x3C7), + 0x1D76D => array(0x3C8), 0x1D76E => array(0x3C9), 0x1D76F => array(0x2207), + 0x1D770 => array(0x3B1), 0x1D771 => array(0x3B2), 0x1D772 => array(0x3B3), + 0x1D773 => array(0x3B4), 0x1D774 => array(0x3B5), 0x1D775 => array(0x3B6), + 0x1D776 => array(0x3B7), 0x1D777 => array(0x3B8), 0x1D778 => array(0x3B9), + 0x1D779 => array(0x3BA), 0x1D77A => array(0x3BB), 0x1D77B => array(0x3BC), + 0x1D77C => array(0x3BD), 0x1D77D => array(0x3BE), 0x1D77E => array(0x3BF), + 0x1D77F => array(0x3C0), 0x1D780 => array(0x3C1), 0x1D781 => array(0x3C3), + 0x1D782 => array(0x3C3), 0x1D783 => array(0x3C4), 0x1D784 => array(0x3C5), + 0x1D785 => array(0x3C6), 0x1D786 => array(0x3C7), 0x1D787 => array(0x3C8), + 0x1D788 => array(0x3C9), 0x1D789 => array(0x2202), 0x1D78A => array(0x3B5), + 0x1D78B => array(0x3B8), 0x1D78C => array(0x3BA), 0x1D78D => array(0x3C6), + 0x1D78E => array(0x3C1), 0x1D78F => array(0x3C0), 0x1D790 => array(0x3B1), + 0x1D791 => array(0x3B2), 0x1D792 => array(0x3B3), 0x1D793 => array(0x3B4), + 0x1D794 => array(0x3B5), 0x1D795 => array(0x3B6), 0x1D796 => array(0x3B7), + 0x1D797 => array(0x3B8), 0x1D798 => array(0x3B9), 0x1D799 => array(0x3BA), + 0x1D79A => array(0x3BB), 0x1D79B => array(0x3BC), 0x1D79C => array(0x3BD), + 0x1D79D => array(0x3BE), 0x1D79E => array(0x3BF), 0x1D79F => array(0x3C0), + 0x1D7A0 => array(0x3C1), 0x1D7A1 => array(0x3B8), 0x1D7A2 => array(0x3C3), + 0x1D7A3 => array(0x3C4), 0x1D7A4 => array(0x3C5), 0x1D7A5 => array(0x3C6), + 0x1D7A6 => array(0x3C7), 0x1D7A7 => array(0x3C8), 0x1D7A8 => array(0x3C9), + 0x1D7A9 => array(0x2207), 0x1D7AA => array(0x3B1), 0x1D7AB => array(0x3B2), + 0x1D7AC => array(0x3B3), 0x1D7AD => array(0x3B4), 0x1D7AE => array(0x3B5), + 0x1D7AF => array(0x3B6), 0x1D7B0 => array(0x3B7), 0x1D7B1 => array(0x3B8), + 0x1D7B2 => array(0x3B9), 0x1D7B3 => array(0x3BA), 0x1D7B4 => array(0x3BB), + 0x1D7B5 => array(0x3BC), 0x1D7B6 => array(0x3BD), 0x1D7B7 => array(0x3BE), + 0x1D7B8 => array(0x3BF), 0x1D7B9 => array(0x3C0), 0x1D7BA => array(0x3C1), + 0x1D7BB => array(0x3C3), 0x1D7BC => array(0x3C3), 0x1D7BD => array(0x3C4), + 0x1D7BE => array(0x3C5), 0x1D7BF => array(0x3C6), 0x1D7C0 => array(0x3C7), + 0x1D7C1 => array(0x3C8), 0x1D7C2 => array(0x3C9), 0x1D7C3 => array(0x2202), + 0x1D7C4 => array(0x3B5), 0x1D7C5 => array(0x3B8), 0x1D7C6 => array(0x3BA), + 0x1D7C7 => array(0x3C6), 0x1D7C8 => array(0x3C1), 0x1D7C9 => array(0x3C0), + 0x1D7CA => array(0x3DD), 0x1D7CB => array(0x3DD), 0x1D7CE => array(0x30), + 0x1D7CF => array(0x31), 0x1D7D0 => array(0x32), 0x1D7D1 => array(0x33), + 0x1D7D2 => array(0x34), 0x1D7D3 => array(0x35), 0x1D7D4 => array(0x36), + 0x1D7D5 => array(0x37), 0x1D7D6 => array(0x38), 0x1D7D7 => array(0x39), + 0x1D7D8 => array(0x30), 0x1D7D9 => array(0x31), 0x1D7DA => array(0x32), + 0x1D7DB => array(0x33), 0x1D7DC => array(0x34), 0x1D7DD => array(0x35), + 0x1D7DE => array(0x36), 0x1D7DF => array(0x37), 0x1D7E0 => array(0x38), + 0x1D7E1 => array(0x39), 0x1D7E2 => array(0x30), 0x1D7E3 => array(0x31), + 0x1D7E4 => array(0x32), 0x1D7E5 => array(0x33), 0x1D7E6 => array(0x34), + 0x1D7E7 => array(0x35), 0x1D7E8 => array(0x36), 0x1D7E9 => array(0x37), + 0x1D7EA => array(0x38), 0x1D7EB => array(0x39), 0x1D7EC => array(0x30), + 0x1D7ED => array(0x31), 0x1D7EE => array(0x32), 0x1D7EF => array(0x33), + 0x1D7F0 => array(0x34), 0x1D7F1 => array(0x35), 0x1D7F2 => array(0x36), + 0x1D7F3 => array(0x37), 0x1D7F4 => array(0x38), 0x1D7F5 => array(0x39), + 0x1D7F6 => array(0x30), 0x1D7F7 => array(0x31), 0x1D7F8 => array(0x32), + 0x1D7F9 => array(0x33), 0x1D7FA => array(0x34), 0x1D7FB => array(0x35), + 0x1D7FC => array(0x36), 0x1D7FD => array(0x37), 0x1D7FE => array(0x38), + 0x1D7FF => array(0x39), 0x1EE00 => array(0x627), 0x1EE01 => array(0x628), + 0x1EE02 => array(0x62C), 0x1EE03 => array(0x62F), 0x1EE05 => array(0x648), + 0x1EE06 => array(0x632), 0x1EE07 => array(0x62D), 0x1EE08 => array(0x637), + 0x1EE09 => array(0x64A), 0x1EE0A => array(0x643), 0x1EE0B => array(0x644), + 0x1EE0C => array(0x645), 0x1EE0D => array(0x646), 0x1EE0E => array(0x633), + 0x1EE0F => array(0x639), 0x1EE10 => array(0x641), 0x1EE11 => array(0x635), + 0x1EE12 => array(0x642), 0x1EE13 => array(0x631), 0x1EE14 => array(0x634), + 0x1EE15 => array(0x62A), 0x1EE16 => array(0x62B), 0x1EE17 => array(0x62E), + 0x1EE18 => array(0x630), 0x1EE19 => array(0x636), 0x1EE1A => array(0x638), + 0x1EE1B => array(0x63A), 0x1EE1C => array(0x66E), 0x1EE1D => array(0x6BA), + 0x1EE1E => array(0x6A1), 0x1EE1F => array(0x66F), 0x1EE21 => array(0x628), + 0x1EE22 => array(0x62C), 0x1EE24 => array(0x647), 0x1EE27 => array(0x62D), + 0x1EE29 => array(0x64A), 0x1EE2A => array(0x643), 0x1EE2B => array(0x644), + 0x1EE2C => array(0x645), 0x1EE2D => array(0x646), 0x1EE2E => array(0x633), + 0x1EE2F => array(0x639), 0x1EE30 => array(0x641), 0x1EE31 => array(0x635), + 0x1EE32 => array(0x642), 0x1EE34 => array(0x634), 0x1EE35 => array(0x62A), + 0x1EE36 => array(0x62B), 0x1EE37 => array(0x62E), 0x1EE39 => array(0x636), + 0x1EE3B => array(0x63A), 0x1EE42 => array(0x62C), 0x1EE47 => array(0x62D), + 0x1EE49 => array(0x64A), 0x1EE4B => array(0x644), 0x1EE4D => array(0x646), + 0x1EE4E => array(0x633), 0x1EE4F => array(0x639), 0x1EE51 => array(0x635), + 0x1EE52 => array(0x642), 0x1EE54 => array(0x634), 0x1EE57 => array(0x62E), + 0x1EE59 => array(0x636), 0x1EE5B => array(0x63A), 0x1EE5D => array(0x6BA), + 0x1EE5F => array(0x66F), 0x1EE61 => array(0x628), 0x1EE62 => array(0x62C), + 0x1EE64 => array(0x647), 0x1EE67 => array(0x62D), 0x1EE68 => array(0x637), + 0x1EE69 => array(0x64A), 0x1EE6A => array(0x643), 0x1EE6C => array(0x645), + 0x1EE6D => array(0x646), 0x1EE6E => array(0x633), 0x1EE6F => array(0x639), + 0x1EE70 => array(0x641), 0x1EE71 => array(0x635), 0x1EE72 => array(0x642), + 0x1EE74 => array(0x634), 0x1EE75 => array(0x62A), 0x1EE76 => array(0x62B), + 0x1EE77 => array(0x62E), 0x1EE79 => array(0x636), 0x1EE7A => array(0x638), + 0x1EE7B => array(0x63A), 0x1EE7C => array(0x66E), 0x1EE7E => array(0x6A1), + 0x1EE80 => array(0x627), 0x1EE81 => array(0x628), 0x1EE82 => array(0x62C), + 0x1EE83 => array(0x62F), 0x1EE84 => array(0x647), 0x1EE85 => array(0x648), + 0x1EE86 => array(0x632), 0x1EE87 => array(0x62D), 0x1EE88 => array(0x637), + 0x1EE89 => array(0x64A), 0x1EE8B => array(0x644), 0x1EE8C => array(0x645), + 0x1EE8D => array(0x646), 0x1EE8E => array(0x633), 0x1EE8F => array(0x639), + 0x1EE90 => array(0x641), 0x1EE91 => array(0x635), 0x1EE92 => array(0x642), + 0x1EE93 => array(0x631), 0x1EE94 => array(0x634), 0x1EE95 => array(0x62A), + 0x1EE96 => array(0x62B), 0x1EE97 => array(0x62E), 0x1EE98 => array(0x630), + 0x1EE99 => array(0x636), 0x1EE9A => array(0x638), 0x1EE9B => array(0x63A), + 0x1EEA1 => array(0x628), 0x1EEA2 => array(0x62C), 0x1EEA3 => array(0x62F), + 0x1EEA5 => array(0x648), 0x1EEA6 => array(0x632), 0x1EEA7 => array(0x62D), + 0x1EEA8 => array(0x637), 0x1EEA9 => array(0x64A), 0x1EEAB => array(0x644), + 0x1EEAC => array(0x645), 0x1EEAD => array(0x646), 0x1EEAE => array(0x633), + 0x1EEAF => array(0x639), 0x1EEB0 => array(0x641), 0x1EEB1 => array(0x635), + 0x1EEB2 => array(0x642), 0x1EEB3 => array(0x631), 0x1EEB4 => array(0x634), + 0x1EEB5 => array(0x62A), 0x1EEB6 => array(0x62B), 0x1EEB7 => array(0x62E), + 0x1EEB8 => array(0x630), 0x1EEB9 => array(0x636), 0x1EEBA => array(0x638), + 0x1EEBB => array(0x63A), 0x1F12A => array(0x3014, 0x73, 0x3015), 0x1F12B => array(0x63), + 0x1F12C => array(0x72), 0x1F12D => array(0x63, 0x64), 0x1F12E => array(0x77, 0x7A), + 0x1F130 => array(0x61), 0x1F131 => array(0x62), 0x1F132 => array(0x63), + 0x1F133 => array(0x64), 0x1F134 => array(0x65), 0x1F135 => array(0x66), + 0x1F136 => array(0x67), 0x1F137 => array(0x68), 0x1F138 => array(0x69), + 0x1F139 => array(0x6A), 0x1F13A => array(0x6B), 0x1F13B => array(0x6C), + 0x1F13C => array(0x6D), 0x1F13D => array(0x6E), 0x1F13E => array(0x6F), + 0x1F13F => array(0x70), 0x1F140 => array(0x71), 0x1F141 => array(0x72), + 0x1F142 => array(0x73), 0x1F143 => array(0x74), 0x1F144 => array(0x75), + 0x1F145 => array(0x76), 0x1F146 => array(0x77), 0x1F147 => array(0x78), + 0x1F148 => array(0x79), 0x1F149 => array(0x7A), 0x1F14A => array(0x68, 0x76), + 0x1F14B => array(0x6D, 0x76), 0x1F14C => array(0x73, 0x64), 0x1F14D => array(0x73, 0x73), + 0x1F14E => array(0x70, 0x70, 0x76), 0x1F14F => array(0x77, 0x63), 0x1F16A => array(0x6D, 0x63), + 0x1F16B => array(0x6D, 0x64), 0x1F190 => array(0x64, 0x6A), 0x1F200 => array(0x307B, 0x304B), + 0x1F201 => array(0x30B3, 0x30B3), 0x1F202 => array(0x30B5), 0x1F210 => array(0x624B), + 0x1F211 => array(0x5B57), 0x1F212 => array(0x53CC), 0x1F213 => array(0x30C7), + 0x1F214 => array(0x4E8C), 0x1F215 => array(0x591A), 0x1F216 => array(0x89E3), + 0x1F217 => array(0x5929), 0x1F218 => array(0x4EA4), 0x1F219 => array(0x6620), + 0x1F21A => array(0x7121), 0x1F21B => array(0x6599), 0x1F21C => array(0x524D), + 0x1F21D => array(0x5F8C), 0x1F21E => array(0x518D), 0x1F21F => array(0x65B0), + 0x1F220 => array(0x521D), 0x1F221 => array(0x7D42), 0x1F222 => array(0x751F), + 0x1F223 => array(0x8CA9), 0x1F224 => array(0x58F0), 0x1F225 => array(0x5439), + 0x1F226 => array(0x6F14), 0x1F227 => array(0x6295), 0x1F228 => array(0x6355), + 0x1F229 => array(0x4E00), 0x1F22A => array(0x4E09), 0x1F22B => array(0x904A), + 0x1F22C => array(0x5DE6), 0x1F22D => array(0x4E2D), 0x1F22E => array(0x53F3), + 0x1F22F => array(0x6307), 0x1F230 => array(0x8D70), 0x1F231 => array(0x6253), + 0x1F232 => array(0x7981), 0x1F233 => array(0x7A7A), 0x1F234 => array(0x5408), + 0x1F235 => array(0x6E80), 0x1F236 => array(0x6709), 0x1F237 => array(0x6708), + 0x1F238 => array(0x7533), 0x1F239 => array(0x5272), 0x1F23A => array(0x55B6), + 0x1F240 => array(0x3014, 0x672C, 0x3015), 0x1F241 => array(0x3014, 0x4E09, 0x3015), 0x1F242 => array(0x3014, 0x4E8C, 0x3015), + 0x1F243 => array(0x3014, 0x5B89, 0x3015), 0x1F244 => array(0x3014, 0x70B9, 0x3015), 0x1F245 => array(0x3014, 0x6253, 0x3015), + 0x1F246 => array(0x3014, 0x76D7, 0x3015), 0x1F247 => array(0x3014, 0x52DD, 0x3015), 0x1F248 => array(0x3014, 0x6557, 0x3015), + 0x1F250 => array(0x5F97), 0x1F251 => array(0x53EF), 0x2F800 => array(0x4E3D), + 0x2F801 => array(0x4E38), 0x2F802 => array(0x4E41), 0x2F803 => array(0x20122), + 0x2F804 => array(0x4F60), 0x2F805 => array(0x4FAE), 0x2F806 => array(0x4FBB), + 0x2F807 => array(0x5002), 0x2F808 => array(0x507A), 0x2F809 => array(0x5099), + 0x2F80A => array(0x50E7), 0x2F80B => array(0x50CF), 0x2F80C => array(0x349E), + 0x2F80D => array(0x2063A), 0x2F80E => array(0x514D), 0x2F80F => array(0x5154), + 0x2F810 => array(0x5164), 0x2F811 => array(0x5177), 0x2F812 => array(0x2051C), + 0x2F813 => array(0x34B9), 0x2F814 => array(0x5167), 0x2F815 => array(0x518D), + 0x2F816 => array(0x2054B), 0x2F817 => array(0x5197), 0x2F818 => array(0x51A4), + 0x2F819 => array(0x4ECC), 0x2F81A => array(0x51AC), 0x2F81B => array(0x51B5), + 0x2F81C => array(0x291DF), 0x2F81D => array(0x51F5), 0x2F81E => array(0x5203), + 0x2F81F => array(0x34DF), 0x2F820 => array(0x523B), 0x2F821 => array(0x5246), + 0x2F822 => array(0x5272), 0x2F823 => array(0x5277), 0x2F824 => array(0x3515), + 0x2F825 => array(0x52C7), 0x2F826 => array(0x52C9), 0x2F827 => array(0x52E4), + 0x2F828 => array(0x52FA), 0x2F829 => array(0x5305), 0x2F82A => array(0x5306), + 0x2F82B => array(0x5317), 0x2F82C => array(0x5349), 0x2F82D => array(0x5351), + 0x2F82E => array(0x535A), 0x2F82F => array(0x5373), 0x2F830 => array(0x537D), + 0x2F831 => array(0x537F), 0x2F832 => array(0x537F), 0x2F833 => array(0x537F), + 0x2F834 => array(0x20A2C), 0x2F835 => array(0x7070), 0x2F836 => array(0x53CA), + 0x2F837 => array(0x53DF), 0x2F838 => array(0x20B63), 0x2F839 => array(0x53EB), + 0x2F83A => array(0x53F1), 0x2F83B => array(0x5406), 0x2F83C => array(0x549E), + 0x2F83D => array(0x5438), 0x2F83E => array(0x5448), 0x2F83F => array(0x5468), + 0x2F840 => array(0x54A2), 0x2F841 => array(0x54F6), 0x2F842 => array(0x5510), + 0x2F843 => array(0x5553), 0x2F844 => array(0x5563), 0x2F845 => array(0x5584), + 0x2F846 => array(0x5584), 0x2F847 => array(0x5599), 0x2F848 => array(0x55AB), + 0x2F849 => array(0x55B3), 0x2F84A => array(0x55C2), 0x2F84B => array(0x5716), + 0x2F84C => array(0x5606), 0x2F84D => array(0x5717), 0x2F84E => array(0x5651), + 0x2F84F => array(0x5674), 0x2F850 => array(0x5207), 0x2F851 => array(0x58EE), + 0x2F852 => array(0x57CE), 0x2F853 => array(0x57F4), 0x2F854 => array(0x580D), + 0x2F855 => array(0x578B), 0x2F856 => array(0x5832), 0x2F857 => array(0x5831), + 0x2F858 => array(0x58AC), 0x2F859 => array(0x214E4), 0x2F85A => array(0x58F2), + 0x2F85B => array(0x58F7), 0x2F85C => array(0x5906), 0x2F85D => array(0x591A), + 0x2F85E => array(0x5922), 0x2F85F => array(0x5962), 0x2F860 => array(0x216A8), + 0x2F861 => array(0x216EA), 0x2F862 => array(0x59EC), 0x2F863 => array(0x5A1B), + 0x2F864 => array(0x5A27), 0x2F865 => array(0x59D8), 0x2F866 => array(0x5A66), + 0x2F867 => array(0x36EE), 0x2F869 => array(0x5B08), 0x2F86A => array(0x5B3E), + 0x2F86B => array(0x5B3E), 0x2F86C => array(0x219C8), 0x2F86D => array(0x5BC3), + 0x2F86E => array(0x5BD8), 0x2F86F => array(0x5BE7), 0x2F870 => array(0x5BF3), + 0x2F871 => array(0x21B18), 0x2F872 => array(0x5BFF), 0x2F873 => array(0x5C06), + 0x2F875 => array(0x5C22), 0x2F876 => array(0x3781), 0x2F877 => array(0x5C60), + 0x2F878 => array(0x5C6E), 0x2F879 => array(0x5CC0), 0x2F87A => array(0x5C8D), + 0x2F87B => array(0x21DE4), 0x2F87C => array(0x5D43), 0x2F87D => array(0x21DE6), + 0x2F87E => array(0x5D6E), 0x2F87F => array(0x5D6B), 0x2F880 => array(0x5D7C), + 0x2F881 => array(0x5DE1), 0x2F882 => array(0x5DE2), 0x2F883 => array(0x382F), + 0x2F884 => array(0x5DFD), 0x2F885 => array(0x5E28), 0x2F886 => array(0x5E3D), + 0x2F887 => array(0x5E69), 0x2F888 => array(0x3862), 0x2F889 => array(0x22183), + 0x2F88A => array(0x387C), 0x2F88B => array(0x5EB0), 0x2F88C => array(0x5EB3), + 0x2F88D => array(0x5EB6), 0x2F88E => array(0x5ECA), 0x2F88F => array(0x2A392), + 0x2F890 => array(0x5EFE), 0x2F891 => array(0x22331), 0x2F892 => array(0x22331), + 0x2F893 => array(0x8201), 0x2F894 => array(0x5F22), 0x2F895 => array(0x5F22), + 0x2F896 => array(0x38C7), 0x2F897 => array(0x232B8), 0x2F898 => array(0x261DA), + 0x2F899 => array(0x5F62), 0x2F89A => array(0x5F6B), 0x2F89B => array(0x38E3), + 0x2F89C => array(0x5F9A), 0x2F89D => array(0x5FCD), 0x2F89E => array(0x5FD7), + 0x2F89F => array(0x5FF9), 0x2F8A0 => array(0x6081), 0x2F8A1 => array(0x393A), + 0x2F8A2 => array(0x391C), 0x2F8A3 => array(0x6094), 0x2F8A4 => array(0x226D4), + 0x2F8A5 => array(0x60C7), 0x2F8A6 => array(0x6148), 0x2F8A7 => array(0x614C), + 0x2F8A8 => array(0x614E), 0x2F8A9 => array(0x614C), 0x2F8AA => array(0x617A), + 0x2F8AB => array(0x618E), 0x2F8AC => array(0x61B2), 0x2F8AD => array(0x61A4), + 0x2F8AE => array(0x61AF), 0x2F8AF => array(0x61DE), 0x2F8B0 => array(0x61F2), + 0x2F8B1 => array(0x61F6), 0x2F8B2 => array(0x6210), 0x2F8B3 => array(0x621B), + 0x2F8B4 => array(0x625D), 0x2F8B5 => array(0x62B1), 0x2F8B6 => array(0x62D4), + 0x2F8B7 => array(0x6350), 0x2F8B8 => array(0x22B0C), 0x2F8B9 => array(0x633D), + 0x2F8BA => array(0x62FC), 0x2F8BB => array(0x6368), 0x2F8BC => array(0x6383), + 0x2F8BD => array(0x63E4), 0x2F8BE => array(0x22BF1), 0x2F8BF => array(0x6422), + 0x2F8C0 => array(0x63C5), 0x2F8C1 => array(0x63A9), 0x2F8C2 => array(0x3A2E), + 0x2F8C3 => array(0x6469), 0x2F8C4 => array(0x647E), 0x2F8C5 => array(0x649D), + 0x2F8C6 => array(0x6477), 0x2F8C7 => array(0x3A6C), 0x2F8C8 => array(0x654F), + 0x2F8C9 => array(0x656C), 0x2F8CA => array(0x2300A), 0x2F8CB => array(0x65E3), + 0x2F8CC => array(0x66F8), 0x2F8CD => array(0x6649), 0x2F8CE => array(0x3B19), + 0x2F8CF => array(0x6691), 0x2F8D0 => array(0x3B08), 0x2F8D1 => array(0x3AE4), + 0x2F8D2 => array(0x5192), 0x2F8D3 => array(0x5195), 0x2F8D4 => array(0x6700), + 0x2F8D5 => array(0x669C), 0x2F8D6 => array(0x80AD), 0x2F8D7 => array(0x43D9), + 0x2F8D8 => array(0x6717), 0x2F8D9 => array(0x671B), 0x2F8DA => array(0x6721), + 0x2F8DB => array(0x675E), 0x2F8DC => array(0x6753), 0x2F8DD => array(0x233C3), + 0x2F8DE => array(0x3B49), 0x2F8DF => array(0x67FA), 0x2F8E0 => array(0x6785), + 0x2F8E1 => array(0x6852), 0x2F8E2 => array(0x6885), 0x2F8E3 => array(0x2346D), + 0x2F8E4 => array(0x688E), 0x2F8E5 => array(0x681F), 0x2F8E6 => array(0x6914), + 0x2F8E7 => array(0x3B9D), 0x2F8E8 => array(0x6942), 0x2F8E9 => array(0x69A3), + 0x2F8EA => array(0x69EA), 0x2F8EB => array(0x6AA8), 0x2F8EC => array(0x236A3), + 0x2F8ED => array(0x6ADB), 0x2F8EE => array(0x3C18), 0x2F8EF => array(0x6B21), + 0x2F8F0 => array(0x238A7), 0x2F8F1 => array(0x6B54), 0x2F8F2 => array(0x3C4E), + 0x2F8F3 => array(0x6B72), 0x2F8F4 => array(0x6B9F), 0x2F8F5 => array(0x6BBA), + 0x2F8F6 => array(0x6BBB), 0x2F8F7 => array(0x23A8D), 0x2F8F8 => array(0x21D0B), + 0x2F8F9 => array(0x23AFA), 0x2F8FA => array(0x6C4E), 0x2F8FB => array(0x23CBC), + 0x2F8FC => array(0x6CBF), 0x2F8FD => array(0x6CCD), 0x2F8FE => array(0x6C67), + 0x2F8FF => array(0x6D16), 0x2F900 => array(0x6D3E), 0x2F901 => array(0x6D77), + 0x2F902 => array(0x6D41), 0x2F903 => array(0x6D69), 0x2F904 => array(0x6D78), + 0x2F905 => array(0x6D85), 0x2F906 => array(0x23D1E), 0x2F907 => array(0x6D34), + 0x2F908 => array(0x6E2F), 0x2F909 => array(0x6E6E), 0x2F90A => array(0x3D33), + 0x2F90B => array(0x6ECB), 0x2F90C => array(0x6EC7), 0x2F90D => array(0x23ED1), + 0x2F90E => array(0x6DF9), 0x2F90F => array(0x6F6E), 0x2F910 => array(0x23F5E), + 0x2F911 => array(0x23F8E), 0x2F912 => array(0x6FC6), 0x2F913 => array(0x7039), + 0x2F914 => array(0x701E), 0x2F915 => array(0x701B), 0x2F916 => array(0x3D96), + 0x2F917 => array(0x704A), 0x2F918 => array(0x707D), 0x2F919 => array(0x7077), + 0x2F91A => array(0x70AD), 0x2F91B => array(0x20525), 0x2F91C => array(0x7145), + 0x2F91D => array(0x24263), 0x2F91E => array(0x719C), 0x2F920 => array(0x7228), + 0x2F921 => array(0x7235), 0x2F922 => array(0x7250), 0x2F923 => array(0x24608), + 0x2F924 => array(0x7280), 0x2F925 => array(0x7295), 0x2F926 => array(0x24735), + 0x2F927 => array(0x24814), 0x2F928 => array(0x737A), 0x2F929 => array(0x738B), + 0x2F92A => array(0x3EAC), 0x2F92B => array(0x73A5), 0x2F92C => array(0x3EB8), + 0x2F92D => array(0x3EB8), 0x2F92E => array(0x7447), 0x2F92F => array(0x745C), + 0x2F930 => array(0x7471), 0x2F931 => array(0x7485), 0x2F932 => array(0x74CA), + 0x2F933 => array(0x3F1B), 0x2F934 => array(0x7524), 0x2F935 => array(0x24C36), + 0x2F936 => array(0x753E), 0x2F937 => array(0x24C92), 0x2F938 => array(0x7570), + 0x2F939 => array(0x2219F), 0x2F93A => array(0x7610), 0x2F93B => array(0x24FA1), + 0x2F93C => array(0x24FB8), 0x2F93D => array(0x25044), 0x2F93E => array(0x3FFC), + 0x2F93F => array(0x4008), 0x2F940 => array(0x76F4), 0x2F941 => array(0x250F3), + 0x2F942 => array(0x250F2), 0x2F943 => array(0x25119), 0x2F944 => array(0x25133), + 0x2F945 => array(0x771E), 0x2F946 => array(0x771F), 0x2F947 => array(0x771F), + 0x2F948 => array(0x774A), 0x2F949 => array(0x4039), 0x2F94A => array(0x778B), + 0x2F94B => array(0x4046), 0x2F94C => array(0x4096), 0x2F94D => array(0x2541D), + 0x2F94E => array(0x784E), 0x2F94F => array(0x788C), 0x2F950 => array(0x78CC), + 0x2F951 => array(0x40E3), 0x2F952 => array(0x25626), 0x2F953 => array(0x7956), + 0x2F954 => array(0x2569A), 0x2F955 => array(0x256C5), 0x2F956 => array(0x798F), + 0x2F957 => array(0x79EB), 0x2F958 => array(0x412F), 0x2F959 => array(0x7A40), + 0x2F95A => array(0x7A4A), 0x2F95B => array(0x7A4F), 0x2F95C => array(0x2597C), + 0x2F95D => array(0x25AA7), 0x2F95E => array(0x25AA7), 0x2F960 => array(0x4202), + 0x2F961 => array(0x25BAB), 0x2F962 => array(0x7BC6), 0x2F963 => array(0x7BC9), + 0x2F964 => array(0x4227), 0x2F965 => array(0x25C80), 0x2F966 => array(0x7CD2), + 0x2F967 => array(0x42A0), 0x2F968 => array(0x7CE8), 0x2F969 => array(0x7CE3), + 0x2F96A => array(0x7D00), 0x2F96B => array(0x25F86), 0x2F96C => array(0x7D63), + 0x2F96D => array(0x4301), 0x2F96E => array(0x7DC7), 0x2F96F => array(0x7E02), + 0x2F970 => array(0x7E45), 0x2F971 => array(0x4334), 0x2F972 => array(0x26228), + 0x2F973 => array(0x26247), 0x2F974 => array(0x4359), 0x2F975 => array(0x262D9), + 0x2F976 => array(0x7F7A), 0x2F977 => array(0x2633E), 0x2F978 => array(0x7F95), + 0x2F979 => array(0x7FFA), 0x2F97A => array(0x8005), 0x2F97B => array(0x264DA), + 0x2F97C => array(0x26523), 0x2F97D => array(0x8060), 0x2F97E => array(0x265A8), + 0x2F97F => array(0x8070), 0x2F980 => array(0x2335F), 0x2F981 => array(0x43D5), + 0x2F982 => array(0x80B2), 0x2F983 => array(0x8103), 0x2F984 => array(0x440B), + 0x2F985 => array(0x813E), 0x2F986 => array(0x5AB5), 0x2F987 => array(0x267A7), + 0x2F988 => array(0x267B5), 0x2F989 => array(0x23393), 0x2F98A => array(0x2339C), + 0x2F98B => array(0x8201), 0x2F98C => array(0x8204), 0x2F98D => array(0x8F9E), + 0x2F98E => array(0x446B), 0x2F98F => array(0x8291), 0x2F990 => array(0x828B), + 0x2F991 => array(0x829D), 0x2F992 => array(0x52B3), 0x2F993 => array(0x82B1), + 0x2F994 => array(0x82B3), 0x2F995 => array(0x82BD), 0x2F996 => array(0x82E6), + 0x2F997 => array(0x26B3C), 0x2F998 => array(0x82E5), 0x2F999 => array(0x831D), + 0x2F99A => array(0x8363), 0x2F99B => array(0x83AD), 0x2F99C => array(0x8323), + 0x2F99D => array(0x83BD), 0x2F99E => array(0x83E7), 0x2F99F => array(0x8457), + 0x2F9A0 => array(0x8353), 0x2F9A1 => array(0x83CA), 0x2F9A2 => array(0x83CC), + 0x2F9A3 => array(0x83DC), 0x2F9A4 => array(0x26C36), 0x2F9A5 => array(0x26D6B), + 0x2F9A6 => array(0x26CD5), 0x2F9A7 => array(0x452B), 0x2F9A8 => array(0x84F1), + 0x2F9A9 => array(0x84F3), 0x2F9AA => array(0x8516), 0x2F9AB => array(0x273CA), + 0x2F9AC => array(0x8564), 0x2F9AD => array(0x26F2C), 0x2F9AE => array(0x455D), + 0x2F9AF => array(0x4561), 0x2F9B0 => array(0x26FB1), 0x2F9B1 => array(0x270D2), + 0x2F9B2 => array(0x456B), 0x2F9B3 => array(0x8650), 0x2F9B4 => array(0x865C), + 0x2F9B5 => array(0x8667), 0x2F9B6 => array(0x8669), 0x2F9B7 => array(0x86A9), + 0x2F9B8 => array(0x8688), 0x2F9B9 => array(0x870E), 0x2F9BA => array(0x86E2), + 0x2F9BB => array(0x8779), 0x2F9BC => array(0x8728), 0x2F9BD => array(0x876B), + 0x2F9BE => array(0x8786), 0x2F9C0 => array(0x87E1), 0x2F9C1 => array(0x8801), + 0x2F9C2 => array(0x45F9), 0x2F9C3 => array(0x8860), 0x2F9C4 => array(0x8863), + 0x2F9C5 => array(0x27667), 0x2F9C6 => array(0x88D7), 0x2F9C7 => array(0x88DE), + 0x2F9C8 => array(0x4635), 0x2F9C9 => array(0x88FA), 0x2F9CA => array(0x34BB), + 0x2F9CB => array(0x278AE), 0x2F9CC => array(0x27966), 0x2F9CD => array(0x46BE), + 0x2F9CE => array(0x46C7), 0x2F9CF => array(0x8AA0), 0x2F9D0 => array(0x8AED), + 0x2F9D1 => array(0x8B8A), 0x2F9D2 => array(0x8C55), 0x2F9D3 => array(0x27CA8), + 0x2F9D4 => array(0x8CAB), 0x2F9D5 => array(0x8CC1), 0x2F9D6 => array(0x8D1B), + 0x2F9D7 => array(0x8D77), 0x2F9D8 => array(0x27F2F), 0x2F9D9 => array(0x20804), + 0x2F9DA => array(0x8DCB), 0x2F9DB => array(0x8DBC), 0x2F9DC => array(0x8DF0), + 0x2F9DD => array(0x208DE), 0x2F9DE => array(0x8ED4), 0x2F9DF => array(0x8F38), + 0x2F9E0 => array(0x285D2), 0x2F9E1 => array(0x285ED), 0x2F9E2 => array(0x9094), + 0x2F9E3 => array(0x90F1), 0x2F9E4 => array(0x9111), 0x2F9E5 => array(0x2872E), + 0x2F9E6 => array(0x911B), 0x2F9E7 => array(0x9238), 0x2F9E8 => array(0x92D7), + 0x2F9E9 => array(0x92D8), 0x2F9EA => array(0x927C), 0x2F9EB => array(0x93F9), + 0x2F9EC => array(0x9415), 0x2F9ED => array(0x28BFA), 0x2F9EE => array(0x958B), + 0x2F9EF => array(0x4995), 0x2F9F0 => array(0x95B7), 0x2F9F1 => array(0x28D77), + 0x2F9F2 => array(0x49E6), 0x2F9F3 => array(0x96C3), 0x2F9F4 => array(0x5DB2), + 0x2F9F5 => array(0x9723), 0x2F9F6 => array(0x29145), 0x2F9F7 => array(0x2921A), + 0x2F9F8 => array(0x4A6E), 0x2F9F9 => array(0x4A76), 0x2F9FA => array(0x97E0), + 0x2F9FB => array(0x2940A), 0x2F9FC => array(0x4AB2), 0x2F9FD => array(0x29496), + 0x2F9FE => array(0x980B), 0x2F9FF => array(0x980B), 0x2FA00 => array(0x9829), + 0x2FA01 => array(0x295B6), 0x2FA02 => array(0x98E2), 0x2FA03 => array(0x4B33), + 0x2FA04 => array(0x9929), 0x2FA05 => array(0x99A7), 0x2FA06 => array(0x99C2), + 0x2FA07 => array(0x99FE), 0x2FA08 => array(0x4BCE), 0x2FA09 => array(0x29B30), + 0x2FA0A => array(0x9B12), 0x2FA0B => array(0x9C40), 0x2FA0C => array(0x9CFD), + 0x2FA0D => array(0x4CCE), 0x2FA0E => array(0x4CED), 0x2FA0F => array(0x9D67), + 0x2FA10 => array(0x2A0CE), 0x2FA11 => array(0x4CF8), 0x2FA12 => array(0x2A105), + 0x2FA13 => array(0x2A20E), 0x2FA14 => array(0x2A291), 0x2FA15 => array(0x9EBB), + 0x2FA16 => array(0x4D56), 0x2FA17 => array(0x9EF9), 0x2FA18 => array(0x9EFE), + 0x2FA19 => array(0x9F05), 0x2FA1A => array(0x9F0F), 0x2FA1B => array(0x9F16), + 0x2FA1C => array(0x9F3B), 0x2FA1D => array(0x2A600) + ), + 'norm_combcls' => array(0x334 => 1, 0x335 => 1, 0x336 => 1, 0x337 => 1, + 0x338 => 1, 0x93C => 7, 0x9BC => 7, 0xA3C => 7, 0xABC => 7, + 0xB3C => 7, 0xCBC => 7, 0x1037 => 7, 0x3099 => 8, 0x309A => 8, + 0x94D => 9, 0x9CD => 9, 0xA4D => 9, 0xACD => 9, 0xB4D => 9, + 0xBCD => 9, 0xC4D => 9, 0xCCD => 9, 0xD4D => 9, 0xDCA => 9, + 0xE3A => 9, 0xF84 => 9, 0x1039 => 9, 0x1714 => 9, 0x1734 => 9, + 0x17D2 => 9, 0x5B0 => 10, 0x5B1 => 11, 0x5B2 => 12, 0x5B3 => 13, + 0x5B4 => 14, 0x5B5 => 15, 0x5B6 => 16, 0x5B7 => 17, 0x5B8 => 18, + 0x5B9 => 19, 0x5BB => 20, 0x5Bc => 21, 0x5BD => 22, 0x5BF => 23, + 0x5C1 => 24, 0x5C2 => 25, 0xFB1E => 26, 0x64B => 27, 0x64C => 28, + 0x64D => 29, 0x64E => 30, 0x64F => 31, 0x650 => 32, 0x651 => 33, + 0x652 => 34, 0x670 => 35, 0x711 => 36, 0xC55 => 84, 0xC56 => 91, + 0xE38 => 103, 0xE39 => 103, 0xE48 => 107, 0xE49 => 107, 0xE4A => 107, + 0xE4B => 107, 0xEB8 => 118, 0xEB9 => 118, 0xEC8 => 122, 0xEC9 => 122, + 0xECA => 122, 0xECB => 122, 0xF71 => 129, 0xF72 => 130, 0xF7A => 130, + 0xF7B => 130, 0xF7C => 130, 0xF7D => 130, 0xF80 => 130, 0xF74 => 132, + 0x321 => 202, 0x322 => 202, 0x327 => 202, 0x328 => 202, 0x31B => 216, + 0xF39 => 216, 0x1D165 => 216, 0x1D166 => 216, 0x1D16E => 216, 0x1D16F => 216, + 0x1D170 => 216, 0x1D171 => 216, 0x1D172 => 216, 0x302A => 218, 0x316 => 220, + 0x317 => 220, 0x318 => 220, 0x319 => 220, 0x31C => 220, 0x31D => 220, + 0x31E => 220, 0x31F => 220, 0x320 => 220, 0x323 => 220, 0x324 => 220, + 0x325 => 220, 0x326 => 220, 0x329 => 220, 0x32A => 220, 0x32B => 220, + 0x32C => 220, 0x32D => 220, 0x32E => 220, 0x32F => 220, 0x330 => 220, + 0x331 => 220, 0x332 => 220, 0x333 => 220, 0x339 => 220, 0x33A => 220, + 0x33B => 220, 0x33C => 220, 0x347 => 220, 0x348 => 220, 0x349 => 220, + 0x34D => 220, 0x34E => 220, 0x353 => 220, 0x354 => 220, 0x355 => 220, + 0x356 => 220, 0x591 => 220, 0x596 => 220, 0x59B => 220, 0x5A3 => 220, + 0x5A4 => 220, 0x5A5 => 220, 0x5A6 => 220, 0x5A7 => 220, 0x5AA => 220, + 0x655 => 220, 0x656 => 220, 0x6E3 => 220, 0x6EA => 220, 0x6ED => 220, + 0x731 => 220, 0x734 => 220, 0x737 => 220, 0x738 => 220, 0x739 => 220, + 0x73B => 220, 0x73C => 220, 0x73E => 220, 0x742 => 220, 0x744 => 220, + 0x746 => 220, 0x748 => 220, 0x952 => 220, 0xF18 => 220, 0xF19 => 220, + 0xF35 => 220, 0xF37 => 220, 0xFC6 => 220, 0x193B => 220, 0x20E8 => 220, + 0x1D17B => 220, 0x1D17C => 220, 0x1D17D => 220, 0x1D17E => 220, 0x1D17F => 220, + 0x1D180 => 220, 0x1D181 => 220, 0x1D182 => 220, 0x1D18A => 220, 0x1D18B => 220, + 0x59A => 222, 0x5AD => 222, 0x1929 => 222, 0x302D => 222, 0x302E => 224, + 0x302F => 224, 0x1D16D => 226, 0x5AE => 228, 0x18A9 => 228, 0x302B => 228, + 0x300 => 230, 0x301 => 230, 0x302 => 230, 0x303 => 230, 0x304 => 230, + 0x305 => 230, 0x306 => 230, 0x307 => 230, 0x308 => 230, 0x309 => 230, + 0x30A => 230, 0x30B => 230, 0x30C => 230, 0x30D => 230, 0x30E => 230, + 0x30F => 230, 0x310 => 230, 0x311 => 230, 0x312 => 230, 0x313 => 230, + 0x314 => 230, 0x33D => 230, 0x33E => 230, 0x33F => 230, 0x340 => 230, + 0x341 => 230, 0x342 => 230, 0x343 => 230, 0x344 => 230, 0x346 => 230, + 0x34A => 230, 0x34B => 230, 0x34C => 230, 0x350 => 230, 0x351 => 230, + 0x352 => 230, 0x357 => 230, 0x363 => 230, 0x364 => 230, 0x365 => 230, + 0x366 => 230, 0x367 => 230, 0x368 => 230, 0x369 => 230, 0x36A => 230, + 0x36B => 230, 0x36C => 230, 0x36D => 230, 0x36E => 230, 0x36F => 230, + 0x483 => 230, 0x484 => 230, 0x485 => 230, 0x486 => 230, 0x592 => 230, + 0x593 => 230, 0x594 => 230, 0x595 => 230, 0x597 => 230, 0x598 => 230, + 0x599 => 230, 0x59C => 230, 0x59D => 230, 0x59E => 230, 0x59F => 230, + 0x5A0 => 230, 0x5A1 => 230, 0x5A8 => 230, 0x5A9 => 230, 0x5AB => 230, + 0x5AC => 230, 0x5AF => 230, 0x5C4 => 230, 0x610 => 230, 0x611 => 230, + 0x612 => 230, 0x613 => 230, 0x614 => 230, 0x615 => 230, 0x653 => 230, + 0x654 => 230, 0x657 => 230, 0x658 => 230, 0x6D6 => 230, 0x6D7 => 230, + 0x6D8 => 230, 0x6D9 => 230, 0x6DA => 230, 0x6DB => 230, 0x6DC => 230, + 0x6DF => 230, 0x6E0 => 230, 0x6E1 => 230, 0x6E2 => 230, 0x6E4 => 230, + 0x6E7 => 230, 0x6E8 => 230, 0x6EB => 230, 0x6EC => 230, 0x730 => 230, + 0x732 => 230, 0x733 => 230, 0x735 => 230, 0x736 => 230, 0x73A => 230, + 0x73D => 230, 0x73F => 230, 0x740 => 230, 0x741 => 230, 0x743 => 230, + 0x745 => 230, 0x747 => 230, 0x749 => 230, 0x74A => 230, 0x951 => 230, + 0x953 => 230, 0x954 => 230, 0xF82 => 230, 0xF83 => 230, 0xF86 => 230, + 0xF87 => 230, 0x170D => 230, 0x193A => 230, 0x20D0 => 230, 0x20D1 => 230, + 0x20D4 => 230, 0x20D5 => 230, 0x20D6 => 230, 0x20D7 => 230, 0x20DB => 230, + 0x20DC => 230, 0x20E1 => 230, 0x20E7 => 230, 0x20E9 => 230, 0xFE20 => 230, + 0xFE21 => 230, 0xFE22 => 230, 0xFE23 => 230, 0x1D185 => 230, 0x1D186 => 230, + 0x1D187 => 230, 0x1D189 => 230, 0x1D188 => 230, 0x1D1AA => 230, 0x1D1AB => 230, + 0x1D1AC => 230, 0x1D1AD => 230, 0x315 => 232, 0x31A => 232, 0x302C => 232, + 0x35F => 233, 0x362 => 233, 0x35D => 234, 0x35E => 234, 0x360 => 234, + 0x361 => 234, 0x345 => 240 + ) + ); +} + diff --git a/lib/classes/idna/class.idna_convert_wrapper.php b/lib/classes/idna/class.idna_convert_wrapper.php index efba7fb1..d9597c91 100644 --- a/lib/classes/idna/class.idna_convert_wrapper.php +++ b/lib/classes/idna/class.idna_convert_wrapper.php @@ -39,10 +39,12 @@ class idna_convert_wrapper public function __construct() { // Instantiate it - //$this->idna_converter = new idna_convert(array('idn_version' => '2008', 'encode_german_sz' => false)); - - // use this when using new version of IdnaConverter (which does not work yet) - $this->idna_converter = new Mso\IdnaConvert\IdnaConvert(); + if (version_compare("5.6.0", PHP_VERSION, ">=")) { + $this->idna_converter = new idna_convert(array('idn_version' => '2008', 'encode_german_sz' => false)); + } else { + // use this when using new version of IdnaConverter (which does not work yet) + $this->idna_converter = new Mso\IdnaConvert\IdnaConvert(); + } } /** @@ -57,9 +59,12 @@ class idna_convert_wrapper public function encode($to_encode) { - $to_encode = $this->is_utf8($to_encode) ? $to_encode : utf8_encode($to_encode); - return $this->idna_converter->encode($to_encode); - //return $this->_do_action('encode', $to_encode); + if (version_compare("5.6.0", PHP_VERSION, ">=")) { + return $this->_do_action('encode', $to_encode); + } else { + $to_encode = $this->is_utf8($to_encode) ? $to_encode : utf8_encode($to_encode); + return $this->idna_converter->encode($to_encode); + } } /** @@ -74,8 +79,11 @@ class idna_convert_wrapper public function decode($to_decode) { - return $this->idna_converter->decode($to_decode); - //return $this->_do_action('decode', $to_decode); + if (version_compare("5.6.0", PHP_VERSION, ">=")) { + return $this->_do_action('decode', $to_decode); + } else { + return $this->idna_converter->decode($to_decode); + } } /** From b22e70804b5350b71584953781cff8fd758acb16 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sun, 19 Jun 2016 20:15:33 +0200 Subject: [PATCH 0111/1335] update new IdnaConvert class with needed fixes in them Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/idna/ext/IdnaConvert.php | 24 ++++++++----------- lib/classes/idna/ext/Punycode.php | 35 +++++++++++++++++++--------- 2 files changed, 34 insertions(+), 25 deletions(-) diff --git a/lib/classes/idna/ext/IdnaConvert.php b/lib/classes/idna/ext/IdnaConvert.php index 868ac2a3..3c1b45c5 100644 --- a/lib/classes/idna/ext/IdnaConvert.php +++ b/lib/classes/idna/ext/IdnaConvert.php @@ -54,12 +54,10 @@ namespace Mso\IdnaConvert; class IdnaConvert { - const Version = '1.0.2'; + const Version = '1.1.0'; const SubVersion = 'main'; // Internal settings, do not touch! - const PunycodePrefix = 'xn--'; - protected $encoding = 'utf8'; // Default input charset is UTF-8 protected $strictMode = false; // Behave strict or not protected $idnVersion = '2008'; // Can be either 2003 (old) or 2008 (default) @@ -203,21 +201,17 @@ class IdnaConvert { list ($email_pref, $input) = explode('@', $input, 2); $arr = explode('.', $input); foreach ($arr as $k => $v) { - if (preg_match('!^' . preg_quote(self::PunycodePrefix, '!') . '!', $v)) { - $conv = $punyCode->decode($v); - if ($conv) { - $arr[$k] = $conv; - } + $conv = $punyCode->decode($v); + if ($conv) { + $arr[$k] = $conv; } } $input = join('.', $arr); $arr = explode('.', $email_pref); foreach ($arr as $k => $v) { - if (preg_match('!^' . preg_quote(self::PunycodePrefix, '!') . '!', $v)) { - $conv = $punyCode->decode($v); - if ($conv) { - $arr[$k] = $conv; - } + $conv = $punyCode->decode($v); + if ($conv) { + $arr[$k] = $conv; } } $email_pref = join('.', $arr); @@ -248,7 +242,9 @@ class IdnaConvert { $arr = explode('.', $input); foreach ($arr as $k => $v) { $conv = $punyCode->decode($v); - $arr[$k] = ($conv) ? $conv : $v; + if ($conv) { + $arr[$k] = $conv; + } } $return = join('.', $arr); } diff --git a/lib/classes/idna/ext/Punycode.php b/lib/classes/idna/ext/Punycode.php index 14a57f1c..781466b2 100644 --- a/lib/classes/idna/ext/Punycode.php +++ b/lib/classes/idna/ext/Punycode.php @@ -77,11 +77,32 @@ class Punycode implements PunycodeInterface $this->UnicodeTranscoder = $UnicodeTranscoder; } + /** + * Returns the used prefix for punycode-encoded strings + * @return string + */ public function getPunycodePrefix() { return self::punycodePrefix; } + /** + * Checks, whether or not the provided string is a valid punycode string + * @param string $encoded + * @return boolean + */ + public function validate($encoded) { + // Check for existence of the prefix + if (strpos($encoded, self::punycodePrefix) !== 0) { + return false; + } + // If nothing is left after the prefix, it is hopeless + if (strlen(trim($encoded)) <= strlen(self::punycodePrefix)) { + return false; + } + return true; + } + /** * The actual decoding algorithm * @param string @@ -89,19 +110,11 @@ class Punycode implements PunycodeInterface */ public function decode($encoded) { - $decoded = []; - // find the Punycode prefix - if (!preg_match('!^' . preg_quote(self::punycodePrefix, '!') . '!', $encoded)) { - // *** froxlor patch *** - return $encoded; - // *** end froxlor patch *** - throw new \InvalidArgumentException('This is not a punycode string'); - } - $encode_test = preg_replace('!^' . preg_quote(self::punycodePrefix, '!') . '!', '', $encoded); - // If nothing left after removing the prefix, it is hopeless - if (!$encode_test) { + if (!$this->validate($encoded)) { return false; } + + $decoded = []; // Find last occurence of the delimiter $delim_pos = strrpos($encoded, '-'); if ($delim_pos > self::byteLength(self::punycodePrefix)) { From 6f8aa1cbc00fe782db76a5d8bccc67eaae2ab550 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sun, 19 Jun 2016 20:22:00 +0200 Subject: [PATCH 0112/1335] update another idnaconvert related class Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/idna/ext/UnicodeTranscoder.php | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/lib/classes/idna/ext/UnicodeTranscoder.php b/lib/classes/idna/ext/UnicodeTranscoder.php index 3d9118fb..dab888ff 100644 --- a/lib/classes/idna/ext/UnicodeTranscoder.php +++ b/lib/classes/idna/ext/UnicodeTranscoder.php @@ -80,7 +80,7 @@ class UnicodeTranscoder implements UnicodeTranscoderInterface $output = []; $out_len = 0; - $inp_len = strlen($input); + $inp_len = self::byteLength($input); $mode = 'next'; $test = 'none'; for ($k = 0; $k < $inp_len; ++$k) { @@ -200,7 +200,7 @@ class UnicodeTranscoder implements UnicodeTranscoderInterface { $output = []; $out_len = 0; - $inp_len = strlen($input); + $inp_len = self::byteLength($input); $mode = 'd'; $b64 = ''; @@ -322,7 +322,7 @@ class UnicodeTranscoder implements UnicodeTranscoderInterface { $output = []; - $inp_len = strlen($input); + $inp_len = self::byteLength($input); // Input length must be dividable by 4 if ($inp_len % 4) { throw new \InvalidArgumentException('Input UCS4 string is broken'); @@ -340,4 +340,19 @@ class UnicodeTranscoder implements UnicodeTranscoderInterface return $output; } + + /** + * Gets the length of a string in bytes even if mbstring function + * overloading is turned on + * + * @param string $string the string for which to get the length. + * @return integer the length of the string in bytes. + */ + protected static function byteLength($string) + { + if ((extension_loaded('mbstring') && (ini_get('mbstring.func_overload') & 0x02) === 0x02)) { + return mb_strlen($string, '8bit'); + } + return strlen((binary) $string); + } } From 5e9e2996d7957479e51e9c9ac068105a041263d9 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sun, 19 Jun 2016 22:08:17 +0200 Subject: [PATCH 0113/1335] avoid php-5.6 operator Signed-off-by: Michael Kaufmann (d00p) --- scripts/jobs/cron_tasks.inc.dns.20.pdns.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/jobs/cron_tasks.inc.dns.20.pdns.php b/scripts/jobs/cron_tasks.inc.dns.20.pdns.php index 15844c71..6158d8a8 100644 --- a/scripts/jobs/cron_tasks.inc.dns.20.pdns.php +++ b/scripts/jobs/cron_tasks.inc.dns.20.pdns.php @@ -74,7 +74,9 @@ class pdns extends DnsBase $domain['id'], $isFroxlorHostname); if (count($subzones)) { - array_push($zoneContent->records, ...$subzones); + foreach ($subzones as $subzone) { + $zoneContent->records[] = $subzone; + } } $pdnsDomId = $this->_insertZone($zoneContent->origin, $zoneContent->serial); $this->_insertRecords($pdnsDomId, $zoneContent->records, $zoneContent->origin); From 6df08f6b9aba85f2d248b8d48156b57a955a9209 Mon Sep 17 00:00:00 2001 From: Daniel Reichelt Date: Sun, 19 Jun 2016 16:56:53 +0200 Subject: [PATCH 0114/1335] don't split .gitignore they would end up in .deb packages --- .gitignore | 12 +++++++++--- lib/.gitignore | 1 - templates/.gitignore | 4 ---- 3 files changed, 9 insertions(+), 8 deletions(-) delete mode 100644 lib/.gitignore delete mode 100644 templates/.gitignore diff --git a/.gitignore b/.gitignore index 0ef4964b..a87df6c9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ -templates/* -logs/* install/update.log +templates/* +lib/userdata.inc.php +logs/* .buildpath .project .settings/ @@ -8,4 +9,9 @@ install/update.log *~ .well-known .idea -*.iml \ No newline at end of file +*.iml + +!templates/Froxlor/ +!templates/Sparkle/ +!templates/misc/ +templates/Froxlor/assets/img/logo_custom.png diff --git a/lib/.gitignore b/lib/.gitignore deleted file mode 100644 index 015be9fe..00000000 --- a/lib/.gitignore +++ /dev/null @@ -1 +0,0 @@ -userdata.inc.php diff --git a/templates/.gitignore b/templates/.gitignore deleted file mode 100644 index 33c4d146..00000000 --- a/templates/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -!Froxlor/ -!Sparkle/ -Froxlor/assets/img/logo_custom.png -!misc/ From 1e8bc553b8edefc22a6fc12ce63a5d2ff76c7962 Mon Sep 17 00:00:00 2001 From: Daniel Reichelt Date: Sun, 19 Jun 2016 23:43:42 +0200 Subject: [PATCH 0115/1335] fix/add SAPI checks to prevent execution via webserver --- install/scripts/language-check.php | 5 +---- install/scripts/switch-server-ip.php | 5 +++++ 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/install/scripts/language-check.php b/install/scripts/language-check.php index 17c8463a..872609c5 100644 --- a/install/scripts/language-check.php +++ b/install/scripts/language-check.php @@ -19,10 +19,7 @@ $baseLanguage = 'english.lng.php'; // Check if we're in the CLI -if(@php_sapi_name() != 'cli' - && @php_sapi_name() != 'cgi' - && @php_sapi_name() != 'cgi-fcgi' -) { +if(@php_sapi_name() !== 'cli') { die('This script will only work in the shell.'); } diff --git a/install/scripts/switch-server-ip.php b/install/scripts/switch-server-ip.php index 506cca9f..d8f098f9 100644 --- a/install/scripts/switch-server-ip.php +++ b/install/scripts/switch-server-ip.php @@ -16,6 +16,11 @@ * */ +// Check if we're in the CLI +if(@php_sapi_name() !== 'cli') { + die('This script will only work in the shell.'); +} + // give control to command line handler try { CmdLineHandler::processParameters($argc, $argv); From 7ec777c9dd9d840ed6b127b08fe2a1191f7f2014 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 20 Jun 2016 09:55:27 +0200 Subject: [PATCH 0116/1335] put index back in location-context; try_files not really necessary if not using PHP, also eases use of proxy_pass users; thx to karstenk Signed-off-by: Michael Kaufmann (d00p) --- scripts/jobs/cron_tasks.inc.http.30.nginx.php | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/scripts/jobs/cron_tasks.inc.http.30.nginx.php b/scripts/jobs/cron_tasks.inc.http.30.nginx.php index 3f723b69..de1c69be 100644 --- a/scripts/jobs/cron_tasks.inc.http.30.nginx.php +++ b/scripts/jobs/cron_tasks.inc.http.30.nginx.php @@ -671,7 +671,7 @@ class nginx extends HttpConfigBase { $path_options .= "\t\t" . 'autoindex on;' . "\n"; $this->vhost_root_autoindex = false; } - // $path_options.= "\t\t" . 'try_files $uri $uri/ @rewrites;'."\n"; + // check if we have a htpasswd for this path // (damn nginx does not like more than one // 'location'-part with the same path) @@ -859,24 +859,17 @@ class nginx extends HttpConfigBase { $this->_deactivated = false; } + $webroot_text .= "\n\t".'location / {'."\n"; + if ($domain['phpenabled'] == '1') { $webroot_text .= "\t" . 'index index.php index.html index.htm;'."\n"; + $webroot_text .= "\t\t" . 'try_files $uri $uri/ @rewrites;'."\n"; } else { $webroot_text .= "\t" . 'index index.html index.htm;'."\n"; } - $webroot_text .= "\n\t".'location / {'."\n"; - $webroot_text .= "\t\t" . 'try_files $uri $uri/'; - if ($domain['phpenabled'] == '1') - { - $webroot_text .= ' @rewrites;'."\n"; - } - else - { - $webroot_text .= ";\n"; - } if ($this->vhost_root_autoindex) { $webroot_text .= "\t\t".'autoindex on;'."\n"; From a121363dd230f3f8af6d1f603db9bc81acfee66d Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Fri, 24 Jun 2016 07:38:19 +0200 Subject: [PATCH 0117/1335] update pdns-config templates / commands Signed-off-by: Michael Kaufmann (d00p) --- lib/configfiles/gentoo.xml | 14 +++++++------- lib/configfiles/jessie.xml | 10 +++++----- lib/configfiles/precise.xml | 11 ++++++----- lib/configfiles/trusty.xml | 12 +++++++----- lib/configfiles/wheezy.xml | 15 ++++++++------- 5 files changed, 33 insertions(+), 29 deletions(-) diff --git a/lib/configfiles/gentoo.xml b/lib/configfiles/gentoo.xml index e86c2478..28083420 100644 --- a/lib/configfiles/gentoo.xml +++ b/lib/configfiles/gentoo.xml @@ -390,13 +390,13 @@ mail IN A - + +allow-axfr-ips=127.0.0.0/8,::1, ################################# # allow-dnsupdate-from A global setting to allow DNS updates from these IP ranges. @@ -914,8 +914,8 @@ include-dir=/etc/powerdns/froxlor/ - + - + +allow-axfr-ips=127.0.0.0/8,::1, ################################# # allow-dnsupdate-from A global setting to allow DNS updates from these IP ranges. @@ -1456,7 +1456,7 @@ include-dir=/etc/powerdns/froxlor/ - - + +allow-axfr-ips=127.0.0.0/8,::1, ################################# # allow-dnsupdate-from A global setting to allow DNS updates from these IP ranges. @@ -898,8 +898,8 @@ include-dir=/etc/powerdns/froxlor - + - + - + allow-recursion=127.0.0.1 config-dir=/etc/powerdns daemon=yes @@ -362,8 +363,8 @@ include-dir=/etc/powerdns/froxlor/ ]]> - + - + - diff --git a/lib/configfiles/trusty.xml b/lib/configfiles/trusty.xml index fd4f1af7..824cd147 100644 --- a/lib/configfiles/trusty.xml +++ b/lib/configfiles/trusty.xml @@ -377,8 +377,9 @@ exit "$RETVAL" - + allow-recursion=127.0.0.1 config-dir=/etc/powerdns daemon=yes @@ -396,8 +397,8 @@ include-dir=/etc/powerdns/froxlor/ - + allow-recursion=127.0.0.1 config-dir=/etc/powerdns daemon=yes @@ -431,11 +433,11 @@ socket-dir=/var/run version-string=powerdns bind-config=named.conf bind-check-interval=300 -include=/etc/powerdns/pdns_froxlor.conf +include-dir=/etc/powerdns/pdns.d ]]> - diff --git a/lib/configfiles/wheezy.xml b/lib/configfiles/wheezy.xml index c524689c..d54b1a3e 100644 --- a/lib/configfiles/wheezy.xml +++ b/lib/configfiles/wheezy.xml @@ -417,14 +417,14 @@ exit "$RETVAL" - + +allow-axfr-ips=127.0.0.0/8,::1, ################################# # allow-recursion List of netmasks that are allowed to recurse @@ -737,8 +737,8 @@ include-dir=/etc/powerdns/froxlor - + - + - + Date: Fri, 24 Jun 2016 07:41:28 +0200 Subject: [PATCH 0118/1335] try to be more strict-mode compatible, fixes #1635 Signed-off-by: Michael Kaufmann (d00p) --- install/froxlor.sql | 8 ++++---- install/updates/froxlor/0.9/update_0.9.inc.php | 13 ++++++++++++- lib/version.inc.php | 2 +- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/install/froxlor.sql b/install/froxlor.sql index 7de09553..54f62a4b 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -23,7 +23,7 @@ CREATE TABLE `ftp_users` ( `shell` varchar(255) NOT NULL default '/bin/false', `login_enabled` enum('N','Y') NOT NULL default 'N', `login_count` int(15) NOT NULL default '0', - `last_login` datetime NOT NULL default '0000-00-00 00:00:00', + `last_login` datetime default NULL, `up_count` int(15) NOT NULL default '0', `up_bytes` bigint(30) NOT NULL default '0', `down_count` int(15) NOT NULL default '0', @@ -272,7 +272,7 @@ CREATE TABLE `panel_ipsandports` ( `namevirtualhost_statement` tinyint(1) NOT NULL default '0', `vhostcontainer` tinyint(1) NOT NULL default '0', `vhostcontainer_servername_statement` tinyint(1) NOT NULL default '0', - `specialsettings` text, + `specialsettings` text default NULL, `ssl` tinyint(4) NOT NULL default '0', `ssl_cert_file` varchar(255) NOT NULL, `ssl_key_file` varchar(255) NOT NULL, @@ -560,14 +560,14 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES ('panel', 'password_special_char_required', '0'), ('panel', 'password_special_char', '!?<>§$%+#=@'), ('panel', 'version', '0.9.36'), - ('panel', 'db_version', '201605180'); + ('panel', 'db_version', '201606190'); DROP TABLE IF EXISTS `panel_tasks`; CREATE TABLE `panel_tasks` ( `id` int(11) unsigned NOT NULL auto_increment, `type` int(11) NOT NULL default '0', - `data` text NOT NULL, + `data` text, PRIMARY KEY (`id`) ) ENGINE=MyISAM CHARSET=utf8 COLLATE=utf8_general_ci; diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index cf1cdec5..c01fb274 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -3369,8 +3369,19 @@ if (isDatabaseVersion('201605120')) { if (isDatabaseVersion('201605170')) { showUpdateStep("Adding new dns-editor setting for customers"); - Database::query("ALTER TABLE `panel_customers` ADD `dnsenabled` tinyint(1) NOT NULL default '0' AFTER `perlenabled`;"); + Database::query("ALTER TABLE `".TABLE_PANEL_CUSTOMERS."` ADD `dnsenabled` tinyint(1) NOT NULL default '0' AFTER `perlenabled`;"); lastStepStatus(0); updateToDbVersion('201605180'); } + +if (isDatabaseVersion('201605180')) { + + showUpdateStep("Changing tables to be more mysql strict-mode compatible"); + Database::query("ALTER TABLE `".TABLE_FTP_USERS."` CHANGE `last_login` `last_login` DATETIME NULL DEFAULT NULL;"); + Database::query("ALTER TABLE `".TABLE_PANEL_IPSANDPORTS."` CHANGE `specialsettings` `specialsettings` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL;"); + Database::query("ALTER TABLE `".TABLE_PANEL_TASKS."` CHANGE `data` `data` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL;"); + lastStepStatus(0); + + updateToDbVersion('201606190'); +} diff --git a/lib/version.inc.php b/lib/version.inc.php index 924d7cf1..1bd799c0 100644 --- a/lib/version.inc.php +++ b/lib/version.inc.php @@ -19,7 +19,7 @@ $version = '0.9.36'; // Database version (YYYYMMDDC where C is a daily counter) -$dbversion = '201605180'; +$dbversion = '201606190'; // Distribution branding-tag (used for Debian etc.) $branding = ''; From 164e1108e503ed33eee206d416a269f72f8b553b Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Fri, 24 Jun 2016 07:49:52 +0200 Subject: [PATCH 0119/1335] fix for sql changes, refs #1635 Signed-off-by: Michael Kaufmann (d00p) --- install/froxlor.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/froxlor.sql b/install/froxlor.sql index 54f62a4b..b801ae9b 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -272,7 +272,7 @@ CREATE TABLE `panel_ipsandports` ( `namevirtualhost_statement` tinyint(1) NOT NULL default '0', `vhostcontainer` tinyint(1) NOT NULL default '0', `vhostcontainer_servername_statement` tinyint(1) NOT NULL default '0', - `specialsettings` text default NULL, + `specialsettings` text, `ssl` tinyint(4) NOT NULL default '0', `ssl_cert_file` varchar(255) NOT NULL, `ssl_key_file` varchar(255) NOT NULL, From 3f790bc334bfdf5e9d815375ac455a9b9069cd66 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Fri, 24 Jun 2016 08:03:42 +0200 Subject: [PATCH 0120/1335] keep the same structure for every distro + minor permission fixes Signed-off-by: Michael Kaufmann (d00p) --- lib/configfiles/gentoo.xml | 2 +- lib/configfiles/jessie.xml | 12 ++++++++---- lib/configfiles/precise.xml | 8 +++++--- lib/configfiles/trusty.xml | 7 ++++--- lib/configfiles/wheezy.xml | 11 +++++++---- 5 files changed, 25 insertions(+), 15 deletions(-) diff --git a/lib/configfiles/gentoo.xml b/lib/configfiles/gentoo.xml index 28083420..3e0e5c3b 100644 --- a/lib/configfiles/gentoo.xml +++ b/lib/configfiles/gentoo.xml @@ -1457,7 +1457,7 @@ include-dir=/etc/powerdns/froxlor/ + chmod="600"> named.conf diff --git a/lib/configfiles/jessie.xml b/lib/configfiles/jessie.xml index e3133fae..7a3e19f8 100644 --- a/lib/configfiles/jessie.xml +++ b/lib/configfiles/jessie.xml @@ -566,7 +566,6 @@ guardian=yes # include-dir Include *.conf files from this directory # # include-dir= -include-dir=/etc/powerdns/pdns.d ################################# # launch Which backends to launch and order to query them in @@ -893,7 +892,8 @@ version-string=powerdns # # webserver-print-arguments=no -include-dir=/etc/powerdns/froxlor +# include froxlor-bind-specific config +include-dir=/etc/powerdns/froxlor/ ]]> @@ -1107,7 +1107,6 @@ guardian=yes # include-dir Include *.conf files from this directory # # include-dir= -include-dir=/etc/powerdns/pdns.d ################################# # launch Which backends to launch and order to query them in @@ -1434,10 +1433,15 @@ version-string=powerdns # webserver-print-arguments If the webserver should print arguments # # webserver-print-arguments=no + +# include froxlor-bind-specific config +include-dir=/etc/powerdns/froxlor/ ]]> - + + + named.conf bind-check-interval=300 -include=/etc/powerdns/pdns_froxlor.conf +include-dir=/etc/powerdns/froxlor/ ]]> - + + #local-ipv6=YOUR_IPv6_(if_any) diff --git a/lib/configfiles/trusty.xml b/lib/configfiles/trusty.xml index 824cd147..f21517b4 100644 --- a/lib/configfiles/trusty.xml +++ b/lib/configfiles/trusty.xml @@ -433,12 +433,13 @@ socket-dir=/var/run version-string=powerdns bind-config=named.conf bind-check-interval=300 -include-dir=/etc/powerdns/pdns.d +include-dir=/etc/powerdns/froxlor/ ]]> - + + #local-ipv6=YOUR_IPv6_(if_any) diff --git a/lib/configfiles/wheezy.xml b/lib/configfiles/wheezy.xml index d54b1a3e..bf63a447 100644 --- a/lib/configfiles/wheezy.xml +++ b/lib/configfiles/wheezy.xml @@ -732,7 +732,8 @@ socket-dir=/var/run # allowed methods are anonymous / powerdns / full / custom version-string=powerdns -include-dir=/etc/powerdns/froxlor +# include froxlor-bind-specific config +include-dir=/etc/powerdns/froxlor/ ]]> @@ -1070,12 +1071,14 @@ socket-dir=/var/run # allowed methods are anonymous / powerdns / full / custom version-string=powerdns -include-dir=/etc/powerdns/pdns.d +# include froxlor-bind-specific config +include-dir=/etc/powerdns/froxlor/ ]]> - + + Date: Fri, 24 Jun 2016 08:41:17 +0200 Subject: [PATCH 0121/1335] generic html form handling: don't remove leading tabs (+remove commented out code in the vicinity, probably debugging remnants) --- lib/classes/output/class.htmlform.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/classes/output/class.htmlform.php b/lib/classes/output/class.htmlform.php index 692a86aa..2bc6edca 100644 --- a/lib/classes/output/class.htmlform.php +++ b/lib/classes/output/class.htmlform.php @@ -68,8 +68,6 @@ class htmlform $style = (isset($fielddata['style']) ? ' class="'.$fielddata['style'].'"' : ''); $mandatory = self::_getMandatoryFlag($fielddata); $data_field = self::_parseDataField($fieldname, $fielddata); - //$data_field = str_replace("\n", "", $data_field); - $data_field = str_replace("\t", "", $data_field); if (isset($fielddata['has_nextto'])) { $nexto = array('field' => $fieldname); $data_field.='{NEXTTOFIELD_'.$fieldname.'}'; @@ -79,7 +77,6 @@ class htmlform eval("self::\$_form .= \"" . getTemplate("misc/form/table_row", "1") . "\";"); } else { $data_field = self::_parseDataField($fieldname, $fielddata); - //$data_field = str_replace("\n", "", $data_field); $data_field = str_replace("\t", "", $data_field); $data_field = $fielddata['next_to_prefix'].$data_field; self::$_form = str_replace( From 83f40401be8005889cbbadfeecc0791d64440f43 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 11 Jul 2016 09:15:21 +0200 Subject: [PATCH 0122/1335] fix change_date for PDNS records Signed-off-by: Michael Kaufmann (d00p) --- scripts/jobs/cron_tasks.inc.dns.20.pdns.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/scripts/jobs/cron_tasks.inc.dns.20.pdns.php b/scripts/jobs/cron_tasks.inc.dns.20.pdns.php index 6158d8a8..594cb41d 100644 --- a/scripts/jobs/cron_tasks.inc.dns.20.pdns.php +++ b/scripts/jobs/cron_tasks.inc.dns.20.pdns.php @@ -118,6 +118,8 @@ class pdns extends DnsBase private function _insertRecords($domainid = 0, $records, $origin) { + $changedate = date('Ymds', time()); + $ins_stmt = $this->pdns_db->prepare(" INSERT INTO records set `domain_id` = :did, @@ -127,7 +129,7 @@ class pdns extends DnsBase `ttl` = :ttl, `prio` = :prio, `disabled` = '0', - `change_date` = UNIX_TIMESTAMP() + `change_date` = :changedate "); foreach ($records as $record) @@ -151,7 +153,8 @@ class pdns extends DnsBase 'type' => $record->type, 'content' => $record->content, 'ttl' => $record->ttl, - 'prio' => $record->priority + 'prio' => $record->priority, + 'changedate' => $changedate ); $ins_stmt->execute($ins_data); } From 1c369e550338bc35955e93b0e2b228b15f6ace38 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 14 Jul 2016 07:58:56 +0200 Subject: [PATCH 0123/1335] add possibility to disable web or traffic cron by setting the max-percentage value to 0, fixes #1639 Signed-off-by: Michael Kaufmann (d00p) --- actions/admin/settings/120.system.php | 4 +- lng/english.lng.php | 6 +- lng/german.lng.php | 6 +- scripts/jobs/cron_usage.inc.diskspace.php | 355 ++++++++------- scripts/jobs/cron_usage_report.php | 532 +++++++++++----------- 5 files changed, 456 insertions(+), 447 deletions(-) diff --git a/actions/admin/settings/120.system.php b/actions/admin/settings/120.system.php index 46faefcf..810c931e 100644 --- a/actions/admin/settings/120.system.php +++ b/actions/admin/settings/120.system.php @@ -145,7 +145,7 @@ return array( 'settinggroup' => 'system', 'varname' => 'report_webmax', 'type' => 'int', - 'int_min' => 1, + 'int_min' => 0, 'int_max' => 150, 'default' => 90, 'save_method' => 'storeSettingField', @@ -155,7 +155,7 @@ return array( 'settinggroup' => 'system', 'varname' => 'report_trafficmax', 'type' => 'int', - 'int_min' => 1, + 'int_min' => 0, 'int_max' => 150, 'default' => 90, 'save_method' => 'storeSettingField', diff --git a/lng/english.lng.php b/lng/english.lng.php index 98b58f1a..c0d1d49b 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -1314,8 +1314,10 @@ $lng['phpfpm']['ownvhost']['description'] = 'If enabled, Froxlor will also be ru // ADDED IN FROXLOR 0.9.17 $lng['crondesc']['cron_usage_report'] = 'Web- and traffic-reports'; $lng['serversettings']['report']['report'] = 'Enable sending of reports about web- and traffic-usage'; -$lng['serversettings']['report']['webmax'] = 'Warning-level in percent for webspace'; -$lng['serversettings']['report']['trafficmax'] = 'Warning-level in percent for traffic'; +$lng['serversettings']['report']['webmax']['title'] = 'Warning-level in percent for webspace'; +$lng['serversettings']['report']['webmax']['description'] = 'Valid values are 0 up to 150. Setting this value to 0 disables this report.'; +$lng['serversettings']['report']['trafficmax']['title'] = 'Warning-level in percent for traffic'; +$lng['serversettings']['report']['trafficmax']['description'] = 'Valid values are 0 up to 150. Setting this value to 0 disables this report.'; $lng['mails']['trafficmaxpercent']['mailbody'] = 'Dear {NAME},\n\nyou used {TRAFFICUSED} MB of your available {TRAFFIC} MB of traffic.\nThis is more than {MAX_PERCENT}%.\n\nYours sincerely, your administrator'; $lng['mails']['trafficmaxpercent']['subject'] = 'Reaching your traffic limit'; $lng['admin']['templates']['trafficmaxpercent'] = 'Notification mail for customers when given maximum of percent of traffic is exhausted'; diff --git a/lng/german.lng.php b/lng/german.lng.php index f9397b1f..16b05c54 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -1293,8 +1293,10 @@ $lng['phpfpm']['ownvhost']['description'] = 'Wenn verwendet, wird Froxlor selbst // ADDED IN FROXLOR 0.9.17 $lng['crondesc']['cron_usage_report'] = 'Webspace- und Trafficreport'; $lng['serversettings']['report']['report'] = 'Aktiviere das Senden von Reports über Webspace- und Trafficverbrauch'; -$lng['serversettings']['report']['webmax'] = 'Warn-Level in Prozent für Webspace'; -$lng['serversettings']['report']['trafficmax'] = 'Warn-Level in Prozent für Traffic'; +$lng['serversettings']['report']['webmax']['title'] = 'Warn-Level in Prozent für Webspace'; +$lng['serversettings']['report']['webmax']['description'] = 'Gültige Werte sind von 0 bis 150. Der Wert 0 deaktiviert diesen Report.'; +$lng['serversettings']['report']['trafficmax']['title'] = 'Warn-Level in Prozent für Traffic'; +$lng['serversettings']['report']['trafficmax']['description'] = 'Gültige Werte sind von 0 bis 150. Der Wert 0 deaktiviert diesen Report.'; $lng['mails']['trafficmaxpercent']['mailbody'] = 'Sehr geehrte(r) {NAME},\n\nSie haben bereits {TRAFFICUSED} MB von Ihren insgesamt {TRAFFIC} MB Traffic verbraucht.\nDies sind mehr als {MAX_PERCENT}%.\n\nVielen Dank,\nIhr Administrator'; $lng['mails']['trafficmaxpercent']['subject'] = 'Sie erreichen bald Ihr Traffic-Limit'; $lng['admin']['templates']['trafficmaxpercent'] = 'Hinweismail für Kunden, wenn sie die angegebenen Prozent des Traffics verbraucht haben'; diff --git a/scripts/jobs/cron_usage.inc.diskspace.php b/scripts/jobs/cron_usage.inc.diskspace.php index 2c74ef64..045ae9a2 100644 --- a/scripts/jobs/cron_usage.inc.diskspace.php +++ b/scripts/jobs/cron_usage.inc.diskspace.php @@ -15,194 +15,197 @@ * */ -/** - * report about diskusage for customers - */ -$result_stmt = Database::query(" - SELECT `c`.`customerid`, `c`.`adminid`, `c`.`name`, `c`.`firstname`, - `c`.`company`, `c`.`diskspace`, `c`.`diskspace_used`, `c`.`email`, `c`.`def_language`, - `a`.`name` AS `adminname`, `a`.`email` AS `adminmail` - FROM `" . TABLE_PANEL_CUSTOMERS . "` AS `c` - LEFT JOIN `" . TABLE_PANEL_ADMINS . "` AS `a` - ON `a`.`adminid` = `c`.`adminid` - WHERE `c`.`diskspace` > '0' AND `c`.`reportsent` <> '2' -"); +if ((int)Settings::Get('system.report_webmax') > 0) +{ + /** + * report about diskusage for customers + */ + $result_stmt = Database::query(" + SELECT `c`.`customerid`, `c`.`adminid`, `c`.`name`, `c`.`firstname`, + `c`.`company`, `c`.`diskspace`, `c`.`diskspace_used`, `c`.`email`, `c`.`def_language`, + `a`.`name` AS `adminname`, `a`.`email` AS `adminmail` + FROM `" . TABLE_PANEL_CUSTOMERS . "` AS `c` + LEFT JOIN `" . TABLE_PANEL_ADMINS . "` AS `a` + ON `a`.`adminid` = `c`.`adminid` + WHERE `c`.`diskspace` > '0' AND `c`.`reportsent` <> '2' + "); -while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) { + while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) { - if (isset($row['diskspace']) - && $row['diskspace_used'] != null - && $row['diskspace_used'] > 0 - && (($row['diskspace_used'] * 100) / $row['diskspace']) >= (int)Settings::Get('system.report_webmax') - ) { + if (isset($row['diskspace']) + && $row['diskspace_used'] != null + && $row['diskspace_used'] > 0 + && (($row['diskspace_used'] * 100) / $row['diskspace']) >= (int)Settings::Get('system.report_webmax') + ) { - $rep_userinfo = array( - 'name' => $row['name'], - 'firstname' => $row['firstname'], - 'company' => $row['company'] - ); - $replace_arr = array( - 'SALUTATION' => getCorrectUserSalutation($rep_userinfo), - 'NAME' => $row['name'], // < keep this for compatibility - '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 */ - 'USAGE_PERCENT' => round(($row['diskspace_used'] * 100) / $row['diskspace'], 2), - 'MAX_PERCENT' => Settings::Get('system.report_webmax') - ); + $rep_userinfo = array( + 'name' => $row['name'], + 'firstname' => $row['firstname'], + 'company' => $row['company'] + ); + $replace_arr = array( + 'SALUTATION' => getCorrectUserSalutation($rep_userinfo), + 'NAME' => $row['name'], // < keep this for compatibility + '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 */ + 'USAGE_PERCENT' => round(($row['diskspace_used'] * 100) / $row['diskspace'], 2), + 'MAX_PERCENT' => Settings::Get('system.report_webmax') + ); - $lngfile_stmt = Database::prepare(" - SELECT `file` FROM `" . TABLE_PANEL_LANGUAGE . "` - WHERE `language` = :deflang - "); - $lngfile = Database::pexecute_first($lngfile_stmt, array('deflang' => $row['def_language'])); + $lngfile_stmt = Database::prepare(" + SELECT `file` FROM `" . TABLE_PANEL_LANGUAGE . "` + WHERE `language` = :deflang + "); + $lngfile = Database::pexecute_first($lngfile_stmt, array('deflang' => $row['def_language'])); - if ($lngfile !== null) { - $langfile = $lngfile['file']; - } else { - $lngfile = Database::pexecute_first($lngfile_stmt, array('deflang' => Settings::Get('panel.standardlanguage'))); - $langfile = $lngfile['file']; + if ($lngfile !== null) { + $langfile = $lngfile['file']; + } else { + $lngfile = Database::pexecute_first($lngfile_stmt, array('deflang' => Settings::Get('panel.standardlanguage'))); + $langfile = $lngfile['file']; + } + + // include english language file (fallback) + include_once makeCorrectFile(FROXLOR_INSTALL_DIR . '/lng/english.lng.php'); + // include admin/customer language file + include_once makeCorrectFile(FROXLOR_INSTALL_DIR . '/' . $langfile); + + // Get mail templates from database; the ones from 'admin' are fetched for fallback + $result2_stmt = Database::prepare(" + SELECT `value` FROM `" . TABLE_PANEL_TEMPLATES . "` + WHERE `adminid` = :adminid + AND `language` = :lang + AND `templategroup` = 'mails' AND `varname` = :varname + "); + $result2_data = array( + 'adminid' => $row['adminid'], + 'lang' => $row['def_language'], + 'varname' => 'diskmaxpercent_subject' + ); + $result2 = Database::pexecute_first($result2_stmt, $result2_data); + $mail_subject = html_entity_decode(replace_variables((($result2['value'] != '') ? $result2['value'] : $lng['mails']['diskmaxpercent']['subject']), $replace_arr)); + + $result2_data['varname'] = 'diskmaxpercent_mailbody'; + $result2 = Database::pexecute_first($result2_stmt, $result2_data); + $mail_body = html_entity_decode(replace_variables((($result2['value'] != '') ? $result2['value'] : $lng['mails']['diskmaxpercent']['mailbody']), $replace_arr)); + + $_mailerror = false; + try { + $mail->SetFrom($row['adminmail'], $row['adminname']); + $mail->Subject = $mail_subject; + $mail->AltBody = $mail_body; + $mail->MsgHTML(nl2br($mail_body)); + $mail->AddAddress($row['email'], $row['name']); + $mail->Send(); + } catch(phpmailerException $e) { + $mailerr_msg = $e->errorMessage(); + $_mailerror = true; + } catch (Exception $e) { + $mailerr_msg = $e->getMessage(); + $_mailerror = true; + } + + if ($_mailerror) { + $cronlog->logAction(CRON_ACTION, LOG_ERR, "Error sending mail: " . $mailerr_msg); + echo "Error sending mail: " . $mailerr_msg . "\n"; + } + + $mail->ClearAddresses(); + $upd_stmt = Database::prepare(" + UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET `reportsent` = '2' + WHERE `customerid` = :customerid + "); + Database::pexecute($upd_stmt, array('customerid' => $row['customerid'])); } - - // include english language file (fallback) - include_once makeCorrectFile(FROXLOR_INSTALL_DIR . '/lng/english.lng.php'); - // include admin/customer language file - include_once makeCorrectFile(FROXLOR_INSTALL_DIR . '/' . $langfile); - - // Get mail templates from database; the ones from 'admin' are fetched for fallback - $result2_stmt = Database::prepare(" - SELECT `value` FROM `" . TABLE_PANEL_TEMPLATES . "` - WHERE `adminid` = :adminid - AND `language` = :lang - AND `templategroup` = 'mails' AND `varname` = :varname - "); - $result2_data = array( - 'adminid' => $row['adminid'], - 'lang' => $row['def_language'], - 'varname' => 'diskmaxpercent_subject' - ); - $result2 = Database::pexecute_first($result2_stmt, $result2_data); - $mail_subject = html_entity_decode(replace_variables((($result2['value'] != '') ? $result2['value'] : $lng['mails']['diskmaxpercent']['subject']), $replace_arr)); - - $result2_data['varname'] = 'diskmaxpercent_mailbody'; - $result2 = Database::pexecute_first($result2_stmt, $result2_data); - $mail_body = html_entity_decode(replace_variables((($result2['value'] != '') ? $result2['value'] : $lng['mails']['diskmaxpercent']['mailbody']), $replace_arr)); - - $_mailerror = false; - try { - $mail->SetFrom($row['adminmail'], $row['adminname']); - $mail->Subject = $mail_subject; - $mail->AltBody = $mail_body; - $mail->MsgHTML(nl2br($mail_body)); - $mail->AddAddress($row['email'], $row['name']); - $mail->Send(); - } catch(phpmailerException $e) { - $mailerr_msg = $e->errorMessage(); - $_mailerror = true; - } catch (Exception $e) { - $mailerr_msg = $e->getMessage(); - $_mailerror = true; - } - - if ($_mailerror) { - $cronlog->logAction(CRON_ACTION, LOG_ERR, "Error sending mail: " . $mailerr_msg); - echo "Error sending mail: " . $mailerr_msg . "\n"; - } - - $mail->ClearAddresses(); - $upd_stmt = Database::prepare(" - UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET `reportsent` = '2' - WHERE `customerid` = :customerid - "); - Database::pexecute($upd_stmt, array('customerid' => $row['customerid'])); } -} -/** - * report about diskusage for admins/reseller - */ -$result_stmt = Database::query(" - SELECT `a`.* FROM `" . TABLE_PANEL_ADMINS . "` `a` WHERE `a`.`reportsent` <> '2' -"); + /** + * report about diskusage for admins/reseller + */ + $result_stmt = Database::query(" + SELECT `a`.* FROM `" . TABLE_PANEL_ADMINS . "` `a` WHERE `a`.`reportsent` <> '2' + "); -while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) { + while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) { - if (isset($row['diskspace']) - && $row['diskspace_used'] != null - && $row['diskspace_used'] > 0 - && (($row['diskspace_used'] * 100) / $row['diskspace']) >= (int)Settings::Get('system.report_webmax') - ) { + if (isset($row['diskspace']) + && $row['diskspace_used'] != null + && $row['diskspace_used'] > 0 + && (($row['diskspace_used'] * 100) / $row['diskspace']) >= (int)Settings::Get('system.report_webmax') + ) { - $replace_arr = array( - 'NAME' => $row['name'], - 'DISKAVAILABLE' => ($row['diskspace'] / 1024), /* 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' => ($row['diskspace_used'] * 100) / $row['diskspace'], - 'MAX_PERCENT' => Settings::Get('system.report_webmax') - ); + $replace_arr = array( + 'NAME' => $row['name'], + 'DISKAVAILABLE' => ($row['diskspace'] / 1024), /* 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' => ($row['diskspace_used'] * 100) / $row['diskspace'], + 'MAX_PERCENT' => Settings::Get('system.report_webmax') + ); - $lngfile_stmt = Database::prepare(" - SELECT `file` FROM `" . TABLE_PANEL_LANGUAGE . "` - WHERE `language` = :deflang - "); - $lngfile = Database::pexecute_first($lngfile_stmt, array('deflang' => $row['def_language'])); + $lngfile_stmt = Database::prepare(" + SELECT `file` FROM `" . TABLE_PANEL_LANGUAGE . "` + WHERE `language` = :deflang + "); + $lngfile = Database::pexecute_first($lngfile_stmt, array('deflang' => $row['def_language'])); - if ($lngfile !== null) { - $langfile = $lngfile['file']; - } else { - $lngfile = Database::pexecute_first($lngfile_stmt, array('deflang' => Settings::Get('panel.standardlanguage'))); - $langfile = $lngfile['file']; + if ($lngfile !== null) { + $langfile = $lngfile['file']; + } else { + $lngfile = Database::pexecute_first($lngfile_stmt, array('deflang' => Settings::Get('panel.standardlanguage'))); + $langfile = $lngfile['file']; + } + + // include english language file (fallback) + include_once makeCorrectFile(FROXLOR_INSTALL_DIR . '/lng/english.lng.php'); + // include admin/customer language file + include_once makeCorrectFile(FROXLOR_INSTALL_DIR . '/' . $langfile); + + // Get mail templates from database; the ones from 'admin' are fetched for fallback + $result2_stmt = Database::prepare(" + SELECT `value` FROM `" . TABLE_PANEL_TEMPLATES . "` + WHERE `adminid` = :adminid + AND `language` = :lang + AND `templategroup` = 'mails' AND `varname` = :varname + "); + $result2_data = array( + 'adminid' => $row['adminid'], + 'lang' => $row['def_language'], + 'varname' => 'diskmaxpercent_subject' + ); + $result2 = Database::pexecute_first($result2_stmt, $result2_data); + $mail_subject = html_entity_decode(replace_variables((($result2['value'] != '') ? $result2['value'] : $lng['mails']['diskmaxpercent']['subject']), $replace_arr)); + + $result2_data['varname'] = 'diskmaxpercent_mailbody'; + $result2 = Database::pexecute_first($result2_stmt, $result2_data); + $mail_body = html_entity_decode(replace_variables((($result2['value'] != '') ? $result2['value'] : $lng['mails']['diskmaxpercent']['mailbody']), $replace_arr)); + + $_mailerror = false; + try { + $mail->SetFrom($row['email'], $row['name']); + $mail->Subject = $mail_subject; + $mail->AltBody = $mail_body; + $mail->MsgHTML(nl2br($mail_body)); + $mail->AddAddress($row['email'], $row['name']); + $mail->Send(); + } catch(phpmailerException $e) { + $mailerr_msg = $e->errorMessage(); + $_mailerror = true; + } catch (Exception $e) { + $mailerr_msg = $e->getMessage(); + $_mailerror = true; + } + + if ($_mailerror) { + $cronlog->logAction(CRON_ACTION, LOG_ERR, "Error sending mail: " . $mailerr_msg); + echo "Error sending mail: " . $mailerr_msg . "\n"; + } + + $mail->ClearAddresses(); + $upd_stmt = Database::prepare(" + UPDATE `" . TABLE_PANEL_ADMINS . "` SET `reportsent` = '2' + WHERE `adminid` = :adminid + "); + Database::pexecute($upd_stmt, array('adminid' => $row['adminid'])); } - - // include english language file (fallback) - include_once makeCorrectFile(FROXLOR_INSTALL_DIR . '/lng/english.lng.php'); - // include admin/customer language file - include_once makeCorrectFile(FROXLOR_INSTALL_DIR . '/' . $langfile); - - // Get mail templates from database; the ones from 'admin' are fetched for fallback - $result2_stmt = Database::prepare(" - SELECT `value` FROM `" . TABLE_PANEL_TEMPLATES . "` - WHERE `adminid` = :adminid - AND `language` = :lang - AND `templategroup` = 'mails' AND `varname` = :varname - "); - $result2_data = array( - 'adminid' => $row['adminid'], - 'lang' => $row['def_language'], - 'varname' => 'diskmaxpercent_subject' - ); - $result2 = Database::pexecute_first($result2_stmt, $result2_data); - $mail_subject = html_entity_decode(replace_variables((($result2['value'] != '') ? $result2['value'] : $lng['mails']['diskmaxpercent']['subject']), $replace_arr)); - - $result2_data['varname'] = 'diskmaxpercent_mailbody'; - $result2 = Database::pexecute_first($result2_stmt, $result2_data); - $mail_body = html_entity_decode(replace_variables((($result2['value'] != '') ? $result2['value'] : $lng['mails']['diskmaxpercent']['mailbody']), $replace_arr)); - - $_mailerror = false; - try { - $mail->SetFrom($row['email'], $row['name']); - $mail->Subject = $mail_subject; - $mail->AltBody = $mail_body; - $mail->MsgHTML(nl2br($mail_body)); - $mail->AddAddress($row['email'], $row['name']); - $mail->Send(); - } catch(phpmailerException $e) { - $mailerr_msg = $e->errorMessage(); - $_mailerror = true; - } catch (Exception $e) { - $mailerr_msg = $e->getMessage(); - $_mailerror = true; - } - - if ($_mailerror) { - $cronlog->logAction(CRON_ACTION, LOG_ERR, "Error sending mail: " . $mailerr_msg); - echo "Error sending mail: " . $mailerr_msg . "\n"; - } - - $mail->ClearAddresses(); - $upd_stmt = Database::prepare(" - UPDATE `" . TABLE_PANEL_ADMINS . "` SET `reportsent` = '2' - WHERE `adminid` = :adminid - "); - Database::pexecute($upd_stmt, array('adminid' => $row['adminid'])); } -} +} // webmax > 0 diff --git a/scripts/jobs/cron_usage_report.php b/scripts/jobs/cron_usage_report.php index 60d9cc21..8de32c71 100644 --- a/scripts/jobs/cron_usage_report.php +++ b/scripts/jobs/cron_usage_report.php @@ -34,289 +34,291 @@ if (PHPMailer::ValidateAddress(Settings::Get('panel.adminmail')) !== false) { } } -// Warn the customers at xx% traffic-usage +if ((int)Settings::Get('system.report_trafficmax') > 0) +{ + // Warn the customers at xx% traffic-usage + $result_stmt = Database::prepare(" + SELECT `c`.`customerid`, `c`.`adminid`, `c`.`name`, `c`.`firstname`, + `c`.`company`, `c`.`traffic`, `c`.`email`, `c`.`def_language`, + `a`.`name` AS `adminname`, `a`.`email` AS `adminmail`, + (SELECT SUM(`t`.`http` + `t`.`ftp_up` + `t`.`ftp_down` + `t`.`mail`) + FROM `" . TABLE_PANEL_TRAFFIC . "` `t` + WHERE `t`.`customerid` = `c`.`customerid` AND `t`.`year` = :year AND `t`.`month` = :month + ) as `traffic_used` + FROM `" . TABLE_PANEL_CUSTOMERS . "` AS `c` + LEFT JOIN `" . TABLE_PANEL_ADMINS . "` AS `a` + ON `a`.`adminid` = `c`.`adminid` WHERE `c`.`reportsent` <> '1' + "); -$result_stmt = Database::prepare(" - SELECT `c`.`customerid`, `c`.`adminid`, `c`.`name`, `c`.`firstname`, - `c`.`company`, `c`.`traffic`, `c`.`email`, `c`.`def_language`, - `a`.`name` AS `adminname`, `a`.`email` AS `adminmail`, - (SELECT SUM(`t`.`http` + `t`.`ftp_up` + `t`.`ftp_down` + `t`.`mail`) - FROM `" . TABLE_PANEL_TRAFFIC . "` `t` - WHERE `t`.`customerid` = `c`.`customerid` AND `t`.`year` = :year AND `t`.`month` = :month - ) as `traffic_used` - FROM `" . TABLE_PANEL_CUSTOMERS . "` AS `c` - LEFT JOIN `" . TABLE_PANEL_ADMINS . "` AS `a` - ON `a`.`adminid` = `c`.`adminid` WHERE `c`.`reportsent` <> '1' -"); + $result_data = array( + 'year' => date("Y", $yesterday), + 'month' => date("m", $yesterday) + ); + Database::pexecute($result_stmt, $result_data); -$result_data = array( - 'year' => date("Y", $yesterday), - 'month' => date("m", $yesterday) -); -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') + ) { + $rep_userinfo = array( + 'name' => $row['name'], + 'firstname' => $row['firstname'], + 'company' => $row['company'] + ); + $replace_arr = array( + 'SALUTATION' => getCorrectUserSalutation($rep_userinfo), + 'NAME' => $row['name'], // < keep this for compatibility + '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 */ + 'USAGE_PERCENT' => round(($row['traffic_used'] * 100) / $row['traffic'], 2), + 'MAX_PERCENT' => 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( - 'name' => $row['name'], - 'firstname' => $row['firstname'], - 'company' => $row['company'] - ); - $replace_arr = array( - 'SALUTATION' => getCorrectUserSalutation($rep_userinfo), - 'NAME' => $row['name'], // < keep this for compatibility - '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 */ - 'USAGE_PERCENT' => round(($row['traffic_used'] * 100) / $row['traffic'], 2), - 'MAX_PERCENT' => Settings::Get('system.report_trafficmax') - ); + $lngfile_stmt = Database::prepare(" + SELECT `file` FROM `" . TABLE_PANEL_LANGUAGE . "` + WHERE `language` = :deflang + "); + $lngfile = Database::pexecute_first($lngfile_stmt, array('deflang' => $row['def_language'])); - $lngfile_stmt = Database::prepare(" - SELECT `file` FROM `" . TABLE_PANEL_LANGUAGE . "` - WHERE `language` = :deflang - "); - $lngfile = Database::pexecute_first($lngfile_stmt, array('deflang' => $row['def_language'])); + if ($lngfile !== null) { + $langfile = $lngfile['file']; + } else { + $lngfile = Database::pexecute_first($lngfile_stmt, array('deflang' => Settings::Get('panel.standardlanguage'))); + $langfile = $lngfile['file']; + } - if ($lngfile !== null) { - $langfile = $lngfile['file']; - } else { - $lngfile = Database::pexecute_first($lngfile_stmt, array('deflang' => Settings::Get('panel.standardlanguage'))); - $langfile = $lngfile['file']; + // include english language file (fallback) + include_once makeCorrectFile(FROXLOR_INSTALL_DIR . '/lng/english.lng.php'); + // include admin/customer language file + include_once makeCorrectFile(FROXLOR_INSTALL_DIR . '/' . $langfile); + + // Get mail templates from database; the ones from 'admin' are fetched for fallback + $result2_stmt = Database::prepare(" + SELECT `value` FROM `" . TABLE_PANEL_TEMPLATES . "` + WHERE `adminid` = :adminid + AND `language` = :lang + AND `templategroup` = 'mails' AND `varname` = :varname + "); + $result2_data = array( + 'adminid' => $row['adminid'], + 'lang' => $row['def_language'], + 'varname' => 'trafficmaxpercent_subject' + ); + $result2 = Database::pexecute_first($result2_stmt, $result2_data); + $mail_subject = html_entity_decode(replace_variables((($result2['value'] != '') ? $result2['value'] : $lng['mails']['trafficmaxpercent']['subject']), $replace_arr)); + + $result2_data['varname'] = 'trafficmaxpercent_mailbody'; + $result2 = Database::pexecute_first($result2_stmt, $result2_data); + $mail_body = html_entity_decode(replace_variables((($result2['value'] != '') ? $result2['value'] : $lng['mails']['trafficmaxpercent']['mailbody']), $replace_arr)); + + $_mailerror = false; + try { + $mail->SetFrom($row['adminmail'], $row['adminname']); + $mail->Subject = $mail_subject; + $mail->AltBody = $mail_body; + $mail->MsgHTML(nl2br($mail_body)); + $mail->AddAddress($row['email'], $row['firstname'] . ' ' . $row['name']); + $mail->Send(); + } catch(phpmailerException $e) { + $mailerr_msg = $e->errorMessage(); + $_mailerror = true; + } catch (Exception $e) { + $mailerr_msg = $e->getMessage(); + $_mailerror = true; + } + + if ($_mailerror) { + $cronlog->logAction(CRON_ACTION, LOG_ERR, 'Error sending mail: ' . $mailerr_msg); + echo 'Error sending mail: ' . $mailerr_msg . "\n"; + } + + $mail->ClearAddresses(); + $upd_stmt = Database::prepare(" + UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET `reportsent` = '1' + WHERE `customerid` = :customerid + "); + Database::pexecute($upd_stmt, array('customerid' => $row['customerid'])); } - - // include english language file (fallback) - include_once makeCorrectFile(FROXLOR_INSTALL_DIR . '/lng/english.lng.php'); - // include admin/customer language file - include_once makeCorrectFile(FROXLOR_INSTALL_DIR . '/' . $langfile); - - // Get mail templates from database; the ones from 'admin' are fetched for fallback - $result2_stmt = Database::prepare(" - SELECT `value` FROM `" . TABLE_PANEL_TEMPLATES . "` - WHERE `adminid` = :adminid - AND `language` = :lang - AND `templategroup` = 'mails' AND `varname` = :varname - "); - $result2_data = array( - 'adminid' => $row['adminid'], - 'lang' => $row['def_language'], - 'varname' => 'trafficmaxpercent_subject' - ); - $result2 = Database::pexecute_first($result2_stmt, $result2_data); - $mail_subject = html_entity_decode(replace_variables((($result2['value'] != '') ? $result2['value'] : $lng['mails']['trafficmaxpercent']['subject']), $replace_arr)); - - $result2_data['varname'] = 'trafficmaxpercent_mailbody'; - $result2 = Database::pexecute_first($result2_stmt, $result2_data); - $mail_body = html_entity_decode(replace_variables((($result2['value'] != '') ? $result2['value'] : $lng['mails']['trafficmaxpercent']['mailbody']), $replace_arr)); - - $_mailerror = false; - try { - $mail->SetFrom($row['adminmail'], $row['adminname']); - $mail->Subject = $mail_subject; - $mail->AltBody = $mail_body; - $mail->MsgHTML(nl2br($mail_body)); - $mail->AddAddress($row['email'], $row['firstname'] . ' ' . $row['name']); - $mail->Send(); - } catch(phpmailerException $e) { - $mailerr_msg = $e->errorMessage(); - $_mailerror = true; - } catch (Exception $e) { - $mailerr_msg = $e->getMessage(); - $_mailerror = true; - } - - if ($_mailerror) { - $cronlog->logAction(CRON_ACTION, LOG_ERR, 'Error sending mail: ' . $mailerr_msg); - echo 'Error sending mail: ' . $mailerr_msg . "\n"; - } - - $mail->ClearAddresses(); - $upd_stmt = Database::prepare(" - UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET `reportsent` = '1' - WHERE `customerid` = :customerid - "); - Database::pexecute($upd_stmt, array('customerid' => $row['customerid'])); - } -} - -// Warn the admins at xx% traffic-usage -$result_stmt = Database::prepare(" - SELECT `a`.*, - (SELECT SUM(`t`.`http` + `t`.`ftp_up` + `t`.`ftp_down` + `t`.`mail`) - FROM `" . TABLE_PANEL_TRAFFIC_ADMINS . "` `t` - WHERE `t`.`adminid` = `a`.`adminid` AND `t`.`year` = :year AND `t`.`month` = :month - ) as `traffic_used_total` - FROM `" . TABLE_PANEL_ADMINS . "` `a` WHERE `a`.`reportsent` = '0' -"); - -$result_data = array( - 'year' => date("Y", $yesterday), - 'month' => date("m", $yesterday) -); -Database::pexecute($result_stmt, $result_data); - -while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) { - - if (isset($row['traffic']) - && $row['traffic'] > 0 - && (($row['traffic_used_total'] * 100) / $row['traffic']) >= (int)Settings::Get('system.report_trafficmax') - ) { - - $replace_arr = array( - 'NAME' => $row['name'], - '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 */ - 'USAGE_PERCENT' => round(($row['traffic_used_total'] * 100) / $row['traffic'], 2), - 'MAX_PERCENT' => Settings::Get('system.report_trafficmax') - ); - - $lngfile_stmt = Database::prepare(" - SELECT `file` FROM `" . TABLE_PANEL_LANGUAGE . "` - WHERE `language` = :deflang - "); - $lngfile = Database::pexecute_first($lngfile_stmt, array('deflang' => $row['def_language'])); - - if ($lngfile !== null) { - $langfile = $lngfile['file']; - } else { - $lngfile = Database::pexecute_first($lngfile_stmt, array('deflang' => Settings::Get('panel.standardlanguage'))); - $langfile = $lngfile['file']; - } - - // include english language file (fallback) - include_once makeCorrectFile(FROXLOR_INSTALL_DIR . '/lng/english.lng.php'); - // include admin/customer language file - include_once makeCorrectFile(FROXLOR_INSTALL_DIR . '/' . $langfile); - - // Get mail templates from database; the ones from 'admin' are fetched for fallback - $result2_stmt = Database::prepare(" - SELECT `value` FROM `" . TABLE_PANEL_TEMPLATES . "` - WHERE `adminid` = :adminid - AND `language` = :lang - AND `templategroup` = 'mails' AND `varname` = :varname - "); - $resul2_data = array( - 'adminid' => $row['adminid'], - 'lang' => $row['def_language'], - 'varname' => 'trafficmaxpercent_subject' - ); - $result2 = Database::pexecute_first($result2_stmt, $result2_data); - $mail_subject = html_entity_decode(replace_variables((($result2['value'] != '') ? $result2['value'] : $lng['mails']['trafficmaxpercent']['subject']), $replace_arr)); - - $resul2_data['varname'] = 'trafficmaxpercent_mailbody'; - $result2 = Database::pexecute_first($result2_stmt, $result2_data); - $mail_body = html_entity_decode(replace_variables((($result2['value'] != '') ? $result2['value'] : $lng['mails']['trafficmaxpercent']['mailbody']), $replace_arr)); - - $_mailerror = false; - try { - $mail->SetFrom($row['email'], $row['name']); - $mail->Subject = $mail_subject; - $mail->AltBody = $mail_body; - $mail->MsgHTML(nl2br($mail_body)); - $mail->AddAddress($row['email'], $row['name']); - $mail->Send(); - } catch(phpmailerException $e) { - $mailerr_msg = $e->errorMessage(); - $_mailerror = true; - } catch (Exception $e) { - $mailerr_msg = $e->getMessage(); - $_mailerror = true; - } - - if ($_mailerror) { - $cronlog->logAction(CRON_ACTION, LOG_ERR, "Error sending mail: " . $mailerr_msg); - echo "Error sending mail: " . $mailerr_msg . "\n"; - } - - $mail->ClearAddresses(); - $upd_stmt = Database::prepare(" - UPDATE `" . TABLE_PANEL_ADMINS . "` SET `reportsent` = '1' - WHERE `adminid` = :adminid - "); - Database::pexecute($upd_stmt, array('adminid' => $row['adminid'])); } - // Another month, let's build our report - if (date('d') == '01') { + // Warn the admins at xx% traffic-usage + $result_stmt = Database::prepare(" + SELECT `a`.*, + (SELECT SUM(`t`.`http` + `t`.`ftp_up` + `t`.`ftp_down` + `t`.`mail`) + FROM `" . TABLE_PANEL_TRAFFIC_ADMINS . "` `t` + WHERE `t`.`adminid` = `a`.`adminid` AND `t`.`year` = :year AND `t`.`month` = :month + ) as `traffic_used_total` + FROM `" . TABLE_PANEL_ADMINS . "` `a` WHERE `a`.`reportsent` = '0' + "); - $mail_subject = 'Trafficreport ' . date("m/y", $yesterday) . ' for ' . $row['name']; - $mail_body = 'Trafficreport ' . date("m/y", $yesterday) . ' for ' . $row['name'] . "\n"; - $mail_body.= '---------------------------------------------------------------' . "\n"; - $mail_body.= 'Loginname Traffic used (Percent) | Traffic available' . "\n"; - $customers_stmt = Database::prepare(" - SELECT `c`.*, - (SELECT SUM(`t`.`http` + `t`.`ftp_up` + `t`.`ftp_down` + `t`.`mail`) - FROM `" . TABLE_PANEL_TRAFFIC . "` `t` - WHERE `t`.`customerid` = `c`.`customerid` AND `t`.`year` = :year AND `t`.`month` = :month - ) as `traffic_used_total` - FROM `" . TABLE_PANEL_CUSTOMERS . "` `c` WHERE `c`.`adminid` = :adminid - "); - $customers_data = array( - 'year' => date("Y", $yesterday), - 'month' => date("m", $yesterday), - 'adminid' => $row['adminid'] - ); - Database::pexecute($customers_stmt, $customers_data); + $result_data = array( + 'year' => date("Y", $yesterday), + 'month' => date("m", $yesterday) + ); + Database::pexecute($result_stmt, $result_data); - while ($customer = $customers_stmt->fetch(PDO::FETCH_ASSOC)) { - $t = $customer['traffic_used_total']/1048576; - if ($customer['traffic'] > 0) { - $p = (($customer['traffic_used_total'] * 100) / $customer['traffic'] ); - $tg = $customer['traffic']/1048576; + while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) { + + if (isset($row['traffic']) + && $row['traffic'] > 0 + && (($row['traffic_used_total'] * 100) / $row['traffic']) >= (int)Settings::Get('system.report_trafficmax') + ) { + + $replace_arr = array( + 'NAME' => $row['name'], + '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 */ + 'USAGE_PERCENT' => round(($row['traffic_used_total'] * 100) / $row['traffic'], 2), + 'MAX_PERCENT' => Settings::Get('system.report_trafficmax') + ); + + $lngfile_stmt = Database::prepare(" + SELECT `file` FROM `" . TABLE_PANEL_LANGUAGE . "` + WHERE `language` = :deflang + "); + $lngfile = Database::pexecute_first($lngfile_stmt, array('deflang' => $row['def_language'])); + + if ($lngfile !== null) { + $langfile = $lngfile['file']; + } else { + $lngfile = Database::pexecute_first($lngfile_stmt, array('deflang' => Settings::Get('panel.standardlanguage'))); + $langfile = $lngfile['file']; + } + + // include english language file (fallback) + include_once makeCorrectFile(FROXLOR_INSTALL_DIR . '/lng/english.lng.php'); + // include admin/customer language file + include_once makeCorrectFile(FROXLOR_INSTALL_DIR . '/' . $langfile); + + // Get mail templates from database; the ones from 'admin' are fetched for fallback + $result2_stmt = Database::prepare(" + SELECT `value` FROM `" . TABLE_PANEL_TEMPLATES . "` + WHERE `adminid` = :adminid + AND `language` = :lang + AND `templategroup` = 'mails' AND `varname` = :varname + "); + $resul2_data = array( + 'adminid' => $row['adminid'], + 'lang' => $row['def_language'], + 'varname' => 'trafficmaxpercent_subject' + ); + $result2 = Database::pexecute_first($result2_stmt, $result2_data); + $mail_subject = html_entity_decode(replace_variables((($result2['value'] != '') ? $result2['value'] : $lng['mails']['trafficmaxpercent']['subject']), $replace_arr)); + + $resul2_data['varname'] = 'trafficmaxpercent_mailbody'; + $result2 = Database::pexecute_first($result2_stmt, $result2_data); + $mail_body = html_entity_decode(replace_variables((($result2['value'] != '') ? $result2['value'] : $lng['mails']['trafficmaxpercent']['mailbody']), $replace_arr)); + + $_mailerror = false; + try { + $mail->SetFrom($row['email'], $row['name']); + $mail->Subject = $mail_subject; + $mail->AltBody = $mail_body; + $mail->MsgHTML(nl2br($mail_body)); + $mail->AddAddress($row['email'], $row['name']); + $mail->Send(); + } catch(phpmailerException $e) { + $mailerr_msg = $e->errorMessage(); + $_mailerror = true; + } catch (Exception $e) { + $mailerr_msg = $e->getMessage(); + $_mailerror = true; + } + + if ($_mailerror) { + $cronlog->logAction(CRON_ACTION, LOG_ERR, "Error sending mail: " . $mailerr_msg); + echo "Error sending mail: " . $mailerr_msg . "\n"; + } + + $mail->ClearAddresses(); + $upd_stmt = Database::prepare(" + UPDATE `" . TABLE_PANEL_ADMINS . "` SET `reportsent` = '1' + WHERE `adminid` = :adminid + "); + Database::pexecute($upd_stmt, array('adminid' => $row['adminid'])); + } + + // Another month, let's build our report + if (date('d') == '01') { + + $mail_subject = 'Trafficreport ' . date("m/y", $yesterday) . ' for ' . $row['name']; + $mail_body = 'Trafficreport ' . date("m/y", $yesterday) . ' for ' . $row['name'] . "\n"; + $mail_body.= '---------------------------------------------------------------' . "\n"; + $mail_body.= 'Loginname Traffic used (Percent) | Traffic available' . "\n"; + $customers_stmt = Database::prepare(" + SELECT `c`.*, + (SELECT SUM(`t`.`http` + `t`.`ftp_up` + `t`.`ftp_down` + `t`.`mail`) + FROM `" . TABLE_PANEL_TRAFFIC . "` `t` + WHERE `t`.`customerid` = `c`.`customerid` AND `t`.`year` = :year AND `t`.`month` = :month + ) as `traffic_used_total` + FROM `" . TABLE_PANEL_CUSTOMERS . "` `c` WHERE `c`.`adminid` = :adminid + "); + $customers_data = array( + 'year' => date("Y", $yesterday), + 'month' => date("m", $yesterday), + 'adminid' => $row['adminid'] + ); + Database::pexecute($customers_stmt, $customers_data); + + while ($customer = $customers_stmt->fetch(PDO::FETCH_ASSOC)) { + $t = $customer['traffic_used_total']/1048576; + if ($customer['traffic'] > 0) { + $p = (($customer['traffic_used_total'] * 100) / $customer['traffic'] ); + $tg = $customer['traffic']/1048576; + $str = sprintf('%00.1f GB ( %00.1f %% )', $t, $p); + $mail_body.= sprintf('%-15s', $customer['loginname']) . ' ' . sprintf('%-25s', $str) . ' ' . sprintf('%00.1f GB', $tg) . "\n"; + } else if ($customer['traffic'] == 0) { + $str = sprintf('%00.1f GB ( - )', $t); + $mail_body.= sprintf('%-15s', $customer['loginname']) . ' ' . sprintf('%-25s', $str) . ' ' . '0' . "\n"; + } else { + $str = sprintf('%00.1f GB ( - )', $t); + $mail_body.= sprintf('%-15s', $customer['loginname']) . ' ' . sprintf('%-25s', $str) . ' ' . 'unlimited' . "\n"; + } + } + + $mail_body.= '---------------------------------------------------------------' . "\n"; + + $t = $row['traffic_used_total']/1048576; + if ($row['traffic'] > 0) { + $p = (($row['traffic_used_total'] * 100) / $row['traffic']); + $tg = $row['traffic']/1048576; $str = sprintf('%00.1f GB ( %00.1f %% )', $t, $p); - $mail_body.= sprintf('%-15s', $customer['loginname']) . ' ' . sprintf('%-25s', $str) . ' ' . sprintf('%00.1f GB', $tg) . "\n"; - } else if ($customer['traffic'] == 0) { + $mail_body.= sprintf('%-15s', $row['loginname']) . ' ' . sprintf('%-25s', $str) . ' ' . sprintf('%00.1f GB', $tg) . "\n"; + } else if ($row['traffic'] == 0) { $str = sprintf('%00.1f GB ( - )', $t); - $mail_body.= sprintf('%-15s', $customer['loginname']) . ' ' . sprintf('%-25s', $str) . ' ' . '0' . "\n"; + $mail_body.= sprintf('%-15s', $row['loginname']) . ' ' . sprintf('%-25s', $str) . ' ' . '0' . "\n"; } else { $str = sprintf('%00.1f GB ( - )', $t); - $mail_body.= sprintf('%-15s', $customer['loginname']) . ' ' . sprintf('%-25s', $str) . ' ' . 'unlimited' . "\n"; + $mail_body.= sprintf('%-15s', $row['loginname']) . ' ' . sprintf('%-25s', $str) . ' ' . 'unlimited' . "\n"; } + + $_mailerror = false; + try { + $mail->SetFrom($row['email'], $row['name']); + $mail->Subject = $mail_subject; + $mail->Body = $mail_body; + $mail->AddAddress($row['email'], $row['name']); + $mail->Send(); + } catch(phpmailerException $e) { + $mailerr_msg = $e->errorMessage(); + $_mailerror = true; + } catch (Exception $e) { + $mailerr_msg = $e->getMessage(); + $_mailerror = true; + } + + if ($_mailerror) { + $cronlog->logAction(CRON_ACTION, LOG_ERR, 'Error sending mail: ' . $mailerr_msg); + echo 'Error sending mail: ' . $mailerr_msg . "\n"; + } + + $mail->ClearAddresses(); } - - $mail_body.= '---------------------------------------------------------------' . "\n"; - - $t = $row['traffic_used_total']/1048576; - if ($row['traffic'] > 0) { - $p = (($row['traffic_used_total'] * 100) / $row['traffic']); - $tg = $row['traffic']/1048576; - $str = sprintf('%00.1f GB ( %00.1f %% )', $t, $p); - $mail_body.= sprintf('%-15s', $row['loginname']) . ' ' . sprintf('%-25s', $str) . ' ' . sprintf('%00.1f GB', $tg) . "\n"; - } else if ($row['traffic'] == 0) { - $str = sprintf('%00.1f GB ( - )', $t); - $mail_body.= sprintf('%-15s', $row['loginname']) . ' ' . sprintf('%-25s', $str) . ' ' . '0' . "\n"; - } else { - $str = sprintf('%00.1f GB ( - )', $t); - $mail_body.= sprintf('%-15s', $row['loginname']) . ' ' . sprintf('%-25s', $str) . ' ' . 'unlimited' . "\n"; - } - - $_mailerror = false; - try { - $mail->SetFrom($row['email'], $row['name']); - $mail->Subject = $mail_subject; - $mail->Body = $mail_body; - $mail->AddAddress($row['email'], $row['name']); - $mail->Send(); - } catch(phpmailerException $e) { - $mailerr_msg = $e->errorMessage(); - $_mailerror = true; - } catch (Exception $e) { - $mailerr_msg = $e->getMessage(); - $_mailerror = true; - } - - if ($_mailerror) { - $cronlog->logAction(CRON_ACTION, LOG_ERR, 'Error sending mail: ' . $mailerr_msg); - echo 'Error sending mail: ' . $mailerr_msg . "\n"; - } - - $mail->ClearAddresses(); } -} +} // trafficmax > 0 // include diskspace-usage report, #466 include dirname(__FILE__).'/cron_usage.inc.diskspace.php'; From 8366e575125582f87506a1f60f9a08b89934cab0 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 14 Jul 2016 08:25:28 +0200 Subject: [PATCH 0124/1335] Adding new setting for mod_php users to specify content of the global directory options file, fixes #1638 Signed-off-by: Michael Kaufmann (d00p) --- actions/admin/settings/130.webserver.php | 152 +++++++++++------- install/froxlor.sql | 3 +- .../updates/froxlor/0.9/update_0.9.inc.php | 9 ++ lib/version.inc.php | 2 +- lng/english.lng.php | 10 +- lng/german.lng.php | 10 +- 6 files changed, 118 insertions(+), 68 deletions(-) diff --git a/actions/admin/settings/130.webserver.php b/actions/admin/settings/130.webserver.php index d3bdc77d..b6cfc9a8 100644 --- a/actions/admin/settings/130.webserver.php +++ b/actions/admin/settings/130.webserver.php @@ -16,7 +16,6 @@ * @package Settings * */ - return array( 'groups' => array( 'webserver' => array( @@ -29,11 +28,15 @@ return array( 'type' => 'option', 'default' => 'apache2', 'option_mode' => 'one', - 'option_options' => array('apache2' => 'Apache 2', 'lighttpd' => 'ligHTTPd', 'nginx' => 'Nginx'), + 'option_options' => array( + 'apache2' => 'Apache 2', + 'lighttpd' => 'ligHTTPd', + 'nginx' => 'Nginx' + ), 'save_method' => 'storeSettingField', 'plausibility_check_method' => 'checkPhpInterfaceSetting', 'overview_option' => true - ), + ), 'system_apache_24' => array( 'label' => $lng['serversettings']['apache_24'], 'settinggroup' => 'system', @@ -41,34 +44,38 @@ return array( 'type' => 'bool', 'default' => false, 'save_method' => 'storeSettingField', - 'websrv_avail' => array('apache2') - ), - 'system_apache_itksupport' => array( - 'label' => $lng['serversettings']['apache_itksupport'], - 'settinggroup' => 'system', - 'varname' => 'apacheitksupport', - 'type' => 'bool', - 'default' => false, - 'save_method' => 'storeSettingField', - 'visible' => (Settings::Get('system.mod_fcgid') == 0 && Settings::Get('phpfpm.enabled') == 0), - 'websrv_avail' => array('apache2') - ), + 'websrv_avail' => array( + 'apache2' + ) + ), + 'system_apache_itksupport' => array( + 'label' => $lng['serversettings']['apache_itksupport'], + 'settinggroup' => 'system', + 'varname' => 'apacheitksupport', + 'type' => 'bool', + 'default' => false, + 'save_method' => 'storeSettingField', + 'visible' => (Settings::Get('system.mod_fcgid') == 0 && Settings::Get('phpfpm.enabled') == 0), + 'websrv_avail' => array( + 'apache2' + ) + ), 'system_httpuser' => array( 'label' => $lng['admin']['webserver_user'], 'settinggroup' => 'system', 'varname' => 'httpuser', 'type' => 'string', 'default' => 'www-data', - 'save_method' => 'storeSettingWebserverFcgidFpmUser', - ), + 'save_method' => 'storeSettingWebserverFcgidFpmUser' + ), 'system_httpgroup' => array( 'label' => $lng['admin']['webserver_group'], 'settinggroup' => 'system', 'varname' => 'httpgroup', 'type' => 'string', 'default' => 'www-data', - 'save_method' => 'storeSettingField', - ), + 'save_method' => 'storeSettingField' + ), 'system_apacheconf_vhost' => array( 'label' => $lng['serversettings']['apacheconf_vhost'], 'settinggroup' => 'system', @@ -76,8 +83,8 @@ return array( 'type' => 'string', 'string_type' => 'filedir', 'default' => '/etc/apache2/sites-enabled/', - 'save_method' => 'storeSettingField', - ), + 'save_method' => 'storeSettingField' + ), 'system_apacheconf_diroptions' => array( 'label' => $lng['serversettings']['apacheconf_diroptions'], 'settinggroup' => 'system', @@ -85,8 +92,8 @@ return array( 'type' => 'string', 'string_type' => 'filedir', 'default' => '/etc/apache2/sites-enabled/', - 'save_method' => 'storeSettingField', - ), + 'save_method' => 'storeSettingField' + ), 'system_apacheconf_htpasswddir' => array( 'label' => $lng['serversettings']['apacheconf_htpasswddir'], 'settinggroup' => 'system', @@ -94,8 +101,8 @@ return array( 'type' => 'string', 'string_type' => 'confdir', 'default' => '/etc/apache2/htpasswd/', - 'save_method' => 'storeSettingField', - ), + 'save_method' => 'storeSettingField' + ), 'system_logfiles_directory' => array( 'label' => $lng['serversettings']['logfiles_directory'], 'settinggroup' => 'system', @@ -103,8 +110,8 @@ return array( 'type' => 'string', 'string_type' => 'dir', 'default' => '/var/customers/logs/', - 'save_method' => 'storeSettingField', - ), + 'save_method' => 'storeSettingField' + ), 'system_customersslpath' => array( 'label' => $lng['serversettings']['customerssl_directory'], 'settinggroup' => 'system', @@ -112,8 +119,8 @@ return array( 'type' => 'string', 'string_type' => 'confdir', 'default' => '/etc/ssl/froxlor-custom/', - 'save_method' => 'storeSettingField', - ), + 'save_method' => 'storeSettingField' + ), 'system_phpappendopenbasedir' => array( 'label' => $lng['serversettings']['phpappendopenbasedir'], 'settinggroup' => 'system', @@ -121,8 +128,8 @@ return array( 'type' => 'string', 'string_emptyallowed' => true, 'default' => '', - 'save_method' => 'storeSettingField', - ), + 'save_method' => 'storeSettingField' + ), 'system_deactivateddocroot' => array( 'label' => $lng['serversettings']['deactivateddocroot'], 'settinggroup' => 'system', @@ -131,24 +138,36 @@ return array( 'string_type' => 'dir', 'string_emptyallowed' => true, 'default' => '', - 'save_method' => 'storeSettingField', - ), + 'save_method' => 'storeSettingField' + ), 'system_default_vhostconf' => array( 'label' => $lng['serversettings']['default_vhostconf'], 'settinggroup' => 'system', 'varname' => 'default_vhostconf', 'type' => 'text', 'default' => '', + 'save_method' => 'storeSettingField' + ), + 'system_apache_globaldiropt' => array( + 'label' => $lng['serversettings']['apache_globaldiropt'], + 'settinggroup' => 'system', + 'varname' => 'apacheglobaldiropt', + 'type' => 'text', + 'default' => '', 'save_method' => 'storeSettingField', - ), + 'visible' => (Settings::Get('system.mod_fcgid') == 0 && Settings::Get('phpfpm.enabled') == 0), + 'websrv_avail' => array( + 'apache2' + ) + ), 'system_apachereload_command' => array( 'label' => $lng['serversettings']['apachereload_command'], 'settinggroup' => 'system', 'varname' => 'apachereload_command', 'type' => 'string', 'default' => '/etc/init.d/apache2 reload', - 'save_method' => 'storeSettingField', - ), + 'save_method' => 'storeSettingField' + ), 'system_phpreload_command' => array( 'label' => $lng['serversettings']['phpreload_command'], 'settinggroup' => 'system', @@ -156,8 +175,10 @@ return array( 'type' => 'string', 'default' => '', 'save_method' => 'storeSettingField', - 'websrv_avail' => array('nginx') - ), + 'websrv_avail' => array( + 'nginx' + ) + ), 'system_nginx_php_backend' => array( 'label' => $lng['serversettings']['nginx_php_backend'], 'settinggroup' => 'system', @@ -165,8 +186,10 @@ return array( 'type' => 'string', 'default' => '127.0.0.1:8888', 'save_method' => 'storeSettingField', - 'websrv_avail' => array('nginx') - ), + 'websrv_avail' => array( + 'nginx' + ) + ), 'nginx_fastcgiparams' => array( 'label' => $lng['serversettings']['nginx_fastcgiparams'], 'settinggroup' => 'nginx', @@ -175,16 +198,18 @@ return array( 'string_type' => 'file', 'default' => '/etc/nginx/fastcgi_params', 'save_method' => 'storeSettingField', - 'websrv_avail' => array('nginx') - ), + 'websrv_avail' => array( + 'nginx' + ) + ), 'defaultwebsrverrhandler_enabled' => array( 'label' => $lng['serversettings']['defaultwebsrverrhandler_enabled'], 'settinggroup' => 'defaultwebsrverrhandler', 'varname' => 'enabled', 'type' => 'bool', 'default' => false, - 'save_method' => 'storeSettingField', - ), + 'save_method' => 'storeSettingField' + ), 'defaultwebsrverrhandler_err401' => array( 'label' => $lng['serversettings']['defaultwebsrverrhandler_err401'], 'settinggroup' => 'defaultwebsrverrhandler', @@ -192,8 +217,11 @@ return array( 'type' => 'string', 'default' => '', 'save_method' => 'storeSettingField', - 'websrv_avail' => array('apache2', 'nginx') - ), + 'websrv_avail' => array( + 'apache2', + 'nginx' + ) + ), 'defaultwebsrverrhandler_err403' => array( 'label' => $lng['serversettings']['defaultwebsrverrhandler_err403'], 'settinggroup' => 'defaultwebsrverrhandler', @@ -201,16 +229,19 @@ return array( 'type' => 'string', 'default' => '', 'save_method' => 'storeSettingField', - 'websrv_avail' => array('apache2', 'nginx') - ), + 'websrv_avail' => array( + 'apache2', + 'nginx' + ) + ), 'defaultwebsrverrhandler_err404' => array( 'label' => $lng['serversettings']['defaultwebsrverrhandler_err404'], 'settinggroup' => 'defaultwebsrverrhandler', 'varname' => 'err404', 'type' => 'string', 'default' => '', - 'save_method' => 'storeSettingField', - ), + 'save_method' => 'storeSettingField' + ), 'defaultwebsrverrhandler_err500' => array( 'label' => $lng['serversettings']['defaultwebsrverrhandler_err500'], 'settinggroup' => 'defaultwebsrverrhandler', @@ -218,8 +249,11 @@ return array( 'type' => 'string', 'default' => '', 'save_method' => 'storeSettingField', - 'websrv_avail' => array('apache2', 'nginx') - ), + 'websrv_avail' => array( + 'apache2', + 'nginx' + ) + ), 'customredirect_enabled' => array( 'label' => $lng['serversettings']['customredirect_enabled'], 'settinggroup' => 'customredirect', @@ -227,8 +261,11 @@ return array( 'type' => 'bool', 'default' => false, 'save_method' => 'storeSettingField', - 'websrv_avail' => array('apache2', 'lighttpd') - ), + 'websrv_avail' => array( + 'apache2', + 'lighttpd' + ) + ), 'customredirect_default' => array( 'label' => $lng['serversettings']['customredirect_default'], 'settinggroup' => 'customredirect', @@ -238,9 +275,12 @@ return array( 'option_mode' => 'one', 'option_options_method' => 'getRedirectCodes', 'save_method' => 'storeSettingField', - 'websrv_avail' => array('apache2', 'lighttpd') + 'websrv_avail' => array( + 'apache2', + 'lighttpd' ) ) ) ) - ); + ) +); diff --git a/install/froxlor.sql b/install/froxlor.sql index b801ae9b..1fb365cb 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -529,6 +529,7 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES ('system', 'backupenabled', '0'), ('system', 'dnsenabled', '0'), ('system', 'dns_server', 'bind'), + ('system', 'apacheglobaldiropt', ''), ('panel', 'decimal_places', '4'), ('panel', 'adminmail', 'admin@SERVERNAME'), ('panel', 'phpmyadmin_url', ''), @@ -560,7 +561,7 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES ('panel', 'password_special_char_required', '0'), ('panel', 'password_special_char', '!?<>§$%+#=@'), ('panel', 'version', '0.9.36'), - ('panel', 'db_version', '201606190'); + ('panel', 'db_version', '201607140'); DROP TABLE IF EXISTS `panel_tasks`; diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index c01fb274..53454e2b 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -3385,3 +3385,12 @@ if (isDatabaseVersion('201605180')) { updateToDbVersion('201606190'); } + +if (isDatabaseVersion('201606190')) { + + showUpdateStep("Adding new setting for mod_php users to specify content of the global directory options file"); + Settings::AddNew("system.apacheglobaldiropt", ""); + lastStepStatus(0); + + updateToDbVersion('201607140'); +} diff --git a/lib/version.inc.php b/lib/version.inc.php index 1bd799c0..d5d32af0 100644 --- a/lib/version.inc.php +++ b/lib/version.inc.php @@ -19,7 +19,7 @@ $version = '0.9.36'; // Database version (YYYYMMDDC where C is a daily counter) -$dbversion = '201606190'; +$dbversion = '201607140'; // Distribution branding-tag (used for Debian etc.) $branding = ''; diff --git a/lng/english.lng.php b/lng/english.lng.php index c0d1d49b..835aeaae 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -779,8 +779,11 @@ $lng['message']['noreceipients'] = 'No e-mail has been sent because there are no $lng['admin']['sslsettings'] = 'SSL settings'; $lng['cronjobs']['notyetrun'] = 'Not yet run'; $lng['serversettings']['default_vhostconf']['title'] = 'Default vHost-settings'; -$lng['serversettings']['default_vhostconf']['description'] = 'The content of this field will be included into this ip/port vHost container directly. Attention: The code won\'t be checked for any errors. If it contains errors, webserver might not start again!'; -$lng['serversettings']['default_vhostconf_domain']['description'] = 'The content of this field will be included into the domain vHost container directly. Attention: The code won\'t be checked for any errors. If it contains errors, webserver might not start again!'; +$lng['admin']['specialsettings_replacements'] = "You can use the following variables:
{DOMAIN}, {DOCROOT}, {CUSTOMER}, {IP}, {PORT}, {SCHEME}
"; +$lng['serversettings']['default_vhostconf']['description'] = 'The content of this field will be included into this ip/port vHost container directly. '.$lng['admin']['specialsettings_replacements'].' Attention: The code won\'t be checked for any errors. If it contains errors, webserver might not start again!'; +$lng['serversettings']['apache_globaldiropt']['title'] = 'Directory options for customer-prefix'; +$lng['serversettings']['apache_globaldiropt']['description'] = 'The content of this field will be included into the 05_froxlor_dirfix_nofcgid.conf apache config. If empty, the default value is used:

apache >=2.4
Require all granted
AllowOverride All


apache <=2.2
Order allow,deny
allow from all
'; +$lng['serversettings']['default_vhostconf_domain']['description'] = 'The content of this field will be included into the domain vHost container directly. '.$lng['admin']['specialsettings_replacements'].' Attention: The code won\'t be checked for any errors. If it contains errors, webserver might not start again!'; $lng['error']['invalidip'] = 'Invalid IP address: %s'; $lng['serversettings']['decimal_places'] = 'Number of decimal places in traffic/webspace output'; @@ -1854,9 +1857,6 @@ $lng['integrity_check']['SubdomainSslRedirect'] = 'False SSL-redirect flag for n $lng['integrity_check']['FroxlorLocalGroupMemberForFcgidPhpFpm'] = 'froxlor-user in the customer groups (for FCGID/php-fpm)'; $lng['integrity_check']['WebserverGroupMemberForFcgidPhpFpm'] = 'Webserver-user in the customer groups (for FCGID/php-fpm)'; $lng['integrity_check']['SubdomainLetsencrypt'] = 'Main domains with no SSL-Port assigned don\'t have any subdomains with active SSL redirect'; -$lng['admin']['specialsettings_replacements'] = "You can use the following variables:
{DOMAIN}, {DOCROOT}, {CUSTOMER}, {IP}, {PORT}, {SCHEME}
"; -$lng['serversettings']['default_vhostconf']['description'] = 'The content of this field will be included into this ip/port vHost container directly. '.$lng['admin']['specialsettings_replacements'].' Attention: The code won\'t be checked for any errors. If it contains errors, webserver might not start again!'; -$lng['serversettings']['default_vhostconf_domain']['description'] = 'The content of this field will be included into the domain vHost container directly. '.$lng['admin']['specialsettings_replacements'].' Attention: The code won\'t be checked for any errors. If it contains errors, webserver might not start again!'; $lng['admin']['mod_fcgid_umask']['title'] = 'Umask (default: 022)'; // Added for apcuinfo diff --git a/lng/german.lng.php b/lng/german.lng.php index 16b05c54..b6ae0b81 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -775,8 +775,11 @@ $lng['message']['noreceipients'] = 'Es wurde keine E-Mail versendet da sich kein $lng['admin']['sslsettings'] = 'SSL-Einstellungen'; $lng['cronjobs']['notyetrun'] = 'Bisher nicht gestartet'; $lng['serversettings']['default_vhostconf']['title'] = 'Standard vHost-Einstellungen'; -$lng['serversettings']['default_vhostconf']['description'] = 'Der Inhalt dieses Feldes wird direkt in den IP/Port-vHost-Container übernommen.
ACHTUNG: Der Code wird nicht auf Fehler geprüft. Etwaige Fehler werden also auch übernommen. Der Webserver könnte nicht mehr starten!'; -$lng['serversettings']['default_vhostconf_domain']['description'] = 'Der Inhalt dieses Feldes wird direkt in jeden Domain-vHost-Container übernommen.
ACHTUNG: Der Code wird nicht auf Fehler geprüft. Etwaige Fehler werden also auch übernommen. Der Webserver könnte nicht mehr starten!'; +$lng['admin']['specialsettings_replacements'] = "Die folgenden Variablen können verwendet werden:
{DOMAIN}, {DOCROOT}, {CUSTOMER}, {IP}, {PORT}, {SCHEME}
"; +$lng['serversettings']['default_vhostconf']['description'] = 'Der Inhalt dieses Feldes wird direkt in den IP/Port-vHost-Container übernommen. '.$lng['admin']['specialsettings_replacements'].'
ACHTUNG: Der Code wird nicht auf Fehler geprüft. Etwaige Fehler werden also auch übernommen. Der Webserver könnte nicht mehr starten!'; +$lng['serversettings']['default_vhostconf_domain']['description'] = 'Der Inhalt dieses Feldes wird direkt in jeden Domain-vHost-Container übernommen. '. $lng['admin']['specialsettings_replacements'].'ACHTUNG: Der Code wird nicht auf Fehler geprüft. Etwaige Fehler werden also auch übernommen. Der Webserver könnte nicht mehr starten!'; +$lng['serversettings']['apache_globaldiropt']['title'] = 'Kunden-Prefix Ordner-Optionen'; +$lng['serversettings']['apache_globaldiropt']['description'] = 'Der Inhaltdieses Feldes wird in die 05_froxlor_dirfix_nofcgid.conf Apache Konfigurationsdatei eingefügt. Wenn leer werden folgende Standardwerte verwendet:

apache >=2.4
Require all granted
AllowOverride All


apache <=2.2
Order allow,deny
allow from all
'; $lng['error']['invalidip'] = 'Ungültige IP-Adresse: "%s"'; $lng['serversettings']['decimal_places'] = 'Nachkommastellen bei der Ausgabe von Traffic/Webspace'; @@ -1580,9 +1583,6 @@ $lng['integrity_check']['SubdomainSslRedirect'] = 'Falsches SSL-redirect Flag be $lng['integrity_check']['FroxlorLocalGroupMemberForFcgidPhpFpm'] = 'froxlor-Benutzer in Kunden-Gruppen (für FCGID/php-fpm)'; $lng['integrity_check']['WebserverGroupMemberForFcgidPhpFpm'] = 'Webserver-Benutzer in Kunden-Gruppen (für FCGID/php-fpm)'; $lng['integrity_check']['SubdomainLetsencrypt'] = 'Hauptdomains ohne zugewiesenen SSL-Port haben keine Subdomain mit aktiviertem SSL-Redirect'; -$lng['admin']['specialsettings_replacements'] = "Die folgenden Variablen können verwendet werden:
{DOMAIN}, {DOCROOT}, {CUSTOMER}, {IP}, {PORT}, {SCHEME}
"; -$lng['serversettings']['default_vhostconf']['description'] = 'Der Inhalt dieses Feldes wird direkt in den IP/Port-vHost-Container übernommen. '.$lng['admin']['specialsettings_replacements'].'
ACHTUNG: Der Code wird nicht auf Fehler geprüft. Etwaige Fehler werden also auch übernommen. Der Webserver könnte nicht mehr starten!'; -$lng['serversettings']['default_vhostconf_domain']['description'] = 'Der Inhalt dieses Feldes wird direkt in jeden Domain-vHost-Container übernommen. '. $lng['admin']['specialsettings_replacements'].'ACHTUNG: Der Code wird nicht auf Fehler geprüft. Etwaige Fehler werden also auch übernommen. Der Webserver könnte nicht mehr starten!'; $lng['admin']['mod_fcgid_umask']['title'] = 'Umask (Standard: 022)'; // Added for let's encrypt From 3d2cb879b09e7ecd22a665b7e55446c3765c1e42 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 14 Jul 2016 08:36:14 +0200 Subject: [PATCH 0125/1335] actually use the new setting for custom directory options file content, refs #1638 Signed-off-by: Michael Kaufmann (d00p) --- .../jobs/cron_tasks.inc.http.10.apache.php | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/scripts/jobs/cron_tasks.inc.http.10.apache.php b/scripts/jobs/cron_tasks.inc.http.10.apache.php index bc1e5b13..0d45d0d1 100644 --- a/scripts/jobs/cron_tasks.inc.http.10.apache.php +++ b/scripts/jobs/cron_tasks.inc.http.10.apache.php @@ -81,13 +81,23 @@ class apache extends HttpConfigBase { } $this->virtualhosts_data[$vhosts_filename].= ' ' . "\n"; - // >=apache-2.4 enabled? - if (Settings::Get('system.apache24') == '1') { - $this->virtualhosts_data[$vhosts_filename].= ' Require all granted' . "\n"; - $this->virtualhosts_data[$vhosts_filename].= ' AllowOverride All' . "\n"; - } else { - $this->virtualhosts_data[$vhosts_filename].= ' Order allow,deny' . "\n"; - $this->virtualhosts_data[$vhosts_filename].= ' allow from all' . "\n"; + + // check for custom values, see #1638 + $custom_opts = Settings::Get('system.apacheglobaldiropt'); + if (!empty($custom_opts)) + { + $this->virtualhosts_data[$vhosts_filename].= $custom_opts; + } + else + { + // >=apache-2.4 enabled? + if (Settings::Get('system.apache24') == '1') { + $this->virtualhosts_data[$vhosts_filename].= ' Require all granted' . "\n"; + $this->virtualhosts_data[$vhosts_filename].= ' AllowOverride All' . "\n"; + } else { + $this->virtualhosts_data[$vhosts_filename].= ' Order allow,deny' . "\n"; + $this->virtualhosts_data[$vhosts_filename].= ' allow from all' . "\n"; + } } $this->virtualhosts_data[$vhosts_filename].= ' ' . "\n"; } From b63fc5b5089a903ac48d22775ad9a4ed262fbc9e Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 14 Jul 2016 08:45:10 +0200 Subject: [PATCH 0126/1335] fix glued strings in german language file Signed-off-by: Michael Kaufmann (d00p) --- lng/german.lng.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lng/german.lng.php b/lng/german.lng.php index b6ae0b81..c8bdd0f0 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -779,7 +779,7 @@ $lng['admin']['specialsettings_replacements'] = "Die folgenden Variablen können $lng['serversettings']['default_vhostconf']['description'] = 'Der Inhalt dieses Feldes wird direkt in den IP/Port-vHost-Container übernommen. '.$lng['admin']['specialsettings_replacements'].'
ACHTUNG: Der Code wird nicht auf Fehler geprüft. Etwaige Fehler werden also auch übernommen. Der Webserver könnte nicht mehr starten!'; $lng['serversettings']['default_vhostconf_domain']['description'] = 'Der Inhalt dieses Feldes wird direkt in jeden Domain-vHost-Container übernommen. '. $lng['admin']['specialsettings_replacements'].'ACHTUNG: Der Code wird nicht auf Fehler geprüft. Etwaige Fehler werden also auch übernommen. Der Webserver könnte nicht mehr starten!'; $lng['serversettings']['apache_globaldiropt']['title'] = 'Kunden-Prefix Ordner-Optionen'; -$lng['serversettings']['apache_globaldiropt']['description'] = 'Der Inhaltdieses Feldes wird in die 05_froxlor_dirfix_nofcgid.conf Apache Konfigurationsdatei eingefügt. Wenn leer werden folgende Standardwerte verwendet:

apache >=2.4
Require all granted
AllowOverride All


apache <=2.2
Order allow,deny
allow from all
'; +$lng['serversettings']['apache_globaldiropt']['description'] = 'Der Inhalt dieses Feldes wird in die 05_froxlor_dirfix_nofcgid.conf Apache Konfigurationsdatei eingefügt. Wenn leer werden folgende Standardwerte verwendet:

apache >=2.4
Require all granted
AllowOverride All


apache <=2.2
Order allow,deny
allow from all
'; $lng['error']['invalidip'] = 'Ungültige IP-Adresse: "%s"'; $lng['serversettings']['decimal_places'] = 'Nachkommastellen bei der Ausgabe von Traffic/Webspace'; From c3753478f2bdd9f0d6c33573305099ccf09efffe Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 14 Jul 2016 09:14:37 +0200 Subject: [PATCH 0127/1335] add newline after custom options to avoid possible syntax error in apache-config, thx to J-BBB for testing Signed-off-by: Michael Kaufmann (d00p) --- scripts/jobs/cron_tasks.inc.http.10.apache.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/jobs/cron_tasks.inc.http.10.apache.php b/scripts/jobs/cron_tasks.inc.http.10.apache.php index 0d45d0d1..19b20409 100644 --- a/scripts/jobs/cron_tasks.inc.http.10.apache.php +++ b/scripts/jobs/cron_tasks.inc.http.10.apache.php @@ -86,7 +86,7 @@ class apache extends HttpConfigBase { $custom_opts = Settings::Get('system.apacheglobaldiropt'); if (!empty($custom_opts)) { - $this->virtualhosts_data[$vhosts_filename].= $custom_opts; + $this->virtualhosts_data[$vhosts_filename].= $custom_opts . "\n"; } else { From 658965366734615765651e899f433ebbbf9f5d06 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 18 Jul 2016 16:47:19 +0200 Subject: [PATCH 0128/1335] set version to 0.9.37-rc1 for upcoming release candidate Signed-off-by: Michael Kaufmann (d00p) --- install/froxlor.sql | 2 +- install/updates/froxlor/0.9/update_0.9.inc.php | 8 ++++++++ lib/version.inc.php | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/install/froxlor.sql b/install/froxlor.sql index 1fb365cb..1cdeade2 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -560,7 +560,7 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES ('panel', 'password_numeric', '0'), ('panel', 'password_special_char_required', '0'), ('panel', 'password_special_char', '!?<>§$%+#=@'), - ('panel', 'version', '0.9.36'), + ('panel', 'version', '0.9.37-rc1'), ('panel', 'db_version', '201607140'); diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index 53454e2b..6deec65d 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -3394,3 +3394,11 @@ if (isDatabaseVersion('201606190')) { updateToDbVersion('201607140'); } + +if (isFroxlorVersion('0.9.36')) { + + showUpdateStep("Updating from 0.9.36 to 0.9.37-rc1"); + lastStepStatus(0); + + updateToVersion('0.9.37-rc1'); +} diff --git a/lib/version.inc.php b/lib/version.inc.php index d5d32af0..ada557e4 100644 --- a/lib/version.inc.php +++ b/lib/version.inc.php @@ -16,7 +16,7 @@ */ // Main version variable -$version = '0.9.36'; +$version = '0.9.37-rc1'; // Database version (YYYYMMDDC where C is a daily counter) $dbversion = '201607140'; From 6ab8cb1d7c82b645ef3c1d6b29afa612b4ee59d2 Mon Sep 17 00:00:00 2001 From: Michael Wyraz Date: Thu, 21 Jul 2016 15:51:13 +0200 Subject: [PATCH 0129/1335] Adding option to hide some elements in customer panel --- actions/admin/settings/100.panel.php | 24 +++++++++++++++++++ install/froxlor.sql | 2 +- .../updates/froxlor/0.9/update_0.9.inc.php | 9 +++++++ lib/version.inc.php | 2 +- lng/english.lng.php | 8 +++++++ lng/german.lng.php | 8 +++++++ 6 files changed, 51 insertions(+), 2 deletions(-) diff --git a/actions/admin/settings/100.panel.php b/actions/admin/settings/100.panel.php index 63f771de..9a262377 100644 --- a/actions/admin/settings/100.panel.php +++ b/actions/admin/settings/100.panel.php @@ -227,6 +227,30 @@ return array( 'default' => false, 'save_method' => 'storeSettingField', ), + 'panel_customer_hide_options' => array( + 'label' => $lng['serversettings']['panel_customer_hide_options'], + 'settinggroup' => 'panel', + 'varname' => 'customer_hide_options', + 'type' => 'option', + 'default' => '', + 'option_mode' => 'multiple', + 'option_options' => array( + 'email' => $lng['menue']['email']['email'], + 'mysql' => $lng['menue']['mysql']['mysql'], + 'domains' => $lng['menue']['domains']['domains'], + 'ftp' => $lng['menue']['ftp']['ftp'], + 'extras' => $lng['menue']['extras']['extras'], + 'extras.directoryprotection' => $lng['menue']['extras']['extras']." / ".$lng['menue']['extras']['directoryprotection'], + 'extras.pathoptions' => $lng['menue']['extras']['extras']." / ".$lng['menue']['extras']['pathoptions'], + 'extras.logger' => $lng['menue']['extras']['extras']." / ".$lng['menue']['logger']['logger'], + 'extras.backup' => $lng['menue']['extras']['extras']." / ".$lng['menue']['extras']['backup'], + 'traffic' => $lng['menue']['traffic']['traffic'], + 'traffic.http' => $lng['menue']['traffic']['traffic']." / HTTP", + 'traffic.ftp' => $lng['menue']['traffic']['traffic']." / FTP", + 'traffic.mail' => $lng['menue']['traffic']['traffic']." / Mail", + ), + 'save_method' => 'storeSettingField', + ), ), ), ), diff --git a/install/froxlor.sql b/install/froxlor.sql index 1cdeade2..77005af0 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -561,7 +561,7 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES ('panel', 'password_special_char_required', '0'), ('panel', 'password_special_char', '!?<>§$%+#=@'), ('panel', 'version', '0.9.37-rc1'), - ('panel', 'db_version', '201607140'); + ('panel', 'db_version', '201607210'); DROP TABLE IF EXISTS `panel_tasks`; diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index 6deec65d..6bacb52b 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -3402,3 +3402,12 @@ if (isFroxlorVersion('0.9.36')) { updateToVersion('0.9.37-rc1'); } + +if (isDatabaseVersion('201607140')) { + + showUpdateStep("Adding new setting to hide certain options in customer panel"); + Settings::AddNew("panel.customer_hide_options", ""); + lastStepStatus(0); + + updateToDbVersion('201607210'); +} diff --git a/lib/version.inc.php b/lib/version.inc.php index ada557e4..ab7a59d4 100644 --- a/lib/version.inc.php +++ b/lib/version.inc.php @@ -19,7 +19,7 @@ $version = '0.9.37-rc1'; // Database version (YYYYMMDDC where C is a daily counter) -$dbversion = '201607140'; +$dbversion = '201607210'; // Distribution branding-tag (used for Debian etc.) $branding = ''; diff --git a/lng/english.lng.php b/lng/english.lng.php index 835aeaae..f699776a 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -374,6 +374,14 @@ $lng['serversettings']['nameservers']['description'] = 'A comma separated list c $lng['serversettings']['mxservers']['title'] = 'MX servers'; $lng['serversettings']['mxservers']['description'] = 'A comma separated list containing a pair of a number and a hostname separated by whitespace (e.g. \'10 mx.example.com\') containing the mx servers.'; +/** + * Added in froxlor 0.9.7-rc1 + **/ + +$lng['serversettings']['panel_customer_hide_options']['title'] = 'Hide certain options in customer panel'; +$lng['serversettings']['panel_customer_hide_options']['description'] = 'Select options to hide in customer panel. To select multiple options, hold down CTRL while selecting.'; + + /** * CHANGED BETWEEN 1.2.12 and 1.2.13 */ diff --git a/lng/german.lng.php b/lng/german.lng.php index c8bdd0f0..9b9f6165 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -371,6 +371,14 @@ $lng['serversettings']['nameservers']['description'] = 'Eine durch Komma getrenn $lng['serversettings']['mxservers']['title'] = 'MX-Server'; $lng['serversettings']['mxservers']['description'] = 'Eine durch Komma getrenne Liste, die ein Paar mit einer Nummer und den Hostnamen einen MX-Servers, getrennt durch ein Leerzeichen, enthält (z. B. \'10 mx.example.tld\').'; +/** + * Added in froxlor 0.9.7-rc1 + **/ + +$lng['serversettings']['panel_customer_hide_options']['title'] = 'Optionen im Kundenbereich ausblenden'; +$lng['serversettings']['panel_customer_hide_options']['description'] = 'Wählen Sie hier die gewünschten Optionen aus, welche im Kundenbereich ausgeblendet werden sollen. Für Mehrfachauswahl, halten Sie während der Auswahl STRG gedrückt.'; + + /** * CHANGED BETWEEN 1.2.12 and 1.2.13 */ From 084e72968ad4322011f5bd849cefbf832c60034d Mon Sep 17 00:00:00 2001 From: Michael Wyraz Date: Fri, 22 Jul 2016 09:30:55 +0200 Subject: [PATCH 0130/1335] Moved new texts to end of language file --- lng/english.lng.php | 13 +++++-------- lng/german.lng.php | 12 ++++-------- 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/lng/english.lng.php b/lng/english.lng.php index f699776a..d85061a8 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -374,14 +374,6 @@ $lng['serversettings']['nameservers']['description'] = 'A comma separated list c $lng['serversettings']['mxservers']['title'] = 'MX servers'; $lng['serversettings']['mxservers']['description'] = 'A comma separated list containing a pair of a number and a hostname separated by whitespace (e.g. \'10 mx.example.com\') containing the mx servers.'; -/** - * Added in froxlor 0.9.7-rc1 - **/ - -$lng['serversettings']['panel_customer_hide_options']['title'] = 'Hide certain options in customer panel'; -$lng['serversettings']['panel_customer_hide_options']['description'] = 'Select options to hide in customer panel. To select multiple options, hold down CTRL while selecting.'; - - /** * CHANGED BETWEEN 1.2.12 and 1.2.13 */ @@ -2029,3 +2021,8 @@ $lng['serversettings']['dns_server']['description'] = 'Remember that daemons hav $lng['error']['domain_nopunycode'] = 'You must not specify punycode (IDNA). The domain will automatically be converted'; $lng['admin']['dnsenabled'] = 'Enable DNS editor'; $lng['error']['dns_record_toolong'] = 'Records/labels can only be up to 63 characters'; + +// Added in froxlor 0.9.7-rc1 +$lng['serversettings']['panel_customer_hide_options']['title'] = 'Hide certain options in customer panel'; +$lng['serversettings']['panel_customer_hide_options']['description'] = 'Select options to hide in customer panel. To select multiple options, hold down CTRL while selecting.'; + diff --git a/lng/german.lng.php b/lng/german.lng.php index 9b9f6165..d04eb316 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -371,14 +371,6 @@ $lng['serversettings']['nameservers']['description'] = 'Eine durch Komma getrenn $lng['serversettings']['mxservers']['title'] = 'MX-Server'; $lng['serversettings']['mxservers']['description'] = 'Eine durch Komma getrenne Liste, die ein Paar mit einer Nummer und den Hostnamen einen MX-Servers, getrennt durch ein Leerzeichen, enthält (z. B. \'10 mx.example.tld\').'; -/** - * Added in froxlor 0.9.7-rc1 - **/ - -$lng['serversettings']['panel_customer_hide_options']['title'] = 'Optionen im Kundenbereich ausblenden'; -$lng['serversettings']['panel_customer_hide_options']['description'] = 'Wählen Sie hier die gewünschten Optionen aus, welche im Kundenbereich ausgeblendet werden sollen. Für Mehrfachauswahl, halten Sie während der Auswahl STRG gedrückt.'; - - /** * CHANGED BETWEEN 1.2.12 and 1.2.13 */ @@ -1682,3 +1674,7 @@ $lng['serversettings']['dns_server']['description'] = 'Dienste müssen mit den f $lng['error']['domain_nopunycode'] = 'Die Eingabe von Punycode (IDNA) ist nicht notwendig. Die Domain wird automatisch konvertiert.'; $lng['admin']['dnsenabled'] = 'Zugriff auf DNS Editor'; $lng['error']['dns_record_toolong'] = 'Records/Labels können maximal 63 Zeichen lang sein'; + +// Added in froxlor 0.9.37-rc1 +$lng['serversettings']['panel_customer_hide_options']['title'] = 'Optionen im Kundenbereich ausblenden'; +$lng['serversettings']['panel_customer_hide_options']['description'] = 'Wählen Sie hier die gewünschten Optionen aus, welche im Kundenbereich ausgeblendet werden sollen. Für Mehrfachauswahl, halten Sie während der Auswahl STRG gedrückt.'; From d31c4fa37c53dc0cc579f82504445b5c8199cc9f Mon Sep 17 00:00:00 2001 From: Michael Wyraz Date: Fri, 22 Jul 2016 09:31:20 +0200 Subject: [PATCH 0131/1335] Hide customer menu items based on new settings --- lib/navigation/00.froxlor.main.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/navigation/00.froxlor.main.php b/lib/navigation/00.froxlor.main.php index 2a989942..79447400 100644 --- a/lib/navigation/00.froxlor.main.php +++ b/lib/navigation/00.froxlor.main.php @@ -48,11 +48,12 @@ return array ( 'email' => array ( 'url' => 'customer_email.php', 'label' => $lng['menue']['email']['email'], + 'show_element' => ( !in_array('email',explode(',',Settings::Get('panel.customer_hide_options'))) ), 'elements' => array ( array ( 'url' => 'customer_email.php?page=emails', 'label' => $lng['menue']['email']['emails'], - 'required_resources' => 'emails', + 'required_resources' => 'emails' ), array ( 'url' => 'customer_email.php?page=emails&action=add', @@ -71,6 +72,7 @@ return array ( 'mysql' => array ( 'url' => 'customer_mysql.php', 'label' => $lng['menue']['mysql']['mysql'], + 'show_element' => ( !in_array('mysql',explode(',',Settings::Get('panel.customer_hide_options'))) ), 'elements' => array ( array ( 'url' => 'customer_mysql.php?page=mysqls', @@ -89,6 +91,7 @@ return array ( 'domains' => array ( 'url' => 'customer_domains.php', 'label' => $lng['menue']['domains']['domains'], + 'show_element' => ( !in_array('domains',explode(',',Settings::Get('panel.customer_hide_options'))) ), 'elements' => array ( array ( 'url' => 'customer_domains.php?page=domains', @@ -99,6 +102,7 @@ return array ( 'ftp' => array ( 'url' => 'customer_ftp.php', 'label' => $lng['menue']['ftp']['ftp'], + 'show_element' => ( !in_array('ftp',explode(',',Settings::Get('panel.customer_hide_options'))) ), 'elements' => array ( array ( 'url' => 'customer_ftp.php?page=accounts', @@ -115,19 +119,22 @@ return array ( 'extras' => array ( 'url' => 'customer_extras.php', 'label' => $lng['menue']['extras']['extras'], + 'show_element' => ( !in_array('extras',explode(',',Settings::Get('panel.customer_hide_options'))) ), 'elements' => array ( array ( 'url' => 'customer_extras.php?page=htpasswds', 'label' => $lng['menue']['extras']['directoryprotection'], + 'show_element' => ( !in_array('extras.directoryprotection',explode(',',Settings::Get('panel.customer_hide_options'))) ), ), array ( 'url' => 'customer_extras.php?page=htaccess', 'label' => $lng['menue']['extras']['pathoptions'], + 'show_element' => ( !in_array('extras.pathoptions',explode(',',Settings::Get('panel.customer_hide_options'))) ), ), array ( 'url' => 'customer_logger.php?page=log', 'label' => $lng['menue']['logger']['logger'], - 'show_element' => ( Settings::Get('logger.enabled') == true ) + 'show_element' => ( Settings::Get('logger.enabled') == true ) && ( !in_array('extras.logger',explode(',',Settings::Get('panel.customer_hide_options'))) ) ), array ( 'url' => 'customer_extras.php?page=backup', @@ -139,6 +146,7 @@ return array ( 'traffic' => array ( 'url' => 'customer_traffic.php', 'label' => $lng['menue']['traffic']['traffic'], + 'show_element' => ( !in_array('traffic',explode(',',Settings::Get('panel.customer_hide_options'))) ), 'elements' => array ( array ( 'url' => 'customer_traffic.php?page=current', From 5165cac4e265d5b8ef6ea7d904b934ba30b98177 Mon Sep 17 00:00:00 2001 From: Michael Wyraz Date: Fri, 22 Jul 2016 09:44:20 +0200 Subject: [PATCH 0132/1335] Hide http/ftp/mail traffic charts based on new settings --- templates/Sparkle/assets/js/traffic.js | 6 +++--- templates/Sparkle/customer/traffic/traffic.tpl | 18 ++++++++++++------ .../customer/traffic/traffic_details.tpl | 18 ++++++++++++------ 3 files changed, 27 insertions(+), 15 deletions(-) diff --git a/templates/Sparkle/assets/js/traffic.js b/templates/Sparkle/assets/js/traffic.js index 453dad93..ffa6f1f7 100644 --- a/templates/Sparkle/assets/js/traffic.js +++ b/templates/Sparkle/assets/js/traffic.js @@ -93,9 +93,9 @@ $(document).ready(function() { }; - $.plot('#ftpchart', ftpdata, options); - $.plot('#httpchart', httpdata, options); - $.plot('#mailchart', maildata, options); + $('#ftpchart').plot(ftpdata, options); + $('#httpchart').plot(httpdata, options); + $('#mailchart').plot(maildata, options); $("
").css({ position: "absolute", diff --git a/templates/Sparkle/customer/traffic/traffic.tpl b/templates/Sparkle/customer/traffic/traffic.tpl index 442b8470..459c3ea4 100644 --- a/templates/Sparkle/customer/traffic/traffic.tpl +++ b/templates/Sparkle/customer/traffic/traffic.tpl @@ -37,12 +37,18 @@ $header $footer diff --git a/templates/Sparkle/customer/traffic/traffic_details.tpl b/templates/Sparkle/customer/traffic/traffic_details.tpl index e38c7e21..e41f8578 100644 --- a/templates/Sparkle/customer/traffic/traffic_details.tpl +++ b/templates/Sparkle/customer/traffic/traffic_details.tpl @@ -31,12 +31,18 @@ $header $footer From a95233041e2adc329b40be3c011e7cc9acedb815 Mon Sep 17 00:00:00 2001 From: Michael Wyraz Date: Fri, 22 Jul 2016 09:56:40 +0200 Subject: [PATCH 0133/1335] Better translations for new feature to hide menu items and traffic charts from customer panel --- lng/english.lng.php | 4 ++-- lng/german.lng.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lng/english.lng.php b/lng/english.lng.php index d85061a8..4d51f0bd 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -2023,6 +2023,6 @@ $lng['admin']['dnsenabled'] = 'Enable DNS editor'; $lng['error']['dns_record_toolong'] = 'Records/labels can only be up to 63 characters'; // Added in froxlor 0.9.7-rc1 -$lng['serversettings']['panel_customer_hide_options']['title'] = 'Hide certain options in customer panel'; -$lng['serversettings']['panel_customer_hide_options']['description'] = 'Select options to hide in customer panel. To select multiple options, hold down CTRL while selecting.'; +$lng['serversettings']['panel_customer_hide_options']['title'] = 'Hide menu items and traffic charts in customer panel'; +$lng['serversettings']['panel_customer_hide_options']['description'] = 'Select items to hide in customer panel. To select multiple options, hold down CTRL while selecting.'; diff --git a/lng/german.lng.php b/lng/german.lng.php index d04eb316..d1f50438 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -1676,5 +1676,5 @@ $lng['admin']['dnsenabled'] = 'Zugriff auf DNS Editor'; $lng['error']['dns_record_toolong'] = 'Records/Labels können maximal 63 Zeichen lang sein'; // Added in froxlor 0.9.37-rc1 -$lng['serversettings']['panel_customer_hide_options']['title'] = 'Optionen im Kundenbereich ausblenden'; -$lng['serversettings']['panel_customer_hide_options']['description'] = 'Wählen Sie hier die gewünschten Optionen aus, welche im Kundenbereich ausgeblendet werden sollen. Für Mehrfachauswahl, halten Sie während der Auswahl STRG gedrückt.'; +$lng['serversettings']['panel_customer_hide_options']['title'] = 'Menüpunkte und Traffic-Charts im Kundenbereich ausblenden'; +$lng['serversettings']['panel_customer_hide_options']['description'] = 'Wählen Sie hier die gewünschten Menüpunkte und Traffic-Charts aus, welche im Kundenbereich ausgeblendet werden sollen. Für Mehrfachauswahl, halten Sie während der Auswahl STRG gedrückt.'; From 54e2f83b17cba6c5c96badd57d09453a44a8d995 Mon Sep 17 00:00:00 2001 From: Michael Wyraz Date: Fri, 22 Jul 2016 10:30:13 +0200 Subject: [PATCH 0134/1335] function Settings::IsInList that tests if an option of a multi-select setting is set --- lib/classes/settings/class.Settings.php | 17 ++++++++++++++++ lib/navigation/00.froxlor.main.php | 20 +++++++++---------- .../Sparkle/customer/traffic/traffic.tpl | 6 +++--- .../customer/traffic/traffic_details.tpl | 6 +++--- 4 files changed, 33 insertions(+), 16 deletions(-) diff --git a/lib/classes/settings/class.Settings.php b/lib/classes/settings/class.Settings.php index b8ce3866..e90d5245 100644 --- a/lib/classes/settings/class.Settings.php +++ b/lib/classes/settings/class.Settings.php @@ -124,6 +124,23 @@ class Settings { return $result; } + /** + * tests if a setting-value that i s a comma separated list contains an entry + * + * @param string $setting a group and a varname separated by a dot (group.varname) + * @param string $entry the entry that is expected to be in the list + * + * @return boolean true, if the list contains $entry + */ + public function pIsInList($setting = null, $entry = null) { + $s=Settings::Get($setting); + if ($s==null) { + return false; + } + $slist = explode(",",$s); + return in_array($entry, $slist); + } + /** * update a setting / set a new value * diff --git a/lib/navigation/00.froxlor.main.php b/lib/navigation/00.froxlor.main.php index 79447400..2d070c0c 100644 --- a/lib/navigation/00.froxlor.main.php +++ b/lib/navigation/00.froxlor.main.php @@ -48,7 +48,7 @@ return array ( 'email' => array ( 'url' => 'customer_email.php', 'label' => $lng['menue']['email']['email'], - 'show_element' => ( !in_array('email',explode(',',Settings::Get('panel.customer_hide_options'))) ), + 'show_element' => ( !Settings::IsInList('panel.customer_hide_options','email') ), 'elements' => array ( array ( 'url' => 'customer_email.php?page=emails', @@ -72,7 +72,7 @@ return array ( 'mysql' => array ( 'url' => 'customer_mysql.php', 'label' => $lng['menue']['mysql']['mysql'], - 'show_element' => ( !in_array('mysql',explode(',',Settings::Get('panel.customer_hide_options'))) ), + 'show_element' => ( !Settings::IsInList('panel.customer_hide_options','mysql') ), 'elements' => array ( array ( 'url' => 'customer_mysql.php?page=mysqls', @@ -91,7 +91,7 @@ return array ( 'domains' => array ( 'url' => 'customer_domains.php', 'label' => $lng['menue']['domains']['domains'], - 'show_element' => ( !in_array('domains',explode(',',Settings::Get('panel.customer_hide_options'))) ), + 'show_element' => ( !Settings::IsInList('panel.customer_hide_options','domains') ), 'elements' => array ( array ( 'url' => 'customer_domains.php?page=domains', @@ -102,7 +102,7 @@ return array ( 'ftp' => array ( 'url' => 'customer_ftp.php', 'label' => $lng['menue']['ftp']['ftp'], - 'show_element' => ( !in_array('ftp',explode(',',Settings::Get('panel.customer_hide_options'))) ), + 'show_element' => ( !Settings::IsInList('panel.customer_hide_options','ftp') ), 'elements' => array ( array ( 'url' => 'customer_ftp.php?page=accounts', @@ -119,34 +119,34 @@ return array ( 'extras' => array ( 'url' => 'customer_extras.php', 'label' => $lng['menue']['extras']['extras'], - 'show_element' => ( !in_array('extras',explode(',',Settings::Get('panel.customer_hide_options'))) ), + 'show_element' => ( !Settings::IsInList('panel.customer_hide_options','extras') ), 'elements' => array ( array ( 'url' => 'customer_extras.php?page=htpasswds', 'label' => $lng['menue']['extras']['directoryprotection'], - 'show_element' => ( !in_array('extras.directoryprotection',explode(',',Settings::Get('panel.customer_hide_options'))) ), + 'show_element' => ( !Settings::IsInList('panel.customer_hide_options','extras.directoryprotection') ), ), array ( 'url' => 'customer_extras.php?page=htaccess', 'label' => $lng['menue']['extras']['pathoptions'], - 'show_element' => ( !in_array('extras.pathoptions',explode(',',Settings::Get('panel.customer_hide_options'))) ), + 'show_element' => ( !Settings::IsInList('panel.customer_hide_options','extras.pathoptions') ), ), array ( 'url' => 'customer_logger.php?page=log', 'label' => $lng['menue']['logger']['logger'], - 'show_element' => ( Settings::Get('logger.enabled') == true ) && ( !in_array('extras.logger',explode(',',Settings::Get('panel.customer_hide_options'))) ) + 'show_element' => ( Settings::Get('logger.enabled') == true ) && ( !Settings::IsInList('panel.customer_hide_options','extras.logger') ), ), array ( 'url' => 'customer_extras.php?page=backup', 'label' => $lng['menue']['extras']['backup'], - 'show_element' => ( Settings::Get('system.backupenabled') == true ), + 'show_element' => ( Settings::Get('system.backupenabled') == true ) && ( !Settings::IsInList('panel.customer_hide_options','extras.backup') ), ), ), ), 'traffic' => array ( 'url' => 'customer_traffic.php', 'label' => $lng['menue']['traffic']['traffic'], - 'show_element' => ( !in_array('traffic',explode(',',Settings::Get('panel.customer_hide_options'))) ), + 'show_element' => ( !Settings::IsInList('panel.customer_hide_options','traffic') ), 'elements' => array ( array ( 'url' => 'customer_traffic.php?page=current', diff --git a/templates/Sparkle/customer/traffic/traffic.tpl b/templates/Sparkle/customer/traffic/traffic.tpl index 459c3ea4..fd4d9ef9 100644 --- a/templates/Sparkle/customer/traffic/traffic.tpl +++ b/templates/Sparkle/customer/traffic/traffic.tpl @@ -37,15 +37,15 @@ $header
$footer - diff --git a/templates/Sparkle/customer/ftp/accounts_account.tpl b/templates/Sparkle/customer/ftp/accounts_account.tpl index 8038e719..254894fe 100644 --- a/templates/Sparkle/customer/ftp/accounts_account.tpl +++ b/templates/Sparkle/customer/ftp/accounts_account.tpl @@ -2,6 +2,9 @@ {$row['username']} {$row['description']} {$row['documentroot']} + + {$row['shell']} + {$lng['panel']['edit']} From d31589ba99a4928117221c65fd31ce72400edad5 Mon Sep 17 00:00:00 2001 From: w6g23 Date: Sun, 28 Aug 2016 12:59:09 +0200 Subject: [PATCH 0162/1335] Set a User Agent in the HTTP request fetching the LE challenge URI for self check A rule (e.g. Wordpress plugin iThemes) might block requests with empty User Agents. --- lib/classes/ssl/class.lescript.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/classes/ssl/class.lescript.php b/lib/classes/ssl/class.lescript.php index 0ae0e1f8..d4b69704 100644 --- a/lib/classes/ssl/class.lescript.php +++ b/lib/classes/ssl/class.lescript.php @@ -176,7 +176,9 @@ class lescript $this->log("Token for $domain saved at $tokenPath and should be available at $uri"); // simple self check - if ($payload !== trim(@file_get_contents($uri))) { + $selfcheckContextOptions = array('http' => array('header' => "User Agent: Froxlor")); + $selfcheckContext = stream_context_create($selfcheckContextOptions); + if ($payload !== trim(@file_get_contents($uri, false, $selfcheckContext))) { $errmsg = json_encode(error_get_last()); if ($errmsg != "null") { $errmsg = "; PHP error: " . $errmsg; From a84090516691cd690e5d78e089c072df07ef8a0d Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sun, 28 Aug 2016 14:07:13 +0200 Subject: [PATCH 0163/1335] set version specific user-agent in lescript like we do in ajax stuff Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/ssl/class.lescript.php | 7 +++++-- scripts/jobs/cron_letsencrypt.php | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/classes/ssl/class.lescript.php b/lib/classes/ssl/class.lescript.php index d4b69704..a6377868 100644 --- a/lib/classes/ssl/class.lescript.php +++ b/lib/classes/ssl/class.lescript.php @@ -38,9 +38,12 @@ class lescript private $accountKey; - public function __construct($logger) + private $version; + + public function __construct($logger, $version = '1') { $this->logger = $logger; + $this->version = $version; if (Settings::Get('system.letsencryptca') == 'production') { $ca = 'https://acme-v01.api.letsencrypt.org'; } else { @@ -176,7 +179,7 @@ class lescript $this->log("Token for $domain saved at $tokenPath and should be available at $uri"); // simple self check - $selfcheckContextOptions = array('http' => array('header' => "User Agent: Froxlor")); + $selfcheckContextOptions = array('http' => array('header' => "User Agent: Froxlor/".$this->version)); $selfcheckContext = stream_context_create($selfcheckContextOptions); if ($payload !== trim(@file_get_contents($uri, false, $selfcheckContext))) { $errmsg = json_encode(error_get_last()); diff --git a/scripts/jobs/cron_letsencrypt.php b/scripts/jobs/cron_letsencrypt.php index 8fe3d5af..c3c9a88c 100644 --- a/scripts/jobs/cron_letsencrypt.php +++ b/scripts/jobs/cron_letsencrypt.php @@ -132,7 +132,7 @@ foreach ($certrows as $certrow) { try { // Initialize Lescript with documentroot - $le = new lescript($cronlog); + $le = new lescript($cronlog, $version); // Initialize Lescript $le->initAccount($certrow); From 7f56e980095be4e484983ccb1f067f86279ac7c8 Mon Sep 17 00:00:00 2001 From: Oliver Rahner Date: Mon, 29 Aug 2016 10:46:21 +0200 Subject: [PATCH 0164/1335] do not die after token self check We have to finish the challenge request so that the auth does not linger in state "pending", but goes to "invalid". See https://forum.froxlor.org/index.php/topic/13463-lets-encrypt-zertifikate-werden-nicht-erneuert/#entry32895 --- lib/classes/ssl/class.lescript.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/classes/ssl/class.lescript.php b/lib/classes/ssl/class.lescript.php index a6377868..1136d5db 100644 --- a/lib/classes/ssl/class.lescript.php +++ b/lib/classes/ssl/class.lescript.php @@ -189,7 +189,7 @@ class lescript $errmsg = ""; } @unlink($tokenPath); - throw new \RuntimeException("Please check $uri - token not available" . $errmsg); + $cronlog->logAction(CRON_ACTION, LOG_ERR,"letsencrypt Please check $uri - token not available" . $errmsg); } $this->log("Sending request to challenge"); From d7388f20e67d85cb6d3c1b987e1a19dc7666f3b5 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Wed, 31 Aug 2016 15:12:01 +0200 Subject: [PATCH 0165/1335] fix idna convert for >=php-5.6 users when customers want to add a subdomain with an idna-encoded domain Signed-off-by: Michael Kaufmann (d00p) --- customer_domains.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/customer_domains.php b/customer_domains.php index 4f2a0973..66fbc0e1 100644 --- a/customer_domains.php +++ b/customer_domains.php @@ -265,7 +265,7 @@ if ($page == 'overview') { } $subdomain = $idna_convert->encode(preg_replace(array('/\:(\d)+$/', '/^https?\:\/\//'), '', validate($_POST['subdomain'], 'subdomain', '', 'subdomainiswrong'))); - $domain = $idna_convert->encode($_POST['domain']); + $domain = $_POST['domain']; $domain_stmt = Database::prepare("SELECT * FROM `" . TABLE_PANEL_DOMAINS . "` WHERE `domain` = :domain AND `customerid` = :customerid From 34767a14d55424bb0a56466113193130869762be Mon Sep 17 00:00:00 2001 From: Oliver Rahner Date: Wed, 31 Aug 2016 16:35:59 +0200 Subject: [PATCH 0166/1335] Remove dependency on hard coded agreement URL for Let's Encrypt Change the process to first create a new registration, which delivers the current TOS url in the response's header, then modify the newly created registration to accept the agreement. --- lib/classes/ssl/class.lescript.php | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/lib/classes/ssl/class.lescript.php b/lib/classes/ssl/class.lescript.php index 1136d5db..91256ab4 100644 --- a/lib/classes/ssl/class.lescript.php +++ b/lib/classes/ssl/class.lescript.php @@ -30,7 +30,7 @@ class lescript { // https://letsencrypt.org/repository/ - public $license = 'https://letsencrypt.org/documents/LE-SA-v1.1.1-August-1-2016.pdf'; + public $license; private $logger; @@ -79,8 +79,9 @@ class lescript if ($this->client->getLastCode() != 201) { throw new \RuntimeException("Account not initialized, probably due to rate limiting. Whole response: " . json_encode($response)); } - - $this->postNewReg(); + $this->license = $this->client->getAgreementURL(); + + $this->postRegAgreement(parse_url($this->client->getLastLocation(), PHP_URL_PATH)); $this->log('New account certificate registered'); } else { @@ -316,6 +317,16 @@ class lescript )); } + private function postRegAgreement($uri) + { + $this->log('Accepting agreement at URL: ' . $this->license); + + return $this->signedRequest($uri, array( + 'resource' => 'reg', + 'agreement' => $this->license + )); + } + private function generateCSR($privateKey, array $domains) { $domain = reset($domains); @@ -517,6 +528,13 @@ class Client preg_match_all('~Link: <(.+)>;rel="up"~', $this->lastHeader, $matches); return $matches[1]; } + + public function getAgreementURL() + { + preg_match_all('~Link: <(.+)>;rel="terms-of-service"~', $this->lastHeader, $matches); + return $matches[1][0]; + } + } class Base64UrlSafeEncoder From 9260319ac145f11b8005c4a2e65ca27944965997 Mon Sep 17 00:00:00 2001 From: Oliver Rahner Date: Sun, 4 Sep 2016 21:27:23 +0200 Subject: [PATCH 0167/1335] Letsencrypt: only update registration when necessary if no Terms of Service are presented by the ACME server when registering, don't update registration --- lib/classes/ssl/class.lescript.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/classes/ssl/class.lescript.php b/lib/classes/ssl/class.lescript.php index 91256ab4..61e9f289 100644 --- a/lib/classes/ssl/class.lescript.php +++ b/lib/classes/ssl/class.lescript.php @@ -81,7 +81,10 @@ class lescript } $this->license = $this->client->getAgreementURL(); - $this->postRegAgreement(parse_url($this->client->getLastLocation(), PHP_URL_PATH)); + // Terms of Servce are optional according to ACME specs; if no ToS are presented, no need to update registration + if (!empty($this->license)) { + $this->postRegAgreement(parse_url($this->client->getLastLocation(), PHP_URL_PATH)); + } $this->log('New account certificate registered'); } else { From e4887362ec33d6ee191a0df27303ea74de05d54e Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 5 Sep 2016 17:01:10 +0200 Subject: [PATCH 0168/1335] added let's encrypt for froxlor vhost - untested for now, testers are welcome Signed-off-by: Michael Kaufmann (d00p) --- actions/admin/settings/131.ssl.php | 30 +- actions/admin/settings/135.fcgid.php | 2 +- actions/admin/settings/136.phpfpm.php | 2 +- install/froxlor.sql | 6 +- .../updates/froxlor/0.9/update_0.9.inc.php | 10 + lib/classes/ssl/class.lescript.php | 25 +- lib/version.inc.php | 2 +- lng/english.lng.php | 4 + lng/german.lng.php | 4 + scripts/jobs/cron_letsencrypt.php | 92 +++ .../jobs/cron_tasks.inc.http.10.apache.php | 63 +- .../jobs/cron_tasks.inc.http.20.lighttpd.php | 603 +++++++++--------- scripts/jobs/cron_tasks.inc.http.30.nginx.php | 32 + 13 files changed, 524 insertions(+), 351 deletions(-) diff --git a/actions/admin/settings/131.ssl.php b/actions/admin/settings/131.ssl.php index 07d28ced..f7b5c4e7 100644 --- a/actions/admin/settings/131.ssl.php +++ b/actions/admin/settings/131.ssl.php @@ -81,13 +81,29 @@ return array( 'save_method' => 'storeSettingField', ), 'system_leenabled' => array( - 'label' => $lng['serversettings']['leenabled'], - 'settinggroup' => 'system', - 'varname' => 'leenabled', - 'type' => 'bool', - 'default' => false, - 'cronmodule' => 'froxlor/letsencrypt', - 'save_method' => 'storeSettingField' + 'label' => $lng['serversettings']['leenabled'], + 'settinggroup' => 'system', + 'varname' => 'leenabled', + 'type' => 'bool', + 'default' => false, + 'cronmodule' => 'froxlor/letsencrypt', + 'save_method' => 'storeSettingField' + ), + 'system_le_froxlor_enabled' => array( + 'label' => $lng['serversettings']['le_froxlor_enabled'], + 'settinggroup' => 'system', + 'varname' => 'le_froxlor_enabled', + 'type' => 'bool', + 'default' => false, + 'save_method' => 'storeSettingField' + ), + 'system_le_froxlor_redirect' => array( + 'label' => $lng['serversettings']['le_froxlor_redirect'], + 'settinggroup' => 'system', + 'varname' => 'le_froxlor_redirect', + 'type' => 'bool', + 'default' => false, + 'save_method' => 'storeSettingField' ), 'system_letsencryptca' => array( 'label' => $lng['serversettings']['letsencryptca'], diff --git a/actions/admin/settings/135.fcgid.php b/actions/admin/settings/135.fcgid.php index 847ccdd5..0161ce7f 100644 --- a/actions/admin/settings/135.fcgid.php +++ b/actions/admin/settings/135.fcgid.php @@ -102,7 +102,7 @@ return array( 'settinggroup' => 'system', 'varname' => 'mod_fcgid_ownvhost', 'type' => 'bool', - 'default' => false, + 'default' => true, 'save_method' => 'storeSettingField', 'websrv_avail' => array('apache2') ), diff --git a/actions/admin/settings/136.phpfpm.php b/actions/admin/settings/136.phpfpm.php index 914da31c..c4a3afd1 100644 --- a/actions/admin/settings/136.phpfpm.php +++ b/actions/admin/settings/136.phpfpm.php @@ -35,7 +35,7 @@ return array( 'settinggroup' => 'phpfpm', 'varname' => 'enabled_ownvhost', 'type' => 'bool', - 'default' => false, + 'default' => true, 'save_method' => 'storeSettingField' ), 'system_phpfpm_httpuser' => array( diff --git a/install/froxlor.sql b/install/froxlor.sql index 87d9e0c7..0457bd5f 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -521,7 +521,7 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES ('system', 'lepublickey', 'unset'), ('system', 'letsencryptca', 'production'), ('system', 'letsencryptcountrycode', 'DE'), - ('system', 'letsencryptstate', 'Germany'), + ('system', 'letsencryptstate', 'Hessen'), ('system', 'letsencryptchallengepath', '/var/www/froxlor'), ('system', 'letsencryptkeysize', '4096'), ('system', 'letsencryptreuseold', 0), @@ -532,6 +532,8 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES ('system', 'apacheglobaldiropt', ''), ('system', 'allow_customer_shell', '0'), ('system', 'available_shells', ''), + ('system', 'le_froxlor_enabled', '0'), + ('system', 'le_froxlor_redirect', '0'), ('panel', 'decimal_places', '4'), ('panel', 'adminmail', 'admin@SERVERNAME'), ('panel', 'phpmyadmin_url', ''), @@ -563,7 +565,7 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES ('panel', 'password_special_char_required', '0'), ('panel', 'password_special_char', '!?<>§$%+#=@'), ('panel', 'version', '0.9.37'), - ('panel', 'db_version', '201608260'); + ('panel', 'db_version', '201609050'); DROP TABLE IF EXISTS `panel_tasks`; diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index d8fa3a50..f801cdf5 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -3425,3 +3425,13 @@ if (isDatabaseVersion('201607210')) { updateToDbVersion('201608260'); } + +if (isDatabaseVersion('201608260')) { + + showUpdateStep("Adding new settings to use Let's Encrypt for froxlor"); + Settings::AddNew("system.le_froxlor_enabled", "0"); + Settings::AddNew("system.le_froxlor_redirect", "0"); + lastStepStatus(0); + + updateToDbVersion('201609050'); +} diff --git a/lib/classes/ssl/class.lescript.php b/lib/classes/ssl/class.lescript.php index 61e9f289..fe31a5c0 100644 --- a/lib/classes/ssl/class.lescript.php +++ b/lib/classes/ssl/class.lescript.php @@ -53,7 +53,7 @@ class lescript $this->log("Using '$ca' to generate certificate"); } - public function initAccount($certrow) + public function initAccount($certrow, $isFroxlorVhost = false) { // Let's see if we have the private accountkey $this->accountKey = $certrow['leprivatekey']; @@ -66,12 +66,17 @@ class lescript $keys = $this->generateKey(); // Only store the accountkey in production, in staging always generate a new key if (Settings::Get('system.letsencryptca') == 'production') { - $upd_stmt = Database::prepare("UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET `lepublickey` = :public, `leprivatekey` = :private " . "WHERE `customerid` = :customerid;"); - Database::pexecute($upd_stmt, array( - 'public' => $keys['public'], - 'private' => $keys['private'], - 'customerid' => $certrow['customerid'] - )); + if ($isFroxlorVhost) { + Settings::Set('system.lepublickey', $keys['public']); + Settings::Set('system.leprivatekey', $keys['private']); + } else { + $upd_stmt = Database::prepare("UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET `lepublickey` = :public, `leprivatekey` = :private " . "WHERE `customerid` = :customerid;"); + Database::pexecute($upd_stmt, array( + 'public' => $keys['public'], + 'private' => $keys['private'], + 'customerid' => $certrow['customerid'] + )); + } } $this->accountKey = $keys['private']; @@ -80,10 +85,10 @@ class lescript throw new \RuntimeException("Account not initialized, probably due to rate limiting. Whole response: " . json_encode($response)); } $this->license = $this->client->getAgreementURL(); - + // Terms of Servce are optional according to ACME specs; if no ToS are presented, no need to update registration - if (!empty($this->license)) { - $this->postRegAgreement(parse_url($this->client->getLastLocation(), PHP_URL_PATH)); + if (!empty($this->license)) { + $this->postRegAgreement(parse_url($this->client->getLastLocation(), PHP_URL_PATH)); } $this->log('New account certificate registered'); } else { diff --git a/lib/version.inc.php b/lib/version.inc.php index ac68d42a..5ce58c67 100644 --- a/lib/version.inc.php +++ b/lib/version.inc.php @@ -19,7 +19,7 @@ $version = '0.9.37'; // Database version (YYYYMMDDC where C is a daily counter) -$dbversion = '201608260'; +$dbversion = '201609050'; // Distribution branding-tag (used for Debian etc.) $branding = ''; diff --git a/lng/english.lng.php b/lng/english.lng.php index c506ba39..62b6e62a 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -2032,3 +2032,7 @@ $lng['serversettings']['allow_allow_customer_shell']['description'] = ' 'froxlor.panel', + 'domain' => Settings::Get('system.hostname'), + 'domainid' => 0, + 'documentroot' => FROXLOR_INSTALL_DIR, + 'leprivatekey' => Settings::Get('system.leprivatekey'), + 'lepublickey' => Settings::Get('system.lepublickey'), + '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 + ); + + $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) { + $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']; + } + $domains = array( + $certrow['domain'], + 'www.'.$certrow['domain'] + ); + + // Only renew let's encrypt certificate if no broken ssl_redirect is enabled + if ($certrow['ssl_redirect'] != 2) { + $cronlog->logAction(CRON_ACTION, LOG_DEBUG, "Updating " . $certrow['domain']); + + $cronlog = FroxlorLogger::getInstanceOf(array( + 'loginname' => $certrow['loginname'] + )); + + try { + // Initialize Lescript with documentroot + $le = new lescript($cronlog, $version); + + // Initialize Lescript + $le->initAccount($certrow, true); + + // Request the new certificate (old key may be used) + $return = $le->signDomains($domains, $certrow['ssl_key_file'], $certrow['ssl_csr_file']); + + // We are interessted in the expirationdate + $newcert = openssl_x509_parse($return['crt']); + + // Store the new data + Database::pexecute($updcert_stmt, + array( + 'id' => $certrow['id'], + 'domainid' => $certrow['domainid'], + 'crt' => $return['crt'], + 'key' => $return['key'], + 'ca' => $return['chain'], + 'chain' => $return['chain'], + 'csr' => $return['csr'], + 'expirationdate' => date('Y-m-d H:i:s', $newcert['validTo_time_t']) + )); + + if ($certrow['ssl_redirect'] == 3) { + Settings::Set('system.le_froxlor_redirect', '1'); + } + + $cronlog->logAction(CRON_ACTION, LOG_INFO, "Updated Let's Encrypt certificate for " . $certrow['domain']); + + $changedetected = 1; + } catch (Exception $e) { + $cronlog->logAction(CRON_ACTION, LOG_ERR, + "Could not get Let's Encrypt certificate for " . $certrow['domain'] . ": " . $e->getMessage()); + } + } else { + $cronlog->logAction(CRON_ACTION, LOG_WARNING, + "Skipping Let's Encrypt generation for " . $certrow['domain'] . " due to an enabled ssl_redirect"); + } +} + +// customer domains $certrows = $certificates_stmt->fetchAll(PDO::FETCH_ASSOC); foreach ($certrows as $certrow) { diff --git a/scripts/jobs/cron_tasks.inc.http.10.apache.php b/scripts/jobs/cron_tasks.inc.http.10.apache.php index 8a7da463..c183e3e6 100644 --- a/scripts/jobs/cron_tasks.inc.http.10.apache.php +++ b/scripts/jobs/cron_tasks.inc.http.10.apache.php @@ -363,12 +363,32 @@ class apache extends HttpConfigBase { $row_ipsandports['ssl_cert_chainfile'] = Settings::Get('system.ssl_cert_chainfile'); } - if ($row_ipsandports['ssl_cert_file'] != '') { + $domain = array( + 'id' => 0, + 'domain' => Settings::Get('system.hostname'), + 'adminid' => 1, /* first admin-user (superadmin) */ + 'loginname' => 'froxlor.panel', + 'documentroot' => $mypath, + ); + + // override corresponding array values + $domain['ssl_cert_file'] = $row_ipsandports['ssl_cert_file']; + $domain['ssl_key_file'] = $row_ipsandports['ssl_key_file']; + $domain['ssl_ca_file'] = $row_ipsandports['ssl_ca_file']; + $domain['ssl_cert_chainfile'] = $row_ipsandports['ssl_cert_chainfile']; + + // SSL STUFF + $dssl = new DomainSSL(); + // this sets the ssl-related array-indices in the $domain array + // if the domain has customer-defined ssl-certificates + $dssl->setDomainSSLFilesArray($domain); + + if ($domain['ssl_cert_file'] != '') { // check for existence, #1485 - if (!file_exists($row_ipsandports['ssl_cert_file'])) { - $this->logger->logAction(CRON_ACTION, LOG_ERR, $ipport . ' :: certificate file "'.$row_ipsandports['ssl_cert_file'].'" does not exist! Cannot create ssl-directives'); - echo $ipport . ' :: certificate file "'.$row_ipsandports['ssl_cert_file'].'" does not exist! Cannot create SSL-directives'."\n"; + if (!file_exists($domain['ssl_cert_file'])) { + $this->logger->logAction(CRON_ACTION, LOG_ERR, $ipport . ' :: certificate file "'.$domain['ssl_cert_file'].'" does not exist! Cannot create ssl-directives'); + echo $ipport . ' :: certificate file "'.$domain['ssl_cert_file'].'" does not exist! Cannot create SSL-directives'."\n"; } else { $this->virtualhosts_data[$vhosts_filename] .= ' SSLEngine On' . "\n"; @@ -377,36 +397,36 @@ class apache extends HttpConfigBase { $this->virtualhosts_data[$vhosts_filename] .= ' SSLHonorCipherOrder On' . "\n"; $this->virtualhosts_data[$vhosts_filename] .= ' SSLCipherSuite ' . Settings::Get('system.ssl_cipher_list') . "\n"; $this->virtualhosts_data[$vhosts_filename] .= ' SSLVerifyDepth 10' . "\n"; - $this->virtualhosts_data[$vhosts_filename] .= ' SSLCertificateFile ' . makeCorrectFile($row_ipsandports['ssl_cert_file']) . "\n"; + $this->virtualhosts_data[$vhosts_filename] .= ' SSLCertificateFile ' . makeCorrectFile($domain['ssl_cert_file']) . "\n"; - if ($row_ipsandports['ssl_key_file'] != '') { + if ($domain['ssl_key_file'] != '') { // check for existence, #1485 - if (!file_exists($row_ipsandports['ssl_key_file'])) { - $this->logger->logAction(CRON_ACTION, LOG_ERR, $ipport . ' :: certificate key file "'.$row_ipsandports['ssl_key_file'].'" does not exist! Cannot create ssl-directives'); - echo $ipport . ' :: certificate key file "'.$row_ipsandports['ssl_key_file'].'" does not exist! SSL-directives might not be working'."\n"; + if (!file_exists($domain['ssl_key_file'])) { + $this->logger->logAction(CRON_ACTION, LOG_ERR, $ipport . ' :: certificate key file "'.$domain['ssl_key_file'].'" does not exist! Cannot create ssl-directives'); + echo $ipport . ' :: certificate key file "'.$domain['ssl_key_file'].'" does not exist! SSL-directives might not be working'."\n"; } else { - $this->virtualhosts_data[$vhosts_filename] .= ' SSLCertificateKeyFile ' . makeCorrectFile($row_ipsandports['ssl_key_file']) . "\n"; + $this->virtualhosts_data[$vhosts_filename] .= ' SSLCertificateKeyFile ' . makeCorrectFile($domain['ssl_key_file']) . "\n"; } } - if ($row_ipsandports['ssl_ca_file'] != '') { + if ($domain['ssl_ca_file'] != '') { // check for existence, #1485 - if (!file_exists($row_ipsandports['ssl_ca_file'])) { - $this->logger->logAction(CRON_ACTION, LOG_ERR, $ipport . ' :: certificate CA file "'.$row_ipsandports['ssl_ca_file'].'" does not exist! Cannot create ssl-directives'); - echo $ipport . ' :: certificate CA file "'.$row_ipsandports['ssl_ca_file'].'" does not exist! SSL-directives might not be working'."\n"; + if (!file_exists($domain['ssl_ca_file'])) { + $this->logger->logAction(CRON_ACTION, LOG_ERR, $ipport . ' :: certificate CA file "'.$domain['ssl_ca_file'].'" does not exist! Cannot create ssl-directives'); + echo $ipport . ' :: certificate CA file "'.$domain['ssl_ca_file'].'" does not exist! SSL-directives might not be working'."\n"; } else { - $this->virtualhosts_data[$vhosts_filename] .= ' SSLCACertificateFile ' . makeCorrectFile($row_ipsandports['ssl_ca_file']) . "\n"; + $this->virtualhosts_data[$vhosts_filename] .= ' SSLCACertificateFile ' . makeCorrectFile($domain['ssl_ca_file']) . "\n"; } } // #418 - if ($row_ipsandports['ssl_cert_chainfile'] != '') { + if ($domain['ssl_cert_chainfile'] != '') { // check for existence, #1485 - if (!file_exists($row_ipsandports['ssl_cert_chainfile'])) { - $this->logger->logAction(CRON_ACTION, LOG_ERR, $ipport . ' :: certificate chain file "'.$row_ipsandports['ssl_cert_chainfile'].'" does not exist! Cannot create ssl-directives'); - echo $ipport . ' :: certificate chain file "'.$row_ipsandports['ssl_cert_chainfile'].'" does not exist! SSL-directives might not be working'."\n"; + if (!file_exists($domain['ssl_cert_chainfile'])) { + $this->logger->logAction(CRON_ACTION, LOG_ERR, $ipport . ' :: certificate chain file "'.$domain['ssl_cert_chainfile'].'" does not exist! Cannot create ssl-directives'); + echo $ipport . ' :: certificate chain file "'.$domain['ssl_cert_chainfile'].'" does not exist! SSL-directives might not be working'."\n"; } else { - $this->virtualhosts_data[$vhosts_filename] .= ' SSLCertificateChainFile ' . makeCorrectFile($row_ipsandports['ssl_cert_chainfile']) . "\n"; + $this->virtualhosts_data[$vhosts_filename] .= ' SSLCertificateChainFile ' . makeCorrectFile($domain['ssl_cert_chainfile']) . "\n"; } } } @@ -845,6 +865,9 @@ class apache extends HttpConfigBase { } } + // avoid using any whitespaces + $domain['documentroot'] = trim($domain['documentroot']); + if (preg_match('/^https?\:\/\//', $domain['documentroot'])) { $corrected_docroot = $this->idnaConvert->encode($domain['documentroot']); diff --git a/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php b/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php index c91bd78c..ccd137c2 100644 --- a/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php +++ b/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php @@ -1,4 +1,7 @@ - (2003-2009) - * @author Froxlor team (2010-) - * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt - * @package Cron + * @copyright (c) the authors + * @author Florian Lippert (2003-2009) + * @author Froxlor team (2010-) + * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt + * @package Cron * - * @TODO ssl-redirect to non-standard port + * @todo ssl-redirect to non-standard port */ -require_once(dirname(__FILE__).'/../classes/class.HttpConfigBase.php'); +require_once (dirname(__FILE__) . '/../classes/class.HttpConfigBase.php'); + +class lighttpd extends HttpConfigBase +{ -class lighttpd extends HttpConfigBase { private $logger = false; + private $idnaConvert = false; // protected protected $lighttpd_data = array(); + protected $needed_htpasswds = array(); + protected $auth_backend_loaded = false; + protected $htpasswd_files = array(); + protected $mod_accesslog_loaded = "0"; /** @@ -39,14 +49,15 @@ class lighttpd extends HttpConfigBase { */ private $_deactivated = false; - public function __construct($logger, $idnaConvert) { + public function __construct($logger, $idnaConvert) + { $this->logger = $logger; $this->idnaConvert = $idnaConvert; } - - public function reload() { - if ((int)Settings::Get('phpfpm.enabled') == 1) { + public function reload() + { + if ((int) Settings::Get('phpfpm.enabled') == 1) { $this->logger->logAction(CRON_ACTION, LOG_INFO, 'lighttpd::reload: reloading php-fpm'); safe_exec(escapeshellcmd(Settings::Get('phpfpm.reload'))); } @@ -54,15 +65,15 @@ class lighttpd extends HttpConfigBase { safe_exec(escapeshellcmd(Settings::Get('system.apachereload_command'))); } - - public function createIpPort() { + public function createIpPort() + { $result_ipsandports_stmt = Database::query("SELECT * FROM `" . TABLE_PANEL_IPSANDPORTS . "` ORDER BY `ip` ASC, `port` ASC"); while ($row_ipsandports = $result_ipsandports_stmt->fetch(PDO::FETCH_ASSOC)) { if (filter_var($row_ipsandports['ip'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { $ip = '[' . $row_ipsandports['ip'] . ']'; $port = $row_ipsandports['port']; - $ipv6 = 'server.use-ipv6 = "enable"'."\n"; + $ipv6 = 'server.use-ipv6 = "enable"' . "\n"; } else { $ip = $row_ipsandports['ip']; $port = $row_ipsandports['port']; @@ -72,22 +83,22 @@ class lighttpd extends HttpConfigBase { $this->logger->logAction(CRON_ACTION, LOG_INFO, 'lighttpd::createIpPort: creating ip/port settings for ' . $ip . ":" . $port); $vhost_filename = makeCorrectFile(Settings::Get('system.apacheconf_vhost') . '/10_froxlor_ipandport_' . trim(str_replace(':', '.', $row_ipsandports['ip']), '.') . '.' . $row_ipsandports['port'] . '.conf'); - if (!isset($this->lighttpd_data[$vhost_filename])) { + if (! isset($this->lighttpd_data[$vhost_filename])) { $this->lighttpd_data[$vhost_filename] = ''; } - $this->lighttpd_data[$vhost_filename].= '$SERVER["socket"] == "' . $ip . ':' . $port . '" {' . "\n"; + $this->lighttpd_data[$vhost_filename] .= '$SERVER["socket"] == "' . $ip . ':' . $port . '" {' . "\n"; if ($row_ipsandports['listen_statement'] == '1') { - $this->lighttpd_data[$vhost_filename].= 'server.port = ' . $port . "\n"; - $this->lighttpd_data[$vhost_filename].= 'server.bind = "' . $ip . '"' . "\n"; - $this->lighttpd_data[$vhost_filename].= $ipv6; + $this->lighttpd_data[$vhost_filename] .= 'server.port = ' . $port . "\n"; + $this->lighttpd_data[$vhost_filename] .= 'server.bind = "' . $ip . '"' . "\n"; + $this->lighttpd_data[$vhost_filename] .= $ipv6; } if ($row_ipsandports['vhostcontainer'] == '1') { $myhost = str_replace('.', '\.', Settings::Get('system.hostname')); - $this->lighttpd_data[$vhost_filename].= '# Froxlor default vhost' . "\n"; - $this->lighttpd_data[$vhost_filename].= '$HTTP["host"] =~ "^(?:www\.|)' . $myhost . '$" {' . "\n"; + $this->lighttpd_data[$vhost_filename] .= '# Froxlor default vhost' . "\n"; + $this->lighttpd_data[$vhost_filename] .= '$HTTP["host"] =~ "^(?:www\.|)' . $myhost . '$" {' . "\n"; if ($row_ipsandports['docroot'] == '') { if (Settings::Get('system.froxlordirectlyviahostname')) { @@ -100,27 +111,28 @@ class lighttpd extends HttpConfigBase { $mypath = makeCorrectDir($row_ipsandports['docroot']); } - $this->lighttpd_data[$vhost_filename].= ' server.document-root = "'.$mypath.'"'."\n"; + $this->lighttpd_data[$vhost_filename] .= ' server.document-root = "' . $mypath . '"' . "\n"; /** * dirprotection, see #72 - * @TODO use better regex for this, deferred until 0.9.5 * - $this->lighttpd_data[$vhost_filename].= ' $HTTP["url"] =~ "^/(.+)\/(.+)\.php" {' . "\n"; - $this->lighttpd_data[$vhost_filename].= ' url.access-deny = ("")' . "\n"; - $this->lighttpd_data[$vhost_filename].= ' }' . "\n"; + * @todo use better regex for this, deferred until 0.9.5 + * + * $this->lighttpd_data[$vhost_filename].= ' $HTTP["url"] =~ "^/(.+)\/(.+)\.php" {' . "\n"; + * $this->lighttpd_data[$vhost_filename].= ' url.access-deny = ("")' . "\n"; + * $this->lighttpd_data[$vhost_filename].= ' }' . "\n"; */ /** * own php-fpm vhost */ - if ((int)Settings::Get('phpfpm.enabled') == 1) { + if ((int) Settings::Get('phpfpm.enabled') == 1) { $domain = array( 'id' => 'none', 'domain' => Settings::Get('system.hostname'), 'adminid' => 1, /* first admin-user (superadmin) */ - 'mod_fcgid_starter' => -1, - 'mod_fcgid_maxrequests' => -1, + 'mod_fcgid_starter' => - 1, + 'mod_fcgid_maxrequests' => - 1, 'guid' => Settings::Get('phpfpm.vhost_httpuser'), 'openbasedir' => 0, 'email' => Settings::Get('panel.adminmail'), @@ -130,28 +142,22 @@ class lighttpd extends HttpConfigBase { $php = new phpinterface($domain); - $this->lighttpd_data[$vhost_filename].= ' fastcgi.server = ( '."\n"; - $this->lighttpd_data[$vhost_filename].= "\t".'".php" => ('."\n"; - $this->lighttpd_data[$vhost_filename].= "\t\t".'"localhost" => ('."\n"; - $this->lighttpd_data[$vhost_filename].= "\t\t".'"socket" => "'.$php->getInterface()->getSocketFile().'",'."\n"; - $this->lighttpd_data[$vhost_filename].= "\t\t".'"check-local" => "enable",'."\n"; - $this->lighttpd_data[$vhost_filename].= "\t\t".'"disable-time" => 1'."\n"; - $this->lighttpd_data[$vhost_filename].= "\t".')'."\n"; - $this->lighttpd_data[$vhost_filename].= "\t".')'."\n"; - $this->lighttpd_data[$vhost_filename].= ' )'."\n"; + $this->lighttpd_data[$vhost_filename] .= ' fastcgi.server = ( ' . "\n"; + $this->lighttpd_data[$vhost_filename] .= "\t" . '".php" => (' . "\n"; + $this->lighttpd_data[$vhost_filename] .= "\t\t" . '"localhost" => (' . "\n"; + $this->lighttpd_data[$vhost_filename] .= "\t\t" . '"socket" => "' . $php->getInterface()->getSocketFile() . '",' . "\n"; + $this->lighttpd_data[$vhost_filename] .= "\t\t" . '"check-local" => "enable",' . "\n"; + $this->lighttpd_data[$vhost_filename] .= "\t\t" . '"disable-time" => 1' . "\n"; + $this->lighttpd_data[$vhost_filename] .= "\t" . ')' . "\n"; + $this->lighttpd_data[$vhost_filename] .= "\t" . ')' . "\n"; + $this->lighttpd_data[$vhost_filename] .= ' )' . "\n"; } if ($row_ipsandports['specialsettings'] != '') { - $this->lighttpd_data[$vhost_filename].= $this->processSpecialConfigTemplate( - $row_ipsandports['specialsettings'], - $domain, - $row_ipsandports['ip'], - $row_ipsandports['port'], - $row_ipsandports['ssl'] == '1' - ). "\n"; + $this->lighttpd_data[$vhost_filename] .= $this->processSpecialConfigTemplate($row_ipsandports['specialsettings'], $domain, $row_ipsandports['ip'], $row_ipsandports['port'], $row_ipsandports['ssl'] == '1') . "\n"; } - $this->lighttpd_data[$vhost_filename].= '}' . "\n"; + $this->lighttpd_data[$vhost_filename] .= '}' . "\n"; } if ($row_ipsandports['ssl'] == '1') { @@ -163,29 +169,49 @@ class lighttpd extends HttpConfigBase { $row_ipsandports['ssl_ca_file'] = Settings::Get('system.ssl_ca_file'); } - if ($row_ipsandports['ssl_cert_file'] != '') { - - // check for existence, #1485 - if (!file_exists($row_ipsandports['ssl_cert_file'])) { - $this->logger->logAction(CRON_ACTION, LOG_ERR, $ip.':'.$port . ' :: certificate file "'.$row_ipsandports['ssl_cert_file'].'" does not exist! Cannot create ssl-directives'); - echo $ip.':'.$port . ' :: certificate file "'.$row_ipsandports['ssl_cert_file'].'" does not exist! Cannot create SSL-directives'."\n"; - } else { - $this->lighttpd_data[$vhost_filename].= 'ssl.engine = "enable"' . "\n"; - $this->lighttpd_data[$vhost_filename].= 'ssl.use-sslv2 = "disable"' . "\n"; - $this->lighttpd_data[$vhost_filename].= 'ssl.cipher-list = "' . Settings::Get('system.ssl_cipher_list') . '"' . "\n"; - $this->lighttpd_data[$vhost_filename].= 'ssl.honor-cipher-order = "enable"' . "\n"; - $this->lighttpd_data[$vhost_filename].= 'ssl.pemfile = "' . makeCorrectFile($row_ipsandports['ssl_cert_file']) . '"' . "\n"; - - if ($row_ipsandports['ssl_ca_file'] != '') { - // check for existence, #1485 - if (!file_exists($row_ipsandports['ssl_ca_file'])) { - $this->logger->logAction(CRON_ACTION, LOG_ERR, $ip.':'.$port . ' :: certificate CA file "'.$row_ipsandports['ssl_ca_file'].'" does not exist! Cannot create ssl-directives'); - echo $ip.':'.port . ' :: certificate CA file "'.$row_ipsandports['ssl_ca_file'].'" does not exist! SSL-directives might not be working'."\n"; - } else { - $this->lighttpd_data[$vhost_filename].= 'ssl.ca-file = "' . makeCorrectFile($row_ipsandports['ssl_ca_file']) . '"' . "\n"; - } - } - } + $domain = array( + 'id' => 0, + 'domain' => Settings::Get('system.hostname'), + 'adminid' => 1, /* first admin-user (superadmin) */ + 'loginname' => 'froxlor.panel', + 'documentroot' => $mypath + ); + + // override corresponding array values + $domain['ssl_cert_file'] = $row_ipsandports['ssl_cert_file']; + $domain['ssl_key_file'] = $row_ipsandports['ssl_key_file']; + $domain['ssl_ca_file'] = $row_ipsandports['ssl_ca_file']; + $domain['ssl_cert_chainfile'] = $row_ipsandports['ssl_cert_chainfile']; + + // SSL STUFF + $dssl = new DomainSSL(); + // this sets the ssl-related array-indices in the $domain array + // if the domain has customer-defined ssl-certificates + $dssl->setDomainSSLFilesArray($domain); + + if ($domain['ssl_cert_file'] != '') { + + // check for existence, #1485 + if (! file_exists($domain['ssl_cert_file'])) { + $this->logger->logAction(CRON_ACTION, LOG_ERR, $ip . ':' . $port . ' :: certificate file "' . $domain['ssl_cert_file'] . '" does not exist! Cannot create ssl-directives'); + echo $ip . ':' . $port . ' :: certificate file "' . $domain['ssl_cert_file'] . '" does not exist! Cannot create SSL-directives' . "\n"; + } else { + $this->lighttpd_data[$vhost_filename] .= 'ssl.engine = "enable"' . "\n"; + $this->lighttpd_data[$vhost_filename] .= 'ssl.use-sslv2 = "disable"' . "\n"; + $this->lighttpd_data[$vhost_filename] .= 'ssl.cipher-list = "' . Settings::Get('system.ssl_cipher_list') . '"' . "\n"; + $this->lighttpd_data[$vhost_filename] .= 'ssl.honor-cipher-order = "enable"' . "\n"; + $this->lighttpd_data[$vhost_filename] .= 'ssl.pemfile = "' . makeCorrectFile($domain['ssl_cert_file']) . '"' . "\n"; + + if ($domain['ssl_ca_file'] != '') { + // check for existence, #1485 + if (! file_exists($domain['ssl_ca_file'])) { + $this->logger->logAction(CRON_ACTION, LOG_ERR, $ip . ':' . $port . ' :: certificate CA file "' . $domain['ssl_ca_file'] . '" does not exist! Cannot create ssl-directives'); + echo $ip . ':' . port . ' :: certificate CA file "' . $domain['ssl_ca_file'] . '" does not exist! SSL-directives might not be working' . "\n"; + } else { + $this->lighttpd_data[$vhost_filename] .= 'ssl.ca-file = "' . makeCorrectFile($domain['ssl_ca_file']) . '"' . "\n"; + } + } + } } } @@ -200,11 +226,11 @@ class lighttpd extends HttpConfigBase { sort($vhosts); foreach ($vhosts as $vhost) { - $this->lighttpd_data[$vhost_filename].= ' include "'.$vhost.'"'."\n"; + $this->lighttpd_data[$vhost_filename] .= ' include "' . $vhost . '"' . "\n"; } } - $this->lighttpd_data[$vhost_filename].= '}' . "\n"; + $this->lighttpd_data[$vhost_filename] .= '}' . "\n"; } /** @@ -213,36 +239,36 @@ class lighttpd extends HttpConfigBase { $this->_createStandardErrorHandler(); } - /** * define a default server.error-handler-404-statement, bug #unknown-yet */ - private function _createStandardErrorHandler() { - if (Settings::Get('defaultwebsrverrhandler.enabled') == '1' - && Settings::Get('defaultwebsrverrhandler.err404') != '' - ) { + private function _createStandardErrorHandler() + { + if (Settings::Get('defaultwebsrverrhandler.enabled') == '1' && Settings::Get('defaultwebsrverrhandler.err404') != '') { $vhost_filename = makeCorrectFile(Settings::Get('system.apacheconf_vhost') . '/05_froxlor_default_errorhandler.conf'); - if (!isset($this->lighttpd_data[$vhost_filename])) { + if (! isset($this->lighttpd_data[$vhost_filename])) { $this->lighttpd_data[$vhost_filename] = ''; } $defhandler = Settings::Get('defaultwebsrverrhandler.err404'); - if (!validateUrl($defhandler)) { + if (! validateUrl($defhandler)) { $defhandler = makeCorrectFile($defhandler); } - $this->lighttpd_data[$vhost_filename] = 'server.error-handler-404 = "'.$defhandler.'"'; + $this->lighttpd_data[$vhost_filename] = 'server.error-handler-404 = "' . $defhandler . '"'; } } - - protected function create_htaccess($domain) { + protected function create_htaccess($domain) + { $needed_htpasswds = array(); $result_htpasswds_stmt = Database::prepare(" SELECT * FROM " . TABLE_PANEL_HTPASSWDS . " WHERE `path` LIKE :docroot "); - Database::pexecute($result_htpasswds_stmt, array('docroot' => $domain['documentroot'] . '%')); + Database::pexecute($result_htpasswds_stmt, array( + 'docroot' => $domain['documentroot'] . '%' + )); $htaccess_text = ''; while ($row_htpasswds = $result_htpasswds_stmt->fetch(PDO::FETCH_ASSOC)) { @@ -251,30 +277,30 @@ class lighttpd extends HttpConfigBase { $filename = $row_htpasswds['customerid'] . '-' . md5($row_htpasswds['path']) . '.htpasswd'; - if (!in_array($row_htpasswds['path'], $needed_htpasswds)) { - if (!isset($this->needed_htpasswds[$filename])) { + if (! in_array($row_htpasswds['path'], $needed_htpasswds)) { + if (! isset($this->needed_htpasswds[$filename])) { $this->needed_htpasswds[$filename] = ''; } - if (!strstr($this->needed_htpasswds[$filename], $row_htpasswds['username'] . ':' . $row_htpasswds['password'])) { - $this->needed_htpasswds[$filename].= $row_htpasswds['username'] . ':' . $row_htpasswds['password'] . "\n"; + if (! strstr($this->needed_htpasswds[$filename], $row_htpasswds['username'] . ':' . $row_htpasswds['password'])) { + $this->needed_htpasswds[$filename] .= $row_htpasswds['username'] . ':' . $row_htpasswds['password'] . "\n"; } $htaccess_path = substr($row_htpasswds['path'], strlen($domain['documentroot']) - 1); $htaccess_path = makeCorrectDir($htaccess_path); - $htaccess_text.= ' $HTTP["url"] =~ "^'.$htaccess_path.'" {' . "\n"; - $htaccess_text.= ' auth.backend = "htpasswd"' . "\n"; - $htaccess_text.= ' auth.backend.htpasswd.userfile = "' . makeCorrectFile(Settings::Get('system.apacheconf_htpasswddir') . '/' . $filename) . '"' . "\n"; - $htaccess_text.= ' auth.require = ( ' . "\n"; - $htaccess_text.= ' "' . $htaccess_path . '" =>' . "\n"; - $htaccess_text.= ' (' . "\n"; - $htaccess_text.= ' "method" => "basic",' . "\n"; - $htaccess_text.= ' "realm" => "'.$row_htpasswds['authname'].'",' . "\n"; - $htaccess_text.= ' "require" => "valid-user"' . "\n"; - $htaccess_text.= ' )' . "\n"; - $htaccess_text.= ' )' . "\n"; - $htaccess_text.= ' }' . "\n"; + $htaccess_text .= ' $HTTP["url"] =~ "^' . $htaccess_path . '" {' . "\n"; + $htaccess_text .= ' auth.backend = "htpasswd"' . "\n"; + $htaccess_text .= ' auth.backend.htpasswd.userfile = "' . makeCorrectFile(Settings::Get('system.apacheconf_htpasswddir') . '/' . $filename) . '"' . "\n"; + $htaccess_text .= ' auth.require = ( ' . "\n"; + $htaccess_text .= ' "' . $htaccess_path . '" =>' . "\n"; + $htaccess_text .= ' (' . "\n"; + $htaccess_text .= ' "method" => "basic",' . "\n"; + $htaccess_text .= ' "realm" => "' . $row_htpasswds['authname'] . '",' . "\n"; + $htaccess_text .= ' "require" => "valid-user"' . "\n"; + $htaccess_text .= ' )' . "\n"; + $htaccess_text .= ' )' . "\n"; + $htaccess_text .= ' }' . "\n"; $needed_htpasswds[] = $row_htpasswds['path']; } @@ -283,75 +309,58 @@ class lighttpd extends HttpConfigBase { return $htaccess_text; } + public function createVirtualHosts() + {} - public function createVirtualHosts() { - } + public function createFileDirOptions() + {} + protected function composePhpOptions($domain) + {} - public function createFileDirOptions() { - } - - - protected function composePhpOptions($domain) { - } - - - public function createOwnVhostStarter() { - } - - - protected function createLighttpdHosts($ipid, $ssl, $vhost_filename) { + public function createOwnVhostStarter() + {} + protected function createLighttpdHosts($ipid, $ssl, $vhost_filename) + { $domains = WebserverBase::getVhostsToCreate(); foreach ($domains as $domain) { if (is_dir(Settings::Get('system.apacheconf_vhost'))) { - safe_exec('mkdir -p '.escapeshellarg(makeCorrectDir(Settings::Get('system.apacheconf_vhost').'/vhosts/'))); + safe_exec('mkdir -p ' . escapeshellarg(makeCorrectDir(Settings::Get('system.apacheconf_vhost') . '/vhosts/'))); // determine correct include-path: // e.g. '/etc/lighttpd/conf-enabled/vhosts/ has to become' // 'conf-enabled/vhosts/' (damn debian, but luckily works too on other distros) - $_tmp_path = substr(makeCorrectDir(Settings::Get('system.apacheconf_vhost')), 0, -1); + $_tmp_path = substr(makeCorrectDir(Settings::Get('system.apacheconf_vhost')), 0, - 1); $_pos = strrpos($_tmp_path, '/'); - $_inc_path = substr($_tmp_path, $_pos+1); + $_inc_path = substr($_tmp_path, $_pos + 1); // maindomain - if ((int)$domain['parentdomainid'] == 0 - && isCustomerStdSubdomain((int)$domain['id']) == false - && ((int)$domain['ismainbutsubto'] == 0 - || domainMainToSubExists($domain['ismainbutsubto']) == false) - ) { + if ((int) $domain['parentdomainid'] == 0 && isCustomerStdSubdomain((int) $domain['id']) == false && ((int) $domain['ismainbutsubto'] == 0 || domainMainToSubExists($domain['ismainbutsubto']) == false)) { $vhost_no = '50'; - } - // sub-but-main-domain - elseif ((int)$domain['parentdomainid'] == 0 - && isCustomerStdSubdomain((int)$domain['id']) == false - && (int)$domain['ismainbutsubto'] > 0 - ) { + } // sub-but-main-domain + elseif ((int) $domain['parentdomainid'] == 0 && isCustomerStdSubdomain((int) $domain['id']) == false && (int) $domain['ismainbutsubto'] > 0) { $vhost_no = '51'; - } - // subdomains + } // subdomains else { // number of dots in a domain specifies it's position (and depth of subdomain) starting at 89 going downwards on higher depth - $vhost_no = (string)(90 - substr_count($domain['domain'], ".") + 1); + $vhost_no = (string) (90 - substr_count($domain['domain'], ".") + 1); } if ($ssl == '1') { - $vhost_no = (int)$vhost_no += 10; + $vhost_no = (int) $vhost_no += 10; } - $vhost_filename = makeCorrectFile(Settings::Get('system.apacheconf_vhost').'/vhosts/'.$vhost_no.'_'.$domain['domain'].'.conf'); - $included_vhosts[] = $_inc_path.'/vhosts/'.$vhost_no.'_'.$domain['domain'].'.conf'; + $vhost_filename = makeCorrectFile(Settings::Get('system.apacheconf_vhost') . '/vhosts/' . $vhost_no . '_' . $domain['domain'] . '.conf'); + $included_vhosts[] = $_inc_path . '/vhosts/' . $vhost_no . '_' . $domain['domain'] . '.conf'; } - if (!isset($this->lighttpd_data[$vhost_filename])) { + if (! isset($this->lighttpd_data[$vhost_filename])) { $this->lighttpd_data[$vhost_filename] = ''; } - if ((!empty($this->lighttpd_data[$vhost_filename]) - && !is_dir(Settings::Get('system.apacheconf_vhost'))) - || is_dir(Settings::Get('system.apacheconf_vhost')) - ) { + if ((! empty($this->lighttpd_data[$vhost_filename]) && ! is_dir(Settings::Get('system.apacheconf_vhost'))) || is_dir(Settings::Get('system.apacheconf_vhost'))) { if ($ssl == '1') { $ssl_vhost = true; $ips_and_ports_index = 'ssl_ipandport'; @@ -370,74 +379,70 @@ class lighttpd extends HttpConfigBase { return $included_vhosts; } - - protected function getVhostContent($domain, $ssl_vhost = false, $ipid) { - if ($ssl_vhost === true - && $domain['ssl'] != '1' - && $domain['ssl_redirect'] != '1' - ) { + protected function getVhostContent($domain, $ssl_vhost = false, $ipid) + { + if ($ssl_vhost === true && $domain['ssl'] != '1' && $domain['ssl_redirect'] != '1') { return ''; } $vhost_content = ''; - $vhost_content.= $this->getServerNames($domain) . " {\n"; + $vhost_content .= $this->getServerNames($domain) . " {\n"; // respect ssl_redirect settings, #542 - if ($ssl_vhost == false - && $domain['ssl'] == '1' - && $domain['ssl_redirect'] == '1' - ) { + if ($ssl_vhost == false && $domain['ssl'] == '1' && $domain['ssl_redirect'] == '1') { // We must not check if our port differs from port 443, // but if there is a destination-port != 443 $_sslport = ''; // This returns the first port that is != 443 with ssl enabled, if any // ordered by ssl-certificate (if any) so that the ip/port combo // with certificate is used - $ssldestport_stmt = Database::prepare( - "SELECT `ip`.`port` FROM ".TABLE_PANEL_IPSANDPORTS." `ip` - LEFT JOIN `".TABLE_DOMAINTOIP."` `dip` ON (`ip`.`id` = `dip`.`id_ipandports`) + $ssldestport_stmt = Database::prepare("SELECT `ip`.`port` FROM " . TABLE_PANEL_IPSANDPORTS . " `ip` + LEFT JOIN `" . TABLE_DOMAINTOIP . "` `dip` ON (`ip`.`id` = `dip`.`id_ipandports`) WHERE `dip`.`id_domain` = :domainid AND `ip`.`ssl` = '1' AND `ip`.`port` != 443 - ORDER BY `ip`.`ssl_cert_file` DESC, `ip`.`port` LIMIT 1;" - ); - $ssldestport = Database::pexecute_first($ssldestport_stmt, array('domainid' => $domain['id'])); + ORDER BY `ip`.`ssl_cert_file` DESC, `ip`.`port` LIMIT 1;"); + $ssldestport = Database::pexecute_first($ssldestport_stmt, array( + 'domainid' => $domain['id'] + )); if ($ssldestport['port'] != '') { - $_sslport = ":".$ssldestport['port']; + $_sslport = ":" . $ssldestport['port']; } $domain['documentroot'] = 'https://' . $domain['domain'] . $_sslport . '/'; } - if (preg_match('/^https?\:\/\//', $domain['documentroot'])) { - $vhost_content.= ' url.redirect = (' . "\n"; - $vhost_content.= ' "^/(.*)$" => "'. $this->idnaConvert->encode($domain['documentroot']) . '$1"'. "\n"; - $vhost_content.= ' )' . "\n"; + // avoid using any whitespaces + $domain['documentroot'] = trim($domain['documentroot']); + if (preg_match('/^https?\:\/\//', $domain['documentroot'])) { + $vhost_content .= ' url.redirect = (' . "\n"; + $vhost_content .= ' "^/(.*)$" => "' . $this->idnaConvert->encode($domain['documentroot']) . '$1"' . "\n"; + $vhost_content .= ' )' . "\n"; } else { mkDirWithCorrectOwnership($domain['customerroot'], $domain['documentroot'], $domain['guid'], $domain['guid'], true, true); $only_webroot = false; - if ($ssl_vhost === false - && $domain['ssl_redirect'] == '1' - ) { + if ($ssl_vhost === false && $domain['ssl_redirect'] == '1') { $only_webroot = true; } - $vhost_content.= $this->getWebroot($domain, $ssl_vhost); - if (!$only_webroot) { + $vhost_content .= $this->getWebroot($domain, $ssl_vhost); + if (! $only_webroot) { if ($this->_deactivated == false) { - $vhost_content.= $this->create_htaccess($domain); - $vhost_content.= $this->create_pathOptions($domain); - $vhost_content.= $this->composePhpOptions($domain); - $vhost_content.= $this->getStats($domain); + $vhost_content .= $this->create_htaccess($domain); + $vhost_content .= $this->create_pathOptions($domain); + $vhost_content .= $this->composePhpOptions($domain); + $vhost_content .= $this->getStats($domain); $ipandport_stmt = Database::prepare(" - SELECT * FROM `".TABLE_PANEL_IPSANDPORTS."` + SELECT * FROM `" . TABLE_PANEL_IPSANDPORTS . "` WHERE `id` = :id "); - $ipandport = Database::pexecute_first($ipandport_stmt, array('id' => $ipid)); + $ipandport = Database::pexecute_first($ipandport_stmt, array( + 'id' => $ipid + )); $domain['ip'] = $ipandport['ip']; $domain['port'] = $ipandport['port']; @@ -453,52 +458,34 @@ class lighttpd extends HttpConfigBase { // if the domain has customer-defined ssl-certificates $dssl->setDomainSSLFilesArray($domain); - $vhost_content.= $this->getSslSettings($domain, $ssl_vhost); + $vhost_content .= $this->getSslSettings($domain, $ssl_vhost); if ($domain['specialsettings'] != "") { - $vhost_content.= $this->processSpecialConfigTemplate( - $domain['specialsettings'], - $domain, - $domain['ip'], - $domain['port'], - $ssl_vhost). "\n"; + $vhost_content .= $this->processSpecialConfigTemplate($domain['specialsettings'], $domain, $domain['ip'], $domain['port'], $ssl_vhost) . "\n"; } if ($ipandport['default_vhostconf_domain'] != '') { - $vhost_content.= $this->processSpecialConfigTemplate( - $ipandport['default_vhostconf_domain'], - $domain, - $domain['ip'], - $domain['port'], - $ssl_vhost) . "\n"; + $vhost_content .= $this->processSpecialConfigTemplate($ipandport['default_vhostconf_domain'], $domain, $domain['ip'], $domain['port'], $ssl_vhost) . "\n"; } if (Settings::Get('system.default_vhostconf') != '') { - $vhost_content.= $this->processSpecialConfigTemplate( - Settings::Get('system.default_vhostconf'), - $domain, - $domain['ip'], - $domain['port'], - $ssl_vhost). "\n"; + $vhost_content .= $this->processSpecialConfigTemplate(Settings::Get('system.default_vhostconf'), $domain, $domain['ip'], $domain['port'], $ssl_vhost) . "\n"; } } - $vhost_content.= $this->getLogFiles($domain); + $vhost_content .= $this->getLogFiles($domain); } } - $vhost_content.= '}' . "\n"; + $vhost_content .= '}' . "\n"; return $vhost_content; } - - protected function getSslSettings($domain, $ssl_vhost) { + protected function getSslSettings($domain, $ssl_vhost) + { $ssl_settings = ''; - if ($ssl_vhost === true - && $domain['ssl'] == '1' - && (int)Settings::Get('system.use_ssl') == 1 - ) { + if ($ssl_vhost === true && $domain['ssl'] == '1' && (int) Settings::Get('system.use_ssl') == 1) { if ($domain['ssl_cert_file'] == '') { $domain['ssl_cert_file'] = Settings::Get('system.ssl_cert_file'); } @@ -508,17 +495,17 @@ class lighttpd extends HttpConfigBase { } if ($domain['ssl_cert_file'] != '') { - - $ssl_settings.= 'ssl.engine = "enable"' . "\n"; - $ssl_settings.= 'ssl.use-sslv2 = "disable"' . "\n"; - $ssl_settings.= 'ssl.cipher-list = "' . Settings::Get('system.ssl_cipher_list') . '"' . "\n"; - $ssl_settings.= 'ssl.honor-cipher-order = "enable"' . "\n"; - $ssl_settings.= 'ssl.pemfile = "' . makeCorrectFile($domain['ssl_cert_file']) . '"' . "\n"; + + $ssl_settings .= 'ssl.engine = "enable"' . "\n"; + $ssl_settings .= 'ssl.use-sslv2 = "disable"' . "\n"; + $ssl_settings .= 'ssl.cipher-list = "' . Settings::Get('system.ssl_cipher_list') . '"' . "\n"; + $ssl_settings .= 'ssl.honor-cipher-order = "enable"' . "\n"; + $ssl_settings .= 'ssl.pemfile = "' . makeCorrectFile($domain['ssl_cert_file']) . '"' . "\n"; if ($domain['ssl_ca_file'] != '') { - $ssl_settings.= 'ssl.ca-file = "' . makeCorrectFile($domain['ssl_ca_file']) . '"' . "\n"; + $ssl_settings .= 'ssl.ca-file = "' . makeCorrectFile($domain['ssl_ca_file']) . '"' . "\n"; } - + if ($domain['hsts'] > 0) { $vhost_content .= '$HTTP["scheme"] == "https" { setenv.add-response-header = ( "Strict-Transport-Security" => "max-age=' . $domain['hsts']; @@ -535,9 +522,8 @@ class lighttpd extends HttpConfigBase { return $ssl_settings; } - - protected function getLogFiles($domain) { - + protected function getLogFiles($domain) + { $logfiles_text = ''; $speciallogfile = ''; @@ -558,11 +544,11 @@ class lighttpd extends HttpConfigBase { chown($access_log, Settings::Get('system.httpuser')); chgrp($access_log, Settings::Get('system.httpgroup')); - $logfiles_text.= ' accesslog.filename = "' . $access_log . '"' . "\n"; + $logfiles_text .= ' accesslog.filename = "' . $access_log . '"' . "\n"; if (Settings::Get('system.awstats_enabled') == '1') { - if ((int)$domain['parentdomainid'] == 0) { + if ((int) $domain['parentdomainid'] == 0) { // prepare the aliases and subdomains for stats config files $server_alias = ''; $alias_domains_stmt = Database::prepare(" @@ -570,19 +556,21 @@ class lighttpd extends HttpConfigBase { FROM `" . TABLE_PANEL_DOMAINS . "` WHERE `aliasdomain` = :domainid OR `parentdomainid` = :domainid "); - Database::pexecute($alias_domains_stmt, array('domainid' => $domain['id'])); + Database::pexecute($alias_domains_stmt, array( + 'domainid' => $domain['id'] + )); while (($alias_domain = $alias_domains_stmt->fetch(PDO::FETCH_ASSOC)) !== false) { - $server_alias.= ' ' . $alias_domain['domain'] . ' '; + $server_alias .= ' ' . $alias_domain['domain'] . ' '; if ($alias_domain['iswildcarddomain'] == '1') { - $server_alias.= '*.' . $domain['domain']; + $server_alias .= '*.' . $domain['domain']; } else { if ($alias_domain['wwwserveralias'] == '1') { - $server_alias.= 'www.' . $alias_domain['domain']; + $server_alias .= 'www.' . $alias_domain['domain']; } else { - $server_alias.= ''; + $server_alias .= ''; } } } @@ -608,30 +596,32 @@ class lighttpd extends HttpConfigBase { return $logfiles_text; } - - protected function create_pathOptions($domain) { + protected function create_pathOptions($domain) + { $result_stmt = Database::prepare(" SELECT * FROM " . TABLE_PANEL_HTACCESS . " WHERE `path` LIKE :docroot "); - Database::pexecute($result_stmt, array('docroot' => $domain['documentroot'] . '%')); + Database::pexecute($result_stmt, array( + 'docroot' => $domain['documentroot'] . '%' + )); $path_options = ''; $error_string = ''; while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) { - if (!empty($row['error404path'])) { + if (! empty($row['error404path'])) { $defhandler = $row['error404path']; - if (!validateUrl($defhandler)) { + if (! validateUrl($defhandler)) { $defhandler = makeCorrectFile($domain['documentroot'] . '/' . $defhandler); } - $error_string.= ' server.error-handler-404 = "' . $defhandler . '"' . "\n\n"; + $error_string .= ' server.error-handler-404 = "' . $defhandler . '"' . "\n\n"; } if ($row['options_indexes'] != '0') { - if (!empty($error_string)) { - $path_options.= $error_string; + if (! empty($error_string)) { + $path_options .= $error_string; // reset $error_string here to prevent duplicate entries $error_string = ''; } @@ -641,89 +631,87 @@ class lighttpd extends HttpConfigBase { // We need to remove the last slash, otherwise the regex wouldn't work if ($row['path'] != $domain['documentroot']) { - $path = substr($path, 0, -1); + $path = substr($path, 0, - 1); } - $path_options.= ' $HTTP["url"] =~ "^' . $path . '($|/)" {' . "\n"; - $path_options.= "\t" . 'dir-listing.activate = "enable"' . "\n"; - $path_options.= ' }' . "\n\n"; + $path_options .= ' $HTTP["url"] =~ "^' . $path . '($|/)" {' . "\n"; + $path_options .= "\t" . 'dir-listing.activate = "enable"' . "\n"; + $path_options .= ' }' . "\n\n"; } else { $path_options = $error_string; } - if (customerHasPerlEnabled($domain['customerid']) - && $row['options_cgi'] != '0' - ) { + if (customerHasPerlEnabled($domain['customerid']) && $row['options_cgi'] != '0') { $path = makeCorrectDir(substr($row['path'], strlen($domain['documentroot']) - 1)); mkDirWithCorrectOwnership($domain['documentroot'], $row['path'], $domain['guid'], $domain['guid']); // We need to remove the last slash, otherwise the regex wouldn't work if ($row['path'] != $domain['documentroot']) { - $path = substr($path, 0, -1); + $path = substr($path, 0, - 1); } - $path_options.= ' $HTTP["url"] =~ "^' . $path . '($|/)" {' . "\n"; - $path_options.= "\t" . 'cgi.assign = (' . "\n"; - $path_options.= "\t\t" . '".pl" => "'.makeCorrectFile(Settings::Get('system.perl_path')).'",' . "\n"; - $path_options.= "\t\t" . '".cgi" => "'.makeCorrectFile(Settings::Get('system.perl_path')).'"' . "\n"; - $path_options.= "\t" . ')' . "\n"; - $path_options.= ' }' . "\n\n"; + $path_options .= ' $HTTP["url"] =~ "^' . $path . '($|/)" {' . "\n"; + $path_options .= "\t" . 'cgi.assign = (' . "\n"; + $path_options .= "\t\t" . '".pl" => "' . makeCorrectFile(Settings::Get('system.perl_path')) . '",' . "\n"; + $path_options .= "\t\t" . '".cgi" => "' . makeCorrectFile(Settings::Get('system.perl_path')) . '"' . "\n"; + $path_options .= "\t" . ')' . "\n"; + $path_options .= ' }' . "\n\n"; } } return $path_options; } - - protected function getDirOptions($domain) { + protected function getDirOptions($domain) + { $result_stmt = Database::prepare(" SELECT * FROM " . TABLE_PANEL_HTPASSWDS . " WHERE `customerid` = :customerid "); - Database::pexecute($result_stmt, array('customerid' => $domain['customerid'])); + Database::pexecute($result_stmt, array( + 'customerid' => $domain['customerid'] + )); while ($row_htpasswds = $result_stmt->fetch(PDO::FETCH_ASSOC)) { - if ($auth_backend_loaded[$domain['ipandport']] != 'yes' - && $auth_backend_loaded[$domain['ssl_ipandport']] != 'yes' - ) { + if ($auth_backend_loaded[$domain['ipandport']] != 'yes' && $auth_backend_loaded[$domain['ssl_ipandport']] != 'yes') { $filename = $domain['customerid'] . '.htpasswd'; if ($this->auth_backend_loaded[$domain['ipandport']] != 'yes') { $auth_backend_loaded[$domain['ipandport']] = 'yes'; - $diroption_text.= 'auth.backend = "htpasswd"' . "\n"; - $diroption_text.= 'auth.backend.htpasswd.userfile = "' . makeCorrectFile(Settings::Get('system.apacheconf_htpasswddir') . '/' . $filename) . '"' . "\n"; + $diroption_text .= 'auth.backend = "htpasswd"' . "\n"; + $diroption_text .= 'auth.backend.htpasswd.userfile = "' . makeCorrectFile(Settings::Get('system.apacheconf_htpasswddir') . '/' . $filename) . '"' . "\n"; $this->needed_htpasswds[$filename] = $row_htpasswds['username'] . ':' . $row_htpasswds['password'] . "\n"; - $diroption_text.= 'auth.require = ( ' . "\n"; + $diroption_text .= 'auth.require = ( ' . "\n"; $previous_domain_id = '1'; } elseif ($this->auth_backend_loaded[$domain['ssl_ipandport']] != 'yes') { $auth_backend_loaded[$domain['ssl_ipandport']] = 'yes'; - $diroption_text.= 'auth.backend= "htpasswd"' . "\n"; - $diroption_text.= 'auth.backend.htpasswd.userfile = "' . makeCorrectFile(Settings::Get('system.apacheconf_htpasswddir') . '/' . $filename) . '"' . "\n"; + $diroption_text .= 'auth.backend= "htpasswd"' . "\n"; + $diroption_text .= 'auth.backend.htpasswd.userfile = "' . makeCorrectFile(Settings::Get('system.apacheconf_htpasswddir') . '/' . $filename) . '"' . "\n"; $this->needed_htpasswds[$filename] = $row_htpasswds['username'] . ':' . $row_htpasswds['password'] . "\n"; - $diroption_text.= 'auth.require = ( ' . "\n"; + $diroption_text .= 'auth.require = ( ' . "\n"; $previous_domain_id = '1'; } } - $diroption_text.= '"' . makeCorrectDir($row_htpasswds['path']) . '" =>' . "\n"; - $diroption_text.= '(' . "\n"; - $diroption_text.= ' "method" => "basic",' . "\n"; - $diroption_text.= ' "realm" => "'.$row_htpasswds['authname'].'",' . "\n"; - $diroption_text.= ' "require" => "valid-user"' . "\n"; - $diroption_text.= ')' . "\n"; + $diroption_text .= '"' . makeCorrectDir($row_htpasswds['path']) . '" =>' . "\n"; + $diroption_text .= '(' . "\n"; + $diroption_text .= ' "method" => "basic",' . "\n"; + $diroption_text .= ' "realm" => "' . $row_htpasswds['authname'] . '",' . "\n"; + $diroption_text .= ' "require" => "valid-user"' . "\n"; + $diroption_text .= ')' . "\n"; if ($this->auth_backend_loaded[$domain['ssl_ipandport']] == 'yes') { - $this->needed_htpasswds[$domain['ssl_ipandport']].= $diroption_text; + $this->needed_htpasswds[$domain['ssl_ipandport']] .= $diroption_text; } if ($this->auth_backend_loaded[$domain['ipandport']] != 'yes') { - $this->needed_htpasswds[$domain['ipandport']].= $diroption_text; + $this->needed_htpasswds[$domain['ipandport']] .= $diroption_text; } } return ' auth.backend.htpasswd.userfile = "' . makeCorrectFile(Settings::Get('system.apacheconf_htpasswddir') . '/' . $filename) . '"' . "\n"; } - - protected function getServerNames($domain) { + protected function getServerNames($domain) + { $server_string = array(); $domain_name = str_replace('.', '\.', $domain['domain']); @@ -733,7 +721,7 @@ class lighttpd extends HttpConfigBase { if ($domain['wwwserveralias'] == '1') { $server_string[] = '^(?:www\.|)' . $domain_name . '$'; } else { - $server_string[] = '^'.$domain_name.'$'; + $server_string[] = '^' . $domain_name . '$'; } } @@ -742,7 +730,9 @@ class lighttpd extends HttpConfigBase { FROM `" . TABLE_PANEL_DOMAINS . "` WHERE `aliasdomain` = :domainid "); - Database::pexecute($alias_domains_stmt, array('domainid' => $domain['id'])); + Database::pexecute($alias_domains_stmt, array( + 'domainid' => $domain['id'] + )); while (($alias_domain = $alias_domains_stmt->fetch(PDO::FETCH_ASSOC)) !== false) { $alias_domain_name = str_replace('.', '\.', $alias_domain['domain']); @@ -753,12 +743,12 @@ class lighttpd extends HttpConfigBase { if ($alias_domain['wwwserveralias'] == '1') { $server_string[] = '^(?:www\.|)' . $alias_domain_name . '$'; } else { - $server_string[] = '^'.$alias_domain_name . '$'; + $server_string[] = '^' . $alias_domain_name . '$'; } } } - for ($i = 0;$i < sizeof($server_string); $i++) { + for ($i = 0; $i < sizeof($server_string); $i ++) { $data = $server_string[$i]; if (sizeof($server_string) > 1) { @@ -785,35 +775,31 @@ class lighttpd extends HttpConfigBase { return $servernames_text; } - - protected function getWebroot($domain, $ssl) { + protected function getWebroot($domain, $ssl) + { $webroot_text = ''; - if ($domain['deactivated'] == '1' - && Settings::Get('system.deactivateddocroot') != '' - ) { - $webroot_text.= ' # Using docroot for deactivated users...' . "\n"; - $webroot_text.= ' server.document-root = "' . makeCorrectDir(Settings::Get('system.deactivateddocroot')) . "\"\n"; + if ($domain['deactivated'] == '1' && Settings::Get('system.deactivateddocroot') != '') { + $webroot_text .= ' # Using docroot for deactivated users...' . "\n"; + $webroot_text .= ' server.document-root = "' . makeCorrectDir(Settings::Get('system.deactivateddocroot')) . "\"\n"; $this->_deactivated = true; } else { - if ($ssl === false - && $domain['ssl_redirect'] == '1' - ) { + if ($ssl === false && $domain['ssl_redirect'] == '1') { $redirect_domain = $this->idnaConvert->encode('https://' . $domain['domain']); - $webroot_text.= ' url.redirect = ('."\n"; - $webroot_text.= "\t" . '"^/(.*)" => "' . $redirect_domain . '/$1",' . "\n"; - $webroot_text.= "\t" . '"" => "' . $redirect_domain . '",' . "\n"; - $webroot_text.= "\t" . '"/" => "' . $redirect_domain . '"' . "\n"; - $webroot_text.= ' )'."\n"; + $webroot_text .= ' url.redirect = (' . "\n"; + $webroot_text .= "\t" . '"^/(.*)" => "' . $redirect_domain . '/$1",' . "\n"; + $webroot_text .= "\t" . '"" => "' . $redirect_domain . '",' . "\n"; + $webroot_text .= "\t" . '"/" => "' . $redirect_domain . '"' . "\n"; + $webroot_text .= ' )' . "\n"; } elseif (preg_match("#^https?://#i", $domain['documentroot'])) { $redirect_domain = $this->idnaConvert->encode($domain['documentroot']); - $webroot_text.= ' url.redirect = ('."\n"; - $webroot_text.= "\t" . '"^/(.*)" => "' . $redirect_domain . '/$1",' . "\n"; - $webroot_text.= "\t" . '"" => "' . $redirect_domain . '",' . "\n"; - $webroot_text.= "\t" . '"/" => "' . $redirect_domain . '"' . "\n"; - $webroot_text.= ' )'."\n"; + $webroot_text .= ' url.redirect = (' . "\n"; + $webroot_text .= "\t" . '"^/(.*)" => "' . $redirect_domain . '/$1",' . "\n"; + $webroot_text .= "\t" . '"" => "' . $redirect_domain . '",' . "\n"; + $webroot_text .= "\t" . '"/" => "' . $redirect_domain . '"' . "\n"; + $webroot_text .= ' )' . "\n"; } else { - $webroot_text.= ' server.document-root = "' . makeCorrectDir($domain['documentroot']) . "\"\n"; + $webroot_text .= ' server.document-root = "' . makeCorrectDir($domain['documentroot']) . "\"\n"; } $this->_deactivated = false; } @@ -821,57 +807,56 @@ class lighttpd extends HttpConfigBase { return $webroot_text; } - /** * Lets set the text part for the stats software */ - protected function getStats($domain) { + protected function getStats($domain) + { $stats_text = ''; if ($domain['speciallogfile'] == '1') { if ($domain['parentdomainid'] == '0') { if (Settings::Get('system.awstats_enabled') == '1') { - $stats_text.= ' alias.url = ( "/awstats/" => "'.makeCorrectFile($domain['customerroot'] . '/awstats/' . $domain['domain']).'" )' . "\n"; - $stats_text.= ' alias.url += ( "/awstats-icon" => "' . makeCorrectDir(Settings::Get('system.awstats_icons')) . '" )' . "\n"; + $stats_text .= ' alias.url = ( "/awstats/" => "' . makeCorrectFile($domain['customerroot'] . '/awstats/' . $domain['domain']) . '" )' . "\n"; + $stats_text .= ' alias.url += ( "/awstats-icon" => "' . makeCorrectDir(Settings::Get('system.awstats_icons')) . '" )' . "\n"; } else { - $stats_text.= ' alias.url = ( "/webalizer/" => "'.makeCorrectFile($domain['customerroot'] . '/webalizer/' . $domain['domain']).'/" )' . "\n"; + $stats_text .= ' alias.url = ( "/webalizer/" => "' . makeCorrectFile($domain['customerroot'] . '/webalizer/' . $domain['domain']) . '/" )' . "\n"; } } else { if (Settings::Get('system.awstats_enabled') == '1') { - $stats_text.= ' alias.url = ( "/awstats/" => "'.makeCorrectFile($domain['customerroot'] . '/awstats/' . $domain['parentdomain']).'" )' . "\n"; - $stats_text.= ' alias.url += ( "/awstats-icon" => "' . makeCorrectDir(Settings::Get('system.awstats_icons')) . '" )' . "\n"; + $stats_text .= ' alias.url = ( "/awstats/" => "' . makeCorrectFile($domain['customerroot'] . '/awstats/' . $domain['parentdomain']) . '" )' . "\n"; + $stats_text .= ' alias.url += ( "/awstats-icon" => "' . makeCorrectDir(Settings::Get('system.awstats_icons')) . '" )' . "\n"; } else { - $stats_text.= ' alias.url = ( "/webalizer/" => "'.makeCorrectFile($domain['customerroot'] . '/webalizer/' . $domain['parentdomain']).'/" )' . "\n"; + $stats_text .= ' alias.url = ( "/webalizer/" => "' . makeCorrectFile($domain['customerroot'] . '/webalizer/' . $domain['parentdomain']) . '/" )' . "\n"; } } } else { if ($domain['customerroot'] != $domain['documentroot']) { if (Settings::Get('system.awstats_enabled') == '1') { - $stats_text.= ' alias.url = ( "/awstats/" => "'.makeCorrectFile($domain['customerroot'] . '/awstats/' . $domain['domain']).'" )' . "\n"; - $stats_text.= ' alias.url += ( "/awstats-icon" => "' . makeCorrectDir(Settings::Get('system.awstats_icons')) . '" )' . "\n"; + $stats_text .= ' alias.url = ( "/awstats/" => "' . makeCorrectFile($domain['customerroot'] . '/awstats/' . $domain['domain']) . '" )' . "\n"; + $stats_text .= ' alias.url += ( "/awstats-icon" => "' . makeCorrectDir(Settings::Get('system.awstats_icons')) . '" )' . "\n"; } else { - $stats_text.= ' alias.url = ( "/webalizer/" => "'.makeCorrectFile($domain['customerroot'] . '/webalizer/').'" )' . "\n"; + $stats_text .= ' alias.url = ( "/webalizer/" => "' . makeCorrectFile($domain['customerroot'] . '/webalizer/') . '" )' . "\n"; } - } - // if the docroots are equal, we still have to set an alias for awstats + } // if the docroots are equal, we still have to set an alias for awstats // because the stats are in /awstats/[domain], not just /awstats/ // also, the awstats-icons are someplace else too! // -> webalizer does not need this! elseif (Settings::Get('system.awstats_enabled') == '1') { - $stats_text.= ' alias.url = ( "/awstats/" => "'.makeCorrectFile($domain['documentroot'] . '/awstats/' . $domain['domain']).'" )' . "\n"; - $stats_text.= ' alias.url += ( "/awstats-icon" => "' . makeCorrectDir(Settings::Get('system.awstats_icons')) . '" )' . "\n"; + $stats_text .= ' alias.url = ( "/awstats/" => "' . makeCorrectFile($domain['documentroot'] . '/awstats/' . $domain['domain']) . '" )' . "\n"; + $stats_text .= ' alias.url += ( "/awstats-icon" => "' . makeCorrectDir(Settings::Get('system.awstats_icons')) . '" )' . "\n"; } } return $stats_text; } - - public function writeConfigs() { + public function writeConfigs() + { $this->logger->logAction(CRON_ACTION, LOG_INFO, "lighttpd::writeConfigs: rebuilding " . Settings::Get('system.apacheconf_vhost')); $vhostDir = new frxDirectory(Settings::Get('system.apacheconf_vhost')); - if (!$vhostDir->isConfigDir()) { + if (! $vhostDir->isConfigDir()) { // Save one big file $vhosts_file = ''; @@ -883,7 +868,7 @@ class lighttpd extends HttpConfigBase { ksort($this->lighttpd_data); foreach ($this->lighttpd_data as $vhosts_filename => $vhost_content) { - $vhosts_file.= $vhost_content . "\n\n"; + $vhosts_file .= $vhost_content . "\n\n"; } $vhosts_filename = Settings::Get('system.apacheconf_vhost'); @@ -894,7 +879,7 @@ class lighttpd extends HttpConfigBase { fwrite($vhosts_file_handler, $vhosts_file); fclose($vhosts_file_handler); } else { - if (!file_exists(Settings::Get('system.apacheconf_vhost'))) { + if (! file_exists(Settings::Get('system.apacheconf_vhost'))) { $this->logger->logAction(CRON_ACTION, LOG_NOTICE, 'lighttpd::writeConfigs: mkdir ' . escapeshellarg(makeCorrectDir(Settings::Get('system.apacheconf_vhost')))); safe_exec('mkdir ' . escapeshellarg(makeCorrectDir(Settings::Get('system.apacheconf_vhost')))); } @@ -906,7 +891,7 @@ class lighttpd extends HttpConfigBase { // Apply header $vhosts_file = '# ' . basename($vhosts_filename) . "\n" . '# Created ' . date('d.m.Y H:i') . "\n" . '# Do NOT manually edit this file, all changes will be deleted after the next domain change at the panel.' . "\n" . "\n" . $vhosts_file; - if (!empty($vhosts_filename)) { + if (! empty($vhosts_filename)) { $vhosts_file_handler = fopen($vhosts_filename, 'w'); fwrite($vhosts_file_handler, $vhosts_file); fclose($vhosts_file_handler); @@ -918,7 +903,7 @@ class lighttpd extends HttpConfigBase { $htpasswdDir = new frxDirectory(Settings::Get('system.apacheconf_htpasswddir')); if ($htpasswdDir->isConfigDir()) { foreach ($this->needed_htpasswds as $key => $data) { - if (!is_dir(Settings::Get('system.apacheconf_htpasswddir'))) { + if (! is_dir(Settings::Get('system.apacheconf_htpasswddir'))) { mkdir(makeCorrectDir(Settings::Get('system.apacheconf_htpasswddir'))); } diff --git a/scripts/jobs/cron_tasks.inc.http.30.nginx.php b/scripts/jobs/cron_tasks.inc.http.30.nginx.php index 92be94d0..10304be5 100644 --- a/scripts/jobs/cron_tasks.inc.http.30.nginx.php +++ b/scripts/jobs/cron_tasks.inc.http.30.nginx.php @@ -150,6 +150,35 @@ class nginx extends HttpConfigBase { if ($row_ipsandports['ssl_cert_file'] != '' && file_exists($row_ipsandports['ssl_cert_file'])) { $ssl_vhost = true; } + + $domain = array( + 'id' => 0, + 'domain' => Settings::Get('system.hostname'), + 'adminid' => 1, /* first admin-user (superadmin) */ + 'loginname' => 'froxlor.panel', + 'documentroot' => $mypath + ); + + // override corresponding array values + $domain['ssl_cert_file'] = $row_ipsandports['ssl_cert_file']; + $domain['ssl_key_file'] = $row_ipsandports['ssl_key_file']; + $domain['ssl_ca_file'] = $row_ipsandports['ssl_ca_file']; + $domain['ssl_cert_chainfile'] = $row_ipsandports['ssl_cert_chainfile']; + + // SSL STUFF + $dssl = new DomainSSL(); + // this sets the ssl-related array-indices in the $domain array + // if the domain has customer-defined ssl-certificates + $dssl->setDomainSSLFilesArray($domain); + + if ($domain['ssl_cert_file'] != '' && file_exists($domain['ssl_cert_file'])) { + // override corresponding array values + $row_ipsandports['ssl_cert_file'] = $domain['ssl_cert_file']; + $row_ipsandports['ssl_key_file'] = $domain['ssl_key_file']; + $row_ipsandports['ssl_ca_file'] = $domain['ssl_ca_file']; + $row_ipsandports['ssl_cert_chainfile'] = $domain['ssl_cert_chainfile']; + $ssl_vhost = true; + } } /** @@ -415,6 +444,9 @@ class nginx extends HttpConfigBase { $domain['documentroot'] = 'https://' . $domain['domain'] . $_sslport . '/'; } + // avoid using any whitespaces + $domain['documentroot'] = trim($domain['documentroot']); + // create ssl settings first since they are required for normal and redirect vhosts if ($ssl_vhost === true && $domain['ssl'] == '1' From 08f36243e9fe37951eda1990c8f78788e9aca4cf Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 8 Sep 2016 12:28:43 +0200 Subject: [PATCH 0169/1335] only renew froxlor.panel LE cert if required Signed-off-by: Michael Kaufmann (d00p) --- scripts/jobs/cron_letsencrypt.php | 124 +++++++++++++++++------------- 1 file changed, 70 insertions(+), 54 deletions(-) diff --git a/scripts/jobs/cron_letsencrypt.php b/scripts/jobs/cron_letsencrypt.php index 9a20a714..cfca074e 100644 --- a/scripts/jobs/cron_letsencrypt.php +++ b/scripts/jobs/cron_letsencrypt.php @@ -111,7 +111,8 @@ if (Settings::Get('system.le_froxlor_enabled') == '1') { 'ssl_cert_file' => null, 'ssl_key_file' => null, 'ssl_ca_file' => null, - 'ssl_csr_file' => null + 'ssl_csr_file' => null, + 'id' => null ); $froxlor_ssl_settings_stmt = Database::prepare(" @@ -121,6 +122,7 @@ if (Settings::Get('system.le_froxlor_enabled') == '1') { "); $froxlor_ssl = Database::pexecute_first($froxlor_ssl_settings_stmt); + $insert_or_update_required = true; if ($froxlor_ssl) { $certrow['id'] = $froxlor_ssl['id']; $certrow['expirationdate'] = $froxlor_ssl['expirationdate']; @@ -128,60 +130,74 @@ if (Settings::Get('system.le_froxlor_enabled') == '1') { $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']; - } - $domains = array( - $certrow['domain'], - 'www.'.$certrow['domain'] - ); - - // Only renew let's encrypt certificate if no broken ssl_redirect is enabled - if ($certrow['ssl_redirect'] != 2) { - $cronlog->logAction(CRON_ACTION, LOG_DEBUG, "Updating " . $certrow['domain']); - - $cronlog = FroxlorLogger::getInstanceOf(array( - 'loginname' => $certrow['loginname'] - )); - - try { - // Initialize Lescript with documentroot - $le = new lescript($cronlog, $version); - - // Initialize Lescript - $le->initAccount($certrow, true); - - // Request the new certificate (old key may be used) - $return = $le->signDomains($domains, $certrow['ssl_key_file'], $certrow['ssl_csr_file']); - - // We are interessted in the expirationdate - $newcert = openssl_x509_parse($return['crt']); - - // Store the new data - Database::pexecute($updcert_stmt, - array( - 'id' => $certrow['id'], - 'domainid' => $certrow['domainid'], - 'crt' => $return['crt'], - 'key' => $return['key'], - 'ca' => $return['chain'], - 'chain' => $return['chain'], - 'csr' => $return['csr'], - 'expirationdate' => date('Y-m-d H:i:s', $newcert['validTo_time_t']) - )); - - if ($certrow['ssl_redirect'] == 3) { - Settings::Set('system.le_froxlor_redirect', '1'); - } - - $cronlog->logAction(CRON_ACTION, LOG_INFO, "Updated Let's Encrypt certificate for " . $certrow['domain']); - - $changedetected = 1; - } catch (Exception $e) { - $cronlog->logAction(CRON_ACTION, LOG_ERR, - "Could not get Let's Encrypt certificate for " . $certrow['domain'] . ": " . $e->getMessage()); - } } else { - $cronlog->logAction(CRON_ACTION, LOG_WARNING, - "Skipping Let's Encrypt generation for " . $certrow['domain'] . " due to an enabled ssl_redirect"); + // 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'])) { + $insert_or_update_required = false; + } + } + + if ($insert_or_update_required) + { + $domains = array( + $certrow['domain'], + 'www.'.$certrow['domain'] + ); + + // Only renew let's encrypt certificate if no broken ssl_redirect is enabled + if ($certrow['ssl_redirect'] != 2) { + $cronlog->logAction(CRON_ACTION, LOG_DEBUG, "Updating " . $certrow['domain']); + + $cronlog = FroxlorLogger::getInstanceOf(array( + 'loginname' => $certrow['loginname'] + )); + + try { + // Initialize Lescript with documentroot + $le = new lescript($cronlog, $version); + + // Initialize Lescript + $le->initAccount($certrow, true); + + // Request the new certificate (old key may be used) + $return = $le->signDomains($domains, $certrow['ssl_key_file'], $certrow['ssl_csr_file']); + + // We are interessted in the expirationdate + $newcert = openssl_x509_parse($return['crt']); + + // Store the new data + Database::pexecute($updcert_stmt, + array( + 'id' => $certrow['id'], + 'domainid' => $certrow['domainid'], + 'crt' => $return['crt'], + 'key' => $return['key'], + 'ca' => $return['chain'], + 'chain' => $return['chain'], + 'csr' => $return['csr'], + 'expirationdate' => date('Y-m-d H:i:s', $newcert['validTo_time_t']) + )); + + if ($certrow['ssl_redirect'] == 3) { + Settings::Set('system.le_froxlor_redirect', '1'); + } + + $cronlog->logAction(CRON_ACTION, LOG_INFO, "Updated Let's Encrypt certificate for " . $certrow['domain']); + + $changedetected = 1; + } catch (Exception $e) { + $cronlog->logAction(CRON_ACTION, LOG_ERR, + "Could not get Let's Encrypt certificate for " . $certrow['domain'] . ": " . $e->getMessage()); + } + } else { + $cronlog->logAction(CRON_ACTION, LOG_WARNING, + "Skipping Let's Encrypt generation for " . $certrow['domain'] . " due to an enabled ssl_redirect"); + } } } From d1a3defef074df70511185462c98796e90f550bd Mon Sep 17 00:00:00 2001 From: Daniel Reichelt Date: Sat, 10 Sep 2016 21:02:52 +0200 Subject: [PATCH 0170/1335] LE: change log level to LOG_INFO --- scripts/jobs/cron_letsencrypt.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/scripts/jobs/cron_letsencrypt.php b/scripts/jobs/cron_letsencrypt.php index cfca074e..7bdf22cc 100644 --- a/scripts/jobs/cron_letsencrypt.php +++ b/scripts/jobs/cron_letsencrypt.php @@ -151,7 +151,7 @@ if (Settings::Get('system.le_froxlor_enabled') == '1') { // Only renew let's encrypt certificate if no broken ssl_redirect is enabled if ($certrow['ssl_redirect'] != 2) { - $cronlog->logAction(CRON_ACTION, LOG_DEBUG, "Updating " . $certrow['domain']); + $cronlog->logAction(CRON_ACTION, LOG_INFO, "Updating " . $certrow['domain']); $cronlog = FroxlorLogger::getInstanceOf(array( 'loginname' => $certrow['loginname'] @@ -212,15 +212,15 @@ foreach ($certrows as $certrow) { // Only renew let's encrypt certificate if no broken ssl_redirect is enabled if ($certrow['ssl_redirect'] != 2) { - $cronlog->logAction(CRON_ACTION, LOG_DEBUG, "Updating " . $certrow['domain']); + $cronlog->logAction(CRON_ACTION, LOG_INFO, "Updating " . $certrow['domain']); - $cronlog->logAction(CRON_ACTION, LOG_DEBUG, "Adding SAN entry: " . $certrow['domain']); + $cronlog->logAction(CRON_ACTION, LOG_INFO, "Adding SAN entry: " . $certrow['domain']); $domains = array( $certrow['domain'] ); // add www. to SAN list if ($certrow['wwwserveralias'] == 1) { - $cronlog->logAction(CRON_ACTION, LOG_DEBUG, "Adding SAN entry: www." . $certrow['domain']); + $cronlog->logAction(CRON_ACTION, LOG_INFO, "Adding SAN entry: www." . $certrow['domain']); $domains[] = 'www.' . $certrow['domain']; } @@ -230,10 +230,10 @@ foreach ($certrows as $certrow) { )); $aliasdomains = $aliasdomains_stmt->fetchAll(PDO::FETCH_ASSOC); foreach ($aliasdomains as $aliasdomain) { - $cronlog->logAction(CRON_ACTION, LOG_DEBUG, "Adding SAN entry: " . $aliasdomain['domain']); + $cronlog->logAction(CRON_ACTION, LOG_INFO, "Adding SAN entry: " . $aliasdomain['domain']); $domains[] = $aliasdomain['domain']; if ($aliasdomain['wwwserveralias'] == 1) { - $cronlog->logAction(CRON_ACTION, LOG_DEBUG, "Adding SAN entry: www." . $aliasdomain['domain']); + $cronlog->logAction(CRON_ACTION, LOG_INFO, "Adding SAN entry: www." . $aliasdomain['domain']); $domains[] = 'www.' . $aliasdomain['domain']; } } From b8bfd7ff4c73a7e32bc30230cbbae592ac333b51 Mon Sep 17 00:00:00 2001 From: Daniel Reichelt Date: Sun, 11 Sep 2016 00:47:32 +0200 Subject: [PATCH 0171/1335] LE: don't re-use old CSRs, always generate new ones fixes #1652 --- scripts/jobs/cron_letsencrypt.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/jobs/cron_letsencrypt.php b/scripts/jobs/cron_letsencrypt.php index 7bdf22cc..5e460698 100644 --- a/scripts/jobs/cron_letsencrypt.php +++ b/scripts/jobs/cron_letsencrypt.php @@ -165,7 +165,7 @@ if (Settings::Get('system.le_froxlor_enabled') == '1') { $le->initAccount($certrow, true); // Request the new certificate (old key may be used) - $return = $le->signDomains($domains, $certrow['ssl_key_file'], $certrow['ssl_csr_file']); + $return = $le->signDomains($domains, $certrow['ssl_key_file']); // We are interessted in the expirationdate $newcert = openssl_x509_parse($return['crt']); @@ -246,7 +246,7 @@ foreach ($certrows as $certrow) { $le->initAccount($certrow); // Request the new certificate (old key may be used) - $return = $le->signDomains($domains, $certrow['ssl_key_file'], $certrow['ssl_csr_file']); + $return = $le->signDomains($domains, $certrow['ssl_key_file']); // We are interessted in the expirationdate $newcert = openssl_x509_parse($return['crt']); From d56afda27484a50ce9d6e50f7abacb6ac8b1fdbe Mon Sep 17 00:00:00 2001 From: Daniel Reichelt Date: Sun, 11 Sep 2016 02:24:00 +0200 Subject: [PATCH 0172/1335] fix "undefined index" warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PHP Notice: Undefined index: parentdomainid in […]/froxlor/lib/classes/webserver/class.DomainSSL.php on line 49 --- scripts/jobs/cron_tasks.inc.http.10.apache.php | 1 + scripts/jobs/cron_tasks.inc.http.20.lighttpd.php | 3 ++- scripts/jobs/cron_tasks.inc.http.30.nginx.php | 3 ++- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/scripts/jobs/cron_tasks.inc.http.10.apache.php b/scripts/jobs/cron_tasks.inc.http.10.apache.php index c183e3e6..06eca8d4 100644 --- a/scripts/jobs/cron_tasks.inc.http.10.apache.php +++ b/scripts/jobs/cron_tasks.inc.http.10.apache.php @@ -369,6 +369,7 @@ class apache extends HttpConfigBase { 'adminid' => 1, /* first admin-user (superadmin) */ 'loginname' => 'froxlor.panel', 'documentroot' => $mypath, + 'parentdomainid' => 0, ); // override corresponding array values diff --git a/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php b/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php index ccd137c2..1b7dfadb 100644 --- a/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php +++ b/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php @@ -174,7 +174,8 @@ class lighttpd extends HttpConfigBase 'domain' => Settings::Get('system.hostname'), 'adminid' => 1, /* first admin-user (superadmin) */ 'loginname' => 'froxlor.panel', - 'documentroot' => $mypath + 'documentroot' => $mypath, + 'parentdomainid' => 0, ); // override corresponding array values diff --git a/scripts/jobs/cron_tasks.inc.http.30.nginx.php b/scripts/jobs/cron_tasks.inc.http.30.nginx.php index 10304be5..985b4f7e 100644 --- a/scripts/jobs/cron_tasks.inc.http.30.nginx.php +++ b/scripts/jobs/cron_tasks.inc.http.30.nginx.php @@ -156,7 +156,8 @@ class nginx extends HttpConfigBase { 'domain' => Settings::Get('system.hostname'), 'adminid' => 1, /* first admin-user (superadmin) */ 'loginname' => 'froxlor.panel', - 'documentroot' => $mypath + 'documentroot' => $mypath, + 'parentdomainid' => 0, ); // override corresponding array values From c51840e760b954f07789d8e87f063d4cf8c708ec Mon Sep 17 00:00:00 2001 From: "Jens A. Koch" Date: Sun, 11 Sep 2016 12:00:26 +0200 Subject: [PATCH 0173/1335] bugfix for error, when trying to call function logAction() on undefined var $cronlog --- lib/classes/ssl/class.lescript.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/classes/ssl/class.lescript.php b/lib/classes/ssl/class.lescript.php index fe31a5c0..dc8ec83b 100644 --- a/lib/classes/ssl/class.lescript.php +++ b/lib/classes/ssl/class.lescript.php @@ -198,7 +198,7 @@ class lescript $errmsg = ""; } @unlink($tokenPath); - $cronlog->logAction(CRON_ACTION, LOG_ERR,"letsencrypt Please check $uri - token not available" . $errmsg); + $this->logger->logAction(CRON_ACTION, LOG_ERR, "letsencrypt Please check $uri - token not available" . $errmsg); } $this->log("Sending request to challenge"); From b8c20473799a548b114d8b109791f948f7bb5e76 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sun, 11 Sep 2016 17:48:08 +0200 Subject: [PATCH 0174/1335] try to implement ssl-redirect for froxlor-vhost; combine various settings that are froxlor-vhost related into its own category, fixes #1480 Signed-off-by: Michael Kaufmann (d00p) --- actions/admin/settings/120.system.php | 8 - actions/admin/settings/122.froxlorvhost.php | 163 ++++ actions/admin/settings/131.ssl.php | 272 ++++--- actions/admin/settings/135.fcgid.php | 38 - actions/admin/settings/136.phpfpm.php | 42 +- actions/admin/settings/160.nameserver.php | 10 - lib/classes/webserver/class.DomainSSL.php | 2 +- lng/english.lng.php | 1 + lng/german.lng.php | 1 + scripts/classes/class.DnsBase.php | 56 +- scripts/classes/class.HttpConfigBase.php | 107 ++- scripts/jobs/cron_letsencrypt.php | 127 ++- .../jobs/cron_tasks.inc.http.10.apache.php | 723 +++++++++--------- .../jobs/cron_tasks.inc.http.20.lighttpd.php | 102 +-- scripts/jobs/cron_tasks.inc.http.30.nginx.php | 102 +-- 15 files changed, 931 insertions(+), 823 deletions(-) create mode 100644 actions/admin/settings/122.froxlorvhost.php diff --git a/actions/admin/settings/120.system.php b/actions/admin/settings/120.system.php index 810c931e..464244c5 100644 --- a/actions/admin/settings/120.system.php +++ b/actions/admin/settings/120.system.php @@ -69,14 +69,6 @@ return array( 'save_method' => 'storeSettingHostname', 'plausibility_check_method' => 'checkHostname', ), - 'system_froxlordirectlyviahostname' => array( - 'label' => $lng['serversettings']['froxlordirectlyviahostname'], - 'settinggroup' => 'system', - 'varname' => 'froxlordirectlyviahostname', - 'type' => 'bool', - 'default' => false, - 'save_method' => 'storeSettingField', - ), 'system_validatedomain' => array( 'label' => $lng['serversettings']['validate_domain'], 'settinggroup' => 'system', diff --git a/actions/admin/settings/122.froxlorvhost.php b/actions/admin/settings/122.froxlorvhost.php new file mode 100644 index 00000000..e92fa582 --- /dev/null +++ b/actions/admin/settings/122.froxlorvhost.php @@ -0,0 +1,163 @@ + (2016-) + * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt + * @package Settings + * + */ +return array( + 'groups' => array( + 'froxlorvhost' => array( + 'title' => $lng['admin']['froxlorvhost'], + 'fields' => array( + /** + * Webserver-Vhost + */ + 'system_froxlordirectlyviahostname' => array( + 'label' => $lng['serversettings']['froxlordirectlyviahostname'], + 'settinggroup' => 'system', + 'varname' => 'froxlordirectlyviahostname', + 'type' => 'bool', + 'default' => false, + 'save_method' => 'storeSettingField' + ), + /** + * SSL / Let's Encrypt + */ + 'system_le_froxlor_enabled' => array( + 'label' => $lng['serversettings']['le_froxlor_enabled'], + 'settinggroup' => 'system', + 'varname' => 'le_froxlor_enabled', + 'type' => 'bool', + 'default' => false, + 'save_method' => 'storeSettingField', + 'visible' => Settings::Get('system.leenabled') + ), + 'system_le_froxlor_redirect' => array( + 'label' => $lng['serversettings']['le_froxlor_redirect'], + 'settinggroup' => 'system', + 'varname' => 'le_froxlor_redirect', + 'type' => 'bool', + 'default' => false, + 'save_method' => 'storeSettingField', + 'visible' => Settings::Get('system.leenabled') + ), + /** + * FCGID + */ + 'system_mod_fcgid_enabled_ownvhost' => array( + 'label' => $lng['serversettings']['mod_fcgid_ownvhost'], + 'settinggroup' => 'system', + 'varname' => 'mod_fcgid_ownvhost', + 'type' => 'bool', + 'default' => true, + 'save_method' => 'storeSettingField', + 'websrv_avail' => array( + 'apache2' + ), + 'visible' => Settings::Get('system.mod_fcgid') + ), + 'system_mod_fcgid_httpuser' => array( + 'label' => $lng['admin']['mod_fcgid_user'], + 'settinggroup' => 'system', + 'varname' => 'mod_fcgid_httpuser', + 'type' => 'string', + 'default' => 'froxlorlocal', + 'save_method' => 'storeSettingWebserverFcgidFpmUser', + 'websrv_avail' => array( + 'apache2' + ), + 'visible' => Settings::Get('system.mod_fcgid') + ), + 'system_mod_fcgid_httpgroup' => array( + 'label' => $lng['admin']['mod_fcgid_group'], + 'settinggroup' => 'system', + 'varname' => 'mod_fcgid_httpgroup', + 'type' => 'string', + 'default' => 'froxlorlocal', + 'save_method' => 'storeSettingField', + 'websrv_avail' => array( + 'apache2' + ), + 'visible' => Settings::Get('system.mod_fcgid') + ), + 'system_mod_fcgid_defaultini_ownvhost' => array( + 'label' => $lng['serversettings']['mod_fcgid']['defaultini_ownvhost'], + 'settinggroup' => 'system', + 'varname' => 'mod_fcgid_defaultini_ownvhost', + 'type' => 'option', + 'default' => '2', + 'option_mode' => 'one', + 'option_options_method' => 'getPhpConfigs', + 'save_method' => 'storeSettingField', + 'websrv_avail' => array( + 'apache2' + ), + 'visible' => Settings::Get('system.mod_fcgid') + ), + /** + * php-fpm + */ + 'system_phpfpm_enabled_ownvhost' => array( + 'label' => $lng['phpfpm']['ownvhost'], + 'settinggroup' => 'phpfpm', + 'varname' => 'enabled_ownvhost', + 'type' => 'bool', + 'default' => true, + 'save_method' => 'storeSettingField', + 'visible' => Settings::Get('phpfpm.enabled') + ), + 'system_phpfpm_httpuser' => array( + 'label' => $lng['phpfpm']['vhost_httpuser'], + 'settinggroup' => 'phpfpm', + 'varname' => 'vhost_httpuser', + 'type' => 'string', + 'default' => 'froxlorlocal', + 'save_method' => 'storeSettingWebserverFcgidFpmUser', + 'visible' => Settings::Get('phpfpm.enabled') + ), + 'system_phpfpm_httpgroup' => array( + 'label' => $lng['phpfpm']['vhost_httpgroup'], + 'settinggroup' => 'phpfpm', + 'varname' => 'vhost_httpgroup', + 'type' => 'string', + 'default' => 'froxlorlocal', + 'save_method' => 'storeSettingField', + 'visible' => Settings::Get('phpfpm.enabled') + ), + 'system_phpfpm_defaultini_ownvhost' => array( + 'label' => $lng['serversettings']['mod_fcgid']['defaultini_ownvhost'], + 'settinggroup' => 'phpfpm', + 'varname' => 'vhost_defaultini', + 'type' => 'option', + 'default' => '2', + 'option_mode' => 'one', + 'option_options_method' => 'getPhpConfigs', + 'save_method' => 'storeSettingField', + 'visible' => Settings::Get('phpfpm.enabled') + ), + /** + * DNS + */ + 'system_dns_createhostnameentry' => array( + 'label' => $lng['serversettings']['dns_createhostnameentry'], + 'settinggroup' => 'system', + 'varname' => 'dns_createhostnameentry', + 'type' => 'bool', + 'default' => false, + 'save_method' => 'storeSettingField', + 'visible' => Settings::Get('system.bind_enable') + ) + ) + ) + ) +); diff --git a/actions/admin/settings/131.ssl.php b/actions/admin/settings/131.ssl.php index f7b5c4e7..7dff2d50 100644 --- a/actions/admin/settings/131.ssl.php +++ b/actions/admin/settings/131.ssl.php @@ -16,150 +16,136 @@ * @package Settings * */ - return array( 'groups' => array( - 'ssl' => array( - 'title' => $lng['admin']['sslsettings'], - 'fields' => array( - 'system_ssl_enabled' => array( - 'label' => $lng['serversettings']['ssl']['use_ssl'], - 'settinggroup' => 'system', - 'varname' => 'use_ssl', - 'type' => 'bool', - 'default' => false, - 'save_method' => 'storeSettingField', - 'overview_option' => true - ), - 'system_ssl_cipher_list' => array( - 'label' => $lng['serversettings']['ssl']['ssl_cipher_list'], - 'settinggroup' => 'system', - 'varname' => 'ssl_cipher_list', - 'type' => 'string', - 'string_emptyallowed' => false, - 'default' => 'ECDH+AESGCM:ECDH+AES256:!aNULL:!MD5:!DSS:!DH:!AES128', - 'save_method' => 'storeSettingField', - ), - 'system_ssl_cert_file' => array( - 'label' => $lng['serversettings']['ssl']['ssl_cert_file'], - 'settinggroup' => 'system', - 'varname' => 'ssl_cert_file', - 'type' => 'string', - 'string_type' => 'file', - 'string_emptyallowed' => true, - 'default' => '/etc/apache2/apache2.pem', - 'save_method' => 'storeSettingField', - ), - 'system_ssl_key_file' => array( - 'label' => $lng['serversettings']['ssl']['ssl_key_file'], - 'settinggroup' => 'system', - 'varname' => 'ssl_key_file', - 'type' => 'string', - 'string_type' => 'file', - 'string_emptyallowed' => true, - 'default' => '/etc/apache2/apache2.key', - 'save_method' => 'storeSettingField', - ), - 'system_ssl_cert_chainfile' => array( - 'label' => $lng['admin']['ipsandports']['ssl_cert_chainfile'], - 'settinggroup' => 'system', - 'varname' => 'ssl_cert_chainfile', - 'type' => 'string', - 'string_type' => 'file', - 'string_emptyallowed' => true, - 'default' => '', - 'save_method' => 'storeSettingField', - ), - 'system_ssl_ca_file' => array( - 'label' => $lng['serversettings']['ssl']['ssl_ca_file'], - 'settinggroup' => 'system', - 'varname' => 'ssl_ca_file', - 'type' => 'string', - 'string_type' => 'file', - 'string_emptyallowed' => true, - 'default' => '', - 'save_method' => 'storeSettingField', - ), - 'system_leenabled' => array( - 'label' => $lng['serversettings']['leenabled'], - 'settinggroup' => 'system', - 'varname' => 'leenabled', - 'type' => 'bool', - 'default' => false, - 'cronmodule' => 'froxlor/letsencrypt', - 'save_method' => 'storeSettingField' - ), - 'system_le_froxlor_enabled' => array( - 'label' => $lng['serversettings']['le_froxlor_enabled'], - 'settinggroup' => 'system', - 'varname' => 'le_froxlor_enabled', - 'type' => 'bool', - 'default' => false, - 'save_method' => 'storeSettingField' - ), - 'system_le_froxlor_redirect' => array( - 'label' => $lng['serversettings']['le_froxlor_redirect'], - 'settinggroup' => 'system', - 'varname' => 'le_froxlor_redirect', - 'type' => 'bool', - 'default' => false, - 'save_method' => 'storeSettingField' - ), - 'system_letsencryptca' => array( - 'label' => $lng['serversettings']['letsencryptca'], - 'settinggroup' => 'system', - 'varname' => 'letsencryptca', - 'type' => 'option', - 'default' => 'testing', - 'option_mode' => 'one', - 'option_options' => array('testing' => 'https://acme-staging.api.letsencrypt.org (Test)', 'production' => 'https://acme-v01.api.letsencrypt.org (Live)'), - 'save_method' => 'storeSettingField', - ), - 'system_letsencryptcountrycode' => array( - 'label' => $lng['serversettings']['letsencryptcountrycode'], - 'settinggroup' => 'system', - 'varname' => 'letsencryptcountrycode', - 'type' => 'string', - 'string_emptyallowed' => false, - 'default' => 'DE', - 'save_method' => 'storeSettingField', - ), - 'system_letsencryptstate' => array( - 'label' => $lng['serversettings']['letsencryptstate'], - 'settinggroup' => 'system', - 'varname' => 'letsencryptstate', - 'type' => 'string', - 'string_emptyallowed' => false, - 'default' => 'Hessen', - 'save_method' => 'storeSettingField', - ), - 'system_letsencryptchallengepath' => array( - 'label' => $lng['serversettings']['letsencryptchallengepath'], - 'settinggroup' => 'system', - 'varname' => 'letsencryptchallengepath', - 'type' => 'string', - 'string_emptyallowed' => false, - 'default' => FROXLOR_INSTALL_DIR, - 'save_method' => 'storeSettingField', - ), - 'system_letsencryptkeysize' => array( - 'label' => $lng['serversettings']['letsencryptkeysize'], - 'settinggroup' => 'system', - 'varname' => 'letsencryptkeysize', - 'type' => 'int', - 'int_min' => 2048, - 'default' => 4096, - 'save_method' => 'storeSettingField', - ), - 'system_letsencryptreuseold' => array( - 'label' => $lng['serversettings']['letsencryptreuseold'], - 'settinggroup' => 'system', - 'varname' => 'letsencryptreuseold', - 'type' => 'bool', - 'default' => false, - 'save_method' => 'storeSettingField', - ), - ) + 'ssl' => array( + 'title' => $lng['admin']['sslsettings'], + 'fields' => array( + 'system_ssl_enabled' => array( + 'label' => $lng['serversettings']['ssl']['use_ssl'], + 'settinggroup' => 'system', + 'varname' => 'use_ssl', + 'type' => 'bool', + 'default' => false, + 'save_method' => 'storeSettingField', + 'overview_option' => true + ), + 'system_ssl_cipher_list' => array( + 'label' => $lng['serversettings']['ssl']['ssl_cipher_list'], + 'settinggroup' => 'system', + 'varname' => 'ssl_cipher_list', + 'type' => 'string', + 'string_emptyallowed' => false, + 'default' => 'ECDH+AESGCM:ECDH+AES256:!aNULL:!MD5:!DSS:!DH:!AES128', + 'save_method' => 'storeSettingField' + ), + 'system_ssl_cert_file' => array( + 'label' => $lng['serversettings']['ssl']['ssl_cert_file'], + 'settinggroup' => 'system', + 'varname' => 'ssl_cert_file', + 'type' => 'string', + 'string_type' => 'file', + 'string_emptyallowed' => true, + 'default' => '/etc/apache2/apache2.pem', + 'save_method' => 'storeSettingField' + ), + 'system_ssl_key_file' => array( + 'label' => $lng['serversettings']['ssl']['ssl_key_file'], + 'settinggroup' => 'system', + 'varname' => 'ssl_key_file', + 'type' => 'string', + 'string_type' => 'file', + 'string_emptyallowed' => true, + 'default' => '/etc/apache2/apache2.key', + 'save_method' => 'storeSettingField' + ), + 'system_ssl_cert_chainfile' => array( + 'label' => $lng['admin']['ipsandports']['ssl_cert_chainfile'], + 'settinggroup' => 'system', + 'varname' => 'ssl_cert_chainfile', + 'type' => 'string', + 'string_type' => 'file', + 'string_emptyallowed' => true, + 'default' => '', + 'save_method' => 'storeSettingField' + ), + 'system_ssl_ca_file' => array( + 'label' => $lng['serversettings']['ssl']['ssl_ca_file'], + 'settinggroup' => 'system', + 'varname' => 'ssl_ca_file', + 'type' => 'string', + 'string_type' => 'file', + 'string_emptyallowed' => true, + 'default' => '', + 'save_method' => 'storeSettingField' + ), + 'system_leenabled' => array( + 'label' => $lng['serversettings']['leenabled'], + 'settinggroup' => 'system', + 'varname' => 'leenabled', + 'type' => 'bool', + 'default' => false, + 'cronmodule' => 'froxlor/letsencrypt', + 'save_method' => 'storeSettingField' + ), + 'system_letsencryptca' => array( + 'label' => $lng['serversettings']['letsencryptca'], + 'settinggroup' => 'system', + 'varname' => 'letsencryptca', + 'type' => 'option', + 'default' => 'testing', + 'option_mode' => 'one', + 'option_options' => array( + 'testing' => 'https://acme-staging.api.letsencrypt.org (Test)', + 'production' => 'https://acme-v01.api.letsencrypt.org (Live)' + ), + 'save_method' => 'storeSettingField' + ), + 'system_letsencryptcountrycode' => array( + 'label' => $lng['serversettings']['letsencryptcountrycode'], + 'settinggroup' => 'system', + 'varname' => 'letsencryptcountrycode', + 'type' => 'string', + 'string_emptyallowed' => false, + 'default' => 'DE', + 'save_method' => 'storeSettingField' + ), + 'system_letsencryptstate' => array( + 'label' => $lng['serversettings']['letsencryptstate'], + 'settinggroup' => 'system', + 'varname' => 'letsencryptstate', + 'type' => 'string', + 'string_emptyallowed' => false, + 'default' => 'Hessen', + 'save_method' => 'storeSettingField' + ), + 'system_letsencryptchallengepath' => array( + 'label' => $lng['serversettings']['letsencryptchallengepath'], + 'settinggroup' => 'system', + 'varname' => 'letsencryptchallengepath', + 'type' => 'string', + 'string_emptyallowed' => false, + 'default' => FROXLOR_INSTALL_DIR, + 'save_method' => 'storeSettingField' + ), + 'system_letsencryptkeysize' => array( + 'label' => $lng['serversettings']['letsencryptkeysize'], + 'settinggroup' => 'system', + 'varname' => 'letsencryptkeysize', + 'type' => 'int', + 'int_min' => 2048, + 'default' => 4096, + 'save_method' => 'storeSettingField' + ), + 'system_letsencryptreuseold' => array( + 'label' => $lng['serversettings']['letsencryptreuseold'], + 'settinggroup' => 'system', + 'varname' => 'letsencryptreuseold', + 'type' => 'bool', + 'default' => false, + 'save_method' => 'storeSettingField' + ) ) ) - ); + ) +); diff --git a/actions/admin/settings/135.fcgid.php b/actions/admin/settings/135.fcgid.php index 0161ce7f..050ede3b 100644 --- a/actions/admin/settings/135.fcgid.php +++ b/actions/admin/settings/135.fcgid.php @@ -97,44 +97,6 @@ return array( 'option_options_method' => 'getPhpConfigs', 'save_method' => 'storeSettingField', ), - 'system_mod_fcgid_enabled_ownvhost' => array( - 'label' => $lng['serversettings']['mod_fcgid_ownvhost'], - 'settinggroup' => 'system', - 'varname' => 'mod_fcgid_ownvhost', - 'type' => 'bool', - 'default' => true, - 'save_method' => 'storeSettingField', - 'websrv_avail' => array('apache2') - ), - 'system_mod_fcgid_httpuser' => array( - 'label' => $lng['admin']['mod_fcgid_user'], - 'settinggroup' => 'system', - 'varname' => 'mod_fcgid_httpuser', - 'type' => 'string', - 'default' => 'froxlorlocal', - 'save_method' => 'storeSettingWebserverFcgidFpmUser', - 'websrv_avail' => array('apache2') - ), - 'system_mod_fcgid_httpgroup' => array( - 'label' => $lng['admin']['mod_fcgid_group'], - 'settinggroup' => 'system', - 'varname' => 'mod_fcgid_httpgroup', - 'type' => 'string', - 'default' => 'froxlorlocal', - 'save_method' => 'storeSettingField', - 'websrv_avail' => array('apache2') - ), - 'system_mod_fcgid_defaultini_ownvhost' => array( - 'label' => $lng['serversettings']['mod_fcgid']['defaultini_ownvhost'], - 'settinggroup' => 'system', - 'varname' => 'mod_fcgid_defaultini_ownvhost', - 'type' => 'option', - 'default' => '2', - 'option_mode' => 'one', - 'option_options_method' => 'getPhpConfigs', - 'save_method' => 'storeSettingField', - 'websrv_avail' => array('apache2') - ), 'system_mod_fcgid_idle_timeout' => array( 'label' => $lng['serversettings']['mod_fcgid']['idle_timeout'], 'settinggroup' => 'system', diff --git a/actions/admin/settings/136.phpfpm.php b/actions/admin/settings/136.phpfpm.php index c4a3afd1..750a4660 100644 --- a/actions/admin/settings/136.phpfpm.php +++ b/actions/admin/settings/136.phpfpm.php @@ -30,46 +30,12 @@ return array( 'plausibility_check_method' => 'checkFcgidPhpFpm', 'overview_option' => true ), - 'system_phpfpm_enabled_ownvhost' => array( - 'label' => $lng['phpfpm']['ownvhost'], + 'system_phpfpm_defaultini' => array( + 'label' => $lng['serversettings']['mod_fcgid']['defaultini'], 'settinggroup' => 'phpfpm', - 'varname' => 'enabled_ownvhost', - 'type' => 'bool', - 'default' => true, - 'save_method' => 'storeSettingField' - ), - 'system_phpfpm_httpuser' => array( - 'label' => $lng['phpfpm']['vhost_httpuser'], - 'settinggroup' => 'phpfpm', - 'varname' => 'vhost_httpuser', - 'type' => 'string', - 'default' => 'froxlorlocal', - 'save_method' => 'storeSettingWebserverFcgidFpmUser' - ), - 'system_phpfpm_httpgroup' => array( - 'label' => $lng['phpfpm']['vhost_httpgroup'], - 'settinggroup' => 'phpfpm', - 'varname' => 'vhost_httpgroup', - 'type' => 'string', - 'default' => 'froxlorlocal', - 'save_method' => 'storeSettingField' - ), - 'system_phpfpm_defaultini' => array( - 'label' => $lng['serversettings']['mod_fcgid']['defaultini'], - 'settinggroup' => 'phpfpm', - 'varname' => 'defaultini', - 'type' => 'option', - 'default' => '1', - 'option_mode' => 'one', - 'option_options_method' => 'getPhpConfigs', - 'save_method' => 'storeSettingField' - ), - 'system_phpfpm_defaultini_ownvhost' => array( - 'label' => $lng['serversettings']['mod_fcgid']['defaultini_ownvhost'], - 'settinggroup' => 'phpfpm', - 'varname' => 'vhost_defaultini', + 'varname' => 'defaultini', 'type' => 'option', - 'default' => '2', + 'default' => '1', 'option_mode' => 'one', 'option_options_method' => 'getPhpConfigs', 'save_method' => 'storeSettingField' diff --git a/actions/admin/settings/160.nameserver.php b/actions/admin/settings/160.nameserver.php index 11d2b6d3..bd7da456 100644 --- a/actions/admin/settings/160.nameserver.php +++ b/actions/admin/settings/160.nameserver.php @@ -97,14 +97,6 @@ return array( 'default' => '', 'save_method' => 'storeSettingField', ), - 'system_dns_createhostnameentry' => array( - 'label' => $lng['serversettings']['dns_createhostnameentry'], - 'settinggroup' => 'system', - 'varname' => 'dns_createhostnameentry', - 'type' => 'bool', - 'default' => false, - 'save_method' => 'storeSettingField' - ), 'system_dns_createmailentry' => array( 'label' => $lng['serversettings']['mail_also_with_mxservers'], 'settinggroup' => 'system', @@ -127,5 +119,3 @@ return array( ), ), ); - -?> \ No newline at end of file diff --git a/lib/classes/webserver/class.DomainSSL.php b/lib/classes/webserver/class.DomainSSL.php index dbf0d487..82ae9fcd 100644 --- a/lib/classes/webserver/class.DomainSSL.php +++ b/lib/classes/webserver/class.DomainSSL.php @@ -46,7 +46,7 @@ class DomainSSL { || $dom_certs['ssl_cert_file'] == '' ) { // maybe its parent? - if ($domain['parentdomainid'] != 0) { + if (isset($domain['parentdomainid']) && $domain['parentdomainid'] != 0) { $dom_certs = Database::pexecute_first($dom_certs_stmt, array('domid' => $domain['parentdomainid'])); } } diff --git a/lng/english.lng.php b/lng/english.lng.php index 62b6e62a..d4a2c4bd 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -2036,3 +2036,4 @@ $lng['serversettings']['le_froxlor_enabled']['title'] = "Enable Let's Encrypt fo $lng['serversettings']['le_froxlor_enabled']['description'] = "If activated, the froxlor vhost will automatically be secured using a Let's Encrypt certificate."; $lng['serversettings']['le_froxlor_redirect']['title'] = "Enable SSL-redirect for the froxlor vhost"; $lng['serversettings']['le_froxlor_redirect']['description'] = "If activated, all http requests to your froxlor will be redirected to the corresponding SSL site."; +$lng['admin']['froxlorvhost'] = 'Froxlor VirtualHost settings'; diff --git a/lng/german.lng.php b/lng/german.lng.php index 44ef1bdd..30c693b7 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -1688,3 +1688,4 @@ $lng['serversettings']['le_froxlor_enabled']['title'] = "Let's Encrypt für den $lng['serversettings']['le_froxlor_enabled']['description'] = "Wenn dies aktiviert ist, erstellt froxlor für seinen vhost automatisch ein Let's Encrypt Zertifikat."; $lng['serversettings']['le_froxlor_redirect']['title'] = "SSL-Weiterleitung für den froxlor Vhost aktivieren"; $lng['serversettings']['le_froxlor_redirect']['description'] = "Wenn dies aktiviert ist, werden alle HTTP Anfragen an die entsprechende SSL Seite weitergeleitet."; +$lng['admin']['froxlorvhost'] = 'Froxlor VirtualHost Einstellungen'; diff --git a/scripts/classes/class.DnsBase.php b/scripts/classes/class.DnsBase.php index 65753b59..46c83926 100644 --- a/scripts/classes/class.DnsBase.php +++ b/scripts/classes/class.DnsBase.php @@ -1,10 +1,24 @@ (2016-) + * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt + * @package Cron + * + */ + +/** * Class DnsBase * * Base class for all DNS server configs - * */ abstract class DnsBase { @@ -69,8 +83,7 @@ abstract class DnsBase protected function getDomainList() { - $result_domains_stmt = Database::query( - " + $result_domains_stmt = Database::query(" SELECT `d`.`id`, `d`.`domain`, @@ -131,35 +144,21 @@ abstract class DnsBase $domains[$key]['children'] = array(); } if ($domains[$key]['ismainbutsubto'] > 0) { - if (isset($domains[ $domains[$key]['ismainbutsubto'] ])) { - $domains[ $domains[$key]['ismainbutsubto'] ]['children'][] = $domains[$key]['id']; + if (isset($domains[$domains[$key]['ismainbutsubto']])) { + $domains[$domains[$key]['ismainbutsubto']]['children'][] = $domains[$key]['id']; } else { - $this->_logger->logAction(CRON_ACTION, LOG_ERR, - 'Database inconsistency: domain ' . $domain['domain'] . ' (ID #' . $key . - ') is set to to be subdomain to non-existent domain ID #' . - $domains[$key]['ismainbutsubto'] . - '. No DNS record(s) will be created for this domain.'); + $this->_logger->logAction(CRON_ACTION, LOG_ERR, 'Database inconsistency: domain ' . $domain['domain'] . ' (ID #' . $key . ') is set to to be subdomain to non-existent domain ID #' . $domains[$key]['ismainbutsubto'] . '. No DNS record(s) will be created for this domain.'); } } } - $this->_logger->logAction(CRON_ACTION, LOG_DEBUG, - str_pad('domId', 9, ' ') . str_pad('domain', 40, ' ') . - 'ismainbutsubto ' . str_pad('parent domain', 40, ' ') . - "list of child domain ids"); - foreach ($domains as $domain) { - $logLine = - str_pad($domain['id'], 9, ' ') . - str_pad($domain['domain'], 40, ' ') . - str_pad($domain['ismainbutsubto'], 15, ' ') . - str_pad(((isset($domains[ $domain['ismainbutsubto'] ])) ? - $domains[ $domain['ismainbutsubto'] ]['domain'] : - '-'), 40, ' ') . - join(', ', $domain['children']); - $this->_logger->logAction(CRON_ACTION, LOG_DEBUG, $logLine); - } + $this->_logger->logAction(CRON_ACTION, LOG_DEBUG, str_pad('domId', 9, ' ') . str_pad('domain', 40, ' ') . 'ismainbutsubto ' . str_pad('parent domain', 40, ' ') . "list of child domain ids"); + foreach ($domains as $domain) { + $logLine = str_pad($domain['id'], 9, ' ') . str_pad($domain['domain'], 40, ' ') . str_pad($domain['ismainbutsubto'], 15, ' ') . str_pad(((isset($domains[$domain['ismainbutsubto']])) ? $domains[$domain['ismainbutsubto']]['domain'] : '-'), 40, ' ') . join(', ', $domain['children']); + $this->_logger->logAction(CRON_ACTION, LOG_DEBUG, $logLine); + } - return $domains; + return $domains; } public function reloadDaemon() @@ -171,8 +170,7 @@ abstract class DnsBase if ($cmdStatus === 0) { $this->_logger->logAction(CRON_ACTION, LOG_INFO, Settings::Get('system.dns_server') . ' daemon reloaded'); } else { - $this->_logger->logAction(CRON_ACTION, LOG_ERR, 'Error while running `' . $cmd . - '`: exit code (' . $cmdStatus . ') - please check your system logs'); + $this->_logger->logAction(CRON_ACTION, LOG_ERR, 'Error while running `' . $cmd . '`: exit code (' . $cmdStatus . ') - please check your system logs'); } } diff --git a/scripts/classes/class.HttpConfigBase.php b/scripts/classes/class.HttpConfigBase.php index 071f43c1..d24559c2 100644 --- a/scripts/classes/class.HttpConfigBase.php +++ b/scripts/classes/class.HttpConfigBase.php @@ -1,11 +1,28 @@ (2016-) + * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt + * @package Cron + * + */ + +/** * Class HttpConfigBase * * Base class for all HTTP server configs * */ -class HttpConfigBase { +class HttpConfigBase +{ /** * process special config as template, by substituting {VARIABLE} with the @@ -13,26 +30,92 @@ class HttpConfigBase { * * The following variables are known at the moment: * - * {DOMAIN} - domain name - * {IP} - IP for this domain - * {PORT} - Port for this domain - * {CUSTOMER} - customer name - * {IS_SSL} - evaluates to 'ssl' if domain/ip is ssl, otherwise it is an empty string - * {DOCROOT} - document root for this domain + * {DOMAIN} - domain name + * {IP} - IP for this domain + * {PORT} - Port for this domain + * {CUSTOMER} - customer name + * {IS_SSL} - evaluates to 'ssl' if domain/ip is ssl, otherwise it is an empty string + * {DOCROOT} - document root for this domain * - * @param $template + * @param + * $template * @return string */ - protected function processSpecialConfigTemplate($template, $domain, $ip, $port, $is_ssl_vhost) { + protected function processSpecialConfigTemplate($template, $domain, $ip, $port, $is_ssl_vhost) + { $templateVars = array( 'DOMAIN' => $domain['domain'], 'CUSTOMER' => $domain['loginname'], 'IP' => $ip, 'PORT' => $port, - 'SCHEME' => ($is_ssl_vhost)?'https':'http', + 'SCHEME' => ($is_ssl_vhost) ? 'https' : 'http', 'DOCROOT' => $domain['documentroot'] ); return replace_variables($template, $templateVars); } -} \ No newline at end of file + protected function getMyPath($ip_port = null) + { + if (! empty($ip_port) && $ip_port['docroot'] == '') { + if (Settings::Get('system.froxlordirectlyviahostname')) { + $mypath = makeCorrectDir(dirname(dirname(dirname(__FILE__)))); + } else { + $mypath = makeCorrectDir(dirname(dirname(dirname(dirname(__FILE__))))); + } + } else { + // user-defined docroot, #417 + $mypath = makeCorrectDir($row_ipsandports['docroot']); + } + return $mypath; + } + + protected function checkAlternativeSslPort() + { + // We must not check if our port differs from port 443, + // but if there is a destination-port != 443 + $_sslport = ''; + // This returns the first port that is != 443 with ssl enabled, + // ordered by ssl-certificate (if any) so that the ip/port combo + // with certificate is used + $ssldestport_stmt = Database::prepare(" + SELECT `ip`.`port` FROM " . TABLE_PANEL_IPSANDPORTS . " `ip` + WHERE `ip`.`ssl` = '1' AND `ip`.`port` != 443 + ORDER BY `ip`.`ssl_cert_file` DESC, `ip`.`port` LIMIT 1; + "); + $ssldestport = Database::pexecute_first($ssldestport_stmt); + + if ($ssldestport['port'] != '') { + $_sslport = ":" . $ssldestport['port']; + } + + return $_sslport; + } + + protected function froxlorVhostHasLetsEncryptCert() + { + // 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'])) { + return true; + } + return false; + } + + protected function froxlorVhostLetsEncryptNeedsRenew() + { + $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 && ! empty($froxlor_ssl['ssl_cert_file'])) { + return true; + } + return false; + } +} diff --git a/scripts/jobs/cron_letsencrypt.php b/scripts/jobs/cron_letsencrypt.php index 5e460698..8df9055f 100644 --- a/scripts/jobs/cron_letsencrypt.php +++ b/scripts/jobs/cron_letsencrypt.php @@ -27,8 +27,7 @@ if (! extension_loaded('curl')) { exit(); } -$certificates_stmt = Database::query( - " +$certificates_stmt = Database::query(" SELECT domssl.`id`, domssl.`domainid`, @@ -63,8 +62,7 @@ $certificates_stmt = Database::query( ) "); -$aliasdomains_stmt = Database::prepare( - " +$aliasdomains_stmt = Database::prepare(" SELECT dom.`id` as domainid, dom.`domain`, @@ -76,8 +74,7 @@ $aliasdomains_stmt = Database::prepare( AND dom.`iswildcarddomain` = 0 "); -$updcert_stmt = Database::prepare( - " +$updcert_stmt = Database::prepare(" REPLACE INTO `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` SET @@ -116,7 +113,7 @@ if (Settings::Get('system.le_froxlor_enabled') == '1') { ); $froxlor_ssl_settings_stmt = Database::prepare(" - SELECT * FROM `".TABLE_PANEL_DOMAIN_SSL_SETTINGS."` + SELECT * FROM `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` WHERE `domainid` = '0' AND (`expirationdate` < DATE_ADD(NOW(), INTERVAL 30 DAY) OR `expirationdate` IS NULL) "); @@ -134,69 +131,62 @@ if (Settings::Get('system.le_froxlor_enabled') == '1') { // 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' + 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'])) { + if ($froxlor_ssl && ! empty($froxlor_ssl['ssl_cert_file'])) { $insert_or_update_required = false; } } - if ($insert_or_update_required) - { + if ($insert_or_update_required) { $domains = array( $certrow['domain'], - 'www.'.$certrow['domain'] + 'www.' . $certrow['domain'] ); // Only renew let's encrypt certificate if no broken ssl_redirect is enabled - if ($certrow['ssl_redirect'] != 2) { - $cronlog->logAction(CRON_ACTION, LOG_INFO, "Updating " . $certrow['domain']); + // - this temp. deactivation of the ssl-redirect is handled by the webserver-cronjob + $cronlog->logAction(CRON_ACTION, LOG_INFO, "Updating " . $certrow['domain']); - $cronlog = FroxlorLogger::getInstanceOf(array( - 'loginname' => $certrow['loginname'] + $cronlog = FroxlorLogger::getInstanceOf(array( + 'loginname' => $certrow['loginname'] + )); + + try { + // Initialize Lescript with documentroot + $le = new lescript($cronlog, $version); + + // Initialize Lescript + $le->initAccount($certrow, true); + + // Request the new certificate (old key may be used) + $return = $le->signDomains($domains, $certrow['ssl_key_file']); + + // We are interessted in the expirationdate + $newcert = openssl_x509_parse($return['crt']); + + // Store the new data + Database::pexecute($updcert_stmt, array( + 'id' => $certrow['id'], + 'domainid' => $certrow['domainid'], + 'crt' => $return['crt'], + 'key' => $return['key'], + 'ca' => $return['chain'], + 'chain' => $return['chain'], + 'csr' => $return['csr'], + 'expirationdate' => date('Y-m-d H:i:s', $newcert['validTo_time_t']) )); - try { - // Initialize Lescript with documentroot - $le = new lescript($cronlog, $version); - - // Initialize Lescript - $le->initAccount($certrow, true); - - // Request the new certificate (old key may be used) - $return = $le->signDomains($domains, $certrow['ssl_key_file']); - - // We are interessted in the expirationdate - $newcert = openssl_x509_parse($return['crt']); - - // Store the new data - Database::pexecute($updcert_stmt, - array( - 'id' => $certrow['id'], - 'domainid' => $certrow['domainid'], - 'crt' => $return['crt'], - 'key' => $return['key'], - 'ca' => $return['chain'], - 'chain' => $return['chain'], - 'csr' => $return['csr'], - 'expirationdate' => date('Y-m-d H:i:s', $newcert['validTo_time_t']) - )); - - if ($certrow['ssl_redirect'] == 3) { - Settings::Set('system.le_froxlor_redirect', '1'); - } - - $cronlog->logAction(CRON_ACTION, LOG_INFO, "Updated Let's Encrypt certificate for " . $certrow['domain']); - - $changedetected = 1; - } catch (Exception $e) { - $cronlog->logAction(CRON_ACTION, LOG_ERR, - "Could not get Let's Encrypt certificate for " . $certrow['domain'] . ": " . $e->getMessage()); + if ($certrow['ssl_redirect'] == 3) { + Settings::Set('system.le_froxlor_redirect', '1'); } - } else { - $cronlog->logAction(CRON_ACTION, LOG_WARNING, - "Skipping Let's Encrypt generation for " . $certrow['domain'] . " due to an enabled ssl_redirect"); + + $cronlog->logAction(CRON_ACTION, LOG_INFO, "Updated Let's Encrypt certificate for " . $certrow['domain']); + + $changedetected = 1; + } catch (Exception $e) { + $cronlog->logAction(CRON_ACTION, LOG_ERR, "Could not get Let's Encrypt certificate for " . $certrow['domain'] . ": " . $e->getMessage()); } } } @@ -252,17 +242,16 @@ foreach ($certrows as $certrow) { $newcert = openssl_x509_parse($return['crt']); // Store the new data - Database::pexecute($updcert_stmt, - array( - 'id' => $certrow['id'], - 'domainid' => $certrow['domainid'], - 'crt' => $return['crt'], - 'key' => $return['key'], - 'ca' => $return['chain'], - 'chain' => $return['chain'], - 'csr' => $return['csr'], - 'expirationdate' => date('Y-m-d H:i:s', $newcert['validTo_time_t']) - )); + Database::pexecute($updcert_stmt, array( + 'id' => $certrow['id'], + 'domainid' => $certrow['domainid'], + 'crt' => $return['crt'], + 'key' => $return['key'], + 'ca' => $return['chain'], + 'chain' => $return['chain'], + 'csr' => $return['csr'], + 'expirationdate' => date('Y-m-d H:i:s', $newcert['validTo_time_t']) + )); if ($certrow['ssl_redirect'] == 3) { Database::pexecute($upddom_stmt, array( @@ -274,12 +263,10 @@ foreach ($certrows as $certrow) { $changedetected = 1; } catch (Exception $e) { - $cronlog->logAction(CRON_ACTION, LOG_ERR, - "Could not get Let's Encrypt certificate for " . $certrow['domain'] . ": " . $e->getMessage()); + $cronlog->logAction(CRON_ACTION, LOG_ERR, "Could not get Let's Encrypt certificate for " . $certrow['domain'] . ": " . $e->getMessage()); } } else { - $cronlog->logAction(CRON_ACTION, LOG_WARNING, - "Skipping Let's Encrypt generation for " . $certrow['domain'] . " due to an enabled ssl_redirect"); + $cronlog->logAction(CRON_ACTION, LOG_WARNING, "Skipping Let's Encrypt generation for " . $certrow['domain'] . " due to an enabled ssl_redirect"); } } diff --git a/scripts/jobs/cron_tasks.inc.http.10.apache.php b/scripts/jobs/cron_tasks.inc.http.10.apache.php index 06eca8d4..88e30c6f 100644 --- a/scripts/jobs/cron_tasks.inc.http.10.apache.php +++ b/scripts/jobs/cron_tasks.inc.http.10.apache.php @@ -1,4 +1,7 @@ - (2003-2009) - * @author Froxlor team (2010-) - * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt - * @package Cron + * @copyright (c) the authors + * @author Florian Lippert (2003-2009) + * @author Froxlor team (2010-) + * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt + * @package Cron * */ -require_once(dirname(__FILE__).'/../classes/class.HttpConfigBase.php'); +require_once (dirname(__FILE__) . '/../classes/class.HttpConfigBase.php'); + +class apache extends HttpConfigBase +{ -class apache extends HttpConfigBase { private $logger = false; + private $idnaConvert = false; // protected protected $known_vhostfilenames = array(); + protected $known_diroptionsfilenames = array(); + protected $known_htpasswdsfilenames = array(); + protected $virtualhosts_data = array(); + protected $diroptions_data = array(); + protected $htpasswds_data = array(); /** @@ -39,14 +50,15 @@ class apache extends HttpConfigBase { */ private $_deactivated = false; - public function __construct($logger, $idnaConvert) { + public function __construct($logger, $idnaConvert) + { $this->logger = $logger; $this->idnaConvert = $idnaConvert; } - - public function reload() { - if ((int)Settings::Get('phpfpm.enabled') == 1) { + public function reload() + { + if ((int) Settings::Get('phpfpm.enabled') == 1) { $this->logger->logAction(CRON_ACTION, LOG_INFO, 'apache::reload: reloading php-fpm'); safe_exec(escapeshellcmd(Settings::Get('phpfpm.reload'))); } @@ -54,11 +66,11 @@ class apache extends HttpConfigBase { safe_exec(escapeshellcmd(Settings::Get('system.apachereload_command'))); } - /** * define a standard -statement, bug #32 */ - private function _createStandardDirectoryEntry() { + private function _createStandardDirectoryEntry() + { $vhosts_folder = ''; if (is_dir(Settings::Get('system.apacheconf_vhost'))) { $vhosts_folder = makeCorrectDir(Settings::Get('system.apacheconf_vhost')); @@ -67,53 +79,43 @@ class apache extends HttpConfigBase { } $vhosts_filename = makeCorrectFile($vhosts_folder . '/05_froxlor_dirfix_nofcgid.conf'); - if (Settings::Get('system.mod_fcgid') == '1' - || Settings::Get('phpfpm.enabled') == '1' - ) { + if (Settings::Get('system.mod_fcgid') == '1' || Settings::Get('phpfpm.enabled') == '1') { // if we use fcgid or php-fpm we don't need this file if (file_exists($vhosts_filename)) { $this->logger->logAction(CRON_ACTION, LOG_NOTICE, 'apache::_createStandardDirectoryEntry: unlinking ' . basename($vhosts_filename)); unlink(makeCorrectFile($vhosts_filename)); } } else { - if (!isset($this->virtualhosts_data[$vhosts_filename])) { + if (! isset($this->virtualhosts_data[$vhosts_filename])) { $this->virtualhosts_data[$vhosts_filename] = ''; } - $this->virtualhosts_data[$vhosts_filename].= ' ' . "\n"; + $this->virtualhosts_data[$vhosts_filename] .= ' ' . "\n"; // check for custom values, see #1638 $custom_opts = Settings::Get('system.apacheglobaldiropt'); - if (!empty($custom_opts)) - { - $this->virtualhosts_data[$vhosts_filename].= $custom_opts . "\n"; - } - else - { + if (! empty($custom_opts)) { + $this->virtualhosts_data[$vhosts_filename] .= $custom_opts . "\n"; + } else { // >=apache-2.4 enabled? if (Settings::Get('system.apache24') == '1') { - $this->virtualhosts_data[$vhosts_filename].= ' Require all granted' . "\n"; - $this->virtualhosts_data[$vhosts_filename].= ' AllowOverride All' . "\n"; + $this->virtualhosts_data[$vhosts_filename] .= ' Require all granted' . "\n"; + $this->virtualhosts_data[$vhosts_filename] .= ' AllowOverride All' . "\n"; } else { - $this->virtualhosts_data[$vhosts_filename].= ' Order allow,deny' . "\n"; - $this->virtualhosts_data[$vhosts_filename].= ' allow from all' . "\n"; + $this->virtualhosts_data[$vhosts_filename] .= ' Order allow,deny' . "\n"; + $this->virtualhosts_data[$vhosts_filename] .= ' allow from all' . "\n"; } } - $this->virtualhosts_data[$vhosts_filename].= ' ' . "\n"; + $this->virtualhosts_data[$vhosts_filename] .= ' ' . "\n"; } } - /** * define a default ErrorDocument-statement, bug #unknown-yet */ - private function _createStandardErrorHandler() { - if (Settings::Get('defaultwebsrverrhandler.enabled') == '1' - && (Settings::Get('defaultwebsrverrhandler.err401') != '' - || Settings::Get('defaultwebsrverrhandler.err403') != '' - || Settings::Get('defaultwebsrverrhandler.err404') != '' - || Settings::Get('defaultwebsrverrhandler.err500') != '') - ) { + private function _createStandardErrorHandler() + { + if (Settings::Get('defaultwebsrverrhandler.enabled') == '1' && (Settings::Get('defaultwebsrverrhandler.err401') != '' || Settings::Get('defaultwebsrverrhandler.err403') != '' || Settings::Get('defaultwebsrverrhandler.err404') != '' || Settings::Get('defaultwebsrverrhandler.err500') != '')) { $vhosts_folder = ''; if (is_dir(Settings::Get('system.apacheconf_vhost'))) { $vhosts_folder = makeCorrectDir(Settings::Get('system.apacheconf_vhost')); @@ -123,17 +125,22 @@ class apache extends HttpConfigBase { $vhosts_filename = makeCorrectFile($vhosts_folder . '/05_froxlor_default_errorhandler.conf'); - if (!isset($this->virtualhosts_data[$vhosts_filename])) { + if (! isset($this->virtualhosts_data[$vhosts_filename])) { $this->virtualhosts_data[$vhosts_filename] = ''; } - $statusCodes = array('401', '403', '404', '500'); + $statusCodes = array( + '401', + '403', + '404', + '500' + ); foreach ($statusCodes as $statusCode) { if (Settings::Get('defaultwebsrverrhandler.err' . $statusCode) != '') { $defhandler = Settings::Get('defaultwebsrverrhandler.err' . $statusCode); - if (!validateUrl($defhandler)) { - if (substr($defhandler, 0, 1) != '"' && substr($defhandler, -1, 1) != '"') { - $defhandler = '"'.makeCorrectFile($defhandler).'"'; + if (! validateUrl($defhandler)) { + if (substr($defhandler, 0, 1) != '"' && substr($defhandler, - 1, 1) != '"') { + $defhandler = '"' . makeCorrectFile($defhandler) . '"'; } } $this->virtualhosts_data[$vhosts_filename] .= 'ErrorDocument ' . $statusCode . ' ' . $defhandler . "\n"; @@ -142,8 +149,8 @@ class apache extends HttpConfigBase { } } - - public function createIpPort() { + public function createIpPort() + { $result_ipsandports_stmt = Database::query("SELECT * FROM `" . TABLE_PANEL_IPSANDPORTS . "` ORDER BY `ip` ASC, `port` ASC"); while ($row_ipsandports = $result_ipsandports_stmt->fetch(PDO::FETCH_ASSOC)) { @@ -156,7 +163,7 @@ class apache extends HttpConfigBase { $this->logger->logAction(CRON_ACTION, LOG_INFO, 'apache::createIpPort: creating ip/port settings for ' . $ipport); $vhosts_filename = makeCorrectFile(Settings::Get('system.apacheconf_vhost') . '/10_froxlor_ipandport_' . trim(str_replace(':', '.', $row_ipsandports['ip']), '.') . '.' . $row_ipsandports['port'] . '.conf'); - if (!isset($this->virtualhosts_data[$vhosts_filename])) { + if (! isset($this->virtualhosts_data[$vhosts_filename])) { $this->virtualhosts_data[$vhosts_filename] = ''; } @@ -170,7 +177,7 @@ class apache extends HttpConfigBase { if (Settings::Get('system.apache24') == '1') { $this->logger->logAction(CRON_ACTION, LOG_NOTICE, $ipport . ' :: namevirtualhost-statement no longer needed for apache-2.4'); } else { - $this->virtualhosts_data[$vhosts_filename].= 'NameVirtualHost ' . $ipport . "\n"; + $this->virtualhosts_data[$vhosts_filename] .= 'NameVirtualHost ' . $ipport . "\n"; $this->logger->logAction(CRON_ACTION, LOG_DEBUG, $ipport . ' :: inserted namevirtualhost-statement'); } } @@ -178,171 +185,180 @@ class apache extends HttpConfigBase { if ($row_ipsandports['vhostcontainer'] == '1') { $this->virtualhosts_data[$vhosts_filename] .= '' . "\n"; - if ($row_ipsandports['docroot'] == '') { - /** - * add 'real'-vhost content here, like doc-root :) - */ - if (Settings::Get('system.froxlordirectlyviahostname')) { - $mypath = makeCorrectDir(dirname(dirname(dirname(__FILE__)))); - } else { - $mypath = makeCorrectDir(dirname(dirname(dirname(dirname(__FILE__))))); - } - } else { - // user-defined docroot, #417 - $mypath = makeCorrectDir($row_ipsandports['docroot']); - } + $mypath = $this->getMyPath($row_ipsandports); - $this->virtualhosts_data[$vhosts_filename] .= 'DocumentRoot "'.$mypath.'"'."\n"; + $this->virtualhosts_data[$vhosts_filename] .= 'DocumentRoot "' . $mypath . '"' . "\n"; if ($row_ipsandports['vhostcontainer_servername_statement'] == '1') { $this->virtualhosts_data[$vhosts_filename] .= ' ServerName ' . Settings::Get('system.hostname') . "\n"; } - // create fcgid -Part (starter is created in apache_fcgid) - if (Settings::Get('system.mod_fcgid_ownvhost') == '1' - && Settings::Get('system.mod_fcgid') == '1' - ) { - $configdir = makeCorrectDir(Settings::Get('system.mod_fcgid_configdir') . '/froxlor.panel/' . Settings::Get('system.hostname')); - $this->virtualhosts_data[$vhosts_filename] .= ' FcgidIdleTimeout ' . Settings::Get('system.mod_fcgid_idle_timeout') . "\n"; - if ((int)Settings::Get('system.mod_fcgid_wrapper') == 0) { - $this->virtualhosts_data[$vhosts_filename] .= ' SuexecUserGroup "' . Settings::Get('system.mod_fcgid_httpuser') . '" "' . Settings::Get('system.mod_fcgid_httpgroup') . '"' . "\n"; - $this->virtualhosts_data[$vhosts_filename] .= ' ScriptAlias /php/ ' . $configdir . "\n"; + $is_redirect = false; + // check for SSL redirect + if ($row_ipsandports['ssl'] == '0' && Settings::Get('system.le_froxlor_redirect') == '1') { + $is_redirect = true; + // check whether froxlor uses Let's Encrypt and not cert is being generated yet + // or a renew is ongoing - disable redirect + if (System::Get('system.le_froxlor_enabled') && ($this->froxlorVhostHasLetsEncryptCert() == false || $this->froxlorVhostLetsEncryptNeedsRenew())) { + $this->virtualhosts_data[$vhosts_filename] .= '# temp. disabled ssl-redirect due to Let\'s Encrypt certificate generation.' . PHP_EOL; + $is_redirect = false; } else { + $_sslport = $this->checkAlternativeSslPort(); + + $mypath = 'https://' . Settings::Get('system.hostname') . $_sslport . '/'; + $code = '301'; + $modrew_red = ' [R=' . $code . ';L,NE]'; + + // redirect everything, not only root-directory, #541 + $this->virtualhosts_data[$vhosts_filename] .= ' ' . "\n"; + $this->virtualhosts_data[$vhosts_filename] .= ' RewriteEngine On' . "\n"; + $this->virtualhosts_data[$vhosts_filename] .= ' RewriteCond %{HTTPS} off' . "\n"; + if (System::Get('system.le_froxlor_enabled') == '1') { + $this->virtualhosts_data[$vhosts_filename] .= ' RewriteCond %{REQUEST_URI} !^/\.well-known/acme-challenge' . "\n"; + } + $this->virtualhosts_data[$vhosts_filename] .= ' RewriteRule ^/(.*) ' . $mypath . '$1' . $modrew_red . "\n"; + $this->virtualhosts_data[$vhosts_filename] .= ' ' . "\n"; + $this->virtualhosts_data[$vhosts_filename] .= ' ' . "\n"; + $this->virtualhosts_data[$vhosts_filename] .= ' Redirect ' . $code . ' / ' . $mypath . "\n"; + $this->virtualhosts_data[$vhosts_filename] .= ' ' . "\n"; + } + } + + if (!$is_redirect) { + // create fcgid -Part (starter is created in apache_fcgid) + if (Settings::Get('system.mod_fcgid_ownvhost') == '1' && Settings::Get('system.mod_fcgid') == '1') { + $configdir = makeCorrectDir(Settings::Get('system.mod_fcgid_configdir') . '/froxlor.panel/' . Settings::Get('system.hostname')); + $this->virtualhosts_data[$vhosts_filename] .= ' FcgidIdleTimeout ' . Settings::Get('system.mod_fcgid_idle_timeout') . "\n"; + if ((int) Settings::Get('system.mod_fcgid_wrapper') == 0) { + $this->virtualhosts_data[$vhosts_filename] .= ' SuexecUserGroup "' . Settings::Get('system.mod_fcgid_httpuser') . '" "' . Settings::Get('system.mod_fcgid_httpgroup') . '"' . "\n"; + $this->virtualhosts_data[$vhosts_filename] .= ' ScriptAlias /php/ ' . $configdir . "\n"; + } else { + $domain = array( + 'id' => 'none', + 'domain' => Settings::Get('system.hostname'), + 'adminid' => 1, /* first admin-user (superadmin) */ + 'mod_fcgid_starter' => - 1, + 'mod_fcgid_maxrequests' => - 1, + 'guid' => Settings::Get('phpfpm.vhost_httpuser'), + 'openbasedir' => 0, + 'email' => Settings::Get('panel.adminmail'), + 'loginname' => 'froxlor.panel', + 'documentroot' => $mypath + ); + $php = new phpinterface($domain); + $phpconfig = $php->getPhpConfig(Settings::Get('system.mod_fcgid_defaultini_ownvhost')); + + $starter_filename = makeCorrectFile($configdir . '/php-fcgi-starter'); + $this->virtualhosts_data[$vhosts_filename] .= ' SuexecUserGroup "' . Settings::Get('system.mod_fcgid_httpuser') . '" "' . Settings::Get('system.mod_fcgid_httpgroup') . '"' . "\n"; + $this->virtualhosts_data[$vhosts_filename] .= ' ' . "\n"; + $file_extensions = explode(' ', $phpconfig['file_extensions']); + $this->virtualhosts_data[$vhosts_filename] .= ' ' . "\n"; + $this->virtualhosts_data[$vhosts_filename] .= ' SetHandler fcgid-script' . "\n"; + foreach ($file_extensions as $file_extension) { + $this->virtualhosts_data[$vhosts_filename] .= ' FcgidWrapper ' . $starter_filename . ' .' . $file_extension . "\n"; + } + $this->virtualhosts_data[$vhosts_filename] .= ' Options +ExecCGI' . "\n"; + $this->virtualhosts_data[$vhosts_filename] .= ' ' . "\n"; + // >=apache-2.4 enabled? + if (Settings::Get('system.apache24') == '1') { + $mypath_dir = new frxDirectory($mypath); + // only create the require all granted if there is not active directory-protection + // for this path, as this would be the first require and therefore grant all access + if ($mypath_dir->isUserProtected() == false) { + $this->virtualhosts_data[$vhosts_filename] .= ' Require all granted' . "\n"; + $this->virtualhosts_data[$vhosts_filename] .= ' AllowOverride All' . "\n"; + } + } else { + $this->virtualhosts_data[$vhosts_filename] .= ' Order allow,deny' . "\n"; + $this->virtualhosts_data[$vhosts_filename] .= ' allow from all' . "\n"; + } + $this->virtualhosts_data[$vhosts_filename] .= ' ' . "\n"; + } + } // create php-fpm -Part (config is created in apache_fcgid) + elseif (Settings::Get('phpfpm.enabled') == '1') { $domain = array( 'id' => 'none', 'domain' => Settings::Get('system.hostname'), 'adminid' => 1, /* first admin-user (superadmin) */ - 'mod_fcgid_starter' => -1, - 'mod_fcgid_maxrequests' => -1, + 'mod_fcgid_starter' => - 1, + 'mod_fcgid_maxrequests' => - 1, 'guid' => Settings::Get('phpfpm.vhost_httpuser'), 'openbasedir' => 0, 'email' => Settings::Get('panel.adminmail'), 'loginname' => 'froxlor.panel', 'documentroot' => $mypath ); + $php = new phpinterface($domain); - $phpconfig = $php->getPhpConfig(Settings::Get('system.mod_fcgid_defaultini_ownvhost')); - - $starter_filename = makeCorrectFile($configdir . '/php-fcgi-starter'); - $this->virtualhosts_data[$vhosts_filename] .= ' SuexecUserGroup "' . Settings::Get('system.mod_fcgid_httpuser') . '" "' . Settings::Get('system.mod_fcgid_httpgroup') . '"' . "\n"; - $this->virtualhosts_data[$vhosts_filename].= ' ' . "\n"; - $file_extensions = explode(' ', $phpconfig['file_extensions']); - $this->virtualhosts_data[$vhosts_filename].= ' ' . "\n"; - $this->virtualhosts_data[$vhosts_filename].= ' SetHandler fcgid-script' . "\n"; - foreach ($file_extensions as $file_extension) { - $this->virtualhosts_data[$vhosts_filename].= ' FcgidWrapper ' . $starter_filename . ' .' . $file_extension . "\n"; + $phpconfig = $php->getPhpConfig(Settings::Get('phpfpm.vhost_defaultini')); + $srvName = substr(md5($ipport), 0, 4) . '.fpm.external'; + if ($row_ipsandports['ssl']) { + $srvName = substr(md5($ipport), 0, 4) . '.ssl-fpm.external'; } - $this->virtualhosts_data[$vhosts_filename].= ' Options +ExecCGI' . "\n"; - $this->virtualhosts_data[$vhosts_filename].= ' ' . "\n"; - // >=apache-2.4 enabled? - if (Settings::Get('system.apache24') == '1') { - $mypath_dir = new frxDirectory($mypath); - // only create the require all granted if there is not active directory-protection - // for this path, as this would be the first require and therefore grant all access - if ($mypath_dir->isUserProtected() == false) { - $this->virtualhosts_data[$vhosts_filename].= ' Require all granted' . "\n"; - $this->virtualhosts_data[$vhosts_filename].= ' AllowOverride All' . "\n"; - } + + // mod_proxy stuff for apache-2.4 + if (Settings::Get('system.apache24') == '1' && Settings::Get('phpfpm.use_mod_proxy') == '1') { + $this->virtualhosts_data[$vhosts_filename] .= ' ' . "\n"; + $this->virtualhosts_data[$vhosts_filename] .= ' SetHandler proxy:unix:' . $php->getInterface()->getSocketFile() . '|fcgi://localhost' . "\n"; + $this->virtualhosts_data[$vhosts_filename] .= ' ' . "\n"; } else { - $this->virtualhosts_data[$vhosts_filename].= ' Order allow,deny' . "\n"; - $this->virtualhosts_data[$vhosts_filename].= ' allow from all' . "\n"; + $this->virtualhosts_data[$vhosts_filename] .= ' FastCgiExternalServer ' . $php->getInterface()->getAliasConfigDir() . $srvName . ' -socket ' . $php->getInterface()->getSocketFile() . ' -idle-timeout ' . Settings::Get('phpfpm.idle_timeout') . "\n"; + $this->virtualhosts_data[$vhosts_filename] .= ' ' . "\n"; + $file_extensions = explode(' ', $phpconfig['file_extensions']); + $this->virtualhosts_data[$vhosts_filename] .= ' ' . "\n"; + $this->virtualhosts_data[$vhosts_filename] .= ' AddHandler php5-fastcgi .php' . "\n"; + $this->virtualhosts_data[$vhosts_filename] .= ' Action php5-fastcgi /fastcgiphp' . "\n"; + $this->virtualhosts_data[$vhosts_filename] .= ' Options +ExecCGI' . "\n"; + $this->virtualhosts_data[$vhosts_filename] .= ' ' . "\n"; + // >=apache-2.4 enabled? + if (Settings::Get('system.apache24') == '1') { + $mypath_dir = new frxDirectory($mypath); + // only create the require all granted if there is not active directory-protection + // for this path, as this would be the first require and therefore grant all access + if ($mypath_dir->isUserProtected() == false) { + $this->virtualhosts_data[$vhosts_filename] .= ' Require all granted' . "\n"; + $this->virtualhosts_data[$vhosts_filename] .= ' AllowOverride All' . "\n"; + } + } else { + $this->virtualhosts_data[$vhosts_filename] .= ' Order allow,deny' . "\n"; + $this->virtualhosts_data[$vhosts_filename] .= ' allow from all' . "\n"; + } + $this->virtualhosts_data[$vhosts_filename] .= ' ' . "\n"; + $this->virtualhosts_data[$vhosts_filename] .= ' Alias /fastcgiphp ' . $php->getInterface()->getAliasConfigDir() . $srvName . "\n"; } - $this->virtualhosts_data[$vhosts_filename].= ' ' . "\n"; - } - } - // create php-fpm -Part (config is created in apache_fcgid) - elseif (Settings::Get('phpfpm.enabled') == '1') { - $domain = array( - 'id' => 'none', - 'domain' => Settings::Get('system.hostname'), - 'adminid' => 1, /* first admin-user (superadmin) */ - 'mod_fcgid_starter' => -1, - 'mod_fcgid_maxrequests' => -1, - 'guid' => Settings::Get('phpfpm.vhost_httpuser'), - 'openbasedir' => 0, - 'email' => Settings::Get('panel.adminmail'), - 'loginname' => 'froxlor.panel', - 'documentroot' => $mypath, - ); - - $php = new phpinterface($domain); - $phpconfig = $php->getPhpConfig(Settings::Get('phpfpm.vhost_defaultini')); - $srvName = substr(md5($ipport),0,4).'.fpm.external'; - if ($row_ipsandports['ssl']) { - $srvName = substr(md5($ipport),0,4).'.ssl-fpm.external'; - } - - // mod_proxy stuff for apache-2.4 - if (Settings::Get('system.apache24') == '1' - && Settings::Get('phpfpm.use_mod_proxy') == '1' - ) { - $this->virtualhosts_data[$vhosts_filename] .= ' '. "\n"; - $this->virtualhosts_data[$vhosts_filename] .= ' SetHandler proxy:unix:' . $php->getInterface()->getSocketFile() . '|fcgi://localhost'. "\n"; - $this->virtualhosts_data[$vhosts_filename] .= ' ' . "\n"; - } else { - $this->virtualhosts_data[$vhosts_filename] .= ' FastCgiExternalServer ' . $php->getInterface()->getAliasConfigDir() . $srvName .' -socket ' . $php->getInterface()->getSocketFile() . ' -idle-timeout ' . Settings::Get('phpfpm.idle_timeout') . "\n"; - $this->virtualhosts_data[$vhosts_filename] .= ' ' . "\n"; - $file_extensions = explode(' ', $phpconfig['file_extensions']); - $this->virtualhosts_data[$vhosts_filename] .= ' ' . "\n"; - $this->virtualhosts_data[$vhosts_filename] .= ' AddHandler php5-fastcgi .php'. "\n"; - $this->virtualhosts_data[$vhosts_filename] .= ' Action php5-fastcgi /fastcgiphp' . "\n"; - $this->virtualhosts_data[$vhosts_filename].= ' Options +ExecCGI' . "\n"; - $this->virtualhosts_data[$vhosts_filename].= ' ' . "\n"; - // >=apache-2.4 enabled? - if (Settings::Get('system.apache24') == '1') { - $mypath_dir = new frxDirectory($mypath); - // only create the require all granted if there is not active directory-protection - // for this path, as this would be the first require and therefore grant all access - if ($mypath_dir->isUserProtected() == false) { - $this->virtualhosts_data[$vhosts_filename] .= ' Require all granted' . "\n"; - $this->virtualhosts_data[$vhosts_filename] .= ' AllowOverride All' . "\n"; - } - } else { - $this->virtualhosts_data[$vhosts_filename] .= ' Order allow,deny' . "\n"; - $this->virtualhosts_data[$vhosts_filename] .= ' allow from all' . "\n"; - } - $this->virtualhosts_data[$vhosts_filename] .= ' ' . "\n"; - $this->virtualhosts_data[$vhosts_filename] .= ' Alias /fastcgiphp ' . $php->getInterface()->getAliasConfigDir() . $srvName . "\n"; - } - } - else - { - // mod_php - $domain = array( - 'id' => 'none', - 'domain' => Settings::Get('system.hostname'), - 'adminid' => 1, /* first admin-user (superadmin) */ + // mod_php + $domain = array( + 'id' => 'none', + 'domain' => Settings::Get('system.hostname'), + 'adminid' => 1, /* first admin-user (superadmin) */ 'guid' => Settings::Get('system.httpuser'), - 'openbasedir' => 0, - 'email' => Settings::Get('panel.adminmail'), - 'loginname' => 'froxlor.panel', - 'documentroot' => $mypath - ); - } + 'openbasedir' => 0, + 'email' => Settings::Get('panel.adminmail'), + 'loginname' => 'froxlor.panel', + 'documentroot' => $mypath + ); + } + } // end of ssl-redirect check /** * dirprotection, see #72 - * @TODO deferred until 0.9.5, needs more testing - $this->virtualhosts_data[$vhosts_filename] .= "\t\n"; - $this->virtualhosts_data[$vhosts_filename] .= "\t\tAllow from all\n"; - $this->virtualhosts_data[$vhosts_filename] .= "\t\tOptions -Indexes\n"; - $this->virtualhosts_data[$vhosts_filename] .= "\t\n"; - - $this->virtualhosts_data[$vhosts_filename] .= "\t\n"; - $this->virtualhosts_data[$vhosts_filename] .= "\t\tOrder Deny,Allow\n"; - $this->virtualhosts_data[$vhosts_filename] .= "\t\tDeny from All\n"; - $this->virtualhosts_data[$vhosts_filename] .= "\t\n"; - * end of dirprotection + * + * @todo deferred until 0.9.5, needs more testing + * $this->virtualhosts_data[$vhosts_filename] .= "\t\n"; + * $this->virtualhosts_data[$vhosts_filename] .= "\t\tAllow from all\n"; + * $this->virtualhosts_data[$vhosts_filename] .= "\t\tOptions -Indexes\n"; + * $this->virtualhosts_data[$vhosts_filename] .= "\t\n"; + * + * $this->virtualhosts_data[$vhosts_filename] .= "\t\n"; + * $this->virtualhosts_data[$vhosts_filename] .= "\t\tOrder Deny,Allow\n"; + * $this->virtualhosts_data[$vhosts_filename] .= "\t\tDeny from All\n"; + * $this->virtualhosts_data[$vhosts_filename] .= "\t\n"; + * end of dirprotection */ if ($row_ipsandports['specialsettings'] != '') { - $this->virtualhosts_data[$vhosts_filename] .= $this->processSpecialConfigTemplate( - $row_ipsandports['specialsettings'], - $domain, - $row_ipsandports['ip'], - $row_ipsandports['port'], - $row_ipsandports['ssl'] == '1') . "\n"; + $this->virtualhosts_data[$vhosts_filename] .= $this->processSpecialConfigTemplate($row_ipsandports['specialsettings'], $domain, $row_ipsandports['ip'], $row_ipsandports['port'], $row_ipsandports['ssl'] == '1') . "\n"; } if ($row_ipsandports['ssl'] == '1' && Settings::Get('system.use_ssl') == '1') { @@ -369,7 +385,7 @@ class apache extends HttpConfigBase { 'adminid' => 1, /* first admin-user (superadmin) */ 'loginname' => 'froxlor.panel', 'documentroot' => $mypath, - 'parentdomainid' => 0, + 'parentdomainid' => 0 ); // override corresponding array values @@ -387,9 +403,9 @@ class apache extends HttpConfigBase { if ($domain['ssl_cert_file'] != '') { // check for existence, #1485 - if (!file_exists($domain['ssl_cert_file'])) { - $this->logger->logAction(CRON_ACTION, LOG_ERR, $ipport . ' :: certificate file "'.$domain['ssl_cert_file'].'" does not exist! Cannot create ssl-directives'); - echo $ipport . ' :: certificate file "'.$domain['ssl_cert_file'].'" does not exist! Cannot create SSL-directives'."\n"; + if (! file_exists($domain['ssl_cert_file'])) { + $this->logger->logAction(CRON_ACTION, LOG_ERR, $ipport . ' :: certificate file "' . $domain['ssl_cert_file'] . '" does not exist! Cannot create ssl-directives'); + echo $ipport . ' :: certificate file "' . $domain['ssl_cert_file'] . '" does not exist! Cannot create SSL-directives' . "\n"; } else { $this->virtualhosts_data[$vhosts_filename] .= ' SSLEngine On' . "\n"; @@ -402,9 +418,9 @@ class apache extends HttpConfigBase { if ($domain['ssl_key_file'] != '') { // check for existence, #1485 - if (!file_exists($domain['ssl_key_file'])) { - $this->logger->logAction(CRON_ACTION, LOG_ERR, $ipport . ' :: certificate key file "'.$domain['ssl_key_file'].'" does not exist! Cannot create ssl-directives'); - echo $ipport . ' :: certificate key file "'.$domain['ssl_key_file'].'" does not exist! SSL-directives might not be working'."\n"; + if (! file_exists($domain['ssl_key_file'])) { + $this->logger->logAction(CRON_ACTION, LOG_ERR, $ipport . ' :: certificate key file "' . $domain['ssl_key_file'] . '" does not exist! Cannot create ssl-directives'); + echo $ipport . ' :: certificate key file "' . $domain['ssl_key_file'] . '" does not exist! SSL-directives might not be working' . "\n"; } else { $this->virtualhosts_data[$vhosts_filename] .= ' SSLCertificateKeyFile ' . makeCorrectFile($domain['ssl_key_file']) . "\n"; } @@ -412,9 +428,9 @@ class apache extends HttpConfigBase { if ($domain['ssl_ca_file'] != '') { // check for existence, #1485 - if (!file_exists($domain['ssl_ca_file'])) { - $this->logger->logAction(CRON_ACTION, LOG_ERR, $ipport . ' :: certificate CA file "'.$domain['ssl_ca_file'].'" does not exist! Cannot create ssl-directives'); - echo $ipport . ' :: certificate CA file "'.$domain['ssl_ca_file'].'" does not exist! SSL-directives might not be working'."\n"; + if (! file_exists($domain['ssl_ca_file'])) { + $this->logger->logAction(CRON_ACTION, LOG_ERR, $ipport . ' :: certificate CA file "' . $domain['ssl_ca_file'] . '" does not exist! Cannot create ssl-directives'); + echo $ipport . ' :: certificate CA file "' . $domain['ssl_ca_file'] . '" does not exist! SSL-directives might not be working' . "\n"; } else { $this->virtualhosts_data[$vhosts_filename] .= ' SSLCACertificateFile ' . makeCorrectFile($domain['ssl_ca_file']) . "\n"; } @@ -423,9 +439,9 @@ class apache extends HttpConfigBase { // #418 if ($domain['ssl_cert_chainfile'] != '') { // check for existence, #1485 - if (!file_exists($domain['ssl_cert_chainfile'])) { - $this->logger->logAction(CRON_ACTION, LOG_ERR, $ipport . ' :: certificate chain file "'.$domain['ssl_cert_chainfile'].'" does not exist! Cannot create ssl-directives'); - echo $ipport . ' :: certificate chain file "'.$domain['ssl_cert_chainfile'].'" does not exist! SSL-directives might not be working'."\n"; + if (! file_exists($domain['ssl_cert_chainfile'])) { + $this->logger->logAction(CRON_ACTION, LOG_ERR, $ipport . ' :: certificate chain file "' . $domain['ssl_cert_chainfile'] . '" does not exist! Cannot create ssl-directives'); + echo $ipport . ' :: certificate chain file "' . $domain['ssl_cert_chainfile'] . '" does not exist! SSL-directives might not be working' . "\n"; } else { $this->virtualhosts_data[$vhosts_filename] .= ' SSLCertificateChainFile ' . makeCorrectFile($domain['ssl_cert_chainfile']) . "\n"; } @@ -451,7 +467,6 @@ class apache extends HttpConfigBase { $this->_createStandardErrorHandler(); } - /** * We put together the needed php options in the virtualhost entries * @@ -460,29 +475,26 @@ class apache extends HttpConfigBase { * * @return string */ - protected function composePhpOptions($domain, $ssl_vhost = false) { + protected function composePhpOptions($domain, $ssl_vhost = false) + { $php_options_text = ''; if ($domain['phpenabled'] == '1') { // This vHost has PHP enabled and we are using the regular mod_php - if ($domain['openbasedir'] == '1') - { - if ($domain['openbasedir_path'] == '1' || strstr($domain['documentroot'], ":") !== false) - { - $_phpappendopenbasedir = appendOpenBasedirPath($domain['customerroot'], true); - } - else - { - $_phpappendopenbasedir = appendOpenBasedirPath($domain['documentroot'], true); - } + if ($domain['openbasedir'] == '1') { + if ($domain['openbasedir_path'] == '1' || strstr($domain['documentroot'], ":") !== false) { + $_phpappendopenbasedir = appendOpenBasedirPath($domain['customerroot'], true); + } else { + $_phpappendopenbasedir = appendOpenBasedirPath($domain['documentroot'], true); + } $_custom_openbasedir = explode(':', Settings::Get('system.phpappendopenbasedir')); foreach ($_custom_openbasedir as $cobd) { $_phpappendopenbasedir .= appendOpenBasedirPath($cobd); } - $php_options_text .= ' php_admin_value open_basedir "' . $_phpappendopenbasedir . '"'."\n"; + $php_options_text .= ' php_admin_value open_basedir "' . $_phpappendopenbasedir . '"' . "\n"; } } else { $php_options_text .= ' # PHP is disabled for this vHost' . "\n"; @@ -494,22 +506,22 @@ class apache extends HttpConfigBase { * why is this here? Because it only works with mod_php */ if (Settings::get('system.apacheitksupport') == 1) { - $php_options_text .= ' ' . "\n"; - $php_options_text .= ' AssignUserID '. $domain['loginname'] . ' ' . $domain['loginname'] . "\n"; - $php_options_text .= ' ' . "\n"; + $php_options_text .= ' ' . "\n"; + $php_options_text .= ' AssignUserID ' . $domain['loginname'] . ' ' . $domain['loginname'] . "\n"; + $php_options_text .= ' ' . "\n"; } return $php_options_text; } - - public function createOwnVhostStarter() {} - + public function createOwnVhostStarter() + {} /** * We collect all servernames and Aliases */ - protected function getServerNames($domain) { + protected function getServerNames($domain) + { $servernames_text = ' ServerName ' . $domain['domain'] . "\n"; $server_alias = ''; @@ -528,7 +540,9 @@ class apache extends HttpConfigBase { FROM `" . TABLE_PANEL_DOMAINS . "` WHERE `aliasdomain`= :domainid "); - Database::pexecute($alias_domains_stmt, array('domainid' => $domain['id'])); + Database::pexecute($alias_domains_stmt, array( + 'domainid' => $domain['id'] + )); while (($alias_domain = $alias_domains_stmt->fetch(PDO::FETCH_ASSOC)) !== false) { $server_alias = ' ServerAlias ' . $alias_domain['domain']; @@ -548,18 +562,16 @@ class apache extends HttpConfigBase { return $servernames_text; } - /** * Let's get the webroot */ - protected function getWebroot($domain) { + protected function getWebroot($domain) + { $webroot_text = ''; $domain['customerroot'] = makeCorrectDir($domain['customerroot']); $domain['documentroot'] = makeCorrectDir($domain['documentroot']); - 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 .= ' DocumentRoot "' . makeCorrectDir(Settings::Get('system.deactivateddocroot')) . "\"\n"; $this->_deactivated = true; @@ -571,11 +583,11 @@ class apache extends HttpConfigBase { return $webroot_text; } - /** * Lets set the text part for the stats software */ - protected function getStats($domain) { + protected function getStats($domain) + { $stats_text = ''; if ($domain['speciallogfile'] == '1') { @@ -589,31 +601,29 @@ class apache extends HttpConfigBase { } else { if ($domain['customerroot'] != $domain['documentroot']) { if (Settings::Get('system.awstats_enabled') == '1') { - $stats_text.= ' Alias /awstats "' . makeCorrectFile($domain['customerroot'] . '/awstats/' . $domain['domain']) . '"' . "\n"; - $stats_text.= ' Alias /awstats-icon "' . makeCorrectDir(Settings::Get('system.awstats_icons')) . '"' . "\n"; + $stats_text .= ' Alias /awstats "' . makeCorrectFile($domain['customerroot'] . '/awstats/' . $domain['domain']) . '"' . "\n"; + $stats_text .= ' Alias /awstats-icon "' . makeCorrectDir(Settings::Get('system.awstats_icons')) . '"' . "\n"; } else { - $stats_text.= ' Alias /webalizer "' . makeCorrectFile($domain['customerroot'] . '/webalizer') . '"' . "\n"; + $stats_text .= ' Alias /webalizer "' . makeCorrectFile($domain['customerroot'] . '/webalizer') . '"' . "\n"; } - } - // if the docroots are equal, we still have to set an alias for awstats + } // if the docroots are equal, we still have to set an alias for awstats // because the stats are in /awstats/[domain], not just /awstats/ // also, the awstats-icons are someplace else too! // -> webalizer does not need this! elseif (Settings::Get('system.awstats_enabled') == '1') { - $stats_text.= ' Alias /awstats "' . makeCorrectFile($domain['documentroot'] . '/awstats/' . $domain['domain']) . '"' . "\n"; - $stats_text.= ' Alias /awstats-icon "' . makeCorrectDir(Settings::Get('system.awstats_icons')) . '"' . "\n"; + $stats_text .= ' Alias /awstats "' . makeCorrectFile($domain['documentroot'] . '/awstats/' . $domain['domain']) . '"' . "\n"; + $stats_text .= ' Alias /awstats-icon "' . makeCorrectDir(Settings::Get('system.awstats_icons')) . '"' . "\n"; } } return $stats_text; } - /** * Lets set the logfiles */ - protected function getLogfiles($domain) { - + protected function getLogfiles($domain) + { $logfiles_text = ''; if ($domain['speciallogfile'] == '1') { @@ -640,10 +650,10 @@ class apache extends HttpConfigBase { chgrp($access_log, Settings::Get('system.httpgroup')); $logfiles_text .= ' ErrorLog "' . $error_log . "\"\n"; - $logfiles_text .= ' CustomLog "' . $access_log .'" combined' . "\n"; + $logfiles_text .= ' CustomLog "' . $access_log . '" combined' . "\n"; if (Settings::Get('system.awstats_enabled') == '1') { - if ((int)$domain['parentdomainid'] == 0) { + if ((int) $domain['parentdomainid'] == 0) { // prepare the aliases and subdomains for stats config files $server_alias = ''; $alias_domains_stmt = Database::prepare(" @@ -651,7 +661,9 @@ class apache extends HttpConfigBase { FROM `" . TABLE_PANEL_DOMAINS . "` WHERE `aliasdomain` = :domainid OR `parentdomainid` = :domainid "); - Database::pexecute($alias_domains_stmt, array('domainid' => $domain['id'])); + Database::pexecute($alias_domains_stmt, array( + 'domainid' => $domain['id'] + )); while (($alias_domain = $alias_domains_stmt->fetch(PDO::FETCH_ASSOC)) !== false) { @@ -682,31 +694,24 @@ class apache extends HttpConfigBase { return $logfiles_text; } - /** * Get the filename for the virtualhost */ - protected function getVhostFilename($domain, $ssl_vhost = false) { - if ((int)$domain['parentdomainid'] == 0 - && isCustomerStdSubdomain((int)$domain['id']) == false - && ((int)$domain['ismainbutsubto'] == 0 - || domainMainToSubExists($domain['ismainbutsubto']) == false) - ) { + protected function getVhostFilename($domain, $ssl_vhost = false) + { + if ((int) $domain['parentdomainid'] == 0 && isCustomerStdSubdomain((int) $domain['id']) == false && ((int) $domain['ismainbutsubto'] == 0 || domainMainToSubExists($domain['ismainbutsubto']) == false)) { $vhost_no = '35'; - } elseif ((int)$domain['parentdomainid'] == 0 - && isCustomerStdSubdomain((int)$domain['id']) == false - && (int)$domain['ismainbutsubto'] > 0 - ) { + } elseif ((int) $domain['parentdomainid'] == 0 && isCustomerStdSubdomain((int) $domain['id']) == false && (int) $domain['ismainbutsubto'] > 0) { $vhost_no = '30'; } else { // number of dots in a domain specifies it's position (and depth of subdomain) starting at 29 going downwards on higher depth - $vhost_no = (string)(30 - substr_count($domain['domain'], ".") + 1); + $vhost_no = (string) (30 - substr_count($domain['domain'], ".") + 1); } if ($ssl_vhost === true) { - $vhost_filename = makeCorrectFile(Settings::Get('system.apacheconf_vhost') . '/'.$vhost_no.'_froxlor_ssl_vhost_' . $domain['domain'] . '.conf'); + $vhost_filename = makeCorrectFile(Settings::Get('system.apacheconf_vhost') . '/' . $vhost_no . '_froxlor_ssl_vhost_' . $domain['domain'] . '.conf'); } else { - $vhost_filename = makeCorrectFile(Settings::Get('system.apacheconf_vhost') . '/'.$vhost_no.'_froxlor_normal_vhost_' . $domain['domain'] . '.conf'); + $vhost_filename = makeCorrectFile(Settings::Get('system.apacheconf_vhost') . '/' . $vhost_no . '_froxlor_normal_vhost_' . $domain['domain'] . '.conf'); } return $vhost_filename; @@ -715,20 +720,16 @@ class apache extends HttpConfigBase { /** * We compose the virtualhost entry for one domain */ - protected function getVhostContent($domain, $ssl_vhost = false) { - if ($ssl_vhost === true - && ($domain['ssl_redirect'] != '1' - && $domain['ssl'] != '1') - ) { + protected function getVhostContent($domain, $ssl_vhost = false) + { + if ($ssl_vhost === true && ($domain['ssl_redirect'] != '1' && $domain['ssl'] != '1')) { return ''; } - $query = "SELECT * FROM `".TABLE_PANEL_IPSANDPORTS."` `i`, `".TABLE_DOMAINTOIP."` `dip` + $query = "SELECT * FROM `" . TABLE_PANEL_IPSANDPORTS . "` `i`, `" . TABLE_DOMAINTOIP . "` `dip` WHERE dip.id_domain = :domainid AND i.id = dip.id_ipandports "; - if ($ssl_vhost === true - && ($domain['ssl'] == '1' || $domain['ssl_redirect'] == '1') - ) { + if ($ssl_vhost === true && ($domain['ssl'] == '1' || $domain['ssl_redirect'] == '1')) { // by ordering by cert-file the row with filled out SSL-Fields will be shown last, thus it is enough to fill out 1 set of SSL-Fields $query .= "AND i.ssl = '1' ORDER BY i.ssl_cert_file ASC;"; } else { @@ -737,7 +738,9 @@ class apache extends HttpConfigBase { $vhost_content = ''; $result_stmt = Database::prepare($query); - Database::pexecute($result_stmt, array('domainid' => $domain['id'])); + Database::pexecute($result_stmt, array( + 'domainid' => $domain['id'] + )); $ipportlist = ''; $_vhost_content = ''; @@ -760,29 +763,21 @@ class apache extends HttpConfigBase { } if (filter_var($domain['ip'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { - $ipport = '['.$domain['ip'].']:'.$domain['port']. ' '; + $ipport = '[' . $domain['ip'] . ']:' . $domain['port'] . ' '; } else { - $ipport = $domain['ip'].':'.$domain['port'].' '; + $ipport = $domain['ip'] . ':' . $domain['port'] . ' '; } if ($ipandport['default_vhostconf_domain'] != '') { - $_vhost_content .= $this->processSpecialConfigTemplate( - $ipandport['default_vhostconf_domain'], - $domain, - $domain['ip'], - $domain['port'], - $ssl_vhost) . "\n"; + $_vhost_content .= $this->processSpecialConfigTemplate($ipandport['default_vhostconf_domain'], $domain, $domain['ip'], $domain['port'], $ssl_vhost) . "\n"; } $ipportlist .= $ipport; } $vhost_content .= '' . "\n"; - $vhost_content.= $this->getServerNames($domain); + $vhost_content .= $this->getServerNames($domain); - if (($ssl_vhost == false - && $domain['ssl'] == '1' - && $domain['ssl_redirect'] == '1') - ) { + if (($ssl_vhost == false && $domain['ssl'] == '1' && $domain['ssl_redirect'] == '1')) { // We must not check if our port differs from port 443, // but if there is a destination-port != 443 $_sslport = ''; @@ -790,25 +785,24 @@ class apache extends HttpConfigBase { // ordered by ssl-certificate (if any) so that the ip/port combo // with certificate is used $ssldestport_stmt = Database::prepare(" - SELECT `ip`.`port` FROM ".TABLE_PANEL_IPSANDPORTS." `ip` - LEFT JOIN `".TABLE_DOMAINTOIP."` `dip` ON (`ip`.`id` = `dip`.`id_ipandports`) + SELECT `ip`.`port` FROM " . TABLE_PANEL_IPSANDPORTS . " `ip` + LEFT JOIN `" . TABLE_DOMAINTOIP . "` `dip` ON (`ip`.`id` = `dip`.`id_ipandports`) WHERE `dip`.`id_domain` = :domainid AND `ip`.`ssl` = '1' AND `ip`.`port` != 443 ORDER BY `ip`.`ssl_cert_file` DESC, `ip`.`port` LIMIT 1; "); - $ssldestport = Database::pexecute_first($ssldestport_stmt, array('domainid' => $domain['id'])); + $ssldestport = Database::pexecute_first($ssldestport_stmt, array( + 'domainid' => $domain['id'] + )); if ($ssldestport['port'] != '') { - $_sslport = ":".$ssldestport['port']; + $_sslport = ":" . $ssldestport['port']; } $domain['documentroot'] = 'https://' . $domain['domain'] . $_sslport . '/'; } - if ($ssl_vhost === true - && $domain['ssl'] == '1' - && Settings::Get('system.use_ssl') == '1' - ) { + if ($ssl_vhost === true && $domain['ssl'] == '1' && Settings::Get('system.use_ssl') == '1') { if ($domain['ssl_cert_file'] == '') { $domain['ssl_cert_file'] = Settings::Get('system.ssl_cert_file'); } @@ -876,41 +870,35 @@ class apache extends HttpConfigBase { $code = getDomainRedirectCode($domain['id']); $modrew_red = ''; if ($code != '') { - $modrew_red = ' [R='. $code . ';L,NE]'; + $modrew_red = ' [R=' . $code . ';L,NE]'; } // redirect everything, not only root-directory, #541 - $vhost_content .= ' '."\n"; + $vhost_content .= ' ' . "\n"; $vhost_content .= ' RewriteEngine On' . "\n"; - if (!$ssl_vhost) { + if (! $ssl_vhost) { $vhost_content .= ' RewriteCond %{HTTPS} off' . "\n"; } if ($domain['letsencrypt'] == '1') { $vhost_content .= ' RewriteCond %{REQUEST_URI} !^/\.well-known/acme-challenge' . "\n"; } - $vhost_content .= ' RewriteRule ^/(.*) '. $corrected_docroot.'$1' . $modrew_red . "\n"; + $vhost_content .= ' RewriteRule ^/(.*) ' . $corrected_docroot . '$1' . $modrew_red . "\n"; $vhost_content .= ' ' . "\n"; - $vhost_content .= ' '."\n"; - $vhost_content .= ' Redirect '.$code.' / ' . $this->idnaConvert->encode($domain['documentroot']) . "\n"; + $vhost_content .= ' ' . "\n"; + $vhost_content .= ' Redirect ' . $code . ' / ' . $this->idnaConvert->encode($domain['documentroot']) . "\n"; $vhost_content .= ' ' . "\n"; - } else { mkDirWithCorrectOwnership($domain['customerroot'], $domain['documentroot'], $domain['guid'], $domain['guid'], true, true); $vhost_content .= $this->getWebroot($domain); if ($this->_deactivated == false) { - $vhost_content .= $this->composePhpOptions($domain,$ssl_vhost); + $vhost_content .= $this->composePhpOptions($domain, $ssl_vhost); $vhost_content .= $this->getStats($domain); } $vhost_content .= $this->getLogfiles($domain); if ($domain['specialsettings'] != '') { - $vhost_content .= $this->processSpecialConfigTemplate( - $domain['specialsettings'], - $domain, - $domain['ip'], - $domain['port'], - $ssl_vhost) . "\n"; + $vhost_content .= $this->processSpecialConfigTemplate($domain['specialsettings'], $domain, $domain['ip'], $domain['port'], $ssl_vhost) . "\n"; } if ($_vhost_content != '') { @@ -918,12 +906,7 @@ class apache extends HttpConfigBase { } if (Settings::Get('system.default_vhostconf') != '') { - $vhost_content .= $this->processSpecialConfigTemplate( - Settings::Get('system.default_vhostconf'), - $domain, - $domain['ip'], - $domain['port'], - $ssl_vhost) . "\n"; + $vhost_content .= $this->processSpecialConfigTemplate(Settings::Get('system.default_vhostconf'), $domain, $domain['ip'], $domain['port'], $ssl_vhost) . "\n"; } } @@ -932,12 +915,11 @@ class apache extends HttpConfigBase { return $vhost_content; } - /** * We compose the virtualhost entries for the domains */ - public function createVirtualHosts() { - + public function createVirtualHosts() + { $domains = WebserverBase::getVhostsToCreate(); foreach ($domains as $domain) { @@ -947,11 +929,9 @@ class apache extends HttpConfigBase { // Apply header $this->virtualhosts_data[$vhosts_filename] = '# Domain ID: ' . $domain['id'] . ' - CustomerID: ' . $domain['customerid'] . ' - CustomerLogin: ' . $domain['loginname'] . "\n"; - if ($domain['deactivated'] != '1' - || Settings::Get('system.deactivateddocroot') != '' - ) { + if ($domain['deactivated'] != '1' || Settings::Get('system.deactivateddocroot') != '') { // Create vhost without ssl - $this->virtualhosts_data[$vhosts_filename].= $this->getVhostContent($domain, false); + $this->virtualhosts_data[$vhosts_filename] .= $this->getVhostContent($domain, false); if ($domain['ssl'] == '1' || $domain['ssl_redirect'] == '1') { // Adding ssl stuff if enabled @@ -965,11 +945,11 @@ class apache extends HttpConfigBase { } } - /** * We compose the diroption entries for the paths */ - public function createFileDirOptions() { + public function createFileDirOptions() + { $result_stmt = Database::query(" SELECT `htac`.*, `c`.`guid`, `c`.`documentroot` AS `customerroot` FROM `" . TABLE_PANEL_HTACCESS . "` `htac` @@ -979,10 +959,7 @@ class apache extends HttpConfigBase { $diroptions = array(); while ($row_diroptions = $result_stmt->fetch(PDO::FETCH_ASSOC)) { - if ($row_diroptions['customerid'] != 0 - && isset($row_diroptions['customerroot']) - && $row_diroptions['customerroot'] != '' - ) { + if ($row_diroptions['customerid'] != 0 && isset($row_diroptions['customerroot']) && $row_diroptions['customerroot'] != '') { $diroptions[$row_diroptions['path']] = $row_diroptions; $diroptions[$row_diroptions['path']]['htpasswds'] = array(); } @@ -996,11 +973,8 @@ class apache extends HttpConfigBase { "); while ($row_htpasswds = $result_stmt->fetch(PDO::FETCH_ASSOC)) { - if ($row_htpasswds['customerid'] != 0 - && isset($row_htpasswds['customerroot']) - && $row_htpasswds['customerroot'] != '' - ) { - if (!isset($diroptions[$row_htpasswds['path']]) || !is_array($diroptions[$row_htpasswds['path']])) { + if ($row_htpasswds['customerid'] != 0 && isset($row_htpasswds['customerroot']) && $row_htpasswds['customerroot'] != '') { + if (! isset($diroptions[$row_htpasswds['path']]) || ! is_array($diroptions[$row_htpasswds['path']])) { $diroptions[$row_htpasswds['path']] = array(); } @@ -1017,7 +991,7 @@ class apache extends HttpConfigBase { mkDirWithCorrectOwnership($row_diroptions['customerroot'], $row_diroptions['path'], $row_diroptions['guid'], $row_diroptions['guid']); $diroptions_filename = makeCorrectFile(Settings::Get('system.apacheconf_diroptions') . '/40_froxlor_diroption_' . md5($row_diroptions['path']) . '.conf'); - if (!isset($this->diroptions_data[$diroptions_filename])) { + if (! isset($this->diroptions_data[$diroptions_filename])) { $this->diroptions_data[$diroptions_filename] = ''; } @@ -1026,59 +1000,48 @@ class apache extends HttpConfigBase { $this->diroptions_data[$diroptions_filename] .= '' . "\n"; - if (isset($row_diroptions['options_indexes']) - && $row_diroptions['options_indexes'] == '1' - ) { + if (isset($row_diroptions['options_indexes']) && $row_diroptions['options_indexes'] == '1') { $this->diroptions_data[$diroptions_filename] .= ' Options +Indexes'; // add perl options if enabled - if ($cperlenabled - && isset($row_diroptions['options_cgi']) - && $row_diroptions['options_cgi'] == '1' - ) { - $this->diroptions_data[$diroptions_filename] .= ' +ExecCGI -MultiViews +SymLinksIfOwnerMatch +FollowSymLinks'."\n"; + if ($cperlenabled && isset($row_diroptions['options_cgi']) && $row_diroptions['options_cgi'] == '1') { + $this->diroptions_data[$diroptions_filename] .= ' +ExecCGI -MultiViews +SymLinksIfOwnerMatch +FollowSymLinks' . "\n"; } else { $this->diroptions_data[$diroptions_filename] .= "\n"; } $this->logger->logAction(CRON_ACTION, LOG_INFO, 'Setting Options +Indexes for ' . $row_diroptions['path']); } - if (isset($row_diroptions['options_indexes']) - && $row_diroptions['options_indexes'] == '0' - ) { + if (isset($row_diroptions['options_indexes']) && $row_diroptions['options_indexes'] == '0') { $this->diroptions_data[$diroptions_filename] .= ' Options -Indexes'; // add perl options if enabled - if ($cperlenabled - && isset($row_diroptions['options_cgi']) - && $row_diroptions['options_cgi'] == '1' - ) { - $this->diroptions_data[$diroptions_filename] .= ' +ExecCGI -MultiViews +SymLinksIfOwnerMatch +FollowSymLinks'."\n"; + if ($cperlenabled && isset($row_diroptions['options_cgi']) && $row_diroptions['options_cgi'] == '1') { + $this->diroptions_data[$diroptions_filename] .= ' +ExecCGI -MultiViews +SymLinksIfOwnerMatch +FollowSymLinks' . "\n"; } else { $this->diroptions_data[$diroptions_filename] .= "\n"; } $this->logger->logAction(CRON_ACTION, LOG_INFO, 'Setting Options -Indexes for ' . $row_diroptions['path']); } - $statusCodes = array('404', '403', '500'); + $statusCodes = array( + '404', + '403', + '500' + ); foreach ($statusCodes as $statusCode) { - if (isset($row_diroptions['error' . $statusCode . 'path']) - && $row_diroptions['error' . $statusCode . 'path'] != '' - ) { + if (isset($row_diroptions['error' . $statusCode . 'path']) && $row_diroptions['error' . $statusCode . 'path'] != '') { $defhandler = $row_diroptions['error' . $statusCode . 'path']; - if (!validateUrl($defhandler)) { - if (substr($defhandler, 0, 1) != '"' && substr($defhandler, -1, 1) != '"') { - $defhandler = '"'.makeCorrectFile($defhandler).'"'; + if (! validateUrl($defhandler)) { + if (substr($defhandler, 0, 1) != '"' && substr($defhandler, - 1, 1) != '"') { + $defhandler = '"' . makeCorrectFile($defhandler) . '"'; } } - $this->diroptions_data[$diroptions_filename].= ' ErrorDocument ' . $statusCode . ' ' . $defhandler . "\n"; + $this->diroptions_data[$diroptions_filename] .= ' ErrorDocument ' . $statusCode . ' ' . $defhandler . "\n"; } } - if ($cperlenabled - && isset($row_diroptions['options_cgi']) - && $row_diroptions['options_cgi'] == '1' - ) { + if ($cperlenabled && isset($row_diroptions['options_cgi']) && $row_diroptions['options_cgi'] == '1') { $this->diroptions_data[$diroptions_filename] .= ' AllowOverride None' . "\n"; $this->diroptions_data[$diroptions_filename] .= ' AddHandler cgi-script .cgi .pl' . "\n"; // >=apache-2.4 enabled? @@ -1088,7 +1051,7 @@ class apache extends HttpConfigBase { // for this path, as this would be the first require and therefore grant all access if ($mypath_dir->isUserProtected() == false) { $this->diroptions_data[$diroptions_filename] .= ' Require all granted' . "\n"; - //$this->diroptions_data[$diroptions_filename] .= ' AllowOverride All' . "\n"; + // $this->diroptions_data[$diroptions_filename] .= ' AllowOverride All' . "\n"; } } else { $this->diroptions_data[$diroptions_filename] .= ' Order allow,deny' . "\n"; @@ -1097,39 +1060,39 @@ class apache extends HttpConfigBase { $this->logger->logAction(CRON_ACTION, LOG_INFO, 'Enabling perl execution for ' . $row_diroptions['path']); // check for suexec-workaround, #319 - if ((int)Settings::Get('perl.suexecworkaround') == 1) { + if ((int) Settings::Get('perl.suexecworkaround') == 1) { // symlink this directory to suexec-safe-path $loginname = getCustomerDetail($row_diroptions['customerid'], 'loginname'); - $suexecpath = makeCorrectDir(Settings::Get('perl.suexecpath').'/'.$loginname.'/'.md5($row_diroptions['path']).'/'); + $suexecpath = makeCorrectDir(Settings::Get('perl.suexecpath') . '/' . $loginname . '/' . md5($row_diroptions['path']) . '/'); - if (!file_exists($suexecpath)) { - safe_exec('mkdir -p '.escapeshellarg($suexecpath)); - safe_exec('chown -R '.escapeshellarg($row_diroptions['guid']).':'.escapeshellarg($row_diroptions['guid']).' '.escapeshellarg($suexecpath)); + if (! file_exists($suexecpath)) { + safe_exec('mkdir -p ' . escapeshellarg($suexecpath)); + safe_exec('chown -R ' . escapeshellarg($row_diroptions['guid']) . ':' . escapeshellarg($row_diroptions['guid']) . ' ' . escapeshellarg($suexecpath)); } // symlink to {$givenpath}/cgi-bin // NOTE: symlinks are FILES, so do not append a / here - $perlsymlink = makeCorrectFile($row_diroptions['path'].'/cgi-bin'); - if (!file_exists($perlsymlink)) { - safe_exec('ln -s '.escapeshellarg($suexecpath).' '.escapeshellarg($perlsymlink)); + $perlsymlink = makeCorrectFile($row_diroptions['path'] . '/cgi-bin'); + if (! file_exists($perlsymlink)) { + safe_exec('ln -s ' . escapeshellarg($suexecpath) . ' ' . escapeshellarg($perlsymlink)); } - safe_exec('chown -h '.escapeshellarg($row_diroptions['guid']).':'.escapeshellarg($row_diroptions['guid']).' '.escapeshellarg($perlsymlink)); + safe_exec('chown -h ' . escapeshellarg($row_diroptions['guid']) . ':' . escapeshellarg($row_diroptions['guid']) . ' ' . escapeshellarg($perlsymlink)); } } else { // if no perl-execution is enabled but the workaround is, // we have to remove the symlink and folder in suexecpath - if ((int)Settings::Get('perl.suexecworkaround') == 1) { + if ((int) Settings::Get('perl.suexecworkaround') == 1) { $loginname = getCustomerDetail($row_diroptions['customerid'], 'loginname'); - $suexecpath = makeCorrectDir(Settings::Get('perl.suexecpath').'/'.$loginname.'/'.md5($row_diroptions['path']).'/'); - $perlsymlink = makeCorrectFile($row_diroptions['path'].'/cgi-bin'); + $suexecpath = makeCorrectDir(Settings::Get('perl.suexecpath') . '/' . $loginname . '/' . md5($row_diroptions['path']) . '/'); + $perlsymlink = makeCorrectFile($row_diroptions['path'] . '/cgi-bin'); // remove symlink if (file_exists($perlsymlink)) { - safe_exec('rm -f '.escapeshellarg($perlsymlink)); + safe_exec('rm -f ' . escapeshellarg($perlsymlink)); } // remove folder in suexec-path if (file_exists($suexecpath)) { - safe_exec('rm -rf '.escapeshellarg($suexecpath)); + safe_exec('rm -rf ' . escapeshellarg($suexecpath)); } } } @@ -1137,7 +1100,7 @@ class apache extends HttpConfigBase { if (count($row_diroptions['htpasswds']) > 0) { $htpasswd_filename = makeCorrectFile(Settings::Get('system.apacheconf_htpasswddir') . '/' . $row_diroptions['customerid'] . '-' . md5($row_diroptions['path']) . '.htpasswd'); - if (!isset($this->htpasswds_data[$htpasswd_filename])) { + if (! isset($this->htpasswds_data[$htpasswd_filename])) { $this->htpasswds_data[$htpasswd_filename] = ''; } @@ -1146,7 +1109,7 @@ class apache extends HttpConfigBase { } $this->diroptions_data[$diroptions_filename] .= ' AuthType Basic' . "\n"; - $this->diroptions_data[$diroptions_filename] .= ' AuthName "'.$row_htpasswd['authname'].'"' . "\n"; + $this->diroptions_data[$diroptions_filename] .= ' AuthName "' . $row_htpasswd['authname'] . '"' . "\n"; $this->diroptions_data[$diroptions_filename] .= ' AuthUserFile ' . $htpasswd_filename . "\n"; $this->diroptions_data[$diroptions_filename] .= ' require valid-user' . "\n"; } @@ -1156,22 +1119,22 @@ class apache extends HttpConfigBase { } } - /** * We write the configs */ - public function writeConfigs() { + public function writeConfigs() + { // Write diroptions $this->logger->logAction(CRON_ACTION, LOG_INFO, "apache::writeConfigs: rebuilding " . Settings::Get('system.apacheconf_diroptions')); if (count($this->diroptions_data) > 0) { $optsDir = new frxDirectory(Settings::Get('system.apacheconf_diroptions')); - if (!$optsDir->isConfigDir()) { + if (! $optsDir->isConfigDir()) { // Save one big file $diroptions_file = ''; foreach ($this->diroptions_data as $diroptions_filename => $diroptions_content) { - $diroptions_file.= $diroptions_content . "\n\n"; + $diroptions_file .= $diroptions_content . "\n\n"; } $diroptions_filename = Settings::Get('system.apacheconf_diroptions'); @@ -1182,7 +1145,7 @@ class apache extends HttpConfigBase { fwrite($diroptions_file_handler, $diroptions_file); fclose($diroptions_file_handler); } else { - if (!file_exists(Settings::Get('system.apacheconf_diroptions'))) { + if (! file_exists(Settings::Get('system.apacheconf_diroptions'))) { $this->logger->logAction(CRON_ACTION, LOG_NOTICE, 'apache::writeConfigs: mkdir ' . escapeshellarg(makeCorrectDir(Settings::Get('system.apacheconf_diroptions')))); safe_exec('mkdir ' . escapeshellarg(makeCorrectDir(Settings::Get('system.apacheconf_diroptions')))); } @@ -1204,7 +1167,7 @@ class apache extends HttpConfigBase { $this->logger->logAction(CRON_ACTION, LOG_INFO, "apache::writeConfigs: rebuilding " . Settings::Get('system.apacheconf_htpasswddir')); if (count($this->htpasswds_data) > 0) { - if (!file_exists(Settings::Get('system.apacheconf_htpasswddir'))) { + if (! file_exists(Settings::Get('system.apacheconf_htpasswddir'))) { $umask = umask(); umask(0000); mkdir(Settings::Get('system.apacheconf_htpasswddir'), 0751); @@ -1229,24 +1192,24 @@ class apache extends HttpConfigBase { if (count($this->virtualhosts_data) > 0) { $vhostDir = new frxDirectory(Settings::Get('system.apacheconf_vhost')); - if (!$vhostDir->isConfigDir()) { + if (! $vhostDir->isConfigDir()) { // Save one big file $vhosts_file = ''; // sort by filename so the order is: - // 1. subdomains x-29 - // 2. subdomains as main-domains 30 - // 3. main-domains 35 + // 1. subdomains x-29 + // 2. subdomains as main-domains 30 + // 3. main-domains 35 // #437 ksort($this->virtualhosts_data); foreach ($this->virtualhosts_data as $vhosts_filename => $vhost_content) { - $vhosts_file.= $vhost_content . "\n\n"; + $vhosts_file .= $vhost_content . "\n\n"; } // Include diroptions file in case it exists if (file_exists(Settings::Get('system.apacheconf_diroptions'))) { - $vhosts_file.= "\n" . 'Include ' . Settings::Get('system.apacheconf_diroptions') . "\n\n"; + $vhosts_file .= "\n" . 'Include ' . Settings::Get('system.apacheconf_diroptions') . "\n\n"; } $vhosts_filename = Settings::Get('system.apacheconf_vhost'); @@ -1257,7 +1220,7 @@ class apache extends HttpConfigBase { fwrite($vhosts_file_handler, $vhosts_file); fclose($vhosts_file_handler); } else { - if (!file_exists(Settings::Get('system.apacheconf_vhost'))) { + if (! file_exists(Settings::Get('system.apacheconf_vhost'))) { $this->logger->logAction(CRON_ACTION, LOG_NOTICE, 'apache::writeConfigs: mkdir ' . escapeshellarg(makeCorrectDir(Settings::Get('system.apacheconf_vhost')))); safe_exec('mkdir ' . escapeshellarg(makeCorrectDir(Settings::Get('system.apacheconf_vhost')))); } @@ -1275,6 +1238,4 @@ class apache extends HttpConfigBase { } } } - - } diff --git a/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php b/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php index 1b7dfadb..664dc617 100644 --- a/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php +++ b/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php @@ -100,57 +100,69 @@ class lighttpd extends HttpConfigBase $this->lighttpd_data[$vhost_filename] .= '# Froxlor default vhost' . "\n"; $this->lighttpd_data[$vhost_filename] .= '$HTTP["host"] =~ "^(?:www\.|)' . $myhost . '$" {' . "\n"; - if ($row_ipsandports['docroot'] == '') { - if (Settings::Get('system.froxlordirectlyviahostname')) { - $mypath = makeCorrectDir(dirname(dirname(dirname(__FILE__)))); - } else { - $mypath = makeCorrectDir(dirname(dirname(dirname(dirname(__FILE__))))); - } - } else { - // user-defined docroot, #417 - $mypath = makeCorrectDir($row_ipsandports['docroot']); - } + $mypath = $this->getMyPath($row_ipsandports); $this->lighttpd_data[$vhost_filename] .= ' server.document-root = "' . $mypath . '"' . "\n"; - /** - * dirprotection, see #72 - * - * @todo use better regex for this, deferred until 0.9.5 - * - * $this->lighttpd_data[$vhost_filename].= ' $HTTP["url"] =~ "^/(.+)\/(.+)\.php" {' . "\n"; - * $this->lighttpd_data[$vhost_filename].= ' url.access-deny = ("")' . "\n"; - * $this->lighttpd_data[$vhost_filename].= ' }' . "\n"; - */ + $is_redirect = false; + // check for SSL redirect + if ($row_ipsandports['ssl'] == '0' && Settings::Get('system.le_froxlor_redirect') == '1') { + $is_redirect = true; + // check whether froxlor uses Let's Encrypt and not cert is being generated yet + // or a renew is ongoing - disable redirect + if (System::Get('system.le_froxlor_enabled') && ($this->froxlorVhostHasLetsEncryptCert() == false || $this->froxlorVhostLetsEncryptNeedsRenew())) { + $this->lighttpd_data[$vhost_filename] .= '# temp. disabled ssl-redirect due to Let\'s Encrypt certificate generation.' . PHP_EOL; + $is_redirect = false; + } else { + $_sslport = $this->checkAlternativeSslPort(); + $mypath = 'https://' . Settings::Get('system.hostname') . $_sslport . '/'; - /** - * own php-fpm vhost - */ - if ((int) Settings::Get('phpfpm.enabled') == 1) { - $domain = array( - 'id' => 'none', - 'domain' => Settings::Get('system.hostname'), - 'adminid' => 1, /* first admin-user (superadmin) */ - 'mod_fcgid_starter' => - 1, - 'mod_fcgid_maxrequests' => - 1, - 'guid' => Settings::Get('phpfpm.vhost_httpuser'), - 'openbasedir' => 0, - 'email' => Settings::Get('panel.adminmail'), - 'loginname' => 'froxlor.panel', - 'documentroot' => $mypath - ); + $this->lighttpd_data[$vhost_filename] .= ' url.redirect = (' . "\n"; + $this->lighttpd_data[$vhost_filename] .= ' "^/(.*)$" => "' . $mypath . '$1"' . "\n"; + $this->lighttpd_data[$vhost_filename] .= ' )' . "\n"; + } + } - $php = new phpinterface($domain); + if (!$is_redirect) { + /** + * dirprotection, see #72 + * + * @todo use better regex for this, deferred until 0.9.5 + * + * $this->lighttpd_data[$vhost_filename].= ' $HTTP["url"] =~ "^/(.+)\/(.+)\.php" {' . "\n"; + * $this->lighttpd_data[$vhost_filename].= ' url.access-deny = ("")' . "\n"; + * $this->lighttpd_data[$vhost_filename].= ' }' . "\n"; + */ - $this->lighttpd_data[$vhost_filename] .= ' fastcgi.server = ( ' . "\n"; - $this->lighttpd_data[$vhost_filename] .= "\t" . '".php" => (' . "\n"; - $this->lighttpd_data[$vhost_filename] .= "\t\t" . '"localhost" => (' . "\n"; - $this->lighttpd_data[$vhost_filename] .= "\t\t" . '"socket" => "' . $php->getInterface()->getSocketFile() . '",' . "\n"; - $this->lighttpd_data[$vhost_filename] .= "\t\t" . '"check-local" => "enable",' . "\n"; - $this->lighttpd_data[$vhost_filename] .= "\t\t" . '"disable-time" => 1' . "\n"; - $this->lighttpd_data[$vhost_filename] .= "\t" . ')' . "\n"; - $this->lighttpd_data[$vhost_filename] .= "\t" . ')' . "\n"; - $this->lighttpd_data[$vhost_filename] .= ' )' . "\n"; + /** + * own php-fpm vhost + */ + if ((int) Settings::Get('phpfpm.enabled') == 1) { + $domain = array( + 'id' => 'none', + 'domain' => Settings::Get('system.hostname'), + 'adminid' => 1, /* first admin-user (superadmin) */ + 'mod_fcgid_starter' => - 1, + 'mod_fcgid_maxrequests' => - 1, + 'guid' => Settings::Get('phpfpm.vhost_httpuser'), + 'openbasedir' => 0, + 'email' => Settings::Get('panel.adminmail'), + 'loginname' => 'froxlor.panel', + 'documentroot' => $mypath + ); + + $php = new phpinterface($domain); + + $this->lighttpd_data[$vhost_filename] .= ' fastcgi.server = ( ' . "\n"; + $this->lighttpd_data[$vhost_filename] .= "\t" . '".php" => (' . "\n"; + $this->lighttpd_data[$vhost_filename] .= "\t\t" . '"localhost" => (' . "\n"; + $this->lighttpd_data[$vhost_filename] .= "\t\t" . '"socket" => "' . $php->getInterface()->getSocketFile() . '",' . "\n"; + $this->lighttpd_data[$vhost_filename] .= "\t\t" . '"check-local" => "enable",' . "\n"; + $this->lighttpd_data[$vhost_filename] .= "\t\t" . '"disable-time" => 1' . "\n"; + $this->lighttpd_data[$vhost_filename] .= "\t" . ')' . "\n"; + $this->lighttpd_data[$vhost_filename] .= "\t" . ')' . "\n"; + $this->lighttpd_data[$vhost_filename] .= ' )' . "\n"; + } } if ($row_ipsandports['specialsettings'] != '') { diff --git a/scripts/jobs/cron_tasks.inc.http.30.nginx.php b/scripts/jobs/cron_tasks.inc.http.30.nginx.php index 985b4f7e..d715f193 100644 --- a/scripts/jobs/cron_tasks.inc.http.30.nginx.php +++ b/scripts/jobs/cron_tasks.inc.http.30.nginx.php @@ -134,6 +134,8 @@ class nginx extends HttpConfigBase { $this->nginx_data[$vhost_filename] .= 'server { ' . "\n"; + $mypath = $this->getMyPath($row_ipsandports); + // check for ssl before anything else so // we know whether it's an ssl vhost or not $ssl_vhost = false; @@ -191,26 +193,28 @@ class nginx extends HttpConfigBase { $this->nginx_data[$vhost_filename] .= "\t".'server_name ' . Settings::Get('system.hostname') . ';' . "\n"; $this->nginx_data[$vhost_filename] .= "\t".'access_log /var/log/nginx/access.log;' . "\n"; - $mypath = ''; - - // no custom docroot set? - if ($row_ipsandports['docroot'] == '') { - // check whether the hostname should directly point to - // the froxlor-installation or not - if (Settings::Get('system.froxlordirectlyviahostname')) { - $mypath = makeCorrectDir(dirname(dirname(dirname(__FILE__)))); + $is_redirect = false; + // check for SSL redirect + if ($row_ipsandports['ssl'] == '0' && Settings::Get('system.le_froxlor_redirect') == '1') { + $is_redirect = true; + // check whether froxlor uses Let's Encrypt and not cert is being generated yet + // or a renew is ongoing - disable redirect + if (System::Get('system.le_froxlor_enabled') && ($this->froxlorVhostHasLetsEncryptCert() == false || $this->froxlorVhostLetsEncryptNeedsRenew())) { + $this->nginx_data[$vhost_filename] .= '# temp. disabled ssl-redirect due to Let\'s Encrypt certificate generation.' . PHP_EOL; + $is_redirect = false; } else { - $mypath = makeCorrectDir(dirname(dirname(dirname(dirname(__FILE__))))); + $_sslport = $this->checkAlternativeSslPort(); + $mypath = 'https://' . Settings::Get('system.hostname') . $_sslport . '/'; + $this->nginx_data[$vhost_filename] .= "\t".'return 301 '.$mypath.'$request_uri;'."\n"; } - } else { - // user-defined docroot, #417 - $mypath = makeCorrectDir($row_ipsandports['docroot']); } - $this->nginx_data[$vhost_filename] .= "\t".'root '.$mypath.';'."\n"; - $this->nginx_data[$vhost_filename] .= "\t".'index index.php index.html index.htm;'."\n\n"; - $this->nginx_data[$vhost_filename] .= "\t".'location / {'."\n"; - $this->nginx_data[$vhost_filename] .= "\t".'}'."\n"; + if (!$is_redirect) { + $this->nginx_data[$vhost_filename] .= "\t".'root '.$mypath.';'."\n"; + $this->nginx_data[$vhost_filename] .= "\t".'index index.php index.html index.htm;'."\n\n"; + $this->nginx_data[$vhost_filename] .= "\t".'location / {'."\n"; + $this->nginx_data[$vhost_filename] .= "\t".'}'."\n"; + } if ($row_ipsandports['specialsettings'] != '') { $this->nginx_data[$vhost_filename].= $this->processSpecialConfigTemplate( @@ -227,44 +231,46 @@ class nginx extends HttpConfigBase { * SSL config options */ if ($row_ipsandports['ssl'] == '1') { - $row_ipsandports['domain'] = Settings::Get('system.hostname'); + $row_ipsandports['domain'] = Settings::Get('system.hostname'); $this->nginx_data[$vhost_filename].=$this->composeSslSettings($row_ipsandports); } - $this->nginx_data[$vhost_filename] .= "\tlocation ~ \.php {\n"; - $this->nginx_data[$vhost_filename] .= "\t\tfastcgi_split_path_info ^(.+\.php)(/.+)\$;\n"; - $this->nginx_data[$vhost_filename] .= "\t\tinclude ".Settings::Get('nginx.fastcgiparams').";\n"; - $this->nginx_data[$vhost_filename] .= "\t\tfastcgi_param SCRIPT_FILENAME \$document_root\$fastcgi_script_name;\n"; - $this->nginx_data[$vhost_filename] .= "\t\tfastcgi_param PATH_INFO \$fastcgi_path_info;\n"; - $this->nginx_data[$vhost_filename] .= "\t\ttry_files \$fastcgi_script_name =404;\n"; + if (!$is_redirect) { + $this->nginx_data[$vhost_filename] .= "\tlocation ~ \.php {\n"; + $this->nginx_data[$vhost_filename] .= "\t\tfastcgi_split_path_info ^(.+\.php)(/.+)\$;\n"; + $this->nginx_data[$vhost_filename] .= "\t\tinclude ".Settings::Get('nginx.fastcgiparams').";\n"; + $this->nginx_data[$vhost_filename] .= "\t\tfastcgi_param SCRIPT_FILENAME \$document_root\$fastcgi_script_name;\n"; + $this->nginx_data[$vhost_filename] .= "\t\tfastcgi_param PATH_INFO \$fastcgi_path_info;\n"; + $this->nginx_data[$vhost_filename] .= "\t\ttry_files \$fastcgi_script_name =404;\n"; - if ($row_ipsandports['ssl'] == '1') { - $this->nginx_data[$vhost_filename] .= "\t\tfastcgi_param HTTPS on;\n"; + if ($row_ipsandports['ssl'] == '1') { + $this->nginx_data[$vhost_filename] .= "\t\tfastcgi_param HTTPS on;\n"; + } + + if ((int)Settings::Get('phpfpm.enabled') == 1 && (int)Settings::Get('phpfpm.enabled_ownvhost') == 1) { + $domain = array( + 'id' => 'none', + 'domain' => Settings::Get('system.hostname'), + 'adminid' => 1, /* first admin-user (superadmin) */ + 'mod_fcgid_starter' => -1, + 'mod_fcgid_maxrequests' => -1, + 'guid' => Settings::Get('phpfpm.vhost_httpuser'), + 'openbasedir' => 0, + 'email' => Settings::Get('panel.adminmail'), + 'loginname' => 'froxlor.panel', + 'documentroot' => $mypath, + ); + + $php = new phpinterface($domain); + $this->nginx_data[$vhost_filename] .= "\t\tfastcgi_pass unix:".$php->getInterface()->getSocketFile().";\n"; + } else { + $this->nginx_data[$vhost_filename] .= "\t\tfastcgi_pass ".Settings::Get('system.nginx_php_backend').";\n"; + } + + $this->nginx_data[$vhost_filename] .= "\t\tfastcgi_index index.php;\n"; + $this->nginx_data[$vhost_filename] .= "\t}\n"; } - if ((int)Settings::Get('phpfpm.enabled') == 1 && (int)Settings::Get('phpfpm.enabled_ownvhost') == 1) { - $domain = array( - 'id' => 'none', - 'domain' => Settings::Get('system.hostname'), - 'adminid' => 1, /* first admin-user (superadmin) */ - 'mod_fcgid_starter' => -1, - 'mod_fcgid_maxrequests' => -1, - 'guid' => Settings::Get('phpfpm.vhost_httpuser'), - 'openbasedir' => 0, - 'email' => Settings::Get('panel.adminmail'), - 'loginname' => 'froxlor.panel', - 'documentroot' => $mypath, - ); - - $php = new phpinterface($domain); - $this->nginx_data[$vhost_filename] .= "\t\tfastcgi_pass unix:".$php->getInterface()->getSocketFile().";\n"; - } else { - $this->nginx_data[$vhost_filename] .= "\t\tfastcgi_pass ".Settings::Get('system.nginx_php_backend').";\n"; - } - - $this->nginx_data[$vhost_filename] .= "\t\tfastcgi_index index.php;\n"; - $this->nginx_data[$vhost_filename] .= "\t}\n"; - $this->nginx_data[$vhost_filename] .= "}\n\n"; // End of Froxlor server{}-part } From 3b157a8c664d5b6d85a9bc52dde102cb716da1a4 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 12 Sep 2016 11:23:29 +0200 Subject: [PATCH 0175/1335] do not hide unavailable options, just disable them so people can see what is actually possible but just not available due to webserver-usage or other settings Signed-off-by: Michael Kaufmann (d00p) --- .../bool/function.getFormFieldOutputBool.php | 2 +- .../date/function.getFormFieldOutputDate.php | 4 ++-- .../file/function.getFormFieldOutputFile.php | 2 +- lib/functions/formfields/function.buildFormEx.php | 4 ++-- .../formfields/function.getFormFieldOutput.php | 12 +++++++++--- .../formfields/function.getFormGroupOutput.php | 8 +++++--- .../function.getFormFieldOutputString.php | 2 +- .../int/function.getFormFieldOutputInt.php | 4 ++-- .../option/function.getFormFieldOutputOption.php | 4 ++-- .../string/function.getFormFieldOutputString.php | 2 +- .../text/function.getFormFieldOutputText.php | 2 +- lng/english.lng.php | 2 ++ lng/german.lng.php | 2 ++ templates/Sparkle/formfields/bool.tpl | 2 +- templates/Sparkle/formfields/file.tpl | 2 +- templates/Sparkle/formfields/hiddenstring.tpl | 2 +- templates/Sparkle/formfields/option.tpl | 2 +- templates/Sparkle/formfields/string.tpl | 2 +- templates/Sparkle/formfields/text.tpl | 2 +- 19 files changed, 37 insertions(+), 25 deletions(-) diff --git a/lib/functions/formfields/bool/function.getFormFieldOutputBool.php b/lib/functions/formfields/bool/function.getFormFieldOutputBool.php index 1c33cbf8..d2e94f84 100644 --- a/lib/functions/formfields/bool/function.getFormFieldOutputBool.php +++ b/lib/functions/formfields/bool/function.getFormFieldOutputBool.php @@ -17,7 +17,7 @@ * */ -function getFormFieldOutputBool($fieldname, $fielddata) +function getFormFieldOutputBool($fieldname, $fielddata, $do_show = true) { $label = $fielddata['label']; $boolswitch = makeYesNo($fieldname, '1', '0', $fielddata['value']); diff --git a/lib/functions/formfields/date/function.getFormFieldOutputDate.php b/lib/functions/formfields/date/function.getFormFieldOutputDate.php index 7b757076..2c39a4b4 100644 --- a/lib/functions/formfields/date/function.getFormFieldOutputDate.php +++ b/lib/functions/formfields/date/function.getFormFieldOutputDate.php @@ -17,12 +17,12 @@ * */ -function getFormFieldOutputDate($fieldname, $fielddata) +function getFormFieldOutputDate($fieldname, $fielddata, $do_show = true) { if(isset($fielddata['date_timestamp']) && $fielddata['date_timestamp'] === true) { $fielddata['value'] = date('Y-m-d', $fielddata['value']); } - return getFormFieldOutputString($fieldname, $fielddata); + return getFormFieldOutputString($fieldname, $fielddata, $do_show); } diff --git a/lib/functions/formfields/file/function.getFormFieldOutputFile.php b/lib/functions/formfields/file/function.getFormFieldOutputFile.php index a8498440..7789ad4a 100644 --- a/lib/functions/formfields/file/function.getFormFieldOutputFile.php +++ b/lib/functions/formfields/file/function.getFormFieldOutputFile.php @@ -15,7 +15,7 @@ * */ -function getFormFieldOutputFile($fieldname, $fielddata) +function getFormFieldOutputFile($fieldname, $fielddata, $do_show = true) { $label = $fielddata['label']; $value = htmlentities($fielddata['value']); diff --git a/lib/functions/formfields/function.buildFormEx.php b/lib/functions/formfields/function.buildFormEx.php index 69162471..96048840 100644 --- a/lib/functions/formfields/function.buildFormEx.php +++ b/lib/functions/formfields/function.buildFormEx.php @@ -49,7 +49,7 @@ function buildFormEx($form, $part = '') { $do_show = $groupdetails['visible']; } - if ($do_show) { + //if ($do_show) { if (isset($groupdetails['title']) && $groupdetails['title'] != '') { $fields .= getFormGroupOutput($groupname, $groupdetails); } @@ -66,7 +66,7 @@ function buildFormEx($form, $part = '') { $fields .= getFormFieldOutput($fieldname, $fielddetails); } } - } + //} } } } diff --git a/lib/functions/formfields/function.getFormFieldOutput.php b/lib/functions/formfields/function.getFormFieldOutput.php index e610ee8f..c7e5b840 100644 --- a/lib/functions/formfields/function.getFormFieldOutput.php +++ b/lib/functions/formfields/function.getFormFieldOutput.php @@ -19,6 +19,8 @@ function getFormFieldOutput($fieldname, $fielddata) { + global $lng; + $returnvalue = ''; if (is_array($fielddata) && isset($fielddata['type']) @@ -51,6 +53,7 @@ function getFormFieldOutput($fieldname, $fielddata) { $websrv = Settings::Get('system.webserver'); if (!in_array($websrv, $fielddata['websrv_avail'])) { $do_show = false; + $fielddata['label'].= sprintf($lng['serversettings']['option_unavailable_websrv'], implode(", ", $fielddata['websrv_avail'])); } } @@ -59,11 +62,14 @@ function getFormFieldOutput($fieldname, $fielddata) { // be false due to websrv_avail if (isset($fielddata['visible']) && $do_show) { $do_show = $fielddata['visible']; + if (!$do_show) { + $fielddata['label'].= $lng['serversettings']['option_unavailable']; + } } - if ($do_show) { - $returnvalue = call_user_func('getFormFieldOutput' . ucfirst($fielddata['type']), $fieldname, $fielddata); - } + //if ($do_show) { + $returnvalue = call_user_func('getFormFieldOutput' . ucfirst($fielddata['type']), $fieldname, $fielddata, $do_show); + //} } return $returnvalue; } diff --git a/lib/functions/formfields/function.getFormGroupOutput.php b/lib/functions/formfields/function.getFormGroupOutput.php index 46bbe4fa..974cffac 100644 --- a/lib/functions/formfields/function.getFormGroupOutput.php +++ b/lib/functions/formfields/function.getFormGroupOutput.php @@ -81,11 +81,13 @@ function getFormOverviewGroupOutput($groupname, $groupdetails) { $websrv = Settings::Get('system.webserver'); if (!in_array($websrv, $groupdetails['websrv_avail'])) { $do_show = false; + $title .= sprintf($lng['serversettings']['option_unavailable_websrv'], implode(", ", $groupdetails['websrv_avail'])); + // hack disabled flag into select-box + $option = str_replace('checked="checked" /> + disabled="disabled" type="checkbox" name="{$fieldname}" value="1" checked="checked" /> diff --git a/templates/Sparkle/formfields/file.tpl b/templates/Sparkle/formfields/file.tpl index b7940962..c8b2aff0 100644 --- a/templates/Sparkle/formfields/file.tpl +++ b/templates/Sparkle/formfields/file.tpl @@ -1,4 +1,4 @@ {$label} - + disabled="disabled" type="file" class="file" name="{$fieldname}" /> diff --git a/templates/Sparkle/formfields/hiddenstring.tpl b/templates/Sparkle/formfields/hiddenstring.tpl index 4552c600..37b81de6 100644 --- a/templates/Sparkle/formfields/hiddenstring.tpl +++ b/templates/Sparkle/formfields/hiddenstring.tpl @@ -1,4 +1,4 @@ {$label} - + disabled="disabled" type="password" class="text" name="{$fieldname}" value="{$value}" /> \ No newline at end of file diff --git a/templates/Sparkle/formfields/option.tpl b/templates/Sparkle/formfields/option.tpl index 26f4ae3d..489ca542 100644 --- a/templates/Sparkle/formfields/option.tpl +++ b/templates/Sparkle/formfields/option.tpl @@ -1,4 +1,4 @@ {$label} - + diff --git a/templates/Sparkle/formfields/string.tpl b/templates/Sparkle/formfields/string.tpl index aa27675f..ae864af9 100644 --- a/templates/Sparkle/formfields/string.tpl +++ b/templates/Sparkle/formfields/string.tpl @@ -1,4 +1,4 @@ {$label} - + disabled="disabled" type="text" class="text" name="{$fieldname}" value="{$value}" /> diff --git a/templates/Sparkle/formfields/text.tpl b/templates/Sparkle/formfields/text.tpl index 68ba7396..d0a736f7 100644 --- a/templates/Sparkle/formfields/text.tpl +++ b/templates/Sparkle/formfields/text.tpl @@ -1,4 +1,4 @@ {$label} - + From 4229d8dda4b24b1ba077a602e4aa8420f9e4e521 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 12 Sep 2016 11:49:07 +0200 Subject: [PATCH 0176/1335] make path to acme.conf global alias file customizable Signed-off-by: Michael Kaufmann (d00p) --- actions/admin/settings/131.ssl.php | 9 +++++++++ install/froxlor.sql | 3 ++- install/updates/froxlor/0.9/update_0.9.inc.php | 16 ++++++++++++++++ .../updates/preconfig/0.9/preconfig_0.9.inc.php | 10 ++++++++++ lib/configfiles/gentoo.xml | 6 +++--- lib/configfiles/jessie.xml | 4 ++-- lib/configfiles/precise.xml | 4 ++-- lib/configfiles/rhel_centos.xml | 2 +- lib/configfiles/trusty.xml | 6 +++--- lib/configfiles/wheezy.xml | 6 +++--- lib/version.inc.php | 2 +- lng/english.lng.php | 2 ++ lng/german.lng.php | 2 ++ scripts/jobs/cron_tasks.inc.http.30.nginx.php | 3 ++- 14 files changed, 58 insertions(+), 17 deletions(-) diff --git a/actions/admin/settings/131.ssl.php b/actions/admin/settings/131.ssl.php index 7dff2d50..56c27cd5 100644 --- a/actions/admin/settings/131.ssl.php +++ b/actions/admin/settings/131.ssl.php @@ -88,6 +88,15 @@ return array( 'cronmodule' => 'froxlor/letsencrypt', 'save_method' => 'storeSettingField' ), + 'system_letsencryptacmeconf' => array( + 'label' => $lng['serversettings']['letsencryptacmeconf'], + 'settinggroup' => 'system', + 'varname' => 'letsencryptacmeconf', + 'type' => 'string', + 'string_type' => 'file', + 'default' => '/etc/apache2/conf-enabled/acme.conf', + 'save_method' => 'storeSettingField', + ), 'system_letsencryptca' => array( 'label' => $lng['serversettings']['letsencryptca'], 'settinggroup' => 'system', diff --git a/install/froxlor.sql b/install/froxlor.sql index 0457bd5f..b323b917 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -534,6 +534,7 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES ('system', 'available_shells', ''), ('system', 'le_froxlor_enabled', '0'), ('system', 'le_froxlor_redirect', '0'), + ('system', 'letsencryptacmeconf', '/etc/apache2/conf-enabled/acme.conf'), ('panel', 'decimal_places', '4'), ('panel', 'adminmail', 'admin@SERVERNAME'), ('panel', 'phpmyadmin_url', ''), @@ -565,7 +566,7 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES ('panel', 'password_special_char_required', '0'), ('panel', 'password_special_char', '!?<>§$%+#=@'), ('panel', 'version', '0.9.37'), - ('panel', 'db_version', '201609050'); + ('panel', 'db_version', '201609120'); DROP TABLE IF EXISTS `panel_tasks`; diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index f801cdf5..5f3c5a8b 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -3435,3 +3435,19 @@ if (isDatabaseVersion('201608260')) { updateToDbVersion('201609050'); } + +if (isDatabaseVersion('201609050')) { + + showUpdateStep("Adding new settings for acme.conf (Let's Encrypt)"); + // get user-chosen value + $websrv_default = "/etc/apache2/conf-enabled/acme.conf"; + if (Settings::Get('system.webserver') == 'nginx') { + $websrv_default = "/etc/nginx/acme.conf"; + } + $acmeconffile = isset($_POST['acmeconffile']) ? $_POST['acmeconffile'] : $websrv_default; + $acmeconffile = makeCorrectFile($acmeconffile); + Settings::AddNew("system.letsencryptacmeconf", $acmeconffile); + lastStepStatus(0); + + updateToDbVersion('201609120'); +} diff --git a/install/updates/preconfig/0.9/preconfig_0.9.inc.php b/install/updates/preconfig/0.9/preconfig_0.9.inc.php index 403d55d3..22406220 100644 --- a/install/updates/preconfig/0.9/preconfig_0.9.inc.php +++ b/install/updates/preconfig/0.9/preconfig_0.9.inc.php @@ -679,4 +679,14 @@ function parseAndOutputPreconfig(&$has_preconfig, &$return, $current_version, $c $question .= $dnsdaemons . ''; eval("\$return.=\"" . getTemplate("update/preconfigitem") . "\";"); } + + if (versionInUpdate($current_db_version, '201609120')) { + if (Settings::Get('system.leenabled') == 1) { + $has_preconfig = true; + $description = 'You can now customize the path to your acme.conf file (global alias for Let\'s Encrypt). If you already set up Let\'s Encrypt and the acme.conf file, please set this to the complete path to the file!

'; + $question = 'Path to the acme.conf alias-file.
'; + $question .= '
'; + eval("\$return.=\"" . getTemplate("update/preconfigitem") . "\";"); + } + } } diff --git a/lib/configfiles/gentoo.xml b/lib/configfiles/gentoo.xml index 871dddd6..69b4dd8d 100644 --- a/lib/configfiles/gentoo.xml +++ b/lib/configfiles/gentoo.xml @@ -64,7 +64,7 @@ ]]>
- + {{settings.system.leenabled}} - + {{settings.system.leenabled}} - + {{settings.system.leenabled}} - + {{settings.system.leenabled}} - + {{settings.system.leenabled}} - + {{settings.system.leenabled}} - + {{settings.system.leenabled}} //service[@type='http']/general/commands - + {{settings.system.leenabled}} - + {{settings.system.leenabled}} - + {{settings.system.leenabled}} - + {{settings.system.leenabled}} - + {{settings.system.leenabled}} - + {{settings.system.leenabled}} - + {{settings.system.leenabled}} Availble only for: %s'; $lng['serversettings']['option_unavailable'] = '
Option not availble due to other settings.'; +$lng['serversettings']['letsencryptacmeconf']['title'] = "Path to the acme.conf snippet"; +$lng['serversettings']['letsencryptacmeconf']['description'] = "File name of the config snippet which allows the web server to serve the acme challenge."; diff --git a/lng/german.lng.php b/lng/german.lng.php index 228c66a3..84c52f31 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -1691,3 +1691,5 @@ $lng['serversettings']['le_froxlor_redirect']['description'] = "Wenn dies aktivi $lng['admin']['froxlorvhost'] = 'Froxlor VirtualHost Einstellungen'; $lng['serversettings']['option_unavailable_websrv'] = '
Nur verfügbar für: %s'; $lng['serversettings']['option_unavailable'] = '
Option aufgrund anderer Einstellungen nicht verfügbar.'; +$lng['serversettings']['letsencryptacmeconf']['title'] = "Pfad zu acme.conf"; +$lng['serversettings']['letsencryptacmeconf']['description'] = "Dateiname der Konfiguration, die dem Webserver erlaubt, die ACME-Challenges zu bedienen."; diff --git a/scripts/jobs/cron_tasks.inc.http.30.nginx.php b/scripts/jobs/cron_tasks.inc.http.30.nginx.php index d715f193..ac81e94b 100644 --- a/scripts/jobs/cron_tasks.inc.http.30.nginx.php +++ b/scripts/jobs/cron_tasks.inc.http.30.nginx.php @@ -464,7 +464,8 @@ class nginx extends HttpConfigBase { if (Settings::Get('system.use_ssl') == '1' && Settings::Get('system.leenabled') == '1') { - $vhost_content.= "\t".'include /etc/nginx/acme.conf;'."\n"; + $acmeConfFilename = Settings::Get('system.letsencryptacmeconf'); + $vhost_content.= "\t".'include '.$acmeConfFilename.';'."\n"; } // if the documentroot is an URL we just redirect From c6ba9df18af675f54b725e1117cb6e701e0285ec Mon Sep 17 00:00:00 2001 From: Daniel Reichelt Date: Tue, 13 Sep 2016 12:00:50 +0200 Subject: [PATCH 0177/1335] fix variable identifier in HttpConfigBase::getMyPath() --- scripts/classes/class.HttpConfigBase.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/classes/class.HttpConfigBase.php b/scripts/classes/class.HttpConfigBase.php index d24559c2..993d47ca 100644 --- a/scripts/classes/class.HttpConfigBase.php +++ b/scripts/classes/class.HttpConfigBase.php @@ -64,7 +64,7 @@ class HttpConfigBase } } else { // user-defined docroot, #417 - $mypath = makeCorrectDir($row_ipsandports['docroot']); + $mypath = makeCorrectDir($ip_port['docroot']); } return $mypath; } From d8b6d87ade2ec44b58352442661379cc1b193a38 Mon Sep 17 00:00:00 2001 From: Daniel Reichelt Date: Fri, 16 Sep 2016 07:32:22 +0200 Subject: [PATCH 0178/1335] cron/nginx: remove echo'ed messages they already get logged --- scripts/jobs/cron_tasks.inc.http.30.nginx.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/scripts/jobs/cron_tasks.inc.http.30.nginx.php b/scripts/jobs/cron_tasks.inc.http.30.nginx.php index ac81e94b..af44aea4 100644 --- a/scripts/jobs/cron_tasks.inc.http.30.nginx.php +++ b/scripts/jobs/cron_tasks.inc.http.30.nginx.php @@ -610,7 +610,6 @@ class nginx extends HttpConfigBase { // check for existence, #1485 if (!file_exists($domain_or_ip['ssl_cert_file'])) { $this->logger->logAction(CRON_ACTION, LOG_ERR, $domain_or_ip['domain'] . ' :: certificate file "'.$domain_or_ip['ssl_cert_file'].'" does not exist! Cannot create ssl-directives'); - echo $domain_or_ip['domain'] . ' :: certificate file "'.$domain_or_ip['ssl_cert_file'].'" does not exist! Cannot create SSL-directives'."\n"; } else { // obsolete: ssl on now belongs to the listen block as 'ssl' at the end //$sslsettings .= "\t" . 'ssl on;' . "\n"; @@ -624,7 +623,6 @@ class nginx extends HttpConfigBase { // check for existence, #1485 if (!file_exists($domain_or_ip['ssl_key_file'])) { $this->logger->logAction(CRON_ACTION, LOG_ERR, $domain_or_ip['domain'] . ' :: certificate key file "'.$domain_or_ip['ssl_key_file'].'" does not exist! Cannot create ssl-directives'); - echo $domain_or_ip['domain'] . ' :: certificate key file "'.$domain_or_ip['ssl_key_file'].'" does not exist! SSL-directives might not be working'."\n"; } else { $sslsettings .= "\t" . 'ssl_certificate_key ' .makeCorrectFile($domain_or_ip['ssl_key_file']) . ';' . "\n"; } @@ -634,7 +632,6 @@ class nginx extends HttpConfigBase { // check for existence, #1485 if (!file_exists($domain_or_ip['ssl_ca_file'])) { $this->logger->logAction(CRON_ACTION, LOG_ERR, $domain_or_ip['domain'] . ' :: certificate CA file "'.$domain_or_ip['ssl_ca_file'].'" does not exist! Cannot create ssl-directives'); - echo $domain_or_ip['domain'] . ' :: certificate CA file "'.$domain_or_ip['ssl_ca_file'].'" does not exist! SSL-directives might not be working'."\n"; } else { $sslsettings.= "\t" . 'ssl_client_certificate ' . makeCorrectFile($domain_or_ip['ssl_ca_file']) . ';' . "\n"; } From 41e769d6815568b9f2f7d392b85ca615c0594535 Mon Sep 17 00:00:00 2001 From: Daniel Reichelt Date: Fri, 16 Sep 2016 07:50:10 +0200 Subject: [PATCH 0179/1335] cron/nginx: remove ssl_client_certificate Adding the CA certificate to an nginx vhost via ssl_client_certificate is outright wrong. Moreover, the CA certificate data is already written to the certificate file itself (class.DomainSSL.php:83-85). fixes #1650 --- scripts/jobs/cron_tasks.inc.http.30.nginx.php | 9 --------- 1 file changed, 9 deletions(-) diff --git a/scripts/jobs/cron_tasks.inc.http.30.nginx.php b/scripts/jobs/cron_tasks.inc.http.30.nginx.php index af44aea4..ae9e5a41 100644 --- a/scripts/jobs/cron_tasks.inc.http.30.nginx.php +++ b/scripts/jobs/cron_tasks.inc.http.30.nginx.php @@ -628,15 +628,6 @@ class nginx extends HttpConfigBase { } } - if ($domain_or_ip['ssl_ca_file'] != '') { - // check for existence, #1485 - if (!file_exists($domain_or_ip['ssl_ca_file'])) { - $this->logger->logAction(CRON_ACTION, LOG_ERR, $domain_or_ip['domain'] . ' :: certificate CA file "'.$domain_or_ip['ssl_ca_file'].'" does not exist! Cannot create ssl-directives'); - } else { - $sslsettings.= "\t" . 'ssl_client_certificate ' . makeCorrectFile($domain_or_ip['ssl_ca_file']) . ';' . "\n"; - } - } - if (isset($domain_or_ip['hsts']) && $domain_or_ip['hsts'] > 0) { $vhost_content .= 'add_header Strict-Transport-Security "max-age=' . $domain_or_ip['hsts']; if ($domain_or_ip['hsts_sub'] == 1) { From 56c8e907002cb61622f711d46cd0e487b95b2a1c Mon Sep 17 00:00:00 2001 From: Daniel Reichelt Date: Fri, 16 Sep 2016 09:15:35 +0200 Subject: [PATCH 0180/1335] display system hostname and some memory info on dashboard hostname may be useful in situations where several froxlors are reverse-proxied on a single webserver --- admin_index.php | 9 +++++++++ lng/english.lng.php | 4 ++++ lng/german.lng.php | 4 ++++ templates/Sparkle/admin/index/index.tpl | 8 ++++++++ 4 files changed, 25 insertions(+) diff --git a/admin_index.php b/admin_index.php index baa1a2aa..a6247d05 100644 --- a/admin_index.php +++ b/admin_index.php @@ -144,6 +144,15 @@ if ($page == 'overview') { $cron_last_runs = getCronjobsLastRun(); $outstanding_tasks = getOutstandingTasks(); + $system_hostname = gethostname(); + $meminfo= explode("\n", @file_get_contents("/proc/meminfo")); + $memory = ""; + for ($i = 0; $i < sizeof($meminfo); ++$i) { + if (substr($meminfo[$i], 0, 3) === "Mem") { + $memory.= $meminfo[$i] . PHP_EOL; + } + } + if (function_exists('sys_getloadavg')) { $loadArray = sys_getloadavg(); $load = number_format($loadArray[0], 2, '.', '') . " / " . number_format($loadArray[1], 2, '.', '') . " / " . number_format($loadArray[2], 2, '.', ''); diff --git a/lng/english.lng.php b/lng/english.lng.php index 76848bb3..e2fc7522 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -2041,3 +2041,7 @@ $lng['serversettings']['option_unavailable_websrv'] = '
Avail $lng['serversettings']['option_unavailable'] = '
Option not availble due to other settings.'; $lng['serversettings']['letsencryptacmeconf']['title'] = "Path to the acme.conf snippet"; $lng['serversettings']['letsencryptacmeconf']['description'] = "File name of the config snippet which allows the web server to serve the acme challenge."; + +// Added in froxlor 0.9.38 +$lng['admin']['hostname'] = 'Hostname'; +$lng['admin']['memory'] = 'Memory usage'; diff --git a/lng/german.lng.php b/lng/german.lng.php index 84c52f31..dd77baba 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -1693,3 +1693,7 @@ $lng['serversettings']['option_unavailable_websrv'] = '
Nur v $lng['serversettings']['option_unavailable'] = '
Option aufgrund anderer Einstellungen nicht verfügbar.'; $lng['serversettings']['letsencryptacmeconf']['title'] = "Pfad zu acme.conf"; $lng['serversettings']['letsencryptacmeconf']['description'] = "Dateiname der Konfiguration, die dem Webserver erlaubt, die ACME-Challenges zu bedienen."; + +// Added in froxlor 0.9.38 +$lng['admin']['hostname'] = 'Hostname'; +$lng['admin']['memory'] = 'Speicherauslastung'; diff --git a/templates/Sparkle/admin/index/index.tpl b/templates/Sparkle/admin/index/index.tpl index efe000a4..0e8fa3de 100644 --- a/templates/Sparkle/admin/index/index.tpl +++ b/templates/Sparkle/admin/index/index.tpl @@ -211,6 +211,10 @@ $header + + {$lng['admin']['hostname']}: + {$system_hostname} + {$lng['admin']['serversoftware']}: {$_SERVER['SERVER_SOFTWARE']} @@ -227,6 +231,10 @@ $header {$lng['admin']['webserverinterface']}: $webserverinterface + + {$lng['admin']['memory']}: +
$memory
+ {$lng['admin']['sysload']}: $load From 84ee5a21929a5cf05e1ca1ecfe42e6e0208950af Mon Sep 17 00:00:00 2001 From: Daniel Reichelt Date: Fri, 16 Sep 2016 18:09:09 +0200 Subject: [PATCH 0181/1335] cron/apache: remove echo'ed messages they already get logged --- scripts/jobs/cron_tasks.inc.http.10.apache.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/scripts/jobs/cron_tasks.inc.http.10.apache.php b/scripts/jobs/cron_tasks.inc.http.10.apache.php index 88e30c6f..bc2d1258 100644 --- a/scripts/jobs/cron_tasks.inc.http.10.apache.php +++ b/scripts/jobs/cron_tasks.inc.http.10.apache.php @@ -405,7 +405,6 @@ class apache extends HttpConfigBase // check for existence, #1485 if (! file_exists($domain['ssl_cert_file'])) { $this->logger->logAction(CRON_ACTION, LOG_ERR, $ipport . ' :: certificate file "' . $domain['ssl_cert_file'] . '" does not exist! Cannot create ssl-directives'); - echo $ipport . ' :: certificate file "' . $domain['ssl_cert_file'] . '" does not exist! Cannot create SSL-directives' . "\n"; } else { $this->virtualhosts_data[$vhosts_filename] .= ' SSLEngine On' . "\n"; @@ -420,7 +419,6 @@ class apache extends HttpConfigBase // check for existence, #1485 if (! file_exists($domain['ssl_key_file'])) { $this->logger->logAction(CRON_ACTION, LOG_ERR, $ipport . ' :: certificate key file "' . $domain['ssl_key_file'] . '" does not exist! Cannot create ssl-directives'); - echo $ipport . ' :: certificate key file "' . $domain['ssl_key_file'] . '" does not exist! SSL-directives might not be working' . "\n"; } else { $this->virtualhosts_data[$vhosts_filename] .= ' SSLCertificateKeyFile ' . makeCorrectFile($domain['ssl_key_file']) . "\n"; } @@ -430,7 +428,6 @@ class apache extends HttpConfigBase // check for existence, #1485 if (! file_exists($domain['ssl_ca_file'])) { $this->logger->logAction(CRON_ACTION, LOG_ERR, $ipport . ' :: certificate CA file "' . $domain['ssl_ca_file'] . '" does not exist! Cannot create ssl-directives'); - echo $ipport . ' :: certificate CA file "' . $domain['ssl_ca_file'] . '" does not exist! SSL-directives might not be working' . "\n"; } else { $this->virtualhosts_data[$vhosts_filename] .= ' SSLCACertificateFile ' . makeCorrectFile($domain['ssl_ca_file']) . "\n"; } @@ -441,7 +438,6 @@ class apache extends HttpConfigBase // check for existence, #1485 if (! file_exists($domain['ssl_cert_chainfile'])) { $this->logger->logAction(CRON_ACTION, LOG_ERR, $ipport . ' :: certificate chain file "' . $domain['ssl_cert_chainfile'] . '" does not exist! Cannot create ssl-directives'); - echo $ipport . ' :: certificate chain file "' . $domain['ssl_cert_chainfile'] . '" does not exist! SSL-directives might not be working' . "\n"; } else { $this->virtualhosts_data[$vhosts_filename] .= ' SSLCertificateChainFile ' . makeCorrectFile($domain['ssl_cert_chainfile']) . "\n"; } From ec474e2b4c695f21d905347f49e3a3e30ed7cb82 Mon Sep 17 00:00:00 2001 From: Daniel Reichelt Date: Fri, 16 Sep 2016 18:52:23 +0200 Subject: [PATCH 0182/1335] fix "Could not find class 'System'" exceptions --- scripts/jobs/cron_tasks.inc.http.10.apache.php | 4 ++-- scripts/jobs/cron_tasks.inc.http.20.lighttpd.php | 2 +- scripts/jobs/cron_tasks.inc.http.30.nginx.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/jobs/cron_tasks.inc.http.10.apache.php b/scripts/jobs/cron_tasks.inc.http.10.apache.php index bc2d1258..3a1898fe 100644 --- a/scripts/jobs/cron_tasks.inc.http.10.apache.php +++ b/scripts/jobs/cron_tasks.inc.http.10.apache.php @@ -199,7 +199,7 @@ class apache extends HttpConfigBase $is_redirect = true; // check whether froxlor uses Let's Encrypt and not cert is being generated yet // or a renew is ongoing - disable redirect - if (System::Get('system.le_froxlor_enabled') && ($this->froxlorVhostHasLetsEncryptCert() == false || $this->froxlorVhostLetsEncryptNeedsRenew())) { + if (Settings::Get('system.le_froxlor_enabled') && ($this->froxlorVhostHasLetsEncryptCert() == false || $this->froxlorVhostLetsEncryptNeedsRenew())) { $this->virtualhosts_data[$vhosts_filename] .= '# temp. disabled ssl-redirect due to Let\'s Encrypt certificate generation.' . PHP_EOL; $is_redirect = false; } else { @@ -213,7 +213,7 @@ class apache extends HttpConfigBase $this->virtualhosts_data[$vhosts_filename] .= ' ' . "\n"; $this->virtualhosts_data[$vhosts_filename] .= ' RewriteEngine On' . "\n"; $this->virtualhosts_data[$vhosts_filename] .= ' RewriteCond %{HTTPS} off' . "\n"; - if (System::Get('system.le_froxlor_enabled') == '1') { + if (Settings::Get('system.le_froxlor_enabled') == '1') { $this->virtualhosts_data[$vhosts_filename] .= ' RewriteCond %{REQUEST_URI} !^/\.well-known/acme-challenge' . "\n"; } $this->virtualhosts_data[$vhosts_filename] .= ' RewriteRule ^/(.*) ' . $mypath . '$1' . $modrew_red . "\n"; diff --git a/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php b/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php index 664dc617..2d87aaec 100644 --- a/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php +++ b/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php @@ -110,7 +110,7 @@ class lighttpd extends HttpConfigBase $is_redirect = true; // check whether froxlor uses Let's Encrypt and not cert is being generated yet // or a renew is ongoing - disable redirect - if (System::Get('system.le_froxlor_enabled') && ($this->froxlorVhostHasLetsEncryptCert() == false || $this->froxlorVhostLetsEncryptNeedsRenew())) { + if (Settings::Get('system.le_froxlor_enabled') && ($this->froxlorVhostHasLetsEncryptCert() == false || $this->froxlorVhostLetsEncryptNeedsRenew())) { $this->lighttpd_data[$vhost_filename] .= '# temp. disabled ssl-redirect due to Let\'s Encrypt certificate generation.' . PHP_EOL; $is_redirect = false; } else { diff --git a/scripts/jobs/cron_tasks.inc.http.30.nginx.php b/scripts/jobs/cron_tasks.inc.http.30.nginx.php index ae9e5a41..77f14c71 100644 --- a/scripts/jobs/cron_tasks.inc.http.30.nginx.php +++ b/scripts/jobs/cron_tasks.inc.http.30.nginx.php @@ -199,7 +199,7 @@ class nginx extends HttpConfigBase { $is_redirect = true; // check whether froxlor uses Let's Encrypt and not cert is being generated yet // or a renew is ongoing - disable redirect - if (System::Get('system.le_froxlor_enabled') && ($this->froxlorVhostHasLetsEncryptCert() == false || $this->froxlorVhostLetsEncryptNeedsRenew())) { + if (Settings::Get('system.le_froxlor_enabled') && ($this->froxlorVhostHasLetsEncryptCert() == false || $this->froxlorVhostLetsEncryptNeedsRenew())) { $this->nginx_data[$vhost_filename] .= '# temp. disabled ssl-redirect due to Let\'s Encrypt certificate generation.' . PHP_EOL; $is_redirect = false; } else { From 8e1a1043a5bd9934c8dcb99ac88db0c6c81432fe Mon Sep 17 00:00:00 2001 From: Daniel Reichelt Date: Sat, 17 Sep 2016 07:33:48 +0200 Subject: [PATCH 0183/1335] don't limit this query to emaildomains ...to catch possibly existing orphaned entries in the mail-user/mail- forwarding tables --- admin_domains.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/admin_domains.php b/admin_domains.php index c840b277..ded6616e 100644 --- a/admin_domains.php +++ b/admin_domains.php @@ -166,7 +166,7 @@ if ($page == 'domains' || $page == 'overview') { $subresult_stmt = Database::prepare(" SELECT `id` FROM `" . TABLE_PANEL_DOMAINS . "` - WHERE (`id` = :id OR `parentdomainid` = :id " . $rsd_sql . ") AND `isemaildomain` = '1'"); + WHERE (`id` = :id OR `parentdomainid` = :id " . $rsd_sql . ")"); Database::pexecute($subresult_stmt, array( 'id' => $id )); From f3c74bd718be3957b0f362a5a7cd45e6bbc035e2 Mon Sep 17 00:00:00 2001 From: Daniel Reichelt Date: Sat, 17 Sep 2016 07:34:56 +0200 Subject: [PATCH 0184/1335] remove unused code --- admin_domains.php | 1 - 1 file changed, 1 deletion(-) diff --git a/admin_domains.php b/admin_domains.php index ded6616e..7523e8b7 100644 --- a/admin_domains.php +++ b/admin_domains.php @@ -195,7 +195,6 @@ if ($page == 'domains' || $page == 'overview') { Database::pexecute($del_stmt, array( 'id' => $id )); - $deleted_domains = $del_stmt->rowCount(); $upd_stmt = Database::prepare(" UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET From b366f0474343fc373673a7c2bc73740ea491a44d Mon Sep 17 00:00:00 2001 From: Daniel Reichelt Date: Sat, 17 Sep 2016 08:13:10 +0200 Subject: [PATCH 0185/1335] re-assign new main domain to remaining mainbutsubto-domains --- admin_domains.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/admin_domains.php b/admin_domains.php index 7523e8b7..0e7d4fbd 100644 --- a/admin_domains.php +++ b/admin_domains.php @@ -189,6 +189,20 @@ if ($page == 'domains' || $page == 'overview') { $log->logAction(ADM_ACTION, LOG_NOTICE, "deleted domain/s from mail-tables"); } + // if mainbutsubto-domains are not to be deleted, re-assign the (ismainbutsubto value of the main + // domain which is being deleted) as their new ismainbutsubto value + if ($remove_subbutmain_domains !== 1) { + $upd_stmt = Database::prepare(" + UPDATE `" . TABLE_PANEL_DOMAINS . "` SET + `ismainbutsubto` = :newIsMainButSubtoValue + WHERE `ismainbutsubto` = :deletedMainDomainId + "); + Database::pexecute($upd_stmt, array( + 'newIsMainButSubtoValue' => $result['ismainbutsubto'], + 'deletedMainDomainId' => $id, + )); + } + $del_stmt = Database::prepare(" DELETE FROM `" . TABLE_PANEL_DOMAINS . "` WHERE `id` = :id OR `parentdomainid` = :id " . $rsd_sql); From 16751d74461c77cf42234a1166a82374cc829b9b Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 19 Sep 2016 07:47:37 +0200 Subject: [PATCH 0186/1335] correct check for existing user in ftp-members list when adding a new customer Signed-off-by: Michael Kaufmann (d00p) --- admin_customers.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/admin_customers.php b/admin_customers.php index 7c41f687..d9d95e7a 100644 --- a/admin_customers.php +++ b/admin_customers.php @@ -869,7 +869,7 @@ if ($page == 'customers' } // check froxlor-local user membership in ftp-group // without this check addition may duplicate user in list if httpuser == local_user - if (strpos($ins_data['members'], $local_user) !== false) { + if (strpos($ins_data['members'], $local_user) == false) { $ins_data['members'] .= ','.$local_user; } } From 58835ef81fe5bb90b6a2663b1781a0172f5cd3a3 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 19 Sep 2016 10:03:44 +0200 Subject: [PATCH 0187/1335] Warning: Non-standard capitalization of includeSubDomains Header contains the token . The recommended capitalization is . Signed-off-by: Michael Kaufmann (d00p) --- scripts/jobs/cron_tasks.inc.http.10.apache.php | 2 +- scripts/jobs/cron_tasks.inc.http.20.lighttpd.php | 2 +- scripts/jobs/cron_tasks.inc.http.30.nginx.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/jobs/cron_tasks.inc.http.10.apache.php b/scripts/jobs/cron_tasks.inc.http.10.apache.php index 3a1898fe..d20a0fd5 100644 --- a/scripts/jobs/cron_tasks.inc.http.10.apache.php +++ b/scripts/jobs/cron_tasks.inc.http.10.apache.php @@ -840,7 +840,7 @@ class apache extends HttpConfigBase $vhost_content .= ' ' . "\n"; $vhost_content .= ' Header always set Strict-Transport-Security "max-age=' . $domain['hsts']; if ($domain['hsts_sub'] == 1) { - $vhost_content .= '; includeSubdomains'; + $vhost_content .= '; includeSubDomains'; } if ($domain['hsts_preload'] == 1) { $vhost_content .= '; preload'; diff --git a/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php b/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php index 2d87aaec..de8718a9 100644 --- a/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php +++ b/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php @@ -523,7 +523,7 @@ class lighttpd extends HttpConfigBase $vhost_content .= '$HTTP["scheme"] == "https" { setenv.add-response-header = ( "Strict-Transport-Security" => "max-age=' . $domain['hsts']; if ($domain['hsts_sub'] == 1) { - $vhost_content .= '; includeSubdomains'; + $vhost_content .= '; includeSubDomains'; } if ($domain['hsts_preload'] == 1) { $vhost_content .= '; preload'; diff --git a/scripts/jobs/cron_tasks.inc.http.30.nginx.php b/scripts/jobs/cron_tasks.inc.http.30.nginx.php index 77f14c71..de6dd6b5 100644 --- a/scripts/jobs/cron_tasks.inc.http.30.nginx.php +++ b/scripts/jobs/cron_tasks.inc.http.30.nginx.php @@ -631,7 +631,7 @@ class nginx extends HttpConfigBase { if (isset($domain_or_ip['hsts']) && $domain_or_ip['hsts'] > 0) { $vhost_content .= 'add_header Strict-Transport-Security "max-age=' . $domain_or_ip['hsts']; if ($domain_or_ip['hsts_sub'] == 1) { - $vhost_content .= '; includeSubdomains'; + $vhost_content .= '; includeSubDomains'; } if ($domain_or_ip['hsts_preload'] == 1) { $vhost_content .= '; preload'; From a1e4374ada38bb14ba42ac6a68420af54a10da09 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 19 Sep 2016 11:42:08 +0200 Subject: [PATCH 0188/1335] show detailed information about diskspace-usage (web-, mail-, mysql-usage separated) Signed-off-by: Michael Kaufmann (d00p) --- admin_customers.php | 9 +++++++++ customer_index.php | 9 ++++++++- lng/english.lng.php | 1 + templates/Sparkle/admin/customers/customers_customer.tpl | 6 +++--- templates/Sparkle/customer/index/index.tpl | 7 ++++--- 5 files changed, 25 insertions(+), 7 deletions(-) diff --git a/admin_customers.php b/admin_customers.php index d9d95e7a..f73bfb6c 100644 --- a/admin_customers.php +++ b/admin_customers.php @@ -84,6 +84,15 @@ if ($page == 'customers' $domains = $domains_stmt->fetch(PDO::FETCH_ASSOC); $row['domains'] = intval($domains['domains']); $dec_places = Settings::Get('panel.decimal_places'); + + // get disk-space usages for web, mysql and mail + $usages_stmt = Database::prepare("SELECT * FROM `".TABLE_PANEL_DISKSPACE."` WHERE `customerid` = :cid ORDER BY `stamp` DESC LIMIT 1"); + $usages = Database::pexecute_first($usages_stmt, array('cid' => $row['customerid'])); + + $row['webspace_used'] = round($usages['webspace'] / 1024, $dec_places); + $row['mailspace_used'] = round($usages['mail'] / 1024, $dec_places); + $row['dbspace_used'] = round($usages['mysql'] / 1024, $dec_places); + $row['traffic_used'] = round($row['traffic_used'] / (1024 * 1024), $dec_places); $row['traffic'] = round($row['traffic'] / (1024 * 1024), $dec_places); $row['diskspace_used'] = round($row['diskspace_used'] / 1024, $dec_places); diff --git a/customer_index.php b/customer_index.php index 9d8772d7..25f390dd 100644 --- a/customer_index.php +++ b/customer_index.php @@ -78,8 +78,15 @@ if ($page == 'overview') { $yesterday = time() - (60 * 60 * 24); $month = date('M Y', $yesterday); + // get disk-space usages for web, mysql and mail + $usages_stmt = Database::prepare("SELECT * FROM `".TABLE_PANEL_DISKSPACE."` WHERE `customerid` = :cid ORDER BY `stamp` DESC LIMIT 1"); + $usages = Database::pexecute_first($usages_stmt, array('cid' => $userinfo['customerid'])); + $userinfo['diskspace'] = round($userinfo['diskspace'] / 1024, Settings::Get('panel.decimal_places')); - $userinfo['diskspace_used'] = round($userinfo['diskspace_used'] / 1024, Settings::Get('panel.decimal_places')); + $userinfo['diskspace_used'] = round($usages['webspace'] / 1024, Settings::Get('panel.decimal_places')); + $userinfo['mailspace_used'] = round($usages['mail'] / 1024, Settings::Get('panel.decimal_places')); + $userinfo['dbspace_used'] = round($usages['mysql'] / 1024, Settings::Get('panel.decimal_places')); + $userinfo['traffic'] = round($userinfo['traffic'] / (1024 * 1024), Settings::Get('panel.decimal_places')); $userinfo['traffic_used'] = round($userinfo['traffic_used'] / (1024 * 1024), Settings::Get('panel.decimal_places')); $userinfo = str_replace_array('-1', $lng['customer']['unlimited'], $userinfo, 'diskspace traffic mysqls emails email_accounts email_forwarders email_quota ftps tickets subdomains'); diff --git a/lng/english.lng.php b/lng/english.lng.php index e2fc7522..8d7fc200 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -71,6 +71,7 @@ $lng['customer']['ftps'] = 'FTP-accounts'; $lng['customer']['subdomains'] = 'Subdomains'; $lng['customer']['domains'] = 'Domains'; $lng['customer']['unlimited'] = '∞'; +$lng['customer']['mib'] = 'MiB'; /** * Customermenue diff --git a/templates/Sparkle/admin/customers/customers_customer.tpl b/templates/Sparkle/admin/customers/customers_customer.tpl index 519076c9..444b3760 100644 --- a/templates/Sparkle/admin/customers/customers_customer.tpl +++ b/templates/Sparkle/admin/customers/customers_customer.tpl @@ -26,16 +26,16 @@ Webspace: -
+
-
+
-
+
diff --git a/templates/Sparkle/customer/index/index.tpl b/templates/Sparkle/customer/index/index.tpl index 168c009e..1ba1a5cb 100644 --- a/templates/Sparkle/customer/index/index.tpl +++ b/templates/Sparkle/customer/index/index.tpl @@ -71,8 +71,9 @@ $header {$userinfo['email_accounts_used']} {$lng['panel']['used']}
- {$userinfo['email_accounts']} {$lng['panel']['available']} + {$userinfo['email_accounts']} {$lng['panel']['available']}
+ {$userinfo['mailspace_used']} {$lng['customer']['mib']}
@@ -113,8 +114,9 @@ $header {$userinfo['mysqls_used']} {$lng['panel']['used']}
- {$userinfo['mysqls']} {$lng['panel']['available']} + {$userinfo['mysqls']} {$lng['panel']['available']}
+ {$userinfo['dbspace_used']} {$lng['customer']['mib']}
@@ -248,4 +250,3 @@ $header
$footer - From 6a85c37b481e2838bfb4569dbe43544b3814f263 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 19 Sep 2016 12:17:56 +0200 Subject: [PATCH 0189/1335] do not replace multiple quotes with just one, as it leads to possible syntax-errors in php.ini, thx to hp7007 Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/phpinterface/class.phpinterface_fcgid.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/classes/phpinterface/class.phpinterface_fcgid.php b/lib/classes/phpinterface/class.phpinterface_fcgid.php index fab0a8db..1cb21dd1 100644 --- a/lib/classes/phpinterface/class.phpinterface_fcgid.php +++ b/lib/classes/phpinterface/class.phpinterface_fcgid.php @@ -166,14 +166,13 @@ class phpinterface_fcgid { ); //insert a small header for the file - $phpini_file = ";\n"; $phpini_file.= "; php.ini created/changed on " . date("Y.m.d H:i:s") . " for domain '" . $this->_domain['domain'] . "' with id #" . $this->_domain['id'] . " from php template '" . $phpconfig['description'] . "' with id #" . $phpconfig['id'] . "\n"; $phpini_file.= "; Do not change anything in this file, it will be overwritten by the Froxlor Cronjob!\n"; $phpini_file.= ";\n\n"; $phpini_file.= replace_variables($phpconfig['phpsettings'], $php_ini_variables); $phpini_file = str_replace('"none"', 'none', $phpini_file); - $phpini_file = preg_replace('/\"+/', '"', $phpini_file); + //$phpini_file = preg_replace('/\"+/', '"', $phpini_file); $phpini_file_handler = fopen($this->getIniFile(), 'w'); fwrite($phpini_file_handler, $phpini_file); fclose($phpini_file_handler); From 6197a97dc1285c7ac117c27ae206605ae6da1b23 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 20 Sep 2016 09:41:36 +0200 Subject: [PATCH 0190/1335] Add settings to speficy smtp auth data for mails sent by froxlor Signed-off-by: Michael Kaufmann (d00p) --- actions/admin/settings/120.system.php | 59 +++++++++++++++++++ install/froxlor.sql | 9 ++- .../updates/froxlor/0.9/update_0.9.inc.php | 24 ++++++++ .../preconfig/0.9/preconfig_0.9.inc.php | 20 +++++++ lib/functions/output/function.dieWithMail.php | 12 ++++ lib/init.php | 16 ++++- lib/version.inc.php | 2 +- lng/english.lng.php | 9 ++- lng/german.lng.php | 9 ++- scripts/jobs/cron_usage_report.php | 12 ++++ 10 files changed, 164 insertions(+), 8 deletions(-) diff --git a/actions/admin/settings/120.system.php b/actions/admin/settings/120.system.php index 464244c5..b08ce88e 100644 --- a/actions/admin/settings/120.system.php +++ b/actions/admin/settings/120.system.php @@ -152,6 +152,65 @@ return array( 'default' => 90, 'save_method' => 'storeSettingField', ), + + 'system_mail_use_smtp' => array( + 'label' => $lng['serversettings']['mail_use_smtp'], + 'settinggroup' => 'system', + 'varname' => 'mail_use_smtp', + 'type' => 'bool', + 'default' => false, + 'save_method' => 'storeSettingField', + ), + 'system_mail_smtp_host' => array( + 'label' => $lng['serversettings']['mail_smtp_host'], + 'settinggroup' => 'system', + 'varname' => 'mail_smtp_host', + 'type' => 'string', + 'default' => 'localhost', + 'save_method' => 'storeSettingField', + ), + 'system_mail_smtp_port' => array( + 'label' => $lng['serversettings']['mail_smtp_port'], + 'settinggroup' => 'system', + 'varname' => 'mail_smtp_port', + 'type' => 'int', + 'int_min' => 1, + 'int_max' => 65535, + 'default' => 25, + 'save_method' => 'storeSettingField', + ), + 'system_mail_smtp_usetls' => array( + 'label' => $lng['serversettings']['mail_smtp_usetls'], + 'settinggroup' => 'system', + 'varname' => 'mail_smtp_usetls', + 'type' => 'bool', + 'default' => true, + 'save_method' => 'storeSettingField', + ), + 'system_mail_smtp_auth' => array( + 'label' => $lng['serversettings']['mail_smtp_auth'], + 'settinggroup' => 'system', + 'varname' => 'mail_smtp_auth', + 'type' => 'bool', + 'default' => true, + 'save_method' => 'storeSettingField', + ), + 'system_mail_smtp_user' => array( + 'label' => $lng['serversettings']['mail_smtp_user'], + 'settinggroup' => 'system', + 'varname' => 'mail_smtp_user', + 'type' => 'string', + 'default' => '', + 'save_method' => 'storeSettingField', + ), + 'system_mail_smtp_passwd' => array( + 'label' => $lng['serversettings']['mail_smtp_passwd'], + 'settinggroup' => 'system', + 'varname' => 'mail_smtp_passwd', + 'type' => 'hiddenString', + 'default' => '', + 'save_method' => 'storeSettingField', + ), ), ), ), diff --git a/install/froxlor.sql b/install/froxlor.sql index b323b917..f30c943a 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -535,6 +535,13 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES ('system', 'le_froxlor_enabled', '0'), ('system', 'le_froxlor_redirect', '0'), ('system', 'letsencryptacmeconf', '/etc/apache2/conf-enabled/acme.conf'), + ('system', 'mail_use_smtp', '0'), + ('system', 'mail_smtp_host', 'localhost'), + ('system', 'mail_smtp_port', '25'), + ('system', 'mail_smtp_usetls', '1'), + ('system', 'mail_smtp_auth', '1'), + ('system', 'mail_smtp_user', ''), + ('system', 'mail_smtp_passwd', ''), ('panel', 'decimal_places', '4'), ('panel', 'adminmail', 'admin@SERVERNAME'), ('panel', 'phpmyadmin_url', ''), @@ -566,7 +573,7 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES ('panel', 'password_special_char_required', '0'), ('panel', 'password_special_char', '!?<>§$%+#=@'), ('panel', 'version', '0.9.37'), - ('panel', 'db_version', '201609120'); + ('panel', 'db_version', '201609200'); DROP TABLE IF EXISTS `panel_tasks`; diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index 5f3c5a8b..7da2b984 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -3451,3 +3451,27 @@ if (isDatabaseVersion('201609050')) { updateToDbVersion('201609120'); } + +if (isDatabaseVersion('201609120')) { + + showUpdateStep("Adding new SMTP settings for emails sent by froxlor"); + // get user-chosen value + $smtp_enable = isset($_POST['smtp_enable']) ? (int) $_POST['smtp_enable'] : 0; + $smtp_host = isset($_POST['smtp_host']) ? $_POST['smtp_host'] : "localhost"; + $smtp_port = isset($_POST['smtp_port']) ? (int)$_POST['smtp_port'] : 25; + $smtp_usetls = isset($_POST['smtp_usetls']) ? (int) $_POST['smtp_usetls'] : 1; + $smtp_useauth = isset($_POST['smtp_auth']) ? (int) $_POST['smtp_auth'] : 1; + $smtp_user = isset($_POST['smtp_user']) ? $_POST['smtp_user'] : ""; + $smtp_passwd = isset($_POST['smtp_passwd']) ? $_POST['smtp_passwd'] : ""; + + Settings::AddNew("system.mail_use_smtp", $smtp_enable); + Settings::AddNew("system.mail_smtp_host", $smtp_host); + Settings::AddNew("system.mail_smtp_port", $smtp_port); + Settings::AddNew("system.mail_smtp_usetls", $smtp_usetls); + Settings::AddNew("system.mail_smtp_auth", $smtp_useauth); + Settings::AddNew("system.mail_smtp_user", $smtp_user); + Settings::AddNew("system.mail_smtp_passwd", $smtp_passwd); + lastStepStatus(0); + + updateToDbVersion('201609200'); +} diff --git a/install/updates/preconfig/0.9/preconfig_0.9.inc.php b/install/updates/preconfig/0.9/preconfig_0.9.inc.php index 22406220..a25b78fc 100644 --- a/install/updates/preconfig/0.9/preconfig_0.9.inc.php +++ b/install/updates/preconfig/0.9/preconfig_0.9.inc.php @@ -689,4 +689,24 @@ function parseAndOutputPreconfig(&$has_preconfig, &$return, $current_version, $c eval("\$return.=\"" . getTemplate("update/preconfigitem") . "\";"); } } + + if (versionInUpdate($current_db_version, '201609200')) { + $has_preconfig = true; + $description = 'Specify SMTP settings which froxlor should use to send mail (optional)

'; + $question = 'Enable sending mails via SMTP?
'; + $question .= makeyesno('smtp_enable', '1', '0', '0') . '
'; + $question .= 'Enable sending mails via SMTP?
'; + $question .= '
'; + $question .= 'TCP port to connect to?
'; + $question .= '
'; + $question .= 'Enable TLS encryption?
'; + $question .= makeyesno('smtp_usetls', '1', '0', '1') . '
'; + $question .= 'Enable SMTP authentication?
'; + $question .= makeyesno('smtp_auth', '1', '0', '1') . '
'; + $question .= 'SMTP user?
'; + $question .= '
'; + $question .= 'SMTP password?
'; + $question .= '
'; + eval("\$return.=\"" . getTemplate("update/preconfigitem") . "\";"); + } } diff --git a/lib/functions/output/function.dieWithMail.php b/lib/functions/output/function.dieWithMail.php index 77ac2604..21890bdd 100644 --- a/lib/functions/output/function.dieWithMail.php +++ b/lib/functions/output/function.dieWithMail.php @@ -34,6 +34,18 @@ function dieWithMail($message, $subject = "[froxlor] Cronjob error") { $_mail = new PHPMailer(true); $_mail->CharSet = "UTF-8"; + if (Settings::Get('system.mail_use_smtp')) { + $_mail->isSMTP(); + $_mail->Host = Settings::Get('system.mail_smtp_host'); + $_mail->SMTPAuth = Settings::Get('system.mail_smtp_auth') == '1' ? true : false; + $_mail->Username = Settings::Get('system.mail_smtp_user'); + $_mail->Password = Settings::Get('system.mail_smtp_passwd'); + if (Settings::Get('system.mail_smtp_usetls')) { + $_mail->SMTPSecure = 'tls'; + } + $_mail->Port = Settings::Get('system.mail_smtp_port'); + } + if (PHPMailer::ValidateAddress(Settings::Get('panel.adminmail')) !== false) { // set return-to address and custom sender-name, see #76 $_mail->SetFrom(Settings::Get('panel.adminmail'), Settings::Get('panel.adminmail_defname')); diff --git a/lib/init.php b/lib/init.php index 585588ec..1c116a2c 100644 --- a/lib/init.php +++ b/lib/init.php @@ -142,7 +142,7 @@ if (version_compare(PHP_VERSION, "5.4.0", "<")) { */ if (get_magic_quotes_gpc()) { $in = array(&$_GET, &$_POST, &$_COOKIE); - + while (list($k, $v) = each($in)) { foreach ($v as $key => $val) { if (!is_array($val)) { @@ -265,7 +265,7 @@ while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) { // versions didn't have that and it will // lead to a lot of undfined variables // before the admin can even update - if (isset($row['iso'])) { + if (isset($row['iso'])) { $iso[$row['iso']] = $row['language']; } } @@ -542,6 +542,18 @@ if ($page == '') { $mail = new PHPMailer(true); $mail->CharSet = "UTF-8"; +if (Settings::Get('system.mail_use_smtp')) { + $mail->isSMTP(); + $mail->Host = Settings::Get('system.mail_smtp_host'); + $mail->SMTPAuth = Settings::Get('system.mail_smtp_auth') == '1' ? true : false; + $mail->Username = Settings::Get('system.mail_smtp_user'); + $mail->Password = Settings::Get('system.mail_smtp_passwd'); + if (Settings::Get('system.mail_smtp_usetls')) { + $mail->SMTPSecure = 'tls'; + } + $mail->Port = Settings::Get('system.mail_smtp_port'); +} + if (PHPMailer::ValidateAddress(Settings::Get('panel.adminmail')) !== false) { // set return-to address and custom sender-name, see #76 $mail->SetFrom(Settings::Get('panel.adminmail'), Settings::Get('panel.adminmail_defname')); diff --git a/lib/version.inc.php b/lib/version.inc.php index d0e8de30..8a1d56d3 100644 --- a/lib/version.inc.php +++ b/lib/version.inc.php @@ -19,7 +19,7 @@ $version = '0.9.37'; // Database version (YYYYMMDDC where C is a daily counter) -$dbversion = '201609120'; +$dbversion = '201609200'; // Distribution branding-tag (used for Debian etc.) $branding = ''; diff --git a/lng/english.lng.php b/lng/english.lng.php index 8d7fc200..6d2b9608 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -2042,7 +2042,12 @@ $lng['serversettings']['option_unavailable_websrv'] = '
Avail $lng['serversettings']['option_unavailable'] = '
Option not availble due to other settings.'; $lng['serversettings']['letsencryptacmeconf']['title'] = "Path to the acme.conf snippet"; $lng['serversettings']['letsencryptacmeconf']['description'] = "File name of the config snippet which allows the web server to serve the acme challenge."; - -// Added in froxlor 0.9.38 $lng['admin']['hostname'] = 'Hostname'; $lng['admin']['memory'] = 'Memory usage'; +$lng['serversettings']['mail_use_smtp'] = 'Set mailer to use SMTP'; +$lng['serversettings']['mail_smtp_host'] = 'Specify SMTP server'; +$lng['serversettings']['mail_smtp_usetls'] = 'Enable TLS encryption'; +$lng['serversettings']['mail_smtp_auth'] = 'Enable SMTP authentication'; +$lng['serversettings']['mail_smtp_port'] = 'TCP port to connect to'; +$lng['serversettings']['mail_smtp_user'] = 'SMTP username'; +$lng['serversettings']['mail_smtp_passwd'] = 'SMTP password'; diff --git a/lng/german.lng.php b/lng/german.lng.php index dd77baba..a1ebeba4 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -1693,7 +1693,12 @@ $lng['serversettings']['option_unavailable_websrv'] = '
Nur v $lng['serversettings']['option_unavailable'] = '
Option aufgrund anderer Einstellungen nicht verfügbar.'; $lng['serversettings']['letsencryptacmeconf']['title'] = "Pfad zu acme.conf"; $lng['serversettings']['letsencryptacmeconf']['description'] = "Dateiname der Konfiguration, die dem Webserver erlaubt, die ACME-Challenges zu bedienen."; - -// Added in froxlor 0.9.38 $lng['admin']['hostname'] = 'Hostname'; $lng['admin']['memory'] = 'Speicherauslastung'; +$lng['serversettings']['mail_use_smtp'] = 'Nutze SMTP für das Senden von E-Mails'; +$lng['serversettings']['mail_smtp_host'] = 'SMTP Server'; +$lng['serversettings']['mail_smtp_usetls'] = 'Aktiviere TLS Verschlüsselung'; +$lng['serversettings']['mail_smtp_auth'] = 'Nutze SMTP Authentifizierung'; +$lng['serversettings']['mail_smtp_port'] = 'TCP Port für SMTP'; +$lng['serversettings']['mail_smtp_user'] = 'SMTP Benutzer'; +$lng['serversettings']['mail_smtp_passwd'] = 'SMTP Passwort'; diff --git a/scripts/jobs/cron_usage_report.php b/scripts/jobs/cron_usage_report.php index 8de32c71..2ff94b52 100644 --- a/scripts/jobs/cron_usage_report.php +++ b/scripts/jobs/cron_usage_report.php @@ -26,6 +26,18 @@ $yesterday = time() - (60 * 60 * 24); $mail = new PHPMailer(true); $mail->CharSet = "UTF-8"; +if (Settings::Get('system.mail_use_smtp')) { + $mail->isSMTP(); + $mail->Host = Settings::Get('system.mail_smtp_host'); + $mail->SMTPAuth = Settings::Get('system.mail_smtp_auth') == '1' ? true : false; + $mail->Username = Settings::Get('system.mail_smtp_user'); + $mail->Password = Settings::Get('system.mail_smtp_passwd'); + if (Settings::Get('system.mail_smtp_usetls')) { + $mail->SMTPSecure = 'tls'; + } + $mail->Port = Settings::Get('system.mail_smtp_port'); +} + if (PHPMailer::ValidateAddress(Settings::Get('panel.adminmail')) !== false) { // set return-to address and custom sender-name, see #76 $mail->SetFrom(Settings::Get('panel.adminmail'), Settings::Get('panel.adminmail_defname')); From 98c8f519a64e4b43ba6621b2aac1308e86072cf1 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Wed, 21 Sep 2016 07:38:07 +0200 Subject: [PATCH 0191/1335] validate customer entered subdomains, fixes #1653 Signed-off-by: Michael Kaufmann (d00p) --- customer_domains.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/customer_domains.php b/customer_domains.php index 66fbc0e1..6849c0e8 100644 --- a/customer_domains.php +++ b/customer_domains.php @@ -277,6 +277,13 @@ if ($page == 'overview') { $completedomain = $subdomain . '.' . $domain; + if (Settings::Get('system.validate_domain') && ! validateDomain($completedomain)) { + standard_error(array( + 'stringiswrong', + 'mydomain' + )); + } + if ($completedomain == Settings::Get('system.hostname')) { standard_error('admin_domain_emailsystemhostname'); } From 9799e05ce4642e8ea62e6893eff4b08b3526560f Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 22 Sep 2016 07:46:50 +0200 Subject: [PATCH 0192/1335] idna convert the whole URI for uri's in docroot as redirect, fixes #1654 Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/idna/class.idna_convert_wrapper.php | 10 ++++++++++ scripts/jobs/cron_tasks.inc.http.10.apache.php | 4 ++-- scripts/jobs/cron_tasks.inc.http.20.lighttpd.php | 2 +- scripts/jobs/cron_tasks.inc.http.30.nginx.php | 2 +- 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/lib/classes/idna/class.idna_convert_wrapper.php b/lib/classes/idna/class.idna_convert_wrapper.php index d9597c91..51f909eb 100644 --- a/lib/classes/idna/class.idna_convert_wrapper.php +++ b/lib/classes/idna/class.idna_convert_wrapper.php @@ -67,6 +67,16 @@ class idna_convert_wrapper } } + public function encode_uri($to_encode) + { + if (version_compare("5.6.0", PHP_VERSION, ">=")) { + return $this->_do_action('encode', $to_encode); + } else { + $to_encode = $this->is_utf8($to_encode) ? $to_encode : utf8_encode($to_encode); + return $this->idna_converter->encodeUri($to_encode); + } + } + /** * Decode a domain name, a email address or a list of one of both. * diff --git a/scripts/jobs/cron_tasks.inc.http.10.apache.php b/scripts/jobs/cron_tasks.inc.http.10.apache.php index d20a0fd5..4d6acb46 100644 --- a/scripts/jobs/cron_tasks.inc.http.10.apache.php +++ b/scripts/jobs/cron_tasks.inc.http.10.apache.php @@ -860,7 +860,7 @@ class apache extends HttpConfigBase $domain['documentroot'] = trim($domain['documentroot']); if (preg_match('/^https?\:\/\//', $domain['documentroot'])) { - $corrected_docroot = $this->idnaConvert->encode($domain['documentroot']); + $corrected_docroot = $this->idnaConvert->encode_uri($domain['documentroot']); // Get domain's redirect code $code = getDomainRedirectCode($domain['id']); @@ -881,7 +881,7 @@ class apache extends HttpConfigBase $vhost_content .= ' RewriteRule ^/(.*) ' . $corrected_docroot . '$1' . $modrew_red . "\n"; $vhost_content .= '
' . "\n"; $vhost_content .= ' ' . "\n"; - $vhost_content .= ' Redirect ' . $code . ' / ' . $this->idnaConvert->encode($domain['documentroot']) . "\n"; + $vhost_content .= ' Redirect ' . $code . ' / ' . $corrected_docroot . "\n"; $vhost_content .= ' ' . "\n"; } else { diff --git a/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php b/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php index de8718a9..90b70cc0 100644 --- a/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php +++ b/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php @@ -430,7 +430,7 @@ class lighttpd extends HttpConfigBase if (preg_match('/^https?\:\/\//', $domain['documentroot'])) { $vhost_content .= ' url.redirect = (' . "\n"; - $vhost_content .= ' "^/(.*)$" => "' . $this->idnaConvert->encode($domain['documentroot']) . '$1"' . "\n"; + $vhost_content .= ' "^/(.*)$" => "' . $this->idnaConvert->encode_uri($domain['documentroot']) . '$1"' . "\n"; $vhost_content .= ' )' . "\n"; } else { diff --git a/scripts/jobs/cron_tasks.inc.http.30.nginx.php b/scripts/jobs/cron_tasks.inc.http.30.nginx.php index de6dd6b5..81263633 100644 --- a/scripts/jobs/cron_tasks.inc.http.30.nginx.php +++ b/scripts/jobs/cron_tasks.inc.http.30.nginx.php @@ -470,7 +470,7 @@ class nginx extends HttpConfigBase { // if the documentroot is an URL we just redirect if (preg_match('/^https?\:\/\//', $domain['documentroot'])) { - $uri = $this->idnaConvert->encode($domain['documentroot']); + $uri = $this->idnaConvert->encode_uri($domain['documentroot']); if (substr($uri, -1) == '/') { $uri = substr($uri, 0, -1); } From 97d035eee9d8f24572fb32e9e800f097a9ba76f8 Mon Sep 17 00:00:00 2001 From: FliegenKLATSCH Date: Sat, 30 Apr 2016 17:24:11 +0200 Subject: [PATCH 0193/1335] fix group by clause to be be compliant with mysql 5.7 --- customer_domains.php | 2 +- customer_traffic.php | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/customer_domains.php b/customer_domains.php index 6849c0e8..7cb60472 100644 --- a/customer_domains.php +++ b/customer_domains.php @@ -730,7 +730,7 @@ if ($page == 'overview') { AND `dip`.`id_ipandports` IN (SELECT `id_ipandports` FROM `".TABLE_DOMAINTOIP."` WHERE `id_domain` = :id) - GROUP BY `d`.`domain` + GROUP BY `d`.`id`, `d`.`domain` ORDER BY `d`.`domain` ASC" ); Database::pexecute($domains_stmt, array("id" => $result['id'], "customerid" => $userinfo['customerid'])); diff --git a/customer_traffic.php b/customer_traffic.php index 8b3aaf17..abca00b0 100644 --- a/customer_traffic.php +++ b/customer_traffic.php @@ -115,8 +115,7 @@ if (!is_null($month) && !is_null($year)) { $result_stmt = Database::prepare("SELECT `month`, `year`, SUM(`http`) AS http, SUM(`ftp_up`) AS ftp_up, SUM(`ftp_down`) AS ftp_down, SUM(`mail`) AS mail FROM `" . TABLE_PANEL_TRAFFIC . "` WHERE `customerid` = :customerid - GROUP BY CONCAT(`year`,`month`) - ORDER BY CONCAT(`year`,`month`) DESC + GROUP BY `year` DESC, `month` DESC LIMIT 12" ); Database::pexecute($result_stmt, array("customerid" => $userinfo['customerid'])); From 02797d9abc01402ee632b77637cc0b622d432ba3 Mon Sep 17 00:00:00 2001 From: Daniel Reichelt Date: Sat, 24 Sep 2016 21:58:20 +0200 Subject: [PATCH 0194/1335] no longer show NameVirtualHost option for apache 2.4 --- admin_ipsandports.php | 1 + lib/formfields/admin/ipsandports/formfield.ipsandports_add.php | 2 +- lib/formfields/admin/ipsandports/formfield.ipsandports_edit.php | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/admin_ipsandports.php b/admin_ipsandports.php index f03f6635..eddcf429 100644 --- a/admin_ipsandports.php +++ b/admin_ipsandports.php @@ -33,6 +33,7 @@ if ($page == 'ipsandports' $websrv = Settings::Get('system.webserver'); $is_nginx = ($websrv == 'nginx'); $is_apache = ($websrv == 'apache2'); + $is_apache24 = $is_apache && (Settings::Get('system.apache24') === '1'); if ($action == '') { diff --git a/lib/formfields/admin/ipsandports/formfield.ipsandports_add.php b/lib/formfields/admin/ipsandports/formfield.ipsandports_add.php index 6fa8c3e7..9dd24326 100644 --- a/lib/formfields/admin/ipsandports/formfield.ipsandports_add.php +++ b/lib/formfields/admin/ipsandports/formfield.ipsandports_add.php @@ -49,7 +49,7 @@ return array( 'value' => array('1') ), 'namevirtualhost_statement' => array( - 'visible' => $is_apache, + 'visible' => $is_apache && !$is_apache24, 'label' => $lng['admin']['ipsandports']['create_namevirtualhost_statement'], 'type' => 'checkbox', 'values' => array( diff --git a/lib/formfields/admin/ipsandports/formfield.ipsandports_edit.php b/lib/formfields/admin/ipsandports/formfield.ipsandports_edit.php index 5dbea0c3..f52a8230 100644 --- a/lib/formfields/admin/ipsandports/formfield.ipsandports_edit.php +++ b/lib/formfields/admin/ipsandports/formfield.ipsandports_edit.php @@ -51,7 +51,7 @@ return array( 'value' => array($result['listen_statement']) ), 'namevirtualhost_statement' => array( - 'visible' => $is_apache, + 'visible' => $is_apache && !$is_apache24, 'label' => $lng['admin']['ipsandports']['create_namevirtualhost_statement'], 'type' => 'checkbox', 'values' => array( From ed0ede645ab4ff9d1c4e5e6fc39b7f9adb8a681c Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 26 Sep 2016 11:48:36 +0200 Subject: [PATCH 0195/1335] added ssl-certificate overview for admins and customers to show CN, Issuer, ValidFrom and ValidTo dates Signed-off-by: Michael Kaufmann (d00p) --- admin_domains.php | 5 + customer_domains.php | 5 + lib/navigation/00.froxlor.main.php | 9 ++ lng/english.lng.php | 4 + lng/german.lng.php | 4 + ssl_certificates.php | 133 ++++++++++++++++++ .../Sparkle/ssl_certificates/certs_cert.tpl | 33 +++++ .../Sparkle/ssl_certificates/certs_error.tpl | 3 + .../Sparkle/ssl_certificates/certs_list.tpl | 57 ++++++++ 9 files changed, 253 insertions(+) create mode 100644 ssl_certificates.php create mode 100644 templates/Sparkle/ssl_certificates/certs_cert.tpl create mode 100644 templates/Sparkle/ssl_certificates/certs_error.tpl create mode 100644 templates/Sparkle/ssl_certificates/certs_list.tpl diff --git a/admin_domains.php b/admin_domains.php index 0e7d4fbd..6517e2f4 100644 --- a/admin_domains.php +++ b/admin_domains.php @@ -2099,6 +2099,11 @@ if ($page == 'domains' || $page == 'overview') { } elseif ($page == 'domaindnseditor' && Settings::Get('system.dnsenabled') == '1') { require_once __DIR__.'/dns_editor.php'; + +} elseif ($page == 'sslcertificates') { + + require_once __DIR__.'/ssl_certificates.php'; + } function formatDomainEntry(&$row, &$idna_convert) diff --git a/customer_domains.php b/customer_domains.php index 6849c0e8..b78053c8 100644 --- a/customer_domains.php +++ b/customer_domains.php @@ -935,4 +935,9 @@ if ($page == 'overview') { } elseif ($page == 'domaindnseditor' && $userinfo['dnsenabled'] == '1' && Settings::Get('system.dnsenabled') == '1') { require_once __DIR__.'/dns_editor.php'; + +} elseif ($page == 'sslcertificates') { + + require_once __DIR__.'/ssl_certificates.php'; + } diff --git a/lib/navigation/00.froxlor.main.php b/lib/navigation/00.froxlor.main.php index 838042c5..faa3feec 100644 --- a/lib/navigation/00.froxlor.main.php +++ b/lib/navigation/00.froxlor.main.php @@ -95,6 +95,10 @@ return array( array( 'url' => 'customer_domains.php?page=domains', 'label' => $lng['menue']['domains']['settings'] + ), + array( + 'url' => 'customer_domains.php?page=sslcertificates', + 'label' => $lng['domains']['ssl_certificates'] ) ) ), @@ -200,6 +204,11 @@ return array( 'label' => $lng['admin']['domains'], 'required_resources' => 'domains' ), + array( + 'url' => 'admin_domains.php?page=sslcertificates', + 'label' => $lng['domains']['ssl_certificates'], + 'required_resources' => 'domains' + ), array( 'url' => 'admin_ipsandports.php?page=ipsandports', 'label' => $lng['admin']['ipsandports']['ipsandports'], diff --git a/lng/english.lng.php b/lng/english.lng.php index 6d2b9608..6859fd06 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -2051,3 +2051,7 @@ $lng['serversettings']['mail_smtp_auth'] = 'Enable SMTP authentication'; $lng['serversettings']['mail_smtp_port'] = 'TCP port to connect to'; $lng['serversettings']['mail_smtp_user'] = 'SMTP username'; $lng['serversettings']['mail_smtp_passwd'] = 'SMTP password'; +$lng['domains']['ssl_certificates'] = 'SSL certificates'; +$lng['domains']['ssl_certificate_removed'] = 'The certificate with the id #%s has been removed successfully'; +$lng['domains']['ssl_certificate_error'] = "Error reading certificate for domain: %s"; +$lng['domains']['no_ssl_certificates'] = "There are no domains with SSL certificate"; diff --git a/lng/german.lng.php b/lng/german.lng.php index a1ebeba4..fc6d9489 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -1702,3 +1702,7 @@ $lng['serversettings']['mail_smtp_auth'] = 'Nutze SMTP Authentifizierung'; $lng['serversettings']['mail_smtp_port'] = 'TCP Port für SMTP'; $lng['serversettings']['mail_smtp_user'] = 'SMTP Benutzer'; $lng['serversettings']['mail_smtp_passwd'] = 'SMTP Passwort'; +$lng['domains']['ssl_certificates'] = 'SSL Zertifikate'; +$lng['domains']['ssl_certificate_removed'] = 'Das Zertifikat mit der ID #%s wurde erfolgreich gelöscht.'; +$lng['domains']['ssl_certificate_error'] = "Fehler beim Lesen des Zertifikats für die Domain: %s"; +$lng['domains']['no_ssl_certificates'] = "Es wurden keine SSL-Zertifikate gefunden"; diff --git a/ssl_certificates.php b/ssl_certificates.php new file mode 100644 index 00000000..bfe81553 --- /dev/null +++ b/ssl_certificates.php @@ -0,0 +1,133 @@ + (2016-) + * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt + * @package Panel + * + */ + +// This file is being included in admin_domains and customer_domains + // and therefore does not need to require lib/init.php + +$del_stmt = Database::prepare("DELETE FROM `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` WHERE id = :id"); +$success_message = ""; + +// do the delete and then just showa success-message and the certificates list again +if ($action == 'delete') { + $id = isset($_GET['id']) ? (int) $_GET['id'] : 0; + if ($id > 0) { + Database::pexecute($del_stmt, array( + 'id' => $id + )); + $success_message = sprintf($lng['domains']['ssl_certificate_removed'], $id); + } +} + +$log->logAction(USR_ACTION, LOG_NOTICE, "viewed domains::ssl_certificates"); +$fields = array( + 'd.domain' => $lng['domains']['domainname'] +); +$paging = new paging($userinfo, TABLE_PANEL_DOMAIN_SSL_SETTINGS, $fields); + +// select all my (accessable) certificates +$certs_stmt_query = "SELECT s.*, d.domain, d.letsencrypt, c.customerid, c.loginname + FROM `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` s + LEFT JOIN `" . TABLE_PANEL_DOMAINS . "` d ON `d`.`id` = `s`.`domainid` + LEFT JOIN `" . TABLE_PANEL_CUSTOMERS . "` c ON `c`.`customerid` = `d`.`customerid` + WHERE "; + +$qry_params = array(); + +if (AREA == 'admin' && $userinfo['customers_see_all'] == '0') { + // admin with only customer-specific permissions + $certs_stmt_query .= "d.adminid = :adminid "; + $qry_params['adminid'] = $userinfo['adminid']; +} elseif (AREA == 'customer') { + // customer-area + $certs_stmt_query .= "d.customerid = :cid "; + $qry_params['cid'] = $userinfo['customerid']; +} else { + $certs_stmt_query .= "1 "; +} + +// sorting by domain-name +$certs_stmt_query .= $paging->getSqlWhere(true) . " " . $paging->getSqlOrderBy() . " " . $paging->getSqlLimit(); + +$certs_stmt = Database::prepare($certs_stmt_query); +Database::pexecute($certs_stmt, $qry_params); +$all_certs = $certs_stmt->fetchAll(PDO::FETCH_ASSOC); +$certificates = ""; + +if (count($all_certs) == 0) { + $message = $lng['domains']['no_ssl_certificates']; + $sortcode = ""; + $arrowcode = array('d.domain' => ''); + $searchcode = ""; + $pagingcode = ""; + eval("\$certificates.=\"" . getTemplate("ssl_certificates/certs_error", true) . "\";"); +} else { + $paging->setEntries(count($all_certs)); + $sortcode = $paging->getHtmlSortCode($lng); + $arrowcode = $paging->getHtmlArrowCode($filename . '?page=' . $page . '&s=' . $s); + $searchcode = $paging->getHtmlSearchCode($lng); + $pagingcode = $paging->getHtmlPagingCode($filename . '?page=' . $page . '&s=' . $s); + + foreach ($all_certs as $idx => $cert) { + if ($paging->checkDisplay($idx)) { + + if (empty($cert['domain']) || empty($cert['ssl_cert_file'])) { + // no domain found to the entry or empty entry - safely delete it from the DB + Database::pexecute($del_stmt, array( + 'id' => $cert['id'] + )); + continue; + } + + $cert_data = openssl_x509_parse($cert['ssl_cert_file']); + + $cert['domain'] = $idna_convert->encode($cert['domain']); + + $adminCustomerLink = ""; + if (AREA == 'admin') { + if (! empty($cert['loginname'])) { + $adminCustomerLink = ' (
' . $cert['loginname'] . ')'; + } + } + + if ($cert_data) { + $validFrom = date('d.m.Y H:i:s', $cert_data['validFrom_time_t']); + $validTo = date('d.m.Y H:i:s', $cert_data['validTo_time_t']); + + $isValid = true; + if ($cert_data['validTo_time_t'] < time()) { + $isValid = false; + } + + $row = htmlentities_array($cert); + eval("\$certificates.=\"" . getTemplate("ssl_certificates/certs_cert", true) . "\";"); + } else { + $message = sprintf($lng['domains']['ssl_certificate_error'], $cert['domain']); + eval("\$certificates.=\"" . getTemplate("ssl_certificates/certs_error", true) . "\";"); + } + } else { + continue; + } + } +} +eval("echo \"" . getTemplate("ssl_certificates/certs_list", true) . "\";"); diff --git a/templates/Sparkle/ssl_certificates/certs_cert.tpl b/templates/Sparkle/ssl_certificates/certs_cert.tpl new file mode 100644 index 00000000..90d502fa --- /dev/null +++ b/templates/Sparkle/ssl_certificates/certs_cert.tpl @@ -0,0 +1,33 @@ +class="domain-expired"> + + {$row['domain']} + {$adminCustomerLink} + + + {$cert_data['subject']['CN']} + + + {$cert_data['issuer']['O']} + + + {$validFrom} + + + + {$validTo} + + + + + + {$lng['panel']['edit']} +   + + + {$lng['panel']['letsencrypt']} + + + {$lng['panel']['delete']} + + + diff --git a/templates/Sparkle/ssl_certificates/certs_error.tpl b/templates/Sparkle/ssl_certificates/certs_error.tpl new file mode 100644 index 00000000..e573c664 --- /dev/null +++ b/templates/Sparkle/ssl_certificates/certs_error.tpl @@ -0,0 +1,3 @@ + + {$message} + diff --git a/templates/Sparkle/ssl_certificates/certs_list.tpl b/templates/Sparkle/ssl_certificates/certs_list.tpl new file mode 100644 index 00000000..b474940e --- /dev/null +++ b/templates/Sparkle/ssl_certificates/certs_list.tpl @@ -0,0 +1,57 @@ + $header +
+
+

+   + {$lng['domains']['ssl_certificates']} +

+
+ + +
+
{$lng['success']['success']}
+
+ $success_message +
+
+
+ +
+ +
+ + + +
+ {$searchcode} +
+ + + + + + + + + + + + + + + + + + + + + + + {$certificates} + +
{$lng['domains']['domainname']} {$arrowcode['d.domain']}Certificate forIssuerValid fromValid until{$lng['panel']['options']}
{$pagingcode}
+
+ +
+
+$footer From 06a55ef91e280687e6a85912769dc35aeb84a151 Mon Sep 17 00:00:00 2001 From: Grigory Morozov Date: Mon, 26 Sep 2016 22:52:03 +0700 Subject: [PATCH 0196/1335] Prevent duplicates in ipsandports table --- install/froxlor.sql | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/install/froxlor.sql b/install/froxlor.sql index f30c943a..a91c7023 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -280,7 +280,8 @@ CREATE TABLE `panel_ipsandports` ( `default_vhostconf_domain` text, `ssl_cert_chainfile` varchar(255) NOT NULL, `docroot` varchar(255) NOT NULL default '', - PRIMARY KEY (`id`) + PRIMARY KEY (`id`), + UNIQUE KEY `ip_port` (`ip`,`port`) ) ENGINE=MyISAM CHARSET=utf8 COLLATE=utf8_general_ci; From dc428b7de2b8aa25f46fed8ac08e833564cd4ba5 Mon Sep 17 00:00:00 2001 From: Grigory Morozov Date: Mon, 26 Sep 2016 23:23:11 +0700 Subject: [PATCH 0197/1335] Adding unique key to ipsandports on upgrade Please guide on checking the correct Froxlor version, not sure how it works --- install/updates/froxlor/0.9/update_0.9.inc.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index 7da2b984..750d05b3 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -3321,6 +3321,14 @@ if (isFroxlorVersion('0.9.35.1')) { updateToVersion('0.9.36'); } +if (isFroxlorVersion('0.9.37')) { + showUpdateStep("Updating from 0.9.37 to 0.9.38", false); + showUpdateStep("Adding unique key to ipsandports table"); + Database::query("ALTER TABLE `" . TABLE_PANEL_IPSANDPORTS . "` ADD UNIQUE KEY `ip_port` (`ip`,`port`)"); + lastStepStatus(0); + updateToVersion('0.9.38'); +} + if (isDatabaseVersion('201604270')) { showUpdateStep("Adding new dns related tables and settings"); From 2e198dbe5cda1ad74ad7904e0c4d63551241c11d Mon Sep 17 00:00:00 2001 From: FliegenKLATSCH Date: Sun, 1 May 2016 18:01:05 +0200 Subject: [PATCH 0198/1335] fix default value for domain reg./term. date --- admin_domains.php | 12 ++++++++++++ install/froxlor.sql | 8 ++++---- install/updates/froxlor/0.9/update_0.9.inc.php | 11 +++++++++++ lib/version.inc.php | 2 +- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/admin_domains.php b/admin_domains.php index 0e7d4fbd..5bddacd1 100644 --- a/admin_domains.php +++ b/admin_domains.php @@ -393,6 +393,9 @@ if ($page == 'domains' || $page == 'overview') { '0', '' )); + if ($registration_date == '0000-00-00') { + $registration_date = null; + } $termination_date = trim($_POST['termination_date']); $termination_date = validate($termination_date, 'termination_date', '/^(19|20)\d\d[-](0[1-9]|1[012])[-](0[1-9]|[12][0-9]|3[01])$/', '', array( @@ -400,6 +403,9 @@ if ($page == 'domains' || $page == 'overview') { '0', '' )); + if ($termination_date == '0000-00-00') { + $termination_date = null; + } if ($userinfo['change_serversettings'] == '1') { @@ -1206,12 +1212,18 @@ if ($page == 'domains' || $page == 'overview') { '0', '' )); + if ($registration_date == '0000-00-00') { + $registration_date = null; + } $termination_date = trim($_POST['termination_date']); $termination_date = validate($termination_date, 'termination_date', '/^(19|20)\d\d[-](0[1-9]|1[012])[-](0[1-9]|[12][0-9]|3[01])$/', '', array( '0000-00-00', '0', '' )); + if ($termination_date == '0000-00-00') { + $termination_date = null; + } $isemaildomain = 0; if (isset($_POST['isemaildomain'])) { diff --git a/install/froxlor.sql b/install/froxlor.sql index f30c943a..cd6daeb7 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -66,7 +66,7 @@ CREATE TABLE `mail_virtual` ( `id` int(11) NOT NULL auto_increment, `email` varchar(255) NOT NULL default '', `email_full` varchar(255) NOT NULL default '', - `destination` text NOT NULL, + `destination` text NOT NULL default '', `domainid` int(11) NOT NULL default '0', `customerid` int(11) NOT NULL default '0', `popaccountid` int(11) NOT NULL default '0', @@ -245,8 +245,8 @@ CREATE TABLE `panel_domains` ( `deactivated` tinyint(1) NOT NULL default '0', `bindserial` varchar(10) NOT NULL default '2000010100', `add_date` int( 11 ) NOT NULL default '0', - `registration_date` date NOT NULL, - `termination_date` date NOT NULL, + `registration_date` date DEFAULT NULL, + `termination_date` date DEFAULT NULL, `phpsettingid` INT( 11 ) UNSIGNED NOT NULL DEFAULT '1', `mod_fcgid_starter` int(4) default '-1', `mod_fcgid_maxrequests` int(4) default '-1', @@ -573,7 +573,7 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES ('panel', 'password_special_char_required', '0'), ('panel', 'password_special_char', '!?<>§$%+#=@'), ('panel', 'version', '0.9.37'), - ('panel', 'db_version', '201609200'); + ('panel', 'db_version', '201609240'); DROP TABLE IF EXISTS `panel_tasks`; diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index 7da2b984..de3c2de8 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -3475,3 +3475,14 @@ if (isDatabaseVersion('201609120')) { updateToDbVersion('201609200'); } + +if (isDatabaseVersion('201609200')) { + + showUpdateStep("Changing tables to be more mysql strict-mode compatible"); + Database::query("ALTER TABLE `".TABLE_MAIL_VIRTUAL."` CHANGE `destination` `destination` TEXT NOT NULL DEFAULT '';"); + Database::query("ALTER TABLE `".TABLE_PANEL_DOMAINS."` CHANGE `registration_date` `registration_date` DATE NULL DEFAULT NULL;"); + Database::query("ALTER TABLE `".TABLE_PANEL_DOMAINS."` CHANGE `termination_date` `termination_date` DATE NULL DEFAULT NULL;"); + lastStepStatus(0); + + updateToDbVersion('201609240'); +} diff --git a/lib/version.inc.php b/lib/version.inc.php index 8a1d56d3..6f16ab32 100644 --- a/lib/version.inc.php +++ b/lib/version.inc.php @@ -19,7 +19,7 @@ $version = '0.9.37'; // Database version (YYYYMMDDC where C is a daily counter) -$dbversion = '201609200'; +$dbversion = '201609240'; // Distribution branding-tag (used for Debian etc.) $branding = ''; From 3ff85e167c158720297c6f7f1a8611850d3ef958 Mon Sep 17 00:00:00 2001 From: Grigory Morozov Date: Tue, 27 Sep 2016 23:08:30 +0700 Subject: [PATCH 0199/1335] Update update_0.9.inc.php --- install/updates/froxlor/0.9/update_0.9.inc.php | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index 750d05b3..cc2463b7 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -3321,14 +3321,6 @@ if (isFroxlorVersion('0.9.35.1')) { updateToVersion('0.9.36'); } -if (isFroxlorVersion('0.9.37')) { - showUpdateStep("Updating from 0.9.37 to 0.9.38", false); - showUpdateStep("Adding unique key to ipsandports table"); - Database::query("ALTER TABLE `" . TABLE_PANEL_IPSANDPORTS . "` ADD UNIQUE KEY `ip_port` (`ip`,`port`)"); - lastStepStatus(0); - updateToVersion('0.9.38'); -} - if (isDatabaseVersion('201604270')) { showUpdateStep("Adding new dns related tables and settings"); @@ -3483,3 +3475,10 @@ if (isDatabaseVersion('201609120')) { updateToDbVersion('201609200'); } + +if (isDatabaseVersion('201609200')) { + showUpdateStep("Adding unique key to ipsandports table"); + Database::query("ALTER TABLE `" . TABLE_PANEL_IPSANDPORTS . "` ADD UNIQUE KEY `ip_port` (`ip`,`port`)"); + lastStepStatus(0); + updateToDbVersion('201609270'); +} From 4a3e02c1f0f92c3fbb32ef0bef46b030e744ae71 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Fri, 7 Oct 2016 11:01:45 +0200 Subject: [PATCH 0200/1335] add HSTS for domains (admin-side) and froxlor-vhost; fixes #1660 Signed-off-by: Michael Kaufmann (d00p) --- actions/admin/settings/131.ssl.php | 28 +- install/froxlor.sql | 2 +- .../updates/froxlor/0.9/update_0.9.inc.php | 27 +- lib/classes/output/class.htmlform.php | 27 + .../admin/domains/formfield.domains_add.php | 104 +++- .../admin/domains/formfield.domains_edit.php | 104 +++- lib/init.php | 23 +- lib/version.inc.php | 2 +- lng/english.lng.php | 9 +- lng/german.lng.php | 9 +- .../jobs/cron_tasks.inc.http.10.apache.php | 2 +- .../jobs/cron_tasks.inc.http.20.lighttpd.php | 2 +- scripts/jobs/cron_tasks.inc.http.30.nginx.php | 589 +++++++++--------- 13 files changed, 534 insertions(+), 394 deletions(-) diff --git a/actions/admin/settings/131.ssl.php b/actions/admin/settings/131.ssl.php index 56c27cd5..dc03f926 100644 --- a/actions/admin/settings/131.ssl.php +++ b/actions/admin/settings/131.ssl.php @@ -153,7 +153,33 @@ return array( 'type' => 'bool', 'default' => false, 'save_method' => 'storeSettingField' - ) + ), + 'system_hsts_maxage' => array( + 'label' => $lng['admin']['domain_hsts_maxage'], + 'settinggroup' => 'system', + 'varname' => 'hsts_maxage', + 'type' => 'int', + 'int_min' => 0, + 'int_max' => 94608000, // 3-years + 'default' => 0, + 'save_method' => 'storeSettingField' + ), + 'system_hsts_incsub' => array( + 'label' => $lng['admin']['domain_hsts_incsub'], + 'settinggroup' => 'system', + 'varname' => 'hsts_incsub', + 'type' => 'bool', + 'default' => false, + 'save_method' => 'storeSettingField' + ), + 'system_hsts_preload' => array( + 'label' => $lng['admin']['domain_hsts_preload'], + 'settinggroup' => 'system', + 'varname' => 'hsts_preload', + 'type' => 'bool', + 'default' => false, + 'save_method' => 'storeSettingField' + ), ) ) ) diff --git a/install/froxlor.sql b/install/froxlor.sql index cd6daeb7..63ce8661 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -573,7 +573,7 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES ('panel', 'password_special_char_required', '0'), ('panel', 'password_special_char', '!?<>§$%+#=@'), ('panel', 'version', '0.9.37'), - ('panel', 'db_version', '201609240'); + ('panel', 'db_version', '201610070'); DROP TABLE IF EXISTS `panel_tasks`; diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index de3c2de8..66fea160 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -3478,11 +3478,26 @@ if (isDatabaseVersion('201609120')) { if (isDatabaseVersion('201609200')) { - showUpdateStep("Changing tables to be more mysql strict-mode compatible"); - Database::query("ALTER TABLE `".TABLE_MAIL_VIRTUAL."` CHANGE `destination` `destination` TEXT NOT NULL DEFAULT '';"); - Database::query("ALTER TABLE `".TABLE_PANEL_DOMAINS."` CHANGE `registration_date` `registration_date` DATE NULL DEFAULT NULL;"); - Database::query("ALTER TABLE `".TABLE_PANEL_DOMAINS."` CHANGE `termination_date` `termination_date` DATE NULL DEFAULT NULL;"); - lastStepStatus(0); + showUpdateStep("Changing tables to be more mysql strict-mode compatible"); + Database::query("ALTER TABLE `".TABLE_MAIL_VIRTUAL."` CHANGE `destination` `destination` TEXT NOT NULL DEFAULT '';"); + Database::query("ALTER TABLE `".TABLE_PANEL_DOMAINS."` CHANGE `registration_date` `registration_date` DATE NULL DEFAULT NULL;"); + Database::query("ALTER TABLE `".TABLE_PANEL_DOMAINS."` CHANGE `termination_date` `termination_date` DATE NULL DEFAULT NULL;"); + lastStepStatus(0); - updateToDbVersion('201609240'); + updateToDbVersion('201609240'); +} + +if (isDatabaseVersion('201609240')) { + + showUpdateStep("Add HSTS settings for froxlor-vhost"); + Settings::AddNew("system.hsts_maxage", 0); + Settings::AddNew("system.hsts_incsub", 0); + Settings::AddNew("system.hsts_preload", 0); + lastStepStatus(0); + + showUpdateStep("Settings HSTS default values for all domains (deactivated)"); + Database::query("UPDATE `".TABLE_PANEL_DOMAINS."` SET `hsts_sub` = '0', `hsts_preload` = '0';"); + lastStepStatus(0); + + updateToDbVersion('201610070'); } diff --git a/lib/classes/output/class.htmlform.php b/lib/classes/output/class.htmlform.php index 2bc6edca..6f1c4a3b 100644 --- a/lib/classes/output/class.htmlform.php +++ b/lib/classes/output/class.htmlform.php @@ -122,6 +122,8 @@ class htmlform return self::_checkbox($fieldname, $data); break; case 'file': return self::_file($fieldname, $data); break; + case 'int': + return self::_int($fieldname, $data); break; } } @@ -313,4 +315,29 @@ class htmlform return $return; } + private static function _int($fieldname = '', $data = array()) + { + $return = ''; + $extras = ''; + if(isset($data['int_min'])) { + $extras .= ' min="'.$data['int_min'].'"'; + } + if(isset($data['int_max'])) { + $extras .= ' max="'.$data['int_max'].'"'; + } + + // add support to save reloaded forms + if (isset($data['value'])) { + $value = $data['value']; + } elseif (isset($_SESSION['requestData'][$fieldname])) { + $value = $_SESSION['requestData'][$fieldname]; + } else { + $value = ''; + } + + $type = 'number'; + $ulfield = ''; + eval("\$return = \"" . getTemplate("misc/form/input_text", "1") . "\";"); + return $return; + } } diff --git a/lib/formfields/admin/domains/formfield.domains_add.php b/lib/formfields/admin/domains/formfield.domains_add.php index c85d6500..15ca70e4 100644 --- a/lib/formfields/admin/domains/formfield.domains_add.php +++ b/lib/formfields/admin/domains/formfield.domains_add.php @@ -101,40 +101,6 @@ return array( 'is_array' => 1, 'mandatory' => true ), - 'ssl_ipandport' => array( - 'label' => $lng['domains']['ipandport_ssl_multi']['title'], - 'desc' => $lng['domains']['ipandport_ssl_multi']['description'], - 'type' => 'checkbox', - 'values' => $ssl_ipsandports, - 'value' => '', - 'is_array' => 1 - ), - 'ssl_redirect' => array( - 'visible' => (Settings::Get('system.use_ssl') == '1' ? ($ssl_ipsandports != '' ? true : false) : false), - 'label' => $lng['domains']['ssl_redirect']['title'], - 'desc' => $lng['domains']['ssl_redirect']['description'], - 'type' => 'checkbox', - 'values' => array( - array ('label' => $lng['panel']['yes'], 'value' => '1') - ), - 'value' => array() - ), - 'letsencrypt' => array( - 'visible' => (Settings::Get('system.use_ssl') == '1' ? (Settings::Get('system.leenabled') == '1' ? ($ssl_ipsandports != '' ? true : false) : false) : false), - 'label' => $lng['admin']['letsencrypt']['title'], - 'desc' => $lng['admin']['letsencrypt']['description'], - 'type' => 'checkbox', - 'values' => array( - array ('label' => $lng['panel']['yes'], 'value' => '1') - ), - 'value' => array() - ), - 'no_ssl_available_info' => array( - 'visible' => (Settings::Get('system.use_ssl') == '1' ? ($ssl_ipsandports == '' ? true : false) : false), - 'label' => 'SSL', - 'type' => 'label', - 'value' => $lng['panel']['nosslipsavailable'] - ), 'selectserveralias' => array( 'label' => $lng['admin']['selectserveralias'], 'desc' => $lng['admin']['selectserveralias_desc'], @@ -161,6 +127,76 @@ return array( ) ) ), + 'section_bssl' => array( + 'title' => $lng['admin']['webserversettings_ssl'], + 'image' => 'icons/domain_add.png', + 'visible' => Settings::Get('system.use_ssl') == '1' ? true : false, + 'fields' => array( + 'ssl_ipandport' => array( + 'label' => $lng['domains']['ipandport_ssl_multi']['title'], + 'desc' => $lng['domains']['ipandport_ssl_multi']['description'], + 'type' => 'checkbox', + 'values' => $ssl_ipsandports, + 'value' => '', + 'is_array' => 1 + ), + 'ssl_redirect' => array( + 'visible' => ($ssl_ipsandports != '' ? true : false), + 'label' => $lng['domains']['ssl_redirect']['title'], + 'desc' => $lng['domains']['ssl_redirect']['description'], + 'type' => 'checkbox', + 'values' => array( + array ('label' => $lng['panel']['yes'], 'value' => '1') + ), + 'value' => array() + ), + 'letsencrypt' => array( + 'visible' => (Settings::Get('system.leenabled') == '1' ? ($ssl_ipsandports != '' ? true : false) : false), + 'label' => $lng['admin']['letsencrypt']['title'], + 'desc' => $lng['admin']['letsencrypt']['description'], + 'type' => 'checkbox', + 'values' => array( + array ('label' => $lng['panel']['yes'], 'value' => '1') + ), + 'value' => array() + ), + 'no_ssl_available_info' => array( + 'visible' => ($ssl_ipsandports == '' ? true : false), + 'label' => 'SSL', + 'type' => 'label', + 'value' => $lng['panel']['nosslipsavailable'] + ), + 'hsts_maxage' => array( + 'visible' => ($ssl_ipsandports != '' ? true : false), + 'label' => $lng['admin']['domain_hsts_maxage']['title'], + 'desc' => $lng['admin']['domain_hsts_maxage']['description'], + 'type' => 'int', + 'int_min' => 0, + 'int_max' => 94608000, // 3-years + 'value' => 0 + ), + 'hsts_incsub' => array( + 'visible' => ($ssl_ipsandports != '' ? true : false), + 'label' => $lng['admin']['domain_hsts_incsub']['title'], + 'desc' => $lng['admin']['domain_hsts_incsub']['description'], + 'type' => 'checkbox', + 'values' => array( + array ('label' => $lng['panel']['yes'], 'value' => '1') + ), + 'value' => array() + ), + 'hsts_preload' => array( + 'visible' => ($ssl_ipsandports != '' ? true : false), + 'label' => $lng['admin']['domain_hsts_preload']['title'], + 'desc' => $lng['admin']['domain_hsts_preload']['description'], + 'type' => 'checkbox', + 'values' => array( + array ('label' => $lng['panel']['yes'], 'value' => '1') + ), + 'value' => array() + ), + ), + ), 'section_c' => array( 'title' => $lng['admin']['phpserversettings'], 'image' => 'icons/domain_add.png', diff --git a/lib/formfields/admin/domains/formfield.domains_edit.php b/lib/formfields/admin/domains/formfield.domains_edit.php index 6837da0a..11b6c6bc 100644 --- a/lib/formfields/admin/domains/formfield.domains_edit.php +++ b/lib/formfields/admin/domains/formfield.domains_edit.php @@ -113,40 +113,6 @@ return array( 'is_array' => 1, 'mandatory' => true ), - 'ssl_ipandport' => array( - 'label' => $lng['domains']['ipandport_ssl_multi']['title'], - 'desc' => $lng['domains']['ipandport_ssl_multi']['description'], - 'type' => 'checkbox', - 'values' => $ssl_ipsandports, - 'value' => $usedips, - 'is_array' => 1 - ), - 'ssl_redirect' => array( - 'visible' => (Settings::Get('system.use_ssl') == '1' ? ($ssl_ipsandports != '' ? true : false) : false), - 'label' => $lng['domains']['ssl_redirect']['title'], - 'desc' => $lng['domains']['ssl_redirect']['description'] . ($result['temporary_ssl_redirect'] > 1 ? $lng['domains']['ssl_redirect_temporarilydisabled'] : ''), - 'type' => 'checkbox', - 'values' => array( - array ('label' => $lng['panel']['yes'], 'value' => '1') - ), - 'value' => array($result['ssl_redirect']) - ), - 'letsencrypt' => array( - 'visible' => (Settings::Get('system.use_ssl') == '1' ? (Settings::Get('system.leenabled') == '1' ? ($ssl_ipsandports != '' ? true : false) : false) : false), - 'label' => $lng['admin']['letsencrypt']['title'], - 'desc' => $lng['admin']['letsencrypt']['description'], - 'type' => 'checkbox', - 'values' => array( - array ('label' => $lng['panel']['yes'], 'value' => '1') - ), - 'value' => array($result['letsencrypt']) - ), - 'no_ssl_available_info' => array( - 'visible' => (Settings::Get('system.use_ssl') == '1' ? ($ssl_ipsandports == '' ? true : false) : false), - 'label' => 'SSL', - 'type' => 'label', - 'value' => $lng['panel']['nosslipsavailable'] - ), 'selectserveralias' => array( 'label' => $lng['admin']['selectserveralias'], 'desc' => $lng['admin']['selectserveralias_desc'], @@ -184,6 +150,76 @@ return array( ) ) ), + 'section_bssl' => array( + 'title' => $lng['admin']['webserversettings_ssl'], + 'image' => 'icons/domain_edit.png', + 'visible' => Settings::Get('system.use_ssl') == '1' ? true : false, + 'fields' => array( + 'ssl_ipandport' => array( + 'label' => $lng['domains']['ipandport_ssl_multi']['title'], + 'desc' => $lng['domains']['ipandport_ssl_multi']['description'], + 'type' => 'checkbox', + 'values' => $ssl_ipsandports, + 'value' => $usedips, + 'is_array' => 1 + ), + 'ssl_redirect' => array( + 'visible' => ($ssl_ipsandports != '' ? true : false), + 'label' => $lng['domains']['ssl_redirect']['title'], + 'desc' => $lng['domains']['ssl_redirect']['description'] . ($result['temporary_ssl_redirect'] > 1 ? $lng['domains']['ssl_redirect_temporarilydisabled'] : ''), + 'type' => 'checkbox', + 'values' => array( + array ('label' => $lng['panel']['yes'], 'value' => '1') + ), + 'value' => array($result['ssl_redirect']) + ), + 'letsencrypt' => array( + 'visible' => (Settings::Get('system.leenabled') == '1' ? ($ssl_ipsandports != '' ? true : false) : false), + 'label' => $lng['admin']['letsencrypt']['title'], + 'desc' => $lng['admin']['letsencrypt']['description'], + 'type' => 'checkbox', + 'values' => array( + array ('label' => $lng['panel']['yes'], 'value' => '1') + ), + 'value' => array($result['letsencrypt']) + ), + 'no_ssl_available_info' => array( + 'visible' => ($ssl_ipsandports == '' ? true : false), + 'label' => 'SSL', + 'type' => 'label', + 'value' => $lng['panel']['nosslipsavailable'] + ), + 'hsts_maxage' => array( + 'visible' => ($ssl_ipsandports != '' ? true : false), + 'label' => $lng['admin']['domain_hsts_maxage']['title'], + 'desc' => $lng['admin']['domain_hsts_maxage']['description'], + 'type' => 'int', + 'int_min' => 0, + 'int_max' => 94608000, // 3-years + 'value' => $result['hsts'] + ), + 'hsts_incsub' => array( + 'visible' => ($ssl_ipsandports != '' ? true : false), + 'label' => $lng['admin']['domain_hsts_incsub']['title'], + 'desc' => $lng['admin']['domain_hsts_incsub']['description'], + 'type' => 'checkbox', + 'values' => array( + array ('label' => $lng['panel']['yes'], 'value' => '1') + ), + 'value' => array($result['hsts_sub']) + ), + 'hsts_preload' => array( + 'visible' => ($ssl_ipsandports != '' ? true : false), + 'label' => $lng['admin']['domain_hsts_preload']['title'], + 'desc' => $lng['admin']['domain_hsts_preload']['description'], + 'type' => 'checkbox', + 'values' => array( + array ('label' => $lng['panel']['yes'], 'value' => '1') + ), + 'value' => array($result['hsts_preload']) + ), + ) + ), 'section_c' => array( 'title' => $lng['admin']['phpserversettings'], 'image' => 'icons/domain_edit.png', diff --git a/lib/init.php b/lib/init.php index 1c116a2c..b4e4c018 100644 --- a/lib/init.php +++ b/lib/init.php @@ -39,11 +39,6 @@ header("X-XSS-Protection: 1; mode=block"); // Don't allow to load Froxlor in an iframe to prevent i.e. clickjacking header("X-Frame-Options: DENY"); -// If Froxlor was called via HTTPS -> enforce it for the next time -if (isset($_SERVER['HTTPS']) && (strtolower($_SERVER['HTTPS']) != 'off')) { - header("Strict-Transport-Security: max-age=15768000"); -} - // Internet Explorer shall not guess the Content-Type, see: // http://blogs.msdn.com/ie/archive/2008/07/02/ie8-security-part-v-comprehensive-protection.aspx header("X-Content-Type-Options: nosniff"); @@ -127,6 +122,24 @@ require FROXLOR_INSTALL_DIR.'/lib/tables.inc.php'; */ $idna_convert = new idna_convert_wrapper(); +/** + * If Froxlor was called via HTTPS -> enforce it for the next time by settings HSTS header according to settings + */ +if (isset($_SERVER['HTTPS']) && (strtolower($_SERVER['HTTPS']) != 'off')) { + $maxage = Settings::Get('system.hsts_maxage'); + if (empty($maxage)) { + $maxage = 0; + } + $hsts_header = "Strict-Transport-Security: max-age=".$maxage; + if (Settings::Get('system.hsts_incsub') == '1') { + $hsts_header .= "; includeSubDomains"; + } + if (Settings::Get('system.hsts_preload') == '1') { + $hsts_header .= "; preload"; + } + header($hsts_header); +} + /** * disable magic_quotes_runtime if enabled */ diff --git a/lib/version.inc.php b/lib/version.inc.php index 6f16ab32..52a85722 100644 --- a/lib/version.inc.php +++ b/lib/version.inc.php @@ -19,7 +19,7 @@ $version = '0.9.37'; // Database version (YYYYMMDDC where C is a daily counter) -$dbversion = '201609240'; +$dbversion = '201610070'; // Distribution branding-tag (used for Debian etc.) $branding = ''; diff --git a/lng/english.lng.php b/lng/english.lng.php index 6859fd06..036cf118 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -2027,7 +2027,7 @@ $lng['error']['dns_record_toolong'] = 'Records/labels can only be up to 63 chara $lng['serversettings']['panel_customer_hide_options']['title'] = 'Hide menu items and traffic charts in customer panel'; $lng['serversettings']['panel_customer_hide_options']['description'] = 'Select items to hide in customer panel. To select multiple options, hold down CTRL while selecting.'; -// Added in froxlor 0.9.37.1 +// Added in froxlor 0.9.38-rc1 $lng['serversettings']['allow_allow_customer_shell']['title'] = 'Allow customers to enable shell access for ftp-users'; $lng['serversettings']['allow_allow_customer_shell']['description'] = 'Please note: Shell access allows the user to execute various binaries on your system. Use with extrem caution. Please only activate this if you REALLY know what you are doing!!!'; $lng['serversettings']['available_shells']['title'] = 'List of available shells'; @@ -2055,3 +2055,10 @@ $lng['domains']['ssl_certificates'] = 'SSL certificates'; $lng['domains']['ssl_certificate_removed'] = 'The certificate with the id #%s has been removed successfully'; $lng['domains']['ssl_certificate_error'] = "Error reading certificate for domain: %s"; $lng['domains']['no_ssl_certificates'] = "There are no domains with SSL certificate"; +$lng['admin']['webserversettings_ssl'] = 'Webserver SSL settings'; +$lng['admin']['domain_hsts_maxage']['title'] = 'HTTP Strict Transport Security (HSTS)'; +$lng['admin']['domain_hsts_maxage']['description'] = 'Specify the max-age value for the Strict-Transport-Security header
The value 0 will disable HSTS for the domain. Most user set a value of 31536000 (one year).'; +$lng['admin']['domain_hsts_incsub']['title'] = 'Include HSTS for any subdomain'; +$lng['admin']['domain_hsts_incsub']['description'] = 'The optional "includeSubDomains" directive, if present, signals the UA that the HSTS Policy applies to this HSTS Host as well as any subdomains of the host\'s domain name.'; +$lng['admin']['domain_hsts_preload']['title'] = 'Include domain in HSTS preload list'; +$lng['admin']['domain_hsts_preload']['description'] = 'If you would like this domain to be included in the HSTS preload list maintained by Chrome (and used by Firefox and Safari), then use activate this.
Sending the preload directive from your site can have PERMANENT CONSEQUENCES and prevent users from accessing your site and any of its subdomains.
Please read the details at hstspreload.appspot.com/#removal before sending the header with "preload".'; diff --git a/lng/german.lng.php b/lng/german.lng.php index fc6d9489..0af16748 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -1679,7 +1679,7 @@ $lng['error']['dns_record_toolong'] = 'Records/Labels können maximal 63 Zeichen $lng['serversettings']['panel_customer_hide_options']['title'] = 'Menüpunkte und Traffic-Charts im Kundenbereich ausblenden'; $lng['serversettings']['panel_customer_hide_options']['description'] = 'Wählen Sie hier die gewünschten Menüpunkte und Traffic-Charts aus, welche im Kundenbereich ausgeblendet werden sollen. Für Mehrfachauswahl, halten Sie während der Auswahl STRG gedrückt.'; -// Added in froxlor 0.9.37.1 +// Added in froxlor 0.9.38-rc1 $lng['serversettings']['allow_allow_customer_shell']['title'] = 'Erlaube Kunden für FTP Benutzer eine Shell auszuwählen'; $lng['serversettings']['allow_allow_customer_shell']['description'] = 'Bitte beachten: Shell Zugriff gestattet dem Benutzer verschiedene Programme auf Ihrem System auszuführen. Mit großer Vorsicht verwenden. Bitte aktiviere dies nur wenn WIRKLICH bekannt ist, was das bedeutet!!!'; $lng['serversettings']['available_shells']['title'] = 'Liste der verfügbaren Shells'; @@ -1706,3 +1706,10 @@ $lng['domains']['ssl_certificates'] = 'SSL Zertifikate'; $lng['domains']['ssl_certificate_removed'] = 'Das Zertifikat mit der ID #%s wurde erfolgreich gelöscht.'; $lng['domains']['ssl_certificate_error'] = "Fehler beim Lesen des Zertifikats für die Domain: %s"; $lng['domains']['no_ssl_certificates'] = "Es wurden keine SSL-Zertifikate gefunden"; +$lng['admin']['webserversettings_ssl'] = 'Webserver SSL-Einstellungen'; +$lng['admin']['domain_hsts_maxage']['title'] = 'HTTP Strict Transport Security (HSTS)'; +$lng['admin']['domain_hsts_maxage']['description'] = '"max-age" Wert für den Strict-Transport-Security Header
Der Wert 0 deaktiviert HSTS für diese Domain. Meist wird der Wert 31536000 gerne genutzt (ein Jahr).'; +$lng['admin']['domain_hsts_incsub']['title'] = 'Inkludiere HSTS für jede Subdomain'; +$lng['admin']['domain_hsts_incsub']['description'] = 'Die optionale "includeSubDomains" Direktive, wenn vorhanden, signalisiert dem UA, dass die HSTS that the HSTS Regel für diese Domain und auch jede Subdomain dieser gilt.'; +$lng['admin']['domain_hsts_preload']['title'] = 'Füge Domain in die HSTS preload Liste hinzu'; +$lng['admin']['domain_hsts_preload']['description'] = 'Wenn die Domain in die HSTS preload Liste, verwaltet von Chrome (und genutzt von Firefox und Safari), hinzugefügt werden soll, dann aktiviere diese Einstellung.
Die preload-Direktive zu senden kann PERMANTENTE KONSEQUENZEN haben und dazu führen, dass Benutzer auf diese Domain und auch Subdomains nicht zugreifen können.
Beachte Details unter hstspreload.appspot.com/#removal bevor ein Header mit "preload" gesendet wird.'; diff --git a/scripts/jobs/cron_tasks.inc.http.10.apache.php b/scripts/jobs/cron_tasks.inc.http.10.apache.php index 4d6acb46..07ec5752 100644 --- a/scripts/jobs/cron_tasks.inc.http.10.apache.php +++ b/scripts/jobs/cron_tasks.inc.http.10.apache.php @@ -836,7 +836,7 @@ class apache extends HttpConfigBase $vhost_content .= ' SSLCertificateChainFile ' . makeCorrectFile($domain['ssl_cert_chainfile']) . "\n"; } - if ($domain['hsts'] > 0) { + if ($domain['hsts'] >= 0) { $vhost_content .= ' ' . "\n"; $vhost_content .= ' Header always set Strict-Transport-Security "max-age=' . $domain['hsts']; if ($domain['hsts_sub'] == 1) { diff --git a/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php b/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php index 90b70cc0..651f80db 100644 --- a/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php +++ b/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php @@ -519,7 +519,7 @@ class lighttpd extends HttpConfigBase $ssl_settings .= 'ssl.ca-file = "' . makeCorrectFile($domain['ssl_ca_file']) . '"' . "\n"; } - if ($domain['hsts'] > 0) { + if ($domain['hsts'] >= 0) { $vhost_content .= '$HTTP["scheme"] == "https" { setenv.add-response-header = ( "Strict-Transport-Security" => "max-age=' . $domain['hsts']; if ($domain['hsts_sub'] == 1) { diff --git a/scripts/jobs/cron_tasks.inc.http.30.nginx.php b/scripts/jobs/cron_tasks.inc.http.30.nginx.php index 81263633..3a79d610 100644 --- a/scripts/jobs/cron_tasks.inc.http.30.nginx.php +++ b/scripts/jobs/cron_tasks.inc.http.30.nginx.php @@ -1,4 +1,7 @@ - (2010-) - * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt - * @package Cron + * @copyright (c) the authors + * @author Froxlor team (2010-) + * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt + * @package Cron * */ -require_once(dirname(__FILE__).'/../classes/class.HttpConfigBase.php'); +require_once (dirname(__FILE__) . '/../classes/class.HttpConfigBase.php'); + +class nginx extends HttpConfigBase +{ -class nginx extends HttpConfigBase { private $logger = false; + private $idnaConvert = false; + private $nginx_server = array(); // protected protected $nginx_data = array(); + protected $needed_htpasswds = array(); + protected $auth_backend_loaded = false; + protected $htpasswds_data = array(); + protected $known_htpasswdsfilenames = array(); + protected $mod_accesslog_loaded = '0'; + protected $vhost_root_autoindex = false; + protected $known_vhostfilenames = array(); + /** * indicator whether a customer is deactivated or not * if yes, only the webroot will be generated @@ -39,42 +54,36 @@ class nginx extends HttpConfigBase { */ private $_deactivated = false; - public function __construct($logger, $idnaConvert, $nginx_server=array()) { + public function __construct($logger, $idnaConvert, $nginx_server = array()) + { $this->logger = $logger; $this->idnaConvert = $idnaConvert; $this->nginx_server = $nginx_server; } - - public function reload() { + public function reload() + { $this->logger->logAction(CRON_ACTION, LOG_INFO, 'nginx::reload: reloading nginx'); safe_exec(Settings::Get('system.apachereload_command')); /** * nginx does not auto-spawn fcgi-processes */ - if (Settings::Get('system.phpreload_command') != '' - && (int)Settings::Get('phpfpm.enabled') == 0 - ) { + if (Settings::Get('system.phpreload_command') != '' && (int) Settings::Get('phpfpm.enabled') == 0) { $this->logger->logAction(CRON_ACTION, LOG_INFO, 'nginx::reload: restarting php processes'); safe_exec(Settings::Get('system.phpreload_command')); - } elseif ((int)Settings::Get('phpfpm.enabled') == 1) { + } elseif ((int) Settings::Get('phpfpm.enabled') == 1) { $this->logger->logAction(CRON_ACTION, LOG_INFO, 'nginx::reload: reloading php-fpm'); safe_exec(escapeshellcmd(Settings::Get('phpfpm.reload'))); } } - /** * define a default ErrorDocument-statement, bug #unknown-yet */ - private function _createStandardErrorHandler() { - if (Settings::Get('defaultwebsrverrhandler.enabled') == '1' - && (Settings::Get('defaultwebsrverrhandler.err401') != '' - || Settings::Get('defaultwebsrverrhandler.err403') != '' - || Settings::Get('defaultwebsrverrhandler.err404') != '' - || Settings::Get('defaultwebsrverrhandler.err500') != '') - ) { + private function _createStandardErrorHandler() + { + if (Settings::Get('defaultwebsrverrhandler.enabled') == '1' && (Settings::Get('defaultwebsrverrhandler.err401') != '' || Settings::Get('defaultwebsrverrhandler.err403') != '' || Settings::Get('defaultwebsrverrhandler.err404') != '' || Settings::Get('defaultwebsrverrhandler.err500') != '')) { $vhosts_folder = ''; if (is_dir(Settings::Get('system.apacheconf_vhost'))) { $vhosts_folder = makeCorrectDir(Settings::Get('system.apacheconf_vhost')); @@ -84,33 +93,36 @@ class nginx extends HttpConfigBase { $vhosts_filename = makeCorrectFile($vhosts_folder . '/05_froxlor_default_errorhandler.conf'); - if (!isset($this->nginx_data[$vhosts_filename])) { + if (! isset($this->nginx_data[$vhosts_filename])) { $this->nginx_data[$vhosts_filename] = ''; } - $statusCodes = array('401', '403', '404', '500'); + $statusCodes = array( + '401', + '403', + '404', + '500' + ); foreach ($statusCodes as $statusCode) { if (Settings::Get('defaultwebsrverrhandler.err' . $statusCode) != '') { $defhandler = Settings::Get('defaultwebsrverrhandler.err' . $statusCode); - if (!validateUrl($defhandler)) { + if (! validateUrl($defhandler)) { $defhandler = makeCorrectFile($defhandler); } - $this->nginx_data[$vhosts_filename].= 'error_page ' . $statusCode . ' ' . $defhandler . ';' . "\n"; + $this->nginx_data[$vhosts_filename] .= 'error_page ' . $statusCode . ' ' . $defhandler . ';' . "\n"; } } } } + public function createVirtualHosts() + {} - public function createVirtualHosts() { - } + public function createFileDirOptions() + {} - - public function createFileDirOptions() { - } - - - public function createIpPort() { + public function createIpPort() + { $result_ipsandports_stmt = Database::query(" SELECT * FROM `" . TABLE_PANEL_IPSANDPORTS . "` ORDER BY `ip` ASC, `port` ASC "); @@ -126,7 +138,7 @@ class nginx extends HttpConfigBase { $this->logger->logAction(CRON_ACTION, LOG_INFO, 'nginx::createIpPort: creating ip/port settings for ' . $ip . ":" . $port); $vhost_filename = makeCorrectFile(Settings::Get('system.apacheconf_vhost') . '/10_froxlor_ipandport_' . trim(str_replace(':', '.', $row_ipsandports['ip']), '.') . '.' . $row_ipsandports['port'] . '.conf'); - if (!isset($this->nginx_data[$vhost_filename])) { + if (! isset($this->nginx_data[$vhost_filename])) { $this->nginx_data[$vhost_filename] = ''; } @@ -159,7 +171,7 @@ class nginx extends HttpConfigBase { 'adminid' => 1, /* first admin-user (superadmin) */ 'loginname' => 'froxlor.panel', 'documentroot' => $mypath, - 'parentdomainid' => 0, + 'parentdomainid' => 0 ); // override corresponding array values @@ -187,11 +199,11 @@ class nginx extends HttpConfigBase { /** * this HAS to be set for the default host in nginx or else no vhost will work */ - $this->nginx_data[$vhost_filename] .= "\t". 'listen ' . $ip . ':' . $port . ' default_server'. ($ssl_vhost == true ? ' ssl' : '') . ';' . "\n"; + $this->nginx_data[$vhost_filename] .= "\t" . 'listen ' . $ip . ':' . $port . ' default_server' . ($ssl_vhost == true ? ' ssl' : '') . ';' . "\n"; - $this->nginx_data[$vhost_filename] .= "\t".'# Froxlor default vhost' . "\n"; - $this->nginx_data[$vhost_filename] .= "\t".'server_name ' . Settings::Get('system.hostname') . ';' . "\n"; - $this->nginx_data[$vhost_filename] .= "\t".'access_log /var/log/nginx/access.log;' . "\n"; + $this->nginx_data[$vhost_filename] .= "\t" . '# Froxlor default vhost' . "\n"; + $this->nginx_data[$vhost_filename] .= "\t" . 'server_name ' . Settings::Get('system.hostname') . ';' . "\n"; + $this->nginx_data[$vhost_filename] .= "\t" . 'access_log /var/log/nginx/access.log;' . "\n"; $is_redirect = false; // check for SSL redirect @@ -205,26 +217,23 @@ class nginx extends HttpConfigBase { } else { $_sslport = $this->checkAlternativeSslPort(); $mypath = 'https://' . Settings::Get('system.hostname') . $_sslport . '/'; - $this->nginx_data[$vhost_filename] .= "\t".'return 301 '.$mypath.'$request_uri;'."\n"; + $this->nginx_data[$vhost_filename] .= "\t" . 'return 301 ' . $mypath . '$request_uri;' . "\n"; } } - if (!$is_redirect) { - $this->nginx_data[$vhost_filename] .= "\t".'root '.$mypath.';'."\n"; - $this->nginx_data[$vhost_filename] .= "\t".'index index.php index.html index.htm;'."\n\n"; - $this->nginx_data[$vhost_filename] .= "\t".'location / {'."\n"; - $this->nginx_data[$vhost_filename] .= "\t".'}'."\n"; + if (! $is_redirect) { + $this->nginx_data[$vhost_filename] .= "\t" . 'root ' . $mypath . ';' . "\n"; + $this->nginx_data[$vhost_filename] .= "\t" . 'index index.php index.html index.htm;' . "\n\n"; + $this->nginx_data[$vhost_filename] .= "\t" . 'location / {' . "\n"; + $this->nginx_data[$vhost_filename] .= "\t" . '}' . "\n"; } if ($row_ipsandports['specialsettings'] != '') { - $this->nginx_data[$vhost_filename].= $this->processSpecialConfigTemplate( - $row_ipsandports['specialsettings'], - array('domain'=> Settings::Get('system.hostname'), - 'loginname' => Settings::Get('phpfpm.vhost_httpuser'), - 'documentroot'=> $mypath), - $row_ipsandports['ip'], - $row_ipsandports['port'], - $row_ipsandports['ssl'] == '1'). "\n"; + $this->nginx_data[$vhost_filename] .= $this->processSpecialConfigTemplate($row_ipsandports['specialsettings'], array( + 'domain' => Settings::Get('system.hostname'), + 'loginname' => Settings::Get('phpfpm.vhost_httpuser'), + 'documentroot' => $mypath + ), $row_ipsandports['ip'], $row_ipsandports['port'], $row_ipsandports['ssl'] == '1') . "\n"; } /** @@ -232,13 +241,13 @@ class nginx extends HttpConfigBase { */ if ($row_ipsandports['ssl'] == '1') { $row_ipsandports['domain'] = Settings::Get('system.hostname'); - $this->nginx_data[$vhost_filename].=$this->composeSslSettings($row_ipsandports); + $this->nginx_data[$vhost_filename] .= $this->composeSslSettings($row_ipsandports); } - if (!$is_redirect) { + if (! $is_redirect) { $this->nginx_data[$vhost_filename] .= "\tlocation ~ \.php {\n"; $this->nginx_data[$vhost_filename] .= "\t\tfastcgi_split_path_info ^(.+\.php)(/.+)\$;\n"; - $this->nginx_data[$vhost_filename] .= "\t\tinclude ".Settings::Get('nginx.fastcgiparams').";\n"; + $this->nginx_data[$vhost_filename] .= "\t\tinclude " . Settings::Get('nginx.fastcgiparams') . ";\n"; $this->nginx_data[$vhost_filename] .= "\t\tfastcgi_param SCRIPT_FILENAME \$document_root\$fastcgi_script_name;\n"; $this->nginx_data[$vhost_filename] .= "\t\tfastcgi_param PATH_INFO \$fastcgi_path_info;\n"; $this->nginx_data[$vhost_filename] .= "\t\ttry_files \$fastcgi_script_name =404;\n"; @@ -247,24 +256,24 @@ class nginx extends HttpConfigBase { $this->nginx_data[$vhost_filename] .= "\t\tfastcgi_param HTTPS on;\n"; } - if ((int)Settings::Get('phpfpm.enabled') == 1 && (int)Settings::Get('phpfpm.enabled_ownvhost') == 1) { + if ((int) Settings::Get('phpfpm.enabled') == 1 && (int) Settings::Get('phpfpm.enabled_ownvhost') == 1) { $domain = array( 'id' => 'none', 'domain' => Settings::Get('system.hostname'), 'adminid' => 1, /* first admin-user (superadmin) */ - 'mod_fcgid_starter' => -1, - 'mod_fcgid_maxrequests' => -1, + 'mod_fcgid_starter' => - 1, + 'mod_fcgid_maxrequests' => - 1, 'guid' => Settings::Get('phpfpm.vhost_httpuser'), 'openbasedir' => 0, 'email' => Settings::Get('panel.adminmail'), 'loginname' => 'froxlor.panel', - 'documentroot' => $mypath, + 'documentroot' => $mypath ); $php = new phpinterface($domain); - $this->nginx_data[$vhost_filename] .= "\t\tfastcgi_pass unix:".$php->getInterface()->getSocketFile().";\n"; + $this->nginx_data[$vhost_filename] .= "\t\tfastcgi_pass unix:" . $php->getInterface()->getSocketFile() . ";\n"; } else { - $this->nginx_data[$vhost_filename] .= "\t\tfastcgi_pass ".Settings::Get('system.nginx_php_backend').";\n"; + $this->nginx_data[$vhost_filename] .= "\t\tfastcgi_pass " . Settings::Get('system.nginx_php_backend') . ";\n"; } $this->nginx_data[$vhost_filename] .= "\t\tfastcgi_index index.php;\n"; @@ -284,36 +293,32 @@ class nginx extends HttpConfigBase { $this->_createStandardErrorHandler(); } - /** * create vhosts */ - protected function createNginxHosts() { - + protected function createNginxHosts() + { $domains = WebserverBase::getVhostsToCreate(); foreach ($domains as $domain) { if (is_dir(Settings::Get('system.apacheconf_vhost'))) { - safe_exec('mkdir -p '.escapeshellarg(makeCorrectDir(Settings::Get('system.apacheconf_vhost')))); + safe_exec('mkdir -p ' . escapeshellarg(makeCorrectDir(Settings::Get('system.apacheconf_vhost')))); } $vhost_filename = $this->getVhostFilename($domain); - if (!isset($this->nginx_data[$vhost_filename])) { + if (! isset($this->nginx_data[$vhost_filename])) { $this->nginx_data[$vhost_filename] = ''; } - if ((empty($this->nginx_data[$vhost_filename]) - && !is_dir(Settings::Get('system.apacheconf_vhost'))) - || is_dir(Settings::Get('system.apacheconf_vhost')) - ) { + if ((empty($this->nginx_data[$vhost_filename]) && ! is_dir(Settings::Get('system.apacheconf_vhost'))) || is_dir(Settings::Get('system.apacheconf_vhost'))) { $domain['nonexistinguri'] = '/' . md5(uniqid(microtime(), 1)) . '.htm'; // Create non-ssl host - $this->nginx_data[$vhost_filename].= $this->getVhostContent($domain, false); + $this->nginx_data[$vhost_filename] .= $this->getVhostContent($domain, false); if ($domain['ssl'] == '1' || $domain['ssl_redirect'] == '1') { $vhost_filename_ssl = $this->getVhostFilename($domain, true); - if (!isset($this->nginx_data[$vhost_filename_ssl])) { + if (! isset($this->nginx_data[$vhost_filename_ssl])) { $this->nginx_data[$vhost_filename_ssl] = ''; } // Now enable ssl stuff @@ -323,57 +328,45 @@ class nginx extends HttpConfigBase { } } - - protected function getVhostFilename($domain, $ssl_vhost = false) { - if ((int)$domain['parentdomainid'] == 0 - && isCustomerStdSubdomain((int)$domain['id']) == false - && ((int)$domain['ismainbutsubto'] == 0 - || domainMainToSubExists($domain['ismainbutsubto']) == false) - ) { + protected function getVhostFilename($domain, $ssl_vhost = false) + { + if ((int) $domain['parentdomainid'] == 0 && isCustomerStdSubdomain((int) $domain['id']) == false && ((int) $domain['ismainbutsubto'] == 0 || domainMainToSubExists($domain['ismainbutsubto']) == false)) { $vhost_no = '35'; - } elseif ((int)$domain['parentdomainid'] == 0 - && isCustomerStdSubdomain((int)$domain['id']) == false - && (int)$domain['ismainbutsubto'] > 0 - ) { + } elseif ((int) $domain['parentdomainid'] == 0 && isCustomerStdSubdomain((int) $domain['id']) == false && (int) $domain['ismainbutsubto'] > 0) { $vhost_no = '30'; } else { // number of dots in a domain specifies it's position (and depth of subdomain) starting at 29 going downwards on higher depth - $vhost_no = (string)(30 - substr_count($domain['domain'], ".") + 1); + $vhost_no = (string) (30 - substr_count($domain['domain'], ".") + 1); } if ($ssl_vhost === true) { - $vhost_filename = makeCorrectFile(Settings::Get('system.apacheconf_vhost') . '/'.$vhost_no.'_froxlor_ssl_vhost_' . $domain['domain'] . '.conf'); + $vhost_filename = makeCorrectFile(Settings::Get('system.apacheconf_vhost') . '/' . $vhost_no . '_froxlor_ssl_vhost_' . $domain['domain'] . '.conf'); } else { - $vhost_filename = makeCorrectFile(Settings::Get('system.apacheconf_vhost') . '/'.$vhost_no.'_froxlor_normal_vhost_' . $domain['domain'] . '.conf'); + $vhost_filename = makeCorrectFile(Settings::Get('system.apacheconf_vhost') . '/' . $vhost_no . '_froxlor_normal_vhost_' . $domain['domain'] . '.conf'); } return $vhost_filename; } - - protected function getVhostContent($domain, $ssl_vhost = false) { - if ($ssl_vhost === true - && $domain['ssl'] != '1' - && $domain['ssl_redirect'] != '1' - ) { + protected function getVhostContent($domain, $ssl_vhost = false) + { + if ($ssl_vhost === true && $domain['ssl'] != '1' && $domain['ssl_redirect'] != '1') { return ''; } // check whether the customer is deactivated and NO docroot for deactivated users has been set# $ddr = Settings::Get('system.deactivateddocroot'); if ($domain['deactivated'] == '1' && empty($ddr)) { - return '# Customer deactivated and a docroot for deactivated users hasn\'t been set.' . "\n"; + return '# Customer deactivated and a docroot for deactivated users hasn\'t been set.' . "\n"; } $vhost_content = ''; $_vhost_content = ''; - $query = "SELECT * FROM `".TABLE_PANEL_IPSANDPORTS."` `i`, `".TABLE_DOMAINTOIP."` `dip` + $query = "SELECT * FROM `" . TABLE_PANEL_IPSANDPORTS . "` `i`, `" . TABLE_DOMAINTOIP . "` `dip` WHERE dip.id_domain = :domainid AND i.id = dip.id_ipandports "; - if ($ssl_vhost === true - && ($domain['ssl'] == '1' || $domain['ssl_redirect'] == '1') - ) { + if ($ssl_vhost === true && ($domain['ssl'] == '1' || $domain['ssl_redirect'] == '1')) { // by ordering by cert-file the row with filled out SSL-Fields will be shown last, // thus it is enough to fill out 1 set of SSL-Fields $query .= "AND i.ssl = 1 ORDER BY i.ssl_cert_file ASC;"; @@ -382,10 +375,12 @@ class nginx extends HttpConfigBase { } // start vhost - $vhost_content.= 'server { ' . "\n"; + $vhost_content .= 'server { ' . "\n"; $result_stmt = Database::prepare($query); - Database::pexecute($result_stmt, array('domainid' => $domain['id'])); + Database::pexecute($result_stmt, array( + 'domainid' => $domain['id'] + )); while ($ipandport = $result_stmt->fetch(PDO::FETCH_ASSOC)) { @@ -411,41 +406,34 @@ class nginx extends HttpConfigBase { } if ($ipandport['default_vhostconf_domain'] != '') { - $_vhost_content .= $this->processSpecialConfigTemplate( - $ipandport['default_vhostconf_domain'], - $domain, - $domain['ip'], - $domain['port'], - $ssl_vhost). "\n"; + $_vhost_content .= $this->processSpecialConfigTemplate($ipandport['default_vhostconf_domain'], $domain, $domain['ip'], $domain['port'], $ssl_vhost) . "\n"; } - $vhost_content.= "\t" . 'listen ' . $ipport . ($ssl_vhost == true ? ' ssl' : '') . ';' . "\n"; + $vhost_content .= "\t" . 'listen ' . $ipport . ($ssl_vhost == true ? ' ssl' : '') . ';' . "\n"; } // get all server-names $vhost_content .= $this->getServerNames($domain); // respect ssl_redirect settings, #542 - if ($ssl_vhost == false - && $domain['ssl'] == '1' - && $domain['ssl_redirect'] == '1') { + if ($ssl_vhost == false && $domain['ssl'] == '1' && $domain['ssl_redirect'] == '1') { // We must not check if our port differs from port 443, // but if there is a destination-port != 443 $_sslport = ''; // This returns the first port that is != 443 with ssl enabled, if any // ordered by ssl-certificate (if any) so that the ip/port combo // with certificate is used - $ssldestport_stmt = Database::prepare( - "SELECT `ip`.`port` FROM ".TABLE_PANEL_IPSANDPORTS." `ip` - LEFT JOIN `".TABLE_DOMAINTOIP."` `dip` ON (`ip`.`id` = `dip`.`id_ipandports`) + $ssldestport_stmt = Database::prepare("SELECT `ip`.`port` FROM " . TABLE_PANEL_IPSANDPORTS . " `ip` + LEFT JOIN `" . TABLE_DOMAINTOIP . "` `dip` ON (`ip`.`id` = `dip`.`id_ipandports`) WHERE `dip`.`id_domain` = :domainid AND `ip`.`ssl` = '1' AND `ip`.`port` != 443 - ORDER BY `ip`.`ssl_cert_file` DESC, `ip`.`port` LIMIT 1;" - ); - $ssldestport = Database::pexecute_first($ssldestport_stmt, array('domainid' => $domain['id'])); + ORDER BY `ip`.`ssl_cert_file` DESC, `ip`.`port` LIMIT 1;"); + $ssldestport = Database::pexecute_first($ssldestport_stmt, array( + 'domainid' => $domain['id'] + )); if ($ssldestport['port'] != '') { - $_sslport = ":".$ssldestport['port']; + $_sslport = ":" . $ssldestport['port']; } $domain['documentroot'] = 'https://' . $domain['domain'] . $_sslport . '/'; @@ -455,26 +443,22 @@ class nginx extends HttpConfigBase { $domain['documentroot'] = trim($domain['documentroot']); // create ssl settings first since they are required for normal and redirect vhosts - if ($ssl_vhost === true - && $domain['ssl'] == '1' - && Settings::Get('system.use_ssl') == '1' - ) { - $vhost_content.= "\n" . $this->composeSslSettings($domain) . "\n"; + if ($ssl_vhost === true && $domain['ssl'] == '1' && Settings::Get('system.use_ssl') == '1') { + $vhost_content .= "\n" . $this->composeSslSettings($domain) . "\n"; } - if (Settings::Get('system.use_ssl') == '1' && Settings::Get('system.leenabled') == '1') - { + if (Settings::Get('system.use_ssl') == '1' && Settings::Get('system.leenabled') == '1') { $acmeConfFilename = Settings::Get('system.letsencryptacmeconf'); - $vhost_content.= "\t".'include '.$acmeConfFilename.';'."\n"; + $vhost_content .= "\t" . 'include ' . $acmeConfFilename . ';' . "\n"; } // if the documentroot is an URL we just redirect if (preg_match('/^https?\:\/\//', $domain['documentroot'])) { $uri = $this->idnaConvert->encode_uri($domain['documentroot']); - if (substr($uri, -1) == '/') { - $uri = substr($uri, 0, -1); + if (substr($uri, - 1) == '/') { + $uri = substr($uri, 0, - 1); } - $vhost_content .= "\t".'return 301 '.$uri.'$request_uri;'."\n"; + $vhost_content .= "\t" . 'return 301 ' . $uri . '$request_uri;' . "\n"; } else { mkDirWithCorrectOwnership($domain['customerroot'], $domain['documentroot'], $domain['guid'], $domain['guid'], true); @@ -484,18 +468,12 @@ class nginx extends HttpConfigBase { if ($this->_deactivated == false) { $vhost_content = $this->mergeVhostCustom($vhost_content, $this->create_pathOptions($domain)) . "\n"; - $vhost_content.= $this->composePhpOptions($domain, $ssl_vhost); + $vhost_content .= $this->composePhpOptions($domain, $ssl_vhost); - $vhost_content.= isset($this->needed_htpasswds[$domain['id']]) ? $this->needed_htpasswds[$domain['id']] . "\n" : ''; + $vhost_content .= isset($this->needed_htpasswds[$domain['id']]) ? $this->needed_htpasswds[$domain['id']] . "\n" : ''; if ($domain['specialsettings'] != "") { - $vhost_content = $this->mergeVhostCustom($vhost_content, $this->processSpecialConfigTemplate( - $domain['specialsettings'], - $domain, - $domain['ip'], - $domain['port'], - $ssl_vhost - )); + $vhost_content = $this->mergeVhostCustom($vhost_content, $this->processSpecialConfigTemplate($domain['specialsettings'], $domain, $domain['ip'], $domain['port'], $ssl_vhost)); } if ($_vhost_content != '') { @@ -503,13 +481,7 @@ class nginx extends HttpConfigBase { } if (Settings::Get('system.default_vhostconf') != '') { - $vhost_content = $this->mergeVhostCustom($vhost_content, - $this->processSpecialConfigTemplate( - Settings::Get('system.default_vhostconf'), - $domain, - $domain['ip'], - $domain['port'], - $ssl_vhost)."\n"); + $vhost_content = $this->mergeVhostCustom($vhost_content, $this->processSpecialConfigTemplate(Settings::Get('system.default_vhostconf'), $domain, $domain['ip'], $domain['port'], $ssl_vhost) . "\n"); } } } @@ -518,16 +490,23 @@ class nginx extends HttpConfigBase { return $vhost_content; } - protected function mergeVhostCustom($vhost_frx, $vhost_usr) { + protected function mergeVhostCustom($vhost_frx, $vhost_usr) + { // Clean froxlor defined settings $vhost_frx = explode("\n", preg_replace('/[ \t]+/', ' ', trim(preg_replace('/\t+/', '', $vhost_frx)))); // Break into array items $vhost_frx = array_map("trim", $vhost_frx); // remove unnecessary whitespaces // Clean user defined settings $vhost_usr = str_replace("\r", "\n", $vhost_usr); // Remove windows linebreaks - $vhost_usr = str_replace(array("{ ", " }"), array("{\n", "\n}"), $vhost_usr); // Break blocks into lines + $vhost_usr = str_replace(array( + "{ ", + " }" + ), array( + "{\n", + "\n}" + ), $vhost_usr); // Break blocks into lines $vhost_usr = explode("\n", preg_replace('/[ \t]+/', ' ', trim(preg_replace('/\t+/', '', $vhost_usr)))); // Break into array items - $vhost_usr = array_filter($vhost_usr, create_function('$a','return preg_match("#\S#", $a);')); // Remove empty lines + $vhost_usr = array_filter($vhost_usr, create_function('$a', 'return preg_match("#\S#", $a);')); // Remove empty lines // Cycle through the user defined settings $currentBlock = array(); @@ -537,10 +516,10 @@ class nginx extends HttpConfigBase { $currentBlock[] = $line; if (strpos($line, "{") !== false) { - $blockLevel++; + $blockLevel ++; } if (strpos($line, "}") !== false && $blockLevel > 0) { - $blockLevel--; + $blockLevel --; } if ($line == "}" && $blockLevel == 0) { @@ -548,10 +527,10 @@ class nginx extends HttpConfigBase { // Add to existing block $pos = array_search($currentBlock[0], $vhost_frx); do { - $pos++; + $pos ++; } while ($vhost_frx[$pos] != "}"); - for ($i = 1; $i < count($currentBlock) - 1; $i++) { + for ($i = 1; $i < count($currentBlock) - 1; $i ++) { array_splice($vhost_frx, $pos + $i - 1, 0, $currentBlock[$i]); } } else { @@ -566,13 +545,13 @@ class nginx extends HttpConfigBase { } $nextLevel = 0; - for ($i = 0; $i < count($vhost_frx); $i++) { + for ($i = 0; $i < count($vhost_frx); $i ++) { if (substr_count($vhost_frx[$i], "}") != 0 && substr_count($vhost_frx[$i], "{") == 0) { $nextLevel -= 1; $vhost_frx[$i] .= "\n"; } if ($nextLevel > 0) { - for ($j = 0; $j < $nextLevel; $j++) { + for ($j = 0; $j < $nextLevel; $j ++) { $vhost_frx[$i] = " " . $vhost_frx[$i]; } } @@ -584,8 +563,8 @@ class nginx extends HttpConfigBase { return implode("\n", $vhost_frx); } - protected function composeSslSettings($domain_or_ip) { - + protected function composeSslSettings($domain_or_ip) + { $sslsettings = ''; if ($domain_or_ip['ssl_cert_file'] == '') { @@ -607,93 +586,95 @@ class nginx extends HttpConfigBase { if ($domain_or_ip['ssl_cert_file'] != '') { - // check for existence, #1485 - if (!file_exists($domain_or_ip['ssl_cert_file'])) { - $this->logger->logAction(CRON_ACTION, LOG_ERR, $domain_or_ip['domain'] . ' :: certificate file "'.$domain_or_ip['ssl_cert_file'].'" does not exist! Cannot create ssl-directives'); - } else { - // obsolete: ssl on now belongs to the listen block as 'ssl' at the end - //$sslsettings .= "\t" . 'ssl on;' . "\n"; - $sslsettings .= "\t" . 'ssl_protocols TLSv1 TLSv1.1 TLSv1.2;' . "\n"; - $sslsettings .= "\t" . 'ssl_ciphers ' . Settings::Get('system.ssl_cipher_list') . ';' . "\n"; - $sslsettings .= "\t" . 'ssl_ecdh_curve secp384r1;' . "\n"; - $sslsettings .= "\t" . 'ssl_prefer_server_ciphers on;' . "\n"; - $sslsettings .= "\t" . 'ssl_certificate ' . makeCorrectFile($domain_or_ip['ssl_cert_file']) . ';' . "\n"; + // check for existence, #1485 + if (! file_exists($domain_or_ip['ssl_cert_file'])) { + $this->logger->logAction(CRON_ACTION, LOG_ERR, $domain_or_ip['domain'] . ' :: certificate file "' . $domain_or_ip['ssl_cert_file'] . '" does not exist! Cannot create ssl-directives'); + } else { + // obsolete: ssl on now belongs to the listen block as 'ssl' at the end + // $sslsettings .= "\t" . 'ssl on;' . "\n"; + $sslsettings .= "\t" . 'ssl_protocols TLSv1 TLSv1.1 TLSv1.2;' . "\n"; + $sslsettings .= "\t" . 'ssl_ciphers ' . Settings::Get('system.ssl_cipher_list') . ';' . "\n"; + $sslsettings .= "\t" . 'ssl_ecdh_curve secp384r1;' . "\n"; + $sslsettings .= "\t" . 'ssl_prefer_server_ciphers on;' . "\n"; + $sslsettings .= "\t" . 'ssl_certificate ' . makeCorrectFile($domain_or_ip['ssl_cert_file']) . ';' . "\n"; - if ($domain_or_ip['ssl_key_file'] != '') { - // check for existence, #1485 - if (!file_exists($domain_or_ip['ssl_key_file'])) { - $this->logger->logAction(CRON_ACTION, LOG_ERR, $domain_or_ip['domain'] . ' :: certificate key file "'.$domain_or_ip['ssl_key_file'].'" does not exist! Cannot create ssl-directives'); - } else { - $sslsettings .= "\t" . 'ssl_certificate_key ' .makeCorrectFile($domain_or_ip['ssl_key_file']) . ';' . "\n"; - } - } + if ($domain_or_ip['ssl_key_file'] != '') { + // check for existence, #1485 + if (! file_exists($domain_or_ip['ssl_key_file'])) { + $this->logger->logAction(CRON_ACTION, LOG_ERR, $domain_or_ip['domain'] . ' :: certificate key file "' . $domain_or_ip['ssl_key_file'] . '" does not exist! Cannot create ssl-directives'); + } else { + $sslsettings .= "\t" . 'ssl_certificate_key ' . makeCorrectFile($domain_or_ip['ssl_key_file']) . ';' . "\n"; + } + } - if (isset($domain_or_ip['hsts']) && $domain_or_ip['hsts'] > 0) { - $vhost_content .= 'add_header Strict-Transport-Security "max-age=' . $domain_or_ip['hsts']; - if ($domain_or_ip['hsts_sub'] == 1) { - $vhost_content .= '; includeSubDomains'; - } - if ($domain_or_ip['hsts_preload'] == 1) { - $vhost_content .= '; preload'; - } - $vhost_content .= '";' . "\n"; + if (isset($domain_or_ip['hsts']) && $domain_or_ip['hsts'] >= 0) { + $vhost_content .= 'add_header Strict-Transport-Security "max-age=' . $domain_or_ip['hsts']; + if ($domain_or_ip['hsts_sub'] == 1) { + $vhost_content .= '; includeSubDomains'; + } + if ($domain_or_ip['hsts_preload'] == 1) { + $vhost_content .= '; preload'; + } + $vhost_content .= '";' . "\n"; + } } - } } return $sslsettings; } - - protected function create_pathOptions($domain) { + protected function create_pathOptions($domain) + { $has_location = false; $result_stmt = Database::prepare(" SELECT * FROM " . TABLE_PANEL_HTACCESS . " WHERE `path` LIKE :docroot "); - Database::pexecute($result_stmt, array('docroot' => $domain['documentroot'] . '%')); + Database::pexecute($result_stmt, array( + 'docroot' => $domain['documentroot'] . '%' + )); $path_options = ''; $htpasswds = $this->getHtpasswds($domain); // for each entry in the htaccess table while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) { - if (!empty($row['error404path'])) { + if (! empty($row['error404path'])) { $defhandler = $row['error404path']; - if (!validateUrl($defhandler)) { + if (! validateUrl($defhandler)) { $defhandler = makeCorrectFile($defhandler); } - $path_options .= "\t".'error_page 404 ' . $defhandler . ';' . "\n"; + $path_options .= "\t" . 'error_page 404 ' . $defhandler . ';' . "\n"; } - if (!empty($row['error403path'])) { + if (! empty($row['error403path'])) { $defhandler = $row['error403path']; - if (!validateUrl($defhandler)) { + if (! validateUrl($defhandler)) { $defhandler = makeCorrectFile($defhandler); } - $path_options .= "\t".'error_page 403 ' . $defhandler . ';' . "\n"; + $path_options .= "\t" . 'error_page 403 ' . $defhandler . ';' . "\n"; } - if (!empty($row['error500path'])) { + if (! empty($row['error500path'])) { $defhandler = $row['error500path']; - if (!validateUrl($defhandler)) { + if (! validateUrl($defhandler)) { $defhandler = makeCorrectFile($defhandler); } - $path_options .= "\t".'error_page 500 502 503 504 ' . $defhandler . ';' . "\n"; + $path_options .= "\t" . 'error_page 500 502 503 504 ' . $defhandler . ';' . "\n"; } - // if ($row['options_indexes'] != '0') { + // if ($row['options_indexes'] != '0') { $path = makeCorrectDir(substr($row['path'], strlen($domain['documentroot']) - 1)); mkDirWithCorrectOwnership($domain['documentroot'], $row['path'], $domain['guid'], $domain['guid']); - $path_options .= "\t".'# '.$path."\n"; + $path_options .= "\t" . '# ' . $path . "\n"; if ($path == '/') { if ($row['options_indexes'] != '0') { $this->vhost_root_autoindex = true; } - $path_options .= "\t".'location ' . $path . ' {' . "\n"; + $path_options .= "\t" . 'location ' . $path . ' {' . "\n"; if ($this->vhost_root_autoindex) { $path_options .= "\t\t" . 'autoindex on;' . "\n"; $this->vhost_root_autoindex = false; @@ -705,80 +686,77 @@ class nginx extends HttpConfigBase { if (count($htpasswds) > 0) { foreach ($htpasswds as $idx => $single) { switch ($single['path']) { - case '/awstats/': - case '/webalizer/': - // no stats-alias in "location /"-context - break; - default: - if ($single['path'] == '/') { - $path_options .= "\t\t" . 'auth_basic "' . $single['authname'] . '";' . "\n"; - $path_options .= "\t\t" . 'auth_basic_user_file ' . makeCorrectFile($single['usrf']) . ';'."\n"; - $path_options .= "\t\t" . 'location ~ ^(.+?\.php)(/.*)?$ {' . "\n"; - $path_options .= "\t\t\t" . 'try_files ' . $domain['nonexistinguri'] . ' @php;' . "\n"; - $path_options .= "\t\t" . '}' . "\n"; - // remove already used entries so we do not have doubles - unset($htpasswds[$idx]); - } + case '/awstats/': + case '/webalizer/': + // no stats-alias in "location /"-context + break; + default: + if ($single['path'] == '/') { + $path_options .= "\t\t" . 'auth_basic "' . $single['authname'] . '";' . "\n"; + $path_options .= "\t\t" . 'auth_basic_user_file ' . makeCorrectFile($single['usrf']) . ';' . "\n"; + $path_options .= "\t\t" . 'location ~ ^(.+?\.php)(/.*)?$ {' . "\n"; + $path_options .= "\t\t\t" . 'try_files ' . $domain['nonexistinguri'] . ' @php;' . "\n"; + $path_options .= "\t\t" . '}' . "\n"; + // remove already used entries so we do not have doubles + unset($htpasswds[$idx]); + } } } } - $path_options .= "\t".'}' . "\n"; + $path_options .= "\t" . '}' . "\n"; $this->vhost_root_autoindex = false; } else { - $path_options .= "\t".'location ' . $path . ' {' . "\n"; + $path_options .= "\t" . 'location ' . $path . ' {' . "\n"; if ($this->vhost_root_autoindex || $row['options_indexes'] != '0') { $path_options .= "\t\t" . 'autoindex on;' . "\n"; $this->vhost_root_autoindex = false; } - $path_options .= "\t".'} ' . "\n"; + $path_options .= "\t" . '} ' . "\n"; } - // } + // } /** * Perl support * required the fastCGI wrapper to be running to receive the CGI requests. */ - if (customerHasPerlEnabled($domain['customerid']) - && $row['options_cgi'] != '0' - ) { + if (customerHasPerlEnabled($domain['customerid']) && $row['options_cgi'] != '0') { $path = makeCorrectDir(substr($row['path'], strlen($domain['documentroot']) - 1)); mkDirWithCorrectOwnership($domain['documentroot'], $row['path'], $domain['guid'], $domain['guid']); // We need to remove the last slash, otherwise the regex wouldn't work if ($row['path'] != $domain['documentroot']) { - $path = substr($path, 0, -1); + $path = substr($path, 0, - 1); } $path_options .= "\t" . 'location ~ \(.pl|.cgi)$ {' . "\n"; $path_options .= "\t\t" . 'gzip off; #gzip makes scripts feel slower since they have to complete before getting gzipped' . "\n"; - $path_options .= "\t\t" . 'fastcgi_pass '. Settings::Get('system.perl_server') . ';' . "\n"; + $path_options .= "\t\t" . 'fastcgi_pass ' . Settings::Get('system.perl_server') . ';' . "\n"; $path_options .= "\t\t" . 'fastcgi_index index.cgi;' . "\n"; - $path_options .= "\t\t" . 'include '.Settings::Get('nginx.fastcgiparams').';'."\n"; + $path_options .= "\t\t" . 'include ' . Settings::Get('nginx.fastcgiparams') . ';' . "\n"; $path_options .= "\t" . '}' . "\n"; } - } // now the rest of the htpasswds if (count($htpasswds) > 0) { foreach ($htpasswds as $idx => $single) { - //if ($single['path'] != '/') { + // if ($single['path'] != '/') { switch ($single['path']) { - case '/awstats/': - case '/webalizer/': - $path_options .= $this->getStats($domain,$single); - unset($htpasswds[$idx]); - break; - default: - $path_options .= "\t" . 'location ' . makeCorrectDir($single['path']) . ' {' . "\n"; - $path_options .= "\t\t" . 'auth_basic "' . $single['authname'] . '";' . "\n"; - $path_options .= "\t\t" . 'auth_basic_user_file ' . makeCorrectFile($single['usrf']) . ';'."\n"; - $path_options .= "\t\t" . 'location ~ ^(.+?\.php)(/.*)?$ {' . "\n"; - $path_options .= "\t\t\t" . 'try_files ' . $domain['nonexistinguri'] . ' @php;' . "\n"; - $path_options .= "\t\t" . '}' . "\n"; - $path_options .= "\t".'}' . "\n"; + case '/awstats/': + case '/webalizer/': + $path_options .= $this->getStats($domain, $single); + unset($htpasswds[$idx]); + break; + default: + $path_options .= "\t" . 'location ' . makeCorrectDir($single['path']) . ' {' . "\n"; + $path_options .= "\t\t" . 'auth_basic "' . $single['authname'] . '";' . "\n"; + $path_options .= "\t\t" . 'auth_basic_user_file ' . makeCorrectFile($single['usrf']) . ';' . "\n"; + $path_options .= "\t\t" . 'location ~ ^(.+?\.php)(/.*)?$ {' . "\n"; + $path_options .= "\t\t\t" . 'try_files ' . $domain['nonexistinguri'] . ' @php;' . "\n"; + $path_options .= "\t\t" . '}' . "\n"; + $path_options .= "\t" . '}' . "\n"; } - //} + // } unset($htpasswds[$idx]); } } @@ -786,16 +764,18 @@ class nginx extends HttpConfigBase { return $path_options; } - - protected function getHtpasswds($domain) { - + protected function getHtpasswds($domain) + { $result_stmt = Database::prepare(" SELECT * FROM `" . TABLE_PANEL_HTPASSWDS . "` AS a JOIN `" . TABLE_PANEL_DOMAINS . "` AS b USING (`customerid`) WHERE b.customerid = :customerid AND b.domain = :domain "); - Database::pexecute($result_stmt, array('customerid' => $domain['customerid'], 'domain' => $domain['domain'])); + Database::pexecute($result_stmt, array( + 'customerid' => $domain['customerid'], + 'domain' => $domain['domain'] + )); $returnval = array(); $x = 0; @@ -804,15 +784,15 @@ class nginx extends HttpConfigBase { $htpasswd_filename = makeCorrectFile(Settings::Get('system.apacheconf_htpasswddir') . '/' . $row_htpasswds['customerid'] . '-' . md5($row_htpasswds['path']) . '.htpasswd'); // ensure we can write to the array with index $htpasswd_filename - if (!isset($this->htpasswds_data[$htpasswd_filename])) { + if (! isset($this->htpasswds_data[$htpasswd_filename])) { $this->htpasswds_data[$htpasswd_filename] = ''; } - $this->htpasswds_data[$htpasswd_filename].= $row_htpasswds['username'] . ':' . $row_htpasswds['password'] . "\n"; + $this->htpasswds_data[$htpasswd_filename] .= $row_htpasswds['username'] . ':' . $row_htpasswds['password'] . "\n"; // if the domains and their web contents are located in a subdirectory of // the nginx user, we have to evaluate the right path which is to protect - if (stripos($row_htpasswds['path'], $domain['documentroot']) !== false ) { + if (stripos($row_htpasswds['path'], $domain['documentroot']) !== false) { // if the website contents is located in the user directory $path = makeCorrectDir(substr($row_htpasswds['path'], strlen($domain['documentroot']) - 1)); } else { @@ -827,7 +807,7 @@ class nginx extends HttpConfigBase { // Ensure there is only one auth name per password block, otherwise // the directives are inserted multiple times -> invalid config $authname = $row_htpasswds['authname']; - for ($i = 0; $i < $x; $i++) { + for ($i = 0; $i < $x; $i ++) { if ($returnval[$i]['usrf'] == $htpasswd_filename) { $authname = $returnval[$i]['authname']; break; @@ -836,7 +816,7 @@ class nginx extends HttpConfigBase { $returnval[$x]['authname'] = $authname; $returnval[$x]['usrf'] = $htpasswd_filename; - $x++; + $x ++; } } @@ -846,66 +826,59 @@ class nginx extends HttpConfigBase { return $returnval; } - - protected function composePhpOptions($domain, $ssl_vhost = false) { + protected function composePhpOptions($domain, $ssl_vhost = false) + { $phpopts = ''; if ($domain['phpenabled'] == '1') { - $phpopts = "\tlocation ~ \.php {\n"; + $phpopts = "\tlocation ~ \.php {\n"; $phpopts .= "\t\t" . 'try_files ' . $domain['nonexistinguri'] . ' @php;' . "\n"; $phpopts .= "\t" . '}' . "\n\n"; $phpopts .= "\tlocation @php {\n"; $phpopts .= "\t\tfastcgi_split_path_info ^(.+\.php)(/.+)\$;\n"; - $phpopts .= "\t\tinclude ".Settings::Get('nginx.fastcgiparams').";\n"; + $phpopts .= "\t\tinclude " . Settings::Get('nginx.fastcgiparams') . ";\n"; $phpopts .= "\t\tfastcgi_param SCRIPT_FILENAME \$document_root\$fastcgi_script_name;\n"; $phpopts .= "\t\tfastcgi_param PATH_INFO \$fastcgi_path_info;\n"; $phpopts .= "\t\ttry_files \$fastcgi_script_name =404;\n"; - $phpopts .= "\t\tfastcgi_pass ".Settings::Get('system.nginx_php_backend').";\n"; + $phpopts .= "\t\tfastcgi_pass " . Settings::Get('system.nginx_php_backend') . ";\n"; $phpopts .= "\t\tfastcgi_index index.php;\n"; if ($domain['ssl'] == '1' && $ssl_vhost) { $phpopts .= "\t\tfastcgi_param HTTPS on;\n"; } $phpopts .= "\t}\n\n"; - } return $phpopts; } - - protected function getWebroot($domain, $ssl) { + protected function getWebroot($domain, $ssl) + { $webroot_text = ''; - if ($domain['deactivated'] == '1' - && Settings::Get('system.deactivateddocroot') != '' - ) { - $webroot_text .= "\t".'# Using docroot for deactivated users...' . "\n"; - $webroot_text .= "\t".'root '.makeCorrectDir(Settings::Get('system.deactivateddocroot')).';'."\n"; + if ($domain['deactivated'] == '1' && Settings::Get('system.deactivateddocroot') != '') { + $webroot_text .= "\t" . '# Using docroot for deactivated users...' . "\n"; + $webroot_text .= "\t" . 'root ' . makeCorrectDir(Settings::Get('system.deactivateddocroot')) . ';' . "\n"; $this->_deactivated = true; } else { - $webroot_text .= "\t".'root '.makeCorrectDir($domain['documentroot']).';'."\n"; + $webroot_text .= "\t" . 'root ' . makeCorrectDir($domain['documentroot']) . ';' . "\n"; $this->_deactivated = false; } - $webroot_text .= "\n\t".'location / {'."\n"; + $webroot_text .= "\n\t" . 'location / {' . "\n"; - if ($domain['phpenabled'] == '1') - { - $webroot_text .= "\t" . 'index index.php index.html index.htm;'."\n"; - $webroot_text .= "\t\t" . 'try_files $uri $uri/ @rewrites;'."\n"; - } - else - { - $webroot_text .= "\t" . 'index index.html index.htm;'."\n"; + if ($domain['phpenabled'] == '1') { + $webroot_text .= "\t" . 'index index.php index.html index.htm;' . "\n"; + $webroot_text .= "\t\t" . 'try_files $uri $uri/ @rewrites;' . "\n"; + } else { + $webroot_text .= "\t" . 'index index.html index.htm;' . "\n"; } if ($this->vhost_root_autoindex) { - $webroot_text .= "\t\t".'autoindex on;'."\n"; + $webroot_text .= "\t\t" . 'autoindex on;' . "\n"; $this->vhost_root_autoindex = false; } - $webroot_text .= "\t".'}'."\n\n"; - if ($domain['phpenabled'] == '1') - { + $webroot_text .= "\t" . '}' . "\n\n"; + if ($domain['phpenabled'] == '1') { $webroot_text .= "\tlocation @rewrites {\n"; $webroot_text .= "\t\trewrite ^ /index.php last;\n"; $webroot_text .= "\t}\n\n"; @@ -914,8 +887,8 @@ class nginx extends HttpConfigBase { return $webroot_text; } - - protected function getStats($domain, $single) { + protected function getStats($domain, $single) + { $stats_text = ''; // define basic path to the stats @@ -927,9 +900,9 @@ class nginx extends HttpConfigBase { // if this is a parentdomain, we use this domain-name if ($domain['parentdomainid'] == '0') { - $alias_dir = makeCorrectDir($alias_dir.'/'.$domain['domain']); + $alias_dir = makeCorrectDir($alias_dir . '/' . $domain['domain']); } else { - $alias_dir = makeCorrectDir($alias_dir.'/'.$domain['parentdomain']); + $alias_dir = makeCorrectDir($alias_dir . '/' . $domain['parentdomain']); } if (Settings::Get('system.awstats_enabled') == '1') { @@ -941,8 +914,8 @@ class nginx extends HttpConfigBase { } $stats_text .= "\t\t" . 'alias ' . $alias_dir . ';' . "\n"; - $stats_text .= "\t\t" . 'auth_basic "' . $single['authname'] . '";' . "\n"; - $stats_text .= "\t\t" . 'auth_basic_user_file ' . makeCorrectFile($single['usrf']) . ';'."\n"; + $stats_text .= "\t\t" . 'auth_basic "' . $single['authname'] . '";' . "\n"; + $stats_text .= "\t\t" . 'auth_basic_user_file ' . makeCorrectFile($single['usrf']) . ';' . "\n"; $stats_text .= "\t" . '}' . "\n\n"; // awstats icons @@ -955,8 +928,8 @@ class nginx extends HttpConfigBase { return $stats_text; } - - protected function getLogFiles($domain) { + protected function getLogFiles($domain) + { $logfiles_text = ''; $speciallogfile = ''; @@ -981,11 +954,11 @@ class nginx extends HttpConfigBase { chown($access_log, Settings::Get('system.httpuser')); chgrp($access_log, Settings::Get('system.httpgroup')); - $logfiles_text .= "\t".'access_log ' . $access_log . ' combined;' . "\n"; - $logfiles_text .= "\t".'error_log ' . $error_log . ' error;' . "\n"; + $logfiles_text .= "\t" . 'access_log ' . $access_log . ' combined;' . "\n"; + $logfiles_text .= "\t" . 'error_log ' . $error_log . ' error;' . "\n"; if (Settings::Get('system.awstats_enabled') == '1') { - if ((int)$domain['parentdomainid'] == 0) { + if ((int) $domain['parentdomainid'] == 0) { // prepare the aliases and subdomains for stats config files $server_alias = ''; $alias_domains_stmt = Database::prepare(" @@ -993,7 +966,9 @@ class nginx extends HttpConfigBase { FROM `" . TABLE_PANEL_DOMAINS . "` WHERE `aliasdomain` = :domainid OR `parentdomainid` = :domainid "); - Database::pexecute($alias_domains_stmt, array('domainid' => $domain['id'])); + Database::pexecute($alias_domains_stmt, array( + 'domainid' => $domain['id'] + )); while (($alias_domain = $alias_domains_stmt->fetch(PDO::FETCH_ASSOC)) !== false) { $server_alias .= ' ' . $alias_domain['domain'] . ' '; @@ -1027,12 +1002,11 @@ class nginx extends HttpConfigBase { return $logfiles_text; } + public function createOwnVhostStarter() + {} - public function createOwnVhostStarter() { - } - - - protected function getServerNames($domain) { + protected function getServerNames($domain) + { $server_alias = ''; if ($domain['iswildcarddomain'] == '1') { @@ -1046,7 +1020,9 @@ class nginx extends HttpConfigBase { FROM `" . TABLE_PANEL_DOMAINS . "` WHERE `aliasdomain` = :domainid "); - Database::pexecute($alias_domains_stmt, array('domainid' => $domain['id'])); + Database::pexecute($alias_domains_stmt, array( + 'domainid' => $domain['id'] + )); while (($alias_domain = $alias_domains_stmt->fetch(PDO::FETCH_ASSOC)) !== false) { $server_alias .= ' ' . $alias_domain['domain']; @@ -1054,25 +1030,25 @@ class nginx extends HttpConfigBase { if ($alias_domain['iswildcarddomain'] == '1') { $server_alias .= ' *.' . $alias_domain['domain']; } elseif ($alias_domain['wwwserveralias'] == '1') { - $server_alias.= ' www.' . $alias_domain['domain']; + $server_alias .= ' www.' . $alias_domain['domain']; } } - $servernames_text = "\t".'server_name '.$domain['domain']; + $servernames_text = "\t" . 'server_name ' . $domain['domain']; if (trim($server_alias) != '') { - $servernames_text .= ' '.$server_alias; + $servernames_text .= ' ' . $server_alias; } $servernames_text .= ';' . "\n"; return $servernames_text; } - - public function writeConfigs() { + public function writeConfigs() + { $this->logger->logAction(CRON_ACTION, LOG_INFO, "nginx::writeConfigs: rebuilding " . Settings::Get('system.apacheconf_vhost')); $vhostDir = new frxDirectory(Settings::Get('system.apacheconf_vhost')); - if (!$vhostDir->isConfigDir()) { + if (! $vhostDir->isConfigDir()) { // Save one big file $vhosts_file = ''; @@ -1083,7 +1059,7 @@ class nginx extends HttpConfigBase { ksort($this->nginx_data); foreach ($this->nginx_data as $vhosts_filename => $vhost_content) { - $vhosts_file.= $vhost_content . "\n\n"; + $vhosts_file .= $vhost_content . "\n\n"; } $vhosts_filename = Settings::Get('system.apacheconf_vhost'); @@ -1094,7 +1070,7 @@ class nginx extends HttpConfigBase { fwrite($vhosts_file_handler, $vhosts_file); fclose($vhosts_file_handler); } else { - if (!file_exists(Settings::Get('system.apacheconf_vhost'))) { + if (! file_exists(Settings::Get('system.apacheconf_vhost'))) { $this->logger->logAction(CRON_ACTION, LOG_NOTICE, 'nginx::writeConfigs: mkdir ' . escapeshellarg(makeCorrectDir(Settings::Get('system.apacheconf_vhost')))); safe_exec('mkdir -p ' . escapeshellarg(makeCorrectDir(Settings::Get('system.apacheconf_vhost')))); } @@ -1106,23 +1082,22 @@ class nginx extends HttpConfigBase { // Apply header $vhosts_file = '# ' . basename($vhosts_filename) . "\n" . '# Created ' . date('d.m.Y H:i') . "\n" . '# Do NOT manually edit this file, all changes will be deleted after the next domain change at the panel.' . "\n" . "\n" . $vhosts_file; - if (!empty($vhosts_filename)) { + if (! empty($vhosts_filename)) { $vhosts_file_handler = fopen($vhosts_filename, 'w'); fwrite($vhosts_file_handler, $vhosts_file); fclose($vhosts_file_handler); } - } } // htaccess stuff if (count($this->htpasswds_data) > 0) { - if (!file_exists(Settings::Get('system.apacheconf_htpasswddir'))) { + if (! file_exists(Settings::Get('system.apacheconf_htpasswddir'))) { $umask = umask(); umask(0000); mkdir(Settings::Get('system.apacheconf_htpasswddir'), 0751); umask($umask); - } elseif (!is_dir(Settings::Get('system.apacheconf_htpasswddir'))) { + } elseif (! is_dir(Settings::Get('system.apacheconf_htpasswddir'))) { $this->logger->logAction(CRON_ACTION, LOG_WARNING, 'WARNING!!! ' . Settings::Get('system.apacheconf_htpasswddir') . ' is not a directory. htpasswd directory protection is disabled!!!'); } @@ -1138,6 +1113,4 @@ class nginx extends HttpConfigBase { } } } - - } From d198729222b36bf0d82efed605376675f4e1c769 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Fri, 7 Oct 2016 11:48:13 +0200 Subject: [PATCH 0201/1335] make the hsts domain settings actually saveable; add hsts domain settings for customer-panel; refs #1660 Signed-off-by: Michael Kaufmann (d00p) --- admin_domains.php | 62 +++++++++++++-- customer_domains.php | 33 ++++++-- .../domains/formfield.domains_add.php | 78 ++++++++++++++----- .../domains/formfield.domains_edit.php | 58 +++++++++++--- 4 files changed, 187 insertions(+), 44 deletions(-) diff --git a/admin_domains.php b/admin_domains.php index abb66e6b..99dc415d 100644 --- a/admin_domains.php +++ b/admin_domains.php @@ -584,12 +584,23 @@ if ($page == 'domains' || $page == 'overview') { $ssl_ipandports[] = $ssl_ipandport; } } + + // HSTS + $hsts_maxage = isset($_POST['hsts_maxage']) ? (int)$_POST['hsts_maxage'] : 0; + $hsts_sub = isset($_POST['hsts_sub']) && (int)$_POST['hsts_sub'] == 1 ? 1 : 0; + $hsts_preload = isset($_POST['hsts_preload']) && (int)$_POST['hsts_preload'] == 1 ? 1 : 0; + } else { $ssl_redirect = 0; $letsencrypt = 0; // we need this for the serialize // if ssl is disabled or no ssl-ip/port exists $ssl_ipandports[] = - 1; + + // HSTS + $hsts_maxage = 0; + $hsts_sub = 0; + $hsts_preload = 0; } } else { $ssl_redirect = 0; @@ -597,6 +608,11 @@ if ($page == 'domains' || $page == 'overview') { // we need this for the serialize // if ssl is disabled or no ssl-ip/port exists $ssl_ipandports[] = - 1; + + // HSTS + $hsts_maxage = 0; + $hsts_sub = 0; + $hsts_preload = 0; } // We can't enable let's encrypt for wildcard - domains @@ -760,7 +776,10 @@ if ($page == 'domains' || $page == 'overview') { 'registration_date' => $registration_date, 'termination_date' => $termination_date, 'issubof' => $issubof, - 'letsencrypt' => $letsencrypt + 'letsencrypt' => $letsencrypt, + 'hsts' => $hsts_maxage, + 'hsts_sub' => $hsts_sub, + 'hsts_preload' => $hsts_preload ); $security_questions = array( @@ -808,7 +827,10 @@ if ($page == 'domains' || $page == 'overview') { 'mod_fcgid_starter' => $mod_fcgid_starter, 'mod_fcgid_maxrequests' => $mod_fcgid_maxrequests, 'ismainbutsubto' => $issubof, - 'letsencrypt' => $letsencrypt + 'letsencrypt' => $letsencrypt, + 'hsts' => $hsts_maxage, + 'hsts_sub' => $hsts_sub, + 'hsts_preload' => $hsts_preload ); $ins_stmt = Database::prepare(" @@ -836,12 +858,15 @@ if ($page == 'domains' || $page == 'overview') { `ssl_redirect` = :ssl_redirect, `add_date` = :add_date, `registration_date` = :registration_date, - `termination_date` = :termination_date, + `termination_date` = :termination_date, `phpsettingid` = :phpsettingid, `mod_fcgid_starter` = :mod_fcgid_starter, `mod_fcgid_maxrequests` = :mod_fcgid_maxrequests, `ismainbutsubto` = :ismainbutsubto, - `letsencrypt` = :letsencrypt + `letsencrypt` = :letsencrypt, + `hsts` = :hsts, + `hsts_sub` = :hsts_sub, + `hsts_preload` = :hsts_preload "); Database::pexecute($ins_stmt, $ins_data); $domainid = Database::lastInsertId(); @@ -1398,12 +1423,23 @@ if ($page == 'domains' || $page == 'overview') { $ssl_ipandports[] = $ssl_ipandport; } } + + // HSTS + $hsts_maxage = isset($_POST['hsts_maxage']) ? (int)$_POST['hsts_maxage'] : 0; + $hsts_sub = isset($_POST['hsts_sub']) && (int)$_POST['hsts_sub'] == 1 ? 1 : 0; + $hsts_preload = isset($_POST['hsts_preload']) && (int)$_POST['hsts_preload'] == 1 ? 1 : 0; + } else { $ssl_redirect = 0; $letsencrypt = 0; // we need this for the serialize // if ssl is disabled or no ssl-ip/port exists $ssl_ipandports[] = - 1; + + // HSTS + $hsts_maxage = 0; + $hsts_sub = 0; + $hsts_preload = 0; } } else { $ssl_redirect = 0; @@ -1411,6 +1447,11 @@ if ($page == 'domains' || $page == 'overview') { // we need this for the serialize // if ssl is disabled or no ssl-ip/port exists $ssl_ipandports[] = - 1; + + // HSTS + $hsts_maxage = 0; + $hsts_sub = 0; + $hsts_preload = 0; } // We can't enable let's encrypt for wildcard domains @@ -1548,7 +1589,10 @@ if ($page == 'domains' || $page == 'overview') { 'speciallogverified' => $speciallogverified, 'ipandport' => serialize($ipandports), 'ssl_ipandport' => serialize($ssl_ipandports), - 'letsencrypt' => $letsencrypt + 'letsencrypt' => $letsencrypt, + 'hsts' => $hsts_maxage, + 'hsts_sub' => $hsts_sub, + 'hsts_preload' => $hsts_preload ); $security_questions = array( @@ -1708,6 +1752,9 @@ if ($page == 'domains' || $page == 'overview') { $update_data['termination_date'] = $termination_date; $update_data['ismainbutsubto'] = $issubof; $update_data['letsencrypt'] = $letsencrypt; + $update_data['hsts'] = $hsts_maxage; + $update_data['hsts_sub'] = $hsts_sub; + $update_data['hsts_preload'] = $hsts_preload; $update_data['id'] = $id; $update_stmt = Database::prepare(" @@ -1735,7 +1782,10 @@ if ($page == 'domains' || $page == 'overview') { `registration_date` = :registration_date, `termination_date` = :termination_date, `ismainbutsubto` = :ismainbutsubto, - `letsencrypt` = :letsencrypt + `letsencrypt` = :letsencrypt, + `hsts` = :hsts, + `hsts_sub` = :hsts_sub, + `hsts_preload` = :hsts_preload WHERE `id` = :id "); Database::pexecute($update_stmt, $update_data); diff --git a/customer_domains.php b/customer_domains.php index 2aa9bbab..e79af357 100644 --- a/customer_domains.php +++ b/customer_domains.php @@ -374,6 +374,11 @@ if ($page == 'overview') { $ssl_redirect = 2; } + // HSTS + $hsts_maxage = isset($_POST['hsts_maxage']) ? (int)$_POST['hsts_maxage'] : 0; + $hsts_sub = isset($_POST['hsts_sub']) && (int)$_POST['hsts_sub'] == 1 ? 1 : 0; + $hsts_preload = isset($_POST['hsts_preload']) && (int)$_POST['hsts_preload'] == 1 ? 1 : 0; + if ($path == '') { standard_error('patherror'); } elseif ($subdomain == '') { @@ -416,7 +421,10 @@ if ($page == 'overview') { `specialsettings` = :specialsettings, `ssl_redirect` = :ssl_redirect, `phpsettingid` = :phpsettingid, - `letsencrypt` = :letsencrypt" + `letsencrypt` = :letsencrypt, + `hsts` = :hsts, + `hsts_sub` = :hsts_sub, + `hsts_preload` = :hsts_preload" ); $params = array( "customerid" => $userinfo['customerid'], @@ -433,7 +441,10 @@ if ($page == 'overview') { "specialsettings" => $domain_check['specialsettings'], "ssl_redirect" => $ssl_redirect, "phpsettingid" => $phpsid_result['phpsettingid'], - "letsencrypt" => $letsencrypt + "letsencrypt" => $letsencrypt, + "hsts" => $hsts_maxage, + "hsts_sub" => $hsts_sub, + "hsts_preload" => $hsts_preload ); Database::pexecute($stmt, $params); @@ -527,8 +538,7 @@ if ($page == 'overview') { } } elseif ($action == 'edit' && $id != 0) { - $stmt = Database::prepare("SELECT `d`.`id`, `d`.`customerid`, `d`.`domain`, `d`.`documentroot`, `d`.`isemaildomain`, `d`.`isbinddomain`, `d`.`wwwserveralias`, `d`.`iswildcarddomain`, - `d`.`parentdomainid`, `d`.`ssl_redirect`, `d`.`aliasdomain`, `d`.`openbasedir`, `d`.`openbasedir_path`, `d`.`letsencrypt`, `pd`.`subcanemaildomain` + $stmt = Database::prepare("SELECT `d`.*, `pd`.`subcanemaildomain` FROM `" . TABLE_PANEL_DOMAINS . "` `d`, `" . TABLE_PANEL_DOMAINS . "` `pd` WHERE `d`.`customerid` = :customerid AND `d`.`id` = :id @@ -634,7 +644,7 @@ if ($page == 'overview') { // We can't enable let's encrypt for wildcard - domains if ($iswildcarddomain == '1' && $letsencrypt == '1') { - standard_error('nowildcardwithletsencrypt'); + standard_error('nowildcardwithletsencrypt'); } // Temporarily deactivate ssl_redirect until Let's Encrypt certificate was generated @@ -642,6 +652,11 @@ if ($page == 'overview') { $ssl_redirect = 2; } + // HSTS + $hsts_maxage = isset($_POST['hsts_maxage']) ? (int)$_POST['hsts_maxage'] : 0; + $hsts_sub = isset($_POST['hsts_sub']) && (int)$_POST['hsts_sub'] == 1 ? 1 : 0; + $hsts_preload = isset($_POST['hsts_preload']) && (int)$_POST['hsts_preload'] == 1 ? 1 : 0; + if ($path == '') { standard_error('patherror'); } else { @@ -677,7 +692,10 @@ if ($page == 'overview') { `aliasdomain`= :aliasdomain, `openbasedir_path`= :openbasedir_path, `ssl_redirect`= :ssl_redirect, - `letsencrypt`= :letsencrypt + `letsencrypt`= :letsencrypt, + `hsts` = :hsts, + `hsts_sub` = :hsts_sub, + `hsts_preload` = :hsts_preload, WHERE `customerid`= :customerid AND `id`= :id" ); @@ -690,6 +708,9 @@ if ($page == 'overview') { "openbasedir_path" => $openbasedir_path, "ssl_redirect" => $ssl_redirect, "letsencrypt" => $letsencrypt, + "hsts" => $hsts_maxage, + "hsts_sub" => $hsts_sub, + "hsts_preload" => $hsts_preload, "customerid" => $userinfo['customerid'], "id" => $id ); diff --git a/lib/formfields/customer/domains/formfield.domains_add.php b/lib/formfields/customer/domains/formfield.domains_add.php index 2f24fcae..e25302b0 100644 --- a/lib/formfields/customer/domains/formfield.domains_add.php +++ b/lib/formfields/customer/domains/formfield.domains_add.php @@ -66,33 +66,69 @@ return array( 'type' => 'label', 'value' => $lng['customer']['selectserveralias_addinfo'] ), - 'ssl_redirect' => array( - 'visible' => (Settings::Get('system.use_ssl') == '1' ? ($ssl_ipsandports != '' ? true : false) : false), - 'label' => $lng['domains']['ssl_redirect']['title'], - 'desc' => $lng['domains']['ssl_redirect']['description'], - 'type' => 'checkbox', - 'values' => array( - array ('label' => $lng['panel']['yes'], 'value' => '1') - ), - 'value' => array() - ), - 'letsencrypt' => array( - 'visible' => (Settings::Get('system.use_ssl') == '1' ? (Settings::Get('system.leenabled') == '1' ? ($ssl_ipsandports != '' ? true : false) : false) : false), - 'label' => $lng['customer']['letsencrypt']['title'], - 'desc' => $lng['customer']['letsencrypt']['description'], - 'type' => 'checkbox', - 'values' => array( - array ('label' => $lng['panel']['yes'], 'value' => '1') - ), - 'value' => array() - ), 'openbasedir_path' => array( 'label' => $lng['domain']['openbasedirpath'], 'type' => 'select', 'select_var' => $openbasedir ) ) - ) + ), + 'section_bssl' => array( + 'title' => $lng['admin']['webserversettings_ssl'], + 'image' => 'icons/domain_add.png', + 'visible' => Settings::Get('system.use_ssl') == '1' ? true : false, + 'fields' => array( + 'ssl_redirect' => array( + 'visible' => ($ssl_ipsandports != '' ? true : false), + 'label' => $lng['domains']['ssl_redirect']['title'], + 'desc' => $lng['domains']['ssl_redirect']['description'], + 'type' => 'checkbox', + 'values' => array( + array ('label' => $lng['panel']['yes'], 'value' => '1') + ), + 'value' => array() + ), + 'letsencrypt' => array( + 'visible' => (Settings::Get('system.leenabled') == '1' ? ($ssl_ipsandports != '' ? true : false) : false), + 'label' => $lng['customer']['letsencrypt']['title'], + 'desc' => $lng['customer']['letsencrypt']['description'], + 'type' => 'checkbox', + 'values' => array( + array ('label' => $lng['panel']['yes'], 'value' => '1') + ), + 'value' => array() + ), + 'hsts_maxage' => array( + 'visible' => ($ssl_ipsandports != '' ? true : false), + 'label' => $lng['admin']['domain_hsts_maxage']['title'], + 'desc' => $lng['admin']['domain_hsts_maxage']['description'], + 'type' => 'int', + 'int_min' => 0, + 'int_max' => 94608000, // 3-years + 'value' => 0 + ), + 'hsts_incsub' => array( + 'visible' => ($ssl_ipsandports != '' ? true : false), + 'label' => $lng['admin']['domain_hsts_incsub']['title'], + 'desc' => $lng['admin']['domain_hsts_incsub']['description'], + 'type' => 'checkbox', + 'values' => array( + array ('label' => $lng['panel']['yes'], 'value' => '1') + ), + 'value' => array() + ), + 'hsts_preload' => array( + 'visible' => ($ssl_ipsandports != '' ? true : false), + 'label' => $lng['admin']['domain_hsts_preload']['title'], + 'desc' => $lng['admin']['domain_hsts_preload']['description'], + 'type' => 'checkbox', + 'values' => array( + array ('label' => $lng['panel']['yes'], 'value' => '1') + ), + 'value' => array() + ), + ), + ), ) ) ); diff --git a/lib/formfields/customer/domains/formfield.domains_edit.php b/lib/formfields/customer/domains/formfield.domains_edit.php index 0b319248..2e753e21 100644 --- a/lib/formfields/customer/domains/formfield.domains_edit.php +++ b/lib/formfields/customer/domains/formfield.domains_edit.php @@ -76,14 +76,27 @@ return array( ), 'value' => array($result['isemaildomain']) ), + 'openbasedir_path' => array( + 'visible' => ($result['openbasedir'] == '1') ? true : false, + 'label' => $lng['domain']['openbasedirpath'], + 'type' => 'select', + 'select_var' => $openbasedir + ) + ) + ), + 'section_bssl' => array( + 'title' => $lng['admin']['webserversettings_ssl'], + 'image' => 'icons/domain_edit.png', + 'visible' => Settings::Get('system.use_ssl') == '1' ? true : false, + 'fields' => array( 'ssl_redirect' => array( 'visible' => (Settings::Get('system.use_ssl') == '1' ? ($ssl_ipsandports != '' ? (domainHasSslIpPort($result['id']) ? true : false) : false) : false), 'label' => $lng['domains']['ssl_redirect']['title'], 'desc' => $lng['domains']['ssl_redirect']['description'] . ($result['temporary_ssl_redirect'] > 1 ? $lng['domains']['ssl_redirect_temporarilydisabled'] : ''), 'type' => 'checkbox', 'values' => array( - array ('label' => $lng['panel']['yes'], 'value' => '1') - ), + array ('label' => $lng['panel']['yes'], 'value' => '1') + ), 'value' => array($result['ssl_redirect']) ), 'letsencrypt' => array( @@ -92,18 +105,41 @@ return array( 'desc' => $lng['customer']['letsencrypt']['description'], 'type' => 'checkbox', 'values' => array( - array ('label' => $lng['panel']['yes'], 'value' => '1') - ), + array ('label' => $lng['panel']['yes'], 'value' => '1') + ), 'value' => array($result['letsencrypt']) ), - 'openbasedir_path' => array( - 'visible' => ($result['openbasedir'] == '1') ? true : false, - 'label' => $lng['domain']['openbasedirpath'], - 'type' => 'select', - 'select_var' => $openbasedir - ) + 'hsts_maxage' => array( + 'visible' => ($ssl_ipsandports != '' ? true : false), + 'label' => $lng['admin']['domain_hsts_maxage']['title'], + 'desc' => $lng['admin']['domain_hsts_maxage']['description'], + 'type' => 'int', + 'int_min' => 0, + 'int_max' => 94608000, // 3-years + 'value' => $result['hsts'] + ), + 'hsts_incsub' => array( + 'visible' => ($ssl_ipsandports != '' ? true : false), + 'label' => $lng['admin']['domain_hsts_incsub']['title'], + 'desc' => $lng['admin']['domain_hsts_incsub']['description'], + 'type' => 'checkbox', + 'values' => array( + array ('label' => $lng['panel']['yes'], 'value' => '1') + ), + 'value' => array($result['hsts_sub']) + ), + 'hsts_preload' => array( + 'visible' => ($ssl_ipsandports != '' ? true : false), + 'label' => $lng['admin']['domain_hsts_preload']['title'], + 'desc' => $lng['admin']['domain_hsts_preload']['description'], + 'type' => 'checkbox', + 'values' => array( + array ('label' => $lng['panel']['yes'], 'value' => '1') + ), + 'value' => array($result['hsts_preload']) + ), ) - ) + ), ) ) ); From 41c07d5b71da94808e70c154b526960270611dc8 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Fri, 7 Oct 2016 11:54:08 +0200 Subject: [PATCH 0202/1335] also add new hsts panel settings to froxlor.sql for new installs; refs #1660 Signed-off-by: Michael Kaufmann (d00p) --- install/froxlor.sql | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/install/froxlor.sql b/install/froxlor.sql index 63ce8661..c09e74a3 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -254,7 +254,7 @@ CREATE TABLE `panel_domains` ( `letsencrypt` tinyint(1) NOT NULL default '0', `hsts` varchar(10) NOT NULL default '0', `hsts_sub` tinyint(1) NOT NULL default '0', - `hsts_preload` tinyint(1) NOT NULL default '1', + `hsts_preload` tinyint(1) NOT NULL default '0', PRIMARY KEY (`id`), KEY `customerid` (`customerid`), KEY `parentdomain` (`parentdomainid`), @@ -542,6 +542,9 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES ('system', 'mail_smtp_auth', '1'), ('system', 'mail_smtp_user', ''), ('system', 'mail_smtp_passwd', ''), + ('system', 'hsts_maxage', '0'), + ('system', 'hsts_sub', '0'), + ('system', 'hsts_preload', '0'), ('panel', 'decimal_places', '4'), ('panel', 'adminmail', 'admin@SERVERNAME'), ('panel', 'phpmyadmin_url', ''), From 06e44b6e2b17949f40c8ba365ba451b47f4af9a7 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Fri, 7 Oct 2016 16:07:23 +0200 Subject: [PATCH 0203/1335] fix sql-query in customer_domains when updating domain Signed-off-by: Michael Kaufmann (d00p) --- customer_domains.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/customer_domains.php b/customer_domains.php index e79af357..d96d4fa0 100644 --- a/customer_domains.php +++ b/customer_domains.php @@ -695,7 +695,7 @@ if ($page == 'overview') { `letsencrypt`= :letsencrypt, `hsts` = :hsts, `hsts_sub` = :hsts_sub, - `hsts_preload` = :hsts_preload, + `hsts_preload` = :hsts_preload WHERE `customerid`= :customerid AND `id`= :id" ); From f1f7d7dd14814df2cc146e50c633d7ee3b26ac8b Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Fri, 7 Oct 2016 16:11:23 +0200 Subject: [PATCH 0204/1335] add db-version to error-reporting Signed-off-by: Michael Kaufmann (d00p) --- admin_index.php | 3 ++- customer_index.php | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/admin_index.php b/admin_index.php index a6247d05..177278bf 100644 --- a/admin_index.php +++ b/admin_index.php @@ -369,7 +369,8 @@ if ($page == 'overview') { $mail_body .= "File: ".$_error['file'].':'.$_error['line']."\n\n"; $mail_body .= "Trace:\n".trim($_error['trace'])."\n\n"; $mail_body .= "-------------------------------------------------------------\n\n"; - $mail_body .= "Froxlor-version: ".$version."\n\n"; + $mail_body .= "Froxlor-version: ".$version."\n"; + $mail_body .= "DB-version: ".$dbversion."\n\n"; $mail_body .= "End of report"; $mail_html = nl2br($mail_body); diff --git a/customer_index.php b/customer_index.php index 25f390dd..5e6c2aaf 100644 --- a/customer_index.php +++ b/customer_index.php @@ -274,7 +274,8 @@ if ($page == 'overview') { $mail_body .= "File: ".$_error['file'].':'.$_error['line']."\n\n"; $mail_body .= "Trace:\n".trim($_error['trace'])."\n\n"; $mail_body .= "-------------------------------------------------------------\n\n"; - $mail_body .= "Froxlor-version: ".$version."\n\n"; + $mail_body .= "Froxlor-version: ".$version."\n"; + $mail_body .= "DB-version: ".$dbversion."\n\n"; $mail_body .= "End of report"; $mail_html = str_replace("\n", "
", $mail_body); From 75d8d0b39752dfb40d9c128f247ac87c6caf5ea5 Mon Sep 17 00:00:00 2001 From: Florian Aders Date: Fri, 7 Oct 2016 19:08:32 +0200 Subject: [PATCH 0205/1335] Fix sslsettings in hsts for nginx --- scripts/jobs/cron_tasks.inc.http.30.nginx.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/jobs/cron_tasks.inc.http.30.nginx.php b/scripts/jobs/cron_tasks.inc.http.30.nginx.php index 3a79d610..6c454e12 100644 --- a/scripts/jobs/cron_tasks.inc.http.30.nginx.php +++ b/scripts/jobs/cron_tasks.inc.http.30.nginx.php @@ -608,14 +608,14 @@ class nginx extends HttpConfigBase } if (isset($domain_or_ip['hsts']) && $domain_or_ip['hsts'] >= 0) { - $vhost_content .= 'add_header Strict-Transport-Security "max-age=' . $domain_or_ip['hsts']; + $sslsettings .= 'add_header Strict-Transport-Security "max-age=' . $domain_or_ip['hsts']; if ($domain_or_ip['hsts_sub'] == 1) { - $vhost_content .= '; includeSubDomains'; + $sslsettings .= '; includeSubDomains'; } if ($domain_or_ip['hsts_preload'] == 1) { - $vhost_content .= '; preload'; + $sslsettings .= '; preload'; } - $vhost_content .= '";' . "\n"; + $sslsettings .= '";' . "\n"; } } } From 91c2d4efbe1dc1ea852b2d9e327d2712e31c96aa Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 11 Oct 2016 07:49:52 +0200 Subject: [PATCH 0206/1335] do not redirect when requesting let's encrypt certificates in nginx (same as we do in apache) Signed-off-by: Michael Kaufmann (d00p) --- scripts/jobs/cron_tasks.inc.http.30.nginx.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/scripts/jobs/cron_tasks.inc.http.30.nginx.php b/scripts/jobs/cron_tasks.inc.http.30.nginx.php index 6c454e12..6f765162 100644 --- a/scripts/jobs/cron_tasks.inc.http.30.nginx.php +++ b/scripts/jobs/cron_tasks.inc.http.30.nginx.php @@ -217,7 +217,9 @@ class nginx extends HttpConfigBase } else { $_sslport = $this->checkAlternativeSslPort(); $mypath = 'https://' . Settings::Get('system.hostname') . $_sslport . '/'; - $this->nginx_data[$vhost_filename] .= "\t" . 'return 301 ' . $mypath . '$request_uri;' . "\n"; + $this->nginx_data[$vhost_filename] .= "\t" . 'if ($request_uri !~ "^/\.well-known/acme-challenge/\w+$") {' . "\n"; + $this->nginx_data[$vhost_filename] .= "\t\t" . 'return 301 ' . $mypath . '$request_uri;' . "\n"; + $this->nginx_data[$vhost_filename] .= "\t" . '}' . "\n"; } } @@ -458,7 +460,9 @@ class nginx extends HttpConfigBase if (substr($uri, - 1) == '/') { $uri = substr($uri, 0, - 1); } - $vhost_content .= "\t" . 'return 301 ' . $uri . '$request_uri;' . "\n"; + $vhost_content .= "\t" . 'if ($request_uri !~ "^/\.well-known/acme-challenge/\w+$") {' . "\n"; + $vhost_content .= "\t\t" . 'return 301 ' . $uri . '$request_uri;' . "\n"; + $vhost_content .= "\t" . '}' . "\n"; } else { mkDirWithCorrectOwnership($domain['customerroot'], $domain['documentroot'], $domain['guid'], $domain['guid'], true); From 58c14376d6839e6d85257a9f5e16b8ec3a684c24 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 11 Oct 2016 07:50:25 +0200 Subject: [PATCH 0207/1335] set version to 0.9.38-rc1 for upcoming release candidate Signed-off-by: Michael Kaufmann (d00p) --- install/froxlor.sql | 2 +- install/updates/froxlor/0.9/update_0.9.inc.php | 6 ++++++ lib/version.inc.php | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/install/froxlor.sql b/install/froxlor.sql index c09e74a3..c17abf45 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -575,7 +575,7 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES ('panel', 'password_numeric', '0'), ('panel', 'password_special_char_required', '0'), ('panel', 'password_special_char', '!?<>§$%+#=@'), - ('panel', 'version', '0.9.37'), + ('panel', 'version', '0.9.38-rc1'), ('panel', 'db_version', '201610070'); diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index 66fea160..3767ec8a 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -3501,3 +3501,9 @@ if (isDatabaseVersion('201609240')) { updateToDbVersion('201610070'); } + +if (isFroxlorVersion('0.9.37')) { + + showUpdateStep("Updating from 0.9.37 to 0.9.38-rc1", false); + updateToVersion('0.9.38-rc1'); +} diff --git a/lib/version.inc.php b/lib/version.inc.php index 52a85722..7fa046a2 100644 --- a/lib/version.inc.php +++ b/lib/version.inc.php @@ -16,7 +16,7 @@ */ // Main version variable -$version = '0.9.37'; +$version = '0.9.38-rc1'; // Database version (YYYYMMDDC where C is a daily counter) $dbversion = '201610070'; From fc2ae594cbf2da6e96e954fc18c893a50941ba5a Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 13 Oct 2016 09:51:57 +0200 Subject: [PATCH 0208/1335] enable custom redirect codes also for nginx Signed-off-by: Michael Kaufmann (d00p) --- lib/formfields/customer/domains/formfield.domains_add.php | 2 +- lib/formfields/customer/domains/formfield.domains_edit.php | 2 +- scripts/jobs/cron_tasks.inc.http.10.apache.php | 2 ++ scripts/jobs/cron_tasks.inc.http.30.nginx.php | 7 ++++++- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/lib/formfields/customer/domains/formfield.domains_add.php b/lib/formfields/customer/domains/formfield.domains_add.php index e25302b0..714a3eb9 100644 --- a/lib/formfields/customer/domains/formfield.domains_add.php +++ b/lib/formfields/customer/domains/formfield.domains_add.php @@ -54,7 +54,7 @@ return array( 'type' => 'text' ), 'redirectcode' => array( - 'visible' => ((Settings::Get('system.webserver') == 'apache2' && Settings::Get('customredirect.enabled') == '1') ? true : false), + 'visible' => ((Settings::Get('system.webserver') != 'lighttpd' && Settings::Get('customredirect.enabled') == '1') ? true : false), 'label' => $lng['domains']['redirectifpathisurl'], 'desc' => $lng['domains']['redirectifpathisurlinfo'], 'type' => 'select', diff --git a/lib/formfields/customer/domains/formfield.domains_edit.php b/lib/formfields/customer/domains/formfield.domains_edit.php index 2e753e21..dc3403fd 100644 --- a/lib/formfields/customer/domains/formfield.domains_edit.php +++ b/lib/formfields/customer/domains/formfield.domains_edit.php @@ -54,7 +54,7 @@ return array( 'value' => $urlvalue ), 'redirectcode' => array( - 'visible' => ((Settings::Get('system.webserver') == 'apache2' && Settings::Get('customredirect.enabled') == '1') ? true : false), + 'visible' => ((Settings::Get('system.webserver') != 'lighttpd' && Settings::Get('customredirect.enabled') == '1') ? true : false), 'label' => $lng['domains']['redirectifpathisurl'], 'desc' => $lng['domains']['redirectifpathisurlinfo'], 'type' => 'select', diff --git a/scripts/jobs/cron_tasks.inc.http.10.apache.php b/scripts/jobs/cron_tasks.inc.http.10.apache.php index 07ec5752..8fce5cc0 100644 --- a/scripts/jobs/cron_tasks.inc.http.10.apache.php +++ b/scripts/jobs/cron_tasks.inc.http.10.apache.php @@ -862,6 +862,8 @@ class apache extends HttpConfigBase if (preg_match('/^https?\:\/\//', $domain['documentroot'])) { $corrected_docroot = $this->idnaConvert->encode_uri($domain['documentroot']); + // prevent empty return-cde + $code = "301"; // Get domain's redirect code $code = getDomainRedirectCode($domain['id']); $modrew_red = ''; diff --git a/scripts/jobs/cron_tasks.inc.http.30.nginx.php b/scripts/jobs/cron_tasks.inc.http.30.nginx.php index 6f765162..a9e2c457 100644 --- a/scripts/jobs/cron_tasks.inc.http.30.nginx.php +++ b/scripts/jobs/cron_tasks.inc.http.30.nginx.php @@ -460,8 +460,13 @@ class nginx extends HttpConfigBase if (substr($uri, - 1) == '/') { $uri = substr($uri, 0, - 1); } + // prevent empty return-cde + $code = "301"; + // Get domain's redirect code + $code = getDomainRedirectCode($domain['id']); + $vhost_content .= "\t" . 'if ($request_uri !~ "^/\.well-known/acme-challenge/\w+$") {' . "\n"; - $vhost_content .= "\t\t" . 'return 301 ' . $uri . '$request_uri;' . "\n"; + $vhost_content .= "\t\t" . 'return ' . $code .' ' . $uri . '$request_uri;' . "\n"; $vhost_content .= "\t" . '}' . "\n"; } else { mkDirWithCorrectOwnership($domain['customerroot'], $domain['documentroot'], $domain['guid'], $domain['guid'], true); From ce31a0b3fdd67835396a66944e4e0d87c1d64ca3 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 13 Oct 2016 10:16:17 +0200 Subject: [PATCH 0209/1335] enable custom redirect codes also for lighttpd Signed-off-by: Michael Kaufmann (d00p) --- actions/admin/settings/130.webserver.php | 12 ++---------- .../customer/domains/formfield.domains_add.php | 2 +- .../customer/domains/formfield.domains_edit.php | 2 +- scripts/jobs/cron_tasks.inc.http.20.lighttpd.php | 9 ++++++++- 4 files changed, 12 insertions(+), 13 deletions(-) diff --git a/actions/admin/settings/130.webserver.php b/actions/admin/settings/130.webserver.php index b6cfc9a8..4374b148 100644 --- a/actions/admin/settings/130.webserver.php +++ b/actions/admin/settings/130.webserver.php @@ -260,11 +260,7 @@ return array( 'varname' => 'enabled', 'type' => 'bool', 'default' => false, - 'save_method' => 'storeSettingField', - 'websrv_avail' => array( - 'apache2', - 'lighttpd' - ) + 'save_method' => 'storeSettingField' ), 'customredirect_default' => array( 'label' => $lng['serversettings']['customredirect_default'], @@ -274,11 +270,7 @@ return array( 'default' => '1', 'option_mode' => 'one', 'option_options_method' => 'getRedirectCodes', - 'save_method' => 'storeSettingField', - 'websrv_avail' => array( - 'apache2', - 'lighttpd' - ) + 'save_method' => 'storeSettingField' ) ) ) diff --git a/lib/formfields/customer/domains/formfield.domains_add.php b/lib/formfields/customer/domains/formfield.domains_add.php index 714a3eb9..fb9adc47 100644 --- a/lib/formfields/customer/domains/formfield.domains_add.php +++ b/lib/formfields/customer/domains/formfield.domains_add.php @@ -54,7 +54,7 @@ return array( 'type' => 'text' ), 'redirectcode' => array( - 'visible' => ((Settings::Get('system.webserver') != 'lighttpd' && Settings::Get('customredirect.enabled') == '1') ? true : false), + 'visible' => (Settings::Get('customredirect.enabled') == '1' ? true : false), 'label' => $lng['domains']['redirectifpathisurl'], 'desc' => $lng['domains']['redirectifpathisurlinfo'], 'type' => 'select', diff --git a/lib/formfields/customer/domains/formfield.domains_edit.php b/lib/formfields/customer/domains/formfield.domains_edit.php index dc3403fd..2a7b593b 100644 --- a/lib/formfields/customer/domains/formfield.domains_edit.php +++ b/lib/formfields/customer/domains/formfield.domains_edit.php @@ -54,7 +54,7 @@ return array( 'value' => $urlvalue ), 'redirectcode' => array( - 'visible' => ((Settings::Get('system.webserver') != 'lighttpd' && Settings::Get('customredirect.enabled') == '1') ? true : false), + 'visible' => (Settings::Get('customredirect.enabled') == '1' ? true : false), 'label' => $lng['domains']['redirectifpathisurl'], 'desc' => $lng['domains']['redirectifpathisurlinfo'], 'type' => 'select', diff --git a/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php b/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php index 651f80db..7c38b3c6 100644 --- a/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php +++ b/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php @@ -429,8 +429,15 @@ class lighttpd extends HttpConfigBase $domain['documentroot'] = trim($domain['documentroot']); if (preg_match('/^https?\:\/\//', $domain['documentroot'])) { + $uri = $this->idnaConvert->encode_uri($domain['documentroot']); + // prevent empty return-cde + $code = "301"; + // Get domain's redirect code + $code = getDomainRedirectCode($domain['id']); + + $vhost_content .= ' url.redirect-code = ' . $code. "\n"; $vhost_content .= ' url.redirect = (' . "\n"; - $vhost_content .= ' "^/(.*)$" => "' . $this->idnaConvert->encode_uri($domain['documentroot']) . '$1"' . "\n"; + $vhost_content .= ' "^/(.*)$" => "' . $uri . '$1"' . "\n"; $vhost_content .= ' )' . "\n"; } else { From f406962dfd2b2ea63ee75c4f46d4058b8785cd8e Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 13 Oct 2016 10:48:28 +0200 Subject: [PATCH 0210/1335] allow empty select-value for panel.customer_hide_options Signed-off-by: Michael Kaufmann (d00p) --- actions/admin/settings/100.panel.php | 1 + .../formfields/option/function.validateFormFieldOption.php | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/actions/admin/settings/100.panel.php b/actions/admin/settings/100.panel.php index 9a262377..49308bed 100644 --- a/actions/admin/settings/100.panel.php +++ b/actions/admin/settings/100.panel.php @@ -234,6 +234,7 @@ return array( 'type' => 'option', 'default' => '', 'option_mode' => 'multiple', + 'option_emptyallowed' => true, 'option_options' => array( 'email' => $lng['menue']['email']['email'], 'mysql' => $lng['menue']['mysql']['mysql'], diff --git a/lib/functions/formfields/option/function.validateFormFieldOption.php b/lib/functions/formfields/option/function.validateFormFieldOption.php index bbfa80ff..cbda0408 100644 --- a/lib/functions/formfields/option/function.validateFormFieldOption.php +++ b/lib/functions/formfields/option/function.validateFormFieldOption.php @@ -20,7 +20,7 @@ function validateFormFieldOption($fieldname, $fielddata, $newfieldvalue) { $returnvalue = true; - + if(isset($fielddata['option_mode']) && $fielddata['option_mode'] == 'multiple') { $options = explode(',', $newfieldvalue); @@ -33,13 +33,16 @@ function validateFormFieldOption($fieldname, $fielddata, $newfieldvalue) { $returnvalue = isset($fielddata['option_options'][$newfieldvalue]); } - + if($returnvalue === true) { return true; } else { + if (isset($fielddata['option_emptyallowed']) && $fielddata['option_emptyallowed']) { + return true; + } return 'not in option'; } } From 3597a89da30f17dcf4d6a59ce3d7534397d68c05 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 13 Oct 2016 11:08:26 +0200 Subject: [PATCH 0211/1335] add missing customer_hide_option for fresh installs Signed-off-by: Michael Kaufmann (d00p) --- install/froxlor.sql | 1 + 1 file changed, 1 insertion(+) diff --git a/install/froxlor.sql b/install/froxlor.sql index c17abf45..840d6301 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -575,6 +575,7 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES ('panel', 'password_numeric', '0'), ('panel', 'password_special_char_required', '0'), ('panel', 'password_special_char', '!?<>§$%+#=@'), + ('panel', 'customer_hide_options', ''), ('panel', 'version', '0.9.38-rc1'), ('panel', 'db_version', '201610070'); From 2bee58166b5b3e9d4005b0ae4c45e07e23e56667 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 18 Oct 2016 09:07:20 +0200 Subject: [PATCH 0212/1335] show also Subject-alternative-names in ssl-overview Signed-off-by: Michael Kaufmann (d00p) --- ssl_certificates.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/ssl_certificates.php b/ssl_certificates.php index bfe81553..9733faf4 100644 --- a/ssl_certificates.php +++ b/ssl_certificates.php @@ -119,6 +119,18 @@ if (count($all_certs) == 0) { $isValid = false; } + $san_list = ""; + if (isset($cert_data['extensions']['subjectAltName']) && !empty($cert_data['extensions']['subjectAltName'])) { + $SANs = explode(",", $cert_data['extensions']['subjectAltName']); + $SANs = array_map('trim', $SANs); + foreach ($SANs as $san) { + $san = str_replace("DNS:", "", $san); + if ($san != $cert_data['subject']['CN'] && strpos($san, "othername:") === false) { + $san_list .= $san."
"; + } + } + } + $row = htmlentities_array($cert); eval("\$certificates.=\"" . getTemplate("ssl_certificates/certs_cert", true) . "\";"); } else { From 979b1b0ad836fd5238a6ec564716479740c760f2 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 18 Oct 2016 13:23:35 +0200 Subject: [PATCH 0213/1335] forgot to add template for subjectAlternativeName stuff in ssl-overview Signed-off-by: Michael Kaufmann (d00p) --- templates/Sparkle/ssl_certificates/certs_cert.tpl | 1 + 1 file changed, 1 insertion(+) diff --git a/templates/Sparkle/ssl_certificates/certs_cert.tpl b/templates/Sparkle/ssl_certificates/certs_cert.tpl index 90d502fa..250a57f1 100644 --- a/templates/Sparkle/ssl_certificates/certs_cert.tpl +++ b/templates/Sparkle/ssl_certificates/certs_cert.tpl @@ -5,6 +5,7 @@ {$cert_data['subject']['CN']} +
SAN: {$san_list}
{$cert_data['issuer']['O']} From b4e8458076b0773d766f0282f4ae80511cece7dc Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 18 Oct 2016 15:32:14 +0200 Subject: [PATCH 0214/1335] enhance findDirs function and filter awstats/webalizer (sub)folders for target-directory selection Signed-off-by: Michael Kaufmann (d00p) --- lib/functions/filedir/function.findDirs.php | 75 +++++++++++++++------ 1 file changed, 54 insertions(+), 21 deletions(-) diff --git a/lib/functions/filedir/function.findDirs.php b/lib/functions/filedir/function.findDirs.php index 8d1c0669..fb8f57e5 100644 --- a/lib/functions/filedir/function.findDirs.php +++ b/lib/functions/filedir/function.findDirs.php @@ -17,27 +17,57 @@ * */ + /** * Returns an array of found directories * * This function checks every found directory if they match either $uid or $gid, if they do * the found directory is valid. It uses recursive-iterators to find subdirectories. * - * @param string $path the path to start searching in - * @param int $uid the uid which must match the found directories - * @param int $gid the gid which must match the found direcotries + * @param string $path + * the path to start searching in + * @param int $uid + * the uid which must match the found directories + * @param int $gid + * the gid which must match the found direcotries * * @return array Array of found valid paths */ -function findDirs($path, $uid, $gid) { - - $_fileList = array (); +function findDirs($path, $uid, $gid) +{ + $_fileList = array(); $path = makeCorrectDir($path); // valid directory? if (is_dir($path)) { + + // Will exclude everything under these directories + $exclude = array( + 'awstats', + 'webalizer' + ); + + /** + * + * @param SplFileInfo $file + * @param mixed $key + * @param RecursiveCallbackFilterIterator $iterator + * @return bool True if you need to recurse or if the item is acceptable + */ + $filter = function ($file, $key, $iterator) use ($exclude) { + if (in_array($file->getFilename(), $exclude)) { + return false; + } + return true; + }; + // create RecursiveIteratorIterator - $its = new RecursiveIteratorIterator(new IgnorantRecursiveDirectoryIterator($path)); + $its = new RecursiveIteratorIterator( + new RecursiveCallbackFilterIterator( + new IgnorantRecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS), + $filter + ) + ); // we can limit the recursion-depth, but will it be helpful or // will people start asking "why do I only see 2 subdirectories, i want to use /a/b/c" // let's keep this in mind and see whether it will be useful @@ -50,24 +80,27 @@ function findDirs($path, $uid, $gid) { $_fileList[] = makeCorrectDir(dirname($fullFileName)); } } + $_fileList[] = $path; } return array_unique($_fileList); - } /** -* If you use RecursiveDirectoryIterator with RecursiveIteratorIterator and run -* into UnexpectedValueException you may use this little hack to ignore those -* directories, such as lost+found on linux. -* (User "antennen" @ http://php.net/manual/en/class.recursivedirectoryiterator.php#101654) -**/ -class IgnorantRecursiveDirectoryIterator extends RecursiveDirectoryIterator { - function getChildren() { - try { - return new IgnorantRecursiveDirectoryIterator($this->getPathname()); - } catch(UnexpectedValueException $e) { - return new RecursiveArrayIterator(array()); - } - } + * If you use RecursiveDirectoryIterator with RecursiveIteratorIterator and run + * into UnexpectedValueException you may use this little hack to ignore those + * directories, such as lost+found on linux. + * (User "antennen" @ http://php.net/manual/en/class.recursivedirectoryiterator.php#101654) + */ +class IgnorantRecursiveDirectoryIterator extends RecursiveDirectoryIterator +{ + + function getChildren() + { + try { + return new IgnorantRecursiveDirectoryIterator($this->getPathname()); + } catch (UnexpectedValueException $e) { + return new RecursiveArrayIterator(array()); + } + } } From fa45de6586a489bf84915ace9e04f170ad6e255f Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Wed, 19 Oct 2016 19:22:52 +0200 Subject: [PATCH 0215/1335] fix empty dir parameter for makeCorrectDir() when directory-selection is set to 'Dropdown', thx to D2Red for letting me debug on his system Signed-off-by: Michael Kaufmann (d00p) --- lib/functions/filedir/function.makeCorrectDir.php | 2 +- lib/functions/filedir/function.makePathfield.php | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/functions/filedir/function.makeCorrectDir.php b/lib/functions/filedir/function.makeCorrectDir.php index f457818b..75dc00d1 100644 --- a/lib/functions/filedir/function.makeCorrectDir.php +++ b/lib/functions/filedir/function.makeCorrectDir.php @@ -26,7 +26,7 @@ */ function makeCorrectDir($dir) { - assert('is_string($dir) && strlen($dir) > 0 /* $dir does not look like an actual folder name */'); + assert('is_string($dir) && strlen($dir) > 0', 'Value "' . $dir .'" does not look like an actual folder name'); $dir = trim($dir); diff --git a/lib/functions/filedir/function.makePathfield.php b/lib/functions/filedir/function.makePathfield.php index 3cc8c762..c5815dfe 100644 --- a/lib/functions/filedir/function.makePathfield.php +++ b/lib/functions/filedir/function.makePathfield.php @@ -64,7 +64,12 @@ function makePathfield($path, $uid, $gid, $value = '', $dom = false) { $_field = ''; foreach ($dirList as $key => $dir) { if (strpos($dir, $path) === 0) { - $dir = makeCorrectDir(substr($dir, strlen($path))); + $dir = substr($dir, strlen($path)); + // docroot cut off of current directory == empty -> directory is the docroot + if (empty($dir)) { + $dir = '/'; + } + $dir = makeCorrectDir($dir); } $_field.= makeoption($dir, $dir, $value); } From af77453bfe2e7163bd1167bb53fe72d0bd29f73e Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 20 Oct 2016 09:23:44 +0200 Subject: [PATCH 0216/1335] do not generate ssl-vhost-container without a certificate Signed-off-by: Michael Kaufmann (d00p) --- scripts/jobs/cron_tasks.inc.http.10.apache.php | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/scripts/jobs/cron_tasks.inc.http.10.apache.php b/scripts/jobs/cron_tasks.inc.http.10.apache.php index 8fce5cc0..a39c068d 100644 --- a/scripts/jobs/cron_tasks.inc.http.10.apache.php +++ b/scripts/jobs/cron_tasks.inc.http.10.apache.php @@ -183,6 +183,10 @@ class apache extends HttpConfigBase } if ($row_ipsandports['vhostcontainer'] == '1') { + + $without_vhost = $this->virtualhosts_data[$vhosts_filename]; + $close_vhost = true; + $this->virtualhosts_data[$vhosts_filename] .= '' . "\n"; $mypath = $this->getMyPath($row_ipsandports); @@ -332,7 +336,7 @@ class apache extends HttpConfigBase 'id' => 'none', 'domain' => Settings::Get('system.hostname'), 'adminid' => 1, /* first admin-user (superadmin) */ - 'guid' => Settings::Get('system.httpuser'), + 'guid' => Settings::Get('system.httpuser'), 'openbasedir' => 0, 'email' => Settings::Get('panel.adminmail'), 'loginname' => 'froxlor.panel', @@ -443,10 +447,19 @@ class apache extends HttpConfigBase } } } + } else { + // if there is no cert-file specified but we are generating a ssl-vhost, + // we should return an empty string because this vhost would suck dick, ref #1583 + $this->logger->logAction(CRON_ACTION, LOG_ERR, $domain['domain'] . ' :: empty certificate file! Cannot create ssl-directives'); + $this->virtualhosts_data[$vhosts_filename] = $without_vhost; + $this->virtualhosts_data[$vhosts_filename] .= '# no ssl-certificate was specified for this domain, therefore no explicit vhost-container is being generated'; + $close_vhost = false; } } - $this->virtualhosts_data[$vhosts_filename] .= '' . "\n"; + if ($close_vhost) { + $this->virtualhosts_data[$vhosts_filename] .= '' . "\n"; + } $this->logger->logAction(CRON_ACTION, LOG_DEBUG, $ipport . ' :: inserted vhostcontainer'); } unset($vhosts_filename); From 884b2ed9137cf4d68b2875f86bd6bdd597be6932 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 25 Oct 2016 12:24:57 +0200 Subject: [PATCH 0217/1335] various fixes for idn converted domains + fix undefined index alias when editing a domain with alias Signed-off-by: Michael Kaufmann (d00p) --- admin_domains.php | 2 +- scripts/jobs/cron_tasks.inc.http.10.apache.php | 18 +++++++++--------- ssl_certificates.php | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/admin_domains.php b/admin_domains.php index 99dc415d..e1bbff05 100644 --- a/admin_domains.php +++ b/admin_domains.php @@ -1227,7 +1227,7 @@ if ($page == 'domains' || $page == 'overview') { $adminid = $result['adminid']; } - $aliasdomain = intval($_POST['alias']); + $aliasdomain = isset($_POST['alias']) ? intval($_POST['alias']) : 0; $issubof = intval($_POST['issubof']); $subcanemaildomain = intval($_POST['subcanemaildomain']); $caneditdomain = isset($_POST['caneditdomain']) ? intval($_POST['caneditdomain']) : 0; diff --git a/scripts/jobs/cron_tasks.inc.http.10.apache.php b/scripts/jobs/cron_tasks.inc.http.10.apache.php index a39c068d..95be0e1c 100644 --- a/scripts/jobs/cron_tasks.inc.http.10.apache.php +++ b/scripts/jobs/cron_tasks.inc.http.10.apache.php @@ -1,5 +1,4 @@ -Part (starter is created in apache_fcgid) if (Settings::Get('system.mod_fcgid_ownvhost') == '1' && Settings::Get('system.mod_fcgid') == '1') { $configdir = makeCorrectDir(Settings::Get('system.mod_fcgid_configdir') . '/froxlor.panel/' . Settings::Get('system.hostname')); @@ -278,13 +277,14 @@ class apache extends HttpConfigBase } $this->virtualhosts_data[$vhosts_filename] .= ' ' . "\n"; } - } // create php-fpm -Part (config is created in apache_fcgid) + } elseif (Settings::Get('phpfpm.enabled') == '1') { + // create php-fpm -Part (config is created in apache_fcgid) $domain = array( 'id' => 'none', 'domain' => Settings::Get('system.hostname'), 'adminid' => 1, /* first admin-user (superadmin) */ - 'mod_fcgid_starter' => - 1, + 'mod_fcgid_starter' => - 1, 'mod_fcgid_maxrequests' => - 1, 'guid' => Settings::Get('phpfpm.vhost_httpuser'), 'openbasedir' => 0, @@ -615,10 +615,10 @@ class apache extends HttpConfigBase } else { $stats_text .= ' Alias /webalizer "' . makeCorrectFile($domain['customerroot'] . '/webalizer') . '"' . "\n"; } - } // if the docroots are equal, we still have to set an alias for awstats - // because the stats are in /awstats/[domain], not just /awstats/ - // also, the awstats-icons are someplace else too! - // -> webalizer does not need this! + } // if the docroots are equal, we still have to set an alias for awstats + // because the stats are in /awstats/[domain], not just /awstats/ + // also, the awstats-icons are someplace else too! + // -> webalizer does not need this! elseif (Settings::Get('system.awstats_enabled') == '1') { $stats_text .= ' Alias /awstats "' . makeCorrectFile($domain['documentroot'] . '/awstats/' . $domain['domain']) . '"' . "\n"; $stats_text .= ' Alias /awstats-icon "' . makeCorrectDir(Settings::Get('system.awstats_icons')) . '"' . "\n"; @@ -873,7 +873,7 @@ class apache extends HttpConfigBase $domain['documentroot'] = trim($domain['documentroot']); if (preg_match('/^https?\:\/\//', $domain['documentroot'])) { - $corrected_docroot = $this->idnaConvert->encode_uri($domain['documentroot']); + $corrected_docroot = $domain['documentroot']; // prevent empty return-cde $code = "301"; diff --git a/ssl_certificates.php b/ssl_certificates.php index 9733faf4..8c59c63b 100644 --- a/ssl_certificates.php +++ b/ssl_certificates.php @@ -96,7 +96,7 @@ if (count($all_certs) == 0) { $cert_data = openssl_x509_parse($cert['ssl_cert_file']); - $cert['domain'] = $idna_convert->encode($cert['domain']); + $cert['domain'] = $idna_convert->decode($cert['domain']); $adminCustomerLink = ""; if (AREA == 'admin') { From 462fca732801e8ea26d4adc483a8e328d945516c Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Fri, 28 Oct 2016 10:57:51 +0200 Subject: [PATCH 0218/1335] do not add www.[froxlorfqdn] to SAN list of certificate request for Let's Encrypt froxlor-vhost certificate; fixes #1662 Signed-off-by: Michael Kaufmann (d00p) --- scripts/jobs/cron_letsencrypt.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/scripts/jobs/cron_letsencrypt.php b/scripts/jobs/cron_letsencrypt.php index 8df9055f..2101fcca 100644 --- a/scripts/jobs/cron_letsencrypt.php +++ b/scripts/jobs/cron_letsencrypt.php @@ -141,8 +141,7 @@ if (Settings::Get('system.le_froxlor_enabled') == '1') { if ($insert_or_update_required) { $domains = array( - $certrow['domain'], - 'www.' . $certrow['domain'] + $certrow['domain'] ); // Only renew let's encrypt certificate if no broken ssl_redirect is enabled From 304926260fbd6479329cafbfd2e938c0fb2fe378 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sat, 29 Oct 2016 14:20:50 +0200 Subject: [PATCH 0219/1335] PowerDNS does not like multi-line-format, thx to Marc Signed-off-by: Michael Kaufmann (d00p) --- lib/functions/dns/function.createDomainZone.php | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/functions/dns/function.createDomainZone.php b/lib/functions/dns/function.createDomainZone.php index 938d7200..ad21ee45 100644 --- a/lib/functions/dns/function.createDomainZone.php +++ b/lib/functions/dns/function.createDomainZone.php @@ -275,13 +275,11 @@ function createDomainZone($domain_id, $froxlorhostname = false, $isMainButSubTo Database::pexecute($upd_stmt, array('serial' => $domain['bindserial'], 'id' => $domain['id'])); } - $soa_content = $primary_ns . " " . escapeSoaAdminMail(Settings::Get('panel.adminmail')) . " (" . PHP_EOL; - $soa_content .= $domain['bindserial'] . "\t; serial" . PHP_EOL; + // PowerDNS does not like multi-line-format + $soa_content = $primary_ns . " " . escapeSoaAdminMail(Settings::Get('panel.adminmail')) . " "; + $soa_content .= $domain['bindserial'] . " "; // TODO for now, dummy time-periods - $soa_content .= "1800\t; refresh (30 mins)" . PHP_EOL; - $soa_content .= "900\t; retry (15 mins)" . PHP_EOL; - $soa_content .= "604800\t; expire (7 days)" . PHP_EOL; - $soa_content .= "1200\t)\t; minimum (20 mins)"; + $soa_content .= "1800 900 604800 1200"; $soa_record = new DnsEntry('@', 'SOA', $soa_content); array_unshift($zonerecords, $soa_record); From a44be363a67881b1e3655aa80cadf57ee2bc1d33 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 31 Oct 2016 10:53:18 +0100 Subject: [PATCH 0220/1335] set domain, letsencrypt-flag and loginname for froxlor-hostname in ssl-overview because the certificate will get removed as invalid otherwise, thx to Tbyte Signed-off-by: Michael Kaufmann (d00p) --- ssl_certificates.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/ssl_certificates.php b/ssl_certificates.php index 8c59c63b..0d4f9c03 100644 --- a/ssl_certificates.php +++ b/ssl_certificates.php @@ -86,11 +86,21 @@ if (count($all_certs) == 0) { foreach ($all_certs as $idx => $cert) { if ($paging->checkDisplay($idx)) { + // respect froxlor-hostname + if ($cert['domainid'] == 0) { + $cert['domain'] = Settings::Get('system.hostname'); + $cert['letsencrypt'] = Settings::Get('system.le_froxlor_enabled'); + $cert['loginname'] = 'froxlor.panel'; + } + if (empty($cert['domain']) || empty($cert['ssl_cert_file'])) { // no domain found to the entry or empty entry - safely delete it from the DB + /* Database::pexecute($del_stmt, array( 'id' => $cert['id'] )); + */ + echo "no domain found to the entry or empty entry - safely delete it from the DB"; continue; } @@ -99,7 +109,7 @@ if (count($all_certs) == 0) { $cert['domain'] = $idna_convert->decode($cert['domain']); $adminCustomerLink = ""; - if (AREA == 'admin') { + if (AREA == 'admin' && $cert['domainid'] > 0) { if (! empty($cert['loginname'])) { $adminCustomerLink = ' ( Date: Mon, 31 Oct 2016 11:17:56 +0100 Subject: [PATCH 0221/1335] disable tlsv1.1 for ssl settings Signed-off-by: Michael Kaufmann (d00p) --- scripts/jobs/cron_tasks.inc.http.10.apache.php | 4 ++-- scripts/jobs/cron_tasks.inc.http.20.lighttpd.php | 4 ++++ scripts/jobs/cron_tasks.inc.http.30.nginx.php | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/scripts/jobs/cron_tasks.inc.http.10.apache.php b/scripts/jobs/cron_tasks.inc.http.10.apache.php index 95be0e1c..30644a56 100644 --- a/scripts/jobs/cron_tasks.inc.http.10.apache.php +++ b/scripts/jobs/cron_tasks.inc.http.10.apache.php @@ -412,7 +412,7 @@ class apache extends HttpConfigBase } else { $this->virtualhosts_data[$vhosts_filename] .= ' SSLEngine On' . "\n"; - $this->virtualhosts_data[$vhosts_filename] .= ' SSLProtocol ALL -SSLv2 -SSLv3' . "\n"; + $this->virtualhosts_data[$vhosts_filename] .= ' SSLProtocol -ALL +TLSv1.0 +TLSv1.2' . "\n"; // this makes it more secure, thx to Marcel (08/2013) $this->virtualhosts_data[$vhosts_filename] .= ' SSLHonorCipherOrder On' . "\n"; $this->virtualhosts_data[$vhosts_filename] .= ' SSLCipherSuite ' . Settings::Get('system.ssl_cipher_list') . "\n"; @@ -830,7 +830,7 @@ class apache extends HttpConfigBase if ($domain['ssl_cert_file'] != '') { $vhost_content .= ' SSLEngine On' . "\n"; - $vhost_content .= ' SSLProtocol ALL -SSLv2 -SSLv3' . "\n"; + $vhost_content .= ' SSLProtocol -ALL +TLSv1.0 +TLSv1.2' . "\n"; // this makes it more secure, thx to Marcel (08/2013) $vhost_content .= ' SSLHonorCipherOrder On' . "\n"; $vhost_content .= ' SSLCipherSuite ' . Settings::Get('system.ssl_cipher_list') . "\n"; diff --git a/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php b/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php index 7c38b3c6..a8cd208e 100644 --- a/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php +++ b/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php @@ -210,7 +210,9 @@ class lighttpd extends HttpConfigBase echo $ip . ':' . $port . ' :: certificate file "' . $domain['ssl_cert_file'] . '" does not exist! Cannot create SSL-directives' . "\n"; } else { $this->lighttpd_data[$vhost_filename] .= 'ssl.engine = "enable"' . "\n"; + $this->lighttpd_data[$vhost_filename] .= 'ssl.use-compression = "disable"' . "\n"; $this->lighttpd_data[$vhost_filename] .= 'ssl.use-sslv2 = "disable"' . "\n"; + $this->lighttpd_data[$vhost_filename] .= 'ssl.use-sslv3 = "disable"' . "\n"; $this->lighttpd_data[$vhost_filename] .= 'ssl.cipher-list = "' . Settings::Get('system.ssl_cipher_list') . '"' . "\n"; $this->lighttpd_data[$vhost_filename] .= 'ssl.honor-cipher-order = "enable"' . "\n"; $this->lighttpd_data[$vhost_filename] .= 'ssl.pemfile = "' . makeCorrectFile($domain['ssl_cert_file']) . '"' . "\n"; @@ -517,7 +519,9 @@ class lighttpd extends HttpConfigBase if ($domain['ssl_cert_file'] != '') { $ssl_settings .= 'ssl.engine = "enable"' . "\n"; + $ssl_settings .= 'ssl.use-compression = "disable"' . "\n"; $ssl_settings .= 'ssl.use-sslv2 = "disable"' . "\n"; + $ssl_settings .= 'ssl.use-sslv3 = "disable"' . "\n"; $ssl_settings .= 'ssl.cipher-list = "' . Settings::Get('system.ssl_cipher_list') . '"' . "\n"; $ssl_settings .= 'ssl.honor-cipher-order = "enable"' . "\n"; $ssl_settings .= 'ssl.pemfile = "' . makeCorrectFile($domain['ssl_cert_file']) . '"' . "\n"; diff --git a/scripts/jobs/cron_tasks.inc.http.30.nginx.php b/scripts/jobs/cron_tasks.inc.http.30.nginx.php index a9e2c457..d4baa6c8 100644 --- a/scripts/jobs/cron_tasks.inc.http.30.nginx.php +++ b/scripts/jobs/cron_tasks.inc.http.30.nginx.php @@ -601,7 +601,7 @@ class nginx extends HttpConfigBase } else { // obsolete: ssl on now belongs to the listen block as 'ssl' at the end // $sslsettings .= "\t" . 'ssl on;' . "\n"; - $sslsettings .= "\t" . 'ssl_protocols TLSv1 TLSv1.1 TLSv1.2;' . "\n"; + $sslsettings .= "\t" . 'ssl_protocols TLSv1 TLSv1.2;' . "\n"; $sslsettings .= "\t" . 'ssl_ciphers ' . Settings::Get('system.ssl_cipher_list') . ';' . "\n"; $sslsettings .= "\t" . 'ssl_ecdh_curve secp384r1;' . "\n"; $sslsettings .= "\t" . 'ssl_prefer_server_ciphers on;' . "\n"; From ec6ddd054dde84d316d38fab96bc996baddaf883 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 31 Oct 2016 11:18:40 +0100 Subject: [PATCH 0222/1335] remove certificate entry for LE if admin/customer disables LE for a domain Signed-off-by: Michael Kaufmann (d00p) --- actions/admin/settings/122.froxlorvhost.php | 2 +- admin_domains.php | 18 ++++++++- customer_domains.php | 19 +++++++--- ...function.storeSettingClearCertificates.php | 37 +++++++++++++++++++ 4 files changed, 68 insertions(+), 8 deletions(-) create mode 100644 lib/functions/settings/function.storeSettingClearCertificates.php diff --git a/actions/admin/settings/122.froxlorvhost.php b/actions/admin/settings/122.froxlorvhost.php index e92fa582..75e138b4 100644 --- a/actions/admin/settings/122.froxlorvhost.php +++ b/actions/admin/settings/122.froxlorvhost.php @@ -39,7 +39,7 @@ return array( 'varname' => 'le_froxlor_enabled', 'type' => 'bool', 'default' => false, - 'save_method' => 'storeSettingField', + 'save_method' => 'storeSettingClearCertificates', 'visible' => Settings::Get('system.leenabled') ), 'system_le_froxlor_redirect' => array( diff --git a/admin_domains.php b/admin_domains.php index e1bbff05..42125117 100644 --- a/admin_domains.php +++ b/admin_domains.php @@ -1081,8 +1081,12 @@ if ($page == 'domains' || $page == 'overview') { } elseif ($action == 'edit' && $id != 0) { $result_stmt = Database::prepare(" - SELECT `d`.*, `c`.`customerid` FROM `" . TABLE_PANEL_DOMAINS . "` `d` LEFT JOIN `" . TABLE_PANEL_CUSTOMERS . "` `c` USING(`customerid`) - WHERE `d`.`parentdomainid` = '0' AND `d`.`id` = :id" . ($userinfo['customers_see_all'] ? '' : " AND `d`.`adminid` = :adminid")); + SELECT `d`.*, `c`.`customerid` + FROM `" . TABLE_PANEL_DOMAINS . "` `d` + LEFT JOIN `" . TABLE_PANEL_CUSTOMERS . "` `c` USING(`customerid`) + WHERE `d`.`parentdomainid` = '0' + AND `d`.`id` = :id" . ($userinfo['customers_see_all'] ? '' : " AND `d`.`adminid` = :adminid") + ); $params = array( 'id' => $id ); @@ -1640,6 +1644,16 @@ if ($page == 'domains' || $page == 'overview') { $log->logAction(ADM_ACTION, LOG_NOTICE, "deleted domain #" . $id . " from mail-tables"); } + // check whether LE has been disabled, so we remove the certificate + if ($letsencrypt == '0' && $result['letsencrypt'] == '1') { + $del_stmt = Database::prepare(" + DELETE FROM `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` WHERE `domainid` = :id + "); + Database::pexecute($del_stmt, array( + 'id' => $id + )); + } + $updatechildren = ''; if ($subcanemaildomain == '0' && $result['subcanemaildomain'] != '0') { diff --git a/customer_domains.php b/customer_domains.php index d96d4fa0..43e444af 100644 --- a/customer_domains.php +++ b/customer_domains.php @@ -720,11 +720,20 @@ if ($page == 'overview') { // trigger when domain id for alias destination has changed: both for old and new destination triggerLetsEncryptCSRForAliasDestinationDomain($result['aliasdomain'], $log); triggerLetsEncryptCSRForAliasDestinationDomain($aliasdomain, $log); - } else - if ($result['wwwserveralias'] != $wwwserveralias || $result['letsencrypt'] != $letsencrypt) { - // or when wwwserveralias or letsencrypt was changed - triggerLetsEncryptCSRForAliasDestinationDomain($aliasdomain, $log); - } + } elseif ($result['wwwserveralias'] != $wwwserveralias || $result['letsencrypt'] != $letsencrypt) { + // or when wwwserveralias or letsencrypt was changed + triggerLetsEncryptCSRForAliasDestinationDomain($aliasdomain, $log); + } + + // check whether LE has been disabled, so we remove the certificate + if ($letsencrypt == '0' && $result['letsencrypt'] == '1') { + $del_stmt = Database::prepare(" + DELETE FROM `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` WHERE `domainid` = :id + "); + Database::pexecute($del_stmt, array( + 'id' => $id + )); + } inserttask('1'); diff --git a/lib/functions/settings/function.storeSettingClearCertificates.php b/lib/functions/settings/function.storeSettingClearCertificates.php new file mode 100644 index 00000000..32d19944 --- /dev/null +++ b/lib/functions/settings/function.storeSettingClearCertificates.php @@ -0,0 +1,37 @@ + (2010-) +* @license GPLv2 http://files.froxlor.org/misc/COPYING.txt +* @package Functions +* +*/ + +function storeSettingClearCertificates($fieldname, $fielddata, $newfieldvalue) { + + $returnvalue = storeSettingField($fieldname, $fielddata, $newfieldvalue); + + if ($returnvalue !== false + && is_array($fielddata) + && isset($fielddata['settinggroup']) + && $fielddata['settinggroup'] == 'system' + && isset($fielddata['varname']) + && $fielddata['varname'] == 'le_froxlor_enabled' + && $newfieldvalue == '0' + ) { + Database::query(" + DELETE FROM `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` WHERE `domainid` = '0' + "); + } + + return $returnvalue; +} From 60c1babd93b4db6663e507a34f56f551e5db2b1f Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 31 Oct 2016 20:44:31 +0100 Subject: [PATCH 0223/1335] fix wrong protocol name for apache Signed-off-by: Michael Kaufmann (d00p) --- scripts/jobs/cron_tasks.inc.http.10.apache.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/jobs/cron_tasks.inc.http.10.apache.php b/scripts/jobs/cron_tasks.inc.http.10.apache.php index 30644a56..156435a3 100644 --- a/scripts/jobs/cron_tasks.inc.http.10.apache.php +++ b/scripts/jobs/cron_tasks.inc.http.10.apache.php @@ -412,7 +412,7 @@ class apache extends HttpConfigBase } else { $this->virtualhosts_data[$vhosts_filename] .= ' SSLEngine On' . "\n"; - $this->virtualhosts_data[$vhosts_filename] .= ' SSLProtocol -ALL +TLSv1.0 +TLSv1.2' . "\n"; + $this->virtualhosts_data[$vhosts_filename] .= ' SSLProtocol -ALL +TLSv1 +TLSv1.2' . "\n"; // this makes it more secure, thx to Marcel (08/2013) $this->virtualhosts_data[$vhosts_filename] .= ' SSLHonorCipherOrder On' . "\n"; $this->virtualhosts_data[$vhosts_filename] .= ' SSLCipherSuite ' . Settings::Get('system.ssl_cipher_list') . "\n"; @@ -830,7 +830,7 @@ class apache extends HttpConfigBase if ($domain['ssl_cert_file'] != '') { $vhost_content .= ' SSLEngine On' . "\n"; - $vhost_content .= ' SSLProtocol -ALL +TLSv1.0 +TLSv1.2' . "\n"; + $vhost_content .= ' SSLProtocol -ALL +TLSv1 +TLSv1.2' . "\n"; // this makes it more secure, thx to Marcel (08/2013) $vhost_content .= ' SSLHonorCipherOrder On' . "\n"; $vhost_content .= ' SSLCipherSuite ' . Settings::Get('system.ssl_cipher_list') . "\n"; From 4274b8a73764b85dc4e40a6d5407872dbbfd6f67 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 3 Nov 2016 10:52:33 +0100 Subject: [PATCH 0224/1335] fix setting of hsts values when security questions are asked; insert config-rebuild task if only hsts stuff changes; fix undefined variable regarding alias domain Signed-off-by: Michael Kaufmann (d00p) --- admin_domains.php | 16 ++++++++-------- customer_domains.php | 8 ++++++-- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/admin_domains.php b/admin_domains.php index 42125117..0f86f7b6 100644 --- a/admin_domains.php +++ b/admin_domains.php @@ -777,7 +777,7 @@ if ($page == 'domains' || $page == 'overview') { 'termination_date' => $termination_date, 'issubof' => $issubof, 'letsencrypt' => $letsencrypt, - 'hsts' => $hsts_maxage, + 'hsts_maxage' => $hsts_maxage, 'hsts_sub' => $hsts_sub, 'hsts_preload' => $hsts_preload ); @@ -1402,6 +1402,11 @@ if ($page == 'domains' || $page == 'overview') { $letsencrypt = (int) $_POST['letsencrypt']; } + // HSTS + $hsts_maxage = isset($_POST['hsts_maxage']) ? (int)$_POST['hsts_maxage'] : 0; + $hsts_sub = isset($_POST['hsts_sub']) && (int)$_POST['hsts_sub'] == 1 ? 1 : 0; + $hsts_preload = isset($_POST['hsts_preload']) && (int)$_POST['hsts_preload'] == 1 ? 1 : 0; + $ssl_ipandports = array(); if (isset($_POST['ssl_ipandport']) && ! is_array($_POST['ssl_ipandport'])) { $_POST['ssl_ipandport'] = unserialize($_POST['ssl_ipandport']); @@ -1428,11 +1433,6 @@ if ($page == 'domains' || $page == 'overview') { } } - // HSTS - $hsts_maxage = isset($_POST['hsts_maxage']) ? (int)$_POST['hsts_maxage'] : 0; - $hsts_sub = isset($_POST['hsts_sub']) && (int)$_POST['hsts_sub'] == 1 ? 1 : 0; - $hsts_preload = isset($_POST['hsts_preload']) && (int)$_POST['hsts_preload'] == 1 ? 1 : 0; - } else { $ssl_redirect = 0; $letsencrypt = 0; @@ -1594,7 +1594,7 @@ if ($page == 'domains' || $page == 'overview') { 'ipandport' => serialize($ipandports), 'ssl_ipandport' => serialize($ssl_ipandports), 'letsencrypt' => $letsencrypt, - 'hsts' => $hsts_maxage, + 'hsts_maxage' => $hsts_maxage, 'hsts_sub' => $hsts_sub, 'hsts_preload' => $hsts_preload ); @@ -1615,7 +1615,7 @@ if ($page == 'domains' || $page == 'overview') { $wwwserveralias = ($serveraliasoption == '1') ? '1' : '0'; $iswildcarddomain = ($serveraliasoption == '0') ? '1' : '0'; - if ($documentroot != $result['documentroot'] || $ssl_redirect != $result['ssl_redirect'] || $wwwserveralias != $result['wwwserveralias'] || $iswildcarddomain != $result['iswildcarddomain'] || $openbasedir != $result['openbasedir'] || $phpsettingid != $result['phpsettingid'] || $mod_fcgid_starter != $result['mod_fcgid_starter'] || $mod_fcgid_maxrequests != $result['mod_fcgid_maxrequests'] || $specialsettings != $result['specialsettings'] || $aliasdomain != $result['aliasdomain'] || $issubof != $result['ismainbutsubto'] || $email_only != $result['email_only'] || ($speciallogfile != $result['speciallogfile'] && $speciallogverified == '1') || $letsencrypt != $result['letsencrypt']) { + if ($documentroot != $result['documentroot'] || $ssl_redirect != $result['ssl_redirect'] || $wwwserveralias != $result['wwwserveralias'] || $iswildcarddomain != $result['iswildcarddomain'] || $openbasedir != $result['openbasedir'] || $phpsettingid != $result['phpsettingid'] || $mod_fcgid_starter != $result['mod_fcgid_starter'] || $mod_fcgid_maxrequests != $result['mod_fcgid_maxrequests'] || $specialsettings != $result['specialsettings'] || $aliasdomain != $result['aliasdomain'] || $issubof != $result['ismainbutsubto'] || $email_only != $result['email_only'] || ($speciallogfile != $result['speciallogfile'] && $speciallogverified == '1') || $letsencrypt != $result['letsencrypt'] || $hsts_maxage != $result['hsts'] || $hsts_sub != $result['hsts_sub'] || $hsts_preload != $result['hsts_preload']) { inserttask('1'); } diff --git a/customer_domains.php b/customer_domains.php index 43e444af..06471cb8 100644 --- a/customer_domains.php +++ b/customer_domains.php @@ -578,7 +578,7 @@ if ($page == 'overview') { $_doredirect = true; } - $aliasdomain = intval($_POST['alias']); + $aliasdomain = isset($_POST['alias']) ? intval($_POST['alias']) : 0; if (isset($_POST['selectserveralias'])) { $iswildcarddomain = ($_POST['selectserveralias'] == '0') ? '1' : '0'; @@ -681,7 +681,11 @@ if ($page == 'overview') { || $aliasdomain != $result['aliasdomain'] || $openbasedir_path != $result['openbasedir_path'] || $ssl_redirect != $result['ssl_redirect'] - || $letsencrypt != $result['letsencrypt']) { + || $letsencrypt != $result['letsencrypt'] + || $hsts_maxage != $result['hsts'] + || $hsts_sub != $result['hsts_sub'] + || $hsts_preload != $result['hsts_preload'] + ) { $log->logAction(USR_ACTION, LOG_INFO, "edited domain '" . $idna_convert->decode($result['domain']) . "'"); $stmt = Database::prepare("UPDATE `" . TABLE_PANEL_DOMAINS . "` SET From c409d8a6bacd35338d9daa73b91ff5a57e44fcb1 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 3 Nov 2016 14:14:33 +0100 Subject: [PATCH 0225/1335] re-enable removing of orphaned entries, fixes #1666 Signed-off-by: Michael Kaufmann (d00p) --- ssl_certificates.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/ssl_certificates.php b/ssl_certificates.php index 0d4f9c03..5a8547d7 100644 --- a/ssl_certificates.php +++ b/ssl_certificates.php @@ -95,12 +95,9 @@ if (count($all_certs) == 0) { if (empty($cert['domain']) || empty($cert['ssl_cert_file'])) { // no domain found to the entry or empty entry - safely delete it from the DB - /* Database::pexecute($del_stmt, array( 'id' => $cert['id'] )); - */ - echo "no domain found to the entry or empty entry - safely delete it from the DB"; continue; } From d6b56262cef78e1cce8271b82f18387c221f78f9 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Fri, 4 Nov 2016 18:28:32 +0100 Subject: [PATCH 0226/1335] fix unnecessary idn encoding Signed-off-by: Michael Kaufmann (d00p) --- scripts/jobs/cron_tasks.inc.http.20.lighttpd.php | 2 +- scripts/jobs/cron_tasks.inc.http.30.nginx.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php b/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php index a8cd208e..1f37d4c8 100644 --- a/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php +++ b/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php @@ -431,7 +431,7 @@ class lighttpd extends HttpConfigBase $domain['documentroot'] = trim($domain['documentroot']); if (preg_match('/^https?\:\/\//', $domain['documentroot'])) { - $uri = $this->idnaConvert->encode_uri($domain['documentroot']); + $uri = $domain['documentroot']; // prevent empty return-cde $code = "301"; // Get domain's redirect code diff --git a/scripts/jobs/cron_tasks.inc.http.30.nginx.php b/scripts/jobs/cron_tasks.inc.http.30.nginx.php index d4baa6c8..b282b1e1 100644 --- a/scripts/jobs/cron_tasks.inc.http.30.nginx.php +++ b/scripts/jobs/cron_tasks.inc.http.30.nginx.php @@ -456,7 +456,7 @@ class nginx extends HttpConfigBase // if the documentroot is an URL we just redirect if (preg_match('/^https?\:\/\//', $domain['documentroot'])) { - $uri = $this->idnaConvert->encode_uri($domain['documentroot']); + $uri = $domain['documentroot']; if (substr($uri, - 1) == '/') { $uri = substr($uri, 0, - 1); } From fad607c6e8de772ddaa8efd5e1e5750b17dacf6b Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 7 Nov 2016 07:28:58 +0100 Subject: [PATCH 0227/1335] set version to 0.9.38-rc2 for second release candidate --- install/froxlor.sql | 2 +- install/updates/froxlor/0.9/update_0.9.inc.php | 6 ++++++ lib/version.inc.php | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/install/froxlor.sql b/install/froxlor.sql index 840d6301..e8ad16bb 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -576,7 +576,7 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES ('panel', 'password_special_char_required', '0'), ('panel', 'password_special_char', '!?<>§$%+#=@'), ('panel', 'customer_hide_options', ''), - ('panel', 'version', '0.9.38-rc1'), + ('panel', 'version', '0.9.38-rc2'), ('panel', 'db_version', '201610070'); diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index 3767ec8a..c9968770 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -3507,3 +3507,9 @@ if (isFroxlorVersion('0.9.37')) { showUpdateStep("Updating from 0.9.37 to 0.9.38-rc1", false); updateToVersion('0.9.38-rc1'); } + +if (isFroxlorVersion('0.9.38-rc1')) { + + showUpdateStep("Updating from 0.9.38-rc1 to 0.9.38-rc2", false); + updateToVersion('0.9.38-rc2'); +} diff --git a/lib/version.inc.php b/lib/version.inc.php index 7fa046a2..ef48a2b2 100644 --- a/lib/version.inc.php +++ b/lib/version.inc.php @@ -16,7 +16,7 @@ */ // Main version variable -$version = '0.9.38-rc1'; +$version = '0.9.38-rc2'; // Database version (YYYYMMDDC where C is a daily counter) $dbversion = '201610070'; From 7e4164da265bbd54dccdb8b2365abd41bfcaa427 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 10 Nov 2016 10:07:00 +0100 Subject: [PATCH 0228/1335] do not double validate openbasedir-values, as appendOpenbasedirPath() already takes care of that; also fix /dev/urandom as openbasedir-path-value to be treated as file correctly, fixes #1669 Signed-off-by: Michael Kaufmann (d00p) --- .../phpinterface/class.phpinterface_fcgid.php | 9 --- .../phpinterface/class.phpinterface_fpm.php | 9 --- .../function.appendOpenbasedirPath.php | 65 ++++++++++--------- 3 files changed, 34 insertions(+), 49 deletions(-) diff --git a/lib/classes/phpinterface/class.phpinterface_fcgid.php b/lib/classes/phpinterface/class.phpinterface_fcgid.php index 1cb21dd1..ca611661 100644 --- a/lib/classes/phpinterface/class.phpinterface_fcgid.php +++ b/lib/classes/phpinterface/class.phpinterface_fcgid.php @@ -135,15 +135,6 @@ class phpinterface_fcgid { $openbasedir .= appendOpenBasedirPath($this->getTempDir()); $openbasedir .= $_phpappendopenbasedir; - $openbasedir = explode(':', $openbasedir); - $clean_openbasedir = array(); - foreach ($openbasedir as $number => $path) { - if (trim($path) != '/') { - $clean_openbasedir[] = makeCorrectDir($path); - } - } - $openbasedir = implode(':', $clean_openbasedir); - } else { $openbasedir = 'none'; $openbasedirc = ';'; diff --git a/lib/classes/phpinterface/class.phpinterface_fpm.php b/lib/classes/phpinterface/class.phpinterface_fpm.php index 156940ba..5c39d00b 100644 --- a/lib/classes/phpinterface/class.phpinterface_fpm.php +++ b/lib/classes/phpinterface/class.phpinterface_fpm.php @@ -267,15 +267,6 @@ class phpinterface_fpm { $openbasedir .= appendOpenBasedirPath($this->getTempDir()); $openbasedir .= $_phpappendopenbasedir; - - $openbasedir = explode(':', $openbasedir); - $clean_openbasedir = array(); - foreach ($openbasedir as $number => $path) { - if (trim($path) != '/') { - $clean_openbasedir[] = makeCorrectDir($path); - } - } - $openbasedir = implode(':', $clean_openbasedir); } } $fpm_config.= 'php_admin_value[session.save_path] = ' . makeCorrectDir(Settings::Get('phpfpm.tmpdir') . '/' . $this->_domain['loginname'] . '/') . "\n"; diff --git a/lib/functions/validate/function.appendOpenbasedirPath.php b/lib/functions/validate/function.appendOpenbasedirPath.php index 51c0db99..cdcc11f6 100644 --- a/lib/functions/validate/function.appendOpenbasedirPath.php +++ b/lib/functions/validate/function.appendOpenbasedirPath.php @@ -21,40 +21,43 @@ * to a line for a open_basedir directive * * @param string $path - * the path to check and append + * the path to check and append * @param boolean $first - * if true, no ':' will be prefixed to the path - * + * if true, no ':' will be prefixed to the path + * * @return string */ function appendOpenBasedirPath($path = '', $first = false) { - if ($path != '' && $path != '/' - && (! preg_match("#^/dev#i", $path) || preg_match("#^/dev/urandom#i", $path)) - && ! preg_match("#^/proc#i", $path) - && ! preg_match("#^/etc#i", $path) - && ! preg_match("#^/sys#i", $path) - && ! preg_match("#:#", $path) - ) { - - $path = makeCorrectDir($path); - - // check for php-version that requires the trailing - // slash to be removed as it does not allow the usage - // of the subfolders within the given folder, fixes #797 - if ((PHP_MINOR_VERSION == 2 && PHP_VERSION_ID >= 50216) || PHP_VERSION_ID >= 50304) { - // check trailing slash - if (substr($path, - 1, 1) == '/') { - // remove it - $path = substr($path, 0, - 1); - } - } - - if ($first) { - return $path; - } - - return ':' . $path; - } - return ''; + if ($path != '' && $path != '/' && + (! preg_match("#^/dev#i", $path) || preg_match("#^/dev/urandom#i", $path)) + && ! preg_match("#^/proc#i", $path) + && ! preg_match("#^/etc#i", $path) + && ! preg_match("#^/sys#i", $path) + && ! preg_match("#:#", $path)) { + + if (preg_match("#^/dev/urandom#i", $path)) { + $path = makeCorrectFile($path); + } else { + $path = makeCorrectDir($path); + } + + // check for php-version that requires the trailing + // slash to be removed as it does not allow the usage + // of the subfolders within the given folder, fixes #797 + if ((PHP_MINOR_VERSION == 2 && PHP_VERSION_ID >= 50216) || PHP_VERSION_ID >= 50304) { + // check trailing slash + if (substr($path, - 1, 1) == '/') { + // remove it + $path = substr($path, 0, - 1); + } + } + + if ($first) { + return $path; + } + + return ':' . $path; + } + return ''; } From b0326c640c15470c495ee23312961d84ab175179 Mon Sep 17 00:00:00 2001 From: Dominic Schallert Date: Sun, 13 Nov 2016 14:41:39 +0100 Subject: [PATCH 0229/1335] Feature #1671 Checkbox to enable/disable PHP (vhost settings + fpm pool) for an entire vhost --- admin_domains.php | 23 ++++++++++++++++++- install/froxlor.sql | 1 + .../updates/froxlor/0.9/update_0.9.inc.php | 11 +++++++++ .../admin/domains/formfield.domains_add.php | 8 +++++++ .../admin/domains/formfield.domains_edit.php | 8 +++++++ 5 files changed, 50 insertions(+), 1 deletion(-) diff --git a/admin_domains.php b/admin_domains.php index 0f86f7b6..93fe910c 100644 --- a/admin_domains.php +++ b/admin_domains.php @@ -453,6 +453,7 @@ if ($page == 'domains' || $page == 'overview') { if ($userinfo['caneditphpsettings'] == '1' || $userinfo['change_serversettings'] == '1') { + $phpenabled = isset($POST_['phpenabled']) ? intval($_POST['phpenabled']) : 0; $openbasedir = isset($_POST['openbasedir']) ? intval($_POST['openbasedir']) : 0; if ((int) Settings::Get('system.mod_fcgid') == 1 || (int) Settings::Get('phpfpm.enabled') == 1) { @@ -493,7 +494,9 @@ if ($page == 'domains' || $page == 'overview') { } } else { + $phpenabled = '1'; $openbasedir = '1'; + if ((int) Settings::Get('phpfpm.enabled') == 1) { $phpsettingid = Settings::Get('phpfpm.defaultini'); } else { @@ -688,6 +691,10 @@ if ($page == 'domains' || $page == 'overview') { if (count($ipandports) == 0) { standard_error('noipportgiven'); } + + if($phpenabled != '1') { + $phpenabled = '0'; + } if ($openbasedir != '1') { $openbasedir = '0'; @@ -768,6 +775,7 @@ if ($page == 'domains' || $page == 'overview') { 'ipandport' => serialize($ipandports), 'ssl_redirect' => $ssl_redirect, 'ssl_ipandport' => serialize($ssl_ipandports), + 'phpenabled' => $phpenabled, 'openbasedir' => $openbasedir, 'phpsettingid' => $phpsettingid, 'mod_fcgid_starter' => $mod_fcgid_starter, @@ -816,6 +824,7 @@ if ($page == 'domains' || $page == 'overview') { 'email_only' => $email_only, 'subcanemaildomain' => $subcanemaildomain, 'caneditdomain' => $caneditdomain, + 'phpenabled' => $phpenabled, 'openbasedir' => $openbasedir, 'speciallogfile' => $speciallogfile, 'specialsettings' => $specialsettings, @@ -852,6 +861,7 @@ if ($page == 'domains' || $page == 'overview') { `email_only` = :email_only, `subcanemaildomain` = :subcanemaildomain, `caneditdomain` = :caneditdomain, + `phpenabled` = :phpenabled, `openbasedir` = :openbasedir, `speciallogfile` = :speciallogfile, `specialsettings` = :specialsettings, @@ -1326,6 +1336,7 @@ if ($page == 'domains' || $page == 'overview') { if ($userinfo['caneditphpsettings'] == '1' || $userinfo['change_serversettings'] == '1') { + $phpenabled = isset($_POST['phpenabled']) ? intval($_POST['phpenabled']) : 0; $openbasedir = isset($_POST['openbasedir']) ? intval($_POST['openbasedir']) : 0; if ((int) Settings::Get('system.mod_fcgid') == 1 || (int) Settings::Get('phpfpm.enabled') == 1) { @@ -1360,6 +1371,7 @@ if ($page == 'domains' || $page == 'overview') { $mod_fcgid_maxrequests = $result['mod_fcgid_maxrequests']; } } else { + $phpenabled = $result['phpenabled']; $openbasedir = $result['openbasedir']; $phpsettingid = $result['phpsettingid']; $mod_fcgid_starter = $result['mod_fcgid_starter']; @@ -1471,6 +1483,10 @@ if ($page == 'domains' || $page == 'overview') { if (! preg_match('/^https?\:\/\//', $documentroot)) { $documentroot = makeCorrectDir($documentroot); } + + if ($phpenabled != '1') { + $phpenabled = '0'; + } if ($openbasedir != '1') { $openbasedir = '0'; @@ -1580,6 +1596,7 @@ if ($page == 'domains' || $page == 'overview') { 'dkim' => $dkim, 'selectserveralias' => $serveraliasoption, 'ssl_redirect' => $ssl_redirect, + 'phpenabled' => $phpenabled, 'openbasedir' => $openbasedir, 'phpsettingid' => $phpsettingid, 'mod_fcgid_starter' => $mod_fcgid_starter, @@ -1615,7 +1632,7 @@ if ($page == 'domains' || $page == 'overview') { $wwwserveralias = ($serveraliasoption == '1') ? '1' : '0'; $iswildcarddomain = ($serveraliasoption == '0') ? '1' : '0'; - if ($documentroot != $result['documentroot'] || $ssl_redirect != $result['ssl_redirect'] || $wwwserveralias != $result['wwwserveralias'] || $iswildcarddomain != $result['iswildcarddomain'] || $openbasedir != $result['openbasedir'] || $phpsettingid != $result['phpsettingid'] || $mod_fcgid_starter != $result['mod_fcgid_starter'] || $mod_fcgid_maxrequests != $result['mod_fcgid_maxrequests'] || $specialsettings != $result['specialsettings'] || $aliasdomain != $result['aliasdomain'] || $issubof != $result['ismainbutsubto'] || $email_only != $result['email_only'] || ($speciallogfile != $result['speciallogfile'] && $speciallogverified == '1') || $letsencrypt != $result['letsencrypt'] || $hsts_maxage != $result['hsts'] || $hsts_sub != $result['hsts_sub'] || $hsts_preload != $result['hsts_preload']) { + if ($documentroot != $result['documentroot'] || $ssl_redirect != $result['ssl_redirect'] || $wwwserveralias != $result['wwwserveralias'] || $iswildcarddomain != $result['iswildcarddomain'] || $phpenabled != $result['phpenabled'] || $openbasedir != $result['openbasedir'] || $phpsettingid != $result['phpsettingid'] || $mod_fcgid_starter != $result['mod_fcgid_starter'] || $mod_fcgid_maxrequests != $result['mod_fcgid_maxrequests'] || $specialsettings != $result['specialsettings'] || $aliasdomain != $result['aliasdomain'] || $issubof != $result['ismainbutsubto'] || $email_only != $result['email_only'] || ($speciallogfile != $result['speciallogfile'] && $speciallogverified == '1') || $letsencrypt != $result['letsencrypt'] || $hsts_maxage != $result['hsts'] || $hsts_sub != $result['hsts_sub'] || $hsts_preload != $result['hsts_preload']) { inserttask('1'); } @@ -1756,6 +1773,7 @@ if ($page == 'domains' || $page == 'overview') { $update_data['zonefile'] = $zonefile; $update_data['wwwserveralias'] = $wwwserveralias; $update_data['iswildcarddomain'] = $iswildcarddomain; + $update_data['phpenabled'] = $phpenabled; $update_data['openbasedir'] = $openbasedir; $update_data['speciallogfile'] = $speciallogfile; $update_data['phpsettingid'] = $phpsettingid; @@ -1787,6 +1805,7 @@ if ($page == 'domains' || $page == 'overview') { `zonefile` = :zonefile, `wwwserveralias` = :wwwserveralias, `iswildcarddomain` = :iswildcarddomain, + `phpenabled` = :phpenabled, `openbasedir` = :openbasedir, `speciallogfile` = :speciallogfile, `phpsettingid` = :phpsettingid, @@ -1806,6 +1825,7 @@ if ($page == 'domains' || $page == 'overview') { $_update_data['customerid'] = $customerid; $_update_data['adminid'] = $adminid; + $_update_data['phpenabled'] = $phpenabled; $_update_data['openbasedir'] = $openbasedir; $_update_data['phpsettingid'] = $phpsettingid; $_update_data['mod_fcgid_starter'] = $mod_fcgid_starter; @@ -1824,6 +1844,7 @@ if ($page == 'domains' || $page == 'overview') { UPDATE `" . TABLE_PANEL_DOMAINS . "` SET `customerid` = :customerid, `adminid` = :adminid, + `phpenabled` = :phpenabled, `openbasedir` = :openbasedir, `phpsettingid` = :phpsettingid, `mod_fcgid_starter` = :mod_fcgid_starter, diff --git a/install/froxlor.sql b/install/froxlor.sql index e8ad16bb..c81dc912 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -237,6 +237,7 @@ CREATE TABLE `panel_domains` ( `dkim_pubkey` text, `wwwserveralias` tinyint(1) NOT NULL default '1', `parentdomainid` int(11) NOT NULL default '0', + `phpenabled` tinyint(1) NOT NULL default '0', `openbasedir` tinyint(1) NOT NULL default '0', `openbasedir_path` tinyint(1) NOT NULL default '0', `speciallogfile` tinyint(1) NOT NULL default '0', diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index c9968770..36f5aad1 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -3513,3 +3513,14 @@ if (isFroxlorVersion('0.9.38-rc1')) { showUpdateStep("Updating from 0.9.38-rc1 to 0.9.38-rc2", false); updateToVersion('0.9.38-rc2'); } + +if (isFroxlorVersion('0.9.38-rc2')) { + + showUpdateStep("Updating from 0.9.38-rc2 to 0.9.38-rc3", false); + + showUpdateStep("Updating database table definition for panel_domains"); + Database::query("ALTER TABLE `" . TABLE_PANEL_DOMAINS . "` ADD `phpenabled` tinyint(1) NOT NULL default '1' AFTER `openbasedir`;"); + lastStepStatus(0); + + updateToVersion('0.9.38-rc3'); +} diff --git a/lib/formfields/admin/domains/formfield.domains_add.php b/lib/formfields/admin/domains/formfield.domains_add.php index 15ca70e4..bce5b886 100644 --- a/lib/formfields/admin/domains/formfield.domains_add.php +++ b/lib/formfields/admin/domains/formfield.domains_add.php @@ -202,6 +202,14 @@ return array( 'image' => 'icons/domain_add.png', 'visible' => (($userinfo['change_serversettings'] == '1' || $userinfo['caneditphpsettings'] == '1') ? true : false), 'fields' => array( + 'phpenabled' => array( + 'label' => $lng['admin']['phpenabled'], + 'type' => 'checkbox', + 'values' => array( + array ('label' => $lng['panel']['yes'], 'value' => '1') + ), + 'value' => array('1') + ), 'openbasedir' => array( 'label' => 'OpenBasedir', 'type' => 'checkbox', diff --git a/lib/formfields/admin/domains/formfield.domains_edit.php b/lib/formfields/admin/domains/formfield.domains_edit.php index 11b6c6bc..73e00497 100644 --- a/lib/formfields/admin/domains/formfield.domains_edit.php +++ b/lib/formfields/admin/domains/formfield.domains_edit.php @@ -225,6 +225,14 @@ return array( 'image' => 'icons/domain_edit.png', 'visible' => (($userinfo['change_serversettings'] == '1' || $userinfo['caneditphpsettings'] == '1') ? true : false), 'fields' => array( + 'phpenabled' => array( + 'label' => $lng['admin']['phpenabled'], + 'type' => 'checkbox', + 'values' => array( + array ('label' => $lng['panel']['yes'], 'value' => '1') + ), + 'value' => array('1') + ), 'openbasedir' => array( 'label' => 'OpenBasedir', 'type' => 'checkbox', From 1519db16373f487ccee3305bb6fb5adc5a4f1f0e Mon Sep 17 00:00:00 2001 From: Dominic Date: Sun, 13 Nov 2016 14:48:50 +0100 Subject: [PATCH 0230/1335] Update update_0.9.inc.php --- install/updates/froxlor/0.9/update_0.9.inc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index 36f5aad1..71e7441e 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -3519,7 +3519,7 @@ if (isFroxlorVersion('0.9.38-rc2')) { showUpdateStep("Updating from 0.9.38-rc2 to 0.9.38-rc3", false); showUpdateStep("Updating database table definition for panel_domains"); - Database::query("ALTER TABLE `" . TABLE_PANEL_DOMAINS . "` ADD `phpenabled` tinyint(1) NOT NULL default '1' AFTER `openbasedir`;"); + Database::query("ALTER TABLE `" . TABLE_PANEL_DOMAINS . "` ADD `phpenabled` tinyint(1) NOT NULL default '1' AFTER `parentdomainid`;"); lastStepStatus(0); updateToVersion('0.9.38-rc3'); From e5053bad154b505e5cb6ca7f5dd34aa1163aeb31 Mon Sep 17 00:00:00 2001 From: Dominic Schallert Date: Sun, 13 Nov 2016 15:15:43 +0100 Subject: [PATCH 0231/1335] Introduced phpenabled_customer and phpenabled_vhost, updated cron scripts, updated dbversion --- install/froxlor.sql | 2 +- .../updates/froxlor/0.9/update_0.9.inc.php | 20 +++++++++---------- lib/classes/webserver/class.WebserverBase.php | 5 +++-- lib/version.inc.php | 2 +- .../jobs/cron_tasks.inc.http.10.apache.php | 2 +- .../cron_tasks.inc.http.15.apache_fcgid.php | 2 +- .../cron_tasks.inc.http.25.lighttpd_fcgid.php | 2 +- scripts/jobs/cron_tasks.inc.http.30.nginx.php | 6 +++--- .../cron_tasks.inc.http.35.nginx_phpfpm.php | 2 +- 9 files changed, 21 insertions(+), 22 deletions(-) diff --git a/install/froxlor.sql b/install/froxlor.sql index c81dc912..a8dc13c3 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -578,7 +578,7 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES ('panel', 'password_special_char', '!?<>§$%+#=@'), ('panel', 'customer_hide_options', ''), ('panel', 'version', '0.9.38-rc2'), - ('panel', 'db_version', '201610070'); + ('panel', 'db_version', '201611130'); DROP TABLE IF EXISTS `panel_tasks`; diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index 71e7441e..cce1ebfd 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -3502,6 +3502,15 @@ if (isDatabaseVersion('201609240')) { updateToDbVersion('201610070'); } +if (isDatabaseVersion('201610070')) { + + showUpdateStep("Updating database table definition for panel_domains"); + Database::query("ALTER TABLE `" . TABLE_PANEL_DOMAINS . "` ADD `phpenabled` tinyint(1) NOT NULL default '1' AFTER `parentdomainid`;"); + lastStepStatus(0); + + updateToDbVersion('201611130'); +} + if (isFroxlorVersion('0.9.37')) { showUpdateStep("Updating from 0.9.37 to 0.9.38-rc1", false); @@ -3513,14 +3522,3 @@ if (isFroxlorVersion('0.9.38-rc1')) { showUpdateStep("Updating from 0.9.38-rc1 to 0.9.38-rc2", false); updateToVersion('0.9.38-rc2'); } - -if (isFroxlorVersion('0.9.38-rc2')) { - - showUpdateStep("Updating from 0.9.38-rc2 to 0.9.38-rc3", false); - - showUpdateStep("Updating database table definition for panel_domains"); - Database::query("ALTER TABLE `" . TABLE_PANEL_DOMAINS . "` ADD `phpenabled` tinyint(1) NOT NULL default '1' AFTER `parentdomainid`;"); - lastStepStatus(0); - - updateToVersion('0.9.38-rc3'); -} diff --git a/lib/classes/webserver/class.WebserverBase.php b/lib/classes/webserver/class.WebserverBase.php index d67b9868..b827f04c 100644 --- a/lib/classes/webserver/class.WebserverBase.php +++ b/lib/classes/webserver/class.WebserverBase.php @@ -31,8 +31,9 @@ class WebserverBase { $query = "SELECT `d`.*, `pd`.`domain` AS `parentdomain`, `c`.`loginname`, `d`.`phpsettingid`, `c`.`adminid`, `c`.`guid`, `c`.`email`, `c`.`documentroot` AS `customerroot`, `c`.`deactivated`, - `c`.`phpenabled` AS `phpenabled`, `d`.`mod_fcgid_starter`, - `d`.`mod_fcgid_maxrequests` + `c`.`phpenabled` AS `phpenabled_customer`, + `d`.`phpenabled` AS `phpenabled_vhost`, + `d`.`mod_fcgid_starter`,`d`.`mod_fcgid_maxrequests` FROM `".TABLE_PANEL_DOMAINS."` `d` LEFT JOIN `".TABLE_PANEL_CUSTOMERS."` `c` USING(`customerid`) diff --git a/lib/version.inc.php b/lib/version.inc.php index ef48a2b2..72c746d7 100644 --- a/lib/version.inc.php +++ b/lib/version.inc.php @@ -19,7 +19,7 @@ $version = '0.9.38-rc2'; // Database version (YYYYMMDDC where C is a daily counter) -$dbversion = '201610070'; +$dbversion = '201611130'; // Distribution branding-tag (used for Debian etc.) $branding = ''; diff --git a/scripts/jobs/cron_tasks.inc.http.10.apache.php b/scripts/jobs/cron_tasks.inc.http.10.apache.php index 156435a3..f597dcad 100644 --- a/scripts/jobs/cron_tasks.inc.http.10.apache.php +++ b/scripts/jobs/cron_tasks.inc.http.10.apache.php @@ -488,7 +488,7 @@ class apache extends HttpConfigBase { $php_options_text = ''; - if ($domain['phpenabled'] == '1') { + if ($domain['phpenabled_customer'] == 1 && $domain['phpenabled_vhost'] == '1') { // This vHost has PHP enabled and we are using the regular mod_php if ($domain['openbasedir'] == '1') { diff --git a/scripts/jobs/cron_tasks.inc.http.15.apache_fcgid.php b/scripts/jobs/cron_tasks.inc.http.15.apache_fcgid.php index 6e569d8f..6c9bd2a2 100644 --- a/scripts/jobs/cron_tasks.inc.http.15.apache_fcgid.php +++ b/scripts/jobs/cron_tasks.inc.http.15.apache_fcgid.php @@ -23,7 +23,7 @@ class apache_fcgid extends apache { $php_options_text = ''; - if($domain['phpenabled'] == '1') + if($domain['phpenabled_customer'] == 1 && $domain['phpenabled_vhost'] == '1') { $php = new phpinterface($domain); $phpconfig = $php->getPhpConfig((int)$domain['phpsettingid']); diff --git a/scripts/jobs/cron_tasks.inc.http.25.lighttpd_fcgid.php b/scripts/jobs/cron_tasks.inc.http.25.lighttpd_fcgid.php index 9f2e4ad2..a53c4b24 100644 --- a/scripts/jobs/cron_tasks.inc.http.25.lighttpd_fcgid.php +++ b/scripts/jobs/cron_tasks.inc.http.25.lighttpd_fcgid.php @@ -21,7 +21,7 @@ class lighttpd_fcgid extends lighttpd { $php_options_text = ''; - if($domain['phpenabled'] == '1') + if($domain['phpenabled_customer'] == 1 && $domain['phpenabled_vhost'] == '1') { $php = new phpinterface($domain); $phpconfig = $php->getPhpConfig((int)$domain['phpsettingid']); diff --git a/scripts/jobs/cron_tasks.inc.http.30.nginx.php b/scripts/jobs/cron_tasks.inc.http.30.nginx.php index b282b1e1..6d2fb617 100644 --- a/scripts/jobs/cron_tasks.inc.http.30.nginx.php +++ b/scripts/jobs/cron_tasks.inc.http.30.nginx.php @@ -838,7 +838,7 @@ class nginx extends HttpConfigBase protected function composePhpOptions($domain, $ssl_vhost = false) { $phpopts = ''; - if ($domain['phpenabled'] == '1') { + if ($domain['phpenabled_customer'] == 1 && $domain['phpenabled_vhost'] == '1') { $phpopts = "\tlocation ~ \.php {\n"; $phpopts .= "\t\t" . 'try_files ' . $domain['nonexistinguri'] . ' @php;' . "\n"; $phpopts .= "\t" . '}' . "\n\n"; @@ -874,7 +874,7 @@ class nginx extends HttpConfigBase $webroot_text .= "\n\t" . 'location / {' . "\n"; - if ($domain['phpenabled'] == '1') { + if ($domain['phpenabled_customer'] == 1 && $domain['phpenabled_vhost'] == '1') { $webroot_text .= "\t" . 'index index.php index.html index.htm;' . "\n"; $webroot_text .= "\t\t" . 'try_files $uri $uri/ @rewrites;' . "\n"; } else { @@ -887,7 +887,7 @@ class nginx extends HttpConfigBase } $webroot_text .= "\t" . '}' . "\n\n"; - if ($domain['phpenabled'] == '1') { + if ($domain['phpenabled_customer'] == 1 && $domain['phpenabled_vhost'] == '1') { $webroot_text .= "\tlocation @rewrites {\n"; $webroot_text .= "\t\trewrite ^ /index.php last;\n"; $webroot_text .= "\t}\n\n"; diff --git a/scripts/jobs/cron_tasks.inc.http.35.nginx_phpfpm.php b/scripts/jobs/cron_tasks.inc.http.35.nginx_phpfpm.php index f47ccd82..4a94c51d 100644 --- a/scripts/jobs/cron_tasks.inc.http.35.nginx_phpfpm.php +++ b/scripts/jobs/cron_tasks.inc.http.35.nginx_phpfpm.php @@ -20,7 +20,7 @@ class nginx_phpfpm extends nginx protected function composePhpOptions($domain, $ssl_vhost = false) { $php_options_text = ''; - if ($domain['phpenabled'] == '1') { + if ($domain['phpenabled_customer'] == 1 && $domain['phpenabled_vhost'] == '1') { $php = new phpinterface($domain); $phpconfig = $php->getPhpConfig((int)$domain['phpsettingid']); From d93cfff1729d7e0cd1756f0a2997d68dd3941d07 Mon Sep 17 00:00:00 2001 From: Dominic Schallert Date: Sun, 13 Nov 2016 15:18:07 +0100 Subject: [PATCH 0232/1335] updates always at the end of the file --- install/updates/froxlor/0.9/update_0.9.inc.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index cce1ebfd..7f2387ae 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -3502,15 +3502,6 @@ if (isDatabaseVersion('201609240')) { updateToDbVersion('201610070'); } -if (isDatabaseVersion('201610070')) { - - showUpdateStep("Updating database table definition for panel_domains"); - Database::query("ALTER TABLE `" . TABLE_PANEL_DOMAINS . "` ADD `phpenabled` tinyint(1) NOT NULL default '1' AFTER `parentdomainid`;"); - lastStepStatus(0); - - updateToDbVersion('201611130'); -} - if (isFroxlorVersion('0.9.37')) { showUpdateStep("Updating from 0.9.37 to 0.9.38-rc1", false); @@ -3522,3 +3513,12 @@ if (isFroxlorVersion('0.9.38-rc1')) { showUpdateStep("Updating from 0.9.38-rc1 to 0.9.38-rc2", false); updateToVersion('0.9.38-rc2'); } + +if (isDatabaseVersion('201610070')) { + + showUpdateStep("Updating database table definition for panel_domains"); + Database::query("ALTER TABLE `" . TABLE_PANEL_DOMAINS . "` ADD `phpenabled` tinyint(1) NOT NULL default '1' AFTER `parentdomainid`;"); + lastStepStatus(0); + + updateToDbVersion('201611130'); +} From 30087548b0b7cbb0ca8e35cb304b9841a8dba50d Mon Sep 17 00:00:00 2001 From: Dominic Schallert Date: Sun, 13 Nov 2016 15:33:56 +0100 Subject: [PATCH 0233/1335] Fixed the checkbox value --- lib/formfields/admin/domains/formfield.domains_edit.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/formfields/admin/domains/formfield.domains_edit.php b/lib/formfields/admin/domains/formfield.domains_edit.php index 73e00497..d458b5d3 100644 --- a/lib/formfields/admin/domains/formfield.domains_edit.php +++ b/lib/formfields/admin/domains/formfield.domains_edit.php @@ -231,7 +231,7 @@ return array( 'values' => array( array ('label' => $lng['panel']['yes'], 'value' => '1') ), - 'value' => array('1') + 'value' => array($result['phpenabled']) ), 'openbasedir' => array( 'label' => 'OpenBasedir', From 432645431c1be3191676f3604809ec4827a7beea Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 15 Nov 2016 08:03:34 +0100 Subject: [PATCH 0234/1335] allow CIDR values in AXFR setting, fixes #1672 Signed-off-by: Michael Kaufmann (d00p) --- .../function.validateFormFieldString.php | 2 +- .../validate/function.validate_ip.php | 28 ++++++++++++++++--- scripts/jobs/cron_tasks.inc.dns.10.bind.php | 4 +-- scripts/jobs/cron_tasks.inc.dns.20.pdns.php | 6 ++-- 4 files changed, 28 insertions(+), 12 deletions(-) diff --git a/lib/functions/formfields/string/function.validateFormFieldString.php b/lib/functions/formfields/string/function.validateFormFieldString.php index addfc8b6..45544a18 100644 --- a/lib/functions/formfields/string/function.validateFormFieldString.php +++ b/lib/functions/formfields/string/function.validateFormFieldString.php @@ -122,7 +122,7 @@ function validateFormFieldString($fieldname, $fielddata, $newfieldvalue) $newfieldvalue = ''; $returnvalue = 'stringmustntbeempty'; } else { - $newfieldvalue = validate_ip2($newfieldvalue, true, true, true); + $newfieldvalue = validate_ip2($newfieldvalue, true, 'invalidip', true, true, true); $returnvalue = ($newfieldvalue !== false ? true : 'invalidip'); } } diff --git a/lib/functions/validate/function.validate_ip.php b/lib/functions/validate/function.validate_ip.php index bd8055cc..f3caa492 100644 --- a/lib/functions/validate/function.validate_ip.php +++ b/lib/functions/validate/function.validate_ip.php @@ -49,23 +49,43 @@ function validate_ip($ip, $return_bool = false, $lng = 'invalidip') { * @param string $lng index for error-message (if $return_bool is false) * @param bool $allow_localhost whether to allow 127.0.0.1 * @param bool $allow_priv whether to allow private network addresses + * @param bool $allow_cidr whether to allow CIDR values e.g. 10.10.10.10/16 * * @return string|bool ip address on success, false on failure */ -function validate_ip2($ip, $return_bool = false, $lng = 'invalidip', $allow_localhost = false, $allow_priv = false) { +function validate_ip2($ip, $return_bool = false, $lng = 'invalidip', $allow_localhost = false, $allow_priv = false, $allow_cidr = false) { - $filter_lan = $allow_priv ? FILTER_FLAG_NO_RES_RANGE : (FILTER_FLAG_NO_RES_RANGE | FILTER_FLAG_NO_PRIV_RANGE); + $cidr = ""; + if ($allow_cidr) { + $org_ip = $ip; + $ip_cidr = explode("/", $ip); + if (count($ip_cidr) == 2) { + $ip = $ip_cidr[0]; + $cidr = "/".$ip_cidr[1]; + } else { + $ip = $org_ip; + } + } elseif (strpos($ip, "/") !== false) { + if ($return_bool) { + return false; + } else { + standard_error($lng, $ip); + exit(); + } + } + + $filter_lan = $allow_priv ? FILTER_FLAG_NO_RES_RANGE : (FILTER_FLAG_NO_RES_RANGE | FILTER_FLAG_NO_PRIV_RANGE); 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_lan) ) { - return $ip; + return $ip.$cidr; } // special case where localhost ip is allowed (mysql-access-hosts for example) if ($allow_localhost && $ip == '127.0.0.1') { - return $ip; + return $ip.$cidr; } if ($return_bool) { diff --git a/scripts/jobs/cron_tasks.inc.dns.10.bind.php b/scripts/jobs/cron_tasks.inc.dns.10.bind.php index 06fbc9c8..2934e2a6 100644 --- a/scripts/jobs/cron_tasks.inc.dns.10.bind.php +++ b/scripts/jobs/cron_tasks.inc.dns.10.bind.php @@ -129,9 +129,7 @@ class bind extends DnsBase // AXFR server #100 if (count($this->_axfr) > 0) { foreach ($this->_axfr as $axfrserver) { - if (validate_ip($axfrserver, true) !== false) { - $bindconf_file .= ' ' . $axfrserver . ';' . "\n"; - } + $bindconf_file .= ' ' . $axfrserver . ';' . "\n"; } } // close allow-transfer diff --git a/scripts/jobs/cron_tasks.inc.dns.20.pdns.php b/scripts/jobs/cron_tasks.inc.dns.20.pdns.php index fac7a736..27a63632 100644 --- a/scripts/jobs/cron_tasks.inc.dns.20.pdns.php +++ b/scripts/jobs/cron_tasks.inc.dns.20.pdns.php @@ -194,10 +194,8 @@ class pdns extends DnsBase // AXFR server #100 if (count($this->_axfr) > 0) { foreach ($this->_axfr as $axfrserver) { - if (validate_ip($axfrserver, true) !== false) { - $ins_data['value'] = $axfrserver; - $ins_stmt->execute($ins_data); - } + $ins_data['value'] = $axfrserver; + $ins_stmt->execute($ins_data); } } } From 5f899a5510792a5269f608371a2cd8955346e16e Mon Sep 17 00:00:00 2001 From: Janos Muzsi Date: Thu, 17 Nov 2016 22:50:11 +0100 Subject: [PATCH 0235/1335] Add support for http2 option to nginx --- actions/admin/settings/130.webserver.php | 11 +++++++++++ install/froxlor.sql | 1 + install/updates/froxlor/0.9/update_0.9.inc.php | 10 ++++++++++ lng/english.lng.php | 3 +++ scripts/jobs/cron_tasks.inc.http.30.nginx.php | 8 ++++++-- 5 files changed, 31 insertions(+), 2 deletions(-) diff --git a/actions/admin/settings/130.webserver.php b/actions/admin/settings/130.webserver.php index 4374b148..8a117d5c 100644 --- a/actions/admin/settings/130.webserver.php +++ b/actions/admin/settings/130.webserver.php @@ -179,6 +179,17 @@ return array( 'nginx' ) ), + 'system_nginx_http2_support' => array( + 'label' => $lng['serversettings']['nginx_http2_support'], + 'settinggroup' => 'system', + 'varname' => 'nginx_http2_support', + 'type' => 'bool', + 'default' => false, + 'save_method' => 'storeSettingField', + 'websrv_avail' => array( + 'nginx' + ) + ), 'system_nginx_php_backend' => array( 'label' => $lng['serversettings']['nginx_php_backend'], 'settinggroup' => 'system', diff --git a/install/froxlor.sql b/install/froxlor.sql index e8ad16bb..24805a1b 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -496,6 +496,7 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES ('system', 'ssl_cert_chainfile', ''), ('system', 'ssl_cipher_list', 'ECDH+AESGCM:ECDH+AES256:!aNULL:!MD5:!DSS:!DH:!AES128'), ('system', 'nginx_php_backend', '127.0.0.1:8888'), + ('system', 'nginx_http2_support', '0'), ('system', 'perl_server', 'unix:/var/run/nginx/cgiwrap-dispatch.sock'), ('system', 'phpreload_command', ''), ('system', 'apache24', '0'), diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index c9968770..f625fbc3 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -3513,3 +3513,13 @@ if (isFroxlorVersion('0.9.38-rc1')) { showUpdateStep("Updating from 0.9.38-rc1 to 0.9.38-rc2", false); updateToVersion('0.9.38-rc2'); } +/* +if (isDatabaseVersion('201610070')) { + + showUpdateStep("Add Nginx http2 setting"); + Settings::AddNew("system.nginx_http2_support", 0); + lastStepStatus(0); + + updateToDbVersion('201610270'); +} + */ \ No newline at end of file diff --git a/lng/english.lng.php b/lng/english.lng.php index 036cf118..45996cd0 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -2062,3 +2062,6 @@ $lng['admin']['domain_hsts_incsub']['title'] = 'Include HSTS for any subdomain'; $lng['admin']['domain_hsts_incsub']['description'] = 'The optional "includeSubDomains" directive, if present, signals the UA that the HSTS Policy applies to this HSTS Host as well as any subdomains of the host\'s domain name.'; $lng['admin']['domain_hsts_preload']['title'] = 'Include domain in HSTS preload list'; $lng['admin']['domain_hsts_preload']['description'] = 'If you would like this domain to be included in the HSTS preload list maintained by Chrome (and used by Firefox and Safari), then use activate this.
Sending the preload directive from your site can have PERMANENT CONSEQUENCES and prevent users from accessing your site and any of its subdomains.
Please read the details at hstspreload.appspot.com/#removal before sending the header with "preload".'; + +$lng['serversettings']['nginx_http2_support']['title'] = 'Nginx HTTP2 Support'; +$lng['serversettings']['nginx_http2_support']['description'] = 'enable http2 support for ssl. ENABLE ONLY IF YOUR Nginx SUPPORT THIS FEATURE. (version 1.9.5+)'; diff --git a/scripts/jobs/cron_tasks.inc.http.30.nginx.php b/scripts/jobs/cron_tasks.inc.http.30.nginx.php index b282b1e1..dcff6ff1 100644 --- a/scripts/jobs/cron_tasks.inc.http.30.nginx.php +++ b/scripts/jobs/cron_tasks.inc.http.30.nginx.php @@ -196,10 +196,12 @@ class nginx extends HttpConfigBase } } + $http2 = $ssl_vhost == true && Settings::Get('system.nginx_http2_support') == '1'; + /** * this HAS to be set for the default host in nginx or else no vhost will work */ - $this->nginx_data[$vhost_filename] .= "\t" . 'listen ' . $ip . ':' . $port . ' default_server' . ($ssl_vhost == true ? ' ssl' : '') . ';' . "\n"; + $this->nginx_data[$vhost_filename] .= "\t" . 'listen ' . $ip . ':' . $port . ' default_server' . ($ssl_vhost == true ? ' ssl' : '') . ($http2 == true ? ' http2' : '') . ';' . "\n"; $this->nginx_data[$vhost_filename] .= "\t" . '# Froxlor default vhost' . "\n"; $this->nginx_data[$vhost_filename] .= "\t" . 'server_name ' . Settings::Get('system.hostname') . ';' . "\n"; @@ -411,7 +413,9 @@ class nginx extends HttpConfigBase $_vhost_content .= $this->processSpecialConfigTemplate($ipandport['default_vhostconf_domain'], $domain, $domain['ip'], $domain['port'], $ssl_vhost) . "\n"; } - $vhost_content .= "\t" . 'listen ' . $ipport . ($ssl_vhost == true ? ' ssl' : '') . ';' . "\n"; + $http2 = $ssl_vhost == true && Settings::Get('system.nginx_http2_support') == '1'; + + $vhost_content .= "\t" . 'listen ' . $ipport . ($ssl_vhost == true ? ' ssl' : '') . ($http2 == true ? ' http2' : '') . ';' . "\n"; } // get all server-names From d245bca445af99dad1cf53f6411bf8d9d7dbcdc6 Mon Sep 17 00:00:00 2001 From: Janos Muzsi Date: Fri, 18 Nov 2016 08:32:23 +0100 Subject: [PATCH 0236/1335] correcting the update --- install/updates/froxlor/0.9/update_0.9.inc.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index f625fbc3..821e500d 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -3513,13 +3513,12 @@ if (isFroxlorVersion('0.9.38-rc1')) { showUpdateStep("Updating from 0.9.38-rc1 to 0.9.38-rc2", false); updateToVersion('0.9.38-rc2'); } -/* + if (isDatabaseVersion('201610070')) { showUpdateStep("Add Nginx http2 setting"); Settings::AddNew("system.nginx_http2_support", 0); lastStepStatus(0); - updateToDbVersion('201610270'); + updateToDbVersion('201611180'); } - */ \ No newline at end of file From 547140bafb4e936cfc469858e4b11e9ef93c207b Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Fri, 18 Nov 2016 08:36:35 +0100 Subject: [PATCH 0237/1335] set version to 0.9.38 for upcoming release Signed-off-by: Michael Kaufmann (d00p) --- install/froxlor.sql | 2 +- install/updates/froxlor/0.9/update_0.9.inc.php | 6 ++++++ lib/version.inc.php | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/install/froxlor.sql b/install/froxlor.sql index e8ad16bb..71897be7 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -576,7 +576,7 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES ('panel', 'password_special_char_required', '0'), ('panel', 'password_special_char', '!?<>§$%+#=@'), ('panel', 'customer_hide_options', ''), - ('panel', 'version', '0.9.38-rc2'), + ('panel', 'version', '0.9.38'), ('panel', 'db_version', '201610070'); diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index c9968770..ad448a61 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -3513,3 +3513,9 @@ if (isFroxlorVersion('0.9.38-rc1')) { showUpdateStep("Updating from 0.9.38-rc1 to 0.9.38-rc2", false); updateToVersion('0.9.38-rc2'); } + +if (isFroxlorVersion('0.9.38-rc2')) { + + showUpdateStep("Updating from 0.9.38-rc2 to 0.9.38 final", false); + updateToVersion('0.9.38'); +} diff --git a/lib/version.inc.php b/lib/version.inc.php index ef48a2b2..d959ebf8 100644 --- a/lib/version.inc.php +++ b/lib/version.inc.php @@ -16,7 +16,7 @@ */ // Main version variable -$version = '0.9.38-rc2'; +$version = '0.9.38'; // Database version (YYYYMMDDC where C is a daily counter) $dbversion = '201610070'; From 1984aced9de8fe7de8a0bb4fff9c61b14498587c Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Fri, 18 Nov 2016 08:52:40 +0100 Subject: [PATCH 0238/1335] set db_version correctly everywhere Signed-off-by: Michael Kaufmann (d00p) --- install/froxlor.sql | 2 +- lib/version.inc.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/install/froxlor.sql b/install/froxlor.sql index 5a64a175..20cffe56 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -578,7 +578,7 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES ('panel', 'password_special_char', '!?<>§$%+#=@'), ('panel', 'customer_hide_options', ''), ('panel', 'version', '0.9.38'), - ('panel', 'db_version', '201610070'); + ('panel', 'db_version', '201611180'); DROP TABLE IF EXISTS `panel_tasks`; diff --git a/lib/version.inc.php b/lib/version.inc.php index d959ebf8..b66c0485 100644 --- a/lib/version.inc.php +++ b/lib/version.inc.php @@ -19,7 +19,7 @@ $version = '0.9.38'; // Database version (YYYYMMDDC where C is a daily counter) -$dbversion = '201610070'; +$dbversion = '201611180'; // Distribution branding-tag (used for Debian etc.) $branding = ''; From 9540cb158cfe8c05b2edf38d681d4a0be02740f9 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Fri, 18 Nov 2016 10:10:03 +0100 Subject: [PATCH 0239/1335] set version to 0.9.38.1 b/c of hsts includeSubdomains flag for domains not being saved; added http2-flag for nginx as setting Signed-off-by: Michael Kaufmann (d00p) --- install/froxlor.sql | 2 +- install/updates/froxlor/0.9/update_0.9.inc.php | 10 ++++++++-- lib/formfields/admin/domains/formfield.domains_add.php | 2 +- .../admin/domains/formfield.domains_edit.php | 2 +- lib/version.inc.php | 2 +- 5 files changed, 12 insertions(+), 6 deletions(-) diff --git a/install/froxlor.sql b/install/froxlor.sql index 20cffe56..92471885 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -577,7 +577,7 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES ('panel', 'password_special_char_required', '0'), ('panel', 'password_special_char', '!?<>§$%+#=@'), ('panel', 'customer_hide_options', ''), - ('panel', 'version', '0.9.38'), + ('panel', 'version', '0.9.38.1'), ('panel', 'db_version', '201611180'); diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index 9f286d9c..926b54e5 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -3516,8 +3516,8 @@ if (isFroxlorVersion('0.9.38-rc1')) { if (isFroxlorVersion('0.9.38-rc2')) { - showUpdateStep("Updating from 0.9.38-rc2 to 0.9.38 final", false); - updateToVersion('0.9.38'); + showUpdateStep("Updating from 0.9.38-rc2 to 0.9.38 final", false); + updateToVersion('0.9.38'); } if (isDatabaseVersion('201610070')) { @@ -3528,3 +3528,9 @@ if (isDatabaseVersion('201610070')) { updateToDbVersion('201611180'); } + +if (isFroxlorVersion('0.9.38')) { + + showUpdateStep("Updating from 0.9.38 to 0.9.38.1", false); + updateToVersion('0.9.38.1'); +} diff --git a/lib/formfields/admin/domains/formfield.domains_add.php b/lib/formfields/admin/domains/formfield.domains_add.php index 15ca70e4..1f92f599 100644 --- a/lib/formfields/admin/domains/formfield.domains_add.php +++ b/lib/formfields/admin/domains/formfield.domains_add.php @@ -175,7 +175,7 @@ return array( 'int_max' => 94608000, // 3-years 'value' => 0 ), - 'hsts_incsub' => array( + 'hsts_sub' => array( 'visible' => ($ssl_ipsandports != '' ? true : false), 'label' => $lng['admin']['domain_hsts_incsub']['title'], 'desc' => $lng['admin']['domain_hsts_incsub']['description'], diff --git a/lib/formfields/admin/domains/formfield.domains_edit.php b/lib/formfields/admin/domains/formfield.domains_edit.php index 11b6c6bc..cff9b974 100644 --- a/lib/formfields/admin/domains/formfield.domains_edit.php +++ b/lib/formfields/admin/domains/formfield.domains_edit.php @@ -198,7 +198,7 @@ return array( 'int_max' => 94608000, // 3-years 'value' => $result['hsts'] ), - 'hsts_incsub' => array( + 'hsts_sub' => array( 'visible' => ($ssl_ipsandports != '' ? true : false), 'label' => $lng['admin']['domain_hsts_incsub']['title'], 'desc' => $lng['admin']['domain_hsts_incsub']['description'], diff --git a/lib/version.inc.php b/lib/version.inc.php index b66c0485..527b8454 100644 --- a/lib/version.inc.php +++ b/lib/version.inc.php @@ -16,7 +16,7 @@ */ // Main version variable -$version = '0.9.38'; +$version = '0.9.38.1'; // Database version (YYYYMMDDC where C is a daily counter) $dbversion = '201611180'; From 60a482dce6eca5d4720597495f717aba8770bd29 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Fri, 18 Nov 2016 10:21:02 +0100 Subject: [PATCH 0240/1335] damn, forgot to save the two files to handle customer-side domain-settings regarding the hsts-includeSubdomain issue; version set to 0.9.38.2 Signed-off-by: Michael Kaufmann (d00p) --- install/froxlor.sql | 4 ++-- install/updates/froxlor/0.9/update_0.9.inc.php | 6 ++++++ lib/formfields/customer/domains/formfield.domains_add.php | 2 +- lib/formfields/customer/domains/formfield.domains_edit.php | 2 +- lib/version.inc.php | 2 +- 5 files changed, 11 insertions(+), 5 deletions(-) diff --git a/install/froxlor.sql b/install/froxlor.sql index 92471885..129c6bb6 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -496,7 +496,7 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES ('system', 'ssl_cert_chainfile', ''), ('system', 'ssl_cipher_list', 'ECDH+AESGCM:ECDH+AES256:!aNULL:!MD5:!DSS:!DH:!AES128'), ('system', 'nginx_php_backend', '127.0.0.1:8888'), - ('system', 'nginx_http2_support', '0'), + ('system', 'nginx_http2_support', '0'), ('system', 'perl_server', 'unix:/var/run/nginx/cgiwrap-dispatch.sock'), ('system', 'phpreload_command', ''), ('system', 'apache24', '0'), @@ -577,7 +577,7 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES ('panel', 'password_special_char_required', '0'), ('panel', 'password_special_char', '!?<>§$%+#=@'), ('panel', 'customer_hide_options', ''), - ('panel', 'version', '0.9.38.1'), + ('panel', 'version', '0.9.38.2'), ('panel', 'db_version', '201611180'); diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index 926b54e5..380ba153 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -3534,3 +3534,9 @@ if (isFroxlorVersion('0.9.38')) { showUpdateStep("Updating from 0.9.38 to 0.9.38.1", false); updateToVersion('0.9.38.1'); } + +if (isFroxlorVersion('0.9.38')) { + + showUpdateStep("Updating from 0.9.38.1 to 0.9.38.2", false); + updateToVersion('0.9.38.2'); +} diff --git a/lib/formfields/customer/domains/formfield.domains_add.php b/lib/formfields/customer/domains/formfield.domains_add.php index fb9adc47..1371e579 100644 --- a/lib/formfields/customer/domains/formfield.domains_add.php +++ b/lib/formfields/customer/domains/formfield.domains_add.php @@ -107,7 +107,7 @@ return array( 'int_max' => 94608000, // 3-years 'value' => 0 ), - 'hsts_incsub' => array( + 'hsts_sub' => array( 'visible' => ($ssl_ipsandports != '' ? true : false), 'label' => $lng['admin']['domain_hsts_incsub']['title'], 'desc' => $lng['admin']['domain_hsts_incsub']['description'], diff --git a/lib/formfields/customer/domains/formfield.domains_edit.php b/lib/formfields/customer/domains/formfield.domains_edit.php index 2a7b593b..94bab096 100644 --- a/lib/formfields/customer/domains/formfield.domains_edit.php +++ b/lib/formfields/customer/domains/formfield.domains_edit.php @@ -118,7 +118,7 @@ return array( 'int_max' => 94608000, // 3-years 'value' => $result['hsts'] ), - 'hsts_incsub' => array( + 'hsts_sub' => array( 'visible' => ($ssl_ipsandports != '' ? true : false), 'label' => $lng['admin']['domain_hsts_incsub']['title'], 'desc' => $lng['admin']['domain_hsts_incsub']['description'], diff --git a/lib/version.inc.php b/lib/version.inc.php index 527b8454..045cd82a 100644 --- a/lib/version.inc.php +++ b/lib/version.inc.php @@ -16,7 +16,7 @@ */ // Main version variable -$version = '0.9.38.1'; +$version = '0.9.38.2'; // Database version (YYYYMMDDC where C is a daily counter) $dbversion = '201611180'; From 16f547bce0db7e11725f2d7dcfa2251b7d1d9c42 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Fri, 18 Nov 2016 10:27:11 +0100 Subject: [PATCH 0241/1335] last time....0.9.38.3....what a day Signed-off-by: Michael Kaufmann (d00p) --- install/froxlor.sql | 2 +- install/updates/froxlor/0.9/update_0.9.inc.php | 8 +++++++- lib/version.inc.php | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/install/froxlor.sql b/install/froxlor.sql index 129c6bb6..24bbb15e 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -577,7 +577,7 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES ('panel', 'password_special_char_required', '0'), ('panel', 'password_special_char', '!?<>§$%+#=@'), ('panel', 'customer_hide_options', ''), - ('panel', 'version', '0.9.38.2'), + ('panel', 'version', '0.9.38.3'), ('panel', 'db_version', '201611180'); diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index 380ba153..8fb02c7a 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -3535,8 +3535,14 @@ if (isFroxlorVersion('0.9.38')) { updateToVersion('0.9.38.1'); } -if (isFroxlorVersion('0.9.38')) { +if (isFroxlorVersion('0.9.38.1')) { showUpdateStep("Updating from 0.9.38.1 to 0.9.38.2", false); updateToVersion('0.9.38.2'); } + +if (isFroxlorVersion('0.9.38.2')) { + + showUpdateStep("Updating from 0.9.38.2 to 0.9.38.3", false); + updateToVersion('0.9.38.3'); +} diff --git a/lib/version.inc.php b/lib/version.inc.php index 045cd82a..915532b7 100644 --- a/lib/version.inc.php +++ b/lib/version.inc.php @@ -16,7 +16,7 @@ */ // Main version variable -$version = '0.9.38.2'; +$version = '0.9.38.3'; // Database version (YYYYMMDDC where C is a daily counter) $dbversion = '201611180'; From 16e9fd6bd99324649b1df9f860831c5eaba87610 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Fri, 18 Nov 2016 21:19:18 +0100 Subject: [PATCH 0242/1335] stay php-5.3 compatible Signed-off-by: Michael Kaufmann (d00p) --- lib/functions/filedir/function.makeCorrectDir.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/functions/filedir/function.makeCorrectDir.php b/lib/functions/filedir/function.makeCorrectDir.php index 75dc00d1..dcc91ca2 100644 --- a/lib/functions/filedir/function.makeCorrectDir.php +++ b/lib/functions/filedir/function.makeCorrectDir.php @@ -26,7 +26,11 @@ */ function makeCorrectDir($dir) { - assert('is_string($dir) && strlen($dir) > 0', 'Value "' . $dir .'" does not look like an actual folder name'); + if (version_compare("5.4.6", PHP_VERSION, ">")) { + assert('is_string($dir) && strlen($dir) > 0 /* $dir does not look like an actual folder name */'); + } else { + assert('is_string($dir) && strlen($dir) > 0', 'Value "' . $dir .'" does not look like an actual folder name'); + } $dir = trim($dir); From 43ca4a28e4302c72b0abebb297a8ba4ebb19d2ca Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 21 Nov 2016 08:19:33 +0100 Subject: [PATCH 0243/1335] add acme.conf alias also to froxlor vhost in case the acme-challenge path is not within the froxlor-docroot; fix empty redirect-code, fixes #1674 Signed-off-by: Michael Kaufmann (d00p) --- .../output/function.RedirectCode.php | 35 ++++++++++--------- .../jobs/cron_tasks.inc.http.10.apache.php | 4 +-- .../jobs/cron_tasks.inc.http.20.lighttpd.php | 5 ++- scripts/jobs/cron_tasks.inc.http.30.nginx.php | 22 +++++++----- 4 files changed, 34 insertions(+), 32 deletions(-) diff --git a/lib/functions/output/function.RedirectCode.php b/lib/functions/output/function.RedirectCode.php index b970e3b0..37bd10e1 100644 --- a/lib/functions/output/function.RedirectCode.php +++ b/lib/functions/output/function.RedirectCode.php @@ -16,11 +16,11 @@ /** * return an array of all enabled redirect-codes - * + * * @return array array of enabled redirect-codes */ function getRedirectCodesArray() { - + $sql = "SELECT * FROM `".TABLE_PANEL_REDIRECTCODES."` WHERE `enabled` = '1' ORDER BY `id` ASC"; $result_stmt = Database::query($sql); @@ -35,13 +35,13 @@ function getRedirectCodesArray() { /** * return an array of all enabled redirect-codes * for the settings form - * + * * @return array array of enabled redirect-codes */ function getRedirectCodes() { global $lng; - + $sql = "SELECT * FROM `".TABLE_PANEL_REDIRECTCODES."` WHERE `enabled` = '1' ORDER BY `id` ASC"; $result_stmt = Database::query($sql); @@ -54,16 +54,17 @@ function getRedirectCodes() { } /** - * returns the redirect-code for a given + * returns the redirect-code for a given * domain-id - * + * * @param integer $domainid id of the domain - * + * @param string $default + * * @return string redirect-code */ -function getDomainRedirectCode($domainid = 0) { +function getDomainRedirectCode($domainid = 0, $default = '') { - $code = ''; + $code = $default; if ($domainid > 0) { $result_stmt = Database::prepare(" @@ -83,11 +84,11 @@ function getDomainRedirectCode($domainid = 0) { } /** - * returns the redirect-id for a given + * returns the redirect-id for a given * domain-id - * + * * @param integer $domainid id of the domain - * + * * @return integer redirect-code-id */ function getDomainRedirectId($domainid = 0) { @@ -112,10 +113,10 @@ function getDomainRedirectId($domainid = 0) { /** * adds a redirectcode for a domain - * + * * @param integer $domainid id of the domain to add the code for - * @param integer $redirect selected redirect-id - * + * @param integer $redirect selected redirect-id + * * @return null */ function addRedirectToDomain($domainid = 0, $redirect = 1) { @@ -130,10 +131,10 @@ function addRedirectToDomain($domainid = 0, $redirect = 1) { /** * updates the redirectcode of a domain * if redirect-code is false, nothing happens - * + * * @param integer $domainid id of the domain to update * @param integer $redirect selected redirect-id or false - * + * * @return null */ function updateRedirectOfDomain($domainid = 0, $redirect = false) { diff --git a/scripts/jobs/cron_tasks.inc.http.10.apache.php b/scripts/jobs/cron_tasks.inc.http.10.apache.php index 156435a3..b6a9697a 100644 --- a/scripts/jobs/cron_tasks.inc.http.10.apache.php +++ b/scripts/jobs/cron_tasks.inc.http.10.apache.php @@ -875,10 +875,8 @@ class apache extends HttpConfigBase if (preg_match('/^https?\:\/\//', $domain['documentroot'])) { $corrected_docroot = $domain['documentroot']; - // prevent empty return-cde - $code = "301"; // Get domain's redirect code - $code = getDomainRedirectCode($domain['id']); + $code = getDomainRedirectCode($domain['id'], '301'); $modrew_red = ''; if ($code != '') { $modrew_red = ' [R=' . $code . ';L,NE]'; diff --git a/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php b/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php index 1f37d4c8..a069d056 100644 --- a/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php +++ b/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php @@ -432,10 +432,9 @@ class lighttpd extends HttpConfigBase if (preg_match('/^https?\:\/\//', $domain['documentroot'])) { $uri = $domain['documentroot']; - // prevent empty return-cde - $code = "301"; + // Get domain's redirect code - $code = getDomainRedirectCode($domain['id']); + $code = getDomainRedirectCode($domain['id'], '301'); $vhost_content .= ' url.redirect-code = ' . $code. "\n"; $vhost_content .= ' url.redirect = (' . "\n"; diff --git a/scripts/jobs/cron_tasks.inc.http.30.nginx.php b/scripts/jobs/cron_tasks.inc.http.30.nginx.php index dcff6ff1..a99f41bf 100644 --- a/scripts/jobs/cron_tasks.inc.http.30.nginx.php +++ b/scripts/jobs/cron_tasks.inc.http.30.nginx.php @@ -196,17 +196,22 @@ class nginx extends HttpConfigBase } } - $http2 = $ssl_vhost == true && Settings::Get('system.nginx_http2_support') == '1'; - + $http2 = $ssl_vhost == true && Settings::Get('system.nginx_http2_support') == '1'; + /** * this HAS to be set for the default host in nginx or else no vhost will work */ - $this->nginx_data[$vhost_filename] .= "\t" . 'listen ' . $ip . ':' . $port . ' default_server' . ($ssl_vhost == true ? ' ssl' : '') . ($http2 == true ? ' http2' : '') . ';' . "\n"; + $this->nginx_data[$vhost_filename] .= "\t" . 'listen ' . $ip . ':' . $port . ' default_server' . ($ssl_vhost == true ? ' ssl' : '') . ($http2 == true ? ' http2' : '') . ';' . "\n"; $this->nginx_data[$vhost_filename] .= "\t" . '# Froxlor default vhost' . "\n"; $this->nginx_data[$vhost_filename] .= "\t" . 'server_name ' . Settings::Get('system.hostname') . ';' . "\n"; $this->nginx_data[$vhost_filename] .= "\t" . 'access_log /var/log/nginx/access.log;' . "\n"; + if (Settings::Get('system.use_ssl') == '1' && Settings::Get('system.leenabled') == '1' && Settings::Get('system.le_froxlor_enabled') == '1') { + $acmeConfFilename = Settings::Get('system.letsencryptacmeconf'); + $this->nginx_data[$vhost_filename] .= "\t" . 'include ' . $acmeConfFilename . ';' . "\n"; + } + $is_redirect = false; // check for SSL redirect if ($row_ipsandports['ssl'] == '0' && Settings::Get('system.le_froxlor_redirect') == '1') { @@ -219,7 +224,7 @@ class nginx extends HttpConfigBase } else { $_sslport = $this->checkAlternativeSslPort(); $mypath = 'https://' . Settings::Get('system.hostname') . $_sslport . '/'; - $this->nginx_data[$vhost_filename] .= "\t" . 'if ($request_uri !~ "^/\.well-known/acme-challenge/\w+$") {' . "\n"; + $this->nginx_data[$vhost_filename] .= "\t" . 'if ($request_uri !~ ^/.well-known/acme-challenge/\w+$) {' . "\n"; $this->nginx_data[$vhost_filename] .= "\t\t" . 'return 301 ' . $mypath . '$request_uri;' . "\n"; $this->nginx_data[$vhost_filename] .= "\t" . '}' . "\n"; } @@ -464,12 +469,11 @@ class nginx extends HttpConfigBase if (substr($uri, - 1) == '/') { $uri = substr($uri, 0, - 1); } - // prevent empty return-cde - $code = "301"; - // Get domain's redirect code - $code = getDomainRedirectCode($domain['id']); - $vhost_content .= "\t" . 'if ($request_uri !~ "^/\.well-known/acme-challenge/\w+$") {' . "\n"; + // Get domain's redirect code + $code = getDomainRedirectCode($domain['id'], '301'); + + $vhost_content .= "\t" . 'if ($request_uri !~ ^/.well-known/acme-challenge/\w+$) {' . "\n"; $vhost_content .= "\t\t" . 'return ' . $code .' ' . $uri . '$request_uri;' . "\n"; $vhost_content .= "\t" . '}' . "\n"; } else { From 192e00c71770e320a3151e7cf731e4670d5c9aa4 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 22 Nov 2016 08:04:11 +0100 Subject: [PATCH 0244/1335] do not show NameVirtualHost in IP/Port overview when using apache-2.4 (as NameVirtualHost does not exist there anymore) Signed-off-by: Michael Kaufmann (d00p) --- templates/Sparkle/admin/ipsandports/ipsandports.tpl | 2 +- templates/Sparkle/admin/ipsandports/ipsandports_ipandport.tpl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/Sparkle/admin/ipsandports/ipsandports.tpl b/templates/Sparkle/admin/ipsandports/ipsandports.tpl index 753aa1bd..4112553f 100644 --- a/templates/Sparkle/admin/ipsandports/ipsandports.tpl +++ b/templates/Sparkle/admin/ipsandports/ipsandports.tpl @@ -27,7 +27,7 @@ $header {$lng['admin']['ipsandports']['ip']} {$arrowcode['ip']} {$lng['admin']['ipsandports']['port']} {$arrowcode['port']} Listen - NameVirtualHost + NameVirtualHost vHost-Container Specialsettings ServerName diff --git a/templates/Sparkle/admin/ipsandports/ipsandports_ipandport.tpl b/templates/Sparkle/admin/ipsandports/ipsandports_ipandport.tpl index 245ba681..720c486a 100644 --- a/templates/Sparkle/admin/ipsandports/ipsandports_ipandport.tpl +++ b/templates/Sparkle/admin/ipsandports/ipsandports_ipandport.tpl @@ -2,7 +2,7 @@ {$row['ip']} {$row['port']} {$lng['panel']['yes']}{$lng['panel']['no']} - {$lng['panel']['yes']}{$lng['panel']['no']} + {$lng['panel']['yes']}{$lng['panel']['no']} {$lng['panel']['yes']}{$lng['panel']['no']} {$lng['panel']['yes']}{$lng['panel']['no']} {$lng['panel']['yes']}{$lng['panel']['no']} From 9838ff4da564a4e4dfc3123b11225cfbc1f8cebd Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 22 Nov 2016 08:16:35 +0100 Subject: [PATCH 0245/1335] fix hsts settings for lighttpd, fixes #1677 Signed-off-by: Michael Kaufmann (d00p) --- scripts/jobs/cron_tasks.inc.http.20.lighttpd.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php b/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php index a069d056..a1869a42 100644 --- a/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php +++ b/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php @@ -531,14 +531,14 @@ class lighttpd extends HttpConfigBase if ($domain['hsts'] >= 0) { - $vhost_content .= '$HTTP["scheme"] == "https" { setenv.add-response-header = ( "Strict-Transport-Security" => "max-age=' . $domain['hsts']; + $ssl_settings .= '$HTTP["scheme"] == "https" { setenv.add-response-header = ( "Strict-Transport-Security" => "max-age=' . $domain['hsts']; if ($domain['hsts_sub'] == 1) { - $vhost_content .= '; includeSubDomains'; + $ssl_settings .= '; includeSubDomains'; } if ($domain['hsts_preload'] == 1) { - $vhost_content .= '; preload'; + $ssl_settings .= '; preload'; } - $vhost_content .= '") }' . "\n"; + $ssl_settings .= '") }' . "\n"; } } } From f8996ad7674462d7e897ec83b29784a1707614f3 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 22 Nov 2016 15:08:42 +0100 Subject: [PATCH 0246/1335] catch exception thrown by new IdnaConverter when encoding a non-valid domain, fixes #1678 Signed-off-by: Michael Kaufmann (d00p) --- lib/functions/validate/function.validateUrl.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/functions/validate/function.validateUrl.php b/lib/functions/validate/function.validateUrl.php index 07ce29ac..0515e48d 100644 --- a/lib/functions/validate/function.validateUrl.php +++ b/lib/functions/validate/function.validateUrl.php @@ -37,7 +37,11 @@ function validateUrl($url) { } // needs converting - $url = $idna_convert->encode($url); + try { + $url = $idna_convert->encode($url); + } catch (Exception $e) { + return false; + } $pattern = "/^https?:\/\/[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,4}(\:[0-9]+)?\/?(.+)?$/i"; if (preg_match($pattern, $url)) { From 54200427ab221507b51700d962ae1bc8d305b135 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 24 Nov 2016 10:40:28 +0100 Subject: [PATCH 0247/1335] fix undefined variable when deleting a customer-domain as admin Signed-off-by: Michael Kaufmann (d00p) --- admin_domains.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/admin_domains.php b/admin_domains.php index 0f86f7b6..43f0811b 100644 --- a/admin_domains.php +++ b/admin_domains.php @@ -210,6 +210,8 @@ if ($page == 'domains' || $page == 'overview') { 'id' => $id )); + $deleted_domains = $del_stmt->rowCount(); + $upd_stmt = Database::prepare(" UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET `subdomains_used` = `subdomains_used` - :domaincount From 2c00f982d8beb14a6e74a460f8fffabd409d5f4c Mon Sep 17 00:00:00 2001 From: Janos Muzsi Date: Thu, 24 Nov 2016 22:55:57 +0100 Subject: [PATCH 0248/1335] Change redirect host from main domain name to requested domain name --- scripts/jobs/cron_tasks.inc.http.10.apache.php | 2 +- scripts/jobs/cron_tasks.inc.http.20.lighttpd.php | 14 ++++++++------ scripts/jobs/cron_tasks.inc.http.30.nginx.php | 2 +- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/scripts/jobs/cron_tasks.inc.http.10.apache.php b/scripts/jobs/cron_tasks.inc.http.10.apache.php index b6a9697a..16cf71e8 100644 --- a/scripts/jobs/cron_tasks.inc.http.10.apache.php +++ b/scripts/jobs/cron_tasks.inc.http.10.apache.php @@ -808,7 +808,7 @@ class apache extends HttpConfigBase $_sslport = ":" . $ssldestport['port']; } - $domain['documentroot'] = 'https://' . $domain['domain'] . $_sslport . '/'; + $domain['documentroot'] = 'https://%{HTTP_HOST}' . $_sslport . '/'; } if ($ssl_vhost === true && $domain['ssl'] == '1' && Settings::Get('system.use_ssl') == '1') { diff --git a/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php b/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php index a1869a42..9ca21da1 100644 --- a/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php +++ b/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php @@ -424,7 +424,7 @@ class lighttpd extends HttpConfigBase $_sslport = ":" . $ssldestport['port']; } - $domain['documentroot'] = 'https://' . $domain['domain'] . $_sslport . '/'; + $domain['documentroot'] = 'https://%1' . $_sslport . '/'; } // avoid using any whitespaces @@ -435,11 +435,13 @@ class lighttpd extends HttpConfigBase // Get domain's redirect code $code = getDomainRedirectCode($domain['id'], '301'); - - $vhost_content .= ' url.redirect-code = ' . $code. "\n"; - $vhost_content .= ' url.redirect = (' . "\n"; - $vhost_content .= ' "^/(.*)$" => "' . $uri . '$1"' . "\n"; - $vhost_content .= ' )' . "\n"; + + $vhost_content .= ' $HTTP["host"] =~ "^(.*)$" {'. "\n"; + $vhost_content .= ' url.redirect-code = ' . $code. "\n"; + $vhost_content .= ' url.redirect = (' . "\n"; + $vhost_content .= ' "^/(.*)$" => "' . $uri . '$1"' . "\n"; + $vhost_content .= ' )' . "\n"; + $vhost_content .= ' }' . "\n"; } else { mkDirWithCorrectOwnership($domain['customerroot'], $domain['documentroot'], $domain['guid'], $domain['guid'], true, true); diff --git a/scripts/jobs/cron_tasks.inc.http.30.nginx.php b/scripts/jobs/cron_tasks.inc.http.30.nginx.php index a99f41bf..4d93ab23 100644 --- a/scripts/jobs/cron_tasks.inc.http.30.nginx.php +++ b/scripts/jobs/cron_tasks.inc.http.30.nginx.php @@ -447,7 +447,7 @@ class nginx extends HttpConfigBase $_sslport = ":" . $ssldestport['port']; } - $domain['documentroot'] = 'https://' . $domain['domain'] . $_sslport . '/'; + $domain['documentroot'] = 'https://$host' . $_sslport . '/'; } // avoid using any whitespaces From 18b45c749daea040b78ef0726ea4ea27584b1dab Mon Sep 17 00:00:00 2001 From: Michael Wyraz Date: Fri, 25 Nov 2016 09:54:47 +0100 Subject: [PATCH 0249/1335] Better handling for letsencrypt errors after failed registration or changed license --- .../updates/froxlor/0.9/update_0.9.inc.php | 26 ++++ lib/classes/ssl/class.lescript.php | 118 +++++++++++++++--- lib/version.inc.php | 2 +- scripts/jobs/cron_letsencrypt.php | 2 + 4 files changed, 133 insertions(+), 15 deletions(-) diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index 8fb02c7a..3fd04142 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -3546,3 +3546,29 @@ if (isFroxlorVersion('0.9.38.2')) { showUpdateStep("Updating from 0.9.38.2 to 0.9.38.3", false); updateToVersion('0.9.38.3'); } + +if (isDatabaseVersion('201611180')) { + + showUpdateStep("Adding field to reflect let's-encrypt registration status"); + Database::query("ALTER TABLE `".TABLE_PANEL_CUSTOMERS."` add `leregistered` TINYINT(1) NOT NULL DEFAULT 0;"); + lastStepStatus(0); + + updateToDbVersion('201611240'); +} + +if (isDatabaseVersion('201611240')) { + + showUpdateStep("Adding new setting to reflect let's-encrypt registration status"); + $stmt = Database::prepare(" + INSERT INTO `" . TABLE_PANEL_SETTINGS . "` SET + `settinggroup` = 'system', + `varname` = :varname, + `value` = :value"); + Database::pexecute($stmt, array( + 'varname' => 'leregistered', + 'value' => '0' + )); + lastStepStatus(0); + + updateToDbVersion('201611241'); +} diff --git a/lib/classes/ssl/class.lescript.php b/lib/classes/ssl/class.lescript.php index dc8ec83b..1d3aa5bb 100644 --- a/lib/classes/ssl/class.lescript.php +++ b/lib/classes/ssl/class.lescript.php @@ -38,6 +38,12 @@ class lescript private $accountKey; + private $customerid; + + private $isFroxlorVhost; + + private $isLeProduction; + private $version; public function __construct($logger, $version = '1') @@ -57,44 +63,71 @@ class lescript { // Let's see if we have the private accountkey $this->accountKey = $certrow['leprivatekey']; - if (! $this->accountKey || $this->accountKey == 'unset' || Settings::Get('system.letsencryptca') != 'production') { + $this->customerId = $certrow['customerid']; + $this->isFroxlorVhost = $isFroxlorVhost; + $this->isLeProduction = (Settings::Get('system.letsencryptca') == 'production'); + + $leregistered=$certrow['leregistered']; + + if (! $this->accountKey || $this->accountKey == 'unset' || !$this->isLeProduction) { // generate and save new private key for account // --------------------------------------------- - $this->log('Starting new account registration'); + $this->log('Creating new account key'); $keys = $this->generateKey(); // Only store the accountkey in production, in staging always generate a new key - if (Settings::Get('system.letsencryptca') == 'production') { + if ($this->isLeProduction) { if ($isFroxlorVhost) { Settings::Set('system.lepublickey', $keys['public']); Settings::Set('system.leprivatekey', $keys['private']); + Settings::Set('system.leregistered', 0); // key is not registered } else { - $upd_stmt = Database::prepare("UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET `lepublickey` = :public, `leprivatekey` = :private " . "WHERE `customerid` = :customerid;"); + $upd_stmt = Database::prepare("UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET `lepublickey` = :public, `leprivatekey` = :private, `leregistered` = :registered " . "WHERE `customerid` = :customerid;"); Database::pexecute($upd_stmt, array( 'public' => $keys['public'], 'private' => $keys['private'], - 'customerid' => $certrow['customerid'] + 'registered' => 0, + 'customerid' => $this->customerId )); } } + $leregistered=0; $this->accountKey = $keys['private']; + } else { + $this->log('Using existing account key'); + } + if ($leregistered==0) { // Account not registered + + $this->log('Starting new account registration'); $response = $this->postNewReg(); - if ($this->client->getLastCode() != 201) { + if ($this->client->getLastCode() == 409) { + $this->log('The key was already registered. Using existing account.'); + } else if ($this->client->getLastCode() == 201) { + $this->log('New account registered.'); + } else { throw new \RuntimeException("Account not initialized, probably due to rate limiting. Whole response: " . json_encode($response)); } + $accountUrl=$this->client->getLastLocation(); + + $this->log('Accepting lets encrypt Terms of Service'); + $this->license = $this->client->getAgreementURL(); - // Terms of Servce are optional according to ACME specs; if no ToS are presented, no need to update registration + // Terms of Service are optional according to ACME specs; if no ToS are presented, no need to update registration if (!empty($this->license)) { - $this->postRegAgreement(parse_url($this->client->getLastLocation(), PHP_URL_PATH)); + $response = $this->postRegAgreement(parse_url($accountUrl, PHP_URL_PATH)); + if ($this->client->getLastCode() != 202) { + throw new \RuntimeException("Terms of Service not accepted. Whole response: " . json_encode($response)); + } } - $this->log('New account certificate registered'); - } else { - $this->log('Account already registered. Continuing.'); + $leregistered=1; + $this->setLeRegisteredState($leregistered); // Account registered + $this->log('Lets encrypt Terms of Service accepted'); } + } /** @@ -136,11 +169,17 @@ class lescript ) )); + if ($this->client->getLastCode() == 403) { + $this->log("Got status 403 - setting LE status to unregistered."); + $this->setLeRegisteredState(0); + throw new RuntimeException("Got 'unauthorized' response - we need to re-register at next run. Whole response: " . json_encode($response)); + } + // if response is not an array but a string, it's most likely a server-error, e.g. // ErrorAn error occurred while processing your request. //

Reference #179.d8be1402.1458059103.3613c4db if (! is_array($response)) { - throw new RuntimeException("Invalid response from LE for domain $domain. Whole response: " . $response); + throw new RuntimeException("Invalid response from LE for domain $domain. Whole response: " . json_encode($response)); } if (! array_key_exists('challenges', $response)) { @@ -309,6 +348,21 @@ class lescript ); } + private function setLeRegisteredState($state) + { + if ($this->isLeProduction) { + if ($this->isFroxlorVhost) { + Settings::Set('system.leregistered', $state); + } else { + $upd_stmt = Database::prepare("UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET `leregistered` = :registered " . "WHERE `customerid` = :customerid;"); + Database::pexecute($upd_stmt, array( + 'registered' => $state, + 'customerid' => $this->customerId + )); + } + } + } + private function parsePemFromBody($body) { $pem = chunk_split(base64_encode($body), 64, "\n"); @@ -537,10 +591,46 @@ class Client return $matches[1]; } + public function getAgreementURLFromLastResponse() + { + if (preg_match_all('~Link: <(.+)>;rel="terms-of-service"~', $this->lastHeader, $matches)) { + return $matches[1][0]; + } + return ""; + } + public function getAgreementURLFromDirectory() + { + // FIXME: Current license should be found in /directory but LE does not implement this yet + // $this->curl('GET', '/directory'); + return ""; + } + public function getAgreementURLFromTermsUrl() + { + $this->curl('GET', '/terms'); + if (preg_match_all('~Location: (.+)~', $this->lastHeader, $matches)) { + return trim($matches[1][0]); + } + return ""; + } + public function getAgreementURL() { - preg_match_all('~Link: <(.+)>;rel="terms-of-service"~', $this->lastHeader, $matches); - return $matches[1][0]; + // 1. check the header of the last response + $license=$this->getAgreementURLFromLastResponse(); + if (!empty($license)) return $license; + + // 2. query directory for license + $license=$this->getAgreementURLFromDirectory(); + if (!empty($license)) return $license; + + // 3. query /terms endpoint (not ACME standard but implemented by let's enrypt) + $license=$this->getAgreementURLFromTermsUrl(); + if (!empty($license)) return $license; + + // Fallback: use latest known license. This is only valid for let's encrypt and should be removed as soon as there is an official + // ACME-endpoint to get the current ToS + return "xxxhttps://letsencrypt.org/documents/LE-SA-v1.1.1-August-1-2016.pdf]"; + // return ""; } } diff --git a/lib/version.inc.php b/lib/version.inc.php index 915532b7..056ce6e4 100644 --- a/lib/version.inc.php +++ b/lib/version.inc.php @@ -19,7 +19,7 @@ $version = '0.9.38.3'; // Database version (YYYYMMDDC where C is a daily counter) -$dbversion = '201611180'; +$dbversion = '201611241'; // Distribution branding-tag (used for Debian etc.) $branding = ''; diff --git a/scripts/jobs/cron_letsencrypt.php b/scripts/jobs/cron_letsencrypt.php index 2101fcca..126d7dcd 100644 --- a/scripts/jobs/cron_letsencrypt.php +++ b/scripts/jobs/cron_letsencrypt.php @@ -43,6 +43,7 @@ $certificates_stmt = Database::query(" dom.`ssl_redirect`, cust.`leprivatekey`, cust.`lepublickey`, + cust.`leregistered`, cust.`customerid`, cust.`loginname` FROM @@ -103,6 +104,7 @@ if (Settings::Get('system.le_froxlor_enabled') == '1') { 'documentroot' => FROXLOR_INSTALL_DIR, '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, From 559bd6d892dff34777d60164ac7e392403c2174e Mon Sep 17 00:00:00 2001 From: Michael Wyraz Date: Fri, 25 Nov 2016 10:52:50 +0100 Subject: [PATCH 0250/1335] Better db update --- .../updates/froxlor/0.9/update_0.9.inc.php | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index 3fd04142..d6288caf 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -3549,25 +3549,12 @@ if (isFroxlorVersion('0.9.38.2')) { if (isDatabaseVersion('201611180')) { - showUpdateStep("Adding field to reflect let's-encrypt registration status"); + showUpdateStep("Adding field for let's-encrypt registration status"); Database::query("ALTER TABLE `".TABLE_PANEL_CUSTOMERS."` add `leregistered` TINYINT(1) NOT NULL DEFAULT 0;"); lastStepStatus(0); - updateToDbVersion('201611240'); -} - -if (isDatabaseVersion('201611240')) { - - showUpdateStep("Adding new setting to reflect let's-encrypt registration status"); - $stmt = Database::prepare(" - INSERT INTO `" . TABLE_PANEL_SETTINGS . "` SET - `settinggroup` = 'system', - `varname` = :varname, - `value` = :value"); - Database::pexecute($stmt, array( - 'varname' => 'leregistered', - 'value' => '0' - )); + showUpdateStep("Adding system setting for let's-encrypt registration status"); + Settings::AddNew('system.leregistered', '0'); lastStepStatus(0); updateToDbVersion('201611241'); From 301dadaa023dafb2191cf5d49d4fdf05ced1db0e Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sun, 27 Nov 2016 10:46:50 +0100 Subject: [PATCH 0251/1335] fix global hsts-includeSubdomain setting, thx to iam Signed-off-by: Michael Kaufmann (d00p) --- actions/admin/settings/131.ssl.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/actions/admin/settings/131.ssl.php b/actions/admin/settings/131.ssl.php index dc03f926..126e5559 100644 --- a/actions/admin/settings/131.ssl.php +++ b/actions/admin/settings/131.ssl.php @@ -164,10 +164,10 @@ return array( 'default' => 0, 'save_method' => 'storeSettingField' ), - 'system_hsts_incsub' => array( + 'system_hsts_sub' => array( 'label' => $lng['admin']['domain_hsts_incsub'], 'settinggroup' => 'system', - 'varname' => 'hsts_incsub', + 'varname' => 'hsts_sub', 'type' => 'bool', 'default' => false, 'save_method' => 'storeSettingField' From c00abc3b927dea5404c5626675a2712dee21b464 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sun, 27 Nov 2016 11:40:33 +0100 Subject: [PATCH 0252/1335] move froxlor hsts settings to 'froxlor vhost settings' to make clear it's only for froxlor anbd not a system-wide default for all domains; fix superfluous english text from german language file Signed-off-by: Michael Kaufmann (d00p) --- actions/admin/settings/122.froxlorvhost.php | 31 ++++++++++++++++++++- actions/admin/settings/131.ssl.php | 28 +------------------ lng/german.lng.php | 2 +- 3 files changed, 32 insertions(+), 29 deletions(-) diff --git a/actions/admin/settings/122.froxlorvhost.php b/actions/admin/settings/122.froxlorvhost.php index 75e138b4..6175bb73 100644 --- a/actions/admin/settings/122.froxlorvhost.php +++ b/actions/admin/settings/122.froxlorvhost.php @@ -49,7 +49,36 @@ return array( 'type' => 'bool', 'default' => false, 'save_method' => 'storeSettingField', - 'visible' => Settings::Get('system.leenabled') + 'visible' => Settings::Get('system.use_ssl') + ), + 'system_hsts_maxage' => array( + 'label' => $lng['admin']['domain_hsts_maxage'], + 'settinggroup' => 'system', + 'varname' => 'hsts_maxage', + 'type' => 'int', + 'int_min' => 0, + 'int_max' => 94608000, // 3-years + 'default' => 0, + 'save_method' => 'storeSettingField', + 'visible' => Settings::Get('system.use_ssl') + ), + 'system_hsts_incsub' => array( + 'label' => $lng['admin']['domain_hsts_incsub'], + 'settinggroup' => 'system', + 'varname' => 'hsts_incsub', + 'type' => 'bool', + 'default' => false, + 'save_method' => 'storeSettingField', + 'visible' => Settings::Get('system.use_ssl') + ), + 'system_hsts_preload' => array( + 'label' => $lng['admin']['domain_hsts_preload'], + 'settinggroup' => 'system', + 'varname' => 'hsts_preload', + 'type' => 'bool', + 'default' => false, + 'save_method' => 'storeSettingField', + 'visible' => Settings::Get('system.use_ssl') ), /** * FCGID diff --git a/actions/admin/settings/131.ssl.php b/actions/admin/settings/131.ssl.php index 126e5559..56c27cd5 100644 --- a/actions/admin/settings/131.ssl.php +++ b/actions/admin/settings/131.ssl.php @@ -153,33 +153,7 @@ return array( 'type' => 'bool', 'default' => false, 'save_method' => 'storeSettingField' - ), - 'system_hsts_maxage' => array( - 'label' => $lng['admin']['domain_hsts_maxage'], - 'settinggroup' => 'system', - 'varname' => 'hsts_maxage', - 'type' => 'int', - 'int_min' => 0, - 'int_max' => 94608000, // 3-years - 'default' => 0, - 'save_method' => 'storeSettingField' - ), - 'system_hsts_sub' => array( - 'label' => $lng['admin']['domain_hsts_incsub'], - 'settinggroup' => 'system', - 'varname' => 'hsts_sub', - 'type' => 'bool', - 'default' => false, - 'save_method' => 'storeSettingField' - ), - 'system_hsts_preload' => array( - 'label' => $lng['admin']['domain_hsts_preload'], - 'settinggroup' => 'system', - 'varname' => 'hsts_preload', - 'type' => 'bool', - 'default' => false, - 'save_method' => 'storeSettingField' - ), + ) ) ) ) diff --git a/lng/german.lng.php b/lng/german.lng.php index 0af16748..490d2651 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -1710,6 +1710,6 @@ $lng['admin']['webserversettings_ssl'] = 'Webserver SSL-Einstellungen'; $lng['admin']['domain_hsts_maxage']['title'] = 'HTTP Strict Transport Security (HSTS)'; $lng['admin']['domain_hsts_maxage']['description'] = '"max-age" Wert für den Strict-Transport-Security Header
Der Wert 0 deaktiviert HSTS für diese Domain. Meist wird der Wert 31536000 gerne genutzt (ein Jahr).'; $lng['admin']['domain_hsts_incsub']['title'] = 'Inkludiere HSTS für jede Subdomain'; -$lng['admin']['domain_hsts_incsub']['description'] = 'Die optionale "includeSubDomains" Direktive, wenn vorhanden, signalisiert dem UA, dass die HSTS that the HSTS Regel für diese Domain und auch jede Subdomain dieser gilt.'; +$lng['admin']['domain_hsts_incsub']['description'] = 'Die optionale "includeSubDomains" Direktive, wenn vorhanden, signalisiert dem UA, dass die HSTS Regel für diese Domain und auch jede Subdomain dieser gilt.'; $lng['admin']['domain_hsts_preload']['title'] = 'Füge Domain in die HSTS preload Liste hinzu'; $lng['admin']['domain_hsts_preload']['description'] = 'Wenn die Domain in die HSTS preload Liste, verwaltet von Chrome (und genutzt von Firefox und Safari), hinzugefügt werden soll, dann aktiviere diese Einstellung.
Die preload-Direktive zu senden kann PERMANTENTE KONSEQUENZEN haben und dazu führen, dass Benutzer auf diese Domain und auch Subdomains nicht zugreifen können.
Beachte Details unter hstspreload.appspot.com/#removal bevor ein Header mit "preload" gesendet wird.'; From 56276a19d137e8310a16c32d7b6156451b9c6b98 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sun, 27 Nov 2016 12:12:22 +0100 Subject: [PATCH 0253/1335] set version to 0.9.38.4 for upcoming bugfix release Signed-off-by: Michael Kaufmann (d00p) --- install/froxlor.sql | 2 +- install/updates/froxlor/0.9/update_0.9.inc.php | 6 ++++++ lib/version.inc.php | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/install/froxlor.sql b/install/froxlor.sql index 24bbb15e..b1962ab5 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -577,7 +577,7 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES ('panel', 'password_special_char_required', '0'), ('panel', 'password_special_char', '!?<>§$%+#=@'), ('panel', 'customer_hide_options', ''), - ('panel', 'version', '0.9.38.3'), + ('panel', 'version', '0.9.38.4'), ('panel', 'db_version', '201611180'); diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index 8fb02c7a..72c39d89 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -3546,3 +3546,9 @@ if (isFroxlorVersion('0.9.38.2')) { showUpdateStep("Updating from 0.9.38.2 to 0.9.38.3", false); updateToVersion('0.9.38.3'); } + +if (isFroxlorVersion('0.9.38.3')) { + + showUpdateStep("Updating from 0.9.38.3 to 0.9.38.4", false); + updateToVersion('0.9.38.4'); +} diff --git a/lib/version.inc.php b/lib/version.inc.php index 915532b7..6e9c41fb 100644 --- a/lib/version.inc.php +++ b/lib/version.inc.php @@ -16,7 +16,7 @@ */ // Main version variable -$version = '0.9.38.3'; +$version = '0.9.38.4'; // Database version (YYYYMMDDC where C is a daily counter) $dbversion = '201611180'; From 7b6bbcec48a40602fbaf9e248420a9deab4a7d93 Mon Sep 17 00:00:00 2001 From: micw Date: Sun, 27 Nov 2016 12:45:06 +0100 Subject: [PATCH 0254/1335] Fixed default LE license URL (removed testing stuff) --- lib/classes/ssl/class.lescript.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/classes/ssl/class.lescript.php b/lib/classes/ssl/class.lescript.php index 1d3aa5bb..f0d7c11f 100644 --- a/lib/classes/ssl/class.lescript.php +++ b/lib/classes/ssl/class.lescript.php @@ -629,7 +629,7 @@ class Client // Fallback: use latest known license. This is only valid for let's encrypt and should be removed as soon as there is an official // ACME-endpoint to get the current ToS - return "xxxhttps://letsencrypt.org/documents/LE-SA-v1.1.1-August-1-2016.pdf]"; + return "https://letsencrypt.org/documents/LE-SA-v1.1.1-August-1-2016.pdf"; // return ""; } From 1b18ec45be83088085f0834aa29f8d7f1c4a712b Mon Sep 17 00:00:00 2001 From: Janos Muzsi Date: Sun, 27 Nov 2016 23:18:23 +0100 Subject: [PATCH 0255/1335] correcting lighttpd settings --- scripts/jobs/cron_tasks.inc.http.20.lighttpd.php | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php b/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php index 9ca21da1..5b7fc780 100644 --- a/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php +++ b/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php @@ -435,13 +435,11 @@ class lighttpd extends HttpConfigBase // Get domain's redirect code $code = getDomainRedirectCode($domain['id'], '301'); - - $vhost_content .= ' $HTTP["host"] =~ "^(.*)$" {'. "\n"; - $vhost_content .= ' url.redirect-code = ' . $code. "\n"; - $vhost_content .= ' url.redirect = (' . "\n"; - $vhost_content .= ' "^/(.*)$" => "' . $uri . '$1"' . "\n"; - $vhost_content .= ' )' . "\n"; - $vhost_content .= ' }' . "\n"; + + $vhost_content .= ' url.redirect-code = ' . $code. "\n"; + $vhost_content .= ' url.redirect = (' . "\n"; + $vhost_content .= ' "^/(.*)$" => "' . $uri . '$1"' . "\n"; + $vhost_content .= ' )' . "\n"; } else { mkDirWithCorrectOwnership($domain['customerroot'], $domain['documentroot'], $domain['guid'], $domain['guid'], true, true); From 225539d2e7cf61f6e7c0440c424c9d6bb201ae30 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 29 Nov 2016 09:34:33 +0100 Subject: [PATCH 0256/1335] show ssl-related settings only if customer has ssl-ip assigned (not yet checked per domain); fix hsts-includeSubdomain setting for fresh installations Signed-off-by: Michael Kaufmann (d00p) --- customer_domains.php | 16 +++++++++++++--- install/froxlor.sql | 2 +- .../customer/domains/formfield.domains_add.php | 8 ++------ .../customer/domains/formfield.domains_edit.php | 8 ++------ 4 files changed, 18 insertions(+), 16 deletions(-) diff --git a/customer_domains.php b/customer_domains.php index 06471cb8..cc2a637e 100644 --- a/customer_domains.php +++ b/customer_domains.php @@ -517,7 +517,12 @@ if ($page == 'overview') { // check if we at least have one ssl-ip/port, #1179 $ssl_ipsandports = ''; - $ssl_ip_stmt = Database::prepare("SELECT COUNT(*) as countSSL FROM `panel_ipsandports` WHERE `ssl`='1'"); + $ssl_ip_stmt = Database::prepare(" + SELECT COUNT(*) as countSSL + FROM `".TABLE_PANEL_IPSANDPORTS."` pip + LEFT JOIN `".TABLE_DOMAINTOIP."` dti ON dti.id_ipandports = pip.id + WHERE pip.`ssl`='1' + "); Database::pexecute($ssl_ip_stmt); $resultX = $ssl_ip_stmt->fetch(PDO::FETCH_ASSOC); if (isset($resultX['countSSL']) && (int)$resultX['countSSL'] > 0) { @@ -797,8 +802,13 @@ if ($page == 'overview') { // check if we at least have one ssl-ip/port, #1179 $ssl_ipsandports = ''; - $ssl_ip_stmt = Database::prepare("SELECT COUNT(*) as countSSL FROM `panel_ipsandports` WHERE `ssl`='1'"); - Database::pexecute($ssl_ip_stmt); + $ssl_ip_stmt = Database::prepare(" + SELECT COUNT(*) as countSSL + FROM `".TABLE_PANEL_IPSANDPORTS."` pip + LEFT JOIN `".TABLE_DOMAINTOIP."` dti ON dti.id_ipandports = pip.id + WHERE `dti`.`id_domain` = :id_domain AND pip.`ssl`='1' + "); + Database::pexecute($ssl_ip_stmt, array("id_domain" => $result['id'])); $resultX = $ssl_ip_stmt->fetch(PDO::FETCH_ASSOC); if (isset($resultX['countSSL']) && (int)$resultX['countSSL'] > 0) { $ssl_ipsandports = 'notempty'; diff --git a/install/froxlor.sql b/install/froxlor.sql index b1962ab5..947e6b3a 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -544,7 +544,7 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES ('system', 'mail_smtp_user', ''), ('system', 'mail_smtp_passwd', ''), ('system', 'hsts_maxage', '0'), - ('system', 'hsts_sub', '0'), + ('system', 'hsts_incsub', '0'), ('system', 'hsts_preload', '0'), ('panel', 'decimal_places', '4'), ('panel', 'adminmail', 'admin@SERVERNAME'), diff --git a/lib/formfields/customer/domains/formfield.domains_add.php b/lib/formfields/customer/domains/formfield.domains_add.php index 1371e579..238adfff 100644 --- a/lib/formfields/customer/domains/formfield.domains_add.php +++ b/lib/formfields/customer/domains/formfield.domains_add.php @@ -76,10 +76,9 @@ return array( 'section_bssl' => array( 'title' => $lng['admin']['webserversettings_ssl'], 'image' => 'icons/domain_add.png', - 'visible' => Settings::Get('system.use_ssl') == '1' ? true : false, + 'visible' => Settings::Get('system.use_ssl') == '1' ? ($ssl_ipsandports != '' ? true : false) : false, 'fields' => array( 'ssl_redirect' => array( - 'visible' => ($ssl_ipsandports != '' ? true : false), 'label' => $lng['domains']['ssl_redirect']['title'], 'desc' => $lng['domains']['ssl_redirect']['description'], 'type' => 'checkbox', @@ -89,7 +88,7 @@ return array( 'value' => array() ), 'letsencrypt' => array( - 'visible' => (Settings::Get('system.leenabled') == '1' ? ($ssl_ipsandports != '' ? true : false) : false), + 'visible' => (Settings::Get('system.leenabled') == '1' ? true : false), 'label' => $lng['customer']['letsencrypt']['title'], 'desc' => $lng['customer']['letsencrypt']['description'], 'type' => 'checkbox', @@ -99,7 +98,6 @@ return array( 'value' => array() ), 'hsts_maxage' => array( - 'visible' => ($ssl_ipsandports != '' ? true : false), 'label' => $lng['admin']['domain_hsts_maxage']['title'], 'desc' => $lng['admin']['domain_hsts_maxage']['description'], 'type' => 'int', @@ -108,7 +106,6 @@ return array( 'value' => 0 ), 'hsts_sub' => array( - 'visible' => ($ssl_ipsandports != '' ? true : false), 'label' => $lng['admin']['domain_hsts_incsub']['title'], 'desc' => $lng['admin']['domain_hsts_incsub']['description'], 'type' => 'checkbox', @@ -118,7 +115,6 @@ return array( 'value' => array() ), 'hsts_preload' => array( - 'visible' => ($ssl_ipsandports != '' ? true : false), 'label' => $lng['admin']['domain_hsts_preload']['title'], 'desc' => $lng['admin']['domain_hsts_preload']['description'], 'type' => 'checkbox', diff --git a/lib/formfields/customer/domains/formfield.domains_edit.php b/lib/formfields/customer/domains/formfield.domains_edit.php index 94bab096..6ac6d186 100644 --- a/lib/formfields/customer/domains/formfield.domains_edit.php +++ b/lib/formfields/customer/domains/formfield.domains_edit.php @@ -87,10 +87,9 @@ return array( 'section_bssl' => array( 'title' => $lng['admin']['webserversettings_ssl'], 'image' => 'icons/domain_edit.png', - 'visible' => Settings::Get('system.use_ssl') == '1' ? true : false, + 'visible' => Settings::Get('system.use_ssl') == '1' ? ($ssl_ipsandports != '' ? (domainHasSslIpPort($result['id']) ? true : false) : false) : false, 'fields' => array( 'ssl_redirect' => array( - 'visible' => (Settings::Get('system.use_ssl') == '1' ? ($ssl_ipsandports != '' ? (domainHasSslIpPort($result['id']) ? true : false) : false) : false), 'label' => $lng['domains']['ssl_redirect']['title'], 'desc' => $lng['domains']['ssl_redirect']['description'] . ($result['temporary_ssl_redirect'] > 1 ? $lng['domains']['ssl_redirect_temporarilydisabled'] : ''), 'type' => 'checkbox', @@ -100,7 +99,7 @@ return array( 'value' => array($result['ssl_redirect']) ), 'letsencrypt' => array( - 'visible' => (Settings::Get('system.use_ssl') == '1' ? (Settings::Get('system.leenabled') == '1' ? ($ssl_ipsandports != '' ? (domainHasSslIpPort($result['id']) ? true : false) : false) : false) : false), + 'visible' => Settings::Get('system.leenabled') == '1' ? true : false, 'label' => $lng['customer']['letsencrypt']['title'], 'desc' => $lng['customer']['letsencrypt']['description'], 'type' => 'checkbox', @@ -110,7 +109,6 @@ return array( 'value' => array($result['letsencrypt']) ), 'hsts_maxage' => array( - 'visible' => ($ssl_ipsandports != '' ? true : false), 'label' => $lng['admin']['domain_hsts_maxage']['title'], 'desc' => $lng['admin']['domain_hsts_maxage']['description'], 'type' => 'int', @@ -119,7 +117,6 @@ return array( 'value' => $result['hsts'] ), 'hsts_sub' => array( - 'visible' => ($ssl_ipsandports != '' ? true : false), 'label' => $lng['admin']['domain_hsts_incsub']['title'], 'desc' => $lng['admin']['domain_hsts_incsub']['description'], 'type' => 'checkbox', @@ -129,7 +126,6 @@ return array( 'value' => array($result['hsts_sub']) ), 'hsts_preload' => array( - 'visible' => ($ssl_ipsandports != '' ? true : false), 'label' => $lng['admin']['domain_hsts_preload']['title'], 'desc' => $lng['admin']['domain_hsts_preload']['description'], 'type' => 'checkbox', From 001786dd9719cd117225c4aebc1bb95c1e9bafb7 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Fri, 9 Dec 2016 13:33:57 +0100 Subject: [PATCH 0257/1335] fix incorrect User-Agent header in let's encrypt class, fixes #1683 Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/ssl/class.lescript.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/classes/ssl/class.lescript.php b/lib/classes/ssl/class.lescript.php index dc8ec83b..82f3056f 100644 --- a/lib/classes/ssl/class.lescript.php +++ b/lib/classes/ssl/class.lescript.php @@ -188,7 +188,7 @@ class lescript $this->log("Token for $domain saved at $tokenPath and should be available at $uri"); // simple self check - $selfcheckContextOptions = array('http' => array('header' => "User Agent: Froxlor/".$this->version)); + $selfcheckContextOptions = array('http' => array('header' => "User-Agent: Froxlor/".$this->version)); $selfcheckContext = stream_context_create($selfcheckContextOptions); if ($payload !== trim(@file_get_contents($uri, false, $selfcheckContext))) { $errmsg = json_encode(error_get_last()); From 18514f018069c1a5f9b3b812ff775a82fcf702e6 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sat, 10 Dec 2016 21:43:28 +0100 Subject: [PATCH 0258/1335] fix undefined array when processing specialsettings, fixes #1684 Signed-off-by: Michael Kaufmann (d00p) --- scripts/jobs/cron_tasks.inc.http.10.apache.php | 9 +++++++++ .../jobs/cron_tasks.inc.http.20.lighttpd.php | 18 ++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/scripts/jobs/cron_tasks.inc.http.10.apache.php b/scripts/jobs/cron_tasks.inc.http.10.apache.php index b6a9697a..1f45d934 100644 --- a/scripts/jobs/cron_tasks.inc.http.10.apache.php +++ b/scripts/jobs/cron_tasks.inc.http.10.apache.php @@ -344,6 +344,15 @@ class apache extends HttpConfigBase ); } } // end of ssl-redirect check + else + { + // fallback of froxlor domain-data for processSpecialConfigTemplate() + $domain = array( + 'domain' => Settings::Get('system.hostname'), + 'loginname' => 'froxlor.panel', + 'documentroot' => $mypath + ); + } /** * dirprotection, see #72 diff --git a/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php b/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php index a1869a42..aa06b527 100644 --- a/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php +++ b/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php @@ -162,7 +162,25 @@ class lighttpd extends HttpConfigBase $this->lighttpd_data[$vhost_filename] .= "\t" . ')' . "\n"; $this->lighttpd_data[$vhost_filename] .= "\t" . ')' . "\n"; $this->lighttpd_data[$vhost_filename] .= ' )' . "\n"; + } else { + $domain = array( + 'id' => 'none', + 'domain' => Settings::Get('system.hostname'), + 'adminid' => 1, /* first admin-user (superadmin) */ + 'guid' => Settings::Get('system.httpuser'), + 'openbasedir' => 0, + 'email' => Settings::Get('panel.adminmail'), + 'loginname' => 'froxlor.panel', + 'documentroot' => $mypath + ); } + } else { + // fallback of froxlor domain-data for processSpecialConfigTemplate() + $domain = array( + 'domain' => Settings::Get('system.hostname'), + 'loginname' => 'froxlor.panel', + 'documentroot' => $mypath + ); } if ($row_ipsandports['specialsettings'] != '') { From 3a8996aee2125e85872aff2e5b29381dd94258db Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sun, 11 Dec 2016 08:29:54 +0100 Subject: [PATCH 0259/1335] add missing fields that are only added via update Signed-off-by: Michael Kaufmann (d00p) --- install/froxlor.sql | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/install/froxlor.sql b/install/froxlor.sql index 8856efbf..ea9da93f 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -195,8 +195,9 @@ CREATE TABLE `panel_customers` ( `theme` varchar(255) NOT NULL default 'Sparkle', `custom_notes` text, `custom_notes_show` tinyint(1) NOT NULL default '0', - `lepublickey` mediumtext DEFAULT NULL, - `leprivatekey` mediumtext DEFAULT NULL, + `lepublickey` mediumtext default NULL, + `leprivatekey` mediumtext default NULL, + `leregistered` tinyint(1) NOT NULL default '0' PRIMARY KEY (`customerid`), UNIQUE KEY `loginname` (`loginname`) ) ENGINE=MyISAM CHARSET=utf8 COLLATE=utf8_general_ci; @@ -547,6 +548,7 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES ('system', 'hsts_maxage', '0'), ('system', 'hsts_incsub', '0'), ('system', 'hsts_preload', '0'), + ('system', 'leregistered', '0'), ('panel', 'decimal_places', '4'), ('panel', 'adminmail', 'admin@SERVERNAME'), ('panel', 'phpmyadmin_url', ''), From 9907775c0d6e62babbe762a641185bfe600191bb Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sun, 11 Dec 2016 09:43:05 +0100 Subject: [PATCH 0260/1335] fix installation sql Signed-off-by: Michael Kaufmann (d00p) --- install/froxlor.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/froxlor.sql b/install/froxlor.sql index 94a3c5ce..5c75be4f 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -197,7 +197,7 @@ CREATE TABLE `panel_customers` ( `custom_notes_show` tinyint(1) NOT NULL default '0', `lepublickey` mediumtext default NULL, `leprivatekey` mediumtext default NULL, - `leregistered` tinyint(1) NOT NULL default '0' + `leregistered` tinyint(1) NOT NULL default '0', PRIMARY KEY (`customerid`), UNIQUE KEY `loginname` (`loginname`) ) ENGINE=MyISAM CHARSET=utf8 COLLATE=utf8_general_ci; From 5ed0c3f2f3b2e231d7cbdfa3a64d4947f601cafb Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sun, 11 Dec 2016 09:50:22 +0100 Subject: [PATCH 0261/1335] code-formatting and put phpenabled flag on a better position Signed-off-by: Michael Kaufmann (d00p) --- .../admin/domains/formfield.domains_add.php | 113 +++++++++---- .../admin/domains/formfield.domains_edit.php | 150 +++++++++++++----- 2 files changed, 187 insertions(+), 76 deletions(-) diff --git a/lib/formfields/admin/domains/formfield.domains_add.php b/lib/formfields/admin/domains/formfield.domains_add.php index 97f79ee7..8482123e 100644 --- a/lib/formfields/admin/domains/formfield.domains_add.php +++ b/lib/formfields/admin/domains/formfield.domains_add.php @@ -14,7 +14,6 @@ * @package Formfields * */ - return array( 'domain_add' => array( 'title' => $lng['admin']['domain_add'], @@ -27,20 +26,20 @@ return array( 'domain' => array( 'label' => 'Domain', 'type' => 'text', - 'mandatory' => true, + 'mandatory' => true ), 'customerid' => array( 'label' => $lng['admin']['customer'], 'type' => 'select', 'select_var' => $customers, - 'mandatory' => true, + 'mandatory' => true ), 'adminid' => array( 'visible' => ($userinfo['customers_see_all'] == '1' ? true : false), 'label' => $lng['admin']['admin'], 'type' => 'select', 'select_var' => $admins, - 'mandatory' => true, + 'mandatory' => true ), 'alias' => array( 'label' => $lng['domains']['aliasdomain'], @@ -58,9 +57,14 @@ return array( 'desc' => $lng['admin']['domain_editable']['desc'], 'type' => 'checkbox', 'values' => array( - array ('label' => $lng['panel']['yes'], 'value' => '1') + array( + 'label' => $lng['panel']['yes'], + 'value' => '1' + ) ), - 'value' => array('1') + 'value' => array( + '1' + ) ), 'add_date' => array( 'label' => $lng['domains']['add_date'], @@ -112,7 +116,10 @@ return array( 'desc' => $lng['admin']['speciallogfile']['description'], 'type' => 'checkbox', 'values' => array( - array ('label' => $lng['panel']['yes'], 'value' => '1') + array( + 'label' => $lng['panel']['yes'], + 'value' => '1' + ) ), 'value' => array() ), @@ -146,7 +153,10 @@ return array( 'desc' => $lng['domains']['ssl_redirect']['description'], 'type' => 'checkbox', 'values' => array( - array ('label' => $lng['panel']['yes'], 'value' => '1') + array( + 'label' => $lng['panel']['yes'], + 'value' => '1' + ) ), 'value' => array() ), @@ -156,7 +166,10 @@ return array( 'desc' => $lng['admin']['letsencrypt']['description'], 'type' => 'checkbox', 'values' => array( - array ('label' => $lng['panel']['yes'], 'value' => '1') + array( + 'label' => $lng['panel']['yes'], + 'value' => '1' + ) ), 'value' => array() ), @@ -181,7 +194,10 @@ return array( 'desc' => $lng['admin']['domain_hsts_incsub']['description'], 'type' => 'checkbox', 'values' => array( - array ('label' => $lng['panel']['yes'], 'value' => '1') + array( + 'label' => $lng['panel']['yes'], + 'value' => '1' + ) ), 'value' => array() ), @@ -191,46 +207,59 @@ return array( 'desc' => $lng['admin']['domain_hsts_preload']['description'], 'type' => 'checkbox', 'values' => array( - array ('label' => $lng['panel']['yes'], 'value' => '1') + array( + 'label' => $lng['panel']['yes'], + 'value' => '1' + ) ), 'value' => array() - ), - ), + ) + ) ), 'section_c' => array( 'title' => $lng['admin']['phpserversettings'], 'image' => 'icons/domain_add.png', 'visible' => (($userinfo['change_serversettings'] == '1' || $userinfo['caneditphpsettings'] == '1') ? true : false), 'fields' => array( - 'phpenabled' => array( - 'label' => $lng['admin']['phpenabled'], - 'type' => 'checkbox', - 'values' => array( - array ('label' => $lng['panel']['yes'], 'value' => '1') - ), - 'value' => array('1') - ), 'openbasedir' => array( 'label' => 'OpenBasedir', 'type' => 'checkbox', 'values' => array( - array ('label' => $lng['panel']['yes'], 'value' => '1') + array( + 'label' => $lng['panel']['yes'], + 'value' => '1' + ) ), - 'value' => array('1') + 'value' => array( + '1' + ) + ), + 'phpenabled' => array( + 'label' => $lng['admin']['phpenabled'], + 'type' => 'checkbox', + 'values' => array( + array( + 'label' => $lng['panel']['yes'], + 'value' => '1' + ) + ), + 'value' => array( + '1' + ) ), 'phpsettingid' => array( - 'visible' => (((int)Settings::Get('system.mod_fcgid') == 1 || (int)Settings::Get('phpfpm.enabled') == 1) ? true : false), + 'visible' => (((int) Settings::Get('system.mod_fcgid') == 1 || (int) Settings::Get('phpfpm.enabled') == 1) ? true : false), 'label' => $lng['admin']['phpsettings']['title'], 'type' => 'select', 'select_var' => $phpconfigs ), 'mod_fcgid_starter' => array( - 'visible' => ((int)Settings::Get('system.mod_fcgid') == 1 ? true : false), + 'visible' => ((int) Settings::Get('system.mod_fcgid') == 1 ? true : false), 'label' => $lng['admin']['mod_fcgid_starter']['title'], 'type' => 'text' ), 'mod_fcgid_maxrequests' => array( - 'visible' => ((int)Settings::Get('system.mod_fcgid') == 1 ? true : false), + 'visible' => ((int) Settings::Get('system.mod_fcgid') == 1 ? true : false), 'label' => $lng['admin']['mod_fcgid_maxrequests']['title'], 'type' => 'text' ) @@ -245,9 +274,14 @@ return array( 'label' => 'Nameserver', 'type' => 'checkbox', 'values' => array( - array ('label' => $lng['panel']['yes'], 'value' => '1') + array( + 'label' => $lng['panel']['yes'], + 'value' => '1' + ) ), - 'value' => array('1') + 'value' => array( + '1' + ) ), 'zonefile' => array( 'label' => 'Zonefile', @@ -264,15 +298,23 @@ return array( 'label' => $lng['admin']['emaildomain'], 'type' => 'checkbox', 'values' => array( - array ('label' => $lng['panel']['yes'], 'value' => '1') + array( + 'label' => $lng['panel']['yes'], + 'value' => '1' + ) ), - 'value' => array('1') + 'value' => array( + '1' + ) ), 'email_only' => array( 'label' => $lng['admin']['email_only'], 'type' => 'checkbox', 'values' => array( - array ('label' => $lng['panel']['yes'], 'value' => '1') + array( + 'label' => $lng['panel']['yes'], + 'value' => '1' + ) ), 'value' => array() ), @@ -286,9 +328,14 @@ return array( 'label' => 'DomainKeys', 'type' => 'checkbox', 'values' => array( - array ('label' => $lng['panel']['yes'], 'value' => '1') + array( + 'label' => $lng['panel']['yes'], + 'value' => '1' + ) ), - 'value' => array('1') + 'value' => array( + '1' + ) ) ) ) diff --git a/lib/formfields/admin/domains/formfield.domains_edit.php b/lib/formfields/admin/domains/formfield.domains_edit.php index f3a8ab01..22a293cc 100644 --- a/lib/formfields/admin/domains/formfield.domains_edit.php +++ b/lib/formfields/admin/domains/formfield.domains_edit.php @@ -14,7 +14,6 @@ * @package Formfields * */ - return array( 'domain_edit' => array( 'title' => $lng['admin']['domain_edit'], @@ -28,14 +27,14 @@ return array( 'label' => 'Domain', 'type' => 'label', 'value' => $result['domain'], - 'mandatory' => true, + 'mandatory' => true ), 'customerid' => array( 'label' => $lng['admin']['customer'], 'type' => (Settings::Get('panel.allow_domain_change_customer') == '1' ? 'select' : 'label'), 'select_var' => (isset($customers) ? $customers : null), 'value' => (isset($result['customername']) ? $result['customername'] : null), - 'mandatory' => true, + 'mandatory' => true ), 'adminid' => array( 'visible' => ($userinfo['customers_see_all'] == '1' ? true : false), @@ -43,7 +42,7 @@ return array( 'type' => (Settings::Get('panel.allow_domain_change_admin') == '1' ? 'select' : 'label'), 'select_var' => (isset($admins) ? $admins : null), 'value' => (isset($result['adminname']) ? $result['adminname'] : null), - 'mandatory' => true, + 'mandatory' => true ), 'alias' => array( 'visible' => ($alias_check == '0' ? true : false), @@ -60,16 +59,21 @@ return array( 'associated_info' => array( 'label' => $lng['domains']['associated_with_domain'], 'type' => 'label', - 'value' => $subdomains.' '.$lng['customer']['subdomains'].', '.$alias_check.' '.$lng['domains']['aliasdomains'].', '.$emails.' '.$lng['customer']['emails'].', '.$email_accounts.' '.$lng['customer']['accounts'].', '.$email_forwarders.' '.$lng['customer']['forwarders'] + 'value' => $subdomains . ' ' . $lng['customer']['subdomains'] . ', ' . $alias_check . ' ' . $lng['domains']['aliasdomains'] . ', ' . $emails . ' ' . $lng['customer']['emails'] . ', ' . $email_accounts . ' ' . $lng['customer']['accounts'] . ', ' . $email_forwarders . ' ' . $lng['customer']['forwarders'] ), 'caneditdomain' => array( 'label' => $lng['admin']['domain_editable']['title'], 'desc' => $lng['admin']['domain_editable']['desc'], 'type' => 'checkbox', 'values' => array( - array ('label' => $lng['panel']['yes'], 'value' => '1') + array( + 'label' => $lng['panel']['yes'], + 'value' => '1' + ) ), - 'value' => array($result['caneditdomain']) + 'value' => array( + $result['caneditdomain'] + ) ), 'add_date' => array( 'label' => $lng['domains']['add_date'], @@ -124,9 +128,14 @@ return array( 'desc' => $lng['admin']['speciallogfile']['description'], 'type' => 'checkbox', 'values' => array( - array ('label' => $lng['panel']['yes'], 'value' => '1') + array( + 'label' => $lng['panel']['yes'], + 'value' => '1' + ) ), - 'value' => array($result['speciallogfile']) + 'value' => array( + $result['speciallogfile'] + ) ), 'specialsettings' => array( 'visible' => ($userinfo['change_serversettings'] == '1' ? true : false), @@ -144,9 +153,14 @@ return array( 'desc' => $lng['serversettings']['specialsettingsforsubdomains']['description'], 'type' => 'checkbox', 'values' => array( - array ('label' => $lng['panel']['yes'], 'value' => '1') + array( + 'label' => $lng['panel']['yes'], + 'value' => '1' + ) ), - 'value' => array('1') + 'value' => array( + '1' + ) ) ) ), @@ -169,9 +183,14 @@ return array( 'desc' => $lng['domains']['ssl_redirect']['description'] . ($result['temporary_ssl_redirect'] > 1 ? $lng['domains']['ssl_redirect_temporarilydisabled'] : ''), 'type' => 'checkbox', 'values' => array( - array ('label' => $lng['panel']['yes'], 'value' => '1') + array( + 'label' => $lng['panel']['yes'], + 'value' => '1' + ) ), - 'value' => array($result['ssl_redirect']) + 'value' => array( + $result['ssl_redirect'] + ) ), 'letsencrypt' => array( 'visible' => (Settings::Get('system.leenabled') == '1' ? ($ssl_ipsandports != '' ? true : false) : false), @@ -179,9 +198,14 @@ return array( 'desc' => $lng['admin']['letsencrypt']['description'], 'type' => 'checkbox', 'values' => array( - array ('label' => $lng['panel']['yes'], 'value' => '1') + array( + 'label' => $lng['panel']['yes'], + 'value' => '1' + ) ), - 'value' => array($result['letsencrypt']) + 'value' => array( + $result['letsencrypt'] + ) ), 'no_ssl_available_info' => array( 'visible' => ($ssl_ipsandports == '' ? true : false), @@ -204,9 +228,14 @@ return array( 'desc' => $lng['admin']['domain_hsts_incsub']['description'], 'type' => 'checkbox', 'values' => array( - array ('label' => $lng['panel']['yes'], 'value' => '1') + array( + 'label' => $lng['panel']['yes'], + 'value' => '1' + ) ), - 'value' => array($result['hsts_sub']) + 'value' => array( + $result['hsts_sub'] + ) ), 'hsts_preload' => array( 'visible' => ($ssl_ipsandports != '' ? true : false), @@ -214,10 +243,15 @@ return array( 'desc' => $lng['admin']['domain_hsts_preload']['description'], 'type' => 'checkbox', 'values' => array( - array ('label' => $lng['panel']['yes'], 'value' => '1') + array( + 'label' => $lng['panel']['yes'], + 'value' => '1' + ) ), - 'value' => array($result['hsts_preload']) - ), + 'value' => array( + $result['hsts_preload'] + ) + ) ) ), 'section_c' => array( @@ -225,39 +259,49 @@ return array( 'image' => 'icons/domain_edit.png', 'visible' => (($userinfo['change_serversettings'] == '1' || $userinfo['caneditphpsettings'] == '1') ? true : false), 'fields' => array( - 'phpenabled' => array( - 'label' => $lng['admin']['phpenabled'], - 'type' => 'checkbox', - 'values' => array( - array ('label' => $lng['panel']['yes'], 'value' => '1') - ), - 'value' => array($result['phpenabled']) - ), 'openbasedir' => array( 'label' => 'OpenBasedir', 'type' => 'checkbox', 'values' => array( - array ('label' => $lng['panel']['yes'], 'value' => '1') + array( + 'label' => $lng['panel']['yes'], + 'value' => '1' + ) ), - 'value' => array($result['openbasedir']) + 'value' => array( + $result['openbasedir'] + ) + ), + 'phpenabled' => array( + 'label' => $lng['admin']['phpenabled'], + 'type' => 'checkbox', + 'values' => array( + array( + 'label' => $lng['panel']['yes'], + 'value' => '1' + ) + ), + 'value' => array( + $result['phpenabled'] + ) ), 'phpsettingid' => array( - 'visible' => (((int)Settings::Get('system.mod_fcgid') == 1 || (int)Settings::Get('phpfpm.enabled') == 1) ? true : false), + 'visible' => (((int) Settings::Get('system.mod_fcgid') == 1 || (int) Settings::Get('phpfpm.enabled') == 1) ? true : false), 'label' => $lng['admin']['phpsettings']['title'], 'type' => 'select', 'select_var' => $phpconfigs ), 'mod_fcgid_starter' => array( - 'visible' => ((int)Settings::Get('system.mod_fcgid') == 1 ? true : false), + 'visible' => ((int) Settings::Get('system.mod_fcgid') == 1 ? true : false), 'label' => $lng['admin']['mod_fcgid_starter']['title'], 'type' => 'text', - 'value' => ((int)$result['mod_fcgid_starter'] != - 1 ? $result['mod_fcgid_starter'] : '') + 'value' => ((int) $result['mod_fcgid_starter'] != - 1 ? $result['mod_fcgid_starter'] : '') ), 'mod_fcgid_maxrequests' => array( - 'visible' => ((int)Settings::Get('system.mod_fcgid') == 1 ? true : false), + 'visible' => ((int) Settings::Get('system.mod_fcgid') == 1 ? true : false), 'label' => $lng['admin']['mod_fcgid_maxrequests']['title'], 'type' => 'text', - 'value' => ((int)$result['mod_fcgid_maxrequests'] != - 1 ? $result['mod_fcgid_maxrequests'] : '') + 'value' => ((int) $result['mod_fcgid_maxrequests'] != - 1 ? $result['mod_fcgid_maxrequests'] : '') ) ) ), @@ -270,9 +314,14 @@ return array( 'label' => 'Nameserver', 'type' => 'checkbox', 'values' => array( - array ('label' => $lng['panel']['yes'], 'value' => '1') + array( + 'label' => $lng['panel']['yes'], + 'value' => '1' + ) ), - 'value' => array($result['isbinddomain']) + 'value' => array( + $result['isbinddomain'] + ) ), 'zonefile' => array( 'label' => 'Zonefile', @@ -290,17 +339,27 @@ return array( 'label' => $lng['admin']['emaildomain'], 'type' => 'checkbox', 'values' => array( - array ('label' => $lng['panel']['yes'], 'value' => '1') + array( + 'label' => $lng['panel']['yes'], + 'value' => '1' + ) ), - 'value' => array($result['isemaildomain']) + 'value' => array( + $result['isemaildomain'] + ) ), 'email_only' => array( 'label' => $lng['admin']['email_only'], 'type' => 'checkbox', 'values' => array( - array ('label' => $lng['panel']['yes'], 'value' => '1') + array( + 'label' => $lng['panel']['yes'], + 'value' => '1' + ) ), - 'value' => array($result['email_only']) + 'value' => array( + $result['email_only'] + ) ), 'subcanemaildomain' => array( 'label' => $lng['admin']['subdomainforemail'], @@ -312,9 +371,14 @@ return array( 'label' => 'DomainKeys', 'type' => 'checkbox', 'values' => array( - array ('label' => $lng['panel']['yes'], 'value' => '1') + array( + 'label' => $lng['panel']['yes'], + 'value' => '1' + ) ), - 'value' => array($result['dkim']) + 'value' => array( + $result['dkim'] + ) ) ) ) From f9101f880b439f47a204ed48c5aead2b430e1a12 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Wed, 14 Dec 2016 09:54:49 +0100 Subject: [PATCH 0262/1335] use namserver IP's for axfr/pdns-config, also add placeholder AXFRSERVERS for axfr-servers setting; fixes #1691 Signed-off-by: Michael Kaufmann (d00p) --- admin_configfiles.php | 15 +++++++++++++++ lib/configfiles/gentoo.xml | 6 ++++-- lib/configfiles/jessie.xml | 6 ++++-- lib/configfiles/precise.xml | 6 ++++-- lib/configfiles/trusty.xml | 9 ++++++--- lib/configfiles/wheezy.xml | 6 ++++-- 6 files changed, 37 insertions(+), 11 deletions(-) diff --git a/admin_configfiles.php b/admin_configfiles.php index efbeba7a..c5e38a88 100644 --- a/admin_configfiles.php +++ b/admin_configfiles.php @@ -30,6 +30,19 @@ if ($userinfo['change_serversettings'] == '1') { $customer_tmpdir = Settings::Get('phpfpm.tmpdir'); } + // try to convert namserver hosts to ip's + $ns_ips = ""; + if (Settings::Get('system.nameservers') != '') { + $nameservers = explode(',', Settings::Get('system.nameservers')); + foreach ($nameservers as $nameserver) { + $nameserver = trim($nameserver); + $nameserver_ips = gethostbynamel($nameserver); + if (is_array($nameserver_ips) && count($nameserver_ips) > 0) { + $ns_ips .= implode(",", $nameserver_ips); + } + } + } + $replace_arr = Array( '' => $sql['user'], '' => 'MYSQL_PASSWORD', @@ -39,6 +52,8 @@ if ($userinfo['change_serversettings'] == '1') { '' => Settings::Get('system.hostname'), '' => Settings::Get('system.ipaddress'), '' => Settings::Get('system.nameservers'), + '' => $ns_ips, + '' => Settings::Get('system.axfrservers'), '' => Settings::Get('system.vmail_homedir'), '' => Settings::Get('system.vmail_uid'), '' => Settings::Get('system.vmail_gid'), diff --git a/lib/configfiles/gentoo.xml b/lib/configfiles/gentoo.xml index 69b4dd8d..cbcda2db 100644 --- a/lib/configfiles/gentoo.xml +++ b/lib/configfiles/gentoo.xml @@ -396,7 +396,8 @@ mail IN A ################################# # allow-axfr-ips Allow zonetransfers only to these subnets # -allow-axfr-ips=127.0.0.0/8,::1, +allow-axfr-ips=127.0.0.0/8,::1, +# add these entries to the list if any speficied: ################################# # allow-dnsupdate-from A global setting to allow DNS updates from these IP ranges. @@ -938,7 +939,8 @@ gmysql-password= ################################# # allow-axfr-ips Allow zonetransfers only to these subnets # -allow-axfr-ips=127.0.0.0/8,::1, +allow-axfr-ips=127.0.0.0/8,::1, +# add these entries to the list if any speficied: ################################# # allow-dnsupdate-from A global setting to allow DNS updates from these IP ranges. diff --git a/lib/configfiles/jessie.xml b/lib/configfiles/jessie.xml index 99322e4b..d6613020 100644 --- a/lib/configfiles/jessie.xml +++ b/lib/configfiles/jessie.xml @@ -380,7 +380,8 @@ exit "$RETVAL" ################################# # allow-axfr-ips Allow zonetransfers only to these subnets # -allow-axfr-ips=127.0.0.0/8,::1, +allow-axfr-ips=127.0.0.0/8,::1, +# add these entries to the list if any speficied: ################################# # allow-dnsupdate-from A global setting to allow DNS updates from these IP ranges. @@ -921,7 +922,8 @@ gmysql-password= ################################# # allow-axfr-ips Allow zonetransfers only to these subnets # -# allow-axfr-ips=127.0.0.0/8,::1, +# allow-axfr-ips=127.0.0.0/8,::1, +# add these entries to the list if any speficied: ################################# # allow-dnsupdate-from A global setting to allow DNS updates from these IP ranges. diff --git a/lib/configfiles/precise.xml b/lib/configfiles/precise.xml index 512415a4..c985a38e 100644 --- a/lib/configfiles/precise.xml +++ b/lib/configfiles/precise.xml @@ -346,7 +346,8 @@ exit "$RETVAL" +allow-axfr-ips=127.0.0.0/8,::1, +# add these entries to the list if any speficied: allow-recursion=127.0.0.1 config-dir=/etc/powerdns daemon=yes @@ -407,7 +408,8 @@ include-dir=/etc/powerdns/froxlor/ +allow-axfr-ips=127.0.0.0/8,::1, +# add these entries to the list if any speficied: #local-ipv6=YOUR_IPv6_(if_any) bind-config=named.conf bind-check-interval=180 diff --git a/lib/configfiles/trusty.xml b/lib/configfiles/trusty.xml index ba8bdc3f..7a7aeb8d 100644 --- a/lib/configfiles/trusty.xml +++ b/lib/configfiles/trusty.xml @@ -379,7 +379,8 @@ exit "$RETVAL" +allow-axfr-ips=127.0.0.0/8,::1, +# add these entries to the list if any speficied: allow-recursion=127.0.0.1 config-dir=/etc/powerdns daemon=yes @@ -417,7 +418,8 @@ gmysql-password= +allow-axfr-ips=127.0.0.0/8,::1, +# add these entries to the list if any speficied: allow-recursion=127.0.0.1 config-dir=/etc/powerdns daemon=yes @@ -441,7 +443,8 @@ include-dir=/etc/powerdns/froxlor/ +allow-axfr-ips=127.0.0.0/8,::1, +# add these entries to the list if any speficied: #local-ipv6=YOUR_IPv6_(if_any) bind-config=named.conf bind-check-interval=180 diff --git a/lib/configfiles/wheezy.xml b/lib/configfiles/wheezy.xml index 9d1d5c21..6e286017 100644 --- a/lib/configfiles/wheezy.xml +++ b/lib/configfiles/wheezy.xml @@ -424,7 +424,8 @@ exit "$RETVAL" # allow-axfr-ips If enabled, restrict zonetransfers to originate from these # IP addresses # -allow-axfr-ips=127.0.0.0/8,::1, +allow-axfr-ips=127.0.0.0/8,::1, +# add these entries to the list if any speficied: ################################# # allow-recursion List of netmasks that are allowed to recurse @@ -763,7 +764,8 @@ gmysql-password= # allow-axfr-ips If enabled, restrict zonetransfers to originate from these # IP addresses # -allow-axfr-ips= +allow-axfr-ips=127.0.0.0/8,::1, +# add these entries to the list if any speficied: ################################# # allow-recursion List of netmasks that are allowed to recurse From 4502f7ddf5cef412bc7cf9debeb8e61b807d3547 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Fri, 16 Dec 2016 10:17:48 +0100 Subject: [PATCH 0263/1335] fix Settings::Flush(), thx to Ithariel Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/settings/class.Settings.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/classes/settings/class.Settings.php b/lib/classes/settings/class.Settings.php index e90d5245..e6b8a37b 100644 --- a/lib/classes/settings/class.Settings.php +++ b/lib/classes/settings/class.Settings.php @@ -86,6 +86,7 @@ class Settings { while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) { self::$_data[$row['settinggroup']][$row['varname']] = $row['value']; } + return true; } /** @@ -161,10 +162,16 @@ class Settings { if ($instant_save) { $this->_storeSetting($sstr[0], $sstr[1], $value); } else { - if (!is_array(self::$_data[$sstr[0]])) { + // set temporary data for usage + if (!isset(self::$_data[$sstr[0]]) || !is_array(self::$_data[$sstr[0]])) { self::$_data[$sstr[0]] = array(); } self::$_data[$sstr[0]][$sstr[1]] = $value; + // set update-data when invoking Flush() + if (!isset(self::$_updatedata[$sstr[0]]) || !is_array(self::$_updatedata[$sstr[0]])) { + self::$_updatedata[$sstr[0]] = array(); + } + self::$_updatedata[$sstr[0]][$sstr[1]] = $value; } return true; } @@ -223,8 +230,9 @@ class Settings { // now empty the array self::$_updatedata = array(); // re-read in all settings - $this->_readSettings(); + return $this->_readSettings(); } + return false; } /** From 5c008adf16676349766734300513f06b8337325d Mon Sep 17 00:00:00 2001 From: Stefan Weil Date: Thu, 8 Dec 2016 22:00:00 +0100 Subject: [PATCH 0264/1335] Fix HTML code (wrong tag) `` must be terminated by ``. Signed-off-by: Stefan Weil --- install/templates/dataitemchk.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/templates/dataitemchk.tpl b/install/templates/dataitemchk.tpl index 9211c223..30279bfa 100644 --- a/install/templates/dataitemchk.tpl +++ b/install/templates/dataitemchk.tpl @@ -1,4 +1,4 @@

- {$fieldlabel} + {$fieldlabel}

From 7a617d0aa49ac5975023cf8275b6dd21a474feb3 Mon Sep 17 00:00:00 2001 From: Stefan Weil Date: Sat, 17 Dec 2016 14:31:07 +0100 Subject: [PATCH 0265/1335] Fix description in README.md The entry is called "System", not "Server". Signed-off-by: Stefan Weil --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 67c8c554..423e16d4 100644 --- a/README.md +++ b/README.md @@ -11,8 +11,8 @@ Developed by experienced server administrators, this panel simplifies the effort 3. Point your browser to http://[ip-of-webserver]/froxlor 4. Follow the installer 5. Login as administrator -6. Adjust "Server > Settings" according to your needs -7. Choose your distribution under "Server > Configuration" +6. Adjust "System > Settings" according to your needs +7. Choose your distribution under "System > Configuration" 8. Follow the steps for your services 9. Have fun! From 75622d4737320a29b5c612c2bf77bd96fc113b70 Mon Sep 17 00:00:00 2001 From: pissbeutel Date: Sat, 17 Dec 2016 15:14:02 +0100 Subject: [PATCH 0266/1335] Changed SOA refresh intervall to be compliant within recommended values See: https://www.denic.de/fileadmin/public/documentation/DENIC-23p.pdf The recommended values according to DENIC are: refresh: 3600 - 86400 retry: 900 - 28800 expire: 604800 - 3600000 negTTL: 180 - 86400 --- lib/functions/dns/function.createDomainZone.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/functions/dns/function.createDomainZone.php b/lib/functions/dns/function.createDomainZone.php index ad21ee45..7c4ca858 100644 --- a/lib/functions/dns/function.createDomainZone.php +++ b/lib/functions/dns/function.createDomainZone.php @@ -279,7 +279,7 @@ function createDomainZone($domain_id, $froxlorhostname = false, $isMainButSubTo $soa_content = $primary_ns . " " . escapeSoaAdminMail(Settings::Get('panel.adminmail')) . " "; $soa_content .= $domain['bindserial'] . " "; // TODO for now, dummy time-periods - $soa_content .= "1800 900 604800 1200"; + $soa_content .= "3600 900 604800 1200"; $soa_record = new DnsEntry('@', 'SOA', $soa_content); array_unshift($zonerecords, $soa_record); From 7a603596c5a43b4df30fafb29beaff661649361a Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Wed, 28 Dec 2016 20:13:15 +0100 Subject: [PATCH 0267/1335] validate ip address on installation --- install/lib/class.FroxlorInstall.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/install/lib/class.FroxlorInstall.php b/install/lib/class.FroxlorInstall.php index 792e8202..37acfc7c 100644 --- a/install/lib/class.FroxlorInstall.php +++ b/install/lib/class.FroxlorInstall.php @@ -177,10 +177,14 @@ class FroxlorInstall } // check system-hostname to be a FQDN - if ($this->_validate_ip($this->_data['servername'], true) !== false) { + if ($this->_validate_ip($this->_data['servername']) !== false) { $this->_data['servername'] = ''; } + if (empty($this->_data['serverip'] || $this->_validate_ip($this->_data['serverip']) == false)) { + return false; + } + if (isset($_POST['installstep']) && $_POST['installstep'] == '1' && $this->_data['admin_pass1'] == $this->_data['admin_pass2'] && $this->_data['admin_pass1'] != '' && $this->_data['admin_pass2'] != '' && $this->_data['mysql_unpriv_pass'] != '' && $this->_data['mysql_root_pass'] != '' && $this->_data['servername'] != '' && $this->_data['serverip'] != '' && $this->_data['httpuser'] != '' && $this->_data['httpgroup'] != '' && $this->_data['mysql_unpriv_user'] != $this->_data['mysql_root_user']) { return true; } @@ -781,7 +785,7 @@ class FroxlorInstall } $formdata .= $this->_getSectionItemString('servername', true, $style); // serverip - if (! empty($_POST['installstep']) && $this->_data['serverip'] == '') { + if (! empty($_POST['installstep']) && ($this->_data['serverip'] == '' || $this->_validate_ip($this->_data['serverip']) == false)) { $style = 'color:red;'; } else { $style = ''; From 437446c49d5e6381aa1f74fc72e1b288cca1947d Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 29 Dec 2016 10:54:25 +0100 Subject: [PATCH 0268/1335] update phpMailer to 5.2.21 Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/phpmailer/PHPMailerAutoload.php | 72 +- lib/classes/phpmailer/class.PHPMailer.php | 7915 ++++++++++--------- lib/classes/phpmailer/class.SMTP.php | 2423 +++--- 3 files changed, 5284 insertions(+), 5126 deletions(-) diff --git a/lib/classes/phpmailer/PHPMailerAutoload.php b/lib/classes/phpmailer/PHPMailerAutoload.php index eaa2e303..6c3b632d 100644 --- a/lib/classes/phpmailer/PHPMailerAutoload.php +++ b/lib/classes/phpmailer/PHPMailerAutoload.php @@ -1,49 +1,49 @@ - * @author Jim Jagielski (jimjag) - * @author Andy Prevost (codeworxtech) - * @author Brent R. Matzelle (original founder) - * @copyright 2012 - 2014 Marcus Bointon - * @copyright 2010 - 2012 Jim Jagielski - * @copyright 2004 - 2009 Andy Prevost - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - * @note This program is distributed in the hope that it will be useful - WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. - */ +* PHP Version 5 +* @package PHPMailer +* @link https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project +* @author Marcus Bointon (Synchro/coolbru) +* @author Jim Jagielski (jimjag) +* @author Andy Prevost (codeworxtech) +* @author Brent R. Matzelle (original founder) +* @copyright 2012 - 2014 Marcus Bointon +* @copyright 2010 - 2012 Jim Jagielski +* @copyright 2004 - 2009 Andy Prevost +* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License +* @note This program is distributed in the hope that it will be useful - WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. +*/ /** * PHPMailer SPL autoloader. - * @param string $classname The name of the class to load - */ +* @param string $classname The name of the class to load +*/ function PHPMailerAutoload($classname) { - //Can't use __DIR__ as it's only in PHP 5.3+ - $filename = dirname(__FILE__).DIRECTORY_SEPARATOR.'class.'.strtolower($classname).'.php'; - if (is_readable($filename)) { - require $filename; - } + //Can't use __DIR__ as it's only in PHP 5.3+ + $filename = dirname(__FILE__).DIRECTORY_SEPARATOR.'class.'.strtolower($classname).'.php'; + if (is_readable($filename)) { + require $filename; + } } if (version_compare(PHP_VERSION, '5.1.2', '>=')) { - //SPL autoloading was introduced in PHP 5.1.2 - if (version_compare(PHP_VERSION, '5.3.0', '>=')) { - spl_autoload_register('PHPMailerAutoload', true, true); - } else { - spl_autoload_register('PHPMailerAutoload'); - } + //SPL autoloading was introduced in PHP 5.1.2 + if (version_compare(PHP_VERSION, '5.3.0', '>=')) { + spl_autoload_register('PHPMailerAutoload', true, true); + } else { + spl_autoload_register('PHPMailerAutoload'); + } } else { - /** - * Fall back to traditional autoload for old PHP versions - * @param string $classname The name of the class to load - */ - function __autoload($classname) - { - PHPMailerAutoload($classname); - } + /** + * Fall back to traditional autoload for old PHP versions + * @param string $classname The name of the class to load + */ + function __autoload($classname) + { + PHPMailerAutoload($classname); + } } diff --git a/lib/classes/phpmailer/class.PHPMailer.php b/lib/classes/phpmailer/class.PHPMailer.php index f9013ebb..cdb9413c 100644 --- a/lib/classes/phpmailer/class.PHPMailer.php +++ b/lib/classes/phpmailer/class.PHPMailer.php @@ -1,3909 +1,4010 @@ - * @author Jim Jagielski (jimjag) - * @author Andy Prevost (codeworxtech) - * @author Brent R. Matzelle (original founder) - * @copyright 2012 - 2014 Marcus Bointon - * @copyright 2010 - 2012 Jim Jagielski - * @copyright 2004 - 2009 Andy Prevost - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - * @note This program is distributed in the hope that it will be useful - WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. - */ +* PHP Version 5 +* @package PHPMailer +* @link https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project +* @author Marcus Bointon (Synchro/coolbru) +* @author Jim Jagielski (jimjag) +* @author Andy Prevost (codeworxtech) +* @author Brent R. Matzelle (original founder) +* @copyright 2012 - 2014 Marcus Bointon +* @copyright 2010 - 2012 Jim Jagielski +* @copyright 2004 - 2009 Andy Prevost +* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License +* @note This program is distributed in the hope that it will be useful - WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. +*/ /** * PHPMailer - PHP email creation and transport class. - * @package PHPMailer - * @author Marcus Bointon (Synchro/coolbru) - * @author Jim Jagielski (jimjag) - * @author Andy Prevost (codeworxtech) - * @author Brent R. Matzelle (original founder) - */ +* @package PHPMailer +* @author Marcus Bointon (Synchro/coolbru) +* @author Jim Jagielski (jimjag) +* @author Andy Prevost (codeworxtech) +* @author Brent R. Matzelle (original founder) +*/ class PHPMailer { - /** - * The PHPMailer Version number. - * @var string - */ - public $Version = '5.2.16'; - - /** - * Email priority. - * Options: null (default), 1 = High, 3 = Normal, 5 = low. - * When null, the header is not set at all. - * @var integer - */ - public $Priority = null; - - /** - * The character set of the message. - * @var string - */ - public $CharSet = 'iso-8859-1'; - - /** - * The MIME Content-type of the message. - * @var string - */ - public $ContentType = 'text/plain'; - - /** - * The message encoding. - * Options: "8bit", "7bit", "binary", "base64", and "quoted-printable". - * @var string - */ - public $Encoding = '8bit'; - - /** - * Holds the most recent mailer error message. - * @var string - */ - public $ErrorInfo = ''; - - /** - * The From email address for the message. - * @var string - */ - public $From = 'root@localhost'; - - /** - * The From name of the message. - * @var string - */ - public $FromName = 'Root User'; - - /** - * The Sender email (Return-Path) of the message. - * If not empty, will be sent via -f to sendmail or as 'MAIL FROM' in smtp mode. - * @var string - */ - public $Sender = ''; - - /** - * The Return-Path of the message. - * If empty, it will be set to either From or Sender. - * @var string - * @deprecated Email senders should never set a return-path header; - * it's the receiver's job (RFC5321 section 4.4), so this no longer does anything. - * @link https://tools.ietf.org/html/rfc5321#section-4.4 RFC5321 reference - */ - public $ReturnPath = ''; - - /** - * The Subject of the message. - * @var string - */ - public $Subject = ''; - - /** - * An HTML or plain text message body. - * If HTML then call isHTML(true). - * @var string - */ - public $Body = ''; - - /** - * The plain-text message body. - * This body can be read by mail clients that do not have HTML email - * capability such as mutt & Eudora. - * Clients that can read HTML will view the normal Body. - * @var string - */ - public $AltBody = ''; - - /** - * An iCal message part body. - * Only supported in simple alt or alt_inline message types - * To generate iCal events, use the bundled extras/EasyPeasyICS.php class or iCalcreator - * @link http://sprain.ch/blog/downloads/php-class-easypeasyics-create-ical-files-with-php/ - * @link http://kigkonsult.se/iCalcreator/ - * @var string - */ - public $Ical = ''; - - /** - * The complete compiled MIME message body. - * @access protected - * @var string - */ - protected $MIMEBody = ''; - - /** - * The complete compiled MIME message headers. - * @var string - * @access protected - */ - protected $MIMEHeader = ''; - - /** - * Extra headers that createHeader() doesn't fold in. - * @var string - * @access protected - */ - protected $mailHeader = ''; - - /** - * Word-wrap the message body to this number of chars. - * Set to 0 to not wrap. A useful value here is 78, for RFC2822 section 2.1.1 compliance. - * @var integer - */ - public $WordWrap = 0; - - /** - * Which method to use to send mail. - * Options: "mail", "sendmail", or "smtp". - * @var string - */ - public $Mailer = 'mail'; - - /** - * The path to the sendmail program. - * @var string - */ - public $Sendmail = '/usr/sbin/sendmail'; - - /** - * Whether mail() uses a fully sendmail-compatible MTA. - * One which supports sendmail's "-oi -f" options. - * @var boolean - */ - public $UseSendmailOptions = true; - - /** - * Path to PHPMailer plugins. - * Useful if the SMTP class is not in the PHP include path. - * @var string - * @deprecated Should not be needed now there is an autoloader. - */ - public $PluginDir = ''; - - /** - * The email address that a reading confirmation should be sent to, also known as read receipt. - * @var string - */ - public $ConfirmReadingTo = ''; - - /** - * The hostname to use in the Message-ID header and as default HELO string. - * If empty, PHPMailer attempts to find one with, in order, - * $_SERVER['SERVER_NAME'], gethostname(), php_uname('n'), or the value - * 'localhost.localdomain'. - * @var string - */ - public $Hostname = ''; - - /** - * An ID to be used in the Message-ID header. - * If empty, a unique id will be generated. - * @var string - */ - public $MessageID = ''; - - /** - * The message Date to be used in the Date header. - * If empty, the current date will be added. - * @var string - */ - public $MessageDate = ''; - - /** - * SMTP hosts. - * Either a single hostname or multiple semicolon-delimited hostnames. - * You can also specify a different port - * for each host by using this format: [hostname:port] - * (e.g. "smtp1.example.com:25;smtp2.example.com"). - * You can also specify encryption type, for example: - * (e.g. "tls://smtp1.example.com:587;ssl://smtp2.example.com:465"). - * Hosts will be tried in order. - * @var string - */ - public $Host = 'localhost'; - - /** - * The default SMTP server port. - * @var integer - * @TODO Why is this needed when the SMTP class takes care of it? - */ - public $Port = 25; - - /** - * The SMTP HELO of the message. - * Default is $Hostname. If $Hostname is empty, PHPMailer attempts to find - * one with the same method described above for $Hostname. - * @var string - * @see PHPMailer::$Hostname - */ - public $Helo = ''; - - /** - * What kind of encryption to use on the SMTP connection. - * Options: '', 'ssl' or 'tls' - * @var string - */ - public $SMTPSecure = ''; - - /** - * Whether to enable TLS encryption automatically if a server supports it, - * even if `SMTPSecure` is not set to 'tls'. - * Be aware that in PHP >= 5.6 this requires that the server's certificates are valid. - * @var boolean - */ - public $SMTPAutoTLS = true; - - /** - * Whether to use SMTP authentication. - * Uses the Username and Password properties. - * @var boolean - * @see PHPMailer::$Username - * @see PHPMailer::$Password - */ - public $SMTPAuth = false; - - /** - * Options array passed to stream_context_create when connecting via SMTP. - * @var array - */ - public $SMTPOptions = array(); - - /** - * SMTP username. - * @var string - */ - public $Username = ''; - - /** - * SMTP password. - * @var string - */ - public $Password = ''; - - /** - * SMTP auth type. - * Options are CRAM-MD5, LOGIN, PLAIN, NTLM, XOAUTH2, attempted in that order if not specified - * @var string - */ - public $AuthType = ''; - - /** - * SMTP realm. - * Used for NTLM auth - * @var string - */ - public $Realm = ''; - - /** - * SMTP workstation. - * Used for NTLM auth - * @var string - */ - public $Workstation = ''; - - /** - * The SMTP server timeout in seconds. - * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2 - * @var integer - */ - public $Timeout = 300; - - /** - * SMTP class debug output mode. - * Debug output level. - * Options: - * * `0` No output - * * `1` Commands - * * `2` Data and commands - * * `3` As 2 plus connection status - * * `4` Low-level data output - * @var integer - * @see SMTP::$do_debug - */ - public $SMTPDebug = 0; - - /** - * How to handle debug output. - * Options: - * * `echo` Output plain-text as-is, appropriate for CLI - * * `html` Output escaped, line breaks converted to `
`, appropriate for browser output - * * `error_log` Output to error log as configured in php.ini - * - * Alternatively, you can provide a callable expecting two params: a message string and the debug level: - * - * $mail->Debugoutput = function($str, $level) {echo "debug level $level; message: $str";}; - * - * @var string|callable - * @see SMTP::$Debugoutput - */ - public $Debugoutput = 'echo'; - - /** - * Whether to keep SMTP connection open after each message. - * If this is set to true then to close the connection - * requires an explicit call to smtpClose(). - * @var boolean - */ - public $SMTPKeepAlive = false; - - /** - * Whether to split multiple to addresses into multiple messages - * or send them all in one message. - * Only supported in `mail` and `sendmail` transports, not in SMTP. - * @var boolean - */ - public $SingleTo = false; - - /** - * Storage for addresses when SingleTo is enabled. - * @var array - * @TODO This should really not be public - */ - public $SingleToArray = array(); - - /** - * Whether to generate VERP addresses on send. - * Only applicable when sending via SMTP. - * @link https://en.wikipedia.org/wiki/Variable_envelope_return_path - * @link http://www.postfix.org/VERP_README.html Postfix VERP info - * @var boolean - */ - public $do_verp = false; - - /** - * Whether to allow sending messages with an empty body. - * @var boolean - */ - public $AllowEmpty = false; - - /** - * The default line ending. - * @note The default remains "\n". We force CRLF where we know - * it must be used via self::CRLF. - * @var string - */ - public $LE = "\n"; - - /** - * DKIM selector. - * @var string - */ - public $DKIM_selector = ''; - - /** - * DKIM Identity. - * Usually the email address used as the source of the email. - * @var string - */ - public $DKIM_identity = ''; - - /** - * DKIM passphrase. - * Used if your key is encrypted. - * @var string - */ - public $DKIM_passphrase = ''; - - /** - * DKIM signing domain name. - * @example 'example.com' - * @var string - */ - public $DKIM_domain = ''; - - /** - * DKIM private key file path. - * @var string - */ - public $DKIM_private = ''; - - /** - * Callback Action function name. - * - * The function that handles the result of the send email action. - * It is called out by send() for each email sent. - * - * Value can be any php callable: http://www.php.net/is_callable - * - * Parameters: - * boolean $result result of the send action - * string $to email address of the recipient - * string $cc cc email addresses - * string $bcc bcc email addresses - * string $subject the subject - * string $body the email body - * string $from email address of sender - * @var string - */ - public $action_function = ''; - - /** - * What to put in the X-Mailer header. - * Options: An empty string for PHPMailer default, whitespace for none, or a string to use - * @var string - */ - public $XMailer = ''; - - /** - * Which validator to use by default when validating email addresses. - * May be a callable to inject your own validator, but there are several built-in validators. - * @see PHPMailer::validateAddress() - * @var string|callable - * @static - */ - public static $validator = 'auto'; - - /** - * An instance of the SMTP sender class. - * @var SMTP - * @access protected - */ - protected $smtp = null; - - /** - * The array of 'to' names and addresses. - * @var array - * @access protected - */ - protected $to = array(); - - /** - * The array of 'cc' names and addresses. - * @var array - * @access protected - */ - protected $cc = array(); - - /** - * The array of 'bcc' names and addresses. - * @var array - * @access protected - */ - protected $bcc = array(); - - /** - * The array of reply-to names and addresses. - * @var array - * @access protected - */ - protected $ReplyTo = array(); - - /** - * An array of all kinds of addresses. - * Includes all of $to, $cc, $bcc - * @var array - * @access protected - * @see PHPMailer::$to @see PHPMailer::$cc @see PHPMailer::$bcc - */ - protected $all_recipients = array(); - - /** - * An array of names and addresses queued for validation. - * In send(), valid and non duplicate entries are moved to $all_recipients - * and one of $to, $cc, or $bcc. - * This array is used only for addresses with IDN. - * @var array - * @access protected - * @see PHPMailer::$to @see PHPMailer::$cc @see PHPMailer::$bcc - * @see PHPMailer::$all_recipients - */ - protected $RecipientsQueue = array(); - - /** - * An array of reply-to names and addresses queued for validation. - * In send(), valid and non duplicate entries are moved to $ReplyTo. - * This array is used only for addresses with IDN. - * @var array - * @access protected - * @see PHPMailer::$ReplyTo - */ - protected $ReplyToQueue = array(); - - /** - * The array of attachments. - * @var array - * @access protected - */ - protected $attachment = array(); - - /** - * The array of custom headers. - * @var array - * @access protected - */ - protected $CustomHeader = array(); - - /** - * The most recent Message-ID (including angular brackets). - * @var string - * @access protected - */ - protected $lastMessageID = ''; - - /** - * The message's MIME type. - * @var string - * @access protected - */ - protected $message_type = ''; - - /** - * The array of MIME boundary strings. - * @var array - * @access protected - */ - protected $boundary = array(); - - /** - * The array of available languages. - * @var array - * @access protected - */ - protected $language = array(); - - /** - * The number of errors encountered. - * @var integer - * @access protected - */ - protected $error_count = 0; - - /** - * The S/MIME certificate file path. - * @var string - * @access protected - */ - protected $sign_cert_file = ''; - - /** - * The S/MIME key file path. - * @var string - * @access protected - */ - protected $sign_key_file = ''; - - /** - * The optional S/MIME extra certificates ("CA Chain") file path. - * @var string - * @access protected - */ - protected $sign_extracerts_file = ''; - - /** - * The S/MIME password for the key. - * Used only if the key is encrypted. - * @var string - * @access protected - */ - protected $sign_key_pass = ''; - - /** - * Whether to throw exceptions for errors. - * @var boolean - * @access protected - */ - protected $exceptions = false; - - /** - * Unique ID used for message ID and boundaries. - * @var string - * @access protected - */ - protected $uniqueid = ''; - - /** - * Error severity: message only, continue processing. - */ - const STOP_MESSAGE = 0; - - /** - * Error severity: message, likely ok to continue processing. - */ - const STOP_CONTINUE = 1; - - /** - * Error severity: message, plus full stop, critical error reached. - */ - const STOP_CRITICAL = 2; - - /** - * SMTP RFC standard line ending. - */ - const CRLF = "\r\n"; - - /** - * The maximum line length allowed by RFC 2822 section 2.1.1 - * @var integer - */ - const MAX_LINE_LENGTH = 998; - - /** - * Constructor. - * @param boolean $exceptions Should we throw external exceptions? - */ - public function __construct($exceptions = null) - { - if ($exceptions !== null) { - $this->exceptions = (boolean)$exceptions; - } - } - - /** - * Destructor. - */ - public function __destruct() - { - //Close any open SMTP connection nicely - $this->smtpClose(); - } - - /** - * Call mail() in a safe_mode-aware fashion. - * Also, unless sendmail_path points to sendmail (or something that - * claims to be sendmail), don't pass params (not a perfect fix, - * but it will do) - * @param string $to To - * @param string $subject Subject - * @param string $body Message Body - * @param string $header Additional Header(s) - * @param string $params Params - * @access private - * @return boolean - */ - private function mailPassthru($to, $subject, $body, $header, $params) - { - //Check overloading of mail function to avoid double-encoding - if (ini_get('mbstring.func_overload') & 1) { - $subject = $this->secureHeader($subject); - } else { - $subject = $this->encodeHeader($this->secureHeader($subject)); - } - //Can't use additional_parameters in safe_mode - //@link http://php.net/manual/en/function.mail.php - if (ini_get('safe_mode') or !$this->UseSendmailOptions) { - $result = @mail($to, $subject, $body, $header); - } else { - $result = @mail($to, $subject, $body, $header, $params); - } - return $result; - } - - /** - * Output debugging info via user-defined method. - * Only generates output if SMTP debug output is enabled (@see SMTP::$do_debug). - * @see PHPMailer::$Debugoutput - * @see PHPMailer::$SMTPDebug - * @param string $str - */ - protected function edebug($str) - { - if ($this->SMTPDebug <= 0) { - return; - } - //Avoid clash with built-in function names - if (!in_array($this->Debugoutput, array('error_log', 'html', 'echo')) and is_callable($this->Debugoutput)) { - call_user_func($this->Debugoutput, $str, $this->SMTPDebug); - return; - } - switch ($this->Debugoutput) { - case 'error_log': - //Don't output, just log - error_log($str); - break; - case 'html': - //Cleans up output a bit for a better looking, HTML-safe output - echo htmlentities( - preg_replace('/[\r\n]+/', '', $str), - ENT_QUOTES, - 'UTF-8' - ) - . "
\n"; - break; - case 'echo': - default: - //Normalize line breaks - $str = preg_replace('/\r\n?/ms', "\n", $str); - echo gmdate('Y-m-d H:i:s') . "\t" . str_replace( - "\n", - "\n \t ", - trim($str) - ) . "\n"; - } - } - - /** - * Sets message type to HTML or plain. - * @param boolean $isHtml True for HTML mode. - * @return void - */ - public function isHTML($isHtml = true) - { - if ($isHtml) { - $this->ContentType = 'text/html'; - } else { - $this->ContentType = 'text/plain'; - } - } - - /** - * Send messages using SMTP. - * @return void - */ - public function isSMTP() - { - $this->Mailer = 'smtp'; - } - - /** - * Send messages using PHP's mail() function. - * @return void - */ - public function isMail() - { - $this->Mailer = 'mail'; - } - - /** - * Send messages using $Sendmail. - * @return void - */ - public function isSendmail() - { - $ini_sendmail_path = ini_get('sendmail_path'); - - if (!stristr($ini_sendmail_path, 'sendmail')) { - $this->Sendmail = '/usr/sbin/sendmail'; - } else { - $this->Sendmail = $ini_sendmail_path; - } - $this->Mailer = 'sendmail'; - } - - /** - * Send messages using qmail. - * @return void - */ - public function isQmail() - { - $ini_sendmail_path = ini_get('sendmail_path'); - - if (!stristr($ini_sendmail_path, 'qmail')) { - $this->Sendmail = '/var/qmail/bin/qmail-inject'; - } else { - $this->Sendmail = $ini_sendmail_path; - } - $this->Mailer = 'qmail'; - } - - /** - * Add a "To" address. - * @param string $address The email address to send to - * @param string $name - * @return boolean true on success, false if address already used or invalid in some way - */ - public function addAddress($address, $name = '') - { - return $this->addOrEnqueueAnAddress('to', $address, $name); - } - - /** - * Add a "CC" address. - * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer. - * @param string $address The email address to send to - * @param string $name - * @return boolean true on success, false if address already used or invalid in some way - */ - public function addCC($address, $name = '') - { - return $this->addOrEnqueueAnAddress('cc', $address, $name); - } - - /** - * Add a "BCC" address. - * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer. - * @param string $address The email address to send to - * @param string $name - * @return boolean true on success, false if address already used or invalid in some way - */ - public function addBCC($address, $name = '') - { - return $this->addOrEnqueueAnAddress('bcc', $address, $name); - } - - /** - * Add a "Reply-To" address. - * @param string $address The email address to reply to - * @param string $name - * @return boolean true on success, false if address already used or invalid in some way - */ - public function addReplyTo($address, $name = '') - { - return $this->addOrEnqueueAnAddress('Reply-To', $address, $name); - } - - /** - * Add an address to one of the recipient arrays or to the ReplyTo array. Because PHPMailer - * can't validate addresses with an IDN without knowing the PHPMailer::$CharSet (that can still - * be modified after calling this function), addition of such addresses is delayed until send(). - * Addresses that have been added already return false, but do not throw exceptions. - * @param string $kind One of 'to', 'cc', 'bcc', or 'ReplyTo' - * @param string $address The email address to send, resp. to reply to - * @param string $name - * @throws phpmailerException - * @return boolean true on success, false if address already used or invalid in some way - * @access protected - */ - protected function addOrEnqueueAnAddress($kind, $address, $name) - { - $address = trim($address); - $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim - if (($pos = strrpos($address, '@')) === false) { - // At-sign is misssing. - $error_message = $this->lang('invalid_address') . " (addAnAddress $kind): $address"; - $this->setError($error_message); - $this->edebug($error_message); - if ($this->exceptions) { - throw new phpmailerException($error_message); - } - return false; - } - $params = array($kind, $address, $name); - // Enqueue addresses with IDN until we know the PHPMailer::$CharSet. - if ($this->has8bitChars(substr($address, ++$pos)) and $this->idnSupported()) { - if ($kind != 'Reply-To') { - if (!array_key_exists($address, $this->RecipientsQueue)) { - $this->RecipientsQueue[$address] = $params; - return true; - } - } else { - if (!array_key_exists($address, $this->ReplyToQueue)) { - $this->ReplyToQueue[$address] = $params; - return true; - } - } - return false; - } - // Immediately add standard addresses without IDN. - return call_user_func_array(array($this, 'addAnAddress'), $params); - } - - /** - * Add an address to one of the recipient arrays or to the ReplyTo array. - * Addresses that have been added already return false, but do not throw exceptions. - * @param string $kind One of 'to', 'cc', 'bcc', or 'ReplyTo' - * @param string $address The email address to send, resp. to reply to - * @param string $name - * @throws phpmailerException - * @return boolean true on success, false if address already used or invalid in some way - * @access protected - */ - protected function addAnAddress($kind, $address, $name = '') - { - if (!in_array($kind, array('to', 'cc', 'bcc', 'Reply-To'))) { - $error_message = $this->lang('Invalid recipient kind: ') . $kind; - $this->setError($error_message); - $this->edebug($error_message); - if ($this->exceptions) { - throw new phpmailerException($error_message); - } - return false; - } - if (!$this->validateAddress($address)) { - $error_message = $this->lang('invalid_address') . " (addAnAddress $kind): $address"; - $this->setError($error_message); - $this->edebug($error_message); - if ($this->exceptions) { - throw new phpmailerException($error_message); - } - return false; - } - if ($kind != 'Reply-To') { - if (!array_key_exists(strtolower($address), $this->all_recipients)) { - array_push($this->$kind, array($address, $name)); - $this->all_recipients[strtolower($address)] = true; - return true; - } - } else { - if (!array_key_exists(strtolower($address), $this->ReplyTo)) { - $this->ReplyTo[strtolower($address)] = array($address, $name); - return true; - } - } - return false; - } - - /** - * Parse and validate a string containing one or more RFC822-style comma-separated email addresses - * of the form "display name
" into an array of name/address pairs. - * Uses the imap_rfc822_parse_adrlist function if the IMAP extension is available. - * Note that quotes in the name part are removed. - * @param string $addrstr The address list string - * @param bool $useimap Whether to use the IMAP extension to parse the list - * @return array - * @link http://www.andrew.cmu.edu/user/agreen1/testing/mrbs/web/Mail/RFC822.php A more careful implementation - */ - public function parseAddresses($addrstr, $useimap = true) - { - $addresses = array(); - if ($useimap and function_exists('imap_rfc822_parse_adrlist')) { - //Use this built-in parser if it's available - $list = imap_rfc822_parse_adrlist($addrstr, ''); - foreach ($list as $address) { - if ($address->host != '.SYNTAX-ERROR.') { - if ($this->validateAddress($address->mailbox . '@' . $address->host)) { - $addresses[] = array( - 'name' => (property_exists($address, 'personal') ? $address->personal : ''), - 'address' => $address->mailbox . '@' . $address->host - ); - } - } - } - } else { - //Use this simpler parser - $list = explode(',', $addrstr); - foreach ($list as $address) { - $address = trim($address); - //Is there a separate name part? - if (strpos($address, '<') === false) { - //No separate name, just use the whole thing - if ($this->validateAddress($address)) { - $addresses[] = array( - 'name' => '', - 'address' => $address - ); - } - } else { - list($name, $email) = explode('<', $address); - $email = trim(str_replace('>', '', $email)); - if ($this->validateAddress($email)) { - $addresses[] = array( - 'name' => trim(str_replace(array('"', "'"), '', $name)), - 'address' => $email - ); - } - } - } - } - return $addresses; - } - - /** - * Set the From and FromName properties. - * @param string $address - * @param string $name - * @param boolean $auto Whether to also set the Sender address, defaults to true - * @throws phpmailerException - * @return boolean - */ - public function setFrom($address, $name = '', $auto = true) - { - $address = trim($address); - $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim - // Don't validate now addresses with IDN. Will be done in send(). - if (($pos = strrpos($address, '@')) === false or - (!$this->has8bitChars(substr($address, ++$pos)) or !$this->idnSupported()) and - !$this->validateAddress($address)) { - $error_message = $this->lang('invalid_address') . " (setFrom) $address"; - $this->setError($error_message); - $this->edebug($error_message); - if ($this->exceptions) { - throw new phpmailerException($error_message); - } - return false; - } - $this->From = $address; - $this->FromName = $name; - if ($auto) { - if (empty($this->Sender)) { - $this->Sender = $address; - } - } - return true; - } - - /** - * Return the Message-ID header of the last email. - * Technically this is the value from the last time the headers were created, - * but it's also the message ID of the last sent message except in - * pathological cases. - * @return string - */ - public function getLastMessageID() - { - return $this->lastMessageID; - } - - /** - * Check that a string looks like an email address. - * @param string $address The email address to check - * @param string|callable $patternselect A selector for the validation pattern to use : - * * `auto` Pick best pattern automatically; - * * `pcre8` Use the squiloople.com pattern, requires PCRE > 8.0, PHP >= 5.3.2, 5.2.14; - * * `pcre` Use old PCRE implementation; - * * `php` Use PHP built-in FILTER_VALIDATE_EMAIL; - * * `html5` Use the pattern given by the HTML5 spec for 'email' type form input elements. - * * `noregex` Don't use a regex: super fast, really dumb. - * Alternatively you may pass in a callable to inject your own validator, for example: - * PHPMailer::validateAddress('user@example.com', function($address) { - * return (strpos($address, '@') !== false); - * }); - * You can also set the PHPMailer::$validator static to a callable, allowing built-in methods to use your validator. - * @return boolean - * @static - * @access public - */ - public static function validateAddress($address, $patternselect = null) - { - if (is_null($patternselect)) { - $patternselect = self::$validator; - } - if (is_callable($patternselect)) { - return call_user_func($patternselect, $address); - } - //Reject line breaks in addresses; it's valid RFC5322, but not RFC5321 - if (strpos($address, "\n") !== false or strpos($address, "\r") !== false) { - return false; - } - if (!$patternselect or $patternselect == 'auto') { - //Check this constant first so it works when extension_loaded() is disabled by safe mode - //Constant was added in PHP 5.2.4 - if (defined('PCRE_VERSION')) { - //This pattern can get stuck in a recursive loop in PCRE <= 8.0.2 - if (version_compare(PCRE_VERSION, '8.0.3') >= 0) { - $patternselect = 'pcre8'; - } else { - $patternselect = 'pcre'; - } - } elseif (function_exists('extension_loaded') and extension_loaded('pcre')) { - //Fall back to older PCRE - $patternselect = 'pcre'; - } else { - //Filter_var appeared in PHP 5.2.0 and does not require the PCRE extension - if (version_compare(PHP_VERSION, '5.2.0') >= 0) { - $patternselect = 'php'; - } else { - $patternselect = 'noregex'; - } - } - } - switch ($patternselect) { - case 'pcre8': - /** - * Uses the same RFC5322 regex on which FILTER_VALIDATE_EMAIL is based, but allows dotless domains. - * @link http://squiloople.com/2009/12/20/email-address-validation/ - * @copyright 2009-2010 Michael Rushton - * Feel free to use and redistribute this code. But please keep this copyright notice. - */ - return (boolean)preg_match( - '/^(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){255,})(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){65,}@)' . - '((?>(?>(?>((?>(?>(?>\x0D\x0A)?[\t ])+|(?>[\t ]*\x0D\x0A)?[\t ]+)?)(\((?>(?2)' . - '(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\)))+(?2))|(?2))?)' . - '([!#-\'*+\/-9=?^-~-]+|"(?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\x7F]))*' . - '(?2)")(?>(?1)\.(?1)(?4))*(?1)@(?!(?1)[a-z0-9-]{64,})(?1)(?>([a-z0-9](?>[a-z0-9-]*[a-z0-9])?)' . - '(?>(?1)\.(?!(?1)[a-z0-9-]{64,})(?1)(?5)){0,126}|\[(?:(?>IPv6:(?>([a-f0-9]{1,4})(?>:(?6)){7}' . - '|(?!(?:.*[a-f0-9][:\]]){8,})((?6)(?>:(?6)){0,6})?::(?7)?))|(?>(?>IPv6:(?>(?6)(?>:(?6)){5}:' . - '|(?!(?:.*[a-f0-9]:){6,})(?8)?::(?>((?6)(?>:(?6)){0,4}):)?))?(25[0-5]|2[0-4][0-9]|1[0-9]{2}' . - '|[1-9]?[0-9])(?>\.(?9)){3}))\])(?1)$/isD', - $address - ); - case 'pcre': - //An older regex that doesn't need a recent PCRE - return (boolean)preg_match( - '/^(?!(?>"?(?>\\\[ -~]|[^"])"?){255,})(?!(?>"?(?>\\\[ -~]|[^"])"?){65,}@)(?>' . - '[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*")' . - '(?>\.(?>[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*"))*' . - '@(?>(?![a-z0-9-]{64,})(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)(?>\.(?![a-z0-9-]{64,})' . - '(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)){0,126}|\[(?:(?>IPv6:(?>(?>[a-f0-9]{1,4})(?>:' . - '[a-f0-9]{1,4}){7}|(?!(?:.*[a-f0-9][:\]]){8,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?' . - '::(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?))|(?>(?>IPv6:(?>[a-f0-9]{1,4}(?>:' . - '[a-f0-9]{1,4}){5}:|(?!(?:.*[a-f0-9]:){6,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4})?' . - '::(?>(?:[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4}):)?))?(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}' . - '|[1-9]?[0-9])(?>\.(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}))\])$/isD', - $address - ); - case 'html5': - /** - * This is the pattern used in the HTML5 spec for validation of 'email' type form input elements. - * @link http://www.whatwg.org/specs/web-apps/current-work/#e-mail-state-(type=email) - */ - return (boolean)preg_match( - '/^[a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}' . - '[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/sD', - $address - ); - case 'noregex': - //No PCRE! Do something _very_ approximate! - //Check the address is 3 chars or longer and contains an @ that's not the first or last char - return (strlen($address) >= 3 - and strpos($address, '@') >= 1 - and strpos($address, '@') != strlen($address) - 1); - case 'php': - default: - return (boolean)filter_var($address, FILTER_VALIDATE_EMAIL); - } - } - - /** - * Tells whether IDNs (Internationalized Domain Names) are supported or not. This requires the - * "intl" and "mbstring" PHP extensions. - * @return bool "true" if required functions for IDN support are present - */ - public function idnSupported() - { - // @TODO: Write our own "idn_to_ascii" function for PHP <= 5.2. - return function_exists('idn_to_ascii') and function_exists('mb_convert_encoding'); - } - - /** - * Converts IDN in given email address to its ASCII form, also known as punycode, if possible. - * Important: Address must be passed in same encoding as currently set in PHPMailer::$CharSet. - * This function silently returns unmodified address if: - * - No conversion is necessary (i.e. domain name is not an IDN, or is already in ASCII form) - * - Conversion to punycode is impossible (e.g. required PHP functions are not available) - * or fails for any reason (e.g. domain has characters not allowed in an IDN) - * @see PHPMailer::$CharSet - * @param string $address The email address to convert - * @return string The encoded address in ASCII form - */ - public function punyencodeAddress($address) - { - // Verify we have required functions, CharSet, and at-sign. - if ($this->idnSupported() and - !empty($this->CharSet) and - ($pos = strrpos($address, '@')) !== false) { - $domain = substr($address, ++$pos); - // Verify CharSet string is a valid one, and domain properly encoded in this CharSet. - if ($this->has8bitChars($domain) and @mb_check_encoding($domain, $this->CharSet)) { - $domain = mb_convert_encoding($domain, 'UTF-8', $this->CharSet); - if (($punycode = defined('INTL_IDNA_VARIANT_UTS46') ? - idn_to_ascii($domain, 0, INTL_IDNA_VARIANT_UTS46) : - idn_to_ascii($domain)) !== false) { - return substr($address, 0, $pos) . $punycode; - } - } - } - return $address; - } - - /** - * Create a message and send it. - * Uses the sending method specified by $Mailer. - * @throws phpmailerException - * @return boolean false on error - See the ErrorInfo property for details of the error. - */ - public function send() - { - try { - if (!$this->preSend()) { - return false; - } - return $this->postSend(); - } catch (phpmailerException $exc) { - $this->mailHeader = ''; - $this->setError($exc->getMessage()); - if ($this->exceptions) { - throw $exc; - } - return false; - } - } - - /** - * Prepare a message for sending. - * @throws phpmailerException - * @return boolean - */ - public function preSend() - { - try { - $this->error_count = 0; // Reset errors - $this->mailHeader = ''; - - // Dequeue recipient and Reply-To addresses with IDN - foreach (array_merge($this->RecipientsQueue, $this->ReplyToQueue) as $params) { - $params[1] = $this->punyencodeAddress($params[1]); - call_user_func_array(array($this, 'addAnAddress'), $params); - } - if ((count($this->to) + count($this->cc) + count($this->bcc)) < 1) { - throw new phpmailerException($this->lang('provide_address'), self::STOP_CRITICAL); - } - - // Validate From, Sender, and ConfirmReadingTo addresses - foreach (array('From', 'Sender', 'ConfirmReadingTo') as $address_kind) { - $this->$address_kind = trim($this->$address_kind); - if (empty($this->$address_kind)) { - continue; - } - $this->$address_kind = $this->punyencodeAddress($this->$address_kind); - if (!$this->validateAddress($this->$address_kind)) { - $error_message = $this->lang('invalid_address') . ' (punyEncode) ' . $this->$address_kind; - $this->setError($error_message); - $this->edebug($error_message); - if ($this->exceptions) { - throw new phpmailerException($error_message); - } - return false; - } - } - - // Set whether the message is multipart/alternative - if ($this->alternativeExists()) { - $this->ContentType = 'multipart/alternative'; - } - - $this->setMessageType(); - // Refuse to send an empty message unless we are specifically allowing it - if (!$this->AllowEmpty and empty($this->Body)) { - throw new phpmailerException($this->lang('empty_message'), self::STOP_CRITICAL); - } - - // Create body before headers in case body makes changes to headers (e.g. altering transfer encoding) - $this->MIMEHeader = ''; - $this->MIMEBody = $this->createBody(); - // createBody may have added some headers, so retain them - $tempheaders = $this->MIMEHeader; - $this->MIMEHeader = $this->createHeader(); - $this->MIMEHeader .= $tempheaders; - - // To capture the complete message when using mail(), create - // an extra header list which createHeader() doesn't fold in - if ($this->Mailer == 'mail') { - if (count($this->to) > 0) { - $this->mailHeader .= $this->addrAppend('To', $this->to); - } else { - $this->mailHeader .= $this->headerLine('To', 'undisclosed-recipients:;'); - } - $this->mailHeader .= $this->headerLine( - 'Subject', - $this->encodeHeader($this->secureHeader(trim($this->Subject))) - ); - } - - // Sign with DKIM if enabled - if (!empty($this->DKIM_domain) - && !empty($this->DKIM_private) - && !empty($this->DKIM_selector) - && file_exists($this->DKIM_private)) { - $header_dkim = $this->DKIM_Add( - $this->MIMEHeader . $this->mailHeader, - $this->encodeHeader($this->secureHeader($this->Subject)), - $this->MIMEBody - ); - $this->MIMEHeader = rtrim($this->MIMEHeader, "\r\n ") . self::CRLF . - str_replace("\r\n", "\n", $header_dkim) . self::CRLF; - } - return true; - } catch (phpmailerException $exc) { - $this->setError($exc->getMessage()); - if ($this->exceptions) { - throw $exc; - } - return false; - } - } - - /** - * Actually send a message. - * Send the email via the selected mechanism - * @throws phpmailerException - * @return boolean - */ - public function postSend() - { - try { - // Choose the mailer and send through it - switch ($this->Mailer) { - case 'sendmail': - case 'qmail': - return $this->sendmailSend($this->MIMEHeader, $this->MIMEBody); - case 'smtp': - return $this->smtpSend($this->MIMEHeader, $this->MIMEBody); - case 'mail': - return $this->mailSend($this->MIMEHeader, $this->MIMEBody); - default: - $sendMethod = $this->Mailer.'Send'; - if (method_exists($this, $sendMethod)) { - return $this->$sendMethod($this->MIMEHeader, $this->MIMEBody); - } - - return $this->mailSend($this->MIMEHeader, $this->MIMEBody); - } - } catch (phpmailerException $exc) { - $this->setError($exc->getMessage()); - $this->edebug($exc->getMessage()); - if ($this->exceptions) { - throw $exc; - } - } - return false; - } - - /** - * Send mail using the $Sendmail program. - * @param string $header The message headers - * @param string $body The message body - * @see PHPMailer::$Sendmail - * @throws phpmailerException - * @access protected - * @return boolean - */ - protected function sendmailSend($header, $body) - { - if ($this->Sender != '') { - if ($this->Mailer == 'qmail') { - $sendmail = sprintf('%s -f%s', escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender)); - } else { - $sendmail = sprintf('%s -oi -f%s -t', escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender)); - } - } else { - if ($this->Mailer == 'qmail') { - $sendmail = sprintf('%s', escapeshellcmd($this->Sendmail)); - } else { - $sendmail = sprintf('%s -oi -t', escapeshellcmd($this->Sendmail)); - } - } - if ($this->SingleTo) { - foreach ($this->SingleToArray as $toAddr) { - if (!@$mail = popen($sendmail, 'w')) { - throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); - } - fputs($mail, 'To: ' . $toAddr . "\n"); - fputs($mail, $header); - fputs($mail, $body); - $result = pclose($mail); - $this->doCallback( - ($result == 0), - array($toAddr), - $this->cc, - $this->bcc, - $this->Subject, - $body, - $this->From - ); - if ($result != 0) { - throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); - } - } - } else { - if (!@$mail = popen($sendmail, 'w')) { - throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); - } - fputs($mail, $header); - fputs($mail, $body); - $result = pclose($mail); - $this->doCallback( - ($result == 0), - $this->to, - $this->cc, - $this->bcc, - $this->Subject, - $body, - $this->From - ); - if ($result != 0) { - throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); - } - } - return true; - } - - /** - * Send mail using the PHP mail() function. - * @param string $header The message headers - * @param string $body The message body - * @link http://www.php.net/manual/en/book.mail.php - * @throws phpmailerException - * @access protected - * @return boolean - */ - protected function mailSend($header, $body) - { - $toArr = array(); - foreach ($this->to as $toaddr) { - $toArr[] = $this->addrFormat($toaddr); - } - $to = implode(', ', $toArr); - - $params = null; - //This sets the SMTP envelope sender which gets turned into a return-path header by the receiver - if (!empty($this->Sender)) { - $params = sprintf('-f%s', $this->Sender); - } - if ($this->Sender != '' and !ini_get('safe_mode')) { - $old_from = ini_get('sendmail_from'); - ini_set('sendmail_from', $this->Sender); - } - $result = false; - if ($this->SingleTo and count($toArr) > 1) { - foreach ($toArr as $toAddr) { - $result = $this->mailPassthru($toAddr, $this->Subject, $body, $header, $params); - $this->doCallback($result, array($toAddr), $this->cc, $this->bcc, $this->Subject, $body, $this->From); - } - } else { - $result = $this->mailPassthru($to, $this->Subject, $body, $header, $params); - $this->doCallback($result, $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From); - } - if (isset($old_from)) { - ini_set('sendmail_from', $old_from); - } - if (!$result) { - throw new phpmailerException($this->lang('instantiate'), self::STOP_CRITICAL); - } - return true; - } - - /** - * Get an instance to use for SMTP operations. - * Override this function to load your own SMTP implementation - * @return SMTP - */ - public function getSMTPInstance() - { - if (!is_object($this->smtp)) { - $this->smtp = new SMTP; - } - return $this->smtp; - } - - /** - * Send mail via SMTP. - * Returns false if there is a bad MAIL FROM, RCPT, or DATA input. - * Uses the PHPMailerSMTP class by default. - * @see PHPMailer::getSMTPInstance() to use a different class. - * @param string $header The message headers - * @param string $body The message body - * @throws phpmailerException - * @uses SMTP - * @access protected - * @return boolean - */ - protected function smtpSend($header, $body) - { - $bad_rcpt = array(); - if (!$this->smtpConnect($this->SMTPOptions)) { - throw new phpmailerException($this->lang('smtp_connect_failed'), self::STOP_CRITICAL); - } - if ('' == $this->Sender) { - $smtp_from = $this->From; - } else { - $smtp_from = $this->Sender; - } - if (!$this->smtp->mail($smtp_from)) { - $this->setError($this->lang('from_failed') . $smtp_from . ' : ' . implode(',', $this->smtp->getError())); - throw new phpmailerException($this->ErrorInfo, self::STOP_CRITICAL); - } - - // Attempt to send to all recipients - foreach (array($this->to, $this->cc, $this->bcc) as $togroup) { - foreach ($togroup as $to) { - if (!$this->smtp->recipient($to[0])) { - $error = $this->smtp->getError(); - $bad_rcpt[] = array('to' => $to[0], 'error' => $error['detail']); - $isSent = false; - } else { - $isSent = true; - } - $this->doCallback($isSent, array($to[0]), array(), array(), $this->Subject, $body, $this->From); - } - } - - // Only send the DATA command if we have viable recipients - if ((count($this->all_recipients) > count($bad_rcpt)) and !$this->smtp->data($header . $body)) { - throw new phpmailerException($this->lang('data_not_accepted'), self::STOP_CRITICAL); - } - if ($this->SMTPKeepAlive) { - $this->smtp->reset(); - } else { - $this->smtp->quit(); - $this->smtp->close(); - } - //Create error message for any bad addresses - if (count($bad_rcpt) > 0) { - $errstr = ''; - foreach ($bad_rcpt as $bad) { - $errstr .= $bad['to'] . ': ' . $bad['error']; - } - throw new phpmailerException( - $this->lang('recipients_failed') . $errstr, - self::STOP_CONTINUE - ); - } - return true; - } - - /** - * Initiate a connection to an SMTP server. - * Returns false if the operation failed. - * @param array $options An array of options compatible with stream_context_create() - * @uses SMTP - * @access public - * @throws phpmailerException - * @return boolean - */ - public function smtpConnect($options = null) - { - if (is_null($this->smtp)) { - $this->smtp = $this->getSMTPInstance(); - } - - //If no options are provided, use whatever is set in the instance - if (is_null($options)) { - $options = $this->SMTPOptions; - } - - // Already connected? - if ($this->smtp->connected()) { - return true; - } - - $this->smtp->setTimeout($this->Timeout); - $this->smtp->setDebugLevel($this->SMTPDebug); - $this->smtp->setDebugOutput($this->Debugoutput); - $this->smtp->setVerp($this->do_verp); - $hosts = explode(';', $this->Host); - $lastexception = null; - - foreach ($hosts as $hostentry) { - $hostinfo = array(); - if (!preg_match('/^((ssl|tls):\/\/)*([a-zA-Z0-9\.-]*):?([0-9]*)$/', trim($hostentry), $hostinfo)) { - // Not a valid host entry - continue; - } - // $hostinfo[2]: optional ssl or tls prefix - // $hostinfo[3]: the hostname - // $hostinfo[4]: optional port number - // The host string prefix can temporarily override the current setting for SMTPSecure - // If it's not specified, the default value is used - $prefix = ''; - $secure = $this->SMTPSecure; - $tls = ($this->SMTPSecure == 'tls'); - if ('ssl' == $hostinfo[2] or ('' == $hostinfo[2] and 'ssl' == $this->SMTPSecure)) { - $prefix = 'ssl://'; - $tls = false; // Can't have SSL and TLS at the same time - $secure = 'ssl'; - } elseif ($hostinfo[2] == 'tls') { - $tls = true; - // tls doesn't use a prefix - $secure = 'tls'; - } - //Do we need the OpenSSL extension? - $sslext = defined('OPENSSL_ALGO_SHA1'); - if ('tls' === $secure or 'ssl' === $secure) { - //Check for an OpenSSL constant rather than using extension_loaded, which is sometimes disabled - if (!$sslext) { - throw new phpmailerException($this->lang('extension_missing').'openssl', self::STOP_CRITICAL); - } - } - $host = $hostinfo[3]; - $port = $this->Port; - $tport = (integer)$hostinfo[4]; - if ($tport > 0 and $tport < 65536) { - $port = $tport; - } - if ($this->smtp->connect($prefix . $host, $port, $this->Timeout, $options)) { - try { - if ($this->Helo) { - $hello = $this->Helo; - } else { - $hello = $this->serverHostname(); - } - $this->smtp->hello($hello); - //Automatically enable TLS encryption if: - // * it's not disabled - // * we have openssl extension - // * we are not already using SSL - // * the server offers STARTTLS - if ($this->SMTPAutoTLS and $sslext and $secure != 'ssl' and $this->smtp->getServerExt('STARTTLS')) { - $tls = true; - } - if ($tls) { - if (!$this->smtp->startTLS()) { - throw new phpmailerException($this->lang('connect_host')); - } - // We must resend EHLO after TLS negotiation - $this->smtp->hello($hello); - } - if ($this->SMTPAuth) { - if (!$this->smtp->authenticate( - $this->Username, - $this->Password, - $this->AuthType, - $this->Realm, - $this->Workstation - ) - ) { - throw new phpmailerException($this->lang('authenticate')); - } - } - return true; - } catch (phpmailerException $exc) { - $lastexception = $exc; - $this->edebug($exc->getMessage()); - // We must have connected, but then failed TLS or Auth, so close connection nicely - $this->smtp->quit(); - } - } - } - // If we get here, all connection attempts have failed, so close connection hard - $this->smtp->close(); - // As we've caught all exceptions, just report whatever the last one was - if ($this->exceptions and !is_null($lastexception)) { - throw $lastexception; - } - return false; - } - - /** - * Close the active SMTP session if one exists. - * @return void - */ - public function smtpClose() - { - if (is_a($this->smtp, 'SMTP')) { - if ($this->smtp->connected()) { - $this->smtp->quit(); - $this->smtp->close(); - } - } - } - - /** - * Set the language for error messages. - * Returns false if it cannot load the language file. - * The default language is English. - * @param string $langcode ISO 639-1 2-character language code (e.g. French is "fr") - * @param string $lang_path Path to the language file directory, with trailing separator (slash) - * @return boolean - * @access public - */ - public function setLanguage($langcode = 'en', $lang_path = '') - { - // Define full set of translatable strings in English - $PHPMAILER_LANG = array( - 'authenticate' => 'SMTP Error: Could not authenticate.', - 'connect_host' => 'SMTP Error: Could not connect to SMTP host.', - 'data_not_accepted' => 'SMTP Error: data not accepted.', - 'empty_message' => 'Message body empty', - 'encoding' => 'Unknown encoding: ', - 'execute' => 'Could not execute: ', - 'file_access' => 'Could not access file: ', - 'file_open' => 'File Error: Could not open file: ', - 'from_failed' => 'The following From address failed: ', - 'instantiate' => 'Could not instantiate mail function.', - 'invalid_address' => 'Invalid address: ', - 'mailer_not_supported' => ' mailer is not supported.', - 'provide_address' => 'You must provide at least one recipient email address.', - 'recipients_failed' => 'SMTP Error: The following recipients failed: ', - 'signing' => 'Signing Error: ', - 'smtp_connect_failed' => 'SMTP connect() failed.', - 'smtp_error' => 'SMTP server error: ', - 'variable_set' => 'Cannot set or reset variable: ', - 'extension_missing' => 'Extension missing: ' - ); - if (empty($lang_path)) { - // Calculate an absolute path so it can work if CWD is not here - $lang_path = dirname(__FILE__). DIRECTORY_SEPARATOR . 'language'. DIRECTORY_SEPARATOR; - } - $foundlang = true; - $lang_file = $lang_path . 'phpmailer.lang-' . $langcode . '.php'; - // There is no English translation file - if ($langcode != 'en') { - // Make sure language file path is readable - if (!is_readable($lang_file)) { - $foundlang = false; - } else { - // Overwrite language-specific strings. - // This way we'll never have missing translation keys. - $foundlang = include $lang_file; - } - } - $this->language = $PHPMAILER_LANG; - return (boolean)$foundlang; // Returns false if language not found - } - - /** - * Get the array of strings for the current language. - * @return array - */ - public function getTranslations() - { - return $this->language; - } - - /** - * Create recipient headers. - * @access public - * @param string $type - * @param array $addr An array of recipient, - * where each recipient is a 2-element indexed array with element 0 containing an address - * and element 1 containing a name, like: - * array(array('joe@example.com', 'Joe User'), array('zoe@example.com', 'Zoe User')) - * @return string - */ - public function addrAppend($type, $addr) - { - $addresses = array(); - foreach ($addr as $address) { - $addresses[] = $this->addrFormat($address); - } - return $type . ': ' . implode(', ', $addresses) . $this->LE; - } - - /** - * Format an address for use in a message header. - * @access public - * @param array $addr A 2-element indexed array, element 0 containing an address, element 1 containing a name - * like array('joe@example.com', 'Joe User') - * @return string - */ - public function addrFormat($addr) - { - if (empty($addr[1])) { // No name provided - return $this->secureHeader($addr[0]); - } else { - return $this->encodeHeader($this->secureHeader($addr[1]), 'phrase') . ' <' . $this->secureHeader( - $addr[0] - ) . '>'; - } - } - - /** - * Word-wrap message. - * For use with mailers that do not automatically perform wrapping - * and for quoted-printable encoded messages. - * Original written by philippe. - * @param string $message The message to wrap - * @param integer $length The line length to wrap to - * @param boolean $qp_mode Whether to run in Quoted-Printable mode - * @access public - * @return string - */ - public function wrapText($message, $length, $qp_mode = false) - { - if ($qp_mode) { - $soft_break = sprintf(' =%s', $this->LE); - } else { - $soft_break = $this->LE; - } - // If utf-8 encoding is used, we will need to make sure we don't - // split multibyte characters when we wrap - $is_utf8 = (strtolower($this->CharSet) == 'utf-8'); - $lelen = strlen($this->LE); - $crlflen = strlen(self::CRLF); - - $message = $this->fixEOL($message); - //Remove a trailing line break - if (substr($message, -$lelen) == $this->LE) { - $message = substr($message, 0, -$lelen); - } - - //Split message into lines - $lines = explode($this->LE, $message); - //Message will be rebuilt in here - $message = ''; - foreach ($lines as $line) { - $words = explode(' ', $line); - $buf = ''; - $firstword = true; - foreach ($words as $word) { - if ($qp_mode and (strlen($word) > $length)) { - $space_left = $length - strlen($buf) - $crlflen; - if (!$firstword) { - if ($space_left > 20) { - $len = $space_left; - if ($is_utf8) { - $len = $this->utf8CharBoundary($word, $len); - } elseif (substr($word, $len - 1, 1) == '=') { - $len--; - } elseif (substr($word, $len - 2, 1) == '=') { - $len -= 2; - } - $part = substr($word, 0, $len); - $word = substr($word, $len); - $buf .= ' ' . $part; - $message .= $buf . sprintf('=%s', self::CRLF); - } else { - $message .= $buf . $soft_break; - } - $buf = ''; - } - while (strlen($word) > 0) { - if ($length <= 0) { - break; - } - $len = $length; - if ($is_utf8) { - $len = $this->utf8CharBoundary($word, $len); - } elseif (substr($word, $len - 1, 1) == '=') { - $len--; - } elseif (substr($word, $len - 2, 1) == '=') { - $len -= 2; - } - $part = substr($word, 0, $len); - $word = substr($word, $len); - - if (strlen($word) > 0) { - $message .= $part . sprintf('=%s', self::CRLF); - } else { - $buf = $part; - } - } - } else { - $buf_o = $buf; - if (!$firstword) { - $buf .= ' '; - } - $buf .= $word; - - if (strlen($buf) > $length and $buf_o != '') { - $message .= $buf_o . $soft_break; - $buf = $word; - } - } - $firstword = false; - } - $message .= $buf . self::CRLF; - } - - return $message; - } - - /** - * Find the last character boundary prior to $maxLength in a utf-8 - * quoted-printable encoded string. - * Original written by Colin Brown. - * @access public - * @param string $encodedText utf-8 QP text - * @param integer $maxLength Find the last character boundary prior to this length - * @return integer - */ - public function utf8CharBoundary($encodedText, $maxLength) - { - $foundSplitPos = false; - $lookBack = 3; - while (!$foundSplitPos) { - $lastChunk = substr($encodedText, $maxLength - $lookBack, $lookBack); - $encodedCharPos = strpos($lastChunk, '='); - if (false !== $encodedCharPos) { - // Found start of encoded character byte within $lookBack block. - // Check the encoded byte value (the 2 chars after the '=') - $hex = substr($encodedText, $maxLength - $lookBack + $encodedCharPos + 1, 2); - $dec = hexdec($hex); - if ($dec < 128) { - // Single byte character. - // If the encoded char was found at pos 0, it will fit - // otherwise reduce maxLength to start of the encoded char - if ($encodedCharPos > 0) { - $maxLength = $maxLength - ($lookBack - $encodedCharPos); - } - $foundSplitPos = true; - } elseif ($dec >= 192) { - // First byte of a multi byte character - // Reduce maxLength to split at start of character - $maxLength = $maxLength - ($lookBack - $encodedCharPos); - $foundSplitPos = true; - } elseif ($dec < 192) { - // Middle byte of a multi byte character, look further back - $lookBack += 3; - } - } else { - // No encoded character found - $foundSplitPos = true; - } - } - return $maxLength; - } - - /** - * Apply word wrapping to the message body. - * Wraps the message body to the number of chars set in the WordWrap property. - * You should only do this to plain-text bodies as wrapping HTML tags may break them. - * This is called automatically by createBody(), so you don't need to call it yourself. - * @access public - * @return void - */ - public function setWordWrap() - { - if ($this->WordWrap < 1) { - return; - } - - switch ($this->message_type) { - case 'alt': - case 'alt_inline': - case 'alt_attach': - case 'alt_inline_attach': - $this->AltBody = $this->wrapText($this->AltBody, $this->WordWrap); - break; - default: - $this->Body = $this->wrapText($this->Body, $this->WordWrap); - break; - } - } - - /** - * Assemble message headers. - * @access public - * @return string The assembled headers - */ - public function createHeader() - { - $result = ''; - - if ($this->MessageDate == '') { - $this->MessageDate = self::rfcDate(); - } - $result .= $this->headerLine('Date', $this->MessageDate); - - // To be created automatically by mail() - if ($this->SingleTo) { - if ($this->Mailer != 'mail') { - foreach ($this->to as $toaddr) { - $this->SingleToArray[] = $this->addrFormat($toaddr); - } - } - } else { - if (count($this->to) > 0) { - if ($this->Mailer != 'mail') { - $result .= $this->addrAppend('To', $this->to); - } - } elseif (count($this->cc) == 0) { - $result .= $this->headerLine('To', 'undisclosed-recipients:;'); - } - } - - $result .= $this->addrAppend('From', array(array(trim($this->From), $this->FromName))); - - // sendmail and mail() extract Cc from the header before sending - if (count($this->cc) > 0) { - $result .= $this->addrAppend('Cc', $this->cc); - } - - // sendmail and mail() extract Bcc from the header before sending - if (( - $this->Mailer == 'sendmail' or $this->Mailer == 'qmail' or $this->Mailer == 'mail' - ) - and count($this->bcc) > 0 - ) { - $result .= $this->addrAppend('Bcc', $this->bcc); - } - - if (count($this->ReplyTo) > 0) { - $result .= $this->addrAppend('Reply-To', $this->ReplyTo); - } - - // mail() sets the subject itself - if ($this->Mailer != 'mail') { - $result .= $this->headerLine('Subject', $this->encodeHeader($this->secureHeader($this->Subject))); - } - - if ('' != $this->MessageID and preg_match('/^<.*@.*>$/', $this->MessageID)) { - $this->lastMessageID = $this->MessageID; - } else { - $this->lastMessageID = sprintf('<%s@%s>', $this->uniqueid, $this->serverHostname()); - } - $result .= $this->headerLine('Message-ID', $this->lastMessageID); - if (!is_null($this->Priority)) { - $result .= $this->headerLine('X-Priority', $this->Priority); - } - if ($this->XMailer == '') { - $result .= $this->headerLine( - 'X-Mailer', - 'PHPMailer ' . $this->Version . ' (https://github.com/PHPMailer/PHPMailer)' - ); - } else { - $myXmailer = trim($this->XMailer); - if ($myXmailer) { - $result .= $this->headerLine('X-Mailer', $myXmailer); - } - } - - if ($this->ConfirmReadingTo != '') { - $result .= $this->headerLine('Disposition-Notification-To', '<' . $this->ConfirmReadingTo . '>'); - } - - // Add custom headers - foreach ($this->CustomHeader as $header) { - $result .= $this->headerLine( - trim($header[0]), - $this->encodeHeader(trim($header[1])) - ); - } - if (!$this->sign_key_file) { - $result .= $this->headerLine('MIME-Version', '1.0'); - $result .= $this->getMailMIME(); - } - - return $result; - } - - /** - * Get the message MIME type headers. - * @access public - * @return string - */ - public function getMailMIME() - { - $result = ''; - $ismultipart = true; - switch ($this->message_type) { - case 'inline': - $result .= $this->headerLine('Content-Type', 'multipart/related;'); - $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"'); - break; - case 'attach': - case 'inline_attach': - case 'alt_attach': - case 'alt_inline_attach': - $result .= $this->headerLine('Content-Type', 'multipart/mixed;'); - $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"'); - break; - case 'alt': - case 'alt_inline': - $result .= $this->headerLine('Content-Type', 'multipart/alternative;'); - $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"'); - break; - default: - // Catches case 'plain': and case '': - $result .= $this->textLine('Content-Type: ' . $this->ContentType . '; charset=' . $this->CharSet); - $ismultipart = false; - break; - } - // RFC1341 part 5 says 7bit is assumed if not specified - if ($this->Encoding != '7bit') { - // RFC 2045 section 6.4 says multipart MIME parts may only use 7bit, 8bit or binary CTE - if ($ismultipart) { - if ($this->Encoding == '8bit') { - $result .= $this->headerLine('Content-Transfer-Encoding', '8bit'); - } - // The only remaining alternatives are quoted-printable and base64, which are both 7bit compatible - } else { - $result .= $this->headerLine('Content-Transfer-Encoding', $this->Encoding); - } - } - - if ($this->Mailer != 'mail') { - $result .= $this->LE; - } - - return $result; - } - - /** - * Returns the whole MIME message. - * Includes complete headers and body. - * Only valid post preSend(). - * @see PHPMailer::preSend() - * @access public - * @return string - */ - public function getSentMIMEMessage() - { - return rtrim($this->MIMEHeader . $this->mailHeader, "\n\r") . self::CRLF . self::CRLF . $this->MIMEBody; - } - - /** - * Assemble the message body. - * Returns an empty string on failure. - * @access public - * @throws phpmailerException - * @return string The assembled message body - */ - public function createBody() - { - $body = ''; - //Create unique IDs and preset boundaries - $this->uniqueid = md5(uniqid(time())); - $this->boundary[1] = 'b1_' . $this->uniqueid; - $this->boundary[2] = 'b2_' . $this->uniqueid; - $this->boundary[3] = 'b3_' . $this->uniqueid; - - if ($this->sign_key_file) { - $body .= $this->getMailMIME() . $this->LE; - } - - $this->setWordWrap(); - - $bodyEncoding = $this->Encoding; - $bodyCharSet = $this->CharSet; - //Can we do a 7-bit downgrade? - if ($bodyEncoding == '8bit' and !$this->has8bitChars($this->Body)) { - $bodyEncoding = '7bit'; - //All ISO 8859, Windows codepage and UTF-8 charsets are ascii compatible up to 7-bit - $bodyCharSet = 'us-ascii'; - } - //If lines are too long, and we're not already using an encoding that will shorten them, - //change to quoted-printable transfer encoding for the body part only - if ('base64' != $this->Encoding and self::hasLineLongerThanMax($this->Body)) { - $bodyEncoding = 'quoted-printable'; - } - - $altBodyEncoding = $this->Encoding; - $altBodyCharSet = $this->CharSet; - //Can we do a 7-bit downgrade? - if ($altBodyEncoding == '8bit' and !$this->has8bitChars($this->AltBody)) { - $altBodyEncoding = '7bit'; - //All ISO 8859, Windows codepage and UTF-8 charsets are ascii compatible up to 7-bit - $altBodyCharSet = 'us-ascii'; - } - //If lines are too long, and we're not already using an encoding that will shorten them, - //change to quoted-printable transfer encoding for the alt body part only - if ('base64' != $altBodyEncoding and self::hasLineLongerThanMax($this->AltBody)) { - $altBodyEncoding = 'quoted-printable'; - } - //Use this as a preamble in all multipart message types - $mimepre = "This is a multi-part message in MIME format." . $this->LE . $this->LE; - switch ($this->message_type) { - case 'inline': - $body .= $mimepre; - $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding); - $body .= $this->encodeString($this->Body, $bodyEncoding); - $body .= $this->LE . $this->LE; - $body .= $this->attachAll('inline', $this->boundary[1]); - break; - case 'attach': - $body .= $mimepre; - $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding); - $body .= $this->encodeString($this->Body, $bodyEncoding); - $body .= $this->LE . $this->LE; - $body .= $this->attachAll('attachment', $this->boundary[1]); - break; - case 'inline_attach': - $body .= $mimepre; - $body .= $this->textLine('--' . $this->boundary[1]); - $body .= $this->headerLine('Content-Type', 'multipart/related;'); - $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"'); - $body .= $this->LE; - $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, '', $bodyEncoding); - $body .= $this->encodeString($this->Body, $bodyEncoding); - $body .= $this->LE . $this->LE; - $body .= $this->attachAll('inline', $this->boundary[2]); - $body .= $this->LE; - $body .= $this->attachAll('attachment', $this->boundary[1]); - break; - case 'alt': - $body .= $mimepre; - $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding); - $body .= $this->encodeString($this->AltBody, $altBodyEncoding); - $body .= $this->LE . $this->LE; - $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, 'text/html', $bodyEncoding); - $body .= $this->encodeString($this->Body, $bodyEncoding); - $body .= $this->LE . $this->LE; - if (!empty($this->Ical)) { - $body .= $this->getBoundary($this->boundary[1], '', 'text/calendar; method=REQUEST', ''); - $body .= $this->encodeString($this->Ical, $this->Encoding); - $body .= $this->LE . $this->LE; - } - $body .= $this->endBoundary($this->boundary[1]); - break; - case 'alt_inline': - $body .= $mimepre; - $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding); - $body .= $this->encodeString($this->AltBody, $altBodyEncoding); - $body .= $this->LE . $this->LE; - $body .= $this->textLine('--' . $this->boundary[1]); - $body .= $this->headerLine('Content-Type', 'multipart/related;'); - $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"'); - $body .= $this->LE; - $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding); - $body .= $this->encodeString($this->Body, $bodyEncoding); - $body .= $this->LE . $this->LE; - $body .= $this->attachAll('inline', $this->boundary[2]); - $body .= $this->LE; - $body .= $this->endBoundary($this->boundary[1]); - break; - case 'alt_attach': - $body .= $mimepre; - $body .= $this->textLine('--' . $this->boundary[1]); - $body .= $this->headerLine('Content-Type', 'multipart/alternative;'); - $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"'); - $body .= $this->LE; - $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding); - $body .= $this->encodeString($this->AltBody, $altBodyEncoding); - $body .= $this->LE . $this->LE; - $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding); - $body .= $this->encodeString($this->Body, $bodyEncoding); - $body .= $this->LE . $this->LE; - $body .= $this->endBoundary($this->boundary[2]); - $body .= $this->LE; - $body .= $this->attachAll('attachment', $this->boundary[1]); - break; - case 'alt_inline_attach': - $body .= $mimepre; - $body .= $this->textLine('--' . $this->boundary[1]); - $body .= $this->headerLine('Content-Type', 'multipart/alternative;'); - $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"'); - $body .= $this->LE; - $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding); - $body .= $this->encodeString($this->AltBody, $altBodyEncoding); - $body .= $this->LE . $this->LE; - $body .= $this->textLine('--' . $this->boundary[2]); - $body .= $this->headerLine('Content-Type', 'multipart/related;'); - $body .= $this->textLine("\tboundary=\"" . $this->boundary[3] . '"'); - $body .= $this->LE; - $body .= $this->getBoundary($this->boundary[3], $bodyCharSet, 'text/html', $bodyEncoding); - $body .= $this->encodeString($this->Body, $bodyEncoding); - $body .= $this->LE . $this->LE; - $body .= $this->attachAll('inline', $this->boundary[3]); - $body .= $this->LE; - $body .= $this->endBoundary($this->boundary[2]); - $body .= $this->LE; - $body .= $this->attachAll('attachment', $this->boundary[1]); - break; - default: - // Catch case 'plain' and case '', applies to simple `text/plain` and `text/html` body content types - //Reset the `Encoding` property in case we changed it for line length reasons - $this->Encoding = $bodyEncoding; - $body .= $this->encodeString($this->Body, $this->Encoding); - break; - } - - if ($this->isError()) { - $body = ''; - } elseif ($this->sign_key_file) { - try { - if (!defined('PKCS7_TEXT')) { - throw new phpmailerException($this->lang('extension_missing') . 'openssl'); - } - // @TODO would be nice to use php://temp streams here, but need to wrap for PHP < 5.1 - $file = tempnam(sys_get_temp_dir(), 'mail'); - if (false === file_put_contents($file, $body)) { - throw new phpmailerException($this->lang('signing') . ' Could not write temp file'); - } - $signed = tempnam(sys_get_temp_dir(), 'signed'); - //Workaround for PHP bug https://bugs.php.net/bug.php?id=69197 - if (empty($this->sign_extracerts_file)) { - $sign = @openssl_pkcs7_sign( - $file, - $signed, - 'file://' . realpath($this->sign_cert_file), - array('file://' . realpath($this->sign_key_file), $this->sign_key_pass), - null - ); - } else { - $sign = @openssl_pkcs7_sign( - $file, - $signed, - 'file://' . realpath($this->sign_cert_file), - array('file://' . realpath($this->sign_key_file), $this->sign_key_pass), - null, - PKCS7_DETACHED, - $this->sign_extracerts_file - ); - } - if ($sign) { - @unlink($file); - $body = file_get_contents($signed); - @unlink($signed); - //The message returned by openssl contains both headers and body, so need to split them up - $parts = explode("\n\n", $body, 2); - $this->MIMEHeader .= $parts[0] . $this->LE . $this->LE; - $body = $parts[1]; - } else { - @unlink($file); - @unlink($signed); - throw new phpmailerException($this->lang('signing') . openssl_error_string()); - } - } catch (phpmailerException $exc) { - $body = ''; - if ($this->exceptions) { - throw $exc; - } - } - } - return $body; - } - - /** - * Return the start of a message boundary. - * @access protected - * @param string $boundary - * @param string $charSet - * @param string $contentType - * @param string $encoding - * @return string - */ - protected function getBoundary($boundary, $charSet, $contentType, $encoding) - { - $result = ''; - if ($charSet == '') { - $charSet = $this->CharSet; - } - if ($contentType == '') { - $contentType = $this->ContentType; - } - if ($encoding == '') { - $encoding = $this->Encoding; - } - $result .= $this->textLine('--' . $boundary); - $result .= sprintf('Content-Type: %s; charset=%s', $contentType, $charSet); - $result .= $this->LE; - // RFC1341 part 5 says 7bit is assumed if not specified - if ($encoding != '7bit') { - $result .= $this->headerLine('Content-Transfer-Encoding', $encoding); - } - $result .= $this->LE; - - return $result; - } - - /** - * Return the end of a message boundary. - * @access protected - * @param string $boundary - * @return string - */ - protected function endBoundary($boundary) - { - return $this->LE . '--' . $boundary . '--' . $this->LE; - } - - /** - * Set the message type. - * PHPMailer only supports some preset message types, not arbitrary MIME structures. - * @access protected - * @return void - */ - protected function setMessageType() - { - $type = array(); - if ($this->alternativeExists()) { - $type[] = 'alt'; - } - if ($this->inlineImageExists()) { - $type[] = 'inline'; - } - if ($this->attachmentExists()) { - $type[] = 'attach'; - } - $this->message_type = implode('_', $type); - if ($this->message_type == '') { - //The 'plain' message_type refers to the message having a single body element, not that it is plain-text - $this->message_type = 'plain'; - } - } - - /** - * Format a header line. - * @access public - * @param string $name - * @param string $value - * @return string - */ - public function headerLine($name, $value) - { - return $name . ': ' . $value . $this->LE; - } - - /** - * Return a formatted mail line. - * @access public - * @param string $value - * @return string - */ - public function textLine($value) - { - return $value . $this->LE; - } - - /** - * Add an attachment from a path on the filesystem. - * Returns false if the file could not be found or read. - * @param string $path Path to the attachment. - * @param string $name Overrides the attachment name. - * @param string $encoding File encoding (see $Encoding). - * @param string $type File extension (MIME) type. - * @param string $disposition Disposition to use - * @throws phpmailerException - * @return boolean - */ - public function addAttachment($path, $name = '', $encoding = 'base64', $type = '', $disposition = 'attachment') - { - try { - if (!@is_file($path)) { - throw new phpmailerException($this->lang('file_access') . $path, self::STOP_CONTINUE); - } - - // If a MIME type is not specified, try to work it out from the file name - if ($type == '') { - $type = self::filenameToType($path); - } - - $filename = basename($path); - if ($name == '') { - $name = $filename; - } - - $this->attachment[] = array( - 0 => $path, - 1 => $filename, - 2 => $name, - 3 => $encoding, - 4 => $type, - 5 => false, // isStringAttachment - 6 => $disposition, - 7 => 0 - ); - - } catch (phpmailerException $exc) { - $this->setError($exc->getMessage()); - $this->edebug($exc->getMessage()); - if ($this->exceptions) { - throw $exc; - } - return false; - } - return true; - } - - /** - * Return the array of attachments. - * @return array - */ - public function getAttachments() - { - return $this->attachment; - } - - /** - * Attach all file, string, and binary attachments to the message. - * Returns an empty string on failure. - * @access protected - * @param string $disposition_type - * @param string $boundary - * @return string - */ - protected function attachAll($disposition_type, $boundary) - { - // Return text of body - $mime = array(); - $cidUniq = array(); - $incl = array(); - - // Add all attachments - foreach ($this->attachment as $attachment) { - // Check if it is a valid disposition_filter - if ($attachment[6] == $disposition_type) { - // Check for string attachment - $string = ''; - $path = ''; - $bString = $attachment[5]; - if ($bString) { - $string = $attachment[0]; - } else { - $path = $attachment[0]; - } - - $inclhash = md5(serialize($attachment)); - if (in_array($inclhash, $incl)) { - continue; - } - $incl[] = $inclhash; - $name = $attachment[2]; - $encoding = $attachment[3]; - $type = $attachment[4]; - $disposition = $attachment[6]; - $cid = $attachment[7]; - if ($disposition == 'inline' && array_key_exists($cid, $cidUniq)) { - continue; - } - $cidUniq[$cid] = true; - - $mime[] = sprintf('--%s%s', $boundary, $this->LE); - //Only include a filename property if we have one - if (!empty($name)) { - $mime[] = sprintf( - 'Content-Type: %s; name="%s"%s', - $type, - $this->encodeHeader($this->secureHeader($name)), - $this->LE - ); - } else { - $mime[] = sprintf( - 'Content-Type: %s%s', - $type, - $this->LE - ); - } - // RFC1341 part 5 says 7bit is assumed if not specified - if ($encoding != '7bit') { - $mime[] = sprintf('Content-Transfer-Encoding: %s%s', $encoding, $this->LE); - } - - if ($disposition == 'inline') { - $mime[] = sprintf('Content-ID: <%s>%s', $cid, $this->LE); - } - - // If a filename contains any of these chars, it should be quoted, - // but not otherwise: RFC2183 & RFC2045 5.1 - // Fixes a warning in IETF's msglint MIME checker - // Allow for bypassing the Content-Disposition header totally - if (!(empty($disposition))) { - $encoded_name = $this->encodeHeader($this->secureHeader($name)); - if (preg_match('/[ \(\)<>@,;:\\"\/\[\]\?=]/', $encoded_name)) { - $mime[] = sprintf( - 'Content-Disposition: %s; filename="%s"%s', - $disposition, - $encoded_name, - $this->LE . $this->LE - ); - } else { - if (!empty($encoded_name)) { - $mime[] = sprintf( - 'Content-Disposition: %s; filename=%s%s', - $disposition, - $encoded_name, - $this->LE . $this->LE - ); - } else { - $mime[] = sprintf( - 'Content-Disposition: %s%s', - $disposition, - $this->LE . $this->LE - ); - } - } - } else { - $mime[] = $this->LE; - } - - // Encode as string attachment - if ($bString) { - $mime[] = $this->encodeString($string, $encoding); - if ($this->isError()) { - return ''; - } - $mime[] = $this->LE . $this->LE; - } else { - $mime[] = $this->encodeFile($path, $encoding); - if ($this->isError()) { - return ''; - } - $mime[] = $this->LE . $this->LE; - } - } - } - - $mime[] = sprintf('--%s--%s', $boundary, $this->LE); - - return implode('', $mime); - } - - /** - * Encode a file attachment in requested format. - * Returns an empty string on failure. - * @param string $path The full path to the file - * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable' - * @throws phpmailerException - * @access protected - * @return string - */ - protected function encodeFile($path, $encoding = 'base64') - { - try { - if (!is_readable($path)) { - throw new phpmailerException($this->lang('file_open') . $path, self::STOP_CONTINUE); - } - $magic_quotes = get_magic_quotes_runtime(); - if ($magic_quotes) { - if (version_compare(PHP_VERSION, '5.3.0', '<')) { - set_magic_quotes_runtime(false); - } else { - //Doesn't exist in PHP 5.4, but we don't need to check because - //get_magic_quotes_runtime always returns false in 5.4+ - //so it will never get here - ini_set('magic_quotes_runtime', false); - } - } - $file_buffer = file_get_contents($path); - $file_buffer = $this->encodeString($file_buffer, $encoding); - if ($magic_quotes) { - if (version_compare(PHP_VERSION, '5.3.0', '<')) { - set_magic_quotes_runtime($magic_quotes); - } else { - ini_set('magic_quotes_runtime', $magic_quotes); - } - } - return $file_buffer; - } catch (Exception $exc) { - $this->setError($exc->getMessage()); - return ''; - } - } - - /** - * Encode a string in requested format. - * Returns an empty string on failure. - * @param string $str The text to encode - * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable' - * @access public - * @return string - */ - public function encodeString($str, $encoding = 'base64') - { - $encoded = ''; - switch (strtolower($encoding)) { - case 'base64': - $encoded = chunk_split(base64_encode($str), 76, $this->LE); - break; - case '7bit': - case '8bit': - $encoded = $this->fixEOL($str); - // Make sure it ends with a line break - if (substr($encoded, -(strlen($this->LE))) != $this->LE) { - $encoded .= $this->LE; - } - break; - case 'binary': - $encoded = $str; - break; - case 'quoted-printable': - $encoded = $this->encodeQP($str); - break; - default: - $this->setError($this->lang('encoding') . $encoding); - break; - } - return $encoded; - } - - /** - * Encode a header string optimally. - * Picks shortest of Q, B, quoted-printable or none. - * @access public - * @param string $str - * @param string $position - * @return string - */ - public function encodeHeader($str, $position = 'text') - { - $matchcount = 0; - switch (strtolower($position)) { - case 'phrase': - if (!preg_match('/[\200-\377]/', $str)) { - // Can't use addslashes as we don't know the value of magic_quotes_sybase - $encoded = addcslashes($str, "\0..\37\177\\\""); - if (($str == $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) { - return ($encoded); - } else { - return ("\"$encoded\""); - } - } - $matchcount = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches); - break; - /** @noinspection PhpMissingBreakStatementInspection */ - case 'comment': - $matchcount = preg_match_all('/[()"]/', $str, $matches); - // Intentional fall-through - case 'text': - default: - $matchcount += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches); - break; - } - - //There are no chars that need encoding - if ($matchcount == 0) { - return ($str); - } - - $maxlen = 75 - 7 - strlen($this->CharSet); - // Try to select the encoding which should produce the shortest output - if ($matchcount > strlen($str) / 3) { - // More than a third of the content will need encoding, so B encoding will be most efficient - $encoding = 'B'; - if (function_exists('mb_strlen') && $this->hasMultiBytes($str)) { - // Use a custom function which correctly encodes and wraps long - // multibyte strings without breaking lines within a character - $encoded = $this->base64EncodeWrapMB($str, "\n"); - } else { - $encoded = base64_encode($str); - $maxlen -= $maxlen % 4; - $encoded = trim(chunk_split($encoded, $maxlen, "\n")); - } - } else { - $encoding = 'Q'; - $encoded = $this->encodeQ($str, $position); - $encoded = $this->wrapText($encoded, $maxlen, true); - $encoded = str_replace('=' . self::CRLF, "\n", trim($encoded)); - } - - $encoded = preg_replace('/^(.*)$/m', ' =?' . $this->CharSet . "?$encoding?\\1?=", $encoded); - $encoded = trim(str_replace("\n", $this->LE, $encoded)); - - return $encoded; - } - - /** - * Check if a string contains multi-byte characters. - * @access public - * @param string $str multi-byte text to wrap encode - * @return boolean - */ - public function hasMultiBytes($str) - { - if (function_exists('mb_strlen')) { - return (strlen($str) > mb_strlen($str, $this->CharSet)); - } else { // Assume no multibytes (we can't handle without mbstring functions anyway) - return false; - } - } - - /** - * Does a string contain any 8-bit chars (in any charset)? - * @param string $text - * @return boolean - */ - public function has8bitChars($text) - { - return (boolean)preg_match('/[\x80-\xFF]/', $text); - } - - /** - * Encode and wrap long multibyte strings for mail headers - * without breaking lines within a character. - * Adapted from a function by paravoid - * @link http://www.php.net/manual/en/function.mb-encode-mimeheader.php#60283 - * @access public - * @param string $str multi-byte text to wrap encode - * @param string $linebreak string to use as linefeed/end-of-line - * @return string - */ - public function base64EncodeWrapMB($str, $linebreak = null) - { - $start = '=?' . $this->CharSet . '?B?'; - $end = '?='; - $encoded = ''; - if ($linebreak === null) { - $linebreak = $this->LE; - } - - $mb_length = mb_strlen($str, $this->CharSet); - // Each line must have length <= 75, including $start and $end - $length = 75 - strlen($start) - strlen($end); - // Average multi-byte ratio - $ratio = $mb_length / strlen($str); - // Base64 has a 4:3 ratio - $avgLength = floor($length * $ratio * .75); - - for ($i = 0; $i < $mb_length; $i += $offset) { - $lookBack = 0; - do { - $offset = $avgLength - $lookBack; - $chunk = mb_substr($str, $i, $offset, $this->CharSet); - $chunk = base64_encode($chunk); - $lookBack++; - } while (strlen($chunk) > $length); - $encoded .= $chunk . $linebreak; - } - - // Chomp the last linefeed - $encoded = substr($encoded, 0, -strlen($linebreak)); - return $encoded; - } - - /** - * Encode a string in quoted-printable format. - * According to RFC2045 section 6.7. - * @access public - * @param string $string The text to encode - * @param integer $line_max Number of chars allowed on a line before wrapping - * @return string - * @link http://www.php.net/manual/en/function.quoted-printable-decode.php#89417 Adapted from this comment - */ - public function encodeQP($string, $line_max = 76) - { - // Use native function if it's available (>= PHP5.3) - if (function_exists('quoted_printable_encode')) { - return quoted_printable_encode($string); - } - // Fall back to a pure PHP implementation - $string = str_replace( - array('%20', '%0D%0A.', '%0D%0A', '%'), - array(' ', "\r\n=2E", "\r\n", '='), - rawurlencode($string) - ); - return preg_replace('/[^\r\n]{' . ($line_max - 3) . '}[^=\r\n]{2}/', "$0=\r\n", $string); - } - - /** - * Backward compatibility wrapper for an old QP encoding function that was removed. - * @see PHPMailer::encodeQP() - * @access public - * @param string $string - * @param integer $line_max - * @param boolean $space_conv - * @return string - * @deprecated Use encodeQP instead. - */ - public function encodeQPphp( - $string, - $line_max = 76, - /** @noinspection PhpUnusedParameterInspection */ $space_conv = false - ) { - return $this->encodeQP($string, $line_max); - } - - /** - * Encode a string using Q encoding. - * @link http://tools.ietf.org/html/rfc2047 - * @param string $str the text to encode - * @param string $position Where the text is going to be used, see the RFC for what that means - * @access public - * @return string - */ - public function encodeQ($str, $position = 'text') - { - // There should not be any EOL in the string - $pattern = ''; - $encoded = str_replace(array("\r", "\n"), '', $str); - switch (strtolower($position)) { - case 'phrase': - // RFC 2047 section 5.3 - $pattern = '^A-Za-z0-9!*+\/ -'; - break; - /** @noinspection PhpMissingBreakStatementInspection */ - case 'comment': - // RFC 2047 section 5.2 - $pattern = '\(\)"'; - // intentional fall-through - // for this reason we build the $pattern without including delimiters and [] - case 'text': - default: - // RFC 2047 section 5.1 - // Replace every high ascii, control, =, ? and _ characters - $pattern = '\000-\011\013\014\016-\037\075\077\137\177-\377' . $pattern; - break; - } - $matches = array(); - if (preg_match_all("/[{$pattern}]/", $encoded, $matches)) { - // If the string contains an '=', make sure it's the first thing we replace - // so as to avoid double-encoding - $eqkey = array_search('=', $matches[0]); - if (false !== $eqkey) { - unset($matches[0][$eqkey]); - array_unshift($matches[0], '='); - } - foreach (array_unique($matches[0]) as $char) { - $encoded = str_replace($char, '=' . sprintf('%02X', ord($char)), $encoded); - } - } - // Replace every spaces to _ (more readable than =20) - return str_replace(' ', '_', $encoded); - } - - /** - * Add a string or binary attachment (non-filesystem). - * This method can be used to attach ascii or binary data, - * such as a BLOB record from a database. - * @param string $string String attachment data. - * @param string $filename Name of the attachment. - * @param string $encoding File encoding (see $Encoding). - * @param string $type File extension (MIME) type. - * @param string $disposition Disposition to use - * @return void - */ - public function addStringAttachment( - $string, - $filename, - $encoding = 'base64', - $type = '', - $disposition = 'attachment' - ) { - // If a MIME type is not specified, try to work it out from the file name - if ($type == '') { - $type = self::filenameToType($filename); - } - // Append to $attachment array - $this->attachment[] = array( - 0 => $string, - 1 => $filename, - 2 => basename($filename), - 3 => $encoding, - 4 => $type, - 5 => true, // isStringAttachment - 6 => $disposition, - 7 => 0 - ); - } - - /** - * Add an embedded (inline) attachment from a file. - * This can include images, sounds, and just about any other document type. - * These differ from 'regular' attachments in that they are intended to be - * displayed inline with the message, not just attached for download. - * This is used in HTML messages that embed the images - * the HTML refers to using the $cid value. - * @param string $path Path to the attachment. - * @param string $cid Content ID of the attachment; Use this to reference - * the content when using an embedded image in HTML. - * @param string $name Overrides the attachment name. - * @param string $encoding File encoding (see $Encoding). - * @param string $type File MIME type. - * @param string $disposition Disposition to use - * @return boolean True on successfully adding an attachment - */ - public function addEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = '', $disposition = 'inline') - { - if (!@is_file($path)) { - $this->setError($this->lang('file_access') . $path); - return false; - } - - // If a MIME type is not specified, try to work it out from the file name - if ($type == '') { - $type = self::filenameToType($path); - } - - $filename = basename($path); - if ($name == '') { - $name = $filename; - } - - // Append to $attachment array - $this->attachment[] = array( - 0 => $path, - 1 => $filename, - 2 => $name, - 3 => $encoding, - 4 => $type, - 5 => false, // isStringAttachment - 6 => $disposition, - 7 => $cid - ); - return true; - } - - /** - * Add an embedded stringified attachment. - * This can include images, sounds, and just about any other document type. - * Be sure to set the $type to an image type for images: - * JPEG images use 'image/jpeg', GIF uses 'image/gif', PNG uses 'image/png'. - * @param string $string The attachment binary data. - * @param string $cid Content ID of the attachment; Use this to reference - * the content when using an embedded image in HTML. - * @param string $name - * @param string $encoding File encoding (see $Encoding). - * @param string $type MIME type. - * @param string $disposition Disposition to use - * @return boolean True on successfully adding an attachment - */ - public function addStringEmbeddedImage( - $string, - $cid, - $name = '', - $encoding = 'base64', - $type = '', - $disposition = 'inline' - ) { - // If a MIME type is not specified, try to work it out from the name - if ($type == '' and !empty($name)) { - $type = self::filenameToType($name); - } - - // Append to $attachment array - $this->attachment[] = array( - 0 => $string, - 1 => $name, - 2 => $name, - 3 => $encoding, - 4 => $type, - 5 => true, // isStringAttachment - 6 => $disposition, - 7 => $cid - ); - return true; - } - - /** - * Check if an inline attachment is present. - * @access public - * @return boolean - */ - public function inlineImageExists() - { - foreach ($this->attachment as $attachment) { - if ($attachment[6] == 'inline') { - return true; - } - } - return false; - } - - /** - * Check if an attachment (non-inline) is present. - * @return boolean - */ - public function attachmentExists() - { - foreach ($this->attachment as $attachment) { - if ($attachment[6] == 'attachment') { - return true; - } - } - return false; - } - - /** - * Check if this message has an alternative body set. - * @return boolean - */ - public function alternativeExists() - { - return !empty($this->AltBody); - } - - /** - * Clear queued addresses of given kind. - * @access protected - * @param string $kind 'to', 'cc', or 'bcc' - * @return void - */ - public function clearQueuedAddresses($kind) - { - $RecipientsQueue = $this->RecipientsQueue; - foreach ($RecipientsQueue as $address => $params) { - if ($params[0] == $kind) { - unset($this->RecipientsQueue[$address]); - } - } - } - - /** - * Clear all To recipients. - * @return void - */ - public function clearAddresses() - { - foreach ($this->to as $to) { - unset($this->all_recipients[strtolower($to[0])]); - } - $this->to = array(); - $this->clearQueuedAddresses('to'); - } - - /** - * Clear all CC recipients. - * @return void - */ - public function clearCCs() - { - foreach ($this->cc as $cc) { - unset($this->all_recipients[strtolower($cc[0])]); - } - $this->cc = array(); - $this->clearQueuedAddresses('cc'); - } - - /** - * Clear all BCC recipients. - * @return void - */ - public function clearBCCs() - { - foreach ($this->bcc as $bcc) { - unset($this->all_recipients[strtolower($bcc[0])]); - } - $this->bcc = array(); - $this->clearQueuedAddresses('bcc'); - } - - /** - * Clear all ReplyTo recipients. - * @return void - */ - public function clearReplyTos() - { - $this->ReplyTo = array(); - $this->ReplyToQueue = array(); - } - - /** - * Clear all recipient types. - * @return void - */ - public function clearAllRecipients() - { - $this->to = array(); - $this->cc = array(); - $this->bcc = array(); - $this->all_recipients = array(); - $this->RecipientsQueue = array(); - } - - /** - * Clear all filesystem, string, and binary attachments. - * @return void - */ - public function clearAttachments() - { - $this->attachment = array(); - } - - /** - * Clear all custom headers. - * @return void - */ - public function clearCustomHeaders() - { - $this->CustomHeader = array(); - } - - /** - * Add an error message to the error container. - * @access protected - * @param string $msg - * @return void - */ - protected function setError($msg) - { - $this->error_count++; - if ($this->Mailer == 'smtp' and !is_null($this->smtp)) { - $lasterror = $this->smtp->getError(); - if (!empty($lasterror['error'])) { - $msg .= $this->lang('smtp_error') . $lasterror['error']; - if (!empty($lasterror['detail'])) { - $msg .= ' Detail: '. $lasterror['detail']; - } - if (!empty($lasterror['smtp_code'])) { - $msg .= ' SMTP code: ' . $lasterror['smtp_code']; - } - if (!empty($lasterror['smtp_code_ex'])) { - $msg .= ' Additional SMTP info: ' . $lasterror['smtp_code_ex']; - } - } - } - $this->ErrorInfo = $msg; - } - - /** - * Return an RFC 822 formatted date. - * @access public - * @return string - * @static - */ - public static function rfcDate() - { - // Set the time zone to whatever the default is to avoid 500 errors - // Will default to UTC if it's not set properly in php.ini - date_default_timezone_set(@date_default_timezone_get()); - return date('D, j M Y H:i:s O'); - } - - /** - * Get the server hostname. - * Returns 'localhost.localdomain' if unknown. - * @access protected - * @return string - */ - protected function serverHostname() - { - $result = 'localhost.localdomain'; - if (!empty($this->Hostname)) { - $result = $this->Hostname; - } elseif (isset($_SERVER) and array_key_exists('SERVER_NAME', $_SERVER) and !empty($_SERVER['SERVER_NAME'])) { - $result = $_SERVER['SERVER_NAME']; - } elseif (function_exists('gethostname') && gethostname() !== false) { - $result = gethostname(); - } elseif (php_uname('n') !== false) { - $result = php_uname('n'); - } - return $result; - } - - /** - * Get an error message in the current language. - * @access protected - * @param string $key - * @return string - */ - protected function lang($key) - { - if (count($this->language) < 1) { - $this->setLanguage('en'); // set the default language - } - - if (array_key_exists($key, $this->language)) { - if ($key == 'smtp_connect_failed') { - //Include a link to troubleshooting docs on SMTP connection failure - //this is by far the biggest cause of support questions - //but it's usually not PHPMailer's fault. - return $this->language[$key] . ' https://github.com/PHPMailer/PHPMailer/wiki/Troubleshooting'; - } - return $this->language[$key]; - } else { - //Return the key as a fallback - return $key; - } - } - - /** - * Check if an error occurred. - * @access public - * @return boolean True if an error did occur. - */ - public function isError() - { - return ($this->error_count > 0); - } - - /** - * Ensure consistent line endings in a string. - * Changes every end of line from CRLF, CR or LF to $this->LE. - * @access public - * @param string $str String to fixEOL - * @return string - */ - public function fixEOL($str) - { - // Normalise to \n - $nstr = str_replace(array("\r\n", "\r"), "\n", $str); - // Now convert LE as needed - if ($this->LE !== "\n") { - $nstr = str_replace("\n", $this->LE, $nstr); - } - return $nstr; - } - - /** - * Add a custom header. - * $name value can be overloaded to contain - * both header name and value (name:value) - * @access public - * @param string $name Custom header name - * @param string $value Header value - * @return void - */ - public function addCustomHeader($name, $value = null) - { - if ($value === null) { - // Value passed in as name:value - $this->CustomHeader[] = explode(':', $name, 2); - } else { - $this->CustomHeader[] = array($name, $value); - } - } - - /** - * Returns all custom headers. - * @return array - */ - public function getCustomHeaders() - { - return $this->CustomHeader; - } - - /** - * Create a message from an HTML string. - * Automatically makes modifications for inline images and backgrounds - * and creates a plain-text version by converting the HTML. - * Overwrites any existing values in $this->Body and $this->AltBody - * @access public - * @param string $message HTML message string - * @param string $basedir baseline directory for path - * @param boolean|callable $advanced Whether to use the internal HTML to text converter - * or your own custom converter @see PHPMailer::html2text() - * @return string $message - */ - public function msgHTML($message, $basedir = '', $advanced = false) - { - preg_match_all('/(src|background)=["\'](.*)["\']/Ui', $message, $images); - if (array_key_exists(2, $images)) { - foreach ($images[2] as $imgindex => $url) { - // Convert data URIs into embedded images - if (preg_match('#^data:(image[^;,]*)(;base64)?,#', $url, $match)) { - $data = substr($url, strpos($url, ',')); - if ($match[2]) { - $data = base64_decode($data); - } else { - $data = rawurldecode($data); - } - $cid = md5($url) . '@phpmailer.0'; // RFC2392 S 2 - if ($this->addStringEmbeddedImage($data, $cid, 'embed' . $imgindex, 'base64', $match[1])) { - $message = str_replace( - $images[0][$imgindex], - $images[1][$imgindex] . '="cid:' . $cid . '"', - $message - ); - } - } elseif (substr($url, 0, 4) !== 'cid:' && !preg_match('#^[a-z][a-z0-9+.-]*://#i', $url)) { - // Do not change urls for absolute images (thanks to corvuscorax) - // Do not change urls that are already inline images - $filename = basename($url); - $directory = dirname($url); - if ($directory == '.') { - $directory = ''; - } - $cid = md5($url) . '@phpmailer.0'; // RFC2392 S 2 - if (strlen($basedir) > 1 && substr($basedir, -1) != '/') { - $basedir .= '/'; - } - if (strlen($directory) > 1 && substr($directory, -1) != '/') { - $directory .= '/'; - } - if ($this->addEmbeddedImage( - $basedir . $directory . $filename, - $cid, - $filename, - 'base64', - self::_mime_types((string)self::mb_pathinfo($filename, PATHINFO_EXTENSION)) - ) - ) { - $message = preg_replace( - '/' . $images[1][$imgindex] . '=["\']' . preg_quote($url, '/') . '["\']/Ui', - $images[1][$imgindex] . '="cid:' . $cid . '"', - $message - ); - } - } - } - } - $this->isHTML(true); - // Convert all message body line breaks to CRLF, makes quoted-printable encoding work much better - $this->Body = $this->normalizeBreaks($message); - $this->AltBody = $this->normalizeBreaks($this->html2text($message, $advanced)); - if (!$this->alternativeExists()) { - $this->AltBody = 'To view this email message, open it in a program that understands HTML!' . - self::CRLF . self::CRLF; - } - return $this->Body; - } - - /** - * Convert an HTML string into plain text. - * This is used by msgHTML(). - * Note - older versions of this function used a bundled advanced converter - * which was been removed for license reasons in #232 - * Example usage: - * - * // Use default conversion - * $plain = $mail->html2text($html); - * // Use your own custom converter - * $plain = $mail->html2text($html, function($html) { - * $converter = new MyHtml2text($html); - * return $converter->get_text(); - * }); - * - * @param string $html The HTML text to convert - * @param boolean|callable $advanced Any boolean value to use the internal converter, - * or provide your own callable for custom conversion. - * @return string - */ - public function html2text($html, $advanced = false) - { - if (is_callable($advanced)) { - return call_user_func($advanced, $html); - } - return html_entity_decode( - trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/si', '', $html))), - ENT_QUOTES, - $this->CharSet - ); - } - - /** - * Get the MIME type for a file extension. - * @param string $ext File extension - * @access public - * @return string MIME type of file. - * @static - */ - public static function _mime_types($ext = '') - { - $mimes = array( - 'xl' => 'application/excel', - 'js' => 'application/javascript', - 'hqx' => 'application/mac-binhex40', - 'cpt' => 'application/mac-compactpro', - 'bin' => 'application/macbinary', - 'doc' => 'application/msword', - 'word' => 'application/msword', - 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', - 'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template', - 'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template', - 'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow', - 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', - 'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide', - 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', - 'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template', - 'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12', - 'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12', - 'class' => 'application/octet-stream', - 'dll' => 'application/octet-stream', - 'dms' => 'application/octet-stream', - 'exe' => 'application/octet-stream', - 'lha' => 'application/octet-stream', - 'lzh' => 'application/octet-stream', - 'psd' => 'application/octet-stream', - 'sea' => 'application/octet-stream', - 'so' => 'application/octet-stream', - 'oda' => 'application/oda', - 'pdf' => 'application/pdf', - 'ai' => 'application/postscript', - 'eps' => 'application/postscript', - 'ps' => 'application/postscript', - 'smi' => 'application/smil', - 'smil' => 'application/smil', - 'mif' => 'application/vnd.mif', - 'xls' => 'application/vnd.ms-excel', - 'ppt' => 'application/vnd.ms-powerpoint', - 'wbxml' => 'application/vnd.wap.wbxml', - 'wmlc' => 'application/vnd.wap.wmlc', - 'dcr' => 'application/x-director', - 'dir' => 'application/x-director', - 'dxr' => 'application/x-director', - 'dvi' => 'application/x-dvi', - 'gtar' => 'application/x-gtar', - 'php3' => 'application/x-httpd-php', - 'php4' => 'application/x-httpd-php', - 'php' => 'application/x-httpd-php', - 'phtml' => 'application/x-httpd-php', - 'phps' => 'application/x-httpd-php-source', - 'swf' => 'application/x-shockwave-flash', - 'sit' => 'application/x-stuffit', - 'tar' => 'application/x-tar', - 'tgz' => 'application/x-tar', - 'xht' => 'application/xhtml+xml', - 'xhtml' => 'application/xhtml+xml', - 'zip' => 'application/zip', - 'mid' => 'audio/midi', - 'midi' => 'audio/midi', - 'mp2' => 'audio/mpeg', - 'mp3' => 'audio/mpeg', - 'mpga' => 'audio/mpeg', - 'aif' => 'audio/x-aiff', - 'aifc' => 'audio/x-aiff', - 'aiff' => 'audio/x-aiff', - 'ram' => 'audio/x-pn-realaudio', - 'rm' => 'audio/x-pn-realaudio', - 'rpm' => 'audio/x-pn-realaudio-plugin', - 'ra' => 'audio/x-realaudio', - 'wav' => 'audio/x-wav', - 'bmp' => 'image/bmp', - 'gif' => 'image/gif', - 'jpeg' => 'image/jpeg', - 'jpe' => 'image/jpeg', - 'jpg' => 'image/jpeg', - 'png' => 'image/png', - 'tiff' => 'image/tiff', - 'tif' => 'image/tiff', - 'eml' => 'message/rfc822', - 'css' => 'text/css', - 'html' => 'text/html', - 'htm' => 'text/html', - 'shtml' => 'text/html', - 'log' => 'text/plain', - 'text' => 'text/plain', - 'txt' => 'text/plain', - 'rtx' => 'text/richtext', - 'rtf' => 'text/rtf', - 'vcf' => 'text/vcard', - 'vcard' => 'text/vcard', - 'xml' => 'text/xml', - 'xsl' => 'text/xml', - 'mpeg' => 'video/mpeg', - 'mpe' => 'video/mpeg', - 'mpg' => 'video/mpeg', - 'mov' => 'video/quicktime', - 'qt' => 'video/quicktime', - 'rv' => 'video/vnd.rn-realvideo', - 'avi' => 'video/x-msvideo', - 'movie' => 'video/x-sgi-movie' - ); - if (array_key_exists(strtolower($ext), $mimes)) { - return $mimes[strtolower($ext)]; - } - return 'application/octet-stream'; - } - - /** - * Map a file name to a MIME type. - * Defaults to 'application/octet-stream', i.e.. arbitrary binary data. - * @param string $filename A file name or full path, does not need to exist as a file - * @return string - * @static - */ - public static function filenameToType($filename) - { - // In case the path is a URL, strip any query string before getting extension - $qpos = strpos($filename, '?'); - if (false !== $qpos) { - $filename = substr($filename, 0, $qpos); - } - $pathinfo = self::mb_pathinfo($filename); - return self::_mime_types($pathinfo['extension']); - } - - /** - * Multi-byte-safe pathinfo replacement. - * Drop-in replacement for pathinfo(), but multibyte-safe, cross-platform-safe, old-version-safe. - * Works similarly to the one in PHP >= 5.2.0 - * @link http://www.php.net/manual/en/function.pathinfo.php#107461 - * @param string $path A filename or path, does not need to exist as a file - * @param integer|string $options Either a PATHINFO_* constant, - * or a string name to return only the specified piece, allows 'filename' to work on PHP < 5.2 - * @return string|array - * @static - */ - public static function mb_pathinfo($path, $options = null) - { - $ret = array('dirname' => '', 'basename' => '', 'extension' => '', 'filename' => ''); - $pathinfo = array(); - if (preg_match('%^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^\.\\\\/]+?)|))[\\\\/\.]*$%im', $path, $pathinfo)) { - if (array_key_exists(1, $pathinfo)) { - $ret['dirname'] = $pathinfo[1]; - } - if (array_key_exists(2, $pathinfo)) { - $ret['basename'] = $pathinfo[2]; - } - if (array_key_exists(5, $pathinfo)) { - $ret['extension'] = $pathinfo[5]; - } - if (array_key_exists(3, $pathinfo)) { - $ret['filename'] = $pathinfo[3]; - } - } - switch ($options) { - case PATHINFO_DIRNAME: - case 'dirname': - return $ret['dirname']; - case PATHINFO_BASENAME: - case 'basename': - return $ret['basename']; - case PATHINFO_EXTENSION: - case 'extension': - return $ret['extension']; - case PATHINFO_FILENAME: - case 'filename': - return $ret['filename']; - default: - return $ret; - } - } - - /** - * Set or reset instance properties. - * You should avoid this function - it's more verbose, less efficient, more error-prone and - * harder to debug than setting properties directly. - * Usage Example: - * `$mail->set('SMTPSecure', 'tls');` - * is the same as: - * `$mail->SMTPSecure = 'tls';` - * @access public - * @param string $name The property name to set - * @param mixed $value The value to set the property to - * @return boolean - * @TODO Should this not be using the __set() magic function? - */ - public function set($name, $value = '') - { - if (property_exists($this, $name)) { - $this->$name = $value; - return true; - } else { - $this->setError($this->lang('variable_set') . $name); - return false; - } - } - - /** - * Strip newlines to prevent header injection. - * @access public - * @param string $str - * @return string - */ - public function secureHeader($str) - { - return trim(str_replace(array("\r", "\n"), '', $str)); - } - - /** - * Normalize line breaks in a string. - * Converts UNIX LF, Mac CR and Windows CRLF line breaks into a single line break format. - * Defaults to CRLF (for message bodies) and preserves consecutive breaks. - * @param string $text - * @param string $breaktype What kind of line break to use, defaults to CRLF - * @return string - * @access public - * @static - */ - public static function normalizeBreaks($text, $breaktype = "\r\n") - { - return preg_replace('/(\r\n|\r|\n)/ms', $breaktype, $text); - } - - /** - * Set the public and private key files and password for S/MIME signing. - * @access public - * @param string $cert_filename - * @param string $key_filename - * @param string $key_pass Password for private key - * @param string $extracerts_filename Optional path to chain certificate - */ - public function sign($cert_filename, $key_filename, $key_pass, $extracerts_filename = '') - { - $this->sign_cert_file = $cert_filename; - $this->sign_key_file = $key_filename; - $this->sign_key_pass = $key_pass; - $this->sign_extracerts_file = $extracerts_filename; - } - - /** - * Quoted-Printable-encode a DKIM header. - * @access public - * @param string $txt - * @return string - */ - public function DKIM_QP($txt) - { - $line = ''; - for ($i = 0; $i < strlen($txt); $i++) { - $ord = ord($txt[$i]); - if (((0x21 <= $ord) && ($ord <= 0x3A)) || $ord == 0x3C || ((0x3E <= $ord) && ($ord <= 0x7E))) { - $line .= $txt[$i]; - } else { - $line .= '=' . sprintf('%02X', $ord); - } - } - return $line; - } - - /** - * Generate a DKIM signature. - * @access public - * @param string $signHeader - * @throws phpmailerException - * @return string - */ - public function DKIM_Sign($signHeader) - { - if (!defined('PKCS7_TEXT')) { - if ($this->exceptions) { - throw new phpmailerException($this->lang('extension_missing') . 'openssl'); - } - return ''; - } - $privKeyStr = file_get_contents($this->DKIM_private); - if ($this->DKIM_passphrase != '') { - $privKey = openssl_pkey_get_private($privKeyStr, $this->DKIM_passphrase); - } else { - $privKey = openssl_pkey_get_private($privKeyStr); - } - if (openssl_sign($signHeader, $signature, $privKey, 'sha256WithRSAEncryption')) { //sha1WithRSAEncryption - openssl_pkey_free($privKey); - return base64_encode($signature); - } - openssl_pkey_free($privKey); - return ''; - } - - /** - * Generate a DKIM canonicalization header. - * @access public - * @param string $signHeader Header - * @return string - */ - public function DKIM_HeaderC($signHeader) - { - $signHeader = preg_replace('/\r\n\s+/', ' ', $signHeader); - $lines = explode("\r\n", $signHeader); - foreach ($lines as $key => $line) { - list($heading, $value) = explode(':', $line, 2); - $heading = strtolower($heading); - $value = preg_replace('/\s{2,}/', ' ', $value); // Compress useless spaces - $lines[$key] = $heading . ':' . trim($value); // Don't forget to remove WSP around the value - } - $signHeader = implode("\r\n", $lines); - return $signHeader; - } - - /** - * Generate a DKIM canonicalization body. - * @access public - * @param string $body Message Body - * @return string - */ - public function DKIM_BodyC($body) - { - if ($body == '') { - return "\r\n"; - } - // stabilize line endings - $body = str_replace("\r\n", "\n", $body); - $body = str_replace("\n", "\r\n", $body); - // END stabilize line endings - while (substr($body, strlen($body) - 4, 4) == "\r\n\r\n") { - $body = substr($body, 0, strlen($body) - 2); - } - return $body; - } - - /** - * Create the DKIM header and body in a new message header. - * @access public - * @param string $headers_line Header lines - * @param string $subject Subject - * @param string $body Body - * @return string - */ - public function DKIM_Add($headers_line, $subject, $body) - { - $DKIMsignatureType = 'rsa-sha256'; // Signature & hash algorithms - $DKIMcanonicalization = 'relaxed/simple'; // Canonicalization of header/body - $DKIMquery = 'dns/txt'; // Query method - $DKIMtime = time(); // Signature Timestamp = seconds since 00:00:00 - Jan 1, 1970 (UTC time zone) - $subject_header = "Subject: $subject"; - $headers = explode($this->LE, $headers_line); - $from_header = ''; - $to_header = ''; - $date_header = ''; - $current = ''; - foreach ($headers as $header) { - if (strpos($header, 'From:') === 0) { - $from_header = $header; - $current = 'from_header'; - } elseif (strpos($header, 'To:') === 0) { - $to_header = $header; - $current = 'to_header'; - } elseif (strpos($header, 'Date:') === 0) { - $date_header = $header; - $current = 'date_header'; - } else { - if (!empty($$current) && strpos($header, ' =?') === 0) { - $$current .= $header; - } else { - $current = ''; - } - } - } - $from = str_replace('|', '=7C', $this->DKIM_QP($from_header)); - $to = str_replace('|', '=7C', $this->DKIM_QP($to_header)); - $date = str_replace('|', '=7C', $this->DKIM_QP($date_header)); - $subject = str_replace( - '|', - '=7C', - $this->DKIM_QP($subject_header) - ); // Copied header fields (dkim-quoted-printable) - $body = $this->DKIM_BodyC($body); - $DKIMlen = strlen($body); // Length of body - $DKIMb64 = base64_encode(pack('H*', hash('sha256', $body))); // Base64 of packed binary SHA-256 hash of body - if ('' == $this->DKIM_identity) { - $ident = ''; - } else { - $ident = ' i=' . $this->DKIM_identity . ';'; - } - $dkimhdrs = 'DKIM-Signature: v=1; a=' . - $DKIMsignatureType . '; q=' . - $DKIMquery . '; l=' . - $DKIMlen . '; s=' . - $this->DKIM_selector . - ";\r\n" . - "\tt=" . $DKIMtime . '; c=' . $DKIMcanonicalization . ";\r\n" . - "\th=From:To:Date:Subject;\r\n" . - "\td=" . $this->DKIM_domain . ';' . $ident . "\r\n" . - "\tz=$from\r\n" . - "\t|$to\r\n" . - "\t|$date\r\n" . - "\t|$subject;\r\n" . - "\tbh=" . $DKIMb64 . ";\r\n" . - "\tb="; - $toSign = $this->DKIM_HeaderC( - $from_header . "\r\n" . - $to_header . "\r\n" . - $date_header . "\r\n" . - $subject_header . "\r\n" . - $dkimhdrs - ); - $signed = $this->DKIM_Sign($toSign); - return $dkimhdrs . $signed . "\r\n"; - } - - /** - * Detect if a string contains a line longer than the maximum line length allowed. - * @param string $str - * @return boolean - * @static - */ - public static function hasLineLongerThanMax($str) - { - //+2 to include CRLF line break for a 1000 total - return (boolean)preg_match('/^(.{'.(self::MAX_LINE_LENGTH + 2).',})/m', $str); - } - - /** - * Allows for public read access to 'to' property. - * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included. - * @access public - * @return array - */ - public function getToAddresses() - { - return $this->to; - } - - /** - * Allows for public read access to 'cc' property. - * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included. - * @access public - * @return array - */ - public function getCcAddresses() - { - return $this->cc; - } - - /** - * Allows for public read access to 'bcc' property. - * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included. - * @access public - * @return array - */ - public function getBccAddresses() - { - return $this->bcc; - } - - /** - * Allows for public read access to 'ReplyTo' property. - * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included. - * @access public - * @return array - */ - public function getReplyToAddresses() - { - return $this->ReplyTo; - } - - /** - * Allows for public read access to 'all_recipients' property. - * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included. - * @access public - * @return array - */ - public function getAllRecipientAddresses() - { - return $this->all_recipients; - } - - /** - * Perform a callback. - * @param boolean $isSent - * @param array $to - * @param array $cc - * @param array $bcc - * @param string $subject - * @param string $body - * @param string $from - */ - protected function doCallback($isSent, $to, $cc, $bcc, $subject, $body, $from) - { - if (!empty($this->action_function) && is_callable($this->action_function)) { - $params = array($isSent, $to, $cc, $bcc, $subject, $body, $from); - call_user_func_array($this->action_function, $params); - } - } + /** + * The PHPMailer Version number. + * @var string + */ + public $Version = '5.2.21'; + + /** + * Email priority. + * Options: null (default), 1 = High, 3 = Normal, 5 = low. + * When null, the header is not set at all. + * @var integer + */ + public $Priority = null; + + /** + * The character set of the message. + * @var string + */ + public $CharSet = 'iso-8859-1'; + + /** + * The MIME Content-type of the message. + * @var string + */ + public $ContentType = 'text/plain'; + + /** + * The message encoding. + * Options: "8bit", "7bit", "binary", "base64", and "quoted-printable". + * @var string + */ + public $Encoding = '8bit'; + + /** + * Holds the most recent mailer error message. + * @var string + */ + public $ErrorInfo = ''; + + /** + * The From email address for the message. + * @var string + */ + public $From = 'root@localhost'; + + /** + * The From name of the message. + * @var string + */ + public $FromName = 'Root User'; + + /** + * The Sender email (Return-Path) of the message. + * If not empty, will be sent via -f to sendmail or as 'MAIL FROM' in smtp mode. + * @var string + */ + public $Sender = ''; + + /** + * The Return-Path of the message. + * If empty, it will be set to either From or Sender. + * @var string + * @deprecated Email senders should never set a return-path header; + * it's the receiver's job (RFC5321 section 4.4), so this no longer does anything. + * @link https://tools.ietf.org/html/rfc5321#section-4.4 RFC5321 reference + */ + public $ReturnPath = ''; + + /** + * The Subject of the message. + * @var string + */ + public $Subject = ''; + + /** + * An HTML or plain text message body. + * If HTML then call isHTML(true). + * @var string + */ + public $Body = ''; + + /** + * The plain-text message body. + * This body can be read by mail clients that do not have HTML email + * capability such as mutt & Eudora. + * Clients that can read HTML will view the normal Body. + * @var string + */ + public $AltBody = ''; + + /** + * An iCal message part body. + * Only supported in simple alt or alt_inline message types + * To generate iCal events, use the bundled extras/EasyPeasyICS.php class or iCalcreator + * @link http://sprain.ch/blog/downloads/php-class-easypeasyics-create-ical-files-with-php/ + * @link http://kigkonsult.se/iCalcreator/ + * @var string + */ + public $Ical = ''; + + /** + * The complete compiled MIME message body. + * @access protected + * @var string + */ + protected $MIMEBody = ''; + + /** + * The complete compiled MIME message headers. + * @var string + * @access protected + */ + protected $MIMEHeader = ''; + + /** + * Extra headers that createHeader() doesn't fold in. + * @var string + * @access protected + */ + protected $mailHeader = ''; + + /** + * Word-wrap the message body to this number of chars. + * Set to 0 to not wrap. A useful value here is 78, for RFC2822 section 2.1.1 compliance. + * @var integer + */ + public $WordWrap = 0; + + /** + * Which method to use to send mail. + * Options: "mail", "sendmail", or "smtp". + * @var string + */ + public $Mailer = 'mail'; + + /** + * The path to the sendmail program. + * @var string + */ + public $Sendmail = '/usr/sbin/sendmail'; + + /** + * Whether mail() uses a fully sendmail-compatible MTA. + * One which supports sendmail's "-oi -f" options. + * @var boolean + */ + public $UseSendmailOptions = true; + + /** + * Path to PHPMailer plugins. + * Useful if the SMTP class is not in the PHP include path. + * @var string + * @deprecated Should not be needed now there is an autoloader. + */ + public $PluginDir = ''; + + /** + * The email address that a reading confirmation should be sent to, also known as read receipt. + * @var string + */ + public $ConfirmReadingTo = ''; + + /** + * The hostname to use in the Message-ID header and as default HELO string. + * If empty, PHPMailer attempts to find one with, in order, + * $_SERVER['SERVER_NAME'], gethostname(), php_uname('n'), or the value + * 'localhost.localdomain'. + * @var string + */ + public $Hostname = ''; + + /** + * An ID to be used in the Message-ID header. + * If empty, a unique id will be generated. + * You can set your own, but it must be in the format "", + * as defined in RFC5322 section 3.6.4 or it will be ignored. + * @see https://tools.ietf.org/html/rfc5322#section-3.6.4 + * @var string + */ + public $MessageID = ''; + + /** + * The message Date to be used in the Date header. + * If empty, the current date will be added. + * @var string + */ + public $MessageDate = ''; + + /** + * SMTP hosts. + * Either a single hostname or multiple semicolon-delimited hostnames. + * You can also specify a different port + * for each host by using this format: [hostname:port] + * (e.g. "smtp1.example.com:25;smtp2.example.com"). + * You can also specify encryption type, for example: + * (e.g. "tls://smtp1.example.com:587;ssl://smtp2.example.com:465"). + * Hosts will be tried in order. + * @var string + */ + public $Host = 'localhost'; + + /** + * The default SMTP server port. + * @var integer + * @TODO Why is this needed when the SMTP class takes care of it? + */ + public $Port = 25; + + /** + * The SMTP HELO of the message. + * Default is $Hostname. If $Hostname is empty, PHPMailer attempts to find + * one with the same method described above for $Hostname. + * @var string + * @see PHPMailer::$Hostname + */ + public $Helo = ''; + + /** + * What kind of encryption to use on the SMTP connection. + * Options: '', 'ssl' or 'tls' + * @var string + */ + public $SMTPSecure = ''; + + /** + * Whether to enable TLS encryption automatically if a server supports it, + * even if `SMTPSecure` is not set to 'tls'. + * Be aware that in PHP >= 5.6 this requires that the server's certificates are valid. + * @var boolean + */ + public $SMTPAutoTLS = true; + + /** + * Whether to use SMTP authentication. + * Uses the Username and Password properties. + * @var boolean + * @see PHPMailer::$Username + * @see PHPMailer::$Password + */ + public $SMTPAuth = false; + + /** + * Options array passed to stream_context_create when connecting via SMTP. + * @var array + */ + public $SMTPOptions = array(); + + /** + * SMTP username. + * @var string + */ + public $Username = ''; + + /** + * SMTP password. + * @var string + */ + public $Password = ''; + + /** + * SMTP auth type. + * Options are CRAM-MD5, LOGIN, PLAIN, NTLM, XOAUTH2, attempted in that order if not specified + * @var string + */ + public $AuthType = ''; + + /** + * SMTP realm. + * Used for NTLM auth + * @var string + */ + public $Realm = ''; + + /** + * SMTP workstation. + * Used for NTLM auth + * @var string + */ + public $Workstation = ''; + + /** + * The SMTP server timeout in seconds. + * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2 + * @var integer + */ + public $Timeout = 300; + + /** + * SMTP class debug output mode. + * Debug output level. + * Options: + * * `0` No output + * * `1` Commands + * * `2` Data and commands + * * `3` As 2 plus connection status + * * `4` Low-level data output + * @var integer + * @see SMTP::$do_debug + */ + public $SMTPDebug = 0; + + /** + * How to handle debug output. + * Options: + * * `echo` Output plain-text as-is, appropriate for CLI + * * `html` Output escaped, line breaks converted to `
`, appropriate for browser output + * * `error_log` Output to error log as configured in php.ini + * + * Alternatively, you can provide a callable expecting two params: a message string and the debug level: + * + * $mail->Debugoutput = function($str, $level) {echo "debug level $level; message: $str";}; + * + * @var string|callable + * @see SMTP::$Debugoutput + */ + public $Debugoutput = 'echo'; + + /** + * Whether to keep SMTP connection open after each message. + * If this is set to true then to close the connection + * requires an explicit call to smtpClose(). + * @var boolean + */ + public $SMTPKeepAlive = false; + + /** + * Whether to split multiple to addresses into multiple messages + * or send them all in one message. + * Only supported in `mail` and `sendmail` transports, not in SMTP. + * @var boolean + */ + public $SingleTo = false; + + /** + * Storage for addresses when SingleTo is enabled. + * @var array + * @TODO This should really not be public + */ + public $SingleToArray = array(); + + /** + * Whether to generate VERP addresses on send. + * Only applicable when sending via SMTP. + * @link https://en.wikipedia.org/wiki/Variable_envelope_return_path + * @link http://www.postfix.org/VERP_README.html Postfix VERP info + * @var boolean + */ + public $do_verp = false; + + /** + * Whether to allow sending messages with an empty body. + * @var boolean + */ + public $AllowEmpty = false; + + /** + * The default line ending. + * @note The default remains "\n". We force CRLF where we know + * it must be used via self::CRLF. + * @var string + */ + public $LE = "\n"; + + /** + * DKIM selector. + * @var string + */ + public $DKIM_selector = ''; + + /** + * DKIM Identity. + * Usually the email address used as the source of the email. + * @var string + */ + public $DKIM_identity = ''; + + /** + * DKIM passphrase. + * Used if your key is encrypted. + * @var string + */ + public $DKIM_passphrase = ''; + + /** + * DKIM signing domain name. + * @example 'example.com' + * @var string + */ + public $DKIM_domain = ''; + + /** + * DKIM private key file path. + * @var string + */ + public $DKIM_private = ''; + + /** + * DKIM private key string. + * If set, takes precedence over `$DKIM_private`. + * @var string + */ + public $DKIM_private_string = ''; + + /** + * Callback Action function name. + * + * The function that handles the result of the send email action. + * It is called out by send() for each email sent. + * + * Value can be any php callable: http://www.php.net/is_callable + * + * Parameters: + * boolean $result result of the send action + * string $to email address of the recipient + * string $cc cc email addresses + * string $bcc bcc email addresses + * string $subject the subject + * string $body the email body + * string $from email address of sender + * @var string + */ + public $action_function = ''; + + /** + * What to put in the X-Mailer header. + * Options: An empty string for PHPMailer default, whitespace for none, or a string to use + * @var string + */ + public $XMailer = ''; + + /** + * Which validator to use by default when validating email addresses. + * May be a callable to inject your own validator, but there are several built-in validators. + * @see PHPMailer::validateAddress() + * @var string|callable + * @static + */ + public static $validator = 'auto'; + + /** + * An instance of the SMTP sender class. + * @var SMTP + * @access protected + */ + protected $smtp = null; + + /** + * The array of 'to' names and addresses. + * @var array + * @access protected + */ + protected $to = array(); + + /** + * The array of 'cc' names and addresses. + * @var array + * @access protected + */ + protected $cc = array(); + + /** + * The array of 'bcc' names and addresses. + * @var array + * @access protected + */ + protected $bcc = array(); + + /** + * The array of reply-to names and addresses. + * @var array + * @access protected + */ + protected $ReplyTo = array(); + + /** + * An array of all kinds of addresses. + * Includes all of $to, $cc, $bcc + * @var array + * @access protected + * @see PHPMailer::$to @see PHPMailer::$cc @see PHPMailer::$bcc + */ + protected $all_recipients = array(); + + /** + * An array of names and addresses queued for validation. + * In send(), valid and non duplicate entries are moved to $all_recipients + * and one of $to, $cc, or $bcc. + * This array is used only for addresses with IDN. + * @var array + * @access protected + * @see PHPMailer::$to @see PHPMailer::$cc @see PHPMailer::$bcc + * @see PHPMailer::$all_recipients + */ + protected $RecipientsQueue = array(); + + /** + * An array of reply-to names and addresses queued for validation. + * In send(), valid and non duplicate entries are moved to $ReplyTo. + * This array is used only for addresses with IDN. + * @var array + * @access protected + * @see PHPMailer::$ReplyTo + */ + protected $ReplyToQueue = array(); + + /** + * The array of attachments. + * @var array + * @access protected + */ + protected $attachment = array(); + + /** + * The array of custom headers. + * @var array + * @access protected + */ + protected $CustomHeader = array(); + + /** + * The most recent Message-ID (including angular brackets). + * @var string + * @access protected + */ + protected $lastMessageID = ''; + + /** + * The message's MIME type. + * @var string + * @access protected + */ + protected $message_type = ''; + + /** + * The array of MIME boundary strings. + * @var array + * @access protected + */ + protected $boundary = array(); + + /** + * The array of available languages. + * @var array + * @access protected + */ + protected $language = array(); + + /** + * The number of errors encountered. + * @var integer + * @access protected + */ + protected $error_count = 0; + + /** + * The S/MIME certificate file path. + * @var string + * @access protected + */ + protected $sign_cert_file = ''; + + /** + * The S/MIME key file path. + * @var string + * @access protected + */ + protected $sign_key_file = ''; + + /** + * The optional S/MIME extra certificates ("CA Chain") file path. + * @var string + * @access protected + */ + protected $sign_extracerts_file = ''; + + /** + * The S/MIME password for the key. + * Used only if the key is encrypted. + * @var string + * @access protected + */ + protected $sign_key_pass = ''; + + /** + * Whether to throw exceptions for errors. + * @var boolean + * @access protected + */ + protected $exceptions = false; + + /** + * Unique ID used for message ID and boundaries. + * @var string + * @access protected + */ + protected $uniqueid = ''; + + /** + * Error severity: message only, continue processing. + */ + const STOP_MESSAGE = 0; + + /** + * Error severity: message, likely ok to continue processing. + */ + const STOP_CONTINUE = 1; + + /** + * Error severity: message, plus full stop, critical error reached. + */ + const STOP_CRITICAL = 2; + + /** + * SMTP RFC standard line ending. + */ + const CRLF = "\r\n"; + + /** + * The maximum line length allowed by RFC 2822 section 2.1.1 + * @var integer + */ + const MAX_LINE_LENGTH = 998; + + /** + * Constructor. + * @param boolean $exceptions Should we throw external exceptions? + */ + public function __construct($exceptions = null) + { + if ($exceptions !== null) { + $this->exceptions = (boolean)$exceptions; + } + } + + /** + * Destructor. + */ + public function __destruct() + { + //Close any open SMTP connection nicely + $this->smtpClose(); + } + + /** + * Call mail() in a safe_mode-aware fashion. + * Also, unless sendmail_path points to sendmail (or something that + * claims to be sendmail), don't pass params (not a perfect fix, + * but it will do) + * @param string $to To + * @param string $subject Subject + * @param string $body Message Body + * @param string $header Additional Header(s) + * @param string $params Params + * @access private + * @return boolean + */ + private function mailPassthru($to, $subject, $body, $header, $params) + { + //Check overloading of mail function to avoid double-encoding + if (ini_get('mbstring.func_overload') & 1) { + $subject = $this->secureHeader($subject); + } else { + $subject = $this->encodeHeader($this->secureHeader($subject)); + } + + //Can't use additional_parameters in safe_mode, calling mail() with null params breaks + //@link http://php.net/manual/en/function.mail.php + if (ini_get('safe_mode') or !$this->UseSendmailOptions or is_null($params)) { + $result = @mail($to, $subject, $body, $header); + } else { + $result = @mail($to, $subject, $body, $header, $params); + } + return $result; + } + /** + * Output debugging info via user-defined method. + * Only generates output if SMTP debug output is enabled (@see SMTP::$do_debug). + * @see PHPMailer::$Debugoutput + * @see PHPMailer::$SMTPDebug + * @param string $str + */ + protected function edebug($str) + { + if ($this->SMTPDebug <= 0) { + return; + } + //Avoid clash with built-in function names + if (!in_array($this->Debugoutput, array('error_log', 'html', 'echo')) and is_callable($this->Debugoutput)) { + call_user_func($this->Debugoutput, $str, $this->SMTPDebug); + return; + } + switch ($this->Debugoutput) { + case 'error_log': + //Don't output, just log + error_log($str); + break; + case 'html': + //Cleans up output a bit for a better looking, HTML-safe output + echo htmlentities( + preg_replace('/[\r\n]+/', '', $str), + ENT_QUOTES, + 'UTF-8' + ) + . "
\n"; + break; + case 'echo': + default: + //Normalize line breaks + $str = preg_replace('/\r\n?/ms', "\n", $str); + echo gmdate('Y-m-d H:i:s') . "\t" . str_replace( + "\n", + "\n \t ", + trim($str) + ) . "\n"; + } + } + + /** + * Sets message type to HTML or plain. + * @param boolean $isHtml True for HTML mode. + * @return void + */ + public function isHTML($isHtml = true) + { + if ($isHtml) { + $this->ContentType = 'text/html'; + } else { + $this->ContentType = 'text/plain'; + } + } + + /** + * Send messages using SMTP. + * @return void + */ + public function isSMTP() + { + $this->Mailer = 'smtp'; + } + + /** + * Send messages using PHP's mail() function. + * @return void + */ + public function isMail() + { + $this->Mailer = 'mail'; + } + + /** + * Send messages using $Sendmail. + * @return void + */ + public function isSendmail() + { + $ini_sendmail_path = ini_get('sendmail_path'); + + if (!stristr($ini_sendmail_path, 'sendmail')) { + $this->Sendmail = '/usr/sbin/sendmail'; + } else { + $this->Sendmail = $ini_sendmail_path; + } + $this->Mailer = 'sendmail'; + } + + /** + * Send messages using qmail. + * @return void + */ + public function isQmail() + { + $ini_sendmail_path = ini_get('sendmail_path'); + + if (!stristr($ini_sendmail_path, 'qmail')) { + $this->Sendmail = '/var/qmail/bin/qmail-inject'; + } else { + $this->Sendmail = $ini_sendmail_path; + } + $this->Mailer = 'qmail'; + } + + /** + * Add a "To" address. + * @param string $address The email address to send to + * @param string $name + * @return boolean true on success, false if address already used or invalid in some way + */ + public function addAddress($address, $name = '') + { + return $this->addOrEnqueueAnAddress('to', $address, $name); + } + + /** + * Add a "CC" address. + * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer. + * @param string $address The email address to send to + * @param string $name + * @return boolean true on success, false if address already used or invalid in some way + */ + public function addCC($address, $name = '') + { + return $this->addOrEnqueueAnAddress('cc', $address, $name); + } + + /** + * Add a "BCC" address. + * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer. + * @param string $address The email address to send to + * @param string $name + * @return boolean true on success, false if address already used or invalid in some way + */ + public function addBCC($address, $name = '') + { + return $this->addOrEnqueueAnAddress('bcc', $address, $name); + } + + /** + * Add a "Reply-To" address. + * @param string $address The email address to reply to + * @param string $name + * @return boolean true on success, false if address already used or invalid in some way + */ + public function addReplyTo($address, $name = '') + { + return $this->addOrEnqueueAnAddress('Reply-To', $address, $name); + } + + /** + * Add an address to one of the recipient arrays or to the ReplyTo array. Because PHPMailer + * can't validate addresses with an IDN without knowing the PHPMailer::$CharSet (that can still + * be modified after calling this function), addition of such addresses is delayed until send(). + * Addresses that have been added already return false, but do not throw exceptions. + * @param string $kind One of 'to', 'cc', 'bcc', or 'ReplyTo' + * @param string $address The email address to send, resp. to reply to + * @param string $name + * @throws phpmailerException + * @return boolean true on success, false if address already used or invalid in some way + * @access protected + */ + protected function addOrEnqueueAnAddress($kind, $address, $name) + { + $address = trim($address); + $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim + if (($pos = strrpos($address, '@')) === false) { + // At-sign is misssing. + $error_message = $this->lang('invalid_address') . " (addAnAddress $kind): $address"; + $this->setError($error_message); + $this->edebug($error_message); + if ($this->exceptions) { + throw new phpmailerException($error_message); + } + return false; + } + $params = array($kind, $address, $name); + // Enqueue addresses with IDN until we know the PHPMailer::$CharSet. + if ($this->has8bitChars(substr($address, ++$pos)) and $this->idnSupported()) { + if ($kind != 'Reply-To') { + if (!array_key_exists($address, $this->RecipientsQueue)) { + $this->RecipientsQueue[$address] = $params; + return true; + } + } else { + if (!array_key_exists($address, $this->ReplyToQueue)) { + $this->ReplyToQueue[$address] = $params; + return true; + } + } + return false; + } + // Immediately add standard addresses without IDN. + return call_user_func_array(array($this, 'addAnAddress'), $params); + } + + /** + * Add an address to one of the recipient arrays or to the ReplyTo array. + * Addresses that have been added already return false, but do not throw exceptions. + * @param string $kind One of 'to', 'cc', 'bcc', or 'ReplyTo' + * @param string $address The email address to send, resp. to reply to + * @param string $name + * @throws phpmailerException + * @return boolean true on success, false if address already used or invalid in some way + * @access protected + */ + protected function addAnAddress($kind, $address, $name = '') + { + if (!in_array($kind, array('to', 'cc', 'bcc', 'Reply-To'))) { + $error_message = $this->lang('Invalid recipient kind: ') . $kind; + $this->setError($error_message); + $this->edebug($error_message); + if ($this->exceptions) { + throw new phpmailerException($error_message); + } + return false; + } + if (!$this->validateAddress($address)) { + $error_message = $this->lang('invalid_address') . " (addAnAddress $kind): $address"; + $this->setError($error_message); + $this->edebug($error_message); + if ($this->exceptions) { + throw new phpmailerException($error_message); + } + return false; + } + if ($kind != 'Reply-To') { + if (!array_key_exists(strtolower($address), $this->all_recipients)) { + array_push($this->$kind, array($address, $name)); + $this->all_recipients[strtolower($address)] = true; + return true; + } + } else { + if (!array_key_exists(strtolower($address), $this->ReplyTo)) { + $this->ReplyTo[strtolower($address)] = array($address, $name); + return true; + } + } + return false; + } + + /** + * Parse and validate a string containing one or more RFC822-style comma-separated email addresses + * of the form "display name
" into an array of name/address pairs. + * Uses the imap_rfc822_parse_adrlist function if the IMAP extension is available. + * Note that quotes in the name part are removed. + * @param string $addrstr The address list string + * @param bool $useimap Whether to use the IMAP extension to parse the list + * @return array + * @link http://www.andrew.cmu.edu/user/agreen1/testing/mrbs/web/Mail/RFC822.php A more careful implementation + */ + public function parseAddresses($addrstr, $useimap = true) + { + $addresses = array(); + if ($useimap and function_exists('imap_rfc822_parse_adrlist')) { + //Use this built-in parser if it's available + $list = imap_rfc822_parse_adrlist($addrstr, ''); + foreach ($list as $address) { + if ($address->host != '.SYNTAX-ERROR.') { + if ($this->validateAddress($address->mailbox . '@' . $address->host)) { + $addresses[] = array( + 'name' => (property_exists($address, 'personal') ? $address->personal : ''), + 'address' => $address->mailbox . '@' . $address->host + ); + } + } + } + } else { + //Use this simpler parser + $list = explode(',', $addrstr); + foreach ($list as $address) { + $address = trim($address); + //Is there a separate name part? + if (strpos($address, '<') === false) { + //No separate name, just use the whole thing + if ($this->validateAddress($address)) { + $addresses[] = array( + 'name' => '', + 'address' => $address + ); + } + } else { + list($name, $email) = explode('<', $address); + $email = trim(str_replace('>', '', $email)); + if ($this->validateAddress($email)) { + $addresses[] = array( + 'name' => trim(str_replace(array('"', "'"), '', $name)), + 'address' => $email + ); + } + } + } + } + return $addresses; + } + + /** + * Set the From and FromName properties. + * @param string $address + * @param string $name + * @param boolean $auto Whether to also set the Sender address, defaults to true + * @throws phpmailerException + * @return boolean + */ + public function setFrom($address, $name = '', $auto = true) + { + $address = trim($address); + $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim + // Don't validate now addresses with IDN. Will be done in send(). + if (($pos = strrpos($address, '@')) === false or + (!$this->has8bitChars(substr($address, ++$pos)) or !$this->idnSupported()) and + !$this->validateAddress($address)) { + $error_message = $this->lang('invalid_address') . " (setFrom) $address"; + $this->setError($error_message); + $this->edebug($error_message); + if ($this->exceptions) { + throw new phpmailerException($error_message); + } + return false; + } + $this->From = $address; + $this->FromName = $name; + if ($auto) { + if (empty($this->Sender)) { + $this->Sender = $address; + } + } + return true; + } + + /** + * Return the Message-ID header of the last email. + * Technically this is the value from the last time the headers were created, + * but it's also the message ID of the last sent message except in + * pathological cases. + * @return string + */ + public function getLastMessageID() + { + return $this->lastMessageID; + } + + /** + * Check that a string looks like an email address. + * @param string $address The email address to check + * @param string|callable $patternselect A selector for the validation pattern to use : + * * `auto` Pick best pattern automatically; + * * `pcre8` Use the squiloople.com pattern, requires PCRE > 8.0, PHP >= 5.3.2, 5.2.14; + * * `pcre` Use old PCRE implementation; + * * `php` Use PHP built-in FILTER_VALIDATE_EMAIL; + * * `html5` Use the pattern given by the HTML5 spec for 'email' type form input elements. + * * `noregex` Don't use a regex: super fast, really dumb. + * Alternatively you may pass in a callable to inject your own validator, for example: + * PHPMailer::validateAddress('user@example.com', function($address) { + * return (strpos($address, '@') !== false); + * }); + * You can also set the PHPMailer::$validator static to a callable, allowing built-in methods to use your validator. + * @return boolean + * @static + * @access public + */ + public static function validateAddress($address, $patternselect = null) + { + if (is_null($patternselect)) { + $patternselect = self::$validator; + } + if (is_callable($patternselect)) { + return call_user_func($patternselect, $address); + } + //Reject line breaks in addresses; it's valid RFC5322, but not RFC5321 + if (strpos($address, "\n") !== false or strpos($address, "\r") !== false) { + return false; + } + if (!$patternselect or $patternselect == 'auto') { + //Check this constant first so it works when extension_loaded() is disabled by safe mode + //Constant was added in PHP 5.2.4 + if (defined('PCRE_VERSION')) { + //This pattern can get stuck in a recursive loop in PCRE <= 8.0.2 + if (version_compare(PCRE_VERSION, '8.0.3') >= 0) { + $patternselect = 'pcre8'; + } else { + $patternselect = 'pcre'; + } + } elseif (function_exists('extension_loaded') and extension_loaded('pcre')) { + //Fall back to older PCRE + $patternselect = 'pcre'; + } else { + //Filter_var appeared in PHP 5.2.0 and does not require the PCRE extension + if (version_compare(PHP_VERSION, '5.2.0') >= 0) { + $patternselect = 'php'; + } else { + $patternselect = 'noregex'; + } + } + } + switch ($patternselect) { + case 'pcre8': + /** + * Uses the same RFC5322 regex on which FILTER_VALIDATE_EMAIL is based, but allows dotless domains. + * @link http://squiloople.com/2009/12/20/email-address-validation/ + * @copyright 2009-2010 Michael Rushton + * Feel free to use and redistribute this code. But please keep this copyright notice. + */ + return (boolean)preg_match( + '/^(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){255,})(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){65,}@)' . + '((?>(?>(?>((?>(?>(?>\x0D\x0A)?[\t ])+|(?>[\t ]*\x0D\x0A)?[\t ]+)?)(\((?>(?2)' . + '(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\)))+(?2))|(?2))?)' . + '([!#-\'*+\/-9=?^-~-]+|"(?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\x7F]))*' . + '(?2)")(?>(?1)\.(?1)(?4))*(?1)@(?!(?1)[a-z0-9-]{64,})(?1)(?>([a-z0-9](?>[a-z0-9-]*[a-z0-9])?)' . + '(?>(?1)\.(?!(?1)[a-z0-9-]{64,})(?1)(?5)){0,126}|\[(?:(?>IPv6:(?>([a-f0-9]{1,4})(?>:(?6)){7}' . + '|(?!(?:.*[a-f0-9][:\]]){8,})((?6)(?>:(?6)){0,6})?::(?7)?))|(?>(?>IPv6:(?>(?6)(?>:(?6)){5}:' . + '|(?!(?:.*[a-f0-9]:){6,})(?8)?::(?>((?6)(?>:(?6)){0,4}):)?))?(25[0-5]|2[0-4][0-9]|1[0-9]{2}' . + '|[1-9]?[0-9])(?>\.(?9)){3}))\])(?1)$/isD', + $address + ); + case 'pcre': + //An older regex that doesn't need a recent PCRE + return (boolean)preg_match( + '/^(?!(?>"?(?>\\\[ -~]|[^"])"?){255,})(?!(?>"?(?>\\\[ -~]|[^"])"?){65,}@)(?>' . + '[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*")' . + '(?>\.(?>[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*"))*' . + '@(?>(?![a-z0-9-]{64,})(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)(?>\.(?![a-z0-9-]{64,})' . + '(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)){0,126}|\[(?:(?>IPv6:(?>(?>[a-f0-9]{1,4})(?>:' . + '[a-f0-9]{1,4}){7}|(?!(?:.*[a-f0-9][:\]]){8,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?' . + '::(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?))|(?>(?>IPv6:(?>[a-f0-9]{1,4}(?>:' . + '[a-f0-9]{1,4}){5}:|(?!(?:.*[a-f0-9]:){6,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4})?' . + '::(?>(?:[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4}):)?))?(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}' . + '|[1-9]?[0-9])(?>\.(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}))\])$/isD', + $address + ); + case 'html5': + /** + * This is the pattern used in the HTML5 spec for validation of 'email' type form input elements. + * @link http://www.whatwg.org/specs/web-apps/current-work/#e-mail-state-(type=email) + */ + return (boolean)preg_match( + '/^[a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}' . + '[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/sD', + $address + ); + case 'noregex': + //No PCRE! Do something _very_ approximate! + //Check the address is 3 chars or longer and contains an @ that's not the first or last char + return (strlen($address) >= 3 + and strpos($address, '@') >= 1 + and strpos($address, '@') != strlen($address) - 1); + case 'php': + default: + return (boolean)filter_var($address, FILTER_VALIDATE_EMAIL); + } + } + + /** + * Tells whether IDNs (Internationalized Domain Names) are supported or not. This requires the + * "intl" and "mbstring" PHP extensions. + * @return bool "true" if required functions for IDN support are present + */ + public function idnSupported() + { + // @TODO: Write our own "idn_to_ascii" function for PHP <= 5.2. + return function_exists('idn_to_ascii') and function_exists('mb_convert_encoding'); + } + + /** + * Converts IDN in given email address to its ASCII form, also known as punycode, if possible. + * Important: Address must be passed in same encoding as currently set in PHPMailer::$CharSet. + * This function silently returns unmodified address if: + * - No conversion is necessary (i.e. domain name is not an IDN, or is already in ASCII form) + * - Conversion to punycode is impossible (e.g. required PHP functions are not available) + * or fails for any reason (e.g. domain has characters not allowed in an IDN) + * @see PHPMailer::$CharSet + * @param string $address The email address to convert + * @return string The encoded address in ASCII form + */ + public function punyencodeAddress($address) + { + // Verify we have required functions, CharSet, and at-sign. + if ($this->idnSupported() and + !empty($this->CharSet) and + ($pos = strrpos($address, '@')) !== false) { + $domain = substr($address, ++$pos); + // Verify CharSet string is a valid one, and domain properly encoded in this CharSet. + if ($this->has8bitChars($domain) and @mb_check_encoding($domain, $this->CharSet)) { + $domain = mb_convert_encoding($domain, 'UTF-8', $this->CharSet); + if (($punycode = defined('INTL_IDNA_VARIANT_UTS46') ? + idn_to_ascii($domain, 0, INTL_IDNA_VARIANT_UTS46) : + idn_to_ascii($domain)) !== false) { + return substr($address, 0, $pos) . $punycode; + } + } + } + return $address; + } + + /** + * Create a message and send it. + * Uses the sending method specified by $Mailer. + * @throws phpmailerException + * @return boolean false on error - See the ErrorInfo property for details of the error. + */ + public function send() + { + try { + if (!$this->preSend()) { + return false; + } + return $this->postSend(); + } catch (phpmailerException $exc) { + $this->mailHeader = ''; + $this->setError($exc->getMessage()); + if ($this->exceptions) { + throw $exc; + } + return false; + } + } + + /** + * Prepare a message for sending. + * @throws phpmailerException + * @return boolean + */ + public function preSend() + { + try { + $this->error_count = 0; // Reset errors + $this->mailHeader = ''; + + // Dequeue recipient and Reply-To addresses with IDN + foreach (array_merge($this->RecipientsQueue, $this->ReplyToQueue) as $params) { + $params[1] = $this->punyencodeAddress($params[1]); + call_user_func_array(array($this, 'addAnAddress'), $params); + } + if ((count($this->to) + count($this->cc) + count($this->bcc)) < 1) { + throw new phpmailerException($this->lang('provide_address'), self::STOP_CRITICAL); + } + + // Validate From, Sender, and ConfirmReadingTo addresses + foreach (array('From', 'Sender', 'ConfirmReadingTo') as $address_kind) { + $this->$address_kind = trim($this->$address_kind); + if (empty($this->$address_kind)) { + continue; + } + $this->$address_kind = $this->punyencodeAddress($this->$address_kind); + if (!$this->validateAddress($this->$address_kind)) { + $error_message = $this->lang('invalid_address') . ' (punyEncode) ' . $this->$address_kind; + $this->setError($error_message); + $this->edebug($error_message); + if ($this->exceptions) { + throw new phpmailerException($error_message); + } + return false; + } + } + + // Set whether the message is multipart/alternative + if ($this->alternativeExists()) { + $this->ContentType = 'multipart/alternative'; + } + + $this->setMessageType(); + // Refuse to send an empty message unless we are specifically allowing it + if (!$this->AllowEmpty and empty($this->Body)) { + throw new phpmailerException($this->lang('empty_message'), self::STOP_CRITICAL); + } + + // Create body before headers in case body makes changes to headers (e.g. altering transfer encoding) + $this->MIMEHeader = ''; + $this->MIMEBody = $this->createBody(); + // createBody may have added some headers, so retain them + $tempheaders = $this->MIMEHeader; + $this->MIMEHeader = $this->createHeader(); + $this->MIMEHeader .= $tempheaders; + + // To capture the complete message when using mail(), create + // an extra header list which createHeader() doesn't fold in + if ($this->Mailer == 'mail') { + if (count($this->to) > 0) { + $this->mailHeader .= $this->addrAppend('To', $this->to); + } else { + $this->mailHeader .= $this->headerLine('To', 'undisclosed-recipients:;'); + } + $this->mailHeader .= $this->headerLine( + 'Subject', + $this->encodeHeader($this->secureHeader(trim($this->Subject))) + ); + } + + // Sign with DKIM if enabled + if (!empty($this->DKIM_domain) + && !empty($this->DKIM_selector) + && (!empty($this->DKIM_private_string) + || (!empty($this->DKIM_private) && file_exists($this->DKIM_private)) + ) + ) { + $header_dkim = $this->DKIM_Add( + $this->MIMEHeader . $this->mailHeader, + $this->encodeHeader($this->secureHeader($this->Subject)), + $this->MIMEBody + ); + $this->MIMEHeader = rtrim($this->MIMEHeader, "\r\n ") . self::CRLF . + str_replace("\r\n", "\n", $header_dkim) . self::CRLF; + } + return true; + } catch (phpmailerException $exc) { + $this->setError($exc->getMessage()); + if ($this->exceptions) { + throw $exc; + } + return false; + } + } + + /** + * Actually send a message. + * Send the email via the selected mechanism + * @throws phpmailerException + * @return boolean + */ + public function postSend() + { + try { + // Choose the mailer and send through it + switch ($this->Mailer) { + case 'sendmail': + case 'qmail': + return $this->sendmailSend($this->MIMEHeader, $this->MIMEBody); + case 'smtp': + return $this->smtpSend($this->MIMEHeader, $this->MIMEBody); + case 'mail': + return $this->mailSend($this->MIMEHeader, $this->MIMEBody); + default: + $sendMethod = $this->Mailer.'Send'; + if (method_exists($this, $sendMethod)) { + return $this->$sendMethod($this->MIMEHeader, $this->MIMEBody); + } + + return $this->mailSend($this->MIMEHeader, $this->MIMEBody); + } + } catch (phpmailerException $exc) { + $this->setError($exc->getMessage()); + $this->edebug($exc->getMessage()); + if ($this->exceptions) { + throw $exc; + } + } + return false; + } + + /** + * Send mail using the $Sendmail program. + * @param string $header The message headers + * @param string $body The message body + * @see PHPMailer::$Sendmail + * @throws phpmailerException + * @access protected + * @return boolean + */ + protected function sendmailSend($header, $body) + { + // CVE-2016-10033, CVE-2016-10045: Don't pass -f if characters will be escaped. + if (!empty($this->Sender) and self::isShellSafe($this->Sender)) { + if ($this->Mailer == 'qmail') { + $sendmailFmt = '%s -f%s'; + } else { + $sendmailFmt = '%s -oi -f%s -t'; + } + } else { + if ($this->Mailer == 'qmail') { + $sendmailFmt = '%s'; + } else { + $sendmailFmt = '%s -oi -t'; + } + } + + // TODO: If possible, this should be changed to escapeshellarg. Needs thorough testing. + $sendmail = sprintf($sendmailFmt, escapeshellcmd($this->Sendmail), $this->Sender); + + if ($this->SingleTo) { + foreach ($this->SingleToArray as $toAddr) { + if (!@$mail = popen($sendmail, 'w')) { + throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); + } + fputs($mail, 'To: ' . $toAddr . "\n"); + fputs($mail, $header); + fputs($mail, $body); + $result = pclose($mail); + $this->doCallback( + ($result == 0), + array($toAddr), + $this->cc, + $this->bcc, + $this->Subject, + $body, + $this->From + ); + if ($result != 0) { + throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); + } + } + } else { + if (!@$mail = popen($sendmail, 'w')) { + throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); + } + fputs($mail, $header); + fputs($mail, $body); + $result = pclose($mail); + $this->doCallback( + ($result == 0), + $this->to, + $this->cc, + $this->bcc, + $this->Subject, + $body, + $this->From + ); + if ($result != 0) { + throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); + } + } + return true; + } + + /** + * Fix CVE-2016-10033 and CVE-2016-10045 by disallowing potentially unsafe shell characters. + * + * Note that escapeshellarg and escapeshellcmd are inadequate for our purposes, especially on Windows. + * @param string $string The string to be validated + * @see https://github.com/PHPMailer/PHPMailer/issues/924 CVE-2016-10045 bug report + * @access protected + * @return boolean + */ + protected static function isShellSafe($string) + { + // Future-proof + if (escapeshellcmd($string) !== $string + or !in_array(escapeshellarg($string), array("'$string'", "\"$string\"")) + ) { + return false; + } + + $length = strlen($string); + + for ($i = 0; $i < $length; $i++) { + $c = $string[$i]; + + // All other characters have a special meaning in at least one common shell, including = and +. + // Full stop (.) has a special meaning in cmd.exe, but its impact should be negligible here. + // Note that this does permit non-Latin alphanumeric characters based on the current locale. + if (!ctype_alnum($c) && strpos('@_-.', $c) === false) { + return false; + } + } + + return true; + } + + /** + * Send mail using the PHP mail() function. + * @param string $header The message headers + * @param string $body The message body + * @link http://www.php.net/manual/en/book.mail.php + * @throws phpmailerException + * @access protected + * @return boolean + */ + protected function mailSend($header, $body) + { + $toArr = array(); + foreach ($this->to as $toaddr) { + $toArr[] = $this->addrFormat($toaddr); + } + $to = implode(', ', $toArr); + + $params = null; + //This sets the SMTP envelope sender which gets turned into a return-path header by the receiver + if (!empty($this->Sender) and $this->validateAddress($this->Sender)) { + // CVE-2016-10033, CVE-2016-10045: Don't pass -f if characters will be escaped. + if (self::isShellSafe($this->Sender)) { + $params = sprintf('-f%s', $this->Sender); + } + } + if (!empty($this->Sender) and !ini_get('safe_mode') and $this->validateAddress($this->Sender)) { + $old_from = ini_get('sendmail_from'); + ini_set('sendmail_from', $this->Sender); + } + $result = false; + if ($this->SingleTo and count($toArr) > 1) { + foreach ($toArr as $toAddr) { + $result = $this->mailPassthru($toAddr, $this->Subject, $body, $header, $params); + $this->doCallback($result, array($toAddr), $this->cc, $this->bcc, $this->Subject, $body, $this->From); + } + } else { + $result = $this->mailPassthru($to, $this->Subject, $body, $header, $params); + $this->doCallback($result, $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From); + } + if (isset($old_from)) { + ini_set('sendmail_from', $old_from); + } + if (!$result) { + throw new phpmailerException($this->lang('instantiate'), self::STOP_CRITICAL); + } + return true; + } + + /** + * Get an instance to use for SMTP operations. + * Override this function to load your own SMTP implementation + * @return SMTP + */ + public function getSMTPInstance() + { + if (!is_object($this->smtp)) { + $this->smtp = new SMTP; + } + return $this->smtp; + } + + /** + * Send mail via SMTP. + * Returns false if there is a bad MAIL FROM, RCPT, or DATA input. + * Uses the PHPMailerSMTP class by default. + * @see PHPMailer::getSMTPInstance() to use a different class. + * @param string $header The message headers + * @param string $body The message body + * @throws phpmailerException + * @uses SMTP + * @access protected + * @return boolean + */ + protected function smtpSend($header, $body) + { + $bad_rcpt = array(); + if (!$this->smtpConnect($this->SMTPOptions)) { + throw new phpmailerException($this->lang('smtp_connect_failed'), self::STOP_CRITICAL); + } + if (!empty($this->Sender) and $this->validateAddress($this->Sender)) { + $smtp_from = $this->Sender; + } else { + $smtp_from = $this->From; + } + if (!$this->smtp->mail($smtp_from)) { + $this->setError($this->lang('from_failed') . $smtp_from . ' : ' . implode(',', $this->smtp->getError())); + throw new phpmailerException($this->ErrorInfo, self::STOP_CRITICAL); + } + + // Attempt to send to all recipients + foreach (array($this->to, $this->cc, $this->bcc) as $togroup) { + foreach ($togroup as $to) { + if (!$this->smtp->recipient($to[0])) { + $error = $this->smtp->getError(); + $bad_rcpt[] = array('to' => $to[0], 'error' => $error['detail']); + $isSent = false; + } else { + $isSent = true; + } + $this->doCallback($isSent, array($to[0]), array(), array(), $this->Subject, $body, $this->From); + } + } + + // Only send the DATA command if we have viable recipients + if ((count($this->all_recipients) > count($bad_rcpt)) and !$this->smtp->data($header . $body)) { + throw new phpmailerException($this->lang('data_not_accepted'), self::STOP_CRITICAL); + } + if ($this->SMTPKeepAlive) { + $this->smtp->reset(); + } else { + $this->smtp->quit(); + $this->smtp->close(); + } + //Create error message for any bad addresses + if (count($bad_rcpt) > 0) { + $errstr = ''; + foreach ($bad_rcpt as $bad) { + $errstr .= $bad['to'] . ': ' . $bad['error']; + } + throw new phpmailerException( + $this->lang('recipients_failed') . $errstr, + self::STOP_CONTINUE + ); + } + return true; + } + + /** + * Initiate a connection to an SMTP server. + * Returns false if the operation failed. + * @param array $options An array of options compatible with stream_context_create() + * @uses SMTP + * @access public + * @throws phpmailerException + * @return boolean + */ + public function smtpConnect($options = null) + { + if (is_null($this->smtp)) { + $this->smtp = $this->getSMTPInstance(); + } + + //If no options are provided, use whatever is set in the instance + if (is_null($options)) { + $options = $this->SMTPOptions; + } + + // Already connected? + if ($this->smtp->connected()) { + return true; + } + + $this->smtp->setTimeout($this->Timeout); + $this->smtp->setDebugLevel($this->SMTPDebug); + $this->smtp->setDebugOutput($this->Debugoutput); + $this->smtp->setVerp($this->do_verp); + $hosts = explode(';', $this->Host); + $lastexception = null; + + foreach ($hosts as $hostentry) { + $hostinfo = array(); + if (!preg_match('/^((ssl|tls):\/\/)*([a-zA-Z0-9\.-]*):?([0-9]*)$/', trim($hostentry), $hostinfo)) { + // Not a valid host entry + continue; + } + // $hostinfo[2]: optional ssl or tls prefix + // $hostinfo[3]: the hostname + // $hostinfo[4]: optional port number + // The host string prefix can temporarily override the current setting for SMTPSecure + // If it's not specified, the default value is used + $prefix = ''; + $secure = $this->SMTPSecure; + $tls = ($this->SMTPSecure == 'tls'); + if ('ssl' == $hostinfo[2] or ('' == $hostinfo[2] and 'ssl' == $this->SMTPSecure)) { + $prefix = 'ssl://'; + $tls = false; // Can't have SSL and TLS at the same time + $secure = 'ssl'; + } elseif ($hostinfo[2] == 'tls') { + $tls = true; + // tls doesn't use a prefix + $secure = 'tls'; + } + //Do we need the OpenSSL extension? + $sslext = defined('OPENSSL_ALGO_SHA1'); + if ('tls' === $secure or 'ssl' === $secure) { + //Check for an OpenSSL constant rather than using extension_loaded, which is sometimes disabled + if (!$sslext) { + throw new phpmailerException($this->lang('extension_missing').'openssl', self::STOP_CRITICAL); + } + } + $host = $hostinfo[3]; + $port = $this->Port; + $tport = (integer)$hostinfo[4]; + if ($tport > 0 and $tport < 65536) { + $port = $tport; + } + if ($this->smtp->connect($prefix . $host, $port, $this->Timeout, $options)) { + try { + if ($this->Helo) { + $hello = $this->Helo; + } else { + $hello = $this->serverHostname(); + } + $this->smtp->hello($hello); + //Automatically enable TLS encryption if: + // * it's not disabled + // * we have openssl extension + // * we are not already using SSL + // * the server offers STARTTLS + if ($this->SMTPAutoTLS and $sslext and $secure != 'ssl' and $this->smtp->getServerExt('STARTTLS')) { + $tls = true; + } + if ($tls) { + if (!$this->smtp->startTLS()) { + throw new phpmailerException($this->lang('connect_host')); + } + // We must resend EHLO after TLS negotiation + $this->smtp->hello($hello); + } + if ($this->SMTPAuth) { + if (!$this->smtp->authenticate( + $this->Username, + $this->Password, + $this->AuthType, + $this->Realm, + $this->Workstation + ) + ) { + throw new phpmailerException($this->lang('authenticate')); + } + } + return true; + } catch (phpmailerException $exc) { + $lastexception = $exc; + $this->edebug($exc->getMessage()); + // We must have connected, but then failed TLS or Auth, so close connection nicely + $this->smtp->quit(); + } + } + } + // If we get here, all connection attempts have failed, so close connection hard + $this->smtp->close(); + // As we've caught all exceptions, just report whatever the last one was + if ($this->exceptions and !is_null($lastexception)) { + throw $lastexception; + } + return false; + } + + /** + * Close the active SMTP session if one exists. + * @return void + */ + public function smtpClose() + { + if (is_a($this->smtp, 'SMTP')) { + if ($this->smtp->connected()) { + $this->smtp->quit(); + $this->smtp->close(); + } + } + } + + /** + * Set the language for error messages. + * Returns false if it cannot load the language file. + * The default language is English. + * @param string $langcode ISO 639-1 2-character language code (e.g. French is "fr") + * @param string $lang_path Path to the language file directory, with trailing separator (slash) + * @return boolean + * @access public + */ + public function setLanguage($langcode = 'en', $lang_path = '') + { + // Backwards compatibility for renamed language codes + $renamed_langcodes = array( + 'br' => 'pt_br', + 'cz' => 'cs', + 'dk' => 'da', + 'no' => 'nb', + 'se' => 'sv', + ); + + if (isset($renamed_langcodes[$langcode])) { + $langcode = $renamed_langcodes[$langcode]; + } + + // Define full set of translatable strings in English + $PHPMAILER_LANG = array( + 'authenticate' => 'SMTP Error: Could not authenticate.', + 'connect_host' => 'SMTP Error: Could not connect to SMTP host.', + 'data_not_accepted' => 'SMTP Error: data not accepted.', + 'empty_message' => 'Message body empty', + 'encoding' => 'Unknown encoding: ', + 'execute' => 'Could not execute: ', + 'file_access' => 'Could not access file: ', + 'file_open' => 'File Error: Could not open file: ', + 'from_failed' => 'The following From address failed: ', + 'instantiate' => 'Could not instantiate mail function.', + 'invalid_address' => 'Invalid address: ', + 'mailer_not_supported' => ' mailer is not supported.', + 'provide_address' => 'You must provide at least one recipient email address.', + 'recipients_failed' => 'SMTP Error: The following recipients failed: ', + 'signing' => 'Signing Error: ', + 'smtp_connect_failed' => 'SMTP connect() failed.', + 'smtp_error' => 'SMTP server error: ', + 'variable_set' => 'Cannot set or reset variable: ', + 'extension_missing' => 'Extension missing: ' + ); + if (empty($lang_path)) { + // Calculate an absolute path so it can work if CWD is not here + $lang_path = dirname(__FILE__). DIRECTORY_SEPARATOR . 'language'. DIRECTORY_SEPARATOR; + } + //Validate $langcode + if (!preg_match('/^[a-z]{2}(?:_[a-zA-Z]{2})?$/', $langcode)) { + $langcode = 'en'; + } + $foundlang = true; + $lang_file = $lang_path . 'phpmailer.lang-' . $langcode . '.php'; + // There is no English translation file + if ($langcode != 'en') { + // Make sure language file path is readable + if (!is_readable($lang_file)) { + $foundlang = false; + } else { + // Overwrite language-specific strings. + // This way we'll never have missing translation keys. + $foundlang = include $lang_file; + } + } + $this->language = $PHPMAILER_LANG; + return (boolean)$foundlang; // Returns false if language not found + } + + /** + * Get the array of strings for the current language. + * @return array + */ + public function getTranslations() + { + return $this->language; + } + + /** + * Create recipient headers. + * @access public + * @param string $type + * @param array $addr An array of recipient, + * where each recipient is a 2-element indexed array with element 0 containing an address + * and element 1 containing a name, like: + * array(array('joe@example.com', 'Joe User'), array('zoe@example.com', 'Zoe User')) + * @return string + */ + public function addrAppend($type, $addr) + { + $addresses = array(); + foreach ($addr as $address) { + $addresses[] = $this->addrFormat($address); + } + return $type . ': ' . implode(', ', $addresses) . $this->LE; + } + + /** + * Format an address for use in a message header. + * @access public + * @param array $addr A 2-element indexed array, element 0 containing an address, element 1 containing a name + * like array('joe@example.com', 'Joe User') + * @return string + */ + public function addrFormat($addr) + { + if (empty($addr[1])) { // No name provided + return $this->secureHeader($addr[0]); + } else { + return $this->encodeHeader($this->secureHeader($addr[1]), 'phrase') . ' <' . $this->secureHeader( + $addr[0] + ) . '>'; + } + } + + /** + * Word-wrap message. + * For use with mailers that do not automatically perform wrapping + * and for quoted-printable encoded messages. + * Original written by philippe. + * @param string $message The message to wrap + * @param integer $length The line length to wrap to + * @param boolean $qp_mode Whether to run in Quoted-Printable mode + * @access public + * @return string + */ + public function wrapText($message, $length, $qp_mode = false) + { + if ($qp_mode) { + $soft_break = sprintf(' =%s', $this->LE); + } else { + $soft_break = $this->LE; + } + // If utf-8 encoding is used, we will need to make sure we don't + // split multibyte characters when we wrap + $is_utf8 = (strtolower($this->CharSet) == 'utf-8'); + $lelen = strlen($this->LE); + $crlflen = strlen(self::CRLF); + + $message = $this->fixEOL($message); + //Remove a trailing line break + if (substr($message, -$lelen) == $this->LE) { + $message = substr($message, 0, -$lelen); + } + + //Split message into lines + $lines = explode($this->LE, $message); + //Message will be rebuilt in here + $message = ''; + foreach ($lines as $line) { + $words = explode(' ', $line); + $buf = ''; + $firstword = true; + foreach ($words as $word) { + if ($qp_mode and (strlen($word) > $length)) { + $space_left = $length - strlen($buf) - $crlflen; + if (!$firstword) { + if ($space_left > 20) { + $len = $space_left; + if ($is_utf8) { + $len = $this->utf8CharBoundary($word, $len); + } elseif (substr($word, $len - 1, 1) == '=') { + $len--; + } elseif (substr($word, $len - 2, 1) == '=') { + $len -= 2; + } + $part = substr($word, 0, $len); + $word = substr($word, $len); + $buf .= ' ' . $part; + $message .= $buf . sprintf('=%s', self::CRLF); + } else { + $message .= $buf . $soft_break; + } + $buf = ''; + } + while (strlen($word) > 0) { + if ($length <= 0) { + break; + } + $len = $length; + if ($is_utf8) { + $len = $this->utf8CharBoundary($word, $len); + } elseif (substr($word, $len - 1, 1) == '=') { + $len--; + } elseif (substr($word, $len - 2, 1) == '=') { + $len -= 2; + } + $part = substr($word, 0, $len); + $word = substr($word, $len); + + if (strlen($word) > 0) { + $message .= $part . sprintf('=%s', self::CRLF); + } else { + $buf = $part; + } + } + } else { + $buf_o = $buf; + if (!$firstword) { + $buf .= ' '; + } + $buf .= $word; + + if (strlen($buf) > $length and $buf_o != '') { + $message .= $buf_o . $soft_break; + $buf = $word; + } + } + $firstword = false; + } + $message .= $buf . self::CRLF; + } + + return $message; + } + + /** + * Find the last character boundary prior to $maxLength in a utf-8 + * quoted-printable encoded string. + * Original written by Colin Brown. + * @access public + * @param string $encodedText utf-8 QP text + * @param integer $maxLength Find the last character boundary prior to this length + * @return integer + */ + public function utf8CharBoundary($encodedText, $maxLength) + { + $foundSplitPos = false; + $lookBack = 3; + while (!$foundSplitPos) { + $lastChunk = substr($encodedText, $maxLength - $lookBack, $lookBack); + $encodedCharPos = strpos($lastChunk, '='); + if (false !== $encodedCharPos) { + // Found start of encoded character byte within $lookBack block. + // Check the encoded byte value (the 2 chars after the '=') + $hex = substr($encodedText, $maxLength - $lookBack + $encodedCharPos + 1, 2); + $dec = hexdec($hex); + if ($dec < 128) { + // Single byte character. + // If the encoded char was found at pos 0, it will fit + // otherwise reduce maxLength to start of the encoded char + if ($encodedCharPos > 0) { + $maxLength = $maxLength - ($lookBack - $encodedCharPos); + } + $foundSplitPos = true; + } elseif ($dec >= 192) { + // First byte of a multi byte character + // Reduce maxLength to split at start of character + $maxLength = $maxLength - ($lookBack - $encodedCharPos); + $foundSplitPos = true; + } elseif ($dec < 192) { + // Middle byte of a multi byte character, look further back + $lookBack += 3; + } + } else { + // No encoded character found + $foundSplitPos = true; + } + } + return $maxLength; + } + + /** + * Apply word wrapping to the message body. + * Wraps the message body to the number of chars set in the WordWrap property. + * You should only do this to plain-text bodies as wrapping HTML tags may break them. + * This is called automatically by createBody(), so you don't need to call it yourself. + * @access public + * @return void + */ + public function setWordWrap() + { + if ($this->WordWrap < 1) { + return; + } + + switch ($this->message_type) { + case 'alt': + case 'alt_inline': + case 'alt_attach': + case 'alt_inline_attach': + $this->AltBody = $this->wrapText($this->AltBody, $this->WordWrap); + break; + default: + $this->Body = $this->wrapText($this->Body, $this->WordWrap); + break; + } + } + + /** + * Assemble message headers. + * @access public + * @return string The assembled headers + */ + public function createHeader() + { + $result = ''; + + if ($this->MessageDate == '') { + $this->MessageDate = self::rfcDate(); + } + $result .= $this->headerLine('Date', $this->MessageDate); + + // To be created automatically by mail() + if ($this->SingleTo) { + if ($this->Mailer != 'mail') { + foreach ($this->to as $toaddr) { + $this->SingleToArray[] = $this->addrFormat($toaddr); + } + } + } else { + if (count($this->to) > 0) { + if ($this->Mailer != 'mail') { + $result .= $this->addrAppend('To', $this->to); + } + } elseif (count($this->cc) == 0) { + $result .= $this->headerLine('To', 'undisclosed-recipients:;'); + } + } + + $result .= $this->addrAppend('From', array(array(trim($this->From), $this->FromName))); + + // sendmail and mail() extract Cc from the header before sending + if (count($this->cc) > 0) { + $result .= $this->addrAppend('Cc', $this->cc); + } + + // sendmail and mail() extract Bcc from the header before sending + if (( + $this->Mailer == 'sendmail' or $this->Mailer == 'qmail' or $this->Mailer == 'mail' + ) + and count($this->bcc) > 0 + ) { + $result .= $this->addrAppend('Bcc', $this->bcc); + } + + if (count($this->ReplyTo) > 0) { + $result .= $this->addrAppend('Reply-To', $this->ReplyTo); + } + + // mail() sets the subject itself + if ($this->Mailer != 'mail') { + $result .= $this->headerLine('Subject', $this->encodeHeader($this->secureHeader($this->Subject))); + } + + // Only allow a custom message ID if it conforms to RFC 5322 section 3.6.4 + // https://tools.ietf.org/html/rfc5322#section-3.6.4 + if ('' != $this->MessageID and preg_match('/^<.*@.*>$/', $this->MessageID)) { + $this->lastMessageID = $this->MessageID; + } else { + $this->lastMessageID = sprintf('<%s@%s>', $this->uniqueid, $this->serverHostname()); + } + $result .= $this->headerLine('Message-ID', $this->lastMessageID); + if (!is_null($this->Priority)) { + $result .= $this->headerLine('X-Priority', $this->Priority); + } + if ($this->XMailer == '') { + $result .= $this->headerLine( + 'X-Mailer', + 'PHPMailer ' . $this->Version . ' (https://github.com/PHPMailer/PHPMailer)' + ); + } else { + $myXmailer = trim($this->XMailer); + if ($myXmailer) { + $result .= $this->headerLine('X-Mailer', $myXmailer); + } + } + + if ($this->ConfirmReadingTo != '') { + $result .= $this->headerLine('Disposition-Notification-To', '<' . $this->ConfirmReadingTo . '>'); + } + + // Add custom headers + foreach ($this->CustomHeader as $header) { + $result .= $this->headerLine( + trim($header[0]), + $this->encodeHeader(trim($header[1])) + ); + } + if (!$this->sign_key_file) { + $result .= $this->headerLine('MIME-Version', '1.0'); + $result .= $this->getMailMIME(); + } + + return $result; + } + + /** + * Get the message MIME type headers. + * @access public + * @return string + */ + public function getMailMIME() + { + $result = ''; + $ismultipart = true; + switch ($this->message_type) { + case 'inline': + $result .= $this->headerLine('Content-Type', 'multipart/related;'); + $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"'); + break; + case 'attach': + case 'inline_attach': + case 'alt_attach': + case 'alt_inline_attach': + $result .= $this->headerLine('Content-Type', 'multipart/mixed;'); + $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"'); + break; + case 'alt': + case 'alt_inline': + $result .= $this->headerLine('Content-Type', 'multipart/alternative;'); + $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"'); + break; + default: + // Catches case 'plain': and case '': + $result .= $this->textLine('Content-Type: ' . $this->ContentType . '; charset=' . $this->CharSet); + $ismultipart = false; + break; + } + // RFC1341 part 5 says 7bit is assumed if not specified + if ($this->Encoding != '7bit') { + // RFC 2045 section 6.4 says multipart MIME parts may only use 7bit, 8bit or binary CTE + if ($ismultipart) { + if ($this->Encoding == '8bit') { + $result .= $this->headerLine('Content-Transfer-Encoding', '8bit'); + } + // The only remaining alternatives are quoted-printable and base64, which are both 7bit compatible + } else { + $result .= $this->headerLine('Content-Transfer-Encoding', $this->Encoding); + } + } + + if ($this->Mailer != 'mail') { + $result .= $this->LE; + } + + return $result; + } + + /** + * Returns the whole MIME message. + * Includes complete headers and body. + * Only valid post preSend(). + * @see PHPMailer::preSend() + * @access public + * @return string + */ + public function getSentMIMEMessage() + { + return rtrim($this->MIMEHeader . $this->mailHeader, "\n\r") . self::CRLF . self::CRLF . $this->MIMEBody; + } + + /** + * Create unique ID + * @return string + */ + protected function generateId() { + return md5(uniqid(time())); + } + + /** + * Assemble the message body. + * Returns an empty string on failure. + * @access public + * @throws phpmailerException + * @return string The assembled message body + */ + public function createBody() + { + $body = ''; + //Create unique IDs and preset boundaries + $this->uniqueid = $this->generateId(); + $this->boundary[1] = 'b1_' . $this->uniqueid; + $this->boundary[2] = 'b2_' . $this->uniqueid; + $this->boundary[3] = 'b3_' . $this->uniqueid; + + if ($this->sign_key_file) { + $body .= $this->getMailMIME() . $this->LE; + } + + $this->setWordWrap(); + + $bodyEncoding = $this->Encoding; + $bodyCharSet = $this->CharSet; + //Can we do a 7-bit downgrade? + if ($bodyEncoding == '8bit' and !$this->has8bitChars($this->Body)) { + $bodyEncoding = '7bit'; + //All ISO 8859, Windows codepage and UTF-8 charsets are ascii compatible up to 7-bit + $bodyCharSet = 'us-ascii'; + } + //If lines are too long, and we're not already using an encoding that will shorten them, + //change to quoted-printable transfer encoding for the body part only + if ('base64' != $this->Encoding and self::hasLineLongerThanMax($this->Body)) { + $bodyEncoding = 'quoted-printable'; + } + + $altBodyEncoding = $this->Encoding; + $altBodyCharSet = $this->CharSet; + //Can we do a 7-bit downgrade? + if ($altBodyEncoding == '8bit' and !$this->has8bitChars($this->AltBody)) { + $altBodyEncoding = '7bit'; + //All ISO 8859, Windows codepage and UTF-8 charsets are ascii compatible up to 7-bit + $altBodyCharSet = 'us-ascii'; + } + //If lines are too long, and we're not already using an encoding that will shorten them, + //change to quoted-printable transfer encoding for the alt body part only + if ('base64' != $altBodyEncoding and self::hasLineLongerThanMax($this->AltBody)) { + $altBodyEncoding = 'quoted-printable'; + } + //Use this as a preamble in all multipart message types + $mimepre = "This is a multi-part message in MIME format." . $this->LE . $this->LE; + switch ($this->message_type) { + case 'inline': + $body .= $mimepre; + $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding); + $body .= $this->encodeString($this->Body, $bodyEncoding); + $body .= $this->LE . $this->LE; + $body .= $this->attachAll('inline', $this->boundary[1]); + break; + case 'attach': + $body .= $mimepre; + $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding); + $body .= $this->encodeString($this->Body, $bodyEncoding); + $body .= $this->LE . $this->LE; + $body .= $this->attachAll('attachment', $this->boundary[1]); + break; + case 'inline_attach': + $body .= $mimepre; + $body .= $this->textLine('--' . $this->boundary[1]); + $body .= $this->headerLine('Content-Type', 'multipart/related;'); + $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"'); + $body .= $this->LE; + $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, '', $bodyEncoding); + $body .= $this->encodeString($this->Body, $bodyEncoding); + $body .= $this->LE . $this->LE; + $body .= $this->attachAll('inline', $this->boundary[2]); + $body .= $this->LE; + $body .= $this->attachAll('attachment', $this->boundary[1]); + break; + case 'alt': + $body .= $mimepre; + $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding); + $body .= $this->encodeString($this->AltBody, $altBodyEncoding); + $body .= $this->LE . $this->LE; + $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, 'text/html', $bodyEncoding); + $body .= $this->encodeString($this->Body, $bodyEncoding); + $body .= $this->LE . $this->LE; + if (!empty($this->Ical)) { + $body .= $this->getBoundary($this->boundary[1], '', 'text/calendar; method=REQUEST', ''); + $body .= $this->encodeString($this->Ical, $this->Encoding); + $body .= $this->LE . $this->LE; + } + $body .= $this->endBoundary($this->boundary[1]); + break; + case 'alt_inline': + $body .= $mimepre; + $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding); + $body .= $this->encodeString($this->AltBody, $altBodyEncoding); + $body .= $this->LE . $this->LE; + $body .= $this->textLine('--' . $this->boundary[1]); + $body .= $this->headerLine('Content-Type', 'multipart/related;'); + $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"'); + $body .= $this->LE; + $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding); + $body .= $this->encodeString($this->Body, $bodyEncoding); + $body .= $this->LE . $this->LE; + $body .= $this->attachAll('inline', $this->boundary[2]); + $body .= $this->LE; + $body .= $this->endBoundary($this->boundary[1]); + break; + case 'alt_attach': + $body .= $mimepre; + $body .= $this->textLine('--' . $this->boundary[1]); + $body .= $this->headerLine('Content-Type', 'multipart/alternative;'); + $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"'); + $body .= $this->LE; + $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding); + $body .= $this->encodeString($this->AltBody, $altBodyEncoding); + $body .= $this->LE . $this->LE; + $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding); + $body .= $this->encodeString($this->Body, $bodyEncoding); + $body .= $this->LE . $this->LE; + $body .= $this->endBoundary($this->boundary[2]); + $body .= $this->LE; + $body .= $this->attachAll('attachment', $this->boundary[1]); + break; + case 'alt_inline_attach': + $body .= $mimepre; + $body .= $this->textLine('--' . $this->boundary[1]); + $body .= $this->headerLine('Content-Type', 'multipart/alternative;'); + $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"'); + $body .= $this->LE; + $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding); + $body .= $this->encodeString($this->AltBody, $altBodyEncoding); + $body .= $this->LE . $this->LE; + $body .= $this->textLine('--' . $this->boundary[2]); + $body .= $this->headerLine('Content-Type', 'multipart/related;'); + $body .= $this->textLine("\tboundary=\"" . $this->boundary[3] . '"'); + $body .= $this->LE; + $body .= $this->getBoundary($this->boundary[3], $bodyCharSet, 'text/html', $bodyEncoding); + $body .= $this->encodeString($this->Body, $bodyEncoding); + $body .= $this->LE . $this->LE; + $body .= $this->attachAll('inline', $this->boundary[3]); + $body .= $this->LE; + $body .= $this->endBoundary($this->boundary[2]); + $body .= $this->LE; + $body .= $this->attachAll('attachment', $this->boundary[1]); + break; + default: + // Catch case 'plain' and case '', applies to simple `text/plain` and `text/html` body content types + //Reset the `Encoding` property in case we changed it for line length reasons + $this->Encoding = $bodyEncoding; + $body .= $this->encodeString($this->Body, $this->Encoding); + break; + } + + if ($this->isError()) { + $body = ''; + } elseif ($this->sign_key_file) { + try { + if (!defined('PKCS7_TEXT')) { + throw new phpmailerException($this->lang('extension_missing') . 'openssl'); + } + // @TODO would be nice to use php://temp streams here, but need to wrap for PHP < 5.1 + $file = tempnam(sys_get_temp_dir(), 'mail'); + if (false === file_put_contents($file, $body)) { + throw new phpmailerException($this->lang('signing') . ' Could not write temp file'); + } + $signed = tempnam(sys_get_temp_dir(), 'signed'); + //Workaround for PHP bug https://bugs.php.net/bug.php?id=69197 + if (empty($this->sign_extracerts_file)) { + $sign = @openssl_pkcs7_sign( + $file, + $signed, + 'file://' . realpath($this->sign_cert_file), + array('file://' . realpath($this->sign_key_file), $this->sign_key_pass), + null + ); + } else { + $sign = @openssl_pkcs7_sign( + $file, + $signed, + 'file://' . realpath($this->sign_cert_file), + array('file://' . realpath($this->sign_key_file), $this->sign_key_pass), + null, + PKCS7_DETACHED, + $this->sign_extracerts_file + ); + } + if ($sign) { + @unlink($file); + $body = file_get_contents($signed); + @unlink($signed); + //The message returned by openssl contains both headers and body, so need to split them up + $parts = explode("\n\n", $body, 2); + $this->MIMEHeader .= $parts[0] . $this->LE . $this->LE; + $body = $parts[1]; + } else { + @unlink($file); + @unlink($signed); + throw new phpmailerException($this->lang('signing') . openssl_error_string()); + } + } catch (phpmailerException $exc) { + $body = ''; + if ($this->exceptions) { + throw $exc; + } + } + } + return $body; + } + + /** + * Return the start of a message boundary. + * @access protected + * @param string $boundary + * @param string $charSet + * @param string $contentType + * @param string $encoding + * @return string + */ + protected function getBoundary($boundary, $charSet, $contentType, $encoding) + { + $result = ''; + if ($charSet == '') { + $charSet = $this->CharSet; + } + if ($contentType == '') { + $contentType = $this->ContentType; + } + if ($encoding == '') { + $encoding = $this->Encoding; + } + $result .= $this->textLine('--' . $boundary); + $result .= sprintf('Content-Type: %s; charset=%s', $contentType, $charSet); + $result .= $this->LE; + // RFC1341 part 5 says 7bit is assumed if not specified + if ($encoding != '7bit') { + $result .= $this->headerLine('Content-Transfer-Encoding', $encoding); + } + $result .= $this->LE; + + return $result; + } + + /** + * Return the end of a message boundary. + * @access protected + * @param string $boundary + * @return string + */ + protected function endBoundary($boundary) + { + return $this->LE . '--' . $boundary . '--' . $this->LE; + } + + /** + * Set the message type. + * PHPMailer only supports some preset message types, not arbitrary MIME structures. + * @access protected + * @return void + */ + protected function setMessageType() + { + $type = array(); + if ($this->alternativeExists()) { + $type[] = 'alt'; + } + if ($this->inlineImageExists()) { + $type[] = 'inline'; + } + if ($this->attachmentExists()) { + $type[] = 'attach'; + } + $this->message_type = implode('_', $type); + if ($this->message_type == '') { + //The 'plain' message_type refers to the message having a single body element, not that it is plain-text + $this->message_type = 'plain'; + } + } + + /** + * Format a header line. + * @access public + * @param string $name + * @param string $value + * @return string + */ + public function headerLine($name, $value) + { + return $name . ': ' . $value . $this->LE; + } + + /** + * Return a formatted mail line. + * @access public + * @param string $value + * @return string + */ + public function textLine($value) + { + return $value . $this->LE; + } + + /** + * Add an attachment from a path on the filesystem. + * Returns false if the file could not be found or read. + * @param string $path Path to the attachment. + * @param string $name Overrides the attachment name. + * @param string $encoding File encoding (see $Encoding). + * @param string $type File extension (MIME) type. + * @param string $disposition Disposition to use + * @throws phpmailerException + * @return boolean + */ + public function addAttachment($path, $name = '', $encoding = 'base64', $type = '', $disposition = 'attachment') + { + try { + if (!@is_file($path)) { + throw new phpmailerException($this->lang('file_access') . $path, self::STOP_CONTINUE); + } + + // If a MIME type is not specified, try to work it out from the file name + if ($type == '') { + $type = self::filenameToType($path); + } + + $filename = basename($path); + if ($name == '') { + $name = $filename; + } + + $this->attachment[] = array( + 0 => $path, + 1 => $filename, + 2 => $name, + 3 => $encoding, + 4 => $type, + 5 => false, // isStringAttachment + 6 => $disposition, + 7 => 0 + ); + + } catch (phpmailerException $exc) { + $this->setError($exc->getMessage()); + $this->edebug($exc->getMessage()); + if ($this->exceptions) { + throw $exc; + } + return false; + } + return true; + } + + /** + * Return the array of attachments. + * @return array + */ + public function getAttachments() + { + return $this->attachment; + } + + /** + * Attach all file, string, and binary attachments to the message. + * Returns an empty string on failure. + * @access protected + * @param string $disposition_type + * @param string $boundary + * @return string + */ + protected function attachAll($disposition_type, $boundary) + { + // Return text of body + $mime = array(); + $cidUniq = array(); + $incl = array(); + + // Add all attachments + foreach ($this->attachment as $attachment) { + // Check if it is a valid disposition_filter + if ($attachment[6] == $disposition_type) { + // Check for string attachment + $string = ''; + $path = ''; + $bString = $attachment[5]; + if ($bString) { + $string = $attachment[0]; + } else { + $path = $attachment[0]; + } + + $inclhash = md5(serialize($attachment)); + if (in_array($inclhash, $incl)) { + continue; + } + $incl[] = $inclhash; + $name = $attachment[2]; + $encoding = $attachment[3]; + $type = $attachment[4]; + $disposition = $attachment[6]; + $cid = $attachment[7]; + if ($disposition == 'inline' && array_key_exists($cid, $cidUniq)) { + continue; + } + $cidUniq[$cid] = true; + + $mime[] = sprintf('--%s%s', $boundary, $this->LE); + //Only include a filename property if we have one + if (!empty($name)) { + $mime[] = sprintf( + 'Content-Type: %s; name="%s"%s', + $type, + $this->encodeHeader($this->secureHeader($name)), + $this->LE + ); + } else { + $mime[] = sprintf( + 'Content-Type: %s%s', + $type, + $this->LE + ); + } + // RFC1341 part 5 says 7bit is assumed if not specified + if ($encoding != '7bit') { + $mime[] = sprintf('Content-Transfer-Encoding: %s%s', $encoding, $this->LE); + } + + if ($disposition == 'inline') { + $mime[] = sprintf('Content-ID: <%s>%s', $cid, $this->LE); + } + + // If a filename contains any of these chars, it should be quoted, + // but not otherwise: RFC2183 & RFC2045 5.1 + // Fixes a warning in IETF's msglint MIME checker + // Allow for bypassing the Content-Disposition header totally + if (!(empty($disposition))) { + $encoded_name = $this->encodeHeader($this->secureHeader($name)); + if (preg_match('/[ \(\)<>@,;:\\"\/\[\]\?=]/', $encoded_name)) { + $mime[] = sprintf( + 'Content-Disposition: %s; filename="%s"%s', + $disposition, + $encoded_name, + $this->LE . $this->LE + ); + } else { + if (!empty($encoded_name)) { + $mime[] = sprintf( + 'Content-Disposition: %s; filename=%s%s', + $disposition, + $encoded_name, + $this->LE . $this->LE + ); + } else { + $mime[] = sprintf( + 'Content-Disposition: %s%s', + $disposition, + $this->LE . $this->LE + ); + } + } + } else { + $mime[] = $this->LE; + } + + // Encode as string attachment + if ($bString) { + $mime[] = $this->encodeString($string, $encoding); + if ($this->isError()) { + return ''; + } + $mime[] = $this->LE . $this->LE; + } else { + $mime[] = $this->encodeFile($path, $encoding); + if ($this->isError()) { + return ''; + } + $mime[] = $this->LE . $this->LE; + } + } + } + + $mime[] = sprintf('--%s--%s', $boundary, $this->LE); + + return implode('', $mime); + } + + /** + * Encode a file attachment in requested format. + * Returns an empty string on failure. + * @param string $path The full path to the file + * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable' + * @throws phpmailerException + * @access protected + * @return string + */ + protected function encodeFile($path, $encoding = 'base64') + { + try { + if (!is_readable($path)) { + throw new phpmailerException($this->lang('file_open') . $path, self::STOP_CONTINUE); + } + $magic_quotes = get_magic_quotes_runtime(); + if ($magic_quotes) { + if (version_compare(PHP_VERSION, '5.3.0', '<')) { + set_magic_quotes_runtime(false); + } else { + //Doesn't exist in PHP 5.4, but we don't need to check because + //get_magic_quotes_runtime always returns false in 5.4+ + //so it will never get here + ini_set('magic_quotes_runtime', false); + } + } + $file_buffer = file_get_contents($path); + $file_buffer = $this->encodeString($file_buffer, $encoding); + if ($magic_quotes) { + if (version_compare(PHP_VERSION, '5.3.0', '<')) { + set_magic_quotes_runtime($magic_quotes); + } else { + ini_set('magic_quotes_runtime', $magic_quotes); + } + } + return $file_buffer; + } catch (Exception $exc) { + $this->setError($exc->getMessage()); + return ''; + } + } + + /** + * Encode a string in requested format. + * Returns an empty string on failure. + * @param string $str The text to encode + * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable' + * @access public + * @return string + */ + public function encodeString($str, $encoding = 'base64') + { + $encoded = ''; + switch (strtolower($encoding)) { + case 'base64': + $encoded = chunk_split(base64_encode($str), 76, $this->LE); + break; + case '7bit': + case '8bit': + $encoded = $this->fixEOL($str); + // Make sure it ends with a line break + if (substr($encoded, -(strlen($this->LE))) != $this->LE) { + $encoded .= $this->LE; + } + break; + case 'binary': + $encoded = $str; + break; + case 'quoted-printable': + $encoded = $this->encodeQP($str); + break; + default: + $this->setError($this->lang('encoding') . $encoding); + break; + } + return $encoded; + } + + /** + * Encode a header string optimally. + * Picks shortest of Q, B, quoted-printable or none. + * @access public + * @param string $str + * @param string $position + * @return string + */ + public function encodeHeader($str, $position = 'text') + { + $matchcount = 0; + switch (strtolower($position)) { + case 'phrase': + if (!preg_match('/[\200-\377]/', $str)) { + // Can't use addslashes as we don't know the value of magic_quotes_sybase + $encoded = addcslashes($str, "\0..\37\177\\\""); + if (($str == $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) { + return ($encoded); + } else { + return ("\"$encoded\""); + } + } + $matchcount = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches); + break; + /** @noinspection PhpMissingBreakStatementInspection */ + case 'comment': + $matchcount = preg_match_all('/[()"]/', $str, $matches); + // Intentional fall-through + case 'text': + default: + $matchcount += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches); + break; + } + + //There are no chars that need encoding + if ($matchcount == 0) { + return ($str); + } + + $maxlen = 75 - 7 - strlen($this->CharSet); + // Try to select the encoding which should produce the shortest output + if ($matchcount > strlen($str) / 3) { + // More than a third of the content will need encoding, so B encoding will be most efficient + $encoding = 'B'; + if (function_exists('mb_strlen') && $this->hasMultiBytes($str)) { + // Use a custom function which correctly encodes and wraps long + // multibyte strings without breaking lines within a character + $encoded = $this->base64EncodeWrapMB($str, "\n"); + } else { + $encoded = base64_encode($str); + $maxlen -= $maxlen % 4; + $encoded = trim(chunk_split($encoded, $maxlen, "\n")); + } + } else { + $encoding = 'Q'; + $encoded = $this->encodeQ($str, $position); + $encoded = $this->wrapText($encoded, $maxlen, true); + $encoded = str_replace('=' . self::CRLF, "\n", trim($encoded)); + } + + $encoded = preg_replace('/^(.*)$/m', ' =?' . $this->CharSet . "?$encoding?\\1?=", $encoded); + $encoded = trim(str_replace("\n", $this->LE, $encoded)); + + return $encoded; + } + + /** + * Check if a string contains multi-byte characters. + * @access public + * @param string $str multi-byte text to wrap encode + * @return boolean + */ + public function hasMultiBytes($str) + { + if (function_exists('mb_strlen')) { + return (strlen($str) > mb_strlen($str, $this->CharSet)); + } else { // Assume no multibytes (we can't handle without mbstring functions anyway) + return false; + } + } + + /** + * Does a string contain any 8-bit chars (in any charset)? + * @param string $text + * @return boolean + */ + public function has8bitChars($text) + { + return (boolean)preg_match('/[\x80-\xFF]/', $text); + } + + /** + * Encode and wrap long multibyte strings for mail headers + * without breaking lines within a character. + * Adapted from a function by paravoid + * @link http://www.php.net/manual/en/function.mb-encode-mimeheader.php#60283 + * @access public + * @param string $str multi-byte text to wrap encode + * @param string $linebreak string to use as linefeed/end-of-line + * @return string + */ + public function base64EncodeWrapMB($str, $linebreak = null) + { + $start = '=?' . $this->CharSet . '?B?'; + $end = '?='; + $encoded = ''; + if ($linebreak === null) { + $linebreak = $this->LE; + } + + $mb_length = mb_strlen($str, $this->CharSet); + // Each line must have length <= 75, including $start and $end + $length = 75 - strlen($start) - strlen($end); + // Average multi-byte ratio + $ratio = $mb_length / strlen($str); + // Base64 has a 4:3 ratio + $avgLength = floor($length * $ratio * .75); + + for ($i = 0; $i < $mb_length; $i += $offset) { + $lookBack = 0; + do { + $offset = $avgLength - $lookBack; + $chunk = mb_substr($str, $i, $offset, $this->CharSet); + $chunk = base64_encode($chunk); + $lookBack++; + } while (strlen($chunk) > $length); + $encoded .= $chunk . $linebreak; + } + + // Chomp the last linefeed + $encoded = substr($encoded, 0, -strlen($linebreak)); + return $encoded; + } + + /** + * Encode a string in quoted-printable format. + * According to RFC2045 section 6.7. + * @access public + * @param string $string The text to encode + * @param integer $line_max Number of chars allowed on a line before wrapping + * @return string + * @link http://www.php.net/manual/en/function.quoted-printable-decode.php#89417 Adapted from this comment + */ + public function encodeQP($string, $line_max = 76) + { + // Use native function if it's available (>= PHP5.3) + if (function_exists('quoted_printable_encode')) { + return quoted_printable_encode($string); + } + // Fall back to a pure PHP implementation + $string = str_replace( + array('%20', '%0D%0A.', '%0D%0A', '%'), + array(' ', "\r\n=2E", "\r\n", '='), + rawurlencode($string) + ); + return preg_replace('/[^\r\n]{' . ($line_max - 3) . '}[^=\r\n]{2}/', "$0=\r\n", $string); + } + + /** + * Backward compatibility wrapper for an old QP encoding function that was removed. + * @see PHPMailer::encodeQP() + * @access public + * @param string $string + * @param integer $line_max + * @param boolean $space_conv + * @return string + * @deprecated Use encodeQP instead. + */ + public function encodeQPphp( + $string, + $line_max = 76, + /** @noinspection PhpUnusedParameterInspection */ $space_conv = false + ) { + return $this->encodeQP($string, $line_max); + } + + /** + * Encode a string using Q encoding. + * @link http://tools.ietf.org/html/rfc2047 + * @param string $str the text to encode + * @param string $position Where the text is going to be used, see the RFC for what that means + * @access public + * @return string + */ + public function encodeQ($str, $position = 'text') + { + // There should not be any EOL in the string + $pattern = ''; + $encoded = str_replace(array("\r", "\n"), '', $str); + switch (strtolower($position)) { + case 'phrase': + // RFC 2047 section 5.3 + $pattern = '^A-Za-z0-9!*+\/ -'; + break; + /** @noinspection PhpMissingBreakStatementInspection */ + case 'comment': + // RFC 2047 section 5.2 + $pattern = '\(\)"'; + // intentional fall-through + // for this reason we build the $pattern without including delimiters and [] + case 'text': + default: + // RFC 2047 section 5.1 + // Replace every high ascii, control, =, ? and _ characters + $pattern = '\000-\011\013\014\016-\037\075\077\137\177-\377' . $pattern; + break; + } + $matches = array(); + if (preg_match_all("/[{$pattern}]/", $encoded, $matches)) { + // If the string contains an '=', make sure it's the first thing we replace + // so as to avoid double-encoding + $eqkey = array_search('=', $matches[0]); + if (false !== $eqkey) { + unset($matches[0][$eqkey]); + array_unshift($matches[0], '='); + } + foreach (array_unique($matches[0]) as $char) { + $encoded = str_replace($char, '=' . sprintf('%02X', ord($char)), $encoded); + } + } + // Replace every spaces to _ (more readable than =20) + return str_replace(' ', '_', $encoded); + } + + /** + * Add a string or binary attachment (non-filesystem). + * This method can be used to attach ascii or binary data, + * such as a BLOB record from a database. + * @param string $string String attachment data. + * @param string $filename Name of the attachment. + * @param string $encoding File encoding (see $Encoding). + * @param string $type File extension (MIME) type. + * @param string $disposition Disposition to use + * @return void + */ + public function addStringAttachment( + $string, + $filename, + $encoding = 'base64', + $type = '', + $disposition = 'attachment' + ) { + // If a MIME type is not specified, try to work it out from the file name + if ($type == '') { + $type = self::filenameToType($filename); + } + // Append to $attachment array + $this->attachment[] = array( + 0 => $string, + 1 => $filename, + 2 => basename($filename), + 3 => $encoding, + 4 => $type, + 5 => true, // isStringAttachment + 6 => $disposition, + 7 => 0 + ); + } + + /** + * Add an embedded (inline) attachment from a file. + * This can include images, sounds, and just about any other document type. + * These differ from 'regular' attachments in that they are intended to be + * displayed inline with the message, not just attached for download. + * This is used in HTML messages that embed the images + * the HTML refers to using the $cid value. + * @param string $path Path to the attachment. + * @param string $cid Content ID of the attachment; Use this to reference + * the content when using an embedded image in HTML. + * @param string $name Overrides the attachment name. + * @param string $encoding File encoding (see $Encoding). + * @param string $type File MIME type. + * @param string $disposition Disposition to use + * @return boolean True on successfully adding an attachment + */ + public function addEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = '', $disposition = 'inline') + { + if (!@is_file($path)) { + $this->setError($this->lang('file_access') . $path); + return false; + } + + // If a MIME type is not specified, try to work it out from the file name + if ($type == '') { + $type = self::filenameToType($path); + } + + $filename = basename($path); + if ($name == '') { + $name = $filename; + } + + // Append to $attachment array + $this->attachment[] = array( + 0 => $path, + 1 => $filename, + 2 => $name, + 3 => $encoding, + 4 => $type, + 5 => false, // isStringAttachment + 6 => $disposition, + 7 => $cid + ); + return true; + } + + /** + * Add an embedded stringified attachment. + * This can include images, sounds, and just about any other document type. + * Be sure to set the $type to an image type for images: + * JPEG images use 'image/jpeg', GIF uses 'image/gif', PNG uses 'image/png'. + * @param string $string The attachment binary data. + * @param string $cid Content ID of the attachment; Use this to reference + * the content when using an embedded image in HTML. + * @param string $name + * @param string $encoding File encoding (see $Encoding). + * @param string $type MIME type. + * @param string $disposition Disposition to use + * @return boolean True on successfully adding an attachment + */ + public function addStringEmbeddedImage( + $string, + $cid, + $name = '', + $encoding = 'base64', + $type = '', + $disposition = 'inline' + ) { + // If a MIME type is not specified, try to work it out from the name + if ($type == '' and !empty($name)) { + $type = self::filenameToType($name); + } + + // Append to $attachment array + $this->attachment[] = array( + 0 => $string, + 1 => $name, + 2 => $name, + 3 => $encoding, + 4 => $type, + 5 => true, // isStringAttachment + 6 => $disposition, + 7 => $cid + ); + return true; + } + + /** + * Check if an inline attachment is present. + * @access public + * @return boolean + */ + public function inlineImageExists() + { + foreach ($this->attachment as $attachment) { + if ($attachment[6] == 'inline') { + return true; + } + } + return false; + } + + /** + * Check if an attachment (non-inline) is present. + * @return boolean + */ + public function attachmentExists() + { + foreach ($this->attachment as $attachment) { + if ($attachment[6] == 'attachment') { + return true; + } + } + return false; + } + + /** + * Check if this message has an alternative body set. + * @return boolean + */ + public function alternativeExists() + { + return !empty($this->AltBody); + } + + /** + * Clear queued addresses of given kind. + * @access protected + * @param string $kind 'to', 'cc', or 'bcc' + * @return void + */ + public function clearQueuedAddresses($kind) + { + $RecipientsQueue = $this->RecipientsQueue; + foreach ($RecipientsQueue as $address => $params) { + if ($params[0] == $kind) { + unset($this->RecipientsQueue[$address]); + } + } + } + + /** + * Clear all To recipients. + * @return void + */ + public function clearAddresses() + { + foreach ($this->to as $to) { + unset($this->all_recipients[strtolower($to[0])]); + } + $this->to = array(); + $this->clearQueuedAddresses('to'); + } + + /** + * Clear all CC recipients. + * @return void + */ + public function clearCCs() + { + foreach ($this->cc as $cc) { + unset($this->all_recipients[strtolower($cc[0])]); + } + $this->cc = array(); + $this->clearQueuedAddresses('cc'); + } + + /** + * Clear all BCC recipients. + * @return void + */ + public function clearBCCs() + { + foreach ($this->bcc as $bcc) { + unset($this->all_recipients[strtolower($bcc[0])]); + } + $this->bcc = array(); + $this->clearQueuedAddresses('bcc'); + } + + /** + * Clear all ReplyTo recipients. + * @return void + */ + public function clearReplyTos() + { + $this->ReplyTo = array(); + $this->ReplyToQueue = array(); + } + + /** + * Clear all recipient types. + * @return void + */ + public function clearAllRecipients() + { + $this->to = array(); + $this->cc = array(); + $this->bcc = array(); + $this->all_recipients = array(); + $this->RecipientsQueue = array(); + } + + /** + * Clear all filesystem, string, and binary attachments. + * @return void + */ + public function clearAttachments() + { + $this->attachment = array(); + } + + /** + * Clear all custom headers. + * @return void + */ + public function clearCustomHeaders() + { + $this->CustomHeader = array(); + } + + /** + * Add an error message to the error container. + * @access protected + * @param string $msg + * @return void + */ + protected function setError($msg) + { + $this->error_count++; + if ($this->Mailer == 'smtp' and !is_null($this->smtp)) { + $lasterror = $this->smtp->getError(); + if (!empty($lasterror['error'])) { + $msg .= $this->lang('smtp_error') . $lasterror['error']; + if (!empty($lasterror['detail'])) { + $msg .= ' Detail: '. $lasterror['detail']; + } + if (!empty($lasterror['smtp_code'])) { + $msg .= ' SMTP code: ' . $lasterror['smtp_code']; + } + if (!empty($lasterror['smtp_code_ex'])) { + $msg .= ' Additional SMTP info: ' . $lasterror['smtp_code_ex']; + } + } + } + $this->ErrorInfo = $msg; + } + + /** + * Return an RFC 822 formatted date. + * @access public + * @return string + * @static + */ + public static function rfcDate() + { + // Set the time zone to whatever the default is to avoid 500 errors + // Will default to UTC if it's not set properly in php.ini + date_default_timezone_set(@date_default_timezone_get()); + return date('D, j M Y H:i:s O'); + } + + /** + * Get the server hostname. + * Returns 'localhost.localdomain' if unknown. + * @access protected + * @return string + */ + protected function serverHostname() + { + $result = 'localhost.localdomain'; + if (!empty($this->Hostname)) { + $result = $this->Hostname; + } elseif (isset($_SERVER) and array_key_exists('SERVER_NAME', $_SERVER) and !empty($_SERVER['SERVER_NAME'])) { + $result = $_SERVER['SERVER_NAME']; + } elseif (function_exists('gethostname') && gethostname() !== false) { + $result = gethostname(); + } elseif (php_uname('n') !== false) { + $result = php_uname('n'); + } + return $result; + } + + /** + * Get an error message in the current language. + * @access protected + * @param string $key + * @return string + */ + protected function lang($key) + { + if (count($this->language) < 1) { + $this->setLanguage('en'); // set the default language + } + + if (array_key_exists($key, $this->language)) { + if ($key == 'smtp_connect_failed') { + //Include a link to troubleshooting docs on SMTP connection failure + //this is by far the biggest cause of support questions + //but it's usually not PHPMailer's fault. + return $this->language[$key] . ' https://github.com/PHPMailer/PHPMailer/wiki/Troubleshooting'; + } + return $this->language[$key]; + } else { + //Return the key as a fallback + return $key; + } + } + + /** + * Check if an error occurred. + * @access public + * @return boolean True if an error did occur. + */ + public function isError() + { + return ($this->error_count > 0); + } + + /** + * Ensure consistent line endings in a string. + * Changes every end of line from CRLF, CR or LF to $this->LE. + * @access public + * @param string $str String to fixEOL + * @return string + */ + public function fixEOL($str) + { + // Normalise to \n + $nstr = str_replace(array("\r\n", "\r"), "\n", $str); + // Now convert LE as needed + if ($this->LE !== "\n") { + $nstr = str_replace("\n", $this->LE, $nstr); + } + return $nstr; + } + + /** + * Add a custom header. + * $name value can be overloaded to contain + * both header name and value (name:value) + * @access public + * @param string $name Custom header name + * @param string $value Header value + * @return void + */ + public function addCustomHeader($name, $value = null) + { + if ($value === null) { + // Value passed in as name:value + $this->CustomHeader[] = explode(':', $name, 2); + } else { + $this->CustomHeader[] = array($name, $value); + } + } + + /** + * Returns all custom headers. + * @return array + */ + public function getCustomHeaders() + { + return $this->CustomHeader; + } + + /** + * Create a message body from an HTML string. + * Automatically inlines images and creates a plain-text version by converting the HTML, + * overwriting any existing values in Body and AltBody. + * $basedir is used when handling relative image paths, e.g. + * will look for an image file in $basedir/images/a.png and convert it to inline. + * If you don't want to apply these transformations to your HTML, just set Body and AltBody yourself. + * @access public + * @param string $message HTML message string + * @param string $basedir base directory for relative paths to images + * @param boolean|callable $advanced Whether to use the internal HTML to text converter + * or your own custom converter @see PHPMailer::html2text() + * @return string $message The transformed message Body + */ + public function msgHTML($message, $basedir = '', $advanced = false) + { + preg_match_all('/(src|background)=["\'](.*)["\']/Ui', $message, $images); + if (array_key_exists(2, $images)) { + foreach ($images[2] as $imgindex => $url) { + // Convert data URIs into embedded images + if (preg_match('#^data:(image[^;,]*)(;base64)?,#', $url, $match)) { + $data = substr($url, strpos($url, ',')); + if ($match[2]) { + $data = base64_decode($data); + } else { + $data = rawurldecode($data); + } + $cid = md5($url) . '@phpmailer.0'; // RFC2392 S 2 + if ($this->addStringEmbeddedImage($data, $cid, 'embed' . $imgindex, 'base64', $match[1])) { + $message = str_replace( + $images[0][$imgindex], + $images[1][$imgindex] . '="cid:' . $cid . '"', + $message + ); + } + } elseif (substr($url, 0, 4) !== 'cid:' && !preg_match('#^[a-z][a-z0-9+.-]*://#i', $url)) { + // Do not change urls for absolute images (thanks to corvuscorax) + // Do not change urls that are already inline images + $filename = basename($url); + $directory = dirname($url); + if ($directory == '.') { + $directory = ''; + } + $cid = md5($url) . '@phpmailer.0'; // RFC2392 S 2 + if (strlen($basedir) > 1 && substr($basedir, -1) != '/') { + $basedir .= '/'; + } + if (strlen($directory) > 1 && substr($directory, -1) != '/') { + $directory .= '/'; + } + if ($this->addEmbeddedImage( + $basedir . $directory . $filename, + $cid, + $filename, + 'base64', + self::_mime_types((string)self::mb_pathinfo($filename, PATHINFO_EXTENSION)) + ) + ) { + $message = preg_replace( + '/' . $images[1][$imgindex] . '=["\']' . preg_quote($url, '/') . '["\']/Ui', + $images[1][$imgindex] . '="cid:' . $cid . '"', + $message + ); + } + } + } + } + $this->isHTML(true); + // Convert all message body line breaks to CRLF, makes quoted-printable encoding work much better + $this->Body = $this->normalizeBreaks($message); + $this->AltBody = $this->normalizeBreaks($this->html2text($message, $advanced)); + if (!$this->alternativeExists()) { + $this->AltBody = 'To view this email message, open it in a program that understands HTML!' . + self::CRLF . self::CRLF; + } + return $this->Body; + } + + /** + * Convert an HTML string into plain text. + * This is used by msgHTML(). + * Note - older versions of this function used a bundled advanced converter + * which was been removed for license reasons in #232. + * Example usage: + * + * // Use default conversion + * $plain = $mail->html2text($html); + * // Use your own custom converter + * $plain = $mail->html2text($html, function($html) { + * $converter = new MyHtml2text($html); + * return $converter->get_text(); + * }); + * + * @param string $html The HTML text to convert + * @param boolean|callable $advanced Any boolean value to use the internal converter, + * or provide your own callable for custom conversion. + * @return string + */ + public function html2text($html, $advanced = false) + { + if (is_callable($advanced)) { + return call_user_func($advanced, $html); + } + return html_entity_decode( + trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/si', '', $html))), + ENT_QUOTES, + $this->CharSet + ); + } + + /** + * Get the MIME type for a file extension. + * @param string $ext File extension + * @access public + * @return string MIME type of file. + * @static + */ + public static function _mime_types($ext = '') + { + $mimes = array( + 'xl' => 'application/excel', + 'js' => 'application/javascript', + 'hqx' => 'application/mac-binhex40', + 'cpt' => 'application/mac-compactpro', + 'bin' => 'application/macbinary', + 'doc' => 'application/msword', + 'word' => 'application/msword', + 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template', + 'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template', + 'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow', + 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', + 'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide', + 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template', + 'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12', + 'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12', + 'class' => 'application/octet-stream', + 'dll' => 'application/octet-stream', + 'dms' => 'application/octet-stream', + 'exe' => 'application/octet-stream', + 'lha' => 'application/octet-stream', + 'lzh' => 'application/octet-stream', + 'psd' => 'application/octet-stream', + 'sea' => 'application/octet-stream', + 'so' => 'application/octet-stream', + 'oda' => 'application/oda', + 'pdf' => 'application/pdf', + 'ai' => 'application/postscript', + 'eps' => 'application/postscript', + 'ps' => 'application/postscript', + 'smi' => 'application/smil', + 'smil' => 'application/smil', + 'mif' => 'application/vnd.mif', + 'xls' => 'application/vnd.ms-excel', + 'ppt' => 'application/vnd.ms-powerpoint', + 'wbxml' => 'application/vnd.wap.wbxml', + 'wmlc' => 'application/vnd.wap.wmlc', + 'dcr' => 'application/x-director', + 'dir' => 'application/x-director', + 'dxr' => 'application/x-director', + 'dvi' => 'application/x-dvi', + 'gtar' => 'application/x-gtar', + 'php3' => 'application/x-httpd-php', + 'php4' => 'application/x-httpd-php', + 'php' => 'application/x-httpd-php', + 'phtml' => 'application/x-httpd-php', + 'phps' => 'application/x-httpd-php-source', + 'swf' => 'application/x-shockwave-flash', + 'sit' => 'application/x-stuffit', + 'tar' => 'application/x-tar', + 'tgz' => 'application/x-tar', + 'xht' => 'application/xhtml+xml', + 'xhtml' => 'application/xhtml+xml', + 'zip' => 'application/zip', + 'mid' => 'audio/midi', + 'midi' => 'audio/midi', + 'mp2' => 'audio/mpeg', + 'mp3' => 'audio/mpeg', + 'mpga' => 'audio/mpeg', + 'aif' => 'audio/x-aiff', + 'aifc' => 'audio/x-aiff', + 'aiff' => 'audio/x-aiff', + 'ram' => 'audio/x-pn-realaudio', + 'rm' => 'audio/x-pn-realaudio', + 'rpm' => 'audio/x-pn-realaudio-plugin', + 'ra' => 'audio/x-realaudio', + 'wav' => 'audio/x-wav', + 'bmp' => 'image/bmp', + 'gif' => 'image/gif', + 'jpeg' => 'image/jpeg', + 'jpe' => 'image/jpeg', + 'jpg' => 'image/jpeg', + 'png' => 'image/png', + 'tiff' => 'image/tiff', + 'tif' => 'image/tiff', + 'eml' => 'message/rfc822', + 'css' => 'text/css', + 'html' => 'text/html', + 'htm' => 'text/html', + 'shtml' => 'text/html', + 'log' => 'text/plain', + 'text' => 'text/plain', + 'txt' => 'text/plain', + 'rtx' => 'text/richtext', + 'rtf' => 'text/rtf', + 'vcf' => 'text/vcard', + 'vcard' => 'text/vcard', + 'xml' => 'text/xml', + 'xsl' => 'text/xml', + 'mpeg' => 'video/mpeg', + 'mpe' => 'video/mpeg', + 'mpg' => 'video/mpeg', + 'mov' => 'video/quicktime', + 'qt' => 'video/quicktime', + 'rv' => 'video/vnd.rn-realvideo', + 'avi' => 'video/x-msvideo', + 'movie' => 'video/x-sgi-movie' + ); + if (array_key_exists(strtolower($ext), $mimes)) { + return $mimes[strtolower($ext)]; + } + return 'application/octet-stream'; + } + + /** + * Map a file name to a MIME type. + * Defaults to 'application/octet-stream', i.e.. arbitrary binary data. + * @param string $filename A file name or full path, does not need to exist as a file + * @return string + * @static + */ + public static function filenameToType($filename) + { + // In case the path is a URL, strip any query string before getting extension + $qpos = strpos($filename, '?'); + if (false !== $qpos) { + $filename = substr($filename, 0, $qpos); + } + $pathinfo = self::mb_pathinfo($filename); + return self::_mime_types($pathinfo['extension']); + } + + /** + * Multi-byte-safe pathinfo replacement. + * Drop-in replacement for pathinfo(), but multibyte-safe, cross-platform-safe, old-version-safe. + * Works similarly to the one in PHP >= 5.2.0 + * @link http://www.php.net/manual/en/function.pathinfo.php#107461 + * @param string $path A filename or path, does not need to exist as a file + * @param integer|string $options Either a PATHINFO_* constant, + * or a string name to return only the specified piece, allows 'filename' to work on PHP < 5.2 + * @return string|array + * @static + */ + public static function mb_pathinfo($path, $options = null) + { + $ret = array('dirname' => '', 'basename' => '', 'extension' => '', 'filename' => ''); + $pathinfo = array(); + if (preg_match('%^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^\.\\\\/]+?)|))[\\\\/\.]*$%im', $path, $pathinfo)) { + if (array_key_exists(1, $pathinfo)) { + $ret['dirname'] = $pathinfo[1]; + } + if (array_key_exists(2, $pathinfo)) { + $ret['basename'] = $pathinfo[2]; + } + if (array_key_exists(5, $pathinfo)) { + $ret['extension'] = $pathinfo[5]; + } + if (array_key_exists(3, $pathinfo)) { + $ret['filename'] = $pathinfo[3]; + } + } + switch ($options) { + case PATHINFO_DIRNAME: + case 'dirname': + return $ret['dirname']; + case PATHINFO_BASENAME: + case 'basename': + return $ret['basename']; + case PATHINFO_EXTENSION: + case 'extension': + return $ret['extension']; + case PATHINFO_FILENAME: + case 'filename': + return $ret['filename']; + default: + return $ret; + } + } + + /** + * Set or reset instance properties. + * You should avoid this function - it's more verbose, less efficient, more error-prone and + * harder to debug than setting properties directly. + * Usage Example: + * `$mail->set('SMTPSecure', 'tls');` + * is the same as: + * `$mail->SMTPSecure = 'tls';` + * @access public + * @param string $name The property name to set + * @param mixed $value The value to set the property to + * @return boolean + * @TODO Should this not be using the __set() magic function? + */ + public function set($name, $value = '') + { + if (property_exists($this, $name)) { + $this->$name = $value; + return true; + } else { + $this->setError($this->lang('variable_set') . $name); + return false; + } + } + + /** + * Strip newlines to prevent header injection. + * @access public + * @param string $str + * @return string + */ + public function secureHeader($str) + { + return trim(str_replace(array("\r", "\n"), '', $str)); + } + + /** + * Normalize line breaks in a string. + * Converts UNIX LF, Mac CR and Windows CRLF line breaks into a single line break format. + * Defaults to CRLF (for message bodies) and preserves consecutive breaks. + * @param string $text + * @param string $breaktype What kind of line break to use, defaults to CRLF + * @return string + * @access public + * @static + */ + public static function normalizeBreaks($text, $breaktype = "\r\n") + { + return preg_replace('/(\r\n|\r|\n)/ms', $breaktype, $text); + } + + /** + * Set the public and private key files and password for S/MIME signing. + * @access public + * @param string $cert_filename + * @param string $key_filename + * @param string $key_pass Password for private key + * @param string $extracerts_filename Optional path to chain certificate + */ + public function sign($cert_filename, $key_filename, $key_pass, $extracerts_filename = '') + { + $this->sign_cert_file = $cert_filename; + $this->sign_key_file = $key_filename; + $this->sign_key_pass = $key_pass; + $this->sign_extracerts_file = $extracerts_filename; + } + + /** + * Quoted-Printable-encode a DKIM header. + * @access public + * @param string $txt + * @return string + */ + public function DKIM_QP($txt) + { + $line = ''; + for ($i = 0; $i < strlen($txt); $i++) { + $ord = ord($txt[$i]); + if (((0x21 <= $ord) && ($ord <= 0x3A)) || $ord == 0x3C || ((0x3E <= $ord) && ($ord <= 0x7E))) { + $line .= $txt[$i]; + } else { + $line .= '=' . sprintf('%02X', $ord); + } + } + return $line; + } + + /** + * Generate a DKIM signature. + * @access public + * @param string $signHeader + * @throws phpmailerException + * @return string The DKIM signature value + */ + public function DKIM_Sign($signHeader) + { + if (!defined('PKCS7_TEXT')) { + if ($this->exceptions) { + throw new phpmailerException($this->lang('extension_missing') . 'openssl'); + } + return ''; + } + $privKeyStr = !empty($this->DKIM_private_string) ? $this->DKIM_private_string : file_get_contents($this->DKIM_private); + if ('' != $this->DKIM_passphrase) { + $privKey = openssl_pkey_get_private($privKeyStr, $this->DKIM_passphrase); + } else { + $privKey = openssl_pkey_get_private($privKeyStr); + } + //Workaround for missing digest algorithms in old PHP & OpenSSL versions + //@link http://stackoverflow.com/a/11117338/333340 + if (version_compare(PHP_VERSION, '5.3.0') >= 0 and + in_array('sha256WithRSAEncryption', openssl_get_md_methods(true))) { + if (openssl_sign($signHeader, $signature, $privKey, 'sha256WithRSAEncryption')) { + openssl_pkey_free($privKey); + return base64_encode($signature); + } + } else { + $pinfo = openssl_pkey_get_details($privKey); + $hash = hash('sha256', $signHeader); + //'Magic' constant for SHA256 from RFC3447 + //@link https://tools.ietf.org/html/rfc3447#page-43 + $t = '3031300d060960864801650304020105000420' . $hash; + $pslen = $pinfo['bits'] / 8 - (strlen($t) / 2 + 3); + $eb = pack('H*', '0001' . str_repeat('FF', $pslen) . '00' . $t); + + if (openssl_private_encrypt($eb, $signature, $privKey, OPENSSL_NO_PADDING)) { + openssl_pkey_free($privKey); + return base64_encode($signature); + } + } + openssl_pkey_free($privKey); + return ''; + } + + /** + * Generate a DKIM canonicalization header. + * @access public + * @param string $signHeader Header + * @return string + */ + public function DKIM_HeaderC($signHeader) + { + $signHeader = preg_replace('/\r\n\s+/', ' ', $signHeader); + $lines = explode("\r\n", $signHeader); + foreach ($lines as $key => $line) { + list($heading, $value) = explode(':', $line, 2); + $heading = strtolower($heading); + $value = preg_replace('/\s{2,}/', ' ', $value); // Compress useless spaces + $lines[$key] = $heading . ':' . trim($value); // Don't forget to remove WSP around the value + } + $signHeader = implode("\r\n", $lines); + return $signHeader; + } + + /** + * Generate a DKIM canonicalization body. + * @access public + * @param string $body Message Body + * @return string + */ + public function DKIM_BodyC($body) + { + if ($body == '') { + return "\r\n"; + } + // stabilize line endings + $body = str_replace("\r\n", "\n", $body); + $body = str_replace("\n", "\r\n", $body); + // END stabilize line endings + while (substr($body, strlen($body) - 4, 4) == "\r\n\r\n") { + $body = substr($body, 0, strlen($body) - 2); + } + return $body; + } + + /** + * Create the DKIM header and body in a new message header. + * @access public + * @param string $headers_line Header lines + * @param string $subject Subject + * @param string $body Body + * @return string + */ + public function DKIM_Add($headers_line, $subject, $body) + { + $DKIMsignatureType = 'rsa-sha256'; // Signature & hash algorithms + $DKIMcanonicalization = 'relaxed/simple'; // Canonicalization of header/body + $DKIMquery = 'dns/txt'; // Query method + $DKIMtime = time(); // Signature Timestamp = seconds since 00:00:00 - Jan 1, 1970 (UTC time zone) + $subject_header = "Subject: $subject"; + $headers = explode($this->LE, $headers_line); + $from_header = ''; + $to_header = ''; + $date_header = ''; + $current = ''; + foreach ($headers as $header) { + if (strpos($header, 'From:') === 0) { + $from_header = $header; + $current = 'from_header'; + } elseif (strpos($header, 'To:') === 0) { + $to_header = $header; + $current = 'to_header'; + } elseif (strpos($header, 'Date:') === 0) { + $date_header = $header; + $current = 'date_header'; + } else { + if (!empty($$current) && strpos($header, ' =?') === 0) { + $$current .= $header; + } else { + $current = ''; + } + } + } + $from = str_replace('|', '=7C', $this->DKIM_QP($from_header)); + $to = str_replace('|', '=7C', $this->DKIM_QP($to_header)); + $date = str_replace('|', '=7C', $this->DKIM_QP($date_header)); + $subject = str_replace( + '|', + '=7C', + $this->DKIM_QP($subject_header) + ); // Copied header fields (dkim-quoted-printable) + $body = $this->DKIM_BodyC($body); + $DKIMlen = strlen($body); // Length of body + $DKIMb64 = base64_encode(pack('H*', hash('sha256', $body))); // Base64 of packed binary SHA-256 hash of body + if ('' == $this->DKIM_identity) { + $ident = ''; + } else { + $ident = ' i=' . $this->DKIM_identity . ';'; + } + $dkimhdrs = 'DKIM-Signature: v=1; a=' . + $DKIMsignatureType . '; q=' . + $DKIMquery . '; l=' . + $DKIMlen . '; s=' . + $this->DKIM_selector . + ";\r\n" . + "\tt=" . $DKIMtime . '; c=' . $DKIMcanonicalization . ";\r\n" . + "\th=From:To:Date:Subject;\r\n" . + "\td=" . $this->DKIM_domain . ';' . $ident . "\r\n" . + "\tz=$from\r\n" . + "\t|$to\r\n" . + "\t|$date\r\n" . + "\t|$subject;\r\n" . + "\tbh=" . $DKIMb64 . ";\r\n" . + "\tb="; + $toSign = $this->DKIM_HeaderC( + $from_header . "\r\n" . + $to_header . "\r\n" . + $date_header . "\r\n" . + $subject_header . "\r\n" . + $dkimhdrs + ); + $signed = $this->DKIM_Sign($toSign); + return $dkimhdrs . $signed . "\r\n"; + } + + /** + * Detect if a string contains a line longer than the maximum line length allowed. + * @param string $str + * @return boolean + * @static + */ + public static function hasLineLongerThanMax($str) + { + //+2 to include CRLF line break for a 1000 total + return (boolean)preg_match('/^(.{'.(self::MAX_LINE_LENGTH + 2).',})/m', $str); + } + + /** + * Allows for public read access to 'to' property. + * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included. + * @access public + * @return array + */ + public function getToAddresses() + { + return $this->to; + } + + /** + * Allows for public read access to 'cc' property. + * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included. + * @access public + * @return array + */ + public function getCcAddresses() + { + return $this->cc; + } + + /** + * Allows for public read access to 'bcc' property. + * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included. + * @access public + * @return array + */ + public function getBccAddresses() + { + return $this->bcc; + } + + /** + * Allows for public read access to 'ReplyTo' property. + * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included. + * @access public + * @return array + */ + public function getReplyToAddresses() + { + return $this->ReplyTo; + } + + /** + * Allows for public read access to 'all_recipients' property. + * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included. + * @access public + * @return array + */ + public function getAllRecipientAddresses() + { + return $this->all_recipients; + } + + /** + * Perform a callback. + * @param boolean $isSent + * @param array $to + * @param array $cc + * @param array $bcc + * @param string $subject + * @param string $body + * @param string $from + */ + protected function doCallback($isSent, $to, $cc, $bcc, $subject, $body, $from) + { + if (!empty($this->action_function) && is_callable($this->action_function)) { + $params = array($isSent, $to, $cc, $bcc, $subject, $body, $from); + call_user_func_array($this->action_function, $params); + } + } } /** @@ -3912,13 +4013,13 @@ class PHPMailer */ class phpmailerException extends Exception { - /** - * Prettify error message output - * @return string - */ - public function errorMessage() - { - $errorMsg = '' . $this->getMessage() . "
\n"; - return $errorMsg; - } + /** + * Prettify error message output + * @return string + */ + public function errorMessage() + { + $errorMsg = '' . $this->getMessage() . "
\n"; + return $errorMsg; + } } diff --git a/lib/classes/phpmailer/class.SMTP.php b/lib/classes/phpmailer/class.SMTP.php index 0c016f12..886782dc 100644 --- a/lib/classes/phpmailer/class.SMTP.php +++ b/lib/classes/phpmailer/class.SMTP.php @@ -1,1192 +1,1249 @@ - * @author Jim Jagielski (jimjag) - * @author Andy Prevost (codeworxtech) - * @author Brent R. Matzelle (original founder) - * @copyright 2014 Marcus Bointon - * @copyright 2010 - 2012 Jim Jagielski - * @copyright 2004 - 2009 Andy Prevost - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - * @note This program is distributed in the hope that it will be useful - WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. - */ +* PHP Version 5 +* @package PHPMailer +* @link https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project +* @author Marcus Bointon (Synchro/coolbru) +* @author Jim Jagielski (jimjag) +* @author Andy Prevost (codeworxtech) +* @author Brent R. Matzelle (original founder) +* @copyright 2014 Marcus Bointon +* @copyright 2010 - 2012 Jim Jagielski +* @copyright 2004 - 2009 Andy Prevost +* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License +* @note This program is distributed in the hope that it will be useful - WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. +*/ /** * PHPMailer RFC821 SMTP email transport class. - * Implements RFC 821 SMTP commands and provides some utility methods for sending mail to an SMTP server. - * @package PHPMailer - * @author Chris Ryan - * @author Marcus Bointon - */ +* Implements RFC 821 SMTP commands and provides some utility methods for sending mail to an SMTP server. +* @package PHPMailer +* @author Chris Ryan +* @author Marcus Bointon +*/ class SMTP { - /** - * The PHPMailer SMTP version number. - * @var string - */ - const VERSION = '5.2.16'; - - /** - * SMTP line break constant. - * @var string - */ - const CRLF = "\r\n"; - - /** - * The SMTP port to use if one is not specified. - * @var integer - */ - const DEFAULT_SMTP_PORT = 25; - - /** - * The maximum line length allowed by RFC 2822 section 2.1.1 - * @var integer - */ - const MAX_LINE_LENGTH = 998; - - /** - * Debug level for no output - */ - const DEBUG_OFF = 0; - - /** - * Debug level to show client -> server messages - */ - const DEBUG_CLIENT = 1; - - /** - * Debug level to show client -> server and server -> client messages - */ - const DEBUG_SERVER = 2; - - /** - * Debug level to show connection status, client -> server and server -> client messages - */ - const DEBUG_CONNECTION = 3; - - /** - * Debug level to show all messages - */ - const DEBUG_LOWLEVEL = 4; - - /** - * The PHPMailer SMTP Version number. - * @var string - * @deprecated Use the `VERSION` constant instead - * @see SMTP::VERSION - */ - public $Version = '5.2.16'; - - /** - * SMTP server port number. - * @var integer - * @deprecated This is only ever used as a default value, so use the `DEFAULT_SMTP_PORT` constant instead - * @see SMTP::DEFAULT_SMTP_PORT - */ - public $SMTP_PORT = 25; - - /** - * SMTP reply line ending. - * @var string - * @deprecated Use the `CRLF` constant instead - * @see SMTP::CRLF - */ - public $CRLF = "\r\n"; - - /** - * Debug output level. - * Options: - * * self::DEBUG_OFF (`0`) No debug output, default - * * self::DEBUG_CLIENT (`1`) Client commands - * * self::DEBUG_SERVER (`2`) Client commands and server responses - * * self::DEBUG_CONNECTION (`3`) As DEBUG_SERVER plus connection status - * * self::DEBUG_LOWLEVEL (`4`) Low-level data output, all messages - * @var integer - */ - public $do_debug = self::DEBUG_OFF; - - /** - * How to handle debug output. - * Options: - * * `echo` Output plain-text as-is, appropriate for CLI - * * `html` Output escaped, line breaks converted to `
`, appropriate for browser output - * * `error_log` Output to error log as configured in php.ini - * - * Alternatively, you can provide a callable expecting two params: a message string and the debug level: - * - * $smtp->Debugoutput = function($str, $level) {echo "debug level $level; message: $str";}; - * - * @var string|callable - */ - public $Debugoutput = 'echo'; - - /** - * Whether to use VERP. - * @link http://en.wikipedia.org/wiki/Variable_envelope_return_path - * @link http://www.postfix.org/VERP_README.html Info on VERP - * @var boolean - */ - public $do_verp = false; - - /** - * The timeout value for connection, in seconds. - * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2 - * This needs to be quite high to function correctly with hosts using greetdelay as an anti-spam measure. - * @link http://tools.ietf.org/html/rfc2821#section-4.5.3.2 - * @var integer - */ - public $Timeout = 300; - - /** - * How long to wait for commands to complete, in seconds. - * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2 - * @var integer - */ - public $Timelimit = 300; - - /** - * The socket for the server connection. - * @var resource - */ - protected $smtp_conn; - - /** - * Error information, if any, for the last SMTP command. - * @var array - */ - protected $error = array( - 'error' => '', - 'detail' => '', - 'smtp_code' => '', - 'smtp_code_ex' => '' - ); - - /** - * The reply the server sent to us for HELO. - * If null, no HELO string has yet been received. - * @var string|null - */ - protected $helo_rply = null; - - /** - * The set of SMTP extensions sent in reply to EHLO command. - * Indexes of the array are extension names. - * Value at index 'HELO' or 'EHLO' (according to command that was sent) - * represents the server name. In case of HELO it is the only element of the array. - * Other values can be boolean TRUE or an array containing extension options. - * If null, no HELO/EHLO string has yet been received. - * @var array|null - */ - protected $server_caps = null; - - /** - * The most recent reply received from the server. - * @var string - */ - protected $last_reply = ''; - - /** - * Output debugging info via a user-selected method. - * @see SMTP::$Debugoutput - * @see SMTP::$do_debug - * @param string $str Debug string to output - * @param integer $level The debug level of this message; see DEBUG_* constants - * @return void - */ - protected function edebug($str, $level = 0) - { - if ($level > $this->do_debug) { - return; - } - //Avoid clash with built-in function names - if (!in_array($this->Debugoutput, array('error_log', 'html', 'echo')) and is_callable($this->Debugoutput)) { - call_user_func($this->Debugoutput, $str, $this->do_debug); - return; - } - switch ($this->Debugoutput) { - case 'error_log': - //Don't output, just log - error_log($str); - break; - case 'html': - //Cleans up output a bit for a better looking, HTML-safe output - echo htmlentities( - preg_replace('/[\r\n]+/', '', $str), - ENT_QUOTES, - 'UTF-8' - ) - . "
\n"; - break; - case 'echo': - default: - //Normalize line breaks - $str = preg_replace('/(\r\n|\r|\n)/ms', "\n", $str); - echo gmdate('Y-m-d H:i:s') . "\t" . str_replace( - "\n", - "\n \t ", - trim($str) - )."\n"; - } - } - - /** - * Connect to an SMTP server. - * @param string $host SMTP server IP or host name - * @param integer $port The port number to connect to - * @param integer $timeout How long to wait for the connection to open - * @param array $options An array of options for stream_context_create() - * @access public - * @return boolean - */ - public function connect($host, $port = null, $timeout = 30, $options = array()) - { - static $streamok; - //This is enabled by default since 5.0.0 but some providers disable it - //Check this once and cache the result - if (is_null($streamok)) { - $streamok = function_exists('stream_socket_client'); - } - // Clear errors to avoid confusion - $this->setError(''); - // Make sure we are __not__ connected - if ($this->connected()) { - // Already connected, generate error - $this->setError('Already connected to a server'); - return false; - } - if (empty($port)) { - $port = self::DEFAULT_SMTP_PORT; - } - // Connect to the SMTP server - $this->edebug( - "Connection: opening to $host:$port, timeout=$timeout, options=".var_export($options, true), - self::DEBUG_CONNECTION - ); - $errno = 0; - $errstr = ''; - if ($streamok) { - $socket_context = stream_context_create($options); - //Suppress errors; connection failures are handled at a higher level - $this->smtp_conn = @stream_socket_client( - $host . ":" . $port, - $errno, - $errstr, - $timeout, - STREAM_CLIENT_CONNECT, - $socket_context - ); - } else { - //Fall back to fsockopen which should work in more places, but is missing some features - $this->edebug( - "Connection: stream_socket_client not available, falling back to fsockopen", - self::DEBUG_CONNECTION - ); - $this->smtp_conn = fsockopen( - $host, - $port, - $errno, - $errstr, - $timeout - ); - } - // Verify we connected properly - if (!is_resource($this->smtp_conn)) { - $this->setError( - 'Failed to connect to server', - $errno, - $errstr - ); - $this->edebug( - 'SMTP ERROR: ' . $this->error['error'] - . ": $errstr ($errno)", - self::DEBUG_CLIENT - ); - return false; - } - $this->edebug('Connection: opened', self::DEBUG_CONNECTION); - // SMTP server can take longer to respond, give longer timeout for first read - // Windows does not have support for this timeout function - if (substr(PHP_OS, 0, 3) != 'WIN') { - $max = ini_get('max_execution_time'); - // Don't bother if unlimited - if ($max != 0 && $timeout > $max) { - @set_time_limit($timeout); - } - stream_set_timeout($this->smtp_conn, $timeout, 0); - } - // Get any announcement - $announce = $this->get_lines(); - $this->edebug('SERVER -> CLIENT: ' . $announce, self::DEBUG_SERVER); - return true; - } - - /** - * Initiate a TLS (encrypted) session. - * @access public - * @return boolean - */ - public function startTLS() - { - if (!$this->sendCommand('STARTTLS', 'STARTTLS', 220)) { - return false; - } - - //Allow the best TLS version(s) we can - $crypto_method = STREAM_CRYPTO_METHOD_TLS_CLIENT; - - //PHP 5.6.7 dropped inclusion of TLS 1.1 and 1.2 in STREAM_CRYPTO_METHOD_TLS_CLIENT - //so add them back in manually if we can - if (defined('STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT')) { - $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT; - $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT; - } - - // Begin encrypted connection - if (!stream_socket_enable_crypto( - $this->smtp_conn, - true, - $crypto_method - )) { - return false; - } - return true; - } - - /** - * Perform SMTP authentication. - * Must be run after hello(). - * @see hello() - * @param string $username The user name - * @param string $password The password - * @param string $authtype The auth type (PLAIN, LOGIN, NTLM, CRAM-MD5, XOAUTH2) - * @param string $realm The auth realm for NTLM - * @param string $workstation The auth workstation for NTLM - * @param null|OAuth $OAuth An optional OAuth instance (@see PHPMailerOAuth) - * @return bool True if successfully authenticated.* @access public - */ - public function authenticate( - $username, - $password, - $authtype = null, - $realm = '', - $workstation = '', - $OAuth = null - ) { - if (!$this->server_caps) { - $this->setError('Authentication is not allowed before HELO/EHLO'); - return false; - } - - if (array_key_exists('EHLO', $this->server_caps)) { - // SMTP extensions are available. Let's try to find a proper authentication method - - if (!array_key_exists('AUTH', $this->server_caps)) { - $this->setError('Authentication is not allowed at this stage'); - // 'at this stage' means that auth may be allowed after the stage changes - // e.g. after STARTTLS - return false; - } - - self::edebug('Auth method requested: ' . ($authtype ? $authtype : 'UNKNOWN'), self::DEBUG_LOWLEVEL); - self::edebug( - 'Auth methods available on the server: ' . implode(',', $this->server_caps['AUTH']), - self::DEBUG_LOWLEVEL - ); - - if (empty($authtype)) { - foreach (array('CRAM-MD5', 'LOGIN', 'PLAIN', 'NTLM', 'XOAUTH2') as $method) { - if (in_array($method, $this->server_caps['AUTH'])) { - $authtype = $method; - break; - } - } - if (empty($authtype)) { - $this->setError('No supported authentication methods found'); - return false; - } - self::edebug('Auth method selected: '.$authtype, self::DEBUG_LOWLEVEL); - } - - if (!in_array($authtype, $this->server_caps['AUTH'])) { - $this->setError("The requested authentication method \"$authtype\" is not supported by the server"); - return false; - } - } elseif (empty($authtype)) { - $authtype = 'LOGIN'; - } - switch ($authtype) { - case 'PLAIN': - // Start authentication - if (!$this->sendCommand('AUTH', 'AUTH PLAIN', 334)) { - return false; - } - // Send encoded username and password - if (!$this->sendCommand( - 'User & Password', - base64_encode("\0" . $username . "\0" . $password), - 235 - ) - ) { - return false; - } - break; - case 'LOGIN': - // Start authentication - if (!$this->sendCommand('AUTH', 'AUTH LOGIN', 334)) { - return false; - } - if (!$this->sendCommand("Username", base64_encode($username), 334)) { - return false; - } - if (!$this->sendCommand("Password", base64_encode($password), 235)) { - return false; - } - break; - case 'XOAUTH2': - //If the OAuth Instance is not set. Can be a case when PHPMailer is used - //instead of PHPMailerOAuth - if (is_null($OAuth)) { - return false; - } - $oauth = $OAuth->getOauth64(); - - // Start authentication - if (!$this->sendCommand('AUTH', 'AUTH XOAUTH2 ' . $oauth, 235)) { - return false; - } - break; - case 'NTLM': - /* - * ntlm_sasl_client.php - * Bundled with Permission - * - * How to telnet in windows: - * http://technet.microsoft.com/en-us/library/aa995718%28EXCHG.65%29.aspx - * PROTOCOL Docs http://curl.haxx.se/rfc/ntlm.html#ntlmSmtpAuthentication - */ - require_once 'extras/ntlm_sasl_client.php'; - $temp = new stdClass; - $ntlm_client = new ntlm_sasl_client_class; - //Check that functions are available - if (!$ntlm_client->Initialize($temp)) { - $this->setError($temp->error); - $this->edebug( - 'You need to enable some modules in your php.ini file: ' - . $this->error['error'], - self::DEBUG_CLIENT - ); - return false; - } - //msg1 - $msg1 = $ntlm_client->TypeMsg1($realm, $workstation); //msg1 - - if (!$this->sendCommand( - 'AUTH NTLM', - 'AUTH NTLM ' . base64_encode($msg1), - 334 - ) - ) { - return false; - } - //Though 0 based, there is a white space after the 3 digit number - //msg2 - $challenge = substr($this->last_reply, 3); - $challenge = base64_decode($challenge); - $ntlm_res = $ntlm_client->NTLMResponse( - substr($challenge, 24, 8), - $password - ); - //msg3 - $msg3 = $ntlm_client->TypeMsg3( - $ntlm_res, - $username, - $realm, - $workstation - ); - // send encoded username - return $this->sendCommand('Username', base64_encode($msg3), 235); - case 'CRAM-MD5': - // Start authentication - if (!$this->sendCommand('AUTH CRAM-MD5', 'AUTH CRAM-MD5', 334)) { - return false; - } - // Get the challenge - $challenge = base64_decode(substr($this->last_reply, 4)); - - // Build the response - $response = $username . ' ' . $this->hmac($challenge, $password); - - // send encoded credentials - return $this->sendCommand('Username', base64_encode($response), 235); - default: - $this->setError("Authentication method \"$authtype\" is not supported"); - return false; - } - return true; - } - - /** - * Calculate an MD5 HMAC hash. - * Works like hash_hmac('md5', $data, $key) - * in case that function is not available - * @param string $data The data to hash - * @param string $key The key to hash with - * @access protected - * @return string - */ - protected function hmac($data, $key) - { - if (function_exists('hash_hmac')) { - return hash_hmac('md5', $data, $key); - } - - // The following borrowed from - // http://php.net/manual/en/function.mhash.php#27225 - - // RFC 2104 HMAC implementation for php. - // Creates an md5 HMAC. - // Eliminates the need to install mhash to compute a HMAC - // by Lance Rushing - - $bytelen = 64; // byte length for md5 - if (strlen($key) > $bytelen) { - $key = pack('H*', md5($key)); - } - $key = str_pad($key, $bytelen, chr(0x00)); - $ipad = str_pad('', $bytelen, chr(0x36)); - $opad = str_pad('', $bytelen, chr(0x5c)); - $k_ipad = $key ^ $ipad; - $k_opad = $key ^ $opad; - - return md5($k_opad . pack('H*', md5($k_ipad . $data))); - } - - /** - * Check connection state. - * @access public - * @return boolean True if connected. - */ - public function connected() - { - if (is_resource($this->smtp_conn)) { - $sock_status = stream_get_meta_data($this->smtp_conn); - if ($sock_status['eof']) { - // The socket is valid but we are not connected - $this->edebug( - 'SMTP NOTICE: EOF caught while checking if connected', - self::DEBUG_CLIENT - ); - $this->close(); - return false; - } - return true; // everything looks good - } - return false; - } - - /** - * Close the socket and clean up the state of the class. - * Don't use this function without first trying to use QUIT. - * @see quit() - * @access public - * @return void - */ - public function close() - { - $this->setError(''); - $this->server_caps = null; - $this->helo_rply = null; - if (is_resource($this->smtp_conn)) { - // close the connection and cleanup - fclose($this->smtp_conn); - $this->smtp_conn = null; //Makes for cleaner serialization - $this->edebug('Connection: closed', self::DEBUG_CONNECTION); - } - } - - /** - * Send an SMTP DATA command. - * Issues a data command and sends the msg_data to the server, - * finializing the mail transaction. $msg_data is the message - * that is to be send with the headers. Each header needs to be - * on a single line followed by a with the message headers - * and the message body being separated by and additional . - * Implements rfc 821: DATA - * @param string $msg_data Message data to send - * @access public - * @return boolean - */ - public function data($msg_data) - { - //This will use the standard timelimit - if (!$this->sendCommand('DATA', 'DATA', 354)) { - return false; - } - - /* The server is ready to accept data! - * According to rfc821 we should not send more than 1000 characters on a single line (including the CRLF) - * so we will break the data up into lines by \r and/or \n then if needed we will break each of those into - * smaller lines to fit within the limit. - * We will also look for lines that start with a '.' and prepend an additional '.'. - * NOTE: this does not count towards line-length limit. - */ - - // Normalize line breaks before exploding - $lines = explode("\n", str_replace(array("\r\n", "\r"), "\n", $msg_data)); - - /* To distinguish between a complete RFC822 message and a plain message body, we check if the first field - * of the first line (':' separated) does not contain a space then it _should_ be a header and we will - * process all lines before a blank line as headers. - */ - - $field = substr($lines[0], 0, strpos($lines[0], ':')); - $in_headers = false; - if (!empty($field) && strpos($field, ' ') === false) { - $in_headers = true; - } - - foreach ($lines as $line) { - $lines_out = array(); - if ($in_headers and $line == '') { - $in_headers = false; - } - //Break this line up into several smaller lines if it's too long - //Micro-optimisation: isset($str[$len]) is faster than (strlen($str) > $len), - while (isset($line[self::MAX_LINE_LENGTH])) { - //Working backwards, try to find a space within the last MAX_LINE_LENGTH chars of the line to break on - //so as to avoid breaking in the middle of a word - $pos = strrpos(substr($line, 0, self::MAX_LINE_LENGTH), ' '); - //Deliberately matches both false and 0 - if (!$pos) { - //No nice break found, add a hard break - $pos = self::MAX_LINE_LENGTH - 1; - $lines_out[] = substr($line, 0, $pos); - $line = substr($line, $pos); - } else { - //Break at the found point - $lines_out[] = substr($line, 0, $pos); - //Move along by the amount we dealt with - $line = substr($line, $pos + 1); - } - //If processing headers add a LWSP-char to the front of new line RFC822 section 3.1.1 - if ($in_headers) { - $line = "\t" . $line; - } - } - $lines_out[] = $line; - - //Send the lines to the server - foreach ($lines_out as $line_out) { - //RFC2821 section 4.5.2 - if (!empty($line_out) and $line_out[0] == '.') { - $line_out = '.' . $line_out; - } - $this->client_send($line_out . self::CRLF); - } - } - - //Message data has been sent, complete the command - //Increase timelimit for end of DATA command - $savetimelimit = $this->Timelimit; - $this->Timelimit = $this->Timelimit * 2; - $result = $this->sendCommand('DATA END', '.', 250); - //Restore timelimit - $this->Timelimit = $savetimelimit; - return $result; - } - - /** - * Send an SMTP HELO or EHLO command. - * Used to identify the sending server to the receiving server. - * This makes sure that client and server are in a known state. - * Implements RFC 821: HELO - * and RFC 2821 EHLO. - * @param string $host The host name or IP to connect to - * @access public - * @return boolean - */ - public function hello($host = '') - { - //Try extended hello first (RFC 2821) - return (boolean)($this->sendHello('EHLO', $host) or $this->sendHello('HELO', $host)); - } - - /** - * Send an SMTP HELO or EHLO command. - * Low-level implementation used by hello() - * @see hello() - * @param string $hello The HELO string - * @param string $host The hostname to say we are - * @access protected - * @return boolean - */ - protected function sendHello($hello, $host) - { - $noerror = $this->sendCommand($hello, $hello . ' ' . $host, 250); - $this->helo_rply = $this->last_reply; - if ($noerror) { - $this->parseHelloFields($hello); - } else { - $this->server_caps = null; - } - return $noerror; - } - - /** - * Parse a reply to HELO/EHLO command to discover server extensions. - * In case of HELO, the only parameter that can be discovered is a server name. - * @access protected - * @param string $type - 'HELO' or 'EHLO' - */ - protected function parseHelloFields($type) - { - $this->server_caps = array(); - $lines = explode("\n", $this->helo_rply); - - foreach ($lines as $n => $s) { - //First 4 chars contain response code followed by - or space - $s = trim(substr($s, 4)); - if (empty($s)) { - continue; - } - $fields = explode(' ', $s); - if (!empty($fields)) { - if (!$n) { - $name = $type; - $fields = $fields[0]; - } else { - $name = array_shift($fields); - switch ($name) { - case 'SIZE': - $fields = ($fields ? $fields[0] : 0); - break; - case 'AUTH': - if (!is_array($fields)) { - $fields = array(); - } - break; - default: - $fields = true; - } - } - $this->server_caps[$name] = $fields; - } - } - } - - /** - * Send an SMTP MAIL command. - * Starts a mail transaction from the email address specified in - * $from. Returns true if successful or false otherwise. If True - * the mail transaction is started and then one or more recipient - * commands may be called followed by a data command. - * Implements rfc 821: MAIL FROM: - * @param string $from Source address of this message - * @access public - * @return boolean - */ - public function mail($from) - { - $useVerp = ($this->do_verp ? ' XVERP' : ''); - return $this->sendCommand( - 'MAIL FROM', - 'MAIL FROM:<' . $from . '>' . $useVerp, - 250 - ); - } - - /** - * Send an SMTP QUIT command. - * Closes the socket if there is no error or the $close_on_error argument is true. - * Implements from rfc 821: QUIT - * @param boolean $close_on_error Should the connection close if an error occurs? - * @access public - * @return boolean - */ - public function quit($close_on_error = true) - { - $noerror = $this->sendCommand('QUIT', 'QUIT', 221); - $err = $this->error; //Save any error - if ($noerror or $close_on_error) { - $this->close(); - $this->error = $err; //Restore any error from the quit command - } - return $noerror; - } - - /** - * Send an SMTP RCPT command. - * Sets the TO argument to $toaddr. - * Returns true if the recipient was accepted false if it was rejected. - * Implements from rfc 821: RCPT TO: - * @param string $address The address the message is being sent to - * @access public - * @return boolean - */ - public function recipient($address) - { - return $this->sendCommand( - 'RCPT TO', - 'RCPT TO:<' . $address . '>', - array(250, 251) - ); - } - - /** - * Send an SMTP RSET command. - * Abort any transaction that is currently in progress. - * Implements rfc 821: RSET - * @access public - * @return boolean True on success. - */ - public function reset() - { - return $this->sendCommand('RSET', 'RSET', 250); - } - - /** - * Send a command to an SMTP server and check its return code. - * @param string $command The command name - not sent to the server - * @param string $commandstring The actual command to send - * @param integer|array $expect One or more expected integer success codes - * @access protected - * @return boolean True on success. - */ - protected function sendCommand($command, $commandstring, $expect) - { - if (!$this->connected()) { - $this->setError("Called $command without being connected"); - return false; - } - //Reject line breaks in all commands - if (strpos($commandstring, "\n") !== false or strpos($commandstring, "\r") !== false) { - $this->setError("Command '$command' contained line breaks"); - return false; - } - $this->client_send($commandstring . self::CRLF); - - $this->last_reply = $this->get_lines(); - // Fetch SMTP code and possible error code explanation - $matches = array(); - if (preg_match("/^([0-9]{3})[ -](?:([0-9]\\.[0-9]\\.[0-9]) )?/", $this->last_reply, $matches)) { - $code = $matches[1]; - $code_ex = (count($matches) > 2 ? $matches[2] : null); - // Cut off error code from each response line - $detail = preg_replace( - "/{$code}[ -]".($code_ex ? str_replace('.', '\\.', $code_ex).' ' : '')."/m", - '', - $this->last_reply - ); - } else { - // Fall back to simple parsing if regex fails - $code = substr($this->last_reply, 0, 3); - $code_ex = null; - $detail = substr($this->last_reply, 4); - } - - $this->edebug('SERVER -> CLIENT: ' . $this->last_reply, self::DEBUG_SERVER); - - if (!in_array($code, (array)$expect)) { - $this->setError( - "$command command failed", - $detail, - $code, - $code_ex - ); - $this->edebug( - 'SMTP ERROR: ' . $this->error['error'] . ': ' . $this->last_reply, - self::DEBUG_CLIENT - ); - return false; - } - - $this->setError(''); - return true; - } - - /** - * Send an SMTP SAML command. - * Starts a mail transaction from the email address specified in $from. - * Returns true if successful or false otherwise. If True - * the mail transaction is started and then one or more recipient - * commands may be called followed by a data command. This command - * will send the message to the users terminal if they are logged - * in and send them an email. - * Implements rfc 821: SAML FROM: - * @param string $from The address the message is from - * @access public - * @return boolean - */ - public function sendAndMail($from) - { - return $this->sendCommand('SAML', "SAML FROM:$from", 250); - } - - /** - * Send an SMTP VRFY command. - * @param string $name The name to verify - * @access public - * @return boolean - */ - public function verify($name) - { - return $this->sendCommand('VRFY', "VRFY $name", array(250, 251)); - } - - /** - * Send an SMTP NOOP command. - * Used to keep keep-alives alive, doesn't actually do anything - * @access public - * @return boolean - */ - public function noop() - { - return $this->sendCommand('NOOP', 'NOOP', 250); - } - - /** - * Send an SMTP TURN command. - * This is an optional command for SMTP that this class does not support. - * This method is here to make the RFC821 Definition complete for this class - * and _may_ be implemented in future - * Implements from rfc 821: TURN - * @access public - * @return boolean - */ - public function turn() - { - $this->setError('The SMTP TURN command is not implemented'); - $this->edebug('SMTP NOTICE: ' . $this->error['error'], self::DEBUG_CLIENT); - return false; - } - - /** - * Send raw data to the server. - * @param string $data The data to send - * @access public - * @return integer|boolean The number of bytes sent to the server or false on error - */ - public function client_send($data) - { - $this->edebug("CLIENT -> SERVER: $data", self::DEBUG_CLIENT); - return fwrite($this->smtp_conn, $data); - } - - /** - * Get the latest error. - * @access public - * @return array - */ - public function getError() - { - return $this->error; - } - - /** - * Get SMTP extensions available on the server - * @access public - * @return array|null - */ - public function getServerExtList() - { - return $this->server_caps; - } - - /** - * A multipurpose method - * The method works in three ways, dependent on argument value and current state - * 1. HELO/EHLO was not sent - returns null and set up $this->error - * 2. HELO was sent - * $name = 'HELO': returns server name - * $name = 'EHLO': returns boolean false - * $name = any string: returns null and set up $this->error - * 3. EHLO was sent - * $name = 'HELO'|'EHLO': returns server name - * $name = any string: if extension $name exists, returns boolean True - * or its options. Otherwise returns boolean False - * In other words, one can use this method to detect 3 conditions: - * - null returned: handshake was not or we don't know about ext (refer to $this->error) - * - false returned: the requested feature exactly not exists - * - positive value returned: the requested feature exists - * @param string $name Name of SMTP extension or 'HELO'|'EHLO' - * @return mixed - */ - public function getServerExt($name) - { - if (!$this->server_caps) { - $this->setError('No HELO/EHLO was sent'); - return null; - } - - // the tight logic knot ;) - if (!array_key_exists($name, $this->server_caps)) { - if ($name == 'HELO') { - return $this->server_caps['EHLO']; - } - if ($name == 'EHLO' || array_key_exists('EHLO', $this->server_caps)) { - return false; - } - $this->setError('HELO handshake was used. Client knows nothing about server extensions'); - return null; - } - - return $this->server_caps[$name]; - } - - /** - * Get the last reply from the server. - * @access public - * @return string - */ - public function getLastReply() - { - return $this->last_reply; - } - - /** - * Read the SMTP server's response. - * Either before eof or socket timeout occurs on the operation. - * With SMTP we can tell if we have more lines to read if the - * 4th character is '-' symbol. If it is a space then we don't - * need to read anything else. - * @access protected - * @return string - */ - protected function get_lines() - { - // If the connection is bad, give up straight away - if (!is_resource($this->smtp_conn)) { - return ''; - } - $data = ''; - $endtime = 0; - stream_set_timeout($this->smtp_conn, $this->Timeout); - if ($this->Timelimit > 0) { - $endtime = time() + $this->Timelimit; - } - while (is_resource($this->smtp_conn) && !feof($this->smtp_conn)) { - $str = @fgets($this->smtp_conn, 515); - $this->edebug("SMTP -> get_lines(): \$data is \"$data\"", self::DEBUG_LOWLEVEL); - $this->edebug("SMTP -> get_lines(): \$str is \"$str\"", self::DEBUG_LOWLEVEL); - $data .= $str; - // If 4th character is a space, we are done reading, break the loop, micro-optimisation over strlen - if ((isset($str[3]) and $str[3] == ' ')) { - break; - } - // Timed-out? Log and break - $info = stream_get_meta_data($this->smtp_conn); - if ($info['timed_out']) { - $this->edebug( - 'SMTP -> get_lines(): timed-out (' . $this->Timeout . ' sec)', - self::DEBUG_LOWLEVEL - ); - break; - } - // Now check if reads took too long - if ($endtime and time() > $endtime) { - $this->edebug( - 'SMTP -> get_lines(): timelimit reached ('. - $this->Timelimit . ' sec)', - self::DEBUG_LOWLEVEL - ); - break; - } - } - return $data; - } - - /** - * Enable or disable VERP address generation. - * @param boolean $enabled - */ - public function setVerp($enabled = false) - { - $this->do_verp = $enabled; - } - - /** - * Get VERP address generation mode. - * @return boolean - */ - public function getVerp() - { - return $this->do_verp; - } - - /** - * Set error messages and codes. - * @param string $message The error message - * @param string $detail Further detail on the error - * @param string $smtp_code An associated SMTP error code - * @param string $smtp_code_ex Extended SMTP code - */ - protected function setError($message, $detail = '', $smtp_code = '', $smtp_code_ex = '') - { - $this->error = array( - 'error' => $message, - 'detail' => $detail, - 'smtp_code' => $smtp_code, - 'smtp_code_ex' => $smtp_code_ex - ); - } - - /** - * Set debug output method. - * @param string|callable $method The name of the mechanism to use for debugging output, or a callable to handle it. - */ - public function setDebugOutput($method = 'echo') - { - $this->Debugoutput = $method; - } - - /** - * Get debug output method. - * @return string - */ - public function getDebugOutput() - { - return $this->Debugoutput; - } - - /** - * Set debug output level. - * @param integer $level - */ - public function setDebugLevel($level = 0) - { - $this->do_debug = $level; - } - - /** - * Get debug output level. - * @return integer - */ - public function getDebugLevel() - { - return $this->do_debug; - } - - /** - * Set SMTP timeout. - * @param integer $timeout - */ - public function setTimeout($timeout = 0) - { - $this->Timeout = $timeout; - } - - /** - * Get SMTP timeout. - * @return integer - */ - public function getTimeout() - { - return $this->Timeout; - } + /** + * The PHPMailer SMTP version number. + * @var string + */ + const VERSION = '5.2.21'; + + /** + * SMTP line break constant. + * @var string + */ + const CRLF = "\r\n"; + + /** + * The SMTP port to use if one is not specified. + * @var integer + */ + const DEFAULT_SMTP_PORT = 25; + + /** + * The maximum line length allowed by RFC 2822 section 2.1.1 + * @var integer + */ + const MAX_LINE_LENGTH = 998; + + /** + * Debug level for no output + */ + const DEBUG_OFF = 0; + + /** + * Debug level to show client -> server messages + */ + const DEBUG_CLIENT = 1; + + /** + * Debug level to show client -> server and server -> client messages + */ + const DEBUG_SERVER = 2; + + /** + * Debug level to show connection status, client -> server and server -> client messages + */ + const DEBUG_CONNECTION = 3; + + /** + * Debug level to show all messages + */ + const DEBUG_LOWLEVEL = 4; + + /** + * The PHPMailer SMTP Version number. + * @var string + * @deprecated Use the `VERSION` constant instead + * @see SMTP::VERSION + */ + public $Version = '5.2.21'; + + /** + * SMTP server port number. + * @var integer + * @deprecated This is only ever used as a default value, so use the `DEFAULT_SMTP_PORT` constant instead + * @see SMTP::DEFAULT_SMTP_PORT + */ + public $SMTP_PORT = 25; + + /** + * SMTP reply line ending. + * @var string + * @deprecated Use the `CRLF` constant instead + * @see SMTP::CRLF + */ + public $CRLF = "\r\n"; + + /** + * Debug output level. + * Options: + * * self::DEBUG_OFF (`0`) No debug output, default + * * self::DEBUG_CLIENT (`1`) Client commands + * * self::DEBUG_SERVER (`2`) Client commands and server responses + * * self::DEBUG_CONNECTION (`3`) As DEBUG_SERVER plus connection status + * * self::DEBUG_LOWLEVEL (`4`) Low-level data output, all messages + * @var integer + */ + public $do_debug = self::DEBUG_OFF; + + /** + * How to handle debug output. + * Options: + * * `echo` Output plain-text as-is, appropriate for CLI + * * `html` Output escaped, line breaks converted to `
`, appropriate for browser output + * * `error_log` Output to error log as configured in php.ini + * + * Alternatively, you can provide a callable expecting two params: a message string and the debug level: + * + * $smtp->Debugoutput = function($str, $level) {echo "debug level $level; message: $str";}; + * + * @var string|callable + */ + public $Debugoutput = 'echo'; + + /** + * Whether to use VERP. + * @link http://en.wikipedia.org/wiki/Variable_envelope_return_path + * @link http://www.postfix.org/VERP_README.html Info on VERP + * @var boolean + */ + public $do_verp = false; + + /** + * The timeout value for connection, in seconds. + * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2 + * This needs to be quite high to function correctly with hosts using greetdelay as an anti-spam measure. + * @link http://tools.ietf.org/html/rfc2821#section-4.5.3.2 + * @var integer + */ + public $Timeout = 300; + + /** + * How long to wait for commands to complete, in seconds. + * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2 + * @var integer + */ + public $Timelimit = 300; + + /** + * @var array patterns to extract smtp transaction id from smtp reply + * Only first capture group will be use, use non-capturing group to deal with it + * Extend this class to override this property to fulfil your needs. + */ + protected $smtp_transaction_id_patterns = array( + 'exim' => '/[0-9]{3} OK id=(.*)/', + 'sendmail' => '/[0-9]{3} 2.0.0 (.*) Message/', + 'postfix' => '/[0-9]{3} 2.0.0 Ok: queued as (.*)/' + ); + + /** + * The socket for the server connection. + * @var resource + */ + protected $smtp_conn; + + /** + * Error information, if any, for the last SMTP command. + * @var array + */ + protected $error = array( + 'error' => '', + 'detail' => '', + 'smtp_code' => '', + 'smtp_code_ex' => '' + ); + + /** + * The reply the server sent to us for HELO. + * If null, no HELO string has yet been received. + * @var string|null + */ + protected $helo_rply = null; + + /** + * The set of SMTP extensions sent in reply to EHLO command. + * Indexes of the array are extension names. + * Value at index 'HELO' or 'EHLO' (according to command that was sent) + * represents the server name. In case of HELO it is the only element of the array. + * Other values can be boolean TRUE or an array containing extension options. + * If null, no HELO/EHLO string has yet been received. + * @var array|null + */ + protected $server_caps = null; + + /** + * The most recent reply received from the server. + * @var string + */ + protected $last_reply = ''; + + /** + * Output debugging info via a user-selected method. + * @see SMTP::$Debugoutput + * @see SMTP::$do_debug + * @param string $str Debug string to output + * @param integer $level The debug level of this message; see DEBUG_* constants + * @return void + */ + protected function edebug($str, $level = 0) + { + if ($level > $this->do_debug) { + return; + } + //Avoid clash with built-in function names + if (!in_array($this->Debugoutput, array('error_log', 'html', 'echo')) and is_callable($this->Debugoutput)) { + call_user_func($this->Debugoutput, $str, $level); + return; + } + switch ($this->Debugoutput) { + case 'error_log': + //Don't output, just log + error_log($str); + break; + case 'html': + //Cleans up output a bit for a better looking, HTML-safe output + echo htmlentities( + preg_replace('/[\r\n]+/', '', $str), + ENT_QUOTES, + 'UTF-8' + ) + . "
\n"; + break; + case 'echo': + default: + //Normalize line breaks + $str = preg_replace('/(\r\n|\r|\n)/ms', "\n", $str); + echo gmdate('Y-m-d H:i:s') . "\t" . str_replace( + "\n", + "\n \t ", + trim($str) + )."\n"; + } + } + + /** + * Connect to an SMTP server. + * @param string $host SMTP server IP or host name + * @param integer $port The port number to connect to + * @param integer $timeout How long to wait for the connection to open + * @param array $options An array of options for stream_context_create() + * @access public + * @return boolean + */ + public function connect($host, $port = null, $timeout = 30, $options = array()) + { + static $streamok; + //This is enabled by default since 5.0.0 but some providers disable it + //Check this once and cache the result + if (is_null($streamok)) { + $streamok = function_exists('stream_socket_client'); + } + // Clear errors to avoid confusion + $this->setError(''); + // Make sure we are __not__ connected + if ($this->connected()) { + // Already connected, generate error + $this->setError('Already connected to a server'); + return false; + } + if (empty($port)) { + $port = self::DEFAULT_SMTP_PORT; + } + // Connect to the SMTP server + $this->edebug( + "Connection: opening to $host:$port, timeout=$timeout, options=".var_export($options, true), + self::DEBUG_CONNECTION + ); + $errno = 0; + $errstr = ''; + if ($streamok) { + $socket_context = stream_context_create($options); + set_error_handler(array($this, 'errorHandler')); + $this->smtp_conn = stream_socket_client( + $host . ":" . $port, + $errno, + $errstr, + $timeout, + STREAM_CLIENT_CONNECT, + $socket_context + ); + restore_error_handler(); + } else { + //Fall back to fsockopen which should work in more places, but is missing some features + $this->edebug( + "Connection: stream_socket_client not available, falling back to fsockopen", + self::DEBUG_CONNECTION + ); + set_error_handler(array($this, 'errorHandler')); + $this->smtp_conn = fsockopen( + $host, + $port, + $errno, + $errstr, + $timeout + ); + restore_error_handler(); + } + // Verify we connected properly + if (!is_resource($this->smtp_conn)) { + $this->setError( + 'Failed to connect to server', + $errno, + $errstr + ); + $this->edebug( + 'SMTP ERROR: ' . $this->error['error'] + . ": $errstr ($errno)", + self::DEBUG_CLIENT + ); + return false; + } + $this->edebug('Connection: opened', self::DEBUG_CONNECTION); + // SMTP server can take longer to respond, give longer timeout for first read + // Windows does not have support for this timeout function + if (substr(PHP_OS, 0, 3) != 'WIN') { + $max = ini_get('max_execution_time'); + // Don't bother if unlimited + if ($max != 0 && $timeout > $max) { + @set_time_limit($timeout); + } + stream_set_timeout($this->smtp_conn, $timeout, 0); + } + // Get any announcement + $announce = $this->get_lines(); + $this->edebug('SERVER -> CLIENT: ' . $announce, self::DEBUG_SERVER); + return true; + } + + /** + * Initiate a TLS (encrypted) session. + * @access public + * @return boolean + */ + public function startTLS() + { + if (!$this->sendCommand('STARTTLS', 'STARTTLS', 220)) { + return false; + } + + //Allow the best TLS version(s) we can + $crypto_method = STREAM_CRYPTO_METHOD_TLS_CLIENT; + + //PHP 5.6.7 dropped inclusion of TLS 1.1 and 1.2 in STREAM_CRYPTO_METHOD_TLS_CLIENT + //so add them back in manually if we can + if (defined('STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT')) { + $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT; + $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT; + } + + // Begin encrypted connection + if (!stream_socket_enable_crypto( + $this->smtp_conn, + true, + $crypto_method + )) { + return false; + } + return true; + } + + /** + * Perform SMTP authentication. + * Must be run after hello(). + * @see hello() + * @param string $username The user name + * @param string $password The password + * @param string $authtype The auth type (PLAIN, LOGIN, NTLM, CRAM-MD5, XOAUTH2) + * @param string $realm The auth realm for NTLM + * @param string $workstation The auth workstation for NTLM + * @param null|OAuth $OAuth An optional OAuth instance (@see PHPMailerOAuth) + * @return bool True if successfully authenticated.* @access public + */ + public function authenticate( + $username, + $password, + $authtype = null, + $realm = '', + $workstation = '', + $OAuth = null + ) { + if (!$this->server_caps) { + $this->setError('Authentication is not allowed before HELO/EHLO'); + return false; + } + + if (array_key_exists('EHLO', $this->server_caps)) { + // SMTP extensions are available. Let's try to find a proper authentication method + + if (!array_key_exists('AUTH', $this->server_caps)) { + $this->setError('Authentication is not allowed at this stage'); + // 'at this stage' means that auth may be allowed after the stage changes + // e.g. after STARTTLS + return false; + } + + self::edebug('Auth method requested: ' . ($authtype ? $authtype : 'UNKNOWN'), self::DEBUG_LOWLEVEL); + self::edebug( + 'Auth methods available on the server: ' . implode(',', $this->server_caps['AUTH']), + self::DEBUG_LOWLEVEL + ); + + if (empty($authtype)) { + foreach (array('CRAM-MD5', 'LOGIN', 'PLAIN', 'NTLM', 'XOAUTH2') as $method) { + if (in_array($method, $this->server_caps['AUTH'])) { + $authtype = $method; + break; + } + } + if (empty($authtype)) { + $this->setError('No supported authentication methods found'); + return false; + } + self::edebug('Auth method selected: '.$authtype, self::DEBUG_LOWLEVEL); + } + + if (!in_array($authtype, $this->server_caps['AUTH'])) { + $this->setError("The requested authentication method \"$authtype\" is not supported by the server"); + return false; + } + } elseif (empty($authtype)) { + $authtype = 'LOGIN'; + } + switch ($authtype) { + case 'PLAIN': + // Start authentication + if (!$this->sendCommand('AUTH', 'AUTH PLAIN', 334)) { + return false; + } + // Send encoded username and password + if (!$this->sendCommand( + 'User & Password', + base64_encode("\0" . $username . "\0" . $password), + 235 + ) + ) { + return false; + } + break; + case 'LOGIN': + // Start authentication + if (!$this->sendCommand('AUTH', 'AUTH LOGIN', 334)) { + return false; + } + if (!$this->sendCommand("Username", base64_encode($username), 334)) { + return false; + } + if (!$this->sendCommand("Password", base64_encode($password), 235)) { + return false; + } + break; + case 'XOAUTH2': + //If the OAuth Instance is not set. Can be a case when PHPMailer is used + //instead of PHPMailerOAuth + if (is_null($OAuth)) { + return false; + } + $oauth = $OAuth->getOauth64(); + + // Start authentication + if (!$this->sendCommand('AUTH', 'AUTH XOAUTH2 ' . $oauth, 235)) { + return false; + } + break; + case 'NTLM': + /* + * ntlm_sasl_client.php + * Bundled with Permission + * + * How to telnet in windows: + * http://technet.microsoft.com/en-us/library/aa995718%28EXCHG.65%29.aspx + * PROTOCOL Docs http://curl.haxx.se/rfc/ntlm.html#ntlmSmtpAuthentication + */ + require_once 'extras/ntlm_sasl_client.php'; + $temp = new stdClass; + $ntlm_client = new ntlm_sasl_client_class; + //Check that functions are available + if (!$ntlm_client->initialize($temp)) { + $this->setError($temp->error); + $this->edebug( + 'You need to enable some modules in your php.ini file: ' + . $this->error['error'], + self::DEBUG_CLIENT + ); + return false; + } + //msg1 + $msg1 = $ntlm_client->typeMsg1($realm, $workstation); //msg1 + + if (!$this->sendCommand( + 'AUTH NTLM', + 'AUTH NTLM ' . base64_encode($msg1), + 334 + ) + ) { + return false; + } + //Though 0 based, there is a white space after the 3 digit number + //msg2 + $challenge = substr($this->last_reply, 3); + $challenge = base64_decode($challenge); + $ntlm_res = $ntlm_client->NTLMResponse( + substr($challenge, 24, 8), + $password + ); + //msg3 + $msg3 = $ntlm_client->typeMsg3( + $ntlm_res, + $username, + $realm, + $workstation + ); + // send encoded username + return $this->sendCommand('Username', base64_encode($msg3), 235); + case 'CRAM-MD5': + // Start authentication + if (!$this->sendCommand('AUTH CRAM-MD5', 'AUTH CRAM-MD5', 334)) { + return false; + } + // Get the challenge + $challenge = base64_decode(substr($this->last_reply, 4)); + + // Build the response + $response = $username . ' ' . $this->hmac($challenge, $password); + + // send encoded credentials + return $this->sendCommand('Username', base64_encode($response), 235); + default: + $this->setError("Authentication method \"$authtype\" is not supported"); + return false; + } + return true; + } + + /** + * Calculate an MD5 HMAC hash. + * Works like hash_hmac('md5', $data, $key) + * in case that function is not available + * @param string $data The data to hash + * @param string $key The key to hash with + * @access protected + * @return string + */ + protected function hmac($data, $key) + { + if (function_exists('hash_hmac')) { + return hash_hmac('md5', $data, $key); + } + + // The following borrowed from + // http://php.net/manual/en/function.mhash.php#27225 + + // RFC 2104 HMAC implementation for php. + // Creates an md5 HMAC. + // Eliminates the need to install mhash to compute a HMAC + // by Lance Rushing + + $bytelen = 64; // byte length for md5 + if (strlen($key) > $bytelen) { + $key = pack('H*', md5($key)); + } + $key = str_pad($key, $bytelen, chr(0x00)); + $ipad = str_pad('', $bytelen, chr(0x36)); + $opad = str_pad('', $bytelen, chr(0x5c)); + $k_ipad = $key ^ $ipad; + $k_opad = $key ^ $opad; + + return md5($k_opad . pack('H*', md5($k_ipad . $data))); + } + + /** + * Check connection state. + * @access public + * @return boolean True if connected. + */ + public function connected() + { + if (is_resource($this->smtp_conn)) { + $sock_status = stream_get_meta_data($this->smtp_conn); + if ($sock_status['eof']) { + // The socket is valid but we are not connected + $this->edebug( + 'SMTP NOTICE: EOF caught while checking if connected', + self::DEBUG_CLIENT + ); + $this->close(); + return false; + } + return true; // everything looks good + } + return false; + } + + /** + * Close the socket and clean up the state of the class. + * Don't use this function without first trying to use QUIT. + * @see quit() + * @access public + * @return void + */ + public function close() + { + $this->setError(''); + $this->server_caps = null; + $this->helo_rply = null; + if (is_resource($this->smtp_conn)) { + // close the connection and cleanup + fclose($this->smtp_conn); + $this->smtp_conn = null; //Makes for cleaner serialization + $this->edebug('Connection: closed', self::DEBUG_CONNECTION); + } + } + + /** + * Send an SMTP DATA command. + * Issues a data command and sends the msg_data to the server, + * finializing the mail transaction. $msg_data is the message + * that is to be send with the headers. Each header needs to be + * on a single line followed by a with the message headers + * and the message body being separated by and additional . + * Implements rfc 821: DATA + * @param string $msg_data Message data to send + * @access public + * @return boolean + */ + public function data($msg_data) + { + //This will use the standard timelimit + if (!$this->sendCommand('DATA', 'DATA', 354)) { + return false; + } + + /* The server is ready to accept data! + * According to rfc821 we should not send more than 1000 characters on a single line (including the CRLF) + * so we will break the data up into lines by \r and/or \n then if needed we will break each of those into + * smaller lines to fit within the limit. + * We will also look for lines that start with a '.' and prepend an additional '.'. + * NOTE: this does not count towards line-length limit. + */ + + // Normalize line breaks before exploding + $lines = explode("\n", str_replace(array("\r\n", "\r"), "\n", $msg_data)); + + /* To distinguish between a complete RFC822 message and a plain message body, we check if the first field + * of the first line (':' separated) does not contain a space then it _should_ be a header and we will + * process all lines before a blank line as headers. + */ + + $field = substr($lines[0], 0, strpos($lines[0], ':')); + $in_headers = false; + if (!empty($field) && strpos($field, ' ') === false) { + $in_headers = true; + } + + foreach ($lines as $line) { + $lines_out = array(); + if ($in_headers and $line == '') { + $in_headers = false; + } + //Break this line up into several smaller lines if it's too long + //Micro-optimisation: isset($str[$len]) is faster than (strlen($str) > $len), + while (isset($line[self::MAX_LINE_LENGTH])) { + //Working backwards, try to find a space within the last MAX_LINE_LENGTH chars of the line to break on + //so as to avoid breaking in the middle of a word + $pos = strrpos(substr($line, 0, self::MAX_LINE_LENGTH), ' '); + //Deliberately matches both false and 0 + if (!$pos) { + //No nice break found, add a hard break + $pos = self::MAX_LINE_LENGTH - 1; + $lines_out[] = substr($line, 0, $pos); + $line = substr($line, $pos); + } else { + //Break at the found point + $lines_out[] = substr($line, 0, $pos); + //Move along by the amount we dealt with + $line = substr($line, $pos + 1); + } + //If processing headers add a LWSP-char to the front of new line RFC822 section 3.1.1 + if ($in_headers) { + $line = "\t" . $line; + } + } + $lines_out[] = $line; + + //Send the lines to the server + foreach ($lines_out as $line_out) { + //RFC2821 section 4.5.2 + if (!empty($line_out) and $line_out[0] == '.') { + $line_out = '.' . $line_out; + } + $this->client_send($line_out . self::CRLF); + } + } + + //Message data has been sent, complete the command + //Increase timelimit for end of DATA command + $savetimelimit = $this->Timelimit; + $this->Timelimit = $this->Timelimit * 2; + $result = $this->sendCommand('DATA END', '.', 250); + //Restore timelimit + $this->Timelimit = $savetimelimit; + return $result; + } + + /** + * Send an SMTP HELO or EHLO command. + * Used to identify the sending server to the receiving server. + * This makes sure that client and server are in a known state. + * Implements RFC 821: HELO + * and RFC 2821 EHLO. + * @param string $host The host name or IP to connect to + * @access public + * @return boolean + */ + public function hello($host = '') + { + //Try extended hello first (RFC 2821) + return (boolean)($this->sendHello('EHLO', $host) or $this->sendHello('HELO', $host)); + } + + /** + * Send an SMTP HELO or EHLO command. + * Low-level implementation used by hello() + * @see hello() + * @param string $hello The HELO string + * @param string $host The hostname to say we are + * @access protected + * @return boolean + */ + protected function sendHello($hello, $host) + { + $noerror = $this->sendCommand($hello, $hello . ' ' . $host, 250); + $this->helo_rply = $this->last_reply; + if ($noerror) { + $this->parseHelloFields($hello); + } else { + $this->server_caps = null; + } + return $noerror; + } + + /** + * Parse a reply to HELO/EHLO command to discover server extensions. + * In case of HELO, the only parameter that can be discovered is a server name. + * @access protected + * @param string $type - 'HELO' or 'EHLO' + */ + protected function parseHelloFields($type) + { + $this->server_caps = array(); + $lines = explode("\n", $this->helo_rply); + + foreach ($lines as $n => $s) { + //First 4 chars contain response code followed by - or space + $s = trim(substr($s, 4)); + if (empty($s)) { + continue; + } + $fields = explode(' ', $s); + if (!empty($fields)) { + if (!$n) { + $name = $type; + $fields = $fields[0]; + } else { + $name = array_shift($fields); + switch ($name) { + case 'SIZE': + $fields = ($fields ? $fields[0] : 0); + break; + case 'AUTH': + if (!is_array($fields)) { + $fields = array(); + } + break; + default: + $fields = true; + } + } + $this->server_caps[$name] = $fields; + } + } + } + + /** + * Send an SMTP MAIL command. + * Starts a mail transaction from the email address specified in + * $from. Returns true if successful or false otherwise. If True + * the mail transaction is started and then one or more recipient + * commands may be called followed by a data command. + * Implements rfc 821: MAIL FROM: + * @param string $from Source address of this message + * @access public + * @return boolean + */ + public function mail($from) + { + $useVerp = ($this->do_verp ? ' XVERP' : ''); + return $this->sendCommand( + 'MAIL FROM', + 'MAIL FROM:<' . $from . '>' . $useVerp, + 250 + ); + } + + /** + * Send an SMTP QUIT command. + * Closes the socket if there is no error or the $close_on_error argument is true. + * Implements from rfc 821: QUIT + * @param boolean $close_on_error Should the connection close if an error occurs? + * @access public + * @return boolean + */ + public function quit($close_on_error = true) + { + $noerror = $this->sendCommand('QUIT', 'QUIT', 221); + $err = $this->error; //Save any error + if ($noerror or $close_on_error) { + $this->close(); + $this->error = $err; //Restore any error from the quit command + } + return $noerror; + } + + /** + * Send an SMTP RCPT command. + * Sets the TO argument to $toaddr. + * Returns true if the recipient was accepted false if it was rejected. + * Implements from rfc 821: RCPT TO: + * @param string $address The address the message is being sent to + * @access public + * @return boolean + */ + public function recipient($address) + { + return $this->sendCommand( + 'RCPT TO', + 'RCPT TO:<' . $address . '>', + array(250, 251) + ); + } + + /** + * Send an SMTP RSET command. + * Abort any transaction that is currently in progress. + * Implements rfc 821: RSET + * @access public + * @return boolean True on success. + */ + public function reset() + { + return $this->sendCommand('RSET', 'RSET', 250); + } + + /** + * Send a command to an SMTP server and check its return code. + * @param string $command The command name - not sent to the server + * @param string $commandstring The actual command to send + * @param integer|array $expect One or more expected integer success codes + * @access protected + * @return boolean True on success. + */ + protected function sendCommand($command, $commandstring, $expect) + { + if (!$this->connected()) { + $this->setError("Called $command without being connected"); + return false; + } + //Reject line breaks in all commands + if (strpos($commandstring, "\n") !== false or strpos($commandstring, "\r") !== false) { + $this->setError("Command '$command' contained line breaks"); + return false; + } + $this->client_send($commandstring . self::CRLF); + + $this->last_reply = $this->get_lines(); + // Fetch SMTP code and possible error code explanation + $matches = array(); + if (preg_match("/^([0-9]{3})[ -](?:([0-9]\\.[0-9]\\.[0-9]) )?/", $this->last_reply, $matches)) { + $code = $matches[1]; + $code_ex = (count($matches) > 2 ? $matches[2] : null); + // Cut off error code from each response line + $detail = preg_replace( + "/{$code}[ -]".($code_ex ? str_replace('.', '\\.', $code_ex).' ' : '')."/m", + '', + $this->last_reply + ); + } else { + // Fall back to simple parsing if regex fails + $code = substr($this->last_reply, 0, 3); + $code_ex = null; + $detail = substr($this->last_reply, 4); + } + + $this->edebug('SERVER -> CLIENT: ' . $this->last_reply, self::DEBUG_SERVER); + + if (!in_array($code, (array)$expect)) { + $this->setError( + "$command command failed", + $detail, + $code, + $code_ex + ); + $this->edebug( + 'SMTP ERROR: ' . $this->error['error'] . ': ' . $this->last_reply, + self::DEBUG_CLIENT + ); + return false; + } + + $this->setError(''); + return true; + } + + /** + * Send an SMTP SAML command. + * Starts a mail transaction from the email address specified in $from. + * Returns true if successful or false otherwise. If True + * the mail transaction is started and then one or more recipient + * commands may be called followed by a data command. This command + * will send the message to the users terminal if they are logged + * in and send them an email. + * Implements rfc 821: SAML FROM: + * @param string $from The address the message is from + * @access public + * @return boolean + */ + public function sendAndMail($from) + { + return $this->sendCommand('SAML', "SAML FROM:$from", 250); + } + + /** + * Send an SMTP VRFY command. + * @param string $name The name to verify + * @access public + * @return boolean + */ + public function verify($name) + { + return $this->sendCommand('VRFY', "VRFY $name", array(250, 251)); + } + + /** + * Send an SMTP NOOP command. + * Used to keep keep-alives alive, doesn't actually do anything + * @access public + * @return boolean + */ + public function noop() + { + return $this->sendCommand('NOOP', 'NOOP', 250); + } + + /** + * Send an SMTP TURN command. + * This is an optional command for SMTP that this class does not support. + * This method is here to make the RFC821 Definition complete for this class + * and _may_ be implemented in future + * Implements from rfc 821: TURN + * @access public + * @return boolean + */ + public function turn() + { + $this->setError('The SMTP TURN command is not implemented'); + $this->edebug('SMTP NOTICE: ' . $this->error['error'], self::DEBUG_CLIENT); + return false; + } + + /** + * Send raw data to the server. + * @param string $data The data to send + * @access public + * @return integer|boolean The number of bytes sent to the server or false on error + */ + public function client_send($data) + { + $this->edebug("CLIENT -> SERVER: $data", self::DEBUG_CLIENT); + return fwrite($this->smtp_conn, $data); + } + + /** + * Get the latest error. + * @access public + * @return array + */ + public function getError() + { + return $this->error; + } + + /** + * Get SMTP extensions available on the server + * @access public + * @return array|null + */ + public function getServerExtList() + { + return $this->server_caps; + } + + /** + * A multipurpose method + * The method works in three ways, dependent on argument value and current state + * 1. HELO/EHLO was not sent - returns null and set up $this->error + * 2. HELO was sent + * $name = 'HELO': returns server name + * $name = 'EHLO': returns boolean false + * $name = any string: returns null and set up $this->error + * 3. EHLO was sent + * $name = 'HELO'|'EHLO': returns server name + * $name = any string: if extension $name exists, returns boolean True + * or its options. Otherwise returns boolean False + * In other words, one can use this method to detect 3 conditions: + * - null returned: handshake was not or we don't know about ext (refer to $this->error) + * - false returned: the requested feature exactly not exists + * - positive value returned: the requested feature exists + * @param string $name Name of SMTP extension or 'HELO'|'EHLO' + * @return mixed + */ + public function getServerExt($name) + { + if (!$this->server_caps) { + $this->setError('No HELO/EHLO was sent'); + return null; + } + + // the tight logic knot ;) + if (!array_key_exists($name, $this->server_caps)) { + if ($name == 'HELO') { + return $this->server_caps['EHLO']; + } + if ($name == 'EHLO' || array_key_exists('EHLO', $this->server_caps)) { + return false; + } + $this->setError('HELO handshake was used. Client knows nothing about server extensions'); + return null; + } + + return $this->server_caps[$name]; + } + + /** + * Get the last reply from the server. + * @access public + * @return string + */ + public function getLastReply() + { + return $this->last_reply; + } + + /** + * Read the SMTP server's response. + * Either before eof or socket timeout occurs on the operation. + * With SMTP we can tell if we have more lines to read if the + * 4th character is '-' symbol. If it is a space then we don't + * need to read anything else. + * @access protected + * @return string + */ + protected function get_lines() + { + // If the connection is bad, give up straight away + if (!is_resource($this->smtp_conn)) { + return ''; + } + $data = ''; + $endtime = 0; + stream_set_timeout($this->smtp_conn, $this->Timeout); + if ($this->Timelimit > 0) { + $endtime = time() + $this->Timelimit; + } + while (is_resource($this->smtp_conn) && !feof($this->smtp_conn)) { + $str = @fgets($this->smtp_conn, 515); + $this->edebug("SMTP -> get_lines(): \$data is \"$data\"", self::DEBUG_LOWLEVEL); + $this->edebug("SMTP -> get_lines(): \$str is \"$str\"", self::DEBUG_LOWLEVEL); + $data .= $str; + // If 4th character is a space, we are done reading, break the loop, micro-optimisation over strlen + if ((isset($str[3]) and $str[3] == ' ')) { + break; + } + // Timed-out? Log and break + $info = stream_get_meta_data($this->smtp_conn); + if ($info['timed_out']) { + $this->edebug( + 'SMTP -> get_lines(): timed-out (' . $this->Timeout . ' sec)', + self::DEBUG_LOWLEVEL + ); + break; + } + // Now check if reads took too long + if ($endtime and time() > $endtime) { + $this->edebug( + 'SMTP -> get_lines(): timelimit reached ('. + $this->Timelimit . ' sec)', + self::DEBUG_LOWLEVEL + ); + break; + } + } + return $data; + } + + /** + * Enable or disable VERP address generation. + * @param boolean $enabled + */ + public function setVerp($enabled = false) + { + $this->do_verp = $enabled; + } + + /** + * Get VERP address generation mode. + * @return boolean + */ + public function getVerp() + { + return $this->do_verp; + } + + /** + * Set error messages and codes. + * @param string $message The error message + * @param string $detail Further detail on the error + * @param string $smtp_code An associated SMTP error code + * @param string $smtp_code_ex Extended SMTP code + */ + protected function setError($message, $detail = '', $smtp_code = '', $smtp_code_ex = '') + { + $this->error = array( + 'error' => $message, + 'detail' => $detail, + 'smtp_code' => $smtp_code, + 'smtp_code_ex' => $smtp_code_ex + ); + } + + /** + * Set debug output method. + * @param string|callable $method The name of the mechanism to use for debugging output, or a callable to handle it. + */ + public function setDebugOutput($method = 'echo') + { + $this->Debugoutput = $method; + } + + /** + * Get debug output method. + * @return string + */ + public function getDebugOutput() + { + return $this->Debugoutput; + } + + /** + * Set debug output level. + * @param integer $level + */ + public function setDebugLevel($level = 0) + { + $this->do_debug = $level; + } + + /** + * Get debug output level. + * @return integer + */ + public function getDebugLevel() + { + return $this->do_debug; + } + + /** + * Set SMTP timeout. + * @param integer $timeout + */ + public function setTimeout($timeout = 0) + { + $this->Timeout = $timeout; + } + + /** + * Get SMTP timeout. + * @return integer + */ + public function getTimeout() + { + return $this->Timeout; + } + + /** + * Reports an error number and string. + * @param integer $errno The error number returned by PHP. + * @param string $errmsg The error message returned by PHP. + */ + protected function errorHandler($errno, $errmsg) + { + $notice = 'Connection: Failed to connect to server.'; + $this->setError( + $notice, + $errno, + $errmsg + ); + $this->edebug( + $notice . ' Error number ' . $errno . '. "Error notice: ' . $errmsg, + self::DEBUG_CONNECTION + ); + } + + /** + * Will return the ID of the last smtp transaction based on a list of patterns provided + * in SMTP::$smtp_transaction_id_patterns. + * If no reply has been received yet, it will return null. + * If no pattern has been matched, it will return false. + * @return bool|null|string + */ + public function getLastTransactionID() + { + $reply = $this->getLastReply(); + + if (empty($reply)) { + return null; + } + + foreach($this->smtp_transaction_id_patterns as $smtp_transaction_id_pattern) { + if(preg_match($smtp_transaction_id_pattern, $reply, $matches)) { + return $matches[1]; + } + } + + return false; + } } From d11ddd910f2e24b66a0fb625b7ccc97806330f78 Mon Sep 17 00:00:00 2001 From: Christian Becker Date: Mon, 2 Jan 2017 03:06:18 +0100 Subject: [PATCH 0269/1335] fix hsts removal links without a protocol, they are treated relative which is annoying --- lng/english.lng.php | 2 +- lng/german.lng.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lng/english.lng.php b/lng/english.lng.php index 45996cd0..e3b96eda 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -2061,7 +2061,7 @@ $lng['admin']['domain_hsts_maxage']['description'] = 'Specify the max-age value $lng['admin']['domain_hsts_incsub']['title'] = 'Include HSTS for any subdomain'; $lng['admin']['domain_hsts_incsub']['description'] = 'The optional "includeSubDomains" directive, if present, signals the UA that the HSTS Policy applies to this HSTS Host as well as any subdomains of the host\'s domain name.'; $lng['admin']['domain_hsts_preload']['title'] = 'Include domain in HSTS preload list'; -$lng['admin']['domain_hsts_preload']['description'] = 'If you would like this domain to be included in the HSTS preload list maintained by Chrome (and used by Firefox and Safari), then use activate this.
Sending the preload directive from your site can have PERMANENT CONSEQUENCES and prevent users from accessing your site and any of its subdomains.
Please read the details at hstspreload.appspot.com/#removal before sending the header with "preload".'; +$lng['admin']['domain_hsts_preload']['description'] = 'If you would like this domain to be included in the HSTS preload list maintained by Chrome (and used by Firefox and Safari), then use activate this.
Sending the preload directive from your site can have PERMANENT CONSEQUENCES and prevent users from accessing your site and any of its subdomains.
Please read the details at hstspreload.appspot.com/#removal before sending the header with "preload".'; $lng['serversettings']['nginx_http2_support']['title'] = 'Nginx HTTP2 Support'; $lng['serversettings']['nginx_http2_support']['description'] = 'enable http2 support for ssl. ENABLE ONLY IF YOUR Nginx SUPPORT THIS FEATURE. (version 1.9.5+)'; diff --git a/lng/german.lng.php b/lng/german.lng.php index 490d2651..b31b713e 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -1712,4 +1712,4 @@ $lng['admin']['domain_hsts_maxage']['description'] = '"max-age" Wert für den St $lng['admin']['domain_hsts_incsub']['title'] = 'Inkludiere HSTS für jede Subdomain'; $lng['admin']['domain_hsts_incsub']['description'] = 'Die optionale "includeSubDomains" Direktive, wenn vorhanden, signalisiert dem UA, dass die HSTS Regel für diese Domain und auch jede Subdomain dieser gilt.'; $lng['admin']['domain_hsts_preload']['title'] = 'Füge Domain in die HSTS preload Liste hinzu'; -$lng['admin']['domain_hsts_preload']['description'] = 'Wenn die Domain in die HSTS preload Liste, verwaltet von Chrome (und genutzt von Firefox und Safari), hinzugefügt werden soll, dann aktiviere diese Einstellung.
Die preload-Direktive zu senden kann PERMANTENTE KONSEQUENZEN haben und dazu führen, dass Benutzer auf diese Domain und auch Subdomains nicht zugreifen können.
Beachte Details unter hstspreload.appspot.com/#removal bevor ein Header mit "preload" gesendet wird.'; +$lng['admin']['domain_hsts_preload']['description'] = 'Wenn die Domain in die HSTS preload Liste, verwaltet von Chrome (und genutzt von Firefox und Safari), hinzugefügt werden soll, dann aktiviere diese Einstellung.
Die preload-Direktive zu senden kann PERMANTENTE KONSEQUENZEN haben und dazu führen, dass Benutzer auf diese Domain und auch Subdomains nicht zugreifen können.
Beachte Details unter hstspreload.appspot.com/#removal bevor ein Header mit "preload" gesendet wird.'; From 1ebde2e6a44dd49a749c5684a14ab402107ba7c1 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 5 Jan 2017 11:58:11 +0100 Subject: [PATCH 0270/1335] return correct default redirectCode when none is set, thx to J-BBB Signed-off-by: Michael Kaufmann (d00p) --- lib/functions/output/function.RedirectCode.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/functions/output/function.RedirectCode.php b/lib/functions/output/function.RedirectCode.php index 37bd10e1..c5df262c 100644 --- a/lib/functions/output/function.RedirectCode.php +++ b/lib/functions/output/function.RedirectCode.php @@ -77,7 +77,7 @@ function getDomainRedirectCode($domainid = 0, $default = '') { if (is_array($result) && isset($result['redirect']) ) { - $code = ($result['redirect'] == '---') ? '' : $result['redirect']; + $code = ($result['redirect'] == '---') ? $default : $result['redirect']; } } return $code; From c795cd3320e2fe0ac4e2ff7afb7adc2b37a55cb8 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 10 Jan 2017 08:37:50 +0100 Subject: [PATCH 0271/1335] check for ownership of certificate when deleting as customer, fixes #1699 Signed-off-by: Michael Kaufmann (d00p) --- ssl_certificates.php | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/ssl_certificates.php b/ssl_certificates.php index 5a8547d7..365b2172 100644 --- a/ssl_certificates.php +++ b/ssl_certificates.php @@ -23,14 +23,32 @@ if (! defined('AREA')) $del_stmt = Database::prepare("DELETE FROM `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` WHERE id = :id"); $success_message = ""; -// do the delete and then just showa success-message and the certificates list again +// do the delete and then just show a success-message and the certificates list again if ($action == 'delete') { $id = isset($_GET['id']) ? (int) $_GET['id'] : 0; if ($id > 0) { - Database::pexecute($del_stmt, array( - 'id' => $id - )); - $success_message = sprintf($lng['domains']['ssl_certificate_removed'], $id); + if (AREA == 'customer') { + $chk_stmt = Database::prepare(" + SELECT d.domain FROM `".TABLE_PANEL_DOMAINS."` d + LEFT JOIN `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` s ON s.domainid = d.id + WHERE s.`id` = :id AND d.`customerid` = :cid + "); + $chk = Database::pexecute_first($chk_stmt, array( + 'id' => $id, + 'cid' => $userinfo['customerid'] + )); + if ($chk !== false) { + Database::pexecute($del_stmt, array( + 'id' => $id + )); + $success_message = sprintf($lng['domains']['ssl_certificate_removed'], $id); + } + } else { + Database::pexecute($del_stmt, array( + 'id' => $id + )); + $success_message = sprintf($lng['domains']['ssl_certificate_removed'], $id); + } } } From 02c6545c94fff745e2ea5fa912adcdee4ae6bb80 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 10 Jan 2017 19:11:01 +0100 Subject: [PATCH 0272/1335] update download url for libnss-mysql for debian jessie, fixies #1700 Signed-off-by: Michael Kaufmann (d00p) --- lib/configfiles/jessie.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/configfiles/jessie.xml b/lib/configfiles/jessie.xml index d6613020..ae7e3912 100644 --- a/lib/configfiles/jessie.xml +++ b/lib/configfiles/jessie.xml @@ -4469,9 +4469,9 @@ PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin From d574233f491d27f34efef49684f2bb343123ab8a Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Fri, 13 Jan 2017 19:21:34 +0100 Subject: [PATCH 0273/1335] also reseller/admins who can't see all customers were able to delete arbitrary ssl certificates, refs #1699 Signed-off-by: Michael Kaufmann (d00p) --- ssl_certificates.php | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/ssl_certificates.php b/ssl_certificates.php index 365b2172..875b903e 100644 --- a/ssl_certificates.php +++ b/ssl_certificates.php @@ -27,9 +27,10 @@ $success_message = ""; if ($action == 'delete') { $id = isset($_GET['id']) ? (int) $_GET['id'] : 0; if ($id > 0) { + $chk = (AREA == 'admin' && $userinfo['customers_see_all'] == '1') ? true : false; if (AREA == 'customer') { $chk_stmt = Database::prepare(" - SELECT d.domain FROM `".TABLE_PANEL_DOMAINS."` d + SELECT d.domain FROM `" . TABLE_PANEL_DOMAINS . "` d LEFT JOIN `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` s ON s.domainid = d.id WHERE s.`id` = :id AND d.`customerid` = :cid "); @@ -37,13 +38,18 @@ if ($action == 'delete') { 'id' => $id, 'cid' => $userinfo['customerid'] )); - if ($chk !== false) { - Database::pexecute($del_stmt, array( - 'id' => $id - )); - $success_message = sprintf($lng['domains']['ssl_certificate_removed'], $id); - } - } else { + } elseif (AREA == 'admin' && $userinfo['customers_see_all'] == '0') { + $chk_stmt = Database::prepare(" + SELECT d.domain FROM `" . TABLE_PANEL_DOMAINS . "` d + LEFT JOIN `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` s ON s.domainid = d.id + WHERE s.`id` = :id AND d.`adminid` = :aid + "); + $chk = Database::pexecute_first($chk_stmt, array( + 'id' => $id, + 'aid' => $userinfo['adminid'] + )); + } + if ($chk !== false) { Database::pexecute($del_stmt, array( 'id' => $id )); @@ -90,7 +96,9 @@ $certificates = ""; if (count($all_certs) == 0) { $message = $lng['domains']['no_ssl_certificates']; $sortcode = ""; - $arrowcode = array('d.domain' => ''); + $arrowcode = array( + 'd.domain' => '' + ); $searchcode = ""; $pagingcode = ""; eval("\$certificates.=\"" . getTemplate("ssl_certificates/certs_error", true) . "\";"); @@ -145,13 +153,13 @@ if (count($all_certs) == 0) { } $san_list = ""; - if (isset($cert_data['extensions']['subjectAltName']) && !empty($cert_data['extensions']['subjectAltName'])) { + if (isset($cert_data['extensions']['subjectAltName']) && ! empty($cert_data['extensions']['subjectAltName'])) { $SANs = explode(",", $cert_data['extensions']['subjectAltName']); $SANs = array_map('trim', $SANs); foreach ($SANs as $san) { $san = str_replace("DNS:", "", $san); if ($san != $cert_data['subject']['CN'] && strpos($san, "othername:") === false) { - $san_list .= $san."
"; + $san_list .= $san . "
"; } } } From 044ce6662ad5239f01e7d6a5b5999992af8d6bdd Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Fri, 13 Jan 2017 19:45:17 +0100 Subject: [PATCH 0274/1335] set version to 0.9.38.5 for upcoming release Signed-off-by: Michael Kaufmann (d00p) --- install/froxlor.sql | 2 +- install/updates/froxlor/0.9/update_0.9.inc.php | 8 +++++++- lib/version.inc.php | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/install/froxlor.sql b/install/froxlor.sql index 5c75be4f..456a06fd 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -581,7 +581,7 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES ('panel', 'password_special_char_required', '0'), ('panel', 'password_special_char', '!?<>§$%+#=@'), ('panel', 'customer_hide_options', ''), - ('panel', 'version', '0.9.38.4'), + ('panel', 'version', '0.9.38.5'), ('panel', 'db_version', '201612110'); diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index 8cc422dc..c75af54f 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -3570,6 +3570,12 @@ if (isDatabaseVersion('201611180')) { showUpdateStep("Adding unique key to ipsandports table"); Database::query("ALTER TABLE `" . TABLE_PANEL_IPSANDPORTS . "` ADD UNIQUE KEY `ip_port` (`ip`,`port`)"); lastStepStatus(0); - + updateToDbVersion('201612110'); } + +if (isFroxlorVersion('0.9.38.4')) { + + showUpdateStep("Updating from 0.9.38.4 to 0.9.38.5", false); + updateToVersion('0.9.38.5'); +} diff --git a/lib/version.inc.php b/lib/version.inc.php index 52765f8d..113b065d 100644 --- a/lib/version.inc.php +++ b/lib/version.inc.php @@ -16,7 +16,7 @@ */ // Main version variable -$version = '0.9.38.4'; +$version = '0.9.38.5'; // Database version (YYYYMMDDC where C is a daily counter) $dbversion = '201612110'; From 338cf161d253e2599376e5d11a3984b24f426863 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sat, 14 Jan 2017 18:06:04 +0100 Subject: [PATCH 0275/1335] fix undefined index if let's encrypt is used for the froxlor-vhost Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/ssl/class.lescript.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/classes/ssl/class.lescript.php b/lib/classes/ssl/class.lescript.php index 2acd0934..a87f5bc8 100644 --- a/lib/classes/ssl/class.lescript.php +++ b/lib/classes/ssl/class.lescript.php @@ -63,7 +63,7 @@ class lescript { // Let's see if we have the private accountkey $this->accountKey = $certrow['leprivatekey']; - $this->customerId = $certrow['customerid']; + $this->customerId = (!$isFroxlorVhost ? $certrow['customerid'] : null); $this->isFroxlorVhost = $isFroxlorVhost; $this->isLeProduction = (Settings::Get('system.letsencryptca') == 'production'); From 9fdcd090894095d48bdf445e68a8618140724d17 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sun, 15 Jan 2017 07:44:58 +0100 Subject: [PATCH 0276/1335] fix install class, set version to 0.9.38.6 Signed-off-by: Michael Kaufmann (d00p) --- install/froxlor.sql | 2 +- install/lib/class.FroxlorInstall.php | 2 +- install/updates/froxlor/0.9/update_0.9.inc.php | 6 ++++++ lib/version.inc.php | 2 +- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/install/froxlor.sql b/install/froxlor.sql index 456a06fd..e27409e4 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -581,7 +581,7 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES ('panel', 'password_special_char_required', '0'), ('panel', 'password_special_char', '!?<>§$%+#=@'), ('panel', 'customer_hide_options', ''), - ('panel', 'version', '0.9.38.5'), + ('panel', 'version', '0.9.38.6'), ('panel', 'db_version', '201612110'); diff --git a/install/lib/class.FroxlorInstall.php b/install/lib/class.FroxlorInstall.php index 37acfc7c..ac9fb7f4 100644 --- a/install/lib/class.FroxlorInstall.php +++ b/install/lib/class.FroxlorInstall.php @@ -181,7 +181,7 @@ class FroxlorInstall $this->_data['servername'] = ''; } - if (empty($this->_data['serverip'] || $this->_validate_ip($this->_data['serverip']) == false)) { + if (empty($this->_data['serverip']) || $this->_validate_ip($this->_data['serverip']) == false) { return false; } diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index c75af54f..a759879f 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -3579,3 +3579,9 @@ if (isFroxlorVersion('0.9.38.4')) { showUpdateStep("Updating from 0.9.38.4 to 0.9.38.5", false); updateToVersion('0.9.38.5'); } + +if (isFroxlorVersion('0.9.38.5')) { + + showUpdateStep("Updating from 0.9.38.5 to 0.9.38.6", false); + updateToVersion('0.9.38.6'); +} diff --git a/lib/version.inc.php b/lib/version.inc.php index 113b065d..cf3170c3 100644 --- a/lib/version.inc.php +++ b/lib/version.inc.php @@ -16,7 +16,7 @@ */ // Main version variable -$version = '0.9.38.5'; +$version = '0.9.38.6'; // Database version (YYYYMMDDC where C is a daily counter) $dbversion = '201612110'; From c6962b0992391c86da6e29aaf9b7fc489f6551e6 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 16 Jan 2017 08:43:54 +0100 Subject: [PATCH 0277/1335] fix variable-typo to make phpenabled-flag work when adding new domains, thx to micw Signed-off-by: Michael Kaufmann (d00p) --- admin_domains.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/admin_domains.php b/admin_domains.php index bb4fe9a3..8a28364c 100644 --- a/admin_domains.php +++ b/admin_domains.php @@ -455,7 +455,7 @@ if ($page == 'domains' || $page == 'overview') { if ($userinfo['caneditphpsettings'] == '1' || $userinfo['change_serversettings'] == '1') { - $phpenabled = isset($POST_['phpenabled']) ? intval($_POST['phpenabled']) : 0; + $phpenabled = isset($_POST['phpenabled']) ? intval($_POST['phpenabled']) : 0; $openbasedir = isset($_POST['openbasedir']) ? intval($_POST['openbasedir']) : 0; if ((int) Settings::Get('system.mod_fcgid') == 1 || (int) Settings::Get('phpfpm.enabled') == 1) { @@ -496,7 +496,7 @@ if ($page == 'domains' || $page == 'overview') { } } else { - $phpenabled = '1'; + $phpenabled = '1'; $openbasedir = '1'; if ((int) Settings::Get('phpfpm.enabled') == 1) { @@ -693,8 +693,8 @@ if ($page == 'domains' || $page == 'overview') { if (count($ipandports) == 0) { standard_error('noipportgiven'); } - - if($phpenabled != '1') { + + if ($phpenabled != '1') { $phpenabled = '0'; } @@ -1485,7 +1485,7 @@ if ($page == 'domains' || $page == 'overview') { if (! preg_match('/^https?\:\/\//', $documentroot)) { $documentroot = makeCorrectDir($documentroot); } - + if ($phpenabled != '1') { $phpenabled = '0'; } From 5e0270e6a83e63284e43101c5883ba51371a1595 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laurens=20St=C3=B6tzel?= Date: Tue, 17 Jan 2017 10:18:52 +0100 Subject: [PATCH 0278/1335] Disable SSLCompression (CRIME attack) https://raymii.org/s/tutorials/Strong_SSL_Security_On_Apache2.html#SSL_Compression_(CRIME_attack) --- scripts/jobs/cron_tasks.inc.http.10.apache.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/jobs/cron_tasks.inc.http.10.apache.php b/scripts/jobs/cron_tasks.inc.http.10.apache.php index 174ae623..d7e793fb 100644 --- a/scripts/jobs/cron_tasks.inc.http.10.apache.php +++ b/scripts/jobs/cron_tasks.inc.http.10.apache.php @@ -422,6 +422,8 @@ class apache extends HttpConfigBase $this->virtualhosts_data[$vhosts_filename] .= ' SSLEngine On' . "\n"; $this->virtualhosts_data[$vhosts_filename] .= ' SSLProtocol -ALL +TLSv1 +TLSv1.2' . "\n"; + $this->virtualhosts_data[$vhosts_filename] .= ' SSLCompression Off' . "\n"; + // this makes it more secure, thx to Marcel (08/2013) $this->virtualhosts_data[$vhosts_filename] .= ' SSLHonorCipherOrder On' . "\n"; $this->virtualhosts_data[$vhosts_filename] .= ' SSLCipherSuite ' . Settings::Get('system.ssl_cipher_list') . "\n"; From ab18d9405325856c0b7eae6eb441de0245e8f984 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 17 Jan 2017 11:29:40 +0100 Subject: [PATCH 0279/1335] fix PR #407 - only works for apache-2.4 and missed the entry for customer-vhosts Signed-off-by: Michael Kaufmann (d00p) --- scripts/jobs/cron_tasks.inc.http.10.apache.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/scripts/jobs/cron_tasks.inc.http.10.apache.php b/scripts/jobs/cron_tasks.inc.http.10.apache.php index d7e793fb..47be4431 100644 --- a/scripts/jobs/cron_tasks.inc.http.10.apache.php +++ b/scripts/jobs/cron_tasks.inc.http.10.apache.php @@ -422,8 +422,9 @@ class apache extends HttpConfigBase $this->virtualhosts_data[$vhosts_filename] .= ' SSLEngine On' . "\n"; $this->virtualhosts_data[$vhosts_filename] .= ' SSLProtocol -ALL +TLSv1 +TLSv1.2' . "\n"; - $this->virtualhosts_data[$vhosts_filename] .= ' SSLCompression Off' . "\n"; - + if (Settings::Get('system.apache24') == '1') { + $this->virtualhosts_data[$vhosts_filename] .= ' SSLCompression Off' . "\n"; + } // this makes it more secure, thx to Marcel (08/2013) $this->virtualhosts_data[$vhosts_filename] .= ' SSLHonorCipherOrder On' . "\n"; $this->virtualhosts_data[$vhosts_filename] .= ' SSLCipherSuite ' . Settings::Get('system.ssl_cipher_list') . "\n"; @@ -842,6 +843,9 @@ class apache extends HttpConfigBase if ($domain['ssl_cert_file'] != '') { $vhost_content .= ' SSLEngine On' . "\n"; $vhost_content .= ' SSLProtocol -ALL +TLSv1 +TLSv1.2' . "\n"; + if (Settings::Get('system.apache24') == '1') { + $vhost_content .= ' SSLCompression Off' . "\n"; + } // this makes it more secure, thx to Marcel (08/2013) $vhost_content .= ' SSLHonorCipherOrder On' . "\n"; $vhost_content .= ' SSLCipherSuite ' . Settings::Get('system.ssl_cipher_list') . "\n"; From 1033f502b1b7307b216187a50838760427e819be Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 19 Jan 2017 14:09:51 +0100 Subject: [PATCH 0280/1335] add missing language strings, fixes #1705 Signed-off-by: Michael Kaufmann (d00p) --- lng/english.lng.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lng/english.lng.php b/lng/english.lng.php index e3b96eda..c8c7313f 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -2065,3 +2065,5 @@ $lng['admin']['domain_hsts_preload']['description'] = 'If you would like this do $lng['serversettings']['nginx_http2_support']['title'] = 'Nginx HTTP2 Support'; $lng['serversettings']['nginx_http2_support']['description'] = 'enable http2 support for ssl. ENABLE ONLY IF YOUR Nginx SUPPORT THIS FEATURE. (version 1.9.5+)'; + +$lng['error']['noipportgiven'] = 'No IP/port given'; From afb2bce16dbf43e179d2b4e0b03c4ba0ac74b7f6 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 23 Jan 2017 08:05:48 +0100 Subject: [PATCH 0281/1335] fix missing german language strings, refs #1705 Signed-off-by: Michael Kaufmann (d00p) --- lng/german.lng.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lng/german.lng.php b/lng/german.lng.php index b31b713e..64d501b5 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -1713,3 +1713,8 @@ $lng['admin']['domain_hsts_incsub']['title'] = 'Inkludiere HSTS für jede Subdom $lng['admin']['domain_hsts_incsub']['description'] = 'Die optionale "includeSubDomains" Direktive, wenn vorhanden, signalisiert dem UA, dass die HSTS Regel für diese Domain und auch jede Subdomain dieser gilt.'; $lng['admin']['domain_hsts_preload']['title'] = 'Füge Domain in die HSTS preload Liste hinzu'; $lng['admin']['domain_hsts_preload']['description'] = 'Wenn die Domain in die HSTS preload Liste, verwaltet von Chrome (und genutzt von Firefox und Safari), hinzugefügt werden soll, dann aktiviere diese Einstellung.
Die preload-Direktive zu senden kann PERMANTENTE KONSEQUENZEN haben und dazu führen, dass Benutzer auf diese Domain und auch Subdomains nicht zugreifen können.
Beachte Details unter hstspreload.appspot.com/#removal bevor ein Header mit "preload" gesendet wird.'; + +$lng['serversettings']['nginx_http2_support']['title'] = 'Nginx HTTP2 Unterstützung'; +$lng['serversettings']['nginx_http2_support']['description'] = 'Aktiviere http2 Unterstützung für SSL. NUR AKTIVIEREN, WENN nginx DIESE FUNKTION UNTERSTÜTZT (version 1.9.5+)'; + +$lng['error']['noipportgiven'] = 'Keine IP/Port angegeben'; From e00cb8926dfe0e5c581b1eec38f3ed237bf41726 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 23 Jan 2017 08:12:44 +0100 Subject: [PATCH 0282/1335] set mail-sender to customer mail address when using mod_php, fixes #1707 Signed-off-by: Michael Kaufmann (d00p) --- scripts/jobs/cron_tasks.inc.http.10.apache.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/jobs/cron_tasks.inc.http.10.apache.php b/scripts/jobs/cron_tasks.inc.http.10.apache.php index 47be4431..51e42c55 100644 --- a/scripts/jobs/cron_tasks.inc.http.10.apache.php +++ b/scripts/jobs/cron_tasks.inc.http.10.apache.php @@ -502,7 +502,9 @@ class apache extends HttpConfigBase if ($domain['phpenabled_customer'] == 1 && $domain['phpenabled_vhost'] == '1') { // This vHost has PHP enabled and we are using the regular mod_php - + $cmail = getCustomerDetail($domain['customerid'], 'email'); + $php_options_text .= ' php_admin_value sendmail_path "/usr/sbin/sendmail -t -f '.$cmail.'"' . PHP_EOL; + if ($domain['openbasedir'] == '1') { if ($domain['openbasedir_path'] == '1' || strstr($domain['documentroot'], ":") !== false) { $_phpappendopenbasedir = appendOpenBasedirPath($domain['customerroot'], true); From c2b864a20fee5403db7c3c910e8683dc87762151 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 23 Jan 2017 08:14:14 +0100 Subject: [PATCH 0283/1335] enable/disable php for standard-subdomain when adding a new customer according to the customer-phpenabled value, fixes #1708 Signed-off-by: Michael Kaufmann (d00p) --- admin_customers.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/admin_customers.php b/admin_customers.php index f73bfb6c..dd663ddd 100644 --- a/admin_customers.php +++ b/admin_customers.php @@ -910,7 +910,8 @@ if ($page == 'customers' 'customerid' => $customerid, 'adminid' => $userinfo['adminid'], 'docroot' => $documentroot, - 'adddate' => date('Y-m-d') + 'adddate' => date('Y-m-d'), + 'phpenabled' => $phpenabled ); $ins_stmt = Database::prepare(" INSERT INTO `" . TABLE_PANEL_DOMAINS . "` SET @@ -928,6 +929,7 @@ if ($page == 'customers' `dkim_id` = '0', `dkim_privkey` = '', `dkim_pubkey` = '', + `phpenabled` = :phpenabled, `add_date` = :adddate" ); Database::pexecute($ins_stmt, $ins_data); From 0eaa81b5037335b88d22c5d7056bc0dcf7593a52 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 23 Jan 2017 08:17:18 +0100 Subject: [PATCH 0284/1335] use libnss-mysl deb package from froxlor repo as debians 1.5-5 package is not suitable for jessie Signed-off-by: Michael Kaufmann (d00p) --- lib/configfiles/jessie.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/configfiles/jessie.xml b/lib/configfiles/jessie.xml index ae7e3912..a0eba1c9 100644 --- a/lib/configfiles/jessie.xml +++ b/lib/configfiles/jessie.xml @@ -4469,9 +4469,9 @@ PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin From 8030aae37a31cda6f718d96b3a5f933d36b0cc0c Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 23 Jan 2017 23:53:01 +0100 Subject: [PATCH 0285/1335] fix directory options for deactivated users, fixes #1704 Signed-off-by: Michael Kaufmann (d00p) --- scripts/jobs/cron_tasks.inc.http.10.apache.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/scripts/jobs/cron_tasks.inc.http.10.apache.php b/scripts/jobs/cron_tasks.inc.http.10.apache.php index 51e42c55..7b1f48d5 100644 --- a/scripts/jobs/cron_tasks.inc.http.10.apache.php +++ b/scripts/jobs/cron_tasks.inc.http.10.apache.php @@ -597,6 +597,16 @@ class apache extends HttpConfigBase if ($domain['deactivated'] == '1' && Settings::Get('system.deactivateddocroot') != '') { $webroot_text .= ' # Using docroot for deactivated users...' . "\n"; $webroot_text .= ' DocumentRoot "' . makeCorrectDir(Settings::Get('system.deactivateddocroot')) . "\"\n"; + $webroot_text .= ' ' . "\n"; + // >=apache-2.4 enabled? + if (Settings::Get('system.apache24') == '1') { + $webroot_text .= ' Require all granted' . "\n"; + $webroot_text .= ' AllowOverride All' . "\n"; + } else { + $webroot_text .= ' Order allow,deny' . "\n"; + $webroot_text .= ' allow from all' . "\n"; + } + $webroot_text .= ' ' . "\n"; $this->_deactivated = true; } else { $webroot_text .= ' DocumentRoot "' . $domain['documentroot'] . "\"\n"; From 95a18be5c588d137a51d3555f5490b49cf08add4 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 24 Jan 2017 09:41:45 +0100 Subject: [PATCH 0286/1335] do not use HTTP_HOST variable if mod_rewrite is not used Signed-off-by: Michael Kaufmann (d00p) --- scripts/jobs/cron_tasks.inc.http.10.apache.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/jobs/cron_tasks.inc.http.10.apache.php b/scripts/jobs/cron_tasks.inc.http.10.apache.php index 7b1f48d5..c3de7b5a 100644 --- a/scripts/jobs/cron_tasks.inc.http.10.apache.php +++ b/scripts/jobs/cron_tasks.inc.http.10.apache.php @@ -810,6 +810,7 @@ class apache extends HttpConfigBase $vhost_content .= '' . "\n"; $vhost_content .= $this->getServerNames($domain); + $domain['documentroot_norewrite'] = $domain['documentroot']; if (($ssl_vhost == false && $domain['ssl'] == '1' && $domain['ssl_redirect'] == '1')) { // We must not check if our port differs from port 443, // but if there is a destination-port != 443 @@ -833,6 +834,7 @@ class apache extends HttpConfigBase } $domain['documentroot'] = 'https://%{HTTP_HOST}' . $_sslport . '/'; + $domain['documentroot_norewrite'] = 'https://' . $domain['domain'] . $_sslport . '/'; } if ($ssl_vhost === true && $domain['ssl'] == '1' && Settings::Get('system.use_ssl') == '1') { @@ -921,7 +923,7 @@ class apache extends HttpConfigBase $vhost_content .= ' RewriteRule ^/(.*) ' . $corrected_docroot . '$1' . $modrew_red . "\n"; $vhost_content .= ' ' . "\n"; $vhost_content .= ' ' . "\n"; - $vhost_content .= ' Redirect ' . $code . ' / ' . $corrected_docroot . "\n"; + $vhost_content .= ' Redirect ' . $code . ' / ' . $domain['documentroot_norewrite'] . "\n"; $vhost_content .= ' ' . "\n"; } else { From 3e6c3d725b8f6a58d56931e192bbb3cc93ece679 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 24 Jan 2017 09:43:20 +0100 Subject: [PATCH 0287/1335] set version to 0.9.38.7 for upcoming bugfix release Signed-off-by: Michael Kaufmann (d00p) --- install/froxlor.sql | 2 +- install/updates/froxlor/0.9/update_0.9.inc.php | 6 ++++++ lib/version.inc.php | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/install/froxlor.sql b/install/froxlor.sql index e27409e4..d8d600a0 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -581,7 +581,7 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES ('panel', 'password_special_char_required', '0'), ('panel', 'password_special_char', '!?<>§$%+#=@'), ('panel', 'customer_hide_options', ''), - ('panel', 'version', '0.9.38.6'), + ('panel', 'version', '0.9.38.7'), ('panel', 'db_version', '201612110'); diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index a759879f..a917a7b5 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -3585,3 +3585,9 @@ if (isFroxlorVersion('0.9.38.5')) { showUpdateStep("Updating from 0.9.38.5 to 0.9.38.6", false); updateToVersion('0.9.38.6'); } + +if (isFroxlorVersion('0.9.38.6')) { + + showUpdateStep("Updating from 0.9.38.6 to 0.9.38.7", false); + updateToVersion('0.9.38.7'); +} diff --git a/lib/version.inc.php b/lib/version.inc.php index cf3170c3..3ce16650 100644 --- a/lib/version.inc.php +++ b/lib/version.inc.php @@ -16,7 +16,7 @@ */ // Main version variable -$version = '0.9.38.6'; +$version = '0.9.38.7'; // Database version (YYYYMMDDC where C is a daily counter) $dbversion = '201612110'; From 8f4da0638e333d62d401879fb914b1f3945e94ae Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Wed, 25 Jan 2017 10:42:18 +0100 Subject: [PATCH 0288/1335] allow underscore in dns labels, fixes #1697 Signed-off-by: Michael Kaufmann (d00p) --- dns_editor.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dns_editor.php b/dns_editor.php index 15687b10..98e3a373 100644 --- a/dns_editor.php +++ b/dns_editor.php @@ -57,12 +57,16 @@ if ($action == 'add_record' && ! empty($_POST)) { $errors[] = $lng['error']['domain_nopunycode']; } else { $record = $idna_convert->encode($record); + /* + * see https://redmine.froxlor.org/issues/1697 + * if ($type != 'SRV' && $type != 'TXT') { $check_dom = $record . '.example.com'; if (! validateDomain($check_dom)) { $errors[] = sprintf($lng['error']['subdomainiswrong'], $idna_convert->decode($record)); } } + */ if (strlen($record) > 63) { $errors[] = $lng['error']['dns_record_toolong']; } From 01a363456eab2d3808c024258eaaa303ca671c1a Mon Sep 17 00:00:00 2001 From: Vengance Date: Sat, 28 Jan 2017 15:47:19 +0100 Subject: [PATCH 0289/1335] Fix libnss config (#412) * Fix libnss config --- lib/configfiles/jessie.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/configfiles/jessie.xml b/lib/configfiles/jessie.xml index a0eba1c9..c9ac50af 100644 --- a/lib/configfiles/jessie.xml +++ b/lib/configfiles/jessie.xml @@ -4469,9 +4469,9 @@ PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin From 202eb0931f4b70a0f49e2e4ba6e36891c5a91749 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sat, 28 Jan 2017 21:20:37 +0100 Subject: [PATCH 0290/1335] fix auto-update of database in cronjob if activated Signed-off-by: Michael Kaufmann (d00p) --- install/updates/froxlor/0.9/update_0.9.inc.php | 8 +++++--- install/updatesql.php | 16 +++++++++------- lib/cron_init.php | 1 + 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index a917a7b5..d8aa8735 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -14,9 +14,11 @@ * @package Install * */ -if (! defined('AREA') || (defined('AREA') && AREA != 'admin') || ! isset($userinfo['loginname']) || (isset($userinfo['loginname']) && $userinfo['loginname'] == '')) { - header('Location: ../../../../index.php'); - exit(); +if (!defined('_CRON_UPDATE')) { + if (! defined('AREA') || (defined('AREA') && AREA != 'admin') || ! isset($userinfo['loginname']) || (isset($userinfo['loginname']) && $userinfo['loginname'] == '')) { + header('Location: ../../../../index.php'); + exit(); + } } if (isFroxlorVersion('0.9-r0')) { diff --git a/install/updatesql.php b/install/updatesql.php index 86fe204e..4cc7417f 100644 --- a/install/updatesql.php +++ b/install/updatesql.php @@ -17,13 +17,15 @@ * */ -if (!defined('AREA') - || (defined('AREA') && AREA != 'admin') - || !isset($userinfo['loginname']) - || (isset($userinfo['loginname']) && $userinfo['loginname'] == '') -) { - header('Location: ../index.php'); - exit; +if (!defined('_CRON_UPDATE')) { + if (!defined('AREA') + || (defined('AREA') && AREA != 'admin') + || !isset($userinfo['loginname']) + || (isset($userinfo['loginname']) && $userinfo['loginname'] == '') + ) { + header('Location: ../index.php'); + exit; + } } $updatelog = FroxlorLogger::getInstanceOf(array('loginname' => 'updater')); diff --git a/lib/cron_init.php b/lib/cron_init.php index 68166f5e..a432c593 100644 --- a/lib/cron_init.php +++ b/lib/cron_init.php @@ -205,6 +205,7 @@ if (hasUpdates($version) || hasDbUpdates($dbversion) fwrite($debugHandler, '*** WARNING *** - all new settings etc. will be stored with the default value, that might not always be right for your system!' . "\n"); fwrite($debugHandler, "*** WARNING *** - If you don't want this to happen in the future consider removing the --allow-autoupdate flag from the cronjob\n"); // including update procedures + define('_CRON_UPDATE', 1); include_once FROXLOR_INSTALL_DIR.'/install/updatesql.php'; // pew - everything went better than expected $cronlog->logAction(CRON_ACTION, LOG_WARNING, 'Automatic update done - you should check your settings to be sure everything is fine'); From 2d59e569df709caf7ca5af441235348d6abbc941 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 30 Jan 2017 22:46:53 +0100 Subject: [PATCH 0291/1335] fix phpenabled flag for new subdomains added by customers Signed-off-by: Michael Kaufmann (d00p) --- customer_domains.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/customer_domains.php b/customer_domains.php index cc2a637e..849eb69b 100644 --- a/customer_domains.php +++ b/customer_domains.php @@ -415,6 +415,7 @@ if ($page == 'overview') { `wwwserveralias` = :wwwserveralias, `isemaildomain` = :isemaildomain, `iswildcarddomain` = :iswildcarddomain, + `phpenabled` = :phpenabled, `openbasedir` = :openbasedir, `openbasedir_path` = :openbasedir_path, `speciallogfile` = :speciallogfile, @@ -437,6 +438,7 @@ if ($page == 'overview') { "isemaildomain" => $domain_check['subcanemaildomain'] == '3' ? '1' : '0', "openbasedir" => $domain_check['openbasedir'], "openbasedir_path" => $openbasedir_path, + "phpenabled" => $domain_check['phpenabled'], "speciallogfile" => $domain_check['speciallogfile'], "specialsettings" => $domain_check['specialsettings'], "ssl_redirect" => $ssl_redirect, From 8ef315014cd786a55e1b8e87918a44fde22fee82 Mon Sep 17 00:00:00 2001 From: Douks08 Date: Thu, 9 Feb 2017 10:19:32 +0100 Subject: [PATCH 0292/1335] Update french.lng.php MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Modification de "billets" en "tickets" qui est plus fréquemment utilisé --- lng/french.lng.php | 94 +++++++++++++++++++++++----------------------- 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/lng/french.lng.php b/lng/french.lng.php index aea6d93c..9375aa18 100644 --- a/lng/french.lng.php +++ b/lng/french.lng.php @@ -569,18 +569,18 @@ $lng['serversettings']['webalizer_quiet']['description'] = 'Verbosité du progra $lng['ticket']['admin_email'] = 'root@localhost'; $lng['ticket']['noreply_email'] = 'billets@froxlor'; -$lng['admin']['ticketsystem'] = 'Système de billets'; -$lng['menue']['ticket']['ticket'] = 'Billets de support'; +$lng['admin']['ticketsystem'] = 'Système de tickets'; +$lng['menue']['ticket']['ticket'] = 'Tickets support'; $lng['menue']['ticket']['categories'] = 'Catégories de support'; -$lng['menue']['ticket']['archive'] = 'Archives de billets'; +$lng['menue']['ticket']['archive'] = 'Archives de tickets'; $lng['ticket']['description'] = 'Entrez une description !'; -$lng['ticket']['ticket_new'] = 'Ouvrir un nouveau billet'; -$lng['ticket']['ticket_reply'] = 'Réponse au billet'; -$lng['ticket']['ticket_reopen'] = 'Réouvrir le billet'; +$lng['ticket']['ticket_new'] = 'Ouvrir un nouveau ticket'; +$lng['ticket']['ticket_reply'] = 'Réponse au ticket'; +$lng['ticket']['ticket_reopen'] = 'Réouvrir le ticket'; $lng['ticket']['ticket_newcateory'] = 'Créer une nouvelle catégorie'; $lng['ticket']['ticket_editcateory'] = 'Editer la catégorie'; -$lng['ticket']['ticket_view'] = 'Voir l\'historique du billet'; -$lng['ticket']['ticketcount'] = 'Billets'; +$lng['ticket']['ticket_view'] = 'Voir l\'historique du ticket'; +$lng['ticket']['ticketcount'] = 'Tickets'; $lng['ticket']['ticket_answers'] = 'Réponses'; // $lng['ticket']['lastchange'] = 'Dernière action'; $lng['ticket']['lastchange'] = 'Dernier changement'; @@ -601,8 +601,8 @@ $lng['ticket']['answer'] = 'Répondre'; $lng['ticket']['close'] = 'Fermer'; $lng['ticket']['reopen'] = 'Réouvrir'; $lng['ticket']['archive'] = 'Archive'; -$lng['ticket']['ticket_delete'] = 'Effacer le billet'; -$lng['ticket']['lastarchived'] = 'Billets récemment archivés'; +$lng['ticket']['ticket_delete'] = 'Effacer le ticket'; +$lng['ticket']['lastarchived'] = 'Tickets récemment archivés'; $lng['ticket']['archivedtime'] = 'Archivé'; $lng['ticket']['open'] = 'Ouvert'; $lng['ticket']['wait_reply'] = 'Attente d\'une réponse'; @@ -610,43 +610,43 @@ $lng['ticket']['replied'] = 'Répondu'; $lng['ticket']['closed'] = 'Fermé'; $lng['ticket']['staff'] = 'L\'équipe'; $lng['ticket']['customer'] = 'Client'; -$lng['ticket']['old_tickets'] = 'Messages du billet'; +$lng['ticket']['old_tickets'] = 'Messages du ticket'; $lng['ticket']['search'] = 'Rechercher dans les archives'; $lng['ticket']['nocustomer'] = 'Aucun choix'; $lng['ticket']['archivesearch'] = 'Résultat de la recherche dans les archives'; -$lng['ticket']['noresults'] = 'Aucun billet trouvé'; -$lng['ticket']['notmorethanxopentickets'] = 'Pour éviter les abus, vous ne pouvez avoir plus de %s billets ouverts'; +$lng['ticket']['noresults'] = 'Aucun ticket trouvé'; +$lng['ticket']['notmorethanxopentickets'] = 'Pour éviter les abus, vous ne pouvez avoir plus de %s tickets ouverts'; $lng['ticket']['supportstatus'] = 'Etat du support'; $lng['ticket']['supportavailable'] = 'Nos équipes de support sont disponibles et prètes à vous assister.'; $lng['ticket']['supportnotavailable'] = 'Nos équipes de support ne sont actuellement pas disponibles.'; -$lng['admin']['templates']['ticket'] = 'E-mail de notification pour les billets de support'; -$lng['admin']['templates']['SUBJECT'] = 'Sera remplacé par le sujet du billet de support.'; -$lng['admin']['templates']['new_ticket_for_customer'] = 'Informe le client que le billet a été envoyé'; -$lng['admin']['templates']['new_ticket_by_customer'] = 'Notifie l\'administrateur qu\'un nouveau billet a été ouvert par un client'; -$lng['admin']['templates']['new_reply_ticket_by_customer'] = 'Notifie l\'administrateur d\'une réponse du client au billet'; -$lng['admin']['templates']['new_ticket_by_staff'] = 'Informe le client qu\'un billet a été ouvert par l\'équipe de support'; -$lng['admin']['templates']['new_reply_ticket_by_staff'] = 'Informe le client d\'une réponse de l\'équipe de support au billet'; -$lng['mails']['new_ticket_for_customer']['mailbody'] = 'Bonjour {FIRSTNAME} {NAME},\n\nVotre demande de billet de support ayant comme sujet "{SUBJECT}" a été envoyé.\n\nVous receverez une notification lorsque votre billet aura une réponse.\n\nMerci,\nL\'équipe Froxlor.'; -$lng['mails']['new_ticket_for_customer']['subject'] = 'Votre billet de support a été envoyé'; -$lng['mails']['new_ticket_by_customer']['mailbody'] = 'Bonjour administrateur,\n\nUn nouveau billet de support ayant comme sujet "{SUBJECT}" a été ouvert.\n\nVeuillez vous connecter pour consulter le billet.\n\nMerci,\nl\'équipe Froxlor.'; -$lng['mails']['new_ticket_by_customer']['subject'] = 'Nouveau billet de support soumis'; -$lng['mails']['new_reply_ticket_by_customer']['mailbody'] = 'Bonjour administrateur,\n\nLe billet de support "{SUBJECT}" a reçu une réponse de la part du client.\n\nVeuillez vous connecter pour consulter le billet.\n\nMerci,\nL\'équipe Froxlor.'; -$lng['mails']['new_reply_ticket_by_customer']['subject'] = 'Nouvelle réponse au billet de support'; -$lng['mails']['new_ticket_by_staff']['mailbody'] = 'Bonjour {FIRSTNAME} {NAME},\n\nUn billet de support ayant comme sujet "{SUBJECT}" a été ouvert pour vous par notre équipe.\n\nVeuillez vous connecter pour consulter le billet.\n\nMerci,\nL\'équipe Froxlor.'; +$lng['admin']['templates']['ticket'] = 'E-mail de notification pour les tickets de support'; +$lng['admin']['templates']['SUBJECT'] = 'Sera remplacé par le sujet du ticket de support.'; +$lng['admin']['templates']['new_ticket_for_customer'] = 'Informe le client que le ticket a été envoyé'; +$lng['admin']['templates']['new_ticket_by_customer'] = 'Notifie l\'administrateur qu\'un nouveau ticket a été ouvert par un client'; +$lng['admin']['templates']['new_reply_ticket_by_customer'] = 'Notifie l\'administrateur d\'une réponse du client au ticket'; +$lng['admin']['templates']['new_ticket_by_staff'] = 'Informe le client qu\'un ticket a été ouvert par l\'équipe de support'; +$lng['admin']['templates']['new_reply_ticket_by_staff'] = 'Informe le client d\'une réponse de l\'équipe de support au ticket'; +$lng['mails']['new_ticket_for_customer']['mailbody'] = 'Bonjour {FIRSTNAME} {NAME},\n\nVotre demande de ticket de support ayant comme sujet "{SUBJECT}" a été envoyé.\n\nVous receverez une notification lorsque votre billet aura une réponse.\n\nMerci,\nL\'équipe Froxlor.'; +$lng['mails']['new_ticket_for_customer']['subject'] = 'Votre ticket de support a été envoyé'; +$lng['mails']['new_ticket_by_customer']['mailbody'] = 'Bonjour administrateur,\n\nUn nouveau ticket de support ayant comme sujet "{SUBJECT}" a été ouvert.\n\nVeuillez vous connecter pour consulter le billet.\n\nMerci,\nl\'équipe Froxlor.'; +$lng['mails']['new_ticket_by_customer']['subject'] = 'Nouveau ticket de support soumis'; +$lng['mails']['new_reply_ticket_by_customer']['mailbody'] = 'Bonjour administrateur,\n\nLe ticket de support "{SUBJECT}" a reçu une réponse de la part du client.\n\nVeuillez vous connecter pour consulter le billet.\n\nMerci,\nL\'équipe Froxlor.'; +$lng['mails']['new_reply_ticket_by_customer']['subject'] = 'Nouvelle réponse au ticket de support'; +$lng['mails']['new_ticket_by_staff']['mailbody'] = 'Bonjour {FIRSTNAME} {NAME},\n\nUn ticket de support ayant comme sujet "{SUBJECT}" a été ouvert pour vous par notre équipe.\n\nVeuillez vous connecter pour consulter le billet.\n\nMerci,\nL\'équipe Froxlor.'; $lng['mails']['new_ticket_by_staff']['subject'] = 'Nouvelle demande de support soumise'; -$lng['mails']['new_reply_ticket_by_staff']['mailbody'] = 'Bonjour {FIRSTNAME} {NAME},\n\nLe billet de support ayant comme sujet "{SUBJECT}" a reçu une réponse par notre équipe.\n\nVeuillez vous connecter pour consulter le billet.\n\nMerci,\nL\équipe Froxlor.'; -$lng['mails']['new_reply_ticket_by_staff']['subject'] = 'Nouvelle réponse au billet de support'; -$lng['question']['ticket_reallyclose'] = 'Etes-vous sûr de vouloir clôturer le billet "%s" ?'; -$lng['question']['ticket_reallydelete'] = 'Etes-vous sûr de vouloir supprimer le billet "%s" ?'; +$lng['mails']['new_reply_ticket_by_staff']['mailbody'] = 'Bonjour {FIRSTNAME} {NAME},\n\nLe ticket de support ayant comme sujet "{SUBJECT}" a reçu une réponse par notre équipe.\n\nVeuillez vous connecter pour consulter le billet.\n\nMerci,\nL\équipe Froxlor.'; +$lng['mails']['new_reply_ticket_by_staff']['subject'] = 'Nouvelle réponse au ticket de support'; +$lng['question']['ticket_reallyclose'] = 'Etes-vous sûr de vouloir clôturer le ticket "%s" ?'; +$lng['question']['ticket_reallydelete'] = 'Etes-vous sûr de vouloir supprimer le ticket "%s" ?'; $lng['question']['ticket_reallydeletecat'] = 'Etes-vous sûr de vouloir supprimer la catégorie "%s" ?'; -$lng['question']['ticket_reallyarchive'] = 'Etes-vous sûr de vouloir archiver le billet "%s" ?'; -$lng['error']['nomoreticketsavailable'] = 'Vous n\'avez plus de billets de disponibles. Veuillez contacter votre administrateur.'; -$lng['error']['nocustomerforticket'] = 'Ne peut créer de billet sans client'; -$lng['error']['categoryhastickets'] = 'La catégorie possède des billets.
Veuillez d\'abord supprimer tous les billets de cette catégorie.'; -$lng['admin']['ticketsettings'] = 'Paramètres des billets de support'; -$lng['admin']['archivelastrun'] = 'Derniers billets archivés'; +$lng['question']['ticket_reallyarchive'] = 'Etes-vous sûr de vouloir archiver le ticket "%s" ?'; +$lng['error']['nomoreticketsavailable'] = 'Vous n\'avez plus de tickets de disponibles. Veuillez contacter votre administrateur.'; +$lng['error']['nocustomerforticket'] = 'Impossible de créer un ticket sans clients dans la base'; +$lng['error']['categoryhastickets'] = 'La catégorie possède des tickets.
Veuillez d\'abord supprimer tous les tickets de cette catégorie.'; +$lng['admin']['ticketsettings'] = 'Paramètres des tickets de support'; +$lng['admin']['archivelastrun'] = 'Derniers tickets archivés'; $lng['serversettings']['ticket']['noreply_email']['title'] = 'Adresse e-mail de non réponse'; -$lng['serversettings']['ticket']['noreply_email']['description'] = 'L\'adresse e-mail de l\'expéditeur de notification pour les billets de support, quelque chose du type no-reply@domaine.com'; +$lng['serversettings']['ticket']['noreply_email']['description'] = 'L\'adresse e-mail de l\'expéditeur de notification pour les tickets de support, quelque chose du type no-reply@domaine.com'; $lng['serversettings']['ticket']['worktime_begin']['title'] = 'Début du support (hh:mm)'; $lng['serversettings']['ticket']['worktime_begin']['description'] = 'Horaire de début du support'; $lng['serversettings']['ticket']['worktime_end']['title'] = 'Fin du support (hh:mm)'; @@ -655,21 +655,21 @@ $lng['serversettings']['ticket']['worktime_sat'] = 'Support disponible le samedi $lng['serversettings']['ticket']['worktime_sun'] = 'Support disponible le dimanche ?'; $lng['serversettings']['ticket']['worktime_all']['title'] = 'Aucune limite horaire pour le support'; $lng['serversettings']['ticket']['worktime_all']['description'] = 'Si "Oui", les options pour le début et la fin du support seront écrasés.'; -$lng['serversettings']['ticket']['archiving_days'] = 'Après combien de jours un billet fermé sera automatiquement archivé ?'; -$lng['customer']['tickets'] = 'Billet de support'; +$lng['serversettings']['ticket']['archiving_days'] = 'Après combien de jours un ticket fermé sera automatiquement archivé ?'; +$lng['customer']['tickets'] = 'Ticket de support'; // ADDED IN 1.2.18-svn4 $lng['admin']['domain_nocustomeraddingavailable'] = 'Il n\'est acutellement pas possible d\'ajouter de domaines. Vous devez d\'abord ajouter un client.'; -$lng['serversettings']['ticket']['enable'] = 'Activer le système de billets'; -$lng['serversettings']['ticket']['concurrentlyopen'] = 'Combien de billets peuvent être ouverts au même moment ?'; +$lng['serversettings']['ticket']['enable'] = 'Activer le système de tickets'; +$lng['serversettings']['ticket']['concurrentlyopen'] = 'Combien de tickets peuvent être ouverts au même moment ?'; $lng['error']['norepymailiswrong'] = 'L\'adresse de "non réponse" n\'est pas bonne. Une adresse e-mail valide doit être entrée.'; -$lng['error']['tadminmailiswrong'] = 'L\'adresse de "l\'administrateur de billets" n\'est pas bonne. Une adresse e-mail valide doit être entrée.'; -$lng['ticket']['awaitingticketreply'] = 'Vous avez %s billet(s) de support non répondu(s).'; +$lng['error']['tadminmailiswrong'] = 'L\'adresse de "l\'administrateur de tickets" n\'est pas bonne. Une adresse e-mail valide doit être entrée.'; +$lng['ticket']['awaitingticketreply'] = 'Vous avez %s ticket(s) de support non répondu(s).'; // ADDED IN 1.2.18-svn5 -$lng['serversettings']['ticket']['noreply_name'] = 'Nom de l\'expéditeur e-mail des billets'; +$lng['serversettings']['ticket']['noreply_name'] = 'Nom de l\'expéditeur e-mail des tickets'; // ADDED IN 1.2.19-svn1 @@ -679,8 +679,8 @@ $lng['serversettings']['mod_fcgid']['tmpdir']['title'] = 'Dossier temporaire pou // ADDED IN 1.2.19-svn3 -$lng['serversettings']['ticket']['reset_cycle']['title'] = 'Intervalle de réinitialisation des billets utilisés'; -$lng['serversettings']['ticket']['reset_cycle']['description'] = 'Remettre le compteur de billets à 0 dans le temps imparti'; +$lng['serversettings']['ticket']['reset_cycle']['title'] = 'Intervalle de réinitialisation des tickets utilisés'; +$lng['serversettings']['ticket']['reset_cycle']['description'] = 'Remettre le compteur de tickets à 0 dans le temps imparti'; $lng['admin']['tickets']['daily'] = 'Journalière'; $lng['admin']['tickets']['weekly'] = 'Hebdomadaire'; $lng['admin']['tickets']['monthly'] = 'Mensuelle'; From a4f72cbb408bdb4172a3925535b4f37ece8f63c3 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sun, 12 Feb 2017 16:33:00 +0100 Subject: [PATCH 0293/1335] do not show full path of file on php-error; fixes #1720 Signed-off-by: Michael Kaufmann (d00p) --- lib/functions/froxlor/function.phpErrHandler.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/functions/froxlor/function.phpErrHandler.php b/lib/functions/froxlor/function.phpErrHandler.php index 862230a2..90664e63 100644 --- a/lib/functions/froxlor/function.phpErrHandler.php +++ b/lib/functions/froxlor/function.phpErrHandler.php @@ -25,6 +25,8 @@ function phpErrHandler($errno, $errstr, $errfile, $errline, $errcontext) { if (empty($theme)) { $theme = "Sparkle"; } + // prevent possible file-path-disclosure + $errfile = str_replace(FROXLOR_INSTALL_DIR, "", $errfile); // if we're not on the shell, output a nicer error-message $err_hint = file_get_contents(FROXLOR_INSTALL_DIR.'/templates/'.$theme.'/misc/phperrornice.tpl'); // replace values From 2284706e0c2551a00a90129f325ec56abc22b311 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 2 Mar 2017 07:57:47 +0100 Subject: [PATCH 0294/1335] do not load mod_vroot in proftpd on centos Signed-off-by: Michael Kaufmann (d00p) --- lib/configfiles/rhel_centos.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/configfiles/rhel_centos.xml b/lib/configfiles/rhel_centos.xml index ebe4cb2c..7b8be4b8 100644 --- a/lib/configfiles/rhel_centos.xml +++ b/lib/configfiles/rhel_centos.xml @@ -2089,7 +2089,7 @@ LoadModule mod_ctrls_admin.c # (http://www.castaglia.org/proftpd/modules/mod_vroot.html) # Using this module rather than the kernel's chroot() system call works # around issues with PAM and chroot (http://bugzilla.redhat.com/506735) -LoadModule mod_vroot.c +# LoadModule mod_vroot.c # # Provide a flexible way of specifying that certain configuration directives # only apply to certain sessions, based on credentials such as connection From c0fddbce8117b55ec6807dd511d47b20be9510d2 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Wed, 8 Mar 2017 14:04:40 +0100 Subject: [PATCH 0295/1335] use correct pagination in admin-log/customer-log, fixes #1726 Signed-off-by: Michael Kaufmann (d00p) --- admin_logger.php | 15 ++-- customer_logger.php | 119 +++++++++++++--------------- lib/classes/output/class.paging.php | 11 ++- 3 files changed, 75 insertions(+), 70 deletions(-) diff --git a/admin_logger.php b/admin_logger.php index a409c021..8aba34c8 100644 --- a/admin_logger.php +++ b/admin_logger.php @@ -30,11 +30,12 @@ if ($page == 'log' 'user' => $lng['logger']['user'], 'text' => $lng['logger']['action'] ); - $paging = new paging($userinfo, TABLE_PANEL_LOG, $fields, null, null, 0, 'desc'); - $result_stmt = Database::query(' - SELECT * FROM `' . TABLE_PANEL_LOG . '` ' . $paging->getSqlWhere(false) . ' ' . $paging->getSqlOrderBy() . ' ' . $paging->getSqlLimit() - ); - $logs_count = Database::num_rows(); + $paging = new paging($userinfo, TABLE_PANEL_LOG, $fields, null, null, 0, 'desc', 30); + $query = 'SELECT * FROM `' . TABLE_PANEL_LOG . '` ' . $paging->getSqlWhere(false) . ' ' . $paging->getSqlOrderBy(); + $result_stmt = Database::query($query . ' ' . $paging->getSqlLimit()); + $result_cnt_stmt = Database::query($query); + $res_cnt = $result_cnt_stmt->fetch(PDO::FETCH_ASSOC); + $logs_count = $res_cnt['resultrows']; $paging->setEntries($logs_count); $sortcode = $paging->getHtmlSortCode($lng); $arrowcode = $paging->getHtmlArrowCode($filename . '?page=' . $page . '&s=' . $s); @@ -67,7 +68,7 @@ if ($page == 'log' foreach ($clog as $action => $logrows) { $_action = 0; foreach ($logrows as $row) { - if ($paging->checkDisplay($i)) { + // if ($paging->checkDisplay($i)) { $row = htmlentities_array($row); $row['date'] = date("d.m.y H:i:s", $row['date']); @@ -105,7 +106,7 @@ if ($page == 'log' eval("\$log.=\"" . getTemplate('logger/logger_log') . "\";"); $count++; $_action = $action; - } + // } $i++; } $i++; diff --git a/customer_logger.php b/customer_logger.php index 07e3f8e2..e3290296 100644 --- a/customer_logger.php +++ b/customer_logger.php @@ -16,17 +16,15 @@ * @package Panel * */ - define('AREA', 'customer'); require './lib/init.php'; // redirect if this customer page is hidden via settings -if (Settings::IsInList('panel.customer_hide_options','extras.logger')) { +if (Settings::IsInList('panel.customer_hide_options', 'extras.logger')) { redirectTo('customer_index.php'); } -if ($page == 'log' -) { +if ($page == 'log') { if ($action == '') { $fields = array( 'date' => $lng['logger']['date'], @@ -34,89 +32,86 @@ if ($page == 'log' 'user' => $lng['logger']['user'], 'text' => $lng['logger']['action'] ); - $paging = new paging($userinfo, TABLE_PANEL_LOG, $fields, null, null, 0, 'desc'); - $result_stmt = Database::prepare(' - SELECT * FROM `' . TABLE_PANEL_LOG . '` WHERE `user` = :loginname ' . $paging->getSqlWhere(true) . ' ' . $paging->getSqlOrderBy() . ' ' . $paging->getSqlLimit() - ); - Database::pexecute($result_stmt, array("loginname" => $userinfo['loginname'])); - $logs_count = Database::num_rows(); + $paging = new paging($userinfo, TABLE_PANEL_LOG, $fields, null, null, 0, 'desc', 30); + $query = 'SELECT * FROM `' . TABLE_PANEL_LOG . '` WHERE `user` = :loginname ' . $paging->getSqlWhere(true) . ' ' . $paging->getSqlOrderBy(); + $result_stmt = Database::prepare($query . ' ' . $paging->getSqlLimit()); + Database::pexecute($result_stmt, array( + "loginname" => $userinfo['loginname'] + )); + $result_cnt_stmt = Database::query($query); + $res_cnt = $result_cnt_stmt->fetch(PDO::FETCH_ASSOC); + $logs_count = $res_cnt['resultrows']; $paging->setEntries($logs_count); $sortcode = $paging->getHtmlSortCode($lng); $arrowcode = $paging->getHtmlArrowCode($filename . '?page=' . $page . '&s=' . $s); $searchcode = $paging->getHtmlSearchCode($lng); $pagingcode = $paging->getHtmlPagingCode($filename . '?page=' . $page . '&s=' . $s); $clog = array(); - + while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) { - - if (!isset($clog[$row['action']]) - || !is_array($clog[$row['action']]) - ) { + + if (! isset($clog[$row['action']]) || ! is_array($clog[$row['action']])) { $clog[$row['action']] = array(); } $clog[$row['action']][$row['logid']] = $row; } - - if ($paging->sortfield == 'date' - && $paging->sortorder == 'desc' - ) { + + if ($paging->sortfield == 'date' && $paging->sortorder == 'desc') { krsort($clog); } else { ksort($clog); } - + $i = 0; $count = 0; $log_count = 0; $log = ''; foreach ($clog as $action => $logrows) { - $_action = 0; foreach ($logrows as $row) { - if ($paging->checkDisplay($i)) { - $row = htmlentities_array($row); - $row['date'] = date("d.m.y H:i:s", $row['date']); - - if ($_action != $action) { - switch ($action) { - case USR_ACTION: - $_action = $lng['admin']['customer']; - break; - case RES_ACTION: - $_action = $lng['logger']['reseller']; - break; - case ADM_ACTION: - $_action = $lng['logger']['admin']; - break; - case CRON_ACTION: - $_action = $lng['logger']['cron']; - break; - case LOGIN_ACTION: - $_action = $lng['logger']['login']; - break; - case LOG_ERROR: - $_action = $lng['logger']['intern']; - break; - default: - $_action = $lng['logger']['unknown']; - break; - } - - $row['action'] = $_action; - eval("\$log.=\"" . getTemplate('logger/logger_action') . "\";"); + // if ($paging->checkDisplay($i)) { + $row = htmlentities_array($row); + $row['date'] = date("d.m.y H:i:s", $row['date']); + + if ($_action != $action) { + switch ($action) { + case USR_ACTION: + $_action = $lng['admin']['customer']; + break; + case RES_ACTION: + $_action = $lng['logger']['reseller']; + break; + case ADM_ACTION: + $_action = $lng['logger']['admin']; + break; + case CRON_ACTION: + $_action = $lng['logger']['cron']; + break; + case LOGIN_ACTION: + $_action = $lng['logger']['login']; + break; + case LOG_ERROR: + $_action = $lng['logger']['intern']; + break; + default: + $_action = $lng['logger']['unknown']; + break; } - - $log_count++; - $row['type'] = getLogLevelDesc($row['type']); - eval("\$log.=\"" . getTemplate('logger/logger_log') . "\";"); - $count++; - $_action = $action; + + $row['action'] = $_action; + eval("\$log.=\"" . getTemplate('logger/logger_action') . "\";"); } - $i++; + + $log_count ++; + $row['type'] = getLogLevelDesc($row['type']); + eval("\$log.=\"" . getTemplate('logger/logger_log') . "\";"); + $count ++; + $_action = $action; + // } + $i ++; } - $i++; + $i ++; } - + eval("echo \"" . getTemplate('logger/logger') . "\";"); - } } diff --git a/lib/classes/output/class.paging.php b/lib/classes/output/class.paging.php index 62384248..8ee2bc25 100644 --- a/lib/classes/output/class.paging.php +++ b/lib/classes/output/class.paging.php @@ -88,6 +88,8 @@ class paging { * @var bool */ private $natSorting = false; + + private $_limit = 0; /** * Class constructor. Loads settings from request or from userdata and saves them to session. @@ -101,7 +103,7 @@ class paging { * @param string $default_order default sorting order 'asc' or 'desc' * */ - public function __construct($userinfo, $table, $fields, $entriesperpage = 0, $natSorting = false, $default_field = 0, $default_order = 'asc') { + public function __construct($userinfo, $table, $fields, $entriesperpage = 0, $natSorting = false, $default_field = 0, $default_order = 'asc', $limit = 0) { // entries per page and natsorting-flag are not // passed as parameter anymore, because these are @@ -230,6 +232,8 @@ class paging { 'adminsession' => $userinfo['adminsession'] ); Database::pexecute($upd_stmt, $upd_data); + + $this->_limit = $limit; } /** @@ -378,6 +382,11 @@ class paging { * @return string always empty */ public function getSqlLimit() { + + if ($this->_limit > 0) { + $_offset = ($this->pageno - 1) * $this->_limit; + return ' LIMIT '.$_offset.','.$this->_limit; + } /** * currently not in use */ From 541ab1fe6ee510542f714e2e0a638ea28cbad489 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sat, 11 Mar 2017 07:34:13 +0100 Subject: [PATCH 0296/1335] clearify field label for domain termination date; fixes #1728 Signed-off-by: Michael Kaufmann (d00p) --- lng/english.lng.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lng/english.lng.php b/lng/english.lng.php index c8c7313f..1f45fd0c 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -1972,7 +1972,7 @@ $lng['error']['autoupdate_9'] = 'The downloaded file did not pass the integrity $lng['admin']['server_php'] = 'PHP'; $lng['domains']['termination_date'] = 'Date of termination'; -$lng['domains']['termination_date_overview'] = 'canceled until '; +$lng['domains']['termination_date_overview'] = 'terminated as of '; $lng['panel']['set'] = 'Apply'; $lng['customer']['selectserveralias_addinfo'] = 'This option can be set when editing the domain. Its initial value is inherited from the parent-domain.'; $lng['error']['mailaccistobedeleted'] = "Another account with the same name (%s) is currently being deleted and can therefore not be added at this moment."; From 398d45deae5232ea70f86b805da92f6b817747f5 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 14 Mar 2017 13:46:07 +0100 Subject: [PATCH 0297/1335] fix typo Signed-off-by: Michael Kaufmann (d00p) --- lng/english.lng.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lng/english.lng.php b/lng/english.lng.php index 1f45fd0c..94e5e343 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -2038,8 +2038,8 @@ $lng['serversettings']['le_froxlor_enabled']['description'] = "If activated, the $lng['serversettings']['le_froxlor_redirect']['title'] = "Enable SSL-redirect for the froxlor vhost"; $lng['serversettings']['le_froxlor_redirect']['description'] = "If activated, all http requests to your froxlor will be redirected to the corresponding SSL site."; $lng['admin']['froxlorvhost'] = 'Froxlor VirtualHost settings'; -$lng['serversettings']['option_unavailable_websrv'] = '
Availble only for: %s'; -$lng['serversettings']['option_unavailable'] = '
Option not availble due to other settings.'; +$lng['serversettings']['option_unavailable_websrv'] = '
Available only for: %s'; +$lng['serversettings']['option_unavailable'] = '
Option not available due to other settings.'; $lng['serversettings']['letsencryptacmeconf']['title'] = "Path to the acme.conf snippet"; $lng['serversettings']['letsencryptacmeconf']['description'] = "File name of the config snippet which allows the web server to serve the acme challenge."; $lng['admin']['hostname'] = 'Hostname'; From ad7cf52f21b8cc1d0dde5ecdbcb9fb50e7be56e3 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Fri, 17 Mar 2017 13:05:39 +0100 Subject: [PATCH 0298/1335] Fix rowcount value for logger Signed-off-by: Michael Kaufmann (d00p) --- admin_logger.php | 3 +-- customer_logger.php | 6 ++++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/admin_logger.php b/admin_logger.php index 8aba34c8..9c344702 100644 --- a/admin_logger.php +++ b/admin_logger.php @@ -34,8 +34,7 @@ if ($page == 'log' $query = 'SELECT * FROM `' . TABLE_PANEL_LOG . '` ' . $paging->getSqlWhere(false) . ' ' . $paging->getSqlOrderBy(); $result_stmt = Database::query($query . ' ' . $paging->getSqlLimit()); $result_cnt_stmt = Database::query($query); - $res_cnt = $result_cnt_stmt->fetch(PDO::FETCH_ASSOC); - $logs_count = $res_cnt['resultrows']; + $logs_count = $result_cnt_stmt->rowCount(); $paging->setEntries($logs_count); $sortcode = $paging->getHtmlSortCode($lng); $arrowcode = $paging->getHtmlArrowCode($filename . '?page=' . $page . '&s=' . $s); diff --git a/customer_logger.php b/customer_logger.php index e3290296..65c1db01 100644 --- a/customer_logger.php +++ b/customer_logger.php @@ -38,9 +38,11 @@ if ($page == 'log') { Database::pexecute($result_stmt, array( "loginname" => $userinfo['loginname'] )); - $result_cnt_stmt = Database::query($query); + $result_cnt_stmt = Database::query($query, array( + "loginname" => $userinfo['loginname'] + )); $res_cnt = $result_cnt_stmt->fetch(PDO::FETCH_ASSOC); - $logs_count = $res_cnt['resultrows']; + $logs_count = $result_cnt_stmt->rowCount(); $paging->setEntries($logs_count); $sortcode = $paging->getHtmlSortCode($lng); $arrowcode = $paging->getHtmlArrowCode($filename . '?page=' . $page . '&s=' . $s); From d9e8f432986fc463aa5cdffe75f20519eb7fdf68 Mon Sep 17 00:00:00 2001 From: Vengance Date: Mon, 3 Apr 2017 20:21:44 +0200 Subject: [PATCH 0299/1335] Correct Wiki links MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Correct the links of the no longer existing redmine to Github´s inbuild Wiki --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 423e16d4..e8cec20d 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ Developed by experienced server administrators, this panel simplifies the effort 9. Have fun! ### Detailed installation -http://redmine.froxlor.org/projects/froxlor/wiki/Installationtarball +https://github.com/Froxlor/Froxlor/wiki ## Help @@ -35,7 +35,7 @@ The community is located on http://forum.froxlor.org ### Wiki More documentation may be found in the froxlor - wiki: -http://redmine.froxlor.org/projects/froxlor/wiki +https://github.com/Froxlor/Froxlor/wiki ## License @@ -48,14 +48,14 @@ http://files.froxlor.org/releases/froxlor-latest.tar.gz [MD5](http://files.froxl ### Debian repository -[HowTo](http://redmine.froxlor.org/projects/froxlor/wiki/Installationdebian) +[HowTo](https://github.com/Froxlor/Froxlor/wiki/Install-froxlor-on-debian) /etc/apt/sources.list.d/froxlor.list > deb http://debian.froxlor.org {wheezy|jessie} main ### Gentoo repository -[HowTo](http://redmine.froxlor.org/projects/froxlor/wiki/Installationgentoo) +[HowTo](https://github.com/Froxlor/Froxlor/wiki/Install-froxlor-on-gentoo) http://files.froxlor.org/gentoo/repositories.xml From 9cc69e5b3da1743af350910f488e89257b46ea09 Mon Sep 17 00:00:00 2001 From: Vengance Date: Mon, 3 Apr 2017 20:31:50 +0200 Subject: [PATCH 0300/1335] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e8cec20d..8f6dd272 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ Developed by experienced server administrators, this panel simplifies the effort 9. Have fun! ### Detailed installation -https://github.com/Froxlor/Froxlor/wiki +https://github.com/Froxlor/Froxlor/wiki/Install-froxlor-from-tarball ## Help From d4cd827284ed00888267314465773a369e3aa068 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sun, 9 Apr 2017 10:25:39 +0200 Subject: [PATCH 0301/1335] update wiki-urls in language files Signed-off-by: Michael Kaufmann (d00p) --- lng/dutch.lng.php | 4 ++-- lng/english.lng.php | 8 ++++---- lng/german.lng.php | 8 ++++---- lng/italian.lng.php | 10 +++++----- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/lng/dutch.lng.php b/lng/dutch.lng.php index fa5d87d3..ec195ec7 100644 --- a/lng/dutch.lng.php +++ b/lng/dutch.lng.php @@ -1031,7 +1031,7 @@ $lng['dkim']['dkim_notes']['description'] = 'Notities die van belang kunnen zijn $lng['dkim']['dkim_add_adsp']['title'] = 'DKIM ADSP toevoegen'; $lng['dkim']['dkim_add_adsp']['description'] = 'Indien u niet weet wat dit is, laat het op "actief" staan.'; $lng['dkim']['dkim_add_adsppolicy']['title'] = 'ADSP beleid'; -$lng['dkim']['dkim_add_adsppolicy']['description'] = 'Voor meer informatie inzake deze instelling zie DKIM ADSP policies'; +$lng['dkim']['dkim_add_adsppolicy']['description'] = 'Voor meer informatie inzake deze instelling zie DKIM ADSP policies'; $lng['admin']['cron']['cronsettings'] = 'Instellingen cron-taken'; $lng['cron']['cronname'] = 'naam cron-taak'; @@ -1146,7 +1146,7 @@ $lng['serversettings']['perl_path']['description'] = 'Alleen relevant voor light // ADDED IN FROXLOR 0.9.12-svn1 $lng['admin']['fcgid_settings'] = 'FCGID'; $lng['serversettings']['mod_fcgid_ownvhost']['title'] = 'FCGID inschakelen voor de VHost voor Froxlor'; -$lng['serversettings']['mod_fcgid_ownvhost']['description'] = 'Indien ingeschakeld wordt Froxlor ook uitgevoerd onder een lokale gebruiker
Let op:Dit vereist handmatige configuratie, zie FCGID - handbook'; +$lng['serversettings']['mod_fcgid_ownvhost']['description'] = 'Indien ingeschakeld wordt Froxlor ook uitgevoerd onder een lokale gebruiker
Let op:Dit vereist handmatige configuratie, zie FCGID - handbook'; $lng['admin']['mod_fcgid_user'] = 'Lokale gebruiker voor FCGID (Froxlor vhost)'; $lng['admin']['mod_fcgid_group'] = 'Lokale groep voor FCGID (Froxlor vhost)'; diff --git a/lng/english.lng.php b/lng/english.lng.php index 94e5e343..59765e15 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -534,7 +534,7 @@ $lng['panel']['back'] = 'Back'; // ADDED IN 1.2.16-svn12 $lng['serversettings']['mod_fcgid']['title'] = 'Enable FCGID'; -$lng['serversettings']['mod_fcgid']['description'] = 'Use this to run PHP with the corresponding useraccount.

This needs a special webserver configuration for Apache, see FCGID - handbook'; +$lng['serversettings']['mod_fcgid']['description'] = 'Use this to run PHP with the corresponding useraccount.

This needs a special webserver configuration for Apache, see FCGID - handbook'; $lng['serversettings']['sendalternativemail']['title'] = 'Use alternative email-address'; $lng['serversettings']['sendalternativemail']['description'] = 'Send the password-email to a different address during email-account-creation'; $lng['emails']['alternative_emailaddress'] = 'Alternative e-mail-address'; @@ -1102,7 +1102,7 @@ $lng['dkim']['dkim_notes']['description'] = 'Notes that might be of interest to $lng['dkim']['dkim_add_adsp']['title'] = 'Add DKIM ADSP entry'; $lng['dkim']['dkim_add_adsp']['description'] = 'If you don\'t know what this is, leave it "enabled"'; $lng['dkim']['dkim_add_adsppolicy']['title'] = 'ADSP policy'; -$lng['dkim']['dkim_add_adsppolicy']['description'] = 'For more information about this setting see DKIM ADSP policies'; +$lng['dkim']['dkim_add_adsppolicy']['description'] = 'For more information about this setting see DKIM ADSP policies'; $lng['admin']['cron']['cronsettings'] = 'Cronjob settings'; $lng['cron']['cronname'] = 'cronjob-name'; @@ -1293,7 +1293,7 @@ $lng['error']['intvaluetoolow'] = 'The given number is too low (field %s)'; $lng['error']['intvaluetoohigh'] = 'The given number is too high (field %s)'; $lng['admin']['phpfpm_settings'] = 'PHP-FPM'; $lng['serversettings']['phpfpm']['title'] = 'Enable php-fpm'; -$lng['serversettings']['phpfpm']['description'] = 'This needs a special webserver configuration see FPM-handbook for Apache2 or nginx'; +$lng['serversettings']['phpfpm']['description'] = 'This needs a special webserver configuration see FPM-handbook for Apache2 or nginx'; $lng['serversettings']['phpfpm_settings']['configdir'] = 'Configuration directory of php-fpm'; $lng['serversettings']['phpfpm_settings']['aliasconfigdir'] = 'Configuration Alias-directory of php-fpm'; $lng['serversettings']['phpfpm_settings']['reload'] = 'php-fpm restart command'; @@ -1840,7 +1840,7 @@ $lng['domains']['import_file'] = 'CSV-File'; $lng['success']['domain_import_successfully'] = 'Successfully imported %s domains.'; $lng['error']['domain_import_error'] = 'Following error occurred while importing domains: %s'; $lng['admin']['note'] = 'Note'; -$lng['domains']['import_description'] = 'Detailed information about the structure of the import-file and how to import successfully, please visit http://redmine.froxlor.org/projects/froxlor/wiki/DomainBulkActionDoc'; +$lng['domains']['import_description'] = 'Detailed information about the structure of the import-file and how to import successfully, please visit https://github.com/Froxlor/Froxlor/wiki/Domain-import-documenation'; $lng['usersettings']['custom_notes']['title'] = 'Custom notes'; $lng['usersettings']['custom_notes']['description'] = 'Feel free to put any notes you want/need in here. They will show up in the admin/customer overview for the corresponding user.'; $lng['usersettings']['custom_notes']['show'] = 'Show your notes on the dashboard of the user'; diff --git a/lng/german.lng.php b/lng/german.lng.php index 64d501b5..af0b418a 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -529,7 +529,7 @@ $lng['panel']['back'] = 'Zurück'; // ADDED IN 1.2.16-svn12 $lng['serversettings']['mod_fcgid']['title'] = 'PHP über mod_fcgid/suexec einbinden'; -$lng['serversettings']['mod_fcgid']['description'] = 'PHP wird unter dem Benutzer des Kunden ausgeführt.

Dies benötigt eine spezielle Webserver-Konfiguration für Apache, siehe FCGID-Handbuch.'; +$lng['serversettings']['mod_fcgid']['description'] = 'PHP wird unter dem Benutzer des Kunden ausgeführt.

Dies benötigt eine spezielle Webserver-Konfiguration für Apache, siehe FCGID-Handbuch.'; $lng['serversettings']['sendalternativemail']['title'] = 'Alternative E-Mail-Adresse benutzen'; $lng['serversettings']['sendalternativemail']['description'] = 'Während des Erstellens eines Accounts das Passwort an eine andere E-Mail-Adresse senden'; $lng['emails']['alternative_emailaddress'] = 'Alternative E-Mail-Adresse'; @@ -1075,7 +1075,7 @@ $lng['dkim']['dkim_notes']['description'] = 'Eine Notiz, welche für Menschen in $lng['dkim']['dkim_add_adsp']['title'] = 'DKIM-ADSP Eintrag hinzufügen'; $lng['dkim']['dkim_add_adsp']['description'] = 'Wenn unsicher oder unbekannt, belassen sie es auf "aktiviert"'; $lng['dkim']['dkim_add_adsppolicy']['title'] = 'ADSP-Richtlinie'; -$lng['dkim']['dkim_add_adsppolicy']['description'] = 'Mehr Informationen zu dieser Einstellung (englisch) DKIM-ADSP-Policies'; +$lng['dkim']['dkim_add_adsppolicy']['description'] = 'Mehr Informationen zu dieser Einstellung (englisch) DKIM-ADSP-Policies'; $lng['admin']['cron']['cronsettings'] = 'Cronjob-Einstellungen'; $lng['cron']['cronname'] = 'Cronjob-Name'; @@ -1271,7 +1271,7 @@ $lng['error']['intvaluetoolow'] = 'Die angegebene Zahl ist zu klein (Feld "%s")' $lng['error']['intvaluetoohigh'] = 'Die angegebene Zahl ist zu groß (Feld "%s")'; $lng['admin']['phpfpm_settings'] = 'PHP-FPM'; $lng['serversettings']['phpfpm']['title'] = 'Aktiviere PHP-FPM'; -$lng['serversettings']['phpfpm']['description'] = 'Dies benötigt eine spezielle Webserver-Konfiguration, siehe FPM-Handbuch für Apache2 oder nginx'; +$lng['serversettings']['phpfpm']['description'] = 'Dies benötigt eine spezielle Webserver-Konfiguration, siehe FPM-Handbuch für Apache2 oder nginx'; $lng['serversettings']['phpfpm_settings']['configdir'] = 'Pfad zu php-fpm-Konfigurationen'; $lng['serversettings']['phpfpm_settings']['aliasconfigdir'] = 'Alias-Ordner der php-fpm Konfiguration'; $lng['serversettings']['phpfpm_settings']['reload'] = 'Kommando zum Neustarten von php-fpm'; @@ -1565,7 +1565,7 @@ $lng['domains']['import_file'] = 'CSV-Datei'; $lng['success']['domain_import_successfully'] = 'Erfolgreich %s Domains importiert.'; $lng['error']['domain_import_error'] = 'Der folgende Fehler trat beim Importieren der Domains auf: %s'; $lng['admin']['note'] = 'Hinweis'; -$lng['domains']['import_description'] = 'Detaillierte Informationen über den Aufbau der Importdatei und einen erfolgreichen Import gibt es hier: http://redmine.froxlor.org/projects/froxlor/wiki/DomainBulkActionDoc (englisch)'; +$lng['domains']['import_description'] = 'Detaillierte Informationen über den Aufbau der Importdatei und einen erfolgreichen Import gibt es hier: https://github.com/Froxlor/Froxlor/wiki/Domain-import-documenation (englisch)'; $lng['usersettings']['custom_notes']['title'] = 'Eigene Notizen'; $lng['usersettings']['custom_notes']['description'] = 'Hier können Notizen je nach Lust und Laune eingetragen werden. Diese werden in der Administrator/Kunden-Übersicht bei dem jeweiligen Benutzer angezeigt.'; $lng['usersettings']['custom_notes']['show'] = 'Zeige die Notizen auf dem Dashboard des Benutzers'; diff --git a/lng/italian.lng.php b/lng/italian.lng.php index c636e70e..7962e250 100644 --- a/lng/italian.lng.php +++ b/lng/italian.lng.php @@ -435,7 +435,7 @@ $lng['panel']['translator'] = 'Traduttore'; $lng['error']['stringformaterror'] = 'Il valore per il campo "%s" non è nel formato atteso.'; // ADDED IN 1.2.15-rc1 -// Translated by marone42@googlemail.com on 03/15/2007 (see https://trac.froxlor.org/ticket/126#comment:21) +// Translated by marone42@googlemail.com on 03/15/2007 $lng['admin']['phpversion'] = 'Versione PHP'; $lng['admin']['mysqlserverversion'] = 'Versione MySQL Server'; @@ -1050,7 +1050,7 @@ $lng['dkim']['dkim_notes']['description'] = 'Nota potrebbe essere di interesse, $lng['dkim']['dkim_add_adsp']['title'] = 'Aggiungi un valore DKIM ADSP'; $lng['dkim']['dkim_add_adsp']['description'] = 'Se non si sa di cosa si tratta, lasciare "enabled"'; $lng['dkim']['dkim_add_adsppolicy']['title'] = 'Regola ADSP'; -$lng['dkim']['dkim_add_adsppolicy']['description'] = 'Per ulteriori informazioni su questa impostazione leggere DKIM ADSP policies'; +$lng['dkim']['dkim_add_adsppolicy']['description'] = 'Per ulteriori informazioni su questa impostazione leggere DKIM ADSP policies'; $lng['admin']['cron']['cronsettings'] = 'Impostazioni Cronjob'; $lng['cron']['cronname'] = 'Nome cronjob'; @@ -1171,7 +1171,7 @@ $lng['serversettings']['perl_path']['description'] = 'Rilevante solo se si utili // ADDED IN FROXLOR 0.9.12-svn1 $lng['admin']['fcgid_settings'] = 'FCGID'; $lng['serversettings']['mod_fcgid_ownvhost']['title'] = 'Abilita FCGID per i vhost Froxlor'; -$lng['serversettings']['mod_fcgid_ownvhost']['description'] = 'Se attivato, Froxlor verrà eseguito con un utente locale
ATTENZIONE:Questo richiede una configurazione manuale, vedi FCGID - handbook'; +$lng['serversettings']['mod_fcgid_ownvhost']['description'] = 'Se attivato, Froxlor verrà eseguito con un utente locale
ATTENZIONE:Questo richiede una configurazione manuale, vedi FCGID - handbook'; $lng['admin']['mod_fcgid_user'] = 'Utente locale per FCGID (Froxlor vhost)'; $lng['admin']['mod_fcgid_group'] = 'Gruppo locale per FCGID (Froxlor vhost)'; @@ -1361,7 +1361,7 @@ $lng['admin']['store_defaultindex'] = 'Archivio del file indice predefinito al p $lng['admin']['ipsandports']['ssl_cert_chainfile']['title'] = 'Percorso al file catena dei certificati SSL'; $lng['admin']['ipsandports']['ssl_cert_chainfile']['description'] = 'Principalmente Bundle CA, o similare, presubilmente vuoi impostare questo se hai acquistato un certificato SSL.'; $lng['serversettings']['phpfpm']['title'] = 'Abilita php-fpm'; -$lng['serversettings']['phpfpm']['description'] = 'Questa impostazione richiede una configurazione speciale del server web. Vedi il manuale FPM per Apache2 o nginx'; +$lng['serversettings']['phpfpm']['description'] = 'Questa impostazione richiede una configurazione speciale del server web. Vedi il manuale FPM per Apache2 o nginx'; $lng['serversettings']['phpfpm_settings']['aliasconfigdir'] = 'Configurazione cartella Alias per php-fpm'; $lng['gender']['title'] = 'Titolo'; $lng['gender']['male'] = 'Sig.'; @@ -1795,7 +1795,7 @@ $lng['domains']['import_file'] = 'File CSV'; $lng['success']['domain_import_successfully'] = 'Importato %s dominii con successo.'; $lng['error']['domain_import_error'] = 'Il seguente errore è occorsonell \'importazione di dominii: %s'; $lng['admin']['note'] = 'Nota'; -$lng['domains']['import_description'] = 'Per ottenere informazioni dettagliate sulla struttura del file di importazione e su come importare con successo, visita http://redmine.froxlor.org/projects/froxlor/wiki/DomainBulkActionDoc'; +$lng['domains']['import_description'] = 'Per ottenere informazioni dettagliate sulla struttura del file di importazione e su come importare con successo, visita https://github.com/Froxlor/Froxlor/wiki/Domain-import-documenation'; $lng['usersettings']['custom_notes']['title'] = 'Note personali'; $lng['usersettings']['custom_notes']['description'] = 'Sentiti libero di inserire qualsi nota vuoi o necessiti qui. Apparirano nel riepilogo dell\'amministratore/cliente perl \'utente corrispondente.'; $lng['usersettings']['custom_notes']['show'] = 'Mostra le tue note nel cruscotto dell\'utente'; From ec1bd6e19a3b8c68a1aa2be1bc521d63fea92f71 Mon Sep 17 00:00:00 2001 From: Daniel Reichelt Date: Tue, 11 Apr 2017 17:09:34 +0200 Subject: [PATCH 0302/1335] add OCSP stapling support for apache2 and nginx --- actions/admin/settings/131.ssl.php | 12 ++++ admin_domains.php | 56 +++++++++++++++++-- install/froxlor.sql | 4 +- .../updates/froxlor/0.9/update_0.9.inc.php | 14 +++++ lib/classes/webserver/class.WebserverBase.php | 3 +- .../admin/domains/formfield.domains_add.php | 19 ++++++- .../admin/domains/formfield.domains_edit.php | 21 ++++++- lib/version.inc.php | 2 +- lng/english.lng.php | 7 +++ lng/german.lng.php | 7 +++ .../jobs/cron_tasks.inc.http.10.apache.php | 19 ++++++- scripts/jobs/cron_tasks.inc.http.30.nginx.php | 8 +++ 12 files changed, 160 insertions(+), 12 deletions(-) diff --git a/actions/admin/settings/131.ssl.php b/actions/admin/settings/131.ssl.php index 56c27cd5..7a73da85 100644 --- a/actions/admin/settings/131.ssl.php +++ b/actions/admin/settings/131.ssl.php @@ -79,6 +79,18 @@ return array( 'default' => '', 'save_method' => 'storeSettingField' ), + 'system_apache24_ocsp_cache_path' => array( + 'label' => $lng['serversettings']['ssl']['apache24_ocsp_cache_path'], + 'settinggroup' => 'system', + 'varname' => 'apache24_ocsp_cache_path', + 'type' => 'string', + 'string_type' => 'string', + 'string_emptyallowed' => false, + 'default' => '', + 'visible' => Settings::Get('system.webserver') == "apache2" && + Settings::Get('system.apache24') == 1, + 'save_method' => 'storeSettingField' + ), 'system_leenabled' => array( 'label' => $lng['serversettings']['leenabled'], 'settinggroup' => 'system', diff --git a/admin_domains.php b/admin_domains.php index 8a28364c..f2a877aa 100644 --- a/admin_domains.php +++ b/admin_domains.php @@ -595,6 +595,9 @@ if ($page == 'domains' || $page == 'overview') { $hsts_sub = isset($_POST['hsts_sub']) && (int)$_POST['hsts_sub'] == 1 ? 1 : 0; $hsts_preload = isset($_POST['hsts_preload']) && (int)$_POST['hsts_preload'] == 1 ? 1 : 0; + // OCSP stapling + $ocsp_stapling = isset($_POST['ocsp_stapling']) && (int)$_POST['ocsp_stapling'] == 1 ? 1 : 0; + } else { $ssl_redirect = 0; $letsencrypt = 0; @@ -606,6 +609,9 @@ if ($page == 'domains' || $page == 'overview') { $hsts_maxage = 0; $hsts_sub = 0; $hsts_preload = 0; + + // OCSP stapling + $ocsp_stapling = 0; } } else { $ssl_redirect = 0; @@ -618,6 +624,9 @@ if ($page == 'domains' || $page == 'overview') { $hsts_maxage = 0; $hsts_sub = 0; $hsts_preload = 0; + + // OCSP stapling + $ocsp_stapling = 0; } // We can't enable let's encrypt for wildcard - domains @@ -789,7 +798,8 @@ if ($page == 'domains' || $page == 'overview') { 'letsencrypt' => $letsencrypt, 'hsts_maxage' => $hsts_maxage, 'hsts_sub' => $hsts_sub, - 'hsts_preload' => $hsts_preload + 'hsts_preload' => $hsts_preload, + 'ocsp_stapling' => $ocsp_stapling, ); $security_questions = array( @@ -841,7 +851,8 @@ if ($page == 'domains' || $page == 'overview') { 'letsencrypt' => $letsencrypt, 'hsts' => $hsts_maxage, 'hsts_sub' => $hsts_sub, - 'hsts_preload' => $hsts_preload + 'hsts_preload' => $hsts_preload, + 'ocsp_stapling' => $ocsp_stapling, ); $ins_stmt = Database::prepare(" @@ -878,7 +889,8 @@ if ($page == 'domains' || $page == 'overview') { `letsencrypt` = :letsencrypt, `hsts` = :hsts, `hsts_sub` = :hsts_sub, - `hsts_preload` = :hsts_preload + `hsts_preload` = :hsts_preload, + `ocsp_stapling` = :ocsp_stapling "); Database::pexecute($ins_stmt, $ins_data); $domainid = Database::lastInsertId(); @@ -1421,6 +1433,9 @@ if ($page == 'domains' || $page == 'overview') { $hsts_sub = isset($_POST['hsts_sub']) && (int)$_POST['hsts_sub'] == 1 ? 1 : 0; $hsts_preload = isset($_POST['hsts_preload']) && (int)$_POST['hsts_preload'] == 1 ? 1 : 0; + // OCSP stapling + $ocsp_stapling = isset($_POST['ocsp_stapling']) && (int)$_POST['ocsp_stapling'] == 1 ? 1 : 0; + $ssl_ipandports = array(); if (isset($_POST['ssl_ipandport']) && ! is_array($_POST['ssl_ipandport'])) { $_POST['ssl_ipandport'] = unserialize($_POST['ssl_ipandport']); @@ -1458,6 +1473,9 @@ if ($page == 'domains' || $page == 'overview') { $hsts_maxage = 0; $hsts_sub = 0; $hsts_preload = 0; + + // OCSP stapling + $ocsp_stapling = 0; } } else { $ssl_redirect = 0; @@ -1470,6 +1488,9 @@ if ($page == 'domains' || $page == 'overview') { $hsts_maxage = 0; $hsts_sub = 0; $hsts_preload = 0; + + // OCSP stapling + $ocsp_stapling = 0; } // We can't enable let's encrypt for wildcard domains @@ -1615,7 +1636,8 @@ if ($page == 'domains' || $page == 'overview') { 'letsencrypt' => $letsencrypt, 'hsts_maxage' => $hsts_maxage, 'hsts_sub' => $hsts_sub, - 'hsts_preload' => $hsts_preload + 'hsts_preload' => $hsts_preload, + 'ocsp_stapling' => $ocsp_stapling, ); $security_questions = array( @@ -1634,7 +1656,27 @@ if ($page == 'domains' || $page == 'overview') { $wwwserveralias = ($serveraliasoption == '1') ? '1' : '0'; $iswildcarddomain = ($serveraliasoption == '0') ? '1' : '0'; - if ($documentroot != $result['documentroot'] || $ssl_redirect != $result['ssl_redirect'] || $wwwserveralias != $result['wwwserveralias'] || $iswildcarddomain != $result['iswildcarddomain'] || $phpenabled != $result['phpenabled'] || $openbasedir != $result['openbasedir'] || $phpsettingid != $result['phpsettingid'] || $mod_fcgid_starter != $result['mod_fcgid_starter'] || $mod_fcgid_maxrequests != $result['mod_fcgid_maxrequests'] || $specialsettings != $result['specialsettings'] || $aliasdomain != $result['aliasdomain'] || $issubof != $result['ismainbutsubto'] || $email_only != $result['email_only'] || ($speciallogfile != $result['speciallogfile'] && $speciallogverified == '1') || $letsencrypt != $result['letsencrypt'] || $hsts_maxage != $result['hsts'] || $hsts_sub != $result['hsts_sub'] || $hsts_preload != $result['hsts_preload']) { + if ( + $documentroot != $result['documentroot'] || + $ssl_redirect != $result['ssl_redirect'] || + $wwwserveralias != $result['wwwserveralias'] || + $iswildcarddomain != $result['iswildcarddomain'] || + $phpenabled != $result['phpenabled'] || + $openbasedir != $result['openbasedir'] || + $phpsettingid != $result['phpsettingid'] || + $mod_fcgid_starter != $result['mod_fcgid_starter'] || + $mod_fcgid_maxrequests != $result['mod_fcgid_maxrequests'] || + $specialsettings != $result['specialsettings'] || + $aliasdomain != $result['aliasdomain'] || + $issubof != $result['ismainbutsubto'] || + $email_only != $result['email_only'] || + ($speciallogfile != $result['speciallogfile'] && $speciallogverified == '1') || + $letsencrypt != $result['letsencrypt'] || + $hsts_maxage != $result['hsts'] || + $hsts_sub != $result['hsts_sub'] || + $hsts_preload != $result['hsts_preload'] || + $ocsp_stapling != $result['ocsp_stapling'] + ) { inserttask('1'); } @@ -1789,6 +1831,7 @@ if ($page == 'domains' || $page == 'overview') { $update_data['hsts'] = $hsts_maxage; $update_data['hsts_sub'] = $hsts_sub; $update_data['hsts_preload'] = $hsts_preload; + $update_data['ocsp_stapling'] = $ocsp_stapling; $update_data['id'] = $id; $update_stmt = Database::prepare(" @@ -1820,7 +1863,8 @@ if ($page == 'domains' || $page == 'overview') { `letsencrypt` = :letsencrypt, `hsts` = :hsts, `hsts_sub` = :hsts_sub, - `hsts_preload` = :hsts_preload + `hsts_preload` = :hsts_preload, + `ocsp_stapling` = :ocsp_stapling WHERE `id` = :id "); Database::pexecute($update_stmt, $update_data); diff --git a/install/froxlor.sql b/install/froxlor.sql index d8d600a0..fa352c50 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -257,6 +257,7 @@ CREATE TABLE `panel_domains` ( `hsts` varchar(10) NOT NULL default '0', `hsts_sub` tinyint(1) NOT NULL default '0', `hsts_preload` tinyint(1) NOT NULL default '0', + `ocsp_stapling` tinyint(1) DEFAULT '0', PRIMARY KEY (`id`), KEY `customerid` (`customerid`), KEY `parentdomain` (`parentdomainid`), @@ -503,6 +504,7 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES ('system', 'perl_server', 'unix:/var/run/nginx/cgiwrap-dispatch.sock'), ('system', 'phpreload_command', ''), ('system', 'apache24', '0'), + ('system', 'apache24_ocsp_cache_path', 'shmcb:/var/run/apache2/ocsp-stapling.cache(131072)'), ('system', 'documentroot_use_default_value', '0'), ('system', 'passwordcryptfunc', '3'), ('system', 'axfrservers', ''), @@ -582,7 +584,7 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES ('panel', 'password_special_char', '!?<>§$%+#=@'), ('panel', 'customer_hide_options', ''), ('panel', 'version', '0.9.38.7'), - ('panel', 'db_version', '201612110'); + ('panel', 'db_version', '201704100'); DROP TABLE IF EXISTS `panel_tasks`; diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index d8aa8735..10a2ef19 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -3593,3 +3593,17 @@ if (isFroxlorVersion('0.9.38.6')) { showUpdateStep("Updating from 0.9.38.6 to 0.9.38.7", false); updateToVersion('0.9.38.7'); } + +if (isDatabaseVersion('201612110')) { + + showUpdateStep("Adding field for OCSP stapling"); + Database::query("ALTER TABLE `" . TABLE_PANEL_DOMAINS . + "` ADD `ocsp_stapling` TINYINT(1) NOT NULL DEFAULT '0';"); + lastStepStatus(0); + + showUpdateStep("Adding default setting for Apache 2.4 OCSP cache path"); + Settings::AddNew('system.apache24_ocsp_cache_path', 'shmcb:/var/run/apache2/ocsp-stapling.cache(131072)'); + lastStepStatus(0); + + updateToDbVersion('201704100'); +} diff --git a/lib/classes/webserver/class.WebserverBase.php b/lib/classes/webserver/class.WebserverBase.php index b827f04c..8f7c27c5 100644 --- a/lib/classes/webserver/class.WebserverBase.php +++ b/lib/classes/webserver/class.WebserverBase.php @@ -33,7 +33,8 @@ class WebserverBase { `c`.`documentroot` AS `customerroot`, `c`.`deactivated`, `c`.`phpenabled` AS `phpenabled_customer`, `d`.`phpenabled` AS `phpenabled_vhost`, - `d`.`mod_fcgid_starter`,`d`.`mod_fcgid_maxrequests` + `d`.`mod_fcgid_starter`,`d`.`mod_fcgid_maxrequests`, + `d`.`ocsp_stapling` FROM `".TABLE_PANEL_DOMAINS."` `d` LEFT JOIN `".TABLE_PANEL_CUSTOMERS."` `c` USING(`customerid`) diff --git a/lib/formfields/admin/domains/formfield.domains_add.php b/lib/formfields/admin/domains/formfield.domains_add.php index 8482123e..988c496f 100644 --- a/lib/formfields/admin/domains/formfield.domains_add.php +++ b/lib/formfields/admin/domains/formfield.domains_add.php @@ -213,7 +213,24 @@ return array( ) ), 'value' => array() - ) + ), + 'ocsp_stapling' => array( + 'visible' => ($ssl_ipsandports != '' ? true : false) && + Settings::Get('system.webserver') != 'lighttpd', + 'label' => $lng['admin']['domain_ocsp_stapling']['title'], + 'desc' => $lng['admin']['domain_ocsp_stapling']['description'] . + (Settings::Get('system.webserver') == 'nginx' ? + $lng['admin']['domain_ocsp_stapling']['nginx_version_warning'] : + ""), + 'type' => 'checkbox', + 'values' => array( + array ( + 'label' => $lng['panel']['yes'], + 'value' => '1' + ) + ), + 'value' => array() + ), ) ), 'section_c' => array( diff --git a/lib/formfields/admin/domains/formfield.domains_edit.php b/lib/formfields/admin/domains/formfield.domains_edit.php index 22a293cc..e351c8da 100644 --- a/lib/formfields/admin/domains/formfield.domains_edit.php +++ b/lib/formfields/admin/domains/formfield.domains_edit.php @@ -251,7 +251,26 @@ return array( 'value' => array( $result['hsts_preload'] ) - ) + ), + 'ocsp_stapling' => array( + 'visible' => ($ssl_ipsandports != '' ? true : false) && + Settings::Get('system.webserver') != 'lighttpd', + 'label' => $lng['admin']['domain_ocsp_stapling']['title'], + 'desc' => $lng['admin']['domain_ocsp_stapling']['description'] . + (Settings::Get('system.webserver') == 'nginx' ? + $lng['admin']['domain_ocsp_stapling']['nginx_version_warning'] : + ""), + 'type' => 'checkbox', + 'values' => array( + array ( + 'label' => $lng['panel']['yes'], + 'value' => '1' + ) + ), + 'value' => array( + $result['ocsp_stapling'] + ) + ), ) ), 'section_c' => array( diff --git a/lib/version.inc.php b/lib/version.inc.php index 3ce16650..db028f51 100644 --- a/lib/version.inc.php +++ b/lib/version.inc.php @@ -19,7 +19,7 @@ $version = '0.9.38.7'; // Database version (YYYYMMDDC where C is a daily counter) -$dbversion = '201612110'; +$dbversion = '201704100'; // Distribution branding-tag (used for Debian etc.) $branding = ''; diff --git a/lng/english.lng.php b/lng/english.lng.php index 59765e15..8ed89fd8 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -2067,3 +2067,10 @@ $lng['serversettings']['nginx_http2_support']['title'] = 'Nginx HTTP2 Support'; $lng['serversettings']['nginx_http2_support']['description'] = 'enable http2 support for ssl. ENABLE ONLY IF YOUR Nginx SUPPORT THIS FEATURE. (version 1.9.5+)'; $lng['error']['noipportgiven'] = 'No IP/port given'; + +// Added in froxlor 0.9.38.8 +$lng['admin']['domain_ocsp_stapling']['title'] = 'OCSP stapling'; +$lng['admin']['domain_ocsp_stapling']['description'] = 'See Wikipedia for a detailed explanation of OCSP stapling'; +$lng['admin']['domain_ocsp_stapling']['nginx_version_warning'] = '
WARNING: Nginx version 1.3.7 or above is required for OCSP stapling. If your version is older, the webserver will NOT start correctly while OCSP stapling is enabled!'; +$lng['serversettings']['ssl']['apache24_ocsp_cache_path']['title'] = 'Apache 2.4: path to the OCSP stapling cache'; +$lng['serversettings']['ssl']['apache24_ocsp_cache_path']['description'] = 'Configures the cache used to store OCSP responses which get included in TLS handshakes.'; diff --git a/lng/german.lng.php b/lng/german.lng.php index af0b418a..57e9476c 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -1718,3 +1718,10 @@ $lng['serversettings']['nginx_http2_support']['title'] = 'Nginx HTTP2 Unterstüt $lng['serversettings']['nginx_http2_support']['description'] = 'Aktiviere http2 Unterstützung für SSL. NUR AKTIVIEREN, WENN nginx DIESE FUNKTION UNTERSTÜTZT (version 1.9.5+)'; $lng['error']['noipportgiven'] = 'Keine IP/Port angegeben'; + +// Added in froxlor 0.9.38.8 +$lng['admin']['domain_ocsp_stapling']['title'] = 'OCSP stapling'; +$lng['admin']['domain_ocsp_stapling']['description'] = 'Siehe Wikipedia für eine ausführliche Beschreibung von OCSP-Stapling'; +$lng['admin']['domain_ocsp_stapling']['nginx_version_warning'] = '
WARNUNG: Nginx unterstützt OCSP-Stapling erst ab Version 1.3.7. Wenn Ihre Version älter ist, wird der Webserver bei aktiviertem OCSP-Stapling NICHT korrekt starten.'; +$lng['serversettings']['ssl']['apache24_ocsp_cache_path']['title'] = 'Apache 2.4: Pfad zum OCSP-Stapling-Cache'; +$lng['serversettings']['ssl']['apache24_ocsp_cache_path']['description'] = 'Konfiguriert den Cache-Pfad zum Zwischenspeichern der OCSP-Antworten,
die an TLS-Handshakes angehängt werden.'; diff --git a/scripts/jobs/cron_tasks.inc.http.10.apache.php b/scripts/jobs/cron_tasks.inc.http.10.apache.php index c3de7b5a..85ef9bac 100644 --- a/scripts/jobs/cron_tasks.inc.http.10.apache.php +++ b/scripts/jobs/cron_tasks.inc.http.10.apache.php @@ -107,6 +107,17 @@ class apache extends HttpConfigBase } $this->virtualhosts_data[$vhosts_filename] .= ' ' . "\n"; } + + $ocsp_cache_filename = makeCorrectFile($vhosts_folder . '/03_froxlor_ocsp_cache.conf'); + if (Settings::Get('system.use_ssl') == '1' && Settings::Get('system.apache24') == 1) { + $this->virtualhosts_data[$ocsp_cache_filename] = 'SSLStaplingCache ' . + Settings::Get('system.apache24_ocsp_cache_path') . "\n"; + } else { + if (file_exists($ocsp_cache_filename)) { + $this->logger->logAction(CRON_ACTION, LOG_NOTICE, 'apache::_createStandardDirectoryEntry: unlinking ' . basename($ocsp_cache_filename)); + unlink(makeCorrectFile($ocsp_cache_filename)); + } + } } /** @@ -504,7 +515,7 @@ class apache extends HttpConfigBase // This vHost has PHP enabled and we are using the regular mod_php $cmail = getCustomerDetail($domain['customerid'], 'email'); $php_options_text .= ' php_admin_value sendmail_path "/usr/sbin/sendmail -t -f '.$cmail.'"' . PHP_EOL; - + if ($domain['openbasedir'] == '1') { if ($domain['openbasedir_path'] == '1' || strstr($domain['documentroot'], ":") !== false) { $_phpappendopenbasedir = appendOpenBasedirPath($domain['customerroot'], true); @@ -878,6 +889,12 @@ class apache extends HttpConfigBase $vhost_content .= ' SSLCertificateChainFile ' . makeCorrectFile($domain['ssl_cert_chainfile']) . "\n"; } + if (Settings::Get('system.apache24') == '1' && isset($domain['ocsp_stapling']) && + $domain['ocsp_stapling'] == '1') + { + $vhost_content .= ' SSLUseStapling on' . PHP_EOL; + } + if ($domain['hsts'] >= 0) { $vhost_content .= ' ' . "\n"; $vhost_content .= ' Header always set Strict-Transport-Security "max-age=' . $domain['hsts']; diff --git a/scripts/jobs/cron_tasks.inc.http.30.nginx.php b/scripts/jobs/cron_tasks.inc.http.30.nginx.php index 551d43b9..9e282193 100644 --- a/scripts/jobs/cron_tasks.inc.http.30.nginx.php +++ b/scripts/jobs/cron_tasks.inc.http.30.nginx.php @@ -634,6 +634,14 @@ class nginx extends HttpConfigBase } $sslsettings .= '";' . "\n"; } + + if ((isset($domain_or_ip['ocsp_stapling']) && $domain_or_ip['ocsp_stapling'] == "1") || + (isset($domain_or_ip['letsencrypt']) && $domain_or_ip['letsencrypt'] == "1") ) { + $sslsettings .= "\t" . 'ssl_stapling on;' . "\n"; + $sslsettings .= "\t" . 'ssl_stapling_verify on;' . "\n"; + $sslsettings .= "\t" . 'ssl_trusted_certificate ' . + makeCorrectFile($domain_or_ip['ssl_cert_file']) . ';' . "\n"; + } } } From b11b423217c5cffa4369cd206145498853ed1695 Mon Sep 17 00:00:00 2001 From: Daniel Reichelt Date: Thu, 13 Apr 2017 16:38:17 +0200 Subject: [PATCH 0303/1335] readme: add section about contributing --- README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/README.md b/README.md index 8f6dd272..a6874147 100644 --- a/README.md +++ b/README.md @@ -72,3 +72,15 @@ This has 2 known side-effects at the moment: It may be possible to fix these issues, but they are not a priority at the moment +## Contributing + +Before you start working on a PR, contact us via IRC or the forum to get a +clue whether someone else isn't already working on it or if we don't want to +invest the effort in favour of working on Froxlor 2.0. +Of course, bug fixes are always welcome. +However, at this stage of the 0.9.x branch, we are not looking for new +features or refactoring, especially not the kind which requires changes to a +lot of files. +Currently, we are working on a complete re-write, which, at this point in +time, is not yet public to keep delays due to discussions about internal +details to a minimum. \ No newline at end of file From 872928fb38854e213002cbdc55e56c9582508a23 Mon Sep 17 00:00:00 2001 From: Max Khon Date: Sat, 15 Apr 2017 15:58:50 +0700 Subject: [PATCH 0304/1335] Fix customer logs: - SQLSTATE[HY000]: General error: mode must be an integer - customer_logger.php:78 Undefined variable $_actions --- customer_logger.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/customer_logger.php b/customer_logger.php index 65c1db01..366b9735 100644 --- a/customer_logger.php +++ b/customer_logger.php @@ -38,7 +38,8 @@ if ($page == 'log') { Database::pexecute($result_stmt, array( "loginname" => $userinfo['loginname'] )); - $result_cnt_stmt = Database::query($query, array( + $result_cnt_stmt = Database::prepare($query); + Database::pexecute($result_cnt_stmt, array( "loginname" => $userinfo['loginname'] )); $res_cnt = $result_cnt_stmt->fetch(PDO::FETCH_ASSOC); @@ -69,6 +70,7 @@ if ($page == 'log') { $log_count = 0; $log = ''; foreach ($clog as $action => $logrows) { + $_action = 0; foreach ($logrows as $row) { // if ($paging->checkDisplay($i)) { $row = htmlentities_array($row); From 95b7b57fc6dd008e3a2cc8b5086334ee26a177fa Mon Sep 17 00:00:00 2001 From: Max Khon Date: Sat, 15 Apr 2017 16:12:29 +0700 Subject: [PATCH 0305/1335] Skip views when altering table charsets. --- lib/classes/integrity/class.IntegrityCheck.php | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/classes/integrity/class.IntegrityCheck.php b/lib/classes/integrity/class.IntegrityCheck.php index bae1c2b3..1f8a3fb7 100644 --- a/lib/classes/integrity/class.IntegrityCheck.php +++ b/lib/classes/integrity/class.IntegrityCheck.php @@ -85,11 +85,10 @@ class IntegrityCheck { // fix database Database::query('ALTER DATABASE `' . Database::getDbName() . '` CHARACTER SET utf8 COLLATE utf8_general_ci'); // fix all tables - $handle = Database::query('SHOW TABLES'); - while ($row = $handle->fetch(PDO::FETCH_ASSOC)) { - foreach ($row as $table) { - Database::query('ALTER TABLE `' . $table . '` CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci;'); - } + $handle = Database::query('SHOW FULL TABLES WHERE Table_type != "VIEW"'); + while ($row = $handle->fetch(PDO::FETCH_BOTH)) { + $table = $row[0]; + Database::query('ALTER TABLE `' . $table . '` CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci;'); } $this->_log->logAction(ADM_ACTION, LOG_WARNING, "database charset was different from UTF-8, integrity-check fixed that"); } else { From 3870cc10026bc116dcb4da3ce326383f24de2556 Mon Sep 17 00:00:00 2001 From: Max Khon Date: Mon, 17 Apr 2017 15:55:49 +0700 Subject: [PATCH 0306/1335] Add default value for apache24_ocsp_cache_path, otherwise saving SSL settings for !apache24 fails on apache24_ocsp_cache_path validation. --- actions/admin/settings/131.ssl.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actions/admin/settings/131.ssl.php b/actions/admin/settings/131.ssl.php index 7a73da85..9028ac45 100644 --- a/actions/admin/settings/131.ssl.php +++ b/actions/admin/settings/131.ssl.php @@ -86,7 +86,7 @@ return array( 'type' => 'string', 'string_type' => 'string', 'string_emptyallowed' => false, - 'default' => '', + 'default' => 'shmcb:/var/run/apache2/ocsp-stapling.cache(131072)', 'visible' => Settings::Get('system.webserver') == "apache2" && Settings::Get('system.apache24') == 1, 'save_method' => 'storeSettingField' From 5930ab1c9db2e23d42afb4251b8fbc5f6cffb8d5 Mon Sep 17 00:00:00 2001 From: Soner Sayakci Date: Mon, 17 Apr 2017 12:34:20 +0200 Subject: [PATCH 0307/1335] Fixed MySQL Warning while creating a new user --- admin_customers.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/admin_customers.php b/admin_customers.php index dd663ddd..09e938a9 100644 --- a/admin_customers.php +++ b/admin_customers.php @@ -910,7 +910,7 @@ if ($page == 'customers' 'customerid' => $customerid, 'adminid' => $userinfo['adminid'], 'docroot' => $documentroot, - 'adddate' => date('Y-m-d'), + 'adddate' => time(), 'phpenabled' => $phpenabled ); $ins_stmt = Database::prepare(" @@ -1283,7 +1283,7 @@ if ($page == 'customers' 'customerid' => $result['customerid'], 'adminid' => $userinfo['adminid'], 'docroot' => $result['documentroot'], - 'adddate' => date('Y-m-d') + 'adddate' => time() ); $ins_stmt = Database::prepare(" INSERT INTO `" . TABLE_PANEL_DOMAINS . "` SET From 9db743487618c72cf8b38d9c86e343b5dcf108c4 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 18 Apr 2017 16:55:55 +0200 Subject: [PATCH 0308/1335] add letsencrypt, HSTS settings, oscp-stapling and phpenabled-flag to Domain-import; fixes #416 Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/bulk/class.DomainBulkAction.php | 38 ++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/lib/classes/bulk/class.DomainBulkAction.php b/lib/classes/bulk/class.DomainBulkAction.php index 9523c5a9..a5dac417 100644 --- a/lib/classes/bulk/class.DomainBulkAction.php +++ b/lib/classes/bulk/class.DomainBulkAction.php @@ -93,6 +93,12 @@ class DomainBulkAction /* 16 */ 'use_ssl', /* 17 */ 'registration_date', /* 18 */ 'ips', +/* 19 */ 'letsencrypt', +/* 20 */ 'hsts', +/* 21 */ 'hsts_sub', +/* 22 */ 'hsts_preload', +/* 23 */ 'ocsp_stapling', +/* 24 */ 'phpenabled', /* automatically added */ 'adminid', 'customerid', @@ -200,7 +206,13 @@ class DomainBulkAction `specialsettings` = :specialsettings, `ssl_redirect` = :ssl_redirect, `registration_date` = :registration_date, - `add_date` = :add_date + `add_date` = :add_date, + `letsencrypt` = :letsencrypt, + `hsts` = :hsts, + `hsts_sub` = :hsts_sub, + `hsts_preload` = :hsts_preload, + `ocsp_stapling` = :ocsp_stapling, + `phpenabled` = :phpenabled "); // prepare insert statement for ip/port <> domain @@ -335,6 +347,30 @@ class DomainBulkAction $domain_data['ssl_redirect'] = 0; } + // only check for letsencrypt, hsts and oscp-stapling if ssl is enabled + if ($domain_data['use_ssl'] == 1) { + //lets encrypt + if ($domain_data['letsencrypt'] != 1 || $domain_data['iswildcarddomain'] == 1) { + $domain_data['letsencrypt'] = 0; + } + } else { + $domain_data['letsencrypt'] = 0; + } + + // hsts + if ($domain_data['hsts'] != 1) { + $domain_data['hsts'] = 0; + } + if ($domain_data['hsts_sub'] != 1) { + $domain_data['hsts_sub'] = 0; + } + if ($domain_data['hsts_preload'] != 1) { + $domain_data['hsts_preload'] = 0; + } + if ($domain_data['ocsp_stapling'] != 1) { + $domain_data['ocsp_stapling'] = 0; + } + // add to known domains $this->_knownDomains[] = $domain_data['domain']; From 22414096ade9fa592b2bdcdbcdafb4457568a1ff Mon Sep 17 00:00:00 2001 From: Daniel Reichelt Date: Mon, 1 May 2017 21:24:48 +0200 Subject: [PATCH 0309/1335] doc: extend notes about contributing; add issue template --- .github/CONTRIBUTING.md | 58 +++++++++++++++++++++++++++++++++++ .github/ISSUE_TEMPLATE.md | 64 +++++++++++++++++++++++++++++++++++++++ README.md | 11 +------ 3 files changed, 123 insertions(+), 10 deletions(-) create mode 100644 .github/CONTRIBUTING.md create mode 100644 .github/ISSUE_TEMPLATE.md diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 00000000..ffa292ae --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,58 @@ +# Contributing + +Before you start working on a PR, contact us via IRC in #froxlor on Freenode or +the forum at https://forum.froxlor.org to get a clue whether someone else isn't +already working on it or if we don't want to invest the effort in favour of +working on Froxlor 2.0. +Of course, bug fixes are always welcome. +However, at this stage of the 0.9.x branch, we are not looking for new +features or refactoring, especially not the kind which requires changes to a +lot of files. +Currently, we are working on a complete re-write, which, at this point in +time, is not yet public to keep delays due to discussions about internal +details to a minimum. + + + + +## Checklist + +General rules for PRs are: +* Please save us all some trouble and unnecessary round-trips by _testing_ your +changes. + +* Re-write your commit history to provide a CLEAN history! + + * i.e. do not provide PRs which contain a commit that changes something, + the next changes it back, a third one changes it again, only a little + differently... + + +Thanks! + + + + +### Webserver changes +If you make changes to the functionality of webserver configuration, please +make sure your implementation covers both apache **and** nginx. + + + + +### l10n + +If you add new language strings, please make sure you add the english fallback +strings in + +* `lng/english.lng.php` +* `install/lng/english.lng.php` (if applicable) + + + + +### New settings +If you add new settings, please make sure you add the default values to + +* `install/froxlor.sql` +* handle the update (see `install/updates/froxlor/0.9/update_0.9.inc.php`) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 00000000..ff27e343 --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,64 @@ +# Bug report vs. support request +If you're unsure of whether your problem is a bug or a configuration error +* contact us via IRC in #froxlor on freenode +* or post a thread in our forum at https://forum.froxlor.org + +As a rule of thumb: before reporting an issue +* see if it hasn't been [reported](https://github.com/Froxlor/froxlor/issues) (and possibly already been [fixed](https://github.com/Froxlor/froxlor/issues?utf8=✓&q=is:issue%20is:closed)) first +* try with the git master + + + + + +# Summary + +Please provide a concise summary of the problem you're experiencing... + + + + +# System information +* Froxlor version: $version/$gitSHA1 +* Web server: apache2/nginx/lighttpd +* DNS server: Bind/PowerDNS (standalone)/PowerDNS (Bind-backend) +* POP/IMAP server: Courier/Dovecot +* SMTP server: postfix/exim +* FTP server: proftpd/pureftpd +* OS/Version: ... + + + + +# Steps to reproduce + +1. +2. +3. + + + + +# Expected behavior + +1. +2. +3. + + + + +# Actual behavior + +1. +2. +3. + + + + +# Log files/log entries +syslog: +
+example
+
diff --git a/README.md b/README.md index a6874147..35d9a624 100644 --- a/README.md +++ b/README.md @@ -74,13 +74,4 @@ It may be possible to fix these issues, but they are not a priority at the momen ## Contributing -Before you start working on a PR, contact us via IRC or the forum to get a -clue whether someone else isn't already working on it or if we don't want to -invest the effort in favour of working on Froxlor 2.0. -Of course, bug fixes are always welcome. -However, at this stage of the 0.9.x branch, we are not looking for new -features or refactoring, especially not the kind which requires changes to a -lot of files. -Currently, we are working on a complete re-write, which, at this point in -time, is not yet public to keep delays due to discussions about internal -details to a minimum. \ No newline at end of file +[see here](.github/CONTRIBUTING.md) From a2e0de23e1ed19a8c174b09edcdf559d98b1b5d0 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Fri, 5 May 2017 09:35:06 +0200 Subject: [PATCH 0310/1335] add libnss-extrausers for debian/ubuntu users Signed-off-by: Michael Kaufmann (d00p) --- install/froxlor.sql | 3 +- .../updates/froxlor/0.9/update_0.9.inc.php | 10 +++ .../preconfig/0.9/preconfig_0.9.inc.php | 8 +++ lib/configfiles/jessie.xml | 36 ++++++++++ lib/configfiles/precise.xml | 36 ++++++++++ lib/configfiles/trusty.xml | 36 ++++++++++ lib/configfiles/wheezy.xml | 36 ++++++++++ lib/version.inc.php | 2 +- scripts/classes/class.Extrausers.php | 72 +++++++++++++++++++ scripts/froxlor_master_cronjob.php | 18 +++++ scripts/jobs/cron_tasks.php | 7 ++ 11 files changed, 262 insertions(+), 2 deletions(-) create mode 100644 scripts/classes/class.Extrausers.php diff --git a/install/froxlor.sql b/install/froxlor.sql index fa352c50..fbdabcd5 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -552,6 +552,7 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES ('system', 'hsts_incsub', '0'), ('system', 'hsts_preload', '0'), ('system', 'leregistered', '0'), + ('system', 'nssextrausers', '0'), ('panel', 'decimal_places', '4'), ('panel', 'adminmail', 'admin@SERVERNAME'), ('panel', 'phpmyadmin_url', ''), @@ -584,7 +585,7 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES ('panel', 'password_special_char', '!?<>§$%+#=@'), ('panel', 'customer_hide_options', ''), ('panel', 'version', '0.9.38.7'), - ('panel', 'db_version', '201704100'); + ('panel', 'db_version', '201705050'); DROP TABLE IF EXISTS `panel_tasks`; diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index 10a2ef19..1701f8be 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -3607,3 +3607,13 @@ if (isDatabaseVersion('201612110')) { updateToDbVersion('201704100'); } + +if (isDatabaseVersion('201704100')) { + + showUpdateStep("Adding new setting for libnss-extrausers"); + $system_nssextrausers= isset($_POST['system_nssextrausers']) ? (int) $_POST['system_nssextrausers'] : 0; + Settings::AddNew('system.nssextrausers', $system_nssextrausers); + lastStepStatus(0); + + updateToDbVersion('201705050'); +} diff --git a/install/updates/preconfig/0.9/preconfig_0.9.inc.php b/install/updates/preconfig/0.9/preconfig_0.9.inc.php index a25b78fc..a528c322 100644 --- a/install/updates/preconfig/0.9/preconfig_0.9.inc.php +++ b/install/updates/preconfig/0.9/preconfig_0.9.inc.php @@ -709,4 +709,12 @@ function parseAndOutputPreconfig(&$has_preconfig, &$return, $current_version, $c $question .= '
'; eval("\$return.=\"" . getTemplate("update/preconfigitem") . "\";"); } + + if (versionInUpdate($current_db_version, '201705050')) { + $has_preconfig = true; + $description = 'DEBIAN/UBUNTU ONLY: Enable usage of libnss-extrausers as alternative to libnss-mysql (NOTE: if enabled, go through the configuration steps right after the update!!!)

'; + $question = 'Enable usage of libnss-extrausers?
'; + $question .= makeyesno('system_nssextrausers', '1', '0', '0') . '
'; + eval("\$return.=\"" . getTemplate("update/preconfigitem") . "\";"); + } } diff --git a/lib/configfiles/jessie.xml b/lib/configfiles/jessie.xml index c9ac50af..ccdbe9a3 100644 --- a/lib/configfiles/jessie.xml +++ b/lib/configfiles/jessie.xml @@ -4634,6 +4634,42 @@ aliases: files
+ + + + + + + + + + + + + + + diff --git a/lib/configfiles/precise.xml b/lib/configfiles/precise.xml index c985a38e..24d6967b 100644 --- a/lib/configfiles/precise.xml +++ b/lib/configfiles/precise.xml @@ -1624,6 +1624,42 @@ netmasks: files netgroup: files bootparams: files +automount: files +aliases: files +]]> + +
+ + + +
+ + + + + + + + + diff --git a/lib/configfiles/trusty.xml b/lib/configfiles/trusty.xml index 7a7aeb8d..44500921 100644 --- a/lib/configfiles/trusty.xml +++ b/lib/configfiles/trusty.xml @@ -1642,6 +1642,42 @@ aliases: files + + + + + + + + + + + + + + + diff --git a/lib/configfiles/wheezy.xml b/lib/configfiles/wheezy.xml index 6e286017..b6fe5698 100644 --- a/lib/configfiles/wheezy.xml +++ b/lib/configfiles/wheezy.xml @@ -5433,6 +5433,42 @@ netmasks: files netgroup: files bootparams: files +automount: files +aliases: files +]]> + +
+ + + +
+ + + + + + + + + diff --git a/lib/version.inc.php b/lib/version.inc.php index db028f51..7d67b9da 100644 --- a/lib/version.inc.php +++ b/lib/version.inc.php @@ -19,7 +19,7 @@ $version = '0.9.38.7'; // Database version (YYYYMMDDC where C is a daily counter) -$dbversion = '201704100'; +$dbversion = '201705050'; // Distribution branding-tag (used for Debian etc.) $branding = ''; diff --git a/scripts/classes/class.Extrausers.php b/scripts/classes/class.Extrausers.php new file mode 100644 index 00000000..4b979a79 --- /dev/null +++ b/scripts/classes/class.Extrausers.php @@ -0,0 +1,72 @@ + (2017-) + * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt + * @package Cron + * + */ +class Extrausers +{ + + public static function generateFiles(&$cronlog) + { + // passwd + $passwd = '/var/lib/extrausers/passwd'; + $sql = "SELECT username,'x' as password,uid,gid,'Froxlor User' as comment,homedir,shell FROM ftp_users WHERE login_enabled = 'Y' ORDER BY uid ASC"; + self::_generateFile($passwd, $sql, $cronlog); + + // group + $group = '/var/lib/extrausers/group'; + $sql = "SELECT groupname,'x' as password,gid,members FROM ftp_groups ORDER BY gid ASC"; + self::_generateFile($group, $sql, $cronlog); + + // shadow + $shadow = '/var/lib/extrausers/shadow'; + $sql = "SELECT username,password FROM ftp_users ORDER BY gid ASC"; + self::_generateFile($shadow, $sql, $cronlog); + } + + private static function _generateFile($file, $query, &$cronlog) + { + $type = basename($file); + $cronlog->logAction(CRON_ACTION, LOG_NOTICE, 'Creating ' . $type . ' file'); + + if (! file_exists($file)) { + $cronlog->logAction(CRON_ACTION, LOG_NOTICE, $type . ' file does not yet exist'); + @mkdir(dirname($file), 0750, true); + touch($file); + } + + $data_sel_stmt = Database::query($query); + $data_content = ""; + $cronlog->logAction(CRON_ACTION, LOG_NOTICE, 'Writing ' . $data_sel_stmt->rowCount() . ' entries to ' . $type . ' file'); + while ($u = $data_sel_stmt->fetch(PDO::FETCH_ASSOC)) { + switch ($type) { + case 'passwd': + $line = $u['username'] . ':' . $u['password'] . ':' . $u['uid'] . ':' . $u['gid'] . ':' . $u['comment'] . ':' . $u['homedir'] . ':' . $u['shell'] . PHP_EOL; + break; + case 'group': + $line = $u['groupname'] . ':' . $u['password'] . ':' . $u['gid'] . ':' . $u['members'] . PHP_EOL; + break; + case 'shadow': + $line = $u['username'] . ':' . $u['password'] . ':' . floor(time() / 86400 - 1) . ':0:99999:7:::' . PHP_EOL; + break; + } + $data_content .= $line; + } + if (file_put_contents($file, $data_content) !== false) { + $cronlog->logAction(CRON_ACTION, LOG_NOTICE, 'Succesfully wrote ' . $type . ' file'); + } else { + $cronlog->logAction(CRON_ACTION, LOG_NOTICE, 'Error when writing ' . $type . ' file entries'); + } + } +} diff --git a/scripts/froxlor_master_cronjob.php b/scripts/froxlor_master_cronjob.php index 3e4923d4..d00926c6 100644 --- a/scripts/froxlor_master_cronjob.php +++ b/scripts/froxlor_master_cronjob.php @@ -68,6 +68,9 @@ for ($x = 1; $x < count($argv); $x++) { $cronlog->setCronDebugFlag(defined('CRON_DEBUG_FLAG')); +$tasks_cnt_stmt = Database::query("SELECT COUNT(*) as jobcnt FROM `panel_tasks`"); +$tasks_cnt = $tasks_cnt_stmt->fetch(PDO::FETCH_ASSOC); + // do we have anything to include? if (count($jobs_to_run) > 0) { // include all jobs we want to execute @@ -76,6 +79,21 @@ if (count($jobs_to_run) > 0) { $cronfile = getCronFile($cron); require_once $cronfile; } + + if ($tasks_cnt['jobcnt'] > 0) + { + if (Settings::Get('system.nssextrausers') == 1) + { + include_once makeCorrectFile(FROXLOR_INSTALL_DIR.'/scripts/classes/class.Extrausers.php'); + Extrausers::generateFiles($cronlog); + } + + // clear NSCD cache if using fcgid or fpm, #1570 + if (Settings::Get('system.mod_fcgid') == 1 || (int)Settings::Get('phpfpm.enabled') == 1) { + $false_val = false; + safe_exec('nscd -i group 1> /dev/null', $false_val, array('>')); + } + } } fwrite($debugHandler, 'Cronfiles have been included' . "\n"); diff --git a/scripts/jobs/cron_tasks.php b/scripts/jobs/cron_tasks.php index dd7a754f..cd9bb294 100644 --- a/scripts/jobs/cron_tasks.php +++ b/scripts/jobs/cron_tasks.php @@ -168,6 +168,13 @@ while ($row = $result_tasks_stmt->fetch(PDO::FETCH_ASSOC)) { $cronlog->logAction(CRON_ACTION, LOG_NOTICE, 'Running: chown -R ' . (int)Settings::Get('system.vmail_uid') . ':' . (int)Settings::Get('system.vmail_gid') . ' ' . escapeshellarg($usermaildir)); safe_exec('chown -R ' . (int)Settings::Get('system.vmail_uid') . ':' . (int)Settings::Get('system.vmail_gid') . ' ' . escapeshellarg($usermaildir)); + if (Settings::Get('system.nssextrausers') == 1) + { + // explicitly create files after user has been created to avoid unknown user issues for apache/php-fpm when task#1 runs after this + include_once makeCorrectFile(FROXLOR_INSTALL_DIR.'/scripts/classes/class.Extrausers.php'); + Extrausers::generateFiles($cronlog); + } + // clear NSCD cache if using fcgid or fpm, #1570 if (Settings::Get('system.mod_fcgid') == 1 || (int)Settings::Get('phpfpm.enabled') == 1) { $false_val = false; From bea1677d5d657089bfe37f857f45ac078ae293f9 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Fri, 5 May 2017 09:44:37 +0200 Subject: [PATCH 0311/1335] Add setting for libnss-extrausers to frontend Signed-off-by: Michael Kaufmann (d00p) --- actions/admin/settings/135.fcgid.php | 8 ++++++++ actions/admin/settings/136.phpfpm.php | 8 ++++++++ lng/english.lng.php | 2 ++ lng/german.lng.php | 2 ++ 4 files changed, 20 insertions(+) diff --git a/actions/admin/settings/135.fcgid.php b/actions/admin/settings/135.fcgid.php index 050ede3b..d57e4241 100644 --- a/actions/admin/settings/135.fcgid.php +++ b/actions/admin/settings/135.fcgid.php @@ -105,6 +105,14 @@ return array( 'default' => 30, 'save_method' => 'storeSettingField' ), + 'system_nssextrausers' => array( + 'label' => $lng['serversettings']['nssextrausers'], + 'settinggroup' => 'system', + 'varname' => 'nssextrausers', + 'type' => 'bool', + 'default' => false, + 'save_method' => 'storeSettingField' + ), ) ) ) diff --git a/actions/admin/settings/136.phpfpm.php b/actions/admin/settings/136.phpfpm.php index 750a4660..45fc5e71 100644 --- a/actions/admin/settings/136.phpfpm.php +++ b/actions/admin/settings/136.phpfpm.php @@ -160,6 +160,14 @@ return array( 'visible' => Settings::Get('system.apache24'), 'save_method' => 'storeSettingField' ), + 'system_nssextrausers' => array( + 'label' => $lng['serversettings']['nssextrausers'], + 'settinggroup' => 'system', + 'varname' => 'nssextrausers', + 'type' => 'bool', + 'default' => false, + 'save_method' => 'storeSettingField' + ), ), ), ), diff --git a/lng/english.lng.php b/lng/english.lng.php index 8ed89fd8..5361073f 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -2074,3 +2074,5 @@ $lng['admin']['domain_ocsp_stapling']['description'] = 'See Date: Tue, 9 May 2017 13:17:50 +0200 Subject: [PATCH 0312/1335] fix sql_mode=only_full_group_by in admin_admins.php; fix wrong webserver-user when using FCGID Signed-off-by: Michael Kaufmann (d00p) --- admin_admins.php | 2 +- scripts/jobs/cron_tasks.inc.http.10.apache.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/admin_admins.php b/admin_admins.php index 32e5de12..de104c34 100644 --- a/admin_admins.php +++ b/admin_admins.php @@ -846,7 +846,7 @@ if ($page == 'admins' $ipaddress = makeoption($lng['admin']['allips'], "-1", $result['ip']); $ipsandports_stmt = Database::query(" - SELECT `id`, `ip` FROM `" . TABLE_PANEL_IPSANDPORTS . "` GROUP BY `ip` ORDER BY `ip`, `port` ASC + SELECT `id`, `ip` FROM `" . TABLE_PANEL_IPSANDPORTS . "` GROUP BY `id`, `ip` ORDER BY `ip`, `port` ASC "); while ($row = $ipsandports_stmt->fetch(PDO::FETCH_ASSOC)) { diff --git a/scripts/jobs/cron_tasks.inc.http.10.apache.php b/scripts/jobs/cron_tasks.inc.http.10.apache.php index 85ef9bac..a7ad40f7 100644 --- a/scripts/jobs/cron_tasks.inc.http.10.apache.php +++ b/scripts/jobs/cron_tasks.inc.http.10.apache.php @@ -253,7 +253,7 @@ class apache extends HttpConfigBase 'adminid' => 1, /* first admin-user (superadmin) */ 'mod_fcgid_starter' => - 1, 'mod_fcgid_maxrequests' => - 1, - 'guid' => Settings::Get('phpfpm.vhost_httpuser'), + 'guid' => Settings::Get('system.mod_fcgid_httpuser'), 'openbasedir' => 0, 'email' => Settings::Get('panel.adminmail'), 'loginname' => 'froxlor.panel', From 434f202832008ed427b5b38827b5737dd62d33c3 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Fri, 12 May 2017 12:22:04 +0200 Subject: [PATCH 0313/1335] minor fixes in traffic cron Signed-off-by: Michael Kaufmann (d00p) --- scripts/jobs/cron_traffic.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/scripts/jobs/cron_traffic.php b/scripts/jobs/cron_traffic.php index 282985e5..d5e077d4 100644 --- a/scripts/jobs/cron_traffic.php +++ b/scripts/jobs/cron_traffic.php @@ -115,7 +115,7 @@ while ($row_database = $databases_stmt->fetch(PDO::FETCH_ASSOC)) { Database::needRoot(true, $row_database['dbserver']); $last_dbserver = $row_database['dbserver']; - $database_list = array(); + $databases_list = array(); $databases_list_result_stmt = Database::query("SHOW DATABASES"); while ($databases_list_row = $databases_list_result_stmt->fetch(PDO::FETCH_ASSOC)) { $databases_list[] = strtolower($databases_list_row['Database']); @@ -501,9 +501,7 @@ while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) { Database::pexecute($result_quota_stmt, array('customerid' => $row['customerid'])); // get correct user - if (Settings::Get('system.mod_fcgid') == 1 - && $row['deactivated'] == '0' - ) { + if ((Settings::Get('system.mod_fcgid') == 1 || Settings::Get('phpfpm.enabled') == 1) && $row['deactivated'] == '0') { $user = $row['loginname']; $group = $row['loginname']; } else { From 9aa2cc269bf184bd7f81c0d723e563db6b3c51c3 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Fri, 12 May 2017 12:23:14 +0200 Subject: [PATCH 0314/1335] rename MYSQL_PASSWORD replacer to FROXLOR_MYSQL_PASSWORD in config-templates to avoid false replacement of couriers authmysqlrc, fixes #438 Signed-off-by: Michael Kaufmann (d00p) --- admin_configfiles.php | 2 +- lng/english.lng.php | 2 +- lng/german.lng.php | 2 +- templates/Sparkle/admin/configfiles/configfiles.tpl | 2 +- templates/Sparkle/assets/js/main.js | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/admin_configfiles.php b/admin_configfiles.php index c5e38a88..29ae9437 100644 --- a/admin_configfiles.php +++ b/admin_configfiles.php @@ -45,7 +45,7 @@ if ($userinfo['change_serversettings'] == '1') { $replace_arr = Array( '' => $sql['user'], - '' => 'MYSQL_PASSWORD', + '' => 'FROXLOR_MYSQL_PASSWORD', '' => $sql['db'], '' => $sql['host'], '' => isset($sql['socket']) ? $sql['socket'] : null, diff --git a/lng/english.lng.php b/lng/english.lng.php index 5361073f..bfd981f8 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -1849,7 +1849,7 @@ $lng['error']['fcgidandphpfpmnogoodtogether'] = 'FCGID and PHP-FPM cannot be act // Added in Froxlor 0.9.34 $lng['admin']['configfiles']['legend'] = 'You are about to configure a service/daemon. The following legend explains the nomenclature.'; $lng['admin']['configfiles']['commands'] = 'Commands: These commands are to be executed line by line as root-user in a shell. It is safe to copy the whole block and paste it into the shell.'; -$lng['admin']['configfiles']['files'] = 'Configfiles: This is an example of the contents of a configuration file. The commands before these textfields should open an editor with the target file. Just copy and paste the contents into the editor and save the file.

Please note: The MySQL-password has not been replaced for security reasons. Please replace "MYSQL_PASSWORD" on your own. If you forgot your MySQL-password you\'ll find it in "lib/userdata.inc.php"'; +$lng['admin']['configfiles']['files'] = 'Configfiles: This is an example of the contents of a configuration file. The commands before these textfields should open an editor with the target file. Just copy and paste the contents into the editor and save the file.

Please note: The MySQL-password has not been replaced for security reasons. Please replace "FROXLOR_MYSQL_PASSWORD" on your own. If you forgot your MySQL-password you\'ll find it in "lib/userdata.inc.php"'; $lng['serversettings']['apache_itksupport']['title'] = 'Use modifications for Apache ITK-MPM'; $lng['serversettings']['apache_itksupport']['description'] = 'ATTENTION: use only if you acutally have apache itk-mpm enabled
otherwise your webserver will not be able to start'; $lng['integrity_check']['DatabaseCharset'] = 'Characterset of database (should be UTF-8)'; diff --git a/lng/german.lng.php b/lng/german.lng.php index 7c2f182e..761fdac5 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -1574,7 +1574,7 @@ $lng['error']['fcgidandphpfpmnogoodtogether'] = 'FCGID und PHP-FPM können nicht // Added in Froxlor 0.9.34 $lng['admin']['configfiles']['legend'] = 'Du konfigurierst nun einen Service/Daemon. Die folgende Legende zeigt unsere Nomenklatur.'; $lng['admin']['configfiles']['commands'] = 'Kommandos: Die angezeigten Befehle müssen als Benutzer root in einer Shell ausgeführt werden. Es kann auch problemlos der ganze Block kopiert und in die Shell eingefügt werden.'; -$lng['admin']['configfiles']['files'] = 'Konfigurationsdateien: Dies ist der Inhalt einer Konfigurationsdatei. Der Befehl direkt vor dem Textfeld sollte einen Editor mit der Zieldatei öffnen. Der Inhalt kann nun einfach kopiert und in den Editor eingefügt und die Datei gespeichert werden.

Beachten Sie: Das MySQL-Passwort wurde aus Sicherheitsgründen nicht ersetzt. Bitte ersetzen Sie "MYSQL_PASSWORD" manuell durch das entsprechende Passwort. Falls Sie es vergessen haben sollten, finden Sie es in der Datei "lib/userdata.inc.php".'; +$lng['admin']['configfiles']['files'] = 'Konfigurationsdateien: Dies ist der Inhalt einer Konfigurationsdatei. Der Befehl direkt vor dem Textfeld sollte einen Editor mit der Zieldatei öffnen. Der Inhalt kann nun einfach kopiert und in den Editor eingefügt und die Datei gespeichert werden.

Beachten Sie: Das MySQL-Passwort wurde aus Sicherheitsgründen nicht ersetzt. Bitte ersetzen Sie "FROXLOR_MYSQL_PASSWORD" manuell durch das entsprechende Passwort. Falls Sie es vergessen haben sollten, finden Sie es in der Datei "lib/userdata.inc.php".'; $lng['serversettings']['apache_itksupport']['title'] = 'Anpassungen für Apache ITK-MPM verwenden'; $lng['serversettings']['apache_itksupport']['description'] = '
Achtung: Bitte nur verwenden, wenn wirklich Apache itk-mpm verwendet wird, ansonsten wird der Webserver nicht starten.
'; $lng['integrity_check']['DatabaseCharset'] = 'Characterset der Datenbank (sollte UTF-8 sein)'; diff --git a/templates/Sparkle/admin/configfiles/configfiles.tpl b/templates/Sparkle/admin/configfiles/configfiles.tpl index f35b4051..cab16fb1 100644 --- a/templates/Sparkle/admin/configfiles/configfiles.tpl +++ b/templates/Sparkle/admin/configfiles/configfiles.tpl @@ -27,7 +27,7 @@ eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.

- MYSQL_PASSWORD: + FROXLOR_MYSQL_PASSWORD:
diff --git a/templates/Sparkle/assets/js/main.js b/templates/Sparkle/assets/js/main.js index 3d0ec460..3ea7cc4f 100644 --- a/templates/Sparkle/assets/js/main.js +++ b/templates/Sparkle/assets/js/main.js @@ -205,12 +205,12 @@ $(document).ready(function() { // Config files var configfileTextareas = $("textarea.filecontent, textarea.shell"); - var lastPw = "MYSQL_PASSWORD"; + var lastPw = "FROXLOR_MYSQL_PASSWORD"; $("#configfiles_setmysqlpw").submit(function(event) { event.preventDefault(); var inputVal = $("#configfiles_mysqlpw").val(); if (!inputVal.trim()) { - inputVal = "MYSQL_PASSWORD"; + inputVal = "FROXLOR_MYSQL_PASSWORD"; } configfileTextareas.each(function() { this.value = this.value.replace(lastPw, inputVal); From dae233dd05fd93fe4cb251f9dc6e875f63bf036f Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sun, 4 Jun 2017 09:17:31 +0200 Subject: [PATCH 0315/1335] allow adding domains with multiple dashes, that are NOT punycode (xn--) Signed-off-by: Michael Kaufmann (d00p) --- admin_domains.php | 2 +- customer_domains.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/admin_domains.php b/admin_domains.php index f2a877aa..5b2b31a5 100644 --- a/admin_domains.php +++ b/admin_domains.php @@ -306,7 +306,7 @@ if ($page == 'domains' || $page == 'overview') { standard_error('admin_domain_emailsystemhostname'); } - if (strpos($_POST['domain'], '--') !== false) { + if (substr($_POST['domain'], 0, 4) == 'xn--') { standard_error('domain_nopunycode'); } diff --git a/customer_domains.php b/customer_domains.php index 849eb69b..f492ee8a 100644 --- a/customer_domains.php +++ b/customer_domains.php @@ -260,7 +260,7 @@ if ($page == 'overview') { if ($userinfo['subdomains_used'] < $userinfo['subdomains'] || $userinfo['subdomains'] == '-1') { if (isset($_POST['send']) && $_POST['send'] == 'send') { - if (strpos($_POST['subdomain'], '--') !== false) { + if (substr($_POST['subdomain'], 0, 4) == 'xn--') { standard_error('domain_nopunycode'); } From a3201481f6984f2d0f7db4e5d315ac3c41b54f13 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sun, 4 Jun 2017 09:30:32 +0200 Subject: [PATCH 0316/1335] beatufication in generated vhost configs Signed-off-by: Michael Kaufmann (d00p) --- scripts/jobs/cron_tasks.inc.http.10.apache.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/jobs/cron_tasks.inc.http.10.apache.php b/scripts/jobs/cron_tasks.inc.http.10.apache.php index a7ad40f7..8440d005 100644 --- a/scripts/jobs/cron_tasks.inc.http.10.apache.php +++ b/scripts/jobs/cron_tasks.inc.http.10.apache.php @@ -869,7 +869,7 @@ class apache extends HttpConfigBase $vhost_content .= ' SSLEngine On' . "\n"; $vhost_content .= ' SSLProtocol -ALL +TLSv1 +TLSv1.2' . "\n"; if (Settings::Get('system.apache24') == '1') { - $vhost_content .= ' SSLCompression Off' . "\n"; + $vhost_content .= ' SSLCompression Off' . "\n"; } // this makes it more secure, thx to Marcel (08/2013) $vhost_content .= ' SSLHonorCipherOrder On' . "\n"; From 332e6270075ea8f4507a40b1a404e6774908a3fe Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sun, 4 Jun 2017 09:31:55 +0200 Subject: [PATCH 0317/1335] fix wildcard entries in dns editor, fixes #447; do not add extra dot at the end of SRV entries, fixes #446 Signed-off-by: Michael Kaufmann (d00p) --- dns_editor.php | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/dns_editor.php b/dns_editor.php index 98e3a373..0d6b437e 100644 --- a/dns_editor.php +++ b/dns_editor.php @@ -56,7 +56,19 @@ if ($action == 'add_record' && ! empty($_POST)) { if (strpos($record, '--') !== false) { $errors[] = $lng['error']['domain_nopunycode']; } else { + // check for wildcard-record + $add_wildcard_again = false; + if (substr($record, 0, 2) == '*.') { + $record = substr($record, 2); + $add_wildcard_again = true; + } + // convert entry $record = $idna_convert->encode($record); + + if ($add_wildcard_again) { + $record = '*.'.$record; + } + /* * see https://redmine.froxlor.org/issues/1697 * @@ -175,8 +187,8 @@ if ($action == 'add_record' && ! empty($_POST)) { } } } - // append trailing dot (again) - if ($target != '.') { + // append trailing dot if there's none + if (substr($content, - 1) != '.') { $content .= '.'; } } From e95a9178123904df01538d54433f9c170f9a6794 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sun, 4 Jun 2017 11:29:21 +0200 Subject: [PATCH 0318/1335] allow non fqdn CNAME entries (froxlor appends zone's domain automatically if there's no trailing dot), fixes #434 Signed-off-by: Michael Kaufmann (d00p) --- dns_editor.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dns_editor.php b/dns_editor.php index 0d6b437e..98a4d683 100644 --- a/dns_editor.php +++ b/dns_editor.php @@ -128,6 +128,9 @@ if ($action == 'add_record' && ! empty($_POST)) { if (substr($content, - 1) == '.') { // remove it for checks $content = substr($content, 0, - 1); + } else { + // add domain name + $content .= '.' . $domain; } if (! validateDomain($content)) { $errors[] = $lng['error']['dns_cname_invaliddom']; From aa8a7ee0a98f0c4bced104cec5972cb2387bb704 Mon Sep 17 00:00:00 2001 From: Oliver Rahner Date: Sat, 10 Jun 2017 10:59:41 +0200 Subject: [PATCH 0319/1335] nginx: add index directive to path options for secured directories `index` directives are now created for secured directory locations. --- scripts/jobs/cron_tasks.inc.http.30.nginx.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/scripts/jobs/cron_tasks.inc.http.30.nginx.php b/scripts/jobs/cron_tasks.inc.http.30.nginx.php index 9e282193..20d3dbc8 100644 --- a/scripts/jobs/cron_tasks.inc.http.30.nginx.php +++ b/scripts/jobs/cron_tasks.inc.http.30.nginx.php @@ -719,6 +719,11 @@ class nginx extends HttpConfigBase if ($single['path'] == '/') { $path_options .= "\t\t" . 'auth_basic "' . $single['authname'] . '";' . "\n"; $path_options .= "\t\t" . 'auth_basic_user_file ' . makeCorrectFile($single['usrf']) . ';' . "\n"; + if ($domain['phpenabled_customer'] == 1 && $domain['phpenabled_vhost'] == '1') { + $path_options .= "\t\t" . 'index index.php index.html index.htm;' . "\n"; + } else { + $path_options .= "\t\t" . 'index index.html index.htm;' . "\n"; + } $path_options .= "\t\t" . 'location ~ ^(.+?\.php)(/.*)?$ {' . "\n"; $path_options .= "\t\t\t" . 'try_files ' . $domain['nonexistinguri'] . ' @php;' . "\n"; $path_options .= "\t\t" . '}' . "\n"; @@ -776,6 +781,11 @@ class nginx extends HttpConfigBase $path_options .= "\t" . 'location ' . makeCorrectDir($single['path']) . ' {' . "\n"; $path_options .= "\t\t" . 'auth_basic "' . $single['authname'] . '";' . "\n"; $path_options .= "\t\t" . 'auth_basic_user_file ' . makeCorrectFile($single['usrf']) . ';' . "\n"; + if ($domain['phpenabled_customer'] == 1 && $domain['phpenabled_vhost'] == '1') { + $path_options .= "\t\t" . 'index index.php index.html index.htm;' . "\n"; + } else { + $path_options .= "\t\t" . 'index index.html index.htm;' . "\n"; + } $path_options .= "\t\t" . 'location ~ ^(.+?\.php)(/.*)?$ {' . "\n"; $path_options .= "\t\t\t" . 'try_files ' . $domain['nonexistinguri'] . ' @php;' . "\n"; $path_options .= "\t\t" . '}' . "\n"; From dc22ff6aa35f955258833989c51a226b89049d2a Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 20 Jun 2017 16:48:11 +0200 Subject: [PATCH 0320/1335] fix rebuild of libnss-extrausers files when editing/deleting ftp account, fixes #454 Signed-off-by: Michael Kaufmann (d00p) --- customer_ftp.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/customer_ftp.php b/customer_ftp.php index 27f90864..46cd71f4 100644 --- a/customer_ftp.php +++ b/customer_ftp.php @@ -134,6 +134,12 @@ if ($page == 'overview') { // refs #293 if (isset($_POST['delete_userfiles']) && (int)$_POST['delete_userfiles'] == 1) { inserttask('8', $userinfo['loginname'], $result['homedir']); + } else { + if (Settings::Get('system.nssextrausers') == 1) + { + // this is used so that the libnss-extrausers cron is fired + inserttask(5); + } } $stmt = Database::prepare("UPDATE `" . TABLE_PANEL_CUSTOMERS . "` @@ -431,6 +437,7 @@ if ($page == 'overview') { } $log->logAction(USR_ACTION, LOG_INFO, "edited ftp-account '" . $result['username'] . "'"); + inserttask(5); $description = validate($_POST['ftp_description'], 'description'); $stmt = Database::prepare("UPDATE `" . TABLE_FTP_USERS . "` SET `description` = :desc, `shell` = :shell From 82c719d7864660c152694e85fc4ec18760c6c842 Mon Sep 17 00:00:00 2001 From: Pascal Querner Date: Fri, 7 Jul 2017 22:50:39 +0200 Subject: [PATCH 0321/1335] dont send uri to challenge, if no valid token could be fetched --- lib/classes/ssl/class.lescript.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/classes/ssl/class.lescript.php b/lib/classes/ssl/class.lescript.php index a87f5bc8..6d9ca5a3 100644 --- a/lib/classes/ssl/class.lescript.php +++ b/lib/classes/ssl/class.lescript.php @@ -238,6 +238,7 @@ class lescript } @unlink($tokenPath); $this->logger->logAction(CRON_ACTION, LOG_ERR, "letsencrypt Please check $uri - token not available" . $errmsg); + continue; } $this->log("Sending request to challenge"); From 47cd30a45ecb6ec9d3779f15259f2b3edb1b722f Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Wed, 19 Jul 2017 12:06:36 +0200 Subject: [PATCH 0322/1335] use correct logging-level in customer_mail, thx to priority Signed-off-by: Michael Kaufmann (d00p) --- customer_email.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/customer_email.php b/customer_email.php index 22a4685d..1076832a 100644 --- a/customer_email.php +++ b/customer_email.php @@ -164,7 +164,7 @@ if ($page == 'overview') { Database::pexecute($stmt, array("customerid" => $userinfo['customerid'], "id" => $result['popaccountid'])); $update_users_query_addon .= " , `email_accounts_used` = `email_accounts_used` - 1 "; $number_forwarders-= 1; - $log->logAction(USR_ACTION, LOG_NOTICE, "deleted forwarder for email address '" . $result['email'] . "'"); + $log->logAction(USR_ACTION, LOG_INFO, "deleted forwarder for email address '" . $result['email'] . "'"); } } else { $number_forwarders = 0; @@ -652,7 +652,7 @@ if ($page == 'overview') { $password = validatePassword($password); - $log->logAction(USR_ACTION, LOG_NOTICE, "changed email password for '" . $result['email_full'] . "'"); + $log->logAction(USR_ACTION, LOG_INFO, "changed email password for '" . $result['email_full'] . "'"); $cryptPassword = makeCryptPassword($password); $stmt = Database::prepare("UPDATE `" . TABLE_MAIL_USERS . "` SET " . (Settings::Get('system.mailpwcleartext') == '1' ? "`password` = :password, " : '') . " @@ -699,7 +699,7 @@ if ($page == 'overview') { if ($userinfo['email_quota'] != '-1' && ($quota == 0 || ($quota + $userinfo['email_quota_used'] - $result['quota']) > $userinfo['email_quota'])) { standard_error('allocatetoomuchquota', $quota); } else { - $log->logAction(USR_ACTION, LOG_NOTICE, "updated quota for email address '" . $result['email'] . "' to " . $quota . " MB"); + $log->logAction(USR_ACTION, LOG_INFO, "updated quota for email address '" . $result['email'] . "' to " . $quota . " MB"); $stmt = Database::prepare("UPDATE `" . TABLE_MAIL_USERS . "` SET `quota` = :quota WHERE `id` = :id @@ -834,7 +834,7 @@ if ($page == 'overview') { ); Database::pexecute($stmt, array("cid" => $userinfo['customerid'])); - $log->logAction(USR_ACTION, LOG_NOTICE, "added email forwarder for '" . $result['email_full'] . "'"); + $log->logAction(USR_ACTION, LOG_INFO, "added email forwarder for '" . $result['email_full'] . "'"); redirectTo($filename, array('page' => 'emails', 'action' => 'edit', 'id' => $id, 's' => $s)); } } else { @@ -895,7 +895,7 @@ if ($page == 'overview') { ); Database::pexecute($stmt, array("cid" => $userinfo['customerid'])); - $log->logAction(USR_ACTION, LOG_NOTICE, "deleted email forwarder for '" . $result['email_full'] . "'"); + $log->logAction(USR_ACTION, LOG_INFO, "deleted email forwarder for '" . $result['email_full'] . "'"); redirectTo($filename, array('page' => 'emails', 'action' => 'edit', 'id' => $id, 's' => $s)); } else { ask_yesno('email_reallydelete_forwarder', $filename, array('id' => $id, 'forwarderid' => $forwarderid, 'page' => $page, 'action' => $action), $idna_convert->decode($result['email_full']) . ' -> ' . $idna_convert->decode($forwarder)); From 56e8e32965d19990aa494e0857ed2bf708d77c26 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Wed, 26 Jul 2017 07:30:14 +0200 Subject: [PATCH 0323/1335] set correct permissions for extrausers files/folder, fixes #465 Signed-off-by: Michael Kaufmann (d00p) --- scripts/classes/class.Extrausers.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/scripts/classes/class.Extrausers.php b/scripts/classes/class.Extrausers.php index 4b979a79..17b312af 100644 --- a/scripts/classes/class.Extrausers.php +++ b/scripts/classes/class.Extrausers.php @@ -33,6 +33,13 @@ class Extrausers $shadow = '/var/lib/extrausers/shadow'; $sql = "SELECT username,password FROM ftp_users ORDER BY gid ASC"; self::_generateFile($shadow, $sql, $cronlog); + + // set correct permissions + @chmod('/var/lib/extrausers/', 0755); + @chmod('/var/lib/extrausers/passwd', 0644); + @chmod('/var/lib/extrausers/group', 0644); + @chmod('/var/lib/extrausers/shadow', 0640); + } private static function _generateFile($file, $query, &$cronlog) From c2c517883196ae2ddc5eb5ae5d60fcb9717eda9c Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Wed, 23 Aug 2017 14:03:39 +0200 Subject: [PATCH 0324/1335] remove superfluous character in froxlor.sql file, fixes #471 Signed-off-by: Michael Kaufmann (d00p) --- install/froxlor.sql | 2 +- templates/misc/standardcustomer/index.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/install/froxlor.sql b/install/froxlor.sql index fbdabcd5..d3069621 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -771,7 +771,7 @@ CREATE TABLE `panel_phpconfigs` ( INSERT INTO `panel_phpconfigs` (`id`, `description`, `binary`, `file_extensions`, `mod_fcgid_starter`, `mod_fcgid_maxrequests`, `phpsettings`) VALUES (1, 'Default Config', '/usr/bin/php-cgi', 'php', '-1', '-1', 'allow_call_time_pass_reference = Off\r\nallow_url_fopen = Off\r\nasp_tags = Off\r\ndisable_classes =\r\ndisable_functions = curl_exec,curl_multi_exec,exec,parse_ini_file,passthru,popen,proc_close,proc_get_status,proc_nice,proc_open,proc_terminate,shell_exec,show_source,system\r\ndisplay_errors = Off\r\ndisplay_startup_errors = Off\r\nenable_dl = Off\r\nerror_reporting = E_ALL & ~E_NOTICE\r\nexpose_php = Off\r\nfile_uploads = On\r\ncgi.force_redirect = 1\r\ngpc_order = "GPC"\r\nhtml_errors = Off\r\nignore_repeated_errors = Off\r\nignore_repeated_source = Off\r\ninclude_path = ".:{PEAR_DIR}"\r\nlog_errors = On\r\nlog_errors_max_len = 1024\r\nmagic_quotes_gpc = Off\r\nmagic_quotes_runtime = Off\r\nmagic_quotes_sybase = Off\r\nmax_execution_time = 30\r\nmax_input_time = 60\r\nmemory_limit = 128M\r\n{OPEN_BASEDIR_C}open_basedir = "{OPEN_BASEDIR}"\r\noutput_buffering = 4096\r\npost_max_size = 16M\r\nprecision = 14\r\nregister_argc_argv = Off\r\nregister_globals = Off\r\nreport_memleaks = On\r\nsendmail_path = "/usr/sbin/sendmail -t -i -f {CUSTOMER_EMAIL}"\r\nsession.auto_start = 0\r\nsession.bug_compat_42 = 0\r\nsession.bug_compat_warn = 1\r\nsession.cache_expire = 180\r\nsession.cache_limiter = nocache\r\nsession.cookie_domain =\r\nsession.cookie_lifetime = 0\r\nsession.cookie_path = /\r\nsession.entropy_file = /dev/urandom\r\nsession.entropy_length = 16\r\nsession.gc_divisor = 1000\r\nsession.gc_maxlifetime = 1440\r\nsession.gc_probability = 1\r\nsession.name = PHPSESSID\r\nsession.referer_check =\r\nsession.save_handler = files\r\nsession.save_path = "{TMP_DIR}"\r\nsession.serialize_handler = php\r\nsession.use_cookies = 1\r\nsession.use_trans_sid = 0\r\nshort_open_tag = On\r\nsuhosin.mail.protect = 1\r\nsuhosin.simulation = Off\r\ntrack_errors = Off\r\nupload_max_filesize = 32M\r\nupload_tmp_dir = "{TMP_DIR}"\r\nvariables_order = "GPCS"\r\n;mail.add_x_header = On\r\n;mail.log = "/var/log/phpmail.log"\r\nopcache.restrict_api = "{DOCUMENT_ROOT}"\r\n'), -(2, 'Froxlor Vhost Config', '/usr/bin/php-cgi', 'php', '-1', '-1', 'allow_call_time_pass_reference = Off\r\nallow_url_fopen = On\r\nasp_tags = Off\r\ndisable_classes =\r\ndisable_functions = curl_multi_exec,exec,parse_ini_file,passthru,popen,proc_close,proc_get_status,proc_nice,proc_open,proc_terminate,shell_exec,show_source,system\r\ndisplay_errors = Off\r\ndisplay_startup_errors = Off\r\nenable_dl = Off\r\nerror_reporting = E_ALL & ~E_NOTICE\r\nexpose_php = Off\r\nfile_uploads = On\r\ncgi.force_redirect = 1\r\ngpc_order = "GPC"\r\nhtml_errors = Off\r\nignore_repeated_errors = Off\r\nignore_repeated_source = Off\r\ninclude_path = ".:{PEAR_DIR}"\r\nlog_errors = On\r\nlog_errors_max_len = 1024\r\nmagic_quotes_gpc = Off\r\nmagic_quotes_runtime = Off\r\nmagic_quotes_sybase = Off\r\nmax_execution_time = 60\r\nmax_input_time = 60\r\nmemory_limit = 128M\r\nnoutput_buffering = 4096\r\npost_max_size = 16M\r\nprecision = 14\r\nregister_argc_argv = Off\r\nregister_globals = Off\r\nreport_memleaks = On\r\nsendmail_path = "/usr/sbin/sendmail -t -i -f {CUSTOMER_EMAIL}"\r\nsession.auto_start = 0\r\nsession.bug_compat_42 = 0\r\nsession.bug_compat_warn = 1\r\nsession.cache_expire = 180\r\nsession.cache_limiter = nocache\r\nsession.cookie_domain =\r\nsession.cookie_lifetime = 0\r\nsession.cookie_path = /\r\nsession.entropy_file = /dev/urandom\r\nsession.entropy_length = 16\r\nsession.gc_divisor = 1000\r\nsession.gc_maxlifetime = 1440\r\nsession.gc_probability = 1\r\nsession.name = PHPSESSID\r\nsession.referer_check =\r\nsession.save_handler = files\r\nsession.save_path = "{TMP_DIR}"\r\nsession.serialize_handler = php\r\nsession.use_cookies = 1\r\nsession.use_trans_sid = 0\r\nshort_open_tag = On\r\nsuhosin.mail.protect = 1\r\nsuhosin.simulation = Off\r\ntrack_errors = Off\r\nupload_max_filesize = 32M\r\nupload_tmp_dir = "{TMP_DIR}"\r\nvariables_order = "GPCS"\r\n;mail.add_x_header = On\r\n;mail.log = "/var/log/phpmail.log"\r\nopcache.restrict_api = ""\r\n'); +(2, 'Froxlor Vhost Config', '/usr/bin/php-cgi', 'php', '-1', '-1', 'allow_call_time_pass_reference = Off\r\nallow_url_fopen = On\r\nasp_tags = Off\r\ndisable_classes =\r\ndisable_functions = curl_multi_exec,exec,parse_ini_file,passthru,popen,proc_close,proc_get_status,proc_nice,proc_open,proc_terminate,shell_exec,show_source,system\r\ndisplay_errors = Off\r\ndisplay_startup_errors = Off\r\nenable_dl = Off\r\nerror_reporting = E_ALL & ~E_NOTICE\r\nexpose_php = Off\r\nfile_uploads = On\r\ncgi.force_redirect = 1\r\ngpc_order = "GPC"\r\nhtml_errors = Off\r\nignore_repeated_errors = Off\r\nignore_repeated_source = Off\r\ninclude_path = ".:{PEAR_DIR}"\r\nlog_errors = On\r\nlog_errors_max_len = 1024\r\nmagic_quotes_gpc = Off\r\nmagic_quotes_runtime = Off\r\nmagic_quotes_sybase = Off\r\nmax_execution_time = 60\r\nmax_input_time = 60\r\nmemory_limit = 128M\r\noutput_buffering = 4096\r\npost_max_size = 16M\r\nprecision = 14\r\nregister_argc_argv = Off\r\nregister_globals = Off\r\nreport_memleaks = On\r\nsendmail_path = "/usr/sbin/sendmail -t -i -f {CUSTOMER_EMAIL}"\r\nsession.auto_start = 0\r\nsession.bug_compat_42 = 0\r\nsession.bug_compat_warn = 1\r\nsession.cache_expire = 180\r\nsession.cache_limiter = nocache\r\nsession.cookie_domain =\r\nsession.cookie_lifetime = 0\r\nsession.cookie_path = /\r\nsession.entropy_file = /dev/urandom\r\nsession.entropy_length = 16\r\nsession.gc_divisor = 1000\r\nsession.gc_maxlifetime = 1440\r\nsession.gc_probability = 1\r\nsession.name = PHPSESSID\r\nsession.referer_check =\r\nsession.save_handler = files\r\nsession.save_path = "{TMP_DIR}"\r\nsession.serialize_handler = php\r\nsession.use_cookies = 1\r\nsession.use_trans_sid = 0\r\nshort_open_tag = On\r\nsuhosin.mail.protect = 1\r\nsuhosin.simulation = Off\r\ntrack_errors = Off\r\nupload_max_filesize = 32M\r\nupload_tmp_dir = "{TMP_DIR}"\r\nvariables_order = "GPCS"\r\n;mail.add_x_header = On\r\n;mail.log = "/var/log/phpmail.log"\r\nopcache.restrict_api = ""\r\n'); DROP TABLE IF EXISTS `cronjobs_run`; diff --git a/templates/misc/standardcustomer/index.html b/templates/misc/standardcustomer/index.html index dbba3f63..de82cf3b 100644 --- a/templates/misc/standardcustomer/index.html +++ b/templates/misc/standardcustomer/index.html @@ -57,7 +57,7 @@ From bab982a0e6d13f3a6671fb578a489f28f0615119 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 24 Aug 2017 13:21:21 +0200 Subject: [PATCH 0325/1335] add http2 support for froxlor-vhost and per-domain Signed-off-by: Michael Kaufmann (d00p) --- actions/admin/settings/122.froxlorvhost.php | 13 ++++++++++++ actions/admin/settings/130.webserver.php | 11 ---------- admin_domains.php | 21 ++++++++++++++++--- install/froxlor.sql | 5 +++-- .../updates/froxlor/0.9/update_0.9.inc.php | 16 ++++++++++++++ .../admin/domains/formfield.domains_add.php | 13 ++++++++++++ .../admin/domains/formfield.domains_edit.php | 15 +++++++++++++ lib/version.inc.php | 2 +- lng/english.lng.php | 6 ++++-- lng/german.lng.php | 6 ++++-- .../jobs/cron_tasks.inc.http.10.apache.php | 6 ++++++ scripts/jobs/cron_tasks.inc.http.30.nginx.php | 4 ++-- 12 files changed, 95 insertions(+), 23 deletions(-) diff --git a/actions/admin/settings/122.froxlorvhost.php b/actions/admin/settings/122.froxlorvhost.php index 6175bb73..5ddbfc30 100644 --- a/actions/admin/settings/122.froxlorvhost.php +++ b/actions/admin/settings/122.froxlorvhost.php @@ -80,6 +80,19 @@ return array( 'save_method' => 'storeSettingField', 'visible' => Settings::Get('system.use_ssl') ), + 'system_http2_support' => array( + 'label' => $lng['serversettings']['http2_support'], + 'settinggroup' => 'system', + 'varname' => 'http2_support', + 'type' => 'bool', + 'default' => false, + 'save_method' => 'storeSettingField', + 'websrv_avail' => array( + 'apache2', + 'nginx' + ), + 'visible' => Settings::Get('system.use_ssl') + ), /** * FCGID */ diff --git a/actions/admin/settings/130.webserver.php b/actions/admin/settings/130.webserver.php index 8a117d5c..4374b148 100644 --- a/actions/admin/settings/130.webserver.php +++ b/actions/admin/settings/130.webserver.php @@ -179,17 +179,6 @@ return array( 'nginx' ) ), - 'system_nginx_http2_support' => array( - 'label' => $lng['serversettings']['nginx_http2_support'], - 'settinggroup' => 'system', - 'varname' => 'nginx_http2_support', - 'type' => 'bool', - 'default' => false, - 'save_method' => 'storeSettingField', - 'websrv_avail' => array( - 'nginx' - ) - ), 'system_nginx_php_backend' => array( 'label' => $lng['serversettings']['nginx_php_backend'], 'settinggroup' => 'system', diff --git a/admin_domains.php b/admin_domains.php index 5b2b31a5..7c05f08a 100644 --- a/admin_domains.php +++ b/admin_domains.php @@ -590,6 +590,8 @@ if ($page == 'domains' || $page == 'overview') { } } + $http2 = isset($_POST['http2']) && (int)$_POST['http2'] == 1 ? 1 : 0; + // HSTS $hsts_maxage = isset($_POST['hsts_maxage']) ? (int)$_POST['hsts_maxage'] : 0; $hsts_sub = isset($_POST['hsts_sub']) && (int)$_POST['hsts_sub'] == 1 ? 1 : 0; @@ -601,6 +603,7 @@ if ($page == 'domains' || $page == 'overview') { } else { $ssl_redirect = 0; $letsencrypt = 0; + $http2 = 0; // we need this for the serialize // if ssl is disabled or no ssl-ip/port exists $ssl_ipandports[] = - 1; @@ -616,6 +619,7 @@ if ($page == 'domains' || $page == 'overview') { } else { $ssl_redirect = 0; $letsencrypt = 0; + $http2 = 0; // we need this for the serialize // if ssl is disabled or no ssl-ip/port exists $ssl_ipandports[] = - 1; @@ -796,10 +800,11 @@ if ($page == 'domains' || $page == 'overview') { 'termination_date' => $termination_date, 'issubof' => $issubof, 'letsencrypt' => $letsencrypt, + 'http2' => $http2, 'hsts_maxage' => $hsts_maxage, 'hsts_sub' => $hsts_sub, 'hsts_preload' => $hsts_preload, - 'ocsp_stapling' => $ocsp_stapling, + 'ocsp_stapling' => $ocsp_stapling ); $security_questions = array( @@ -849,10 +854,11 @@ if ($page == 'domains' || $page == 'overview') { 'mod_fcgid_maxrequests' => $mod_fcgid_maxrequests, 'ismainbutsubto' => $issubof, 'letsencrypt' => $letsencrypt, + 'http2' => $http2, 'hsts' => $hsts_maxage, 'hsts_sub' => $hsts_sub, 'hsts_preload' => $hsts_preload, - 'ocsp_stapling' => $ocsp_stapling, + 'ocsp_stapling' => $ocsp_stapling ); $ins_stmt = Database::prepare(" @@ -887,6 +893,7 @@ if ($page == 'domains' || $page == 'overview') { `mod_fcgid_maxrequests` = :mod_fcgid_maxrequests, `ismainbutsubto` = :ismainbutsubto, `letsencrypt` = :letsencrypt, + `http2` = :http2, `hsts` = :hsts, `hsts_sub` = :hsts_sub, `hsts_preload` = :hsts_preload, @@ -1428,6 +1435,8 @@ if ($page == 'domains' || $page == 'overview') { $letsencrypt = (int) $_POST['letsencrypt']; } + $http2 = isset($_POST['http2']) && (int)$_POST['http2'] == 1 ? 1 : 0; + // HSTS $hsts_maxage = isset($_POST['hsts_maxage']) ? (int)$_POST['hsts_maxage'] : 0; $hsts_sub = isset($_POST['hsts_sub']) && (int)$_POST['hsts_sub'] == 1 ? 1 : 0; @@ -1465,6 +1474,7 @@ if ($page == 'domains' || $page == 'overview') { } else { $ssl_redirect = 0; $letsencrypt = 0; + $http2 = 0; // we need this for the serialize // if ssl is disabled or no ssl-ip/port exists $ssl_ipandports[] = - 1; @@ -1480,6 +1490,7 @@ if ($page == 'domains' || $page == 'overview') { } else { $ssl_redirect = 0; $letsencrypt = 0; + $http2 = 0; // we need this for the serialize // if ssl is disabled or no ssl-ip/port exists $ssl_ipandports[] = - 1; @@ -1634,10 +1645,11 @@ if ($page == 'domains' || $page == 'overview') { 'ipandport' => serialize($ipandports), 'ssl_ipandport' => serialize($ssl_ipandports), 'letsencrypt' => $letsencrypt, + 'http2' => $http2, 'hsts_maxage' => $hsts_maxage, 'hsts_sub' => $hsts_sub, 'hsts_preload' => $hsts_preload, - 'ocsp_stapling' => $ocsp_stapling, + 'ocsp_stapling' => $ocsp_stapling ); $security_questions = array( @@ -1672,6 +1684,7 @@ if ($page == 'domains' || $page == 'overview') { $email_only != $result['email_only'] || ($speciallogfile != $result['speciallogfile'] && $speciallogverified == '1') || $letsencrypt != $result['letsencrypt'] || + $http2 != $result['http2'] || $hsts_maxage != $result['hsts'] || $hsts_sub != $result['hsts_sub'] || $hsts_preload != $result['hsts_preload'] || @@ -1828,6 +1841,7 @@ if ($page == 'domains' || $page == 'overview') { $update_data['termination_date'] = $termination_date; $update_data['ismainbutsubto'] = $issubof; $update_data['letsencrypt'] = $letsencrypt; + $update_data['http2'] = $http2; $update_data['hsts'] = $hsts_maxage; $update_data['hsts_sub'] = $hsts_sub; $update_data['hsts_preload'] = $hsts_preload; @@ -1861,6 +1875,7 @@ if ($page == 'domains' || $page == 'overview') { `termination_date` = :termination_date, `ismainbutsubto` = :ismainbutsubto, `letsencrypt` = :letsencrypt, + `http2` = :http2, `hsts` = :hsts, `hsts_sub` = :hsts_sub, `hsts_preload` = :hsts_preload, diff --git a/install/froxlor.sql b/install/froxlor.sql index d3069621..f1b896da 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -258,6 +258,7 @@ CREATE TABLE `panel_domains` ( `hsts_sub` tinyint(1) NOT NULL default '0', `hsts_preload` tinyint(1) NOT NULL default '0', `ocsp_stapling` tinyint(1) DEFAULT '0', + `http2` tinyint(1) DEFAULT '0', PRIMARY KEY (`id`), KEY `customerid` (`customerid`), KEY `parentdomain` (`parentdomainid`), @@ -500,7 +501,7 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES ('system', 'ssl_cert_chainfile', ''), ('system', 'ssl_cipher_list', 'ECDH+AESGCM:ECDH+AES256:!aNULL:!MD5:!DSS:!DH:!AES128'), ('system', 'nginx_php_backend', '127.0.0.1:8888'), - ('system', 'nginx_http2_support', '0'), + ('system', 'http2_support', '0'), ('system', 'perl_server', 'unix:/var/run/nginx/cgiwrap-dispatch.sock'), ('system', 'phpreload_command', ''), ('system', 'apache24', '0'), @@ -585,7 +586,7 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES ('panel', 'password_special_char', '!?<>§$%+#=@'), ('panel', 'customer_hide_options', ''), ('panel', 'version', '0.9.38.7'), - ('panel', 'db_version', '201705050'); + ('panel', 'db_version', '201708240'); DROP TABLE IF EXISTS `panel_tasks`; diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index 1701f8be..b8d6f495 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -3617,3 +3617,19 @@ if (isDatabaseVersion('201704100')) { updateToDbVersion('201705050'); } + +if (isDatabaseVersion('201705050')) { + + showUpdateStep("Updating HTTP2 setting"); + if (Settings::Get('system.nginx_http2_support') != null) { + Database::query("UPDATE `" . TABLE_PANEL_SETTINGS . "` SET `varname` = 'http2_support' WHERE `varname` = 'nginx_http2_support';"); + } else { + Settings::AddNew('system.http2_support', 0); + } + lastStepStatus(0); + showUpdateStep("Adding domain field for HTTP2 stapling"); + Database::query("ALTER TABLE `" . TABLE_PANEL_DOMAINS . "` ADD `http2` TINYINT(1) NOT NULL DEFAULT '0';"); + lastStepStatus(0); + + updateToDbVersion('201708240'); +} diff --git a/lib/formfields/admin/domains/formfield.domains_add.php b/lib/formfields/admin/domains/formfield.domains_add.php index 988c496f..1976b034 100644 --- a/lib/formfields/admin/domains/formfield.domains_add.php +++ b/lib/formfields/admin/domains/formfield.domains_add.php @@ -173,6 +173,19 @@ return array( ), 'value' => array() ), + 'http2' => array( + 'visible' => ($ssl_ipsandports != '' ? true : false) && Settings::Get('system.webserver') != 'lighttpd', + 'label' => $lng['admin']['domain_http2']['title'], + 'desc' => $lng['admin']['domain_http2']['description'], + 'type' => 'checkbox', + 'values' => array( + array ( + 'label' => $lng['panel']['yes'], + 'value' => '1' + ) + ), + 'value' => array() + ), 'no_ssl_available_info' => array( 'visible' => ($ssl_ipsandports == '' ? true : false), 'label' => 'SSL', diff --git a/lib/formfields/admin/domains/formfield.domains_edit.php b/lib/formfields/admin/domains/formfield.domains_edit.php index e351c8da..d668aa9d 100644 --- a/lib/formfields/admin/domains/formfield.domains_edit.php +++ b/lib/formfields/admin/domains/formfield.domains_edit.php @@ -207,6 +207,21 @@ return array( $result['letsencrypt'] ) ), + 'http2' => array( + 'visible' => ($ssl_ipsandports != '' ? true : false) && Settings::Get('system.webserver') != 'lighttpd', + 'label' => $lng['admin']['domain_http2']['title'], + 'desc' => $lng['admin']['domain_http2']['description'], + 'type' => 'checkbox', + 'values' => array( + array ( + 'label' => $lng['panel']['yes'], + 'value' => '1' + ) + ), + 'value' => array( + $result['http2'] + ) + ), 'no_ssl_available_info' => array( 'visible' => ($ssl_ipsandports == '' ? true : false), 'label' => 'SSL', diff --git a/lib/version.inc.php b/lib/version.inc.php index 7d67b9da..187d99b3 100644 --- a/lib/version.inc.php +++ b/lib/version.inc.php @@ -19,7 +19,7 @@ $version = '0.9.38.7'; // Database version (YYYYMMDDC where C is a daily counter) -$dbversion = '201705050'; +$dbversion = '201708240'; // Distribution branding-tag (used for Debian etc.) $branding = ''; diff --git a/lng/english.lng.php b/lng/english.lng.php index bfd981f8..1fb24970 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -2063,8 +2063,8 @@ $lng['admin']['domain_hsts_incsub']['description'] = 'The optional "includeSubDo $lng['admin']['domain_hsts_preload']['title'] = 'Include domain in HSTS preload list'; $lng['admin']['domain_hsts_preload']['description'] = 'If you would like this domain to be included in the HSTS preload list maintained by Chrome (and used by Firefox and Safari), then use activate this.
Sending the preload directive from your site can have PERMANENT CONSEQUENCES and prevent users from accessing your site and any of its subdomains.
Please read the details at hstspreload.appspot.com/#removal before sending the header with "preload".'; -$lng['serversettings']['nginx_http2_support']['title'] = 'Nginx HTTP2 Support'; -$lng['serversettings']['nginx_http2_support']['description'] = 'enable http2 support for ssl. ENABLE ONLY IF YOUR Nginx SUPPORT THIS FEATURE. (version 1.9.5+)'; +$lng['serversettings']['http2_support']['title'] = 'HTTP2 Support'; +$lng['serversettings']['http2_support']['description'] = 'enable HTTP2 support for ssl.
ENABLE ONLY IF YOUR WEBSERVER SUPPORTS THIS FEATURE (nginx version 1.9.5+, apache2 version 2.4.17+)'; $lng['error']['noipportgiven'] = 'No IP/port given'; @@ -2076,3 +2076,5 @@ $lng['serversettings']['ssl']['apache24_ocsp_cache_path']['title'] = 'Apache 2.4 $lng['serversettings']['ssl']['apache24_ocsp_cache_path']['description'] = 'Configures the cache used to store OCSP responses which get included in TLS handshakes.'; $lng['serversettings']['nssextrausers']['title'] = 'Use libnss-extrausers instead of libnss-mysql'; $lng['serversettings']['nssextrausers']['description'] = 'Do not read users from the database but from files. Please only activate if you have already gone through the required configuration steps (system -> libnss-extrausers).
For Debian/Ubuntu only (or if you have compiled libnss-extrausers yourself!)'; +$lng['admin']['domain_http2']['title'] = 'HTTP2 support'; +$lng['admin']['domain_http2']['description'] = 'See Wikipedia for a detailed explanation of HTTP2'; diff --git a/lng/german.lng.php b/lng/german.lng.php index 761fdac5..df6f0bf6 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -1714,8 +1714,8 @@ $lng['admin']['domain_hsts_incsub']['description'] = 'Die optionale "includeSubD $lng['admin']['domain_hsts_preload']['title'] = 'Füge Domain in die HSTS preload Liste hinzu'; $lng['admin']['domain_hsts_preload']['description'] = 'Wenn die Domain in die HSTS preload Liste, verwaltet von Chrome (und genutzt von Firefox und Safari), hinzugefügt werden soll, dann aktiviere diese Einstellung.
Die preload-Direktive zu senden kann PERMANTENTE KONSEQUENZEN haben und dazu führen, dass Benutzer auf diese Domain und auch Subdomains nicht zugreifen können.
Beachte Details unter hstspreload.appspot.com/#removal bevor ein Header mit "preload" gesendet wird.'; -$lng['serversettings']['nginx_http2_support']['title'] = 'Nginx HTTP2 Unterstützung'; -$lng['serversettings']['nginx_http2_support']['description'] = 'Aktiviere http2 Unterstützung für SSL. NUR AKTIVIEREN, WENN nginx DIESE FUNKTION UNTERSTÜTZT (version 1.9.5+)'; +$lng['serversettings']['http2_support']['title'] = 'HTTP2 Unterstützung'; +$lng['serversettings']['http2_support']['description'] = 'Aktiviere HTTP2 Unterstützung für SSL.
NUR AKTIVIEREN, WENN DER WEBSERVER DIESE FUNKTION UNTERSTÜTZT (nginx version 1.9.5+, apache2 version 2.4.17+)'; $lng['error']['noipportgiven'] = 'Keine IP/Port angegeben'; @@ -1727,3 +1727,5 @@ $lng['serversettings']['ssl']['apache24_ocsp_cache_path']['title'] = 'Apache 2.4 $lng['serversettings']['ssl']['apache24_ocsp_cache_path']['description'] = 'Konfiguriert den Cache-Pfad zum Zwischenspeichern der OCSP-Antworten,
die an TLS-Handshakes angehängt werden.'; $lng['serversettings']['nssextrausers']['title'] = 'Verwende libnss-extrausers anstatt libnss-mysql'; $lng['serversettings']['nssextrausers']['description'] = 'Lese Benutzer nicht direkt aus der Datenbank sondern über Dateien, bitte nur aktivieren, wenn die entsprechende Konfiguration vorgenommen wurde (System -> libnss-extrausers).
Nur für Debian/Ubuntu (oder wenn libnss-extrausers manuell kompiliert wurde!)'; +$lng['admin']['domain_http2']['title'] = 'HTTP2 Unterstützung'; +$lng['admin']['domain_http2']['description'] = 'Siehe Wikipedia für eine ausführliche Beschreibung von HTTP2'; diff --git a/scripts/jobs/cron_tasks.inc.http.10.apache.php b/scripts/jobs/cron_tasks.inc.http.10.apache.php index 8440d005..98d1ed17 100644 --- a/scripts/jobs/cron_tasks.inc.http.10.apache.php +++ b/scripts/jobs/cron_tasks.inc.http.10.apache.php @@ -434,6 +434,9 @@ class apache extends HttpConfigBase $this->virtualhosts_data[$vhosts_filename] .= ' SSLEngine On' . "\n"; $this->virtualhosts_data[$vhosts_filename] .= ' SSLProtocol -ALL +TLSv1 +TLSv1.2' . "\n"; if (Settings::Get('system.apache24') == '1') { + if (Settings::Get('system.http2_support') == '1') { + $this->virtualhosts_data[$vhosts_filename] .= ' Protocols h2 http/1.1' . "\n"; + } $this->virtualhosts_data[$vhosts_filename] .= ' SSLCompression Off' . "\n"; } // this makes it more secure, thx to Marcel (08/2013) @@ -869,6 +872,9 @@ class apache extends HttpConfigBase $vhost_content .= ' SSLEngine On' . "\n"; $vhost_content .= ' SSLProtocol -ALL +TLSv1 +TLSv1.2' . "\n"; if (Settings::Get('system.apache24') == '1') { + if (isset($domain['http2']) && $domain['http2'] == '1') { + $vhost_content .= ' Protocols h2 http/1.1' . "\n"; + } $vhost_content .= ' SSLCompression Off' . "\n"; } // this makes it more secure, thx to Marcel (08/2013) diff --git a/scripts/jobs/cron_tasks.inc.http.30.nginx.php b/scripts/jobs/cron_tasks.inc.http.30.nginx.php index 20d3dbc8..bc7f23fc 100644 --- a/scripts/jobs/cron_tasks.inc.http.30.nginx.php +++ b/scripts/jobs/cron_tasks.inc.http.30.nginx.php @@ -196,7 +196,7 @@ class nginx extends HttpConfigBase } } - $http2 = $ssl_vhost == true && Settings::Get('system.nginx_http2_support') == '1'; + $http2 = $ssl_vhost == true && Settings::Get('system.http2_support') == '1'; /** * this HAS to be set for the default host in nginx or else no vhost will work @@ -418,7 +418,7 @@ class nginx extends HttpConfigBase $_vhost_content .= $this->processSpecialConfigTemplate($ipandport['default_vhostconf_domain'], $domain, $domain['ip'], $domain['port'], $ssl_vhost) . "\n"; } - $http2 = $ssl_vhost == true && Settings::Get('system.nginx_http2_support') == '1'; + $http2 = $ssl_vhost == true && (isset($domain['http2']) && $domain['http2'] == '1'); $vhost_content .= "\t" . 'listen ' . $ipport . ($ssl_vhost == true ? ' ssl' : '') . ($http2 == true ? ' http2' : '') . ';' . "\n"; } From 64ebb0ca3826a3f5afd82b32318ba7d663aaa777 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 24 Aug 2017 15:27:47 +0200 Subject: [PATCH 0326/1335] also add http2 flag to domain-import Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/bulk/class.DomainBulkAction.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/classes/bulk/class.DomainBulkAction.php b/lib/classes/bulk/class.DomainBulkAction.php index a5dac417..0c0a6b09 100644 --- a/lib/classes/bulk/class.DomainBulkAction.php +++ b/lib/classes/bulk/class.DomainBulkAction.php @@ -99,6 +99,7 @@ class DomainBulkAction /* 22 */ 'hsts_preload', /* 23 */ 'ocsp_stapling', /* 24 */ 'phpenabled', +/* 25 */ 'http2', /* automatically added */ 'adminid', 'customerid', @@ -212,7 +213,8 @@ class DomainBulkAction `hsts_sub` = :hsts_sub, `hsts_preload` = :hsts_preload, `ocsp_stapling` = :ocsp_stapling, - `phpenabled` = :phpenabled + `phpenabled` = :phpenabled, + `http2` = :http2 "); // prepare insert statement for ip/port <> domain @@ -371,6 +373,14 @@ class DomainBulkAction $domain_data['ocsp_stapling'] = 0; } + if ($domain_data['phpenabled'] != 1) { + $domain_data['phpenabled'] = 0; + } + + if ($domain_data['http2'] != 1) { + $domain_data['http2'] = 0; + } + // add to known domains $this->_knownDomains[] = $domain_data['domain']; From 1753d2895b833a3a84269975144f19bc85b33191 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Fri, 25 Aug 2017 10:34:59 +0200 Subject: [PATCH 0327/1335] add simple smtp-settings test-page, fixes #464 Signed-off-by: Michael Kaufmann (d00p) --- admin_settings.php | 65 +++++++++++++++++++ lib/navigation/00.froxlor.main.php | 4 ++ lng/english.lng.php | 2 + lng/german.lng.php | 2 + templates/Sparkle/admin/settings/testmail.tpl | 48 ++++++++++++++ 5 files changed, 121 insertions(+) create mode 100644 templates/Sparkle/admin/settings/testmail.tpl diff --git a/admin_settings.php b/admin_settings.php index 18289d28..66abf4aa 100644 --- a/admin_settings.php +++ b/admin_settings.php @@ -290,3 +290,68 @@ if ($page == 'overview' && $userinfo['change_serversettings'] == '1') { } eval("echo \"" . getTemplate("settings/integritycheck") . "\";"); } +elseif ($page == 'testmail') +{ + if (isset($_POST['send']) && $_POST['send'] == 'send') + { + $test_addr = isset($_POST['test_addr']) ? $_POST['test_addr'] : null; + + /** + * Initialize the mailingsystem + */ + $testmail = new PHPMailer(true); + $testmail->CharSet = "UTF-8"; + + if (Settings::Get('system.mail_use_smtp')) { + $testmail->isSMTP(); + $testmail->Host = Settings::Get('system.mail_smtp_host'); + $testmail->SMTPAuth = Settings::Get('system.mail_smtp_auth') == '1' ? true : false; + $testmail->Username = Settings::Get('system.mail_smtp_user'); + $testmail->Password = Settings::Get('system.mail_smtp_passwd'); + if (Settings::Get('system.mail_smtp_usetls')) { + $testmail->SMTPSecure = 'tls'; + } + $testmail->Port = Settings::Get('system.mail_smtp_port'); + } + + $_mailerror = false; + if (PHPMailer::ValidateAddress(Settings::Get('panel.adminmail')) !== false) { + // set return-to address and custom sender-name, see #76 + $testmail->SetFrom(Settings::Get('panel.adminmail'), Settings::Get('panel.adminmail_defname')); + if (Settings::Get('panel.adminmail_return') != '') { + $testmail->AddReplyTo(Settings::Get('panel.adminmail_return'), Settings::Get('panel.adminmail_defname')); + } + + try { + $testmail->Subject = "Froxlor Test-Mail"; + $mail_body = "Yay, this worked :)"; + $testmail->AltBody = $mail_body; + $testmail->MsgHTML(str_replace("\n", "
", $mail_body)); + $testmail->AddAddress($test_addr); + $testmail->Send(); + } catch(phpmailerException $e) { + $mailerr_msg = $e->errorMessage(); + $_mailerror = true; + } catch (Exception $e) { + $mailerr_msg = $e->getMessage(); + $_mailerror = true; + } + + if (!$_mailerror) { + // success + $mail->ClearAddresses(); + standard_success('testmailsent', '', array('filename' => 'admin_settings.php', 'page' => 'testmail')); + } + } else { + // invalid sender e-mail + $mailerr_msg = "Invalid sender e-mail address: ".Settings::Get('panel.adminmail'); + $_mailerror = true; + } + } + + $mail_smtp_user = Settings::Get('system.mail_smtp_user'); + $mail_smtp_host = Settings::Get('system.mail_smtp_host'); + $mail_smtp_port = Settings::Get('system.mail_smtp_port'); + + eval("echo \"" . getTemplate("settings/testmail") . "\";"); +} diff --git a/lib/navigation/00.froxlor.main.php b/lib/navigation/00.froxlor.main.php index faa3feec..18dc74ab 100644 --- a/lib/navigation/00.froxlor.main.php +++ b/lib/navigation/00.froxlor.main.php @@ -313,6 +313,10 @@ return array( array( 'url' => 'admin_message.php?page=message', 'label' => $lng['admin']['message'] + ), + array( + 'url' => 'admin_settings.php?page=testmail', + 'label' => $lng['admin']['testmail'] ) ) ) diff --git a/lng/english.lng.php b/lng/english.lng.php index 1fb24970..51e05ef6 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -2078,3 +2078,5 @@ $lng['serversettings']['nssextrausers']['title'] = 'Use libnss-extrausers instea $lng['serversettings']['nssextrausers']['description'] = 'Do not read users from the database but from files. Please only activate if you have already gone through the required configuration steps (system -> libnss-extrausers).
For Debian/Ubuntu only (or if you have compiled libnss-extrausers yourself!)'; $lng['admin']['domain_http2']['title'] = 'HTTP2 support'; $lng['admin']['domain_http2']['description'] = 'See Wikipedia for a detailed explanation of HTTP2'; +$lng['admin']['testmail'] = 'SMTP test'; +$lng['success']['testmailsent'] = 'Test mail sent successfully'; diff --git a/lng/german.lng.php b/lng/german.lng.php index df6f0bf6..22e73ca8 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -1729,3 +1729,5 @@ $lng['serversettings']['nssextrausers']['title'] = 'Verwende libnss-extrausers a $lng['serversettings']['nssextrausers']['description'] = 'Lese Benutzer nicht direkt aus der Datenbank sondern über Dateien, bitte nur aktivieren, wenn die entsprechende Konfiguration vorgenommen wurde (System -> libnss-extrausers).
Nur für Debian/Ubuntu (oder wenn libnss-extrausers manuell kompiliert wurde!)'; $lng['admin']['domain_http2']['title'] = 'HTTP2 Unterstützung'; $lng['admin']['domain_http2']['description'] = 'Siehe Wikipedia für eine ausführliche Beschreibung von HTTP2'; +$lng['admin']['testmail'] = 'SMTP Test'; +$lng['success']['testmailsent'] = 'Test E-Mail erfolgreich gesendet'; diff --git a/templates/Sparkle/admin/settings/testmail.tpl b/templates/Sparkle/admin/settings/testmail.tpl new file mode 100644 index 00000000..6b882563 --- /dev/null +++ b/templates/Sparkle/admin/settings/testmail.tpl @@ -0,0 +1,48 @@ +$header +
+
+

+   + {$lng['admin']['testmail']} +

+
+ + +
+
+
{$lng['admin']['warning']}
+
{$mailerr_msg}
+
+
+
+ +
+ + + + + + + + + + + + + + + +
{$lng['serversettings']['mail_smtp_user']}HostPortSMTP AuthTLS
{$mail_smtp_user}{$mail_smtp_host}{$mail_smtp_port}
+
+
+ + + + + + + +
+
+
+$footer From b263b211a5417b1e8e509a7fb36099867465e41a Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 28 Aug 2017 15:35:07 +0200 Subject: [PATCH 0328/1335] generate multiline txt-record if content is too long, fixes #472 Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/dns/class.DnsEntry.php | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/lib/classes/dns/class.DnsEntry.php b/lib/classes/dns/class.DnsEntry.php index c5bcab79..27f0dd8f 100644 --- a/lib/classes/dns/class.DnsEntry.php +++ b/lib/classes/dns/class.DnsEntry.php @@ -41,7 +41,31 @@ class DnsEntry public function __toString() { - $result = $this->record . "\t" . $this->ttl . "\t" . $this->class . "\t" . $this->type . "\t" . (($this->priority >= 0 && ($this->type == 'MX' || $this->type == 'SRV')) ? $this->priority . "\t" : "") . $this->content . PHP_EOL; + $_content = $this->content; + // check content length for txt records for bind9 (multiline) + if (Settings::Get('system.dns_server') != 'pdns' && $this->type == 'TXT' && strlen($_content) >= 64) { + // split string + $_contentlines = str_split($_content, 63); + // first line + $_l = array_shift($_contentlines); + // check for starting quote + if (substr($_l, 0, 1) == '"') { + $_l = substr($_l, 1); + } + $_content = '( "' . $_l . '"' . PHP_EOL; + $_l = array_pop($_contentlines); + // check for ending quote + if (substr($_l, -1) == '"') { + $_l = substr($_l, 0, -1); + } + foreach ($_contentlines as $_cl) { + // lines in between + $_content .= "\t\t\t\t" . '"' . $_cl . '"' . PHP_EOL; + } + // last line + $_content .= "\t\t\t\t" . '"'.$_l.'" )'; + } + $result = $this->record . "\t" . $this->ttl . "\t" . $this->class . "\t" . $this->type . "\t" . (($this->priority >= 0 && ($this->type == 'MX' || $this->type == 'SRV')) ? $this->priority . "\t" : "") . $_content . PHP_EOL; return $result; } } From 181848290ff89231dbf24a162da6ea028b3ba61d Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 4 Sep 2017 15:28:12 +0200 Subject: [PATCH 0329/1335] move setting to enable usage of libnss-extrausers to 'system' category as mpm-itk + mod_php users might need this too Signed-off-by: Michael Kaufmann (d00p) --- actions/admin/settings/120.system.php | 8 ++++++++ actions/admin/settings/135.fcgid.php | 10 +--------- actions/admin/settings/136.phpfpm.php | 10 +--------- 3 files changed, 10 insertions(+), 18 deletions(-) diff --git a/actions/admin/settings/120.system.php b/actions/admin/settings/120.system.php index b08ce88e..e1b4c5cd 100644 --- a/actions/admin/settings/120.system.php +++ b/actions/admin/settings/120.system.php @@ -94,6 +94,14 @@ return array( 'plausibility_check_method' => 'checkMysqlAccessHost', 'save_method' => 'storeSettingMysqlAccessHost', ), + 'system_nssextrausers' => array( + 'label' => $lng['serversettings']['nssextrausers'], + 'settinggroup' => 'system', + 'varname' => 'nssextrausers', + 'type' => 'bool', + 'default' => false, + 'save_method' => 'storeSettingField' + ), 'system_index_file_extension' => array( 'label' => $lng['serversettings']['index_file_extension'], 'settinggroup' => 'system', diff --git a/actions/admin/settings/135.fcgid.php b/actions/admin/settings/135.fcgid.php index d57e4241..212c03f7 100644 --- a/actions/admin/settings/135.fcgid.php +++ b/actions/admin/settings/135.fcgid.php @@ -104,15 +104,7 @@ return array( 'type' => 'int', 'default' => 30, 'save_method' => 'storeSettingField' - ), - 'system_nssextrausers' => array( - 'label' => $lng['serversettings']['nssextrausers'], - 'settinggroup' => 'system', - 'varname' => 'nssextrausers', - 'type' => 'bool', - 'default' => false, - 'save_method' => 'storeSettingField' - ), + ) ) ) ) diff --git a/actions/admin/settings/136.phpfpm.php b/actions/admin/settings/136.phpfpm.php index 45fc5e71..90dc742f 100644 --- a/actions/admin/settings/136.phpfpm.php +++ b/actions/admin/settings/136.phpfpm.php @@ -159,15 +159,7 @@ return array( 'default' => false, 'visible' => Settings::Get('system.apache24'), 'save_method' => 'storeSettingField' - ), - 'system_nssextrausers' => array( - 'label' => $lng['serversettings']['nssextrausers'], - 'settinggroup' => 'system', - 'varname' => 'nssextrausers', - 'type' => 'bool', - 'default' => false, - 'save_method' => 'storeSettingField' - ), + ) ), ), ), From 15b62aae043b511a96d873843c907d80b1ed58e0 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Fri, 6 Oct 2017 09:29:49 +0200 Subject: [PATCH 0330/1335] add inserttask for config regeneration and fix aliasdomain NULL value if empty, fixes #478 Signed-off-by: Michael Kaufmann (d00p) --- admin_domains.php | 2 ++ lib/classes/bulk/class.DomainBulkAction.php | 15 +++++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/admin_domains.php b/admin_domains.php index 7c05f08a..5e410c45 100644 --- a/admin_domains.php +++ b/admin_domains.php @@ -2223,6 +2223,8 @@ if ($page == 'domains' || $page == 'overview') { // update customer/admin counters updateCounters(false); + inserttask('1'); + inserttask('4'); $result_str = $result['imported'] . ' / ' . $result['all']; standard_success('domain_import_successfully', $result_str, array( diff --git a/lib/classes/bulk/class.DomainBulkAction.php b/lib/classes/bulk/class.DomainBulkAction.php index 0c0a6b09..1331cec5 100644 --- a/lib/classes/bulk/class.DomainBulkAction.php +++ b/lib/classes/bulk/class.DomainBulkAction.php @@ -187,13 +187,14 @@ class DomainBulkAction } // preapre insert statement as it is used a few times + // leave out aliasdomain for now, cause empty = NULL value which cannot be + // added this easily using prepared statements $this->_ins_stmt = Database::prepare(" INSERT INTO `" . TABLE_PANEL_DOMAINS . "` SET `domain` = :domain, `adminid` = :adminid, `customerid` = :customerid, `documentroot` = :documentroot, - `aliasdomain` = :aliasdomain, `isbinddomain` = :isbinddomain, `isemaildomain` = :isemaildomain, `email_only` = :email_only, @@ -307,6 +308,7 @@ class DomainBulkAction } // check for alias-domain + $hasAlias = false; if (! empty($domain_data['aliasdomain'])) { // format $domain_data['aliasdomain'] = $idna_convert->encode(preg_replace(array( @@ -325,6 +327,7 @@ class DomainBulkAction // - we'd better skip return false; } + $hasAlias = $domain_data['aliasdomain']; } // check for use_ssl and ssl_redirect @@ -462,13 +465,21 @@ class DomainBulkAction $use_ssl = (bool)$domain_data['use_ssl']; // don't need that for the domain-insert-statement unset($domain_data['use_ssl']); - + // don't need alias + unset($domain_data['aliasdomain']); + // finally ADD the domain to panel_domains Database::pexecute($this->_ins_stmt, $domain_data); // get the newly inserted domain-id $domain_id = Database::lastInsertId(); + // add alias if any + if ($hasAlias != false) { + $alias_stmt = Database::prepare("UPDATE `".TABLE_PANEL_DOMAINS."` SET `aliasdomain` = :aliasdomain WHERE `id` = :did"); + Database::pexecute($alias_stmt, array('aliasdomain' => $hasAlias, 'did' => $domain_id)); + } + // insert domain <-> ip/port reference if (empty($iplist)) { $iplist = Settings::Get('system.ipaddress'); From 421c29c4918a1fa193846cede4373b045b5d33f3 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 31 Oct 2017 13:03:06 +0100 Subject: [PATCH 0331/1335] remove each() keyword as it is deprecated as of php-7.2, fixes #479 Signed-off-by: Michael Kaufmann (d00p) --- admin_admins.php | 4 ++-- admin_customers.php | 4 ++-- admin_index.php | 2 +- admin_templates.php | 4 ++-- customer_email.php | 5 +++-- customer_index.php | 2 +- index.php | 2 +- install/lib/class.FroxlorInstall.php | 2 +- .../phphelpers/function.array_trim.php | 21 +++++++------------ lib/init.php | 3 ++- 10 files changed, 23 insertions(+), 26 deletions(-) diff --git a/admin_admins.php b/admin_admins.php index de104c34..f0ede7de 100644 --- a/admin_admins.php +++ b/admin_admins.php @@ -440,7 +440,7 @@ if ($page == 'admins' } else { $language_options = ''; - while (list($language_file, $language_name) = each($languages)) { + foreach ($languages as $language_file => $language_name) { $language_options.= makeoption($language_name, $language_file, $userinfo['language'], true); } @@ -840,7 +840,7 @@ if ($page == 'admins' } $language_options = ''; - while (list($language_file, $language_name) = each($languages)) { + foreach ($languages as $language_file => $language_name) { $language_options.= makeoption($language_name, $language_file, $result['def_language'], true); } diff --git a/admin_customers.php b/admin_customers.php index 09e938a9..3a88dd38 100644 --- a/admin_customers.php +++ b/admin_customers.php @@ -1024,7 +1024,7 @@ if ($page == 'customers' } else { $language_options = ''; - while (list($language_file, $language_name) = each($languages)) { + foreach ($languages as $language_file => $language_name) { $language_options.= makeoption($language_name, $language_file, Settings::Get('panel.standardlanguage'), true); } @@ -1630,7 +1630,7 @@ if ($page == 'customers' } else { $language_options = ''; - while (list($language_file, $language_name) = each($languages)) { + foreach ($languages as $language_file => $language_name) { $language_options.= makeoption($language_name, $language_file, $result['def_language'], true); } diff --git a/admin_index.php b/admin_index.php index 177278bf..dae69d6d 100644 --- a/admin_index.php +++ b/admin_index.php @@ -280,7 +280,7 @@ if ($page == 'overview') { $default_lang = $userinfo['def_language']; } - while (list($language_file, $language_name) = each($languages)) { + foreach ($languages as $language_file => $language_name) { $language_options.= makeoption($language_name, $language_file, $default_lang, true); } diff --git a/admin_templates.php b/admin_templates.php index cec93081..5a138fc0 100644 --- a/admin_templates.php +++ b/admin_templates.php @@ -99,7 +99,7 @@ if ($action == '') { } $add = false; - while (list($language_file, $language_name) = each($languages)) { + foreach ($languages as $language_file => $language_name) { $templates_done = array(); $result_stmt = Database::prepare(" @@ -328,7 +328,7 @@ if ($action == '') { $language_options = ''; $template_options = ''; - while (list($language_file, $language_name) = each($languages)) { + foreach ($languages as $language_file => $language_name) { $templates = array(); $result_stmt = Database::prepare(" SELECT `varname` FROM `" . TABLE_PANEL_TEMPLATES . "` diff --git a/customer_email.php b/customer_email.php index 1076832a..479493d6 100644 --- a/customer_email.php +++ b/customer_email.php @@ -96,7 +96,8 @@ if ($page == 'overview') { $row['destination'] = explode(' ', $row['destination']); uasort($row['destination'], 'strcasecmp'); - while (list($dest_id, $destination) = each($row['destination'])) { + $dest_list = $row['destination']; + foreach ($dest_list as $dest_id => $destination) { $row['destination'][$dest_id] = $idna_convert->decode($row['destination'][$dest_id]); if ($row['destination'][$dest_id] == $row['email_full']) { @@ -323,7 +324,7 @@ if ($page == 'overview') { $forwarders = ''; $forwarders_count = 0; - while (list($dest_id, $destination) = each($result['destination'])) { + foreach ($row['destination'] as $dest_id => $destination) { $destination = $idna_convert->decode($destination); if ($destination != $result['email_full'] && $destination != '') { diff --git a/customer_index.php b/customer_index.php index 5e6c2aaf..bb1305c9 100644 --- a/customer_index.php +++ b/customer_index.php @@ -204,7 +204,7 @@ if ($page == 'overview') { } $language_options = ''; - while (list($language_file, $language_name) = each($languages)) { + foreach ($languages as $language_file => $language_name) { $language_options .= makeoption($language_name, $language_file, $default_lang, true); } diff --git a/index.php b/index.php index 423f8fd8..a970c53b 100644 --- a/index.php +++ b/index.php @@ -250,7 +250,7 @@ if ($action == 'login') { $language_options = ''; $language_options .= makeoption($lng['login']['profile_lng'], 'profile', 'profile', true, true); - while (list($language_file, $language_name) = each($languages)) { + foreach ($languages as $language_file => $language_name) { $language_options .= makeoption($language_name, $language_file, 'profile', true); } diff --git a/install/lib/class.FroxlorInstall.php b/install/lib/class.FroxlorInstall.php index ac9fb7f4..5f6ea5e7 100644 --- a/install/lib/class.FroxlorInstall.php +++ b/install/lib/class.FroxlorInstall.php @@ -702,7 +702,7 @@ class FroxlorInstall } // language selection $language_options = ''; - while (list ($language_file, $language_name) = each($this->_languages)) { + foreach ($this->_languages as $language_name => $language_file) { $language_options .= makeoption($language_name, $language_file, $this->_activelng, true, true); } // get language-form-template diff --git a/lib/functions/phphelpers/function.array_trim.php b/lib/functions/phphelpers/function.array_trim.php index ca587c1d..4c64ccfd 100644 --- a/lib/functions/phphelpers/function.array_trim.php +++ b/lib/functions/phphelpers/function.array_trim.php @@ -20,27 +20,22 @@ /** * Returns Array, whose elements have been checked whether thay are empty or not * - * @param array The array to trim + * @param array $source + * The array to trim * @return array The trim'med array * @author Florian Lippert */ - function array_trim($source) { $returnval = array(); - - if(is_array($source)) - { - while(list($var, $val) = each($source)) - { - if($val != ' ' - && $val != '')$returnval[$var] = $val; + if (is_array($source)) { + foreach ($source as $var => $val) { + if ($val != ' ' && $val != '') { + $returnval[$var] = $val; + } } - } - else - { + } else { $returnval = $source; } - return $returnval; } diff --git a/lib/init.php b/lib/init.php index b4e4c018..d5747621 100644 --- a/lib/init.php +++ b/lib/init.php @@ -156,7 +156,8 @@ if (version_compare(PHP_VERSION, "5.4.0", "<")) { if (get_magic_quotes_gpc()) { $in = array(&$_GET, &$_POST, &$_COOKIE); - while (list($k, $v) = each($in)) { + $_in = $in; + foreach ($in as $k => $v) { foreach ($v as $key => $val) { if (!is_array($val)) { $in[$k][$key] = stripslashes($val); From dd3e5e9c6bf2831e3f94939aa19e83807c293619 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 6 Nov 2017 07:36:51 +0100 Subject: [PATCH 0332/1335] fix wrong variable name typo, fixes #484 Signed-off-by: Michael Kaufmann (d00p) --- customer_email.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/customer_email.php b/customer_email.php index 479493d6..02517fc1 100644 --- a/customer_email.php +++ b/customer_email.php @@ -324,7 +324,7 @@ if ($page == 'overview') { $forwarders = ''; $forwarders_count = 0; - foreach ($row['destination'] as $dest_id => $destination) { + foreach ($result['destination'] as $dest_id => $destination) { $destination = $idna_convert->decode($destination); if ($destination != $result['email_full'] && $destination != '') { From 1e03946df7e0a8aebe8d2c680b9349be81dafda3 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Wed, 8 Nov 2017 07:49:40 +0100 Subject: [PATCH 0333/1335] set sql_mode to disable STRICT_MODE usage for froxlor, thx to albech for the hint Signed-off-by: Michael Kaufmann (d00p) --- install/lib/class.FroxlorInstall.php | 6 +++--- lib/classes/database/class.Database.php | 2 +- scripts/jobs/cron_tasks.inc.dns.20.pdns.php | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/install/lib/class.FroxlorInstall.php b/install/lib/class.FroxlorInstall.php index 5f6ea5e7..dc8cf74b 100644 --- a/install/lib/class.FroxlorInstall.php +++ b/install/lib/class.FroxlorInstall.php @@ -204,7 +204,7 @@ class FroxlorInstall $content .= $this->_status_message('begin', $this->_lng['install']['testing_mysql']); $options = array( - 'PDO::MYSQL_ATTR_INIT_COMMAND' => 'set names utf8' + 'PDO::MYSQL_ATTR_INIT_COMMAND' => 'SET names utf8,sql_mode="NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION"' ); $dsn = "mysql:host=" . $this->_data['mysql_host'] . ";"; $fatal_fail = false; @@ -240,7 +240,7 @@ class FroxlorInstall $content .= $this->_importDatabaseData(); // create DB object for new database $options = array( - 'PDO::MYSQL_ATTR_INIT_COMMAND' => 'set names utf8' + 'PDO::MYSQL_ATTR_INIT_COMMAND' => 'SET names utf8,sql_mode="NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION"' ); $dsn = "mysql:host=" . $this->_data['mysql_host'] . ";dbname=" . $this->_data['mysql_database'] . ";"; $another_fail = false; @@ -511,7 +511,7 @@ class FroxlorInstall $content = ""; $content .= $this->_status_message('begin', $this->_lng['install']['testing_new_db']); $options = array( - 'PDO::MYSQL_ATTR_INIT_COMMAND' => 'set names utf8' + 'PDO::MYSQL_ATTR_INIT_COMMAND' => 'SET names utf8,sql_mode="NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION"' ); $dsn = "mysql:host=" . $this->_data['mysql_host'] . ";dbname=" . $this->_data['mysql_database'] . ";"; $fatal_fail = false; diff --git a/lib/classes/database/class.Database.php b/lib/classes/database/class.Database.php index 74a655d7..3174c2b3 100644 --- a/lib/classes/database/class.Database.php +++ b/lib/classes/database/class.Database.php @@ -262,7 +262,7 @@ class Database { // build up connection string $driver = 'mysql'; $dsn = $driver.":"; - $options = array(PDO::MYSQL_ATTR_INIT_COMMAND => 'set names utf8'); + $options = array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET names utf8,sql_mode="NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION"'); $attributes = array('ATTR_ERRMODE' => 'ERRMODE_EXCEPTION'); $dbconf["dsn"] = array( diff --git a/scripts/jobs/cron_tasks.inc.dns.20.pdns.php b/scripts/jobs/cron_tasks.inc.dns.20.pdns.php index 27a63632..cb70417a 100644 --- a/scripts/jobs/cron_tasks.inc.dns.20.pdns.php +++ b/scripts/jobs/cron_tasks.inc.dns.20.pdns.php @@ -227,7 +227,7 @@ class pdns extends DnsBase // build up connection string $driver = 'mysql'; $dsn = $driver.":"; - $options = array(PDO::MYSQL_ATTR_INIT_COMMAND => 'set names utf8'); + $options = array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET names utf8,sql_mode="NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION"'); $attributes = array('ATTR_ERRMODE' => 'ERRMODE_EXCEPTION'); $dbconf = array(); From 45c0915b59ffe553c17e9d05655a7b1eee09b45c Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Fri, 17 Nov 2017 15:15:09 +0100 Subject: [PATCH 0334/1335] fix ssl integration in lighttpd, thx to black-night for the info Signed-off-by: Michael Kaufmann (d00p) --- scripts/jobs/cron_tasks.inc.http.20.lighttpd.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php b/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php index d1ba5d04..a2d5258b 100644 --- a/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php +++ b/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php @@ -535,7 +535,8 @@ class lighttpd extends HttpConfigBase if ($domain['ssl_cert_file'] != '') { - $ssl_settings .= 'ssl.engine = "enable"' . "\n"; + // ssl.engine only necessary once in the ip/port vhost (SERVER['socket'] condition) + //$ssl_settings .= 'ssl.engine = "enable"' . "\n"; $ssl_settings .= 'ssl.use-compression = "disable"' . "\n"; $ssl_settings .= 'ssl.use-sslv2 = "disable"' . "\n"; $ssl_settings .= 'ssl.use-sslv3 = "disable"' . "\n"; From e725b48c4c9e19d6cbd4cb9dd4f34a64e283e082 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 27 Nov 2017 07:48:36 +0100 Subject: [PATCH 0335/1335] add default/global directory options in apache regardless of whether fcgid/fpm is being used or not; fixes #485 Signed-off-by: Michael Kaufmann (d00p) --- .../jobs/cron_tasks.inc.http.10.apache.php | 48 ++++++++----------- 1 file changed, 20 insertions(+), 28 deletions(-) diff --git a/scripts/jobs/cron_tasks.inc.http.10.apache.php b/scripts/jobs/cron_tasks.inc.http.10.apache.php index 98d1ed17..6e9d3277 100644 --- a/scripts/jobs/cron_tasks.inc.http.10.apache.php +++ b/scripts/jobs/cron_tasks.inc.http.10.apache.php @@ -78,36 +78,28 @@ class apache extends HttpConfigBase } $vhosts_filename = makeCorrectFile($vhosts_folder . '/05_froxlor_dirfix_nofcgid.conf'); - if (Settings::Get('system.mod_fcgid') == '1' || Settings::Get('phpfpm.enabled') == '1') { - // if we use fcgid or php-fpm we don't need this file - if (file_exists($vhosts_filename)) { - $this->logger->logAction(CRON_ACTION, LOG_NOTICE, 'apache::_createStandardDirectoryEntry: unlinking ' . basename($vhosts_filename)); - unlink(makeCorrectFile($vhosts_filename)); - } - } else { - if (! isset($this->virtualhosts_data[$vhosts_filename])) { - $this->virtualhosts_data[$vhosts_filename] = ''; - } - - $this->virtualhosts_data[$vhosts_filename] .= ' ' . "\n"; - - // check for custom values, see #1638 - $custom_opts = Settings::Get('system.apacheglobaldiropt'); - if (! empty($custom_opts)) { - $this->virtualhosts_data[$vhosts_filename] .= $custom_opts . "\n"; - } else { - // >=apache-2.4 enabled? - if (Settings::Get('system.apache24') == '1') { - $this->virtualhosts_data[$vhosts_filename] .= ' Require all granted' . "\n"; - $this->virtualhosts_data[$vhosts_filename] .= ' AllowOverride All' . "\n"; - } else { - $this->virtualhosts_data[$vhosts_filename] .= ' Order allow,deny' . "\n"; - $this->virtualhosts_data[$vhosts_filename] .= ' allow from all' . "\n"; - } - } - $this->virtualhosts_data[$vhosts_filename] .= ' ' . "\n"; + if (! isset($this->virtualhosts_data[$vhosts_filename])) { + $this->virtualhosts_data[$vhosts_filename] = ''; } + $this->virtualhosts_data[$vhosts_filename] .= ' ' . "\n"; + + // check for custom values, see #1638 + $custom_opts = Settings::Get('system.apacheglobaldiropt'); + if (! empty($custom_opts)) { + $this->virtualhosts_data[$vhosts_filename] .= $custom_opts . "\n"; + } else { + // >=apache-2.4 enabled? + if (Settings::Get('system.apache24') == '1') { + $this->virtualhosts_data[$vhosts_filename] .= ' Require all granted' . "\n"; + $this->virtualhosts_data[$vhosts_filename] .= ' AllowOverride All' . "\n"; + } else { + $this->virtualhosts_data[$vhosts_filename] .= ' Order allow,deny' . "\n"; + $this->virtualhosts_data[$vhosts_filename] .= ' allow from all' . "\n"; + } + } + $this->virtualhosts_data[$vhosts_filename] .= ' ' . "\n"; + $ocsp_cache_filename = makeCorrectFile($vhosts_folder . '/03_froxlor_ocsp_cache.conf'); if (Settings::Get('system.use_ssl') == '1' && Settings::Get('system.apache24') == 1) { $this->virtualhosts_data[$ocsp_cache_filename] = 'SSLStaplingCache ' . From 5540b02e352fc421763d01d4e69beca6fd6ea11d Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 27 Nov 2017 07:54:41 +0100 Subject: [PATCH 0336/1335] do not remove Let's Encrypt token when self-check fails but rather give out the information as warning. The self-check fails for many users due to different local configurations and might not always be correct; fixes #480 Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/ssl/class.lescript.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/classes/ssl/class.lescript.php b/lib/classes/ssl/class.lescript.php index 6d9ca5a3..8645c67b 100644 --- a/lib/classes/ssl/class.lescript.php +++ b/lib/classes/ssl/class.lescript.php @@ -236,9 +236,7 @@ class lescript } else { $errmsg = ""; } - @unlink($tokenPath); - $this->logger->logAction(CRON_ACTION, LOG_ERR, "letsencrypt Please check $uri - token not available" . $errmsg); - continue; + $this->logger->logAction(CRON_ACTION, LOG_WARNING, "[Lets Encrypt self-check] Please check $uri - token seems to be not available. This is just a simple self-check, it might be wrong but consider using this information when Let's Encrypt fails to issue a certificate" . $errmsg); } $this->log("Sending request to challenge"); From cb31c5258daa6220bb607becb0bc536e00c15f37 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 27 Nov 2017 08:09:33 +0100 Subject: [PATCH 0337/1335] correct setting for php-fpm peardir, fixes #475 Signed-off-by: Michael Kaufmann (d00p) --- actions/admin/settings/136.phpfpm.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/actions/admin/settings/136.phpfpm.php b/actions/admin/settings/136.phpfpm.php index 90dc742f..bcbe153f 100644 --- a/actions/admin/settings/136.phpfpm.php +++ b/actions/admin/settings/136.phpfpm.php @@ -73,6 +73,8 @@ return array( 'varname' => 'peardir', 'type' => 'string', 'string_type' => 'dir', + 'string_delimiter' => ':', + 'string_emptyallowed' => true, 'default' => '/usr/share/php/:/usr/share/php5/', 'save_method' => 'storeSettingField' ), From a5251824ae6862eb86a13e0e47362f17ace8e413 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 27 Nov 2017 08:14:31 +0100 Subject: [PATCH 0338/1335] try to reduce weird path-values when people are getting creative, fixes #487 Signed-off-by: Michael Kaufmann (d00p) --- customer_domains.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/customer_domains.php b/customer_domains.php index f492ee8a..3bfcd382 100644 --- a/customer_domains.php +++ b/customer_domains.php @@ -327,6 +327,9 @@ if ($page == 'overview') { } if (!preg_match('/^https?\:\/\//', $path) || !validateUrl($path)) { + if (strstr($path, ":") !== FALSE) { + standard_error('pathmaynotcontaincolon'); + } // If path is empty or '/' and 'Use domain name as default value for DocumentRoot path' is enabled in settings, // set default path to subdomain or domain name if ((($path == '') || ($path == '/')) && Settings::Get('system.documentroot_use_default_value') == 1) { @@ -334,9 +337,6 @@ if ($page == 'overview') { } else { $path = makeCorrectDir($userinfo['documentroot'] . '/' . $path); } - if (strstr($path, ":") !== FALSE) { - standard_error('pathmaynotcontaincolon'); - } } else { $_doredirect = true; } @@ -571,6 +571,9 @@ if ($page == 'overview') { } if (!preg_match('/^https?\:\/\//', $path) || !validateUrl($path)) { + if (strstr($path, ":") !== FALSE) { + standard_error('pathmaynotcontaincolon'); + } // If path is empty or '/' and 'Use domain name as default value for DocumentRoot path' is enabled in settings, // set default path to subdomain or domain name if ((($path == '') || ($path == '/')) && Settings::Get('system.documentroot_use_default_value') == 1) { @@ -578,9 +581,6 @@ if ($page == 'overview') { } else { $path = makeCorrectDir($userinfo['documentroot'] . '/' . $path); } - if (strstr($path, ":") !== FALSE) { - standard_error('pathmaynotcontaincolon'); - } } else { $_doredirect = true; } From d2a9fa8632bf2ef926dcb4bfc1dd6baf80218d35 Mon Sep 17 00:00:00 2001 From: azerr Date: Fri, 8 Dec 2017 15:39:05 +0100 Subject: [PATCH 0339/1335] extend nginx redirect regex to https --- scripts/jobs/cron_tasks.inc.http.30.nginx.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/jobs/cron_tasks.inc.http.30.nginx.php b/scripts/jobs/cron_tasks.inc.http.30.nginx.php index bc7f23fc..a4fbb630 100644 --- a/scripts/jobs/cron_tasks.inc.http.30.nginx.php +++ b/scripts/jobs/cron_tasks.inc.http.30.nginx.php @@ -224,7 +224,7 @@ class nginx extends HttpConfigBase } else { $_sslport = $this->checkAlternativeSslPort(); $mypath = 'https://' . Settings::Get('system.hostname') . $_sslport . '/'; - $this->nginx_data[$vhost_filename] .= "\t" . 'if ($request_uri !~ ^/.well-known/acme-challenge/\w+$) {' . "\n"; + $this->nginx_data[$vhost_filename] .= "\t" . 'if ($request_uri !~ ^/.well-known/acme-challenge/[\w-]+$) {' . "\n"; $this->nginx_data[$vhost_filename] .= "\t\t" . 'return 301 ' . $mypath . '$request_uri;' . "\n"; $this->nginx_data[$vhost_filename] .= "\t" . '}' . "\n"; } @@ -473,7 +473,7 @@ class nginx extends HttpConfigBase // Get domain's redirect code $code = getDomainRedirectCode($domain['id'], '301'); - $vhost_content .= "\t" . 'if ($request_uri !~ ^/.well-known/acme-challenge/\w+$) {' . "\n"; + $vhost_content .= "\t" . 'if ($request_uri !~ ^/.well-known/acme-challenge/[\w-]+$) {' . "\n"; $vhost_content .= "\t\t" . 'return ' . $code .' ' . $uri . '$request_uri;' . "\n"; $vhost_content .= "\t" . '}' . "\n"; } else { From 297f3f638c0ad14279f67296caf00f8cfa2b185e Mon Sep 17 00:00:00 2001 From: azerr Date: Fri, 8 Dec 2017 17:47:09 +0100 Subject: [PATCH 0340/1335] change sign direction --- scripts/jobs/cron_tasks.inc.http.30.nginx.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/jobs/cron_tasks.inc.http.30.nginx.php b/scripts/jobs/cron_tasks.inc.http.30.nginx.php index a4fbb630..4ff9dbd0 100644 --- a/scripts/jobs/cron_tasks.inc.http.30.nginx.php +++ b/scripts/jobs/cron_tasks.inc.http.30.nginx.php @@ -224,7 +224,7 @@ class nginx extends HttpConfigBase } else { $_sslport = $this->checkAlternativeSslPort(); $mypath = 'https://' . Settings::Get('system.hostname') . $_sslport . '/'; - $this->nginx_data[$vhost_filename] .= "\t" . 'if ($request_uri !~ ^/.well-known/acme-challenge/[\w-]+$) {' . "\n"; + $this->nginx_data[$vhost_filename] .= "\t" . 'if ($request_uri !~ ^/.well-known/acme-challenge/[-\w]+$) {' . "\n"; $this->nginx_data[$vhost_filename] .= "\t\t" . 'return 301 ' . $mypath . '$request_uri;' . "\n"; $this->nginx_data[$vhost_filename] .= "\t" . '}' . "\n"; } @@ -473,7 +473,7 @@ class nginx extends HttpConfigBase // Get domain's redirect code $code = getDomainRedirectCode($domain['id'], '301'); - $vhost_content .= "\t" . 'if ($request_uri !~ ^/.well-known/acme-challenge/[\w-]+$) {' . "\n"; + $vhost_content .= "\t" . 'if ($request_uri !~ ^/.well-known/acme-challenge/[-\w]+$) {' . "\n"; $vhost_content .= "\t\t" . 'return ' . $code .' ' . $uri . '$request_uri;' . "\n"; $vhost_content .= "\t" . '}' . "\n"; } else { From cf4f15a83c4769347ee3175c6a5d1dc399df89dc Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 19 Dec 2017 08:00:39 +0100 Subject: [PATCH 0341/1335] explicitly deactivate TLS (and auto-tls) when setting use-tls is OFF; fixes #496 Signed-off-by: Michael Kaufmann (d00p) --- admin_settings.php | 2 ++ lib/functions/output/function.dieWithMail.php | 2 ++ lib/init.php | 2 ++ scripts/jobs/cron_usage_report.php | 2 ++ 4 files changed, 8 insertions(+) diff --git a/admin_settings.php b/admin_settings.php index 66abf4aa..49e2bd8c 100644 --- a/admin_settings.php +++ b/admin_settings.php @@ -310,6 +310,8 @@ elseif ($page == 'testmail') $testmail->Password = Settings::Get('system.mail_smtp_passwd'); if (Settings::Get('system.mail_smtp_usetls')) { $testmail->SMTPSecure = 'tls'; + } else { + $testmail->SMTPAutoTLS = false; } $testmail->Port = Settings::Get('system.mail_smtp_port'); } diff --git a/lib/functions/output/function.dieWithMail.php b/lib/functions/output/function.dieWithMail.php index 21890bdd..d0e5015b 100644 --- a/lib/functions/output/function.dieWithMail.php +++ b/lib/functions/output/function.dieWithMail.php @@ -42,6 +42,8 @@ function dieWithMail($message, $subject = "[froxlor] Cronjob error") { $_mail->Password = Settings::Get('system.mail_smtp_passwd'); if (Settings::Get('system.mail_smtp_usetls')) { $_mail->SMTPSecure = 'tls'; + } else { + $mail->SMTPAutoTLS = false; } $_mail->Port = Settings::Get('system.mail_smtp_port'); } diff --git a/lib/init.php b/lib/init.php index d5747621..3436b237 100644 --- a/lib/init.php +++ b/lib/init.php @@ -564,6 +564,8 @@ if (Settings::Get('system.mail_use_smtp')) { $mail->Password = Settings::Get('system.mail_smtp_passwd'); if (Settings::Get('system.mail_smtp_usetls')) { $mail->SMTPSecure = 'tls'; + } else { + $mail->SMTPAutoTLS = false; } $mail->Port = Settings::Get('system.mail_smtp_port'); } diff --git a/scripts/jobs/cron_usage_report.php b/scripts/jobs/cron_usage_report.php index 2ff94b52..9bc9eea4 100644 --- a/scripts/jobs/cron_usage_report.php +++ b/scripts/jobs/cron_usage_report.php @@ -34,6 +34,8 @@ if (Settings::Get('system.mail_use_smtp')) { $mail->Password = Settings::Get('system.mail_smtp_passwd'); if (Settings::Get('system.mail_smtp_usetls')) { $mail->SMTPSecure = 'tls'; + } else { + $mail->SMTPAutoTLS = false; } $mail->Port = Settings::Get('system.mail_smtp_port'); } From 148b2fc1beaf379a49064732365155647c0167e3 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Wed, 27 Dec 2017 10:01:07 +0100 Subject: [PATCH 0342/1335] validating config-template parameters; avoid URL misuse, thx to hyp3rlinx for noticing this Signed-off-by: Michael Kaufmann (d00p) --- admin_configfiles.php | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/admin_configfiles.php b/admin_configfiles.php index 29ae9437..949e9417 100644 --- a/admin_configfiles.php +++ b/admin_configfiles.php @@ -82,6 +82,12 @@ if ($userinfo['change_serversettings'] == '1') { $config_dir = makeCorrectDir(FROXLOR_INSTALL_DIR . '/lib/configfiles/'); if ($distribution != "") { + + if (!file_exists($config_dir . '/' . $distribution . ".xml")) { + trigger_error("Unknown distribution, are you playing around with the URL?"); + exit; + } + // create configparser object $configfiles = new ConfigParser($config_dir . '/' . $distribution . ".xml"); @@ -93,6 +99,11 @@ if ($userinfo['change_serversettings'] == '1') { if ($service != "") { + if (!isset($services[$service])) { + trigger_error("Unknown service, are you playing around with the URL?"); + exit; + } + $daemons = $services[$service]->getDaemons(); if ($daemon == "") { @@ -136,6 +147,11 @@ if ($userinfo['change_serversettings'] == '1') { if ($distribution != "" && $service != "" && $daemon != "") { + if (!isset($daemons[$daemon])) { + trigger_error("Unknown daemon, are you playing around with the URL?"); + exit; + } + $confarr = $daemons[$daemon]->getConfig(); $configpage = ''; From 57277eb1e34755bd7f13a7225f5e2d56ad4f6441 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Wed, 27 Dec 2017 10:36:10 +0100 Subject: [PATCH 0343/1335] also add locked users to the passwd file for quota not to rage :P thx J-BBB Signed-off-by: Michael Kaufmann (d00p) --- scripts/classes/class.Extrausers.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/scripts/classes/class.Extrausers.php b/scripts/classes/class.Extrausers.php index 17b312af..2ad38b73 100644 --- a/scripts/classes/class.Extrausers.php +++ b/scripts/classes/class.Extrausers.php @@ -21,7 +21,7 @@ class Extrausers { // passwd $passwd = '/var/lib/extrausers/passwd'; - $sql = "SELECT username,'x' as password,uid,gid,'Froxlor User' as comment,homedir,shell FROM ftp_users WHERE login_enabled = 'Y' ORDER BY uid ASC"; + $sql = "SELECT username,'x' as password,uid,gid,'Froxlor User' as comment,homedir,shell, login_enabled FROM ftp_users ORDER BY uid ASC"; self::_generateFile($passwd, $sql, $cronlog); // group @@ -39,7 +39,6 @@ class Extrausers @chmod('/var/lib/extrausers/passwd', 0644); @chmod('/var/lib/extrausers/group', 0644); @chmod('/var/lib/extrausers/shadow', 0640); - } private static function _generateFile($file, $query, &$cronlog) @@ -59,6 +58,11 @@ class Extrausers while ($u = $data_sel_stmt->fetch(PDO::FETCH_ASSOC)) { switch ($type) { case 'passwd': + if ($u['login_enabled'] != 'Y') { + $u['password'] = '*'; + $u['shell'] = '/bin/false'; + $u['comment'] = 'Locked Froxlor User'; + } $line = $u['username'] . ':' . $u['password'] . ':' . $u['uid'] . ':' . $u['gid'] . ':' . $u['comment'] . ':' . $u['homedir'] . ':' . $u['shell'] . PHP_EOL; break; case 'group': From 66a4309fe551e0b2d3540927f74afe84853df928 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sun, 31 Dec 2017 11:18:59 +0100 Subject: [PATCH 0344/1335] add setting to disable LE self-check; set version to 0.9.38.8 for maintenance/bugfix release Signed-off-by: Michael Kaufmann (d00p) --- actions/admin/settings/131.ssl.php | 8 +++++++ install/froxlor.sql | 5 +++-- .../updates/froxlor/0.9/update_0.9.inc.php | 13 ++++++++++++ .../preconfig/0.9/preconfig_0.9.inc.php | 10 +++++++++ lib/classes/ssl/class.lescript.php | 21 +++++++++++-------- lib/version.inc.php | 4 ++-- lng/english.lng.php | 2 ++ lng/german.lng.php | 2 ++ 8 files changed, 52 insertions(+), 13 deletions(-) diff --git a/actions/admin/settings/131.ssl.php b/actions/admin/settings/131.ssl.php index 9028ac45..68f2a9e8 100644 --- a/actions/admin/settings/131.ssl.php +++ b/actions/admin/settings/131.ssl.php @@ -165,6 +165,14 @@ return array( 'type' => 'bool', 'default' => false, 'save_method' => 'storeSettingField' + ), + 'system_disable_le_selfcheck' => array( + 'label' => $lng['serversettings']['disable_le_selfcheck'], + 'settinggroup' => 'system', + 'varname' => 'disable_le_selfcheck', + 'type' => 'bool', + 'default' => false, + 'save_method' => 'storeSettingField' ) ) ) diff --git a/install/froxlor.sql b/install/froxlor.sql index f1b896da..e90d2112 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -554,6 +554,7 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES ('system', 'hsts_preload', '0'), ('system', 'leregistered', '0'), ('system', 'nssextrausers', '0'), + ('system', 'disable_le_selfcheck', '0'), ('panel', 'decimal_places', '4'), ('panel', 'adminmail', 'admin@SERVERNAME'), ('panel', 'phpmyadmin_url', ''), @@ -585,8 +586,8 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES ('panel', 'password_special_char_required', '0'), ('panel', 'password_special_char', '!?<>§$%+#=@'), ('panel', 'customer_hide_options', ''), - ('panel', 'version', '0.9.38.7'), - ('panel', 'db_version', '201708240'); + ('panel', 'version', '0.9.38.8'), + ('panel', 'db_version', '201712310'); DROP TABLE IF EXISTS `panel_tasks`; diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index b8d6f495..57f57a7d 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -3633,3 +3633,16 @@ if (isDatabaseVersion('201705050')) { updateToDbVersion('201708240'); } + +if (isDatabaseVersion('201708240')) { + + showUpdateStep("Adding new 'disable LE self-check' setting"); + $system_disable_le_selfcheck = isset($_POST['system_disable_le_selfcheck']) ? (int) $_POST['system_disable_le_selfcheck'] : 0; + Settings::AddNew('system.disable_le_selfcheck', $system_disable_le_selfcheck); + lastStepStatus(0); + + updateToDbVersion('201712310'); + + showUpdateStep("Updating from 0.9.38.7 to 0.9.38.8", false); + updateToVersion('0.9.38.8'); +} diff --git a/install/updates/preconfig/0.9/preconfig_0.9.inc.php b/install/updates/preconfig/0.9/preconfig_0.9.inc.php index a528c322..ffbf9503 100644 --- a/install/updates/preconfig/0.9/preconfig_0.9.inc.php +++ b/install/updates/preconfig/0.9/preconfig_0.9.inc.php @@ -717,4 +717,14 @@ function parseAndOutputPreconfig(&$has_preconfig, &$return, $current_version, $c $question .= makeyesno('system_nssextrausers', '1', '0', '0') . '
'; eval("\$return.=\"" . getTemplate("update/preconfigitem") . "\";"); } + + if (versionInUpdate($current_db_version, '201712310')) { + if (Settings::Get('system.leenabled') == 1) { + $has_preconfig = true; + $description = 'Chose whether you want to disable the Let\'s Encrypt selfcheck as it causes false positives for some onfigurations.

'; + $question = 'Disable Let\'s Encrypt self-check?
'; + $question .= makeyesno('system_disable_le_selfcheck', '1', '0', '0') . '
'; + eval("\$return.=\"" . getTemplate("update/preconfigitem") . "\";"); + } + } } diff --git a/lib/classes/ssl/class.lescript.php b/lib/classes/ssl/class.lescript.php index 8645c67b..9923d5b0 100644 --- a/lib/classes/ssl/class.lescript.php +++ b/lib/classes/ssl/class.lescript.php @@ -227,16 +227,19 @@ class lescript $this->log("Token for $domain saved at $tokenPath and should be available at $uri"); // simple self check - $selfcheckContextOptions = array('http' => array('header' => "User-Agent: Froxlor/".$this->version)); - $selfcheckContext = stream_context_create($selfcheckContextOptions); - if ($payload !== trim(@file_get_contents($uri, false, $selfcheckContext))) { - $errmsg = json_encode(error_get_last()); - if ($errmsg != "null") { - $errmsg = "; PHP error: " . $errmsg; - } else { - $errmsg = ""; + if (Settings::Get('system.disable_le_selfcheck') == '0') + { + $selfcheckContextOptions = array('http' => array('header' => "User-Agent: Froxlor/".$this->version)); + $selfcheckContext = stream_context_create($selfcheckContextOptions); + if ($payload !== trim(@file_get_contents($uri, false, $selfcheckContext))) { + $errmsg = json_encode(error_get_last()); + if ($errmsg != "null") { + $errmsg = "; PHP error: " . $errmsg; + } else { + $errmsg = ""; + } + $this->logger->logAction(CRON_ACTION, LOG_WARNING, "[Lets Encrypt self-check] Please check $uri - token seems to be not available. This is just a simple self-check, it might be wrong but consider using this information when Let's Encrypt fails to issue a certificate" . $errmsg); } - $this->logger->logAction(CRON_ACTION, LOG_WARNING, "[Lets Encrypt self-check] Please check $uri - token seems to be not available. This is just a simple self-check, it might be wrong but consider using this information when Let's Encrypt fails to issue a certificate" . $errmsg); } $this->log("Sending request to challenge"); diff --git a/lib/version.inc.php b/lib/version.inc.php index 187d99b3..f6c42a62 100644 --- a/lib/version.inc.php +++ b/lib/version.inc.php @@ -16,10 +16,10 @@ */ // Main version variable -$version = '0.9.38.7'; +$version = '0.9.38.8'; // Database version (YYYYMMDDC where C is a daily counter) -$dbversion = '201708240'; +$dbversion = '201712310'; // Distribution branding-tag (used for Debian etc.) $branding = ''; diff --git a/lng/english.lng.php b/lng/english.lng.php index 51e05ef6..da777141 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -2080,3 +2080,5 @@ $lng['admin']['domain_http2']['title'] = 'HTTP2 support'; $lng['admin']['domain_http2']['description'] = 'See Wikipedia for a detailed explanation of HTTP2'; $lng['admin']['testmail'] = 'SMTP test'; $lng['success']['testmailsent'] = 'Test mail sent successfully'; +$lng['serversettings']['disable_le_selfcheck']['title'] = "Disable Let's Encrypt local self-check"; +$lng['serversettings']['disable_le_selfcheck']['description'] = "If activated, froxlor will not perform its self-check for token accessability. Needed for NATed IP's or similar."; diff --git a/lng/german.lng.php b/lng/german.lng.php index 22e73ca8..7edda2ca 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -1731,3 +1731,5 @@ $lng['admin']['domain_http2']['title'] = 'HTTP2 Unterstützung'; $lng['admin']['domain_http2']['description'] = 'Siehe Wikipedia für eine ausführliche Beschreibung von HTTP2'; $lng['admin']['testmail'] = 'SMTP Test'; $lng['success']['testmailsent'] = 'Test E-Mail erfolgreich gesendet'; +$lng['serversettings']['disable_le_selfcheck']['title'] = "Deaktiviere Let's Encrypt lokale Selbstprüfung"; +$lng['serversettings']['disable_le_selfcheck']['description'] = "Wenn aktiviert wird Froxlor keine Erreichbarkeitsprüfung des Tokens vornehmen. Nötig bei ge-NAT-eten IP's oder Ähnlichem"; From 732c6e3a7820f1b666de5494fa96a312b5cb1952 Mon Sep 17 00:00:00 2001 From: Andreas Grundler Date: Sun, 31 Dec 2017 22:40:19 +0100 Subject: [PATCH 0345/1335] Added nscd -i passwd to clear user --- scripts/jobs/cron_tasks.php | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/jobs/cron_tasks.php b/scripts/jobs/cron_tasks.php index cd9bb294..b7176a53 100644 --- a/scripts/jobs/cron_tasks.php +++ b/scripts/jobs/cron_tasks.php @@ -178,6 +178,7 @@ while ($row = $result_tasks_stmt->fetch(PDO::FETCH_ASSOC)) { // clear NSCD cache if using fcgid or fpm, #1570 if (Settings::Get('system.mod_fcgid') == 1 || (int)Settings::Get('phpfpm.enabled') == 1) { $false_val = false; + safe_exec('nscd -i passwd 1> /dev/null', $false_val, array('>')); safe_exec('nscd -i group 1> /dev/null', $false_val, array('>')); } } From 64653a2bb11a3529893db93be095d72ffa283708 Mon Sep 17 00:00:00 2001 From: Andreas Grundler Date: Mon, 1 Jan 2018 13:54:32 +0100 Subject: [PATCH 0346/1335] =?UTF-8?q?nscd=20-i=20passwd=20in=20froxlor=5Fm?= =?UTF-8?q?aster=5Fcronjob.php=20eingef=C3=BCgt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/froxlor_master_cronjob.php | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/froxlor_master_cronjob.php b/scripts/froxlor_master_cronjob.php index d00926c6..a6d0d817 100644 --- a/scripts/froxlor_master_cronjob.php +++ b/scripts/froxlor_master_cronjob.php @@ -91,6 +91,7 @@ if (count($jobs_to_run) > 0) { // clear NSCD cache if using fcgid or fpm, #1570 if (Settings::Get('system.mod_fcgid') == 1 || (int)Settings::Get('phpfpm.enabled') == 1) { $false_val = false; + safe_exec('nscd -i passwd 1> /dev/null', $false_val, array('>')); safe_exec('nscd -i group 1> /dev/null', $false_val, array('>')); } } From ea96039128989dc55d994a53662a8494edfbf049 Mon Sep 17 00:00:00 2001 From: heavygale Date: Fri, 5 Jan 2018 18:11:23 +0100 Subject: [PATCH 0347/1335] Update preconfig_0.9.inc.php fixed a typo --- install/updates/preconfig/0.9/preconfig_0.9.inc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/updates/preconfig/0.9/preconfig_0.9.inc.php b/install/updates/preconfig/0.9/preconfig_0.9.inc.php index ffbf9503..26b47282 100644 --- a/install/updates/preconfig/0.9/preconfig_0.9.inc.php +++ b/install/updates/preconfig/0.9/preconfig_0.9.inc.php @@ -721,7 +721,7 @@ function parseAndOutputPreconfig(&$has_preconfig, &$return, $current_version, $c if (versionInUpdate($current_db_version, '201712310')) { if (Settings::Get('system.leenabled') == 1) { $has_preconfig = true; - $description = 'Chose whether you want to disable the Let\'s Encrypt selfcheck as it causes false positives for some onfigurations.

'; + $description = 'Chose whether you want to disable the Let\'s Encrypt selfcheck as it causes false positives for some configurations.

'; $question = 'Disable Let\'s Encrypt self-check?
'; $question .= makeyesno('system_disable_le_selfcheck', '1', '0', '0') . '
'; eval("\$return.=\"" . getTemplate("update/preconfigitem") . "\";"); From af55fe5b82593d4fba6f71ed9ad062ec5431c453 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sun, 7 Jan 2018 14:55:25 +0100 Subject: [PATCH 0348/1335] add possibility to add multiple php-fpm instances Signed-off-by: Michael Kaufmann (d00p) --- actions/admin/settings/136.phpfpm.php | 75 --- admin_phpsettings.php | 546 +++++++++++++----- install/froxlor.sql | 32 +- .../updates/froxlor/0.9/update_0.9.inc.php | 70 +++ .../phpinterface/class.phpinterface_fpm.php | 490 ++++++++-------- lib/classes/webserver/class.ConfigIO.php | 205 +++---- lib/classes/webserver/class.WebserverBase.php | 51 +- .../phpconfig/formfield.fpmconfig_add.php | 89 +++ .../phpconfig/formfield.fpmconfig_edit.php | 91 +++ .../phpconfig/formfield.phpconfig_add.php | 6 + .../phpconfig/formfield.phpconfig_edit.php | 6 + lib/navigation/00.froxlor.main.php | 6 + lib/tables.inc.php | 1 + lib/version.inc.php | 2 +- lng/english.lng.php | 6 +- lng/german.lng.php | 6 +- .../jobs/cron_tasks.inc.http.10.apache.php | 330 +++++------ .../jobs/cron_tasks.inc.http.20.lighttpd.php | 11 +- scripts/jobs/cron_tasks.inc.http.30.nginx.php | 11 +- .../Sparkle/admin/phpconfig/fpmconfig_add.tpl | 24 + .../admin/phpconfig/fpmconfig_edit.tpl | 25 + .../Sparkle/admin/phpconfig/fpmdaemons.tpl | 42 ++ .../admin/phpconfig/fpmdaemons_overview.tpl | 17 + .../Sparkle/admin/phpconfig/overview.tpl | 6 +- .../admin/phpconfig/overview_overview.tpl | 6 +- 25 files changed, 1408 insertions(+), 746 deletions(-) create mode 100644 lib/formfields/admin/phpconfig/formfield.fpmconfig_add.php create mode 100644 lib/formfields/admin/phpconfig/formfield.fpmconfig_edit.php create mode 100644 templates/Sparkle/admin/phpconfig/fpmconfig_add.tpl create mode 100644 templates/Sparkle/admin/phpconfig/fpmconfig_edit.tpl create mode 100644 templates/Sparkle/admin/phpconfig/fpmdaemons.tpl create mode 100644 templates/Sparkle/admin/phpconfig/fpmdaemons_overview.tpl diff --git a/actions/admin/settings/136.phpfpm.php b/actions/admin/settings/136.phpfpm.php index bcbe153f..13e733ae 100644 --- a/actions/admin/settings/136.phpfpm.php +++ b/actions/admin/settings/136.phpfpm.php @@ -40,15 +40,6 @@ return array( 'option_options_method' => 'getPhpConfigs', 'save_method' => 'storeSettingField' ), - 'system_phpfpm_configdir' => array( - 'label' => $lng['serversettings']['phpfpm_settings']['configdir'], - 'settinggroup' => 'phpfpm', - 'varname' => 'configdir', - 'type' => 'string', - 'string_type' => 'confdir', - 'default' => '/etc/php-fpm.d/', - 'save_method' => 'storeSettingField' - ), 'system_phpfpm_aliasconfigdir' => array( 'label' => $lng['serversettings']['phpfpm_settings']['aliasconfigdir'], 'settinggroup' => 'phpfpm', @@ -87,72 +78,6 @@ return array( 'default' => '/var/lib/apache2/fastcgi/', 'save_method' => 'storeSettingField' ), - 'system_phpfpm_reload' => array( - 'label' => $lng['serversettings']['phpfpm_settings']['reload'], - 'settinggroup' => 'phpfpm', - 'varname' => 'reload', - 'type' => 'string', - 'default' => '/etc/init.d/php-fpm restart', - 'save_method' => 'storeSettingField' - ), - 'system_phpfpm_pm' => array( - 'label' => $lng['serversettings']['phpfpm_settings']['pm'], - 'settinggroup' => 'phpfpm', - 'varname' => 'pm', - 'type' => 'option', - 'default' => 'static', - 'option_mode' => 'one', - 'option_options' => array('static' => 'static', 'dynamic' => 'dynamic', 'ondemand' => 'ondemand'), - 'save_method' => 'storeSettingField' - ), - 'system_phpfpm_max_children' => array( - 'label' => $lng['serversettings']['phpfpm_settings']['max_children'], - 'settinggroup' => 'phpfpm', - 'varname' => 'max_children', - 'type' => 'int', - 'default' => 1, - 'save_method' => 'storeSettingField' - ), - 'system_phpfpm_start_servers' => array( - 'label' => $lng['serversettings']['phpfpm_settings']['start_servers'], - 'settinggroup' => 'phpfpm', - 'varname' => 'start_servers', - 'type' => 'int', - 'default' => 20, - 'save_method' => 'storeSettingField' - ), - 'system_phpfpm_min_spare_servers' => array( - 'label' => $lng['serversettings']['phpfpm_settings']['min_spare_servers'], - 'settinggroup' => 'phpfpm', - 'varname' => 'min_spare_servers', - 'type' => 'int', - 'default' => 5, - 'save_method' => 'storeSettingField' - ), - 'system_phpfpm_max_spare_servers' => array( - 'label' => $lng['serversettings']['phpfpm_settings']['max_spare_servers'], - 'settinggroup' => 'phpfpm', - 'varname' => 'max_spare_servers', - 'type' => 'int', - 'default' => 35, - 'save_method' => 'storeSettingField' - ), - 'system_phpfpm_max_requests' => array( - 'label' => $lng['serversettings']['phpfpm_settings']['max_requests'], - 'settinggroup' => 'phpfpm', - 'varname' => 'max_requests', - 'type' => 'int', - 'default' => 0, - 'save_method' => 'storeSettingField' - ), - 'system_phpfpm_idle_timeout' => array( - 'label' => $lng['serversettings']['phpfpm_settings']['idle_timeout'], - 'settinggroup' => 'phpfpm', - 'varname' => 'idle_timeout', - 'type' => 'int', - 'default' => 30, - 'save_method' => 'storeSettingField' - ), 'system_phpfpm_use_mod_proxy' => array( 'label' => $lng['phpfpm']['use_mod_proxy'], 'settinggroup' => 'phpfpm', diff --git a/admin_phpsettings.php b/admin_phpsettings.php index 4f7f9824..eb72f741 100644 --- a/admin_phpsettings.php +++ b/admin_phpsettings.php @@ -16,7 +16,6 @@ * @package Panel * */ - define('AREA', 'admin'); require './lib/init.php'; @@ -27,102 +26,107 @@ if (isset($_POST['id'])) { } if ($page == 'overview') { - + if ($action == '') { - + $tablecontent = ''; $count = 0; - $result = Database::query("SELECT * FROM `" . TABLE_PANEL_PHPCONFIGS . "`"); - + $result = Database::query(" + SELECT c.*, fd.description as fpmdesc + FROM `" . TABLE_PANEL_PHPCONFIGS . "` c + LEFT JOIN `" . TABLE_PANEL_FPMDAEMONS . "` fd ON fd.id = c.fpmsettingid + ORDER BY c.description ASC + "); + while ($row = $result->fetch(PDO::FETCH_ASSOC)) { - + $domainresult = false; - $query_params = array('id' => $row['id']); - - $query = "SELECT * FROM `".TABLE_PANEL_DOMAINS."` + $query_params = array( + 'id' => $row['id'] + ); + + $query = "SELECT * FROM `" . TABLE_PANEL_DOMAINS . "` WHERE `phpsettingid` = :id AND `parentdomainid` = '0'"; - - if ((int)$userinfo['domains_see_all'] == 0) { + + if ((int) $userinfo['domains_see_all'] == 0) { $query .= " AND `adminid` = :adminid"; $query_params['adminid'] = $userinfo['adminid']; } - - if ((int)Settings::Get('panel.phpconfigs_hidestdsubdomain') == 1) { + + if ((int) Settings::Get('panel.phpconfigs_hidestdsubdomain') == 1) { $ssdids_res = Database::query(" - SELECT DISTINCT `standardsubdomain` FROM `".TABLE_PANEL_CUSTOMERS."` - WHERE `standardsubdomain` > 0 ORDER BY `standardsubdomain` ASC;" - ); + SELECT DISTINCT `standardsubdomain` FROM `" . TABLE_PANEL_CUSTOMERS . "` + WHERE `standardsubdomain` > 0 ORDER BY `standardsubdomain` ASC;"); $ssdids = array(); while ($ssd = $ssdids_res->fetch(PDO::FETCH_ASSOC)) { $ssdids[] = $ssd['standardsubdomain']; } if (count($ssdids) > 0) { - $query .= " AND `id` NOT IN (".implode(', ', $ssdids).")"; + $query .= " AND `id` NOT IN (" . implode(', ', $ssdids) . ")"; } } - + $domainresult_stmt = Database::prepare($query); Database::pexecute($domainresult_stmt, $query_params); - + $domains = ''; if (Database::num_rows() > 0) { while ($row2 = $domainresult_stmt->fetch(PDO::FETCH_ASSOC)) { - $domains.= $row2['domain'] . '
'; + $domains .= $row2['domain'] . '
'; } } - + // check whether we use that config as froxor-vhost config - if (Settings::Get('system.mod_fcgid_defaultini_ownvhost') == $row['id'] - || Settings::Get('phpfpm.vhost_defaultini') == $row['id'] - ) { + if (Settings::Get('system.mod_fcgid_defaultini_ownvhost') == $row['id'] || Settings::Get('phpfpm.vhost_defaultini') == $row['id']) { $domains .= Settings::Get('system.hostname'); } - + if ($domains == '') { $domains = $lng['admin']['phpsettings']['notused']; } - + // check whether this is our default config - if ((Settings::Get('system.mod_fcgid') == '1' - && Settings::Get('system.mod_fcgid_defaultini') == $row['id']) - || (Settings::Get('phpfpm.enabled') == '1' - && Settings::Get('phpfpm.defaultini') == $row['id']) - ) { - $row['description'] = ''.$row['description'].''; + if ((Settings::Get('system.mod_fcgid') == '1' && Settings::Get('system.mod_fcgid_defaultini') == $row['id']) || (Settings::Get('phpfpm.enabled') == '1' && Settings::Get('phpfpm.defaultini') == $row['id'])) { + $row['description'] = '' . $row['description'] . ''; } - + $count ++; eval("\$tablecontent.=\"" . getTemplate("phpconfig/overview_overview") . "\";"); } - + $log->logAction(ADM_ACTION, LOG_INFO, "php.ini setting overview has been viewed by '" . $userinfo['loginname'] . "'"); eval("echo \"" . getTemplate("phpconfig/overview") . "\";"); } - + if ($action == 'add') { - - if ((int)$userinfo['change_serversettings'] == 1) { - - if (isset($_POST['send']) - && $_POST['send'] == 'send' - ) { + + if ((int) $userinfo['change_serversettings'] == 1) { + + if (isset($_POST['send']) && $_POST['send'] == 'send') { $description = validate($_POST['description'], 'description'); $phpsettings = validate(str_replace("\r\n", "\n", $_POST['phpsettings']), 'phpsettings', '/^[^\0]*$/'); - + if (Settings::Get('system.mod_fcgid') == 1) { $binary = makeCorrectFile(validate($_POST['binary'], 'binary')); $file_extensions = validate($_POST['file_extensions'], 'file_extensions', '/^[a-zA-Z0-9\s]*$/'); - $mod_fcgid_starter = validate($_POST['mod_fcgid_starter'], 'mod_fcgid_starter', '/^[0-9]*$/', '', array('-1', '')); - $mod_fcgid_maxrequests = validate($_POST['mod_fcgid_maxrequests'], 'mod_fcgid_maxrequests', '/^[0-9]*$/', '', array('-1', '')); + $mod_fcgid_starter = validate($_POST['mod_fcgid_starter'], 'mod_fcgid_starter', '/^[0-9]*$/', '', array( + '-1', + '' + )); + $mod_fcgid_maxrequests = validate($_POST['mod_fcgid_maxrequests'], 'mod_fcgid_maxrequests', '/^[0-9]*$/', '', array( + '-1', + '' + )); $mod_fcgid_umask = validate($_POST['mod_fcgid_umask'], 'mod_fcgid_umask', '/^[0-9]*$/'); // disable fpm stuff + $fpm_config_id = 1; $fpm_enableslowlog = 0; $fpm_reqtermtimeout = 0; $fpm_reqslowtimeout = 0; - } - elseif (Settings::Get('phpfpm.enabled') == 1) { - $fpm_enableslowlog = isset($_POST['phpfpm_enable_slowlog']) ? (int)$_POST['phpfpm_enable_slowlog'] : 0; + } elseif (Settings::Get('phpfpm.enabled') == 1) { + $fpm_config_id = intval($_POST['fpmconfig']); + $fpm_enableslowlog = isset($_POST['phpfpm_enable_slowlog']) ? (int) $_POST['phpfpm_enable_slowlog'] : 0; $fpm_reqtermtimeout = validate($_POST['phpfpm_reqtermtimeout'], 'phpfpm_reqtermtimeout', '/^([0-9]+)(|s|m|h|d)$/'); $fpm_reqslowtimeout = validate($_POST['phpfpm_reqslowtimeout'], 'phpfpm_reqslowtimeout', '/^([0-9]+)(|s|m|h|d)$/'); // disable fcgid stuff @@ -132,13 +136,11 @@ if ($page == 'overview') { $mod_fcgid_maxrequests = 0; $mod_fcgid_umask = "022"; } - - if (strlen($description) == 0 - || strlen($description) > 50 - ) { + + if (strlen($description) == 0 || strlen($description) > 50) { standard_error('descriptioninvalid'); } - + $ins_stmt = Database::prepare(" INSERT INTO `" . TABLE_PANEL_PHPCONFIGS . "` SET `description` = :desc, @@ -150,8 +152,8 @@ if ($page == 'overview') { `fpm_slowlog` = :fpmslow, `fpm_reqterm` = :fpmreqterm, `fpm_reqslow` = :fpmreqslow, - `phpsettings` = :phpsettings" - ); + `phpsettings` = :phpsettings, + `fpmsettingid` = :fpmsettingid"); $ins_data = array( 'desc' => $description, 'binary' => $binary, @@ -162,121 +164,128 @@ if ($page == 'overview') { 'fpmslow' => $fpm_enableslowlog, 'fpmreqterm' => $fpm_reqtermtimeout, 'fpmreqslow' => $fpm_reqslowtimeout, - 'phpsettings' => $phpsettings + 'phpsettings' => $phpsettings, + 'fpmsettingid' => $fpm_config_id ); Database::pexecute($ins_stmt, $ins_data); - + inserttask('1'); $log->logAction(ADM_ACTION, LOG_INFO, "php.ini setting with description '" . $description . "' has been created by '" . $userinfo['loginname'] . "'"); - redirectTo($filename, array('page' => $page, 's' => $s)); - + redirectTo($filename, array( + 'page' => $page, + 's' => $s + )); } else { - + $result_stmt = Database::query("SELECT * FROM `" . TABLE_PANEL_PHPCONFIGS . "` WHERE `id` = 1"); $result = $result_stmt->fetch(PDO::FETCH_ASSOC); - - $phpconfig_add_data = include_once dirname(__FILE__).'/lib/formfields/admin/phpconfig/formfield.phpconfig_add.php'; + + $fpmconfigs = ''; + $configs = Database::query("SELECT * FROM `" . TABLE_PANEL_FPMDAEMONS . "` ORDER BY `description` ASC"); + while ($row = $configs->fetch(PDO::FETCH_ASSOC)) { + $fpmconfigs .= makeoption($row['description'], $row['id'], 1, true, true); + } + + $phpconfig_add_data = include_once dirname(__FILE__) . '/lib/formfields/admin/phpconfig/formfield.phpconfig_add.php'; $phpconfig_add_form = htmlform::genHTMLForm($phpconfig_add_data); - + $title = $phpconfig_add_data['phpconfig_add']['title']; $image = $phpconfig_add_data['phpconfig_add']['image']; - + eval("echo \"" . getTemplate("phpconfig/overview_add") . "\";"); } - } else { standard_error('nopermissionsorinvalidid'); } } - + if ($action == 'delete') { - + $result_stmt = Database::prepare(" - SELECT * FROM `" . TABLE_PANEL_PHPCONFIGS . "` WHERE `id` = :id" - ); - $result = Database::pexecute_first($result_stmt, array('id' => $id)); - - if ((Settings::Get('system.mod_fcgid') == '1' - && Settings::Get('system.mod_fcgid_defaultini_ownvhost') == $id) - || (Settings::Get('phpfpm.enabled') == '1' - && Settings::Get('phpfpm.vhost_defaultini') == $id) - ) { + SELECT * FROM `" . TABLE_PANEL_PHPCONFIGS . "` WHERE `id` = :id"); + $result = Database::pexecute_first($result_stmt, array( + 'id' => $id + )); + + if ((Settings::Get('system.mod_fcgid') == '1' && Settings::Get('system.mod_fcgid_defaultini_ownvhost') == $id) || (Settings::Get('phpfpm.enabled') == '1' && Settings::Get('phpfpm.vhost_defaultini') == $id)) { standard_error('cannotdeletehostnamephpconfig'); } - - if ((Settings::Get('system.mod_fcgid') == '1' - && Settings::Get('system.mod_fcgid_defaultini') == $id) - || (Settings::Get('phpfpm.enabled') == '1' - && Settings::Get('phpfpm.defaultini') == $id) - ) { + + if ((Settings::Get('system.mod_fcgid') == '1' && Settings::Get('system.mod_fcgid_defaultini') == $id) || (Settings::Get('phpfpm.enabled') == '1' && Settings::Get('phpfpm.defaultini') == $id)) { standard_error('cannotdeletedefaultphpconfig'); } - - if ($result['id'] != 0 - && $result['id'] == $id - && (int)$userinfo['change_serversettings'] == 1 - && $id != 1 // cannot delete the default php.config - ) { - - if (isset($_POST['send']) - && $_POST['send'] == 'send' - ) { + + if ($result['id'] != 0 && $result['id'] == $id && (int) $userinfo['change_serversettings'] == 1 && $id != 1) // cannot delete the default php.config + { + + if (isset($_POST['send']) && $_POST['send'] == 'send') { // set php-config to default for all domains using the // config that is to be deleted $upd_stmt = Database::prepare(" UPDATE `" . TABLE_PANEL_DOMAINS . "` SET - `phpsettingid` = '1' WHERE `phpsettingid` = :id" - ); - Database::pexecute($upd_stmt, array('id' => $id)); - + `phpsettingid` = '1' WHERE `phpsettingid` = :id"); + Database::pexecute($upd_stmt, array( + 'id' => $id + )); + $del_stmt = Database::prepare(" - DELETE FROM `" . TABLE_PANEL_PHPCONFIGS . "` WHERE `id` = :id" - ); - Database::pexecute($del_stmt, array('id' => $id)); - + DELETE FROM `" . TABLE_PANEL_PHPCONFIGS . "` WHERE `id` = :id"); + Database::pexecute($del_stmt, array( + 'id' => $id + )); + inserttask('1'); - $log->logAction(ADM_ACTION, LOG_INFO, "php.ini setting with id #" . (int)$id . " has been deleted by '" . $userinfo['loginname'] . "'"); - redirectTo($filename, array('page' => $page, 's' => $s)); - + $log->logAction(ADM_ACTION, LOG_INFO, "php.ini setting with id #" . (int) $id . " has been deleted by '" . $userinfo['loginname'] . "'"); + redirectTo($filename, array( + 'page' => $page, + 's' => $s + )); } else { - ask_yesno('phpsetting_reallydelete', $filename, array('id' => $id, 'page' => $page, 'action' => $action), $result['description']); + ask_yesno('phpsetting_reallydelete', $filename, array( + 'id' => $id, + 'page' => $page, + 'action' => $action + ), $result['description']); } } else { standard_error('nopermissionsorinvalidid'); } } - + if ($action == 'edit') { - + $result_stmt = Database::prepare(" - SELECT * FROM `" . TABLE_PANEL_PHPCONFIGS . "` WHERE `id` = :id" - ); - $result = Database::pexecute_first($result_stmt, array('id' => $id)); - - if ($result['id'] != 0 - && $result['id'] == $id - && (int)$userinfo['change_serversettings'] == 1 - ) { - - if (isset($_POST['send']) - && $_POST['send'] == 'send' - ) { + SELECT * FROM `" . TABLE_PANEL_PHPCONFIGS . "` WHERE `id` = :id"); + $result = Database::pexecute_first($result_stmt, array( + 'id' => $id + )); + + if ($result['id'] != 0 && $result['id'] == $id && (int) $userinfo['change_serversettings'] == 1) { + + if (isset($_POST['send']) && $_POST['send'] == 'send') { $description = validate($_POST['description'], 'description'); $phpsettings = validate(str_replace("\r\n", "\n", $_POST['phpsettings']), 'phpsettings', '/^[^\0]*$/'); - + if (Settings::Get('system.mod_fcgid') == 1) { $binary = makeCorrectFile(validate($_POST['binary'], 'binary')); $file_extensions = validate($_POST['file_extensions'], 'file_extensions', '/^[a-zA-Z0-9\s]*$/'); - $mod_fcgid_starter = validate($_POST['mod_fcgid_starter'], 'mod_fcgid_starter', '/^[0-9]*$/', '', array('-1', '')); - $mod_fcgid_maxrequests = validate($_POST['mod_fcgid_maxrequests'], 'mod_fcgid_maxrequests', '/^[0-9]*$/', '', array('-1', '')); + $mod_fcgid_starter = validate($_POST['mod_fcgid_starter'], 'mod_fcgid_starter', '/^[0-9]*$/', '', array( + '-1', + '' + )); + $mod_fcgid_maxrequests = validate($_POST['mod_fcgid_maxrequests'], 'mod_fcgid_maxrequests', '/^[0-9]*$/', '', array( + '-1', + '' + )); $mod_fcgid_umask = validate($_POST['mod_fcgid_umask'], 'mod_fcgid_umask', '/^[0-9]*$/'); // disable fpm stuff + $fpm_config_id = 1; $fpm_enableslowlog = 0; $fpm_reqtermtimeout = 0; $fpm_reqslowtimeout = 0; - } - elseif (Settings::Get('phpfpm.enabled') == 1) { - $fpm_enableslowlog = isset($_POST['phpfpm_enable_slowlog']) ? (int)$_POST['phpfpm_enable_slowlog'] : 0; + } elseif (Settings::Get('phpfpm.enabled') == 1) { + $fpm_config_id = intval($_POST['fpmconfig']); + $fpm_enableslowlog = isset($_POST['phpfpm_enable_slowlog']) ? (int) $_POST['phpfpm_enable_slowlog'] : 0; $fpm_reqtermtimeout = validate($_POST['phpfpm_reqtermtimeout'], 'phpfpm_reqtermtimeout', '/^([0-9]+)(|s|m|h|d)$/'); $fpm_reqslowtimeout = validate($_POST['phpfpm_reqslowtimeout'], 'phpfpm_reqslowtimeout', '/^([0-9]+)(|s|m|h|d)$/'); // disable fcgid stuff @@ -286,13 +295,11 @@ if ($page == 'overview') { $mod_fcgid_maxrequests = 0; $mod_fcgid_umask = "022"; } - - if (strlen($description) == 0 - || strlen($description) > 50 - ) { + + if (strlen($description) == 0 || strlen($description) > 50) { standard_error('descriptioninvalid'); } - + $upd_stmt = Database::prepare(" UPDATE `" . TABLE_PANEL_PHPCONFIGS . "` SET `description` = :desc, @@ -304,39 +311,284 @@ if ($page == 'overview') { `fpm_slowlog` = :fpmslow, `fpm_reqterm` = :fpmreqterm, `fpm_reqslow` = :fpmreqslow, - `phpsettings` = :phpsettings - WHERE `id` = :id" - ); + `phpsettings` = :phpsettings, + `fpmsettingid` = :fpmsettingid + WHERE `id` = :id"); $upd_data = array( - 'desc' => $description, - 'binary' => $binary, - 'fext' => $file_extensions, - 'starter' => $mod_fcgid_starter, - 'mreq' => $mod_fcgid_maxrequests, - 'umask' => $mod_fcgid_umask, - 'fpmslow' => $fpm_enableslowlog, - 'fpmreqterm' => $fpm_reqtermtimeout, - 'fpmreqslow' => $fpm_reqslowtimeout, - 'phpsettings' => $phpsettings, - 'id' => $id + 'desc' => $description, + 'binary' => $binary, + 'fext' => $file_extensions, + 'starter' => $mod_fcgid_starter, + 'mreq' => $mod_fcgid_maxrequests, + 'umask' => $mod_fcgid_umask, + 'fpmslow' => $fpm_enableslowlog, + 'fpmreqterm' => $fpm_reqtermtimeout, + 'fpmreqslow' => $fpm_reqslowtimeout, + 'phpsettings' => $phpsettings, + 'fpmsettingid' => $fpm_config_id, + 'id' => $id ); Database::pexecute($upd_stmt, $upd_data); - + inserttask('1'); $log->logAction(ADM_ACTION, LOG_INFO, "php.ini setting with description '" . $description . "' has been changed by '" . $userinfo['loginname'] . "'"); - redirectTo($filename, array('page' => $page, 's' => $s)); - + redirectTo($filename, array( + 'page' => $page, + 's' => $s + )); } else { - $phpconfig_edit_data = include_once dirname(__FILE__).'/lib/formfields/admin/phpconfig/formfield.phpconfig_edit.php'; + $fpmconfigs = ''; + $configs = Database::query("SELECT * FROM `" . TABLE_PANEL_FPMDAEMONS . "` ORDER BY `description` ASC"); + while ($row = $configs->fetch(PDO::FETCH_ASSOC)) { + $fpmconfigs .= makeoption($row['description'], $row['id'], $id, true, true); + } + + $phpconfig_edit_data = include_once dirname(__FILE__) . '/lib/formfields/admin/phpconfig/formfield.phpconfig_edit.php'; $phpconfig_edit_form = htmlform::genHTMLForm($phpconfig_edit_data); $title = $phpconfig_edit_data['phpconfig_edit']['title']; $image = $phpconfig_edit_data['phpconfig_edit']['image']; - + eval("echo \"" . getTemplate("phpconfig/overview_edit") . "\";"); } + } else { + standard_error('nopermissionsorinvalidid'); + } + } +} elseif ($page == 'fpmdaemons') { + + if ($action == '') { + + $tablecontent = ''; + $count = 0; + $result = Database::query("SELECT * FROM `" . TABLE_PANEL_FPMDAEMONS . "` ORDER BY `description` ASC"); + + while ($row = $result->fetch(PDO::FETCH_ASSOC)) { + $query_params = array( + 'id' => $row['id'] + ); + + $query = "SELECT * FROM `" . TABLE_PANEL_PHPCONFIGS . "` WHERE `fpmsettingid` = :id"; + + $configresult_stmt = Database::prepare($query); + Database::pexecute($configresult_stmt, $query_params); + + $configs = ''; + if (Database::num_rows() > 0) { + while ($row2 = $configresult_stmt->fetch(PDO::FETCH_ASSOC)) { + $configs .= $row2['description'] . '
'; + } + } + + if ($configs == '') { + $configs = $lng['admin']['phpsettings']['notused']; + } + + $count ++; + eval("\$tablecontent.=\"" . getTemplate("phpconfig/fpmdaemons_overview") . "\";"); + } + + $log->logAction(ADM_ACTION, LOG_INFO, "fpm daemons setting overview has been viewed by '" . $userinfo['loginname'] . "'"); + eval("echo \"" . getTemplate("phpconfig/fpmdaemons") . "\";"); + } + + if ($action == 'add') { + + if ((int) $userinfo['change_serversettings'] == 1) { + + if (isset($_POST['send']) && $_POST['send'] == 'send') { + $description = validate($_POST['description'], 'description'); + $reload_cmd = validate($_POST['reload_cmd'], 'reload_cmd'); + $config_dir = validate($_POST['config_dir'], 'config_dir'); + $pm = validate($_POST['pm'], 'pm'); + $max_children = isset($_POST['max_children']) ? (int) $_POST['max_children'] : 0; + $start_servers = isset($_POST['start_servers']) ? (int) $_POST['start_servers'] : 0; + $min_spare_servers = isset($_POST['min_spare_servers']) ? (int) $_POST['min_spare_servers'] : 0; + $max_spare_servers = isset($_POST['max_spare_servers']) ? (int) $_POST['max_spare_servers'] : 0; + $max_requests = isset($_POST['max_requests']) ? (int) $_POST['max_requests'] : 0; + $idle_timeout = isset($_POST['idle_timeout']) ? (int) $_POST['idle_timeout'] : 0; + + if (strlen($description) == 0 || strlen($description) > 50) { + standard_error('descriptioninvalid'); + } + + $ins_stmt = Database::prepare(" + INSERT INTO `" . TABLE_PANEL_FPMDAEMONS . "` SET + `description` = :desc, + `reload_cmd` = :reload_cmd, + `config_dir` = :config_dir, + `pm` = :pm, + `max_children` = :max_children, + `start_servers` = :start_servers, + `min_spare_servers` = :min_spare_servers, + `max_spare_servers` = :max_spare_servers, + `max_requests` = :max_requests, + `idle_timeout` = :idle_timeout + "); + $ins_data = array( + 'desc' => $description, + 'reload_cmd' => $reload_cmd, + 'config_dir' => makeCorrectDir($config_dir), + 'pm' => pm, + 'max_children' => $max_children, + 'start_servers' => $start_servers, + 'min_spare_servers' => $min_spare_servers, + 'max_spare_servers' => $max_spare_servers, + 'max_requests' => $max_requests, + 'idle_timeout' => $idle_timeout + ); + Database::pexecute($ins_stmt, $ins_data); + + inserttask('1'); + $log->logAction(ADM_ACTION, LOG_INFO, "fpm-daemon setting with description '" . $description . "' has been created by '" . $userinfo['loginname'] . "'"); + redirectTo($filename, array( + 'page' => $page, + 's' => $s + )); + } else { + + $pm_select = makeoption('static', 'static', 'static', true, true); + $pm_select.= makeoption('dynamic', 'dynamic', 'static', true, true); + $pm_select.= makeoption('ondemand', 'ondemand', 'static', true, true); + + $fpmconfig_add_data = include_once dirname(__FILE__) . '/lib/formfields/admin/phpconfig/formfield.fpmconfig_add.php'; + $fpmconfig_add_form = htmlform::genHTMLForm($fpmconfig_add_data); + + $title = $fpmconfig_add_data['fpmconfig_add']['title']; + $image = $fpmconfig_add_data['fpmconfig_add']['image']; + + eval("echo \"" . getTemplate("phpconfig/fpmconfig_add") . "\";"); + } + } else { + standard_error('nopermissionsorinvalidid'); + } + } + + if ($action == 'delete') { + + $result_stmt = Database::prepare(" + SELECT * FROM `" . TABLE_PANEL_FPMDAEMONS . "` WHERE `id` = :id"); + $result = Database::pexecute_first($result_stmt, array( + 'id' => $id + )); + + if ($id == 1) { + standard_error('cannotdeletedefaultphpconfig'); + } + + if ($result['id'] != 0 && $result['id'] == $id && (int) $userinfo['change_serversettings'] == 1 && $id != 1) // cannot delete the default php.config + { + + if (isset($_POST['send']) && $_POST['send'] == 'send') { + // set default fpm daemon config for all php-config that use this config that is to be deleted + $upd_stmt = Database::prepare(" + UPDATE `" . TABLE_PANEL_PHPCONFIGS . "` SET + `phpsettingid` = '1' WHERE `phpsettingid` = :id"); + Database::pexecute($upd_stmt, array( + 'id' => $id + )); + + $del_stmt = Database::prepare(" + DELETE FROM `" . TABLE_PANEL_FPMDAEMONS . "` WHERE `id` = :id"); + Database::pexecute($del_stmt, array( + 'id' => $id + )); + + inserttask('1'); + $log->logAction(ADM_ACTION, LOG_INFO, "fpm-daemon setting with id #" . (int) $id . " has been deleted by '" . $userinfo['loginname'] . "'"); + redirectTo($filename, array( + 'page' => $page, + 's' => $s + )); + } else { + ask_yesno('fpmsetting_reallydelete', $filename, array( + 'id' => $id, + 'page' => $page, + 'action' => $action + ), $result['description']); + } + } else { + standard_error('nopermissionsorinvalidid'); + } + } + + if ($action == 'edit') { + + $result_stmt = Database::prepare(" + SELECT * FROM `" . TABLE_PANEL_FPMDAEMONS . "` WHERE `id` = :id"); + $result = Database::pexecute_first($result_stmt, array( + 'id' => $id + )); + + if ($result['id'] != 0 && $result['id'] == $id && (int) $userinfo['change_serversettings'] == 1) { + + if (isset($_POST['send']) && $_POST['send'] == 'send') { + $description = validate($_POST['description'], 'description'); + $reload_cmd = validate($_POST['reload_cmd'], 'reload_cmd'); + $config_dir = validate($_POST['config_dir'], 'config_dir'); + $pm = validate($_POST['pm'], 'pm'); + $max_children = isset($_POST['max_children']) ? (int) $_POST['max_children'] : $result['max_children']; + $start_servers = isset($_POST['start_servers']) ? (int) $_POST['start_servers'] : $result['start_servers']; + $min_spare_servers = isset($_POST['min_spare_servers']) ? (int) $_POST['min_spare_servers'] : $result['min_spare_servers']; + $max_spare_servers = isset($_POST['max_spare_servers']) ? (int) $_POST['max_spare_servers'] : $result['max_spare_servers']; + $max_requests = isset($_POST['max_requests']) ? (int) $_POST['max_requests'] : $result['max_requests']; + $idle_timeout = isset($_POST['idle_timeout']) ? (int) $_POST['idle_timeout'] : $result['idle_timeout']; + + if (strlen($description) == 0 || strlen($description) > 50) { + standard_error('descriptioninvalid'); + } + + $upd_stmt = Database::prepare(" + UPDATE `" . TABLE_PANEL_PHPCONFIGS . "` SET + `description` = :desc, + `reload_cmd` = :reload_cmd, + `config_dir` = :config_dir, + `pm` = :pm, + `max_children` = :max_children, + `start_servers` = :start_servers, + `min_spare_servers` = :min_spare_servers, + `max_spare_servers` = :max_spare_servers, + `max_requests` = :max_requests, + `idle_timeout` = :idle_timeout + WHERE `id` = :id + "); + $upd_data = array( + 'desc' => $description, + 'reload_cmd' => $reload_cmd, + 'config_dir' => makeCorrectDir($config_dir), + 'pm' => pm, + 'max_children' => $max_children, + 'start_servers' => $start_servers, + 'min_spare_servers' => $min_spare_servers, + 'max_spare_servers' => $max_spare_servers, + 'max_requests' => $max_requests, + 'idle_timeout' => $idle_timeout, + 'id' => $id + ); + Database::pexecute($upd_stmt, $upd_data); + + inserttask('1'); + $log->logAction(ADM_ACTION, LOG_INFO, "fpm-daemon setting with description '" . $description . "' has been changed by '" . $userinfo['loginname'] . "'"); + redirectTo($filename, array( + 'page' => $page, + 's' => $s + )); + } else { + + $pm_select = makeoption('static', 'static', $result['pm'], true, true); + $pm_select.= makeoption('dynamic', 'dynamic', $result['pm'], true, true); + $pm_select.= makeoption('ondemand', 'ondemand', $result['pm'], true, true); + + $fpmconfig_edit_data = include_once dirname(__FILE__) . '/lib/formfields/admin/phpconfig/formfield.fpmconfig_edit.php'; + $fpmconfig_edit_form = htmlform::genHTMLForm($fpmconfig_edit_data); + + $title = $fpmconfig_edit_data['fpmconfig_edit']['title']; + $image = $fpmconfig_edit_data['fpmconfig_edit']['image']; + + eval("echo \"" . getTemplate("phpconfig/fpmconfig_edit") . "\";"); + } } else { standard_error('nopermissionsorinvalidid'); } diff --git a/install/froxlor.sql b/install/froxlor.sql index e90d2112..4883fe14 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -587,7 +587,7 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES ('panel', 'password_special_char', '!?<>§$%+#=@'), ('panel', 'customer_hide_options', ''), ('panel', 'version', '0.9.38.8'), - ('panel', 'db_version', '201712310'); + ('panel', 'db_version', '201801070'); DROP TABLE IF EXISTS `panel_tasks`; @@ -753,6 +753,32 @@ CREATE TABLE IF NOT EXISTS `panel_syslog` ( ) ENGINE=MyISAM CHARSET=utf8 COLLATE=utf8_general_ci; + +DROP TABLE IF EXISTS `panel_fpmdaemons`; +CREATE TABLE `panel_fpmdaemons` ( + `id` int(11) unsigned NOT NULL auto_increment, + `description` varchar(50) NOT NULL, + `reload_cmd` varchar(255) NOT NULL, + `config_dir` varchar(255) NOT NULL, + `pm` varchar(15) NOT NULL DEFAULT 'static', + `max_children` int(4) NOT NULL DEFAULT '1', + `start_servers` int(4) NOT NULL DEFAULT '20', + `min_spare_servers` int(4) NOT NULL DEFAULT '5', + `max_spare_servers` int(4) NOT NULL DEFAULT '35', + `max_requests` int(4) NOT NULL DEFAULT '0', + `idle_timeout` int(4) NOT NULL DEFAULT '30', + PRIMARY KEY (`id`), + UNIQUE KEY `reload` (`reload_cmd`), + UNIQUE KEY `config` (`config_dir`) +) ENGINE=MyISAM CHARSET=utf8 COLLATE=utf8_general_ci; + + + +INSERT INTO `panel_fpmdaemons` (`id`, `description`, `reload_cmd`, `config_dir`) VALUES +(1, 'System default', 'service php7.0-fpm restart', '/etc/php/7.0/fpm/pool.d/'); + + + DROP TABLE IF EXISTS `panel_phpconfigs`; CREATE TABLE `panel_phpconfigs` ( `id` int(11) unsigned NOT NULL auto_increment, @@ -766,7 +792,9 @@ CREATE TABLE `panel_phpconfigs` ( `fpm_reqterm` varchar(15) NOT NULL default '60s', `fpm_reqslow` varchar(15) NOT NULL default '5s', `phpsettings` text NOT NULL, - PRIMARY KEY (`id`) + `fpmsettingid` int(11) NOT NULL DEFAULT '1', + PRIMARY KEY (`id`), + KEY `fpmsettingid` (`fpmsettingid`) ) ENGINE=MyISAM CHARSET=utf8 COLLATE=utf8_general_ci; diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index 57f57a7d..8ae1c344 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -3646,3 +3646,73 @@ if (isDatabaseVersion('201708240')) { showUpdateStep("Updating from 0.9.38.7 to 0.9.38.8", false); updateToVersion('0.9.38.8'); } + +if (isDatabaseVersion('201712310')) { + + showUpdateStep("Adding field for fpm-daemon configs"); + Database::query("ALTER TABLE `" . TABLE_PANEL_PHPCONFIGS . "` ADD `fpmsettingid` int(11) NOT NULL DEFAULT '1';"); + lastStepStatus(0); + + showUpdateStep("Adding new fpm-daemons table"); + Database::query("DROP TABLE IF EXISTS `panel_fpmdaemons`;"); + $sql = "CREATE TABLE `panel_fpmdaemons` ( + `id` int(11) unsigned NOT NULL auto_increment, + `description` varchar(50) NOT NULL, + `reload_cmd` varchar(255) NOT NULL, + `config_dir` varchar(255) NOT NULL, + `pm` varchar(15) NOT NULL DEFAULT 'static', + `max_children` int(4) NOT NULL DEFAULT '1', + `start_servers` int(4) NOT NULL DEFAULT '20', + `min_spare_servers` int(4) NOT NULL DEFAULT '5', + `max_spare_servers` int(4) NOT NULL DEFAULT '35', + `max_requests` int(4) NOT NULL DEFAULT '0', + `idle_timeout` int(4) NOT NULL DEFAULT '30', + PRIMARY KEY (`id`), + UNIQUE KEY `reload` (`reload_cmd`), + UNIQUE KEY `config` (`config_dir`) + ) ENGINE=MyISAM CHARSET=utf8 COLLATE=utf8_general_ci;"; + Database::query($sql); + lastStepStatus(0); + + showUpdateStep("Converting php-fpm settings to new layout"); + $ins_stmt = Database::prepare(" + INSERT INTO `panel_fpmdaemons` SET + `id` = 1, + `description` = 'System default', + `reload_cmd` = :reloadcmd, + `config_dir` = :confdir, + `pm` = :pm, + `max_children` = :maxc, + `start_servers` = :starts, + `min_spare_servers` = :minss, + `max_spare_servers` = :maxss, + `max_requests` = :maxr, + `idle_timeout` = :it + "); + Database::pexecute($ins_stmt, array( + 'reloadcmd' => Settings::Get('phpfpm.reload'), + 'confdir' => Settings::Get('phpfpm.configdir'), + 'pm' => Settings::Get('phpfpm.pm'), + 'maxc' => Settings::Get('phpfpm.max_children'), + 'starts' => Settings::Get('phpfpm.start_servers'), + 'minss' => Settings::Get('phpfpm.min_spare_servers'), + 'maxss' => Settings::Get('phpfpm.max_spare_servers'), + 'maxr' => Settings::Get('phpfpm.max_requests'), + 'it' => Settings::Get('phpfpm.idle_timeout') + )); + lastStepStatus(0); + + showUpdateStep("Deleting unneeded settings"); + Database::query("DELETE FROM `".TABLE_PANEL_SETTINGS."` WHERE `settinggroup` = 'phpfpm' AND `varname` = 'reload'"); + Database::query("DELETE FROM `".TABLE_PANEL_SETTINGS."` WHERE `settinggroup` = 'phpfpm' AND `varname` = 'configdir'"); + Database::query("DELETE FROM `".TABLE_PANEL_SETTINGS."` WHERE `settinggroup` = 'phpfpm' AND `varname` = 'pm'"); + Database::query("DELETE FROM `".TABLE_PANEL_SETTINGS."` WHERE `settinggroup` = 'phpfpm' AND `varname` = 'max_children'"); + Database::query("DELETE FROM `".TABLE_PANEL_SETTINGS."` WHERE `settinggroup` = 'phpfpm' AND `varname` = 'start_servers'"); + Database::query("DELETE FROM `".TABLE_PANEL_SETTINGS."` WHERE `settinggroup` = 'phpfpm' AND `varname` = 'min_spare_servers'"); + Database::query("DELETE FROM `".TABLE_PANEL_SETTINGS."` WHERE `settinggroup` = 'phpfpm' AND `varname` = 'max_spare_servers'"); + Database::query("DELETE FROM `".TABLE_PANEL_SETTINGS."` WHERE `settinggroup` = 'phpfpm' AND `varname` = 'max_requests'"); + Database::query("DELETE FROM `".TABLE_PANEL_SETTINGS."` WHERE `settinggroup` = 'phpfpm' AND `varname` = 'idle_timeout'"); + lastStepStatus(0); + + updateToDbVersion('201801070'); +} diff --git a/lib/classes/phpinterface/class.phpinterface_fpm.php b/lib/classes/phpinterface/class.phpinterface_fpm.php index 5c39d00b..b4a577ac 100644 --- a/lib/classes/phpinterface/class.phpinterface_fpm.php +++ b/lib/classes/phpinterface/class.phpinterface_fpm.php @@ -18,19 +18,28 @@ * @since 0.9.16 * */ - -class phpinterface_fpm { +class phpinterface_fpm +{ /** * Domain-Data array + * * @var array - */ + */ private $_domain = array(); /** - * Admin-Date cache array + * fpm config + * * @var array - */ + */ + private $_fpm_cfg = array(); + + /** + * Admin-Date cache array + * + * @var array + */ private $_admin_cache = array(); /** @@ -38,119 +47,129 @@ class phpinterface_fpm { * Mostly taken from http://php.net/manual/en/ini.list.php * * @var array - */ + */ private $_ini = array( - 'php_value' => array( - 'auto_append_file', - 'auto_prepend_file', - 'date.timezone', - 'default_charset', - 'error_reporting', - 'include_path', - 'log_errors_max_len', - 'mail.log', - 'max_execution_time', - 'session.cookie_domain', - 'session.cookie_lifetime', - 'session.cookie_path', - 'session.name', - 'session.serialize_handler', - 'upload_max_filesize', - 'xmlrpc_error_number', - 'session.auto_start', - 'always_populate_raw_post_data', - 'suhosin.session.cryptkey', - 'suhosin.session.cryptraddr', - 'suhosin.session.checkraddr', - 'suhosin.cookie.cryptkey', - 'suhosin.cookie.plainlist', - 'suhosin.cookie.cryptraddr', - 'suhosin.cookie.checkraddr', - 'suhosin.executor.func.blacklist', - 'suhosin.executor.eval.whitelist' - ), - 'php_flag' => array( - 'asp_tags', - 'display_errors', - 'display_startup_errors', - 'html_errors', - 'log_errors', - 'magic_quotes_gpc', - 'magic_quotes_runtime', - 'magic_quotes_sybase', - 'mail.add_x_header', - 'session.cookie_secure', - 'session.use_cookies', - 'short_open_tag', - 'track_errors', - 'xmlrpc_errors', - 'suhosin.simulation', - 'suhosin.session.encrypt', - 'suhosin.session.cryptua', - 'suhosin.session.cryptdocroot', - 'suhosin.cookie.encrypt', - 'suhosin.cookie.cryptua', - 'suhosin.cookie.cryptdocroot', - 'suhosin.executor.disable_eval', - 'mbstring.func_overload' - ), - 'php_admin_value' => array( - 'cgi.redirect_status_env', - 'date.timezone', - 'disable_classes', - 'disable_functions', - 'error_log', - 'gpc_order', - 'max_input_time', - 'max_input_vars', - 'memory_limit', - 'open_basedir', - 'output_buffering', - 'post_max_size', - 'precision', - 'sendmail_path', - 'session.gc_divisor', - 'session.gc_probability', - 'variables_order', - 'opcache.log_verbosity_level', - 'opcache.restrict_api', - 'opcache.revalidate_freq', - 'opcache.max_accelerated_files', - 'opcache.memory_consumption', - 'opcache.interned_strings_buffer' - ), - 'php_admin_flag' => array( - 'allow_call_time_pass_reference', - 'allow_url_fopen', - 'allow_url_include', - 'auto_detect_line_endings', - 'cgi.fix_pathinfo', - 'cgi.force_redirect', - 'enable_dl', - 'expose_php', - 'file_uploads', - 'ignore_repeated_errors', - 'ignore_repeated_source', - 'log_errors', - 'register_argc_argv', - 'report_memleaks', - 'opcache.enable', - 'opcache.consistency_checks', - 'opcache.dups_fix', - 'opcache.load_comments', - 'opcache.revalidate_path', - 'opcache.save_comments', - 'opcache.use_cwd', - 'opcache.validate_timestamps', - 'opcache.fast_shutdown' - ) + 'php_value' => array( + 'auto_append_file', + 'auto_prepend_file', + 'date.timezone', + 'default_charset', + 'error_reporting', + 'include_path', + 'log_errors_max_len', + 'mail.log', + 'max_execution_time', + 'session.cookie_domain', + 'session.cookie_lifetime', + 'session.cookie_path', + 'session.name', + 'session.serialize_handler', + 'upload_max_filesize', + 'xmlrpc_error_number', + 'session.auto_start', + 'always_populate_raw_post_data', + 'suhosin.session.cryptkey', + 'suhosin.session.cryptraddr', + 'suhosin.session.checkraddr', + 'suhosin.cookie.cryptkey', + 'suhosin.cookie.plainlist', + 'suhosin.cookie.cryptraddr', + 'suhosin.cookie.checkraddr', + 'suhosin.executor.func.blacklist', + 'suhosin.executor.eval.whitelist' + ), + 'php_flag' => array( + 'asp_tags', + 'display_errors', + 'display_startup_errors', + 'html_errors', + 'log_errors', + 'magic_quotes_gpc', + 'magic_quotes_runtime', + 'magic_quotes_sybase', + 'mail.add_x_header', + 'session.cookie_secure', + 'session.use_cookies', + 'short_open_tag', + 'track_errors', + 'xmlrpc_errors', + 'suhosin.simulation', + 'suhosin.session.encrypt', + 'suhosin.session.cryptua', + 'suhosin.session.cryptdocroot', + 'suhosin.cookie.encrypt', + 'suhosin.cookie.cryptua', + 'suhosin.cookie.cryptdocroot', + 'suhosin.executor.disable_eval', + 'mbstring.func_overload' + ), + 'php_admin_value' => array( + 'cgi.redirect_status_env', + 'date.timezone', + 'disable_classes', + 'disable_functions', + 'error_log', + 'gpc_order', + 'max_input_time', + 'max_input_vars', + 'memory_limit', + 'open_basedir', + 'output_buffering', + 'post_max_size', + 'precision', + 'sendmail_path', + 'session.gc_divisor', + 'session.gc_probability', + 'variables_order', + 'opcache.log_verbosity_level', + 'opcache.restrict_api', + 'opcache.revalidate_freq', + 'opcache.max_accelerated_files', + 'opcache.memory_consumption', + 'opcache.interned_strings_buffer' + ), + 'php_admin_flag' => array( + 'allow_call_time_pass_reference', + 'allow_url_fopen', + 'allow_url_include', + 'auto_detect_line_endings', + 'cgi.fix_pathinfo', + 'cgi.force_redirect', + 'enable_dl', + 'expose_php', + 'file_uploads', + 'ignore_repeated_errors', + 'ignore_repeated_source', + 'log_errors', + 'register_argc_argv', + 'report_memleaks', + 'opcache.enable', + 'opcache.consistency_checks', + 'opcache.dups_fix', + 'opcache.load_comments', + 'opcache.revalidate_path', + 'opcache.save_comments', + 'opcache.use_cwd', + 'opcache.validate_timestamps', + 'opcache.fast_shutdown' + ) ); /** * main constructor - */ - public function __construct($domain) { + */ + public function __construct($domain) + { $this->_domain = $domain; + $this->_readFpmConfig($domain['fpm_config_id']); + } + + private function _readFpmConfig($fpm_config_id) + { + $stmt = Database::prepare("SELECT * FROM `" . TABLE_PANEL_FPMDAEMONS . "` WHERE `id` = :id"); + $this->_fpm_cfg = Database::pexecute_first($stmt, array( + 'id' => $fpm_config_id + )); } /** @@ -158,47 +177,47 @@ class phpinterface_fpm { * * @param array $phpconfig */ - public function createConfig($phpconfig) { - + public function createConfig($phpconfig) + { $fh = @fopen($this->getConfigFile(), 'w'); - + if ($fh) { - $fpm_pm = Settings::Get('phpfpm.pm'); - $fpm_children = (int)Settings::Get('phpfpm.max_children'); - $fpm_start_servers = (int)Settings::Get('phpfpm.start_servers'); - $fpm_min_spare_servers = (int)Settings::Get('phpfpm.min_spare_servers'); - $fpm_max_spare_servers = (int)Settings::Get('phpfpm.max_spare_servers'); - $fpm_requests = (int)Settings::Get('phpfpm.max_requests'); - $fpm_process_idle_timeout = (int)Settings::Get('phpfpm.idle_timeout'); - + $fpm_pm = $this->_fpm_cfg['pm']; + $fpm_children = (int) $this->_fpm_cfg['max_children']; + $fpm_start_servers = (int) $this->_fpm_cfg['start_servers']; + $fpm_min_spare_servers = (int) $this->_fpm_cfg['min_spare_servers']; + $fpm_max_spare_servers = (int) $this->_fpm_cfg['max_spare_servers']; + $fpm_requests = (int) $this->_fpm_cfg['max_requests']; + $fpm_process_idle_timeout = (int) $this->_fpm_cfg['idle_timeout']; + if ($fpm_children == 0) { $fpm_children = 1; } - - $fpm_config = ';PHP-FPM configuration for "'.$this->_domain['domain'].'" created on ' . date("Y.m.d H:i:s") . "\n"; - $fpm_config.= '['.$this->_domain['domain'].']'."\n"; - $fpm_config.= 'listen = '.$this->getSocketFile()."\n"; + + $fpm_config = ';PHP-FPM configuration for "' . $this->_domain['domain'] . '" created on ' . date("Y.m.d H:i:s") . "\n"; + $fpm_config .= '[' . $this->_domain['domain'] . ']' . "\n"; + $fpm_config .= 'listen = ' . $this->getSocketFile() . "\n"; if ($this->_domain['loginname'] == 'froxlor.panel') { - $fpm_config.= 'listen.owner = '.$this->_domain['guid']."\n"; - $fpm_config.= 'listen.group = '.$this->_domain['guid']."\n"; + $fpm_config .= 'listen.owner = ' . $this->_domain['guid'] . "\n"; + $fpm_config .= 'listen.group = ' . $this->_domain['guid'] . "\n"; } else { - $fpm_config.= 'listen.owner = '.$this->_domain['loginname']."\n"; - $fpm_config.= 'listen.group = '.$this->_domain['loginname']."\n"; + $fpm_config .= 'listen.owner = ' . $this->_domain['loginname'] . "\n"; + $fpm_config .= 'listen.group = ' . $this->_domain['loginname'] . "\n"; } // see #1418 why this is 0660 - $fpm_config.= 'listen.mode = 0660'."\n"; - + $fpm_config .= 'listen.mode = 0660' . "\n"; + if ($this->_domain['loginname'] == 'froxlor.panel') { - $fpm_config.= 'user = '.$this->_domain['guid']."\n"; - $fpm_config.= 'group = '.$this->_domain['guid']."\n"; + $fpm_config .= 'user = ' . $this->_domain['guid'] . "\n"; + $fpm_config .= 'group = ' . $this->_domain['guid'] . "\n"; } else { - $fpm_config.= 'user = '.$this->_domain['loginname']."\n"; - $fpm_config.= 'group = '.$this->_domain['loginname']."\n"; + $fpm_config .= 'user = ' . $this->_domain['loginname'] . "\n"; + $fpm_config .= 'group = ' . $this->_domain['loginname'] . "\n"; } - - $fpm_config.= 'pm = '.$fpm_pm."\n"; - $fpm_config.= 'pm.max_children = '.$fpm_children."\n"; - + + $fpm_config .= 'pm = ' . $fpm_pm . "\n"; + $fpm_config .= 'pm.max_children = ' . $fpm_children . "\n"; + if ($fpm_pm == 'dynamic') { // honor max_children if ($fpm_children < $fpm_min_spare_servers) { @@ -214,35 +233,35 @@ class phpinterface_fpm { if ($fpm_start_servers > $fpm_max_spare_servers) { $fpm_start_servers = $fpm_max_spare_servers; } - $fpm_config.= 'pm.start_servers = '.$fpm_start_servers."\n"; - $fpm_config.= 'pm.min_spare_servers = '.$fpm_min_spare_servers."\n"; - $fpm_config.= 'pm.max_spare_servers = '.$fpm_max_spare_servers."\n"; + $fpm_config .= 'pm.start_servers = ' . $fpm_start_servers . "\n"; + $fpm_config .= 'pm.min_spare_servers = ' . $fpm_min_spare_servers . "\n"; + $fpm_config .= 'pm.max_spare_servers = ' . $fpm_max_spare_servers . "\n"; } elseif ($fpm_pm == 'ondemand') { - $fpm_config.= 'pm.process_idle_timeout = '.$fpm_process_idle_timeout."\n"; + $fpm_config .= 'pm.process_idle_timeout = ' . $fpm_process_idle_timeout . "\n"; } - - $fpm_config.= 'pm.max_requests = '.$fpm_requests."\n"; - + + $fpm_config .= 'pm.max_requests = ' . $fpm_requests . "\n"; + // possible slowlog configs if ($phpconfig['fpm_slowlog'] == '1') { - $fpm_config.= 'request_terminate_timeout = ' . $phpconfig['fpm_reqterm'] . "\n"; - $fpm_config.= 'request_slowlog_timeout = ' . $phpconfig['fpm_reqslow'] . "\n"; + $fpm_config .= 'request_terminate_timeout = ' . $phpconfig['fpm_reqterm'] . "\n"; + $fpm_config .= 'request_slowlog_timeout = ' . $phpconfig['fpm_reqslow'] . "\n"; $slowlog = makeCorrectFile(Settings::Get('system.logfiles_directory') . '/' . $this->_domain['loginname'] . '-php-slow.log'); - $fpm_config.= 'slowlog = ' . $slowlog . "\n"; - $fpm_config.= 'catch_workers_output = yes' . "\n"; + $fpm_config .= 'slowlog = ' . $slowlog . "\n"; + $fpm_config .= 'catch_workers_output = yes' . "\n"; } - - $fpm_config.= ';chroot = '.makeCorrectDir($this->_domain['documentroot'])."\n"; - + + $fpm_config .= ';chroot = ' . makeCorrectDir($this->_domain['documentroot']) . "\n"; + $tmpdir = makeCorrectDir(Settings::Get('phpfpm.tmpdir') . '/' . $this->_domain['loginname'] . '/'); - if (!is_dir($tmpdir)) { + if (! is_dir($tmpdir)) { $this->getTempDir(); } - - $fpm_config.= 'env[TMP] = '.$tmpdir."\n"; - $fpm_config.= 'env[TMPDIR] = '.$tmpdir."\n"; - $fpm_config.= 'env[TEMP] = '.$tmpdir."\n"; - + + $fpm_config .= 'env[TMP] = ' . $tmpdir . "\n"; + $fpm_config .= 'env[TMPDIR] = ' . $tmpdir . "\n"; + $fpm_config .= 'env[TEMP] = ' . $tmpdir . "\n"; + $openbasedir = ''; if ($this->_domain['loginname'] != 'froxlor.panel') { if ($this->_domain['openbasedir'] == '1') { @@ -251,47 +270,45 @@ class phpinterface_fpm { foreach ($_custom_openbasedir as $cobd) { $_phpappendopenbasedir .= appendOpenBasedirPath($cobd); } - + $_custom_openbasedir = explode(':', Settings::Get('system.phpappendopenbasedir')); foreach ($_custom_openbasedir as $cobd) { $_phpappendopenbasedir .= appendOpenBasedirPath($cobd); } - - if ($this->_domain['openbasedir_path'] == '0' - && strstr($this->_domain['documentroot'], ":") === false - ) { + + if ($this->_domain['openbasedir_path'] == '0' && strstr($this->_domain['documentroot'], ":") === false) { $openbasedir = appendOpenBasedirPath($this->_domain['documentroot'], true); } else { $openbasedir = appendOpenBasedirPath($this->_domain['customerroot'], true); } - + $openbasedir .= appendOpenBasedirPath($this->getTempDir()); $openbasedir .= $_phpappendopenbasedir; } } - $fpm_config.= 'php_admin_value[session.save_path] = ' . makeCorrectDir(Settings::Get('phpfpm.tmpdir') . '/' . $this->_domain['loginname'] . '/') . "\n"; - $fpm_config.= 'php_admin_value[upload_tmp_dir] = ' . makeCorrectDir(Settings::Get('phpfpm.tmpdir') . '/' . $this->_domain['loginname'] . '/') . "\n"; - + $fpm_config .= 'php_admin_value[session.save_path] = ' . makeCorrectDir(Settings::Get('phpfpm.tmpdir') . '/' . $this->_domain['loginname'] . '/') . "\n"; + $fpm_config .= 'php_admin_value[upload_tmp_dir] = ' . makeCorrectDir(Settings::Get('phpfpm.tmpdir') . '/' . $this->_domain['loginname'] . '/') . "\n"; + $admin = $this->_getAdminData($this->_domain['adminid']); $php_ini_variables = array( - 'SAFE_MODE' => 'Off', // keep this for compatibility, just in case - 'PEAR_DIR' => Settings::Get('phpfpm.peardir'), - 'TMP_DIR' => $this->getTempDir(), - 'CUSTOMER_EMAIL' => $this->_domain['email'], - 'ADMIN_EMAIL' => $admin['email'], - 'DOMAIN' => $this->_domain['domain'], - 'CUSTOMER' => $this->_domain['loginname'], - 'ADMIN' => $admin['loginname'], - 'OPEN_BASEDIR' => $openbasedir, - 'OPEN_BASEDIR_C' => '', - 'OPEN_BASEDIR_GLOBAL' => Settings::Get('system.phpappendopenbasedir'), - 'DOCUMENT_ROOT' => makeCorrectDir($this->_domain['documentroot']) + 'SAFE_MODE' => 'Off', // keep this for compatibility, just in case + 'PEAR_DIR' => Settings::Get('phpfpm.peardir'), + 'TMP_DIR' => $this->getTempDir(), + 'CUSTOMER_EMAIL' => $this->_domain['email'], + 'ADMIN_EMAIL' => $admin['email'], + 'DOMAIN' => $this->_domain['domain'], + 'CUSTOMER' => $this->_domain['loginname'], + 'ADMIN' => $admin['loginname'], + 'OPEN_BASEDIR' => $openbasedir, + 'OPEN_BASEDIR_C' => '', + 'OPEN_BASEDIR_GLOBAL' => Settings::Get('system.phpappendopenbasedir'), + 'DOCUMENT_ROOT' => makeCorrectDir($this->_domain['documentroot']) ); - + $phpini = replace_variables($phpconfig['phpsettings'], $php_ini_variables); $phpini_array = explode("\n", $phpini); - - $fpm_config.= "\n\n"; + + $fpm_config .= "\n\n"; foreach ($phpini_array as $inisection) { $is = explode("=", $inisection); foreach ($this->_ini as $sec => $possibles) { @@ -300,17 +317,17 @@ class phpinterface_fpm { if (trim($is[0]) == 'open_basedir' && $openbasedir == '') { continue; } - $fpm_config.= $sec.'['.trim($is[0]).'] = ' . trim($is[1]) . "\n"; + $fpm_config .= $sec . '[' . trim($is[0]) . '] = ' . trim($is[1]) . "\n"; } } } - + // now check if 'sendmail_path' has not beed set in the custom-php.ini // if not we use our fallback-default as usual if (strpos($fpm_config, 'php_admin_value[sendmail_path]') === false) { - $fpm_config.= 'php_admin_value[sendmail_path] = /usr/sbin/sendmail -t -i -f '.$this->_domain['email']."\n"; + $fpm_config .= 'php_admin_value[sendmail_path] = /usr/sbin/sendmail -t -i -f ' . $this->_domain['email'] . "\n"; } - + fwrite($fh, $fpm_config, strlen($fpm_config)); fclose($fh); } @@ -322,108 +339,117 @@ class phpinterface_fpm { * * @param string $phpconfig */ - public function createIniFile($phpconfig) { + public function createIniFile($phpconfig) + { return; } /** * fpm-config file * - * @param boolean $createifnotexists create the directory if it does not exist - * + * @param boolean $createifnotexists + * create the directory if it does not exist + * * @return string the full path to the file */ - public function getConfigFile($createifnotexists = true) { - - $configdir = makeCorrectDir(Settings::Get('phpfpm.configdir')); - $config = makeCorrectFile($configdir.'/'.$this->_domain['domain'].'.conf'); - - if (!is_dir($configdir) && $createifnotexists) { + public function getConfigFile($createifnotexists = true) + { + $configdir = $this->_fpm_cfg['config_path']; + $config = makeCorrectFile($configdir . '/' . $this->_domain['domain'] . '.conf'); + + if (! is_dir($configdir) && $createifnotexists) { safe_exec('mkdir -p ' . escapeshellarg($configdir)); } - + return $config; } /** * return path of fpm-socket file * - * @param boolean $createifnotexists create the directory if it does not exist - * + * @param boolean $createifnotexists + * create the directory if it does not exist + * * @return string the full path to the socket */ - public function getSocketFile($createifnotexists = true) { - + public function getSocketFile($createifnotexists = true) + { $socketdir = makeCorrectDir(Settings::Get('phpfpm.fastcgi_ipcdir')); - $socket = strtolower(makeCorrectFile($socketdir.'/'.$this->_domain['loginname'].'-'.$this->_domain['domain'].'-php-fpm.socket')); - - if (!is_dir($socketdir) && $createifnotexists) { - safe_exec('mkdir -p '.escapeshellarg($socketdir)); - safe_exec('chown -R '.Settings::Get('system.httpuser').':'.Settings::Get('system.httpgroup').' '.escapeshellarg($socketdir)); + // add fpm-config-id to filename so it's unique for the fpm-daemon and doesn't interfere with running configs when reuilding + $socket = strtolower(makeCorrectFile($socketdir . '/' . $this->_domain['fpm_config_id'] . '-' . $this->_domain['loginname'] . '-' . $this->_domain['domain'] . '-php-fpm.socket')); + + if (! is_dir($socketdir) && $createifnotexists) { + safe_exec('mkdir -p ' . escapeshellarg($socketdir)); + safe_exec('chown -R ' . Settings::Get('system.httpuser') . ':' . Settings::Get('system.httpgroup') . ' ' . escapeshellarg($socketdir)); } - + return $socket; } /** * fpm-temp directory * - * @param boolean $createifnotexists create the directory if it does not exist - * + * @param boolean $createifnotexists + * create the directory if it does not exist + * * @return string the directory */ - public function getTempDir($createifnotexists = true) { - + public function getTempDir($createifnotexists = true) + { $tmpdir = makeCorrectDir(Settings::Get('phpfpm.tmpdir') . '/' . $this->_domain['loginname'] . '/'); - - if (!is_dir($tmpdir) && $createifnotexists) { + + if (! is_dir($tmpdir) && $createifnotexists) { safe_exec('mkdir -p ' . escapeshellarg($tmpdir)); safe_exec('chown -R ' . $this->_domain['guid'] . ':' . $this->_domain['guid'] . ' ' . escapeshellarg($tmpdir)); safe_exec('chmod 0750 ' . escapeshellarg($tmpdir)); } - + return $tmpdir; } /** * fastcgi-fakedirectory directory * - * @param boolean $createifnotexists create the directory if it does not exist - * + * @param boolean $createifnotexists + * create the directory if it does not exist + * * @return string the directory */ - public function getAliasConfigDir($createifnotexists = true) { - + public function getAliasConfigDir($createifnotexists = true) + { + // ensure default... if (Settings::Get('phpfpm.aliasconfigdir') == null) { Settings::Set('phpfpm.aliasconfigdir', '/var/www/php-fpm'); } - + $configdir = makeCorrectDir(Settings::Get('phpfpm.aliasconfigdir') . '/' . $this->_domain['loginname'] . '/' . $this->_domain['domain'] . '/'); - if (!is_dir($configdir) && $createifnotexists) { + if (! is_dir($configdir) && $createifnotexists) { safe_exec('mkdir -p ' . escapeshellarg($configdir)); safe_exec('chown ' . $this->_domain['guid'] . ':' . $this->_domain['guid'] . ' ' . escapeshellarg($configdir)); } - + return $configdir; } /** * return the admin-data of a specific admin * - * @param int $adminid id of the admin-user - * + * @param int $adminid + * id of the admin-user + * * @return array */ - private function _getAdminData($adminid) { - + private function _getAdminData($adminid) + { $adminid = intval($adminid); - - if (!isset($this->_admin_cache[$adminid])) { + + if (! isset($this->_admin_cache[$adminid])) { $stmt = Database::prepare(" - SELECT `email`, `loginname` FROM `" . TABLE_PANEL_ADMINS . "` WHERE `adminid` = :id" - ); - $this->_admin_cache[$adminid] = Database::pexecute_first($stmt, array('id' => $adminid)); + SELECT `email`, `loginname` FROM `" . TABLE_PANEL_ADMINS . "` WHERE `adminid` = :id"); + $this->_admin_cache[$adminid] = Database::pexecute_first($stmt, array( + 'id' => $adminid + )); } return $this->_admin_cache[$adminid]; } diff --git a/lib/classes/webserver/class.ConfigIO.php b/lib/classes/webserver/class.ConfigIO.php index cbe79b10..10fe1b50 100644 --- a/lib/classes/webserver/class.ConfigIO.php +++ b/lib/classes/webserver/class.ConfigIO.php @@ -17,13 +17,14 @@ * @since 0.9.29 * */ - -class ConfigIO { +class ConfigIO +{ /** * constructor */ - public function __construct() {} + public function __construct() + {} /** * clean up former created configs, including (if enabled) @@ -32,39 +33,40 @@ class ConfigIO { * * @return null */ - public function cleanUp() { - + public function cleanUp() + { + // old error logs $this->_cleanErrLogs(); - + // awstats files $this->_cleanAwstatsFiles(); - + // fcgid files $this->_cleanFcgidFiles(); - + // php-fpm files $this->_cleanFpmFiles(); - + // clean webserver-configs $this->_cleanWebserverConfigs(); - + // old htpasswd files $this->_cleanHtpasswdFiles(); - + // customer-specified ssl-certificates $this->_cleanCustomerSslCerts(); } - private function _cleanErrLogs() { - - $err_dir = makeCorrectDir(FROXLOR_INSTALL_DIR."/logs/"); - if (@is_dir($err_dir)) { - // now get rid of old stuff - //(but append /*.log so we don't delete the directory) - $err_dir.='/*.log'; - safe_exec('rm -rf '. makeCorrectFile($err_dir)); - } + private function _cleanErrLogs() + { + $err_dir = makeCorrectDir(FROXLOR_INSTALL_DIR . "/logs/"); + if (@is_dir($err_dir)) { + // now get rid of old stuff + // (but append /*.log so we don't delete the directory) + $err_dir .= '/*.log'; + safe_exec('rm -rf ' . makeCorrectFile($err_dir)); + } } /** @@ -73,8 +75,9 @@ class ConfigIO { * * @return null */ - private function _cleanCustomerSslCerts() { - + private function _cleanCustomerSslCerts() + { + /* * only clean up if we're actually using SSL */ @@ -82,14 +85,14 @@ class ConfigIO { // get correct directory $configdir = $this->_getFile('system', 'customer_ssl_path'); if ($configdir !== false) { - + $configdir = makeCorrectDir($configdir); - + if (@is_dir($configdir)) { // now get rid of old stuff - //(but append /* so we don't delete the directory) - $configdir.='/*'; - safe_exec('rm -rf '. makeCorrectFile($configdir)); + // (but append /* so we don't delete the directory) + $configdir .= '/*'; + safe_exec('rm -rf ' . makeCorrectFile($configdir)); } } } @@ -100,39 +103,38 @@ class ConfigIO { * * @return null */ - private function _cleanWebserverConfigs() { - + private function _cleanWebserverConfigs() + { + // get directories $configdirs = array(); $dir = $this->_getFile('system', 'apacheconf_vhost'); if ($dir !== false) $configdirs[] = makeCorrectDir($dir); - + $dir = $this->_getFile('system', 'apacheconf_diroptions'); if ($dir !== false) $configdirs[] = makeCorrectDir($dir); - + // file pattern $pattern = "/^([0-9]){2}_(froxlor|syscp)_(.+)\.conf$/"; - + // check ALL the folders foreach ($configdirs as $config_dir) { - + // check directory if (@is_dir($config_dir)) { - + // create directory iterator - $its = new RecursiveIteratorIterator( - new RecursiveDirectoryIterator($config_dir) - ); - + $its = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($config_dir)); + // iterate through all subdirs, // look for vhost/diroption files // and delete them - foreach ($its as $fullFileName => $it ) { + foreach ($its as $fullFileName => $it) { if ($it->isFile() && preg_match($pattern, $it->getFilename())) { // remove file - safe_exec('rm -f '. escapeshellarg(makeCorrectFile($its->getPathname()))); + safe_exec('rm -f ' . escapeshellarg(makeCorrectFile($its->getPathname()))); } } } @@ -144,19 +146,20 @@ class ConfigIO { * * @return null */ - private function _cleanHtpasswdFiles() { - + private function _cleanHtpasswdFiles() + { + // get correct directory $configdir = $this->_getFile('system', 'apacheconf_htpasswddir'); - + if ($configdir !== false) { $configdir = makeCorrectDir($configdir); - + if (@is_dir($configdir)) { // now get rid of old stuff - //(but append /* so we don't delete the directory) - $configdir.='/*'; - safe_exec('rm -rf '. makeCorrectFile($configdir)); + // (but append /* so we don't delete the directory) + $configdir .= '/*'; + safe_exec('rm -rf ' . makeCorrectFile($configdir)); } } } @@ -166,37 +169,36 @@ class ConfigIO { * * @return null */ - private function _cleanAwstatsFiles() { - + private function _cleanAwstatsFiles() + { if (Settings::Get('system.awstats_enabled') == '0') { return; } - - //dhr: cleanout froxlor-generated awstats configs prior to re-creation + + // dhr: cleanout froxlor-generated awstats configs prior to re-creation $awstatsclean['header'] = "## GENERATED BY FROXLOR\n"; $awstatsclean['headerold'] = "## GENERATED BY SYSCP\n"; $awstatsclean['path'] = $this->_getFile('system', 'awstats_conf'); - + /** * don't do anything if the directory does not exist - * (e.g. awstats not installed yet or whatever) + * (e.g. + * awstats not installed yet or whatever) * fixes #45 - */ + */ if ($awstatsclean['path'] !== false && is_dir($awstatsclean['path'])) { $awstatsclean['dir'] = dir($awstatsclean['path']); while ($awstatsclean['entry'] = $awstatsclean['dir']->read()) { - $awstatsclean['fullentry'] = makeCorrectFile($awstatsclean['path'].'/'.$awstatsclean['entry']); + $awstatsclean['fullentry'] = makeCorrectFile($awstatsclean['path'] . '/' . $awstatsclean['entry']); /** * don't do anything if the file does not exist - */ + */ if (@file_exists($awstatsclean['fullentry'])) { $awstatsclean['fh'] = fopen($awstatsclean['fullentry'], 'r'); - $awstatsclean['headerRead'] = fgets($awstatsclean['fh'], strlen($awstatsclean['header'])+1); + $awstatsclean['headerRead'] = fgets($awstatsclean['fh'], strlen($awstatsclean['header']) + 1); fclose($awstatsclean['fh']); - - if ($awstatsclean['headerRead'] == $awstatsclean['header'] - || $awstatsclean['headerRead'] == $awstatsclean['headerold'] - ) { + + if ($awstatsclean['headerRead'] == $awstatsclean['header'] || $awstatsclean['headerRead'] == $awstatsclean['headerold']) { $awstats_conf_file = makeCorrectFile($awstatsclean['fullentry']); @unlink($awstats_conf_file); } @@ -204,7 +206,7 @@ class ConfigIO { } } unset($awstatsclean); - //end dhr + // end dhr } /** @@ -212,39 +214,37 @@ class ConfigIO { * * @return null */ - private function _cleanFcgidFiles() { - + private function _cleanFcgidFiles() + { if (Settings::Get('system.mod_fcgid') == '0') { return; } - + // get correct directory $configdir = $this->_getFile('system', 'mod_fcgid_configdir'); if ($configdir !== false) { - + $configdir = makeCorrectDir($configdir); - + if (@is_dir($configdir)) { // create directory iterator - $its = new RecursiveIteratorIterator( - new RecursiveDirectoryIterator($configdir) - ); - + $its = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($configdir)); + // iterate through all subdirs, // look for php-fcgi-starter files // and take immutable-flag away from them // so we can delete them :) - foreach ($its as $fullFileName => $it ) { + foreach ($its as $fullFileName => $it) { if ($it->isFile() && $it->getFilename() == 'php-fcgi-starter') { // set chattr -i removeImmutable($its->getPathname()); } } - + // now get rid of old stuff - //(but append /* so we don't delete the directory) - $configdir.='/*'; - safe_exec('rm -rf '. makeCorrectFile($configdir)); + // (but append /* so we don't delete the directory) + $configdir .= '/*'; + safe_exec('rm -rf ' . makeCorrectFile($configdir)); } } } @@ -254,33 +254,36 @@ class ConfigIO { * * @return null */ - private function _cleanFpmFiles() { - + private function _cleanFpmFiles() + { if (Settings::Get('phpfpm.enabled') == '0') { return; } - - // get correct directory - $configdir = $this->_getFile('phpfpm', 'configdir'); - if ($configdir !== false) { - - $configdir = makeCorrectDir($configdir); - + + // get all fpm config paths + $fpmconf_sel = Database::prepare("SELECT config_dir FROM `" . TABLE_PANEL_FPMDAEMONS . "`"); + Database::pexecute($fpmconf_sel); + $fpmconf_paths = $fpmconf_sel->fetchAll(PDO::FETCH_ASSOC); + // clean all php-fpm config-dirs + foreach ($fpmconf_paths as $configdir) { + $configdir = makeCorrectDir($configdir['config_dir']); if (@is_dir($configdir)) { // now get rid of old stuff - //(but append /* so we don't delete the directory) - $configdir.='/*'; - safe_exec('rm -rf '. makeCorrectFile($configdir)); + // (but append /* so we don't delete the directory) + $configdir .= '/*'; + safe_exec('rm -rf ' . makeCorrectFile($configdir)); + } else { + safe_exec('mkdir -p ' . $configdir); } } - + // also remove aliasconfigdir #1273 $aliasconfigdir = $this->_getFile('phpfpm', 'aliasconfigdir'); if ($aliasconfigdir !== false) { $aliasconfigdir = makeCorrectDir($aliasconfigdir); if (@is_dir($aliasconfigdir)) { - $aliasconfigdir.='/*'; - safe_exec('rm -rf '. makeCorrectFile($aliasconfigdir)); + $aliasconfigdir .= '/*'; + safe_exec('rm -rf ' . makeCorrectFile($aliasconfigdir)); } } } @@ -288,17 +291,21 @@ class ConfigIO { /** * returns a file/direcotry from the settings and checks whether it exists * - * @param string $group settings-group - * @param string $varname var-name - * @param boolean $check_exists check if the file exists - * + * @param string $group + * settings-group + * @param string $varname + * var-name + * @param boolean $check_exists + * check if the file exists + * * @return string|boolean complete path including filename if any or false on error */ - private function _getFile($group, $varname, $check_exists = true) { - + private function _getFile($group, $varname, $check_exists = true) + { + // read from settings - $file = Settings::Get($group.'.'.$varname); - + $file = Settings::Get($group . '.' . $varname); + // check whether it exists if ($check_exists && @file_exists($file) == false) { return false; diff --git a/lib/classes/webserver/class.WebserverBase.php b/lib/classes/webserver/class.WebserverBase.php index 8f7c27c5..5181bd25 100644 --- a/lib/classes/webserver/class.WebserverBase.php +++ b/lib/classes/webserver/class.WebserverBase.php @@ -17,8 +17,8 @@ * @since 0.9.31 * */ - -class WebserverBase { +class WebserverBase +{ /** * returns an array with all entries required for all @@ -26,8 +26,8 @@ class WebserverBase { * * @return array */ - public static function getVhostsToCreate() { - + public static function getVhostsToCreate() + { $query = "SELECT `d`.*, `pd`.`domain` AS `parentdomain`, `c`.`loginname`, `d`.`phpsettingid`, `c`.`adminid`, `c`.`guid`, `c`.`email`, `c`.`documentroot` AS `customerroot`, `c`.`deactivated`, @@ -35,20 +35,20 @@ class WebserverBase { `d`.`phpenabled` AS `phpenabled_vhost`, `d`.`mod_fcgid_starter`,`d`.`mod_fcgid_maxrequests`, `d`.`ocsp_stapling` - FROM `".TABLE_PANEL_DOMAINS."` `d` + FROM `" . TABLE_PANEL_DOMAINS . "` `d` - LEFT JOIN `".TABLE_PANEL_CUSTOMERS."` `c` USING(`customerid`) - LEFT JOIN `".TABLE_PANEL_DOMAINS."` `pd` ON (`pd`.`id` = `d`.`parentdomainid`) + LEFT JOIN `" . TABLE_PANEL_CUSTOMERS . "` `c` USING(`customerid`) + LEFT JOIN `" . TABLE_PANEL_DOMAINS . "` `pd` ON (`pd`.`id` = `d`.`parentdomainid`) WHERE `d`.`aliasdomain` IS NULL AND `d`.`email_only` <> '1' ORDER BY `d`.`parentdomainid` DESC, `d`.`iswildcarddomain`, `d`.`domain` ASC; "; - + $result_domains_stmt = Database::query($query); - + $domains = array(); while ($domain = $result_domains_stmt->fetch(PDO::FETCH_ASSOC)) { - + // set whole domain $domains[$domain['domain']] = $domain; // set empty-defaults for non-ssl @@ -57,31 +57,46 @@ class WebserverBase { $domains[$domain['domain']]['ssl_key_file'] = ''; $domains[$domain['domain']]['ssl_ca_file'] = ''; $domains[$domain['domain']]['ssl_cert_chainfile'] = ''; - + // now, if the domain has an ssl ip/port assigned, get // the corresponding information from the db if (domainHasSslIpPort($domain['id'])) { - + $ip_stmt = Database::prepare(" SELECT `di`.`id_domain` , `p`.`ssl`, `p`.`ssl_cert_file`, `p`.`ssl_key_file`, `p`.`ssl_ca_file`, `p`.`ssl_cert_chainfile` - FROM `".TABLE_DOMAINTOIP."` `di`, `".TABLE_PANEL_IPSANDPORTS."` `p` + FROM `" . TABLE_DOMAINTOIP . "` `di`, `" . TABLE_PANEL_IPSANDPORTS . "` `p` WHERE `p`.`id` = `di`.`id_ipandports` AND `di`.`id_domain` = :domainid AND `p`.`ssl` = '1' "); - $ssl_ip = Database::pexecute_first($ip_stmt, array('domainid' => $domain['id'])); - + $ssl_ip = Database::pexecute_first($ip_stmt, array( + 'domainid' => $domain['id'] + )); + // set ssl info for domain $domains[$domain['domain']]['ssl'] = '1'; $domains[$domain['domain']]['ssl_cert_file'] = $ssl_ip['ssl_cert_file']; $domains[$domain['domain']]['ssl_key_file'] = $ssl_ip['ssl_key_file']; $domains[$domain['domain']]['ssl_ca_file'] = $ssl_ip['ssl_ca_file']; $domains[$domain['domain']]['ssl_cert_chainfile'] = $ssl_ip['ssl_cert_chainfile']; - + } + + // read fpm-config-id if using fpm + if ((int) Settings::Get('phpfpm.enabled') == 1) { + $fpm_sel_stmt = Database::prepare(" + SELECT f.id FROM `" . TABLE_PANEL_FPMDAEMONS . "` f + LEFT JOIN `" . TABLE_PANEL_PHPCONFIGS . "` p ON p.fpmsettingid = f.id + WHERE p.id = :phpconfigid + "); + $fpm_config = Database::pexecute_first($fpm_sel_stmt, array( + 'phpconfigid' => $domain['phpsettingid'] + )); + if ($fpm_config) { + $domains[$domain['domain']]['fpm_config_id'] = $fpm_config['id']; + } } } - + return $domains; } - } diff --git a/lib/formfields/admin/phpconfig/formfield.fpmconfig_add.php b/lib/formfields/admin/phpconfig/formfield.fpmconfig_add.php new file mode 100644 index 00000000..88a1bb8b --- /dev/null +++ b/lib/formfields/admin/phpconfig/formfield.fpmconfig_add.php @@ -0,0 +1,89 @@ + (2010-) + * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt + * @package Formfields + * + */ + +return array( + 'fpmconfig_add' => array( + 'title' => $lng['admin']['phpsettings']['addsettings'], + 'image' => 'icons/phpsettings_add.png', + 'sections' => array( + 'section_a' => array( + 'title' => $lng['admin']['phpsettings']['addsettings'], + 'image' => 'icons/phpsettings_add.png', + 'fields' => array( + 'description' => array( + 'label' => $lng['admin']['phpsettings']['description'], + 'type' => 'text', + 'maxlength' => 50 + ), + 'reload_cmd' => array( + 'label' => $lng['serversettings']['phpfpm_settings']['reload'], + 'type' => 'text', + 'maxlength' => 255, + 'value' => 'service php7.0-fpm restart' + ), + 'config_dir' => array( + 'label' => $lng['serversettings']['phpfpm_settings']['configdir'], + 'type' => 'text', + 'maxlength' => 255, + 'value' => '/etc/php/7.0/fpm/pool.d/' + ), + 'pm' => array( + 'label' => $lng['serversettings']['phpfpm_settings']['pm'], + 'type' => 'select', + 'select_var' => $pm_select + ), + 'max_children' => array( + 'label' => $lng['serversettings']['phpfpm_settings']['max_children']['title'], + 'desc' => $lng['serversettings']['phpfpm_settings']['max_children']['description'], + 'type' => 'int', + 'value' => 1 + ), + 'start_servers' => array( + 'label' => $lng['serversettings']['phpfpm_settings']['start_servers']['title'], + 'desc' => $lng['serversettings']['phpfpm_settings']['start_servers']['description'], + 'type' => 'int', + 'value' => 20 + ), + 'min_spare_servers' => array( + 'label' => $lng['serversettings']['phpfpm_settings']['min_spare_servers']['title'], + 'desc' => $lng['serversettings']['phpfpm_settings']['min_spare_servers']['description'], + 'type' => 'int', + 'value' => 5 + ), + 'max_spare_servers' => array( + 'label' => $lng['serversettings']['phpfpm_settings']['max_spare_servers']['title'], + 'desc' => $lng['serversettings']['phpfpm_settings']['max_spare_servers']['description'], + 'type' => 'int', + 'value' => 35 + ), + 'max_requests' => array( + 'label' => $lng['serversettings']['phpfpm_settings']['max_requests']['title'], + 'desc' => $lng['serversettings']['phpfpm_settings']['max_requests']['description'], + 'type' => 'int', + 'value' => 0 + ), + 'idle_timeout' => array( + 'label' => $lng['serversettings']['phpfpm_settings']['idle_timeout']['title'], + 'desc' => $lng['serversettings']['phpfpm_settings']['idle_timeout']['description'], + 'type' => 'int', + 'value' => 30 + ) + ) + ) + ) + ) +); diff --git a/lib/formfields/admin/phpconfig/formfield.fpmconfig_edit.php b/lib/formfields/admin/phpconfig/formfield.fpmconfig_edit.php new file mode 100644 index 00000000..a06592ed --- /dev/null +++ b/lib/formfields/admin/phpconfig/formfield.fpmconfig_edit.php @@ -0,0 +1,91 @@ + (2010-) + * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt + * @package Formfields + * + */ + +return array( + 'fpmconfig_edit' => array( + 'title' => $lng['admin']['phpsettings']['editsettings'], + 'image' => 'icons/phpsettings_edit.png', + 'sections' => array( + 'section_a' => array( + 'title' => $lng['admin']['phpsettings']['editsettings'], + 'image' => 'icons/phpsettings_edit.png', + 'fields' => array( + 'description' => array( + 'label' => $lng['admin']['phpsettings']['description'], + 'type' => 'text', + 'maxlength' => 50, + 'value' => $result['description'] + ), + 'reload_cmd' => array( + 'label' => $lng['serversettings']['phpfpm_settings']['reload'], + 'type' => 'text', + 'maxlength' => 255, + 'value' => $result['reload_cmd'] + ), + 'config_dir' => array( + 'label' => $lng['serversettings']['phpfpm_settings']['configdir'], + 'type' => 'text', + 'maxlength' => 255, + 'value' => $result['config_dir'] + ), + 'pm' => array( + 'label' => $lng['serversettings']['phpfpm_settings']['pm'], + 'type' => 'select', + 'select_var' => array('static' => 'static', 'dynamic' => 'dynamic', 'ondemand' => 'ondemand'), + 'value' => $result['pm'] + ), + 'max_children' => array( + 'label' => $lng['serversettings']['phpfpm_settings']['max_children']['title'], + 'desc' => $lng['serversettings']['phpfpm_settings']['max_children']['description'], + 'type' => 'int', + 'value' => $result['max_children'] + ), + 'start_servers' => array( + 'label' => $lng['serversettings']['phpfpm_settings']['start_servers']['title'], + 'desc' => $lng['serversettings']['phpfpm_settings']['start_servers']['description'], + 'type' => 'int', + 'value' => $result['start_servers'] + ), + 'min_spare_servers' => array( + 'label' => $lng['serversettings']['phpfpm_settings']['min_spare_servers']['title'], + 'desc' => $lng['serversettings']['phpfpm_settings']['min_spare_servers']['description'], + 'type' => 'int', + 'value' => $result['min_spare_servers'] + ), + 'max_spare_servers' => array( + 'label' => $lng['serversettings']['phpfpm_settings']['max_spare_servers']['title'], + 'desc' => $lng['serversettings']['phpfpm_settings']['max_spare_servers']['description'], + 'type' => 'int', + 'value' => $result['max_spare_servers'] + ), + 'max_requests' => array( + 'label' => $lng['serversettings']['phpfpm_settings']['max_requests']['title'], + 'desc' => $lng['serversettings']['phpfpm_settings']['max_requests']['description'], + 'type' => 'int', + 'value' => $result['max_requests'] + ), + 'idle_timeout' => array( + 'label' => $lng['serversettings']['phpfpm_settings']['idle_timeout']['title'], + 'desc' => $lng['serversettings']['phpfpm_settings']['idle_timeout']['description'], + 'type' => 'int', + 'value' => $result['idle_timeout'] + ) + ) + ) + ) + ) +); diff --git a/lib/formfields/admin/phpconfig/formfield.phpconfig_add.php b/lib/formfields/admin/phpconfig/formfield.phpconfig_add.php index 3bcc4922..07889120 100644 --- a/lib/formfields/admin/phpconfig/formfield.phpconfig_add.php +++ b/lib/formfields/admin/phpconfig/formfield.phpconfig_add.php @@ -36,6 +36,12 @@ return array( 'maxlength' => 255, 'value' => '/usr/bin/php-cgi' ), + 'fpmconfig' => array( + 'visible' => (Settings::Get('phpfpm.enabled') == 1 ? true : false), + 'label' => $lng['admin']['phpsettings']['fpmdesc'], + 'type' => 'select', + 'select_var' => $fpmconfigs + ), 'file_extensions' => array( 'visible' => (Settings::Get('system.mod_fcgid') == 1 ? true : false), 'label' => $lng['admin']['phpsettings']['file_extensions'], diff --git a/lib/formfields/admin/phpconfig/formfield.phpconfig_edit.php b/lib/formfields/admin/phpconfig/formfield.phpconfig_edit.php index d3a9828f..9ae736ed 100644 --- a/lib/formfields/admin/phpconfig/formfield.phpconfig_edit.php +++ b/lib/formfields/admin/phpconfig/formfield.phpconfig_edit.php @@ -37,6 +37,12 @@ return array( 'maxlength' => 255, 'value' => $result['binary'] ), + 'fpmconfig' => array( + 'visible' => (Settings::Get('phpfpm.enabled') == 1 ? true : false), + 'label' => $lng['admin']['phpsettings']['fpmdesc'], + 'type' => 'select', + 'select_var' => $fpmconfigs + ), 'file_extensions' => array( 'visible' => (Settings::Get('system.mod_fcgid') == 1 ? true : false), 'label' => $lng['admin']['phpsettings']['file_extensions'], diff --git a/lib/navigation/00.froxlor.main.php b/lib/navigation/00.froxlor.main.php index 18dc74ab..fd077c3c 100644 --- a/lib/navigation/00.froxlor.main.php +++ b/lib/navigation/00.froxlor.main.php @@ -279,6 +279,12 @@ return array( 'label' => $lng['menue']['phpsettings']['maintitle'], 'show_element' => (Settings::Get('system.mod_fcgid') == true || Settings::Get('phpfpm.enabled') == true) ), + array( + 'url' => 'admin_phpsettings.php?page=fpmdaemons', + 'label' => $lng['menue']['phpsettings']['fpmdaemons'], + 'required_resources' => 'change_serversettings', + 'show_element' => Settings::Get('phpfpm.enabled') == true + ), array( 'url' => 'admin_settings.php?page=phpinfo', 'label' => $lng['admin']['phpinfo'], diff --git a/lib/tables.inc.php b/lib/tables.inc.php index 39b37e24..c9f71112 100644 --- a/lib/tables.inc.php +++ b/lib/tables.inc.php @@ -51,5 +51,6 @@ define('TABLE_PANEL_DOMAINREDIRECTS', 'domain_redirect_codes'); define('TABLE_PANEL_DOMAIN_SSL_SETTINGS', 'domain_ssl_settings'); define('TABLE_DOMAINTOIP', 'panel_domaintoip'); define('TABLE_DOMAIN_DNS', 'domain_dns_entries'); +define('TABLE_PANEL_FPMDAEMONS', 'panel_fpmdaemons'); require dirname(__FILE__).'/version.inc.php'; diff --git a/lib/version.inc.php b/lib/version.inc.php index f6c42a62..f6174b9a 100644 --- a/lib/version.inc.php +++ b/lib/version.inc.php @@ -19,7 +19,7 @@ $version = '0.9.38.8'; // Database version (YYYYMMDDC where C is a daily counter) -$dbversion = '201712310'; +$dbversion = '201801070'; // Distribution branding-tag (used for Debian etc.) $branding = ''; diff --git a/lng/english.lng.php b/lng/english.lng.php index da777141..2a850d00 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -940,6 +940,7 @@ $lng['admin']['phpsettings']['phpinisettings'] = 'php.ini settings'; $lng['error']['nopermissionsorinvalidid'] = 'You don\'t have enough permissions to change these settings or an invalid id was given.'; $lng['panel']['view'] = 'view'; $lng['question']['phpsetting_reallydelete'] = 'Do you really want to delete these settings? All domains which use these settings currently will be changed to the default config.'; +$lng['question']['fpmsetting_reallydelete'] = 'Do you really want to delete these php-fpm settings? All php configurations which use these settings currently will be changed to the default config.'; $lng['admin']['phpsettings']['addnew'] = 'Create new settings'; $lng['error']['phpsettingidwrong'] = 'A PHP Configuration with this id doesn\'t exist'; $lng['error']['descriptioninvalid'] = 'The description is too short, too long or contains illegal characters.'; @@ -1001,6 +1002,7 @@ $lng['error']['ipportdoesntexist'] = 'The ip/port combination you have chosen do $lng['admin']['phpserversettings'] = 'PHP Settings'; $lng['admin']['phpsettings']['binary'] = 'PHP Binary'; +$lng['admin']['phpsettings']['fpmdesc'] = 'PHP-FPM config'; $lng['admin']['phpsettings']['file_extensions'] = 'File extensions'; $lng['admin']['phpsettings']['file_extensions_note'] = '(without dot, separated by spaces)'; $lng['admin']['mod_fcgid_maxrequests']['title'] = 'Maximum php requests for this domain (empty for default value)'; @@ -1656,7 +1658,7 @@ $lng['traffic']['mail'] = 'Mail (MiB)'; $lng['serversettings']['mod_fcgid']['idle_timeout']['title'] = 'Idle Timeout'; $lng['serversettings']['mod_fcgid']['idle_timeout']['description'] = 'Timeout setting for Mod FastCGI.'; $lng['serversettings']['phpfpm_settings']['idle_timeout']['title'] = 'Idle Timeout'; -$lng['serversettings']['phpfpm_settings']['idle_timeout']['description'] = 'Timeout setting for PHP5 FPM FastCGI.'; +$lng['serversettings']['phpfpm_settings']['idle_timeout']['description'] = 'Timeout setting for PHP FPM FastCGI.'; // ADDED IN 0.9.27-svn2 $lng['panel']['cancel'] = 'Cancel'; @@ -2082,3 +2084,5 @@ $lng['admin']['testmail'] = 'SMTP test'; $lng['success']['testmailsent'] = 'Test mail sent successfully'; $lng['serversettings']['disable_le_selfcheck']['title'] = "Disable Let's Encrypt local self-check"; $lng['serversettings']['disable_le_selfcheck']['description'] = "If activated, froxlor will not perform its self-check for token accessability. Needed for NATed IP's or similar."; +$lng['menue']['phpsettings']['fpmdaemons'] = 'PHP-FPM versions'; +$lng['admin']['phpsettings']['activephpconfigs'] = 'In use for php-config(s)'; diff --git a/lng/german.lng.php b/lng/german.lng.php index 7edda2ca..f20700ec 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -934,6 +934,7 @@ $lng['admin']['phpsettings']['phpinisettings'] = 'php.ini-Einstellungen'; $lng['error']['nopermissionsorinvalidid'] = 'Entweder fehlen Ihnen die nötigen Rechte diese Einstellung zu ändern oder es wurde eine ungültige ID übergeben'; $lng['panel']['view'] = 'ansehen'; $lng['question']['phpsetting_reallydelete'] = 'Wollen Sie diese PHP-Einstellungen wirklich löschen? Alle Domains die diese Einstellungen bis jetzt verwendet haben, werden dann auf die Standardeinstellungen umgestellt.'; +$lng['question']['fpmsetting_reallydelete'] = 'Wollen Sie diese PHP-FPM Einstellungen wirklich löschen? Alle PHP Konfigurationen die diese Einstellungen bis jetzt verwendet haben, werden dann auf die Standardeinstellungen umgestellt.'; $lng['admin']['phpsettings']['addnew'] = 'Neue Konfiguration erstellen'; $lng['error']['phpsettingidwrong'] = 'Eine PHP-Konfiguration mit dieser ID existiert nicht'; $lng['error']['descriptioninvalid'] = 'Der Beschreibungstext ist zu kurz, zu lang oder enthält ungültige Zeichen'; @@ -997,6 +998,7 @@ $lng['error']['ipportdoesntexist'] = 'Die gewählte IP/Port-Kombination existier $lng['admin']['phpserversettings'] = 'PHP-Einstellungen'; $lng['admin']['phpsettings']['binary'] = 'PHP-Binary'; +$lng['admin']['phpsettings']['fpmdesc'] = 'PHP-FPM Config'; $lng['admin']['phpsettings']['file_extensions'] = 'Dateiendungen'; $lng['admin']['phpsettings']['file_extensions_note'] = '(ohne Punkt, durch Leerzeichen getrennt)'; $lng['admin']['mod_fcgid_maxrequests']['title'] = 'Maximale PHP-Requests für diese Domain (leer für Standardwert)'; @@ -1383,7 +1385,7 @@ $lng['traffic']['mail'] = 'Mail (MiB)'; $lng['serversettings']['mod_fcgid']['idle_timeout']['title'] = 'Idle-Timeout'; $lng['serversettings']['mod_fcgid']['idle_timeout']['description'] = 'Timeout-Einstellung für mod_FastCGI.'; $lng['serversettings']['phpfpm_settings']['idle_timeout']['title'] = 'Idle-Timeout'; -$lng['serversettings']['phpfpm_settings']['idle_timeout']['description'] = 'Timeout-Einstellung für PHP5-FPM FastCGI.'; +$lng['serversettings']['phpfpm_settings']['idle_timeout']['description'] = 'Timeout-Einstellung für PHP-FPM FastCGI.'; // ADDED IN 0.9.27-svn2 $lng['admin']['delete_statistics'] = 'Statistiken Löschen'; @@ -1733,3 +1735,5 @@ $lng['admin']['testmail'] = 'SMTP Test'; $lng['success']['testmailsent'] = 'Test E-Mail erfolgreich gesendet'; $lng['serversettings']['disable_le_selfcheck']['title'] = "Deaktiviere Let's Encrypt lokale Selbstprüfung"; $lng['serversettings']['disable_le_selfcheck']['description'] = "Wenn aktiviert wird Froxlor keine Erreichbarkeitsprüfung des Tokens vornehmen. Nötig bei ge-NAT-eten IP's oder Ähnlichem"; +$lng['menue']['phpsettings']['fpmdaemons'] = 'PHP-FPM Versionen'; +$lng['admin']['phpsettings']['activephpconfigs'] = 'In Verwendung für PHP-Konfiguration(en)'; diff --git a/scripts/jobs/cron_tasks.inc.http.10.apache.php b/scripts/jobs/cron_tasks.inc.http.10.apache.php index 6e9d3277..f4107512 100644 --- a/scripts/jobs/cron_tasks.inc.http.10.apache.php +++ b/scripts/jobs/cron_tasks.inc.http.10.apache.php @@ -16,7 +16,7 @@ if (! defined('MASTER_CRONJOB')) * @author Froxlor team (2010-) * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt * @package Cron - * + * */ require_once (dirname(__FILE__) . '/../classes/class.HttpConfigBase.php'); @@ -58,8 +58,15 @@ class apache extends HttpConfigBase public function reload() { if ((int) Settings::Get('phpfpm.enabled') == 1) { - $this->logger->logAction(CRON_ACTION, LOG_INFO, 'apache::reload: reloading php-fpm'); - safe_exec(escapeshellcmd(Settings::Get('phpfpm.reload'))); + // get all start/stop commands + $startstop_sel = Database::prepare("SELECT reload_cmd FROM `" . TABLE_PANEL_FPMDAEMONS . "`"); + Database::pexecute($startstop_sel); + $restart_cmds = $startstop_sel->fetchAll(PDO::FETCH_ASSOC); + // restart all php-fpm instances + foreach ($restart_cmds as $restart_cmd) { + $this->logger->logAction(CRON_ACTION, LOG_INFO, 'apache::reload: running ' . $restart_cmd['reload_cmd']); + safe_exec(escapeshellcmd($restart_cmd['reload_cmd'])); + } } $this->logger->logAction(CRON_ACTION, LOG_INFO, 'apache::reload: reloading apache'); safe_exec(escapeshellcmd(Settings::Get('system.apachereload_command'))); @@ -77,13 +84,13 @@ class apache extends HttpConfigBase $vhosts_folder = makeCorrectDir(dirname(Settings::Get('system.apacheconf_vhost'))); } $vhosts_filename = makeCorrectFile($vhosts_folder . '/05_froxlor_dirfix_nofcgid.conf'); - + if (! isset($this->virtualhosts_data[$vhosts_filename])) { $this->virtualhosts_data[$vhosts_filename] = ''; } - + $this->virtualhosts_data[$vhosts_filename] .= ' ' . "\n"; - + // check for custom values, see #1638 $custom_opts = Settings::Get('system.apacheglobaldiropt'); if (! empty($custom_opts)) { @@ -99,11 +106,10 @@ class apache extends HttpConfigBase } } $this->virtualhosts_data[$vhosts_filename] .= ' ' . "\n"; - + $ocsp_cache_filename = makeCorrectFile($vhosts_folder . '/03_froxlor_ocsp_cache.conf'); if (Settings::Get('system.use_ssl') == '1' && Settings::Get('system.apache24') == 1) { - $this->virtualhosts_data[$ocsp_cache_filename] = 'SSLStaplingCache ' . - Settings::Get('system.apache24_ocsp_cache_path') . "\n"; + $this->virtualhosts_data[$ocsp_cache_filename] = 'SSLStaplingCache ' . Settings::Get('system.apache24_ocsp_cache_path') . "\n"; } else { if (file_exists($ocsp_cache_filename)) { $this->logger->logAction(CRON_ACTION, LOG_NOTICE, 'apache::_createStandardDirectoryEntry: unlinking ' . basename($ocsp_cache_filename)); @@ -124,13 +130,13 @@ class apache extends HttpConfigBase } else { $vhosts_folder = makeCorrectDir(dirname(Settings::Get('system.apacheconf_vhost'))); } - + $vhosts_filename = makeCorrectFile($vhosts_folder . '/05_froxlor_default_errorhandler.conf'); - + if (! isset($this->virtualhosts_data[$vhosts_filename])) { $this->virtualhosts_data[$vhosts_filename] = ''; } - + $statusCodes = array( '401', '403', @@ -154,26 +160,26 @@ class apache extends HttpConfigBase public function createIpPort() { $result_ipsandports_stmt = Database::query("SELECT * FROM `" . TABLE_PANEL_IPSANDPORTS . "` ORDER BY `ip` ASC, `port` ASC"); - + while ($row_ipsandports = $result_ipsandports_stmt->fetch(PDO::FETCH_ASSOC)) { if (filter_var($row_ipsandports['ip'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { $ipport = '[' . $row_ipsandports['ip'] . ']:' . $row_ipsandports['port']; } else { $ipport = $row_ipsandports['ip'] . ':' . $row_ipsandports['port']; } - + $this->logger->logAction(CRON_ACTION, LOG_INFO, 'apache::createIpPort: creating ip/port settings for ' . $ipport); $vhosts_filename = makeCorrectFile(Settings::Get('system.apacheconf_vhost') . '/10_froxlor_ipandport_' . trim(str_replace(':', '.', $row_ipsandports['ip']), '.') . '.' . $row_ipsandports['port'] . '.conf'); - + if (! isset($this->virtualhosts_data[$vhosts_filename])) { $this->virtualhosts_data[$vhosts_filename] = ''; } - + if ($row_ipsandports['listen_statement'] == '1') { $this->virtualhosts_data[$vhosts_filename] .= 'Listen ' . $ipport . "\n"; $this->logger->logAction(CRON_ACTION, LOG_DEBUG, $ipport . ' :: inserted listen-statement'); } - + if ($row_ipsandports['namevirtualhost_statement'] == '1') { // >=apache-2.4 enabled? if (Settings::Get('system.apache24') == '1') { @@ -183,22 +189,22 @@ class apache extends HttpConfigBase $this->logger->logAction(CRON_ACTION, LOG_DEBUG, $ipport . ' :: inserted namevirtualhost-statement'); } } - + if ($row_ipsandports['vhostcontainer'] == '1') { - + $without_vhost = $this->virtualhosts_data[$vhosts_filename]; $close_vhost = true; - + $this->virtualhosts_data[$vhosts_filename] .= '' . "\n"; - + $mypath = $this->getMyPath($row_ipsandports); - + $this->virtualhosts_data[$vhosts_filename] .= 'DocumentRoot "' . $mypath . '"' . "\n"; - + if ($row_ipsandports['vhostcontainer_servername_statement'] == '1') { $this->virtualhosts_data[$vhosts_filename] .= ' ServerName ' . Settings::Get('system.hostname') . "\n"; } - + $is_redirect = false; // check for SSL redirect if ($row_ipsandports['ssl'] == '0' && Settings::Get('system.le_froxlor_redirect') == '1') { @@ -210,11 +216,11 @@ class apache extends HttpConfigBase $is_redirect = false; } else { $_sslport = $this->checkAlternativeSslPort(); - + $mypath = 'https://' . Settings::Get('system.hostname') . $_sslport . '/'; $code = '301'; $modrew_red = ' [R=' . $code . ';L,NE]'; - + // redirect everything, not only root-directory, #541 $this->virtualhosts_data[$vhosts_filename] .= ' ' . "\n"; $this->virtualhosts_data[$vhosts_filename] .= ' RewriteEngine On' . "\n"; @@ -229,7 +235,7 @@ class apache extends HttpConfigBase $this->virtualhosts_data[$vhosts_filename] .= ' ' . "\n"; } } - + if (! $is_redirect) { // create fcgid -Part (starter is created in apache_fcgid) if (Settings::Get('system.mod_fcgid_ownvhost') == '1' && Settings::Get('system.mod_fcgid') == '1') { @@ -253,7 +259,7 @@ class apache extends HttpConfigBase ); $php = new phpinterface($domain); $phpconfig = $php->getPhpConfig(Settings::Get('system.mod_fcgid_defaultini_ownvhost')); - + $starter_filename = makeCorrectFile($configdir . '/php-fcgi-starter'); $this->virtualhosts_data[$vhosts_filename] .= ' SuexecUserGroup "' . Settings::Get('system.mod_fcgid_httpuser') . '" "' . Settings::Get('system.mod_fcgid_httpgroup') . '"' . "\n"; $this->virtualhosts_data[$vhosts_filename] .= ' ' . "\n"; @@ -280,8 +286,7 @@ class apache extends HttpConfigBase } $this->virtualhosts_data[$vhosts_filename] .= ' ' . "\n"; } - } - elseif (Settings::Get('phpfpm.enabled') == '1') { + } elseif (Settings::Get('phpfpm.enabled') == '1') { // create php-fpm -Part (config is created in apache_fcgid) $domain = array( 'id' => 'none', @@ -295,14 +300,14 @@ class apache extends HttpConfigBase 'loginname' => 'froxlor.panel', 'documentroot' => $mypath ); - + $php = new phpinterface($domain); $phpconfig = $php->getPhpConfig(Settings::Get('phpfpm.vhost_defaultini')); $srvName = substr(md5($ipport), 0, 4) . '.fpm.external'; if ($row_ipsandports['ssl']) { $srvName = substr(md5($ipport), 0, 4) . '.ssl-fpm.external'; } - + // mod_proxy stuff for apache-2.4 if (Settings::Get('system.apache24') == '1' && Settings::Get('phpfpm.use_mod_proxy') == '1') { $this->virtualhosts_data[$vhosts_filename] .= ' ' . "\n"; @@ -347,8 +352,7 @@ class apache extends HttpConfigBase ); } } // end of ssl-redirect check - else - { + else { // fallback of froxlor domain-data for processSpecialConfigTemplate() $domain = array( 'domain' => Settings::Get('system.hostname'), @@ -356,7 +360,7 @@ class apache extends HttpConfigBase 'documentroot' => $mypath ); } - + /** * dirprotection, see #72 * @@ -365,36 +369,36 @@ class apache extends HttpConfigBase * $this->virtualhosts_data[$vhosts_filename] .= "\t\tAllow from all\n"; * $this->virtualhosts_data[$vhosts_filename] .= "\t\tOptions -Indexes\n"; * $this->virtualhosts_data[$vhosts_filename] .= "\t\n"; - * + * * $this->virtualhosts_data[$vhosts_filename] .= "\t\n"; * $this->virtualhosts_data[$vhosts_filename] .= "\t\tOrder Deny,Allow\n"; * $this->virtualhosts_data[$vhosts_filename] .= "\t\tDeny from All\n"; * $this->virtualhosts_data[$vhosts_filename] .= "\t\n"; * end of dirprotection */ - + if ($row_ipsandports['specialsettings'] != '') { $this->virtualhosts_data[$vhosts_filename] .= $this->processSpecialConfigTemplate($row_ipsandports['specialsettings'], $domain, $row_ipsandports['ip'], $row_ipsandports['port'], $row_ipsandports['ssl'] == '1') . "\n"; } - + if ($row_ipsandports['ssl'] == '1' && Settings::Get('system.use_ssl') == '1') { if ($row_ipsandports['ssl_cert_file'] == '') { $row_ipsandports['ssl_cert_file'] = Settings::Get('system.ssl_cert_file'); } - + if ($row_ipsandports['ssl_key_file'] == '') { $row_ipsandports['ssl_key_file'] = Settings::Get('system.ssl_key_file'); } - + if ($row_ipsandports['ssl_ca_file'] == '') { $row_ipsandports['ssl_ca_file'] = Settings::Get('system.ssl_ca_file'); } - + // #418 if ($row_ipsandports['ssl_cert_chainfile'] == '') { $row_ipsandports['ssl_cert_chainfile'] = Settings::Get('system.ssl_cert_chainfile'); } - + $domain = array( 'id' => 0, 'domain' => Settings::Get('system.hostname'), @@ -403,26 +407,26 @@ class apache extends HttpConfigBase 'documentroot' => $mypath, 'parentdomainid' => 0 ); - + // override corresponding array values $domain['ssl_cert_file'] = $row_ipsandports['ssl_cert_file']; $domain['ssl_key_file'] = $row_ipsandports['ssl_key_file']; $domain['ssl_ca_file'] = $row_ipsandports['ssl_ca_file']; $domain['ssl_cert_chainfile'] = $row_ipsandports['ssl_cert_chainfile']; - + // SSL STUFF $dssl = new DomainSSL(); // this sets the ssl-related array-indices in the $domain array // if the domain has customer-defined ssl-certificates $dssl->setDomainSSLFilesArray($domain); - + if ($domain['ssl_cert_file'] != '') { - + // check for existence, #1485 if (! file_exists($domain['ssl_cert_file'])) { $this->logger->logAction(CRON_ACTION, LOG_ERR, $ipport . ' :: certificate file "' . $domain['ssl_cert_file'] . '" does not exist! Cannot create ssl-directives'); } else { - + $this->virtualhosts_data[$vhosts_filename] .= ' SSLEngine On' . "\n"; $this->virtualhosts_data[$vhosts_filename] .= ' SSLProtocol -ALL +TLSv1 +TLSv1.2' . "\n"; if (Settings::Get('system.apache24') == '1') { @@ -436,7 +440,7 @@ class apache extends HttpConfigBase $this->virtualhosts_data[$vhosts_filename] .= ' SSLCipherSuite ' . Settings::Get('system.ssl_cipher_list') . "\n"; $this->virtualhosts_data[$vhosts_filename] .= ' SSLVerifyDepth 10' . "\n"; $this->virtualhosts_data[$vhosts_filename] .= ' SSLCertificateFile ' . makeCorrectFile($domain['ssl_cert_file']) . "\n"; - + if ($domain['ssl_key_file'] != '') { // check for existence, #1485 if (! file_exists($domain['ssl_key_file'])) { @@ -445,7 +449,7 @@ class apache extends HttpConfigBase $this->virtualhosts_data[$vhosts_filename] .= ' SSLCertificateKeyFile ' . makeCorrectFile($domain['ssl_key_file']) . "\n"; } } - + if ($domain['ssl_ca_file'] != '') { // check for existence, #1485 if (! file_exists($domain['ssl_ca_file'])) { @@ -454,7 +458,7 @@ class apache extends HttpConfigBase $this->virtualhosts_data[$vhosts_filename] .= ' SSLCACertificateFile ' . makeCorrectFile($domain['ssl_ca_file']) . "\n"; } } - + // #418 if ($domain['ssl_cert_chainfile'] != '') { // check for existence, #1485 @@ -474,7 +478,7 @@ class apache extends HttpConfigBase $close_vhost = false; } } - + if ($close_vhost) { $this->virtualhosts_data[$vhosts_filename] .= '' . "\n"; } @@ -482,12 +486,12 @@ class apache extends HttpConfigBase } unset($vhosts_filename); } - + /** * bug #32 */ $this->_createStandardDirectoryEntry(); - + /** * bug #unknown-yet */ @@ -505,31 +509,31 @@ class apache extends HttpConfigBase protected function composePhpOptions($domain, $ssl_vhost = false) { $php_options_text = ''; - + if ($domain['phpenabled_customer'] == 1 && $domain['phpenabled_vhost'] == '1') { // This vHost has PHP enabled and we are using the regular mod_php $cmail = getCustomerDetail($domain['customerid'], 'email'); - $php_options_text .= ' php_admin_value sendmail_path "/usr/sbin/sendmail -t -f '.$cmail.'"' . PHP_EOL; - + $php_options_text .= ' php_admin_value sendmail_path "/usr/sbin/sendmail -t -f ' . $cmail . '"' . PHP_EOL; + if ($domain['openbasedir'] == '1') { if ($domain['openbasedir_path'] == '1' || strstr($domain['documentroot'], ":") !== false) { $_phpappendopenbasedir = appendOpenBasedirPath($domain['customerroot'], true); } else { $_phpappendopenbasedir = appendOpenBasedirPath($domain['documentroot'], true); } - + $_custom_openbasedir = explode(':', Settings::Get('system.phpappendopenbasedir')); foreach ($_custom_openbasedir as $cobd) { $_phpappendopenbasedir .= appendOpenBasedirPath($cobd); } - + $php_options_text .= ' php_admin_value open_basedir "' . $_phpappendopenbasedir . '"' . "\n"; } } else { $php_options_text .= ' # PHP is disabled for this vHost' . "\n"; $php_options_text .= ' php_flag engine off' . "\n"; } - + /** * check for apache-itk-support, #1400 * why is this here? Because it only works with mod_php @@ -539,7 +543,7 @@ class apache extends HttpConfigBase $php_options_text .= ' AssignUserID ' . $domain['loginname'] . ' ' . $domain['loginname'] . "\n"; $php_options_text .= ' ' . "\n"; } - + return $php_options_text; } @@ -552,18 +556,18 @@ class apache extends HttpConfigBase protected function getServerNames($domain) { $servernames_text = ' ServerName ' . $domain['domain'] . "\n"; - + $server_alias = ''; if ($domain['iswildcarddomain'] == '1') { $server_alias = '*.' . $domain['domain']; } elseif ($domain['wwwserveralias'] == '1') { $server_alias = 'www.' . $domain['domain']; } - + if (trim($server_alias) != '') { $servernames_text .= ' ServerAlias ' . $server_alias . "\n"; } - + $alias_domains_stmt = Database::prepare(" SELECT `domain`, `iswildcarddomain`, `wwwserveralias` FROM `" . TABLE_PANEL_DOMAINS . "` @@ -572,10 +576,10 @@ class apache extends HttpConfigBase Database::pexecute($alias_domains_stmt, array( 'domainid' => $domain['id'] )); - + while (($alias_domain = $alias_domains_stmt->fetch(PDO::FETCH_ASSOC)) !== false) { $server_alias = ' ServerAlias ' . $alias_domain['domain']; - + if ($alias_domain['iswildcarddomain'] == '1') { $server_alias .= ' *.' . $alias_domain['domain']; } else { @@ -583,10 +587,10 @@ class apache extends HttpConfigBase $server_alias .= ' www.' . $alias_domain['domain']; } } - + $servernames_text .= $server_alias . "\n"; } - + $servernames_text .= ' ServerAdmin ' . $domain['email'] . "\n"; return $servernames_text; } @@ -599,7 +603,7 @@ class apache extends HttpConfigBase $webroot_text = ''; $domain['customerroot'] = makeCorrectDir($domain['customerroot']); $domain['documentroot'] = makeCorrectDir($domain['documentroot']); - + if ($domain['deactivated'] == '1' && Settings::Get('system.deactivateddocroot') != '') { $webroot_text .= ' # Using docroot for deactivated users...' . "\n"; $webroot_text .= ' DocumentRoot "' . makeCorrectDir(Settings::Get('system.deactivateddocroot')) . "\"\n"; @@ -618,7 +622,7 @@ class apache extends HttpConfigBase $webroot_text .= ' DocumentRoot "' . $domain['documentroot'] . "\"\n"; $this->_deactivated = false; } - + return $webroot_text; } @@ -628,7 +632,7 @@ class apache extends HttpConfigBase protected function getStats($domain) { $stats_text = ''; - + if ($domain['speciallogfile'] == '1') { $statDomain = ($domain['parentdomainid'] == '0') ? $domain['domain'] : $domain['parentdomain']; if (Settings::Get('system.awstats_enabled') == '1') { @@ -654,7 +658,7 @@ class apache extends HttpConfigBase $stats_text .= ' Alias /awstats-icon "' . makeCorrectDir(Settings::Get('system.awstats_icons')) . '"' . "\n"; } } - + return $stats_text; } @@ -664,7 +668,7 @@ class apache extends HttpConfigBase protected function getLogfiles($domain) { $logfiles_text = ''; - + if ($domain['speciallogfile'] == '1') { if ($domain['parentdomainid'] == '0') { $speciallogfile = '-' . $domain['domain']; @@ -674,23 +678,23 @@ class apache extends HttpConfigBase } else { $speciallogfile = ''; } - + // The normal access/error - logging is enabled $error_log = makeCorrectFile(Settings::Get('system.logfiles_directory') . $domain['loginname'] . $speciallogfile . '-error.log'); // Create the logfile if it does not exist (fixes #46) touch($error_log); chown($error_log, Settings::Get('system.httpuser')); chgrp($error_log, Settings::Get('system.httpgroup')); - + $access_log = makeCorrectFile(Settings::Get('system.logfiles_directory') . $domain['loginname'] . $speciallogfile . '-access.log'); // Create the logfile if it does not exist (fixes #46) touch($access_log); chown($access_log, Settings::Get('system.httpuser')); chgrp($access_log, Settings::Get('system.httpgroup')); - + $logfiles_text .= ' ErrorLog "' . $error_log . "\"\n"; $logfiles_text .= ' CustomLog "' . $access_log . '" combined' . "\n"; - + if (Settings::Get('system.awstats_enabled') == '1') { if ((int) $domain['parentdomainid'] == 0) { // prepare the aliases and subdomains for stats config files @@ -703,25 +707,25 @@ class apache extends HttpConfigBase Database::pexecute($alias_domains_stmt, array( 'domainid' => $domain['id'] )); - + while (($alias_domain = $alias_domains_stmt->fetch(PDO::FETCH_ASSOC)) !== false) { - + $server_alias .= ' ' . $alias_domain['domain'] . ' '; - + if ($alias_domain['iswildcarddomain'] == '1') { $server_alias .= '*.' . $alias_domain['domain']; } elseif ($alias_domain['wwwserveralias'] == '1') { $server_alias .= 'www.' . $alias_domain['domain']; } } - + $alias = ''; if ($domain['iswildcarddomain'] == '1') { $alias = '*.' . $domain['domain']; } elseif ($domain['wwwserveralias'] == '1') { $alias = 'www.' . $domain['domain']; } - + // After inserting the AWStats information, // be sure to build the awstats conf file as well // and chown it using $awstats_params, #258 @@ -729,7 +733,7 @@ class apache extends HttpConfigBase createAWStatsConf(Settings::Get('system.logfiles_directory') . $domain['loginname'] . $speciallogfile . '-access.log', $domain['domain'], $alias . $server_alias, $domain['customerroot'], $domain); } } - + return $logfiles_text; } @@ -746,13 +750,13 @@ class apache extends HttpConfigBase // number of dots in a domain specifies it's position (and depth of subdomain) starting at 29 going downwards on higher depth $vhost_no = (string) (30 - substr_count($domain['domain'], ".") + 1); } - + if ($ssl_vhost === true) { $vhost_filename = makeCorrectFile(Settings::Get('system.apacheconf_vhost') . '/' . $vhost_no . '_froxlor_ssl_vhost_' . $domain['domain'] . '.conf'); } else { $vhost_filename = makeCorrectFile(Settings::Get('system.apacheconf_vhost') . '/' . $vhost_no . '_froxlor_normal_vhost_' . $domain['domain'] . '.conf'); } - + return $vhost_filename; } @@ -764,27 +768,27 @@ class apache extends HttpConfigBase if ($ssl_vhost === true && ($domain['ssl_redirect'] != '1' && $domain['ssl'] != '1')) { return ''; } - + $query = "SELECT * FROM `" . TABLE_PANEL_IPSANDPORTS . "` `i`, `" . TABLE_DOMAINTOIP . "` `dip` WHERE dip.id_domain = :domainid AND i.id = dip.id_ipandports "; - + if ($ssl_vhost === true && ($domain['ssl'] == '1' || $domain['ssl_redirect'] == '1')) { // by ordering by cert-file the row with filled out SSL-Fields will be shown last, thus it is enough to fill out 1 set of SSL-Fields $query .= "AND i.ssl = '1' ORDER BY i.ssl_cert_file ASC;"; } else { $query .= "AND i.ssl = '0';"; } - + $vhost_content = ''; $result_stmt = Database::prepare($query); Database::pexecute($result_stmt, array( 'domainid' => $domain['id'] )); - + $ipportlist = ''; $_vhost_content = ''; while ($ipandport = $result_stmt->fetch(PDO::FETCH_ASSOC)) { - + $ipport = ''; $domain['ip'] = $ipandport['ip']; $domain['port'] = $ipandport['port']; @@ -793,29 +797,29 @@ class apache extends HttpConfigBase $domain['ssl_key_file'] = $ipandport['ssl_key_file']; $domain['ssl_ca_file'] = $ipandport['ssl_ca_file']; $domain['ssl_cert_chainfile'] = $ipandport['ssl_cert_chainfile']; - + // SSL STUFF $dssl = new DomainSSL(); // this sets the ssl-related array-indices in the $domain array // if the domain has customer-defined ssl-certificates $dssl->setDomainSSLFilesArray($domain); } - + if (filter_var($domain['ip'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { $ipport = '[' . $domain['ip'] . ']:' . $domain['port'] . ' '; } else { $ipport = $domain['ip'] . ':' . $domain['port'] . ' '; } - + if ($ipandport['default_vhostconf_domain'] != '') { $_vhost_content .= $this->processSpecialConfigTemplate($ipandport['default_vhostconf_domain'], $domain, $domain['ip'], $domain['port'], $ssl_vhost) . "\n"; } $ipportlist .= $ipport; } - + $vhost_content .= '' . "\n"; $vhost_content .= $this->getServerNames($domain); - + $domain['documentroot_norewrite'] = $domain['documentroot']; if (($ssl_vhost == false && $domain['ssl'] == '1' && $domain['ssl_redirect'] == '1')) { // We must not check if our port differs from port 443, @@ -834,32 +838,32 @@ class apache extends HttpConfigBase $ssldestport = Database::pexecute_first($ssldestport_stmt, array( 'domainid' => $domain['id'] )); - + if ($ssldestport['port'] != '') { $_sslport = ":" . $ssldestport['port']; } - + $domain['documentroot'] = 'https://%{HTTP_HOST}' . $_sslport . '/'; $domain['documentroot_norewrite'] = 'https://' . $domain['domain'] . $_sslport . '/'; } - + if ($ssl_vhost === true && $domain['ssl'] == '1' && Settings::Get('system.use_ssl') == '1') { if ($domain['ssl_cert_file'] == '') { $domain['ssl_cert_file'] = Settings::Get('system.ssl_cert_file'); } - + if ($domain['ssl_key_file'] == '') { $domain['ssl_key_file'] = Settings::Get('system.ssl_key_file'); } - + if ($domain['ssl_ca_file'] == '') { $domain['ssl_ca_file'] = Settings::Get('system.ssl_ca_file'); } - + if ($domain['ssl_cert_chainfile'] == '') { $domain['ssl_cert_chainfile'] = Settings::Get('system.ssl_cert_chainfile'); } - + if ($domain['ssl_cert_file'] != '') { $vhost_content .= ' SSLEngine On' . "\n"; $vhost_content .= ' SSLProtocol -ALL +TLSv1 +TLSv1.2' . "\n"; @@ -874,25 +878,23 @@ class apache extends HttpConfigBase $vhost_content .= ' SSLCipherSuite ' . Settings::Get('system.ssl_cipher_list') . "\n"; $vhost_content .= ' SSLVerifyDepth 10' . "\n"; $vhost_content .= ' SSLCertificateFile ' . makeCorrectFile($domain['ssl_cert_file']) . "\n"; - + if ($domain['ssl_key_file'] != '') { $vhost_content .= ' SSLCertificateKeyFile ' . makeCorrectFile($domain['ssl_key_file']) . "\n"; } - + if ($domain['ssl_ca_file'] != '') { $vhost_content .= ' SSLCACertificateFile ' . makeCorrectFile($domain['ssl_ca_file']) . "\n"; } - + if ($domain['ssl_cert_chainfile'] != '') { $vhost_content .= ' SSLCertificateChainFile ' . makeCorrectFile($domain['ssl_cert_chainfile']) . "\n"; } - - if (Settings::Get('system.apache24') == '1' && isset($domain['ocsp_stapling']) && - $domain['ocsp_stapling'] == '1') - { + + if (Settings::Get('system.apache24') == '1' && isset($domain['ocsp_stapling']) && $domain['ocsp_stapling'] == '1') { $vhost_content .= ' SSLUseStapling on' . PHP_EOL; } - + if ($domain['hsts'] >= 0) { $vhost_content .= ' ' . "\n"; $vhost_content .= ' Header always set Strict-Transport-Security "max-age=' . $domain['hsts']; @@ -912,20 +914,20 @@ class apache extends HttpConfigBase return '# no ssl-certificate was specified for this domain, therefore no explicit vhost is being generated'; } } - + // avoid using any whitespaces $domain['documentroot'] = trim($domain['documentroot']); - + if (preg_match('/^https?\:\/\//', $domain['documentroot'])) { $corrected_docroot = $domain['documentroot']; - + // Get domain's redirect code $code = getDomainRedirectCode($domain['id'], '301'); $modrew_red = ''; if ($code != '') { $modrew_red = ' [R=' . $code . ';L,NE]'; } - + // redirect everything, not only root-directory, #541 $vhost_content .= ' ' . "\n"; $vhost_content .= ' RewriteEngine On' . "\n"; @@ -941,7 +943,7 @@ class apache extends HttpConfigBase $vhost_content .= ' Redirect ' . $code . ' / ' . $domain['documentroot_norewrite'] . "\n"; $vhost_content .= ' ' . "\n"; } else { - + mkDirWithCorrectOwnership($domain['customerroot'], $domain['documentroot'], $domain['guid'], $domain['guid'], true, true); $vhost_content .= $this->getWebroot($domain); if ($this->_deactivated == false) { @@ -949,22 +951,22 @@ class apache extends HttpConfigBase $vhost_content .= $this->getStats($domain); } $vhost_content .= $this->getLogfiles($domain); - + if ($domain['specialsettings'] != '') { $vhost_content .= $this->processSpecialConfigTemplate($domain['specialsettings'], $domain, $domain['ip'], $domain['port'], $ssl_vhost) . "\n"; } - + if ($_vhost_content != '') { $vhost_content .= $_vhost_content; } - + if (Settings::Get('system.default_vhostconf') != '') { $vhost_content .= $this->processSpecialConfigTemplate(Settings::Get('system.default_vhostconf'), $domain, $domain['ip'], $domain['port'], $ssl_vhost) . "\n"; } } - + $vhost_content .= '' . "\n"; - + return $vhost_content; } @@ -975,17 +977,17 @@ class apache extends HttpConfigBase { $domains = WebserverBase::getVhostsToCreate(); foreach ($domains as $domain) { - + $this->logger->logAction(CRON_ACTION, LOG_INFO, 'apache::createVirtualHosts: creating vhost container for domain ' . $domain['id'] . ', customer ' . $domain['loginname']); $vhosts_filename = $this->getVhostFilename($domain); - + // Apply header $this->virtualhosts_data[$vhosts_filename] = '# Domain ID: ' . $domain['id'] . ' - CustomerID: ' . $domain['customerid'] . ' - CustomerLogin: ' . $domain['loginname'] . "\n"; - + if ($domain['deactivated'] != '1' || Settings::Get('system.deactivateddocroot') != '') { // Create vhost without ssl $this->virtualhosts_data[$vhosts_filename] .= $this->getVhostContent($domain, false); - + if ($domain['ssl'] == '1' || $domain['ssl_redirect'] == '1') { // Adding ssl stuff if enabled $vhosts_filename_ssl = $this->getVhostFilename($domain, true); @@ -1010,27 +1012,27 @@ class apache extends HttpConfigBase ORDER BY `htac`.`path` "); $diroptions = array(); - + while ($row_diroptions = $result_stmt->fetch(PDO::FETCH_ASSOC)) { if ($row_diroptions['customerid'] != 0 && isset($row_diroptions['customerroot']) && $row_diroptions['customerroot'] != '') { $diroptions[$row_diroptions['path']] = $row_diroptions; $diroptions[$row_diroptions['path']]['htpasswds'] = array(); } } - + $result_stmt = Database::query(" SELECT `htpw`.*, `c`.`guid`, `c`.`documentroot` AS `customerroot` FROM `" . TABLE_PANEL_HTPASSWDS . "` `htpw` LEFT JOIN `" . TABLE_PANEL_CUSTOMERS . "` `c` USING (`customerid`) ORDER BY `htpw`.`path`, `htpw`.`username` "); - + while ($row_htpasswds = $result_stmt->fetch(PDO::FETCH_ASSOC)) { if ($row_htpasswds['customerid'] != 0 && isset($row_htpasswds['customerroot']) && $row_htpasswds['customerroot'] != '') { if (! isset($diroptions[$row_htpasswds['path']]) || ! is_array($diroptions[$row_htpasswds['path']])) { $diroptions[$row_htpasswds['path']] = array(); } - + $diroptions[$row_htpasswds['path']]['path'] = $row_htpasswds['path']; $diroptions[$row_htpasswds['path']]['guid'] = $row_htpasswds['guid']; $diroptions[$row_htpasswds['path']]['customerroot'] = $row_htpasswds['customerroot']; @@ -1038,24 +1040,24 @@ class apache extends HttpConfigBase $diroptions[$row_htpasswds['path']]['htpasswds'][] = $row_htpasswds; } } - + foreach ($diroptions as $row_diroptions) { $row_diroptions['path'] = makeCorrectDir($row_diroptions['path']); mkDirWithCorrectOwnership($row_diroptions['customerroot'], $row_diroptions['path'], $row_diroptions['guid'], $row_diroptions['guid']); $diroptions_filename = makeCorrectFile(Settings::Get('system.apacheconf_diroptions') . '/40_froxlor_diroption_' . md5($row_diroptions['path']) . '.conf'); - + if (! isset($this->diroptions_data[$diroptions_filename])) { $this->diroptions_data[$diroptions_filename] = ''; } - + if (is_dir($row_diroptions['path'])) { $cperlenabled = customerHasPerlEnabled($row_diroptions['customerid']); - + $this->diroptions_data[$diroptions_filename] .= '' . "\n"; - + if (isset($row_diroptions['options_indexes']) && $row_diroptions['options_indexes'] == '1') { $this->diroptions_data[$diroptions_filename] .= ' Options +Indexes'; - + // add perl options if enabled if ($cperlenabled && isset($row_diroptions['options_cgi']) && $row_diroptions['options_cgi'] == '1') { $this->diroptions_data[$diroptions_filename] .= ' +ExecCGI -MultiViews +SymLinksIfOwnerMatch +FollowSymLinks' . "\n"; @@ -1064,10 +1066,10 @@ class apache extends HttpConfigBase } $this->logger->logAction(CRON_ACTION, LOG_INFO, 'Setting Options +Indexes for ' . $row_diroptions['path']); } - + if (isset($row_diroptions['options_indexes']) && $row_diroptions['options_indexes'] == '0') { $this->diroptions_data[$diroptions_filename] .= ' Options -Indexes'; - + // add perl options if enabled if ($cperlenabled && isset($row_diroptions['options_cgi']) && $row_diroptions['options_cgi'] == '1') { $this->diroptions_data[$diroptions_filename] .= ' +ExecCGI -MultiViews +SymLinksIfOwnerMatch +FollowSymLinks' . "\n"; @@ -1076,7 +1078,7 @@ class apache extends HttpConfigBase } $this->logger->logAction(CRON_ACTION, LOG_INFO, 'Setting Options -Indexes for ' . $row_diroptions['path']); } - + $statusCodes = array( '404', '403', @@ -1093,7 +1095,7 @@ class apache extends HttpConfigBase $this->diroptions_data[$diroptions_filename] .= ' ErrorDocument ' . $statusCode . ' ' . $defhandler . "\n"; } } - + if ($cperlenabled && isset($row_diroptions['options_cgi']) && $row_diroptions['options_cgi'] == '1') { $this->diroptions_data[$diroptions_filename] .= ' AllowOverride None' . "\n"; $this->diroptions_data[$diroptions_filename] .= ' AddHandler cgi-script .cgi .pl' . "\n"; @@ -1111,18 +1113,18 @@ class apache extends HttpConfigBase $this->diroptions_data[$diroptions_filename] .= ' Allow from all' . "\n"; } $this->logger->logAction(CRON_ACTION, LOG_INFO, 'Enabling perl execution for ' . $row_diroptions['path']); - + // check for suexec-workaround, #319 if ((int) Settings::Get('perl.suexecworkaround') == 1) { // symlink this directory to suexec-safe-path $loginname = getCustomerDetail($row_diroptions['customerid'], 'loginname'); $suexecpath = makeCorrectDir(Settings::Get('perl.suexecpath') . '/' . $loginname . '/' . md5($row_diroptions['path']) . '/'); - + if (! file_exists($suexecpath)) { safe_exec('mkdir -p ' . escapeshellarg($suexecpath)); safe_exec('chown -R ' . escapeshellarg($row_diroptions['guid']) . ':' . escapeshellarg($row_diroptions['guid']) . ' ' . escapeshellarg($suexecpath)); } - + // symlink to {$givenpath}/cgi-bin // NOTE: symlinks are FILES, so do not append a / here $perlsymlink = makeCorrectFile($row_diroptions['path'] . '/cgi-bin'); @@ -1138,7 +1140,7 @@ class apache extends HttpConfigBase $loginname = getCustomerDetail($row_diroptions['customerid'], 'loginname'); $suexecpath = makeCorrectDir(Settings::Get('perl.suexecpath') . '/' . $loginname . '/' . md5($row_diroptions['path']) . '/'); $perlsymlink = makeCorrectFile($row_diroptions['path'] . '/cgi-bin'); - + // remove symlink if (file_exists($perlsymlink)) { safe_exec('rm -f ' . escapeshellarg($perlsymlink)); @@ -1149,24 +1151,24 @@ class apache extends HttpConfigBase } } } - + if (count($row_diroptions['htpasswds']) > 0) { $htpasswd_filename = makeCorrectFile(Settings::Get('system.apacheconf_htpasswddir') . '/' . $row_diroptions['customerid'] . '-' . md5($row_diroptions['path']) . '.htpasswd'); - + if (! isset($this->htpasswds_data[$htpasswd_filename])) { $this->htpasswds_data[$htpasswd_filename] = ''; } - + foreach ($row_diroptions['htpasswds'] as $row_htpasswd) { $this->htpasswds_data[$htpasswd_filename] .= $row_htpasswd['username'] . ':' . $row_htpasswd['password'] . "\n"; } - + $this->diroptions_data[$diroptions_filename] .= ' AuthType Basic' . "\n"; $this->diroptions_data[$diroptions_filename] .= ' AuthName "' . $row_htpasswd['authname'] . '"' . "\n"; $this->diroptions_data[$diroptions_filename] .= ' AuthUserFile ' . $htpasswd_filename . "\n"; $this->diroptions_data[$diroptions_filename] .= ' require valid-user' . "\n"; } - + $this->diroptions_data[$diroptions_filename] .= '' . "\n"; } } @@ -1179,19 +1181,19 @@ class apache extends HttpConfigBase { // Write diroptions $this->logger->logAction(CRON_ACTION, LOG_INFO, "apache::writeConfigs: rebuilding " . Settings::Get('system.apacheconf_diroptions')); - + if (count($this->diroptions_data) > 0) { $optsDir = new frxDirectory(Settings::Get('system.apacheconf_diroptions')); if (! $optsDir->isConfigDir()) { // Save one big file $diroptions_file = ''; - + foreach ($this->diroptions_data as $diroptions_filename => $diroptions_content) { $diroptions_file .= $diroptions_content . "\n\n"; } - + $diroptions_filename = Settings::Get('system.apacheconf_diroptions'); - + // Apply header $diroptions_file = '# ' . basename($diroptions_filename) . "\n" . '# Created ' . date('d.m.Y H:i') . "\n" . '# Do NOT manually edit this file, all changes will be deleted after the next domain change at the panel.' . "\n" . "\n" . $diroptions_file; $diroptions_file_handler = fopen($diroptions_filename, 'w'); @@ -1202,11 +1204,11 @@ class apache extends HttpConfigBase $this->logger->logAction(CRON_ACTION, LOG_NOTICE, 'apache::writeConfigs: mkdir ' . escapeshellarg(makeCorrectDir(Settings::Get('system.apacheconf_diroptions')))); safe_exec('mkdir ' . escapeshellarg(makeCorrectDir(Settings::Get('system.apacheconf_diroptions')))); } - + // Write a single file for every diroption foreach ($this->diroptions_data as $diroptions_filename => $diroptions_file) { $this->known_diroptionsfilenames[] = basename($diroptions_filename); - + // Apply header $diroptions_file = '# ' . basename($diroptions_filename) . "\n" . '# Created ' . date('d.m.Y H:i') . "\n" . '# Do NOT manually edit this file, all changes will be deleted after the next domain change at the panel.' . "\n" . "\n" . $diroptions_file; $diroptions_file_handler = fopen($diroptions_filename, 'w'); @@ -1215,10 +1217,10 @@ class apache extends HttpConfigBase } } } - + // Write htpasswds $this->logger->logAction(CRON_ACTION, LOG_INFO, "apache::writeConfigs: rebuilding " . Settings::Get('system.apacheconf_htpasswddir')); - + if (count($this->htpasswds_data) > 0) { if (! file_exists(Settings::Get('system.apacheconf_htpasswddir'))) { $umask = umask(); @@ -1226,7 +1228,7 @@ class apache extends HttpConfigBase mkdir(Settings::Get('system.apacheconf_htpasswddir'), 0751); umask($umask); } - + $htpasswdDir = new frxDirectory(Settings::Get('system.apacheconf_htpasswddir')); if ($htpasswdDir->isConfigDir(true)) { foreach ($this->htpasswds_data as $htpasswd_filename => $htpasswd_file) { @@ -1239,34 +1241,34 @@ class apache extends HttpConfigBase $this->logger->logAction(CRON_ACTION, LOG_WARNING, 'WARNING!!! ' . Settings::Get('system.apacheconf_htpasswddir') . ' is not a directory. htpasswd directory protection is disabled!!!'); } } - + // Write virtualhosts $this->logger->logAction(CRON_ACTION, LOG_INFO, "apache::writeConfigs: rebuilding " . Settings::Get('system.apacheconf_vhost')); - + if (count($this->virtualhosts_data) > 0) { $vhostDir = new frxDirectory(Settings::Get('system.apacheconf_vhost')); if (! $vhostDir->isConfigDir()) { // Save one big file $vhosts_file = ''; - + // sort by filename so the order is: // 1. subdomains x-29 // 2. subdomains as main-domains 30 // 3. main-domains 35 // #437 ksort($this->virtualhosts_data); - + foreach ($this->virtualhosts_data as $vhosts_filename => $vhost_content) { $vhosts_file .= $vhost_content . "\n\n"; } - + // Include diroptions file in case it exists if (file_exists(Settings::Get('system.apacheconf_diroptions'))) { $vhosts_file .= "\n" . 'Include ' . Settings::Get('system.apacheconf_diroptions') . "\n\n"; } - + $vhosts_filename = Settings::Get('system.apacheconf_vhost'); - + // Apply header $vhosts_file = '# ' . basename($vhosts_filename) . "\n" . '# Created ' . date('d.m.Y H:i') . "\n" . '# Do NOT manually edit this file, all changes will be deleted after the next domain change at the panel.' . "\n" . "\n" . $vhosts_file; $vhosts_file_handler = fopen($vhosts_filename, 'w'); @@ -1277,11 +1279,11 @@ class apache extends HttpConfigBase $this->logger->logAction(CRON_ACTION, LOG_NOTICE, 'apache::writeConfigs: mkdir ' . escapeshellarg(makeCorrectDir(Settings::Get('system.apacheconf_vhost')))); safe_exec('mkdir ' . escapeshellarg(makeCorrectDir(Settings::Get('system.apacheconf_vhost')))); } - + // Write a single file for every vhost foreach ($this->virtualhosts_data as $vhosts_filename => $vhosts_file) { $this->known_vhostfilenames[] = basename($vhosts_filename); - + // Apply header $vhosts_file = '# ' . basename($vhosts_filename) . "\n" . '# Created ' . date('d.m.Y H:i') . "\n" . '# Do NOT manually edit this file, all changes will be deleted after the next domain change at the panel.' . "\n" . "\n" . $vhosts_file; $vhosts_file_handler = fopen($vhosts_filename, 'w'); diff --git a/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php b/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php index a2d5258b..3525a471 100644 --- a/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php +++ b/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php @@ -58,8 +58,15 @@ class lighttpd extends HttpConfigBase public function reload() { if ((int) Settings::Get('phpfpm.enabled') == 1) { - $this->logger->logAction(CRON_ACTION, LOG_INFO, 'lighttpd::reload: reloading php-fpm'); - safe_exec(escapeshellcmd(Settings::Get('phpfpm.reload'))); + // get all start/stop commands + $startstop_sel = Database::prepare("SELECT reload_cmd FROM `" . TABLE_PANEL_FPMDAEMONS . "`"); + Database::pexecute($startstop_sel); + $restart_cmds = $startstop_sel->fetchAll(PDO::FETCH_ASSOC); + // restart all php-fpm instances + foreach ($restart_cmds as $restart_cmd) { + $this->logger->logAction(CRON_ACTION, LOG_INFO, 'lighttpd::reload: running ' . $restart_cmd['reload_cmd']); + safe_exec(escapeshellcmd($restart_cmd['reload_cmd'])); + } } $this->logger->logAction(CRON_ACTION, LOG_INFO, 'lighttpd::reload: reloading lighttpd'); safe_exec(escapeshellcmd(Settings::Get('system.apachereload_command'))); diff --git a/scripts/jobs/cron_tasks.inc.http.30.nginx.php b/scripts/jobs/cron_tasks.inc.http.30.nginx.php index bc7f23fc..2bf53daa 100644 --- a/scripts/jobs/cron_tasks.inc.http.30.nginx.php +++ b/scripts/jobs/cron_tasks.inc.http.30.nginx.php @@ -73,8 +73,15 @@ class nginx extends HttpConfigBase $this->logger->logAction(CRON_ACTION, LOG_INFO, 'nginx::reload: restarting php processes'); safe_exec(Settings::Get('system.phpreload_command')); } elseif ((int) Settings::Get('phpfpm.enabled') == 1) { - $this->logger->logAction(CRON_ACTION, LOG_INFO, 'nginx::reload: reloading php-fpm'); - safe_exec(escapeshellcmd(Settings::Get('phpfpm.reload'))); + // get all start/stop commands + $startstop_sel = Database::prepare("SELECT reload_cmd FROM `" . TABLE_PANEL_FPMDAEMONS . "`"); + Database::pexecute($startstop_sel); + $restart_cmds = $startstop_sel->fetchAll(PDO::FETCH_ASSOC); + // restart all php-fpm instances + foreach ($restart_cmds as $restart_cmd) { + $this->logger->logAction(CRON_ACTION, LOG_INFO, 'nginx::reload: running ' . $restart_cmd['reload_cmd']); + safe_exec(escapeshellcmd($restart_cmd['reload_cmd'])); + } } } diff --git a/templates/Sparkle/admin/phpconfig/fpmconfig_add.tpl b/templates/Sparkle/admin/phpconfig/fpmconfig_add.tpl new file mode 100644 index 00000000..51a31dfd --- /dev/null +++ b/templates/Sparkle/admin/phpconfig/fpmconfig_add.tpl @@ -0,0 +1,24 @@ +$header +
+
+

+ {$title}  + {$title} +

+
+ +
+ +
+ + + + + + + {$fpmconfig_add_form} +
+
+
+
+$footer diff --git a/templates/Sparkle/admin/phpconfig/fpmconfig_edit.tpl b/templates/Sparkle/admin/phpconfig/fpmconfig_edit.tpl new file mode 100644 index 00000000..c1383a9b --- /dev/null +++ b/templates/Sparkle/admin/phpconfig/fpmconfig_edit.tpl @@ -0,0 +1,25 @@ +$header +
+
+

+ {$title}  + {$title} +

+
+ +
+ +
+ + + + + + + + {$fpmconfig_edit_form} +
+
+
+
+$footer diff --git a/templates/Sparkle/admin/phpconfig/fpmdaemons.tpl b/templates/Sparkle/admin/phpconfig/fpmdaemons.tpl new file mode 100644 index 00000000..9f4f95b9 --- /dev/null +++ b/templates/Sparkle/admin/phpconfig/fpmdaemons.tpl @@ -0,0 +1,42 @@ +$header +
+
+

+   + {$lng['menue']['phpsettings']['maintitle']} +

+
+ +
+ + + + + + + + + + + + + + + $tablecontent + +
{$lng['admin']['phpsettings']['description']}{$lng['admin']['phpsettings']['activephpconfigs']}{$lng['serversettings']['phpfpm_settings']['reload']}{$lng['serversettings']['phpfpm_settings']['configdir']}{$lng['serversettings']['phpfpm_settings']['pm']}{$lng['panel']['options']}
+ + + + + +
+ +
+$footer diff --git a/templates/Sparkle/admin/phpconfig/fpmdaemons_overview.tpl b/templates/Sparkle/admin/phpconfig/fpmdaemons_overview.tpl new file mode 100644 index 00000000..fe2d894a --- /dev/null +++ b/templates/Sparkle/admin/phpconfig/fpmdaemons_overview.tpl @@ -0,0 +1,17 @@ + + {$row['description']} + {$configs} + {$row['reload_cmd']} + {$row['config_dir']} + {$row['pm']} + + + {$lng['panel']['edit']} + + +   + {$lng['panel']['delete']} + + + + diff --git a/templates/Sparkle/admin/phpconfig/overview.tpl b/templates/Sparkle/admin/phpconfig/overview.tpl index 6fcfc428..aac895ac 100644 --- a/templates/Sparkle/admin/phpconfig/overview.tpl +++ b/templates/Sparkle/admin/phpconfig/overview.tpl @@ -19,7 +19,11 @@ $header {$lng['admin']['phpsettings']['description']} {$lng['admin']['phpsettings']['activedomains']} - {$lng['admin']['phpsettings']['binary']} + + {$lng['admin']['phpsettings']['fpmdesc']} + + {$lng['admin']['phpsettings']['binary']} + {$lng['admin']['phpsettings']['file_extensions']} {$lng['panel']['options']} diff --git a/templates/Sparkle/admin/phpconfig/overview_overview.tpl b/templates/Sparkle/admin/phpconfig/overview_overview.tpl index 2df5fd6f..0ccc9f58 100644 --- a/templates/Sparkle/admin/phpconfig/overview_overview.tpl +++ b/templates/Sparkle/admin/phpconfig/overview_overview.tpl @@ -1,7 +1,11 @@ {$row['description']} {$domains} - {$row['binary']} + + {$row['fpmdesc']} + + {$row['binary']} + {$row['file_extensions']} From 73868b794774567bbd8e5f3bc1c07a7cd6a95b14 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sun, 7 Jan 2018 23:31:39 +0100 Subject: [PATCH 0349/1335] soften the file cleaning to reduce risk Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/webserver/class.ConfigIO.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/classes/webserver/class.ConfigIO.php b/lib/classes/webserver/class.ConfigIO.php index 10fe1b50..14e95085 100644 --- a/lib/classes/webserver/class.ConfigIO.php +++ b/lib/classes/webserver/class.ConfigIO.php @@ -65,7 +65,7 @@ class ConfigIO // now get rid of old stuff // (but append /*.log so we don't delete the directory) $err_dir .= '/*.log'; - safe_exec('rm -rf ' . makeCorrectFile($err_dir)); + safe_exec('rm -f ' . makeCorrectFile($err_dir)); } } @@ -92,7 +92,7 @@ class ConfigIO // now get rid of old stuff // (but append /* so we don't delete the directory) $configdir .= '/*'; - safe_exec('rm -rf ' . makeCorrectFile($configdir)); + safe_exec('rm -f ' . makeCorrectFile($configdir)); } } } @@ -159,7 +159,7 @@ class ConfigIO // now get rid of old stuff // (but append /* so we don't delete the directory) $configdir .= '/*'; - safe_exec('rm -rf ' . makeCorrectFile($configdir)); + safe_exec('rm -f ' . makeCorrectFile($configdir)); } } } @@ -269,9 +269,9 @@ class ConfigIO $configdir = makeCorrectDir($configdir['config_dir']); if (@is_dir($configdir)) { // now get rid of old stuff - // (but append /* so we don't delete the directory) - $configdir .= '/*'; - safe_exec('rm -rf ' . makeCorrectFile($configdir)); + // (but append /*.conf so we don't delete the directory) + $configdir .= '/*.conf'; + safe_exec('rm -f ' . makeCorrectFile($configdir)); } else { safe_exec('mkdir -p ' . $configdir); } From 07caf55f79ed0f9d39738dbe5c55b7a63568bc2f Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 8 Jan 2018 09:16:00 +0100 Subject: [PATCH 0350/1335] fixes to multi-fpm in cron Signed-off-by: Michael Kaufmann (d00p) --- .../phpinterface/class.phpinterface_fpm.php | 5 ++- lib/classes/webserver/class.WebserverBase.php | 34 ++++++++++++------- .../jobs/cron_tasks.inc.http.10.apache.php | 12 ++++++- .../cron_tasks.inc.http.15.apache_fcgid.php | 13 ++++++- 4 files changed, 48 insertions(+), 16 deletions(-) diff --git a/lib/classes/phpinterface/class.phpinterface_fpm.php b/lib/classes/phpinterface/class.phpinterface_fpm.php index b4a577ac..2f84a2a2 100644 --- a/lib/classes/phpinterface/class.phpinterface_fpm.php +++ b/lib/classes/phpinterface/class.phpinterface_fpm.php @@ -160,6 +160,9 @@ class phpinterface_fpm */ public function __construct($domain) { + if (!isset($domain['fpm_config_id']) || empty($domain['fpm_config_id'])) { + $domain['fpm_config_id'] = 1; + } $this->_domain = $domain; $this->_readFpmConfig($domain['fpm_config_id']); } @@ -354,7 +357,7 @@ class phpinterface_fpm */ public function getConfigFile($createifnotexists = true) { - $configdir = $this->_fpm_cfg['config_path']; + $configdir = $this->_fpm_cfg['config_dir']; $config = makeCorrectFile($configdir . '/' . $this->_domain['domain'] . '.conf'); if (! is_dir($configdir) && $createifnotexists) { diff --git a/lib/classes/webserver/class.WebserverBase.php b/lib/classes/webserver/class.WebserverBase.php index 5181bd25..f16073c8 100644 --- a/lib/classes/webserver/class.WebserverBase.php +++ b/lib/classes/webserver/class.WebserverBase.php @@ -46,6 +46,22 @@ class WebserverBase $result_domains_stmt = Database::query($query); + // prepare IP statement + $ip_stmt = Database::prepare(" + SELECT `di`.`id_domain` , `p`.`ssl`, `p`.`ssl_cert_file`, `p`.`ssl_key_file`, `p`.`ssl_ca_file`, `p`.`ssl_cert_chainfile` + FROM `" . TABLE_DOMAINTOIP . "` `di`, `" . TABLE_PANEL_IPSANDPORTS . "` `p` + WHERE `p`.`id` = `di`.`id_ipandports` + AND `di`.`id_domain` = :domainid + AND `p`.`ssl` = '1' + "); + + // prepare fpm-config select query + $fpm_sel_stmt = Database::prepare(" + SELECT f.id FROM `" . TABLE_PANEL_FPMDAEMONS . "` f + LEFT JOIN `" . TABLE_PANEL_PHPCONFIGS . "` p ON p.fpmsettingid = f.id + WHERE p.id = :phpconfigid + "); + $domains = array(); while ($domain = $result_domains_stmt->fetch(PDO::FETCH_ASSOC)) { @@ -61,14 +77,7 @@ class WebserverBase // now, if the domain has an ssl ip/port assigned, get // the corresponding information from the db if (domainHasSslIpPort($domain['id'])) { - - $ip_stmt = Database::prepare(" - SELECT `di`.`id_domain` , `p`.`ssl`, `p`.`ssl_cert_file`, `p`.`ssl_key_file`, `p`.`ssl_ca_file`, `p`.`ssl_cert_chainfile` - FROM `" . TABLE_DOMAINTOIP . "` `di`, `" . TABLE_PANEL_IPSANDPORTS . "` `p` - WHERE `p`.`id` = `di`.`id_ipandports` - AND `di`.`id_domain` = :domainid - AND `p`.`ssl` = '1' - "); + $ssl_ip = Database::pexecute_first($ip_stmt, array( 'domainid' => $domain['id'] )); @@ -83,16 +92,15 @@ class WebserverBase // read fpm-config-id if using fpm if ((int) Settings::Get('phpfpm.enabled') == 1) { - $fpm_sel_stmt = Database::prepare(" - SELECT f.id FROM `" . TABLE_PANEL_FPMDAEMONS . "` f - LEFT JOIN `" . TABLE_PANEL_PHPCONFIGS . "` p ON p.fpmsettingid = f.id - WHERE p.id = :phpconfigid - "); + $fpm_config = Database::pexecute_first($fpm_sel_stmt, array( 'phpconfigid' => $domain['phpsettingid'] )); if ($fpm_config) { $domains[$domain['domain']]['fpm_config_id'] = $fpm_config['id']; + } else { + // fallback + $domains[$domain['domain']]['fpm_config_id'] = 1; } } } diff --git a/scripts/jobs/cron_tasks.inc.http.10.apache.php b/scripts/jobs/cron_tasks.inc.http.10.apache.php index f4107512..c594e6d3 100644 --- a/scripts/jobs/cron_tasks.inc.http.10.apache.php +++ b/scripts/jobs/cron_tasks.inc.http.10.apache.php @@ -287,6 +287,15 @@ class apache extends HttpConfigBase $this->virtualhosts_data[$vhosts_filename] .= ' ' . "\n"; } } elseif (Settings::Get('phpfpm.enabled') == '1') { + // get fpm config + $fpm_sel_stmt = Database::prepare(" + SELECT f.id FROM `" . TABLE_PANEL_FPMDAEMONS . "` f + LEFT JOIN `" . TABLE_PANEL_PHPCONFIGS . "` p ON p.fpmsettingid = f.id + WHERE p.id = :phpconfigid + "); + $fpm_config = Database::pexecute_first($fpm_sel_stmt, array( + 'phpconfigid' => Settings::Get('phpfpm.vhost_defaultini') + )); // create php-fpm -Part (config is created in apache_fcgid) $domain = array( 'id' => 'none', @@ -298,7 +307,8 @@ class apache extends HttpConfigBase 'openbasedir' => 0, 'email' => Settings::Get('panel.adminmail'), 'loginname' => 'froxlor.panel', - 'documentroot' => $mypath + 'documentroot' => $mypath, + 'fpm_config_id' => isset($fpm_config['id']) ? $fpm_config['id'] : 1 ); $php = new phpinterface($domain); diff --git a/scripts/jobs/cron_tasks.inc.http.15.apache_fcgid.php b/scripts/jobs/cron_tasks.inc.http.15.apache_fcgid.php index 6c9bd2a2..ab53539c 100644 --- a/scripts/jobs/cron_tasks.inc.http.15.apache_fcgid.php +++ b/scripts/jobs/cron_tasks.inc.http.15.apache_fcgid.php @@ -155,6 +155,16 @@ class apache_fcgid extends apache ) { $user = Settings::Get('phpfpm.vhost_httpuser'); $group = Settings::Get('phpfpm.vhost_httpgroup'); + + // get fpm config + $fpm_sel_stmt = Database::prepare(" + SELECT f.id FROM `" . TABLE_PANEL_FPMDAEMONS . "` f + LEFT JOIN `" . TABLE_PANEL_PHPCONFIGS . "` p ON p.fpmsettingid = f.id + WHERE p.id = :phpconfigid + "); + $fpm_config = Database::pexecute_first($fpm_sel_stmt, array( + 'phpconfigid' => Settings::Get('phpfpm.vhost_defaultini') + )); } $domain = array( @@ -167,7 +177,8 @@ class apache_fcgid extends apache 'openbasedir' => 0, 'email' => Settings::Get('panel.adminmail'), 'loginname' => 'froxlor.panel', - 'documentroot' => $mypath + 'documentroot' => $mypath, + 'fpm_config_id' => isset($fpm_config['id']) ? $fpm_config['id'] : 1 ); // all the files and folders have to belong to the local user From ebd636494a4d35115f5204ec560a6c8374fe6e5f Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 8 Jan 2018 10:11:38 +0100 Subject: [PATCH 0351/1335] fix wrong table constant Signed-off-by: Michael Kaufmann (d00p) --- admin_phpsettings.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/admin_phpsettings.php b/admin_phpsettings.php index eb72f741..52809867 100644 --- a/admin_phpsettings.php +++ b/admin_phpsettings.php @@ -541,7 +541,7 @@ if ($page == 'overview') { } $upd_stmt = Database::prepare(" - UPDATE `" . TABLE_PANEL_PHPCONFIGS . "` SET + UPDATE `" . TABLE_PANEL_FPMDAEMONS . "` SET `description` = :desc, `reload_cmd` = :reload_cmd, `config_dir` = :config_dir, From 3dc6a64252b5cc38c2283600bebfcd7cdd6793f0 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 8 Jan 2018 10:22:09 +0100 Subject: [PATCH 0352/1335] fix pm select when editing fpmdaemon; fix saving of pm value Signed-off-by: Michael Kaufmann (d00p) --- admin_phpsettings.php | 8 ++++---- .../admin/phpconfig/formfield.fpmconfig_edit.php | 3 +-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/admin_phpsettings.php b/admin_phpsettings.php index 52809867..2b2e33b7 100644 --- a/admin_phpsettings.php +++ b/admin_phpsettings.php @@ -402,7 +402,7 @@ if ($page == 'overview') { $description = validate($_POST['description'], 'description'); $reload_cmd = validate($_POST['reload_cmd'], 'reload_cmd'); $config_dir = validate($_POST['config_dir'], 'config_dir'); - $pm = validate($_POST['pm'], 'pm'); + $pm = $_POST['pm']; $max_children = isset($_POST['max_children']) ? (int) $_POST['max_children'] : 0; $start_servers = isset($_POST['start_servers']) ? (int) $_POST['start_servers'] : 0; $min_spare_servers = isset($_POST['min_spare_servers']) ? (int) $_POST['min_spare_servers'] : 0; @@ -431,7 +431,7 @@ if ($page == 'overview') { 'desc' => $description, 'reload_cmd' => $reload_cmd, 'config_dir' => makeCorrectDir($config_dir), - 'pm' => pm, + 'pm' => $pm, 'max_children' => $max_children, 'start_servers' => $start_servers, 'min_spare_servers' => $min_spare_servers, @@ -528,7 +528,7 @@ if ($page == 'overview') { $description = validate($_POST['description'], 'description'); $reload_cmd = validate($_POST['reload_cmd'], 'reload_cmd'); $config_dir = validate($_POST['config_dir'], 'config_dir'); - $pm = validate($_POST['pm'], 'pm'); + $pm = $_POST['pm']; $max_children = isset($_POST['max_children']) ? (int) $_POST['max_children'] : $result['max_children']; $start_servers = isset($_POST['start_servers']) ? (int) $_POST['start_servers'] : $result['start_servers']; $min_spare_servers = isset($_POST['min_spare_servers']) ? (int) $_POST['min_spare_servers'] : $result['min_spare_servers']; @@ -558,7 +558,7 @@ if ($page == 'overview') { 'desc' => $description, 'reload_cmd' => $reload_cmd, 'config_dir' => makeCorrectDir($config_dir), - 'pm' => pm, + 'pm' => $pm, 'max_children' => $max_children, 'start_servers' => $start_servers, 'min_spare_servers' => $min_spare_servers, diff --git a/lib/formfields/admin/phpconfig/formfield.fpmconfig_edit.php b/lib/formfields/admin/phpconfig/formfield.fpmconfig_edit.php index a06592ed..d5e8c17f 100644 --- a/lib/formfields/admin/phpconfig/formfield.fpmconfig_edit.php +++ b/lib/formfields/admin/phpconfig/formfield.fpmconfig_edit.php @@ -45,8 +45,7 @@ return array( 'pm' => array( 'label' => $lng['serversettings']['phpfpm_settings']['pm'], 'type' => 'select', - 'select_var' => array('static' => 'static', 'dynamic' => 'dynamic', 'ondemand' => 'ondemand'), - 'value' => $result['pm'] + 'select_var' => $pm_select ), 'max_children' => array( 'label' => $lng['serversettings']['phpfpm_settings']['max_children']['title'], From b849a5f29a066e7c8a613edbbc9d5eac5e247989 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 8 Jan 2018 13:59:30 +0100 Subject: [PATCH 0353/1335] preparations for php-config select for customers Signed-off-by: Michael Kaufmann (d00p) --- admin_domains.php | 20 +++++-- customer_domains.php | 58 ++++++++++++++++++- install/froxlor.sql | 3 +- .../updates/froxlor/0.9/update_0.9.inc.php | 9 +++ .../domains/formfield.domains_add.php | 6 ++ .../domains/formfield.domains_edit.php | 6 ++ lib/version.inc.php | 2 +- 7 files changed, 97 insertions(+), 7 deletions(-) diff --git a/admin_domains.php b/admin_domains.php index 5e410c45..9eb1934e 100644 --- a/admin_domains.php +++ b/admin_domains.php @@ -1077,11 +1077,15 @@ if ($page == 'domains' || $page == 'overview') { } $phpconfigs = ''; - $configs = Database::query("SELECT * FROM `" . TABLE_PANEL_PHPCONFIGS . "`"); + $configs = Database::query(" + SELECT c.*, fc.description as interpreter + FROM `" . TABLE_PANEL_PHPCONFIGS . "` c + LEFT JOIN `" . TABLE_PANEL_FPMDAEMONS . "` fc ON fc.id = c.fpmsettingid + "); while ($row = $configs->fetch(PDO::FETCH_ASSOC)) { if ((int) Settings::Get('phpfpm.enabled') == 1) { - $phpconfigs .= makeoption($row['description'], $row['id'], Settings::Get('phpfpm.defaultini'), true, true); + $phpconfigs .= makeoption($row['description'] . " [".$row['interpreter']."]", $row['id'], Settings::Get('phpfpm.defaultini'), true, true); } else { $phpconfigs .= makeoption($row['description'], $row['id'], Settings::Get('system.mod_fcgid_defaultini'), true, true); } @@ -2180,10 +2184,18 @@ if ($page == 'domains' || $page == 'overview') { $result['add_date'] = date('Y-m-d', $result['add_date']); $phpconfigs = ''; - $phpconfigs_result_stmt = Database::query("SELECT * FROM `" . TABLE_PANEL_PHPCONFIGS . "`"); + $phpconfigs_result_stmt = Database::query(" + SELECT c.*, fc.description as interpreter + FROM `" . TABLE_PANEL_PHPCONFIGS . "` c + LEFT JOIN `" . TABLE_PANEL_FPMDAEMONS . "` fc ON fc.id = c.fpmsettingid + "); while ($phpconfigs_row = $phpconfigs_result_stmt->fetch(PDO::FETCH_ASSOC)) { - $phpconfigs .= makeoption($phpconfigs_row['description'], $phpconfigs_row['id'], $result['phpsettingid'], true, true); + if ((int) Settings::Get('phpfpm.enabled') == 1) { + $phpconfigs .= makeoption($phpconfigs_row['description'] . " [".$phpconfigs_row['interpreter']."]", $phpconfigs_row['id'], $result['phpsettingid'], true, true); + } else { + $phpconfigs .= makeoption($phpconfigs_row['description'], $phpconfigs_row['id'], $result['phpsettingid'], true, true); + } } $result = htmlentities_array($result); diff --git a/customer_domains.php b/customer_domains.php index 3bfcd382..f14b69a7 100644 --- a/customer_domains.php +++ b/customer_domains.php @@ -405,6 +405,10 @@ if ($page == 'overview') { // assign default config $phpsid_result['phpsettingid'] = 1; } + // check whether the customer has chosen its own php-config + if (isset($_POST['phpsettingid']) && intval($_POST['phpsettingid']) != $phpsid_result['phpsettingid']) { + $phpsid_result['phpsettingid'] = intval($_POST['phpsettingid']); + } $stmt = Database::prepare("INSERT INTO `" . TABLE_PANEL_DOMAINS . "` SET `customerid` = :customerid, @@ -534,6 +538,27 @@ if ($page == 'overview') { $openbasedir = makeoption($lng['domain']['docroot'], 0, NULL, true) . makeoption($lng['domain']['homedir'], 1, NULL, true); $pathSelect = makePathfield($userinfo['documentroot'], $userinfo['guid'], $userinfo['guid']); + $phpconfigs = ''; + $has_phpconfigs = false; + if (isset($userinfo['allowed_phpconfigs']) && !empty($userinfo['allowed_phpconfigs'])) + { + $has_phpconfigs = true; + $allowed_cfg = json_decode($userinfo['allowed_phpconfigs'], JSON_OBJECT_AS_ARRAY); + $phpconfigs_result_stmt = Database::query(" + SELECT c.*, fc.description as interpreter + FROM `" . TABLE_PANEL_PHPCONFIGS . "` c + LEFT JOIN `" . TABLE_PANEL_FPMDAEMONS . "` fc ON fc.id = c.fpmsettingid + WHERE c.id IN (".implode(", ", $allowed_cfg).") + "); + while ($phpconfigs_row = $phpconfigs_result_stmt->fetch(PDO::FETCH_ASSOC)) { + if ((int) Settings::Get('phpfpm.enabled') == 1) { + $phpconfigs .= makeoption($phpconfigs_row['description'] . " [".$phpconfigs_row['interpreter']."]", $phpconfigs_row['id'], Settings::Get('phpfpm.defaultini'), true, true); + } else { + $phpconfigs .= makeoption($phpconfigs_row['description'], $phpconfigs_row['id'], Settings::Get('system.mod_fcgid_defaultini'), true, true); + } + } + } + $subdomain_add_data = include_once dirname(__FILE__).'/lib/formfields/customer/domains/formfield.domains_add.php'; $subdomain_add_form = htmlform::genHTMLForm($subdomain_add_data); @@ -624,6 +649,13 @@ if ($page == 'overview') { $openbasedir_path = '0'; } + // check whether the customer has chosen its own php-config + if (isset($_POST['phpsettingid'])) { + $phpsettingid = intval($_POST['phpsettingid']); + } else { + $phpsettingid = $result['phpsettingid']; + } + if (isset($_POST['ssl_redirect']) && $_POST['ssl_redirect'] == '1') { // a ssl-redirect only works if there actually is a // ssl ip/port assigned to the domain @@ -692,6 +724,7 @@ if ($page == 'overview') { || $hsts_maxage != $result['hsts'] || $hsts_sub != $result['hsts_sub'] || $hsts_preload != $result['hsts_preload'] + || $phpsettingid != $result['phpsettingid'] ) { $log->logAction(USR_ACTION, LOG_INFO, "edited domain '" . $idna_convert->decode($result['domain']) . "'"); @@ -706,7 +739,8 @@ if ($page == 'overview') { `letsencrypt`= :letsencrypt, `hsts` = :hsts, `hsts_sub` = :hsts_sub, - `hsts_preload` = :hsts_preload + `hsts_preload` = :hsts_preload, + `phpsettingid` = :phpsettingid WHERE `customerid`= :customerid AND `id`= :id" ); @@ -722,6 +756,7 @@ if ($page == 'overview') { "hsts" => $hsts_maxage, "hsts_sub" => $hsts_sub, "hsts_preload" => $hsts_preload, + "phpsettingid" => $phpsettingid, "customerid" => $userinfo['customerid'], "id" => $id ); @@ -846,6 +881,27 @@ if ($page == 'overview') { $result_ipandport['ip'] .= $rowip['ip'] . "
"; } + $phpconfigs = ''; + $has_phpconfigs = false; + if (isset($userinfo['allowed_phpconfigs']) && !empty($userinfo['allowed_phpconfigs'])) + { + $has_phpconfigs = true; + $allowed_cfg = json_decode($userinfo['allowed_phpconfigs'], JSON_OBJECT_AS_ARRAY); + $phpconfigs_result_stmt = Database::query(" + SELECT c.*, fc.description as interpreter + FROM `" . TABLE_PANEL_PHPCONFIGS . "` c + LEFT JOIN `" . TABLE_PANEL_FPMDAEMONS . "` fc ON fc.id = c.fpmsettingid + WHERE c.id IN (".implode(", ", $allowed_cfg).") + "); + while ($phpconfigs_row = $phpconfigs_result_stmt->fetch(PDO::FETCH_ASSOC)) { + if ((int) Settings::Get('phpfpm.enabled') == 1) { + $phpconfigs .= makeoption($phpconfigs_row['description'] . " [".$phpconfigs_row['interpreter']."]", $phpconfigs_row['id'], $result['phpsettingid'], true, true); + } else { + $phpconfigs .= makeoption($phpconfigs_row['description'], $phpconfigs_row['id'], $result['phpsettingid'], true, true); + } + } + } + $domainip = $result_ipandport['ip']; $result = htmlentities_array($result); diff --git a/install/froxlor.sql b/install/froxlor.sql index 4883fe14..3fdc57dd 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -198,6 +198,7 @@ CREATE TABLE `panel_customers` ( `lepublickey` mediumtext default NULL, `leprivatekey` mediumtext default NULL, `leregistered` tinyint(1) NOT NULL default '0', + `allowed_phpconfigs` varchar(500) NOT NULL default '', PRIMARY KEY (`customerid`), UNIQUE KEY `loginname` (`loginname`) ) ENGINE=MyISAM CHARSET=utf8 COLLATE=utf8_general_ci; @@ -587,7 +588,7 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES ('panel', 'password_special_char', '!?<>§$%+#=@'), ('panel', 'customer_hide_options', ''), ('panel', 'version', '0.9.38.8'), - ('panel', 'db_version', '201801070'); + ('panel', 'db_version', '201801080'); DROP TABLE IF EXISTS `panel_tasks`; diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index 8ae1c344..0483ba44 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -3716,3 +3716,12 @@ if (isDatabaseVersion('201712310')) { updateToDbVersion('201801070'); } + +if (isDatabaseVersion('201801070')) { + + showUpdateStep("Adding field allowed_phpconfigs for customers"); + Database::query("ALTER TABLE `" . TABLE_PANEL_CUSTOMERS . "` ADD `allowed_phpconfigs` varchar(500) NOT NULL default '';"); + lastStepStatus(0); + + updateToDbVersion('201801080'); +} diff --git a/lib/formfields/customer/domains/formfield.domains_add.php b/lib/formfields/customer/domains/formfield.domains_add.php index 238adfff..ad660098 100644 --- a/lib/formfields/customer/domains/formfield.domains_add.php +++ b/lib/formfields/customer/domains/formfield.domains_add.php @@ -70,6 +70,12 @@ return array( 'label' => $lng['domain']['openbasedirpath'], 'type' => 'select', 'select_var' => $openbasedir + ), + 'phpsettingid' => array( + 'visible' => (((int) Settings::Get('system.mod_fcgid') == 1 || (int) Settings::Get('phpfpm.enabled') == 1) && $has_phpconfigs ? true : false), + 'label' => $lng['admin']['phpsettings']['title'], + 'type' => 'select', + 'select_var' => $phpconfigs ) ) ), diff --git a/lib/formfields/customer/domains/formfield.domains_edit.php b/lib/formfields/customer/domains/formfield.domains_edit.php index 6ac6d186..31910123 100644 --- a/lib/formfields/customer/domains/formfield.domains_edit.php +++ b/lib/formfields/customer/domains/formfield.domains_edit.php @@ -81,6 +81,12 @@ return array( 'label' => $lng['domain']['openbasedirpath'], 'type' => 'select', 'select_var' => $openbasedir + ), + 'phpsettingid' => array( + 'visible' => (((int) Settings::Get('system.mod_fcgid') == 1 || (int) Settings::Get('phpfpm.enabled') == 1) && $has_phpconfigs ? true : false), + 'label' => $lng['admin']['phpsettings']['title'], + 'type' => 'select', + 'select_var' => $phpconfigs ) ) ), diff --git a/lib/version.inc.php b/lib/version.inc.php index f6174b9a..5f433e8b 100644 --- a/lib/version.inc.php +++ b/lib/version.inc.php @@ -19,7 +19,7 @@ $version = '0.9.38.8'; // Database version (YYYYMMDDC where C is a daily counter) -$dbversion = '201801070'; +$dbversion = '201801080'; // Distribution branding-tag (used for Debian etc.) $branding = ''; From c434249616c5c3b57c7504eed8cec903b44d65fc Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 8 Jan 2018 14:45:00 +0100 Subject: [PATCH 0354/1335] allow admin to set php-configs that can be used by customers Signed-off-by: Michael Kaufmann (d00p) --- admin_customers.php | 60 ++++++++++ .../admin/customer/formfield.customer_add.php | 112 +++++++++++++----- .../customer/formfield.customer_edit.php | 8 ++ 3 files changed, 149 insertions(+), 31 deletions(-) diff --git a/admin_customers.php b/admin_customers.php index 3a88dd38..74dc10b8 100644 --- a/admin_customers.php +++ b/admin_customers.php @@ -532,6 +532,14 @@ if ($page == 'customers' $phpenabled = intval($_POST['phpenabled']); } + $allowed_phpconfigs = array(); + if (isset($_POST['allowed_phpconfigs']) && is_array($_POST['allowed_phpconfigs'])) { + foreach ($_POST['allowed_phpconfigs'] as $allowed_phpconfig) { + $allowed_phpconfig = intval($allowed_phpconfig); + $allowed_phpconfigs[] = $allowed_phpconfig; + } + } + $perlenabled = 0; if (isset($_POST['perlenabled'])) { $perlenabled = intval($_POST['perlenabled']); @@ -693,6 +701,7 @@ if ($page == 'customers' 'tickets' => $tickets, 'mysqls' => $mysqls, 'phpenabled' => $phpenabled, + 'allowed_phpconfigs' => empty($allowed_phpconfigs) ? "" : json_encode($allowed_phpconfigs), 'imap' => $email_imap, 'pop3' => $email_pop3, 'perlenabled' => $perlenabled, @@ -733,6 +742,7 @@ if ($page == 'customers' `mysqls` = :mysqls, `standardsubdomain` = '0', `phpenabled` = :phpenabled, + `allowed_phpconfigs` = :allowed_phpconfigs, `imap` = :imap, `pop3` = :pop3, `perlenabled` = :perlenabled, @@ -1043,6 +1053,26 @@ if ($page == 'customers' $gender_options .= makeoption($lng['gender']['male'], 1, null, true, true); $gender_options .= makeoption($lng['gender']['female'], 2, null, true, true); + $phpconfigs = array(); + $configs = Database::query(" + SELECT c.*, fc.description as interpreter + FROM `" . TABLE_PANEL_PHPCONFIGS . "` c + LEFT JOIN `" . TABLE_PANEL_FPMDAEMONS . "` fc ON fc.id = c.fpmsettingid + "); + while ($row = $configs->fetch(PDO::FETCH_ASSOC)) { + if ((int) Settings::Get('phpfpm.enabled') == 1) { + $phpconfigs[] = array( + 'label' => $row['description'] . " [".$row['interpreter']."]
", + 'value' => $row['id'] + ); + } else { + $phpconfigs[] = array( + 'label' => $row['description']."
", + 'value' => $row['id'] + ); + } + } + $customer_add_data = include_once dirname(__FILE__).'/lib/formfields/admin/customer/formfield.customer_add.php'; $customer_add_form = htmlform::genHTMLForm($customer_add_data); @@ -1205,6 +1235,14 @@ if ($page == 'customers' $phpenabled = intval($_POST['phpenabled']); } + $allowed_phpconfigs = array(); + if (isset($_POST['allowed_phpconfigs']) && is_array($_POST['allowed_phpconfigs'])) { + foreach ($_POST['allowed_phpconfigs'] as $allowed_phpconfig) { + $allowed_phpconfig = intval($allowed_phpconfig); + $allowed_phpconfigs[] = $allowed_phpconfig; + } + } + $perlenabled = 0; if (isset($_POST['perlenabled'])) { $perlenabled = intval($_POST['perlenabled']); @@ -1457,6 +1495,7 @@ if ($page == 'customers' 'mysqls' => $mysqls, 'deactivated' => $deactivated, 'phpenabled' => $phpenabled, + 'allowed_phpconfigs' => empty($allowed_phpconfigs) ? "" : json_encode($allowed_phpconfigs), 'imap' => $email_imap, 'pop3' => $email_pop3, 'perlenabled' => $perlenabled, @@ -1490,6 +1529,7 @@ if ($page == 'customers' `mysqls` = :mysqls, `deactivated` = :deactivated, `phpenabled` = :phpenabled, + `allowed_phpconfigs` = :allowed_phpconfigs, `email_quota` = :email_quota, `imap` = :imap, `pop3` = :pop3, @@ -1695,6 +1735,26 @@ if ($page == 'customers' $gender_options .= makeoption($lng['gender']['male'], 1, ($result['gender'] == '1' ? true : false), true, true); $gender_options .= makeoption($lng['gender']['female'], 2, ($result['gender'] == '2' ? true : false), true, true); + $phpconfigs = array(); + $configs = Database::query(" + SELECT c.*, fc.description as interpreter + FROM `" . TABLE_PANEL_PHPCONFIGS . "` c + LEFT JOIN `" . TABLE_PANEL_FPMDAEMONS . "` fc ON fc.id = c.fpmsettingid + "); + while ($row = $configs->fetch(PDO::FETCH_ASSOC)) { + if ((int) Settings::Get('phpfpm.enabled') == 1) { + $phpconfigs[] = array( + 'label' => $row['description'] . " [".$row['interpreter']."]
", + 'value' => $row['id'] + ); + } else { + $phpconfigs[] = array( + 'label' => $row['description']."
", + 'value' => $row['id'] + ); + } + } + $customer_edit_data = include_once dirname(__FILE__).'/lib/formfields/admin/customer/formfield.customer_edit.php'; $customer_edit_form = htmlform::genHTMLForm($customer_edit_data); diff --git a/lib/formfields/admin/customer/formfield.customer_add.php b/lib/formfields/admin/customer/formfield.customer_add.php index eaa408aa..9d3d26ef 100644 --- a/lib/formfields/admin/customer/formfield.customer_add.php +++ b/lib/formfields/admin/customer/formfield.customer_add.php @@ -14,7 +14,6 @@ * @package Formfields * */ - return array( 'customer_add' => array( 'title' => $lng['admin']['customer_add'], @@ -29,20 +28,30 @@ return array( 'type' => 'text' ), 'createstdsubdomain' => array( - 'label' => $lng['admin']['stdsubdomain_add'].'?', + 'label' => $lng['admin']['stdsubdomain_add'] . '?', 'type' => 'checkbox', 'values' => array( - array ('label' => $lng['panel']['yes'], 'value' => '1') - ), - 'value' => array('1') + array( + 'label' => $lng['panel']['yes'], + 'value' => '1' + ) + ), + 'value' => array( + '1' + ) ), 'store_defaultindex' => array( - 'label' => $lng['admin']['store_defaultindex'].'?', + 'label' => $lng['admin']['store_defaultindex'] . '?', 'type' => 'checkbox', 'values' => array( - array ('label' => $lng['panel']['yes'], 'value' => '1') - ), - 'value' => array('1') + array( + 'label' => $lng['panel']['yes'], + 'value' => '1' + ) + ), + 'value' => array( + '1' + ) ), 'new_customer_password' => array( 'label' => $lng['login']['password'], @@ -53,15 +62,20 @@ return array( 'label' => $lng['customer']['generated_pwd'], 'type' => 'text', 'visible' => (Settings::Get('panel.password_regex') == ''), - 'value' => generatePassword(), + 'value' => generatePassword() ), 'sendpassword' => array( 'label' => $lng['admin']['sendpassword'], 'type' => 'checkbox', 'values' => array( - array ('label' => $lng['panel']['yes'], 'value' => '1') - ), - 'value' => array('1') + array( + 'label' => $lng['panel']['yes'], + 'value' => '1' + ) + ), + 'value' => array( + '1' + ) ), 'def_language' => array( 'label' => $lng['login']['language'], @@ -135,7 +149,10 @@ return array( 'label' => $lng['usersettings']['custom_notes']['show'], 'type' => 'checkbox', 'values' => array( - array ('label' => $lng['panel']['yes'], 'value' => '1') + array( + 'label' => $lng['panel']['yes'], + 'value' => '1' + ) ), 'value' => array() ) @@ -206,18 +223,28 @@ return array( 'label' => $lng['customer']['email_imap'], 'type' => 'checkbox', 'values' => array( - array ('label' => $lng['panel']['yes'], 'value' => '1') - ), - 'value' => array('1'), + array( + 'label' => $lng['panel']['yes'], + 'value' => '1' + ) + ), + 'value' => array( + '1' + ), 'mandatory' => true ), 'email_pop3' => array( 'label' => $lng['customer']['email_pop3'], 'type' => 'checkbox', 'values' => array( - array ('label' => $lng['panel']['yes'], 'value' => '1') - ), - 'value' => array('1'), + array( + 'label' => $lng['panel']['yes'], + 'value' => '1' + ) + ), + 'value' => array( + '1' + ), 'mandatory' => true ), 'ftps' => array( @@ -244,28 +271,51 @@ return array( 'ul_field' => $mysqls_ul ), 'phpenabled' => array( - 'label' => $lng['admin']['phpenabled'].'?', + 'label' => $lng['admin']['phpenabled'] . '?', 'type' => 'checkbox', 'values' => array( - array ('label' => $lng['panel']['yes'], 'value' => '1') - ), - 'value' => array('1') + array( + 'label' => $lng['panel']['yes'], + 'value' => '1' + ) + ), + 'value' => array( + '1' + ) + ), + 'allowed_phpconfigs' => array( + 'visible' => (((int) Settings::Get('system.mod_fcgid') == 1 || (int) Settings::Get('phpfpm.enabled') == 1) ? true : false), + 'label' => $lng['admin']['phpsettings']['title'], + 'type' => 'checkbox', + 'values' => $phpconfigs, + 'value' => ((int) Settings::Get('system.mod_fcgid') == 1 ? array( + Settings::Get('system.mod_fcgid_defaultini') + ) : (int) Settings::Get('phpfpm.enabled') == 1) ? array( + Settings::Get('phpfpm.defaultini') + ) : array(), + 'is_array' => 1 ), 'perlenabled' => array( - 'label' => $lng['admin']['perlenabled'].'?', + 'label' => $lng['admin']['perlenabled'] . '?', 'type' => 'checkbox', 'values' => array( - array ('label' => $lng['panel']['yes'], 'value' => '1') - ) + array( + 'label' => $lng['panel']['yes'], + 'value' => '1' + ) + ) ), 'dnsenabled' => array( - 'label' => $lng['admin']['dnsenabled'].'?', + 'label' => $lng['admin']['dnsenabled'] . '?', 'type' => 'checkbox', 'values' => array( - array ('label' => $lng['panel']['yes'], 'value' => '1') - ), + array( + 'label' => $lng['panel']['yes'], + 'value' => '1' + ) + ), 'visible' => (Settings::Get('system.dnsenabled') == '1' ? true : false) - ), + ) ) ) ) diff --git a/lib/formfields/admin/customer/formfield.customer_edit.php b/lib/formfields/admin/customer/formfield.customer_edit.php index b099651f..c5f74d74 100644 --- a/lib/formfields/admin/customer/formfield.customer_edit.php +++ b/lib/formfields/admin/customer/formfield.customer_edit.php @@ -260,6 +260,14 @@ return array( ), 'value' => array($result['phpenabled']) ), + 'allowed_phpconfigs' => array( + 'visible' => (((int) Settings::Get('system.mod_fcgid') == 1 || (int) Settings::Get('phpfpm.enabled') == 1) ? true : false), + 'label' => $lng['admin']['phpsettings']['title'], + 'type' => 'checkbox', + 'values' => $phpconfigs, + 'value' => isset($result['allowed_phpconfigs']) && !empty($result['allowed_phpconfigs']) ? json_decode($result['allowed_phpconfigs'], JSON_OBJECT_AS_ARRAY) : array(), + 'is_array' => 1 + ), 'perlenabled' => array( 'label' => $lng['admin']['perlenabled'].'?', 'type' => 'checkbox', From eaa10ce6a5387d19c3d6ab16a573d0e37286631a Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 8 Jan 2018 15:31:27 +0100 Subject: [PATCH 0355/1335] add option to update php-configs for all subdomains when editing a domain as admin (default: yes) Signed-off-by: Michael Kaufmann (d00p) --- admin_domains.php | 16 +++++++++++++--- .../admin/domains/formfield.domains_edit.php | 15 +++++++++++++++ lng/english.lng.php | 2 ++ lng/german.lng.php | 2 ++ 4 files changed, 32 insertions(+), 3 deletions(-) diff --git a/admin_domains.php b/admin_domains.php index 9eb1934e..0faa8854 100644 --- a/admin_domains.php +++ b/admin_domains.php @@ -1363,6 +1363,7 @@ if ($page == 'domains' || $page == 'overview') { $phpenabled = isset($_POST['phpenabled']) ? intval($_POST['phpenabled']) : 0; $openbasedir = isset($_POST['openbasedir']) ? intval($_POST['openbasedir']) : 0; + $phpfs = (isset($_POST['phpsettingsforsubdomains']) && intval($_POST['phpsettingsforsubdomains']) == 1) ? 1 : 0; if ((int) Settings::Get('system.mod_fcgid') == 1 || (int) Settings::Get('phpfpm.enabled') == 1) { $phpsettingid = (int) $_POST['phpsettingid']; @@ -1392,6 +1393,7 @@ if ($page == 'domains' || $page == 'overview') { } } else { $phpsettingid = $result['phpsettingid']; + $phpfs = 1; $mod_fcgid_starter = $result['mod_fcgid_starter']; $mod_fcgid_maxrequests = $result['mod_fcgid_maxrequests']; } @@ -1399,6 +1401,7 @@ if ($page == 'domains' || $page == 'overview') { $phpenabled = $result['phpenabled']; $openbasedir = $result['openbasedir']; $phpsettingid = $result['phpsettingid']; + $phpfs = 1; $mod_fcgid_starter = $result['mod_fcgid_starter']; $mod_fcgid_maxrequests = $result['mod_fcgid_maxrequests']; } @@ -1637,6 +1640,7 @@ if ($page == 'domains' || $page == 'overview') { 'phpenabled' => $phpenabled, 'openbasedir' => $openbasedir, 'phpsettingid' => $phpsettingid, + 'phpsettingsforsubdomains' => $phpfs, 'mod_fcgid_starter' => $mod_fcgid_starter, 'mod_fcgid_maxrequests' => $mod_fcgid_maxrequests, 'specialsettings' => $specialsettings, @@ -1892,11 +1896,18 @@ if ($page == 'domains' || $page == 'overview') { $_update_data['adminid'] = $adminid; $_update_data['phpenabled'] = $phpenabled; $_update_data['openbasedir'] = $openbasedir; - $_update_data['phpsettingid'] = $phpsettingid; $_update_data['mod_fcgid_starter'] = $mod_fcgid_starter; $_update_data['mod_fcgid_maxrequests'] = $mod_fcgid_maxrequests; $_update_data['parentdomainid'] = $id; + // if php config is to be set for all subdomains, check here + $update_phpconfig = ''; + $phpfs = isset($_POST['phpsettingsforsubdomains']) ? 1 : 0; + if ($phpfs == 1) { + $_update_data['phpsettingid'] = $phpsettingid; + $update_phpconfig = ", `phpsettingid` = :phpsettingid"; + } + // if we have no more ssl-ip's for this domain, // all its subdomains must have "ssl-redirect = 0" // and disable let's encrypt @@ -1911,10 +1922,9 @@ if ($page == 'domains' || $page == 'overview') { `adminid` = :adminid, `phpenabled` = :phpenabled, `openbasedir` = :openbasedir, - `phpsettingid` = :phpsettingid, `mod_fcgid_starter` = :mod_fcgid_starter, `mod_fcgid_maxrequests` = :mod_fcgid_maxrequests - " . $upd_specialsettings . $updatechildren . $update_sslredirect . " + " . $update_phpconfig . $upd_specialsettings . $updatechildren . $update_sslredirect . " WHERE `parentdomainid` = :parentdomainid "); Database::pexecute($_update_stmt, $_update_data); diff --git a/lib/formfields/admin/domains/formfield.domains_edit.php b/lib/formfields/admin/domains/formfield.domains_edit.php index d668aa9d..5588ab69 100644 --- a/lib/formfields/admin/domains/formfield.domains_edit.php +++ b/lib/formfields/admin/domains/formfield.domains_edit.php @@ -325,6 +325,21 @@ return array( 'type' => 'select', 'select_var' => $phpconfigs ), + 'phpsettingsforsubdomains' => array( + 'visible' => ($userinfo['change_serversettings'] == '1' ? true : false), + 'label' => $lng['admin']['phpsettingsforsubdomains'], + 'desc' => $lng['serversettings']['phpsettingsforsubdomains']['description'], + 'type' => 'checkbox', + 'values' => array( + array( + 'label' => $lng['panel']['yes'], + 'value' => '1' + ) + ), + 'value' => array( + '1' + ) + ), 'mod_fcgid_starter' => array( 'visible' => ((int) Settings::Get('system.mod_fcgid') == 1 ? true : false), 'label' => $lng['admin']['mod_fcgid_starter']['title'], diff --git a/lng/english.lng.php b/lng/english.lng.php index 2a850d00..039073ff 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -2086,3 +2086,5 @@ $lng['serversettings']['disable_le_selfcheck']['title'] = "Disable Let's Encrypt $lng['serversettings']['disable_le_selfcheck']['description'] = "If activated, froxlor will not perform its self-check for token accessability. Needed for NATed IP's or similar."; $lng['menue']['phpsettings']['fpmdaemons'] = 'PHP-FPM versions'; $lng['admin']['phpsettings']['activephpconfigs'] = 'In use for php-config(s)'; +$lng['admin']['phpsettingsforsubdomains'] = 'Apply php-config to all subdomains:'; +$lng['serversettings']['phpsettingsforsubdomains']['description'] = 'If yes the chosen php-config will be updated to all subdomains'; diff --git a/lng/german.lng.php b/lng/german.lng.php index f20700ec..941ebf82 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -1737,3 +1737,5 @@ $lng['serversettings']['disable_le_selfcheck']['title'] = "Deaktiviere Let's Enc $lng['serversettings']['disable_le_selfcheck']['description'] = "Wenn aktiviert wird Froxlor keine Erreichbarkeitsprüfung des Tokens vornehmen. Nötig bei ge-NAT-eten IP's oder Ähnlichem"; $lng['menue']['phpsettings']['fpmdaemons'] = 'PHP-FPM Versionen'; $lng['admin']['phpsettings']['activephpconfigs'] = 'In Verwendung für PHP-Konfiguration(en)'; +$lng['admin']['phpsettingsforsubdomains'] = 'PHP-Config für alle Subdomains übernehmen:'; +$lng['serversettings']['phpsettingsforsubdomains']['description'] = 'Wenn ja, wird die gewählte PHP-Config für alle Subdomains übernommen'; From d8abe30c449c3471ca5c3d698fc0e9a8fdd02a97 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 9 Jan 2018 09:25:56 +0100 Subject: [PATCH 0356/1335] create dummy pool-config whenever a fpm-daemons configdir is empty so it still restarts Signed-off-by: Michael Kaufmann (d00p) --- .../phpinterface/class.phpinterface_fpm.php | 21 +++++++++++++++++++ .../jobs/cron_tasks.inc.http.10.apache.php | 9 +++++++- .../jobs/cron_tasks.inc.http.20.lighttpd.php | 9 +++++++- scripts/jobs/cron_tasks.inc.http.30.nginx.php | 9 +++++++- 4 files changed, 45 insertions(+), 3 deletions(-) diff --git a/lib/classes/phpinterface/class.phpinterface_fpm.php b/lib/classes/phpinterface/class.phpinterface_fpm.php index 2f84a2a2..2622b090 100644 --- a/lib/classes/phpinterface/class.phpinterface_fpm.php +++ b/lib/classes/phpinterface/class.phpinterface_fpm.php @@ -435,6 +435,27 @@ class phpinterface_fpm return $configdir; } + /** + * create a dummy fpm pool config with minimal configuration + * (this is used whenever a config directory is empty but needs at least one pool to startup/restart) + * + * @param string $configdir + */ + public static function createDummyPool($configdir) + { + if (! is_dir($configdir)) { + safe_exec('mkdir -p ' . escapeshellarg($configdir)); + } + $config = makeCorrectFile($configdir . '/dummy.conf'); + $dummy = "[dummy] +user = ".Settings::Get('system.httpuser')." +listen = /run/" . base64_encode($configdir) . "-fpm.sock +pm = static +pm.max_children = 1 +"; + file_put_contents($config, $dummy); + } + /** * return the admin-data of a specific admin * diff --git a/scripts/jobs/cron_tasks.inc.http.10.apache.php b/scripts/jobs/cron_tasks.inc.http.10.apache.php index c594e6d3..5887e9a7 100644 --- a/scripts/jobs/cron_tasks.inc.http.10.apache.php +++ b/scripts/jobs/cron_tasks.inc.http.10.apache.php @@ -59,11 +59,18 @@ class apache extends HttpConfigBase { if ((int) Settings::Get('phpfpm.enabled') == 1) { // get all start/stop commands - $startstop_sel = Database::prepare("SELECT reload_cmd FROM `" . TABLE_PANEL_FPMDAEMONS . "`"); + $startstop_sel = Database::prepare("SELECT reload_cmd, config_dir FROM `" . TABLE_PANEL_FPMDAEMONS . "`"); Database::pexecute($startstop_sel); $restart_cmds = $startstop_sel->fetchAll(PDO::FETCH_ASSOC); // restart all php-fpm instances foreach ($restart_cmds as $restart_cmd) { + // check whether the config dir is empty (no domains uses this daemon) + // so we need to create a dummy + $isDirEmpty = !(new \FilesystemIterator($restart_cmd['config_dir']))->valid(); + if ($isDirEmpty) { + $this->logger->logAction(CRON_ACTION, LOG_INFO, 'apache::reload: fpm config directory "' . $restart_cmd['config_dir'] . '" is empty. Creating dummy.'); + phpinterface_fpm::createDummyPool($restart_cmd['config_dir']); + } $this->logger->logAction(CRON_ACTION, LOG_INFO, 'apache::reload: running ' . $restart_cmd['reload_cmd']); safe_exec(escapeshellcmd($restart_cmd['reload_cmd'])); } diff --git a/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php b/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php index 3525a471..99232c8c 100644 --- a/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php +++ b/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php @@ -59,11 +59,18 @@ class lighttpd extends HttpConfigBase { if ((int) Settings::Get('phpfpm.enabled') == 1) { // get all start/stop commands - $startstop_sel = Database::prepare("SELECT reload_cmd FROM `" . TABLE_PANEL_FPMDAEMONS . "`"); + $startstop_sel = Database::prepare("SELECT reload_cmd, config_dir FROM `" . TABLE_PANEL_FPMDAEMONS . "`"); Database::pexecute($startstop_sel); $restart_cmds = $startstop_sel->fetchAll(PDO::FETCH_ASSOC); // restart all php-fpm instances foreach ($restart_cmds as $restart_cmd) { + // check whether the config dir is empty (no domains uses this daemon) + // so we need to create a dummy + $isDirEmpty = !(new \FilesystemIterator($restart_cmd['config_dir']))->valid(); + if ($isDirEmpty) { + $this->logger->logAction(CRON_ACTION, LOG_INFO, 'lighttpd::reload: fpm config directory "' . $restart_cmd['config_dir'] . '" is empty. Creating dummy.'); + phpinterface_fpm::createDummyPool($restart_cmd['config_dir']); + } $this->logger->logAction(CRON_ACTION, LOG_INFO, 'lighttpd::reload: running ' . $restart_cmd['reload_cmd']); safe_exec(escapeshellcmd($restart_cmd['reload_cmd'])); } diff --git a/scripts/jobs/cron_tasks.inc.http.30.nginx.php b/scripts/jobs/cron_tasks.inc.http.30.nginx.php index 2bf53daa..0685ed7c 100644 --- a/scripts/jobs/cron_tasks.inc.http.30.nginx.php +++ b/scripts/jobs/cron_tasks.inc.http.30.nginx.php @@ -74,11 +74,18 @@ class nginx extends HttpConfigBase safe_exec(Settings::Get('system.phpreload_command')); } elseif ((int) Settings::Get('phpfpm.enabled') == 1) { // get all start/stop commands - $startstop_sel = Database::prepare("SELECT reload_cmd FROM `" . TABLE_PANEL_FPMDAEMONS . "`"); + $startstop_sel = Database::prepare("SELECT reload_cmd, config_dir FROM `" . TABLE_PANEL_FPMDAEMONS . "`"); Database::pexecute($startstop_sel); $restart_cmds = $startstop_sel->fetchAll(PDO::FETCH_ASSOC); // restart all php-fpm instances foreach ($restart_cmds as $restart_cmd) { + // check whether the config dir is empty (no domains uses this daemon) + // so we need to create a dummy + $isDirEmpty = !(new \FilesystemIterator($restart_cmd['config_dir']))->valid(); + if ($isDirEmpty) { + $this->logger->logAction(CRON_ACTION, LOG_INFO, 'nginx::reload: fpm config directory "' . $restart_cmd['config_dir'] . '" is empty. Creating dummy.'); + phpinterface_fpm::createDummyPool($restart_cmd['config_dir']); + } $this->logger->logAction(CRON_ACTION, LOG_INFO, 'nginx::reload: running ' . $restart_cmd['reload_cmd']); safe_exec(escapeshellcmd($restart_cmd['reload_cmd'])); } From 3969ef63c5b96180a9239166e3b232b7e4ed657b Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 9 Jan 2018 11:18:03 +0100 Subject: [PATCH 0357/1335] do not check hide-options 'domains' in customer_tickets, fixes #502 Signed-off-by: Michael Kaufmann (d00p) --- customer_tickets.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/customer_tickets.php b/customer_tickets.php index d8562826..1a7508b9 100644 --- a/customer_tickets.php +++ b/customer_tickets.php @@ -20,10 +20,6 @@ define('AREA', 'customer'); require './lib/init.php'; -// redirect if this customer page is hidden via settings -if (Settings::IsInList('panel.customer_hide_options','domains')) { - redirectTo('customer_index.php'); -} if (isset($_POST['id'])) { From 9aaadb1f8b4997fe9c6b82131f7581f1199e15e4 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 9 Jan 2018 14:40:36 +0100 Subject: [PATCH 0358/1335] implement lets-encrypt api-v02 (testing only currently; not activated in froxlor, test with 'php froxlor_master_cronjob.php --letsencrypt_v2 --debug' but set api endpoint to staging); no chain is returned currently, seems to be a known bug Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/ssl/class.lescript.php | 75 +--- lib/classes/ssl/class.lescript_v2.php | 599 ++++++++++++++++++++++++++ scripts/jobs/cron_letsencrypt_v2.php | 292 +++++++++++++ 3 files changed, 898 insertions(+), 68 deletions(-) create mode 100644 lib/classes/ssl/class.lescript_v2.php create mode 100644 scripts/jobs/cron_letsencrypt_v2.php diff --git a/lib/classes/ssl/class.lescript.php b/lib/classes/ssl/class.lescript.php index 9923d5b0..c1745be7 100644 --- a/lib/classes/ssl/class.lescript.php +++ b/lib/classes/ssl/class.lescript.php @@ -30,7 +30,6 @@ class lescript { // https://letsencrypt.org/repository/ - public $license; private $logger; @@ -111,19 +110,7 @@ class lescript } $accountUrl=$this->client->getLastLocation(); - $this->log('Accepting lets encrypt Terms of Service'); - - $this->license = $this->client->getAgreementURL(); - - // Terms of Service are optional according to ACME specs; if no ToS are presented, no need to update registration - if (!empty($this->license)) { - $response = $this->postRegAgreement(parse_url($accountUrl, PHP_URL_PATH)); - if ($this->client->getLastCode() != 202) { - throw new \RuntimeException("Terms of Service not accepted. Whole response: " . json_encode($response)); - } - } - - $leregistered=1; + $leregistered = 1; $this->setLeRegisteredState($leregistered); // Account registered $this->log('Lets encrypt Terms of Service accepted'); } @@ -373,21 +360,16 @@ class lescript private function postNewReg() { + $this->log('Getting last terms of service URL'); + $directory = $this->client->get('/directory'); + if (!isset($directory['meta']) || !isset($directory['meta']['terms-of-service'])) { + throw new \RuntimeException("No terms of service link available!"); + } $this->log('Sending registration to letsencrypt server'); return $this->signedRequest('/acme/new-reg', array( 'resource' => 'new-reg', - 'agreement' => $this->license - )); - } - - private function postRegAgreement($uri) - { - $this->log('Accepting agreement at URL: ' . $this->license); - - return $this->signedRequest($uri, array( - 'resource' => 'reg', - 'agreement' => $this->license + 'agreement' => $directory['meta']['terms-of-service'] )); } @@ -592,49 +574,6 @@ class Client preg_match_all('~Link: <(.+)>;rel="up"~', $this->lastHeader, $matches); return $matches[1]; } - - public function getAgreementURLFromLastResponse() - { - if (preg_match_all('~Link: <(.+)>;rel="terms-of-service"~', $this->lastHeader, $matches)) { - return $matches[1][0]; - } - return ""; - } - public function getAgreementURLFromDirectory() - { - // FIXME: Current license should be found in /directory but LE does not implement this yet - // $this->curl('GET', '/directory'); - return ""; - } - public function getAgreementURLFromTermsUrl() - { - $this->curl('GET', '/terms'); - if (preg_match_all('~Location: (.+)~', $this->lastHeader, $matches)) { - return trim($matches[1][0]); - } - return ""; - } - - public function getAgreementURL() - { - // 1. check the header of the last response - $license=$this->getAgreementURLFromLastResponse(); - if (!empty($license)) return $license; - - // 2. query directory for license - $license=$this->getAgreementURLFromDirectory(); - if (!empty($license)) return $license; - - // 3. query /terms endpoint (not ACME standard but implemented by let's enrypt) - $license=$this->getAgreementURLFromTermsUrl(); - if (!empty($license)) return $license; - - // Fallback: use latest known license. This is only valid for let's encrypt and should be removed as soon as there is an official - // ACME-endpoint to get the current ToS - return "https://letsencrypt.org/documents/LE-SA-v1.1.1-August-1-2016.pdf"; - // return ""; - } - } class Base64UrlSafeEncoder diff --git a/lib/classes/ssl/class.lescript_v2.php b/lib/classes/ssl/class.lescript_v2.php new file mode 100644 index 00000000..074def62 --- /dev/null +++ b/lib/classes/ssl/class.lescript_v2.php @@ -0,0 +1,599 @@ + +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// This file is copied from https://github.com/analogic/lescript +// and modified to work without files and integrate in Froxlor +class lescript_v2 +{ + + // https://letsencrypt.org/repository/ + private $logger; + + private $client; + + private $accountKey; + + private $customerid; + + private $isFroxlorVhost; + + private $isLeProduction; + + private $version; + + private $_req_uris = array(); + + private $_acc_location = null; + + public function __construct($logger, $version = '2') + { + $this->logger = $logger; + $this->version = $version; + if (Settings::Get('system.letsencryptca') == 'production') { + $ca = 'https://acme-v02.api.letsencrypt.org'; + } else { + $ca = 'https://acme-staging-v02.api.letsencrypt.org'; + } + $this->client = new Client($ca); + $this->log("Using '$ca' to generate certificate"); + + // get request-uris from /directory + $response = $this->client->get('/directory'); + $this->_req_uris['newAccount'] = $response['newAccount']; + $this->_req_uris['newOrder'] = $response['newOrder']; + $this->_req_uris['newNonce'] = $response['newNonce']; + $this->_req_uris['revokeCert'] = $response['revokeCert']; + } + + public function initAccount($certrow, $isFroxlorVhost = false) + { + // Let's see if we have the private accountkey + $this->accountKey = $certrow['leprivatekey']; + $this->customerId = (! $isFroxlorVhost ? $certrow['customerid'] : null); + $this->isFroxlorVhost = $isFroxlorVhost; + $this->isLeProduction = (Settings::Get('system.letsencryptca') == 'production'); + + $leregistered = $certrow['leregistered']; + + if (! $this->accountKey || $this->accountKey == 'unset' || ! $this->isLeProduction) { + + // generate and save new private key for account + // --------------------------------------------- + + $this->log('Creating new account key'); + $keys = $this->generateKey(); + // Only store the accountkey in production, in staging always generate a new key + if ($this->isLeProduction) { + if ($isFroxlorVhost) { + Settings::Set('system.lepublickey', $keys['public']); + Settings::Set('system.leprivatekey', $keys['private']); + Settings::Set('system.leregistered', 0); // key is not registered + } else { + $upd_stmt = Database::prepare("UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET `lepublickey` = :public, `leprivatekey` = :private, `leregistered` = :registered " . "WHERE `customerid` = :customerid;"); + Database::pexecute($upd_stmt, array( + 'public' => $keys['public'], + 'private' => $keys['private'], + 'registered' => 0, + 'customerid' => $this->customerId + )); + } + } + $leregistered = 0; + $this->accountKey = $keys['private']; + } else { + $this->log('Using existing account key'); + } + + if ($leregistered == 0) { // Account not registered + + $this->log('Starting new account registration'); + $response = $this->postNewReg(); + if ($this->client->getLastCode() == 409) { + $this->log('The key was already registered. Using existing account.'); + } else if ($this->client->getLastCode() == 201) { + $this->log('New account registered.'); + } else { + throw new \RuntimeException("Account not initialized, probably due to rate limiting. Whole response: " . json_encode($response)); + } + $this->_acc_location = $this->client->getLastLocation(); + + $leregistered = 1; + $this->setLeRegisteredState($leregistered); + } + } + + /** + * + * @param array $domains + * @param string $domainkey + * @param string $csr + * optional, same behavior as $reuseCsr from the original class, but we're passing the content of the csr already + * + * @throws \RuntimeException + * @return string[] + */ + public function signDomains(array $domains, $domainkey = null, $csr = null) + { + if (! $this->accountKey) { + throw new \RuntimeException("Account not initialized"); + } + + $this->log('Starting certificate generation process for domains'); + + $privateAccountKey = openssl_pkey_get_private($this->accountKey); + $accountKeyDetails = openssl_pkey_get_details($privateAccountKey); + + // start domains authentication + // ---------------------------- + + foreach ($domains as $domain) { + + // 1. getting available authentication options + // ------------------------------------------- + + $this->log("Requesting challenge for $domain"); + + $response = $this->signedRequest($this->_req_uris['newOrder'], array( + "identifiers" => array( + array( + "type" => "dns", + "value" => $domain + ) + ) + ), false); + + if ($this->client->getLastCode() == 403) { + $this->log("Got status 403 - setting LE status to unregistered."); + $this->setLeRegisteredState(0); + throw new RuntimeException("Got 'unauthorized' response - we need to re-register at next run. Whole response: " . json_encode($response)); + } + + // if response is not an array but a string, it's most likely a server-error, e.g. + // ErrorAn error occurred while processing your request. + //

Reference #179.d8be1402.1458059103.3613c4db + if (! is_array($response)) { + throw new RuntimeException("Invalid response from LE for domain $domain. Whole response: " . json_encode($response)); + } + + if (! array_key_exists('authorizations', $response)) { + throw new RuntimeException("No authorizations received for $domain. Whole response: " . json_encode($response)); + } + + // get authorization + $auth_response = $this->client->get($response['authorizations'][0]); + + if (! array_key_exists('challenges', $auth_response)) { + throw new RuntimeException("No challenges received for $domain. Whole response: " . json_encode($auth_response)); + } + + // choose http-01 challenge only + $challenge = array_reduce($auth_response['challenges'], function ($v, $w) { + return $v ? $v : ($w['type'] == 'http-01' ? $w : false); + }); + + if (! $challenge) { + throw new RuntimeException("HTTP Challenge for $domain is not available. Whole response: " . json_encode($response)); + } + + $this->log("Got challenge token for $domain"); + $location = $challenge['url']; + $finalizeLink = $response['finalize']; + + // 2. saving authentication token for web verification + // --------------------------------------------------- + + $directory = Settings::Get('system.letsencryptchallengepath') . '/.well-known/acme-challenge'; + $tokenPath = $directory . '/' . $challenge['token']; + + if (! file_exists($directory) && ! @mkdir($directory, 0755, true)) { + throw new \RuntimeException("Couldn't create directory to expose challenge: ${tokenPath}"); + } + + $header = array( + // need to be in precise order! + "e" => Base64UrlSafeEncoder::encode($accountKeyDetails["rsa"]["e"]), + "kty" => "RSA", + "n" => Base64UrlSafeEncoder::encode($accountKeyDetails["rsa"]["n"]) + ); + $payload = $challenge['token'] . '.' . Base64UrlSafeEncoder::encode(hash('sha256', json_encode($header), true)); + + file_put_contents($tokenPath, $payload); + chmod($tokenPath, 0644); + + // 3. verification process itself + // ------------------------------- + + $uri = "http://${domain}/.well-known/acme-challenge/${challenge['token']}"; + + $this->log("Token for $domain saved at $tokenPath and should be available at $uri"); + + // simple self check + if (Settings::Get('system.disable_le_selfcheck') == '0') { + $selfcheckContextOptions = array( + 'http' => array( + 'header' => "User-Agent: Froxlor/" . $this->version + ) + ); + $selfcheckContext = stream_context_create($selfcheckContextOptions); + if ($payload !== trim(@file_get_contents($uri, false, $selfcheckContext))) { + $errmsg = json_encode(error_get_last()); + if ($errmsg != "null") { + $errmsg = "; PHP error: " . $errmsg; + } else { + $errmsg = ""; + } + $this->logger->logAction(CRON_ACTION, LOG_WARNING, "[Lets Encrypt self-check] Please check $uri - token seems to be not available. This is just a simple self-check, it might be wrong but consider using this information when Let's Encrypt fails to issue a certificate" . $errmsg); + } + } + + $this->log("Sending request to challenge"); + + // send request to challenge + $result = $this->signedRequest($challenge['url'], array( + "type" => "http-01", + "keyAuthorization" => $payload, + "token" => $challenge['token'] + ), false); + + // waiting loop + // we wait for a maximum of 30 seconds to avoid endless loops + $count = 0; + do { + if (empty($result['status']) || $result['status'] == "invalid") { + @unlink($tokenPath); + throw new \RuntimeException("Verification ended with error: " . json_encode($result)); + } + $ended = ! ($result['status'] === "pending" || $result['status'] === "processing"); + + if (! $ended) { + $this->log("Verification " . $result['status'] . ", sleeping 1s"); + sleep(1); + $count ++; + } + + $result = $this->client->get($location); + } while (! $ended && $count < 30); + + $this->log("Verification ended with status: ${result['status']}"); + @unlink($tokenPath); + } + + // requesting certificate + // ---------------------- + + // generate private key for domain if not exist + if (empty($domainkey) || Settings::Get('system.letsencryptreuseold') == 0) { + $keys = $this->generateKey(); + $domainkey = $keys['private']; + } + + // load domain key + $privateDomainKey = openssl_pkey_get_private($domainkey); + + if (empty($csr)) { + $csr = $this->generateCSR($privateDomainKey, $domains); + } + + // request certificates creation + $result = $this->signedRequest($finalizeLink, array( + 'csr' => $csr + ), false); + if ($this->client->getLastCode() !== 200) { + throw new \RuntimeException("Invalid response code: " . $this->client->getLastCode() . ", " . json_encode($result)); + } + if (! isset($result['certificate'])) { + throw new \RuntimeException("No certificate URL specified in result"); + } + + $certificates = array(); + $certdata = $this->client->get($result['certificate']); + $this->log("Got certificate! YAY!"); + $certificates[] = $certdata; + foreach ($this->client->getLastLinks() as $link) { + $this->log("Requesting chained cert at $link"); + $result = $this->client->get($link); + $certificates[] = $result; + } + + if (empty($certificates)) + throw new \RuntimeException('No certificates generated'); + + $fullchain = implode("\n", $certificates); + $crt = array_shift($certificates); + $chain = implode("\n", $certificates); + + $this->log("Done, returning new certificates and key"); + return array( + 'fullchain' => $fullchain, + 'crt' => $crt, + 'chain' => $chain, + 'key' => $domainkey, + 'csr' => $csr + ); + } + + private function setLeRegisteredState($state) + { + if ($this->isLeProduction) { + if ($this->isFroxlorVhost) { + Settings::Set('system.leregistered', $state); + } else { + $upd_stmt = Database::prepare("UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET `leregistered` = :registered " . "WHERE `customerid` = :customerid;"); + Database::pexecute($upd_stmt, array( + 'registered' => $state, + 'customerid' => $this->customerId + )); + } + } + } + + private function parsePemFromBody($body) + { + $pem = chunk_split(base64_encode($body), 64, "\n"); + return "-----BEGIN CERTIFICATE-----\n" . $pem . "-----END CERTIFICATE-----\n"; + } + + private function postNewReg() + { + $this->log('Getting last terms of service URL'); + $directory = $this->client->get('/directory'); + if (! isset($directory['meta']) || ! isset($directory['meta']['termsOfService'])) { + throw new \RuntimeException("No terms of service link available!"); + } + $this->log('Sending registration to letsencrypt server'); + + return $this->signedRequest($this->_req_uris['newAccount'], array( + 'termsOfServiceAgreed' => true + )); + } + + private function generateCSR($privateKey, array $domains) + { + $domain = reset($domains); + $san = implode(",", array_map(function ($dns) { + return "DNS:" . $dns; + }, $domains)); + $tmpConf = tmpfile(); + $tmpConfMeta = stream_get_meta_data($tmpConf); + $tmpConfPath = $tmpConfMeta["uri"]; + + // workaround to get SAN working + fwrite($tmpConf, 'HOME = . +RANDFILE = $ENV::HOME/.rnd +[ req ] +default_bits = ' . Settings::Get('system.letsencryptkeysize') . ' +default_keyfile = privkey.pem +distinguished_name = req_distinguished_name +req_extensions = v3_req +[ req_distinguished_name ] +countryName = Country Name (2 letter code) +[ v3_req ] +basicConstraints = CA:FALSE +subjectAltName = ' . $san . ' +keyUsage = nonRepudiation, digitalSignature, keyEncipherment'); + + $csr = openssl_csr_new(array( + "CN" => $domain, + "ST" => Settings::Get('system.letsencryptstate'), + "C" => Settings::Get('system.letsencryptcountrycode'), + "O" => "Unknown" + ), $privateKey, array( + "config" => $tmpConfPath, + "digest_alg" => "sha256" + )); + + if (! $csr) + throw new \RuntimeException("CSR couldn't be generated! " . openssl_error_string()); + + openssl_csr_export($csr, $csr); + fclose($tmpConf); + + preg_match('~REQUEST-----(.*)-----END~s', $csr, $matches); + + return trim(Base64UrlSafeEncoder::encode(base64_decode($matches[1]))); + } + + private function generateKey() + { + $res = openssl_pkey_new(array( + "private_key_type" => OPENSSL_KEYTYPE_RSA, + "private_key_bits" => (int) Settings::Get('system.letsencryptkeysize') + )); + + if (! openssl_pkey_export($res, $privateKey)) { + throw new \RuntimeException("Key export failed!"); + } + + $details = openssl_pkey_get_details($res); + + return array( + 'private' => $privateKey, + 'public' => $details['key'] + ); + } + + private function signedRequest($uri, array $payload, $needs_jwk = true) + { + $privateKey = openssl_pkey_get_private($this->accountKey); + $details = openssl_pkey_get_details($privateKey); + + $header = array( + "alg" => "RS256" + ); + + if ($needs_jwk) { + $header["jwk"] = array( + "kty" => "RSA", + "n" => Base64UrlSafeEncoder::encode($details["rsa"]["n"]), + "e" => Base64UrlSafeEncoder::encode($details["rsa"]["e"]) + ); + } else { + // need account-url + $header["kid"] = $this->_acc_location; + } + + $protected = $header; + $protected["nonce"] = $this->client->getLastNonce(); + $protected["url"] = $uri; + + $payload64 = Base64UrlSafeEncoder::encode(json_encode($payload, JSON_UNESCAPED_SLASHES)); + $protected64 = Base64UrlSafeEncoder::encode(json_encode($protected)); + + openssl_sign($protected64 . '.' . $payload64, $signed, $privateKey, "SHA256"); + + $signed64 = Base64UrlSafeEncoder::encode($signed); + + $data = array( + 'protected' => $protected64, + 'payload' => $payload64, + 'signature' => $signed64 + ); + + $this->log("Sending signed request to $uri"); + return $this->client->post($uri, json_encode($data)); + } + + protected function log($message) + { + $this->logger->logAction(CRON_ACTION, LOG_INFO, "letsencrypt-v2 " . $message); + } +} + +class Client +{ + + private $lastCode; + + public $lastHeader; + + private $base; + + public function __construct($base) + { + $this->base = $base; + } + + private function curl($method, $url, $data = null) + { + $headers = array( + 'Accept: application/json', + 'Content-Type: application/json' + ); + $handle = curl_init(); + curl_setopt($handle, CURLOPT_URL, preg_match('~^http~', $url) ? $url : $this->base . $url); + curl_setopt($handle, CURLOPT_HTTPHEADER, $headers); + curl_setopt($handle, CURLOPT_RETURNTRANSFER, true); + curl_setopt($handle, CURLOPT_HEADER, true); + + // DO NOT DO THAT! + // curl_setopt($handle, CURLOPT_SSL_VERIFYHOST, false); + // curl_setopt($handle, CURLOPT_SSL_VERIFYPEER, false); + + switch ($method) { + case 'GET': + break; + case 'POST': + curl_setopt($handle, CURLOPT_POST, true); + curl_setopt($handle, CURLOPT_POSTFIELDS, $data); + break; + } + $response = curl_exec($handle); + + if (curl_errno($handle)) { + throw new \RuntimeException('Curl: ' . curl_error($handle)); + } + + $header_size = curl_getinfo($handle, CURLINFO_HEADER_SIZE); + + $header = substr($response, 0, $header_size); + $body = substr($response, $header_size); + + $this->lastHeader = $header; + $this->lastCode = curl_getinfo($handle, CURLINFO_HTTP_CODE); + + $data = json_decode($body, true); + return $data === null ? $body : $data; + } + + public function post($url, $data) + { + return $this->curl('POST', $url, $data); + } + + public function get($url) + { + return $this->curl('GET', $url); + } + + public function getLastNonce() + { + if (preg_match('~Replay\-Nonce: (.+)~i', $this->lastHeader, $matches)) { + return trim($matches[1]); + } + + $this->curl('GET', '/directory'); + return $this->getLastNonce(); + } + + public function getLastLocation() + { + if (preg_match('~Location: (.+)~i', $this->lastHeader, $matches)) { + return trim($matches[1]); + } + return null; + } + + public function getLastCode() + { + return $this->lastCode; + } + + public function getLastLinks() + { + preg_match_all('~Link: <(.+)>;rel="up"~', $this->lastHeader, $matches); + return $matches[1]; + } +} + +class Base64UrlSafeEncoder +{ + + public static function encode($input) + { + return str_replace('=', '', strtr(base64_encode($input), '+/', '-_')); + } + + public static function decode($input) + { + $remainder = strlen($input) % 4; + if ($remainder) { + $padlen = 4 - $remainder; + $input .= str_repeat('=', $padlen); + } + return base64_decode(strtr($input, '-_', '+/')); + } +} diff --git a/scripts/jobs/cron_letsencrypt_v2.php b/scripts/jobs/cron_letsencrypt_v2.php new file mode 100644 index 00000000..b08d3679 --- /dev/null +++ b/scripts/jobs/cron_letsencrypt_v2.php @@ -0,0 +1,292 @@ + + * @author Froxlor team (2016-) + * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt + * @package Cron + * + * @since 0.9.35 + * + */ + +$cronlog->logAction(CRON_ACTION, LOG_INFO, "Updating Let's Encrypt certificates"); + +if (! extension_loaded('curl')) { + $cronlog->logAction(CRON_ACTION, LOG_ERR, "Let's Encrypt requires the php cURL extension to be installed."); + exit(); +} + +$certificates_stmt = Database::query(" + SELECT + domssl.`id`, + domssl.`domainid`, + domssl.expirationdate, + domssl.`ssl_cert_file`, + domssl.`ssl_key_file`, + domssl.`ssl_ca_file`, + domssl.`ssl_csr_file`, + dom.`domain`, + dom.`wwwserveralias`, + dom.`iswildcarddomain`, + dom.`documentroot`, + dom.`id` AS 'domainid', + dom.`ssl_redirect`, + cust.`leprivatekey`, + cust.`lepublickey`, + cust.`leregistered`, + cust.`customerid`, + 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 dom.`letsencrypt` = 1 + AND dom.`aliasdomain` IS NULL + AND ( + domssl.`expirationdate` < DATE_ADD(NOW(), INTERVAL 30 DAY) + OR domssl.`expirationdate` IS NULL + ) + "); + +$aliasdomains_stmt = Database::prepare(" + SELECT + dom.`id` as domainid, + dom.`domain`, + dom.`wwwserveralias`, + dom.`iswildcarddomain` + FROM `" . TABLE_PANEL_DOMAINS . "` AS dom + WHERE + dom.`aliasdomain` = :id + AND dom.`letsencrypt` = 1 + "); + +$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, + `expirationdate` = :expirationdate + "); + +$upddom_stmt = Database::prepare("UPDATE `" . TABLE_PANEL_DOMAINS . "` SET `ssl_redirect` = '1' WHERE `id` = :domainid"); + +// flag for re-generation of vhost files +$changedetected = 0; + +// 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_INSTALL_DIR, + '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_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); + + $insert_or_update_required = true; + if ($froxlor_ssl) { + $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'])) { + $insert_or_update_required = false; + } + } + + if ($insert_or_update_required) { + $domains = array( + $certrow['domain'] + ); + + // 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 + $cronlog->logAction(CRON_ACTION, LOG_INFO, "Updating " . $certrow['domain']); + + $cronlog = FroxlorLogger::getInstanceOf(array( + 'loginname' => $certrow['loginname'] + )); + + try { + // Initialize Lescript with documentroot + $le = new lescript_v2($cronlog, $version); + + // Initialize Lescript + $le->initAccount($certrow, true); + + // Request the new certificate (old key may be used) + $return = $le->signDomains($domains, $certrow['ssl_key_file']); + + // We are interessted in the expirationdate + $newcert = openssl_x509_parse($return['crt']); + + // Store the new data + Database::pexecute($updcert_stmt, array( + 'id' => $certrow['id'], + 'domainid' => $certrow['domainid'], + 'crt' => $return['crt'], + 'key' => $return['key'], + 'ca' => $return['chain'], + 'chain' => $return['chain'], + 'csr' => $return['csr'], + 'expirationdate' => date('Y-m-d H:i:s', $newcert['validTo_time_t']) + )); + + if ($certrow['ssl_redirect'] == 3) { + Settings::Set('system.le_froxlor_redirect', '1'); + } + + $cronlog->logAction(CRON_ACTION, LOG_INFO, "Updated Let's Encrypt certificate for " . $certrow['domain']); + + $changedetected = 1; + } catch (Exception $e) { + $cronlog->logAction(CRON_ACTION, LOG_ERR, "Could not get Let's Encrypt certificate for " . $certrow['domain'] . ": " . $e->getMessage()); + } + } +} + +// customer domains +$certrows = $certificates_stmt->fetchAll(PDO::FETCH_ASSOC); +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'] + )); + + // Only renew let's encrypt certificate if no broken ssl_redirect is enabled + if ($certrow['ssl_redirect'] != 2) { + $cronlog->logAction(CRON_ACTION, LOG_INFO, "Updating " . $certrow['domain']); + + $cronlog->logAction(CRON_ACTION, LOG_INFO, "Adding SAN entry: " . $certrow['domain']); + $domains = array( + $certrow['domain'] + ); + if ($certrow['iswildcarddomain'] == 1) { + $cronlog->logAction(CRON_ACTION, LOG_INFO, "Adding SAN entry: *." . $certrow['domain']); + $domains[] = '*.' . $certrow['domain']; + } + elseif ($certrow['wwwserveralias'] == 1) { + // add www. to SAN list + $cronlog->logAction(CRON_ACTION, LOG_INFO, "Adding SAN entry: www." . $certrow['domain']); + $domains[] = 'www.' . $certrow['domain']; + } + + // add alias domains (and possibly www.) to SAN list + Database::pexecute($aliasdomains_stmt, array( + 'id' => $certrow['domainid'] + )); + $aliasdomains = $aliasdomains_stmt->fetchAll(PDO::FETCH_ASSOC); + foreach ($aliasdomains as $aliasdomain) { + $cronlog->logAction(CRON_ACTION, LOG_INFO, "Adding SAN entry: " . $aliasdomain['domain']); + $domains[] = $aliasdomain['domain']; + if ($aliasdomain['iswildcarddomain'] == 1) { + $cronlog->logAction(CRON_ACTION, LOG_INFO, "Adding SAN entry: *." . $aliasdomain['domain']); + $domains[] = '*.' . $aliasdomain['domain']; + } + elseif ($aliasdomain['wwwserveralias'] == 1) { + $cronlog->logAction(CRON_ACTION, LOG_INFO, "Adding SAN entry: www." . $aliasdomain['domain']); + $domains[] = 'www.' . $aliasdomain['domain']; + } + } + + try { + // Initialize Lescript with documentroot + $le = new lescript_v2($cronlog, $version); + + // Initialize Lescript + $le->initAccount($certrow); + + // Request the new certificate (old key may be used) + $return = $le->signDomains($domains, $certrow['ssl_key_file']); + + // We are interessted in the expirationdate + $newcert = openssl_x509_parse($return['crt']); + + // Store the new data + Database::pexecute($updcert_stmt, array( + 'id' => $certrow['id'], + 'domainid' => $certrow['domainid'], + 'crt' => $return['crt'], + 'key' => $return['key'], + 'ca' => $return['chain'], + 'chain' => $return['chain'], + 'csr' => $return['csr'], + 'expirationdate' => date('Y-m-d H:i:s', $newcert['validTo_time_t']) + )); + + if ($certrow['ssl_redirect'] == 3) { + Database::pexecute($upddom_stmt, array( + 'domainid' => $certrow['domainid'] + )); + } + + $cronlog->logAction(CRON_ACTION, LOG_INFO, "Updated Let's Encrypt certificate for " . $certrow['domain']); + + $changedetected = 1; + } catch (Exception $e) { + $cronlog->logAction(CRON_ACTION, LOG_ERR, "Could not get Let's Encrypt certificate for " . $certrow['domain'] . ": " . $e->getMessage()); + } + } else { + $cronlog->logAction(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) { + inserttask(1); +} + +// reset logger +$cronlog = FroxlorLogger::getInstanceOf(array( + 'loginname' => 'cronjob' +)); +$cronlog->logAction(CRON_ACTION, LOG_INFO, "Let's Encrypt certificates have been updated"); From d40d1f30b63fa21d73d6be2239d6235885d0e725 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 9 Jan 2018 14:50:52 +0100 Subject: [PATCH 0359/1335] make it a setting to switch between ACME v1 and v2 Signed-off-by: Michael Kaufmann (d00p) --- actions/admin/settings/131.ssl.php | 17 +++++++++++++++-- install/froxlor.sql | 3 ++- install/updates/froxlor/0.9/update_0.9.inc.php | 9 +++++++++ lib/version.inc.php | 2 +- scripts/jobs/cron_letsencrypt.php | 6 ++++++ 5 files changed, 33 insertions(+), 4 deletions(-) diff --git a/actions/admin/settings/131.ssl.php b/actions/admin/settings/131.ssl.php index 68f2a9e8..b9b2d459 100644 --- a/actions/admin/settings/131.ssl.php +++ b/actions/admin/settings/131.ssl.php @@ -100,6 +100,19 @@ return array( 'cronmodule' => 'froxlor/letsencrypt', 'save_method' => 'storeSettingField' ), + 'system_leapiversion' => array( + 'label' => $lng['serversettings']['leapiversion'], + 'settinggroup' => 'system', + 'varname' => 'leapiversion', + 'type' => 'option', + 'default' => '1', + 'option_mode' => 'one', + 'option_options' => array( + '1' => 'ACME v1', + '2' => 'ACME v2' + ), + 'save_method' => 'storeSettingField' + ), 'system_letsencryptacmeconf' => array( 'label' => $lng['serversettings']['letsencryptacmeconf'], 'settinggroup' => 'system', @@ -117,8 +130,8 @@ return array( 'default' => 'testing', 'option_mode' => 'one', 'option_options' => array( - 'testing' => 'https://acme-staging.api.letsencrypt.org (Test)', - 'production' => 'https://acme-v01.api.letsencrypt.org (Live)' + 'testing' => 'https://acme-staging'.(Settings::Get('system.leapiversion') == '2' ? '-v02' : '').'.api.letsencrypt.org (Test)', + 'production' => 'https://acme-v0'.Settings::Get('system.leapiversion').'.api.letsencrypt.org (Live)' ), 'save_method' => 'storeSettingField' ), diff --git a/install/froxlor.sql b/install/froxlor.sql index 3fdc57dd..e96c62b6 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -534,6 +534,7 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES ('system', 'letsencryptkeysize', '4096'), ('system', 'letsencryptreuseold', 0), ('system', 'leenabled', '0'), + ('system', 'leapiversion', '1'), ('system', 'backupenabled', '0'), ('system', 'dnsenabled', '0'), ('system', 'dns_server', 'bind'), @@ -588,7 +589,7 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES ('panel', 'password_special_char', '!?<>§$%+#=@'), ('panel', 'customer_hide_options', ''), ('panel', 'version', '0.9.38.8'), - ('panel', 'db_version', '201801080'); + ('panel', 'db_version', '201801090'); DROP TABLE IF EXISTS `panel_tasks`; diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index 0483ba44..de39f9e3 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -3725,3 +3725,12 @@ if (isDatabaseVersion('201801070')) { updateToDbVersion('201801080'); } + +if (isDatabaseVersion('201801080')) { + + showUpdateStep("Adding new setting for Let's Encrypt ACME version"); + Settings::AddNew('system.leapiversion', '1'); + lastStepStatus(0); + + updateToDbVersion('201801090'); +} diff --git a/lib/version.inc.php b/lib/version.inc.php index 5f433e8b..2632a02f 100644 --- a/lib/version.inc.php +++ b/lib/version.inc.php @@ -19,7 +19,7 @@ $version = '0.9.38.8'; // Database version (YYYYMMDDC where C is a daily counter) -$dbversion = '201801080'; +$dbversion = '201801090'; // Distribution branding-tag (used for Debian etc.) $branding = ''; diff --git a/scripts/jobs/cron_letsencrypt.php b/scripts/jobs/cron_letsencrypt.php index 126d7dcd..b25b9a8d 100644 --- a/scripts/jobs/cron_letsencrypt.php +++ b/scripts/jobs/cron_letsencrypt.php @@ -20,6 +20,12 @@ if (! defined('MASTER_CRONJOB')) * */ +if (Settings::Get('system.leapiversion') == '2') { + // use ACME v2 is specified + require_once __DIR__ . '/cron_letsencrypt_v2.php'; + exit; +} + $cronlog->logAction(CRON_ACTION, LOG_INFO, "Updating Let's Encrypt certificates"); if (! extension_loaded('curl')) { From b2b9d4e31a5128233cc9a82d4c20d6fe41f71c34 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 9 Jan 2018 14:56:41 +0100 Subject: [PATCH 0360/1335] add missing setting-strings for new acme-version setting; allow using let's encrypt with wildcard-alias when using ACME-v2 Signed-off-by: Michael Kaufmann (d00p) --- admin_domains.php | 8 ++++---- customer_domains.php | 4 ++-- lng/english.lng.php | 2 ++ lng/german.lng.php | 2 ++ 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/admin_domains.php b/admin_domains.php index 0faa8854..7812ad2d 100644 --- a/admin_domains.php +++ b/admin_domains.php @@ -633,8 +633,8 @@ if ($page == 'domains' || $page == 'overview') { $ocsp_stapling = 0; } - // We can't enable let's encrypt for wildcard - domains - if ($serveraliasoption == '0' && $letsencrypt == '1') { + // We can't enable let's encrypt for wildcard - domains if using acme-v1 + if ($serveraliasoption == '0' && $letsencrypt == '1' && Settings::Get('system.leapiversion') == '1') { standard_error('nowildcardwithletsencrypt'); } @@ -1511,8 +1511,8 @@ if ($page == 'domains' || $page == 'overview') { $ocsp_stapling = 0; } - // We can't enable let's encrypt for wildcard domains - if ($serveraliasoption == '0' && $letsencrypt == '1') { + // We can't enable let's encrypt for wildcard domains when using acme-v1 + if ($serveraliasoption == '0' && $letsencrypt == '1' && Settings::Get('system.leapiversion') == '1') { standard_error('nowildcardwithletsencrypt'); } diff --git a/customer_domains.php b/customer_domains.php index f14b69a7..350c715f 100644 --- a/customer_domains.php +++ b/customer_domains.php @@ -681,8 +681,8 @@ if ($page == 'overview') { $letsencrypt = '0'; } - // We can't enable let's encrypt for wildcard - domains - if ($iswildcarddomain == '1' && $letsencrypt == '1') { + // We can't enable let's encrypt for wildcard - domains when using acme-v1 + if ($iswildcarddomain == '1' && $letsencrypt == '1' && Settings::Get('system.leapiversion') == '1') { standard_error('nowildcardwithletsencrypt'); } diff --git a/lng/english.lng.php b/lng/english.lng.php index 039073ff..bb49e106 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -2088,3 +2088,5 @@ $lng['menue']['phpsettings']['fpmdaemons'] = 'PHP-FPM versions'; $lng['admin']['phpsettings']['activephpconfigs'] = 'In use for php-config(s)'; $lng['admin']['phpsettingsforsubdomains'] = 'Apply php-config to all subdomains:'; $lng['serversettings']['phpsettingsforsubdomains']['description'] = 'If yes the chosen php-config will be updated to all subdomains'; +$lng['serversettings']['leapiversion']['title'] = "Chose Let's Encrypt ACME implementation"; +$lng['serversettings']['leapiversion']['description'] = "Chose between ACME v1 and ACME v2 implementation for Let's Encrypt. ACME v2 let's you use wildcard-certificates."; diff --git a/lng/german.lng.php b/lng/german.lng.php index 941ebf82..271687db 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -1739,3 +1739,5 @@ $lng['menue']['phpsettings']['fpmdaemons'] = 'PHP-FPM Versionen'; $lng['admin']['phpsettings']['activephpconfigs'] = 'In Verwendung für PHP-Konfiguration(en)'; $lng['admin']['phpsettingsforsubdomains'] = 'PHP-Config für alle Subdomains übernehmen:'; $lng['serversettings']['phpsettingsforsubdomains']['description'] = 'Wenn ja, wird die gewählte PHP-Config für alle Subdomains übernommen'; +$lng['serversettings']['leapiversion']['title'] = "Wähle Let's Encrypt ACME Implementierung"; +$lng['serversettings']['leapiversion']['description'] = "Wähle zwischen ACME v1 und ACME v2 Implementierung von Let's Encrypt. ACME v2 erlaubt Wildcard-Zertifikate."; From f49cb81e4923e8668575a983a65245de8faa2599 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 9 Jan 2018 15:33:23 +0100 Subject: [PATCH 0361/1335] disable wildcard-usage with Let's Encrypt as ACME-v2 only supports the dns-01 challenge for now (pity) Signed-off-by: Michael Kaufmann (d00p) --- admin_domains.php | 10 ++++++++++ customer_domains.php | 5 +++++ lng/english.lng.php | 3 ++- lng/german.lng.php | 3 ++- 4 files changed, 19 insertions(+), 2 deletions(-) diff --git a/admin_domains.php b/admin_domains.php index 7812ad2d..6ce493ac 100644 --- a/admin_domains.php +++ b/admin_domains.php @@ -637,6 +637,11 @@ if ($page == 'domains' || $page == 'overview') { if ($serveraliasoption == '0' && $letsencrypt == '1' && Settings::Get('system.leapiversion') == '1') { standard_error('nowildcardwithletsencrypt'); } + // if using acme-v2 we cannot issue wildcard-certificates + // because they currently only support the dns-01 challenge + if ($serveraliasoption == '0' && $letsencrypt == '1' && Settings::Get('system.leapiversion') == '2') { + standard_error('nowildcardwithletsencryptv2'); + } // Temporarily deactivate ssl_redirect until Let's Encrypt certificate was generated if ($ssl_redirect > 0 && $letsencrypt == 1) { @@ -1515,6 +1520,11 @@ if ($page == 'domains' || $page == 'overview') { if ($serveraliasoption == '0' && $letsencrypt == '1' && Settings::Get('system.leapiversion') == '1') { standard_error('nowildcardwithletsencrypt'); } + // if using acme-v2 we cannot issue wildcard-certificates + // because they currently only support the dns-01 challenge + if ($serveraliasoption == '0' && $letsencrypt == '1' && Settings::Get('system.leapiversion') == '2') { + standard_error('nowildcardwithletsencryptv2'); + } // Temporarily deactivate ssl_redirect until Let's Encrypt certificate was generated if ($ssl_redirect > 0 && $letsencrypt == 1 && $result['letsencrypt'] != $letsencrypt) { diff --git a/customer_domains.php b/customer_domains.php index 350c715f..23bbb07f 100644 --- a/customer_domains.php +++ b/customer_domains.php @@ -685,6 +685,11 @@ if ($page == 'overview') { if ($iswildcarddomain == '1' && $letsencrypt == '1' && Settings::Get('system.leapiversion') == '1') { standard_error('nowildcardwithletsencrypt'); } + // if using acme-v2 we cannot issue wildcard-certificates + // because they currently only support the dns-01 challenge + if ($iswildcarddomain == '0' && $letsencrypt == '1' && Settings::Get('system.leapiversion') == '2') { + standard_error('nowildcardwithletsencryptv2'); + } // Temporarily deactivate ssl_redirect until Let's Encrypt certificate was generated if ($ssl_redirect > 0 && $letsencrypt == 1 && $result['letsencrypt'] != $letsencrypt) { diff --git a/lng/english.lng.php b/lng/english.lng.php index bb49e106..45190d40 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -1940,7 +1940,7 @@ $lng['admin']['letsencrypt']['description'] = 'Get a free certificate from . The certificate will be created and renewed automatically.
ATTENTION: This feature is still in beta.'; $lng['error']['sslredirectonlypossiblewithsslipport'] = 'Using Let\'s Encrypt is only possible when the domain has at least one ssl-enabled IP/port combination assigned.'; -$lng['error']['nowildcardwithletsencrypt'] = 'Let\'s Encrypt cannot (yet) handle wildcard-domains. Please set the ServerAlias to WWW or disable it completely'; +$lng['error']['nowildcardwithletsencrypt'] = 'Let\'s Encrypt cannot handle wildcard-domains using ACME v1. Please set the ServerAlias to WWW or disable it completely'; $lng['panel']['letsencrypt'] = 'Using Let\'s encrypt'; $lng['crondesc']['cron_letsencrypt'] = 'updating Let\'s Encrypt certificates'; $lng['serversettings']['letsencryptca']['title'] = "Let's Encrypt environment"; @@ -2090,3 +2090,4 @@ $lng['admin']['phpsettingsforsubdomains'] = 'Apply php-config to all subdomains: $lng['serversettings']['phpsettingsforsubdomains']['description'] = 'If yes the chosen php-config will be updated to all subdomains'; $lng['serversettings']['leapiversion']['title'] = "Chose Let's Encrypt ACME implementation"; $lng['serversettings']['leapiversion']['description'] = "Chose between ACME v1 and ACME v2 implementation for Let's Encrypt. ACME v2 let's you use wildcard-certificates."; +$lng['error']['nowildcardwithletsencryptv2'] = 'Let\'s Encrypt can only validate wildcard-domains by DNS with ACME v2, sorry. Please set the ServerAlias to WWW or disable it completely'; diff --git a/lng/german.lng.php b/lng/german.lng.php index 271687db..7ae3b8d4 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -1593,7 +1593,7 @@ $lng['admin']['letsencrypt']['description'] = 'Holt ein kostenloses Zertifikat v $lng['customer']['letsencrypt']['title'] = 'Benutze Let\'s Encrypt'; $lng['customer']['letsencrypt']['description'] = 'Holt ein kostenloses Zertifikat von
Let\'s Encrypt. Das Zertifikat wird automatisch erstellt und verlängert.
ACHTUNG: Dieses Feature befindet sich noch im Test.'; $lng['error']['sslredirectonlypossiblewithsslipport'] = 'Die Nutzung von Let\'s Encrypt ist nur möglich, wenn die Domain mindestens eine IP/Port - Kombination mit aktiviertem SSL zugewiesen hat.'; -$lng['error']['nowildcardwithletsencrypt'] = 'Let\'s Encrypt kann (noch) nicht mit Wildcard-Domains umgehen. Bitte den ServerAlias auf WWW setzen oder deaktivieren'; +$lng['error']['nowildcardwithletsencrypt'] = 'Let\'s Encrypt kann in ACME v1 nicht mit Wildcard-Domains umgehen. Bitte den ServerAlias auf WWW setzen oder deaktivieren'; $lng['panel']['letsencrypt'] = 'Benutzt Let\'s encrypt'; $lng['crondesc']['cron_letsencrypt'] = 'aktualisiert Let\'s Encrypt Zertifikate'; $lng['serversettings']['letsencryptca']['title'] = "Let's Encrypt Umgebung"; @@ -1741,3 +1741,4 @@ $lng['admin']['phpsettingsforsubdomains'] = 'PHP-Config für alle Subdomains üb $lng['serversettings']['phpsettingsforsubdomains']['description'] = 'Wenn ja, wird die gewählte PHP-Config für alle Subdomains übernommen'; $lng['serversettings']['leapiversion']['title'] = "Wähle Let's Encrypt ACME Implementierung"; $lng['serversettings']['leapiversion']['description'] = "Wähle zwischen ACME v1 und ACME v2 Implementierung von Let's Encrypt. ACME v2 erlaubt Wildcard-Zertifikate."; +$lng['error']['nowildcardwithletsencryptv2'] = 'Let\'s Encrypt kann in ACME v2 Wildcard-Domains nur via DNS validieren, sorry. Bitte den ServerAlias auf WWW setzen oder deaktivieren'; From 07a4f045f182c68a49d76f25400ff69d4ac12342 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 9 Jan 2018 16:08:13 +0100 Subject: [PATCH 0362/1335] do not advertise wildcard-certificates as it might never be possible in froxlor (no http-01 challenge) Signed-off-by: Michael Kaufmann (d00p) --- lng/english.lng.php | 2 +- lng/german.lng.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lng/english.lng.php b/lng/english.lng.php index 45190d40..c864e66d 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -2089,5 +2089,5 @@ $lng['admin']['phpsettings']['activephpconfigs'] = 'In use for php-config(s)'; $lng['admin']['phpsettingsforsubdomains'] = 'Apply php-config to all subdomains:'; $lng['serversettings']['phpsettingsforsubdomains']['description'] = 'If yes the chosen php-config will be updated to all subdomains'; $lng['serversettings']['leapiversion']['title'] = "Chose Let's Encrypt ACME implementation"; -$lng['serversettings']['leapiversion']['description'] = "Chose between ACME v1 and ACME v2 implementation for Let's Encrypt. ACME v2 let's you use wildcard-certificates."; +$lng['serversettings']['leapiversion']['description'] = "Chose between ACME v1 and ACME v2 implementation for Let's Encrypt."; $lng['error']['nowildcardwithletsencryptv2'] = 'Let\'s Encrypt can only validate wildcard-domains by DNS with ACME v2, sorry. Please set the ServerAlias to WWW or disable it completely'; diff --git a/lng/german.lng.php b/lng/german.lng.php index 7ae3b8d4..7943e0d4 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -1740,5 +1740,5 @@ $lng['admin']['phpsettings']['activephpconfigs'] = 'In Verwendung für PHP-Konfi $lng['admin']['phpsettingsforsubdomains'] = 'PHP-Config für alle Subdomains übernehmen:'; $lng['serversettings']['phpsettingsforsubdomains']['description'] = 'Wenn ja, wird die gewählte PHP-Config für alle Subdomains übernommen'; $lng['serversettings']['leapiversion']['title'] = "Wähle Let's Encrypt ACME Implementierung"; -$lng['serversettings']['leapiversion']['description'] = "Wähle zwischen ACME v1 und ACME v2 Implementierung von Let's Encrypt. ACME v2 erlaubt Wildcard-Zertifikate."; +$lng['serversettings']['leapiversion']['description'] = "Wähle zwischen ACME v1 und ACME v2 Implementierung von Let's Encrypt."; $lng['error']['nowildcardwithletsencryptv2'] = 'Let\'s Encrypt kann in ACME v2 Wildcard-Domains nur via DNS validieren, sorry. Bitte den ServerAlias auf WWW setzen oder deaktivieren'; From 84abb33e5490bb8ae860c97cd9ece66b28d0a4f4 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 9 Jan 2018 17:12:55 +0100 Subject: [PATCH 0363/1335] exclude wildcard-domains agains also vor ACMEv2 of LE2 Signed-off-by: Michael Kaufmann (d00p) --- scripts/jobs/cron_letsencrypt_v2.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/jobs/cron_letsencrypt_v2.php b/scripts/jobs/cron_letsencrypt_v2.php index b08d3679..6b1148d3 100644 --- a/scripts/jobs/cron_letsencrypt_v2.php +++ b/scripts/jobs/cron_letsencrypt_v2.php @@ -57,6 +57,7 @@ $certificates_stmt = Database::query(" dom.`customerid` = cust.`customerid` AND dom.`letsencrypt` = 1 AND dom.`aliasdomain` IS NULL + AND dom.`iswildcarddomain` = 0 AND ( domssl.`expirationdate` < DATE_ADD(NOW(), INTERVAL 30 DAY) OR domssl.`expirationdate` IS NULL @@ -73,6 +74,7 @@ $aliasdomains_stmt = Database::prepare(" WHERE dom.`aliasdomain` = :id AND dom.`letsencrypt` = 1 + AND dom.`iswildcarddomain` = 0 "); $updcert_stmt = Database::prepare(" From ba58991d11f80ac28694f485b695c96d4659a586 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 9 Jan 2018 17:14:57 +0100 Subject: [PATCH 0364/1335] allow per php-config setting of adding '-pass-header Authorization' / 'CGIPassAuth On' to the domains vhosts Signed-off-by: Michael Kaufmann (d00p) --- install/froxlor.sql | 3 ++- install/updates/froxlor/0.9/update_0.9.inc.php | 9 +++++++++ .../admin/phpconfig/formfield.phpconfig_add.php | 9 +++++++++ .../admin/phpconfig/formfield.phpconfig_edit.php | 9 +++++++++ lib/version.inc.php | 2 +- lng/english.lng.php | 1 + lng/german.lng.php | 1 + scripts/jobs/cron_tasks.inc.http.10.apache.php | 11 ++++++++++- .../jobs/cron_tasks.inc.http.15.apache_fcgid.php | 14 +++++++++++++- 9 files changed, 55 insertions(+), 4 deletions(-) diff --git a/install/froxlor.sql b/install/froxlor.sql index e96c62b6..26ec3281 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -589,7 +589,7 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES ('panel', 'password_special_char', '!?<>§$%+#=@'), ('panel', 'customer_hide_options', ''), ('panel', 'version', '0.9.38.8'), - ('panel', 'db_version', '201801090'); + ('panel', 'db_version', '201801091'); DROP TABLE IF EXISTS `panel_tasks`; @@ -795,6 +795,7 @@ CREATE TABLE `panel_phpconfigs` ( `fpm_reqslow` varchar(15) NOT NULL default '5s', `phpsettings` text NOT NULL, `fpmsettingid` int(11) NOT NULL DEFAULT '1', + `pass_authorizationheader` tinyint(1) NOT NULL default '0', PRIMARY KEY (`id`), KEY `fpmsettingid` (`fpmsettingid`) ) ENGINE=MyISAM CHARSET=utf8 COLLATE=utf8_general_ci; diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index de39f9e3..52f26e39 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -3734,3 +3734,12 @@ if (isDatabaseVersion('201801080')) { updateToDbVersion('201801090'); } + +if (isDatabaseVersion('201801090')) { + + showUpdateStep("Adding field pass_authorizationheader for php-configs"); + Database::query("ALTER TABLE `" . TABLE_PANEL_PHPCONFIGS . "` ADD `pass_authorizationheader` tinyint(1) NOT NULL default '0';"); + lastStepStatus(0); + + updateToDbVersion('201801091'); +} diff --git a/lib/formfields/admin/phpconfig/formfield.phpconfig_add.php b/lib/formfields/admin/phpconfig/formfield.phpconfig_add.php index 07889120..6d30adfd 100644 --- a/lib/formfields/admin/phpconfig/formfield.phpconfig_add.php +++ b/lib/formfields/admin/phpconfig/formfield.phpconfig_add.php @@ -90,6 +90,15 @@ return array( 'maxlength' => 10, 'value' => '5s' ), + 'phpfpm_pass_authorizationheader' => array( + 'visible' => (Settings::Get('phpfpm.enabled') == 1 ? true : false), + 'label' => $lng['admin']['phpsettings']['pass_authorizationheader'], + 'type' => 'checkbox', + 'values' => array( + array ('label' => $lng['panel']['yes'], 'value' => '1') + ), + 'value' => array() + ), 'phpsettings' => array( 'style' => 'align-top', 'label' => $lng['admin']['phpsettings']['phpinisettings'], diff --git a/lib/formfields/admin/phpconfig/formfield.phpconfig_edit.php b/lib/formfields/admin/phpconfig/formfield.phpconfig_edit.php index 9ae736ed..e2e8fd11 100644 --- a/lib/formfields/admin/phpconfig/formfield.phpconfig_edit.php +++ b/lib/formfields/admin/phpconfig/formfield.phpconfig_edit.php @@ -93,6 +93,15 @@ return array( 'maxlength' => 10, 'value' => $result['fpm_reqslow'] ), + 'phpfpm_pass_authorizationheader' => array( + 'visible' => (Settings::Get('phpfpm.enabled') == 1 ? true : false), + 'label' => $lng['admin']['phpsettings']['pass_authorizationheader'], + 'type' => 'checkbox', + 'values' => array( + array ('label' => $lng['panel']['yes'], 'value' => '1') + ), + 'value' => array($result['pass_authorizationheader']) + ), 'phpsettings' => array( 'style' => 'align-top', 'label' => $lng['admin']['phpsettings']['phpinisettings'], diff --git a/lib/version.inc.php b/lib/version.inc.php index 2632a02f..40aa7b18 100644 --- a/lib/version.inc.php +++ b/lib/version.inc.php @@ -19,7 +19,7 @@ $version = '0.9.38.8'; // Database version (YYYYMMDDC where C is a daily counter) -$dbversion = '201801090'; +$dbversion = '201801091'; // Distribution branding-tag (used for Debian etc.) $branding = ''; diff --git a/lng/english.lng.php b/lng/english.lng.php index c864e66d..b33762e3 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -2091,3 +2091,4 @@ $lng['serversettings']['phpsettingsforsubdomains']['description'] = 'If yes the $lng['serversettings']['leapiversion']['title'] = "Chose Let's Encrypt ACME implementation"; $lng['serversettings']['leapiversion']['description'] = "Chose between ACME v1 and ACME v2 implementation for Let's Encrypt."; $lng['error']['nowildcardwithletsencryptv2'] = 'Let\'s Encrypt can only validate wildcard-domains by DNS with ACME v2, sorry. Please set the ServerAlias to WWW or disable it completely'; +$lng['admin']['phpsettings']['pass_authorizationheader'] = 'Add "-pass-header Authorization" to vhosts'; diff --git a/lng/german.lng.php b/lng/german.lng.php index 7943e0d4..1302358b 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -1742,3 +1742,4 @@ $lng['serversettings']['phpsettingsforsubdomains']['description'] = 'Wenn ja, wi $lng['serversettings']['leapiversion']['title'] = "Wähle Let's Encrypt ACME Implementierung"; $lng['serversettings']['leapiversion']['description'] = "Wähle zwischen ACME v1 und ACME v2 Implementierung von Let's Encrypt."; $lng['error']['nowildcardwithletsencryptv2'] = 'Let\'s Encrypt kann in ACME v2 Wildcard-Domains nur via DNS validieren, sorry. Bitte den ServerAlias auf WWW setzen oder deaktivieren'; +$lng['admin']['phpsettings']['pass_authorizationheader'] = 'Füge "-pass-header Authorization" in Vhosts ein'; diff --git a/scripts/jobs/cron_tasks.inc.http.10.apache.php b/scripts/jobs/cron_tasks.inc.http.10.apache.php index 5887e9a7..b3147f03 100644 --- a/scripts/jobs/cron_tasks.inc.http.10.apache.php +++ b/scripts/jobs/cron_tasks.inc.http.10.apache.php @@ -330,8 +330,17 @@ class apache extends HttpConfigBase $this->virtualhosts_data[$vhosts_filename] .= ' ' . "\n"; $this->virtualhosts_data[$vhosts_filename] .= ' SetHandler proxy:unix:' . $php->getInterface()->getSocketFile() . '|fcgi://localhost' . "\n"; $this->virtualhosts_data[$vhosts_filename] .= ' ' . "\n"; + if ($phpconfig['pass_authorizationheader'] == '1') { + $this->virtualhosts_data[$vhosts_filename] .= ' ' . "\n"; + $this->virtualhosts_data[$vhosts_filename] .= ' CGIPassAuth On' . "\n"; + $this->virtualhosts_data[$vhosts_filename] .= ' ' . "\n"; + } } else { - $this->virtualhosts_data[$vhosts_filename] .= ' FastCgiExternalServer ' . $php->getInterface()->getAliasConfigDir() . $srvName . ' -socket ' . $php->getInterface()->getSocketFile() . ' -idle-timeout ' . Settings::Get('phpfpm.idle_timeout') . "\n"; + $addheader = ""; + if ($phpconfig['pass_authorizationheader'] == '1') { + $addheader = " -pass-header Authorization"; + } + $this->virtualhosts_data[$vhosts_filename] .= ' FastCgiExternalServer ' . $php->getInterface()->getAliasConfigDir() . $srvName . ' -socket ' . $php->getInterface()->getSocketFile() . ' -idle-timeout ' . Settings::Get('phpfpm.idle_timeout') . $addheader . "\n"; $this->virtualhosts_data[$vhosts_filename] .= ' ' . "\n"; $file_extensions = explode(' ', $phpconfig['file_extensions']); $this->virtualhosts_data[$vhosts_filename] .= ' ' . "\n"; diff --git a/scripts/jobs/cron_tasks.inc.http.15.apache_fcgid.php b/scripts/jobs/cron_tasks.inc.http.15.apache_fcgid.php index ab53539c..b2385abb 100644 --- a/scripts/jobs/cron_tasks.inc.http.15.apache_fcgid.php +++ b/scripts/jobs/cron_tasks.inc.http.15.apache_fcgid.php @@ -54,13 +54,25 @@ class apache_fcgid extends apache // for this path, as this would be the first require and therefore grant all access if ($mypath_dir->isUserProtected() == false) { $php_options_text.= ' ' . "\n"; + if ($phpconfig['pass_authorizationheader'] == '1') { + $php_options_text.= ' CGIPassAuth On' . "\n"; + } $php_options_text.= ' Require all granted' . "\n"; $php_options_text.= ' AllowOverride All' . "\n"; $php_options_text.= ' ' . "\n"; + } elseif ($phpconfig['pass_authorizationheader'] == '1') { + // allow Pass of Authorization header + $php_options_text.= ' ' . "\n"; + $php_options_text.= ' CGIPassAuth On' . "\n"; + $php_options_text.= ' ' . "\n"; } } else { - $php_options_text.= ' FastCgiExternalServer ' . $php->getInterface()->getAliasConfigDir() . $srvName . ' -socket ' . $php->getInterface()->getSocketFile() . ' -idle-timeout ' . Settings::Get('phpfpm.idle_timeout') . "\n"; + $addheader = ""; + if ($phpconfig['pass_authorizationheader'] == '1') { + $addheader = " -pass-header Authorization"; + } + $php_options_text.= ' FastCgiExternalServer ' . $php->getInterface()->getAliasConfigDir() . $srvName . ' -socket ' . $php->getInterface()->getSocketFile() . ' -idle-timeout ' . Settings::Get('phpfpm.idle_timeout') . $addheader . "\n"; $php_options_text.= ' ' . "\n"; $php_options_text.= ' ' . "\n"; $php_options_text.= ' SetHandler php5-fastcgi'. "\n"; From 6b44dfe9b238e52572dc952e778d906a6060675c Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 9 Jan 2018 17:22:20 +0100 Subject: [PATCH 0365/1335] well, we should also save the new settings :) Signed-off-by: Michael Kaufmann (d00p) --- admin_phpsettings.php | 14 +++++++++++--- lng/english.lng.php | 2 +- lng/german.lng.php | 2 +- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/admin_phpsettings.php b/admin_phpsettings.php index 2b2e33b7..0fa1b831 100644 --- a/admin_phpsettings.php +++ b/admin_phpsettings.php @@ -124,11 +124,13 @@ if ($page == 'overview') { $fpm_enableslowlog = 0; $fpm_reqtermtimeout = 0; $fpm_reqslowtimeout = 0; + $fpm_pass_authorizationheader = 0; } elseif (Settings::Get('phpfpm.enabled') == 1) { $fpm_config_id = intval($_POST['fpmconfig']); $fpm_enableslowlog = isset($_POST['phpfpm_enable_slowlog']) ? (int) $_POST['phpfpm_enable_slowlog'] : 0; $fpm_reqtermtimeout = validate($_POST['phpfpm_reqtermtimeout'], 'phpfpm_reqtermtimeout', '/^([0-9]+)(|s|m|h|d)$/'); $fpm_reqslowtimeout = validate($_POST['phpfpm_reqslowtimeout'], 'phpfpm_reqslowtimeout', '/^([0-9]+)(|s|m|h|d)$/'); + $fpm_pass_authorizationheader = isset($_POST['phpfpm_pass_authorizationheader']) ? (int) $_POST['phpfpm_pass_authorizationheader'] : 0; // disable fcgid stuff $binary = '/usr/bin/php-cgi'; $file_extensions = 'php'; @@ -153,7 +155,8 @@ if ($page == 'overview') { `fpm_reqterm` = :fpmreqterm, `fpm_reqslow` = :fpmreqslow, `phpsettings` = :phpsettings, - `fpmsettingid` = :fpmsettingid"); + `fpmsettingid` = :fpmsettingid, + `pass_authorizationheader` = :fpmpassauth"); $ins_data = array( 'desc' => $description, 'binary' => $binary, @@ -165,7 +168,8 @@ if ($page == 'overview') { 'fpmreqterm' => $fpm_reqtermtimeout, 'fpmreqslow' => $fpm_reqslowtimeout, 'phpsettings' => $phpsettings, - 'fpmsettingid' => $fpm_config_id + 'fpmsettingid' => $fpm_config_id, + 'fpmpassauth' => $fpm_pass_authorizationheader ); Database::pexecute($ins_stmt, $ins_data); @@ -283,11 +287,13 @@ if ($page == 'overview') { $fpm_enableslowlog = 0; $fpm_reqtermtimeout = 0; $fpm_reqslowtimeout = 0; + $fpm_pass_authorizationheader = 0; } elseif (Settings::Get('phpfpm.enabled') == 1) { $fpm_config_id = intval($_POST['fpmconfig']); $fpm_enableslowlog = isset($_POST['phpfpm_enable_slowlog']) ? (int) $_POST['phpfpm_enable_slowlog'] : 0; $fpm_reqtermtimeout = validate($_POST['phpfpm_reqtermtimeout'], 'phpfpm_reqtermtimeout', '/^([0-9]+)(|s|m|h|d)$/'); $fpm_reqslowtimeout = validate($_POST['phpfpm_reqslowtimeout'], 'phpfpm_reqslowtimeout', '/^([0-9]+)(|s|m|h|d)$/'); + $fpm_pass_authorizationheader = isset($_POST['phpfpm_pass_authorizationheader']) ? (int) $_POST['phpfpm_pass_authorizationheader'] : 0; // disable fcgid stuff $binary = '/usr/bin/php-cgi'; $file_extensions = 'php'; @@ -312,7 +318,8 @@ if ($page == 'overview') { `fpm_reqterm` = :fpmreqterm, `fpm_reqslow` = :fpmreqslow, `phpsettings` = :phpsettings, - `fpmsettingid` = :fpmsettingid + `fpmsettingid` = :fpmsettingid, + `pass_authorizationheader` = :fpmpassauth WHERE `id` = :id"); $upd_data = array( 'desc' => $description, @@ -326,6 +333,7 @@ if ($page == 'overview') { 'fpmreqslow' => $fpm_reqslowtimeout, 'phpsettings' => $phpsettings, 'fpmsettingid' => $fpm_config_id, + 'fpmpassauth' => $fpm_pass_authorizationheader, 'id' => $id ); Database::pexecute($upd_stmt, $upd_data); diff --git a/lng/english.lng.php b/lng/english.lng.php index b33762e3..fe598231 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -2091,4 +2091,4 @@ $lng['serversettings']['phpsettingsforsubdomains']['description'] = 'If yes the $lng['serversettings']['leapiversion']['title'] = "Chose Let's Encrypt ACME implementation"; $lng['serversettings']['leapiversion']['description'] = "Chose between ACME v1 and ACME v2 implementation for Let's Encrypt."; $lng['error']['nowildcardwithletsencryptv2'] = 'Let\'s Encrypt can only validate wildcard-domains by DNS with ACME v2, sorry. Please set the ServerAlias to WWW or disable it completely'; -$lng['admin']['phpsettings']['pass_authorizationheader'] = 'Add "-pass-header Authorization" to vhosts'; +$lng['admin']['phpsettings']['pass_authorizationheader'] = 'Add "-pass-header Authorization" / "CGIPassAuth On" to vhosts'; diff --git a/lng/german.lng.php b/lng/german.lng.php index 1302358b..197eab84 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -1742,4 +1742,4 @@ $lng['serversettings']['phpsettingsforsubdomains']['description'] = 'Wenn ja, wi $lng['serversettings']['leapiversion']['title'] = "Wähle Let's Encrypt ACME Implementierung"; $lng['serversettings']['leapiversion']['description'] = "Wähle zwischen ACME v1 und ACME v2 Implementierung von Let's Encrypt."; $lng['error']['nowildcardwithletsencryptv2'] = 'Let\'s Encrypt kann in ACME v2 Wildcard-Domains nur via DNS validieren, sorry. Bitte den ServerAlias auf WWW setzen oder deaktivieren'; -$lng['admin']['phpsettings']['pass_authorizationheader'] = 'Füge "-pass-header Authorization" in Vhosts ein'; +$lng['admin']['phpsettings']['pass_authorizationheader'] = 'Füge "-pass-header Authorization" / "CGIPassAuth On" in Vhosts ein'; From 9312e4967e5559cf48370431180aca4c81bc68ee Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Wed, 10 Jan 2018 08:45:20 +0100 Subject: [PATCH 0366/1335] fix access to idle-timeout setting which moved from global settings to per-fpm-daemon setting, thx to gunnyst for pointing this out Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/phpinterface/class.phpinterface.php | 6 ++++++ scripts/jobs/cron_tasks.inc.http.10.apache.php | 2 +- scripts/jobs/cron_tasks.inc.http.15.apache_fcgid.php | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/classes/phpinterface/class.phpinterface.php b/lib/classes/phpinterface/class.phpinterface.php index 9bf6bf41..88fe7bbc 100644 --- a/lib/classes/phpinterface/class.phpinterface.php +++ b/lib/classes/phpinterface/class.phpinterface.php @@ -91,6 +91,12 @@ class phpinterface { SELECT * FROM `" . TABLE_PANEL_PHPCONFIGS . "` WHERE `id` = :id" ); $this->_php_configs_cache[$php_config_id] = Database::pexecute_first($stmt, array('id' => $php_config_id)); + if ((int)Settings::Get('phpfpm.enabled') == 1) { + $stmt = Database::prepare(" + SELECT * FROM `" . TABLE_PANEL_FPMDAEMONS . "` WHERE `id` = :id" + ); + $this->_php_configs_cache[$php_config_id]['fpm_settings'] = Database::pexecute_first($stmt, array('id' => $this->_php_configs_cache[$php_config_id]['fpmsettingid'])); + } } return $this->_php_configs_cache[$php_config_id]; diff --git a/scripts/jobs/cron_tasks.inc.http.10.apache.php b/scripts/jobs/cron_tasks.inc.http.10.apache.php index b3147f03..1eb0528d 100644 --- a/scripts/jobs/cron_tasks.inc.http.10.apache.php +++ b/scripts/jobs/cron_tasks.inc.http.10.apache.php @@ -340,7 +340,7 @@ class apache extends HttpConfigBase if ($phpconfig['pass_authorizationheader'] == '1') { $addheader = " -pass-header Authorization"; } - $this->virtualhosts_data[$vhosts_filename] .= ' FastCgiExternalServer ' . $php->getInterface()->getAliasConfigDir() . $srvName . ' -socket ' . $php->getInterface()->getSocketFile() . ' -idle-timeout ' . Settings::Get('phpfpm.idle_timeout') . $addheader . "\n"; + $this->virtualhosts_data[$vhosts_filename] .= ' FastCgiExternalServer ' . $php->getInterface()->getAliasConfigDir() . $srvName . ' -socket ' . $php->getInterface()->getSocketFile() . ' -idle-timeout ' . $phpconfig['fpm_settings']['idle_timeout'] . $addheader . "\n"; $this->virtualhosts_data[$vhosts_filename] .= ' ' . "\n"; $file_extensions = explode(' ', $phpconfig['file_extensions']); $this->virtualhosts_data[$vhosts_filename] .= ' ' . "\n"; diff --git a/scripts/jobs/cron_tasks.inc.http.15.apache_fcgid.php b/scripts/jobs/cron_tasks.inc.http.15.apache_fcgid.php index b2385abb..fcd16533 100644 --- a/scripts/jobs/cron_tasks.inc.http.15.apache_fcgid.php +++ b/scripts/jobs/cron_tasks.inc.http.15.apache_fcgid.php @@ -72,7 +72,7 @@ class apache_fcgid extends apache if ($phpconfig['pass_authorizationheader'] == '1') { $addheader = " -pass-header Authorization"; } - $php_options_text.= ' FastCgiExternalServer ' . $php->getInterface()->getAliasConfigDir() . $srvName . ' -socket ' . $php->getInterface()->getSocketFile() . ' -idle-timeout ' . Settings::Get('phpfpm.idle_timeout') . $addheader . "\n"; + $php_options_text.= ' FastCgiExternalServer ' . $php->getInterface()->getAliasConfigDir() . $srvName . ' -socket ' . $php->getInterface()->getSocketFile() . ' -idle-timeout ' . $phpconfig['fpm_settings']['idle_timeout'] . $addheader . "\n"; $php_options_text.= ' ' . "\n"; $php_options_text.= ' ' . "\n"; $php_options_text.= ' SetHandler php5-fastcgi'. "\n"; From 4bcdfc0786067b69f9eef1c3da60d51ed2c625be Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Wed, 10 Jan 2018 10:12:26 +0100 Subject: [PATCH 0367/1335] Added option to set the TLS protocol versions to be used in webservers Signed-off-by: Michael Kaufmann (d00p) --- actions/admin/settings/131.ssl.php | 23 +++++++++++++++---- install/froxlor.sql | 3 ++- .../updates/froxlor/0.9/update_0.9.inc.php | 9 ++++++++ lng/english.lng.php | 2 ++ lng/german.lng.php | 2 ++ .../jobs/cron_tasks.inc.http.10.apache.php | 4 ++-- scripts/jobs/cron_tasks.inc.http.30.nginx.php | 2 +- 7 files changed, 36 insertions(+), 9 deletions(-) diff --git a/actions/admin/settings/131.ssl.php b/actions/admin/settings/131.ssl.php index b9b2d459..1ad1768a 100644 --- a/actions/admin/settings/131.ssl.php +++ b/actions/admin/settings/131.ssl.php @@ -30,6 +30,20 @@ return array( 'save_method' => 'storeSettingField', 'overview_option' => true ), + 'system_ssl_protocols' => array( + 'label' => $lng['serversettings']['ssl']['ssl_protocols'], + 'settinggroup' => 'system', + 'varname' => 'ssl_protocols', + 'type' => 'option', + 'default' => 'TLSv1,TLSv1.2', + 'option_mode' => 'multiple', + 'option_options' => array( + 'TLSv1' => 'TLSv1', + 'TLSv1.1' => 'TLSv1.1', + 'TLSv1.2' => 'TLSv1.2' + ), + 'save_method' => 'storeSettingField' + ), 'system_ssl_cipher_list' => array( 'label' => $lng['serversettings']['ssl']['ssl_cipher_list'], 'settinggroup' => 'system', @@ -87,8 +101,7 @@ return array( 'string_type' => 'string', 'string_emptyallowed' => false, 'default' => 'shmcb:/var/run/apache2/ocsp-stapling.cache(131072)', - 'visible' => Settings::Get('system.webserver') == "apache2" && - Settings::Get('system.apache24') == 1, + 'visible' => Settings::Get('system.webserver') == "apache2" && Settings::Get('system.apache24') == 1, 'save_method' => 'storeSettingField' ), 'system_leenabled' => array( @@ -120,7 +133,7 @@ return array( 'type' => 'string', 'string_type' => 'file', 'default' => '/etc/apache2/conf-enabled/acme.conf', - 'save_method' => 'storeSettingField', + 'save_method' => 'storeSettingField' ), 'system_letsencryptca' => array( 'label' => $lng['serversettings']['letsencryptca'], @@ -130,8 +143,8 @@ return array( 'default' => 'testing', 'option_mode' => 'one', 'option_options' => array( - 'testing' => 'https://acme-staging'.(Settings::Get('system.leapiversion') == '2' ? '-v02' : '').'.api.letsencrypt.org (Test)', - 'production' => 'https://acme-v0'.Settings::Get('system.leapiversion').'.api.letsencrypt.org (Live)' + 'testing' => 'https://acme-staging' . (Settings::Get('system.leapiversion') == '2' ? '-v02' : '') . '.api.letsencrypt.org (Test)', + 'production' => 'https://acme-v0' . Settings::Get('system.leapiversion') . '.api.letsencrypt.org (Live)' ), 'save_method' => 'storeSettingField' ), diff --git a/install/froxlor.sql b/install/froxlor.sql index 26ec3281..f56d0a39 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -557,6 +557,7 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES ('system', 'leregistered', '0'), ('system', 'nssextrausers', '0'), ('system', 'disable_le_selfcheck', '0'), + ('system', 'ssl_protocols', 'TLSv1,TLSv1.2'), ('panel', 'decimal_places', '4'), ('panel', 'adminmail', 'admin@SERVERNAME'), ('panel', 'phpmyadmin_url', ''), @@ -589,7 +590,7 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES ('panel', 'password_special_char', '!?<>§$%+#=@'), ('panel', 'customer_hide_options', ''), ('panel', 'version', '0.9.38.8'), - ('panel', 'db_version', '201801091'); + ('panel', 'db_version', '201801100'); DROP TABLE IF EXISTS `panel_tasks`; diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index 52f26e39..48212f1d 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -3743,3 +3743,12 @@ if (isDatabaseVersion('201801090')) { updateToDbVersion('201801091'); } + +if (isDatabaseVersion('201801091')) { + + showUpdateStep("Adding new setting for SSL protocols"); + Settings::AddNew('system.ssl_protocols', 'TLSv1,TLSv1.2'); + lastStepStatus(0); + + updateToDbVersion('201801100'); +} diff --git a/lng/english.lng.php b/lng/english.lng.php index fe598231..0893cdb6 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -2092,3 +2092,5 @@ $lng['serversettings']['leapiversion']['title'] = "Chose Let's Encrypt ACME impl $lng['serversettings']['leapiversion']['description'] = "Chose between ACME v1 and ACME v2 implementation for Let's Encrypt."; $lng['error']['nowildcardwithletsencryptv2'] = 'Let\'s Encrypt can only validate wildcard-domains by DNS with ACME v2, sorry. Please set the ServerAlias to WWW or disable it completely'; $lng['admin']['phpsettings']['pass_authorizationheader'] = 'Add "-pass-header Authorization" / "CGIPassAuth On" to vhosts'; +$lng['serversettings']['ssl']['ssl_protocols']['title'] = 'Configure the TLS protocol version'; +$lng['serversettings']['ssl']['ssl_protocols']['description'] = 'This is a list of ssl protocols that you want (or don\'t want) to use when using SSL. Notice: Some older browsers may not support the newest protcol versions.

Default value is:

TLSv1, TLSv1.2
'; diff --git a/lng/german.lng.php b/lng/german.lng.php index 197eab84..830bb1c3 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -1743,3 +1743,5 @@ $lng['serversettings']['leapiversion']['title'] = "Wähle Let's Encrypt ACME Imp $lng['serversettings']['leapiversion']['description'] = "Wähle zwischen ACME v1 und ACME v2 Implementierung von Let's Encrypt."; $lng['error']['nowildcardwithletsencryptv2'] = 'Let\'s Encrypt kann in ACME v2 Wildcard-Domains nur via DNS validieren, sorry. Bitte den ServerAlias auf WWW setzen oder deaktivieren'; $lng['admin']['phpsettings']['pass_authorizationheader'] = 'Füge "-pass-header Authorization" / "CGIPassAuth On" in Vhosts ein'; +$lng['serversettings']['ssl']['ssl_protocols']['title'] = 'SSL Protokollversion festlegen'; +$lng['serversettings']['ssl']['ssl_protocols']['description'] = 'Dies ist eine Liste von SSL/TLS Protokollversionen die genutzt werden sollen (oder auch nicht genutzt werden sollen), wenn SSL verwendet wird. Hinweis: Ältere Browser sind möglicherweise nicht vollständig zum neusten Protokoll kompatibel.

Standard-Wert ist:
TLSv1, TLSv1.2
'; diff --git a/scripts/jobs/cron_tasks.inc.http.10.apache.php b/scripts/jobs/cron_tasks.inc.http.10.apache.php index 1eb0528d..46fe68d3 100644 --- a/scripts/jobs/cron_tasks.inc.http.10.apache.php +++ b/scripts/jobs/cron_tasks.inc.http.10.apache.php @@ -454,7 +454,7 @@ class apache extends HttpConfigBase } else { $this->virtualhosts_data[$vhosts_filename] .= ' SSLEngine On' . "\n"; - $this->virtualhosts_data[$vhosts_filename] .= ' SSLProtocol -ALL +TLSv1 +TLSv1.2' . "\n"; + $this->virtualhosts_data[$vhosts_filename] .= ' SSLProtocol -ALL' . str_replace(","," +", Settings::Get('system.ssl_protocols')) . "\n"; if (Settings::Get('system.apache24') == '1') { if (Settings::Get('system.http2_support') == '1') { $this->virtualhosts_data[$vhosts_filename] .= ' Protocols h2 http/1.1' . "\n"; @@ -892,7 +892,7 @@ class apache extends HttpConfigBase if ($domain['ssl_cert_file'] != '') { $vhost_content .= ' SSLEngine On' . "\n"; - $vhost_content .= ' SSLProtocol -ALL +TLSv1 +TLSv1.2' . "\n"; + $vhost_content .= ' SSLProtocol -ALL' . str_replace(","," +", Settings::Get('system.ssl_protocols')) . "\n"; if (Settings::Get('system.apache24') == '1') { if (isset($domain['http2']) && $domain['http2'] == '1') { $vhost_content .= ' Protocols h2 http/1.1' . "\n"; diff --git a/scripts/jobs/cron_tasks.inc.http.30.nginx.php b/scripts/jobs/cron_tasks.inc.http.30.nginx.php index 0685ed7c..5c932fd1 100644 --- a/scripts/jobs/cron_tasks.inc.http.30.nginx.php +++ b/scripts/jobs/cron_tasks.inc.http.30.nginx.php @@ -623,7 +623,7 @@ class nginx extends HttpConfigBase } else { // obsolete: ssl on now belongs to the listen block as 'ssl' at the end // $sslsettings .= "\t" . 'ssl on;' . "\n"; - $sslsettings .= "\t" . 'ssl_protocols TLSv1 TLSv1.2;' . "\n"; + $sslsettings .= "\t" . 'ssl_protocols ' . str_replace(",", " ", Settings::Get('system.ssl_protocols')) . ';' . "\n"; $sslsettings .= "\t" . 'ssl_ciphers ' . Settings::Get('system.ssl_cipher_list') . ';' . "\n"; $sslsettings .= "\t" . 'ssl_ecdh_curve secp384r1;' . "\n"; $sslsettings .= "\t" . 'ssl_prefer_server_ciphers on;' . "\n"; From 281b131c628b07464f28d28e6194f9b04780d521 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Wed, 10 Jan 2018 10:21:02 +0100 Subject: [PATCH 0368/1335] ups, forgot to adjust version in lib/version.inc.php Signed-off-by: Michael Kaufmann (d00p) --- lib/version.inc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/version.inc.php b/lib/version.inc.php index 40aa7b18..fb8d88d3 100644 --- a/lib/version.inc.php +++ b/lib/version.inc.php @@ -19,7 +19,7 @@ $version = '0.9.38.8'; // Database version (YYYYMMDDC where C is a daily counter) -$dbversion = '201801091'; +$dbversion = '201801100'; // Distribution branding-tag (used for Debian etc.) $branding = ''; From f2a79d4d965346ac3fd74fef3e4f04fd6fef70b0 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Wed, 10 Jan 2018 15:20:19 +0100 Subject: [PATCH 0369/1335] add new security.limit_extensions setting to fpm-daemon settings Signed-off-by: Michael Kaufmann (d00p) --- admin_phpsettings.php | 12 +++++++++--- install/froxlor.sql | 3 ++- install/updates/froxlor/0.9/update_0.9.inc.php | 9 +++++++++ lib/classes/phpinterface/class.phpinterface_fpm.php | 2 ++ .../admin/phpconfig/formfield.fpmconfig_add.php | 9 +++++++++ .../admin/phpconfig/formfield.fpmconfig_edit.php | 9 +++++++++ lib/version.inc.php | 2 +- lng/english.lng.php | 2 ++ lng/german.lng.php | 2 ++ scripts/jobs/cron_tasks.inc.http.10.apache.php | 10 +++++++++- scripts/jobs/cron_tasks.inc.http.15.apache_fcgid.php | 10 +++++++++- 11 files changed, 63 insertions(+), 7 deletions(-) diff --git a/admin_phpsettings.php b/admin_phpsettings.php index 0fa1b831..f8dcc5ef 100644 --- a/admin_phpsettings.php +++ b/admin_phpsettings.php @@ -417,6 +417,7 @@ if ($page == 'overview') { $max_spare_servers = isset($_POST['max_spare_servers']) ? (int) $_POST['max_spare_servers'] : 0; $max_requests = isset($_POST['max_requests']) ? (int) $_POST['max_requests'] : 0; $idle_timeout = isset($_POST['idle_timeout']) ? (int) $_POST['idle_timeout'] : 0; + $limit_extensions = validate($_POST['limit_extensions'], 'limit_extensions'); if (strlen($description) == 0 || strlen($description) > 50) { standard_error('descriptioninvalid'); @@ -433,7 +434,8 @@ if ($page == 'overview') { `min_spare_servers` = :min_spare_servers, `max_spare_servers` = :max_spare_servers, `max_requests` = :max_requests, - `idle_timeout` = :idle_timeout + `idle_timeout` = :idle_timeout, + `limit_extensions` = :limit_extensions "); $ins_data = array( 'desc' => $description, @@ -445,7 +447,8 @@ if ($page == 'overview') { 'min_spare_servers' => $min_spare_servers, 'max_spare_servers' => $max_spare_servers, 'max_requests' => $max_requests, - 'idle_timeout' => $idle_timeout + 'idle_timeout' => $idle_timeout, + 'limit_extensions' => $limit_extensions ); Database::pexecute($ins_stmt, $ins_data); @@ -543,6 +546,7 @@ if ($page == 'overview') { $max_spare_servers = isset($_POST['max_spare_servers']) ? (int) $_POST['max_spare_servers'] : $result['max_spare_servers']; $max_requests = isset($_POST['max_requests']) ? (int) $_POST['max_requests'] : $result['max_requests']; $idle_timeout = isset($_POST['idle_timeout']) ? (int) $_POST['idle_timeout'] : $result['idle_timeout']; + $limit_extensions = validate($_POST['limit_extensions'], 'limit_extensions'); if (strlen($description) == 0 || strlen($description) > 50) { standard_error('descriptioninvalid'); @@ -559,7 +563,8 @@ if ($page == 'overview') { `min_spare_servers` = :min_spare_servers, `max_spare_servers` = :max_spare_servers, `max_requests` = :max_requests, - `idle_timeout` = :idle_timeout + `idle_timeout` = :idle_timeout, + `limit_extensions` = :limit_extensions WHERE `id` = :id "); $upd_data = array( @@ -573,6 +578,7 @@ if ($page == 'overview') { 'max_spare_servers' => $max_spare_servers, 'max_requests' => $max_requests, 'idle_timeout' => $idle_timeout, + 'limit_extensions' => $limit_extensions, 'id' => $id ); Database::pexecute($upd_stmt, $upd_data); diff --git a/install/froxlor.sql b/install/froxlor.sql index f56d0a39..cbcae082 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -590,7 +590,7 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES ('panel', 'password_special_char', '!?<>§$%+#=@'), ('panel', 'customer_hide_options', ''), ('panel', 'version', '0.9.38.8'), - ('panel', 'db_version', '201801100'); + ('panel', 'db_version', '201801101'); DROP TABLE IF EXISTS `panel_tasks`; @@ -770,6 +770,7 @@ CREATE TABLE `panel_fpmdaemons` ( `max_spare_servers` int(4) NOT NULL DEFAULT '35', `max_requests` int(4) NOT NULL DEFAULT '0', `idle_timeout` int(4) NOT NULL DEFAULT '30', + `limit_extensions` varchar(255) NOT NULL default '.php', PRIMARY KEY (`id`), UNIQUE KEY `reload` (`reload_cmd`), UNIQUE KEY `config` (`config_dir`) diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index 48212f1d..9891df2b 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -3752,3 +3752,12 @@ if (isDatabaseVersion('201801091')) { updateToDbVersion('201801100'); } + +if (isDatabaseVersion('201801100')) { + + showUpdateStep("Adding field for security.limit_extensions fpm-setting"); + Database::query("ALTER TABLE `" . TABLE_PANEL_FPMDAEMONS . "` ADD `limit_extensions` varchar(255) NOT NULL default '.php';"); + lastStepStatus(0); + + updateToDbVersion('201801101'); +} diff --git a/lib/classes/phpinterface/class.phpinterface_fpm.php b/lib/classes/phpinterface/class.phpinterface_fpm.php index 2622b090..0509666a 100644 --- a/lib/classes/phpinterface/class.phpinterface_fpm.php +++ b/lib/classes/phpinterface/class.phpinterface_fpm.php @@ -192,6 +192,7 @@ class phpinterface_fpm $fpm_max_spare_servers = (int) $this->_fpm_cfg['max_spare_servers']; $fpm_requests = (int) $this->_fpm_cfg['max_requests']; $fpm_process_idle_timeout = (int) $this->_fpm_cfg['idle_timeout']; + $fpm_limit_extensions = $this->_fpm_cfg['limit_extensions']; if ($fpm_children == 0) { $fpm_children = 1; @@ -255,6 +256,7 @@ class phpinterface_fpm } $fpm_config .= ';chroot = ' . makeCorrectDir($this->_domain['documentroot']) . "\n"; + $fpm_config .= 'security.limit_extensions = '.$fpm_limit_extensions . "\n"; $tmpdir = makeCorrectDir(Settings::Get('phpfpm.tmpdir') . '/' . $this->_domain['loginname'] . '/'); if (! is_dir($tmpdir)) { diff --git a/lib/formfields/admin/phpconfig/formfield.fpmconfig_add.php b/lib/formfields/admin/phpconfig/formfield.fpmconfig_add.php index 88a1bb8b..a8d91d86 100644 --- a/lib/formfields/admin/phpconfig/formfield.fpmconfig_add.php +++ b/lib/formfields/admin/phpconfig/formfield.fpmconfig_add.php @@ -81,6 +81,15 @@ return array( 'desc' => $lng['serversettings']['phpfpm_settings']['idle_timeout']['description'], 'type' => 'int', 'value' => 30 + ), + 'limit_extensions' => array( + 'label' => $lng['serversettings']['phpfpm_settings']['limit_extensions']['title'], + 'desc' => $lng['serversettings']['phpfpm_settings']['limit_extensions']['description'], + 'type' => 'string', + 'string_delimiter' => ' ', + 'string_regexp' => '^\.[a-z]([a-z0-9]+)', + 'string_emptyallowed' => false, + 'value' => '.php' ) ) ) diff --git a/lib/formfields/admin/phpconfig/formfield.fpmconfig_edit.php b/lib/formfields/admin/phpconfig/formfield.fpmconfig_edit.php index d5e8c17f..6b8199e3 100644 --- a/lib/formfields/admin/phpconfig/formfield.fpmconfig_edit.php +++ b/lib/formfields/admin/phpconfig/formfield.fpmconfig_edit.php @@ -82,6 +82,15 @@ return array( 'desc' => $lng['serversettings']['phpfpm_settings']['idle_timeout']['description'], 'type' => 'int', 'value' => $result['idle_timeout'] + ), + 'limit_extensions' => array( + 'label' => $lng['serversettings']['phpfpm_settings']['limit_extensions']['title'], + 'desc' => $lng['serversettings']['phpfpm_settings']['limit_extensions']['description'], + 'type' => 'string', + 'string_delimiter' => ' ', + 'string_regexp' => '^\.[a-z]([a-z0-9]+)', + 'string_emptyallowed' => false, + 'value' => $result['limit_extensions'] ) ) ) diff --git a/lib/version.inc.php b/lib/version.inc.php index fb8d88d3..ac4adb0f 100644 --- a/lib/version.inc.php +++ b/lib/version.inc.php @@ -19,7 +19,7 @@ $version = '0.9.38.8'; // Database version (YYYYMMDDC where C is a daily counter) -$dbversion = '201801100'; +$dbversion = '201801101'; // Distribution branding-tag (used for Debian etc.) $branding = ''; diff --git a/lng/english.lng.php b/lng/english.lng.php index 0893cdb6..0830a2ef 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -2094,3 +2094,5 @@ $lng['error']['nowildcardwithletsencryptv2'] = 'Let\'s Encrypt can only validate $lng['admin']['phpsettings']['pass_authorizationheader'] = 'Add "-pass-header Authorization" / "CGIPassAuth On" to vhosts'; $lng['serversettings']['ssl']['ssl_protocols']['title'] = 'Configure the TLS protocol version'; $lng['serversettings']['ssl']['ssl_protocols']['description'] = 'This is a list of ssl protocols that you want (or don\'t want) to use when using SSL. Notice: Some older browsers may not support the newest protcol versions.

Default value is:
TLSv1, TLSv1.2
'; +$lng['serversettings']['phpfpm_settings']['limit_extensions']['title'] = 'Allowed extensions'; +$lng['serversettings']['phpfpm_settings']['limit_extensions']['description'] = 'Limits the extensions of the main script FPM will allow to parse. This can prevent configuration mistakes on the web server side. You should only limit FPM to .php extensions to prevent malicious users to use other extensions to execute php code. Default value: .php'; diff --git a/lng/german.lng.php b/lng/german.lng.php index 830bb1c3..160ebb9a 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -1745,3 +1745,5 @@ $lng['error']['nowildcardwithletsencryptv2'] = 'Let\'s Encrypt kann in ACME v2 W $lng['admin']['phpsettings']['pass_authorizationheader'] = 'Füge "-pass-header Authorization" / "CGIPassAuth On" in Vhosts ein'; $lng['serversettings']['ssl']['ssl_protocols']['title'] = 'SSL Protokollversion festlegen'; $lng['serversettings']['ssl']['ssl_protocols']['description'] = 'Dies ist eine Liste von SSL/TLS Protokollversionen die genutzt werden sollen (oder auch nicht genutzt werden sollen), wenn SSL verwendet wird. Hinweis: Ältere Browser sind möglicherweise nicht vollständig zum neusten Protokoll kompatibel.

Standard-Wert ist:
TLSv1, TLSv1.2
'; +$lng['serversettings']['phpfpm_settings']['limit_extensions']['title'] = 'Erlaubte Dateiendungen'; +$lng['serversettings']['phpfpm_settings']['limit_extensions']['description'] = 'Beschränkt die Dateierweiterungen des Haupt-Skripts, das FPM zu parsen erlaubt. Dies kann Konfigurationsfehler auf der Webserverseite verhindern. Sie sollten FPM nur auf .php Erweiterungen beschränken, um zu verhindern, dass bösartige Nutzter andere Erweiterungen verwenden, um PHP Code auszuführen. Standardwert: .php'; diff --git a/scripts/jobs/cron_tasks.inc.http.10.apache.php b/scripts/jobs/cron_tasks.inc.http.10.apache.php index 46fe68d3..ce6bc072 100644 --- a/scripts/jobs/cron_tasks.inc.http.10.apache.php +++ b/scripts/jobs/cron_tasks.inc.http.10.apache.php @@ -327,7 +327,15 @@ class apache extends HttpConfigBase // mod_proxy stuff for apache-2.4 if (Settings::Get('system.apache24') == '1' && Settings::Get('phpfpm.use_mod_proxy') == '1') { - $this->virtualhosts_data[$vhosts_filename] .= ' ' . "\n"; + $filesmatch = $phpconfig['limit_extensions']; + $extensions = explode(" ", $filesmatch); + $filesmatch = ""; + foreach ($extensions as $ext) { + $filesmatch .= $ext.'|'; + } + // start block, cut off last pipe and close block + $filesmatch = '('.substr($filesmatch, -1).')'; + $this->virtualhosts_data[$vhosts_filename] .= ' '. "\n"; $this->virtualhosts_data[$vhosts_filename] .= ' SetHandler proxy:unix:' . $php->getInterface()->getSocketFile() . '|fcgi://localhost' . "\n"; $this->virtualhosts_data[$vhosts_filename] .= ' ' . "\n"; if ($phpconfig['pass_authorizationheader'] == '1') { diff --git a/scripts/jobs/cron_tasks.inc.http.15.apache_fcgid.php b/scripts/jobs/cron_tasks.inc.http.15.apache_fcgid.php index fcd16533..85e5ae64 100644 --- a/scripts/jobs/cron_tasks.inc.http.15.apache_fcgid.php +++ b/scripts/jobs/cron_tasks.inc.http.15.apache_fcgid.php @@ -44,7 +44,15 @@ class apache_fcgid extends apache if (Settings::Get('system.apache24') == '1' && Settings::Get('phpfpm.use_mod_proxy') == '1' ) { - $php_options_text.= ' '. "\n"; + $filesmatch = $phpconfig['limit_extensions']; + $extensions = explode(" ", $filesmatch); + $filesmatch = ""; + foreach ($extensions as $ext) { + $filesmatch .= $ext.'|'; + } + // start block, cut off last pipe and close block + $filesmatch = '('.substr($filesmatch, -1).')'; + $php_options_text.= ' '. "\n"; $php_options_text.= ' SetHandler proxy:unix:' . $php->getInterface()->getSocketFile() . '|fcgi://localhost'. "\n"; $php_options_text.= ' ' . "\n"; From 941dd14c725ce745fecee64e2ec176dacdb9762d Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Wed, 10 Jan 2018 15:22:22 +0100 Subject: [PATCH 0370/1335] read limit_extensions value from wrong array Signed-off-by: Michael Kaufmann (d00p) --- scripts/jobs/cron_tasks.inc.http.10.apache.php | 2 +- scripts/jobs/cron_tasks.inc.http.15.apache_fcgid.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/jobs/cron_tasks.inc.http.10.apache.php b/scripts/jobs/cron_tasks.inc.http.10.apache.php index ce6bc072..76e1e5ce 100644 --- a/scripts/jobs/cron_tasks.inc.http.10.apache.php +++ b/scripts/jobs/cron_tasks.inc.http.10.apache.php @@ -327,7 +327,7 @@ class apache extends HttpConfigBase // mod_proxy stuff for apache-2.4 if (Settings::Get('system.apache24') == '1' && Settings::Get('phpfpm.use_mod_proxy') == '1') { - $filesmatch = $phpconfig['limit_extensions']; + $filesmatch = $phpconfig['fpm_settings']['limit_extensions']; $extensions = explode(" ", $filesmatch); $filesmatch = ""; foreach ($extensions as $ext) { diff --git a/scripts/jobs/cron_tasks.inc.http.15.apache_fcgid.php b/scripts/jobs/cron_tasks.inc.http.15.apache_fcgid.php index 85e5ae64..6c4bfcb3 100644 --- a/scripts/jobs/cron_tasks.inc.http.15.apache_fcgid.php +++ b/scripts/jobs/cron_tasks.inc.http.15.apache_fcgid.php @@ -44,7 +44,7 @@ class apache_fcgid extends apache if (Settings::Get('system.apache24') == '1' && Settings::Get('phpfpm.use_mod_proxy') == '1' ) { - $filesmatch = $phpconfig['limit_extensions']; + $filesmatch = $phpconfig['fpm_settings']['limit_extensions']; $extensions = explode(" ", $filesmatch); $filesmatch = ""; foreach ($extensions as $ext) { From bba872618aaeae063320ded4092361984152e591 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Wed, 10 Jan 2018 15:28:48 +0100 Subject: [PATCH 0371/1335] fix ssl-procotols in apache-cron; fix files-match regex in apache-cron Signed-off-by: Michael Kaufmann (d00p) --- scripts/jobs/cron_tasks.inc.http.10.apache.php | 8 ++++---- scripts/jobs/cron_tasks.inc.http.15.apache_fcgid.php | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/scripts/jobs/cron_tasks.inc.http.10.apache.php b/scripts/jobs/cron_tasks.inc.http.10.apache.php index 76e1e5ce..92ac03e8 100644 --- a/scripts/jobs/cron_tasks.inc.http.10.apache.php +++ b/scripts/jobs/cron_tasks.inc.http.10.apache.php @@ -331,10 +331,10 @@ class apache extends HttpConfigBase $extensions = explode(" ", $filesmatch); $filesmatch = ""; foreach ($extensions as $ext) { - $filesmatch .= $ext.'|'; + $filesmatch .= substr($ext, 1).'|'; } // start block, cut off last pipe and close block - $filesmatch = '('.substr($filesmatch, -1).')'; + $filesmatch = '('.substr($filesmatch, 0, -1).')'; $this->virtualhosts_data[$vhosts_filename] .= ' '. "\n"; $this->virtualhosts_data[$vhosts_filename] .= ' SetHandler proxy:unix:' . $php->getInterface()->getSocketFile() . '|fcgi://localhost' . "\n"; $this->virtualhosts_data[$vhosts_filename] .= ' ' . "\n"; @@ -462,7 +462,7 @@ class apache extends HttpConfigBase } else { $this->virtualhosts_data[$vhosts_filename] .= ' SSLEngine On' . "\n"; - $this->virtualhosts_data[$vhosts_filename] .= ' SSLProtocol -ALL' . str_replace(","," +", Settings::Get('system.ssl_protocols')) . "\n"; + $this->virtualhosts_data[$vhosts_filename] .= ' SSLProtocol -ALL +' . str_replace(","," +", Settings::Get('system.ssl_protocols')) . "\n"; if (Settings::Get('system.apache24') == '1') { if (Settings::Get('system.http2_support') == '1') { $this->virtualhosts_data[$vhosts_filename] .= ' Protocols h2 http/1.1' . "\n"; @@ -900,7 +900,7 @@ class apache extends HttpConfigBase if ($domain['ssl_cert_file'] != '') { $vhost_content .= ' SSLEngine On' . "\n"; - $vhost_content .= ' SSLProtocol -ALL' . str_replace(","," +", Settings::Get('system.ssl_protocols')) . "\n"; + $vhost_content .= ' SSLProtocol -ALL +' . str_replace(","," +", Settings::Get('system.ssl_protocols')) . "\n"; if (Settings::Get('system.apache24') == '1') { if (isset($domain['http2']) && $domain['http2'] == '1') { $vhost_content .= ' Protocols h2 http/1.1' . "\n"; diff --git a/scripts/jobs/cron_tasks.inc.http.15.apache_fcgid.php b/scripts/jobs/cron_tasks.inc.http.15.apache_fcgid.php index 6c4bfcb3..6d507ec4 100644 --- a/scripts/jobs/cron_tasks.inc.http.15.apache_fcgid.php +++ b/scripts/jobs/cron_tasks.inc.http.15.apache_fcgid.php @@ -48,10 +48,10 @@ class apache_fcgid extends apache $extensions = explode(" ", $filesmatch); $filesmatch = ""; foreach ($extensions as $ext) { - $filesmatch .= $ext.'|'; + $filesmatch .= substr($ext, 1).'|'; } // start block, cut off last pipe and close block - $filesmatch = '('.substr($filesmatch, -1).')'; + $filesmatch = '('.substr($filesmatch, 0, -1).')'; $php_options_text.= ' '. "\n"; $php_options_text.= ' SetHandler proxy:unix:' . $php->getInterface()->getSocketFile() . '|fcgi://localhost'. "\n"; $php_options_text.= ' ' . "\n"; From 62006d584e910681e1287909b97b8c4b4754c8c9 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Wed, 10 Jan 2018 15:42:11 +0100 Subject: [PATCH 0372/1335] fix limit_extensions setting Signed-off-by: Michael Kaufmann (d00p) --- admin_phpsettings.php | 4 ++-- lib/formfields/admin/phpconfig/formfield.fpmconfig_add.php | 5 +---- lib/formfields/admin/phpconfig/formfield.fpmconfig_edit.php | 5 +---- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/admin_phpsettings.php b/admin_phpsettings.php index f8dcc5ef..b86c9de3 100644 --- a/admin_phpsettings.php +++ b/admin_phpsettings.php @@ -417,7 +417,7 @@ if ($page == 'overview') { $max_spare_servers = isset($_POST['max_spare_servers']) ? (int) $_POST['max_spare_servers'] : 0; $max_requests = isset($_POST['max_requests']) ? (int) $_POST['max_requests'] : 0; $idle_timeout = isset($_POST['idle_timeout']) ? (int) $_POST['idle_timeout'] : 0; - $limit_extensions = validate($_POST['limit_extensions'], 'limit_extensions'); + $limit_extensions = validate($_POST['limit_extensions'], 'limit_extensions', '^(\.[a-z]([a-z0-9]+)\ ?)+$'); if (strlen($description) == 0 || strlen($description) > 50) { standard_error('descriptioninvalid'); @@ -546,7 +546,7 @@ if ($page == 'overview') { $max_spare_servers = isset($_POST['max_spare_servers']) ? (int) $_POST['max_spare_servers'] : $result['max_spare_servers']; $max_requests = isset($_POST['max_requests']) ? (int) $_POST['max_requests'] : $result['max_requests']; $idle_timeout = isset($_POST['idle_timeout']) ? (int) $_POST['idle_timeout'] : $result['idle_timeout']; - $limit_extensions = validate($_POST['limit_extensions'], 'limit_extensions'); + $limit_extensions = validate($_POST['limit_extensions'], 'limit_extensions', '^(\.[a-z]([a-z0-9]+)\ ?)+$'); if (strlen($description) == 0 || strlen($description) > 50) { standard_error('descriptioninvalid'); diff --git a/lib/formfields/admin/phpconfig/formfield.fpmconfig_add.php b/lib/formfields/admin/phpconfig/formfield.fpmconfig_add.php index a8d91d86..d5e86de2 100644 --- a/lib/formfields/admin/phpconfig/formfield.fpmconfig_add.php +++ b/lib/formfields/admin/phpconfig/formfield.fpmconfig_add.php @@ -85,10 +85,7 @@ return array( 'limit_extensions' => array( 'label' => $lng['serversettings']['phpfpm_settings']['limit_extensions']['title'], 'desc' => $lng['serversettings']['phpfpm_settings']['limit_extensions']['description'], - 'type' => 'string', - 'string_delimiter' => ' ', - 'string_regexp' => '^\.[a-z]([a-z0-9]+)', - 'string_emptyallowed' => false, + 'type' => 'text', 'value' => '.php' ) ) diff --git a/lib/formfields/admin/phpconfig/formfield.fpmconfig_edit.php b/lib/formfields/admin/phpconfig/formfield.fpmconfig_edit.php index 6b8199e3..74593812 100644 --- a/lib/formfields/admin/phpconfig/formfield.fpmconfig_edit.php +++ b/lib/formfields/admin/phpconfig/formfield.fpmconfig_edit.php @@ -86,10 +86,7 @@ return array( 'limit_extensions' => array( 'label' => $lng['serversettings']['phpfpm_settings']['limit_extensions']['title'], 'desc' => $lng['serversettings']['phpfpm_settings']['limit_extensions']['description'], - 'type' => 'string', - 'string_delimiter' => ' ', - 'string_regexp' => '^\.[a-z]([a-z0-9]+)', - 'string_emptyallowed' => false, + 'type' => 'text', 'value' => $result['limit_extensions'] ) ) From eed3a91385ae5459094553282db08ca66db92aad Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Wed, 10 Jan 2018 15:45:21 +0100 Subject: [PATCH 0373/1335] fix (not yet perfect) regex for limit_extension setting Signed-off-by: Michael Kaufmann (d00p) --- admin_phpsettings.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/admin_phpsettings.php b/admin_phpsettings.php index b86c9de3..f43ce116 100644 --- a/admin_phpsettings.php +++ b/admin_phpsettings.php @@ -417,7 +417,7 @@ if ($page == 'overview') { $max_spare_servers = isset($_POST['max_spare_servers']) ? (int) $_POST['max_spare_servers'] : 0; $max_requests = isset($_POST['max_requests']) ? (int) $_POST['max_requests'] : 0; $idle_timeout = isset($_POST['idle_timeout']) ? (int) $_POST['idle_timeout'] : 0; - $limit_extensions = validate($_POST['limit_extensions'], 'limit_extensions', '^(\.[a-z]([a-z0-9]+)\ ?)+$'); + $limit_extensions = validate($_POST['limit_extensions'], 'limit_extensions', '/^(\.[a-z]([a-z0-9]+)\ ?)+$/'); if (strlen($description) == 0 || strlen($description) > 50) { standard_error('descriptioninvalid'); @@ -546,7 +546,7 @@ if ($page == 'overview') { $max_spare_servers = isset($_POST['max_spare_servers']) ? (int) $_POST['max_spare_servers'] : $result['max_spare_servers']; $max_requests = isset($_POST['max_requests']) ? (int) $_POST['max_requests'] : $result['max_requests']; $idle_timeout = isset($_POST['idle_timeout']) ? (int) $_POST['idle_timeout'] : $result['idle_timeout']; - $limit_extensions = validate($_POST['limit_extensions'], 'limit_extensions', '^(\.[a-z]([a-z0-9]+)\ ?)+$'); + $limit_extensions = validate($_POST['limit_extensions'], 'limit_extensions', '/^(\.[a-z]([a-z0-9]+)\ ?)+$/'); if (strlen($description) == 0 || strlen($description) > 50) { standard_error('descriptioninvalid'); From 29433ce96313b04f2e6c5921e5d9c8d1f19c6a52 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Wed, 10 Jan 2018 15:49:07 +0100 Subject: [PATCH 0374/1335] escape possible dots in extension (e.g. to allow .php.xml or similar) Signed-off-by: Michael Kaufmann (d00p) --- scripts/jobs/cron_tasks.inc.http.10.apache.php | 2 +- scripts/jobs/cron_tasks.inc.http.15.apache_fcgid.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/jobs/cron_tasks.inc.http.10.apache.php b/scripts/jobs/cron_tasks.inc.http.10.apache.php index 92ac03e8..420e9c22 100644 --- a/scripts/jobs/cron_tasks.inc.http.10.apache.php +++ b/scripts/jobs/cron_tasks.inc.http.10.apache.php @@ -334,7 +334,7 @@ class apache extends HttpConfigBase $filesmatch .= substr($ext, 1).'|'; } // start block, cut off last pipe and close block - $filesmatch = '('.substr($filesmatch, 0, -1).')'; + $filesmatch = '('.str_replace(".", "\.", substr($filesmatch, 0, -1)).')'; $this->virtualhosts_data[$vhosts_filename] .= ' '. "\n"; $this->virtualhosts_data[$vhosts_filename] .= ' SetHandler proxy:unix:' . $php->getInterface()->getSocketFile() . '|fcgi://localhost' . "\n"; $this->virtualhosts_data[$vhosts_filename] .= ' ' . "\n"; diff --git a/scripts/jobs/cron_tasks.inc.http.15.apache_fcgid.php b/scripts/jobs/cron_tasks.inc.http.15.apache_fcgid.php index 6d507ec4..0b3119e7 100644 --- a/scripts/jobs/cron_tasks.inc.http.15.apache_fcgid.php +++ b/scripts/jobs/cron_tasks.inc.http.15.apache_fcgid.php @@ -51,7 +51,7 @@ class apache_fcgid extends apache $filesmatch .= substr($ext, 1).'|'; } // start block, cut off last pipe and close block - $filesmatch = '('.substr($filesmatch, 0, -1).')'; + $filesmatch = '('.str_replace(".", "\.", substr($filesmatch, 0, -1)).')'; $php_options_text.= ' '. "\n"; $php_options_text.= ' SetHandler proxy:unix:' . $php->getInterface()->getSocketFile() . '|fcgi://localhost'. "\n"; $php_options_text.= ' ' . "\n"; From 8c617732800a2281c69cc2d5e33ecbe3952bf981 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Wed, 10 Jan 2018 17:01:17 +0100 Subject: [PATCH 0375/1335] fix usage of extensions from limit_extensions list in apache-cron Signed-off-by: Michael Kaufmann (d00p) --- scripts/jobs/cron_tasks.inc.http.10.apache.php | 11 +++++++++-- scripts/jobs/cron_tasks.inc.http.15.apache_fcgid.php | 10 +++++++++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/scripts/jobs/cron_tasks.inc.http.10.apache.php b/scripts/jobs/cron_tasks.inc.http.10.apache.php index 420e9c22..55f230d0 100644 --- a/scripts/jobs/cron_tasks.inc.http.10.apache.php +++ b/scripts/jobs/cron_tasks.inc.http.10.apache.php @@ -350,8 +350,15 @@ class apache extends HttpConfigBase } $this->virtualhosts_data[$vhosts_filename] .= ' FastCgiExternalServer ' . $php->getInterface()->getAliasConfigDir() . $srvName . ' -socket ' . $php->getInterface()->getSocketFile() . ' -idle-timeout ' . $phpconfig['fpm_settings']['idle_timeout'] . $addheader . "\n"; $this->virtualhosts_data[$vhosts_filename] .= ' ' . "\n"; - $file_extensions = explode(' ', $phpconfig['file_extensions']); - $this->virtualhosts_data[$vhosts_filename] .= ' ' . "\n"; + $filesmatch = $phpconfig['fpm_settings']['limit_extensions']; + $extensions = explode(" ", $filesmatch); + $filesmatch = ""; + foreach ($extensions as $ext) { + $filesmatch .= substr($ext, 1).'|'; + } + // start block, cut off last pipe and close block + $filesmatch = '('.str_replace(".", "\.", substr($filesmatch, 0, -1)).')'; + $this->virtualhosts_data[$vhosts_filename] .= ' '. "\n"; $this->virtualhosts_data[$vhosts_filename] .= ' AddHandler php5-fastcgi .php' . "\n"; $this->virtualhosts_data[$vhosts_filename] .= ' Action php5-fastcgi /fastcgiphp' . "\n"; $this->virtualhosts_data[$vhosts_filename] .= ' Options +ExecCGI' . "\n"; diff --git a/scripts/jobs/cron_tasks.inc.http.15.apache_fcgid.php b/scripts/jobs/cron_tasks.inc.http.15.apache_fcgid.php index 0b3119e7..acf49948 100644 --- a/scripts/jobs/cron_tasks.inc.http.15.apache_fcgid.php +++ b/scripts/jobs/cron_tasks.inc.http.15.apache_fcgid.php @@ -82,7 +82,15 @@ class apache_fcgid extends apache } $php_options_text.= ' FastCgiExternalServer ' . $php->getInterface()->getAliasConfigDir() . $srvName . ' -socket ' . $php->getInterface()->getSocketFile() . ' -idle-timeout ' . $phpconfig['fpm_settings']['idle_timeout'] . $addheader . "\n"; $php_options_text.= ' ' . "\n"; - $php_options_text.= ' ' . "\n"; + $filesmatch = $phpconfig['fpm_settings']['limit_extensions']; + $extensions = explode(" ", $filesmatch); + $filesmatch = ""; + foreach ($extensions as $ext) { + $filesmatch .= substr($ext, 1).'|'; + } + // start block, cut off last pipe and close block + $filesmatch = '('.str_replace(".", "\.", substr($filesmatch, 0, -1)).')'; + $php_options_text.= ' '. "\n"; $php_options_text.= ' SetHandler php5-fastcgi'. "\n"; $php_options_text.= ' Action php5-fastcgi /fastcgiphp' . "\n"; $php_options_text.= ' Options +ExecCGI' . "\n"; From 29968e6026ebcc81978ba34c45a299444ebf3f78 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 11 Jan 2018 10:44:42 +0100 Subject: [PATCH 0376/1335] add stretch config-templates, testers/feedback welcome Signed-off-by: Michael Kaufmann (d00p) --- lib/configfiles/stretch.xml | 4582 +++++++++++++++++ lng/english.lng.php | 6 +- lng/german.lng.php | 6 +- .../Sparkle/admin/configfiles/configfiles.tpl | 18 +- 4 files changed, 4592 insertions(+), 20 deletions(-) create mode 100644 lib/configfiles/stretch.xml diff --git a/lib/configfiles/stretch.xml b/lib/configfiles/stretch.xml new file mode 100644 index 00000000..a425306a --- /dev/null +++ b/lib/configfiles/stretch.xml @@ -0,0 +1,4582 @@ + + + + + + + + + + + {{settings.system.apacheconf_vhost}} + + + + + {{settings.system.apacheconf_vhost}} + + + + + + + {{settings.system.apacheconf_diroptions}} + + + + + {{settings.system.apacheconf_diroptions}} + + + + + + + + + + + {{settings.system.deactivateddocroot}} + + + + + + + + + //service[@type='http']/general/commands + + + + {{settings.phpfpm.enabled}} + + + + + + {{settings.system.leenabled}} + + + Require all granted + +]]> + +
+ + + + + + + "{{settings.system.letsencryptchallengepath}}/.well-known/acme-challenge/") + +# default listening port for IPv6 falls back to the IPv4 port +include_shell "/usr/share/lighttpd/use-ipv6.pl " + server.port +include_shell "/usr/share/lighttpd/create-mime.assign.pl" +include_shell "/usr/share/lighttpd/include-conf-enabled.pl" +]]> + + + //service[@type='http']/general/commands + + {{settings.system.apacheconf_vhost}} + + > /etc/lighttpd/lighttpd.conf]]> + + + {{settings.system.apacheconf_vhost}} + + > /etc/lighttpd/lighttpd.conf]]> + + + {{settings.system.apacheconf_diroptions}} + + > /etc/lighttpd/lighttpd.conf]]> + + + {{settings.system.apacheconf_diroptions}} + + > /etc/lighttpd/lighttpd.conf]]> + + + + + + + + + + {{settings.phpfpm.enabled}} + + {{settings.system.mod_fcgid}} + + + + + + + + + + + + + {{settings.system.leenabled}} + + + + + + {{settings.phpfpm.enabled}} + + {{settings.system.mod_fcgid}} + + + + + //service[@type='http']/general/commands + + {{settings.phpfpm.enabled}} + + {{settings.system.mod_fcgid}} + + + + + + + + + + + + > /etc/bind/named.conf.local]]> + + + + + + + + + +# add these entries to the list if any speficied: + +################################# +# allow-dnsupdate-from A global setting to allow DNS updates from these IP ranges. +# +# allow-dnsupdate-from=127.0.0.0/8,::1 + +################################# +# allow-recursion List of subnets that are allowed to recurse +# +allow-recursion=127.0.0.1 + +################################# +# also-notify When notifying a domain, also notify these nameservers +# +# also-notify= + +################################# +# any-to-tcp Answer ANY queries with tc=1, shunting to TCP +# +# any-to-tcp=no + +################################# +# cache-ttl Seconds to store packets in the PacketCache +# +# cache-ttl=20 + +################################# +# carbon-interval Number of seconds between carbon (graphite) updates +# +# carbon-interval=30 + +################################# +# carbon-ourname If set, overrides our reported hostname for carbon stats +# +# carbon-ourname= + +################################# +# carbon-server If set, send metrics in carbon (graphite) format to this server +# +# carbon-server= + +################################# +# chroot If set, chroot to this directory for more security +# +# chroot= + +################################# +# config-dir Location of configuration directory (pdns.conf) +# +config-dir=/etc/powerdns + +################################# +# config-name Name of this virtual configuration - will rename the binary image +# +# config-name= + +################################# +# control-console Debugging switch - don't use +# +# control-console=no + +################################# +# daemon Operate as a daemon +# +daemon=yes + +################################# +# default-ksk-algorithms Default KSK algorithms +# +# default-ksk-algorithms=rsasha256 + +################################# +# default-ksk-size Default KSK size (0 means default) +# +# default-ksk-size=0 + +################################# +# default-soa-mail mail address to insert in the SOA record if none set in the backend +# +# default-soa-mail= + +################################# +# default-soa-name name to insert in the SOA record if none set in the backend +# +# default-soa-name=a.misconfigured.powerdns.server + +################################# +# default-ttl Seconds a result is valid if not set otherwise +# +# default-ttl=3600 + +################################# +# default-zsk-algorithms Default ZSK algorithms +# +# default-zsk-algorithms=rsasha256 + +################################# +# default-zsk-size Default ZSK size (0 means default) +# +# default-zsk-size=0 + +################################# +# direct-dnskey Fetch DNSKEY RRs from backend during DNSKEY synthesis +# +# direct-dnskey=no + +################################# +# disable-axfr Disable zonetransfers but do allow TCP queries +# +# disable-axfr=no + +################################# +# disable-axfr-rectify Disable the rectify step during an outgoing AXFR. Only required for regression testing. +# +# disable-axfr-rectify=no + +################################# +# disable-tcp Do not listen to TCP queries +# +# disable-tcp=no + +################################# +# distributor-threads Default number of Distributor (backend) threads to start +# +# distributor-threads=3 + +################################# +# do-ipv6-additional-processing Do AAAA additional processing +# +# do-ipv6-additional-processing=yes + +################################# +# edns-subnet-processing If we should act on EDNS Subnet options +# +# edns-subnet-processing=no + +################################# +# entropy-source If set, read entropy from this file +# +# entropy-source=/dev/urandom + +################################# +# experimental-api-key REST API Static authentication key (required for API use) +# +# experimental-api-key= + +################################# +# experimental-api-readonly If the JSON API should disallow data modification +# +# experimental-api-readonly=no + +################################# +# experimental-dname-processing If we should support DNAME records +# +# experimental-dname-processing=no + +################################# +# experimental-dnsupdate Enable/Disable DNS update (RFC2136) support. Default is no. +# +# experimental-dnsupdate=no + +################################# +# experimental-json-interface If the webserver should serve JSON data +# +# experimental-json-interface=no + +################################# +# experimental-logfile Filename of the log file for JSON parser +# +# experimental-logfile=/var/log/pdns.log + +################################# +# forward-dnsupdate A global setting to allow DNS update packages that are for a Slave domain, to be forwarded to the master. +# +# forward-dnsupdate=yes + +################################# +# guardian Run within a guardian process +# +guardian=yes + +################################# +# include-dir Include *.conf files from this directory +# +# include-dir= + +################################# +# launch Which backends to launch and order to query them in +# +# launch= + +################################# +# load-modules Load this module - supply absolute or relative path +# +# load-modules= + +################################# +# local-address Local IP addresses to which we bind +# +local-address=,127.0.0.1 + +################################# +# local-address-nonexist-fail Fail to start if one or more of the local-address's do not exist on this server +# +# local-address-nonexist-fail=yes + +################################# +# local-ipv6 Local IP address to which we bind +# +# local-ipv6= + +################################# +# local-ipv6-nonexist-fail Fail to start if one or more of the local-ipv6 addresses do not exist on this server +# +# local-ipv6-nonexist-fail=yes + +################################# +# local-port The port on which we listen +# +# local-port=53 + +################################# +# log-dns-details If PDNS should log DNS non-erroneous details +# +# log-dns-details=no + +################################# +# log-dns-queries If PDNS should log all incoming DNS queries +# +# log-dns-queries=no + +################################# +# logging-facility Log under a specific facility +# +# logging-facility= + +################################# +# loglevel Amount of logging. Higher is more. Do not set below 3 +# +# loglevel=4 + +################################# +# lua-prequery-script Lua script with prequery handler +# +# lua-prequery-script= + +################################# +# master Act as a master +# +master=yes + +################################# +# max-cache-entries Maximum number of cache entries +# +# max-cache-entries=1000000 + +################################# +# max-ent-entries Maximum number of empty non-terminals in a zone +# +# max-ent-entries=100000 + +################################# +# max-nsec3-iterations Limit the number of NSEC3 hash iterations +# +# max-nsec3-iterations=500 + +################################# +# max-queue-length Maximum queuelength before considering situation lost +# +# max-queue-length=5000 + +################################# +# max-signature-cache-entries Maximum number of signatures cache entries +# +# max-signature-cache-entries= + +################################# +# max-tcp-connections Maximum number of TCP connections +# +# max-tcp-connections=10 + +################################# +# module-dir Default directory for modules +# +# module-dir=/usr/lib/TRIPLET/pdns + +################################# +# negquery-cache-ttl Seconds to store negative query results in the QueryCache +# +# negquery-cache-ttl=60 + +################################# +# no-shuffle Set this to prevent random shuffling of answers - for regression testing +# +# no-shuffle=off + +################################# +# only-notify Only send AXFR NOTIFY to these IP addresses or netmasks +# +# only-notify=0.0.0.0/0,::/0 + +################################# +# out-of-zone-additional-processing Do out of zone additional processing +# +# out-of-zone-additional-processing=yes + +################################# +# overload-queue-length Maximum queuelength moving to packetcache only +# +# overload-queue-length=0 + +################################# +# pipebackend-abi-version Version of the pipe backend ABI +# +# pipebackend-abi-version=1 + +################################# +# prevent-self-notification Don't send notifications to what we think is ourself +# +# prevent-self-notification=yes + +################################# +# query-cache-ttl Seconds to store query results in the QueryCache +# +# query-cache-ttl=20 + +################################# +# query-local-address Source IP address for sending queries +# +# query-local-address=0.0.0.0 + +################################# +# query-local-address6 Source IPv6 address for sending queries +# +# query-local-address6=:: + +################################# +# query-logging Hint backends that queries should be logged +# +# query-logging=no + +################################# +# queue-limit Maximum number of milliseconds to queue a query +# +# queue-limit=1500 + +################################# +# receiver-threads Default number of receiver threads to start +# +# receiver-threads=1 + +################################# +# recursive-cache-ttl Seconds to store packets for recursive queries in the PacketCache +# +# recursive-cache-ttl=10 + +################################# +# recursor If recursion is desired, IP address of a recursing nameserver +# +# recursor=no + +################################# +# retrieval-threads Number of AXFR-retrieval threads for slave operation +# +# retrieval-threads=2 + +################################# +# reuseport Enable higher performance on compliant kernels by using SO_REUSEPORT allowing each receiver thread to open its own socket +# +# reuseport=no + +################################# +# security-poll-suffix Domain name from which to query security update notifications +# +# security-poll-suffix=secpoll.powerdns.com. + +################################# +# send-root-referral Send out old-fashioned root-referral instead of ServFail in case of no authority +# +# send-root-referral=no + +################################# +# server-id Returned when queried for 'server.id' TXT or NSID, defaults to hostname - disabled or custom +# +# server-id= + +################################# +# setgid If set, change group id to this gid for more security +# +setgid=pdns + +################################# +# setuid If set, change user id to this uid for more security +# +setuid=pdns + +################################# +# signing-threads Default number of signer threads to start +# +# signing-threads=3 + +################################# +# slave Act as a slave +# +# slave=no + +################################# +# slave-cycle-interval Reschedule failed SOA serial checks once every .. seconds +# +# slave-cycle-interval=60 + +################################# +# slave-renotify If we should send out notifications for slaved updates +# +# slave-renotify=no + +################################# +# soa-expire-default Default SOA expire +# +# soa-expire-default=604800 + +################################# +# soa-minimum-ttl Default SOA minimum ttl +# +# soa-minimum-ttl=3600 + +################################# +# soa-refresh-default Default SOA refresh +# +# soa-refresh-default=10800 + +################################# +# soa-retry-default Default SOA retry +# +# soa-retry-default=3600 + +################################# +# socket-dir Where the controlsocket will live +# +# socket-dir=/var/run + +################################# +# tcp-control-address If set, PowerDNS can be controlled over TCP on this address +# +# tcp-control-address= + +################################# +# tcp-control-port If set, PowerDNS can be controlled over TCP on this address +# +# tcp-control-port=53000 + +################################# +# tcp-control-range If set, remote control of PowerDNS is possible over these networks only +# +# tcp-control-range=127.0.0.0/8, 10.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12, ::1/128, fe80::/10 + +################################# +# tcp-control-secret If set, PowerDNS can be controlled over TCP after passing this secret +# +# tcp-control-secret= + +################################# +# traceback-handler Enable the traceback handler (Linux only) +# +# traceback-handler=yes + +################################# +# trusted-notification-proxy IP address of incoming notification proxy +# +# trusted-notification-proxy= + +################################# +# udp-truncation-threshold Maximum UDP response size before we truncate +# +# udp-truncation-threshold=1680 + +################################# +# version-string PowerDNS version in packets - full, anonymous, powerdns or custom +# + +version-string=powerdns +################################# +# webserver Start a webserver for monitoring +# +# webserver=no + +################################# +# webserver-address IP Address of webserver to listen on +# +# webserver-address=127.0.0.1 + +################################# +# webserver-allow-from Webserver access is only allowed from these subnets +# +# webserver-allow-from=0.0.0.0/0,::/0 + +################################# +# webserver-password Password required for accessing the webserver +# +# webserver-password= + +################################# +# webserver-port Port of webserver to listen on +# +# webserver-port=8081 + +################################# +# webserver-print-arguments If the webserver should print arguments +# +# webserver-print-arguments=no + +# include froxlor-bind-specific config +include-dir=/etc/powerdns/froxlor/ +]]> + + + + + + + + + + + + + +# add these entries to the list if any speficied: + +################################# +# allow-dnsupdate-from A global setting to allow DNS updates from these IP ranges. +# +# allow-dnsupdate-from=127.0.0.0/8,::1 + +################################# +# allow-recursion List of subnets that are allowed to recurse +# +allow-recursion=127.0.0.1 + +################################# +# also-notify When notifying a domain, also notify these nameservers +# +# also-notify= + +################################# +# any-to-tcp Answer ANY queries with tc=1, shunting to TCP +# +# any-to-tcp=no + +################################# +# cache-ttl Seconds to store packets in the PacketCache +# +# cache-ttl=20 + +################################# +# carbon-interval Number of seconds between carbon (graphite) updates +# +# carbon-interval=30 + +################################# +# carbon-ourname If set, overrides our reported hostname for carbon stats +# +# carbon-ourname= + +################################# +# carbon-server If set, send metrics in carbon (graphite) format to this server +# +# carbon-server= + +################################# +# chroot If set, chroot to this directory for more security +# +# chroot= + +################################# +# config-dir Location of configuration directory (pdns.conf) +# +config-dir=/etc/powerdns + +################################# +# config-name Name of this virtual configuration - will rename the binary image +# +# config-name= + +################################# +# control-console Debugging switch - don't use +# +# control-console=no + +################################# +# daemon Operate as a daemon +# +daemon=yes + +################################# +# default-ksk-algorithms Default KSK algorithms +# +# default-ksk-algorithms=rsasha256 + +################################# +# default-ksk-size Default KSK size (0 means default) +# +# default-ksk-size=0 + +################################# +# default-soa-mail mail address to insert in the SOA record if none set in the backend +# +# default-soa-mail= + +################################# +# default-soa-name name to insert in the SOA record if none set in the backend +# +# default-soa-name=a.misconfigured.powerdns.server + +################################# +# default-ttl Seconds a result is valid if not set otherwise +# +# default-ttl=3600 + +################################# +# default-zsk-algorithms Default ZSK algorithms +# +# default-zsk-algorithms=rsasha256 + +################################# +# default-zsk-size Default ZSK size (0 means default) +# +# default-zsk-size=0 + +################################# +# direct-dnskey Fetch DNSKEY RRs from backend during DNSKEY synthesis +# +# direct-dnskey=no + +################################# +# disable-axfr Disable zonetransfers but do allow TCP queries +# +# disable-axfr=no + +################################# +# disable-axfr-rectify Disable the rectify step during an outgoing AXFR. Only required for regression testing. +# +# disable-axfr-rectify=no + +################################# +# disable-tcp Do not listen to TCP queries +# +# disable-tcp=no + +################################# +# distributor-threads Default number of Distributor (backend) threads to start +# +# distributor-threads=3 + +################################# +# do-ipv6-additional-processing Do AAAA additional processing +# +# do-ipv6-additional-processing=yes + +################################# +# edns-subnet-processing If we should act on EDNS Subnet options +# +# edns-subnet-processing=no + +################################# +# entropy-source If set, read entropy from this file +# +# entropy-source=/dev/urandom + +################################# +# experimental-api-key REST API Static authentication key (required for API use) +# +# experimental-api-key= + +################################# +# experimental-api-readonly If the JSON API should disallow data modification +# +# experimental-api-readonly=no + +################################# +# experimental-dname-processing If we should support DNAME records +# +# experimental-dname-processing=no + +################################# +# experimental-dnsupdate Enable/Disable DNS update (RFC2136) support. Default is no. +# +# experimental-dnsupdate=no + +################################# +# experimental-json-interface If the webserver should serve JSON data +# +# experimental-json-interface=no + +################################# +# experimental-logfile Filename of the log file for JSON parser +# +# experimental-logfile=/var/log/pdns.log + +################################# +# forward-dnsupdate A global setting to allow DNS update packages that are for a Slave domain, to be forwarded to the master. +# +# forward-dnsupdate=yes + +################################# +# guardian Run within a guardian process +# +guardian=yes + +################################# +# include-dir Include *.conf files from this directory +# +# include-dir= + +################################# +# launch Which backends to launch and order to query them in +# +# launch= +launch=bind + +################################# +# load-modules Load this module - supply absolute or relative path +# +# load-modules= + +################################# +# local-address Local IP addresses to which we bind +# +local-address=,127.0.0.1 + +################################# +# local-address-nonexist-fail Fail to start if one or more of the local-address's do not exist on this server +# +# local-address-nonexist-fail=yes + +################################# +# local-ipv6 Local IP address to which we bind +# +# local-ipv6= + +################################# +# local-ipv6-nonexist-fail Fail to start if one or more of the local-ipv6 addresses do not exist on this server +# +# local-ipv6-nonexist-fail=yes + +################################# +# local-port The port on which we listen +# +# local-port=53 + +################################# +# log-dns-details If PDNS should log DNS non-erroneous details +# +# log-dns-details=no + +################################# +# log-dns-queries If PDNS should log all incoming DNS queries +# +# log-dns-queries=no + +################################# +# logging-facility Log under a specific facility +# +# logging-facility= + +################################# +# loglevel Amount of logging. Higher is more. Do not set below 3 +# +# loglevel=4 + +################################# +# lua-prequery-script Lua script with prequery handler +# +# lua-prequery-script= + +################################# +# master Act as a master +# +master=yes + +################################# +# max-cache-entries Maximum number of cache entries +# +# max-cache-entries=1000000 + +################################# +# max-ent-entries Maximum number of empty non-terminals in a zone +# +# max-ent-entries=100000 + +################################# +# max-nsec3-iterations Limit the number of NSEC3 hash iterations +# +# max-nsec3-iterations=500 + +################################# +# max-queue-length Maximum queuelength before considering situation lost +# +# max-queue-length=5000 + +################################# +# max-signature-cache-entries Maximum number of signatures cache entries +# +# max-signature-cache-entries= + +################################# +# max-tcp-connections Maximum number of TCP connections +# +# max-tcp-connections=10 + +################################# +# module-dir Default directory for modules +# +# module-dir=/usr/lib/TRIPLET/pdns + +################################# +# negquery-cache-ttl Seconds to store negative query results in the QueryCache +# +# negquery-cache-ttl=60 + +################################# +# no-shuffle Set this to prevent random shuffling of answers - for regression testing +# +# no-shuffle=off + +################################# +# only-notify Only send AXFR NOTIFY to these IP addresses or netmasks +# +# only-notify=0.0.0.0/0,::/0 + +################################# +# out-of-zone-additional-processing Do out of zone additional processing +# +# out-of-zone-additional-processing=yes + +################################# +# overload-queue-length Maximum queuelength moving to packetcache only +# +# overload-queue-length=0 + +################################# +# pipebackend-abi-version Version of the pipe backend ABI +# +# pipebackend-abi-version=1 + +################################# +# prevent-self-notification Don't send notifications to what we think is ourself +# +# prevent-self-notification=yes + +################################# +# query-cache-ttl Seconds to store query results in the QueryCache +# +# query-cache-ttl=20 + +################################# +# query-local-address Source IP address for sending queries +# +# query-local-address=0.0.0.0 + +################################# +# query-local-address6 Source IPv6 address for sending queries +# +# query-local-address6=:: + +################################# +# query-logging Hint backends that queries should be logged +# +# query-logging=no + +################################# +# queue-limit Maximum number of milliseconds to queue a query +# +# queue-limit=1500 + +################################# +# receiver-threads Default number of receiver threads to start +# +# receiver-threads=1 + +################################# +# recursive-cache-ttl Seconds to store packets for recursive queries in the PacketCache +# +# recursive-cache-ttl=10 + +################################# +# recursor If recursion is desired, IP address of a recursing nameserver +# +# recursor=no + +################################# +# retrieval-threads Number of AXFR-retrieval threads for slave operation +# +# retrieval-threads=2 + +################################# +# reuseport Enable higher performance on compliant kernels by using SO_REUSEPORT allowing each receiver thread to open its own socket +# +# reuseport=no + +################################# +# security-poll-suffix Domain name from which to query security update notifications +# +# security-poll-suffix=secpoll.powerdns.com. + +################################# +# send-root-referral Send out old-fashioned root-referral instead of ServFail in case of no authority +# +# send-root-referral=no + +################################# +# server-id Returned when queried for 'server.id' TXT or NSID, defaults to hostname - disabled or custom +# +# server-id= + +################################# +# setgid If set, change group id to this gid for more security +# +setgid=pdns + +################################# +# setuid If set, change user id to this uid for more security +# +setuid=pdns + +################################# +# signing-threads Default number of signer threads to start +# +# signing-threads=3 + +################################# +# slave Act as a slave +# +# slave=no + +################################# +# slave-cycle-interval Reschedule failed SOA serial checks once every .. seconds +# +# slave-cycle-interval=60 + +################################# +# slave-renotify If we should send out notifications for slaved updates +# +# slave-renotify=no + +################################# +# soa-expire-default Default SOA expire +# +# soa-expire-default=604800 + +################################# +# soa-minimum-ttl Default SOA minimum ttl +# +# soa-minimum-ttl=3600 + +################################# +# soa-refresh-default Default SOA refresh +# +# soa-refresh-default=10800 + +################################# +# soa-retry-default Default SOA retry +# +# soa-retry-default=3600 + +################################# +# socket-dir Where the controlsocket will live +# +# socket-dir=/var/run + +################################# +# tcp-control-address If set, PowerDNS can be controlled over TCP on this address +# +# tcp-control-address= + +################################# +# tcp-control-port If set, PowerDNS can be controlled over TCP on this address +# +# tcp-control-port=53000 + +################################# +# tcp-control-range If set, remote control of PowerDNS is possible over these networks only +# +# tcp-control-range=127.0.0.0/8, 10.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12, ::1/128, fe80::/10 + +################################# +# tcp-control-secret If set, PowerDNS can be controlled over TCP after passing this secret +# +# tcp-control-secret= + +################################# +# traceback-handler Enable the traceback handler (Linux only) +# +# traceback-handler=yes + +################################# +# trusted-notification-proxy IP address of incoming notification proxy +# +# trusted-notification-proxy= + +################################# +# udp-truncation-threshold Maximum UDP response size before we truncate +# +# udp-truncation-threshold=1680 + +################################# +# version-string PowerDNS version in packets - full, anonymous, powerdns or custom +# + +version-string=powerdns +################################# +# webserver Start a webserver for monitoring +# +# webserver=no + +################################# +# webserver-address IP Address of webserver to listen on +# +# webserver-address=127.0.0.1 + +################################# +# webserver-allow-from Webserver access is only allowed from these subnets +# +# webserver-allow-from=0.0.0.0/0,::/0 + +################################# +# webserver-password Password required for accessing the webserver +# +# webserver-password= + +################################# +# webserver-port Port of webserver to listen on +# +# webserver-port=8081 + +################################# +# webserver-print-arguments If the webserver should print arguments +# +# webserver-print-arguments=no + +# include froxlor-bind-specific config +include-dir=/etc/powerdns/froxlor/ +]]> + + + + + named.conf + +# How often to check for zone changes. See 'Operation' section. +bind-check-interval=180 + +# Uncomment to enable Huffman compression on zone data. +# Currently saves around 20% of memory actually used, but slows down operation. +# bind-enable-huffman +]]> + + + + + + + + + + + + {{settings.system.vmail_gid}} + + + + + {{settings.system.vmail_uid}} + + + + + + + + + + + + + + + + + +password = +dbname = +hosts = +query = SELECT destination FROM mail_virtual WHERE email = '%s' AND trim(destination) <> '' +]]> + + + + +password = +dbname = +hosts = +query = SELECT domain FROM panel_domains WHERE domain = '%s' AND isemaildomain = '1' +]]> + + + + +password = +dbname = +expansion_limit = 1 +hosts = +query = SELECT CONCAT(homedir,maildir) FROM mail_users WHERE email = '%s' +]]> + + + + +password = +dbname = +hosts = +query = SELECT DISTINCT username FROM mail_users WHERE email in ((SELECT mail_virtual.email_full FROM mail_virtual WHERE mail_virtual.email = '%s' UNION SELECT mail_virtual.destination FROM mail_virtual WHERE mail_virtual.email = '%s')); +]]> + + + + +password = +dbname = +expansion_limit = 1 +hosts = +query = SELECT uid FROM mail_users WHERE email = '%s' +]]> + + + + +password = +dbname = +expansion_limit = 1 +hosts = +query = SELECT gid FROM mail_users WHERE email = '%s' +]]> + + + + +]]> + + + + + + + + + + + //service[@type='smtp']/general/commands[@index=1] + + //service[@type='smtp']/general/installs[@index=1] + + //service[@type='smtp']/general/commands[@index=2] + + + + +# SENDING MAIL +# +# The myorigin parameter specifies the domain that locally-posted +# mail appears to come from. The default is to append $myhostname, +# which is fine for small sites. If you run a domain with multiple +# machines, you should (1) change this to $mydomain and (2) set up +# a domain-wide alias database that aliases each user to +# user@that.users.mailhost. +# +# For the sake of consistency between sender and recipient addresses, +# myorigin also specifies the default domain name that is appended +# to recipient addresses that have no @domain part. +# +# Debian GNU/Linux specific: Specifying a file name will cause the +# first line of that file to be used as the name. The Debian default +# is /etc/mailname. +# +#myorigin = /etc/mailname +#myorigin = $myhostname +#myorigin = $mydomain + +# RECEIVING MAIL + +# The inet_interfaces parameter specifies the network interface +# addresses that this mail system receives mail on. By default, +# the software claims all active interfaces on the machine. The +# parameter also controls delivery of mail to user@[ip.address]. +# +# See also the proxy_interfaces parameter, for network addresses that +# are forwarded to us via a proxy or network address translator. +# +# Note: you need to stop/start Postfix when this parameter changes. +# +inet_interfaces = all +#inet_interfaces = $myhostname +#inet_interfaces = $myhostname, localhost + +# The proxy_interfaces parameter specifies the network interface +# addresses that this mail system receives mail on by way of a +# proxy or network address translation unit. This setting extends +# the address list specified with the inet_interfaces parameter. +# +# You must specify your proxy/NAT addresses when your system is a +# backup MX host for other domains, otherwise mail delivery loops +# will happen when the primary MX host is down. +# +#proxy_interfaces = +#proxy_interfaces = 1.2.3.4 + +# The mydestination parameter specifies the list of domains that this +# machine considers itself the final destination for. +# +# These domains are routed to the delivery agent specified with the +# local_transport parameter setting. By default, that is the UNIX +# compatible delivery agent that lookups all recipients in /etc/passwd +# and /etc/aliases or their equivalent. +# +# The default is $myhostname + localhost.$mydomain. On a mail domain +# gateway, you should also include $mydomain. +# +# Do not specify the names of virtual domains - those domains are +# specified elsewhere (see VIRTUAL_README). +# +# Do not specify the names of domains that this machine is backup MX +# host for. Specify those names via the relay_domains settings for +# the SMTP server, or use permit_mx_backup if you are lazy (see +# STANDARD_CONFIGURATION_README). +# +# The local machine is always the final destination for mail addressed +# to user@[the.net.work.address] of an interface that the mail system +# receives mail on (see the inet_interfaces parameter). +# +# Specify a list of host or domain names, /file/name or type:table +# patterns, separated by commas and/or whitespace. A /file/name +# pattern is replaced by its contents; a type:table is matched when +# a name matches a lookup key (the right-hand side is ignored). +# Continue long lines by starting the next line with whitespace. +# +# See also below, section "REJECTING MAIL FOR UNKNOWN LOCAL USERS". +# +#mydestination = $myhostname, localhost.$mydomain, localhost +mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain +#mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain, +# mail.$mydomain, www.$mydomain, ftp.$mydomain + +# REJECTING MAIL FOR UNKNOWN LOCAL USERS +# +# The local_recipient_maps parameter specifies optional lookup tables +# with all names or addresses of users that are local with respect +# to $mydestination, $inet_interfaces or $proxy_interfaces. +# +# If this parameter is defined, then the SMTP server will reject +# mail for unknown local users. This parameter is defined by default. +# +# To turn off local recipient checking in the SMTP server, specify +# local_recipient_maps = (i.e. empty). +# +# The default setting assumes that you use the default Postfix local +# delivery agent for local delivery. You need to update the +# local_recipient_maps setting if: +# +# - You define $mydestination domain recipients in files other than +# /etc/passwd, /etc/aliases, or the $virtual_alias_maps files. +# For example, you define $mydestination domain recipients in +# the $virtual_mailbox_maps files. +# +# - You redefine the local delivery agent in master.cf. +# +# - You redefine the "local_transport" setting in main.cf. +# +# - You use the "luser_relay", "mailbox_transport", or "fallback_transport" +# feature of the Postfix local delivery agent (see local(8)). +# +# Details are described in the LOCAL_RECIPIENT_README file. +# +# Beware: if the Postfix SMTP server runs chrooted, you probably have +# to access the passwd file via the proxymap service, in order to +# overcome chroot restrictions. The alternative, having a copy of +# the system passwd file in the chroot jail is just not practical. +# +# The right-hand side of the lookup tables is conveniently ignored. +# In the left-hand side, specify a bare username, an @domain.tld +# wild-card, or specify a user@domain.tld address. +# +#local_recipient_maps = unix:passwd.byname $alias_maps +#local_recipient_maps = proxy:unix:passwd.byname $alias_maps +#local_recipient_maps = + +# The unknown_local_recipient_reject_code specifies the SMTP server +# response code when a recipient domain matches $mydestination or +# ${proxy,inet}_interfaces, while $local_recipient_maps is non-empty +# and the recipient address or address local-part is not found. +# +# The default setting is 550 (reject mail) but it is safer to start +# with 450 (try again later) until you are certain that your +# local_recipient_maps settings are OK. +# +unknown_local_recipient_reject_code = 550 + +# TRUST AND RELAY CONTROL + +# The mynetworks parameter specifies the list of "trusted" SMTP +# clients that have more privileges than "strangers". +# +# In particular, "trusted" SMTP clients are allowed to relay mail +# through Postfix. See the smtpd_recipient_restrictions parameter +# in postconf(5). +# +# You can specify the list of "trusted" network addresses by hand +# or you can let Postfix do it for you (which is the default). +# +# By default (mynetworks_style = subnet), Postfix "trusts" SMTP +# clients in the same IP subnetworks as the local machine. +# On Linux, this does works correctly only with interfaces specified +# with the "ifconfig" command. +# +# Specify "mynetworks_style = class" when Postfix should "trust" SMTP +# clients in the same IP class A/B/C networks as the local machine. +# Don't do this with a dialup site - it would cause Postfix to "trust" +# your entire provider's network. Instead, specify an explicit +# mynetworks list by hand, as described below. +# +# Specify "mynetworks_style = host" when Postfix should "trust" +# only the local machine. +# +#mynetworks_style = class +#mynetworks_style = subnet +#mynetworks_style = host + +# Alternatively, you can specify the mynetworks list by hand, in +# which case Postfix ignores the mynetworks_style setting. +# +# Specify an explicit list of network/netmask patterns, where the +# mask specifies the number of bits in the network part of a host +# address. +# +# You can also specify the absolute pathname of a pattern file instead +# of listing the patterns here. Specify type:table for table-based lookups +# (the value on the table right-hand side is not used). +# +#mynetworks = 168.100.189.0/28, 127.0.0.0/8 +#mynetworks = $config_directory/mynetworks +#mynetworks = hash:/etc/postfix/network_table +mynetworks = 127.0.0.0/8 + +# The relay_domains parameter restricts what destinations this system will +# relay mail to. See the smtpd_recipient_restrictions description in +# postconf(5) for detailed information. +# +# By default, Postfix relays mail +# - from "trusted" clients (IP address matches $mynetworks) to any destination, +# - from "untrusted" clients to destinations that match $relay_domains or +# subdomains thereof, except addresses with sender-specified routing. +# The default relay_domains value is $mydestination. +# +# In addition to the above, the Postfix SMTP server by default accepts mail +# that Postfix is final destination for: +# - destinations that match $inet_interfaces or $proxy_interfaces, +# - destinations that match $mydestination +# - destinations that match $virtual_alias_domains, +# - destinations that match $virtual_mailbox_domains. +# These destinations do not need to be listed in $relay_domains. +# +# Specify a list of hosts or domains, /file/name patterns or type:name +# lookup tables, separated by commas and/or whitespace. Continue +# long lines by starting the next line with whitespace. A file name +# is replaced by its contents; a type:name table is matched when a +# (parent) domain appears as lookup key. +# +# NOTE: Postfix will not automatically forward mail for domains that +# list this system as their primary or backup MX host. See the +# permit_mx_backup restriction description in postconf(5). +# +#relay_domains = $mydestination + +# INTERNET OR INTRANET + +# The relayhost parameter specifies the default host to send mail to +# when no entry is matched in the optional transport(5) table. When +# no relayhost is given, mail is routed directly to the destination. +# +# On an intranet, specify the organizational domain name. If your +# internal DNS uses no MX records, specify the name of the intranet +# gateway host instead. +# +# In the case of SMTP, specify a domain, host, host:port, [host]:port, +# [address] or [address]:port; the form [host] turns off MX lookups. +# +# If you're connected via UUCP, see also the default_transport parameter. +# +#relayhost = $mydomain +#relayhost = [gateway.my.domain] +#relayhost = [mailserver.isp.tld] +#relayhost = uucphost +#relayhost = [an.ip.add.ress] + +# REJECTING UNKNOWN RELAY USERS +# +# The relay_recipient_maps parameter specifies optional lookup tables +# with all addresses in the domains that match $relay_domains. +# +# If this parameter is defined, then the SMTP server will reject +# mail for unknown relay users. This feature is off by default. +# +# The right-hand side of the lookup tables is conveniently ignored. +# In the left-hand side, specify an @domain.tld wild-card, or specify +# a user@domain.tld address. +# +#relay_recipient_maps = hash:/etc/postfix/relay_recipients + +# INPUT RATE CONTROL +# +# The in_flow_delay configuration parameter implements mail input +# flow control. This feature is turned on by default, although it +# still needs further development (it's disabled on SCO UNIX due +# to an SCO bug). +# +# A Postfix process will pause for $in_flow_delay seconds before +# accepting a new message, when the message arrival rate exceeds the +# message delivery rate. With the default 100 SMTP server process +# limit, this limits the mail inflow to 100 messages a second more +# than the number of messages delivered per second. +# +# Specify 0 to disable the feature. Valid delays are 0..10. +# +#in_flow_delay = 1s + +# ADDRESS REWRITING +# +# The ADDRESS_REWRITING_README document gives information about +# address masquerading or other forms of address rewriting including +# username->Firstname.Lastname mapping. + +# ADDRESS REDIRECTION (VIRTUAL DOMAIN) +# +# The VIRTUAL_README document gives information about the many forms +# of domain hosting that Postfix supports. + +# "USER HAS MOVED" BOUNCE MESSAGES +# +# See the discussion in the ADDRESS_REWRITING_README document. + +# TRANSPORT MAP +# +# See the discussion in the ADDRESS_REWRITING_README document. + +# ALIAS DATABASE +# +# The alias_maps parameter specifies the list of alias databases used +# by the local delivery agent. The default list is system dependent. +# +# On systems with NIS, the default is to search the local alias +# database, then the NIS alias database. See aliases(5) for syntax +# details. +# +# If you change the alias database, run "postalias /etc/aliases" (or +# wherever your system stores the mail alias file), or simply run +# "newaliases" to build the necessary DBM or DB file. +# +# It will take a minute or so before changes become visible. Use +# "postfix reload" to eliminate the delay. +# +#alias_maps = dbm:/etc/aliases +#alias_maps = hash:/etc/aliases +#alias_maps = hash:/etc/aliases, nis:mail.aliases +#alias_maps = netinfo:/aliases + +# The alias_database parameter specifies the alias database(s) that +# are built with "newaliases" or "sendmail -bi". This is a separate +# configuration parameter, because alias_maps (see above) may specify +# tables that are not necessarily all under control by Postfix. +# +#alias_database = dbm:/etc/aliases +#alias_database = dbm:/etc/mail/aliases +#alias_database = hash:/etc/aliases +#alias_database = hash:/etc/aliases, hash:/opt/majordomo/aliases + +# ADDRESS EXTENSIONS (e.g., user+foo) +# +# The recipient_delimiter parameter specifies the separator between +# user names and address extensions (user+foo). See canonical(5), +# local(8), relocated(5) and virtual(5) for the effects this has on +# aliases, canonical, virtual, relocated and .forward file lookups. +# Basically, the software tries user+foo and .forward+foo before +# trying user and .forward. +# +#recipient_delimiter = + + +# DELIVERY TO MAILBOX +# +# The home_mailbox parameter specifies the optional pathname of a +# mailbox file relative to a user's home directory. The default +# mailbox file is /var/spool/mail/user or /var/mail/user. Specify +# "Maildir/" for qmail-style delivery (the / is required). +# +#home_mailbox = Mailbox +#home_mailbox = Maildir/ + +# The mail_spool_directory parameter specifies the directory where +# UNIX-style mailboxes are kept. The default setting depends on the +# system type. +# +#mail_spool_directory = /var/mail +#mail_spool_directory = /var/spool/mail + +# The mailbox_command parameter specifies the optional external +# command to use instead of mailbox delivery. The command is run as +# the recipient with proper HOME, SHELL and LOGNAME environment settings. +# Exception: delivery for root is done as $default_user. +# +# Other environment variables of interest: USER (recipient username), +# EXTENSION (address extension), DOMAIN (domain part of address), +# and LOCAL (the address localpart). +# +# Unlike other Postfix configuration parameters, the mailbox_command +# parameter is not subjected to $parameter substitutions. This is to +# make it easier to specify shell syntax (see example below). +# +# Avoid shell meta characters because they will force Postfix to run +# an expensive shell process. Procmail alone is expensive enough. +# +# IF YOU USE THIS TO DELIVER MAIL SYSTEM-WIDE, YOU MUST SET UP AN +# ALIAS THAT FORWARDS MAIL FOR ROOT TO A REAL USER. +# +mailbox_command = /usr/lib/dovecot/deliver +#mailbox_command = /usr/bin/procmail -a "$EXTENSION" + +# The mailbox_transport specifies the optional transport in master.cf +# to use after processing aliases and .forward files. This parameter +# has precedence over the mailbox_command, fallback_transport and +# luser_relay parameters. +# +# Specify a string of the form transport:nexthop, where transport is +# the name of a mail delivery transport defined in master.cf. The +# :nexthop part is optional. For more details see the sample transport +# configuration file. +# +# NOTE: if you use this feature for accounts not in the UNIX password +# file, then you must update the "local_recipient_maps" setting in +# the main.cf file, otherwise the SMTP server will reject mail for +# non-UNIX accounts with "User unknown in local recipient table". +# +# Cyrus IMAP over LMTP. Specify ``lmtpunix cmd="lmtpd" +# listen="/var/imap/socket/lmtp" prefork=0'' in cyrus.conf. +#mailbox_transport = lmtp:unix:/var/imap/socket/lmtp +# +# Cyrus IMAP via command line. Uncomment the "cyrus...pipe" and +# subsequent line in master.cf. +#mailbox_transport = cyrus + +# The fallback_transport specifies the optional transport in master.cf +# to use for recipients that are not found in the UNIX passwd database. +# This parameter has precedence over the luser_relay parameter. +# +# Specify a string of the form transport:nexthop, where transport is +# the name of a mail delivery transport defined in master.cf. The +# :nexthop part is optional. For more details see the sample transport +# configuration file. +# +# NOTE: if you use this feature for accounts not in the UNIX password +# file, then you must update the "local_recipient_maps" setting in +# the main.cf file, otherwise the SMTP server will reject mail for +# non-UNIX accounts with "User unknown in local recipient table". +# +#fallback_transport = lmtp:unix:/file/name +#fallback_transport = cyrus +#fallback_transport = + +# The luser_relay parameter specifies an optional destination address +# for unknown recipients. By default, mail for unknown@$mydestination, +# unknown@[$inet_interfaces] or unknown@[$proxy_interfaces] is returned +# as undeliverable. +# +# The following expansions are done on luser_relay: $user (recipient +# username), $shell (recipient shell), $home (recipient home directory), +# $recipient (full recipient address), $extension (recipient address +# extension), $domain (recipient domain), $local (entire recipient +# localpart), $recipient_delimiter. Specify ${name?value} or +# ${name:value} to expand value only when $name does (does not) exist. +# +# luser_relay works only for the default Postfix local delivery agent. +# +# NOTE: if you use this feature for accounts not in the UNIX password +# file, then you must specify "local_recipient_maps =" (i.e. empty) in +# the main.cf file, otherwise the SMTP server will reject mail for +# non-UNIX accounts with "User unknown in local recipient table". +# +#luser_relay = $user@other.host +#luser_relay = $local@other.host +#luser_relay = admin+$local + +# JUNK MAIL CONTROLS +# +# The controls listed here are only a very small subset. The file +# SMTPD_ACCESS_README provides an overview. + +# The header_checks parameter specifies an optional table with patterns +# that each logical message header is matched against, including +# headers that span multiple physical lines. +# +# By default, these patterns also apply to MIME headers and to the +# headers of attached messages. With older Postfix versions, MIME and +# attached message headers were treated as body text. +# +# For details, see "man header_checks". +# +#header_checks = regexp:/etc/postfix/header_checks + +# FAST ETRN SERVICE +# +# Postfix maintains per-destination logfiles with information about +# deferred mail, so that mail can be flushed quickly with the SMTP +# "ETRN domain.tld" command, or by executing "sendmail -qRdomain.tld". +# See the ETRN_README document for a detailed description. +# +# The fast_flush_domains parameter controls what destinations are +# eligible for this service. By default, they are all domains that +# this server is willing to relay mail to. +# +#fast_flush_domains = $relay_domains + +# SHOW SOFTWARE VERSION OR NOT +# +# The smtpd_banner parameter specifies the text that follows the 220 +# code in the SMTP server's greeting banner. Some people like to see +# the mail version advertised. By default, Postfix shows no version. +# +# You MUST specify $myhostname at the start of the text. That is an +# RFC requirement. Postfix itself does not care. +# +#smtpd_banner = $myhostname ESMTP $mail_name +#smtpd_banner = $myhostname ESMTP $mail_name ($mail_version) +smtpd_banner = $myhostname ESMTP $mail_name (Debian/GNU) + + +# PARALLEL DELIVERY TO THE SAME DESTINATION +# +# How many parallel deliveries to the same user or domain? With local +# delivery, it does not make sense to do massively parallel delivery +# to the same user, because mailbox updates must happen sequentially, +# and expensive pipelines in .forward files can cause disasters when +# too many are run at the same time. With SMTP deliveries, 10 +# simultaneous connections to the same domain could be sufficient to +# raise eyebrows. +# +# Each message delivery transport has its XXX_destination_concurrency_limit +# parameter. The default is $default_destination_concurrency_limit for +# most delivery transports. For the local delivery agent the default is 2. + +#local_destination_concurrency_limit = 2 +#default_destination_concurrency_limit = 20 + +# DEBUGGING CONTROL +# +# The debug_peer_level parameter specifies the increment in verbose +# logging level when an SMTP client or server host name or address +# matches a pattern in the debug_peer_list parameter. +# +#debug_peer_level = 2 + +# The debug_peer_list parameter specifies an optional list of domain +# or network patterns, /file/name patterns or type:name tables. When +# an SMTP client or server host name or address matches a pattern, +# increase the verbose logging level by the amount specified in the +# debug_peer_level parameter. +# +#debug_peer_list = 127.0.0.1 +#debug_peer_list = some.domain + +# The debugger_command specifies the external command that is executed +# when a Postfix daemon program is run with the -D option. +# +# Use "command .. & sleep 5" so that the debugger can attach before +# the process marches on. If you use an X-based debugger, be sure to +# set up your XAUTHORITY environment variable before starting Postfix. +# +debugger_command = + PATH=/bin:/usr/bin:/usr/local/bin:/usr/X11R6/bin + ddd $daemon_directory/$process_name $process_id & sleep 5 + +# If you can't use X, use this to capture the call stack when a +# daemon crashes. The result is in a file in the configuration +# directory, and is named after the process name and the process ID. +# +# debugger_command = +# PATH=/bin:/usr/bin:/usr/local/bin; export PATH; (echo cont; +# echo where) | gdb $daemon_directory/$process_name $process_id 2>&1 +# >$config_directory/$process_name.$process_id.log & sleep 5 +# +# Another possibility is to run gdb under a detached screen session. +# To attach to the screen sesssion, su root and run "screen -r +# " where uniquely matches one of the detached +# sessions (from "screen -list"). +# +# debugger_command = +# PATH=/bin:/usr/bin:/sbin:/usr/sbin; export PATH; screen +# -dmS $process_name gdb $daemon_directory/$process_name +# $process_id & sleep 1 + +# INSTALL-TIME CONFIGURATION INFORMATION +# +# The following parameters are used when installing a new Postfix version. +# +# sendmail_path: The full pathname of the Postfix sendmail command. +# This is the Sendmail-compatible mail posting interface. +# +sendmail_path = /usr/sbin/sendmail + +# newaliases_path: The full pathname of the Postfix newaliases command. +# This is the Sendmail-compatible command to build alias databases. +# +newaliases_path = /usr/bin/newaliases + +# mailq_path: The full pathname of the Postfix mailq command. This +# is the Sendmail-compatible mail queue listing command. +# +mailq_path = /usr/bin/mailq + +# setgid_group: The group for mail submission and queue management +# commands. This must be a group name with a numerical group ID that +# is not shared with other accounts, not even with the Postfix account. +# +setgid_group = postdrop + +# html_directory: The location of the Postfix HTML documentation. +# +html_directory = no + +# manpage_directory: The location of the Postfix on-line manual pages. +# +manpage_directory = /usr/share/man + +# sample_directory: The location of the Postfix sample configuration files. +# This parameter is obsolete as of Postfix 2.1. +# +sample_directory = /usr/share/doc/postfix + +# readme_directory: The location of the Postfix README files. +# +readme_directory = /usr/share/doc/postfix +inet_protocols = ipv4 + +append_dot_mydomain = no +biff = no +smtpd_helo_required = yes +smtpd_recipient_restrictions = permit_mynetworks, + permit_sasl_authenticated, + reject_unauth_destination, + reject_unauth_pipelining, + reject_non_fqdn_recipient +smtpd_sender_restrictions = permit_mynetworks, + reject_sender_login_mismatch, + permit_sasl_authenticated, + reject_unknown_helo_hostname, + reject_unknown_recipient_domain, + reject_unknown_sender_domain +smtpd_client_restrictions = permit_mynetworks, + permit_sasl_authenticated, + reject_unknown_client_hostname + +# Postfix 2.10 requires this option. Postfix < 2.10 ignores this. +# The option is intentionally left empty. +smtpd_relay_restrictions = + +# Maximum size of Message in bytes (50MB) +message_size_limit = 52428800 + +## SASL Auth Settings +smtpd_sasl_auth_enable = yes +smtpd_sasl_local_domain = $myhostname +broken_sasl_auth_clients = yes +## Dovecot Settings for deliver, SASL Auth and virtual transport +smtpd_sasl_type = dovecot +virtual_transport = dovecot +dovecot_destination_recipient_limit = 1 +smtpd_sasl_path = private/auth + +# Virtual delivery settings +virtual_mailbox_base = / +virtual_mailbox_maps = mysql:/etc/postfix/mysql-virtual_mailbox_maps.cf +virtual_mailbox_domains = mysql:/etc/postfix/mysql-virtual_mailbox_domains.cf +virtual_alias_maps = mysql:/etc/postfix/mysql-virtual_alias_maps.cf +smtpd_sender_login_maps = mysql:/etc/postfix/mysql-virtual_sender_permissions.cf +virtual_uid_maps = static: +virtual_gid_maps = static: + +# Local delivery settings +local_transport = local +alias_maps = $alias_database + +# Default Mailbox size, is set to 0 which means unlimited! +mailbox_size_limit = 0 +virtual_mailbox_limit = 0 + +### TLS settings +### +## TLS for outgoing mails from the server to another server +#smtp_tls_security_level = may +#smtp_tls_note_starttls_offer = yes +## TLS for incoming connections (clients or other mail servers) +#smtpd_tls_security_level = may +#smtpd_tls_cert_file = /etc/ssl/server/.pem +#smtpd_tls_key_file = $smtpd_tls_cert_file +#smtpd_tls_CAfile = /etc/ssl/certs/ca-certificates.crt +#smtpd_tls_loglevel = 1 +#smtpd_tls_received_header = yes +]]> + + + //service[@type='smtp']/general/files[@index=0] + + + + + //service[@type='smtp']/general/commands[@index=3] + + + + + + + + + + + + + to select which instance is used (an alternative +# to -c ). The instance name is also added to Dovecot processes +# in ps output. +#instance_name = dovecot + +# Greeting message for clients. +#login_greeting = Dovecot ready. + +# Space separated list of trusted network ranges. Connections from these +# IPs are allowed to override their IP addresses and ports (for logging and +# for authentication checks). disable_plaintext_auth is also ignored for +# these networks. Typically you'd specify your IMAP proxy servers here. +#login_trusted_networks = + +# Space separated list of login access check sockets (e.g. tcpwrap) +#login_access_sockets = + +# With proxy_maybe=yes if proxy destination matches any of these IPs, don't do +# proxying. This isn't necessary normally, but may be useful if the destination +# IP is e.g. a load balancer's IP. +#auth_proxy_self = + +# Show more verbose process titles (in ps). Currently shows user name and +# IP address. Useful for seeing who are actually using the IMAP processes +# (eg. shared mailboxes or if same uid is used for multiple accounts). +#verbose_proctitle = no + +# Should all processes be killed when Dovecot master process shuts down. +# Setting this to "no" means that Dovecot can be upgraded without +# forcing existing client connections to close (although that could also be +# a problem if the upgrade is e.g. because of a security fix). +#shutdown_clients = yes + +# If non-zero, run mail commands via this many connections to doveadm server, +# instead of running them directly in the same process. +#doveadm_worker_count = 0 +# UNIX socket or host:port used for connecting to doveadm server +#doveadm_socket_path = doveadm-server + +# Space separated list of environment variables that are preserved on Dovecot +# startup and passed down to all of its child processes. You can also give +# key=value pairs to always set specific settings. +#import_environment = TZ + +## +## Dictionary server settings +## + +# Dictionary can be used to store key=value lists. This is used by several +# plugins. The dictionary can be accessed either directly or though a +# dictionary server. The following dict block maps dictionary names to URIs +# when the server is used. These can then be referenced using URIs in format +# "proxy::". + +dict { + #quota = mysql:/etc/dovecot/dovecot-dict-sql.conf.ext + #expire = sqlite:/etc/dovecot/dovecot-dict-sql.conf.ext +} + +# Most of the actual configuration gets included below. The filenames are +# first sorted by their ASCII value and parsed in that order. The 00-prefixes +# in filenames are intended to make it easier to understand the ordering. +!include conf.d/*.conf + +# A config file can also tried to be included without giving an error if +# it's not found: +!include_try local.conf +]]> + + + + dbname= user= password= + +# Default password scheme. +# +# List of supported schemes is in +# http://wiki2.dovecot.org/Authentication/PasswordSchemes +# +default_pass_scheme = CRYPT + +# passdb query to retrieve the password. It can return fields: +# password - The user's password. This field must be returned. +# user - user@domain from the database. Needed with case-insensitive lookups. +# username and domain - An alternative way to represent the "user" field. +# +# The "user" field is often necessary with case-insensitive lookups to avoid +# e.g. "name" and "nAme" logins creating two different mail directories. If +# your user and domain names are in separate fields, you can return "username" +# and "domain" fields instead of "user". +# +# The query can also return other fields which have a special meaning, see +# http://wiki2.dovecot.org/PasswordDatabase/ExtraFields +# +# Commonly used available substitutions (see http://wiki2.dovecot.org/Variables +# for full list): +# %u = entire user@domain +# %n = user part of user@domain +# %d = domain part of user@domain +# +# Note that these can be used only as input to SQL query. If the query outputs +# any of these substitutions, they're not touched. Otherwise it would be +# difficult to have eg. usernames containing '%' characters. +# +# Example: +# password_query = SELECT userid AS user, pw AS password \ +# FROM users WHERE userid = '%u' AND active = 'Y' +# +#password_query = \ +# SELECT username, domain, password \ +# FROM users WHERE username = '%n' AND domain = '%d' + +# userdb query to retrieve the user information. It can return fields: +# uid - System UID (overrides mail_uid setting) +# gid - System GID (overrides mail_gid setting) +# home - Home directory +# mail - Mail location (overrides mail_location setting) +# +# None of these are strictly required. If you use a single UID and GID, and +# home or mail directory fits to a template string, you could use userdb static +# instead. For a list of all fields that can be returned, see +# http://wiki2.dovecot.org/UserDatabase/ExtraFields +# +# Examples: +# user_query = SELECT home, uid, gid FROM users WHERE userid = '%u' +# user_query = SELECT dir AS home, user AS uid, group AS gid FROM users where userid = '%u' +# user_query = SELECT home, 501 AS uid, 501 AS gid FROM users WHERE userid = '%u' +# +#user_query = \ +# SELECT home, uid, gid \ +# FROM users WHERE username = '%n' AND domain = '%d' +user_query = SELECT CONCAT(homedir, maildir) AS home, CONCAT('maildir:', homedir, maildir) AS mail, uid, gid, CONCAT('*:storage=', quota, 'M') as quota_rule FROM mail_users WHERE (username = '%u' OR email = '%u') + +# If you wish to avoid two SQL lookups (passdb + userdb), you can use +# userdb prefetch instead of userdb sql in dovecot.conf. In that case you'll +# also have to return userdb fields in password_query prefixed with "userdb_" +# string. For example: +#password_query = \ +# SELECT userid AS user, password, \ +# home AS userdb_home, uid AS userdb_uid, gid AS userdb_gid \ +# FROM users WHERE userid = '%u' +password_query = SELECT username AS user, password_enc AS password, CONCAT(homedir, maildir) AS userdb_home, uid AS userdb_uid, gid AS userdb_gid, CONCAT('maildir:', homedir, maildir) AS userdb_mail, CONCAT('*:storage=', quota, 'M') as userdb_quota_rule FROM mail_users WHERE (username = '%u' OR email = '%u') AND ((imap = 1 AND '%Ls' = 'imap') OR (pop3 = 1 AND '%Ls' = 'pop3') OR '%Ls' = 'smtp' OR '%Ls' = 'sieve') + +# Query to get a list of all usernames. +#iterate_query = SELECT username AS user FROM users +]]> + + + + to characters. For example "#@/@" means +# that '#' and '/' characters are translated to '@'. +#auth_username_translation = + +# Username formatting before it's looked up from databases. You can use +# the standard variables here, eg. %Lu would lowercase the username, %n would +# drop away the domain if it was given, or "%n-AT-%d" would change the '@' into +# "-AT-". This translation is done after auth_username_translation changes. +#auth_username_format = %Lu + +# If you want to allow master users to log in by specifying the master +# username within the normal username string (ie. not using SASL mechanism's +# support for it), you can specify the separator character here. The format +# is then . UW-IMAP uses "*" as the +# separator, so that could be a good choice. +#auth_master_user_separator = + +# Username to use for users logging in with ANONYMOUS SASL mechanism +#auth_anonymous_username = anonymous + +# Maximum number of dovecot-auth worker processes. They're used to execute +# blocking passdb and userdb queries (eg. MySQL and PAM). They're +# automatically created and destroyed as needed. +#auth_worker_max_count = 30 + +# Host name to use in GSSAPI principal names. The default is to use the +# name returned by gethostname(). Use "$ALL" (with quotes) to allow all keytab +# entries. +#auth_gssapi_hostname = + +# Kerberos keytab to use for the GSSAPI mechanism. Will use the system +# default (usually /etc/krb5.keytab) if not specified. You may need to change +# the auth service to run as root to be able to read this file. +#auth_krb5_keytab = + +# Do NTLM and GSS-SPNEGO authentication using Samba's winbind daemon and +# ntlm_auth helper. +#auth_use_winbind = no + +# Path for Samba's ntlm_auth helper binary. +#auth_winbind_helper_path = /usr/bin/ntlm_auth + +# Time to delay before replying to failed authentications. +#auth_failure_delay = 2 secs + +# Require a valid SSL client certificate or the authentication fails. +#auth_ssl_require_client_cert = no + +# Take the username from client's SSL certificate, using +# X509_NAME_get_text_by_NID() which returns the subject's DN's +# CommonName. +#auth_ssl_username_from_cert = no + +# Space separated list of wanted authentication mechanisms: +# plain login digest-md5 cram-md5 ntlm rpa apop anonymous gssapi otp skey +# gss-spnego +# NOTE: See also disable_plaintext_auth setting. +auth_mechanisms = plain login + +## +## Password and user databases +## + +# +# Password database is used to verify user's password (and nothing more). +# You can have multiple passdbs and userdbs. This is useful if you want to +# allow both system users (/etc/passwd) and virtual users to login without +# duplicating the system users into virtual database. +# +# +# +# User database specifies where mails are located and what user/group IDs +# own them. For single-UID configuration use "static" userdb. +# +# + +#!include auth-deny.conf.ext +#!include auth-master.conf.ext + +#!include auth-system.conf.ext +!include auth-sql.conf.ext +#!include auth-ldap.conf.ext +#!include auth-passwdfile.conf.ext +#!include auth-checkpassword.conf.ext +#!include auth-vpopmail.conf.ext +#!include auth-static.conf.ext +]]> + + + + +# +mail_location = mbox:~/mail:INBOX=/var/mail/%u + +# If you need to set multiple mailbox locations or want to change default +# namespace settings, you can do it by defining namespace sections. +# +# You can have private, shared and public namespaces. Private namespaces +# are for user's personal mails. Shared namespaces are for accessing other +# users' mailboxes that have been shared. Public namespaces are for shared +# mailboxes that are managed by sysadmin. If you create any shared or public +# namespaces you'll typically want to enable ACL plugin also, otherwise all +# users can access all the shared mailboxes, assuming they have permissions +# on filesystem level to do so. +namespace inbox { + # Namespace type: private, shared or public + #type = private + + # Hierarchy separator to use. You should use the same separator for all + # namespaces or some clients get confused. '/' is usually a good one. + # The default however depends on the underlying mail storage format. + #separator = + + # Prefix required to access this namespace. This needs to be different for + # all namespaces. For example "Public/". + #prefix = + + # Physical location of the mailbox. This is in same format as + # mail_location, which is also the default for it. + #location = + + # There can be only one INBOX, and this setting defines which namespace + # has it. + inbox = yes + + # If namespace is hidden, it's not advertised to clients via NAMESPACE + # extension. You'll most likely also want to set list=no. This is mostly + # useful when converting from another server with different namespaces which + # you want to deprecate but still keep working. For example you can create + # hidden namespaces with prefixes "~/mail/", "~%u/mail/" and "mail/". + #hidden = no + + # Show the mailboxes under this namespace with LIST command. This makes the + # namespace visible for clients that don't support NAMESPACE extension. + # "children" value lists child mailboxes, but hides the namespace prefix. + #list = yes + + # Namespace handles its own subscriptions. If set to "no", the parent + # namespace handles them (empty prefix should always have this as "yes") + #subscriptions = yes +} + +# Example shared namespace configuration +#namespace { + #type = shared + #separator = / + + # Mailboxes are visible under "shared/user@domain/" + # %%n, %%d and %%u are expanded to the destination user. + #prefix = shared/%%u/ + + # Mail location for other users' mailboxes. Note that %variables and ~/ + # expands to the logged in user's data. %%n, %%d, %%u and %%h expand to the + # destination user's data. + #location = maildir:%%h/Maildir:INDEX=~/Maildir/shared/%%u + + # Use the default namespace for saving subscriptions. + #subscriptions = no + + # List the shared/ namespace only if there are visible shared mailboxes. + #list = children +#} +# Should shared INBOX be visible as "shared/user" or "shared/user/INBOX"? +#mail_shared_explicit_inbox = no + +# System user and group used to access mails. If you use multiple, userdb +# can override these by returning uid or gid fields. You can use either numbers +# or names. +#mail_uid = +#mail_gid = + +# Group to enable temporarily for privileged operations. Currently this is +# used only with INBOX when either its initial creation or dotlocking fails. +# Typically this is set to "mail" to give access to /var/mail. +#mail_privileged_group = + +# Grant access to these supplementary groups for mail processes. Typically +# these are used to set up access to shared mailboxes. Note that it may be +# dangerous to set these if users can create symlinks (e.g. if "mail" group is +# set here, ln -s /var/mail ~/mail/var could allow a user to delete others' +# mailboxes, or ln -s /secret/shared/box ~/mail/mybox would allow reading it). +mail_access_groups = vmail + +# Allow full filesystem access to clients. There's no access checks other than +# what the operating system does for the active UID/GID. It works with both +# maildir and mboxes, allowing you to prefix mailboxes names with eg. /path/ +# or ~user/. +#mail_full_filesystem_access = no + +# Dictionary for key=value mailbox attributes. Currently used by URLAUTH, but +# soon intended to be used by METADATA as well. +#mail_attribute_dict = + +## +## Mail processes +## + +# Don't use mmap() at all. This is required if you store indexes to shared +# filesystems (NFS or clustered filesystem). +#mmap_disable = no + +# Rely on O_EXCL to work when creating dotlock files. NFS supports O_EXCL +# since version 3, so this should be safe to use nowadays by default. +#dotlock_use_excl = yes + +# When to use fsync() or fdatasync() calls: +# optimized (default): Whenever necessary to avoid losing important data +# always: Useful with e.g. NFS when write()s are delayed +# never: Never use it (best performance, but crashes can lose data) +#mail_fsync = optimized + +# Locking method for index files. Alternatives are fcntl, flock and dotlock. +# Dotlocking uses some tricks which may create more disk I/O than other locking +# methods. NFS users: flock doesn't work, remember to change mmap_disable. +#lock_method = fcntl + +# Directory in which LDA/LMTP temporarily stores incoming mails >128 kB. +#mail_temp_dir = /tmp + +# Valid UID range for users, defaults to 500 and above. This is mostly +# to make sure that users can't log in as daemons or other system users. +# Note that denying root logins is hardcoded to dovecot binary and can't +# be done even if first_valid_uid is set to 0. +#first_valid_uid = 500 +#last_valid_uid = 0 + +# Valid GID range for users, defaults to non-root/wheel. Users having +# non-valid GID as primary group ID aren't allowed to log in. If user +# belongs to supplementary groups with non-valid GIDs, those groups are +# not set. +#first_valid_gid = 1 +#last_valid_gid = 0 + +# Maximum allowed length for mail keyword name. It's only forced when trying +# to create new keywords. +#mail_max_keyword_length = 50 + +# ':' separated list of directories under which chrooting is allowed for mail +# processes (ie. /var/mail will allow chrooting to /var/mail/foo/bar too). +# This setting doesn't affect login_chroot, mail_chroot or auth chroot +# settings. If this setting is empty, "/./" in home dirs are ignored. +# WARNING: Never add directories here which local users can modify, that +# may lead to root exploit. Usually this should be done only if you don't +# allow shell access for users. +#valid_chroot_dirs = + +# Default chroot directory for mail processes. This can be overridden for +# specific users in user database by giving /./ in user's home directory +# (eg. /home/./user chroots into /home). Note that usually there is no real +# need to do chrooting, Dovecot doesn't allow users to access files outside +# their mail directory anyway. If your home directories are prefixed with +# the chroot directory, append "/." to mail_chroot. +#mail_chroot = + +# UNIX socket path to master authentication server to find users. +# This is used by imap (for shared users) and lda. +#auth_socket_path = /var/run/dovecot/auth-userdb + +# Directory where to look up mail plugins. +#mail_plugin_dir = /usr/lib/dovecot/modules + +# Space separated list of plugins to load for all services. Plugins specific to +# IMAP, LDA, etc. are added to this list in their own .conf files. +#mail_plugins = + +## +## Mailbox handling optimizations +## + +# Mailbox list indexes can be used to optimize IMAP STATUS commands. They are +# also required for IMAP NOTIFY extension to be enabled. +#mailbox_list_index = no + +# The minimum number of mails in a mailbox before updates are done to cache +# file. This allows optimizing Dovecot's behavior to do less disk writes at +# the cost of more disk reads. +#mail_cache_min_mail_count = 0 + +# When IDLE command is running, mailbox is checked once in a while to see if +# there are any new mails or other changes. This setting defines the minimum +# time to wait between those checks. Dovecot can also use dnotify, inotify and +# kqueue to find out immediately when changes occur. +#mailbox_idle_check_interval = 30 secs + +# Save mails with CR+LF instead of plain LF. This makes sending those mails +# take less CPU, especially with sendfile() syscall with Linux and FreeBSD. +# But it also creates a bit more disk I/O which may just make it slower. +# Also note that if other software reads the mboxes/maildirs, they may handle +# the extra CRs wrong and cause problems. +#mail_save_crlf = no + +# Max number of mails to keep open and prefetch to memory. This only works with +# some mailbox formats and/or operating systems. +#mail_prefetch_count = 0 + +# How often to scan for stale temporary files and delete them (0 = never). +# These should exist only after Dovecot dies in the middle of saving mails. +#mail_temp_scan_interval = 1w + +## +## Maildir-specific settings +## + +# By default LIST command returns all entries in maildir beginning with a dot. +# Enabling this option makes Dovecot return only entries which are directories. +# This is done by stat()ing each entry, so it causes more disk I/O. +# (For systems setting struct dirent->d_type, this check is free and it's +# done always regardless of this setting) +#maildir_stat_dirs = no + +# When copying a message, do it with hard links whenever possible. This makes +# the performance much better, and it's unlikely to have any side effects. +#maildir_copy_with_hardlinks = yes + +# Assume Dovecot is the only MUA accessing Maildir: Scan cur/ directory only +# when its mtime changes unexpectedly or when we can't find the mail otherwise. +#maildir_very_dirty_syncs = no + +# If enabled, Dovecot doesn't use the S= in the Maildir filenames for +# getting the mail's physical size, except when recalculating Maildir++ quota. +# This can be useful in systems where a lot of the Maildir filenames have a +# broken size. The performance hit for enabling this is very small. +#maildir_broken_filename_sizes = no + +# Always move mails from new/ directory to cur/, even when the \Recent flags +# aren't being reset. +#maildir_empty_new = no + +## +## mbox-specific settings +## + +# Which locking methods to use for locking mbox. There are four available: +# dotlock: Create .lock file. This is the oldest and most NFS-safe +# solution. If you want to use /var/mail/ like directory, the users +# will need write access to that directory. +# dotlock_try: Same as dotlock, but if it fails because of permissions or +# because there isn't enough disk space, just skip it. +# fcntl : Use this if possible. Works with NFS too if lockd is used. +# flock : May not exist in all systems. Doesn't work with NFS. +# lockf : May not exist in all systems. Doesn't work with NFS. +# +# You can use multiple locking methods; if you do the order they're declared +# in is important to avoid deadlocks if other MTAs/MUAs are using multiple +# locking methods as well. Some operating systems don't allow using some of +# them simultaneously. +# +# The Debian value for mbox_write_locks differs from upstream Dovecot. It is +# changed to be compliant with Debian Policy (section 11.6) for NFS safety. +# Dovecot: mbox_write_locks = dotlock fcntl +# Debian: mbox_write_locks = fcntl dotlock +# +#mbox_read_locks = fcntl +#mbox_write_locks = fcntl dotlock + +# Maximum time to wait for lock (all of them) before aborting. +#mbox_lock_timeout = 5 mins + +# If dotlock exists but the mailbox isn't modified in any way, override the +# lock file after this much time. +#mbox_dotlock_change_timeout = 2 mins + +# When mbox changes unexpectedly we have to fully read it to find out what +# changed. If the mbox is large this can take a long time. Since the change +# is usually just a newly appended mail, it'd be faster to simply read the +# new mails. If this setting is enabled, Dovecot does this but still safely +# fallbacks to re-reading the whole mbox file whenever something in mbox isn't +# how it's expected to be. The only real downside to this setting is that if +# some other MUA changes message flags, Dovecot doesn't notice it immediately. +# Note that a full sync is done with SELECT, EXAMINE, EXPUNGE and CHECK +# commands. +#mbox_dirty_syncs = yes + +# Like mbox_dirty_syncs, but don't do full syncs even with SELECT, EXAMINE, +# EXPUNGE or CHECK commands. If this is set, mbox_dirty_syncs is ignored. +#mbox_very_dirty_syncs = no + +# Delay writing mbox headers until doing a full write sync (EXPUNGE and CHECK +# commands and when closing the mailbox). This is especially useful for POP3 +# where clients often delete all mails. The downside is that our changes +# aren't immediately visible to other MUAs. +#mbox_lazy_writes = yes + +# If mbox size is smaller than this (e.g. 100k), don't write index files. +# If an index file already exists it's still read, just not updated. +#mbox_min_index_size = 0 + +# Mail header selection algorithm to use for MD5 POP3 UIDLs when +# pop3_uidl_format=%m. For backwards compatibility we use apop3d inspired +# algorithm, but it fails if the first Received: header isn't unique in all +# mails. An alternative algorithm is "all" that selects all headers. +#mbox_md5 = apop3d + +## +## mdbox-specific settings +## + +# Maximum dbox file size until it's rotated. +#mdbox_rotate_size = 2M + +# Maximum dbox file age until it's rotated. Typically in days. Day begins +# from midnight, so 1d = today, 2d = yesterday, etc. 0 = check disabled. +#mdbox_rotate_interval = 0 + +# When creating new mdbox files, immediately preallocate their size to +# mdbox_rotate_size. This setting currently works only in Linux with some +# filesystems (ext4, xfs). +#mdbox_preallocate_space = no + +## +## Mail attachments +## + +# sdbox and mdbox support saving mail attachments to external files, which +# also allows single instance storage for them. Other backends don't support +# this for now. + +# Directory root where to store mail attachments. Disabled, if empty. +#mail_attachment_dir = + +# Attachments smaller than this aren't saved externally. It's also possible to +# write a plugin to disable saving specific attachments externally. +#mail_attachment_min_size = 128k + +# Filesystem backend to use for saving attachments: +# posix : No SiS done by Dovecot (but this might help FS's own deduplication) +# sis posix : SiS with immediate byte-by-byte comparison during saving +# sis-queue posix : SiS with delayed comparison and deduplication +#mail_attachment_fs = sis posix + +# Hash format to use in attachment filenames. You can add any text and +# variables: %{md4}, %{md5}, %{sha1}, %{sha256}, %{sha512}, %{size}. +# Variables can be truncated, e.g. %{sha256:80} returns only first 80 bits +#mail_attachment_hash = %{sha1} +]]> + + + + + #service_count = 1 + + # Number of processes to always keep waiting for more connections. + #process_min_avail = 0 + + # If you set service_count=0, you probably need to grow this. + #vsz_limit = $default_vsz_limit +} + +service pop3-login { + inet_listener pop3 { + #port = 110 + } + inet_listener pop3s { + #port = 995 + #ssl = yes + } +} + +service lmtp { + unix_listener lmtp { + #mode = 0666 + } + + # Create inet listener only if you can't use the above UNIX socket + #inet_listener lmtp { + # Avoid making LMTP visible for the entire internet + #address = + #port = + #} +} + +service imap { + # Most of the memory goes to mmap()ing files. You may need to increase this + # limit if you have huge mailboxes. + #vsz_limit = $default_vsz_limit + + # Max. number of IMAP processes (connections) + #process_limit = 1024 +} + +service pop3 { + # Max. number of POP3 processes (connections) + #process_limit = 1024 +} + +service auth { + # auth_socket_path points to this userdb socket by default. It's typically + # used by dovecot-lda, doveadm, possibly imap process, etc. Users that have + # full permissions to this socket are able to get a list of all usernames and + # get the results of everyone's userdb lookups. + # + # The default 0666 mode allows anyone to connect to the socket, but the + # userdb lookups will succeed only if the userdb returns an "uid" field that + # matches the caller process's UID. Also if caller's uid or gid matches the + # socket's uid or gid the lookup succeeds. Anything else causes a failure. + # + # To give the caller full permissions to lookup all users, set the mode to + # something else than 0666 and Dovecot lets the kernel enforce the + # permissions (e.g. 0777 allows everyone full permissions). + unix_listener auth-userdb { + #mode = 0666 + #user = + #group = + } + + # Postfix smtp-auth + unix_listener /var/spool/postfix/private/auth { + mode = 0660 + user = postfix + group = postfix + } + + # Exim4 smtp-auth + unix_listener auth-client { + mode = 0660 + user = mail + # group = Debian-exim + } + + # Auth process is run as this user. + #user = $default_internal_user +} + +service auth-worker { + # Auth worker process is run as root by default, so that it can access + # /etc/shadow. If this isn't necessary, the user should be changed to + # $default_internal_user. + #user = root +} + +service dict { + # If dict proxy is used, mail processes should have access to its socket. + # For example: mode=0660, group=vmail and global mail_access_groups=vmail + unix_listener dict { + #mode = 0600 + #user = + #group = + } +} +]]> + + + + . %d expands to recipient domain. +postmaster_address = postmaster@ + +# Hostname to use in various parts of sent mails (e.g. in Message-Id) and +# in LMTP replies. Default is the system's real hostname@domain. +#hostname = + +# If user is over quota, return with temporary failure instead of +# bouncing the mail. +#quota_full_tempfail = no + +# Binary to use for sending mails. +#sendmail_path = /usr/sbin/sendmail + +# If non-empty, send mails via this SMTP host[:port] instead of sendmail. +#submission_host = + +# Subject: header to use for rejection mails. You can use the same variables +# as for rejection_reason below. +#rejection_subject = Rejected: %s + +# Human readable error message for rejection mails. You can use variables: +# %n = CRLF, %r = reason, %s = original subject, %t = recipient +#rejection_reason = Your message to <%t> was automatically rejected:%n%r + +# Delimiter character between local-part and detail in email address. +#recipient_delimiter = + + +# Header where the original recipient address (SMTP's RCPT TO: address) is taken +# from if not available elsewhere. With dovecot-lda -a parameter overrides this. +# A commonly used header for this is X-Original-To. +#lda_original_recipient_header = + +# Should saving a mail to a nonexistent mailbox automatically create it? +#lda_mailbox_autocreate = no + +# Should automatically created mailboxes be also automatically subscribed? +#lda_mailbox_autosubscribe = no + +protocol lda { + # Space separated list of plugins to load (default is global mail_plugins). + mail_plugins = $mail_plugins quota sieve +} +]]> + + + + + + + + + #service_count = 1 + + # Number of processes to always keep waiting for more connections. + #process_min_avail = 0 + + # If you set service_count=0, you probably need to grow this. + #vsz_limit = 64M +#} + +#service managesieve { + # Max. number of ManageSieve processes (connections) + #process_limit = 1024 +#} + +# Service configuration + +protocol sieve { + # Maximum ManageSieve command line length in bytes. ManageSieve usually does + # not involve overly long command lines, so this setting will not normally + # need adjustment + #managesieve_max_line_length = 65536 + + # Maximum number of ManageSieve connections allowed for a user from each IP + # address. + # NOTE: The username is compared case-sensitively. + #mail_max_userip_connections = 10 + + # Space separated list of plugins to load (none known to be useful so far). + # Do NOT try to load IMAP plugins here. + #mail_plugins = + + # MANAGESIEVE logout format string: + # %i - total number of bytes read from client + # %o - total number of bytes sent to client + #managesieve_logout_format = bytes=%i/%o + + # To fool ManageSieve clients that are focused on CMU's timesieved you can + # specify the IMPLEMENTATION capability that Dovecot reports to clients. + # For example: 'Cyrus timsieved v2.2.13' + #managesieve_implementation_string = Dovecot Pigeonhole + + # Explicitly specify the SIEVE and NOTIFY capability reported by the server + # before login. If left unassigned these will be reported dynamically + # according to what the Sieve interpreter supports by default (after login + # this may differ depending on the user). + #managesieve_sieve_capability = + #managesieve_notify_capability = + + # The maximum number of compile errors that are returned to the client upon + # script upload or script verification. + #managesieve_max_compile_errors = 5 + + # Refer to 90-sieve.conf for script quota configuration and configuration of + # Sieve execution limits. +} +]]> + + + + = 2.1.4) : %v.%u +# Dovecot v0.99.x : %v.%u +# tpop3d : %Mf +# +# Note that Outlook 2003 seems to have problems with %v.%u format which was +# Dovecot's default, so if you're building a new server it would be a good +# idea to change this. %08Xu%08Xv should be pretty fail-safe. +# +#pop3_uidl_format = %08Xu%08Xv + +# Permanently save UIDLs sent to POP3 clients, so pop3_uidl_format changes +# won't change those UIDLs. Currently this works only with Maildir. +#pop3_save_uidl = no + +# What to do about duplicate UIDLs if they exist? +# allow: Show duplicates to clients. +# rename: Append a temporary -2, -3, etc. counter after the UIDL. +#pop3_uidl_duplicates = allow + +# This option changes POP3 behavior so that it's not possible to actually +# delete mails via POP3, only hide them from future POP3 sessions. The mails +# will still be counted towards user's quota until actually deleted via IMAP. +# Use e.g. "$POP3Deleted" as the value (it will be visible as IMAP keyword). +# Make sure you can legally archive mails before enabling this setting. +#pop3_deleted_flag = + +# POP3 logout format string: +# %i - total number of bytes read from client +# %o - total number of bytes sent to client +# %t - number of TOP commands +# %p - number of bytes sent to client as a result of TOP command +# %r - number of RETR commands +# %b - number of bytes sent to client as a result of RETR command +# %d - number of deleted messages +# %m - number of messages (before deletion) +# %s - mailbox size in bytes (before deletion) +# %u - old/new UIDL hash. may help finding out if UIDLs changed unexpectedly +#pop3_logout_format = top=%t/%p, retr=%r/%b, del=%d/%m, size=%s + +# Workarounds for various client bugs: +# outlook-no-nuls: +# Outlook and Outlook Express hang if mails contain NUL characters. +# This setting replaces them with 0x80 character. +# oe-ns-eoh: +# Outlook Express and Netscape Mail breaks if end of headers-line is +# missing. This option simply sends it if it's missing. +# The list is space-separated. +#pop3_client_workarounds = + +protocol pop3 { + # Space separated list of plugins to load (default is global mail_plugins). + #mail_plugins = $mail_plugins + + # Maximum number of POP3 connections allowed for a user from each IP address. + # NOTE: The username is compared case-sensitively. + #mail_max_userip_connections = 10 +} +]]> + + + + See sieve_before fore executing scripts before the user's personal + # script. + #sieve_default = /var/lib/dovecot/sieve/default.sieve + + # Directory for :personal include scripts for the include extension. This + # is also where the ManageSieve service stores the user's scripts. + sieve_dir = ~/sieve + + # Directory for :global include scripts for the include extension. + #sieve_global_dir = + + # Path to a script file or a directory containing script files that need to be + # executed before the user's script. If the path points to a directory, all + # the Sieve scripts contained therein (with the proper .sieve extension) are + # executed. The order of execution within a directory is determined by the + # file names, using a normal 8bit per-character comparison. Multiple script + # file or directory paths can be specified by appending an increasing number. + #sieve_before = + #sieve_before2 = + #sieve_before3 = (etc...) + + # Identical to sieve_before, only the specified scripts are executed after the + # user's script (only when keep is still in effect!). Multiple script file or + # directory paths can be specified by appending an increasing number. + #sieve_after = + #sieve_after2 = + #sieve_after2 = (etc...) + + # Which Sieve language extensions are available to users. By default, all + # supported extensions are available, except for deprecated extensions or + # those that are still under development. Some system administrators may want + # to disable certain Sieve extensions or enable those that are not available + # by default. This setting can use '+' and '-' to specify differences relative + # to the default. For example `sieve_extensions = +imapflags' will enable the + # deprecated imapflags extension in addition to all extensions were already + # enabled by default. + #sieve_extensions = +notify +imapflags + + # Which Sieve language extensions are ONLY available in global scripts. This + # can be used to restrict the use of certain Sieve extensions to administrator + # control, for instance when these extensions can cause security concerns. + # This setting has higher precedence than the `sieve_extensions' setting + # (above), meaning that the extensions enabled with this setting are never + # available to the user's personal script no matter what is specified for the + # `sieve_extensions' setting. The syntax of this setting is similar to the + # `sieve_extensions' setting, with the difference that extensions are + # enabled or disabled for exclusive use in global scripts. Currently, no + # extensions are marked as such by default. + #sieve_global_extensions = + + # The Pigeonhole Sieve interpreter can have plugins of its own. Using this + # setting, the used plugins can be specified. Check the Dovecot wiki + # (wiki2.dovecot.org) or the pigeonhole website + # (http://pigeonhole.dovecot.org) for available plugins. + # The sieve_extprograms plugin is included in this release. + #sieve_plugins = + + # The separator that is expected between the :user and :detail + # address parts introduced by the subaddress extension. This may + # also be a sequence of characters (e.g. '--'). The current + # implementation looks for the separator from the left of the + # localpart and uses the first one encountered. The :user part is + # left of the separator and the :detail part is right. This setting + # is also used by Dovecot's LMTP service. + #recipient_delimiter = + + + # The maximum size of a Sieve script. The compiler will refuse to compile any + # script larger than this limit. If set to 0, no limit on the script size is + # enforced. + #sieve_max_script_size = 1M + + # The maximum number of actions that can be performed during a single script + # execution. If set to 0, no limit on the total number of actions is enforced. + #sieve_max_actions = 32 + + # The maximum number of redirect actions that can be performed during a single + # script execution. If set to 0, no redirect actions are allowed. + #sieve_max_redirects = 4 + + # The maximum number of personal Sieve scripts a single user can have. If set + # to 0, no limit on the number of scripts is enforced. + # (Currently only relevant for ManageSieve) + #sieve_quota_max_scripts = 0 + + # The maximum amount of disk storage a single user's scripts may occupy. If + # set to 0, no limit on the used amount of disk storage is enforced. + # (Currently only relevant for ManageSieve) + #sieve_quota_max_storage = 0 +} +]]> + + + + + + + + + + //service[@type='mail']/general/installs[@index=1] + + //service[@type='mail']/general/files[@index=1] + + //service[@type='mail']/general/commands[@index=1] + + + + + + + + + + "]]> + "]]> + + + + FTP Server" +ServerType standalone +DeferWelcome off + +MultilineRFC2228 on +DefaultServer on +ShowSymlinks on + +TimeoutNoTransfer 600 +TimeoutStalled 600 +TimeoutIdle 1200 + +DisplayLogin welcome.msg +DisplayChdir .message true +ListOptions "-l" + +DenyFilter \*.*/ + +# Use this to jail all users in their homes +# DefaultRoot ~ + +# Users require a valid shell listed in /etc/shells to login. +# Use this directive to release that constrain. +# RequireValidShell off + +# Port 21 is the standard FTP port. +Port 21 + +# In some cases you have to specify passive ports range to by-pass +# firewall limitations. Ephemeral ports can be used for that, but +# feel free to use a more narrow range. +# PassivePorts 49152 65534 + +# If your host was NATted, this option is useful in order to +# allow passive tranfers to work. You have to use your public +# address and opening the passive ports used on your firewall as well. +# MasqueradeAddress 1.2.3.4 + +# This is useful for masquerading address with dynamic IPs: +# refresh any configured MasqueradeAddress directives every 8 hours + +# DynMasqRefresh 28800 + + +# To prevent DoS attacks, set the maximum number of child processes +# to 30. If you need to allow more than 30 concurrent connections +# at once, simply increase this value. Note that this ONLY works +# in standalone mode, in inetd mode you should use an inetd server +# that allows you to limit maximum number of processes per service +# (such as xinetd) +MaxInstances 30 + +# Set the user and group that the server normally runs at. +User proftpd +Group nogroup + +# Umask 022 is a good standard umask to prevent new files and dirs +# (second parm) from being group and world writable. +Umask 022 022 +# Normally, we want files to be overwriteable. +AllowOverwrite on + +# Uncomment this if you are using NIS or LDAP via NSS to retrieve passwords: +# PersistentPasswd off + +# This is required to use both PAM-based authentication and local passwords +# AuthOrder mod_auth_pam.c* mod_auth_unix.c + +# Be warned: use of this directive impacts CPU average load! +# Uncomment this if you like to see progress and transfer rate with ftpwho +# in downloads. That is not needed for uploads rates. +# +# UseSendFile off + +TransferLog /var/log/proftpd/xferlog +SystemLog /var/log/proftpd/proftpd.log + +# Logging onto /var/log/lastlog is enabled but set to off by default +#UseLastlog on + +# In order to keep log file dates consistent after chroot, use timezone info +# from /etc/localtime. If this is not set, and proftpd is configured to +# chroot (e.g. DefaultRoot or ), it will use the non-daylight +# savings timezone regardless of whether DST is in effect. +#SetEnv TZ :/etc/localtime + + +QuotaEngine on + + + +Ratios off + + + +# Delay engine reduces impact of the so-called Timing Attack described in +# http://www.securityfocus.com/bid/11430/discuss +# It is on by default. + +DelayEngine on + + + +ControlsEngine off +ControlsMaxClients 2 +ControlsLog /var/log/proftpd/controls.log +ControlsInterval 5 +ControlsSocket /var/run/proftpd/proftpd.sock + + + +AdminControlsEngine off + + +# +# Alternative authentication frameworks +# +#Include /etc/proftpd/ldap.conf +Include /etc/proftpd/sql.conf + +# +# This is used for FTPS connections +# +Include /etc/proftpd/tls.conf + +# +# Useful to keep VirtualHost/VirtualRoot directives separated +# +#Include /etc/proftpd/virtuals.conf + +# A basic anonymous configuration, no upload directories. + +# +# User ftp +# Group nogroup +# # We want clients to be able to login with "anonymous" as well as "ftp" +# UserAlias anonymous ftp +# # Cosmetic changes, all files belongs to ftp user +# DirFakeUser on ftp +# DirFakeGroup on ftp +# +# RequireValidShell off +# +# # Limit the maximum number of anonymous logins +# MaxClients 10 +# +# # We want 'welcome.msg' displayed at login, and '.message' displayed +# # in each newly chdired directory. +# DisplayLogin welcome.msg +# DisplayChdir .message +# +# # Limit WRITE everywhere in the anonymous chroot +# +# +# DenyAll +# +# +# +# # Uncomment this if you're brave. +# # +# # # Umask 022 is a good standard umask to prevent new files and dirs +# # # (second parm) from being group and world writable. +# # Umask 022 022 +# # +# # DenyAll +# # +# # +# # AllowAll +# # +# # +# +# + +# Include other custom configuration files +Include /etc/proftpd/conf.d/ +]]> + + + + + + + + + +DefaultRoot ~ +RequireValidShell off +AuthOrder mod_sql.c + +# +# Choose a SQL backend among MySQL or PostgreSQL. +# Both modules are loaded in default configuration, so you have to specify the backend +# or comment out the unused module in /etc/proftpd/modules.conf. +# Use 'mysql' or 'postgres' as possible values. +# +SQLBackend mysql +# +SQLEngine on +SQLAuthenticate on +# +# Use both a crypted or plaintext password +SQLAuthTypes Crypt + +SQLAuthenticate users* groups* + +# +# Connection +SQLConnectInfo @ +# +# Describes both users/groups tables +# +SQLUserInfo ftp_users username password uid gid homedir shell +SQLGroupInfo ftp_groups groupname gid members +# +SQLUserWhereClause "login_enabled = 'y'" + +SQLLog PASS login +SQLNamedQuery login UPDATE "last_login=now(), login_count=login_count+1 WHERE username='%u'" ftp_users + +SQLLog RETR download +SQLNamedQuery download UPDATE "down_count=down_count+1, down_bytes=down_bytes+%b WHERE username='%u'" ftp_users + +SQLLog STOR upload +SQLNamedQuery upload UPDATE "up_count=up_count+1, up_bytes=up_bytes+%b WHERE username='%u'" ftp_users + +QuotaEngine on +QuotaShowQuotas on +QuotaDisplayUnits Mb +QuotaLock /var/lock/ftpd.quotatab.lock +QuotaLimitTable sql:/get-quota-limit +QuotaTallyTable sql:/get-quota-tally/update-quota-tally/insert-quota-tally +SQLNamedQuery get-quota-limit SELECT "ftp_users.username AS name, ftp_quotalimits.quota_type, ftp_quotalimits.per_session, ftp_quotalimits.limit_type, panel_customers.diskspace*1024 AS bytes_in_avail, ftp_quotalimits.bytes_out_avail, ftp_quotalimits.bytes_xfer_avail, ftp_quotalimits.files_in_avail, ftp_quotalimits.files_out_avail, ftp_quotalimits.files_xfer_avail FROM ftp_users, ftp_quotalimits, panel_customers WHERE ftp_users.username = '%{0}' AND panel_customers.loginname = SUBSTRING_INDEX('%{0}', 'ftp', 1) AND quota_type ='%{1}'" +SQLNamedQuery get-quota-tally SELECT "name, quota_type, bytes_in_used,bytes_out_used, bytes_xfer_used, files_in_used, files_out_used,files_xfer_used FROM ftp_quotatallies WHERE name = '%{0}' AND quota_type = '%{1}'" +SQLNamedQuery update-quota-tally UPDATE "bytes_in_used = bytes_in_used + %{0}, bytes_out_used = bytes_out_used + %{1}, bytes_xfer_used = bytes_xfer_used + %{2}, files_in_used = files_in_used + %{3}, files_out_used= files_out_used + %{4}, files_xfer_used = files_xfer_used + %{5} WHERE name= '%{6}' AND quota_type = '%{7}'" ftp_quotatallies +SQLNamedQuery insert-quota-tally INSERT "%{0}, %{1}, %{2}, %{3}, %{4},%{5}, %{6}, %{7}" ftp_quotatallies + + +]]> + + + + +TLSEngine on +TLSLog /var/log/proftpd/tls.log +TLSProtocol TLSv1 TLSv1.1 TLSv1.2 +TLSRSACertificateFile /etc/ssl/certs/proftpd.crt +TLSRSACertificateKeyFile /etc/ssl/private/proftpd.key +TLSECCertificateFile /etc/ssl/certs/proftpd_ec.crt +TLSECCertificateKeyFile /etc/ssl/private/proftpd_ec.key +TLSOptions NoCertRequest NoSessionReuseRequired +TLSVerifyClient off + +# Are clients required to use FTP over TLS when talking to this server? +#TLSRequired on + +# Allow SSL/TLS renegotiations when the client requests them, but +# do not force the renegotations. Some clients do not support +# SSL/TLS renegotiations; when mod_tls forces a renegotiation, these +# clients will close the data connection, or there will be a timeout +# on an idle data connection. +# +#TLSRenegotiate required off + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +# Mandatory : user password. You must have a password. + +MYSQLPassword + + +# Mandatory : database to open. + +MYSQLDatabase + + +# Mandatory : how passwords are stored +# Valid values are : "cleartext", "crypt", "sha1", "md5" and "password" +# ("password" = MySQL password() function) +# You can also use "any" to try "crypt", "sha1", "md5" *and* "password" + +MYSQLCrypt any + + +# In the following directives, parts of the strings are replaced at +# run-time before performing queries : +# +# \L is replaced by the login of the user trying to authenticate. +# \I is replaced by the IP address the user connected to. +# \P is replaced by the port number the user connected to. +# \R is replaced by the IP address the user connected from. +# \D is replaced by the remote IP address, as a long decimal number. +# +# Very complex queries can be performed using these substitution strings, +# especially for virtual hosting. + + +# Query to execute in order to fetch the password + +MYSQLGetPW SELECT password FROM ftp_users WHERE username="\L" AND login_enabled="y" + + +# Query to execute in order to fetch the system user name or uid + +MYSQLGetUID SELECT uid FROM ftp_users WHERE username="\L" AND login_enabled="y" + + +# Optional : default UID - if set this overrides MYSQLGetUID + +#MYSQLDefaultUID 1000 + + +# Query to execute in order to fetch the system user group or gid + +MYSQLGetGID SELECT gid FROM ftp_users WHERE username="\L" AND login_enabled="y" + + +# Optional : default GID - if set this overrides MYSQLGetGID + +#MYSQLDefaultGID 1000 + + +# Query to execute in order to fetch the home directory + +MYSQLGetDir SELECT homedir FROM ftp_users WHERE username="\L" AND login_enabled="y" + + +# Optional : query to get the maximal number of files +# Pure-FTPd must have been compiled with virtual quotas support. + +# MySQLGetQTAFS SELECT QuotaFiles FROM users WHERE User='\L' + + +# Optional : query to get the maximal disk usage (virtual quotas) +# The number should be in Megabytes. +# Pure-FTPd must have been compiled with virtual quotas support. + +MySQLGetQTASZ SELECT panel_customers.diskspace/1024 AS QuotaSize FROM panel_customers, ftp_users WHERE username = "\L" AND panel_customers.loginname = SUBSTRING_INDEX('\L', 'ftp', 1) + + +# Optional : ratios. The server has to be compiled with ratio support. + +# MySQLGetRatioUL SELECT ULRatio FROM users WHERE User='\L' +# MySQLGetRatioDL SELECT DLRatio FROM users WHERE User='\L' + + +# Optional : bandwidth throttling. +# The server has to be compiled with throttling support. +# Values are in KB/s . + +# MySQLGetBandwidthUL SELECT ULBandwidth FROM users WHERE User='\L' +# MySQLGetBandwidthDL SELECT DLBandwidth FROM users WHERE User='\L' + +# Enable ~ expansion. NEVER ENABLE THIS BLINDLY UNLESS : +# 1) You know what you are doing. +# 2) Real and virtual users match. + +# MySQLForceTildeExpansion 1 + + +# If you're using a transactionnal storage engine, you can enable SQL +# transactions to avoid races. Leave this commented if you are using the +# traditional MyIsam engine. + +# MySQLTransactions On +]]> + + + + + + + + + + + + + + + + + + + + + + + scripts/froxlor_master_cronjob.php +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + *.log { + missingok + weekly + rotate 4 + compress + delaycompress + notifempty + create + sharedscripts + postrotate + > /dev/null 2>&1 || true + endscript +} +]]> + + + + + + + + + {{settings.system.mod_fcgid_ownvhost}} + + + + + + + + + + + + + + {{settings.system.webserver}} + + + + + + {{settings.system.webserver}} + + + + + {{settings.phpfpm.enabled_ownvhost}} + + {{settings.phpfpm.vhost_httpuser}} + + + + + + {{settings.system.webserver}} + + {{settings.phpfpm.enabled_ownvhost}} + + + + + + + + + + diff --git a/lng/english.lng.php b/lng/english.lng.php index 0830a2ef..6f81978a 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -1828,7 +1828,7 @@ $lng['serversettings']['panel_password_special_char_required']['description'] = $lng['serversettings']['panel_password_special_char']['title'] = 'Special characters list'; $lng['serversettings']['panel_password_special_char']['description'] = 'One of these characters is required if the above option is set.'; $lng['phpfpm']['use_mod_proxy']['title'] = 'Use mod_proxy / mod_proxy_fcgi'; -$lng['phpfpm']['use_mod_proxy']['description'] = 'Activate to use php-fpm via mod_proxy_fcgi. Requires at least apache-2.4.9'; +$lng['phpfpm']['use_mod_proxy']['description'] = 'Must be enabled when using Debian 9.x (Stretch). Activate to use php-fpm via mod_proxy_fcgi. Requires at least apache-2.4.9'; $lng['error']['no_phpinfo'] = 'Sorry, unable to read phpinfo()'; $lng['admin']['movetoadmin'] = 'Move customer'; @@ -1849,9 +1849,9 @@ $lng['usersettings']['custom_notes']['show'] = 'Show your notes on the dashboard $lng['error']['fcgidandphpfpmnogoodtogether'] = 'FCGID and PHP-FPM cannot be activated at the same time'; // Added in Froxlor 0.9.34 -$lng['admin']['configfiles']['legend'] = 'You are about to configure a service/daemon. The following legend explains the nomenclature.'; +$lng['admin']['configfiles']['legend'] = '

You are about to configure a service/daemon

'; $lng['admin']['configfiles']['commands'] = 'Commands: These commands are to be executed line by line as root-user in a shell. It is safe to copy the whole block and paste it into the shell.'; -$lng['admin']['configfiles']['files'] = 'Configfiles: This is an example of the contents of a configuration file. The commands before these textfields should open an editor with the target file. Just copy and paste the contents into the editor and save the file.

Please note: The MySQL-password has not been replaced for security reasons. Please replace "FROXLOR_MYSQL_PASSWORD" on your own. If you forgot your MySQL-password you\'ll find it in "lib/userdata.inc.php"'; +$lng['admin']['configfiles']['files'] = 'Configfiles: The commands before the textfields should open an editor with the target file. Just copy and paste the contents into the editor and save the file.
Please note: The MySQL-password has not been replaced for security reasons. Please replace "FROXLOR_MYSQL_PASSWORD" on your own or use the javascript form below to replace it on-site. If you forgot your MySQL-password you\'ll find it in "lib/userdata.inc.php"'; $lng['serversettings']['apache_itksupport']['title'] = 'Use modifications for Apache ITK-MPM'; $lng['serversettings']['apache_itksupport']['description'] = 'ATTENTION: use only if you acutally have apache itk-mpm enabled
otherwise your webserver will not be able to start'; $lng['integrity_check']['DatabaseCharset'] = 'Characterset of database (should be UTF-8)'; diff --git a/lng/german.lng.php b/lng/german.lng.php index 160ebb9a..f67f55f5 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -1553,7 +1553,7 @@ $lng['serversettings']['panel_password_special_char_required']['description'] = $lng['serversettings']['panel_password_special_char']['title'] = 'Sonderzeichen-Liste'; $lng['serversettings']['panel_password_special_char']['description'] = 'Mindestens eines dieser Sonderzeichen muss in dem Passwort vorkommen, sofern die Sonderzeichen-Option aktiviert ist.'; $lng['phpfpm']['use_mod_proxy']['title'] = 'Verwende mod_proxy / mod_proxy_fcgi'; -$lng['phpfpm']['use_mod_proxy']['description'] = 'Diese Option kann aktiviert werden, um php-fpm via mod_proxy_fcgi einzubinden. Dies setzt mindestens apache-2.4.9 voraus'; +$lng['phpfpm']['use_mod_proxy']['description'] = 'Muss gesetzt sein bei Debian 9.x (Stretch). Diese Option kann aktiviert werden, um php-fpm via mod_proxy_fcgi einzubinden. Dies setzt mindestens apache-2.4.9 voraus'; $lng['error']['no_phpinfo'] = 'Entschuldigung, es ist nicht möglich die phpinfo() auszulesen.'; $lng['admin']['movetoadmin'] = 'Kunde verschieben'; @@ -1574,9 +1574,9 @@ $lng['usersettings']['custom_notes']['show'] = 'Zeige die Notizen auf dem Dashbo $lng['error']['fcgidandphpfpmnogoodtogether'] = 'FCGID und PHP-FPM können nicht gleichzeitig aktiviert werden.'; // Added in Froxlor 0.9.34 -$lng['admin']['configfiles']['legend'] = 'Du konfigurierst nun einen Service/Daemon. Die folgende Legende zeigt unsere Nomenklatur.'; +$lng['admin']['configfiles']['legend'] = '

Du konfigurierst nun einen Service/Daemon.

'; $lng['admin']['configfiles']['commands'] = 'Kommandos: Die angezeigten Befehle müssen als Benutzer root in einer Shell ausgeführt werden. Es kann auch problemlos der ganze Block kopiert und in die Shell eingefügt werden.'; -$lng['admin']['configfiles']['files'] = 'Konfigurationsdateien: Dies ist der Inhalt einer Konfigurationsdatei. Der Befehl direkt vor dem Textfeld sollte einen Editor mit der Zieldatei öffnen. Der Inhalt kann nun einfach kopiert und in den Editor eingefügt und die Datei gespeichert werden.

Beachten Sie: Das MySQL-Passwort wurde aus Sicherheitsgründen nicht ersetzt. Bitte ersetzen Sie "FROXLOR_MYSQL_PASSWORD" manuell durch das entsprechende Passwort. Falls Sie es vergessen haben sollten, finden Sie es in der Datei "lib/userdata.inc.php".'; +$lng['admin']['configfiles']['files'] = 'Konfigurationsdateien: Der Befehl direkt vor dem Textfeld sollte einen Editor mit der Zieldatei öffnen. Der Inhalt kann nun einfach kopiert und in den Editor eingefügt und die Datei gespeichert werden.
Bitte beachten: Das MySQL-Passwort wurde aus Sicherheitsgründen nicht ersetzt. Bitte ersetze "FROXLOR_MYSQL_PASSWORD" manuell oder nutze das folgende Formular, um es temporär auf dieser Seite zu setzen. Falls das Passwort vergessen wurde, findet es sich in der Datei "lib/userdata.inc.php".'; $lng['serversettings']['apache_itksupport']['title'] = 'Anpassungen für Apache ITK-MPM verwenden'; $lng['serversettings']['apache_itksupport']['description'] = '
Achtung: Bitte nur verwenden, wenn wirklich Apache itk-mpm verwendet wird, ansonsten wird der Webserver nicht starten.
'; $lng['integrity_check']['DatabaseCharset'] = 'Characterset der Datenbank (sollte UTF-8 sein)'; diff --git a/templates/Sparkle/admin/configfiles/configfiles.tpl b/templates/Sparkle/admin/configfiles/configfiles.tpl index cab16fb1..d469aed3 100644 --- a/templates/Sparkle/admin/configfiles/configfiles.tpl +++ b/templates/Sparkle/admin/configfiles/configfiles.tpl @@ -11,25 +11,15 @@ $header
-

{$lng['admin']['configfiles']['legend']}


+

{$lng['admin']['configfiles']['legend']}

+

{$lng['admin']['configfiles']['commands']}

+

{$lng['admin']['configfiles']['files']}

- {$lng['admin']['configfiles']['commands']}
- -


-

- {$lng['admin']['configfiles']['files']}
- -

FROXLOR_MYSQL_PASSWORD:
+

From 6e651200ca5c6168aee1f6027e4256a60f64e588 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 11 Jan 2018 13:04:38 +0100 Subject: [PATCH 0377/1335] Make php.ini flag/value possibilities dynamic (settings) Signed-off-by: Michael Kaufmann (d00p) --- actions/admin/settings/136.phpfpm.php | 34 ++++- install/froxlor.sql | 98 ++++++++++++++- .../updates/froxlor/0.9/update_0.9.inc.php | 104 +++++++++++++++ .../phpinterface/class.phpinterface_fpm.php | 118 ++---------------- lib/version.inc.php | 2 +- lng/english.lng.php | 4 + lng/german.lng.php | 4 + 7 files changed, 255 insertions(+), 109 deletions(-) diff --git a/actions/admin/settings/136.phpfpm.php b/actions/admin/settings/136.phpfpm.php index 13e733ae..a62f0c2f 100644 --- a/actions/admin/settings/136.phpfpm.php +++ b/actions/admin/settings/136.phpfpm.php @@ -86,7 +86,39 @@ return array( 'default' => false, 'visible' => Settings::Get('system.apache24'), 'save_method' => 'storeSettingField' - ) + ), + 'system_phpfpm_ini_flags' => array( + 'label' => $lng['phpfpm']['ini_flags'], + 'settinggroup' => 'phpfpm', + 'varname' => 'ini_flags', + 'type' => 'text', + 'default' => '', + 'save_method' => 'storeSettingField' + ), + 'system_phpfpm_ini_values' => array( + 'label' => $lng['phpfpm']['ini_values'], + 'settinggroup' => 'phpfpm', + 'varname' => 'ini_values', + 'type' => 'text', + 'default' => '', + 'save_method' => 'storeSettingField' + ), + 'system_phpfpm_ini_admin_flags' => array( + 'label' => $lng['phpfpm']['ini_admin_flags'], + 'settinggroup' => 'phpfpm', + 'varname' => 'ini_admin_flags', + 'type' => 'text', + 'default' => '', + 'save_method' => 'storeSettingField' + ), + 'system_phpfpm_ini_admin_values' => array( + 'label' => $lng['phpfpm']['ini_admin_values'], + 'settinggroup' => 'phpfpm', + 'varname' => 'ini_admin_values', + 'type' => 'text', + 'default' => '', + 'save_method' => 'storeSettingField' + ) ), ), ), diff --git a/install/froxlor.sql b/install/froxlor.sql index cbcae082..fd762195 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -423,6 +423,102 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES ('phpfpm', 'vhost_defaultini', '2'), ('phpfpm', 'fastcgi_ipcdir', '/var/lib/apache2/fastcgi/'), ('phpfpm', 'use_mod_proxy', '0'), + ('phpfpm', 'ini_flags', 'asp_tags +display_errors +display_startup_errors +html_errors +log_errors +magic_quotes_gpc +magic_quotes_runtime +magic_quotes_sybase +mail.add_x_header +session.cookie_secure +session.use_cookies +short_open_tag +track_errors +xmlrpc_errors +suhosin.simulation +suhosin.session.encrypt +suhosin.session.cryptua +suhosin.session.cryptdocroot +suhosin.cookie.encrypt +suhosin.cookie.cryptua +suhosin.cookie.cryptdocroot +suhosin.executor.disable_eval +mbstring.func_overload'), + ('phpfpm', 'ini_values', 'auto_append_file +auto_prepend_file +date.timezone +default_charset +error_reporting +include_path +log_errors_max_len +mail.log +max_execution_time +session.cookie_domain +session.cookie_lifetime +session.cookie_path +session.name +session.serialize_handler +upload_max_filesize +xmlrpc_error_number +session.auto_start +always_populate_raw_post_data +suhosin.session.cryptkey +suhosin.session.cryptraddr +suhosin.session.checkraddr +suhosin.cookie.cryptkey +suhosin.cookie.plainlist +suhosin.cookie.cryptraddr +suhosin.cookie.checkraddr +suhosin.executor.func.blacklist +suhosin.executor.eval.whitelist'), + ('phpfpm', 'ini_admin_flags', 'allow_call_time_pass_reference +allow_url_fopen +allow_url_include +auto_detect_line_endings +cgi.fix_pathinfo +cgi.force_redirect +enable_dl +expose_php +file_uploads +ignore_repeated_errors +ignore_repeated_source +log_errors +register_argc_argv +report_memleaks +opcache.enable +opcache.consistency_checks +opcache.dups_fix +opcache.load_comments +opcache.revalidate_path +opcache.save_comments +opcache.use_cwd +opcache.validate_timestamps +opcache.fast_shutdown'), + ('phpfpm', 'ini_admin_values', 'cgi.redirect_status_env +date.timezone +disable_classes +disable_functions +error_log +gpc_order +max_input_time +max_input_vars +memory_limit +open_basedir +output_buffering +post_max_size +precision +sendmail_path +session.gc_divisor +session.gc_probability +variables_order +opcache.log_verbosity_level +opcache.restrict_api +opcache.revalidate_freq +opcache.max_accelerated_files +opcache.memory_consumption +opcache.interned_strings_buffer'), ('nginx', 'fastcgiparams', '/etc/nginx/fastcgi_params'), ('system', 'lastaccountnumber', '0'), ('system', 'lastguid', '9999'), @@ -590,7 +686,7 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES ('panel', 'password_special_char', '!?<>§$%+#=@'), ('panel', 'customer_hide_options', ''), ('panel', 'version', '0.9.38.8'), - ('panel', 'db_version', '201801101'); + ('panel', 'db_version', '201801110'); DROP TABLE IF EXISTS `panel_tasks`; diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index 9891df2b..e9e1e39e 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -3761,3 +3761,107 @@ if (isDatabaseVersion('201801100')) { updateToDbVersion('201801101'); } + +if (isDatabaseVersion('201801101')) { + + showUpdateStep("Adding dynamic php-fpm php.ini settings"); + Settings::AddNew('phpfpm.ini_flags', 'asp_tags +display_errors +display_startup_errors +html_errors +log_errors +magic_quotes_gpc +magic_quotes_runtime +magic_quotes_sybase +mail.add_x_header +session.cookie_secure +session.use_cookies +short_open_tag +track_errors +xmlrpc_errors +suhosin.simulation +suhosin.session.encrypt +suhosin.session.cryptua +suhosin.session.cryptdocroot +suhosin.cookie.encrypt +suhosin.cookie.cryptua +suhosin.cookie.cryptdocroot +suhosin.executor.disable_eval +mbstring.func_overload'); + Settings::AddNew('phpfpm.ini_values', 'auto_append_file +auto_prepend_file +date.timezone +default_charset +error_reporting +include_path +log_errors_max_len +mail.log +max_execution_time +session.cookie_domain +session.cookie_lifetime +session.cookie_path +session.name +session.serialize_handler +upload_max_filesize +xmlrpc_error_number +session.auto_start +always_populate_raw_post_data +suhosin.session.cryptkey +suhosin.session.cryptraddr +suhosin.session.checkraddr +suhosin.cookie.cryptkey +suhosin.cookie.plainlist +suhosin.cookie.cryptraddr +suhosin.cookie.checkraddr +suhosin.executor.func.blacklist +suhosin.executor.eval.whitelist'); + Settings::AddNew('phpfpm.ini_admin_flags', 'allow_call_time_pass_reference +allow_url_fopen +allow_url_include +auto_detect_line_endings +cgi.fix_pathinfo +cgi.force_redirect +enable_dl +expose_php +file_uploads +ignore_repeated_errors +ignore_repeated_source +log_errors +register_argc_argv +report_memleaks +opcache.enable +opcache.consistency_checks +opcache.dups_fix +opcache.load_comments +opcache.revalidate_path +opcache.save_comments +opcache.use_cwd +opcache.validate_timestamps +opcache.fast_shutdown'); + Settings::AddNew('phpfpm.ini_admin_values', 'cgi.redirect_status_env +date.timezone +disable_classes +disable_functions +error_log +gpc_order +max_input_time +max_input_vars +memory_limit +open_basedir +output_buffering +post_max_size +precision +sendmail_path +session.gc_divisor +session.gc_probability +variables_order +opcache.log_verbosity_level +opcache.restrict_api +opcache.revalidate_freq +opcache.max_accelerated_files +opcache.memory_consumption +opcache.interned_strings_buffer'); + lastStepStatus(0); + + updateToDbVersion('201801110'); +} diff --git a/lib/classes/phpinterface/class.phpinterface_fpm.php b/lib/classes/phpinterface/class.phpinterface_fpm.php index 0509666a..f72a2410 100644 --- a/lib/classes/phpinterface/class.phpinterface_fpm.php +++ b/lib/classes/phpinterface/class.phpinterface_fpm.php @@ -48,112 +48,7 @@ class phpinterface_fpm * * @var array */ - private $_ini = array( - 'php_value' => array( - 'auto_append_file', - 'auto_prepend_file', - 'date.timezone', - 'default_charset', - 'error_reporting', - 'include_path', - 'log_errors_max_len', - 'mail.log', - 'max_execution_time', - 'session.cookie_domain', - 'session.cookie_lifetime', - 'session.cookie_path', - 'session.name', - 'session.serialize_handler', - 'upload_max_filesize', - 'xmlrpc_error_number', - 'session.auto_start', - 'always_populate_raw_post_data', - 'suhosin.session.cryptkey', - 'suhosin.session.cryptraddr', - 'suhosin.session.checkraddr', - 'suhosin.cookie.cryptkey', - 'suhosin.cookie.plainlist', - 'suhosin.cookie.cryptraddr', - 'suhosin.cookie.checkraddr', - 'suhosin.executor.func.blacklist', - 'suhosin.executor.eval.whitelist' - ), - 'php_flag' => array( - 'asp_tags', - 'display_errors', - 'display_startup_errors', - 'html_errors', - 'log_errors', - 'magic_quotes_gpc', - 'magic_quotes_runtime', - 'magic_quotes_sybase', - 'mail.add_x_header', - 'session.cookie_secure', - 'session.use_cookies', - 'short_open_tag', - 'track_errors', - 'xmlrpc_errors', - 'suhosin.simulation', - 'suhosin.session.encrypt', - 'suhosin.session.cryptua', - 'suhosin.session.cryptdocroot', - 'suhosin.cookie.encrypt', - 'suhosin.cookie.cryptua', - 'suhosin.cookie.cryptdocroot', - 'suhosin.executor.disable_eval', - 'mbstring.func_overload' - ), - 'php_admin_value' => array( - 'cgi.redirect_status_env', - 'date.timezone', - 'disable_classes', - 'disable_functions', - 'error_log', - 'gpc_order', - 'max_input_time', - 'max_input_vars', - 'memory_limit', - 'open_basedir', - 'output_buffering', - 'post_max_size', - 'precision', - 'sendmail_path', - 'session.gc_divisor', - 'session.gc_probability', - 'variables_order', - 'opcache.log_verbosity_level', - 'opcache.restrict_api', - 'opcache.revalidate_freq', - 'opcache.max_accelerated_files', - 'opcache.memory_consumption', - 'opcache.interned_strings_buffer' - ), - 'php_admin_flag' => array( - 'allow_call_time_pass_reference', - 'allow_url_fopen', - 'allow_url_include', - 'auto_detect_line_endings', - 'cgi.fix_pathinfo', - 'cgi.force_redirect', - 'enable_dl', - 'expose_php', - 'file_uploads', - 'ignore_repeated_errors', - 'ignore_repeated_source', - 'log_errors', - 'register_argc_argv', - 'report_memleaks', - 'opcache.enable', - 'opcache.consistency_checks', - 'opcache.dups_fix', - 'opcache.load_comments', - 'opcache.revalidate_path', - 'opcache.save_comments', - 'opcache.use_cwd', - 'opcache.validate_timestamps', - 'opcache.fast_shutdown' - ) - ); + private $_ini = array(); /** * main constructor @@ -165,6 +60,17 @@ class phpinterface_fpm } $this->_domain = $domain; $this->_readFpmConfig($domain['fpm_config_id']); + $this->_buildIniMapping(); + } + + private function _buildIniMapping() + { + $this->_ini = array( + 'php_flag' => explode("\n", Settings::Get('phpfpm.ini_flags')), + 'php_value' => explode("\n", Settings::Get('phpfpm.ini_values')), + 'php_admin_flag' => explode("\n", Settings::Get('phpfpm.ini_admin_flags')), + 'php_admin_value' => explode("\n", Settings::Get('phpfpm.ini_admin_values')) + ); } private function _readFpmConfig($fpm_config_id) diff --git a/lib/version.inc.php b/lib/version.inc.php index ac4adb0f..4f1da38b 100644 --- a/lib/version.inc.php +++ b/lib/version.inc.php @@ -19,7 +19,7 @@ $version = '0.9.38.8'; // Database version (YYYYMMDDC where C is a daily counter) -$dbversion = '201801101'; +$dbversion = '201801110'; // Distribution branding-tag (used for Debian etc.) $branding = ''; diff --git a/lng/english.lng.php b/lng/english.lng.php index 6f81978a..831893f2 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -2096,3 +2096,7 @@ $lng['serversettings']['ssl']['ssl_protocols']['title'] = 'Configure the TLS pro $lng['serversettings']['ssl']['ssl_protocols']['description'] = 'This is a list of ssl protocols that you want (or don\'t want) to use when using SSL. Notice: Some older browsers may not support the newest protcol versions.

Default value is:
TLSv1, TLSv1.2
'; $lng['serversettings']['phpfpm_settings']['limit_extensions']['title'] = 'Allowed extensions'; $lng['serversettings']['phpfpm_settings']['limit_extensions']['description'] = 'Limits the extensions of the main script FPM will allow to parse. This can prevent configuration mistakes on the web server side. You should only limit FPM to .php extensions to prevent malicious users to use other extensions to execute php code. Default value: .php'; +$lng['phpfpm']['ini_flags'] = 'Enter possible php_flags for php.ini. One entry per line'; +$lng['phpfpm']['ini_values'] = 'Enter possible php_values for php.ini. One entry per line'; +$lng['phpfpm']['ini_admin_flags'] = 'Enter possible php_admin_flags for php.ini. One entry per line'; +$lng['phpfpm']['ini_admin_values'] = 'Enter possible php_admin_values for php.ini. One entry per line'; diff --git a/lng/german.lng.php b/lng/german.lng.php index f67f55f5..9ccfa6da 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -1747,3 +1747,7 @@ $lng['serversettings']['ssl']['ssl_protocols']['title'] = 'SSL Protokollversion $lng['serversettings']['ssl']['ssl_protocols']['description'] = 'Dies ist eine Liste von SSL/TLS Protokollversionen die genutzt werden sollen (oder auch nicht genutzt werden sollen), wenn SSL verwendet wird. Hinweis: Ältere Browser sind möglicherweise nicht vollständig zum neusten Protokoll kompatibel.

Standard-Wert ist:
TLSv1, TLSv1.2
'; $lng['serversettings']['phpfpm_settings']['limit_extensions']['title'] = 'Erlaubte Dateiendungen'; $lng['serversettings']['phpfpm_settings']['limit_extensions']['description'] = 'Beschränkt die Dateierweiterungen des Haupt-Skripts, das FPM zu parsen erlaubt. Dies kann Konfigurationsfehler auf der Webserverseite verhindern. Sie sollten FPM nur auf .php Erweiterungen beschränken, um zu verhindern, dass bösartige Nutzter andere Erweiterungen verwenden, um PHP Code auszuführen. Standardwert: .php'; +$lng['phpfpm']['ini_flags'] = 'Mögliche php_flags für die php.ini. Pro Zeile eine Direktive'; +$lng['phpfpm']['ini_values'] = 'Mögliche php_values für die php.ini. Pro Zeile eine Direktive'; +$lng['phpfpm']['ini_admin_flags'] = 'Mögliche php_admin_flags für die php.ini. Pro Zeile eine Direktive'; +$lng['phpfpm']['ini_admin_values'] = 'Mögliche php_admin_values für die php.ini. Pro Zeile eine Direktive'; From 9a00a67f7147b194b6a9413cd540990b9414b0d8 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sat, 13 Jan 2018 11:33:20 +0100 Subject: [PATCH 0378/1335] apache2-suexec => apache2-suexec-pristine for debian stretch Signed-off-by: Michael Kaufmann (d00p) --- lib/configfiles/stretch.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/configfiles/stretch.xml b/lib/configfiles/stretch.xml index a425306a..52f5e48d 100644 --- a/lib/configfiles/stretch.xml +++ b/lib/configfiles/stretch.xml @@ -4529,7 +4529,7 @@ aliases: files - + {{settings.system.mod_fcgid_ownvhost}} @@ -4549,7 +4549,7 @@ aliases: files {{settings.system.webserver}} - + From d16a7b2089dbaef36cfe6d7704a0a0c92d0245fd Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 15 Jan 2018 07:45:50 +0100 Subject: [PATCH 0379/1335] on installation, set apache-2.4 as default if apache is detected; clearify that apache2 is 2.2; output complete folder where the userdata.inc.php file is to be put when necessary to avoid misunderstanding Signed-off-by: Michael Kaufmann (d00p) --- install/lib/class.FroxlorInstall.php | 2 +- install/lng/english.lng.php | 6 +++--- install/lng/french.lng.php | 4 ++-- install/lng/german.lng.php | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/install/lib/class.FroxlorInstall.php b/install/lib/class.FroxlorInstall.php index dc8cf74b..d253d59b 100644 --- a/install/lib/class.FroxlorInstall.php +++ b/install/lib/class.FroxlorInstall.php @@ -1208,7 +1208,7 @@ class FroxlorInstall $this->_data['webserver'] = $_POST['webserver']; } else { if (strtoupper(@php_sapi_name()) == "APACHE2HANDLER" || stristr($_SERVER['SERVER_SOFTWARE'], "apache/2")) { - $this->_data['webserver'] = 'apache2'; + $this->_data['webserver'] = 'apache24'; } elseif (substr(strtoupper(@php_sapi_name()), 0, 8) == "LIGHTTPD" || stristr($_SERVER['SERVER_SOFTWARE'], "lighttpd")) { $this->_data['webserver'] = 'lighttpd'; } elseif (substr(strtoupper(@php_sapi_name()), 0, 8) == "NGINX" || stristr($_SERVER['SERVER_SOFTWARE'], "nginx")) { diff --git a/install/lng/english.lng.php b/install/lng/english.lng.php index 7faf8b1f..409421a5 100644 --- a/install/lng/english.lng.php +++ b/install/lng/english.lng.php @@ -63,7 +63,7 @@ $lng['install']['serversettings'] = 'Server settings'; $lng['install']['servername'] = 'Server name (FQDN, no ip-address)'; $lng['install']['serverip'] = 'Server IP'; $lng['install']['webserver'] = 'Webserver'; -$lng['install']['apache2'] = 'Apache 2'; +$lng['install']['apache2'] = 'Apache 2.2'; $lng['install']['apache24'] = 'Apache 2.4'; $lng['install']['lighttpd'] = 'LigHTTPd'; $lng['install']['nginx'] = 'NGINX'; @@ -83,8 +83,8 @@ $lng['install']['changing_data'] = 'Adjusting settings...'; $lng['install']['creating_entries'] = 'Inserting new values...'; $lng['install']['adding_admin_user'] = 'Creating admin-account...'; $lng['install']['creating_configfile'] = 'Creating configfile...'; -$lng['install']['creating_configfile_temp'] = 'File was saved in /tmp/userdata.inc.php, please move to lib/.'; -$lng['install']['creating_configfile_failed'] = 'Could not create lib/userdata.inc.php, please create it manually with the following content:'; +$lng['install']['creating_configfile_temp'] = 'File was saved in /tmp/userdata.inc.php, please move to '.dirname(dirname(__DIR__)).'/lib/.'; +$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['click_here_to_refresh'] = 'Click here to check again'; diff --git a/install/lng/french.lng.php b/install/lng/french.lng.php index 925b6220..43d86530 100644 --- a/install/lng/french.lng.php +++ b/install/lng/french.lng.php @@ -79,8 +79,8 @@ $lng['install']['changing_data'] = 'Ajustement des paramètres...'; $lng['install']['creating_entries'] = 'Insertion des nouvelles valeurs...'; $lng['install']['adding_admin_user'] = 'Création du compte administrateur...'; $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 lib/.'; -$lng['install']['creating_configfile_failed'] = 'Impossible de créer lib/userdata.inc.php, merci de le créer manuellement avec le contenu suivant:'; +$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_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['click_here_to_refresh'] = 'Cliquez ici pour vérifier à nouveau'; diff --git a/install/lng/german.lng.php b/install/lng/german.lng.php index c8e8b095..02dd9b8d 100644 --- a/install/lng/german.lng.php +++ b/install/lng/german.lng.php @@ -83,8 +83,8 @@ $lng['install']['changing_data'] = 'Einstellungen anpassen...'; $lng['install']['creating_entries'] = 'Trage neue Werte ein...'; $lng['install']['adding_admin_user'] = 'Erstelle Admin-Benutzer...'; $lng['install']['creating_configfile'] = 'Erstelle Konfigurationsdatei...'; -$lng['install']['creating_configfile_temp'] = 'Datei wurde in /tmp/userdata.inc.php gespeichert, bitte nach lib/ verschieben.'; -$lng['install']['creating_configfile_failed'] = 'Konnte lib/userdata.inc.php nicht erstellen, bitte manuell mit folgendem Inhalt anlegen:'; +$lng['install']['creating_configfile_temp'] = 'Datei wurde in /tmp/userdata.inc.php gespeichert, bitte nach '.dirname(dirname(__DIR__)).'/lib/ verschieben.'; +$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['click_here_to_refresh'] = 'Hier klicken, um erneut zu prüfen'; From 52c7839b9ba48137cd22395af0b0b8db8a922d51 Mon Sep 17 00:00:00 2001 From: lonesomewalker Date: Tue, 16 Jan 2018 14:57:42 +0100 Subject: [PATCH 0380/1335] Update class.DnsEntry.php Klammerfehler bei Bind, PowerDNS braucht ja keinen Linesplit. --- lib/classes/dns/class.DnsEntry.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/classes/dns/class.DnsEntry.php b/lib/classes/dns/class.DnsEntry.php index 27f0dd8f..f4726e4d 100644 --- a/lib/classes/dns/class.DnsEntry.php +++ b/lib/classes/dns/class.DnsEntry.php @@ -52,7 +52,7 @@ class DnsEntry if (substr($_l, 0, 1) == '"') { $_l = substr($_l, 1); } - $_content = '( "' . $_l . '"' . PHP_EOL; + $_content = $_l . '"' . PHP_EOL; $_l = array_pop($_contentlines); // check for ending quote if (substr($_l, -1) == '"') { @@ -63,7 +63,7 @@ class DnsEntry $_content .= "\t\t\t\t" . '"' . $_cl . '"' . PHP_EOL; } // last line - $_content .= "\t\t\t\t" . '"'.$_l.'" )'; + $_content .= "\t\t\t\t" . '"'.$_l; } $result = $this->record . "\t" . $this->ttl . "\t" . $this->class . "\t" . $this->type . "\t" . (($this->priority >= 0 && ($this->type == 'MX' || $this->type == 'SRV')) ? $this->priority . "\t" : "") . $_content . PHP_EOL; return $result; From c3fb6f6a1c053c3f5f0b97899fba9137b69ad7ea Mon Sep 17 00:00:00 2001 From: Andreas Grundler Date: Thu, 18 Jan 2018 18:16:53 +0100 Subject: [PATCH 0381/1335] =?UTF-8?q?http2=20Konfiguration=20nur=20einf?= =?UTF-8?q?=C3=BCgen=20wenn=20http2=20in=20den=20Einstellungen=20aktiviert?= =?UTF-8?q?=20ist?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jobs/cron_tasks.inc.http.10.apache.php | 306 +++++++++--------- scripts/jobs/cron_tasks.inc.http.30.nginx.php | 2 +- 2 files changed, 154 insertions(+), 154 deletions(-) diff --git a/scripts/jobs/cron_tasks.inc.http.10.apache.php b/scripts/jobs/cron_tasks.inc.http.10.apache.php index 55f230d0..9fcf71d8 100644 --- a/scripts/jobs/cron_tasks.inc.http.10.apache.php +++ b/scripts/jobs/cron_tasks.inc.http.10.apache.php @@ -16,7 +16,7 @@ if (! defined('MASTER_CRONJOB')) * @author Froxlor team (2010-) * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt * @package Cron - * + * */ require_once (dirname(__FILE__) . '/../classes/class.HttpConfigBase.php'); @@ -91,13 +91,13 @@ class apache extends HttpConfigBase $vhosts_folder = makeCorrectDir(dirname(Settings::Get('system.apacheconf_vhost'))); } $vhosts_filename = makeCorrectFile($vhosts_folder . '/05_froxlor_dirfix_nofcgid.conf'); - + if (! isset($this->virtualhosts_data[$vhosts_filename])) { $this->virtualhosts_data[$vhosts_filename] = ''; } - + $this->virtualhosts_data[$vhosts_filename] .= ' ' . "\n"; - + // check for custom values, see #1638 $custom_opts = Settings::Get('system.apacheglobaldiropt'); if (! empty($custom_opts)) { @@ -113,7 +113,7 @@ class apache extends HttpConfigBase } } $this->virtualhosts_data[$vhosts_filename] .= ' ' . "\n"; - + $ocsp_cache_filename = makeCorrectFile($vhosts_folder . '/03_froxlor_ocsp_cache.conf'); if (Settings::Get('system.use_ssl') == '1' && Settings::Get('system.apache24') == 1) { $this->virtualhosts_data[$ocsp_cache_filename] = 'SSLStaplingCache ' . Settings::Get('system.apache24_ocsp_cache_path') . "\n"; @@ -137,13 +137,13 @@ class apache extends HttpConfigBase } else { $vhosts_folder = makeCorrectDir(dirname(Settings::Get('system.apacheconf_vhost'))); } - + $vhosts_filename = makeCorrectFile($vhosts_folder . '/05_froxlor_default_errorhandler.conf'); - + if (! isset($this->virtualhosts_data[$vhosts_filename])) { $this->virtualhosts_data[$vhosts_filename] = ''; } - + $statusCodes = array( '401', '403', @@ -167,26 +167,26 @@ class apache extends HttpConfigBase public function createIpPort() { $result_ipsandports_stmt = Database::query("SELECT * FROM `" . TABLE_PANEL_IPSANDPORTS . "` ORDER BY `ip` ASC, `port` ASC"); - + while ($row_ipsandports = $result_ipsandports_stmt->fetch(PDO::FETCH_ASSOC)) { if (filter_var($row_ipsandports['ip'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { $ipport = '[' . $row_ipsandports['ip'] . ']:' . $row_ipsandports['port']; } else { $ipport = $row_ipsandports['ip'] . ':' . $row_ipsandports['port']; } - + $this->logger->logAction(CRON_ACTION, LOG_INFO, 'apache::createIpPort: creating ip/port settings for ' . $ipport); $vhosts_filename = makeCorrectFile(Settings::Get('system.apacheconf_vhost') . '/10_froxlor_ipandport_' . trim(str_replace(':', '.', $row_ipsandports['ip']), '.') . '.' . $row_ipsandports['port'] . '.conf'); - + if (! isset($this->virtualhosts_data[$vhosts_filename])) { $this->virtualhosts_data[$vhosts_filename] = ''; } - + if ($row_ipsandports['listen_statement'] == '1') { $this->virtualhosts_data[$vhosts_filename] .= 'Listen ' . $ipport . "\n"; $this->logger->logAction(CRON_ACTION, LOG_DEBUG, $ipport . ' :: inserted listen-statement'); } - + if ($row_ipsandports['namevirtualhost_statement'] == '1') { // >=apache-2.4 enabled? if (Settings::Get('system.apache24') == '1') { @@ -196,22 +196,22 @@ class apache extends HttpConfigBase $this->logger->logAction(CRON_ACTION, LOG_DEBUG, $ipport . ' :: inserted namevirtualhost-statement'); } } - + if ($row_ipsandports['vhostcontainer'] == '1') { - + $without_vhost = $this->virtualhosts_data[$vhosts_filename]; $close_vhost = true; - + $this->virtualhosts_data[$vhosts_filename] .= '' . "\n"; - + $mypath = $this->getMyPath($row_ipsandports); - + $this->virtualhosts_data[$vhosts_filename] .= 'DocumentRoot "' . $mypath . '"' . "\n"; - + if ($row_ipsandports['vhostcontainer_servername_statement'] == '1') { $this->virtualhosts_data[$vhosts_filename] .= ' ServerName ' . Settings::Get('system.hostname') . "\n"; } - + $is_redirect = false; // check for SSL redirect if ($row_ipsandports['ssl'] == '0' && Settings::Get('system.le_froxlor_redirect') == '1') { @@ -223,11 +223,11 @@ class apache extends HttpConfigBase $is_redirect = false; } else { $_sslport = $this->checkAlternativeSslPort(); - + $mypath = 'https://' . Settings::Get('system.hostname') . $_sslport . '/'; $code = '301'; $modrew_red = ' [R=' . $code . ';L,NE]'; - + // redirect everything, not only root-directory, #541 $this->virtualhosts_data[$vhosts_filename] .= ' ' . "\n"; $this->virtualhosts_data[$vhosts_filename] .= ' RewriteEngine On' . "\n"; @@ -242,7 +242,7 @@ class apache extends HttpConfigBase $this->virtualhosts_data[$vhosts_filename] .= ' ' . "\n"; } } - + if (! $is_redirect) { // create fcgid -Part (starter is created in apache_fcgid) if (Settings::Get('system.mod_fcgid_ownvhost') == '1' && Settings::Get('system.mod_fcgid') == '1') { @@ -266,7 +266,7 @@ class apache extends HttpConfigBase ); $php = new phpinterface($domain); $phpconfig = $php->getPhpConfig(Settings::Get('system.mod_fcgid_defaultini_ownvhost')); - + $starter_filename = makeCorrectFile($configdir . '/php-fcgi-starter'); $this->virtualhosts_data[$vhosts_filename] .= ' SuexecUserGroup "' . Settings::Get('system.mod_fcgid_httpuser') . '" "' . Settings::Get('system.mod_fcgid_httpgroup') . '"' . "\n"; $this->virtualhosts_data[$vhosts_filename] .= ' ' . "\n"; @@ -317,14 +317,14 @@ class apache extends HttpConfigBase 'documentroot' => $mypath, 'fpm_config_id' => isset($fpm_config['id']) ? $fpm_config['id'] : 1 ); - + $php = new phpinterface($domain); $phpconfig = $php->getPhpConfig(Settings::Get('phpfpm.vhost_defaultini')); $srvName = substr(md5($ipport), 0, 4) . '.fpm.external'; if ($row_ipsandports['ssl']) { $srvName = substr(md5($ipport), 0, 4) . '.ssl-fpm.external'; } - + // mod_proxy stuff for apache-2.4 if (Settings::Get('system.apache24') == '1' && Settings::Get('phpfpm.use_mod_proxy') == '1') { $filesmatch = $phpconfig['fpm_settings']['limit_extensions']; @@ -401,7 +401,7 @@ class apache extends HttpConfigBase 'documentroot' => $mypath ); } - + /** * dirprotection, see #72 * @@ -410,36 +410,36 @@ class apache extends HttpConfigBase * $this->virtualhosts_data[$vhosts_filename] .= "\t\tAllow from all\n"; * $this->virtualhosts_data[$vhosts_filename] .= "\t\tOptions -Indexes\n"; * $this->virtualhosts_data[$vhosts_filename] .= "\t\n"; - * + * * $this->virtualhosts_data[$vhosts_filename] .= "\t\n"; * $this->virtualhosts_data[$vhosts_filename] .= "\t\tOrder Deny,Allow\n"; * $this->virtualhosts_data[$vhosts_filename] .= "\t\tDeny from All\n"; * $this->virtualhosts_data[$vhosts_filename] .= "\t\n"; * end of dirprotection */ - + if ($row_ipsandports['specialsettings'] != '') { $this->virtualhosts_data[$vhosts_filename] .= $this->processSpecialConfigTemplate($row_ipsandports['specialsettings'], $domain, $row_ipsandports['ip'], $row_ipsandports['port'], $row_ipsandports['ssl'] == '1') . "\n"; } - + if ($row_ipsandports['ssl'] == '1' && Settings::Get('system.use_ssl') == '1') { if ($row_ipsandports['ssl_cert_file'] == '') { $row_ipsandports['ssl_cert_file'] = Settings::Get('system.ssl_cert_file'); } - + if ($row_ipsandports['ssl_key_file'] == '') { $row_ipsandports['ssl_key_file'] = Settings::Get('system.ssl_key_file'); } - + if ($row_ipsandports['ssl_ca_file'] == '') { $row_ipsandports['ssl_ca_file'] = Settings::Get('system.ssl_ca_file'); } - + // #418 if ($row_ipsandports['ssl_cert_chainfile'] == '') { $row_ipsandports['ssl_cert_chainfile'] = Settings::Get('system.ssl_cert_chainfile'); } - + $domain = array( 'id' => 0, 'domain' => Settings::Get('system.hostname'), @@ -448,26 +448,26 @@ class apache extends HttpConfigBase 'documentroot' => $mypath, 'parentdomainid' => 0 ); - + // override corresponding array values $domain['ssl_cert_file'] = $row_ipsandports['ssl_cert_file']; $domain['ssl_key_file'] = $row_ipsandports['ssl_key_file']; $domain['ssl_ca_file'] = $row_ipsandports['ssl_ca_file']; $domain['ssl_cert_chainfile'] = $row_ipsandports['ssl_cert_chainfile']; - + // SSL STUFF $dssl = new DomainSSL(); // this sets the ssl-related array-indices in the $domain array // if the domain has customer-defined ssl-certificates $dssl->setDomainSSLFilesArray($domain); - + if ($domain['ssl_cert_file'] != '') { - + // check for existence, #1485 if (! file_exists($domain['ssl_cert_file'])) { $this->logger->logAction(CRON_ACTION, LOG_ERR, $ipport . ' :: certificate file "' . $domain['ssl_cert_file'] . '" does not exist! Cannot create ssl-directives'); } else { - + $this->virtualhosts_data[$vhosts_filename] .= ' SSLEngine On' . "\n"; $this->virtualhosts_data[$vhosts_filename] .= ' SSLProtocol -ALL +' . str_replace(","," +", Settings::Get('system.ssl_protocols')) . "\n"; if (Settings::Get('system.apache24') == '1') { @@ -481,7 +481,7 @@ class apache extends HttpConfigBase $this->virtualhosts_data[$vhosts_filename] .= ' SSLCipherSuite ' . Settings::Get('system.ssl_cipher_list') . "\n"; $this->virtualhosts_data[$vhosts_filename] .= ' SSLVerifyDepth 10' . "\n"; $this->virtualhosts_data[$vhosts_filename] .= ' SSLCertificateFile ' . makeCorrectFile($domain['ssl_cert_file']) . "\n"; - + if ($domain['ssl_key_file'] != '') { // check for existence, #1485 if (! file_exists($domain['ssl_key_file'])) { @@ -490,7 +490,7 @@ class apache extends HttpConfigBase $this->virtualhosts_data[$vhosts_filename] .= ' SSLCertificateKeyFile ' . makeCorrectFile($domain['ssl_key_file']) . "\n"; } } - + if ($domain['ssl_ca_file'] != '') { // check for existence, #1485 if (! file_exists($domain['ssl_ca_file'])) { @@ -499,7 +499,7 @@ class apache extends HttpConfigBase $this->virtualhosts_data[$vhosts_filename] .= ' SSLCACertificateFile ' . makeCorrectFile($domain['ssl_ca_file']) . "\n"; } } - + // #418 if ($domain['ssl_cert_chainfile'] != '') { // check for existence, #1485 @@ -519,7 +519,7 @@ class apache extends HttpConfigBase $close_vhost = false; } } - + if ($close_vhost) { $this->virtualhosts_data[$vhosts_filename] .= '' . "\n"; } @@ -527,12 +527,12 @@ class apache extends HttpConfigBase } unset($vhosts_filename); } - + /** * bug #32 */ $this->_createStandardDirectoryEntry(); - + /** * bug #unknown-yet */ @@ -550,31 +550,31 @@ class apache extends HttpConfigBase protected function composePhpOptions($domain, $ssl_vhost = false) { $php_options_text = ''; - + if ($domain['phpenabled_customer'] == 1 && $domain['phpenabled_vhost'] == '1') { // This vHost has PHP enabled and we are using the regular mod_php $cmail = getCustomerDetail($domain['customerid'], 'email'); $php_options_text .= ' php_admin_value sendmail_path "/usr/sbin/sendmail -t -f ' . $cmail . '"' . PHP_EOL; - + if ($domain['openbasedir'] == '1') { if ($domain['openbasedir_path'] == '1' || strstr($domain['documentroot'], ":") !== false) { $_phpappendopenbasedir = appendOpenBasedirPath($domain['customerroot'], true); } else { $_phpappendopenbasedir = appendOpenBasedirPath($domain['documentroot'], true); } - + $_custom_openbasedir = explode(':', Settings::Get('system.phpappendopenbasedir')); foreach ($_custom_openbasedir as $cobd) { $_phpappendopenbasedir .= appendOpenBasedirPath($cobd); } - + $php_options_text .= ' php_admin_value open_basedir "' . $_phpappendopenbasedir . '"' . "\n"; } } else { $php_options_text .= ' # PHP is disabled for this vHost' . "\n"; $php_options_text .= ' php_flag engine off' . "\n"; } - + /** * check for apache-itk-support, #1400 * why is this here? Because it only works with mod_php @@ -584,7 +584,7 @@ class apache extends HttpConfigBase $php_options_text .= ' AssignUserID ' . $domain['loginname'] . ' ' . $domain['loginname'] . "\n"; $php_options_text .= ' ' . "\n"; } - + return $php_options_text; } @@ -597,18 +597,18 @@ class apache extends HttpConfigBase protected function getServerNames($domain) { $servernames_text = ' ServerName ' . $domain['domain'] . "\n"; - + $server_alias = ''; if ($domain['iswildcarddomain'] == '1') { $server_alias = '*.' . $domain['domain']; } elseif ($domain['wwwserveralias'] == '1') { $server_alias = 'www.' . $domain['domain']; } - + if (trim($server_alias) != '') { $servernames_text .= ' ServerAlias ' . $server_alias . "\n"; } - + $alias_domains_stmt = Database::prepare(" SELECT `domain`, `iswildcarddomain`, `wwwserveralias` FROM `" . TABLE_PANEL_DOMAINS . "` @@ -617,10 +617,10 @@ class apache extends HttpConfigBase Database::pexecute($alias_domains_stmt, array( 'domainid' => $domain['id'] )); - + while (($alias_domain = $alias_domains_stmt->fetch(PDO::FETCH_ASSOC)) !== false) { $server_alias = ' ServerAlias ' . $alias_domain['domain']; - + if ($alias_domain['iswildcarddomain'] == '1') { $server_alias .= ' *.' . $alias_domain['domain']; } else { @@ -628,10 +628,10 @@ class apache extends HttpConfigBase $server_alias .= ' www.' . $alias_domain['domain']; } } - + $servernames_text .= $server_alias . "\n"; } - + $servernames_text .= ' ServerAdmin ' . $domain['email'] . "\n"; return $servernames_text; } @@ -644,7 +644,7 @@ class apache extends HttpConfigBase $webroot_text = ''; $domain['customerroot'] = makeCorrectDir($domain['customerroot']); $domain['documentroot'] = makeCorrectDir($domain['documentroot']); - + if ($domain['deactivated'] == '1' && Settings::Get('system.deactivateddocroot') != '') { $webroot_text .= ' # Using docroot for deactivated users...' . "\n"; $webroot_text .= ' DocumentRoot "' . makeCorrectDir(Settings::Get('system.deactivateddocroot')) . "\"\n"; @@ -663,7 +663,7 @@ class apache extends HttpConfigBase $webroot_text .= ' DocumentRoot "' . $domain['documentroot'] . "\"\n"; $this->_deactivated = false; } - + return $webroot_text; } @@ -673,7 +673,7 @@ class apache extends HttpConfigBase protected function getStats($domain) { $stats_text = ''; - + if ($domain['speciallogfile'] == '1') { $statDomain = ($domain['parentdomainid'] == '0') ? $domain['domain'] : $domain['parentdomain']; if (Settings::Get('system.awstats_enabled') == '1') { @@ -699,7 +699,7 @@ class apache extends HttpConfigBase $stats_text .= ' Alias /awstats-icon "' . makeCorrectDir(Settings::Get('system.awstats_icons')) . '"' . "\n"; } } - + return $stats_text; } @@ -709,7 +709,7 @@ class apache extends HttpConfigBase protected function getLogfiles($domain) { $logfiles_text = ''; - + if ($domain['speciallogfile'] == '1') { if ($domain['parentdomainid'] == '0') { $speciallogfile = '-' . $domain['domain']; @@ -719,23 +719,23 @@ class apache extends HttpConfigBase } else { $speciallogfile = ''; } - + // The normal access/error - logging is enabled $error_log = makeCorrectFile(Settings::Get('system.logfiles_directory') . $domain['loginname'] . $speciallogfile . '-error.log'); // Create the logfile if it does not exist (fixes #46) touch($error_log); chown($error_log, Settings::Get('system.httpuser')); chgrp($error_log, Settings::Get('system.httpgroup')); - + $access_log = makeCorrectFile(Settings::Get('system.logfiles_directory') . $domain['loginname'] . $speciallogfile . '-access.log'); // Create the logfile if it does not exist (fixes #46) touch($access_log); chown($access_log, Settings::Get('system.httpuser')); chgrp($access_log, Settings::Get('system.httpgroup')); - + $logfiles_text .= ' ErrorLog "' . $error_log . "\"\n"; $logfiles_text .= ' CustomLog "' . $access_log . '" combined' . "\n"; - + if (Settings::Get('system.awstats_enabled') == '1') { if ((int) $domain['parentdomainid'] == 0) { // prepare the aliases and subdomains for stats config files @@ -748,25 +748,25 @@ class apache extends HttpConfigBase Database::pexecute($alias_domains_stmt, array( 'domainid' => $domain['id'] )); - + while (($alias_domain = $alias_domains_stmt->fetch(PDO::FETCH_ASSOC)) !== false) { - + $server_alias .= ' ' . $alias_domain['domain'] . ' '; - + if ($alias_domain['iswildcarddomain'] == '1') { $server_alias .= '*.' . $alias_domain['domain']; } elseif ($alias_domain['wwwserveralias'] == '1') { $server_alias .= 'www.' . $alias_domain['domain']; } } - + $alias = ''; if ($domain['iswildcarddomain'] == '1') { $alias = '*.' . $domain['domain']; } elseif ($domain['wwwserveralias'] == '1') { $alias = 'www.' . $domain['domain']; } - + // After inserting the AWStats information, // be sure to build the awstats conf file as well // and chown it using $awstats_params, #258 @@ -774,7 +774,7 @@ class apache extends HttpConfigBase createAWStatsConf(Settings::Get('system.logfiles_directory') . $domain['loginname'] . $speciallogfile . '-access.log', $domain['domain'], $alias . $server_alias, $domain['customerroot'], $domain); } } - + return $logfiles_text; } @@ -791,13 +791,13 @@ class apache extends HttpConfigBase // number of dots in a domain specifies it's position (and depth of subdomain) starting at 29 going downwards on higher depth $vhost_no = (string) (30 - substr_count($domain['domain'], ".") + 1); } - + if ($ssl_vhost === true) { $vhost_filename = makeCorrectFile(Settings::Get('system.apacheconf_vhost') . '/' . $vhost_no . '_froxlor_ssl_vhost_' . $domain['domain'] . '.conf'); } else { $vhost_filename = makeCorrectFile(Settings::Get('system.apacheconf_vhost') . '/' . $vhost_no . '_froxlor_normal_vhost_' . $domain['domain'] . '.conf'); } - + return $vhost_filename; } @@ -809,27 +809,27 @@ class apache extends HttpConfigBase if ($ssl_vhost === true && ($domain['ssl_redirect'] != '1' && $domain['ssl'] != '1')) { return ''; } - + $query = "SELECT * FROM `" . TABLE_PANEL_IPSANDPORTS . "` `i`, `" . TABLE_DOMAINTOIP . "` `dip` WHERE dip.id_domain = :domainid AND i.id = dip.id_ipandports "; - + if ($ssl_vhost === true && ($domain['ssl'] == '1' || $domain['ssl_redirect'] == '1')) { // by ordering by cert-file the row with filled out SSL-Fields will be shown last, thus it is enough to fill out 1 set of SSL-Fields $query .= "AND i.ssl = '1' ORDER BY i.ssl_cert_file ASC;"; } else { $query .= "AND i.ssl = '0';"; } - + $vhost_content = ''; $result_stmt = Database::prepare($query); Database::pexecute($result_stmt, array( 'domainid' => $domain['id'] )); - + $ipportlist = ''; $_vhost_content = ''; while ($ipandport = $result_stmt->fetch(PDO::FETCH_ASSOC)) { - + $ipport = ''; $domain['ip'] = $ipandport['ip']; $domain['port'] = $ipandport['port']; @@ -838,29 +838,29 @@ class apache extends HttpConfigBase $domain['ssl_key_file'] = $ipandport['ssl_key_file']; $domain['ssl_ca_file'] = $ipandport['ssl_ca_file']; $domain['ssl_cert_chainfile'] = $ipandport['ssl_cert_chainfile']; - + // SSL STUFF $dssl = new DomainSSL(); // this sets the ssl-related array-indices in the $domain array // if the domain has customer-defined ssl-certificates $dssl->setDomainSSLFilesArray($domain); } - + if (filter_var($domain['ip'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { $ipport = '[' . $domain['ip'] . ']:' . $domain['port'] . ' '; } else { $ipport = $domain['ip'] . ':' . $domain['port'] . ' '; } - + if ($ipandport['default_vhostconf_domain'] != '') { $_vhost_content .= $this->processSpecialConfigTemplate($ipandport['default_vhostconf_domain'], $domain, $domain['ip'], $domain['port'], $ssl_vhost) . "\n"; } $ipportlist .= $ipport; } - + $vhost_content .= '' . "\n"; $vhost_content .= $this->getServerNames($domain); - + $domain['documentroot_norewrite'] = $domain['documentroot']; if (($ssl_vhost == false && $domain['ssl'] == '1' && $domain['ssl_redirect'] == '1')) { // We must not check if our port differs from port 443, @@ -879,37 +879,37 @@ class apache extends HttpConfigBase $ssldestport = Database::pexecute_first($ssldestport_stmt, array( 'domainid' => $domain['id'] )); - + if ($ssldestport['port'] != '') { $_sslport = ":" . $ssldestport['port']; } - + $domain['documentroot'] = 'https://%{HTTP_HOST}' . $_sslport . '/'; $domain['documentroot_norewrite'] = 'https://' . $domain['domain'] . $_sslport . '/'; } - + if ($ssl_vhost === true && $domain['ssl'] == '1' && Settings::Get('system.use_ssl') == '1') { if ($domain['ssl_cert_file'] == '') { $domain['ssl_cert_file'] = Settings::Get('system.ssl_cert_file'); } - + if ($domain['ssl_key_file'] == '') { $domain['ssl_key_file'] = Settings::Get('system.ssl_key_file'); } - + if ($domain['ssl_ca_file'] == '') { $domain['ssl_ca_file'] = Settings::Get('system.ssl_ca_file'); } - + if ($domain['ssl_cert_chainfile'] == '') { $domain['ssl_cert_chainfile'] = Settings::Get('system.ssl_cert_chainfile'); } - + if ($domain['ssl_cert_file'] != '') { $vhost_content .= ' SSLEngine On' . "\n"; $vhost_content .= ' SSLProtocol -ALL +' . str_replace(","," +", Settings::Get('system.ssl_protocols')) . "\n"; if (Settings::Get('system.apache24') == '1') { - if (isset($domain['http2']) && $domain['http2'] == '1') { + if (isset($domain['http2']) && $domain['http2'] == '1' && Settings::Get('system.http2_support') == '1') { $vhost_content .= ' Protocols h2 http/1.1' . "\n"; } $vhost_content .= ' SSLCompression Off' . "\n"; @@ -919,23 +919,23 @@ class apache extends HttpConfigBase $vhost_content .= ' SSLCipherSuite ' . Settings::Get('system.ssl_cipher_list') . "\n"; $vhost_content .= ' SSLVerifyDepth 10' . "\n"; $vhost_content .= ' SSLCertificateFile ' . makeCorrectFile($domain['ssl_cert_file']) . "\n"; - + if ($domain['ssl_key_file'] != '') { $vhost_content .= ' SSLCertificateKeyFile ' . makeCorrectFile($domain['ssl_key_file']) . "\n"; } - + if ($domain['ssl_ca_file'] != '') { $vhost_content .= ' SSLCACertificateFile ' . makeCorrectFile($domain['ssl_ca_file']) . "\n"; } - + if ($domain['ssl_cert_chainfile'] != '') { $vhost_content .= ' SSLCertificateChainFile ' . makeCorrectFile($domain['ssl_cert_chainfile']) . "\n"; } - + if (Settings::Get('system.apache24') == '1' && isset($domain['ocsp_stapling']) && $domain['ocsp_stapling'] == '1') { $vhost_content .= ' SSLUseStapling on' . PHP_EOL; } - + if ($domain['hsts'] >= 0) { $vhost_content .= ' ' . "\n"; $vhost_content .= ' Header always set Strict-Transport-Security "max-age=' . $domain['hsts']; @@ -955,20 +955,20 @@ class apache extends HttpConfigBase return '# no ssl-certificate was specified for this domain, therefore no explicit vhost is being generated'; } } - + // avoid using any whitespaces $domain['documentroot'] = trim($domain['documentroot']); - + if (preg_match('/^https?\:\/\//', $domain['documentroot'])) { $corrected_docroot = $domain['documentroot']; - + // Get domain's redirect code $code = getDomainRedirectCode($domain['id'], '301'); $modrew_red = ''; if ($code != '') { $modrew_red = ' [R=' . $code . ';L,NE]'; } - + // redirect everything, not only root-directory, #541 $vhost_content .= ' ' . "\n"; $vhost_content .= ' RewriteEngine On' . "\n"; @@ -984,7 +984,7 @@ class apache extends HttpConfigBase $vhost_content .= ' Redirect ' . $code . ' / ' . $domain['documentroot_norewrite'] . "\n"; $vhost_content .= ' ' . "\n"; } else { - + mkDirWithCorrectOwnership($domain['customerroot'], $domain['documentroot'], $domain['guid'], $domain['guid'], true, true); $vhost_content .= $this->getWebroot($domain); if ($this->_deactivated == false) { @@ -992,22 +992,22 @@ class apache extends HttpConfigBase $vhost_content .= $this->getStats($domain); } $vhost_content .= $this->getLogfiles($domain); - + if ($domain['specialsettings'] != '') { $vhost_content .= $this->processSpecialConfigTemplate($domain['specialsettings'], $domain, $domain['ip'], $domain['port'], $ssl_vhost) . "\n"; } - + if ($_vhost_content != '') { $vhost_content .= $_vhost_content; } - + if (Settings::Get('system.default_vhostconf') != '') { $vhost_content .= $this->processSpecialConfigTemplate(Settings::Get('system.default_vhostconf'), $domain, $domain['ip'], $domain['port'], $ssl_vhost) . "\n"; } } - + $vhost_content .= '' . "\n"; - + return $vhost_content; } @@ -1018,17 +1018,17 @@ class apache extends HttpConfigBase { $domains = WebserverBase::getVhostsToCreate(); foreach ($domains as $domain) { - + $this->logger->logAction(CRON_ACTION, LOG_INFO, 'apache::createVirtualHosts: creating vhost container for domain ' . $domain['id'] . ', customer ' . $domain['loginname']); $vhosts_filename = $this->getVhostFilename($domain); - + // Apply header $this->virtualhosts_data[$vhosts_filename] = '# Domain ID: ' . $domain['id'] . ' - CustomerID: ' . $domain['customerid'] . ' - CustomerLogin: ' . $domain['loginname'] . "\n"; - + if ($domain['deactivated'] != '1' || Settings::Get('system.deactivateddocroot') != '') { // Create vhost without ssl $this->virtualhosts_data[$vhosts_filename] .= $this->getVhostContent($domain, false); - + if ($domain['ssl'] == '1' || $domain['ssl_redirect'] == '1') { // Adding ssl stuff if enabled $vhosts_filename_ssl = $this->getVhostFilename($domain, true); @@ -1053,27 +1053,27 @@ class apache extends HttpConfigBase ORDER BY `htac`.`path` "); $diroptions = array(); - + while ($row_diroptions = $result_stmt->fetch(PDO::FETCH_ASSOC)) { if ($row_diroptions['customerid'] != 0 && isset($row_diroptions['customerroot']) && $row_diroptions['customerroot'] != '') { $diroptions[$row_diroptions['path']] = $row_diroptions; $diroptions[$row_diroptions['path']]['htpasswds'] = array(); } } - + $result_stmt = Database::query(" SELECT `htpw`.*, `c`.`guid`, `c`.`documentroot` AS `customerroot` FROM `" . TABLE_PANEL_HTPASSWDS . "` `htpw` LEFT JOIN `" . TABLE_PANEL_CUSTOMERS . "` `c` USING (`customerid`) ORDER BY `htpw`.`path`, `htpw`.`username` "); - + while ($row_htpasswds = $result_stmt->fetch(PDO::FETCH_ASSOC)) { if ($row_htpasswds['customerid'] != 0 && isset($row_htpasswds['customerroot']) && $row_htpasswds['customerroot'] != '') { if (! isset($diroptions[$row_htpasswds['path']]) || ! is_array($diroptions[$row_htpasswds['path']])) { $diroptions[$row_htpasswds['path']] = array(); } - + $diroptions[$row_htpasswds['path']]['path'] = $row_htpasswds['path']; $diroptions[$row_htpasswds['path']]['guid'] = $row_htpasswds['guid']; $diroptions[$row_htpasswds['path']]['customerroot'] = $row_htpasswds['customerroot']; @@ -1081,24 +1081,24 @@ class apache extends HttpConfigBase $diroptions[$row_htpasswds['path']]['htpasswds'][] = $row_htpasswds; } } - + foreach ($diroptions as $row_diroptions) { $row_diroptions['path'] = makeCorrectDir($row_diroptions['path']); mkDirWithCorrectOwnership($row_diroptions['customerroot'], $row_diroptions['path'], $row_diroptions['guid'], $row_diroptions['guid']); $diroptions_filename = makeCorrectFile(Settings::Get('system.apacheconf_diroptions') . '/40_froxlor_diroption_' . md5($row_diroptions['path']) . '.conf'); - + if (! isset($this->diroptions_data[$diroptions_filename])) { $this->diroptions_data[$diroptions_filename] = ''; } - + if (is_dir($row_diroptions['path'])) { $cperlenabled = customerHasPerlEnabled($row_diroptions['customerid']); - + $this->diroptions_data[$diroptions_filename] .= '' . "\n"; - + if (isset($row_diroptions['options_indexes']) && $row_diroptions['options_indexes'] == '1') { $this->diroptions_data[$diroptions_filename] .= ' Options +Indexes'; - + // add perl options if enabled if ($cperlenabled && isset($row_diroptions['options_cgi']) && $row_diroptions['options_cgi'] == '1') { $this->diroptions_data[$diroptions_filename] .= ' +ExecCGI -MultiViews +SymLinksIfOwnerMatch +FollowSymLinks' . "\n"; @@ -1107,10 +1107,10 @@ class apache extends HttpConfigBase } $this->logger->logAction(CRON_ACTION, LOG_INFO, 'Setting Options +Indexes for ' . $row_diroptions['path']); } - + if (isset($row_diroptions['options_indexes']) && $row_diroptions['options_indexes'] == '0') { $this->diroptions_data[$diroptions_filename] .= ' Options -Indexes'; - + // add perl options if enabled if ($cperlenabled && isset($row_diroptions['options_cgi']) && $row_diroptions['options_cgi'] == '1') { $this->diroptions_data[$diroptions_filename] .= ' +ExecCGI -MultiViews +SymLinksIfOwnerMatch +FollowSymLinks' . "\n"; @@ -1119,7 +1119,7 @@ class apache extends HttpConfigBase } $this->logger->logAction(CRON_ACTION, LOG_INFO, 'Setting Options -Indexes for ' . $row_diroptions['path']); } - + $statusCodes = array( '404', '403', @@ -1136,7 +1136,7 @@ class apache extends HttpConfigBase $this->diroptions_data[$diroptions_filename] .= ' ErrorDocument ' . $statusCode . ' ' . $defhandler . "\n"; } } - + if ($cperlenabled && isset($row_diroptions['options_cgi']) && $row_diroptions['options_cgi'] == '1') { $this->diroptions_data[$diroptions_filename] .= ' AllowOverride None' . "\n"; $this->diroptions_data[$diroptions_filename] .= ' AddHandler cgi-script .cgi .pl' . "\n"; @@ -1154,18 +1154,18 @@ class apache extends HttpConfigBase $this->diroptions_data[$diroptions_filename] .= ' Allow from all' . "\n"; } $this->logger->logAction(CRON_ACTION, LOG_INFO, 'Enabling perl execution for ' . $row_diroptions['path']); - + // check for suexec-workaround, #319 if ((int) Settings::Get('perl.suexecworkaround') == 1) { // symlink this directory to suexec-safe-path $loginname = getCustomerDetail($row_diroptions['customerid'], 'loginname'); $suexecpath = makeCorrectDir(Settings::Get('perl.suexecpath') . '/' . $loginname . '/' . md5($row_diroptions['path']) . '/'); - + if (! file_exists($suexecpath)) { safe_exec('mkdir -p ' . escapeshellarg($suexecpath)); safe_exec('chown -R ' . escapeshellarg($row_diroptions['guid']) . ':' . escapeshellarg($row_diroptions['guid']) . ' ' . escapeshellarg($suexecpath)); } - + // symlink to {$givenpath}/cgi-bin // NOTE: symlinks are FILES, so do not append a / here $perlsymlink = makeCorrectFile($row_diroptions['path'] . '/cgi-bin'); @@ -1181,7 +1181,7 @@ class apache extends HttpConfigBase $loginname = getCustomerDetail($row_diroptions['customerid'], 'loginname'); $suexecpath = makeCorrectDir(Settings::Get('perl.suexecpath') . '/' . $loginname . '/' . md5($row_diroptions['path']) . '/'); $perlsymlink = makeCorrectFile($row_diroptions['path'] . '/cgi-bin'); - + // remove symlink if (file_exists($perlsymlink)) { safe_exec('rm -f ' . escapeshellarg($perlsymlink)); @@ -1192,24 +1192,24 @@ class apache extends HttpConfigBase } } } - + if (count($row_diroptions['htpasswds']) > 0) { $htpasswd_filename = makeCorrectFile(Settings::Get('system.apacheconf_htpasswddir') . '/' . $row_diroptions['customerid'] . '-' . md5($row_diroptions['path']) . '.htpasswd'); - + if (! isset($this->htpasswds_data[$htpasswd_filename])) { $this->htpasswds_data[$htpasswd_filename] = ''; } - + foreach ($row_diroptions['htpasswds'] as $row_htpasswd) { $this->htpasswds_data[$htpasswd_filename] .= $row_htpasswd['username'] . ':' . $row_htpasswd['password'] . "\n"; } - + $this->diroptions_data[$diroptions_filename] .= ' AuthType Basic' . "\n"; $this->diroptions_data[$diroptions_filename] .= ' AuthName "' . $row_htpasswd['authname'] . '"' . "\n"; $this->diroptions_data[$diroptions_filename] .= ' AuthUserFile ' . $htpasswd_filename . "\n"; $this->diroptions_data[$diroptions_filename] .= ' require valid-user' . "\n"; } - + $this->diroptions_data[$diroptions_filename] .= '' . "\n"; } } @@ -1222,19 +1222,19 @@ class apache extends HttpConfigBase { // Write diroptions $this->logger->logAction(CRON_ACTION, LOG_INFO, "apache::writeConfigs: rebuilding " . Settings::Get('system.apacheconf_diroptions')); - + if (count($this->diroptions_data) > 0) { $optsDir = new frxDirectory(Settings::Get('system.apacheconf_diroptions')); if (! $optsDir->isConfigDir()) { // Save one big file $diroptions_file = ''; - + foreach ($this->diroptions_data as $diroptions_filename => $diroptions_content) { $diroptions_file .= $diroptions_content . "\n\n"; } - + $diroptions_filename = Settings::Get('system.apacheconf_diroptions'); - + // Apply header $diroptions_file = '# ' . basename($diroptions_filename) . "\n" . '# Created ' . date('d.m.Y H:i') . "\n" . '# Do NOT manually edit this file, all changes will be deleted after the next domain change at the panel.' . "\n" . "\n" . $diroptions_file; $diroptions_file_handler = fopen($diroptions_filename, 'w'); @@ -1245,11 +1245,11 @@ class apache extends HttpConfigBase $this->logger->logAction(CRON_ACTION, LOG_NOTICE, 'apache::writeConfigs: mkdir ' . escapeshellarg(makeCorrectDir(Settings::Get('system.apacheconf_diroptions')))); safe_exec('mkdir ' . escapeshellarg(makeCorrectDir(Settings::Get('system.apacheconf_diroptions')))); } - + // Write a single file for every diroption foreach ($this->diroptions_data as $diroptions_filename => $diroptions_file) { $this->known_diroptionsfilenames[] = basename($diroptions_filename); - + // Apply header $diroptions_file = '# ' . basename($diroptions_filename) . "\n" . '# Created ' . date('d.m.Y H:i') . "\n" . '# Do NOT manually edit this file, all changes will be deleted after the next domain change at the panel.' . "\n" . "\n" . $diroptions_file; $diroptions_file_handler = fopen($diroptions_filename, 'w'); @@ -1258,10 +1258,10 @@ class apache extends HttpConfigBase } } } - + // Write htpasswds $this->logger->logAction(CRON_ACTION, LOG_INFO, "apache::writeConfigs: rebuilding " . Settings::Get('system.apacheconf_htpasswddir')); - + if (count($this->htpasswds_data) > 0) { if (! file_exists(Settings::Get('system.apacheconf_htpasswddir'))) { $umask = umask(); @@ -1269,7 +1269,7 @@ class apache extends HttpConfigBase mkdir(Settings::Get('system.apacheconf_htpasswddir'), 0751); umask($umask); } - + $htpasswdDir = new frxDirectory(Settings::Get('system.apacheconf_htpasswddir')); if ($htpasswdDir->isConfigDir(true)) { foreach ($this->htpasswds_data as $htpasswd_filename => $htpasswd_file) { @@ -1282,34 +1282,34 @@ class apache extends HttpConfigBase $this->logger->logAction(CRON_ACTION, LOG_WARNING, 'WARNING!!! ' . Settings::Get('system.apacheconf_htpasswddir') . ' is not a directory. htpasswd directory protection is disabled!!!'); } } - + // Write virtualhosts $this->logger->logAction(CRON_ACTION, LOG_INFO, "apache::writeConfigs: rebuilding " . Settings::Get('system.apacheconf_vhost')); - + if (count($this->virtualhosts_data) > 0) { $vhostDir = new frxDirectory(Settings::Get('system.apacheconf_vhost')); if (! $vhostDir->isConfigDir()) { // Save one big file $vhosts_file = ''; - + // sort by filename so the order is: // 1. subdomains x-29 // 2. subdomains as main-domains 30 // 3. main-domains 35 // #437 ksort($this->virtualhosts_data); - + foreach ($this->virtualhosts_data as $vhosts_filename => $vhost_content) { $vhosts_file .= $vhost_content . "\n\n"; } - + // Include diroptions file in case it exists if (file_exists(Settings::Get('system.apacheconf_diroptions'))) { $vhosts_file .= "\n" . 'Include ' . Settings::Get('system.apacheconf_diroptions') . "\n\n"; } - + $vhosts_filename = Settings::Get('system.apacheconf_vhost'); - + // Apply header $vhosts_file = '# ' . basename($vhosts_filename) . "\n" . '# Created ' . date('d.m.Y H:i') . "\n" . '# Do NOT manually edit this file, all changes will be deleted after the next domain change at the panel.' . "\n" . "\n" . $vhosts_file; $vhosts_file_handler = fopen($vhosts_filename, 'w'); @@ -1320,11 +1320,11 @@ class apache extends HttpConfigBase $this->logger->logAction(CRON_ACTION, LOG_NOTICE, 'apache::writeConfigs: mkdir ' . escapeshellarg(makeCorrectDir(Settings::Get('system.apacheconf_vhost')))); safe_exec('mkdir ' . escapeshellarg(makeCorrectDir(Settings::Get('system.apacheconf_vhost')))); } - + // Write a single file for every vhost foreach ($this->virtualhosts_data as $vhosts_filename => $vhosts_file) { $this->known_vhostfilenames[] = basename($vhosts_filename); - + // Apply header $vhosts_file = '# ' . basename($vhosts_filename) . "\n" . '# Created ' . date('d.m.Y H:i') . "\n" . '# Do NOT manually edit this file, all changes will be deleted after the next domain change at the panel.' . "\n" . "\n" . $vhosts_file; $vhosts_file_handler = fopen($vhosts_filename, 'w'); diff --git a/scripts/jobs/cron_tasks.inc.http.30.nginx.php b/scripts/jobs/cron_tasks.inc.http.30.nginx.php index 5c932fd1..d4d27c77 100644 --- a/scripts/jobs/cron_tasks.inc.http.30.nginx.php +++ b/scripts/jobs/cron_tasks.inc.http.30.nginx.php @@ -432,7 +432,7 @@ class nginx extends HttpConfigBase $_vhost_content .= $this->processSpecialConfigTemplate($ipandport['default_vhostconf_domain'], $domain, $domain['ip'], $domain['port'], $ssl_vhost) . "\n"; } - $http2 = $ssl_vhost == true && (isset($domain['http2']) && $domain['http2'] == '1'); + $http2 = $ssl_vhost == true && (isset($domain['http2']) && $domain['http2'] == '1' && Settings::Get('system.http2_support') == '1'); $vhost_content .= "\t" . 'listen ' . $ipport . ($ssl_vhost == true ? ' ssl' : '') . ($http2 == true ? ' http2' : '') . ';' . "\n"; } From ca76e572a2c0be6b92ac23684c6736da73e4b9e5 Mon Sep 17 00:00:00 2001 From: Andreas Grundler Date: Thu, 18 Jan 2018 18:18:30 +0100 Subject: [PATCH 0382/1335] http2 Option darf nur dann zu sehen sein wenn http2 in den Einstellungen aktiv ist --- lib/formfields/admin/domains/formfield.domains_add.php | 2 +- lib/formfields/admin/domains/formfield.domains_edit.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/formfields/admin/domains/formfield.domains_add.php b/lib/formfields/admin/domains/formfield.domains_add.php index 1976b034..71116996 100644 --- a/lib/formfields/admin/domains/formfield.domains_add.php +++ b/lib/formfields/admin/domains/formfield.domains_add.php @@ -174,7 +174,7 @@ return array( 'value' => array() ), 'http2' => array( - 'visible' => ($ssl_ipsandports != '' ? true : false) && Settings::Get('system.webserver') != 'lighttpd', + 'visible' => ($ssl_ipsandports != '' ? true : false) && Settings::Get('system.webserver') != 'lighttpd' && Settings::Get('system.http2_support') == '1', 'label' => $lng['admin']['domain_http2']['title'], 'desc' => $lng['admin']['domain_http2']['description'], 'type' => 'checkbox', diff --git a/lib/formfields/admin/domains/formfield.domains_edit.php b/lib/formfields/admin/domains/formfield.domains_edit.php index 5588ab69..d37f4806 100644 --- a/lib/formfields/admin/domains/formfield.domains_edit.php +++ b/lib/formfields/admin/domains/formfield.domains_edit.php @@ -208,7 +208,7 @@ return array( ) ), 'http2' => array( - 'visible' => ($ssl_ipsandports != '' ? true : false) && Settings::Get('system.webserver') != 'lighttpd', + 'visible' => ($ssl_ipsandports != '' ? true : false) && Settings::Get('system.webserver') != 'lighttpd' && Settings::Get('system.http2_support') == '1', 'label' => $lng['admin']['domain_http2']['title'], 'desc' => $lng['admin']['domain_http2']['description'], 'type' => 'checkbox', From 6ac3cb2014813043388f96ced33531ecf90267d4 Mon Sep 17 00:00:00 2001 From: Andreas Grundler Date: Thu, 18 Jan 2018 18:23:08 +0100 Subject: [PATCH 0383/1335] =?UTF-8?q?Revert=20"http2=20Konfiguration=20nur?= =?UTF-8?q?=20einf=C3=BCgen=20wenn=20http2=20in=20den=20Einstellungen=20ak?= =?UTF-8?q?tiviert=20ist"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit c3fb6f6a1c053c3f5f0b97899fba9137b69ad7ea. --- .../jobs/cron_tasks.inc.http.10.apache.php | 306 +++++++++--------- scripts/jobs/cron_tasks.inc.http.30.nginx.php | 2 +- 2 files changed, 154 insertions(+), 154 deletions(-) diff --git a/scripts/jobs/cron_tasks.inc.http.10.apache.php b/scripts/jobs/cron_tasks.inc.http.10.apache.php index 9fcf71d8..55f230d0 100644 --- a/scripts/jobs/cron_tasks.inc.http.10.apache.php +++ b/scripts/jobs/cron_tasks.inc.http.10.apache.php @@ -16,7 +16,7 @@ if (! defined('MASTER_CRONJOB')) * @author Froxlor team (2010-) * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt * @package Cron - * + * */ require_once (dirname(__FILE__) . '/../classes/class.HttpConfigBase.php'); @@ -91,13 +91,13 @@ class apache extends HttpConfigBase $vhosts_folder = makeCorrectDir(dirname(Settings::Get('system.apacheconf_vhost'))); } $vhosts_filename = makeCorrectFile($vhosts_folder . '/05_froxlor_dirfix_nofcgid.conf'); - + if (! isset($this->virtualhosts_data[$vhosts_filename])) { $this->virtualhosts_data[$vhosts_filename] = ''; } - + $this->virtualhosts_data[$vhosts_filename] .= ' ' . "\n"; - + // check for custom values, see #1638 $custom_opts = Settings::Get('system.apacheglobaldiropt'); if (! empty($custom_opts)) { @@ -113,7 +113,7 @@ class apache extends HttpConfigBase } } $this->virtualhosts_data[$vhosts_filename] .= ' ' . "\n"; - + $ocsp_cache_filename = makeCorrectFile($vhosts_folder . '/03_froxlor_ocsp_cache.conf'); if (Settings::Get('system.use_ssl') == '1' && Settings::Get('system.apache24') == 1) { $this->virtualhosts_data[$ocsp_cache_filename] = 'SSLStaplingCache ' . Settings::Get('system.apache24_ocsp_cache_path') . "\n"; @@ -137,13 +137,13 @@ class apache extends HttpConfigBase } else { $vhosts_folder = makeCorrectDir(dirname(Settings::Get('system.apacheconf_vhost'))); } - + $vhosts_filename = makeCorrectFile($vhosts_folder . '/05_froxlor_default_errorhandler.conf'); - + if (! isset($this->virtualhosts_data[$vhosts_filename])) { $this->virtualhosts_data[$vhosts_filename] = ''; } - + $statusCodes = array( '401', '403', @@ -167,26 +167,26 @@ class apache extends HttpConfigBase public function createIpPort() { $result_ipsandports_stmt = Database::query("SELECT * FROM `" . TABLE_PANEL_IPSANDPORTS . "` ORDER BY `ip` ASC, `port` ASC"); - + while ($row_ipsandports = $result_ipsandports_stmt->fetch(PDO::FETCH_ASSOC)) { if (filter_var($row_ipsandports['ip'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { $ipport = '[' . $row_ipsandports['ip'] . ']:' . $row_ipsandports['port']; } else { $ipport = $row_ipsandports['ip'] . ':' . $row_ipsandports['port']; } - + $this->logger->logAction(CRON_ACTION, LOG_INFO, 'apache::createIpPort: creating ip/port settings for ' . $ipport); $vhosts_filename = makeCorrectFile(Settings::Get('system.apacheconf_vhost') . '/10_froxlor_ipandport_' . trim(str_replace(':', '.', $row_ipsandports['ip']), '.') . '.' . $row_ipsandports['port'] . '.conf'); - + if (! isset($this->virtualhosts_data[$vhosts_filename])) { $this->virtualhosts_data[$vhosts_filename] = ''; } - + if ($row_ipsandports['listen_statement'] == '1') { $this->virtualhosts_data[$vhosts_filename] .= 'Listen ' . $ipport . "\n"; $this->logger->logAction(CRON_ACTION, LOG_DEBUG, $ipport . ' :: inserted listen-statement'); } - + if ($row_ipsandports['namevirtualhost_statement'] == '1') { // >=apache-2.4 enabled? if (Settings::Get('system.apache24') == '1') { @@ -196,22 +196,22 @@ class apache extends HttpConfigBase $this->logger->logAction(CRON_ACTION, LOG_DEBUG, $ipport . ' :: inserted namevirtualhost-statement'); } } - + if ($row_ipsandports['vhostcontainer'] == '1') { - + $without_vhost = $this->virtualhosts_data[$vhosts_filename]; $close_vhost = true; - + $this->virtualhosts_data[$vhosts_filename] .= '' . "\n"; - + $mypath = $this->getMyPath($row_ipsandports); - + $this->virtualhosts_data[$vhosts_filename] .= 'DocumentRoot "' . $mypath . '"' . "\n"; - + if ($row_ipsandports['vhostcontainer_servername_statement'] == '1') { $this->virtualhosts_data[$vhosts_filename] .= ' ServerName ' . Settings::Get('system.hostname') . "\n"; } - + $is_redirect = false; // check for SSL redirect if ($row_ipsandports['ssl'] == '0' && Settings::Get('system.le_froxlor_redirect') == '1') { @@ -223,11 +223,11 @@ class apache extends HttpConfigBase $is_redirect = false; } else { $_sslport = $this->checkAlternativeSslPort(); - + $mypath = 'https://' . Settings::Get('system.hostname') . $_sslport . '/'; $code = '301'; $modrew_red = ' [R=' . $code . ';L,NE]'; - + // redirect everything, not only root-directory, #541 $this->virtualhosts_data[$vhosts_filename] .= ' ' . "\n"; $this->virtualhosts_data[$vhosts_filename] .= ' RewriteEngine On' . "\n"; @@ -242,7 +242,7 @@ class apache extends HttpConfigBase $this->virtualhosts_data[$vhosts_filename] .= ' ' . "\n"; } } - + if (! $is_redirect) { // create fcgid -Part (starter is created in apache_fcgid) if (Settings::Get('system.mod_fcgid_ownvhost') == '1' && Settings::Get('system.mod_fcgid') == '1') { @@ -266,7 +266,7 @@ class apache extends HttpConfigBase ); $php = new phpinterface($domain); $phpconfig = $php->getPhpConfig(Settings::Get('system.mod_fcgid_defaultini_ownvhost')); - + $starter_filename = makeCorrectFile($configdir . '/php-fcgi-starter'); $this->virtualhosts_data[$vhosts_filename] .= ' SuexecUserGroup "' . Settings::Get('system.mod_fcgid_httpuser') . '" "' . Settings::Get('system.mod_fcgid_httpgroup') . '"' . "\n"; $this->virtualhosts_data[$vhosts_filename] .= ' ' . "\n"; @@ -317,14 +317,14 @@ class apache extends HttpConfigBase 'documentroot' => $mypath, 'fpm_config_id' => isset($fpm_config['id']) ? $fpm_config['id'] : 1 ); - + $php = new phpinterface($domain); $phpconfig = $php->getPhpConfig(Settings::Get('phpfpm.vhost_defaultini')); $srvName = substr(md5($ipport), 0, 4) . '.fpm.external'; if ($row_ipsandports['ssl']) { $srvName = substr(md5($ipport), 0, 4) . '.ssl-fpm.external'; } - + // mod_proxy stuff for apache-2.4 if (Settings::Get('system.apache24') == '1' && Settings::Get('phpfpm.use_mod_proxy') == '1') { $filesmatch = $phpconfig['fpm_settings']['limit_extensions']; @@ -401,7 +401,7 @@ class apache extends HttpConfigBase 'documentroot' => $mypath ); } - + /** * dirprotection, see #72 * @@ -410,36 +410,36 @@ class apache extends HttpConfigBase * $this->virtualhosts_data[$vhosts_filename] .= "\t\tAllow from all\n"; * $this->virtualhosts_data[$vhosts_filename] .= "\t\tOptions -Indexes\n"; * $this->virtualhosts_data[$vhosts_filename] .= "\t\n"; - * + * * $this->virtualhosts_data[$vhosts_filename] .= "\t\n"; * $this->virtualhosts_data[$vhosts_filename] .= "\t\tOrder Deny,Allow\n"; * $this->virtualhosts_data[$vhosts_filename] .= "\t\tDeny from All\n"; * $this->virtualhosts_data[$vhosts_filename] .= "\t\n"; * end of dirprotection */ - + if ($row_ipsandports['specialsettings'] != '') { $this->virtualhosts_data[$vhosts_filename] .= $this->processSpecialConfigTemplate($row_ipsandports['specialsettings'], $domain, $row_ipsandports['ip'], $row_ipsandports['port'], $row_ipsandports['ssl'] == '1') . "\n"; } - + if ($row_ipsandports['ssl'] == '1' && Settings::Get('system.use_ssl') == '1') { if ($row_ipsandports['ssl_cert_file'] == '') { $row_ipsandports['ssl_cert_file'] = Settings::Get('system.ssl_cert_file'); } - + if ($row_ipsandports['ssl_key_file'] == '') { $row_ipsandports['ssl_key_file'] = Settings::Get('system.ssl_key_file'); } - + if ($row_ipsandports['ssl_ca_file'] == '') { $row_ipsandports['ssl_ca_file'] = Settings::Get('system.ssl_ca_file'); } - + // #418 if ($row_ipsandports['ssl_cert_chainfile'] == '') { $row_ipsandports['ssl_cert_chainfile'] = Settings::Get('system.ssl_cert_chainfile'); } - + $domain = array( 'id' => 0, 'domain' => Settings::Get('system.hostname'), @@ -448,26 +448,26 @@ class apache extends HttpConfigBase 'documentroot' => $mypath, 'parentdomainid' => 0 ); - + // override corresponding array values $domain['ssl_cert_file'] = $row_ipsandports['ssl_cert_file']; $domain['ssl_key_file'] = $row_ipsandports['ssl_key_file']; $domain['ssl_ca_file'] = $row_ipsandports['ssl_ca_file']; $domain['ssl_cert_chainfile'] = $row_ipsandports['ssl_cert_chainfile']; - + // SSL STUFF $dssl = new DomainSSL(); // this sets the ssl-related array-indices in the $domain array // if the domain has customer-defined ssl-certificates $dssl->setDomainSSLFilesArray($domain); - + if ($domain['ssl_cert_file'] != '') { - + // check for existence, #1485 if (! file_exists($domain['ssl_cert_file'])) { $this->logger->logAction(CRON_ACTION, LOG_ERR, $ipport . ' :: certificate file "' . $domain['ssl_cert_file'] . '" does not exist! Cannot create ssl-directives'); } else { - + $this->virtualhosts_data[$vhosts_filename] .= ' SSLEngine On' . "\n"; $this->virtualhosts_data[$vhosts_filename] .= ' SSLProtocol -ALL +' . str_replace(","," +", Settings::Get('system.ssl_protocols')) . "\n"; if (Settings::Get('system.apache24') == '1') { @@ -481,7 +481,7 @@ class apache extends HttpConfigBase $this->virtualhosts_data[$vhosts_filename] .= ' SSLCipherSuite ' . Settings::Get('system.ssl_cipher_list') . "\n"; $this->virtualhosts_data[$vhosts_filename] .= ' SSLVerifyDepth 10' . "\n"; $this->virtualhosts_data[$vhosts_filename] .= ' SSLCertificateFile ' . makeCorrectFile($domain['ssl_cert_file']) . "\n"; - + if ($domain['ssl_key_file'] != '') { // check for existence, #1485 if (! file_exists($domain['ssl_key_file'])) { @@ -490,7 +490,7 @@ class apache extends HttpConfigBase $this->virtualhosts_data[$vhosts_filename] .= ' SSLCertificateKeyFile ' . makeCorrectFile($domain['ssl_key_file']) . "\n"; } } - + if ($domain['ssl_ca_file'] != '') { // check for existence, #1485 if (! file_exists($domain['ssl_ca_file'])) { @@ -499,7 +499,7 @@ class apache extends HttpConfigBase $this->virtualhosts_data[$vhosts_filename] .= ' SSLCACertificateFile ' . makeCorrectFile($domain['ssl_ca_file']) . "\n"; } } - + // #418 if ($domain['ssl_cert_chainfile'] != '') { // check for existence, #1485 @@ -519,7 +519,7 @@ class apache extends HttpConfigBase $close_vhost = false; } } - + if ($close_vhost) { $this->virtualhosts_data[$vhosts_filename] .= '' . "\n"; } @@ -527,12 +527,12 @@ class apache extends HttpConfigBase } unset($vhosts_filename); } - + /** * bug #32 */ $this->_createStandardDirectoryEntry(); - + /** * bug #unknown-yet */ @@ -550,31 +550,31 @@ class apache extends HttpConfigBase protected function composePhpOptions($domain, $ssl_vhost = false) { $php_options_text = ''; - + if ($domain['phpenabled_customer'] == 1 && $domain['phpenabled_vhost'] == '1') { // This vHost has PHP enabled and we are using the regular mod_php $cmail = getCustomerDetail($domain['customerid'], 'email'); $php_options_text .= ' php_admin_value sendmail_path "/usr/sbin/sendmail -t -f ' . $cmail . '"' . PHP_EOL; - + if ($domain['openbasedir'] == '1') { if ($domain['openbasedir_path'] == '1' || strstr($domain['documentroot'], ":") !== false) { $_phpappendopenbasedir = appendOpenBasedirPath($domain['customerroot'], true); } else { $_phpappendopenbasedir = appendOpenBasedirPath($domain['documentroot'], true); } - + $_custom_openbasedir = explode(':', Settings::Get('system.phpappendopenbasedir')); foreach ($_custom_openbasedir as $cobd) { $_phpappendopenbasedir .= appendOpenBasedirPath($cobd); } - + $php_options_text .= ' php_admin_value open_basedir "' . $_phpappendopenbasedir . '"' . "\n"; } } else { $php_options_text .= ' # PHP is disabled for this vHost' . "\n"; $php_options_text .= ' php_flag engine off' . "\n"; } - + /** * check for apache-itk-support, #1400 * why is this here? Because it only works with mod_php @@ -584,7 +584,7 @@ class apache extends HttpConfigBase $php_options_text .= ' AssignUserID ' . $domain['loginname'] . ' ' . $domain['loginname'] . "\n"; $php_options_text .= ' ' . "\n"; } - + return $php_options_text; } @@ -597,18 +597,18 @@ class apache extends HttpConfigBase protected function getServerNames($domain) { $servernames_text = ' ServerName ' . $domain['domain'] . "\n"; - + $server_alias = ''; if ($domain['iswildcarddomain'] == '1') { $server_alias = '*.' . $domain['domain']; } elseif ($domain['wwwserveralias'] == '1') { $server_alias = 'www.' . $domain['domain']; } - + if (trim($server_alias) != '') { $servernames_text .= ' ServerAlias ' . $server_alias . "\n"; } - + $alias_domains_stmt = Database::prepare(" SELECT `domain`, `iswildcarddomain`, `wwwserveralias` FROM `" . TABLE_PANEL_DOMAINS . "` @@ -617,10 +617,10 @@ class apache extends HttpConfigBase Database::pexecute($alias_domains_stmt, array( 'domainid' => $domain['id'] )); - + while (($alias_domain = $alias_domains_stmt->fetch(PDO::FETCH_ASSOC)) !== false) { $server_alias = ' ServerAlias ' . $alias_domain['domain']; - + if ($alias_domain['iswildcarddomain'] == '1') { $server_alias .= ' *.' . $alias_domain['domain']; } else { @@ -628,10 +628,10 @@ class apache extends HttpConfigBase $server_alias .= ' www.' . $alias_domain['domain']; } } - + $servernames_text .= $server_alias . "\n"; } - + $servernames_text .= ' ServerAdmin ' . $domain['email'] . "\n"; return $servernames_text; } @@ -644,7 +644,7 @@ class apache extends HttpConfigBase $webroot_text = ''; $domain['customerroot'] = makeCorrectDir($domain['customerroot']); $domain['documentroot'] = makeCorrectDir($domain['documentroot']); - + if ($domain['deactivated'] == '1' && Settings::Get('system.deactivateddocroot') != '') { $webroot_text .= ' # Using docroot for deactivated users...' . "\n"; $webroot_text .= ' DocumentRoot "' . makeCorrectDir(Settings::Get('system.deactivateddocroot')) . "\"\n"; @@ -663,7 +663,7 @@ class apache extends HttpConfigBase $webroot_text .= ' DocumentRoot "' . $domain['documentroot'] . "\"\n"; $this->_deactivated = false; } - + return $webroot_text; } @@ -673,7 +673,7 @@ class apache extends HttpConfigBase protected function getStats($domain) { $stats_text = ''; - + if ($domain['speciallogfile'] == '1') { $statDomain = ($domain['parentdomainid'] == '0') ? $domain['domain'] : $domain['parentdomain']; if (Settings::Get('system.awstats_enabled') == '1') { @@ -699,7 +699,7 @@ class apache extends HttpConfigBase $stats_text .= ' Alias /awstats-icon "' . makeCorrectDir(Settings::Get('system.awstats_icons')) . '"' . "\n"; } } - + return $stats_text; } @@ -709,7 +709,7 @@ class apache extends HttpConfigBase protected function getLogfiles($domain) { $logfiles_text = ''; - + if ($domain['speciallogfile'] == '1') { if ($domain['parentdomainid'] == '0') { $speciallogfile = '-' . $domain['domain']; @@ -719,23 +719,23 @@ class apache extends HttpConfigBase } else { $speciallogfile = ''; } - + // The normal access/error - logging is enabled $error_log = makeCorrectFile(Settings::Get('system.logfiles_directory') . $domain['loginname'] . $speciallogfile . '-error.log'); // Create the logfile if it does not exist (fixes #46) touch($error_log); chown($error_log, Settings::Get('system.httpuser')); chgrp($error_log, Settings::Get('system.httpgroup')); - + $access_log = makeCorrectFile(Settings::Get('system.logfiles_directory') . $domain['loginname'] . $speciallogfile . '-access.log'); // Create the logfile if it does not exist (fixes #46) touch($access_log); chown($access_log, Settings::Get('system.httpuser')); chgrp($access_log, Settings::Get('system.httpgroup')); - + $logfiles_text .= ' ErrorLog "' . $error_log . "\"\n"; $logfiles_text .= ' CustomLog "' . $access_log . '" combined' . "\n"; - + if (Settings::Get('system.awstats_enabled') == '1') { if ((int) $domain['parentdomainid'] == 0) { // prepare the aliases and subdomains for stats config files @@ -748,25 +748,25 @@ class apache extends HttpConfigBase Database::pexecute($alias_domains_stmt, array( 'domainid' => $domain['id'] )); - + while (($alias_domain = $alias_domains_stmt->fetch(PDO::FETCH_ASSOC)) !== false) { - + $server_alias .= ' ' . $alias_domain['domain'] . ' '; - + if ($alias_domain['iswildcarddomain'] == '1') { $server_alias .= '*.' . $alias_domain['domain']; } elseif ($alias_domain['wwwserveralias'] == '1') { $server_alias .= 'www.' . $alias_domain['domain']; } } - + $alias = ''; if ($domain['iswildcarddomain'] == '1') { $alias = '*.' . $domain['domain']; } elseif ($domain['wwwserveralias'] == '1') { $alias = 'www.' . $domain['domain']; } - + // After inserting the AWStats information, // be sure to build the awstats conf file as well // and chown it using $awstats_params, #258 @@ -774,7 +774,7 @@ class apache extends HttpConfigBase createAWStatsConf(Settings::Get('system.logfiles_directory') . $domain['loginname'] . $speciallogfile . '-access.log', $domain['domain'], $alias . $server_alias, $domain['customerroot'], $domain); } } - + return $logfiles_text; } @@ -791,13 +791,13 @@ class apache extends HttpConfigBase // number of dots in a domain specifies it's position (and depth of subdomain) starting at 29 going downwards on higher depth $vhost_no = (string) (30 - substr_count($domain['domain'], ".") + 1); } - + if ($ssl_vhost === true) { $vhost_filename = makeCorrectFile(Settings::Get('system.apacheconf_vhost') . '/' . $vhost_no . '_froxlor_ssl_vhost_' . $domain['domain'] . '.conf'); } else { $vhost_filename = makeCorrectFile(Settings::Get('system.apacheconf_vhost') . '/' . $vhost_no . '_froxlor_normal_vhost_' . $domain['domain'] . '.conf'); } - + return $vhost_filename; } @@ -809,27 +809,27 @@ class apache extends HttpConfigBase if ($ssl_vhost === true && ($domain['ssl_redirect'] != '1' && $domain['ssl'] != '1')) { return ''; } - + $query = "SELECT * FROM `" . TABLE_PANEL_IPSANDPORTS . "` `i`, `" . TABLE_DOMAINTOIP . "` `dip` WHERE dip.id_domain = :domainid AND i.id = dip.id_ipandports "; - + if ($ssl_vhost === true && ($domain['ssl'] == '1' || $domain['ssl_redirect'] == '1')) { // by ordering by cert-file the row with filled out SSL-Fields will be shown last, thus it is enough to fill out 1 set of SSL-Fields $query .= "AND i.ssl = '1' ORDER BY i.ssl_cert_file ASC;"; } else { $query .= "AND i.ssl = '0';"; } - + $vhost_content = ''; $result_stmt = Database::prepare($query); Database::pexecute($result_stmt, array( 'domainid' => $domain['id'] )); - + $ipportlist = ''; $_vhost_content = ''; while ($ipandport = $result_stmt->fetch(PDO::FETCH_ASSOC)) { - + $ipport = ''; $domain['ip'] = $ipandport['ip']; $domain['port'] = $ipandport['port']; @@ -838,29 +838,29 @@ class apache extends HttpConfigBase $domain['ssl_key_file'] = $ipandport['ssl_key_file']; $domain['ssl_ca_file'] = $ipandport['ssl_ca_file']; $domain['ssl_cert_chainfile'] = $ipandport['ssl_cert_chainfile']; - + // SSL STUFF $dssl = new DomainSSL(); // this sets the ssl-related array-indices in the $domain array // if the domain has customer-defined ssl-certificates $dssl->setDomainSSLFilesArray($domain); } - + if (filter_var($domain['ip'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { $ipport = '[' . $domain['ip'] . ']:' . $domain['port'] . ' '; } else { $ipport = $domain['ip'] . ':' . $domain['port'] . ' '; } - + if ($ipandport['default_vhostconf_domain'] != '') { $_vhost_content .= $this->processSpecialConfigTemplate($ipandport['default_vhostconf_domain'], $domain, $domain['ip'], $domain['port'], $ssl_vhost) . "\n"; } $ipportlist .= $ipport; } - + $vhost_content .= '' . "\n"; $vhost_content .= $this->getServerNames($domain); - + $domain['documentroot_norewrite'] = $domain['documentroot']; if (($ssl_vhost == false && $domain['ssl'] == '1' && $domain['ssl_redirect'] == '1')) { // We must not check if our port differs from port 443, @@ -879,37 +879,37 @@ class apache extends HttpConfigBase $ssldestport = Database::pexecute_first($ssldestport_stmt, array( 'domainid' => $domain['id'] )); - + if ($ssldestport['port'] != '') { $_sslport = ":" . $ssldestport['port']; } - + $domain['documentroot'] = 'https://%{HTTP_HOST}' . $_sslport . '/'; $domain['documentroot_norewrite'] = 'https://' . $domain['domain'] . $_sslport . '/'; } - + if ($ssl_vhost === true && $domain['ssl'] == '1' && Settings::Get('system.use_ssl') == '1') { if ($domain['ssl_cert_file'] == '') { $domain['ssl_cert_file'] = Settings::Get('system.ssl_cert_file'); } - + if ($domain['ssl_key_file'] == '') { $domain['ssl_key_file'] = Settings::Get('system.ssl_key_file'); } - + if ($domain['ssl_ca_file'] == '') { $domain['ssl_ca_file'] = Settings::Get('system.ssl_ca_file'); } - + if ($domain['ssl_cert_chainfile'] == '') { $domain['ssl_cert_chainfile'] = Settings::Get('system.ssl_cert_chainfile'); } - + if ($domain['ssl_cert_file'] != '') { $vhost_content .= ' SSLEngine On' . "\n"; $vhost_content .= ' SSLProtocol -ALL +' . str_replace(","," +", Settings::Get('system.ssl_protocols')) . "\n"; if (Settings::Get('system.apache24') == '1') { - if (isset($domain['http2']) && $domain['http2'] == '1' && Settings::Get('system.http2_support') == '1') { + if (isset($domain['http2']) && $domain['http2'] == '1') { $vhost_content .= ' Protocols h2 http/1.1' . "\n"; } $vhost_content .= ' SSLCompression Off' . "\n"; @@ -919,23 +919,23 @@ class apache extends HttpConfigBase $vhost_content .= ' SSLCipherSuite ' . Settings::Get('system.ssl_cipher_list') . "\n"; $vhost_content .= ' SSLVerifyDepth 10' . "\n"; $vhost_content .= ' SSLCertificateFile ' . makeCorrectFile($domain['ssl_cert_file']) . "\n"; - + if ($domain['ssl_key_file'] != '') { $vhost_content .= ' SSLCertificateKeyFile ' . makeCorrectFile($domain['ssl_key_file']) . "\n"; } - + if ($domain['ssl_ca_file'] != '') { $vhost_content .= ' SSLCACertificateFile ' . makeCorrectFile($domain['ssl_ca_file']) . "\n"; } - + if ($domain['ssl_cert_chainfile'] != '') { $vhost_content .= ' SSLCertificateChainFile ' . makeCorrectFile($domain['ssl_cert_chainfile']) . "\n"; } - + if (Settings::Get('system.apache24') == '1' && isset($domain['ocsp_stapling']) && $domain['ocsp_stapling'] == '1') { $vhost_content .= ' SSLUseStapling on' . PHP_EOL; } - + if ($domain['hsts'] >= 0) { $vhost_content .= ' ' . "\n"; $vhost_content .= ' Header always set Strict-Transport-Security "max-age=' . $domain['hsts']; @@ -955,20 +955,20 @@ class apache extends HttpConfigBase return '# no ssl-certificate was specified for this domain, therefore no explicit vhost is being generated'; } } - + // avoid using any whitespaces $domain['documentroot'] = trim($domain['documentroot']); - + if (preg_match('/^https?\:\/\//', $domain['documentroot'])) { $corrected_docroot = $domain['documentroot']; - + // Get domain's redirect code $code = getDomainRedirectCode($domain['id'], '301'); $modrew_red = ''; if ($code != '') { $modrew_red = ' [R=' . $code . ';L,NE]'; } - + // redirect everything, not only root-directory, #541 $vhost_content .= ' ' . "\n"; $vhost_content .= ' RewriteEngine On' . "\n"; @@ -984,7 +984,7 @@ class apache extends HttpConfigBase $vhost_content .= ' Redirect ' . $code . ' / ' . $domain['documentroot_norewrite'] . "\n"; $vhost_content .= ' ' . "\n"; } else { - + mkDirWithCorrectOwnership($domain['customerroot'], $domain['documentroot'], $domain['guid'], $domain['guid'], true, true); $vhost_content .= $this->getWebroot($domain); if ($this->_deactivated == false) { @@ -992,22 +992,22 @@ class apache extends HttpConfigBase $vhost_content .= $this->getStats($domain); } $vhost_content .= $this->getLogfiles($domain); - + if ($domain['specialsettings'] != '') { $vhost_content .= $this->processSpecialConfigTemplate($domain['specialsettings'], $domain, $domain['ip'], $domain['port'], $ssl_vhost) . "\n"; } - + if ($_vhost_content != '') { $vhost_content .= $_vhost_content; } - + if (Settings::Get('system.default_vhostconf') != '') { $vhost_content .= $this->processSpecialConfigTemplate(Settings::Get('system.default_vhostconf'), $domain, $domain['ip'], $domain['port'], $ssl_vhost) . "\n"; } } - + $vhost_content .= '' . "\n"; - + return $vhost_content; } @@ -1018,17 +1018,17 @@ class apache extends HttpConfigBase { $domains = WebserverBase::getVhostsToCreate(); foreach ($domains as $domain) { - + $this->logger->logAction(CRON_ACTION, LOG_INFO, 'apache::createVirtualHosts: creating vhost container for domain ' . $domain['id'] . ', customer ' . $domain['loginname']); $vhosts_filename = $this->getVhostFilename($domain); - + // Apply header $this->virtualhosts_data[$vhosts_filename] = '# Domain ID: ' . $domain['id'] . ' - CustomerID: ' . $domain['customerid'] . ' - CustomerLogin: ' . $domain['loginname'] . "\n"; - + if ($domain['deactivated'] != '1' || Settings::Get('system.deactivateddocroot') != '') { // Create vhost without ssl $this->virtualhosts_data[$vhosts_filename] .= $this->getVhostContent($domain, false); - + if ($domain['ssl'] == '1' || $domain['ssl_redirect'] == '1') { // Adding ssl stuff if enabled $vhosts_filename_ssl = $this->getVhostFilename($domain, true); @@ -1053,27 +1053,27 @@ class apache extends HttpConfigBase ORDER BY `htac`.`path` "); $diroptions = array(); - + while ($row_diroptions = $result_stmt->fetch(PDO::FETCH_ASSOC)) { if ($row_diroptions['customerid'] != 0 && isset($row_diroptions['customerroot']) && $row_diroptions['customerroot'] != '') { $diroptions[$row_diroptions['path']] = $row_diroptions; $diroptions[$row_diroptions['path']]['htpasswds'] = array(); } } - + $result_stmt = Database::query(" SELECT `htpw`.*, `c`.`guid`, `c`.`documentroot` AS `customerroot` FROM `" . TABLE_PANEL_HTPASSWDS . "` `htpw` LEFT JOIN `" . TABLE_PANEL_CUSTOMERS . "` `c` USING (`customerid`) ORDER BY `htpw`.`path`, `htpw`.`username` "); - + while ($row_htpasswds = $result_stmt->fetch(PDO::FETCH_ASSOC)) { if ($row_htpasswds['customerid'] != 0 && isset($row_htpasswds['customerroot']) && $row_htpasswds['customerroot'] != '') { if (! isset($diroptions[$row_htpasswds['path']]) || ! is_array($diroptions[$row_htpasswds['path']])) { $diroptions[$row_htpasswds['path']] = array(); } - + $diroptions[$row_htpasswds['path']]['path'] = $row_htpasswds['path']; $diroptions[$row_htpasswds['path']]['guid'] = $row_htpasswds['guid']; $diroptions[$row_htpasswds['path']]['customerroot'] = $row_htpasswds['customerroot']; @@ -1081,24 +1081,24 @@ class apache extends HttpConfigBase $diroptions[$row_htpasswds['path']]['htpasswds'][] = $row_htpasswds; } } - + foreach ($diroptions as $row_diroptions) { $row_diroptions['path'] = makeCorrectDir($row_diroptions['path']); mkDirWithCorrectOwnership($row_diroptions['customerroot'], $row_diroptions['path'], $row_diroptions['guid'], $row_diroptions['guid']); $diroptions_filename = makeCorrectFile(Settings::Get('system.apacheconf_diroptions') . '/40_froxlor_diroption_' . md5($row_diroptions['path']) . '.conf'); - + if (! isset($this->diroptions_data[$diroptions_filename])) { $this->diroptions_data[$diroptions_filename] = ''; } - + if (is_dir($row_diroptions['path'])) { $cperlenabled = customerHasPerlEnabled($row_diroptions['customerid']); - + $this->diroptions_data[$diroptions_filename] .= '' . "\n"; - + if (isset($row_diroptions['options_indexes']) && $row_diroptions['options_indexes'] == '1') { $this->diroptions_data[$diroptions_filename] .= ' Options +Indexes'; - + // add perl options if enabled if ($cperlenabled && isset($row_diroptions['options_cgi']) && $row_diroptions['options_cgi'] == '1') { $this->diroptions_data[$diroptions_filename] .= ' +ExecCGI -MultiViews +SymLinksIfOwnerMatch +FollowSymLinks' . "\n"; @@ -1107,10 +1107,10 @@ class apache extends HttpConfigBase } $this->logger->logAction(CRON_ACTION, LOG_INFO, 'Setting Options +Indexes for ' . $row_diroptions['path']); } - + if (isset($row_diroptions['options_indexes']) && $row_diroptions['options_indexes'] == '0') { $this->diroptions_data[$diroptions_filename] .= ' Options -Indexes'; - + // add perl options if enabled if ($cperlenabled && isset($row_diroptions['options_cgi']) && $row_diroptions['options_cgi'] == '1') { $this->diroptions_data[$diroptions_filename] .= ' +ExecCGI -MultiViews +SymLinksIfOwnerMatch +FollowSymLinks' . "\n"; @@ -1119,7 +1119,7 @@ class apache extends HttpConfigBase } $this->logger->logAction(CRON_ACTION, LOG_INFO, 'Setting Options -Indexes for ' . $row_diroptions['path']); } - + $statusCodes = array( '404', '403', @@ -1136,7 +1136,7 @@ class apache extends HttpConfigBase $this->diroptions_data[$diroptions_filename] .= ' ErrorDocument ' . $statusCode . ' ' . $defhandler . "\n"; } } - + if ($cperlenabled && isset($row_diroptions['options_cgi']) && $row_diroptions['options_cgi'] == '1') { $this->diroptions_data[$diroptions_filename] .= ' AllowOverride None' . "\n"; $this->diroptions_data[$diroptions_filename] .= ' AddHandler cgi-script .cgi .pl' . "\n"; @@ -1154,18 +1154,18 @@ class apache extends HttpConfigBase $this->diroptions_data[$diroptions_filename] .= ' Allow from all' . "\n"; } $this->logger->logAction(CRON_ACTION, LOG_INFO, 'Enabling perl execution for ' . $row_diroptions['path']); - + // check for suexec-workaround, #319 if ((int) Settings::Get('perl.suexecworkaround') == 1) { // symlink this directory to suexec-safe-path $loginname = getCustomerDetail($row_diroptions['customerid'], 'loginname'); $suexecpath = makeCorrectDir(Settings::Get('perl.suexecpath') . '/' . $loginname . '/' . md5($row_diroptions['path']) . '/'); - + if (! file_exists($suexecpath)) { safe_exec('mkdir -p ' . escapeshellarg($suexecpath)); safe_exec('chown -R ' . escapeshellarg($row_diroptions['guid']) . ':' . escapeshellarg($row_diroptions['guid']) . ' ' . escapeshellarg($suexecpath)); } - + // symlink to {$givenpath}/cgi-bin // NOTE: symlinks are FILES, so do not append a / here $perlsymlink = makeCorrectFile($row_diroptions['path'] . '/cgi-bin'); @@ -1181,7 +1181,7 @@ class apache extends HttpConfigBase $loginname = getCustomerDetail($row_diroptions['customerid'], 'loginname'); $suexecpath = makeCorrectDir(Settings::Get('perl.suexecpath') . '/' . $loginname . '/' . md5($row_diroptions['path']) . '/'); $perlsymlink = makeCorrectFile($row_diroptions['path'] . '/cgi-bin'); - + // remove symlink if (file_exists($perlsymlink)) { safe_exec('rm -f ' . escapeshellarg($perlsymlink)); @@ -1192,24 +1192,24 @@ class apache extends HttpConfigBase } } } - + if (count($row_diroptions['htpasswds']) > 0) { $htpasswd_filename = makeCorrectFile(Settings::Get('system.apacheconf_htpasswddir') . '/' . $row_diroptions['customerid'] . '-' . md5($row_diroptions['path']) . '.htpasswd'); - + if (! isset($this->htpasswds_data[$htpasswd_filename])) { $this->htpasswds_data[$htpasswd_filename] = ''; } - + foreach ($row_diroptions['htpasswds'] as $row_htpasswd) { $this->htpasswds_data[$htpasswd_filename] .= $row_htpasswd['username'] . ':' . $row_htpasswd['password'] . "\n"; } - + $this->diroptions_data[$diroptions_filename] .= ' AuthType Basic' . "\n"; $this->diroptions_data[$diroptions_filename] .= ' AuthName "' . $row_htpasswd['authname'] . '"' . "\n"; $this->diroptions_data[$diroptions_filename] .= ' AuthUserFile ' . $htpasswd_filename . "\n"; $this->diroptions_data[$diroptions_filename] .= ' require valid-user' . "\n"; } - + $this->diroptions_data[$diroptions_filename] .= '' . "\n"; } } @@ -1222,19 +1222,19 @@ class apache extends HttpConfigBase { // Write diroptions $this->logger->logAction(CRON_ACTION, LOG_INFO, "apache::writeConfigs: rebuilding " . Settings::Get('system.apacheconf_diroptions')); - + if (count($this->diroptions_data) > 0) { $optsDir = new frxDirectory(Settings::Get('system.apacheconf_diroptions')); if (! $optsDir->isConfigDir()) { // Save one big file $diroptions_file = ''; - + foreach ($this->diroptions_data as $diroptions_filename => $diroptions_content) { $diroptions_file .= $diroptions_content . "\n\n"; } - + $diroptions_filename = Settings::Get('system.apacheconf_diroptions'); - + // Apply header $diroptions_file = '# ' . basename($diroptions_filename) . "\n" . '# Created ' . date('d.m.Y H:i') . "\n" . '# Do NOT manually edit this file, all changes will be deleted after the next domain change at the panel.' . "\n" . "\n" . $diroptions_file; $diroptions_file_handler = fopen($diroptions_filename, 'w'); @@ -1245,11 +1245,11 @@ class apache extends HttpConfigBase $this->logger->logAction(CRON_ACTION, LOG_NOTICE, 'apache::writeConfigs: mkdir ' . escapeshellarg(makeCorrectDir(Settings::Get('system.apacheconf_diroptions')))); safe_exec('mkdir ' . escapeshellarg(makeCorrectDir(Settings::Get('system.apacheconf_diroptions')))); } - + // Write a single file for every diroption foreach ($this->diroptions_data as $diroptions_filename => $diroptions_file) { $this->known_diroptionsfilenames[] = basename($diroptions_filename); - + // Apply header $diroptions_file = '# ' . basename($diroptions_filename) . "\n" . '# Created ' . date('d.m.Y H:i') . "\n" . '# Do NOT manually edit this file, all changes will be deleted after the next domain change at the panel.' . "\n" . "\n" . $diroptions_file; $diroptions_file_handler = fopen($diroptions_filename, 'w'); @@ -1258,10 +1258,10 @@ class apache extends HttpConfigBase } } } - + // Write htpasswds $this->logger->logAction(CRON_ACTION, LOG_INFO, "apache::writeConfigs: rebuilding " . Settings::Get('system.apacheconf_htpasswddir')); - + if (count($this->htpasswds_data) > 0) { if (! file_exists(Settings::Get('system.apacheconf_htpasswddir'))) { $umask = umask(); @@ -1269,7 +1269,7 @@ class apache extends HttpConfigBase mkdir(Settings::Get('system.apacheconf_htpasswddir'), 0751); umask($umask); } - + $htpasswdDir = new frxDirectory(Settings::Get('system.apacheconf_htpasswddir')); if ($htpasswdDir->isConfigDir(true)) { foreach ($this->htpasswds_data as $htpasswd_filename => $htpasswd_file) { @@ -1282,34 +1282,34 @@ class apache extends HttpConfigBase $this->logger->logAction(CRON_ACTION, LOG_WARNING, 'WARNING!!! ' . Settings::Get('system.apacheconf_htpasswddir') . ' is not a directory. htpasswd directory protection is disabled!!!'); } } - + // Write virtualhosts $this->logger->logAction(CRON_ACTION, LOG_INFO, "apache::writeConfigs: rebuilding " . Settings::Get('system.apacheconf_vhost')); - + if (count($this->virtualhosts_data) > 0) { $vhostDir = new frxDirectory(Settings::Get('system.apacheconf_vhost')); if (! $vhostDir->isConfigDir()) { // Save one big file $vhosts_file = ''; - + // sort by filename so the order is: // 1. subdomains x-29 // 2. subdomains as main-domains 30 // 3. main-domains 35 // #437 ksort($this->virtualhosts_data); - + foreach ($this->virtualhosts_data as $vhosts_filename => $vhost_content) { $vhosts_file .= $vhost_content . "\n\n"; } - + // Include diroptions file in case it exists if (file_exists(Settings::Get('system.apacheconf_diroptions'))) { $vhosts_file .= "\n" . 'Include ' . Settings::Get('system.apacheconf_diroptions') . "\n\n"; } - + $vhosts_filename = Settings::Get('system.apacheconf_vhost'); - + // Apply header $vhosts_file = '# ' . basename($vhosts_filename) . "\n" . '# Created ' . date('d.m.Y H:i') . "\n" . '# Do NOT manually edit this file, all changes will be deleted after the next domain change at the panel.' . "\n" . "\n" . $vhosts_file; $vhosts_file_handler = fopen($vhosts_filename, 'w'); @@ -1320,11 +1320,11 @@ class apache extends HttpConfigBase $this->logger->logAction(CRON_ACTION, LOG_NOTICE, 'apache::writeConfigs: mkdir ' . escapeshellarg(makeCorrectDir(Settings::Get('system.apacheconf_vhost')))); safe_exec('mkdir ' . escapeshellarg(makeCorrectDir(Settings::Get('system.apacheconf_vhost')))); } - + // Write a single file for every vhost foreach ($this->virtualhosts_data as $vhosts_filename => $vhosts_file) { $this->known_vhostfilenames[] = basename($vhosts_filename); - + // Apply header $vhosts_file = '# ' . basename($vhosts_filename) . "\n" . '# Created ' . date('d.m.Y H:i') . "\n" . '# Do NOT manually edit this file, all changes will be deleted after the next domain change at the panel.' . "\n" . "\n" . $vhosts_file; $vhosts_file_handler = fopen($vhosts_filename, 'w'); diff --git a/scripts/jobs/cron_tasks.inc.http.30.nginx.php b/scripts/jobs/cron_tasks.inc.http.30.nginx.php index d4d27c77..5c932fd1 100644 --- a/scripts/jobs/cron_tasks.inc.http.30.nginx.php +++ b/scripts/jobs/cron_tasks.inc.http.30.nginx.php @@ -432,7 +432,7 @@ class nginx extends HttpConfigBase $_vhost_content .= $this->processSpecialConfigTemplate($ipandport['default_vhostconf_domain'], $domain, $domain['ip'], $domain['port'], $ssl_vhost) . "\n"; } - $http2 = $ssl_vhost == true && (isset($domain['http2']) && $domain['http2'] == '1' && Settings::Get('system.http2_support') == '1'); + $http2 = $ssl_vhost == true && (isset($domain['http2']) && $domain['http2'] == '1'); $vhost_content .= "\t" . 'listen ' . $ipport . ($ssl_vhost == true ? ' ssl' : '') . ($http2 == true ? ' http2' : '') . ';' . "\n"; } From 7442bf7347134d6e250bfa682b6e344dedf4ab22 Mon Sep 17 00:00:00 2001 From: Andreas Grundler Date: Thu, 18 Jan 2018 18:30:38 +0100 Subject: [PATCH 0384/1335] =?UTF-8?q?http2=20Konfiguration=20nur=20einf?= =?UTF-8?q?=C3=BCgen=20wenn=20http2=20in=20den=20Einstellungen=20aktiviert?= =?UTF-8?q?=20ist?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/jobs/cron_tasks.inc.http.10.apache.php | 2 +- scripts/jobs/cron_tasks.inc.http.30.nginx.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/jobs/cron_tasks.inc.http.10.apache.php b/scripts/jobs/cron_tasks.inc.http.10.apache.php index 55f230d0..2a7ab516 100644 --- a/scripts/jobs/cron_tasks.inc.http.10.apache.php +++ b/scripts/jobs/cron_tasks.inc.http.10.apache.php @@ -909,7 +909,7 @@ class apache extends HttpConfigBase $vhost_content .= ' SSLEngine On' . "\n"; $vhost_content .= ' SSLProtocol -ALL +' . str_replace(","," +", Settings::Get('system.ssl_protocols')) . "\n"; if (Settings::Get('system.apache24') == '1') { - if (isset($domain['http2']) && $domain['http2'] == '1') { + if (isset($domain['http2']) && $domain['http2'] == '1' && Settings::Get('system.http2_support') == '1') { $vhost_content .= ' Protocols h2 http/1.1' . "\n"; } $vhost_content .= ' SSLCompression Off' . "\n"; diff --git a/scripts/jobs/cron_tasks.inc.http.30.nginx.php b/scripts/jobs/cron_tasks.inc.http.30.nginx.php index 5c932fd1..d4d27c77 100644 --- a/scripts/jobs/cron_tasks.inc.http.30.nginx.php +++ b/scripts/jobs/cron_tasks.inc.http.30.nginx.php @@ -432,7 +432,7 @@ class nginx extends HttpConfigBase $_vhost_content .= $this->processSpecialConfigTemplate($ipandport['default_vhostconf_domain'], $domain, $domain['ip'], $domain['port'], $ssl_vhost) . "\n"; } - $http2 = $ssl_vhost == true && (isset($domain['http2']) && $domain['http2'] == '1'); + $http2 = $ssl_vhost == true && (isset($domain['http2']) && $domain['http2'] == '1' && Settings::Get('system.http2_support') == '1'); $vhost_content .= "\t" . 'listen ' . $ipport . ($ssl_vhost == true ? ' ssl' : '') . ($http2 == true ? ' http2' : '') . ';' . "\n"; } From f3733ca24900ac3e5509930b439f5859201565c3 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 18 Jan 2018 19:50:28 +0100 Subject: [PATCH 0385/1335] set apache-2.4 as default ON also in froxlor.sql; fix wrong DbManagerMySQL::disableUser, fixes #505 Signed-off-by: Michael Kaufmann (d00p) --- install/froxlor.sql | 2 +- lib/classes/database/manager/class.DbManagerMySQL.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/install/froxlor.sql b/install/froxlor.sql index fd762195..1bd72d32 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -601,7 +601,7 @@ opcache.interned_strings_buffer'), ('system', 'http2_support', '0'), ('system', 'perl_server', 'unix:/var/run/nginx/cgiwrap-dispatch.sock'), ('system', 'phpreload_command', ''), - ('system', 'apache24', '0'), + ('system', 'apache24', '1'), ('system', 'apache24_ocsp_cache_path', 'shmcb:/var/run/apache2/ocsp-stapling.cache(131072)'), ('system', 'documentroot_use_default_value', '0'), ('system', 'passwordcryptfunc', '3'), diff --git a/lib/classes/database/manager/class.DbManagerMySQL.php b/lib/classes/database/manager/class.DbManagerMySQL.php index bed0eadd..b9a8d3ec 100644 --- a/lib/classes/database/manager/class.DbManagerMySQL.php +++ b/lib/classes/database/manager/class.DbManagerMySQL.php @@ -134,7 +134,7 @@ class DbManagerMySQL { * @param string $host (unused in mysql) */ public function disableUser($username = null, $host = null) { - $stmt = Database::prepare("REVOKE ALL PRIVILEGES, GRANT OPTION FROM `".$row_database['databasename']."`"); + $stmt = Database::prepare('REVOKE ALL PRIVILEGES, GRANT OPTION FROM `' . $username . '`@`' . $host . '`'); Database::pexecute($stmt, array(), false); } From ca0ab1f97a684d0688de17a77cf4152ca3e7509c Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sun, 21 Jan 2018 23:37:09 +0100 Subject: [PATCH 0386/1335] add fixed dovecot/conf.d/10-ssl.conf template for debian stretch; update phpMailer to 5.2.26 Signed-off-by: Michael Kaufmann (d00p) --- lib/configfiles/stretch.xml | 68 +++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/lib/configfiles/stretch.xml b/lib/configfiles/stretch.xml index 52f5e48d..eb6c71d1 100644 --- a/lib/configfiles/stretch.xml +++ b/lib/configfiles/stretch.xml @@ -3372,6 +3372,74 @@ service dict { #group = } } +]]> + +
+ + +ssl = no + +# PEM encoded X.509 SSL/TLS certificate and private key. They're opened before +# dropping root privileges, so keep the key file unreadable by anyone but +# root. Included doc/mkcert.sh can be used to easily generate self-signed +# certificate, just make sure to update the domains in dovecot-openssl.cnf +#ssl_cert = From 3fb92259a86553695b7632eb73e9188bc582ba91 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Fri, 26 Jan 2018 17:17:22 +0100 Subject: [PATCH 0387/1335] readd starting and ending quotes which got removed with the braces in #503 Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/dns/class.DnsEntry.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/classes/dns/class.DnsEntry.php b/lib/classes/dns/class.DnsEntry.php index f4726e4d..11a7a614 100644 --- a/lib/classes/dns/class.DnsEntry.php +++ b/lib/classes/dns/class.DnsEntry.php @@ -52,18 +52,18 @@ class DnsEntry if (substr($_l, 0, 1) == '"') { $_l = substr($_l, 1); } - $_content = $_l . '"' . PHP_EOL; + $_content = '"' . $_l . '"' . PHP_EOL; $_l = array_pop($_contentlines); // check for ending quote - if (substr($_l, -1) == '"') { - $_l = substr($_l, 0, -1); + if (substr($_l, - 1) == '"') { + $_l = substr($_l, 0, - 1); } foreach ($_contentlines as $_cl) { // lines in between $_content .= "\t\t\t\t" . '"' . $_cl . '"' . PHP_EOL; } // last line - $_content .= "\t\t\t\t" . '"'.$_l; + $_content .= "\t\t\t\t" . '"' . $_l . '"'; } $result = $this->record . "\t" . $this->ttl . "\t" . $this->class . "\t" . $this->type . "\t" . (($this->priority >= 0 && ($this->type == 'MX' || $this->type == 'SRV')) ? $this->priority . "\t" : "") . $_content . PHP_EOL; return $result; From fcd0dddfd551b0ce7c4bdf0f757cf6a048858641 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Fri, 26 Jan 2018 17:18:12 +0100 Subject: [PATCH 0388/1335] Update phpMailer to 5.2.26 Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/phpmailer/class.PHPMailer.php | 805 +++++++++++----------- lib/classes/phpmailer/class.SMTP.php | 329 +++++---- 2 files changed, 590 insertions(+), 544 deletions(-) diff --git a/lib/classes/phpmailer/class.PHPMailer.php b/lib/classes/phpmailer/class.PHPMailer.php index cdb9413c..ff04b18b 100644 --- a/lib/classes/phpmailer/class.PHPMailer.php +++ b/lib/classes/phpmailer/class.PHPMailer.php @@ -1,38 +1,38 @@ -* @author Jim Jagielski (jimjag) -* @author Andy Prevost (codeworxtech) -* @author Brent R. Matzelle (original founder) -* @copyright 2012 - 2014 Marcus Bointon -* @copyright 2010 - 2012 Jim Jagielski -* @copyright 2004 - 2009 Andy Prevost -* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License -* @note This program is distributed in the hope that it will be useful - WITHOUT -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -* FITNESS FOR A PARTICULAR PURPOSE. -*/ + * PHP Version 5 + * @package PHPMailer + * @link https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project + * @author Marcus Bointon (Synchro/coolbru) + * @author Jim Jagielski (jimjag) + * @author Andy Prevost (codeworxtech) + * @author Brent R. Matzelle (original founder) + * @copyright 2012 - 2014 Marcus Bointon + * @copyright 2010 - 2012 Jim Jagielski + * @copyright 2004 - 2009 Andy Prevost + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + * @note This program is distributed in the hope that it will be useful - WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ /** * PHPMailer - PHP email creation and transport class. -* @package PHPMailer -* @author Marcus Bointon (Synchro/coolbru) -* @author Jim Jagielski (jimjag) -* @author Andy Prevost (codeworxtech) -* @author Brent R. Matzelle (original founder) -*/ + * @package PHPMailer + * @author Marcus Bointon (Synchro/coolbru) + * @author Jim Jagielski (jimjag) + * @author Andy Prevost (codeworxtech) + * @author Brent R. Matzelle (original founder) + */ class PHPMailer { /** * The PHPMailer Version number. * @var string */ - public $Version = '5.2.21'; - + public $Version = '5.2.26'; + /** * Email priority. * Options: null (default), 1 = High, 3 = Normal, 5 = low. @@ -40,51 +40,51 @@ class PHPMailer * @var integer */ public $Priority = null; - + /** * The character set of the message. * @var string */ public $CharSet = 'iso-8859-1'; - + /** * The MIME Content-type of the message. * @var string */ public $ContentType = 'text/plain'; - + /** * The message encoding. * Options: "8bit", "7bit", "binary", "base64", and "quoted-printable". * @var string */ public $Encoding = '8bit'; - + /** * Holds the most recent mailer error message. * @var string */ public $ErrorInfo = ''; - + /** * The From email address for the message. * @var string */ public $From = 'root@localhost'; - + /** * The From name of the message. * @var string */ public $FromName = 'Root User'; - + /** * The Sender email (Return-Path) of the message. * If not empty, will be sent via -f to sendmail or as 'MAIL FROM' in smtp mode. * @var string */ public $Sender = ''; - + /** * The Return-Path of the message. * If empty, it will be set to either From or Sender. @@ -94,20 +94,20 @@ class PHPMailer * @link https://tools.ietf.org/html/rfc5321#section-4.4 RFC5321 reference */ public $ReturnPath = ''; - + /** * The Subject of the message. * @var string */ public $Subject = ''; - + /** * An HTML or plain text message body. * If HTML then call isHTML(true). * @var string */ public $Body = ''; - + /** * The plain-text message body. * This body can be read by mail clients that do not have HTML email @@ -116,7 +116,7 @@ class PHPMailer * @var string */ public $AltBody = ''; - + /** * An iCal message part body. * Only supported in simple alt or alt_inline message types @@ -126,55 +126,55 @@ class PHPMailer * @var string */ public $Ical = ''; - + /** * The complete compiled MIME message body. * @access protected * @var string */ protected $MIMEBody = ''; - + /** * The complete compiled MIME message headers. * @var string * @access protected */ protected $MIMEHeader = ''; - + /** * Extra headers that createHeader() doesn't fold in. * @var string * @access protected */ protected $mailHeader = ''; - + /** * Word-wrap the message body to this number of chars. * Set to 0 to not wrap. A useful value here is 78, for RFC2822 section 2.1.1 compliance. * @var integer */ public $WordWrap = 0; - + /** * Which method to use to send mail. * Options: "mail", "sendmail", or "smtp". * @var string */ public $Mailer = 'mail'; - + /** * The path to the sendmail program. * @var string */ public $Sendmail = '/usr/sbin/sendmail'; - + /** * Whether mail() uses a fully sendmail-compatible MTA. * One which supports sendmail's "-oi -f" options. * @var boolean */ public $UseSendmailOptions = true; - + /** * Path to PHPMailer plugins. * Useful if the SMTP class is not in the PHP include path. @@ -182,13 +182,13 @@ class PHPMailer * @deprecated Should not be needed now there is an autoloader. */ public $PluginDir = ''; - + /** * The email address that a reading confirmation should be sent to, also known as read receipt. * @var string */ public $ConfirmReadingTo = ''; - + /** * The hostname to use in the Message-ID header and as default HELO string. * If empty, PHPMailer attempts to find one with, in order, @@ -197,7 +197,7 @@ class PHPMailer * @var string */ public $Hostname = ''; - + /** * An ID to be used in the Message-ID header. * If empty, a unique id will be generated. @@ -207,14 +207,14 @@ class PHPMailer * @var string */ public $MessageID = ''; - + /** * The message Date to be used in the Date header. * If empty, the current date will be added. * @var string */ public $MessageDate = ''; - + /** * SMTP hosts. * Either a single hostname or multiple semicolon-delimited hostnames. @@ -227,14 +227,14 @@ class PHPMailer * @var string */ public $Host = 'localhost'; - + /** * The default SMTP server port. * @var integer * @TODO Why is this needed when the SMTP class takes care of it? */ public $Port = 25; - + /** * The SMTP HELO of the message. * Default is $Hostname. If $Hostname is empty, PHPMailer attempts to find @@ -243,14 +243,14 @@ class PHPMailer * @see PHPMailer::$Hostname */ public $Helo = ''; - + /** * What kind of encryption to use on the SMTP connection. * Options: '', 'ssl' or 'tls' * @var string */ public $SMTPSecure = ''; - + /** * Whether to enable TLS encryption automatically if a server supports it, * even if `SMTPSecure` is not set to 'tls'. @@ -258,7 +258,7 @@ class PHPMailer * @var boolean */ public $SMTPAutoTLS = true; - + /** * Whether to use SMTP authentication. * Uses the Username and Password properties. @@ -267,53 +267,53 @@ class PHPMailer * @see PHPMailer::$Password */ public $SMTPAuth = false; - + /** * Options array passed to stream_context_create when connecting via SMTP. * @var array */ public $SMTPOptions = array(); - + /** * SMTP username. * @var string */ public $Username = ''; - + /** * SMTP password. * @var string */ public $Password = ''; - + /** * SMTP auth type. * Options are CRAM-MD5, LOGIN, PLAIN, NTLM, XOAUTH2, attempted in that order if not specified * @var string */ public $AuthType = ''; - + /** * SMTP realm. * Used for NTLM auth * @var string */ public $Realm = ''; - + /** * SMTP workstation. * Used for NTLM auth * @var string */ public $Workstation = ''; - + /** * The SMTP server timeout in seconds. * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2 * @var integer */ public $Timeout = 300; - + /** * SMTP class debug output mode. * Debug output level. @@ -327,7 +327,7 @@ class PHPMailer * @see SMTP::$do_debug */ public $SMTPDebug = 0; - + /** * How to handle debug output. * Options: @@ -343,7 +343,7 @@ class PHPMailer * @see SMTP::$Debugoutput */ public $Debugoutput = 'echo'; - + /** * Whether to keep SMTP connection open after each message. * If this is set to true then to close the connection @@ -351,7 +351,7 @@ class PHPMailer * @var boolean */ public $SMTPKeepAlive = false; - + /** * Whether to split multiple to addresses into multiple messages * or send them all in one message. @@ -359,14 +359,14 @@ class PHPMailer * @var boolean */ public $SingleTo = false; - + /** * Storage for addresses when SingleTo is enabled. * @var array * @TODO This should really not be public */ public $SingleToArray = array(); - + /** * Whether to generate VERP addresses on send. * Only applicable when sending via SMTP. @@ -375,13 +375,13 @@ class PHPMailer * @var boolean */ public $do_verp = false; - + /** * Whether to allow sending messages with an empty body. * @var boolean */ public $AllowEmpty = false; - + /** * The default line ending. * @note The default remains "\n". We force CRLF where we know @@ -389,47 +389,47 @@ class PHPMailer * @var string */ public $LE = "\n"; - + /** * DKIM selector. * @var string */ public $DKIM_selector = ''; - + /** * DKIM Identity. * Usually the email address used as the source of the email. * @var string */ public $DKIM_identity = ''; - + /** * DKIM passphrase. * Used if your key is encrypted. * @var string */ public $DKIM_passphrase = ''; - + /** * DKIM signing domain name. * @example 'example.com' * @var string */ public $DKIM_domain = ''; - + /** * DKIM private key file path. * @var string */ public $DKIM_private = ''; - + /** * DKIM private key string. * If set, takes precedence over `$DKIM_private`. * @var string */ public $DKIM_private_string = ''; - + /** * Callback Action function name. * @@ -440,23 +440,23 @@ class PHPMailer * * Parameters: * boolean $result result of the send action - * string $to email address of the recipient - * string $cc cc email addresses - * string $bcc bcc email addresses + * array $to email addresses of the recipients + * array $cc cc email addresses + * array $bcc bcc email addresses * string $subject the subject * string $body the email body * string $from email address of sender * @var string */ public $action_function = ''; - + /** * What to put in the X-Mailer header. * Options: An empty string for PHPMailer default, whitespace for none, or a string to use * @var string */ public $XMailer = ''; - + /** * Which validator to use by default when validating email addresses. * May be a callable to inject your own validator, but there are several built-in validators. @@ -465,42 +465,42 @@ class PHPMailer * @static */ public static $validator = 'auto'; - + /** * An instance of the SMTP sender class. * @var SMTP * @access protected */ protected $smtp = null; - + /** * The array of 'to' names and addresses. * @var array * @access protected */ protected $to = array(); - + /** * The array of 'cc' names and addresses. * @var array * @access protected */ protected $cc = array(); - + /** * The array of 'bcc' names and addresses. * @var array * @access protected */ protected $bcc = array(); - + /** * The array of reply-to names and addresses. * @var array * @access protected */ protected $ReplyTo = array(); - + /** * An array of all kinds of addresses. * Includes all of $to, $cc, $bcc @@ -509,7 +509,7 @@ class PHPMailer * @see PHPMailer::$to @see PHPMailer::$cc @see PHPMailer::$bcc */ protected $all_recipients = array(); - + /** * An array of names and addresses queued for validation. * In send(), valid and non duplicate entries are moved to $all_recipients @@ -521,7 +521,7 @@ class PHPMailer * @see PHPMailer::$all_recipients */ protected $RecipientsQueue = array(); - + /** * An array of reply-to names and addresses queued for validation. * In send(), valid and non duplicate entries are moved to $ReplyTo. @@ -531,77 +531,77 @@ class PHPMailer * @see PHPMailer::$ReplyTo */ protected $ReplyToQueue = array(); - + /** * The array of attachments. * @var array * @access protected */ protected $attachment = array(); - + /** * The array of custom headers. * @var array * @access protected */ protected $CustomHeader = array(); - + /** * The most recent Message-ID (including angular brackets). * @var string * @access protected */ protected $lastMessageID = ''; - + /** * The message's MIME type. * @var string * @access protected */ protected $message_type = ''; - + /** * The array of MIME boundary strings. * @var array * @access protected */ protected $boundary = array(); - + /** * The array of available languages. * @var array * @access protected */ protected $language = array(); - + /** * The number of errors encountered. * @var integer * @access protected */ protected $error_count = 0; - + /** * The S/MIME certificate file path. * @var string * @access protected */ protected $sign_cert_file = ''; - + /** * The S/MIME key file path. * @var string * @access protected */ protected $sign_key_file = ''; - + /** * The optional S/MIME extra certificates ("CA Chain") file path. * @var string * @access protected */ protected $sign_extracerts_file = ''; - + /** * The S/MIME password for the key. * Used only if the key is encrypted. @@ -609,47 +609,47 @@ class PHPMailer * @access protected */ protected $sign_key_pass = ''; - + /** * Whether to throw exceptions for errors. * @var boolean * @access protected */ protected $exceptions = false; - + /** * Unique ID used for message ID and boundaries. * @var string * @access protected */ protected $uniqueid = ''; - + /** * Error severity: message only, continue processing. */ const STOP_MESSAGE = 0; - + /** * Error severity: message, likely ok to continue processing. */ const STOP_CONTINUE = 1; - + /** * Error severity: message, plus full stop, critical error reached. */ const STOP_CRITICAL = 2; - + /** * SMTP RFC standard line ending. */ const CRLF = "\r\n"; - + /** * The maximum line length allowed by RFC 2822 section 2.1.1 * @var integer */ const MAX_LINE_LENGTH = 998; - + /** * Constructor. * @param boolean $exceptions Should we throw external exceptions? @@ -659,8 +659,10 @@ class PHPMailer if ($exceptions !== null) { $this->exceptions = (boolean)$exceptions; } + //Pick an appropriate debug output format automatically + $this->Debugoutput = (strpos(PHP_SAPI, 'cli') !== false ? 'echo' : 'html'); } - + /** * Destructor. */ @@ -669,7 +671,7 @@ class PHPMailer //Close any open SMTP connection nicely $this->smtpClose(); } - + /** * Call mail() in a safe_mode-aware fashion. * Also, unless sendmail_path points to sendmail (or something that @@ -691,7 +693,7 @@ class PHPMailer } else { $subject = $this->encodeHeader($this->secureHeader($subject)); } - + //Can't use additional_parameters in safe_mode, calling mail() with null params breaks //@link http://php.net/manual/en/function.mail.php if (ini_get('safe_mode') or !$this->UseSendmailOptions or is_null($params)) { @@ -743,7 +745,7 @@ class PHPMailer ) . "\n"; } } - + /** * Sets message type to HTML or plain. * @param boolean $isHtml True for HTML mode. @@ -757,7 +759,7 @@ class PHPMailer $this->ContentType = 'text/plain'; } } - + /** * Send messages using SMTP. * @return void @@ -766,7 +768,7 @@ class PHPMailer { $this->Mailer = 'smtp'; } - + /** * Send messages using PHP's mail() function. * @return void @@ -775,7 +777,7 @@ class PHPMailer { $this->Mailer = 'mail'; } - + /** * Send messages using $Sendmail. * @return void @@ -783,7 +785,7 @@ class PHPMailer public function isSendmail() { $ini_sendmail_path = ini_get('sendmail_path'); - + if (!stristr($ini_sendmail_path, 'sendmail')) { $this->Sendmail = '/usr/sbin/sendmail'; } else { @@ -791,7 +793,7 @@ class PHPMailer } $this->Mailer = 'sendmail'; } - + /** * Send messages using qmail. * @return void @@ -799,7 +801,7 @@ class PHPMailer public function isQmail() { $ini_sendmail_path = ini_get('sendmail_path'); - + if (!stristr($ini_sendmail_path, 'qmail')) { $this->Sendmail = '/var/qmail/bin/qmail-inject'; } else { @@ -807,7 +809,7 @@ class PHPMailer } $this->Mailer = 'qmail'; } - + /** * Add a "To" address. * @param string $address The email address to send to @@ -818,7 +820,7 @@ class PHPMailer { return $this->addOrEnqueueAnAddress('to', $address, $name); } - + /** * Add a "CC" address. * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer. @@ -830,7 +832,7 @@ class PHPMailer { return $this->addOrEnqueueAnAddress('cc', $address, $name); } - + /** * Add a "BCC" address. * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer. @@ -842,7 +844,7 @@ class PHPMailer { return $this->addOrEnqueueAnAddress('bcc', $address, $name); } - + /** * Add a "Reply-To" address. * @param string $address The email address to reply to @@ -853,7 +855,7 @@ class PHPMailer { return $this->addOrEnqueueAnAddress('Reply-To', $address, $name); } - + /** * Add an address to one of the recipient arrays or to the ReplyTo array. Because PHPMailer * can't validate addresses with an IDN without knowing the PHPMailer::$CharSet (that can still @@ -899,7 +901,7 @@ class PHPMailer // Immediately add standard addresses without IDN. return call_user_func_array(array($this, 'addAnAddress'), $params); } - + /** * Add an address to one of the recipient arrays or to the ReplyTo array. * Addresses that have been added already return false, but do not throw exceptions. @@ -944,7 +946,7 @@ class PHPMailer } return false; } - + /** * Parse and validate a string containing one or more RFC822-style comma-separated email addresses * of the form "display name
" into an array of name/address pairs. @@ -999,7 +1001,7 @@ class PHPMailer } return $addresses; } - + /** * Set the From and FromName properties. * @param string $address @@ -1033,7 +1035,7 @@ class PHPMailer } return true; } - + /** * Return the Message-ID header of the last email. * Technically this is the value from the last time the headers were created, @@ -1045,7 +1047,7 @@ class PHPMailer { return $this->lastMessageID; } - + /** * Check that a string looks like an email address. * @param string $address The email address to check @@ -1155,7 +1157,7 @@ class PHPMailer return (boolean)filter_var($address, FILTER_VALIDATE_EMAIL); } } - + /** * Tells whether IDNs (Internationalized Domain Names) are supported or not. This requires the * "intl" and "mbstring" PHP extensions. @@ -1166,7 +1168,7 @@ class PHPMailer // @TODO: Write our own "idn_to_ascii" function for PHP <= 5.2. return function_exists('idn_to_ascii') and function_exists('mb_convert_encoding'); } - + /** * Converts IDN in given email address to its ASCII form, also known as punycode, if possible. * Important: Address must be passed in same encoding as currently set in PHPMailer::$CharSet. @@ -1197,7 +1199,7 @@ class PHPMailer } return $address; } - + /** * Create a message and send it. * Uses the sending method specified by $Mailer. @@ -1220,7 +1222,7 @@ class PHPMailer return false; } } - + /** * Prepare a message for sending. * @throws phpmailerException @@ -1231,7 +1233,7 @@ class PHPMailer try { $this->error_count = 0; // Reset errors $this->mailHeader = ''; - + // Dequeue recipient and Reply-To addresses with IDN foreach (array_merge($this->RecipientsQueue, $this->ReplyToQueue) as $params) { $params[1] = $this->punyencodeAddress($params[1]); @@ -1240,7 +1242,7 @@ class PHPMailer if ((count($this->to) + count($this->cc) + count($this->bcc)) < 1) { throw new phpmailerException($this->lang('provide_address'), self::STOP_CRITICAL); } - + // Validate From, Sender, and ConfirmReadingTo addresses foreach (array('From', 'Sender', 'ConfirmReadingTo') as $address_kind) { $this->$address_kind = trim($this->$address_kind); @@ -1258,18 +1260,18 @@ class PHPMailer return false; } } - + // Set whether the message is multipart/alternative if ($this->alternativeExists()) { $this->ContentType = 'multipart/alternative'; } - + $this->setMessageType(); // Refuse to send an empty message unless we are specifically allowing it if (!$this->AllowEmpty and empty($this->Body)) { throw new phpmailerException($this->lang('empty_message'), self::STOP_CRITICAL); } - + // Create body before headers in case body makes changes to headers (e.g. altering transfer encoding) $this->MIMEHeader = ''; $this->MIMEBody = $this->createBody(); @@ -1277,7 +1279,7 @@ class PHPMailer $tempheaders = $this->MIMEHeader; $this->MIMEHeader = $this->createHeader(); $this->MIMEHeader .= $tempheaders; - + // To capture the complete message when using mail(), create // an extra header list which createHeader() doesn't fold in if ($this->Mailer == 'mail') { @@ -1291,7 +1293,7 @@ class PHPMailer $this->encodeHeader($this->secureHeader(trim($this->Subject))) ); } - + // Sign with DKIM if enabled if (!empty($this->DKIM_domain) && !empty($this->DKIM_selector) @@ -1316,7 +1318,7 @@ class PHPMailer return false; } } - + /** * Actually send a message. * Send the email via the selected mechanism @@ -1340,7 +1342,7 @@ class PHPMailer if (method_exists($this, $sendMethod)) { return $this->$sendMethod($this->MIMEHeader, $this->MIMEBody); } - + return $this->mailSend($this->MIMEHeader, $this->MIMEBody); } } catch (phpmailerException $exc) { @@ -1352,7 +1354,7 @@ class PHPMailer } return false; } - + /** * Send mail using the $Sendmail program. * @param string $header The message headers @@ -1378,10 +1380,10 @@ class PHPMailer $sendmailFmt = '%s -oi -t'; } } - + // TODO: If possible, this should be changed to escapeshellarg. Needs thorough testing. $sendmail = sprintf($sendmailFmt, escapeshellcmd($this->Sendmail), $this->Sender); - + if ($this->SingleTo) { foreach ($this->SingleToArray as $toAddr) { if (!@$mail = popen($sendmail, 'w')) { @@ -1426,7 +1428,7 @@ class PHPMailer } return true; } - + /** * Fix CVE-2016-10033 and CVE-2016-10045 by disallowing potentially unsafe shell characters. * @@ -1444,12 +1446,12 @@ class PHPMailer ) { return false; } - + $length = strlen($string); - + for ($i = 0; $i < $length; $i++) { $c = $string[$i]; - + // All other characters have a special meaning in at least one common shell, including = and +. // Full stop (.) has a special meaning in cmd.exe, but its impact should be negligible here. // Note that this does permit non-Latin alphanumeric characters based on the current locale. @@ -1457,10 +1459,10 @@ class PHPMailer return false; } } - + return true; } - + /** * Send mail using the PHP mail() function. * @param string $header The message headers @@ -1477,7 +1479,7 @@ class PHPMailer $toArr[] = $this->addrFormat($toaddr); } $to = implode(', ', $toArr); - + $params = null; //This sets the SMTP envelope sender which gets turned into a return-path header by the receiver if (!empty($this->Sender) and $this->validateAddress($this->Sender)) { @@ -1508,7 +1510,7 @@ class PHPMailer } return true; } - + /** * Get an instance to use for SMTP operations. * Override this function to load your own SMTP implementation @@ -1521,7 +1523,7 @@ class PHPMailer } return $this->smtp; } - + /** * Send mail via SMTP. * Returns false if there is a bad MAIL FROM, RCPT, or DATA input. @@ -1549,7 +1551,7 @@ class PHPMailer $this->setError($this->lang('from_failed') . $smtp_from . ' : ' . implode(',', $this->smtp->getError())); throw new phpmailerException($this->ErrorInfo, self::STOP_CRITICAL); } - + // Attempt to send to all recipients foreach (array($this->to, $this->cc, $this->bcc) as $togroup) { foreach ($togroup as $to) { @@ -1563,7 +1565,7 @@ class PHPMailer $this->doCallback($isSent, array($to[0]), array(), array(), $this->Subject, $body, $this->From); } } - + // Only send the DATA command if we have viable recipients if ((count($this->all_recipients) > count($bad_rcpt)) and !$this->smtp->data($header . $body)) { throw new phpmailerException($this->lang('data_not_accepted'), self::STOP_CRITICAL); @@ -1587,7 +1589,7 @@ class PHPMailer } return true; } - + /** * Initiate a connection to an SMTP server. * Returns false if the operation failed. @@ -1602,104 +1604,109 @@ class PHPMailer if (is_null($this->smtp)) { $this->smtp = $this->getSMTPInstance(); } - + //If no options are provided, use whatever is set in the instance if (is_null($options)) { $options = $this->SMTPOptions; } - + // Already connected? if ($this->smtp->connected()) { return true; } - + $this->smtp->setTimeout($this->Timeout); $this->smtp->setDebugLevel($this->SMTPDebug); $this->smtp->setDebugOutput($this->Debugoutput); $this->smtp->setVerp($this->do_verp); $hosts = explode(';', $this->Host); $lastexception = null; - + foreach ($hosts as $hostentry) { $hostinfo = array(); - if (!preg_match('/^((ssl|tls):\/\/)*([a-zA-Z0-9\.-]*):?([0-9]*)$/', trim($hostentry), $hostinfo)) { - // Not a valid host entry - continue; - } - // $hostinfo[2]: optional ssl or tls prefix - // $hostinfo[3]: the hostname - // $hostinfo[4]: optional port number - // The host string prefix can temporarily override the current setting for SMTPSecure - // If it's not specified, the default value is used - $prefix = ''; - $secure = $this->SMTPSecure; - $tls = ($this->SMTPSecure == 'tls'); - if ('ssl' == $hostinfo[2] or ('' == $hostinfo[2] and 'ssl' == $this->SMTPSecure)) { - $prefix = 'ssl://'; - $tls = false; // Can't have SSL and TLS at the same time - $secure = 'ssl'; - } elseif ($hostinfo[2] == 'tls') { - $tls = true; - // tls doesn't use a prefix - $secure = 'tls'; - } - //Do we need the OpenSSL extension? - $sslext = defined('OPENSSL_ALGO_SHA1'); - if ('tls' === $secure or 'ssl' === $secure) { - //Check for an OpenSSL constant rather than using extension_loaded, which is sometimes disabled - if (!$sslext) { - throw new phpmailerException($this->lang('extension_missing').'openssl', self::STOP_CRITICAL); + if (!preg_match( + '/^((ssl|tls):\/\/)*([a-zA-Z0-9\.-]*|\[[a-fA-F0-9:]+\]):?([0-9]*)$/', + trim($hostentry), + $hostinfo + )) { + // Not a valid host entry + $this->edebug('Ignoring invalid host: ' . $hostentry); + continue; } - } - $host = $hostinfo[3]; - $port = $this->Port; - $tport = (integer)$hostinfo[4]; - if ($tport > 0 and $tport < 65536) { - $port = $tport; - } - if ($this->smtp->connect($prefix . $host, $port, $this->Timeout, $options)) { - try { - if ($this->Helo) { - $hello = $this->Helo; - } else { - $hello = $this->serverHostname(); + // $hostinfo[2]: optional ssl or tls prefix + // $hostinfo[3]: the hostname + // $hostinfo[4]: optional port number + // The host string prefix can temporarily override the current setting for SMTPSecure + // If it's not specified, the default value is used + $prefix = ''; + $secure = $this->SMTPSecure; + $tls = ($this->SMTPSecure == 'tls'); + if ('ssl' == $hostinfo[2] or ('' == $hostinfo[2] and 'ssl' == $this->SMTPSecure)) { + $prefix = 'ssl://'; + $tls = false; // Can't have SSL and TLS at the same time + $secure = 'ssl'; + } elseif ($hostinfo[2] == 'tls') { + $tls = true; + // tls doesn't use a prefix + $secure = 'tls'; + } + //Do we need the OpenSSL extension? + $sslext = defined('OPENSSL_ALGO_SHA1'); + if ('tls' === $secure or 'ssl' === $secure) { + //Check for an OpenSSL constant rather than using extension_loaded, which is sometimes disabled + if (!$sslext) { + throw new phpmailerException($this->lang('extension_missing').'openssl', self::STOP_CRITICAL); } - $this->smtp->hello($hello); - //Automatically enable TLS encryption if: - // * it's not disabled - // * we have openssl extension - // * we are not already using SSL - // * the server offers STARTTLS - if ($this->SMTPAutoTLS and $sslext and $secure != 'ssl' and $this->smtp->getServerExt('STARTTLS')) { - $tls = true; - } - if ($tls) { - if (!$this->smtp->startTLS()) { - throw new phpmailerException($this->lang('connect_host')); + } + $host = $hostinfo[3]; + $port = $this->Port; + $tport = (integer)$hostinfo[4]; + if ($tport > 0 and $tport < 65536) { + $port = $tport; + } + if ($this->smtp->connect($prefix . $host, $port, $this->Timeout, $options)) { + try { + if ($this->Helo) { + $hello = $this->Helo; + } else { + $hello = $this->serverHostname(); } - // We must resend EHLO after TLS negotiation $this->smtp->hello($hello); - } - if ($this->SMTPAuth) { - if (!$this->smtp->authenticate( - $this->Username, - $this->Password, - $this->AuthType, - $this->Realm, - $this->Workstation - ) - ) { - throw new phpmailerException($this->lang('authenticate')); + //Automatically enable TLS encryption if: + // * it's not disabled + // * we have openssl extension + // * we are not already using SSL + // * the server offers STARTTLS + if ($this->SMTPAutoTLS and $sslext and $secure != 'ssl' and $this->smtp->getServerExt('STARTTLS')) { + $tls = true; + } + if ($tls) { + if (!$this->smtp->startTLS()) { + throw new phpmailerException($this->lang('connect_host')); } + // We must resend EHLO after TLS negotiation + $this->smtp->hello($hello); + } + if ($this->SMTPAuth) { + if (!$this->smtp->authenticate( + $this->Username, + $this->Password, + $this->AuthType, + $this->Realm, + $this->Workstation + ) + ) { + throw new phpmailerException($this->lang('authenticate')); + } + } + return true; + } catch (phpmailerException $exc) { + $lastexception = $exc; + $this->edebug($exc->getMessage()); + // We must have connected, but then failed TLS or Auth, so close connection nicely + $this->smtp->quit(); } - return true; - } catch (phpmailerException $exc) { - $lastexception = $exc; - $this->edebug($exc->getMessage()); - // We must have connected, but then failed TLS or Auth, so close connection nicely - $this->smtp->quit(); } - } } // If we get here, all connection attempts have failed, so close connection hard $this->smtp->close(); @@ -1709,7 +1716,7 @@ class PHPMailer } return false; } - + /** * Close the active SMTP session if one exists. * @return void @@ -1723,7 +1730,7 @@ class PHPMailer } } } - + /** * Set the language for error messages. * Returns false if it cannot load the language file. @@ -1742,12 +1749,13 @@ class PHPMailer 'dk' => 'da', 'no' => 'nb', 'se' => 'sv', + 'sr' => 'rs' ); - + if (isset($renamed_langcodes[$langcode])) { $langcode = $renamed_langcodes[$langcode]; } - + // Define full set of translatable strings in English $PHPMAILER_LANG = array( 'authenticate' => 'SMTP Error: Could not authenticate.', @@ -1794,7 +1802,7 @@ class PHPMailer $this->language = $PHPMAILER_LANG; return (boolean)$foundlang; // Returns false if language not found } - + /** * Get the array of strings for the current language. * @return array @@ -1803,7 +1811,7 @@ class PHPMailer { return $this->language; } - + /** * Create recipient headers. * @access public @@ -1822,7 +1830,7 @@ class PHPMailer } return $type . ': ' . implode(', ', $addresses) . $this->LE; } - + /** * Format an address for use in a message header. * @access public @@ -1840,7 +1848,7 @@ class PHPMailer ) . '>'; } } - + /** * Word-wrap message. * For use with mailers that do not automatically perform wrapping @@ -1864,13 +1872,13 @@ class PHPMailer $is_utf8 = (strtolower($this->CharSet) == 'utf-8'); $lelen = strlen($this->LE); $crlflen = strlen(self::CRLF); - + $message = $this->fixEOL($message); //Remove a trailing line break if (substr($message, -$lelen) == $this->LE) { $message = substr($message, 0, -$lelen); } - + //Split message into lines $lines = explode($this->LE, $message); //Message will be rebuilt in here @@ -1915,7 +1923,7 @@ class PHPMailer } $part = substr($word, 0, $len); $word = substr($word, $len); - + if (strlen($word) > 0) { $message .= $part . sprintf('=%s', self::CRLF); } else { @@ -1928,7 +1936,7 @@ class PHPMailer $buf .= ' '; } $buf .= $word; - + if (strlen($buf) > $length and $buf_o != '') { $message .= $buf_o . $soft_break; $buf = $word; @@ -1938,10 +1946,10 @@ class PHPMailer } $message .= $buf . self::CRLF; } - + return $message; } - + /** * Find the last character boundary prior to $maxLength in a utf-8 * quoted-printable encoded string. @@ -1987,7 +1995,7 @@ class PHPMailer } return $maxLength; } - + /** * Apply word wrapping to the message body. * Wraps the message body to the number of chars set in the WordWrap property. @@ -2001,7 +2009,7 @@ class PHPMailer if ($this->WordWrap < 1) { return; } - + switch ($this->message_type) { case 'alt': case 'alt_inline': @@ -2014,7 +2022,7 @@ class PHPMailer break; } } - + /** * Assemble message headers. * @access public @@ -2023,12 +2031,9 @@ class PHPMailer public function createHeader() { $result = ''; - - if ($this->MessageDate == '') { - $this->MessageDate = self::rfcDate(); - } - $result .= $this->headerLine('Date', $this->MessageDate); - + + $result .= $this->headerLine('Date', $this->MessageDate == '' ? self::rfcDate() : $this->MessageDate); + // To be created automatically by mail() if ($this->SingleTo) { if ($this->Mailer != 'mail') { @@ -2045,14 +2050,14 @@ class PHPMailer $result .= $this->headerLine('To', 'undisclosed-recipients:;'); } } - + $result .= $this->addrAppend('From', array(array(trim($this->From), $this->FromName))); - + // sendmail and mail() extract Cc from the header before sending if (count($this->cc) > 0) { $result .= $this->addrAppend('Cc', $this->cc); } - + // sendmail and mail() extract Bcc from the header before sending if (( $this->Mailer == 'sendmail' or $this->Mailer == 'qmail' or $this->Mailer == 'mail' @@ -2061,16 +2066,16 @@ class PHPMailer ) { $result .= $this->addrAppend('Bcc', $this->bcc); } - + if (count($this->ReplyTo) > 0) { $result .= $this->addrAppend('Reply-To', $this->ReplyTo); } - + // mail() sets the subject itself if ($this->Mailer != 'mail') { $result .= $this->headerLine('Subject', $this->encodeHeader($this->secureHeader($this->Subject))); } - + // Only allow a custom message ID if it conforms to RFC 5322 section 3.6.4 // https://tools.ietf.org/html/rfc5322#section-3.6.4 if ('' != $this->MessageID and preg_match('/^<.*@.*>$/', $this->MessageID)) { @@ -2093,11 +2098,11 @@ class PHPMailer $result .= $this->headerLine('X-Mailer', $myXmailer); } } - + if ($this->ConfirmReadingTo != '') { $result .= $this->headerLine('Disposition-Notification-To', '<' . $this->ConfirmReadingTo . '>'); } - + // Add custom headers foreach ($this->CustomHeader as $header) { $result .= $this->headerLine( @@ -2109,10 +2114,10 @@ class PHPMailer $result .= $this->headerLine('MIME-Version', '1.0'); $result .= $this->getMailMIME(); } - + return $result; } - + /** * Get the message MIME type headers. * @access public @@ -2157,14 +2162,14 @@ class PHPMailer $result .= $this->headerLine('Content-Transfer-Encoding', $this->Encoding); } } - + if ($this->Mailer != 'mail') { $result .= $this->LE; } - + return $result; } - + /** * Returns the whole MIME message. * Includes complete headers and body. @@ -2177,7 +2182,7 @@ class PHPMailer { return rtrim($this->MIMEHeader . $this->mailHeader, "\n\r") . self::CRLF . self::CRLF . $this->MIMEBody; } - + /** * Create unique ID * @return string @@ -2185,7 +2190,7 @@ class PHPMailer protected function generateId() { return md5(uniqid(time())); } - + /** * Assemble the message body. * Returns an empty string on failure. @@ -2201,13 +2206,13 @@ class PHPMailer $this->boundary[1] = 'b1_' . $this->uniqueid; $this->boundary[2] = 'b2_' . $this->uniqueid; $this->boundary[3] = 'b3_' . $this->uniqueid; - + if ($this->sign_key_file) { $body .= $this->getMailMIME() . $this->LE; } - + $this->setWordWrap(); - + $bodyEncoding = $this->Encoding; $bodyCharSet = $this->CharSet; //Can we do a 7-bit downgrade? @@ -2221,7 +2226,7 @@ class PHPMailer if ('base64' != $this->Encoding and self::hasLineLongerThanMax($this->Body)) { $bodyEncoding = 'quoted-printable'; } - + $altBodyEncoding = $this->Encoding; $altBodyCharSet = $this->CharSet; //Can we do a 7-bit downgrade? @@ -2341,7 +2346,7 @@ class PHPMailer $body .= $this->encodeString($this->Body, $this->Encoding); break; } - + if ($this->isError()) { $body = ''; } elseif ($this->sign_key_file) { @@ -2397,7 +2402,7 @@ class PHPMailer } return $body; } - + /** * Return the start of a message boundary. * @access protected @@ -2427,10 +2432,10 @@ class PHPMailer $result .= $this->headerLine('Content-Transfer-Encoding', $encoding); } $result .= $this->LE; - + return $result; } - + /** * Return the end of a message boundary. * @access protected @@ -2441,7 +2446,7 @@ class PHPMailer { return $this->LE . '--' . $boundary . '--' . $this->LE; } - + /** * Set the message type. * PHPMailer only supports some preset message types, not arbitrary MIME structures. @@ -2466,7 +2471,7 @@ class PHPMailer $this->message_type = 'plain'; } } - + /** * Format a header line. * @access public @@ -2478,7 +2483,7 @@ class PHPMailer { return $name . ': ' . $value . $this->LE; } - + /** * Return a formatted mail line. * @access public @@ -2489,9 +2494,10 @@ class PHPMailer { return $value . $this->LE; } - + /** * Add an attachment from a path on the filesystem. + * Never use a user-supplied path to a file! * Returns false if the file could not be found or read. * @param string $path Path to the attachment. * @param string $name Overrides the attachment name. @@ -2507,17 +2513,17 @@ class PHPMailer if (!@is_file($path)) { throw new phpmailerException($this->lang('file_access') . $path, self::STOP_CONTINUE); } - + // If a MIME type is not specified, try to work it out from the file name if ($type == '') { $type = self::filenameToType($path); } - + $filename = basename($path); if ($name == '') { $name = $filename; } - + $this->attachment[] = array( 0 => $path, 1 => $filename, @@ -2528,7 +2534,7 @@ class PHPMailer 6 => $disposition, 7 => 0 ); - + } catch (phpmailerException $exc) { $this->setError($exc->getMessage()); $this->edebug($exc->getMessage()); @@ -2539,7 +2545,7 @@ class PHPMailer } return true; } - + /** * Return the array of attachments. * @return array @@ -2548,7 +2554,7 @@ class PHPMailer { return $this->attachment; } - + /** * Attach all file, string, and binary attachments to the message. * Returns an empty string on failure. @@ -2563,7 +2569,7 @@ class PHPMailer $mime = array(); $cidUniq = array(); $incl = array(); - + // Add all attachments foreach ($this->attachment as $attachment) { // Check if it is a valid disposition_filter @@ -2577,7 +2583,7 @@ class PHPMailer } else { $path = $attachment[0]; } - + $inclhash = md5(serialize($attachment)); if (in_array($inclhash, $incl)) { continue; @@ -2592,7 +2598,7 @@ class PHPMailer continue; } $cidUniq[$cid] = true; - + $mime[] = sprintf('--%s%s', $boundary, $this->LE); //Only include a filename property if we have one if (!empty($name)) { @@ -2613,11 +2619,11 @@ class PHPMailer if ($encoding != '7bit') { $mime[] = sprintf('Content-Transfer-Encoding: %s%s', $encoding, $this->LE); } - + if ($disposition == 'inline') { $mime[] = sprintf('Content-ID: <%s>%s', $cid, $this->LE); } - + // If a filename contains any of these chars, it should be quoted, // but not otherwise: RFC2183 & RFC2045 5.1 // Fixes a warning in IETF's msglint MIME checker @@ -2650,7 +2656,7 @@ class PHPMailer } else { $mime[] = $this->LE; } - + // Encode as string attachment if ($bString) { $mime[] = $this->encodeString($string, $encoding); @@ -2667,12 +2673,12 @@ class PHPMailer } } } - + $mime[] = sprintf('--%s--%s', $boundary, $this->LE); - + return implode('', $mime); } - + /** * Encode a file attachment in requested format. * Returns an empty string on failure. @@ -2714,7 +2720,7 @@ class PHPMailer return ''; } } - + /** * Encode a string in requested format. * Returns an empty string on failure. @@ -2750,7 +2756,7 @@ class PHPMailer } return $encoded; } - + /** * Encode a header string optimally. * Picks shortest of Q, B, quoted-printable or none. @@ -2784,12 +2790,12 @@ class PHPMailer $matchcount += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches); break; } - + //There are no chars that need encoding if ($matchcount == 0) { return ($str); } - + $maxlen = 75 - 7 - strlen($this->CharSet); // Try to select the encoding which should produce the shortest output if ($matchcount > strlen($str) / 3) { @@ -2810,13 +2816,13 @@ class PHPMailer $encoded = $this->wrapText($encoded, $maxlen, true); $encoded = str_replace('=' . self::CRLF, "\n", trim($encoded)); } - + $encoded = preg_replace('/^(.*)$/m', ' =?' . $this->CharSet . "?$encoding?\\1?=", $encoded); $encoded = trim(str_replace("\n", $this->LE, $encoded)); - + return $encoded; } - + /** * Check if a string contains multi-byte characters. * @access public @@ -2831,7 +2837,7 @@ class PHPMailer return false; } } - + /** * Does a string contain any 8-bit chars (in any charset)? * @param string $text @@ -2841,7 +2847,7 @@ class PHPMailer { return (boolean)preg_match('/[\x80-\xFF]/', $text); } - + /** * Encode and wrap long multibyte strings for mail headers * without breaking lines within a character. @@ -2860,7 +2866,7 @@ class PHPMailer if ($linebreak === null) { $linebreak = $this->LE; } - + $mb_length = mb_strlen($str, $this->CharSet); // Each line must have length <= 75, including $start and $end $length = 75 - strlen($start) - strlen($end); @@ -2868,7 +2874,7 @@ class PHPMailer $ratio = $mb_length / strlen($str); // Base64 has a 4:3 ratio $avgLength = floor($length * $ratio * .75); - + for ($i = 0; $i < $mb_length; $i += $offset) { $lookBack = 0; do { @@ -2879,12 +2885,12 @@ class PHPMailer } while (strlen($chunk) > $length); $encoded .= $chunk . $linebreak; } - + // Chomp the last linefeed $encoded = substr($encoded, 0, -strlen($linebreak)); return $encoded; } - + /** * Encode a string in quoted-printable format. * According to RFC2045 section 6.7. @@ -2908,7 +2914,7 @@ class PHPMailer ); return preg_replace('/[^\r\n]{' . ($line_max - 3) . '}[^=\r\n]{2}/', "$0=\r\n", $string); } - + /** * Backward compatibility wrapper for an old QP encoding function that was removed. * @see PHPMailer::encodeQP() @@ -2926,7 +2932,7 @@ class PHPMailer ) { return $this->encodeQP($string, $line_max); } - + /** * Encode a string using Q encoding. * @link http://tools.ietf.org/html/rfc2047 @@ -2974,7 +2980,7 @@ class PHPMailer // Replace every spaces to _ (more readable than =20) return str_replace(' ', '_', $encoded); } - + /** * Add a string or binary attachment (non-filesystem). * This method can be used to attach ascii or binary data, @@ -3009,7 +3015,7 @@ class PHPMailer 7 => 0 ); } - + /** * Add an embedded (inline) attachment from a file. * This can include images, sounds, and just about any other document type. @@ -3017,6 +3023,7 @@ class PHPMailer * displayed inline with the message, not just attached for download. * This is used in HTML messages that embed the images * the HTML refers to using the $cid value. + * Never use a user-supplied path to a file! * @param string $path Path to the attachment. * @param string $cid Content ID of the attachment; Use this to reference * the content when using an embedded image in HTML. @@ -3032,17 +3039,17 @@ class PHPMailer $this->setError($this->lang('file_access') . $path); return false; } - + // If a MIME type is not specified, try to work it out from the file name if ($type == '') { $type = self::filenameToType($path); } - + $filename = basename($path); if ($name == '') { $name = $filename; } - + // Append to $attachment array $this->attachment[] = array( 0 => $path, @@ -3056,7 +3063,7 @@ class PHPMailer ); return true; } - + /** * Add an embedded stringified attachment. * This can include images, sounds, and just about any other document type. @@ -3083,7 +3090,7 @@ class PHPMailer if ($type == '' and !empty($name)) { $type = self::filenameToType($name); } - + // Append to $attachment array $this->attachment[] = array( 0 => $string, @@ -3097,7 +3104,7 @@ class PHPMailer ); return true; } - + /** * Check if an inline attachment is present. * @access public @@ -3112,7 +3119,7 @@ class PHPMailer } return false; } - + /** * Check if an attachment (non-inline) is present. * @return boolean @@ -3126,7 +3133,7 @@ class PHPMailer } return false; } - + /** * Check if this message has an alternative body set. * @return boolean @@ -3135,7 +3142,7 @@ class PHPMailer { return !empty($this->AltBody); } - + /** * Clear queued addresses of given kind. * @access protected @@ -3151,7 +3158,7 @@ class PHPMailer } } } - + /** * Clear all To recipients. * @return void @@ -3164,7 +3171,7 @@ class PHPMailer $this->to = array(); $this->clearQueuedAddresses('to'); } - + /** * Clear all CC recipients. * @return void @@ -3177,7 +3184,7 @@ class PHPMailer $this->cc = array(); $this->clearQueuedAddresses('cc'); } - + /** * Clear all BCC recipients. * @return void @@ -3190,7 +3197,7 @@ class PHPMailer $this->bcc = array(); $this->clearQueuedAddresses('bcc'); } - + /** * Clear all ReplyTo recipients. * @return void @@ -3200,7 +3207,7 @@ class PHPMailer $this->ReplyTo = array(); $this->ReplyToQueue = array(); } - + /** * Clear all recipient types. * @return void @@ -3213,7 +3220,7 @@ class PHPMailer $this->all_recipients = array(); $this->RecipientsQueue = array(); } - + /** * Clear all filesystem, string, and binary attachments. * @return void @@ -3222,7 +3229,7 @@ class PHPMailer { $this->attachment = array(); } - + /** * Clear all custom headers. * @return void @@ -3231,7 +3238,7 @@ class PHPMailer { $this->CustomHeader = array(); } - + /** * Add an error message to the error container. * @access protected @@ -3258,7 +3265,7 @@ class PHPMailer } $this->ErrorInfo = $msg; } - + /** * Return an RFC 822 formatted date. * @access public @@ -3272,7 +3279,7 @@ class PHPMailer date_default_timezone_set(@date_default_timezone_get()); return date('D, j M Y H:i:s O'); } - + /** * Get the server hostname. * Returns 'localhost.localdomain' if unknown. @@ -3293,7 +3300,7 @@ class PHPMailer } return $result; } - + /** * Get an error message in the current language. * @access protected @@ -3305,7 +3312,7 @@ class PHPMailer if (count($this->language) < 1) { $this->setLanguage('en'); // set the default language } - + if (array_key_exists($key, $this->language)) { if ($key == 'smtp_connect_failed') { //Include a link to troubleshooting docs on SMTP connection failure @@ -3319,7 +3326,7 @@ class PHPMailer return $key; } } - + /** * Check if an error occurred. * @access public @@ -3329,7 +3336,7 @@ class PHPMailer { return ($this->error_count > 0); } - + /** * Ensure consistent line endings in a string. * Changes every end of line from CRLF, CR or LF to $this->LE. @@ -3347,7 +3354,7 @@ class PHPMailer } return $nstr; } - + /** * Add a custom header. * $name value can be overloaded to contain @@ -3366,7 +3373,7 @@ class PHPMailer $this->CustomHeader[] = array($name, $value); } } - + /** * Returns all custom headers. * @return array @@ -3375,17 +3382,19 @@ class PHPMailer { return $this->CustomHeader; } - + /** * Create a message body from an HTML string. * Automatically inlines images and creates a plain-text version by converting the HTML, * overwriting any existing values in Body and AltBody. - * $basedir is used when handling relative image paths, e.g. + * Do not source $message content from user input! + * $basedir is prepended when handling relative URLs, e.g. and must not be empty * will look for an image file in $basedir/images/a.png and convert it to inline. - * If you don't want to apply these transformations to your HTML, just set Body and AltBody yourself. + * If you don't provide a $basedir, relative paths will be left untouched (and thus probably break in email) + * If you don't want to apply these transformations to your HTML, just set Body and AltBody directly. * @access public * @param string $message HTML message string - * @param string $basedir base directory for relative paths to images + * @param string $basedir Absolute path to a base directory to prepend to relative paths to images * @param boolean|callable $advanced Whether to use the internal HTML to text converter * or your own custom converter @see PHPMailer::html2text() * @return string $message The transformed message Body @@ -3394,6 +3403,10 @@ class PHPMailer { preg_match_all('/(src|background)=["\'](.*)["\']/Ui', $message, $images); if (array_key_exists(2, $images)) { + if (strlen($basedir) > 1 && substr($basedir, -1) != '/') { + // Ensure $basedir has a trailing / + $basedir .= '/'; + } foreach ($images[2] as $imgindex => $url) { // Convert data URIs into embedded images if (preg_match('#^data:(image[^;,]*)(;base64)?,#', $url, $match)) { @@ -3411,36 +3424,42 @@ class PHPMailer $message ); } - } elseif (substr($url, 0, 4) !== 'cid:' && !preg_match('#^[a-z][a-z0-9+.-]*://#i', $url)) { - // Do not change urls for absolute images (thanks to corvuscorax) - // Do not change urls that are already inline images - $filename = basename($url); - $directory = dirname($url); - if ($directory == '.') { - $directory = ''; - } - $cid = md5($url) . '@phpmailer.0'; // RFC2392 S 2 - if (strlen($basedir) > 1 && substr($basedir, -1) != '/') { - $basedir .= '/'; - } - if (strlen($directory) > 1 && substr($directory, -1) != '/') { - $directory .= '/'; - } - if ($this->addEmbeddedImage( - $basedir . $directory . $filename, - $cid, - $filename, - 'base64', - self::_mime_types((string)self::mb_pathinfo($filename, PATHINFO_EXTENSION)) - ) - ) { - $message = preg_replace( - '/' . $images[1][$imgindex] . '=["\']' . preg_quote($url, '/') . '["\']/Ui', - $images[1][$imgindex] . '="cid:' . $cid . '"', - $message - ); - } + continue; } + if ( + // Only process relative URLs if a basedir is provided (i.e. no absolute local paths) + !empty($basedir) + // Ignore URLs containing parent dir traversal (..) + && (strpos($url, '..') === false) + // Do not change urls that are already inline images + && substr($url, 0, 4) !== 'cid:' + // Do not change absolute URLs, including anonymous protocol + && !preg_match('#^[a-z][a-z0-9+.-]*:?//#i', $url) + ) { + $filename = basename($url); + $directory = dirname($url); + if ($directory == '.') { + $directory = ''; + } + $cid = md5($url) . '@phpmailer.0'; // RFC2392 S 2 + if (strlen($directory) > 1 && substr($directory, -1) != '/') { + $directory .= '/'; + } + if ($this->addEmbeddedImage( + $basedir . $directory . $filename, + $cid, + $filename, + 'base64', + self::_mime_types((string)self::mb_pathinfo($filename, PATHINFO_EXTENSION)) + ) + ) { + $message = preg_replace( + '/' . $images[1][$imgindex] . '=["\']' . preg_quote($url, '/') . '["\']/Ui', + $images[1][$imgindex] . '="cid:' . $cid . '"', + $message + ); + } + } } } $this->isHTML(true); @@ -3453,7 +3472,7 @@ class PHPMailer } return $this->Body; } - + /** * Convert an HTML string into plain text. * This is used by msgHTML(). @@ -3485,7 +3504,7 @@ class PHPMailer $this->CharSet ); } - + /** * Get the MIME type for a file extension. * @param string $ext File extension @@ -3600,7 +3619,7 @@ class PHPMailer } return 'application/octet-stream'; } - + /** * Map a file name to a MIME type. * Defaults to 'application/octet-stream', i.e.. arbitrary binary data. @@ -3618,7 +3637,7 @@ class PHPMailer $pathinfo = self::mb_pathinfo($filename); return self::_mime_types($pathinfo['extension']); } - + /** * Multi-byte-safe pathinfo replacement. * Drop-in replacement for pathinfo(), but multibyte-safe, cross-platform-safe, old-version-safe. @@ -3665,7 +3684,7 @@ class PHPMailer return $ret; } } - + /** * Set or reset instance properties. * You should avoid this function - it's more verbose, less efficient, more error-prone and @@ -3690,7 +3709,7 @@ class PHPMailer return false; } } - + /** * Strip newlines to prevent header injection. * @access public @@ -3701,7 +3720,7 @@ class PHPMailer { return trim(str_replace(array("\r", "\n"), '', $str)); } - + /** * Normalize line breaks in a string. * Converts UNIX LF, Mac CR and Windows CRLF line breaks into a single line break format. @@ -3716,7 +3735,7 @@ class PHPMailer { return preg_replace('/(\r\n|\r|\n)/ms', $breaktype, $text); } - + /** * Set the public and private key files and password for S/MIME signing. * @access public @@ -3732,7 +3751,7 @@ class PHPMailer $this->sign_key_pass = $key_pass; $this->sign_extracerts_file = $extracerts_filename; } - + /** * Quoted-Printable-encode a DKIM header. * @access public @@ -3752,7 +3771,7 @@ class PHPMailer } return $line; } - + /** * Generate a DKIM signature. * @access public @@ -3790,7 +3809,7 @@ class PHPMailer $t = '3031300d060960864801650304020105000420' . $hash; $pslen = $pinfo['bits'] / 8 - (strlen($t) / 2 + 3); $eb = pack('H*', '0001' . str_repeat('FF', $pslen) . '00' . $t); - + if (openssl_private_encrypt($eb, $signature, $privKey, OPENSSL_NO_PADDING)) { openssl_pkey_free($privKey); return base64_encode($signature); @@ -3799,7 +3818,7 @@ class PHPMailer openssl_pkey_free($privKey); return ''; } - + /** * Generate a DKIM canonicalization header. * @access public @@ -3819,7 +3838,7 @@ class PHPMailer $signHeader = implode("\r\n", $lines); return $signHeader; } - + /** * Generate a DKIM canonicalization body. * @access public @@ -3840,7 +3859,7 @@ class PHPMailer } return $body; } - + /** * Create the DKIM header and body in a new message header. * @access public @@ -3920,7 +3939,7 @@ class PHPMailer $signed = $this->DKIM_Sign($toSign); return $dkimhdrs . $signed . "\r\n"; } - + /** * Detect if a string contains a line longer than the maximum line length allowed. * @param string $str @@ -3932,7 +3951,7 @@ class PHPMailer //+2 to include CRLF line break for a 1000 total return (boolean)preg_match('/^(.{'.(self::MAX_LINE_LENGTH + 2).',})/m', $str); } - + /** * Allows for public read access to 'to' property. * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included. @@ -3943,7 +3962,7 @@ class PHPMailer { return $this->to; } - + /** * Allows for public read access to 'cc' property. * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included. @@ -3954,7 +3973,7 @@ class PHPMailer { return $this->cc; } - + /** * Allows for public read access to 'bcc' property. * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included. @@ -3965,7 +3984,7 @@ class PHPMailer { return $this->bcc; } - + /** * Allows for public read access to 'ReplyTo' property. * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included. @@ -3976,7 +3995,7 @@ class PHPMailer { return $this->ReplyTo; } - + /** * Allows for public read access to 'all_recipients' property. * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included. @@ -3987,7 +4006,7 @@ class PHPMailer { return $this->all_recipients; } - + /** * Perform a callback. * @param boolean $isSent @@ -4019,7 +4038,7 @@ class phpmailerException extends Exception */ public function errorMessage() { - $errorMsg = '' . $this->getMessage() . "
\n"; + $errorMsg = '' . htmlspecialchars($this->getMessage()) . "
\n"; return $errorMsg; } -} +} \ No newline at end of file diff --git a/lib/classes/phpmailer/class.SMTP.php b/lib/classes/phpmailer/class.SMTP.php index 886782dc..a3a67baa 100644 --- a/lib/classes/phpmailer/class.SMTP.php +++ b/lib/classes/phpmailer/class.SMTP.php @@ -1,88 +1,88 @@ -* @author Jim Jagielski (jimjag) -* @author Andy Prevost (codeworxtech) -* @author Brent R. Matzelle (original founder) -* @copyright 2014 Marcus Bointon -* @copyright 2010 - 2012 Jim Jagielski -* @copyright 2004 - 2009 Andy Prevost -* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License -* @note This program is distributed in the hope that it will be useful - WITHOUT -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -* FITNESS FOR A PARTICULAR PURPOSE. -*/ + * PHP Version 5 + * @package PHPMailer + * @link https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project + * @author Marcus Bointon (Synchro/coolbru) + * @author Jim Jagielski (jimjag) + * @author Andy Prevost (codeworxtech) + * @author Brent R. Matzelle (original founder) + * @copyright 2014 Marcus Bointon + * @copyright 2010 - 2012 Jim Jagielski + * @copyright 2004 - 2009 Andy Prevost + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + * @note This program is distributed in the hope that it will be useful - WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ /** * PHPMailer RFC821 SMTP email transport class. -* Implements RFC 821 SMTP commands and provides some utility methods for sending mail to an SMTP server. -* @package PHPMailer -* @author Chris Ryan -* @author Marcus Bointon -*/ + * Implements RFC 821 SMTP commands and provides some utility methods for sending mail to an SMTP server. + * @package PHPMailer + * @author Chris Ryan + * @author Marcus Bointon + */ class SMTP { /** * The PHPMailer SMTP version number. * @var string */ - const VERSION = '5.2.21'; - + const VERSION = '5.2.26'; + /** * SMTP line break constant. * @var string */ const CRLF = "\r\n"; - + /** * The SMTP port to use if one is not specified. * @var integer */ const DEFAULT_SMTP_PORT = 25; - + /** * The maximum line length allowed by RFC 2822 section 2.1.1 * @var integer */ const MAX_LINE_LENGTH = 998; - + /** * Debug level for no output */ const DEBUG_OFF = 0; - + /** * Debug level to show client -> server messages */ const DEBUG_CLIENT = 1; - + /** * Debug level to show client -> server and server -> client messages */ const DEBUG_SERVER = 2; - + /** * Debug level to show connection status, client -> server and server -> client messages */ const DEBUG_CONNECTION = 3; - + /** * Debug level to show all messages */ const DEBUG_LOWLEVEL = 4; - + /** * The PHPMailer SMTP Version number. * @var string * @deprecated Use the `VERSION` constant instead * @see SMTP::VERSION */ - public $Version = '5.2.21'; - + public $Version = '5.2.26'; + /** * SMTP server port number. * @var integer @@ -90,7 +90,7 @@ class SMTP * @see SMTP::DEFAULT_SMTP_PORT */ public $SMTP_PORT = 25; - + /** * SMTP reply line ending. * @var string @@ -98,7 +98,7 @@ class SMTP * @see SMTP::CRLF */ public $CRLF = "\r\n"; - + /** * Debug output level. * Options: @@ -110,7 +110,7 @@ class SMTP * @var integer */ public $do_debug = self::DEBUG_OFF; - + /** * How to handle debug output. * Options: @@ -125,7 +125,7 @@ class SMTP * @var string|callable */ public $Debugoutput = 'echo'; - + /** * Whether to use VERP. * @link http://en.wikipedia.org/wiki/Variable_envelope_return_path @@ -133,7 +133,7 @@ class SMTP * @var boolean */ public $do_verp = false; - + /** * The timeout value for connection, in seconds. * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2 @@ -142,31 +142,36 @@ class SMTP * @var integer */ public $Timeout = 300; - + /** * How long to wait for commands to complete, in seconds. * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2 * @var integer */ public $Timelimit = 300; - + /** - * @var array patterns to extract smtp transaction id from smtp reply - * Only first capture group will be use, use non-capturing group to deal with it - * Extend this class to override this property to fulfil your needs. + * @var array Patterns to extract an SMTP transaction id from reply to a DATA command. + * The first capture group in each regex will be used as the ID. */ protected $smtp_transaction_id_patterns = array( 'exim' => '/[0-9]{3} OK id=(.*)/', 'sendmail' => '/[0-9]{3} 2.0.0 (.*) Message/', 'postfix' => '/[0-9]{3} 2.0.0 Ok: queued as (.*)/' ); - + + /** + * @var string The last transaction ID issued in response to a DATA command, + * if one was detected + */ + protected $last_smtp_transaction_id; + /** * The socket for the server connection. * @var resource */ protected $smtp_conn; - + /** * Error information, if any, for the last SMTP command. * @var array @@ -177,14 +182,14 @@ class SMTP 'smtp_code' => '', 'smtp_code_ex' => '' ); - + /** * The reply the server sent to us for HELO. * If null, no HELO string has yet been received. * @var string|null */ protected $helo_rply = null; - + /** * The set of SMTP extensions sent in reply to EHLO command. * Indexes of the array are extension names. @@ -195,13 +200,13 @@ class SMTP * @var array|null */ protected $server_caps = null; - + /** * The most recent reply received from the server. * @var string */ protected $last_reply = ''; - + /** * Output debugging info via a user-selected method. * @see SMTP::$Debugoutput @@ -227,12 +232,11 @@ class SMTP break; case 'html': //Cleans up output a bit for a better looking, HTML-safe output - echo htmlentities( + echo gmdate('Y-m-d H:i:s') . ' ' . htmlentities( preg_replace('/[\r\n]+/', '', $str), ENT_QUOTES, 'UTF-8' - ) - . "
\n"; + ) . "
\n"; break; case 'echo': default: @@ -242,10 +246,10 @@ class SMTP "\n", "\n \t ", trim($str) - )."\n"; + ) . "\n"; } } - + /** * Connect to an SMTP server. * @param string $host SMTP server IP or host name @@ -276,7 +280,8 @@ class SMTP } // Connect to the SMTP server $this->edebug( - "Connection: opening to $host:$port, timeout=$timeout, options=".var_export($options, true), + "Connection: opening to $host:$port, timeout=$timeout, options=" . + var_export($options, true), self::DEBUG_CONNECTION ); $errno = 0; @@ -339,7 +344,7 @@ class SMTP $this->edebug('SERVER -> CLIENT: ' . $announce, self::DEBUG_SERVER); return true; } - + /** * Initiate a TLS (encrypted) session. * @access public @@ -350,28 +355,28 @@ class SMTP if (!$this->sendCommand('STARTTLS', 'STARTTLS', 220)) { return false; } - + //Allow the best TLS version(s) we can $crypto_method = STREAM_CRYPTO_METHOD_TLS_CLIENT; - + //PHP 5.6.7 dropped inclusion of TLS 1.1 and 1.2 in STREAM_CRYPTO_METHOD_TLS_CLIENT //so add them back in manually if we can if (defined('STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT')) { $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT; $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT; } - + // Begin encrypted connection - if (!stream_socket_enable_crypto( + set_error_handler(array($this, 'errorHandler')); + $crypto_ok = stream_socket_enable_crypto( $this->smtp_conn, true, $crypto_method - )) { - return false; - } - return true; + ); + restore_error_handler(); + return $crypto_ok; } - + /** * Perform SMTP authentication. * Must be run after hello(). @@ -396,23 +401,22 @@ class SMTP $this->setError('Authentication is not allowed before HELO/EHLO'); return false; } - + if (array_key_exists('EHLO', $this->server_caps)) { - // SMTP extensions are available. Let's try to find a proper authentication method - + // SMTP extensions are available; try to find a proper authentication method if (!array_key_exists('AUTH', $this->server_caps)) { $this->setError('Authentication is not allowed at this stage'); // 'at this stage' means that auth may be allowed after the stage changes // e.g. after STARTTLS return false; } - + self::edebug('Auth method requested: ' . ($authtype ? $authtype : 'UNKNOWN'), self::DEBUG_LOWLEVEL); self::edebug( 'Auth methods available on the server: ' . implode(',', $this->server_caps['AUTH']), self::DEBUG_LOWLEVEL ); - + if (empty($authtype)) { foreach (array('CRAM-MD5', 'LOGIN', 'PLAIN', 'NTLM', 'XOAUTH2') as $method) { if (in_array($method, $this->server_caps['AUTH'])) { @@ -424,9 +428,9 @@ class SMTP $this->setError('No supported authentication methods found'); return false; } - self::edebug('Auth method selected: '.$authtype, self::DEBUG_LOWLEVEL); + self::edebug('Auth method selected: ' . $authtype, self::DEBUG_LOWLEVEL); } - + if (!in_array($authtype, $this->server_caps['AUTH'])) { $this->setError("The requested authentication method \"$authtype\" is not supported by the server"); return false; @@ -469,7 +473,7 @@ class SMTP return false; } $oauth = $OAuth->getOauth64(); - + // Start authentication if (!$this->sendCommand('AUTH', 'AUTH XOAUTH2 ' . $oauth, 235)) { return false; @@ -499,7 +503,7 @@ class SMTP } //msg1 $msg1 = $ntlm_client->typeMsg1($realm, $workstation); //msg1 - + if (!$this->sendCommand( 'AUTH NTLM', 'AUTH NTLM ' . base64_encode($msg1), @@ -532,10 +536,10 @@ class SMTP } // Get the challenge $challenge = base64_decode(substr($this->last_reply, 4)); - + // Build the response $response = $username . ' ' . $this->hmac($challenge, $password); - + // send encoded credentials return $this->sendCommand('Username', base64_encode($response), 235); default: @@ -544,13 +548,13 @@ class SMTP } return true; } - + /** * Calculate an MD5 HMAC hash. * Works like hash_hmac('md5', $data, $key) * in case that function is not available * @param string $data The data to hash - * @param string $key The key to hash with + * @param string $key The key to hash with * @access protected * @return string */ @@ -559,15 +563,15 @@ class SMTP if (function_exists('hash_hmac')) { return hash_hmac('md5', $data, $key); } - + // The following borrowed from // http://php.net/manual/en/function.mhash.php#27225 - + // RFC 2104 HMAC implementation for php. // Creates an md5 HMAC. // Eliminates the need to install mhash to compute a HMAC // by Lance Rushing - + $bytelen = 64; // byte length for md5 if (strlen($key) > $bytelen) { $key = pack('H*', md5($key)); @@ -577,10 +581,10 @@ class SMTP $opad = str_pad('', $bytelen, chr(0x5c)); $k_ipad = $key ^ $ipad; $k_opad = $key ^ $opad; - + return md5($k_opad . pack('H*', md5($k_ipad . $data))); } - + /** * Check connection state. * @access public @@ -603,7 +607,7 @@ class SMTP } return false; } - + /** * Close the socket and clean up the state of the class. * Don't use this function without first trying to use QUIT. @@ -623,7 +627,7 @@ class SMTP $this->edebug('Connection: closed', self::DEBUG_CONNECTION); } } - + /** * Send an SMTP DATA command. * Issues a data command and sends the msg_data to the server, @@ -642,7 +646,7 @@ class SMTP if (!$this->sendCommand('DATA', 'DATA', 354)) { return false; } - + /* The server is ready to accept data! * According to rfc821 we should not send more than 1000 characters on a single line (including the CRLF) * so we will break the data up into lines by \r and/or \n then if needed we will break each of those into @@ -650,21 +654,21 @@ class SMTP * We will also look for lines that start with a '.' and prepend an additional '.'. * NOTE: this does not count towards line-length limit. */ - + // Normalize line breaks before exploding $lines = explode("\n", str_replace(array("\r\n", "\r"), "\n", $msg_data)); - + /* To distinguish between a complete RFC822 message and a plain message body, we check if the first field * of the first line (':' separated) does not contain a space then it _should_ be a header and we will * process all lines before a blank line as headers. */ - + $field = substr($lines[0], 0, strpos($lines[0], ':')); $in_headers = false; if (!empty($field) && strpos($field, ' ') === false) { $in_headers = true; } - + foreach ($lines as $line) { $lines_out = array(); if ($in_headers and $line == '') { @@ -694,7 +698,7 @@ class SMTP } } $lines_out[] = $line; - + //Send the lines to the server foreach ($lines_out as $line_out) { //RFC2821 section 4.5.2 @@ -704,17 +708,18 @@ class SMTP $this->client_send($line_out . self::CRLF); } } - + //Message data has been sent, complete the command //Increase timelimit for end of DATA command $savetimelimit = $this->Timelimit; $this->Timelimit = $this->Timelimit * 2; $result = $this->sendCommand('DATA END', '.', 250); + $this->recordLastTransactionID(); //Restore timelimit $this->Timelimit = $savetimelimit; return $result; } - + /** * Send an SMTP HELO or EHLO command. * Used to identify the sending server to the receiving server. @@ -730,7 +735,7 @@ class SMTP //Try extended hello first (RFC 2821) return (boolean)($this->sendHello('EHLO', $host) or $this->sendHello('HELO', $host)); } - + /** * Send an SMTP HELO or EHLO command. * Low-level implementation used by hello() @@ -751,7 +756,7 @@ class SMTP } return $noerror; } - + /** * Parse a reply to HELO/EHLO command to discover server extensions. * In case of HELO, the only parameter that can be discovered is a server name. @@ -762,7 +767,7 @@ class SMTP { $this->server_caps = array(); $lines = explode("\n", $this->helo_rply); - + foreach ($lines as $n => $s) { //First 4 chars contain response code followed by - or space $s = trim(substr($s, 4)); @@ -793,7 +798,7 @@ class SMTP } } } - + /** * Send an SMTP MAIL command. * Starts a mail transaction from the email address specified in @@ -814,7 +819,7 @@ class SMTP 250 ); } - + /** * Send an SMTP QUIT command. * Closes the socket if there is no error or the $close_on_error argument is true. @@ -833,7 +838,7 @@ class SMTP } return $noerror; } - + /** * Send an SMTP RCPT command. * Sets the TO argument to $toaddr. @@ -851,7 +856,7 @@ class SMTP array(250, 251) ); } - + /** * Send an SMTP RSET command. * Abort any transaction that is currently in progress. @@ -863,7 +868,7 @@ class SMTP { return $this->sendCommand('RSET', 'RSET', 250); } - + /** * Send a command to an SMTP server and check its return code. * @param string $command The command name - not sent to the server @@ -884,7 +889,7 @@ class SMTP return false; } $this->client_send($commandstring . self::CRLF); - + $this->last_reply = $this->get_lines(); // Fetch SMTP code and possible error code explanation $matches = array(); @@ -893,7 +898,8 @@ class SMTP $code_ex = (count($matches) > 2 ? $matches[2] : null); // Cut off error code from each response line $detail = preg_replace( - "/{$code}[ -]".($code_ex ? str_replace('.', '\\.', $code_ex).' ' : '')."/m", + "/{$code}[ -]" . + ($code_ex ? str_replace('.', '\\.', $code_ex) . ' ' : '') . "/m", '', $this->last_reply ); @@ -903,9 +909,9 @@ class SMTP $code_ex = null; $detail = substr($this->last_reply, 4); } - + $this->edebug('SERVER -> CLIENT: ' . $this->last_reply, self::DEBUG_SERVER); - + if (!in_array($code, (array)$expect)) { $this->setError( "$command command failed", @@ -919,11 +925,11 @@ class SMTP ); return false; } - + $this->setError(''); return true; } - + /** * Send an SMTP SAML command. * Starts a mail transaction from the email address specified in $from. @@ -941,7 +947,7 @@ class SMTP { return $this->sendCommand('SAML', "SAML FROM:$from", 250); } - + /** * Send an SMTP VRFY command. * @param string $name The name to verify @@ -952,7 +958,7 @@ class SMTP { return $this->sendCommand('VRFY', "VRFY $name", array(250, 251)); } - + /** * Send an SMTP NOOP command. * Used to keep keep-alives alive, doesn't actually do anything @@ -963,7 +969,7 @@ class SMTP { return $this->sendCommand('NOOP', 'NOOP', 250); } - + /** * Send an SMTP TURN command. * This is an optional command for SMTP that this class does not support. @@ -979,7 +985,7 @@ class SMTP $this->edebug('SMTP NOTICE: ' . $this->error['error'], self::DEBUG_CLIENT); return false; } - + /** * Send raw data to the server. * @param string $data The data to send @@ -989,9 +995,12 @@ class SMTP public function client_send($data) { $this->edebug("CLIENT -> SERVER: $data", self::DEBUG_CLIENT); - return fwrite($this->smtp_conn, $data); + set_error_handler(array($this, 'errorHandler')); + $result = fwrite($this->smtp_conn, $data); + restore_error_handler(); + return $result; } - + /** * Get the latest error. * @access public @@ -1001,7 +1010,7 @@ class SMTP { return $this->error; } - + /** * Get SMTP extensions available on the server * @access public @@ -1011,7 +1020,7 @@ class SMTP { return $this->server_caps; } - + /** * A multipurpose method * The method works in three ways, dependent on argument value and current state @@ -1037,7 +1046,7 @@ class SMTP $this->setError('No HELO/EHLO was sent'); return null; } - + // the tight logic knot ;) if (!array_key_exists($name, $this->server_caps)) { if ($name == 'HELO') { @@ -1049,10 +1058,10 @@ class SMTP $this->setError('HELO handshake was used. Client knows nothing about server extensions'); return null; } - + return $this->server_caps[$name]; } - + /** * Get the last reply from the server. * @access public @@ -1062,7 +1071,7 @@ class SMTP { return $this->last_reply; } - + /** * Read the SMTP server's response. * Either before eof or socket timeout occurs on the operation. @@ -1089,8 +1098,10 @@ class SMTP $this->edebug("SMTP -> get_lines(): \$data is \"$data\"", self::DEBUG_LOWLEVEL); $this->edebug("SMTP -> get_lines(): \$str is \"$str\"", self::DEBUG_LOWLEVEL); $data .= $str; - // If 4th character is a space, we are done reading, break the loop, micro-optimisation over strlen - if ((isset($str[3]) and $str[3] == ' ')) { + // If response is only 3 chars (not valid, but RFC5321 S4.2 says it must be handled), + // or 4th character is a space, we are done reading, break the loop, + // string array access is a micro-optimisation over strlen + if (!isset($str[3]) or (isset($str[3]) and $str[3] == ' ')) { break; } // Timed-out? Log and break @@ -1105,7 +1116,7 @@ class SMTP // Now check if reads took too long if ($endtime and time() > $endtime) { $this->edebug( - 'SMTP -> get_lines(): timelimit reached ('. + 'SMTP -> get_lines(): timelimit reached (' . $this->Timelimit . ' sec)', self::DEBUG_LOWLEVEL ); @@ -1114,7 +1125,7 @@ class SMTP } return $data; } - + /** * Enable or disable VERP address generation. * @param boolean $enabled @@ -1123,7 +1134,7 @@ class SMTP { $this->do_verp = $enabled; } - + /** * Get VERP address generation mode. * @return boolean @@ -1132,7 +1143,7 @@ class SMTP { return $this->do_verp; } - + /** * Set error messages and codes. * @param string $message The error message @@ -1149,7 +1160,7 @@ class SMTP 'smtp_code_ex' => $smtp_code_ex ); } - + /** * Set debug output method. * @param string|callable $method The name of the mechanism to use for debugging output, or a callable to handle it. @@ -1158,7 +1169,7 @@ class SMTP { $this->Debugoutput = $method; } - + /** * Get debug output method. * @return string @@ -1167,7 +1178,7 @@ class SMTP { return $this->Debugoutput; } - + /** * Set debug output level. * @param integer $level @@ -1176,7 +1187,7 @@ class SMTP { $this->do_debug = $level; } - + /** * Get debug output level. * @return integer @@ -1185,7 +1196,7 @@ class SMTP { return $this->do_debug; } - + /** * Set SMTP timeout. * @param integer $timeout @@ -1194,7 +1205,7 @@ class SMTP { $this->Timeout = $timeout; } - + /** * Get SMTP timeout. * @return integer @@ -1203,47 +1214,63 @@ class SMTP { return $this->Timeout; } - + /** * Reports an error number and string. * @param integer $errno The error number returned by PHP. * @param string $errmsg The error message returned by PHP. + * @param string $errfile The file the error occurred in + * @param integer $errline The line number the error occurred on */ - protected function errorHandler($errno, $errmsg) + protected function errorHandler($errno, $errmsg, $errfile = '', $errline = 0) { - $notice = 'Connection: Failed to connect to server.'; + $notice = 'Connection failed.'; $this->setError( $notice, $errno, $errmsg ); $this->edebug( - $notice . ' Error number ' . $errno . '. "Error notice: ' . $errmsg, + $notice . ' Error #' . $errno . ': ' . $errmsg . " [$errfile line $errline]", self::DEBUG_CONNECTION ); } - + /** - * Will return the ID of the last smtp transaction based on a list of patterns provided - * in SMTP::$smtp_transaction_id_patterns. + * Extract and return the ID of the last SMTP transaction based on + * a list of patterns provided in SMTP::$smtp_transaction_id_patterns. + * Relies on the host providing the ID in response to a DATA command. * If no reply has been received yet, it will return null. - * If no pattern has been matched, it will return false. + * If no pattern was matched, it will return false. * @return bool|null|string */ + protected function recordLastTransactionID() + { + $reply = $this->getLastReply(); + + if (empty($reply)) { + $this->last_smtp_transaction_id = null; + } else { + $this->last_smtp_transaction_id = false; + foreach ($this->smtp_transaction_id_patterns as $smtp_transaction_id_pattern) { + if (preg_match($smtp_transaction_id_pattern, $reply, $matches)) { + $this->last_smtp_transaction_id = $matches[1]; + } + } + } + + return $this->last_smtp_transaction_id; + } + + /** + * Get the queue/transaction ID of the last SMTP transaction + * If no reply has been received yet, it will return null. + * If no pattern was matched, it will return false. + * @return bool|null|string + * @see recordLastTransactionID() + */ public function getLastTransactionID() { - $reply = $this->getLastReply(); - - if (empty($reply)) { - return null; - } - - foreach($this->smtp_transaction_id_patterns as $smtp_transaction_id_pattern) { - if(preg_match($smtp_transaction_id_pattern, $reply, $matches)) { - return $matches[1]; - } - } - - return false; + return $this->last_smtp_transaction_id; } } From 2603a9c869e9262e1e6e7c3d18e5a521be940ea1 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Fri, 26 Jan 2018 17:30:20 +0100 Subject: [PATCH 0389/1335] add new setting to specify values for the PATH env-variable for php-fpm Signed-off-by: Michael Kaufmann (d00p) --- actions/admin/settings/136.phpfpm.php | 11 +++++++++++ install/froxlor.sql | 3 ++- install/updates/froxlor/0.9/update_0.9.inc.php | 9 +++++++++ lib/classes/phpinterface/class.phpinterface_fpm.php | 4 ++++ lib/version.inc.php | 2 +- lng/english.lng.php | 2 ++ lng/german.lng.php | 1 + 7 files changed, 30 insertions(+), 2 deletions(-) diff --git a/actions/admin/settings/136.phpfpm.php b/actions/admin/settings/136.phpfpm.php index a62f0c2f..c8b6e0ea 100644 --- a/actions/admin/settings/136.phpfpm.php +++ b/actions/admin/settings/136.phpfpm.php @@ -69,6 +69,17 @@ return array( 'default' => '/usr/share/php/:/usr/share/php5/', 'save_method' => 'storeSettingField' ), + 'system_phpfpm_envpath' => array( + 'label' => $lng['serversettings']['phpfpm_settings']['envpath'], + 'settinggroup' => 'phpfpm', + 'varname' => 'envpath', + 'type' => 'string', + 'string_type' => 'dir', + 'string_delimiter' => ':', + 'string_emptyallowed' => true, + 'default' => '/usr/local/bin:/usr/bin:/bin', + 'save_method' => 'storeSettingField' + ), 'system_phpfpm_fastcgi_ipcdir' => array( 'label' => $lng['serversettings']['phpfpm_settings']['ipcdir'], 'settinggroup' => 'phpfpm', diff --git a/install/froxlor.sql b/install/froxlor.sql index 1bd72d32..a54798a4 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -414,6 +414,7 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES ('phpfpm', 'max_requests', '0'), ('phpfpm', 'tmpdir', '/var/customers/tmp/'), ('phpfpm', 'peardir', '/usr/share/php/:/usr/share/php5/'), + ('phpfpm', 'envpath', '/usr/local/bin:/usr/bin:/bin'), ('phpfpm', 'enabled_ownvhost', '0'), ('phpfpm', 'vhost_httpuser', 'froxlorlocal'), ('phpfpm', 'vhost_httpgroup', 'froxlorlocal'), @@ -686,7 +687,7 @@ opcache.interned_strings_buffer'), ('panel', 'password_special_char', '!?<>§$%+#=@'), ('panel', 'customer_hide_options', ''), ('panel', 'version', '0.9.38.8'), - ('panel', 'db_version', '201801110'); + ('panel', 'db_version', '201801260'); DROP TABLE IF EXISTS `panel_tasks`; diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index e9e1e39e..6c9fa665 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -3865,3 +3865,12 @@ opcache.interned_strings_buffer'); updateToDbVersion('201801110'); } + +if (isDatabaseVersion('201801110')) { + + showUpdateStep("Adding php-fpm php PATH setting for envrironment"); + Settings::AddNew("phpfpm.envpath", '/usr/local/bin:/usr/bin:/bin'); + lastStepStatus(0); + + updateToDbVersion('201801260'); +} diff --git a/lib/classes/phpinterface/class.phpinterface_fpm.php b/lib/classes/phpinterface/class.phpinterface_fpm.php index f72a2410..daf358ad 100644 --- a/lib/classes/phpinterface/class.phpinterface_fpm.php +++ b/lib/classes/phpinterface/class.phpinterface_fpm.php @@ -169,6 +169,10 @@ class phpinterface_fpm $this->getTempDir(); } + $env_path = Settings::Get('phpfpm.envpath'); + if (!empty($env_path)) { + $fpm_config .= 'env[PATH] = ' . $env_path . "\n"; + } $fpm_config .= 'env[TMP] = ' . $tmpdir . "\n"; $fpm_config .= 'env[TMPDIR] = ' . $tmpdir . "\n"; $fpm_config .= 'env[TEMP] = ' . $tmpdir . "\n"; diff --git a/lib/version.inc.php b/lib/version.inc.php index 4f1da38b..5ebe2f3b 100644 --- a/lib/version.inc.php +++ b/lib/version.inc.php @@ -19,7 +19,7 @@ $version = '0.9.38.8'; // Database version (YYYYMMDDC where C is a daily counter) -$dbversion = '201801110'; +$dbversion = '201801260'; // Distribution branding-tag (used for Debian etc.) $branding = ''; diff --git a/lng/english.lng.php b/lng/english.lng.php index 831893f2..bb58842e 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -2100,3 +2100,5 @@ $lng['phpfpm']['ini_flags'] = 'Enter possible php_flags for php $lng['phpfpm']['ini_values'] = 'Enter possible php_values for php.ini. One entry per line'; $lng['phpfpm']['ini_admin_flags'] = 'Enter possible php_admin_flags for php.ini. One entry per line'; $lng['phpfpm']['ini_admin_values'] = 'Enter possible php_admin_values for php.ini. One entry per line'; +$lng['serversettings']['phpfpm_settings']['envpath'] = 'Paths to add to the PATH environment. Leave empty for no PATH environment variable'; + diff --git a/lng/german.lng.php b/lng/german.lng.php index 9ccfa6da..b822aa3b 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -1751,3 +1751,4 @@ $lng['phpfpm']['ini_flags'] = 'Mögliche php_flags für die php $lng['phpfpm']['ini_values'] = 'Mögliche php_values für die php.ini. Pro Zeile eine Direktive'; $lng['phpfpm']['ini_admin_flags'] = 'Mögliche php_admin_flags für die php.ini. Pro Zeile eine Direktive'; $lng['phpfpm']['ini_admin_values'] = 'Mögliche php_admin_values für die php.ini. Pro Zeile eine Direktive'; +$lng['serversettings']['phpfpm_settings']['envpath'] = 'Pfade für die PATH Umgebungsvariable. Leerlassen, um keine PATH Umgebungsvariable zu setzen.'; From f896fe11a0519d2d26c1b1387fce7b00a32aaed9 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sat, 27 Jan 2018 10:09:43 +0100 Subject: [PATCH 0390/1335] do not split dkim-entry content in generateDkimEntries(); re-add braces to enclose TXT record content Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/dns/class.DnsEntry.php | 4 ++-- lib/functions/dns/function.generateDkimEntries.php | 14 +------------- 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/lib/classes/dns/class.DnsEntry.php b/lib/classes/dns/class.DnsEntry.php index 11a7a614..a5685741 100644 --- a/lib/classes/dns/class.DnsEntry.php +++ b/lib/classes/dns/class.DnsEntry.php @@ -52,7 +52,7 @@ class DnsEntry if (substr($_l, 0, 1) == '"') { $_l = substr($_l, 1); } - $_content = '"' . $_l . '"' . PHP_EOL; + $_content = '("' . $_l . '"' . PHP_EOL; $_l = array_pop($_contentlines); // check for ending quote if (substr($_l, - 1) == '"') { @@ -63,7 +63,7 @@ class DnsEntry $_content .= "\t\t\t\t" . '"' . $_cl . '"' . PHP_EOL; } // last line - $_content .= "\t\t\t\t" . '"' . $_l . '"'; + $_content .= "\t\t\t\t" . '"' . $_l . '")'; } $result = $this->record . "\t" . $this->ttl . "\t" . $this->class . "\t" . $this->type . "\t" . (($this->priority >= 0 && ($this->type == 'MX' || $this->type == 'SRV')) ? $this->priority . "\t" : "") . $_content . PHP_EOL; return $result; diff --git a/lib/functions/dns/function.generateDkimEntries.php b/lib/functions/dns/function.generateDkimEntries.php index 622af765..993df741 100644 --- a/lib/functions/dns/function.generateDkimEntries.php +++ b/lib/functions/dns/function.generateDkimEntries.php @@ -54,20 +54,8 @@ function generateDkimEntries($domain) // end-part $dkim_txt .= 't=s'; - if (Settings::Get('system.dns_server') == 'pdns') { - // PowerDNS does not need/want splitted content - $txt_record_split = $dkim_txt; - } else { - // split if necessary - $txt_record_split = ''; - $lbr = 50; - for ($pos = 0; $pos <= strlen($dkim_txt) - 1; $pos += $lbr) { - $txt_record_split .= (($pos == 0) ? '("' : "\t\t\t\t\t \"") . substr($dkim_txt, $pos, $lbr) . (($pos >= strlen($dkim_txt) - $lbr) ? '")' : '"') . "\n"; - } - } - // dkim-entry - $zone_dkim[] = $txt_record_split; + $zone_dkim[] = $dkim_txt; // adsp-entry if (Settings::Get('dkim.dkim_add_adsp') == "1") { From f034695290575a215c58df76e8a9e49aa6cda8c0 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sat, 27 Jan 2018 11:14:22 +0100 Subject: [PATCH 0391/1335] remove '/etc/postfix/master.cf: line x: using backwards-compatible default setting chroot=y' warning; set correct permission for dkim-public key as it should not be group or other writable Signed-off-by: Michael Kaufmann (d00p) --- lib/configfiles/stretch.xml | 52 +++++++++++++++---------------- scripts/classes/class.DnsBase.php | 2 +- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/lib/configfiles/stretch.xml b/lib/configfiles/stretch.xml index eb6c71d1..80335daa 100644 --- a/lib/configfiles/stretch.xml +++ b/lib/configfiles/stretch.xml @@ -2343,12 +2343,12 @@ virtual_mailbox_limit = 0 # service type private unpriv chroot wakeup maxproc command + args # (yes) (yes) (yes) (never) (100) # ========================================================================== -smtp inet n - - - - smtpd -#smtp inet n - - - 1 postscreen -#smtpd pass - - - - - smtpd -#dnsblog unix - - - - 0 dnsblog -#tlsproxy unix - - - - 0 tlsproxy -#submission inet n - - - - smtpd +smtp inet n - y - - smtpd +#smtp inet n - y - 1 postscreen +#smtpd pass - - y - - smtpd +#dnsblog unix - - y - 0 dnsblog +#tlsproxy unix - - y - 0 tlsproxy +#submission inet n - y - - smtpd # -o syslog_name=postfix/submission # -o smtpd_tls_security_level=encrypt # -o smtpd_sasl_auth_enable=yes @@ -2359,7 +2359,7 @@ smtp inet n - - - - smtpd # -o smtpd_recipient_restrictions= # -o smtpd_relay_restrictions=permit_sasl_authenticated,reject # -o milter_macro_daemon_name=ORIGINATING -#smtps inet n - - - - smtpd +#smtps inet n - y - - smtpd # -o syslog_name=postfix/smtps # -o smtpd_tls_wrappermode=yes # -o smtpd_sasl_auth_enable=yes @@ -2370,32 +2370,32 @@ smtp inet n - - - - smtpd # -o smtpd_recipient_restrictions= # -o smtpd_relay_restrictions=permit_sasl_authenticated,reject # -o milter_macro_daemon_name=ORIGINATING -#628 inet n - - - - qmqpd -pickup unix n - - 60 1 pickup -cleanup unix n - - - 0 cleanup +#628 inet n - y - - qmqpd +pickup unix n - y 60 1 pickup +cleanup unix n - y - 0 cleanup qmgr unix n - n 300 1 qmgr #qmgr unix n - n 300 1 oqmgr -tlsmgr unix - - - 1000? 1 tlsmgr -rewrite unix - - - - - trivial-rewrite -bounce unix - - - - 0 bounce -defer unix - - - - 0 bounce -trace unix - - - - 0 bounce -verify unix - - - - 1 verify -flush unix n - - 1000? 0 flush +tlsmgr unix - - y 1000? 1 tlsmgr +rewrite unix - - y - - trivial-rewrite +bounce unix - - y - 0 bounce +defer unix - - y - 0 bounce +trace unix - - y - 0 bounce +verify unix - - y - 1 verify +flush unix n - y 1000? 0 flush proxymap unix - - n - - proxymap proxywrite unix - - n - 1 proxymap -smtp unix - - - - - smtp -relay unix - - - - - smtp +smtp unix - - y - - smtp +relay unix - - y - - smtp # -o smtp_helo_timeout=5 -o smtp_connect_timeout=5 -showq unix n - - - - showq -error unix - - - - - error -retry unix - - - - - error -discard unix - - - - - discard +showq unix n - y - - showq +error unix - - y - - error +retry unix - - y - - error +discard unix - - y - - discard local unix - n n - - local virtual unix - n n - - virtual -lmtp unix - - - - - lmtp -anvil unix - - - - 1 anvil -scache unix - - - - 1 scache +lmtp unix - - y - - lmtp +anvil unix - - y - 1 anvil +scache unix - - y - 1 scache # # ==================================================================== # Interfaces to non-Postfix software. Be sure to examine the manual diff --git a/scripts/classes/class.DnsBase.php b/scripts/classes/class.DnsBase.php index 46c83926..68a5ba3c 100644 --- a/scripts/classes/class.DnsBase.php +++ b/scripts/classes/class.DnsBase.php @@ -233,7 +233,7 @@ abstract class DnsBase $pubkey_file_handler = fopen($pubkey_filename, "w"); fwrite($pubkey_file_handler, $domain['dkim_pubkey']); fclose($pubkey_file_handler); - safe_exec("chmod 0664 " . escapeshellarg($pubkey_filename)); + safe_exec("chmod 0644 " . escapeshellarg($pubkey_filename)); } $dkimdomains .= $domain['domain'] . "\n"; From 194b7863b8486156c5517af298cccabc1bb485b8 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sat, 27 Jan 2018 11:37:06 +0100 Subject: [PATCH 0392/1335] fix invalid ipv6 value in mysql-access-host setting Signed-off-by: Michael Kaufmann (d00p) --- .../function.storeSettingMysqlAccessHost.php | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/lib/functions/settings/function.storeSettingMysqlAccessHost.php b/lib/functions/settings/function.storeSettingMysqlAccessHost.php index b324f248..decb17b5 100644 --- a/lib/functions/settings/function.storeSettingMysqlAccessHost.php +++ b/lib/functions/settings/function.storeSettingMysqlAccessHost.php @@ -16,33 +16,36 @@ * @package Functions * */ - function storeSettingMysqlAccessHost($fieldname, $fielddata, $newfieldvalue) { $returnvalue = storeSettingField($fieldname, $fielddata, $newfieldvalue); - if($returnvalue !== false && is_array($fielddata) && isset($fielddata['settinggroup']) && $fielddata['settinggroup'] == 'system' && isset($fielddata['varname']) && $fielddata['varname'] == 'mysql_access_host') - { + if ($returnvalue !== false && is_array($fielddata) && isset($fielddata['settinggroup']) && $fielddata['settinggroup'] == 'system' && isset($fielddata['varname']) && $fielddata['varname'] == 'mysql_access_host') { $mysql_access_host_array = array_map('trim', explode(',', $newfieldvalue)); - if(in_array('127.0.0.1', $mysql_access_host_array) - && !in_array('localhost', $mysql_access_host_array)) - { + if (in_array('127.0.0.1', $mysql_access_host_array) && ! in_array('localhost', $mysql_access_host_array)) { $mysql_access_host_array[] = 'localhost'; } - if(!in_array('127.0.0.1', $mysql_access_host_array) - && in_array('localhost', $mysql_access_host_array)) - { + if (! in_array('127.0.0.1', $mysql_access_host_array) && in_array('localhost', $mysql_access_host_array)) { $mysql_access_host_array[] = '127.0.0.1'; } + // be aware that ipv6 addresses are enclosed in [ ] when passed here + $mysql_access_host_array = array_map('cleanMySQLAccessHost', $mysql_access_host_array); + $mysql_access_host_array = array_unique(array_trim($mysql_access_host_array)); $newfieldvalue = implode(',', $mysql_access_host_array); correctMysqlUsers($mysql_access_host_array); } - + return $returnvalue; } -?> +function cleanMySQLAccessHost($value) +{ + if (substr($value, 0, 1) == '[' && substr($value, - 1) == ']') { + return substr($value, 1, - 1); + } + return $value; +} From d8a301530326c559b965ac0ccdd287c20417a867 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sat, 27 Jan 2018 11:50:34 +0100 Subject: [PATCH 0393/1335] put le acme version setting right above the CA setting, less confusing Signed-off-by: Michael Kaufmann (d00p) --- actions/admin/settings/131.ssl.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/actions/admin/settings/131.ssl.php b/actions/admin/settings/131.ssl.php index 1ad1768a..7bdeb3e4 100644 --- a/actions/admin/settings/131.ssl.php +++ b/actions/admin/settings/131.ssl.php @@ -113,6 +113,15 @@ return array( 'cronmodule' => 'froxlor/letsencrypt', 'save_method' => 'storeSettingField' ), + 'system_letsencryptacmeconf' => array( + 'label' => $lng['serversettings']['letsencryptacmeconf'], + 'settinggroup' => 'system', + 'varname' => 'letsencryptacmeconf', + 'type' => 'string', + 'string_type' => 'file', + 'default' => '/etc/apache2/conf-enabled/acme.conf', + 'save_method' => 'storeSettingField' + ), 'system_leapiversion' => array( 'label' => $lng['serversettings']['leapiversion'], 'settinggroup' => 'system', @@ -126,15 +135,6 @@ return array( ), 'save_method' => 'storeSettingField' ), - 'system_letsencryptacmeconf' => array( - 'label' => $lng['serversettings']['letsencryptacmeconf'], - 'settinggroup' => 'system', - 'varname' => 'letsencryptacmeconf', - 'type' => 'string', - 'string_type' => 'file', - 'default' => '/etc/apache2/conf-enabled/acme.conf', - 'save_method' => 'storeSettingField' - ), 'system_letsencryptca' => array( 'label' => $lng['serversettings']['letsencryptca'], 'settinggroup' => 'system', From 840b5ea2292ce7ada0ac900c3f41b2579a06e53c Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sun, 28 Jan 2018 14:33:00 +0100 Subject: [PATCH 0394/1335] add new feature: import/export of settings Signed-off-by: Michael Kaufmann (d00p) --- admin_settings.php | 32 +++++ lib/classes/settings/class.SImExporter.php | 114 ++++++++++++++++++ .../output/function.standard_error.php | 12 ++ lng/english.lng.php | 3 +- lng/german.lng.php | 1 + .../admin/settings/importexport/index.tpl | 26 ++++ templates/Sparkle/admin/settings/settings.tpl | 3 +- .../admin/settings/settings_overview.tpl | 3 +- 8 files changed, 191 insertions(+), 3 deletions(-) create mode 100644 lib/classes/settings/class.SImExporter.php create mode 100644 templates/Sparkle/admin/settings/importexport/index.tpl diff --git a/admin_settings.php b/admin_settings.php index 49e2bd8c..538df344 100644 --- a/admin_settings.php +++ b/admin_settings.php @@ -290,6 +290,38 @@ if ($page == 'overview' && $userinfo['change_serversettings'] == '1') { } eval("echo \"" . getTemplate("settings/integritycheck") . "\";"); } +elseif ($page == 'importexport' && $userinfo['change_serversettings'] == '1') +{ + if (isset($_GET['action']) && $_GET['action'] == "export") { + // export + try { + $json_export = SImExporter::export(); + } catch(Exception $e) { + dynamic_error($e->getMessage()); + } + header('Content-disposition: attachment; filename=Froxlor_settings-'.$version.'-'.$dbversion.'_'.date('d.m.Y').'.json'); + header('Content-type: application/json'); + echo $json_export; + exit; + } elseif (isset($_GET['action']) && $_GET['action'] == "import") { + // import + if (isset($_POST['send']) && $_POST['send'] == 'send') { + // get uploaded file + if (isset($_FILES["import_file"]["tmp_name"])) { + $imp_content = file_get_contents($_FILES["import_file"]["tmp_name"]); + try { + SImExporter::import($imp_content); + } catch(Exception $e) { + dynamic_error($e->getMessage()); + } + standard_success('settingsimported', '', array('filename' => 'admin_settings.php')); + } + dynamic_error("Upload failed"); + } + } else { + eval("echo \"" . getTemplate("settings/importexport/index") . "\";"); + } +} elseif ($page == 'testmail') { if (isset($_POST['send']) && $_POST['send'] == 'send') diff --git a/lib/classes/settings/class.SImExporter.php b/lib/classes/settings/class.SImExporter.php new file mode 100644 index 00000000..206f4e1d --- /dev/null +++ b/lib/classes/settings/class.SImExporter.php @@ -0,0 +1,114 @@ + + * @author Froxlor team (2018-) + * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt + * @package Classes + * + * @since 0.9.39 + * + */ + +/** + * Class SImExporter + * + * Import/Export settings to JSON + * + * @copyright (c) the authors + * @author Michael Kaufmann + * @author Froxlor team (2018-) + * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt + * @package Classes + */ +class SImExporter +{ + + /** + * settings which are not being exported + * + * @var array + */ + private static $_no_export = [ + 'panel.adminmail', + 'admin.show_news_feed', + 'system.lastaccountnumber', + 'system.lastguid', + 'system.ipaddress', + 'system.last_traffic_run', + 'system.hostname', + 'system.mysql_access_host', + 'system.lastcronrun', + 'system.defaultip', + 'system.last_tasks_run', + 'system.last_archive_run', + 'system.leprivatekey', + 'system.lepublickey' + ]; + + public static function export() + { + $result_stmt = Database::query(" + SELECT * FROM `" . TABLE_PANEL_SETTINGS . "` ORDER BY `settingid` ASC + "); + $_data = array(); + while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) { + $index = $row['settinggroup'] . "." . $row['varname']; + if (! in_array($index, self::$_no_export)) { + $_data[$index] = $row['value']; + } + } + // add checksum for validation + $_data['_sha'] = sha1(var_export($_data, true)); + $_export = json_encode($_data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); + if (! $_export) { + throw new Exception("Error exporting settings: " . json_last_error_msg()); + } + return $_export; + } + + public static function import($json_str = null) + { + // decode data + $_data = json_decode($json_str, true); + if ($_data) { + // get validity check data + $_sha = isset($_data['_sha']) ? $_data['_sha'] : false; + $_version = isset($_data['panel.version']) ? $_data['panel.version'] : false; + $_dbversion = isset($_data['panel.db_version']) ? $_data['panel.db_version'] : false; + // check if we have everything we need + if (! $_sha || ! $_version || ! $_dbversion) { + throw new Exception("Invalid froxlor settings data. Unable to import."); + } + // validate import file + unset($_data['_sha']); + // compare + if ($_sha != sha1(var_export($_data, true))) { + throw new Exception("SHA check of import data failed. Unable to import."); + } + // do not import version info - but we need that to possibily update settings + // when there were changes in the variable-name or similar + unset($_data['panel.version']); + unset($_data['panel.db_version']); + /* + // store new data + foreach ($_data as $index => $value) { + Settings::Set($index, $value); + } + // save to DB + Settings::Flush(); + */ + // all good + return true; + } + throw new Exception("Invalid JSON data: " . json_last_error_msg()); + } +} diff --git a/lib/functions/output/function.standard_error.php b/lib/functions/output/function.standard_error.php index d202a2c4..03fb272f 100644 --- a/lib/functions/output/function.standard_error.php +++ b/lib/functions/output/function.standard_error.php @@ -63,3 +63,15 @@ function standard_error($errors = '', $replacer = '') { eval("echo \"" . getTemplate('misc/error', '1') . "\";"); exit; } + +function dynamic_error($message) { + global $userinfo, $s, $header, $footer, $lng, $theme; + $_SESSION['requestData'] = $_POST; + $link = ''; + if (isset($_SERVER['HTTP_REFERER']) && strpos($_SERVER['HTTP_REFERER'], $_SERVER['HTTP_HOST']) !== false) { + $link = ''.$lng['panel']['back'].''; + } + $error = $message; + eval("echo \"" . getTemplate('misc/error', '1') . "\";"); + exit; +} diff --git a/lng/english.lng.php b/lng/english.lng.php index bb58842e..64d12df9 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -2101,4 +2101,5 @@ $lng['phpfpm']['ini_values'] = 'Enter possible php_values for p $lng['phpfpm']['ini_admin_flags'] = 'Enter possible php_admin_flags for php.ini. One entry per line'; $lng['phpfpm']['ini_admin_values'] = 'Enter possible php_admin_values for php.ini. One entry per line'; $lng['serversettings']['phpfpm_settings']['envpath'] = 'Paths to add to the PATH environment. Leave empty for no PATH environment variable'; - +$lng['admin']['configfiles']['importexport'] = 'Import/Export'; +$lng['success']['settingsimported'] = 'Settings imported successfully'; diff --git a/lng/german.lng.php b/lng/german.lng.php index b822aa3b..f9d86ab2 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -1752,3 +1752,4 @@ $lng['phpfpm']['ini_values'] = 'Mögliche php_values für die p $lng['phpfpm']['ini_admin_flags'] = 'Mögliche php_admin_flags für die php.ini. Pro Zeile eine Direktive'; $lng['phpfpm']['ini_admin_values'] = 'Mögliche php_admin_values für die php.ini. Pro Zeile eine Direktive'; $lng['serversettings']['phpfpm_settings']['envpath'] = 'Pfade für die PATH Umgebungsvariable. Leerlassen, um keine PATH Umgebungsvariable zu setzen.'; +$lng['success']['settingsimported'] = 'Einstellungnen erfolgreich importiert'; diff --git a/templates/Sparkle/admin/settings/importexport/index.tpl b/templates/Sparkle/admin/settings/importexport/index.tpl new file mode 100644 index 00000000..41da2dfb --- /dev/null +++ b/templates/Sparkle/admin/settings/importexport/index.tpl @@ -0,0 +1,26 @@ +$header +
+
+

+   + {$lng['admin']['configfiles']['importexport']} +

+
+ +
+ + + +
+

+
+
+ + + + + +
+
+
+$footer diff --git a/templates/Sparkle/admin/settings/settings.tpl b/templates/Sparkle/admin/settings/settings.tpl index 7a3075ed..12082722 100644 --- a/templates/Sparkle/admin/settings/settings.tpl +++ b/templates/Sparkle/admin/settings/settings.tpl @@ -3,7 +3,8 @@

  {$lng['admin']['serversettings']}   - [{$lng['admin']['configfiles']['compactoverview']}] + [{$lng['admin']['configfiles']['compactoverview']}]   + [{$lng['admin']['configfiles']['importexport']}]

diff --git a/templates/Sparkle/admin/settings/settings_overview.tpl b/templates/Sparkle/admin/settings/settings_overview.tpl index c1cd32ac..4a59c4c7 100644 --- a/templates/Sparkle/admin/settings/settings_overview.tpl +++ b/templates/Sparkle/admin/settings/settings_overview.tpl @@ -3,7 +3,8 @@

  {$lng['admin']['serversettings']}   - [{$lng['admin']['configfiles']['overview']}] + [{$lng['admin']['configfiles']['overview']}]   + [{$lng['admin']['configfiles']['importexport']}]

From c62dd2ecf4396b25d3a5ca617c25e48e35006e6a Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 29 Jan 2018 20:04:54 +0100 Subject: [PATCH 0395/1335] fix mysql-strict-mode issue (hopefully for good), enhance error-reporting when importing froxlor.sql on installation Signed-off-by: Michael Kaufmann (d00p) --- install/froxlor.sql | 2 +- install/lib/class.FroxlorInstall.php | 298 ++++++++++++++------------- 2 files changed, 161 insertions(+), 139 deletions(-) diff --git a/install/froxlor.sql b/install/froxlor.sql index a54798a4..35ea8098 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -66,7 +66,7 @@ CREATE TABLE `mail_virtual` ( `id` int(11) NOT NULL auto_increment, `email` varchar(255) NOT NULL default '', `email_full` varchar(255) NOT NULL default '', - `destination` text NOT NULL default '', + `destination` text, `domainid` int(11) NOT NULL default '0', `customerid` int(11) NOT NULL default '0', `popaccountid` int(11) NOT NULL default '0', diff --git a/install/lib/class.FroxlorInstall.php b/install/lib/class.FroxlorInstall.php index d253d59b..50d83898 100644 --- a/install/lib/class.FroxlorInstall.php +++ b/install/lib/class.FroxlorInstall.php @@ -28,7 +28,7 @@ * @author Froxlor team (2010-) * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt * @package Install - * + * */ class FroxlorInstall { @@ -74,10 +74,17 @@ class FroxlorInstall /** * currently used language * - * @var unknown + * @var string */ private $_activelng = 'english'; + /** + * check whether to abort due to errors + * + * @var bool + */ + private $_abort = false; + /** * Class constructor */ @@ -154,7 +161,7 @@ class FroxlorInstall $this->_guessServerName(); $this->_guessServerIP(); $this->_guessWebserver(); - + $this->_getPostField('mysql_host', '127.0.0.1'); $this->_getPostField('mysql_database', 'froxlor'); $this->_getPostField('mysql_unpriv_user', 'froxlor'); @@ -169,22 +176,22 @@ class FroxlorInstall $this->_getPostField('httpuser', $posixusername['name']); $posixgroup = posix_getgrgid(posix_getgid()); $this->_getPostField('httpgroup', $posixgroup['name']); - + if ($this->_data['mysql_host'] == 'localhost' || $this->_data['mysql_host'] == '127.0.0.1') { $this->_data['mysql_access_host'] = $this->_data['mysql_host']; } else { $this->_data['mysql_access_host'] = $this->_data['serverip']; } - + // check system-hostname to be a FQDN if ($this->_validate_ip($this->_data['servername']) !== false) { $this->_data['servername'] = ''; } - + if (empty($this->_data['serverip']) || $this->_validate_ip($this->_data['serverip']) == false) { return false; } - + if (isset($_POST['installstep']) && $_POST['installstep'] == '1' && $this->_data['admin_pass1'] == $this->_data['admin_pass2'] && $this->_data['admin_pass1'] != '' && $this->_data['admin_pass2'] != '' && $this->_data['mysql_unpriv_pass'] != '' && $this->_data['mysql_root_pass'] != '' && $this->_data['servername'] != '' && $this->_data['serverip'] != '' && $this->_data['httpuser'] != '' && $this->_data['httpgroup'] != '' && $this->_data['mysql_unpriv_user'] != $this->_data['mysql_root_user']) { return true; } @@ -199,10 +206,10 @@ class FroxlorInstall private function _doInstall() { $content = ""; - + // check for mysql-root-connection $content .= $this->_status_message('begin', $this->_lng['install']['testing_mysql']); - + $options = array( 'PDO::MYSQL_ATTR_INIT_COMMAND' => 'SET names utf8,sql_mode="NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION"' ); @@ -227,9 +234,9 @@ class FroxlorInstall $fatal_fail = true; } } - + if (! $fatal_fail) { - + // ok, if we are here, the database connection is up and running $content .= $this->_status_message('green', "OK"); // check for existing db and create backup if so @@ -238,36 +245,37 @@ class FroxlorInstall $content .= $this->_createDatabaseAndUser($db_root); // importing data to new database $content .= $this->_importDatabaseData(); - // create DB object for new database - $options = array( - 'PDO::MYSQL_ATTR_INIT_COMMAND' => 'SET names utf8,sql_mode="NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION"' - ); - $dsn = "mysql:host=" . $this->_data['mysql_host'] . ";dbname=" . $this->_data['mysql_database'] . ";"; - $another_fail = false; - try { - $db = new PDO($dsn, $this->_data['mysql_unpriv_user'], $this->_data['mysql_unpriv_pass'], $options); - } catch (PDOException $e) { - // dafuq? this should have happened in _importDatabaseData() - $content .= $this->_status_message('red', $e->getMessage()); - $another_fail = true; - } - ; + if (! $this->_abort) { + // create DB object for new database + $options = array( + 'PDO::MYSQL_ATTR_INIT_COMMAND' => 'SET names utf8,sql_mode="NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION"' + ); + $dsn = "mysql:host=" . $this->_data['mysql_host'] . ";dbname=" . $this->_data['mysql_database'] . ";"; + $another_fail = false; + try { + $db = new PDO($dsn, $this->_data['mysql_unpriv_user'], $this->_data['mysql_unpriv_pass'], $options); + } catch (PDOException $e) { + // dafuq? this should have happened in _importDatabaseData() + $content .= $this->_status_message('red', $e->getMessage()); + $another_fail = true; + } - if (! $another_fail) { - // change settings accordingly - $content .= $this->_doSettings($db); - // create entries - $content .= $this->_doDataEntries($db); - $db = null; - // create config-file - $content .= $this->_createUserdataConf(); + if (! $another_fail) { + // change settings accordingly + $content .= $this->_doSettings($db); + // create entries + $content .= $this->_doDataEntries($db); + $db = null; + // create config-file + $content .= $this->_createUserdataConf(); + } } } - + $content .= "
"; - + // check if we have unrecoverable errors - if ($fatal_fail || $another_fail) { + if ($fatal_fail || $another_fai || $this->_abort) { // D'oh $navigation = ''; $msgcolor = 'red'; @@ -282,9 +290,9 @@ class FroxlorInstall $link = '../index.php'; $linktext = $this->_lng['click_here_to_login']; } - + eval("\$navigation .= \"" . $this->_getTemplate("pagebottom") . "\";"); - + return array( 'pagecontent' => $content, 'pagenavigation' => $navigation @@ -297,7 +305,7 @@ class FroxlorInstall private function _createUserdataConf() { $content = ""; - + $content .= $this->_status_message('begin', $this->_lng['install']['creating_configfile']); $userdata = ""; - + // test if we can store the userdata.inc.php in ../lib if ($fp = @fopen(dirname(dirname(dirname(__FILE__))) . '/lib/userdata.inc.php', 'w')) { $result = @fputs($fp, $userdata, strlen($userdata)); @@ -329,7 +337,7 @@ class FroxlorInstall $escpduserdata = nl2br(htmlspecialchars($userdata)); eval("\$content .= \"" . $this->_getTemplate("textarea") . "\";"); } - + return $content; } @@ -343,9 +351,9 @@ class FroxlorInstall private function _doDataEntries(&$db) { $content = ""; - + $content .= $this->_status_message('begin', $this->_lng['install']['creating_entries']); - + // and lets insert the default ip and port $stmt = $db->prepare(" INSERT INTO `" . TABLE_PANEL_IPSANDPORTS . "` SET @@ -359,7 +367,7 @@ class FroxlorInstall 'serverip' => $this->_data['serverip'] )); $defaultip = $db->lastInsertId(); - + // insert the defaultip $upd_stmt = $db->prepare(" UPDATE `" . TABLE_PANEL_SETTINGS . "` SET @@ -369,9 +377,9 @@ class FroxlorInstall $upd_stmt->execute(array( 'defaultip' => $defaultip )); - + $content .= $this->_status_message('green', 'OK'); - + // last but not least create the main admin $content .= $this->_status_message('begin', $this->_lng['install']['adding_admin_user']); $ins_data = array( @@ -406,11 +414,11 @@ class FroxlorInstall `subdomains` = -1, `traffic` = -1048576 "); - + $ins_stmt->execute($ins_data); - + $content .= $this->_status_message('green', 'OK'); - + return $content; } @@ -441,14 +449,14 @@ class FroxlorInstall private function _doSettings(&$db) { $content = ""; - + $content .= $this->_status_message('begin', $this->_lng['install']['changing_data']); $upd_stmt = $db->prepare(" UPDATE `" . TABLE_PANEL_SETTINGS . "` SET `value` = :value WHERE `settinggroup` = :group AND `varname` = :varname "); - + $this->_updateSetting($upd_stmt, 'admin@' . $this->_data['servername'], 'panel', 'adminmail'); $this->_updateSetting($upd_stmt, $this->_data['serverip'], 'system', 'ipaddress'); $this->_updateSetting($upd_stmt, $this->_data['servername'], 'system', 'hostname'); @@ -457,7 +465,7 @@ class FroxlorInstall $this->_updateSetting($upd_stmt, $this->_data['webserver'], 'system', 'webserver'); $this->_updateSetting($upd_stmt, $this->_data['httpuser'], 'system', 'httpuser'); $this->_updateSetting($upd_stmt, $this->_data['httpgroup'], 'system', 'httpgroup'); - + // necessary changes for webservers != apache2 if ($this->_data['webserver'] == "apache24") { $this->_updateSetting($upd_stmt, 'apache2', 'system', 'webserver'); @@ -477,25 +485,25 @@ class FroxlorInstall $this->_updateSetting($upd_stmt, '/etc/nginx/nginx.pem', 'system', 'ssl_cert_file'); $this->_updateSetting($upd_stmt, '/var/run/', 'phpfpm', 'fastcgi_ipcdir'); } - + $this->_updateSetting($upd_stmt, $this->_data['activate_newsfeed'], 'admin', 'show_news_feed'); $this->_updateSetting($upd_stmt, dirname(dirname(dirname(__FILE__))), 'system', 'letsencryptchallengepath'); - + // insert the lastcronrun to be the installation date $this->_updateSetting($upd_stmt, time(), 'system', 'lastcronrun'); - + // set specific times for some crons (traffic only at night, etc.) $ts = mktime(0, 0, 0, date('m', time()), date('d', time()), date('Y', time())); $db->query("UPDATE `" . TABLE_PANEL_CRONRUNS . "` SET `lastrun` = '" . $ts . "' WHERE `cronfile` ='cron_traffic.php';"); $ts = mktime(1, 0, 0, date('m', time()), date('d', time()), date('Y', time())); $db->query("UPDATE `" . TABLE_PANEL_CRONRUNS . "` SET `lastrun` = '" . $ts . "' WHERE `cronfile` ='cron_used_tickets_reset.php';"); $db->query("UPDATE `" . TABLE_PANEL_CRONRUNS . "` SET `lastrun` = '" . $ts . "' WHERE `cronfile` ='cron_ticketarchive.php';"); - + // insert task 99 to generate a correct cron.d-file automatically $db->query("INSERT INTO `" . TABLE_PANEL_TASKS . "` SET `type` = '99';"); - + $content .= $this->_status_message('green', 'OK'); - + return $content; } @@ -517,16 +525,22 @@ class FroxlorInstall $fatal_fail = false; try { $db = new PDO($dsn, $this->_data['mysql_unpriv_user'], $this->_data['mysql_unpriv_pass'], $options); + $attributes = array( + 'ATTR_ERRMODE' => 'ERRMODE_EXCEPTION' + ); + // set attributes + foreach ($attributes as $k => $v) { + $db->setAttribute(constant("PDO::" . $k), constant("PDO::" . $v)); + } } catch (PDOException $e) { $content .= $this->_status_message('red', $e->getMessage()); $fatal_fail = true; } - ; - + if (! $fatal_fail) { - + $content .= $this->_status_message('green', 'OK'); - + $content .= $this->_status_message('begin', $this->_lng['install']['importing_data']); $db_schema = dirname(dirname(__FILE__)) . '/froxlor.sql'; $sql_query = @file_get_contents($db_schema); @@ -534,14 +548,23 @@ class FroxlorInstall $sql_query = $this->_split_sql_file($sql_query, ';'); for ($i = 0; $i < sizeof($sql_query); $i ++) { if (trim($sql_query[$i]) != '') { - $result = $db->query($sql_query[$i]); + try { + $result = $db->query($sql_query[$i]); + } catch (\PDOException $e) { + $content .= $this->_status_message('red', $e->getMessage()); + $fatal_fail = true; + $this->_abort = true; + break; + } } } + + if (! $fatal_fail) { + $content .= $this->_status_message('green', 'OK'); + } $db = null; - - $content .= $this->_status_message('green', 'OK'); } - + return $content; } @@ -555,56 +578,56 @@ class FroxlorInstall private function _createDatabaseAndUser(&$db_root) { $content = ""; - + // so first we have to delete the database and // the user given for the unpriv-user if they exit $content .= $this->_status_message('begin', $this->_lng['install']['prepare_db']); - + $del_stmt = $db_root->prepare("DELETE FROM `mysql`.`user` WHERE `User` = :user AND `Host` = :accesshost"); $del_stmt->execute(array( 'user' => $this->_data['mysql_unpriv_user'], 'accesshost' => $this->_data['mysql_access_host'] )); - + $del_stmt = $db_root->prepare("DELETE FROM `mysql`.`db` WHERE `User` = :user AND `Host` = :accesshost"); $del_stmt->execute(array( 'user' => $this->_data['mysql_unpriv_user'], 'accesshost' => $this->_data['mysql_access_host'] )); - + $del_stmt = $db_root->prepare("DELETE FROM `mysql`.`tables_priv` WHERE `User` = :user AND `Host` =:accesshost"); $del_stmt->execute(array( 'user' => $this->_data['mysql_unpriv_user'], 'accesshost' => $this->_data['mysql_access_host'] )); - + $del_stmt = $db_root->prepare("DELETE FROM `mysql`.`columns_priv` WHERE `User` = :user AND `Host` = :accesshost"); $del_stmt->execute(array( 'user' => $this->_data['mysql_unpriv_user'], 'accesshost' => $this->_data['mysql_access_host'] )); - + $del_stmt = $db_root->prepare("DROP DATABASE IF EXISTS `" . str_replace('`', '', $this->_data['mysql_database']) . "`;"); $del_stmt->execute(); - + $db_root->query("FLUSH PRIVILEGES;"); $content .= $this->_status_message('green', 'OK'); - + // we have to create a new user and database for the froxlor unprivileged mysql access $content .= $this->_status_message('begin', $this->_lng['install']['create_mysqluser_and_db']); $ins_stmt = $db_root->prepare("CREATE DATABASE `" . str_replace('`', '', $this->_data['mysql_database']) . "` CHARACTER SET=utf8 COLLATE=utf8_general_ci"); $ins_stmt->execute(); - + $mysql_access_host_array = array_map('trim', explode(',', $this->_data['mysql_access_host'])); - + if (in_array('127.0.0.1', $mysql_access_host_array) && ! in_array('localhost', $mysql_access_host_array)) { $mysql_access_host_array[] = 'localhost'; } - + if (! in_array('127.0.0.1', $mysql_access_host_array) && in_array('localhost', $mysql_access_host_array)) { $mysql_access_host_array[] = '127.0.0.1'; } - + $mysql_access_host_array[] = $this->_data['serverip']; foreach ($mysql_access_host_array as $mysql_access_host) { $_db = str_replace('`', '', $this->_data['mysql_database']); @@ -623,11 +646,11 @@ class FroxlorInstall "password" => $this->_data['mysql_unpriv_pass'] )); } - + $db_root->query("FLUSH PRIVILEGES;"); $this->_data['mysql_access_host'] = implode(',', $mysql_access_host_array); $content .= $this->_status_message('green', 'OK'); - + return $content; } @@ -641,7 +664,7 @@ class FroxlorInstall private function _backupExistingDatabase(&$db_root) { $content = ""; - + // check for existing of former database $tables_exist = false; $sql = "SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = :database"; @@ -650,19 +673,19 @@ class FroxlorInstall 'database' => $this->_data['mysql_database'] )); $rows = $db_root->query("SELECT FOUND_ROWS()")->fetchColumn(); - + // check result if ($result_stmt !== false && $rows > 0) { $tables_exist = true; } - + if ($tables_exist) { // tell whats going on $content .= $this->_status_message('begin', $this->_lng['install']['backup_old_db']); - + // create temporary backup-filename $filename = "/tmp/froxlor_backup_" . date('YmdHi') . ".sql"; - + // look for mysqldump $do_backup = false; if (file_exists("/usr/bin/mysqldump")) { @@ -672,7 +695,7 @@ class FroxlorInstall $do_backup = true; $mysql_dump = '/usr/local/bin/mysqldump'; } - + 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; $output = exec($command); @@ -685,7 +708,7 @@ class FroxlorInstall $content .= $this->_status_message('red', $this->_lng['install']['backup_binary_missing']); } } - + return $content; } @@ -707,7 +730,7 @@ class FroxlorInstall } // get language-form-template eval("\$content .= \"" . $this->_getTemplate("lngform") . "\";"); - + // form-data $formdata = ""; /** @@ -747,7 +770,7 @@ class FroxlorInstall $style = ''; } $formdata .= $this->_getSectionItemString('mysql_root_pass', true, $style, 'password'); - + /** * admin data */ @@ -771,7 +794,7 @@ class FroxlorInstall $formdata .= $this->_getSectionItemString('admin_pass2', true, $style, 'password'); // activate newsfeed? $formdata .= $this->_getSectionItemYesNo('activate_newsfeed', true); - + /** * Server data */ @@ -818,11 +841,11 @@ class FroxlorInstall $style = ''; } $formdata .= $this->_getSectionItemString('httpgroup', true, $style); - + // get data-form-template $language = htmlspecialchars($this->_activelng); eval("\$content .= \"" . $this->_getTemplate("dataform2") . "\";"); - + $navigation = ''; return array( 'pagecontent' => $content, @@ -839,7 +862,7 @@ class FroxlorInstall * optional css * @param string $type * optional type of input-box (default: text) - * + * * @return string */ private function _getSectionItemString($fieldname = null, $required = false, $style = "", $type = 'text') @@ -899,26 +922,26 @@ class FroxlorInstall */ private function _requirementCheck() { - + // indicator whether we need to abort or not $_die = false; - + $content = ""; - + // check for correct php version $content .= $this->_status_message('begin', $this->_lng['requirements']['phpversion']); - + if (version_compare("5.3.0", PHP_VERSION, ">=")) { $content .= $this->_status_message('red', $this->_lng['requirements']['notfound'] . ' (' . PHP_VERSION . ')'); $_die = true; } else { if (version_compare("5.6.0", PHP_VERSION, ">=")) { - $content .= $this->_status_message('orange', $this->_lng['requirements']['newerphpprefered'] . ' (' .PHP_VERSION . ')'); + $content .= $this->_status_message('orange', $this->_lng['requirements']['newerphpprefered'] . ' (' . PHP_VERSION . ')'); } else { $content .= $this->_status_message('green', PHP_VERSION); } } - + // Check if magic_quotes_runtime is active | get_magic_quotes_runtime() is always FALSE since 5.4 if (version_compare(PHP_VERSION, "5.4.0", "<")) { $content .= $this->_status_message('begin', $this->_lng['requirements']['phpmagic_quotes_runtime']); @@ -930,85 +953,85 @@ class FroxlorInstall $content .= $this->_status_message('green', 'off'); } } - + // check for php_pdo and pdo_mysql $content .= $this->_status_message('begin', $this->_lng['requirements']['phppdo']); - + if (! extension_loaded('pdo') || in_array("mysql", PDO::getAvailableDrivers()) == false) { $content .= $this->_status_message('red', $this->_lng['requirements']['notinstalled']); $_die = true; } else { $content .= $this->_status_message('green', $this->_lng['requirements']['installed']); } - + // check for xml-extension $content .= $this->_status_message('begin', $this->_lng['requirements']['phpxml']); - + if (! extension_loaded('xml')) { $content .= $this->_status_message('red', $this->_lng['requirements']['notinstalled']); $_die = true; } else { $content .= $this->_status_message('green', $this->_lng['requirements']['installed']); } - + // check for filter-extension $content .= $this->_status_message('begin', $this->_lng['requirements']['phpfilter']); - + if (! extension_loaded('filter')) { $content .= $this->_status_message('red', $this->_lng['requirements']['notinstalled']); $_die = true; } else { $content .= $this->_status_message('green', $this->_lng['requirements']['installed']); } - + // check for posix-extension $content .= $this->_status_message('begin', $this->_lng['requirements']['phpposix']); - + if (! extension_loaded('posix')) { $content .= $this->_status_message('red', $this->_lng['requirements']['notinstalled']); $_die = true; } else { $content .= $this->_status_message('green', $this->_lng['requirements']['installed']); } - + // check for bstring-extension $content .= $this->_status_message('begin', $this->_lng['requirements']['phpmbstring']); - + if (! extension_loaded('mbstring')) { $content .= $this->_status_message('red', $this->_lng['requirements']['notinstalled']); $_die = true; } else { $content .= $this->_status_message('green', $this->_lng['requirements']['installed']); } - + // check for curl extension $content .= $this->_status_message('begin', $this->_lng['requirements']['phpcurl']); - + if (! extension_loaded('curl')) { $content .= $this->_status_message('red', $this->_lng['requirements']['notinstalled']); $_die = true; } else { $content .= $this->_status_message('green', $this->_lng['requirements']['installed']); } - + // check for bcmath extension $content .= $this->_status_message('begin', $this->_lng['requirements']['phpbcmath']); - + if (! extension_loaded('bcmath')) { $content .= $this->_status_message('orange', $this->_lng['requirements']['notinstalled'] . "
" . $this->_lng['requirements']['bcmathdescription']); } else { $content .= $this->_status_message('green', $this->_lng['requirements']['installed']); } - + // check for zip extension $content .= $this->_status_message('begin', $this->_lng['requirements']['phpzip']); - + if (! extension_loaded('zip')) { $content .= $this->_status_message('orange', $this->_lng['requirements']['notinstalled'] . "
" . $this->_lng['requirements']['zipdescription']); } else { $content .= $this->_status_message('green', $this->_lng['requirements']['installed']); } - + // check for open_basedir $content .= $this->_status_message('begin', $this->_lng['requirements']['openbasedir']); $php_ob = @ini_get("open_basedir"); @@ -1018,7 +1041,7 @@ class FroxlorInstall $content .= $this->_status_message('green', 'off'); } $content .= "
"; - + // check if we have unrecoverable errors $navigation = ''; if ($_die) { @@ -1033,7 +1056,7 @@ class FroxlorInstall $linktext = $this->_lng['click_here_to_continue']; } eval("\$navigation .= \"" . $this->_getTemplate("pagebottom") . "\";"); - + return array( 'pagecontent' => $content, 'pagenavigation' => $navigation @@ -1050,7 +1073,7 @@ class FroxlorInstall header("Pragma: no-cache"); header('Last-Modified: ' . gmdate('D, d M Y H:i:s \G\M\T', time())); header('Expires: ' . gmdate('D, d M Y H:i:s \G\M\T', time())); - + // ensure that default timezone is set if (function_exists("date_default_timezone_set") && function_exists("date_default_timezone_get")) { @date_default_timezone_set(@date_default_timezone_get()); @@ -1068,7 +1091,7 @@ class FroxlorInstall // includes the usersettings (MySQL-Username/Passwort) // to test if Froxlor is already installed require $this->_basepath . '/lib/userdata.inc.php'; - + if (isset($sql) && is_array($sql)) { // use sparkle theme for the notice $installed_hint = file_get_contents($this->_basepath . '/templates/Sparkle/misc/alreadyinstalledhint.tpl'); @@ -1085,7 +1108,7 @@ class FroxlorInstall { // set default $standardlanguage = 'english'; - + // check either _GET or _POST if (isset($_GET['language']) && isset($this->_languages[$_GET['language']])) { $this->_activelng = $_GET['language']; @@ -1106,7 +1129,7 @@ class FroxlorInstall break; } } - + $lngfile = $this->_basepath . '/install/lng/' . $this->_activelng . '.lng.php'; if (file_exists($lngfile)) { // includes file /lng/$language.lng.php if it exists @@ -1120,7 +1143,7 @@ class FroxlorInstall * * @param string $template * name of the template including subdirectory - * + * * @return string */ private function _getTemplate($template = null) @@ -1137,7 +1160,7 @@ class FroxlorInstall } else { $templatefile = 'TEMPLATE NOT FOUND: ' . $filename; } - + return $templatefile; } @@ -1168,14 +1191,13 @@ class FroxlorInstall $this->_data['servername'] = $_POST['servername']; return; // from $_SERVER - } else - if (! empty($_SERVER['SERVER_NAME'])) { - // no ips - if ($this->_validate_ip($_SERVER['SERVER_NAME']) == false) { - $this->_data['servername'] = $_SERVER['SERVER_NAME']; - return; - } + } else if (! empty($_SERVER['SERVER_NAME'])) { + // no ips + if ($this->_validate_ip($_SERVER['SERVER_NAME']) == false) { + $this->_data['servername'] = $_SERVER['SERVER_NAME']; + return; } + } // empty $this->_data['servername'] = ''; } @@ -1294,17 +1316,17 @@ class FroxlorInstall */ private function _split_sql_file($sql, $delimiter) { - + // Split up our string into "possible" SQL statements. $tokens = explode($delimiter, $sql); - + // try to save mem. $sql = ""; $output = array(); - + // we don't actually care about the matches preg gives us. $matches = array(); - + // this is faster than calling count($tokens) every time through the loop. $token_count = count($tokens); for ($i = 0; $i < $token_count; $i ++) { @@ -1312,12 +1334,12 @@ class FroxlorInstall if (($i != ($token_count - 1)) || (strlen($tokens[$i] > 0))) { // This is the total number of single quotes in the token. $total_quotes = preg_match_all("/'/", $tokens[$i], $matches); - + // Counts single quotes that are preceded by an odd number of backslashes, // which means they're escaped quotes. $escaped_quotes = preg_match_all("/(? Date: Tue, 30 Jan 2018 07:58:24 +0100 Subject: [PATCH 0396/1335] add requirement check for php-json as settings import/export uses json_decode/json_encode Signed-off-by: Michael Kaufmann (d00p) --- admin_settings.php | 5 +++++ install/lib/class.FroxlorInstall.php | 9 +++++++++ install/lng/english.lng.php | 2 ++ install/lng/german.lng.php | 2 ++ lng/english.lng.php | 1 + lng/german.lng.php | 1 + 6 files changed, 20 insertions(+) diff --git a/admin_settings.php b/admin_settings.php index 538df344..ac8ff85c 100644 --- a/admin_settings.php +++ b/admin_settings.php @@ -292,6 +292,11 @@ if ($page == 'overview' && $userinfo['change_serversettings'] == '1') { } elseif ($page == 'importexport' && $userinfo['change_serversettings'] == '1') { + // check for json-stuff + if (! extension_loaded('json')) { + standard_error('jsonextensionnotfound'); + } + if (isset($_GET['action']) && $_GET['action'] == "export") { // export try { diff --git a/install/lib/class.FroxlorInstall.php b/install/lib/class.FroxlorInstall.php index 50d83898..96c82c74 100644 --- a/install/lib/class.FroxlorInstall.php +++ b/install/lib/class.FroxlorInstall.php @@ -1031,7 +1031,16 @@ class FroxlorInstall } else { $content .= $this->_status_message('green', $this->_lng['requirements']['installed']); } + + // check for json extension + $content .= $this->_status_message('begin', $this->_lng['requirements']['phpjson']); + if (! extension_loaded('json')) { + $content .= $this->_status_message('orange', $this->_lng['requirements']['notinstalled'] . "
" . $this->_lng['requirements']['jsondescription']); + } else { + $content .= $this->_status_message('green', $this->_lng['requirements']['installed']); + } + // check for open_basedir $content .= $this->_status_message('begin', $this->_lng['requirements']['openbasedir']); $php_ob = @ini_get("open_basedir"); diff --git a/install/lng/english.lng.php b/install/lng/english.lng.php index 409421a5..8651bb83 100644 --- a/install/lng/english.lng.php +++ b/install/lng/english.lng.php @@ -35,8 +35,10 @@ $lng['requirements']['phpbcmath'] = 'PHP bcmath-extension...'; $lng['requirements']['phpcurl'] = 'PHP curl-extension...'; $lng['requirements']['phpmbstring'] = 'PHP mbstring-extension...'; $lng['requirements']['phpzip'] = 'PHP zip-extension...'; +$lng['requirements']['phpjson'] = 'PHP json-extension...'; $lng['requirements']['bcmathdescription'] = 'Traffic-calculation related functions will not work correctly!'; $lng['requirements']['zipdescription'] = 'The auto-update feature requires the zip extension.'; +$lng['requirements']['jsondescription'] = 'The settings import/export feature requires the json extension.'; $lng['requirements']['openbasedir'] = 'open_basedir...'; $lng['requirements']['openbasedirenabled'] = 'Froxlor will not work properly with open_basedir enabled. Please disable open_basedir for Froxlor in the coresponding php.ini'; $lng['requirements']['diedbecauseofrequirements'] = 'Cannot install Froxlor without these requirements! Try to fix them and retry.'; diff --git a/install/lng/german.lng.php b/install/lng/german.lng.php index 02dd9b8d..d18e94c0 100644 --- a/install/lng/german.lng.php +++ b/install/lng/german.lng.php @@ -35,8 +35,10 @@ $lng['requirements']['phpbcmath'] = 'PHP bcmath-Erweiterung...'; $lng['requirements']['phpcurl'] = 'PHP curl-Erweiterung...'; $lng['requirements']['phpmbstring'] = 'PHP mbstring-Erweiterung...'; $lng['requirements']['phpzip'] = 'PHP zip-Erweiterung...'; +$lng['requirements']['phpjson'] = 'PHP json-Erweiterung...'; $lng['requirements']['bcmathdescription'] = 'Traffic-Berechnungs bezogene Funktionen stehen nicht vollständig zur Verfügung!'; $lng['requirements']['zipdescription'] = 'Die Auto-Update Funktion benötigt die zip Erweiterung.'; +$lng['requirements']['jsondescription'] = 'Die Einstellungen Import/Export Funktion benötigt die json Erweiterung.'; $lng['requirements']['openbasedir'] = 'open_basedir genutzt wird...'; $lng['requirements']['openbasedirenabled'] = 'Froxlor wird mit aktiviertem open_basedir nicht vollständig funktionieren. Bitte deaktivieren Sie open_basedir für Froxlor in der entsprechenden php.ini'; $lng['requirements']['diedbecauseofrequirements'] = 'Kann Froxlor ohne diese Voraussetzungen nicht installieren! Beheben Sie die angezeigten Probleme und versuchen Sie es erneut.'; diff --git a/lng/english.lng.php b/lng/english.lng.php index 64d12df9..3123a1db 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -2103,3 +2103,4 @@ $lng['phpfpm']['ini_admin_values'] = 'Enter possible php_admin_value Date: Tue, 30 Jan 2018 14:41:56 +0100 Subject: [PATCH 0397/1335] fix typo Signed-off-by: Michael Kaufmann (d00p) --- install/lib/class.FroxlorInstall.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/lib/class.FroxlorInstall.php b/install/lib/class.FroxlorInstall.php index 96c82c74..128a7104 100644 --- a/install/lib/class.FroxlorInstall.php +++ b/install/lib/class.FroxlorInstall.php @@ -275,7 +275,7 @@ class FroxlorInstall $content .= ""; // check if we have unrecoverable errors - if ($fatal_fail || $another_fai || $this->_abort) { + if ($fatal_fail || $another_fail || $this->_abort) { // D'oh $navigation = ''; $msgcolor = 'red'; From f38a0fd8b656aa942bd387b11ae580d6aa09bad6 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 5 Feb 2018 08:36:25 +0100 Subject: [PATCH 0398/1335] remove unnecessary and out-of-date info from README, fixed variable typo in lib/init.php Signed-off-by: Michael Kaufmann (d00p) --- README.md | 19 +++---------------- lib/init.php | 2 +- 2 files changed, 4 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 35d9a624..33cb17cb 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ irc://chat.freenode.net/froxlor ### Forum -The community is located on http://forum.froxlor.org +The community is located on https://forum.froxlor.org/ ### Wiki @@ -44,7 +44,7 @@ May be found in COPYING ## Downloads ### Tarball -http://files.froxlor.org/releases/froxlor-latest.tar.gz [MD5](http://files.froxlor.org/releases/froxlor-latest.tar.gz.md5) [SHA1](http://files.froxlor.org/releases/froxlor-latest.tar.gz.sha1) +https://files.froxlor.org/releases/froxlor-latest.tar.gz [MD5](https://files.froxlor.org/releases/froxlor-latest.tar.gz.md5) [SHA1](https://files.froxlor.org/releases/froxlor-latest.tar.gz.sha1) ### Debian repository @@ -57,20 +57,7 @@ http://files.froxlor.org/releases/froxlor-latest.tar.gz [MD5](http://files.froxl [HowTo](https://github.com/Froxlor/Froxlor/wiki/Install-froxlor-on-gentoo) -http://files.froxlor.org/gentoo/repositories.xml - -## Let's Encrypt support - -This version of Froxlor contains a test implementation of support for [Let's Encrypt](https://letsencrypt.org). This is (as Let's Encrypt is in itself) -still a beta version and may break your system. The way it currently works is by creating a (sub-)domain with the default system - certificate, -after which the Let's Encrypt cronjob orders the certificate for this (sub-)domain and inserts the certificates in the database. With the next run -of the default cronjob, the certificates will be updated on the disk and the webserver reloaded. - -This has 2 known side-effects at the moment: -* The basic ip/port combinations don't work with the Froxlor - integration of Let's Encrypt, since it needs a certificate for the very first creation -* After creating a domain, it will have the default certificate for a short time (by default 5 minutes until the cronjob runs the next time) - -It may be possible to fix these issues, but they are not a priority at the moment +https://files.froxlor.org/gentoo/repositories.xml ## Contributing diff --git a/lib/init.php b/lib/init.php index 3436b237..3dc69df7 100644 --- a/lib/init.php +++ b/lib/init.php @@ -157,7 +157,7 @@ if (version_compare(PHP_VERSION, "5.4.0", "<")) { $in = array(&$_GET, &$_POST, &$_COOKIE); $_in = $in; - foreach ($in as $k => $v) { + foreach ($_in as $k => $v) { foreach ($v as $key => $val) { if (!is_array($val)) { $in[$k][$key] = stripslashes($val); From 0aa707ebc98dd5431b9840651bc4df0d55c626cf Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 6 Feb 2018 12:19:21 +0100 Subject: [PATCH 0399/1335] set version to 0.9.39 for upcoming release Signed-off-by: Michael Kaufmann (d00p) --- install/froxlor.sql | 2 +- install/updates/froxlor/0.9/update_0.9.inc.php | 6 ++++++ lib/configfiles/wheezy.xml | 2 +- lib/version.inc.php | 2 +- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/install/froxlor.sql b/install/froxlor.sql index 35ea8098..25e4c506 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -686,7 +686,7 @@ opcache.interned_strings_buffer'), ('panel', 'password_special_char_required', '0'), ('panel', 'password_special_char', '!?<>§$%+#=@'), ('panel', 'customer_hide_options', ''), - ('panel', 'version', '0.9.38.8'), + ('panel', 'version', '0.9.39'), ('panel', 'db_version', '201801260'); diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index 6c9fa665..cfca0ef6 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -3874,3 +3874,9 @@ if (isDatabaseVersion('201801110')) { updateToDbVersion('201801260'); } + +if (isFroxlorVersion('0.9.38.8')) { + + showUpdateStep("Updating from 0.9.38.8 to 0.9.39 final", false); + updateToVersion('0.9.39'); +} diff --git a/lib/configfiles/wheezy.xml b/lib/configfiles/wheezy.xml index b6fe5698..871e64b5 100644 --- a/lib/configfiles/wheezy.xml +++ b/lib/configfiles/wheezy.xml @@ -1,6 +1,6 @@ - + diff --git a/lib/version.inc.php b/lib/version.inc.php index 5ebe2f3b..2f62182e 100644 --- a/lib/version.inc.php +++ b/lib/version.inc.php @@ -16,7 +16,7 @@ */ // Main version variable -$version = '0.9.38.8'; +$version = '0.9.39'; // Database version (YYYYMMDDC where C is a daily counter) $dbversion = '201801260'; From ec21e28000a5ff360e2d08be9aa96c855b66ffb5 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Wed, 7 Feb 2018 15:10:59 +0100 Subject: [PATCH 0400/1335] use md5() instead of base64_encode for dummy-fpm-socket name to avoid possible equal-sign in string which leads to an invalid socket, fixes #513 Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/phpinterface/class.phpinterface_fpm.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/classes/phpinterface/class.phpinterface_fpm.php b/lib/classes/phpinterface/class.phpinterface_fpm.php index daf358ad..8ec93b3e 100644 --- a/lib/classes/phpinterface/class.phpinterface_fpm.php +++ b/lib/classes/phpinterface/class.phpinterface_fpm.php @@ -361,7 +361,7 @@ class phpinterface_fpm $config = makeCorrectFile($configdir . '/dummy.conf'); $dummy = "[dummy] user = ".Settings::Get('system.httpuser')." -listen = /run/" . base64_encode($configdir) . "-fpm.sock +listen = /run/" . md5($configdir) . "-fpm.sock pm = static pm.max_children = 1 "; From b4dd35eed2a4bac838eedc7bb28b9648e0072016 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 8 Feb 2018 07:35:06 +0100 Subject: [PATCH 0401/1335] correct description of nsswitch.conf file Signed-off-by: Michael Kaufmann (d00p) --- lib/configfiles/jessie.xml | 4 ++-- lib/configfiles/precise.xml | 4 ++-- lib/configfiles/stretch.xml | 4 ++-- lib/configfiles/trusty.xml | 4 ++-- lib/configfiles/wheezy.xml | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/configfiles/jessie.xml b/lib/configfiles/jessie.xml index ccdbe9a3..da0faefe 100644 --- a/lib/configfiles/jessie.xml +++ b/lib/configfiles/jessie.xml @@ -4643,8 +4643,8 @@ aliases: files Date: Fri, 9 Feb 2018 07:33:28 +0100 Subject: [PATCH 0402/1335] fix selected phpfpm daemon when editing php-configuration, fixes #514 Signed-off-by: Michael Kaufmann (d00p) --- admin_phpsettings.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/admin_phpsettings.php b/admin_phpsettings.php index f43ce116..f76b4dcd 100644 --- a/admin_phpsettings.php +++ b/admin_phpsettings.php @@ -349,7 +349,7 @@ if ($page == 'overview') { $fpmconfigs = ''; $configs = Database::query("SELECT * FROM `" . TABLE_PANEL_FPMDAEMONS . "` ORDER BY `description` ASC"); while ($row = $configs->fetch(PDO::FETCH_ASSOC)) { - $fpmconfigs .= makeoption($row['description'], $row['id'], $id, true, true); + $fpmconfigs .= makeoption($row['description'], $row['id'], $result['fpmsettingid'], true, true); } $phpconfig_edit_data = include_once dirname(__FILE__) . '/lib/formfields/admin/phpconfig/formfield.phpconfig_edit.php'; From 05b4c58aa83856fcb21edcfaa6dfa439eaad6eb0 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Fri, 9 Feb 2018 09:20:46 +0100 Subject: [PATCH 0403/1335] fix updating wrong column when deleting a fpm configuration Signed-off-by: Michael Kaufmann (d00p) --- admin_phpsettings.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/admin_phpsettings.php b/admin_phpsettings.php index f76b4dcd..1d922b3f 100644 --- a/admin_phpsettings.php +++ b/admin_phpsettings.php @@ -496,7 +496,7 @@ if ($page == 'overview') { // set default fpm daemon config for all php-config that use this config that is to be deleted $upd_stmt = Database::prepare(" UPDATE `" . TABLE_PANEL_PHPCONFIGS . "` SET - `phpsettingid` = '1' WHERE `phpsettingid` = :id"); + `fpmsettingid` = '1' WHERE `fpmsettingid` = :id"); Database::pexecute($upd_stmt, array( 'id' => $id )); From 4d3fa6eca510329542ec4518c4bc029533051d01 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Fri, 9 Feb 2018 10:50:14 +0100 Subject: [PATCH 0404/1335] get rid of the need for allow_url_fopen Signed-off-by: Michael Kaufmann (d00p) --- admin_autoupdate.php | 238 ++++++++++++++------------ admin_index.php | 52 +++--- lib/ajax.php | 82 +++++---- lib/classes/cURL/class.HttpClient.php | 60 +++++++ lib/classes/ssl/class.lescript.php | 5 +- lib/classes/ssl/class.lescript_v2.php | 9 +- 6 files changed, 254 insertions(+), 192 deletions(-) create mode 100644 lib/classes/cURL/class.HttpClient.php diff --git a/admin_autoupdate.php b/admin_autoupdate.php index da4da2ae..701e8011 100644 --- a/admin_autoupdate.php +++ b/admin_autoupdate.php @@ -17,7 +17,6 @@ * @since 0.9.35 * */ - define('AREA', 'admin'); require './lib/init.php'; @@ -26,177 +25,188 @@ define('UPDATE_URI', "https://version.froxlor.org/Froxlor/legacy/" . $version); define('RELEASE_URI', "https://autoupdate.froxlor.org/froxlor-{version}.zip"); define('CHECKSUM_URI', "https://autoupdate.froxlor.org/froxlor-{version}.zip.sha256"); -// check for allow_url_fopen -if (ini_get('allow_url_fopen') === false) { - redirectTo($filename, array('s' => $s, 'page' => 'error', 'errno' => 1)); -} - // check for archive-stuff if (! extension_loaded('zip')) { - redirectTo($filename, array('s' => $s, 'page' => 'error', 'errno' => 2)); + redirectTo($filename, array( + 's' => $s, + 'page' => 'error', + 'errno' => 2 + )); } // display initial version check if ($page == 'overview') { - + // log our actions $log->logAction(ADM_ACTION, LOG_NOTICE, "checking auto-update"); - + // check for new version - $latestversion = @file(UPDATE_URI); - - if (isset($latestversion[0])) { - $latestversion = explode('|', $latestversion[0]); - - if (is_array($latestversion) - && count($latestversion) >= 1 - ) { - $_version = $latestversion[0]; - $_message = isset($latestversion[1]) ? $latestversion[1] : ''; - $_link = isset($latestversion[2]) ? $latestversion[2] : htmlspecialchars($filename . '?s=' . urlencode($s) . '&page=' . urlencode($page) . '&lookfornewversion=yes'); - - // add the branding so debian guys are not gettings confused - // about their version-number - $version_label = $_version.$branding; - $version_link = $_link; - $message_addinfo = $_message; - - // not numeric -> error-message - if (!preg_match('/^((\d+\\.)(\d+\\.)(\d+\\.)?(\d+)?(\-(svn|dev|rc)(\d+))?)$/', $_version)) { - // check for customized version to not output - // "There is a newer version of froxlor" besides the error-message - redirectTo($filename, array('s' => $s, 'page' => 'error', 'errno' => 3)); - } elseif (version_compare2($version, $_version) == -1) { - // there is a newer version - yay - $isnewerversion = 1; - } else { - // nothing new - $isnewerversion = 0; - } - - // anzeige über version-status mit ggfls. formular - // zum update schritt #1 -> download - if ($isnewerversion == 1) { - $text = 'There is a newer version available. Update to version '.$_version.' now?
(Your current version is: '.$version.')'; - $hiddenparams = ''; - $yesfile = $filename.'?s='.$s.'&page=getdownload'; - eval("echo \"" . getTemplate("misc/question_yesno", true) . "\";"); - exit; - } - elseif ($isnewerversion == 0) { - // all good - standard_success ('noupdatesavail'); - } else { - standard_error ('customized_version'); - } + $latestversion = HttpClient::urlGet(UPDATE_URI); + + $latestversion = explode('|', $latestversion); + + if (is_array($latestversion) && count($latestversion) >= 1) { + $_version = $latestversion[0]; + $_message = isset($latestversion[1]) ? $latestversion[1] : ''; + $_link = isset($latestversion[2]) ? $latestversion[2] : htmlspecialchars($filename . '?s=' . urlencode($s) . '&page=' . urlencode($page) . '&lookfornewversion=yes'); + + // add the branding so debian guys are not gettings confused + // about their version-number + $version_label = $_version . $branding; + $version_link = $_link; + $message_addinfo = $_message; + + // not numeric -> error-message + if (! preg_match('/^((\d+\\.)(\d+\\.)(\d+\\.)?(\d+)?(\-(svn|dev|rc)(\d+))?)$/', $_version)) { + // check for customized version to not output + // "There is a newer version of froxlor" besides the error-message + redirectTo($filename, array( + 's' => $s, + 'page' => 'error', + 'errno' => 3 + )); + } elseif (version_compare2($version, $_version) == - 1) { + // there is a newer version - yay + $isnewerversion = 1; + } else { + // nothing new + $isnewerversion = 0; + } + + // anzeige über version-status mit ggfls. formular + // zum update schritt #1 -> download + if ($isnewerversion == 1) { + $text = 'There is a newer version available. Update to version ' . $_version . ' now?
(Your current version is: ' . $version . ')'; + $hiddenparams = ''; + $yesfile = $filename . '?s=' . $s . '&page=getdownload'; + eval("echo \"" . getTemplate("misc/question_yesno", true) . "\";"); + exit(); + } elseif ($isnewerversion == 0) { + // all good + standard_success('noupdatesavail'); + } else { + standard_error('customized_version'); } } - // error (something weird came from version.froxlor.org) - redirectTo($filename, array('s' => $s, 'page' => 'error', 'errno' => 5)); -} -// download the new archive +}// download the new archive elseif ($page == 'getdownload') { - + // retrieve the new version from the form $newversion = isset($_POST['newversion']) ? $_POST['newversion'] : null; - + // valid? if ($newversion !== null) { - + // define files to get $toLoad = str_replace('{version}', $newversion, RELEASE_URI); $toCheck = str_replace('{version}', $newversion, CHECKSUM_URI); - - // get archive data - $newArchive = @file_get_contents($toLoad); - + // check for local destination folder - if (!is_dir(FROXLOR_INSTALL_DIR.'/updates/')) { - mkdir(FROXLOR_INSTALL_DIR.'/updates/'); + if (! is_dir(FROXLOR_INSTALL_DIR . '/updates/')) { + mkdir(FROXLOR_INSTALL_DIR . '/updates/'); } - + // name archive - $localArchive = FROXLOR_INSTALL_DIR.'/updates/'.basename($toLoad); - - $log->logAction(ADM_ACTION, LOG_NOTICE, "Downloading ".$toLoad." to ".$localArchive); - + $localArchive = FROXLOR_INSTALL_DIR . '/updates/' . basename($toLoad); + + $log->logAction(ADM_ACTION, LOG_NOTICE, "Downloading " . $toLoad . " to " . $localArchive); + // remove old archive if (file_exists($localArchive)) { @unlink($localArchive); } - - // store archive - $fh = fopen($localArchive, 'w'); - if (!fwrite($fh, $newArchive)) { - redirectTo($filename, array('s' => $s, 'page' => 'error', 'errno' => 4)); + + // get archive data + try { + HttpClient::fileGet($toLoad, $localArchive); + } catch (Exception $e) { + redirectTo($filename, array( + 's' => $s, + 'page' => 'error', + 'errno' => 4 + )); } - - // close file-handle - fclose($fh); - + // validate the integrity of the downloaded file - $_shouldsum = @file_get_contents($toCheck); - if (!empty($_shouldsum)) { + $_shouldsum = HttpClient::urlGet($toCheck); + if (! empty($_shouldsum)) { $_t = explode(" ", $_shouldsum); $shouldsum = $_t[0]; } else { $shouldsum = null; } $filesum = hash_file('sha256', $localArchive); - + if ($filesum != $shouldsum) { - redirectTo($filename, array('s' => $s, 'page' => 'error', 'errno' => 9)); + redirectTo($filename, array( + 's' => $s, + 'page' => 'error', + 'errno' => 9 + )); } - + // to the next step - redirectTo($filename, array('s' => $s, 'page' => 'extract', 'archive' => basename($localArchive))); + redirectTo($filename, array( + 's' => $s, + 'page' => 'extract', + 'archive' => basename($localArchive) + )); } - redirectTo($filename, array('s' => $s, 'page' => 'error', 'errno' => 6)); -} -// extract and install new version + redirectTo($filename, array( + 's' => $s, + 'page' => 'error', + 'errno' => 6 + )); +}// extract and install new version elseif ($page == 'extract') { - + $toExtract = isset($_GET['archive']) ? $_GET['archive'] : null; - $localArchive = FROXLOR_INSTALL_DIR.'/updates/'.$toExtract; - - if (isset($_POST['send']) - && $_POST['send'] == 'send' - ) { + $localArchive = FROXLOR_INSTALL_DIR . '/updates/' . $toExtract; + + if (isset($_POST['send']) && $_POST['send'] == 'send') { // decompress from zip - $zip = new ZipArchive; + $zip = new ZipArchive(); $res = $zip->open($localArchive); if ($res === true) { - $log->logAction(ADM_ACTION, LOG_NOTICE, "Extracting ".$localArchive." to ".FROXLOR_INSTALL_DIR); + $log->logAction(ADM_ACTION, LOG_NOTICE, "Extracting " . $localArchive . " to " . FROXLOR_INSTALL_DIR); $zip->extractTo(FROXLOR_INSTALL_DIR); $zip->close(); // success - remove unused archive @unlink($localArchive); } else { // error - redirectTo($filename, array('s' => $s, 'page' => 'error', 'errno' => 8)); + redirectTo($filename, array( + 's' => $s, + 'page' => 'error', + 'errno' => 8 + )); } - + // redirect to update-page? - redirectTo('admin_updates.php', array('s' => $s)); + redirectTo('admin_updates.php', array( + 's' => $s + )); } - - if (!file_exists($localArchive)) { - redirectTo($filename, array('s' => $s, 'page' => 'error', 'errno' => 7)); + + if (! file_exists($localArchive)) { + redirectTo($filename, array( + 's' => $s, + 'page' => 'error', + 'errno' => 7 + )); } - - $text = 'Extract downloaded archive "'.$toExtract.'"?'; + + $text = 'Extract downloaded archive "' . $toExtract . '"?'; $hiddenparams = ''; - $yesfile = $filename.'?s='.$s.'&page=extract&archive='.$toExtract; + $yesfile = $filename . '?s=' . $s . '&page=extract&archive=' . $toExtract; eval("echo \"" . getTemplate("misc/question_yesno", true) . "\";"); } - // display error elseif ($page == 'error') { - + // retrieve error-number via url-parameter - $errno = isset($_GET['errno']) ? (int)$_GET['errno'] : 0; - - // 1 = no allow_url_fopen + $errno = isset($_GET['errno']) ? (int) $_GET['errno'] : 0; + // 2 = no Zlib // 3 = custom version detected // 4 = could not store archive to local hdd @@ -205,5 +215,5 @@ elseif ($page == 'error') { // 7 = local archive does not exist // 8 = could not extract archive // 9 = checksum mismatch - standard_error ('autoupdate_'.$errno); + standard_error('autoupdate_' . $errno); } diff --git a/admin_index.php b/admin_index.php index dae69d6d..4650a24e 100644 --- a/admin_index.php +++ b/admin_index.php @@ -86,41 +86,31 @@ if ($page == 'overview') { || (isset($lookfornewversion) && $lookfornewversion == 'yes') ) { $update_check_uri = 'http://version.froxlor.org/Froxlor/legacy/' . $version; + $latestversion = HttpClient::urlGet($update_check_uri); + $latestversion = explode('|', $latestversion); - if (ini_get('allow_url_fopen')) { - $latestversion = @file($update_check_uri); + if (is_array($latestversion) + && count($latestversion) >= 1 + ) { + $_version = $latestversion[0]; + $_message = isset($latestversion[1]) ? $latestversion[1] : ''; + $_link = isset($latestversion[2]) ? $latestversion[2] : htmlspecialchars($filename . '?s=' . urlencode($s) . '&page=' . urlencode($page) . '&lookfornewversion=yes'); - if (isset($latestversion[0])) { - $latestversion = explode('|', $latestversion[0]); + // add the branding so debian guys are not gettings confused + // about their version-number + $lookfornewversion_lable = $_version.$branding; + $lookfornewversion_link = $_link; + $lookfornewversion_addinfo = $_message; - if (is_array($latestversion) - && count($latestversion) >= 1 - ) { - $_version = $latestversion[0]; - $_message = isset($latestversion[1]) ? $latestversion[1] : ''; - $_link = isset($latestversion[2]) ? $latestversion[2] : htmlspecialchars($filename . '?s=' . urlencode($s) . '&page=' . urlencode($page) . '&lookfornewversion=yes'); - - // add the branding so debian guys are not gettings confused - // about their version-number - $lookfornewversion_lable = $_version.$branding; - $lookfornewversion_link = $_link; - $lookfornewversion_addinfo = $_message; - - // not numeric -> error-message - if (!preg_match('/^((\d+\\.)(\d+\\.)(\d+\\.)?(\d+)?(\-(svn|dev|rc)(\d+))?)$/', $_version)) { - // check for customized version to not output - // "There is a newer version of froxlor" besides the error-message - $isnewerversion = 2; - } elseif (version_compare2($version, $_version) == -1) { - $isnewerversion = 1; - } else { - $isnewerversion = 0; - } - } else { - redirectTo($update_check_uri.'/pretty', NULL, false); - } + // not numeric -> error-message + if (!preg_match('/^((\d+\\.)(\d+\\.)(\d+\\.)?(\d+)?(\-(svn|dev|rc)(\d+))?)$/', $_version)) { + // check for customized version to not output + // "There is a newer version of froxlor" besides the error-message + $isnewerversion = 2; + } elseif (version_compare2($version, $_version) == -1) { + $isnewerversion = 1; } else { - redirectTo($update_check_uri.'/pretty', NULL, false); + $isnewerversion = 0; } } else { redirectTo($update_check_uri.'/pretty', NULL, false); diff --git a/lib/ajax.php b/lib/ajax.php index 23370663..4765f91d 100644 --- a/lib/ajax.php +++ b/lib/ajax.php @@ -18,7 +18,7 @@ // Load the user settings define('FROXLOR_INSTALL_DIR', dirname(dirname(__FILE__))); -if (!file_exists('./userdata.inc.php')) { +if (! file_exists('./userdata.inc.php')) { die(); } require './userdata.inc.php'; @@ -27,10 +27,11 @@ require './classes/database/class.Database.php'; require './classes/settings/class.Settings.php'; require './functions/validate/function.validate_ip.php'; require './functions/validate/function.validateDomain.php'; +require './lib/classes/cURL/class.HttpClient.php'; -if(isset($_POST['action'])) { +if (isset($_POST['action'])) { $action = $_POST['action']; -} elseif(isset($_GET['action'])) { +} elseif (isset($_GET['action'])) { $action = $_GET['action']; } else { $action = ""; @@ -42,51 +43,31 @@ if ($action == "newsfeed") { } else { $feed = "https://inside.froxlor.org/news/"; } - + if (function_exists("simplexml_load_file") == false) { - die(); + outputItem("Newsfeed not available due to missing php-simplexml extension", "Please install the php-simplexml extension in order to view our newsfeed."); + exit(); } - + if (function_exists('curl_version')) { - $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL, $feed); - curl_setopt($ch, CURLOPT_USERAGENT, 'Froxlor/'.$version); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); - $output = curl_exec($ch); - curl_close($ch); + $output = HttpClient::urlGet($feed); $news = simplexml_load_string(trim($output)); } else { - if (ini_get('allow_url_fopen')) { - ini_set('user_agent', 'Froxlor/'.$version); - $news = simplexml_load_file($feed, null, LIBXML_NOCDATA); - } else { - $news = false; - } + outputItem("Newsfeed not available due to missing php-curl extension", "Please install the php-curl extension in order to view our newsfeed."); + exit(); } - + if ($news !== false) { - for ($i = 0; $i < 3; $i++) { + for ($i = 0; $i < 3; $i ++) { $item = $news->channel->item[$i]; - - $title = (string)$item->title; - $link = (string)$item->link; + + $title = (string) $item->title; + $link = (string) $item->link; $date = date("Y-m-d G:i", strtotime($item->pubDate)); $content = preg_replace("/[\r\n]+/", " ", strip_tags($item->description)); $content = substr($content, 0, 150) . "..."; - - echo "
  • -
    -
    - {$title} - - {$date} - -
    -

    - {$content} -

    -
    -
  • "; + + outputItem($title, $content, $link, $date); } } else { echo ""; @@ -94,3 +75,30 @@ if ($action == "newsfeed") { } else { echo "No action set."; } + +function outputItem($title, $content, $link = null, $date = null) +{ + echo "
  • +
    +
    + "; + if (! empty($link)) { + echo ""; + } + echo $title; + if (! empty($link)) { + echo ""; + } + echo ""; + if (! empty($date)) { + echo " + {$date} + "; + } + echo "
    +

    + {$content} +

    +
    +
  • "; +} diff --git a/lib/classes/cURL/class.HttpClient.php b/lib/classes/cURL/class.HttpClient.php new file mode 100644 index 00000000..53a52114 --- /dev/null +++ b/lib/classes/cURL/class.HttpClient.php @@ -0,0 +1,60 @@ + array('header' => "User-Agent: Froxlor/".$this->version)); - $selfcheckContext = stream_context_create($selfcheckContextOptions); - if ($payload !== trim(@file_get_contents($uri, false, $selfcheckContext))) { + $selfcheckpayload = HttpClient::urlGet($uri); + if ($payload !== trim($selfcheckpayload)) { $errmsg = json_encode(error_get_last()); if ($errmsg != "null") { $errmsg = "; PHP error: " . $errmsg; diff --git a/lib/classes/ssl/class.lescript_v2.php b/lib/classes/ssl/class.lescript_v2.php index 074def62..448222eb 100644 --- a/lib/classes/ssl/class.lescript_v2.php +++ b/lib/classes/ssl/class.lescript_v2.php @@ -233,13 +233,8 @@ class lescript_v2 // simple self check if (Settings::Get('system.disable_le_selfcheck') == '0') { - $selfcheckContextOptions = array( - 'http' => array( - 'header' => "User-Agent: Froxlor/" . $this->version - ) - ); - $selfcheckContext = stream_context_create($selfcheckContextOptions); - if ($payload !== trim(@file_get_contents($uri, false, $selfcheckContext))) { + $selfcheckpayload = HttpClient::urlGet($uri); + if ($payload !== trim($selfcheckpayload)) { $errmsg = json_encode(error_get_last()); if ($errmsg != "null") { $errmsg = "; PHP error: " . $errmsg; From 5612720342e1f95ae2f01bb75232dd3b78940f7b Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Fri, 9 Feb 2018 13:57:23 +0100 Subject: [PATCH 0405/1335] only let admin select php-configs that the customer is allowed to use to avoid unwanted php-config changes when customer edits domain, refs #514 Signed-off-by: Michael Kaufmann (d00p) --- admin_domains.php | 18 ++++++- lib/functions/output/function.makeoption.php | 6 ++- .../Sparkle/admin/domains/domains_add.tpl | 1 + templates/Sparkle/assets/js/domains.js | 52 +++++++++++++++++++ 4 files changed, 74 insertions(+), 3 deletions(-) create mode 100644 templates/Sparkle/assets/js/domains.js diff --git a/admin_domains.php b/admin_domains.php index 6ce493ac..34418fdc 100644 --- a/admin_domains.php +++ b/admin_domains.php @@ -2209,12 +2209,19 @@ if ($page == 'domains' || $page == 'overview') { FROM `" . TABLE_PANEL_PHPCONFIGS . "` c LEFT JOIN `" . TABLE_PANEL_FPMDAEMONS . "` fc ON fc.id = c.fpmsettingid "); + $c_allowed_configs = getCustomerDetail($result['customerid'], 'allowed_phpconfigs'); + if (!empty($c_allowed_configs)) { + $c_allowed_configs = json_decode($c_allowed_configs, true); + } else { + $c_allowed_configs = array(); + } while ($phpconfigs_row = $phpconfigs_result_stmt->fetch(PDO::FETCH_ASSOC)) { + $disabled = !empty($c_allowed_configs) && !in_array($phpconfigs_row['id'], $c_allowed_configs); if ((int) Settings::Get('phpfpm.enabled') == 1) { - $phpconfigs .= makeoption($phpconfigs_row['description'] . " [".$phpconfigs_row['interpreter']."]", $phpconfigs_row['id'], $result['phpsettingid'], true, true); + $phpconfigs .= makeoption($phpconfigs_row['description'] . " [".$phpconfigs_row['interpreter']."]", $phpconfigs_row['id'], $result['phpsettingid'], true, true, null, $disabled); } else { - $phpconfigs .= makeoption($phpconfigs_row['description'], $phpconfigs_row['id'], $result['phpsettingid'], true, true); + $phpconfigs .= makeoption($phpconfigs_row['description'], $phpconfigs_row['id'], $result['phpsettingid'], true, true, null, $disabled); } } @@ -2231,6 +2238,13 @@ if ($page == 'domains' || $page == 'overview') { eval("echo \"" . getTemplate("domains/domains_edit") . "\";"); } } + } elseif ($action == 'jqGetCustomerPHPConfigs') { + + $customerid = intval($_POST['customerid']); + $allowed_phpconfigs = getCustomerDetail($customerid, 'allowed_phpconfigs'); + echo !empty($allowed_phpconfigs) ? $allowed_phpconfigs : json_encode(array()); + exit; + } elseif ($action == 'import') { if (isset($_POST['send']) && $_POST['send'] == 'send') { diff --git a/lib/functions/output/function.makeoption.php b/lib/functions/output/function.makeoption.php index 936610b4..a0072137 100644 --- a/lib/functions/output/function.makeoption.php +++ b/lib/functions/output/function.makeoption.php @@ -29,7 +29,7 @@ * @author Florian Lippert */ -function makeoption($title, $value, $selvalue = NULL, $title_trusted = false, $value_trusted = false, $id = NULL) +function makeoption($title, $value, $selvalue = NULL, $title_trusted = false, $value_trusted = false, $id = NULL, $disabled = false) { if($selvalue !== NULL && ((is_array($selvalue) && in_array($value, $selvalue)) || $value == $selvalue)) @@ -40,6 +40,10 @@ function makeoption($title, $value, $selvalue = NULL, $title_trusted = false, $v { $selected = ''; } + + if ($disabled) { + $selected .= ' disabled="disabled"'; + } if(!$title_trusted) { diff --git a/templates/Sparkle/admin/domains/domains_add.tpl b/templates/Sparkle/admin/domains/domains_add.tpl index f760faed..ebce2ce6 100644 --- a/templates/Sparkle/admin/domains/domains_add.tpl +++ b/templates/Sparkle/admin/domains/domains_add.tpl @@ -6,6 +6,7 @@ $header {$title} +
    diff --git a/templates/Sparkle/assets/js/domains.js b/templates/Sparkle/assets/js/domains.js new file mode 100644 index 00000000..b95e04de --- /dev/null +++ b/templates/Sparkle/assets/js/domains.js @@ -0,0 +1,52 @@ +$(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]; + } + } + }; + + /** + * disable unusable php-configuration by customer settings + */ + $('#customerid').change(function() { + var cid = $(this).val(); + var sid = getUrlParameter('s'); + var page = getUrlParameter('page'); + + $.ajax({ + url: "admin_domains.php?s="+sid+"&page="+page+"&action=jqGetCustomerPHPConfigs", + type: "POST", + data: { + customerid: cid + }, + dataType: "json", + success: function(json) { + if (json.length > 0) { + $('#phpsettingid option').each(function() { + var pid = $(this).val(); + $(this).attr("disabled", "disabled"); + for (i in json) { + if (pid == json[i]) { + $(this).removeAttr("disabled"); + } + } + }); + } + }, + error: function(a, b) { + console.log(a, b); + } + }); + }); + +}); From 29803975450a0954ea1971ddcb373b43e9615d44 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sat, 10 Feb 2018 10:13:55 +0100 Subject: [PATCH 0406/1335] fix include path in lib/ajax.php Signed-off-by: Michael Kaufmann (d00p) --- lib/ajax.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ajax.php b/lib/ajax.php index 4765f91d..3b17e436 100644 --- a/lib/ajax.php +++ b/lib/ajax.php @@ -27,7 +27,7 @@ require './classes/database/class.Database.php'; require './classes/settings/class.Settings.php'; require './functions/validate/function.validate_ip.php'; require './functions/validate/function.validateDomain.php'; -require './lib/classes/cURL/class.HttpClient.php'; +require './classes/cURL/class.HttpClient.php'; if (isset($_POST['action'])) { $action = $_POST['action']; From 13b1503bf289668fc6b6a9b4cbd57bbd9c116cdc Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sat, 10 Feb 2018 16:06:57 +0100 Subject: [PATCH 0407/1335] set version to 0.9.39.1 for maintenance release Signed-off-by: Michael Kaufmann (d00p) --- install/froxlor.sql | 2 +- install/updates/froxlor/0.9/update_0.9.inc.php | 6 ++++++ lib/version.inc.php | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/install/froxlor.sql b/install/froxlor.sql index 25e4c506..cfaec5ce 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -686,7 +686,7 @@ opcache.interned_strings_buffer'), ('panel', 'password_special_char_required', '0'), ('panel', 'password_special_char', '!?<>§$%+#=@'), ('panel', 'customer_hide_options', ''), - ('panel', 'version', '0.9.39'), + ('panel', 'version', '0.9.39.1'), ('panel', 'db_version', '201801260'); diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index cfca0ef6..0c53d244 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -3880,3 +3880,9 @@ if (isFroxlorVersion('0.9.38.8')) { showUpdateStep("Updating from 0.9.38.8 to 0.9.39 final", false); updateToVersion('0.9.39'); } + +if (isFroxlorVersion('0.9.39')) { + + showUpdateStep("Updating from 0.9.39 to 0.9.39.1", false); + updateToVersion('0.9.39.1'); +} diff --git a/lib/version.inc.php b/lib/version.inc.php index 2f62182e..336be49b 100644 --- a/lib/version.inc.php +++ b/lib/version.inc.php @@ -16,7 +16,7 @@ */ // Main version variable -$version = '0.9.39'; +$version = '0.9.39.1'; // Database version (YYYYMMDDC where C is a daily counter) $dbversion = '201801260'; From ca4c93ac927c9615847251f392ad8c04211dfb0d Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sun, 11 Feb 2018 13:57:07 +0100 Subject: [PATCH 0408/1335] set default dns server in config-templates; separate CmdLineHandler from switch-server-ip script for further usage Signed-off-by: Michael Kaufmann (d00p) --- install/scripts/switch-server-ip.php | 231 ++++---------------- lib/classes/output/class.CmdLineHandler.php | 196 +++++++++++++++++ lib/configfiles/gentoo.xml | 2 +- lib/configfiles/jessie.xml | 2 +- lib/configfiles/precise.xml | 2 +- lib/configfiles/stretch.xml | 2 +- lib/configfiles/trusty.xml | 2 +- lib/configfiles/wheezy.xml | 2 +- 8 files changed, 240 insertions(+), 199 deletions(-) create mode 100644 lib/classes/output/class.CmdLineHandler.php diff --git a/install/scripts/switch-server-ip.php b/install/scripts/switch-server-ip.php index d8f098f9..b8fcbe4c 100644 --- a/install/scripts/switch-server-ip.php +++ b/install/scripts/switch-server-ip.php @@ -13,46 +13,33 @@ * @author Froxlor team (2016-) * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt * @package Cron - * + * */ // Check if we're in the CLI -if(@php_sapi_name() !== 'cli') { +if (@php_sapi_name() !== 'cli') { die('This script will only work in the shell.'); } -// give control to command line handler -try { - CmdLineHandler::processParameters($argc, $argv); -} catch (Exception $e) { - CmdLineHandler::printerr($e->getMessage()); -} +require dirname(dirname(__DIR__)) . '/lib/classes/output/class.CmdLineHandler.php'; -class CmdLineHandler +class SwitchServerIp extends CmdLineHandler { /** - * internal variable for passed arguments + * list of valid switches * * @var array */ - private static $args = null; - - /** - * Action object read from commandline/config - * - * @var Action - */ - private $_action = null; - - /** - * list of valid parameters/switches - */ public static $switches = array( - /* 'd', // debug / output information for everything */ 'h' ); - // same as --help + + /** + * list of valid parameters + * + * @var array + */ public static $params = array( 'switch', 'list', @@ -60,131 +47,7 @@ class CmdLineHandler 'help' ); - /** - * Returns a CmdLineHandler object with given - * arguments from command line - * - * @param int $argc - * @param array $argv - * - * @return CmdLineHandler - */ - public static function processParameters($argc, $argv) - { - return new CmdLineHandler($argc, $argv); - } - - /** - * returns the Action object generated in - * the class constructor - * - * @return Action - */ - public function getAction() - { - return $this->_action; - } - - /** - * class constructor, validates the command line parameters - * and sets the Action-object if valid - * - * @param int $argc - * @param string[] $argv - * - * @return null - * @throws Exception - */ - private function __construct($argc, $argv) - { - self::$args = $this->_parseArgs($argv); - $this->_action = $this->_createAction(); - } - - /** - * Parses the arguments given via the command line; - * three types are supported: - * 1. - * --parm1 or --parm2=value - * 2. -xyz (multiple switches in one) or -a=value - * 3. parm1 parm2 - * - * The 1. will be mapped as - * ["parm1"] => true, ["parm2"] => "value" - * The 2. as - * ["x"] => true, ["y"] => true, ["z"] => true, ["a"] => "value" - * And the 3. as - * [0] => "parm1", [1] => "parm2" - * - * @param array $argv - * - * @return array - */ - private function _parseArgs($argv) - { - array_shift($argv); - $o = array(); - foreach ($argv as $a) { - if (substr($a, 0, 2) == '--') { - $eq = strpos($a, '='); - if ($eq !== false) { - $o[substr($a, 2, $eq - 2)] = substr($a, $eq + 1); - } else { - $k = substr($a, 2); - if (! isset($o[$k])) { - $o[$k] = true; - } - } - } else - if (substr($a, 0, 1) == '-') { - if (substr($a, 2, 1) == '=') { - $o[substr($a, 1, 1)] = substr($a, 3); - } else { - foreach (str_split(substr($a, 1)) as $k) { - if (! isset($o[$k])) { - $o[$k] = true; - } - } - } - } else { - $o[] = $a; - } - } - return $o; - } - - /** - * Creates an Action-Object for the Action-Handler - * - * @return Action - * @throws Exception - */ - private function _createAction() - { - - // Test for help-switch - if (empty(self::$args) || array_key_exists("help", self::$args) || array_key_exists("h", self::$args)) { - self::printHelp(); - // end of execution - } - // check if no unknown parameters are present - foreach (self::$args as $arg => $value) { - - if (is_numeric($arg)) { - throw new Exception("Unknown parameter '" . $value . "' in argument list"); - } elseif (! in_array($arg, self::$params) && ! in_array($arg, self::$switches)) { - throw new Exception("Unknown parameter '" . $arg . "' in argument list"); - } - } - - // set debugger switch - if (isset(self::$args["d"]) && self::$args["d"] == true) { - // Debugger::getInstance()->setEnabled(true); - // Debugger::getInstance()->debug("debug output enabled"); - } - - return new Action(self::$args); - } + public static $action_class = 'Action'; public static function printHelp() { @@ -207,34 +70,9 @@ class CmdLineHandler // self::println("-d\t\t\tenable debug output"); self::println("-h\t\t\tsame as --help"); self::println(""); - + die(); // end of execution } - - public static function println($msg = "") - { - print $msg . PHP_EOL; - } - - private static function _printcolor($msg = "", $color = "0") - { - print "\033[" . $color . "m" . $msg . "\033[0m" . PHP_EOL; - } - - public static function printerr($msg = "") - { - self::_printcolor($msg, "31"); - } - - public static function printsucc($msg = "") - { - self::_printcolor($msg, "32"); - } - - public static function printwarn($msg = "") - { - self::_printcolor($msg, "33"); - } } class Action @@ -268,11 +106,11 @@ class Action if (array_key_exists("list", $this->_args) || array_key_exists("switch", $this->_args)) { $need_config = true; } - + $this->_checkConfigParam($need_config); - + $this->_parseConfig(); - + if (array_key_exists("list", $this->_args)) { $this->_listIPs(); } @@ -300,11 +138,11 @@ class Action private function _switchIPs() { $ip_list = $this->_args['switch']; - + if (empty($ip_list) || is_bool($ip_list)) { throw new Exception("No paramters given for --switch action."); } - + $ips_to_switch = array(); $ip_list = explode(" ", $ip_list); foreach ($ip_list as $ips_combo) { @@ -324,22 +162,22 @@ class Action } $ips_to_switch[] = $ip_pair; } - + if (count($ips_to_switch) > 0) { $upd_stmt = Database::prepare("UPDATE panel_ipsandports SET `ip` = :newip WHERE `ip` = :oldip"); - + // system.ipaddress $check_sysip_stmt = Database::prepare("SELECT `value` FROM `panel_settings` WHERE `settinggroup` = 'system' and `varname` = 'ipaddress'"); $check_sysip = Database::pexecute_first($check_sysip_stmt); - + // system.mysql_access_host $check_mysqlip_stmt = Database::prepare("SELECT `value` FROM `panel_settings` WHERE `settinggroup` = 'system' and `varname` = 'mysql_access_host'"); $check_mysqlip = Database::pexecute_first($check_mysqlip_stmt); - + // system.axfrservers $check_axfrip_stmt = Database::prepare("SELECT `value` FROM `panel_settings` WHERE `settinggroup` = 'system' and `varname` = 'axfrservers'"); $check_axfrip = Database::pexecute_first($check_axfrip_stmt); - + foreach ($ips_to_switch as $ip_pair) { echo "Switching IP \033[1m" . $ip_pair[0] . "\033[0m to IP \033[1m" . $ip_pair[1] . "\033[0m" . PHP_EOL; Database::pexecute($upd_stmt, array( @@ -347,11 +185,11 @@ class Action 'oldip' => $ip_pair[0] )); $rows_updated = $upd_stmt->rowCount(); - + if ($rows_updated == 0) { CmdLineHandler::printwarn("Note: " . $ip_pair[0] . " not updated to " . $ip_pair[1] . " (possibly no entry found in froxlor database. Use --list to see what IP addresses are added in froxlor"); } - + // check whether the system.ipaddress needs updating if ($check_sysip['value'] == $ip_pair[0]) { $upd2_stmt = Database::prepare("UPDATE `panel_settings` SET `value` = :newip WHERE `settinggroup` = 'system' and `varname` = 'ipaddress'"); @@ -360,7 +198,7 @@ class Action )); CmdLineHandler::printsucc("Updated system-ipaddress from '" . $ip_pair[0] . "' to '" . $ip_pair[1] . "'"); } - + // check whether the system.mysql_access_host needs updating if (strstr($check_mysqlip['value'], $ip_pair[0]) !== false) { $new_mysqlip = str_replace($ip_pair[0], $ip_pair[1], $check_mysqlip['value']); @@ -370,7 +208,7 @@ class Action )); CmdLineHandler::printsucc("Updated mysql_access_host from '" . $check_mysqlip['value'] . "' to '" . $new_mysqlip . "'"); } - + // check whether the system.axfrservers needs updating if (strstr($check_axfrip['value'], $ip_pair[0]) !== false) { $new_axfrip = str_replace($ip_pair[0], $ip_pair[1], $check_axfrip['value']); @@ -382,7 +220,7 @@ class Action } } } - + echo PHP_EOL; CmdLineHandler::printwarn("*** ATTENTION *** Remember to replace IP addresses in configuration files if used anywhere."); CmdLineHandler::printsucc("IP addresses updated"); @@ -391,10 +229,10 @@ class Action private function _parseConfig() { define('FROXLOR_INSTALL_DIR', $this->_args['froxlor-dir']); - if (!file_exists(FROXLOR_INSTALL_DIR . '/lib/classes/database/class.Database.php')) { - throw new Exception("Could not find froxlor's Database class. Is froxlor really installed to '".FROXLOR_INSTALL_DIR."'?"); + if (! file_exists(FROXLOR_INSTALL_DIR . '/lib/classes/database/class.Database.php')) { + throw new Exception("Could not find froxlor's Database class. Is froxlor really installed to '" . FROXLOR_INSTALL_DIR . "'?"); } - if (!file_exists(FROXLOR_INSTALL_DIR . '/lib/userdata.inc.php')) { + if (! file_exists(FROXLOR_INSTALL_DIR . '/lib/userdata.inc.php')) { throw new Exception("Could not find froxlor's userdata.inc.php file. You should use this script only with a fully installed and setup froxlor system."); } require FROXLOR_INSTALL_DIR . '/lib/classes/database/class.Database.php'; @@ -415,3 +253,10 @@ class Action } } } + +// give control to command line handler +try { + SwitchServerIp::processParameters($argc, $argv); +} catch (Exception $e) { + SwitchServerIp::printerr($e->getMessage()); +} diff --git a/lib/classes/output/class.CmdLineHandler.php b/lib/classes/output/class.CmdLineHandler.php new file mode 100644 index 00000000..20e22fe6 --- /dev/null +++ b/lib/classes/output/class.CmdLineHandler.php @@ -0,0 +1,196 @@ + (2018-) + * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt + * @package Cron + * + */ +abstract class CmdLineHandler +{ + + /** + * internal variable for passed arguments + * + * @var array + */ + private static $args = null; + + /** + * Action object read from commandline/config + * + * @var Action + */ + private $_action = null; + + /** + * Returns a CmdLineHandler object with given + * arguments from command line + * + * @param int $argc + * @param array $argv + * + * @return CmdLineHandler + */ + public static function processParameters($argc, $argv) + { + $me = get_called_class(); + return new $me($argc, $argv); + } + + /** + * returns the Action object generated in + * the class constructor + * + * @return Action + */ + public function getAction() + { + return $this->_action; + } + + /** + * class constructor, validates the command line parameters + * and sets the Action-object if valid + * + * @param int $argc + * @param string[] $argv + * + * @return null + * @throws Exception + */ + private function __construct($argc, $argv) + { + self::$args = $this->_parseArgs($argv); + $this->_action = $this->_createAction(); + } + + /** + * Parses the arguments given via the command line; + * three types are supported: + * 1. + * --parm1 or --parm2=value + * 2. -xyz (multiple switches in one) or -a=value + * 3. parm1 parm2 + * + * The 1. will be mapped as + * ["parm1"] => true, ["parm2"] => "value" + * The 2. as + * ["x"] => true, ["y"] => true, ["z"] => true, ["a"] => "value" + * And the 3. as + * [0] => "parm1", [1] => "parm2" + * + * @param array $argv + * + * @return array + */ + private function _parseArgs($argv) + { + array_shift($argv); + $o = array(); + foreach ($argv as $a) { + if (substr($a, 0, 2) == '--') { + $eq = strpos($a, '='); + if ($eq !== false) { + $o[substr($a, 2, $eq - 2)] = substr($a, $eq + 1); + } else { + $k = substr($a, 2); + if (! isset($o[$k])) { + $o[$k] = true; + } + } + } else if (substr($a, 0, 1) == '-') { + if (substr($a, 2, 1) == '=') { + $o[substr($a, 1, 1)] = substr($a, 3); + } else { + foreach (str_split(substr($a, 1)) as $k) { + if (! isset($o[$k])) { + $o[$k] = true; + } + } + } + } else { + $o[] = $a; + } + } + return $o; + } + + /** + * Creates an Action-Object for the Action-Handler + * + * @return Action + * @throws Exception + */ + private function _createAction() + { + + // Test for help-switch + if (empty(self::$args) || array_key_exists("help", self::$args) || array_key_exists("h", self::$args)) { + static::printHelp(); + // end of execution + } + // check if no unknown parameters are present + foreach (self::$args as $arg => $value) { + + if (is_numeric($arg)) { + throw new Exception("Unknown parameter '" . $value . "' in argument list"); + } elseif (! in_array($arg, static::$params) && ! in_array($arg, static::$switches)) { + throw new Exception("Unknown parameter '" . $arg . "' in argument list"); + } + } + + // set debugger switch + if (isset(self::$args["d"]) && self::$args["d"] == true) { + // Debugger::getInstance()->setEnabled(true); + // Debugger::getInstance()->debug("debug output enabled"); + } + + return new static::$action_class(self::$args); + } + + public static function getInput($prompt = "#", $default = "") + { + if (! empty($default)) { + $prompt .= " [" . $default . "]"; + } + $result = readline($prompt . ":"); + if (empty($result) && ! empty($default)) { + $result = $default; + } + return mb_strtolower($result); + } + + public static function println($msg = "") + { + print $msg . PHP_EOL; + } + + private static function _printcolor($msg = "", $color = "0") + { + print "\033[" . $color . "m" . $msg . "\033[0m" . PHP_EOL; + } + + public static function printerr($msg = "") + { + self::_printcolor($msg, "31"); + } + + public static function printsucc($msg = "") + { + self::_printcolor($msg, "32"); + } + + public static function printwarn($msg = "") + { + self::_printcolor($msg, "33"); + } +} diff --git a/lib/configfiles/gentoo.xml b/lib/configfiles/gentoo.xml index cbcda2db..aa7845d6 100644 --- a/lib/configfiles/gentoo.xml +++ b/lib/configfiles/gentoo.xml @@ -353,7 +353,7 @@ exit "$RETVAL" - + - + > /etc/bind/named.conf.local]]> diff --git a/lib/configfiles/precise.xml b/lib/configfiles/precise.xml index ffd68e48..606c6172 100644 --- a/lib/configfiles/precise.xml +++ b/lib/configfiles/precise.xml @@ -334,7 +334,7 @@ exit "$RETVAL" - + > /etc/bind/named.conf]]> diff --git a/lib/configfiles/stretch.xml b/lib/configfiles/stretch.xml index 1ab9cdb2..2ad8cba3 100644 --- a/lib/configfiles/stretch.xml +++ b/lib/configfiles/stretch.xml @@ -354,7 +354,7 @@ exit "$RETVAL" - + > /etc/bind/named.conf.local]]> diff --git a/lib/configfiles/trusty.xml b/lib/configfiles/trusty.xml index f230f997..d04848fa 100644 --- a/lib/configfiles/trusty.xml +++ b/lib/configfiles/trusty.xml @@ -367,7 +367,7 @@ exit "$RETVAL" - + > /etc/bind/named.conf]]> diff --git a/lib/configfiles/wheezy.xml b/lib/configfiles/wheezy.xml index 1ed4e82a..82a25576 100644 --- a/lib/configfiles/wheezy.xml +++ b/lib/configfiles/wheezy.xml @@ -407,7 +407,7 @@ exit "$RETVAL" - + > /etc/bind/named.conf.local]]> From 295fbae6f5fd26459b103163fb9be71232491412 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sun, 11 Feb 2018 16:10:10 +0100 Subject: [PATCH 0409/1335] create bash script to generate proftpd-certificates in case system does not use /bin/bash as shell Signed-off-by: Michael Kaufmann (d00p) --- lib/configfiles/gentoo.xml | 15 +++++++++++---- lib/configfiles/jessie.xml | 15 +++++++++++---- lib/configfiles/precise.xml | 15 +++++++++++---- lib/configfiles/stretch.xml | 15 +++++++++++---- lib/configfiles/trusty.xml | 15 +++++++++++---- lib/configfiles/wheezy.xml | 15 +++++++++++---- 6 files changed, 66 insertions(+), 24 deletions(-) diff --git a/lib/configfiles/gentoo.xml b/lib/configfiles/gentoo.xml index aa7845d6..7b68d374 100644 --- a/lib/configfiles/gentoo.xml +++ b/lib/configfiles/gentoo.xml @@ -3422,10 +3422,17 @@ MAILDIRPATH=.maildir > /etc/portage/package.use]]> - - "]]> - "]]> - + + " +[ -f /etc/ssl/certs/proftpd_ec.crt ] || openssl req -new -x509 -nodes -newkey ec:<(openssl ecparam -name secp521r1) -keyout /etc/ssl/private/proftpd_ec.key -out /etc/ssl/certs/proftpd_ec.crt -days 3650 -subj "/C=US/ST=Some-State/O=Internet Widgits Pty Ltd/CN=" +chmod 0600 /etc/ssl/private/proftpd.key /etc/ssl/private/proftpd_ec.key +]]> + + + + + diff --git a/lib/configfiles/jessie.xml b/lib/configfiles/jessie.xml index dc098d08..10ee0a2f 100644 --- a/lib/configfiles/jessie.xml +++ b/lib/configfiles/jessie.xml @@ -3814,10 +3814,17 @@ plugin { - - "]]> - "]]> - + + " +[ -f /etc/ssl/certs/proftpd_ec.crt ] || openssl req -new -x509 -nodes -newkey ec:<(openssl ecparam -name secp521r1) -keyout /etc/ssl/private/proftpd_ec.key -out /etc/ssl/certs/proftpd_ec.crt -days 3650 -subj "/C=US/ST=Some-State/O=Internet Widgits Pty Ltd/CN=" +chmod 0600 /etc/ssl/private/proftpd.key /etc/ssl/private/proftpd_ec.key +]]> + + + + + diff --git a/lib/configfiles/precise.xml b/lib/configfiles/precise.xml index 606c6172..9983959d 100644 --- a/lib/configfiles/precise.xml +++ b/lib/configfiles/precise.xml @@ -1143,10 +1143,17 @@ MYSQL_AUXOPTIONS_FIELD CONCAT("allowimap=",imap,",allowpop3=",pop3) - - "]]> - "]]> - + + " +[ -f /etc/ssl/certs/proftpd_ec.crt ] || openssl req -new -x509 -nodes -newkey ec:<(openssl ecparam -name secp521r1) -keyout /etc/ssl/private/proftpd_ec.key -out /etc/ssl/certs/proftpd_ec.crt -days 3650 -subj "/C=US/ST=Some-State/O=Internet Widgits Pty Ltd/CN=" +chmod 0600 /etc/ssl/private/proftpd.key /etc/ssl/private/proftpd_ec.key +]]> + + + + + diff --git a/lib/configfiles/stretch.xml b/lib/configfiles/stretch.xml index 2ad8cba3..7d9eab6b 100644 --- a/lib/configfiles/stretch.xml +++ b/lib/configfiles/stretch.xml @@ -3882,10 +3882,17 @@ plugin { - - "]]> - "]]> - + + " +[ -f /etc/ssl/certs/proftpd_ec.crt ] || openssl req -new -x509 -nodes -newkey ec:<(openssl ecparam -name secp521r1) -keyout /etc/ssl/private/proftpd_ec.key -out /etc/ssl/certs/proftpd_ec.crt -days 3650 -subj "/C=US/ST=Some-State/O=Internet Widgits Pty Ltd/CN=" +chmod 0600 /etc/ssl/private/proftpd.key /etc/ssl/private/proftpd_ec.key +]]> + + + + + diff --git a/lib/configfiles/trusty.xml b/lib/configfiles/trusty.xml index d04848fa..2df05eeb 100644 --- a/lib/configfiles/trusty.xml +++ b/lib/configfiles/trusty.xml @@ -1152,10 +1152,17 @@ MYSQL_AUXOPTIONS_FIELD CONCAT("allowimap=",imap,",allowpop3=",pop3) - - "]]> - "]]> - + + " +[ -f /etc/ssl/certs/proftpd_ec.crt ] || openssl req -new -x509 -nodes -newkey ec:<(openssl ecparam -name secp521r1) -keyout /etc/ssl/private/proftpd_ec.key -out /etc/ssl/certs/proftpd_ec.crt -days 3650 -subj "/C=US/ST=Some-State/O=Internet Widgits Pty Ltd/CN=" +chmod 0600 /etc/ssl/private/proftpd.key /etc/ssl/private/proftpd_ec.key +]]> + + + + + diff --git a/lib/configfiles/wheezy.xml b/lib/configfiles/wheezy.xml index 82a25576..55ba59d4 100644 --- a/lib/configfiles/wheezy.xml +++ b/lib/configfiles/wheezy.xml @@ -4716,10 +4716,17 @@ MYSQL_AUXOPTIONS_FIELD CONCAT("allowimap=",imap,",allowpop3=",pop3) - - "]]> - "]]> - + + " +[ -f /etc/ssl/certs/proftpd_ec.crt ] || openssl req -new -x509 -nodes -newkey ec:<(openssl ecparam -name secp521r1) -keyout /etc/ssl/private/proftpd_ec.key -out /etc/ssl/certs/proftpd_ec.crt -days 3650 -subj "/C=US/ST=Some-State/O=Internet Widgits Pty Ltd/CN=" +chmod 0600 /etc/ssl/private/proftpd.key /etc/ssl/private/proftpd_ec.key +]]> + + + + + From 05f1bf0a1fbc4bea5b94f5353bb3cdd9a5de8227 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 12 Feb 2018 07:46:38 +0100 Subject: [PATCH 0410/1335] meh, forgot to uncomment the settings storage for settings-import Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/settings/class.SImExporter.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/classes/settings/class.SImExporter.php b/lib/classes/settings/class.SImExporter.php index 206f4e1d..428d8beb 100644 --- a/lib/classes/settings/class.SImExporter.php +++ b/lib/classes/settings/class.SImExporter.php @@ -98,14 +98,14 @@ class SImExporter // when there were changes in the variable-name or similar unset($_data['panel.version']); unset($_data['panel.db_version']); - /* + // store new data foreach ($_data as $index => $value) { Settings::Set($index, $value); } // save to DB Settings::Flush(); - */ + // all good return true; } From 216f013c9627c51bb43718bf68c246926da594cb Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 12 Feb 2018 07:47:51 +0100 Subject: [PATCH 0411/1335] add script to automatically configure services from shell without the need of copy/paste from the interface Signed-off-by: Michael Kaufmann (d00p) --- install/scripts/config-services.php | 427 +++++++++++++++++++++++++++ install/scripts/switch-server-ip.php | 0 2 files changed, 427 insertions(+) create mode 100755 install/scripts/config-services.php mode change 100644 => 100755 install/scripts/switch-server-ip.php diff --git a/install/scripts/config-services.php b/install/scripts/config-services.php new file mode 100755 index 00000000..1236fbf5 --- /dev/null +++ b/install/scripts/config-services.php @@ -0,0 +1,427 @@ +#!/usr/bin/php + (2018-) + * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt + * @package Cron + * + */ + +// Check if we're in the CLI +if (@php_sapi_name() !== 'cli') { + die('This script will only work in the shell.'); +} + +require dirname(dirname(__DIR__)) . '/lib/classes/output/class.CmdLineHandler.php'; + +class ConfigServicesCmd extends CmdLineHandler +{ + + /** + * list of valid switches + * + * @var array + */ + public static $switches = array( + 'h' + ); + + /** + * list of valid parameters + * + * @var array + */ + public static $params = array( + 'create', + 'apply', + 'daemon', + 'list-daemons', + 'froxlor-dir', + 'help' + ); + + public static $action_class = 'Action'; + + public static function printHelp() + { + self::println(""); + self::println("Help / command line parameters:"); + self::println(""); + // commands + self::println("--create\t\tlets you create a services list configuration for the 'apply' command"); + self::println(""); + self::println("--apply\t\t\tconfigure your services by given configuration file. To create one run the --create command"); + self::println("\t\t\tExample: --apply=/path/to/my-config.json"); + self::println(""); + self::println("--list-daemons\t\tOutput the services that are going to be configured using a given config file. No services will be configured."); + self::println("\t\t\tExample: --apply=/path/to/my-config.json --list-daemons"); + self::println(""); + self::println("--daemon\t\tWhen running --apply you can specify a daemon. This will be the only service that gets configured"); + self::println("\t\t\tExample: --apply=/path/to/my-config.json --daemon=apache24"); + self::println(""); + self::println("--froxlor-dir\t\tpath to froxlor installation"); + self::println("\t\t\tExample: --froxlor-dir=/var/www/froxlor/"); + self::println(""); + self::println("--help\t\t\tshow help screen (this)"); + self::println(""); + // switches + // self::println("-d\t\t\tenable debug output"); + self::println("-h\t\t\tsame as --help"); + self::println(""); + + die(); // end of execution + } +} + +class Action +{ + + private $_args = null; + + private $_name = null; + + private $_db = null; + + public function __construct($args) + { + $this->_args = $args; + $this->_validate(); + } + + public function getActionName() + { + return $this->_name; + } + + /** + * validates the parsed command line parameters + * + * @throws Exception + */ + private function _validate() + { + $this->_checkConfigParam(true); + $this->_parseConfig(); + + require FROXLOR_INSTALL_DIR . '/lib/tables.inc.php'; + require FROXLOR_INSTALL_DIR . '/lib/functions.php'; + require FROXLOR_INSTALL_DIR . '/lib/classes/settings/class.Settings.php'; + require FROXLOR_INSTALL_DIR . '/lib/classes/config/class.ConfigParser.php'; + require FROXLOR_INSTALL_DIR . '/lib/classes/config/class.ConfigService.php'; + require FROXLOR_INSTALL_DIR . '/lib/classes/config/class.ConfigDaemon.php'; + + if (array_key_exists("create", $this->_args)) { + $this->_createConfig(); + } elseif (array_key_exists("apply", $this->_args)) { + $this->_applyConfig(); + } elseif (array_key_exists("list-daemons", $this->_args) || array_key_exists("daemon", $this->_args)) { + CmdLineHandler::printwarn("--list-daemons and --daemon only work together with --apply"); + } + } + + private function _createConfig() + { + $_daemons_config = array( + 'distro' => "" + ); + + $config_dir = FROXLOR_INSTALL_DIR . '/lib/configfiles/'; + // show list of available distro's + $distros = glob($config_dir . '*.xml'); + // tmp array + $distributions_select_data = array(); + // read in all the distros + foreach ($distros as $_distribution) { + // get configparser object + $dist = new ConfigParser($_distribution); + // get distro-info + $dist_display = $this->getCompleteDistroName($dist); + // store in tmp array + $distributions_select_data[$dist_display] = str_replace(".xml", "", strtolower(basename($_distribution))); + } + + // sort by distribution name + ksort($distributions_select_data); + + // list all distributions + $mask = "|%-50.50s |%-50.50s |\n"; + printf($mask, str_repeat("-", 50), str_repeat("-", 50)); + printf($mask, 'dist', 'name'); + printf($mask, str_repeat("-", 50), str_repeat("-", 50)); + foreach ($distributions_select_data as $name => $filename) { + printf($mask, $filename, $name); + } + printf($mask, str_repeat("-", 50), str_repeat("-", 50)); + echo PHP_EOL; + + while (! in_array($_daemons_config['distro'], $distributions_select_data)) { + $_daemons_config['distro'] = CmdLineHandler::getInput("choose distribution", "stretch"); + } + + // go through all services and let user check whether to include it or not + $configfiles = new ConfigParser($config_dir . '/' . $_daemons_config['distro'] . ".xml"); + $services = $configfiles->getServices(); + + foreach ($services as $si => $service) { + echo PHP_EOL . "--- " . strtoupper($si) . " ---" . PHP_EOL . PHP_EOL; + $_daemons_config[$si] = ""; + + $daemons = $service->getDaemons(); + $mask = "|%-50.50s |%-50.50s |\n"; + printf($mask, str_repeat("-", 50), str_repeat("-", 50)); + printf($mask, 'value', 'name'); + printf($mask, str_repeat("-", 50), str_repeat("-", 50)); + $default_daemon = ""; + foreach ($daemons as $di => $dd) { + $title = $dd->title; + if ($dd->default) { + $default_daemon = $di; + $title = $title . " (default)"; + } + printf($mask, $di, $title); + } + printf($mask, "x", "No " . $si); + $daemons['x'] = 'x'; + printf($mask, str_repeat("-", 50), str_repeat("-", 50)); + echo PHP_EOL; + if ($si == 'system') { + $_daemons_config[$si] = array(); + // for the system/other services we need a multiple choice possibility + CmdLineHandler::println("Select every service you need. Enter empty value when done"); + $sysservice = ""; + do { + $sysservice = CmdLineHandler::getInput("choose service"); + if (! empty($sysservice)) { + $_daemons_config[$si][] = $sysservice; + } + } while (! empty($sysservice)); + } else { + // for all others -> only one value + while (! array_key_exists($_daemons_config[$si], $daemons)) { + $_daemons_config[$si] = CmdLineHandler::getInput("choose service", $default_daemon); + } + } + } + + echo PHP_EOL . PHP_EOL; + $daemons_config = json_encode($_daemons_config); + $output = CmdLineHandler::getInput("choose output-filename", "/tmp/froxlor-config-" . date('Ymd') . ".json"); + file_put_contents($output, $daemons_config); + CmdLineHandler::printsucc("Successfully generated service-configfile '" . $output . "'"); + } + + private function getCompleteDistroName($cparser) + { + // get distro-info + $dist_display = $cparser->distributionName; + if ($cparser->distributionCodename != '') { + $dist_display .= " " . $cparser->distributionCodename; + } + if ($cparser->distributionVersion != '') { + $dist_display .= " (" . $cparser->distributionVersion . ")"; + } + if ($cparser->deprecated) { + $dist_display .= " [deprecated]"; + } + return $dist_display; + } + + private function _applyConfig() + { + if (! is_file($this->_args["apply"])) { + throw new Exception("Given config file is not a file"); + } elseif (! file_exists($this->_args["apply"])) { + throw new Exception("Given config file cannot be found ('" . $this->_args["apply"] . "')"); + } elseif (! is_readable($this->_args["apply"])) { + throw new Exception("Given config file cannot be read ('" . $this->_args["apply"] . "')"); + } + + $config = file_get_contents($this->_args["apply"]); + $decoded_config = json_decode($config, true); + + if (array_key_exists("list-daemons", $this->_args)) { + $mask = "|%-50.50s |%-50.50s |\n"; + printf($mask, str_repeat("-", 50), str_repeat("-", 50)); + printf($mask, 'service', 'daemon'); + printf($mask, str_repeat("-", 50), str_repeat("-", 50)); + foreach ($decoded_config as $service => $daemon) { + if (is_array($daemon) && count($daemon) > 0) { + foreach ($daemon as $sysdaemon) { + printf($mask, $service, $sysdaemon); + } + } else { + if ($daemon == 'x') { + $daemon = '--- skipped ---'; + } + printf($mask, $service, $daemon); + } + } + printf($mask, str_repeat("-", 50), str_repeat("-", 50)); + echo PHP_EOL; + exit(); + } + + $only_daemon = null; + if (array_key_exists("daemon", $this->_args)) { + $only_daemon = $this->_args['daemon']; + } + + if (! empty($decoded_config)) { + $config_dir = FROXLOR_INSTALL_DIR . '/lib/configfiles/'; + $configfiles = new ConfigParser($config_dir . '/' . $decoded_config['distro'] . ".xml"); + $services = $configfiles->getServices(); + $replace_arr = $this->_getReplacerArray(); + + foreach ($services as $si => $service) { + echo PHP_EOL . "--- Configuring: " . strtoupper($si) . " ---" . PHP_EOL . PHP_EOL; + if (! isset($decoded_config[$si]) || $decoded_config[$si] == 'x') { + CmdLineHandler::printwarn("Skipping " . strtoupper($si) . " configuration as desired"); + continue; + } + $daemons = $service->getDaemons(); + foreach ($daemons as $di => $dd) { + // check for desired service + if (($si != 'system' && $decoded_config[$si] != $di) || (is_array($decoded_config[$si]) && ! in_array($di, $decoded_config[$si]))) { + continue; + } + CmdLineHandler::println("Configuring '" . $di . "'"); + + if (! empty($only_daemon) && $only_daemon != $di) { + CmdLineHandler::printwarn("Skipping " . $di . " configuration as desired"); + continue; + } + // run all cmds + $confarr = $dd->getConfig(); + foreach ($confarr as $idx => $action) { + switch ($action['type']) { + case "install": + CmdLineHandler::println("Installing required packages"); + passthru(strtr($action['content'], $replace_arr), $result); + if (strlen($result) > 1) { + echo $result; + } + break; + case "command": + exec(strtr($action['content'], $replace_arr)); + break; + case "file": + if (array_key_exists('content', $action)) { + CmdLineHandler::printwarn("Creating file '" . $action['name'] . "'"); + file_put_contents($action['name'], strtr($action['content'], $replace_arr)); + } elseif (array_key_exists('subcommands', $action)) { + foreach ($action['subcommands'] as $fileaction) { + if (array_key_exists('execute', $fileaction) && $fileaction['execute'] == "pre") { + exec(strtr($fileaction['content'], $replace_arr)); + } elseif (array_key_exists('execute', $fileaction) && $fileaction['execute'] == "post") { + exec(strtr($fileaction['content'], $replace_arr)); + } elseif ($fileaction['type'] == 'file') { + CmdLineHandler::printwarn("Creating file '" . $fileaction['name'] . "'"); + file_put_contents($fileaction['name'], strtr($fileaction['content'], $replace_arr)); + } + } + } + break; + } + } + } + } + CmdLineHandler::printsucc("All services have been configured"); + } else { + CmdLineHandler::printerr("Unable to decode given JSON file"); + } + } + + private function _getReplacerArray() + { + $customer_tmpdir = '/tmp/'; + if (Settings::Get('system.mod_fcgid') == '1' && Settings::Get('system.mod_fcgid_tmpdir') != '') { + $customer_tmpdir = Settings::Get('system.mod_fcgid_tmpdir'); + } elseif (Settings::Get('phpfpm.enabled') == '1' && Settings::Get('phpfpm.tmpdir') != '') { + $customer_tmpdir = Settings::Get('phpfpm.tmpdir'); + } + + // try to convert namserver hosts to ip's + $ns_ips = ""; + if (Settings::Get('system.nameservers') != '') { + $nameservers = explode(',', Settings::Get('system.nameservers')); + foreach ($nameservers as $nameserver) { + $nameserver = trim($nameserver); + $nameserver_ips = gethostbynamel($nameserver); + if (is_array($nameserver_ips) && count($nameserver_ips) > 0) { + $ns_ips .= implode(",", $nameserver_ips); + } + } + } + + Database::needSqlData(); + $sql = Database::getSqlData(); + + $replace_arr = array( + '' => $sql['user'], + '' => $sql['passwd'], + '' => $sql['db'], + '' => $sql['host'], + '' => isset($sql['socket']) ? $sql['socket'] : null, + '' => Settings::Get('system.hostname'), + '' => Settings::Get('system.ipaddress'), + '' => Settings::Get('system.nameservers'), + '' => $ns_ips, + '' => Settings::Get('system.axfrservers'), + '' => Settings::Get('system.vmail_homedir'), + '' => Settings::Get('system.vmail_uid'), + '' => Settings::Get('system.vmail_gid'), + '' => (Settings::Get('system.use_ssl') == '1') ? 'imaps pop3s' : '', + '' => makeCorrectDir($customer_tmpdir), + '' => makeCorrectDir(FROXLOR_INSTALL_DIR), + '' => makeCorrectDir(Settings::Get('system.bindconf_directory')), + '' => Settings::Get('system.apachereload_command'), + '' => makeCorrectDir(Settings::Get('system.logfiles_directory')), + '' => makeCorrectDir(Settings::Get('phpfpm.fastcgi_ipcdir')), + '' => Settings::Get('system.httpgroup') + ); + return $replace_arr; + } + + private function _parseConfig() + { + define('FROXLOR_INSTALL_DIR', $this->_args['froxlor-dir']); + if (! file_exists(FROXLOR_INSTALL_DIR . '/lib/classes/database/class.Database.php')) { + throw new Exception("Could not find froxlor's Database class. Is froxlor really installed to '" . FROXLOR_INSTALL_DIR . "'?"); + } + if (! file_exists(FROXLOR_INSTALL_DIR . '/lib/userdata.inc.php')) { + throw new Exception("Could not find froxlor's userdata.inc.php file. You should use this script only with a fully installed and setup froxlor system."); + } + require FROXLOR_INSTALL_DIR . '/lib/classes/database/class.Database.php'; + } + + private function _checkConfigParam($needed = false) + { + if ($needed) { + if (! isset($this->_args["froxlor-dir"])) { + throw new Exception("No configuration given (missing --froxlor-dir parameter?)"); + } elseif (! is_dir($this->_args["froxlor-dir"])) { + throw new Exception("Given --froxlor-dir parameter is not a directory"); + } elseif (! file_exists($this->_args["froxlor-dir"])) { + throw new Exception("Given froxlor directory cannot be found ('" . $this->_args["froxlor-dir"] . "')"); + } elseif (! is_readable($this->_args["froxlor-dir"])) { + throw new Exception("Given froxlor direcotry cannot be read ('" . $this->_args["froxlor-dir"] . "')"); + } + } + } +} + +// give control to command line handler +try { + ConfigServicesCmd::processParameters($argc, $argv); +} catch (Exception $e) { + ConfigServicesCmd::printerr($e->getMessage()); +} diff --git a/install/scripts/switch-server-ip.php b/install/scripts/switch-server-ip.php old mode 100644 new mode 100755 From 017396197ee1e1933d2ca6104823741657ff5efb Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 12 Feb 2018 07:49:07 +0100 Subject: [PATCH 0412/1335] set version to 0.9.39.2 for bugfix release Signed-off-by: Michael Kaufmann (d00p) --- install/froxlor.sql | 2 +- install/updates/froxlor/0.9/update_0.9.inc.php | 6 ++++++ lib/version.inc.php | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/install/froxlor.sql b/install/froxlor.sql index cfaec5ce..567743ac 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -686,7 +686,7 @@ opcache.interned_strings_buffer'), ('panel', 'password_special_char_required', '0'), ('panel', 'password_special_char', '!?<>§$%+#=@'), ('panel', 'customer_hide_options', ''), - ('panel', 'version', '0.9.39.1'), + ('panel', 'version', '0.9.39.2'), ('panel', 'db_version', '201801260'); diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index 0c53d244..e89a18fd 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -3886,3 +3886,9 @@ if (isFroxlorVersion('0.9.39')) { showUpdateStep("Updating from 0.9.39 to 0.9.39.1", false); updateToVersion('0.9.39.1'); } + +if (isFroxlorVersion('0.9.39.1')) { + + showUpdateStep("Updating from 0.9.39.1 to 0.9.39.2", false); + updateToVersion('0.9.39.2'); +} diff --git a/lib/version.inc.php b/lib/version.inc.php index 336be49b..f2d10dde 100644 --- a/lib/version.inc.php +++ b/lib/version.inc.php @@ -16,7 +16,7 @@ */ // Main version variable -$version = '0.9.39.1'; +$version = '0.9.39.2'; // Database version (YYYYMMDDC where C is a daily counter) $dbversion = '201801260'; From bb792f228f866986fe48fc1a598e25933b498ae9 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 12 Feb 2018 13:28:13 +0100 Subject: [PATCH 0413/1335] add new hosting-plans feature Signed-off-by: Michael Kaufmann (d00p) --- admin_customers.php | 28 + admin_plans.php | 492 ++++++++++++++++++ install/froxlor.sql | 15 +- .../updates/froxlor/0.9/update_0.9.inc.php | 21 + .../admin/customer/formfield.customer_add.php | 12 + .../customer/formfield.customer_edit.php | 12 + .../admin/plans/formfield.plans_add.php | 41 ++ .../admin/plans/formfield.plans_edit.php | 43 ++ lib/navigation/00.froxlor.main.php | 5 + lib/tables.inc.php | 1 + lib/version.inc.php | 2 +- lng/english.lng.php | 10 + lng/german.lng.php | 10 + .../Sparkle/admin/customers/customers_add.tpl | 1 + .../admin/customers/customers_edit.tpl | 1 + templates/Sparkle/admin/plans/plans.tpl | 59 +++ templates/Sparkle/admin/plans/plans_add.tpl | 26 + templates/Sparkle/admin/plans/plans_edit.tpl | 27 + templates/Sparkle/admin/plans/plans_plan.tpl | 14 + templates/Sparkle/assets/js/customers.js | 74 +++ 20 files changed, 892 insertions(+), 2 deletions(-) create mode 100644 admin_plans.php create mode 100644 lib/formfields/admin/plans/formfield.plans_add.php create mode 100644 lib/formfields/admin/plans/formfield.plans_edit.php create mode 100644 templates/Sparkle/admin/plans/plans.tpl create mode 100644 templates/Sparkle/admin/plans/plans_add.tpl create mode 100644 templates/Sparkle/admin/plans/plans_edit.tpl create mode 100644 templates/Sparkle/admin/plans/plans_plan.tpl create mode 100644 templates/Sparkle/assets/js/customers.js diff --git a/admin_customers.php b/admin_customers.php index 74dc10b8..ddd139db 100644 --- a/admin_customers.php +++ b/admin_customers.php @@ -1073,6 +1073,20 @@ if ($page == 'customers' } } + // hosting plans + $hosting_plans = ""; + $plans = Database::query(" + SELECT * + FROM `" . TABLE_PANEL_PLANS . "` + ORDER BY name ASC + "); + if (Database::num_rows() > 0){ + $hosting_plans .= makeoption("---", 0, 0, true, true); + } + while ($row = $plans->fetch(PDO::FETCH_ASSOC)) { + $hosting_plans .= makeoption($row['name'], $row['id'], 0, true, true); + } + $customer_add_data = include_once dirname(__FILE__).'/lib/formfields/admin/customer/formfield.customer_add.php'; $customer_add_form = htmlform::genHTMLForm($customer_add_data); @@ -1755,6 +1769,20 @@ if ($page == 'customers' } } + // hosting plans + $hosting_plans = ""; + $plans = Database::query(" + SELECT * + FROM `" . TABLE_PANEL_PLANS . "` + ORDER BY name ASC + "); + if (Database::num_rows() > 0){ + $hosting_plans .= makeoption("---", 0, 0, true, true); + } + while ($row = $plans->fetch(PDO::FETCH_ASSOC)) { + $hosting_plans .= makeoption($row['name'], $row['id'], 0, true, true); + } + $customer_edit_data = include_once dirname(__FILE__).'/lib/formfields/admin/customer/formfield.customer_edit.php'; $customer_edit_form = htmlform::genHTMLForm($customer_edit_data); diff --git a/admin_plans.php b/admin_plans.php new file mode 100644 index 00000000..6ff53949 --- /dev/null +++ b/admin_plans.php @@ -0,0 +1,492 @@ + (2010-) + * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt + * @package Panel + * + */ +define('AREA', 'admin'); +require './lib/init.php'; + +if (isset($_POST['id'])) { + $id = intval($_POST['id']); +} elseif (isset($_GET['id'])) { + $id = intval($_GET['id']); +} + +if ($page == '' || $page == 'overview') { + + if ($action == '') { + + $log->logAction(ADM_ACTION, LOG_NOTICE, "viewed admin_plans"); + $fields = array( + 'p.name' => $lng['admin']['plans']['name'], + 'p.description' => $lng['admin']['plans']['description'], + 'adminname' => $lng['admin']['admin'], + 'p.ts' => $lng['admin']['plans']['last_update'] + ); + $paging = new paging($userinfo, TABLE_PANEL_PLANS, $fields); + $plans = ''; + $result_stmt = Database::prepare(" + SELECT p.*, a.loginname as adminname + FROM `" . TABLE_PANEL_PLANS . "` p, `" . TABLE_PANEL_ADMINS . "` a + WHERE " . ($userinfo['customers_see_all'] ? '' : " `p`.`adminid` = :adminid AND ") . " + `p`.`adminid` = `a`.`adminid` " . $paging->getSqlWhere(false) . " " . $paging->getSqlOrderBy() . " " . $paging->getSqlLimit()); + Database::pexecute($result_stmt, array( + 'adminid' => $userinfo['adminid'] + )); + $paging->setEntries(Database::num_rows()); + $sortcode = $paging->getHtmlSortCode($lng); + $arrowcode = $paging->getHtmlArrowCode($filename . '?page=' . $page . '&s=' . $s); + $searchcode = $paging->getHtmlSearchCode($lng); + $pagingcode = $paging->getHtmlPagingCode($filename . '?page=' . $page . '&s=' . $s); + $i = 0; + $count = 0; + + while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) { + + if ($paging->checkDisplay($i)) { + $row = htmlentities_array($row); + $row['ts_format'] = date("d.m.Y H:i", $row['ts']); + eval("\$plans.=\"" . getTemplate("plans/plans_plan") . "\";"); + $count ++; + } + $i ++; + } + + eval("echo \"" . getTemplate("plans/plans") . "\";"); + } elseif ($action == 'delete' && $id != 0) { + + $result_stmt = Database::prepare(" + SELECT * FROM `" . TABLE_PANEL_PLANS . "` WHERE `id` = :id"); + $result = Database::pexecute_first($result_stmt, array( + 'id' => $id + )); + + if ($result['id'] != 0 && $result['id'] == $id && (int) $userinfo['adminid'] == $result['adminid']) { + if (isset($_POST['send']) && $_POST['send'] == 'send') { + + $del_stmt = Database::prepare(" + DELETE FROM `" . TABLE_PANEL_PLANS . "` WHERE `id` = :id"); + Database::pexecute($del_stmt, array( + 'id' => $id + )); + + $log->logAction(ADM_ACTION, LOG_INFO, "Plan '" . $result['name'] . "' has been deleted by '" . $userinfo['loginname'] . "'"); + redirectTo($filename, array( + 'page' => $page, + 's' => $s + )); + } else { + ask_yesno('plan_reallydelete', $filename, array( + 'id' => $id, + 'page' => $page, + 'action' => $action + ), $result['name']); + } + } else { + standard_error('nopermissionsorinvalidid'); + } + } elseif ($action == 'add') { + + if (isset($_POST['send']) && $_POST['send'] == 'send') { + $name = validate($_POST['name'], 'name'); + $description = validate(str_replace("\r\n", "\n", $_POST['description']), 'description', '/^[^\0]*$/'); + + $value_arr = array(); + + $value_arr['diskspace'] = intval_ressource($_POST['diskspace']); + if (isset($_POST['diskspace_ul'])) { + $value_arr['diskspace'] = - 1; + } + + $value_arr['traffic'] = doubleval_ressource($_POST['traffic']); + if (isset($_POST['traffic_ul'])) { + $value_arr['traffic'] = - 1; + } + + $value_arr['subdomains'] = intval_ressource($_POST['subdomains']); + if (isset($_POST['subdomains_ul'])) { + $value_arr['subdomains'] = - 1; + } + + $value_arr['emails'] = intval_ressource($_POST['emails']); + if (isset($_POST['emails_ul'])) { + $value_arr['emails'] = - 1; + } + + $value_arr['email_accounts'] = intval_ressource($_POST['email_accounts']); + if (isset($_POST['email_accounts_ul'])) { + $value_arr['email_accounts'] = - 1; + } + + $value_arr['email_forwarders'] = intval_ressource($_POST['email_forwarders']); + if (isset($_POST['email_forwarders_ul'])) { + $value_arr['email_forwarders'] = - 1; + } + + if (Settings::Get('system.mail_quota_enabled') == '1') { + $value_arr['email_quota'] = validate($_POST['email_quota'], 'email_quota', '/^\d+$/', 'vmailquotawrong', array( + '0', + '' + )); + if (isset($_POST['email_quota_ul'])) { + $value_arr['email_quota'] = - 1; + } + } else { + $value_arr['email_quota'] = - 1; + } + + $value_arr['email_imap'] = 0; + if (isset($_POST['email_imap'])) { + $value_arr['email_imap'] = intval_ressource($_POST['email_imap']); + } + + $value_arr['email_pop3'] = 0; + if (isset($_POST['email_pop3'])) { + $value_arr['email_pop3'] = intval_ressource($_POST['email_pop3']); + } + + $value_arr['ftps'] = intval_ressource($_POST['ftps']); + if (isset($_POST['ftps_ul'])) { + $value_arr['ftps'] = - 1; + } + + $value_arr['tickets'] = (Settings::Get('ticket.enabled') == 1 ? intval_ressource($_POST['tickets']) : 0); + if (isset($_POST['tickets_ul']) && Settings::Get('ticket.enabled') == '1') { + $value_arr['tickets'] = - 1; + } + + $value_arr['mysqls'] = intval_ressource($_POST['mysqls']); + if (isset($_POST['mysqls_ul'])) { + $value_arr['mysqls'] = - 1; + } + + $value_arr['phpenabled'] = 0; + if (isset($_POST['phpenabled'])) { + $value_arr['phpenabled'] = intval($_POST['phpenabled']); + } + + $value_arr['allowed_phpconfigs'] = array(); + if (isset($_POST['allowed_phpconfigs']) && is_array($_POST['allowed_phpconfigs'])) { + foreach ($_POST['allowed_phpconfigs'] as $allowed_phpconfig) { + $allowed_phpconfig = intval($allowed_phpconfig); + $value_arr['allowed_phpconfigs'][] = $allowed_phpconfig; + } + } + + $value_arr['perlenabled'] = 0; + if (isset($_POST['perlenabled'])) { + $value_arr['perlenabled'] = intval($_POST['perlenabled']); + } + + $value_arr['dnsenabled'] = 0; + if (isset($_POST['dnsenabled'])) { + $value_arr['dnsenabled'] = intval($_POST['dnsenabled']); + } + + $ins_stmt = Database::prepare(" + INSERT INTO `" . TABLE_PANEL_PLANS . "` + SET `adminid` = :adminid, `name` = :name, `description` = :desc, `value` = :valuearr, `ts` = UNIX_TIMESTAMP(); + "); + $ins_data = array( + 'adminid' => $userinfo['adminid'], + 'name' => $name, + 'desc' => $description, + 'valuearr' => json_encode($value_arr) + ); + Database::pexecute($ins_stmt, $ins_data); + + $log->logAction(ADM_ACTION, LOG_WARNING, "added plan '" . $name . "'"); + redirectTo($filename, array( + 'page' => $page, + 's' => $s + )); + } else { + + $diskspace_ul = makecheckbox('diskspace_ul', $lng['customer']['unlimited'], '-1', false, '0', true, true); + $traffic_ul = makecheckbox('traffic_ul', $lng['customer']['unlimited'], '-1', false, '0', true, true); + $subdomains_ul = makecheckbox('subdomains_ul', $lng['customer']['unlimited'], '-1', false, '0', true, true); + $emails_ul = makecheckbox('emails_ul', $lng['customer']['unlimited'], '-1', false, '0', true, true); + $email_accounts_ul = makecheckbox('email_accounts_ul', $lng['customer']['unlimited'], '-1', false, '0', true, true); + $email_forwarders_ul = makecheckbox('email_forwarders_ul', $lng['customer']['unlimited'], '-1', false, '0', true, true); + $email_quota_ul = makecheckbox('email_quota_ul', $lng['customer']['unlimited'], '-1', false, '0', true, true); + $ftps_ul = makecheckbox('ftps_ul', $lng['customer']['unlimited'], '-1', false, '0', true, true); + $tickets_ul = makecheckbox('tickets_ul', $lng['customer']['unlimited'], '-1', false, '0', true, true); + $mysqls_ul = makecheckbox('mysqls_ul', $lng['customer']['unlimited'], '-1', false, '0', true, true); + + $phpconfigs = array(); + $configs = Database::query(" + SELECT c.*, fc.description as interpreter + FROM `" . TABLE_PANEL_PHPCONFIGS . "` c + LEFT JOIN `" . TABLE_PANEL_FPMDAEMONS . "` fc ON fc.id = c.fpmsettingid + "); + while ($row = $configs->fetch(PDO::FETCH_ASSOC)) { + if ((int) Settings::Get('phpfpm.enabled') == 1) { + $phpconfigs[] = array( + 'label' => $row['description'] . " [" . $row['interpreter'] . "]
    ", + 'value' => $row['id'] + ); + } else { + $phpconfigs[] = array( + 'label' => $row['description'] . "
    ", + 'value' => $row['id'] + ); + } + } + + $plans_add_data = include_once dirname(__FILE__) . '/lib/formfields/admin/plans/formfield.plans_add.php'; + $cust_add_data = include_once dirname(__FILE__) . '/lib/formfields/admin/customer/formfield.customer_add.php'; + // unset unneeded stuff + unset($cust_add_data['customer_add']['sections']['section_a']); + unset($cust_add_data['customer_add']['sections']['section_b']); + unset($cust_add_data['customer_add']['sections']['section_cpre']); + // merge + $plans_add_data['plans_add']['sections'] = array_merge($plans_add_data['plans_add']['sections'], $cust_add_data['customer_add']['sections']); + $plans_add_form = htmlform::genHTMLForm($plans_add_data); + + $title = $plans_add_data['plans_add']['title']; + $image = $plans_add_data['plans_add']['image']; + + eval("echo \"" . getTemplate("plans/plans_add") . "\";"); + } + } elseif ($action == 'edit' && $id != 0) { + $result_stmt = Database::prepare(" + SELECT * FROM `" . TABLE_PANEL_PLANS . "` WHERE `id` = :id"); + $result = Database::pexecute_first($result_stmt, array( + 'id' => $id + )); + + if ($result['name'] != '') { + + $result['value'] = json_decode($result['value'], true); + $result = htmlentities_array($result); + + foreach ($result['value'] as $index => $value) { + $result[$index] = $value; + } + $result['allowed_phpconfigs'] = json_encode($result['allowed_phpconfigs']); + + if (isset($_POST['send']) && $_POST['send'] == 'send') { + + $name = validate($_POST['name'], 'name'); + $description = validate(str_replace("\r\n", "\n", $_POST['description']), 'description', '/^[^\0]*$/'); + + $value_arr = array(); + + $value_arr['diskspace'] = intval_ressource($_POST['diskspace']); + if (isset($_POST['diskspace_ul'])) { + $value_arr['diskspace'] = - 1; + } + + $value_arr['traffic'] = doubleval_ressource($_POST['traffic']); + if (isset($_POST['traffic_ul'])) { + $value_arr['traffic'] = - 1; + } + + $value_arr['subdomains'] = intval_ressource($_POST['subdomains']); + if (isset($_POST['subdomains_ul'])) { + $value_arr['subdomains'] = - 1; + } + + $value_arr['emails'] = intval_ressource($_POST['emails']); + if (isset($_POST['emails_ul'])) { + $value_arr['emails'] = - 1; + } + + $value_arr['email_accounts'] = intval_ressource($_POST['email_accounts']); + if (isset($_POST['email_accounts_ul'])) { + $value_arr['email_accounts'] = - 1; + } + + $value_arr['email_forwarders'] = intval_ressource($_POST['email_forwarders']); + if (isset($_POST['email_forwarders_ul'])) { + $value_arr['email_forwarders'] = - 1; + } + + if (Settings::Get('system.mail_quota_enabled') == '1') { + $value_arr['email_quota'] = validate($_POST['email_quota'], 'email_quota', '/^\d+$/', 'vmailquotawrong', array( + '0', + '' + )); + if (isset($_POST['email_quota_ul'])) { + $value_arr['email_quota'] = - 1; + } + } else { + $value_arr['email_quota'] = - 1; + } + + $value_arr['email_imap'] = 0; + if (isset($_POST['email_imap'])) { + $value_arr['email_imap'] = intval_ressource($_POST['email_imap']); + } + + $value_arr['email_pop3'] = 0; + if (isset($_POST['email_pop3'])) { + $value_arr['email_pop3'] = intval_ressource($_POST['email_pop3']); + } + + $value_arr['ftps'] = intval_ressource($_POST['ftps']); + if (isset($_POST['ftps_ul'])) { + $value_arr['ftps'] = - 1; + } + + $value_arr['tickets'] = (Settings::Get('ticket.enabled') == 1 ? intval_ressource($_POST['tickets']) : 0); + if (isset($_POST['tickets_ul']) && Settings::Get('ticket.enabled') == '1') { + $value_arr['tickets'] = - 1; + } + + $value_arr['mysqls'] = intval_ressource($_POST['mysqls']); + if (isset($_POST['mysqls_ul'])) { + $value_arr['mysqls'] = - 1; + } + + $value_arr['phpenabled'] = 0; + if (isset($_POST['phpenabled'])) { + $value_arr['phpenabled'] = intval($_POST['phpenabled']); + } + + $value_arr['allowed_phpconfigs'] = array(); + if (isset($_POST['allowed_phpconfigs']) && is_array($_POST['allowed_phpconfigs'])) { + foreach ($_POST['allowed_phpconfigs'] as $allowed_phpconfig) { + $allowed_phpconfig = intval($allowed_phpconfig); + $value_arr['allowed_phpconfigs'][] = $allowed_phpconfig; + } + } + + $value_arr['perlenabled'] = 0; + if (isset($_POST['perlenabled'])) { + $value_arr['perlenabled'] = intval($_POST['perlenabled']); + } + + $value_arr['dnsenabled'] = 0; + if (isset($_POST['dnsenabled'])) { + $value_arr['dnsenabled'] = intval($_POST['dnsenabled']); + } + + $ins_stmt = Database::prepare(" + UPDATE `" . TABLE_PANEL_PLANS . "` + SET `name` = :name, `description` = :desc, `value` = :valuearr, `ts` = UNIX_TIMESTAMP() + WHERE `id` = :id + "); + $ins_data = array( + 'name' => $name, + 'desc' => $description, + 'valuearr' => json_encode($value_arr), + 'id' => $id + ); + Database::pexecute($ins_stmt, $ins_data); + + $log->logAction(ADM_ACTION, LOG_WARNING, "updated plan '" . $name . "'"); + redirectTo($filename, array( + 'page' => $page, + 's' => $s + )); + } else { + + $diskspace_ul = makecheckbox('diskspace_ul', $lng['customer']['unlimited'], '-1', false, $result['diskspace'], true, true); + if ($result['diskspace'] == '-1') { + $result['diskspace'] = ''; + } + + $traffic_ul = makecheckbox('traffic_ul', $lng['customer']['unlimited'], '-1', false, $result['traffic'], true, true); + if ($result['traffic'] == '-1') { + $result['traffic'] = ''; + } + + $subdomains_ul = makecheckbox('subdomains_ul', $lng['customer']['unlimited'], '-1', false, $result['subdomains'], true, true); + if ($result['subdomains'] == '-1') { + $result['subdomains'] = ''; + } + + $emails_ul = makecheckbox('emails_ul', $lng['customer']['unlimited'], '-1', false, $result['emails'], true, true); + if ($result['emails'] == '-1') { + $result['emails'] = ''; + } + + $email_accounts_ul = makecheckbox('email_accounts_ul', $lng['customer']['unlimited'], '-1', false, $result['email_accounts'], true, true); + if ($result['email_accounts'] == '-1') { + $result['email_accounts'] = ''; + } + + $email_forwarders_ul = makecheckbox('email_forwarders_ul', $lng['customer']['unlimited'], '-1', false, $result['email_forwarders'], true, true); + if ($result['email_forwarders'] == '-1') { + $result['email_forwarders'] = ''; + } + + $email_quota_ul = makecheckbox('email_quota_ul', $lng['customer']['unlimited'], '-1', false, $result['email_quota'], true, true); + if ($result['email_quota'] == '-1') { + $result['email_quota'] = ''; + } + + $ftps_ul = makecheckbox('ftps_ul', $lng['customer']['unlimited'], '-1', false, $result['ftps'], true, true); + if ($result['ftps'] == '-1') { + $result['ftps'] = ''; + } + + $tickets_ul = makecheckbox('tickets_ul', $lng['customer']['unlimited'], '-1', false, $result['tickets'], true, true); + if ($result['tickets'] == '-1') { + $result['tickets'] = ''; + } + + $mysqls_ul = makecheckbox('mysqls_ul', $lng['customer']['unlimited'], '-1', false, $result['mysqls'], true, true); + if ($result['mysqls'] == '-1') { + $result['mysqls'] = ''; + } + + $phpconfigs = array(); + $configs = Database::query(" + SELECT c.*, fc.description as interpreter + FROM `" . TABLE_PANEL_PHPCONFIGS . "` c + LEFT JOIN `" . TABLE_PANEL_FPMDAEMONS . "` fc ON fc.id = c.fpmsettingid + "); + while ($row = $configs->fetch(PDO::FETCH_ASSOC)) { + if ((int) Settings::Get('phpfpm.enabled') == 1) { + $phpconfigs[] = array( + 'label' => $row['description'] . " [" . $row['interpreter'] . "]
    ", + 'value' => $row['id'] + ); + } else { + $phpconfigs[] = array( + 'label' => $row['description'] . "
    ", + 'value' => $row['id'] + ); + } + } + + $plans_edit_data = include_once dirname(__FILE__) . '/lib/formfields/admin/plans/formfield.plans_edit.php'; + $cust_edit_data = include_once dirname(__FILE__) . '/lib/formfields/admin/customer/formfield.customer_edit.php'; + // unset unneeded stuff + unset($cust_edit_data['customer_edit']['sections']['section_a']); + unset($cust_edit_data['customer_edit']['sections']['section_b']); + unset($cust_edit_data['customer_edit']['sections']['section_cpre']); + // merge + $plans_edit_data['plans_edit']['sections'] = array_merge($plans_edit_data['plans_edit']['sections'], $cust_edit_data['customer_edit']['sections']); + $plans_edit_form = htmlform::genHTMLForm($plans_edit_data); + + $title = $plans_edit_data['plans_edit']['title']; + $image = $plans_edit_data['plans_edit']['image']; + + eval("echo \"" . getTemplate("plans/plans_edit") . "\";"); + } + } + } elseif ($action == 'jqGetPlanValues') { + $planid = isset($_POST['planid']) ? (int)$_POST['planid'] : 0; + $result_stmt = Database::prepare(" + SELECT * FROM `" . TABLE_PANEL_PLANS . "` WHERE `id` = :id"); + $result = Database::pexecute_first($result_stmt, array( + 'id' => $planid + )); + echo $result['value']; + exit; + } +} diff --git a/install/froxlor.sql b/install/froxlor.sql index 567743ac..21c0a32d 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -687,7 +687,7 @@ opcache.interned_strings_buffer'), ('panel', 'password_special_char', '!?<>§$%+#=@'), ('panel', 'customer_hide_options', ''), ('panel', 'version', '0.9.39.2'), - ('panel', 'db_version', '201801260'); + ('panel', 'db_version', '201802120'); DROP TABLE IF EXISTS `panel_tasks`; @@ -1028,3 +1028,16 @@ CREATE TABLE `domain_dns_entries` ( PRIMARY KEY (`id`) ) ENGINE=MyISAM CHARSET=utf8 COLLATE=utf8_general_ci; + +DROP TABLE IF EXISTS `panel_plans`; +CREATE TABLE `panel_plans` ( + `id` int(11) NOT NULL auto_increment, + `adminid` int(11) NOT NULL default '0', + `name` varchar(255) NOT NULL default '', + `description` text NOT NULL, + `value` longtext NOT NULL, + `ts` int(15) NOT NULL default '0', + PRIMARY KEY (id), + KEY adminid (adminid) +) ENGINE=MyISAM CHARSET=utf8 COLLATE=utf8_general_ci; + diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index e89a18fd..81cf8b8e 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -3892,3 +3892,24 @@ if (isFroxlorVersion('0.9.39.1')) { showUpdateStep("Updating from 0.9.39.1 to 0.9.39.2", false); updateToVersion('0.9.39.2'); } + +if (isDatabaseVersion('201801260')) { + + showUpdateStep("Adding new plans table"); + Database::query("DROP TABLE IF EXISTS `panel_plans`;"); + $sql = "CREATE TABLE `panel_plans` ( + `id` int(11) NOT NULL auto_increment, + `adminid` int(11) NOT NULL default '0', + `name` varchar(255) NOT NULL default '', + `description` text NOT NULL, + `value` longtext NOT NULL, + `ts` int(15) NOT NULL default '0', + PRIMARY KEY (id), + KEY adminid (adminid) + ) ENGINE=MyISAM CHARSET=utf8 COLLATE=utf8_general_ci;"; + Database::query($sql); + lastStepStatus(0); + + updateToDbVersion('201802120'); +} + diff --git a/lib/formfields/admin/customer/formfield.customer_add.php b/lib/formfields/admin/customer/formfield.customer_add.php index 9d3d26ef..c123f161 100644 --- a/lib/formfields/admin/customer/formfield.customer_add.php +++ b/lib/formfields/admin/customer/formfield.customer_add.php @@ -158,6 +158,18 @@ return array( ) ) ), + 'section_cpre' => array( + 'visible' => !empty($hosting_plans), + 'title' => $lng['admin']['plans']['use_plan'], + 'image' => 'icons/user_add.png', + 'fields' => array( + 'use_plan' => array( + 'label' => $lng['admin']['plans']['use_plan'], + 'type' => 'select', + 'select_var' => $hosting_plans + ) + ) + ), 'section_c' => array( 'title' => $lng['admin']['servicedata'], 'image' => 'icons/user_add.png', diff --git a/lib/formfields/admin/customer/formfield.customer_edit.php b/lib/formfields/admin/customer/formfield.customer_edit.php index c5f74d74..70654183 100644 --- a/lib/formfields/admin/customer/formfield.customer_edit.php +++ b/lib/formfields/admin/customer/formfield.customer_edit.php @@ -150,6 +150,18 @@ return array( ) ) ), + 'section_cpre' => array( + 'visible' => !empty($hosting_plans), + 'title' => $lng['admin']['plans']['use_plan'], + 'image' => 'icons/user_add.png', + 'fields' => array( + 'use_plan' => array( + 'label' => $lng['admin']['plans']['use_plan'], + 'type' => 'select', + 'select_var' => $hosting_plans + ) + ) + ), 'section_c' => array( 'title' => $lng['admin']['servicedata'], 'image' => 'icons/user_edit.png', diff --git a/lib/formfields/admin/plans/formfield.plans_add.php b/lib/formfields/admin/plans/formfield.plans_add.php new file mode 100644 index 00000000..dcb0d1c7 --- /dev/null +++ b/lib/formfields/admin/plans/formfield.plans_add.php @@ -0,0 +1,41 @@ + (2010-) + * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt + * @package Formfields + * + */ + +return array( + 'plans_add' => array( + 'title' => $lng['admin']['plans']['add'], + 'image' => 'icons/templates_add_big.png', + 'sections' => array( + 'section_a' => array( + 'title' => $lng['admin']['plans']['plan_details'], + 'image' => 'icons/templates_add_big.png', + 'fields' => array( + 'name' => array( + 'label' => $lng['admin']['plans']['name'], + 'type' => 'text' + ), + 'description' => array( + 'label' => $lng['admin']['plans']['description'], + 'type' => 'textarea', + 'cols' => 60, + 'rows' => 12 + ) + ) + ) + ) + ) +); diff --git a/lib/formfields/admin/plans/formfield.plans_edit.php b/lib/formfields/admin/plans/formfield.plans_edit.php new file mode 100644 index 00000000..551977d4 --- /dev/null +++ b/lib/formfields/admin/plans/formfield.plans_edit.php @@ -0,0 +1,43 @@ + (2010-) + * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt + * @package Formfields + * + */ + +return array( + 'plans_edit' => array( + 'title' => $lng['admin']['plans']['edit'], + 'image' => 'icons/templates_edit_big.png', + 'sections' => array( + 'section_a' => array( + 'title' => $lng['admin']['plans']['plan_details'], + 'image' => 'icons/templates_edit_big.png', + 'fields' => array( + 'name' => array( + 'label' => $lng['admin']['plans']['name'], + 'type' => 'text', + 'value' => $result['name'] + ), + 'description' => array( + 'label' => $lng['admin']['plans']['description'], + 'type' => 'textarea', + 'cols' => 60, + 'rows' => 12, + 'value' => $result['description'] + ) + ) + ) + ) + ) +); diff --git a/lib/navigation/00.froxlor.main.php b/lib/navigation/00.froxlor.main.php index fd077c3c..a02a3a07 100644 --- a/lib/navigation/00.froxlor.main.php +++ b/lib/navigation/00.froxlor.main.php @@ -214,6 +214,11 @@ return array( 'label' => $lng['admin']['ipsandports']['ipsandports'], 'required_resources' => 'change_serversettings' ), + array( + 'url' => 'admin_plans.php?page=overview', + 'label' => $lng['admin']['plans']['plans'], + 'required_resources' => 'customers' + ), array( 'url' => 'admin_settings.php?page=updatecounters', 'label' => $lng['admin']['updatecounters'], diff --git a/lib/tables.inc.php b/lib/tables.inc.php index c9f71112..207612f0 100644 --- a/lib/tables.inc.php +++ b/lib/tables.inc.php @@ -52,5 +52,6 @@ define('TABLE_PANEL_DOMAIN_SSL_SETTINGS', 'domain_ssl_settings'); define('TABLE_DOMAINTOIP', 'panel_domaintoip'); define('TABLE_DOMAIN_DNS', 'domain_dns_entries'); define('TABLE_PANEL_FPMDAEMONS', 'panel_fpmdaemons'); +define('TABLE_PANEL_PLANS', 'panel_plans'); require dirname(__FILE__).'/version.inc.php'; diff --git a/lib/version.inc.php b/lib/version.inc.php index f2d10dde..329a4c90 100644 --- a/lib/version.inc.php +++ b/lib/version.inc.php @@ -19,7 +19,7 @@ $version = '0.9.39.2'; // Database version (YYYYMMDDC where C is a daily counter) -$dbversion = '201801260'; +$dbversion = '201802120'; // Distribution branding-tag (used for Debian etc.) $branding = ''; diff --git a/lng/english.lng.php b/lng/english.lng.php index 3123a1db..df6ce8ce 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -2104,3 +2104,13 @@ $lng['serversettings']['phpfpm_settings']['envpath'] = 'Paths to add to the PATH $lng['admin']['configfiles']['importexport'] = 'Import/Export'; $lng['success']['settingsimported'] = 'Settings imported successfully'; $lng['error']['jsonextensionnotfound'] = 'This feature requires the php json-extension.'; + +// added in froxlor 0.9.39 +$lng['admin']['plans']['name'] = 'Plan name'; +$lng['admin']['plans']['description'] = 'Description'; +$lng['admin']['plans']['last_update'] = 'Last updated'; +$lng['admin']['plans']['plans'] = 'Hosting plans'; +$lng['admin']['plans']['plan_details'] = 'Plan details'; +$lng['admin']['plans']['add'] = 'Add new plan'; +$lng['admin']['plans']['edit'] = 'Edit plan'; +$lng['admin']['plans']['use_plan'] = 'Apply plan'; diff --git a/lng/german.lng.php b/lng/german.lng.php index e5257bcd..90592c13 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -1754,3 +1754,13 @@ $lng['phpfpm']['ini_admin_values'] = 'Mögliche php_admin_value $lng['serversettings']['phpfpm_settings']['envpath'] = 'Pfade für die PATH Umgebungsvariable. Leerlassen, um keine PATH Umgebungsvariable zu setzen.'; $lng['success']['settingsimported'] = 'Einstellungnen erfolgreich importiert'; $lng['error']['jsonextensionnotfound'] = 'Diese Funktion benötigt die PHP json-Erweiterung.'; + +// added in froxlor 0.9.39 +$lng['admin']['plans']['name'] = 'Plan Name'; +$lng['admin']['plans']['description'] = 'Beschreibung'; +$lng['admin']['plans']['last_update'] = 'Zuletzt aktualisiert'; +$lng['admin']['plans']['plans'] = 'Hosting Pläne'; +$lng['admin']['plans']['plan_details'] = 'Plan Details'; +$lng['admin']['plans']['add'] = 'Neuen Plan anlegen'; +$lng['admin']['plans']['edit'] = 'Plan editieren'; +$lng['admin']['plans']['use_plan'] = 'Plan übernehmen'; diff --git a/templates/Sparkle/admin/customers/customers_add.tpl b/templates/Sparkle/admin/customers/customers_add.tpl index ffabf7d9..472c6b54 100644 --- a/templates/Sparkle/admin/customers/customers_add.tpl +++ b/templates/Sparkle/admin/customers/customers_add.tpl @@ -6,6 +6,7 @@ $header {$title} +
    diff --git a/templates/Sparkle/admin/customers/customers_edit.tpl b/templates/Sparkle/admin/customers/customers_edit.tpl index f9d751ec..5d4d1c34 100644 --- a/templates/Sparkle/admin/customers/customers_edit.tpl +++ b/templates/Sparkle/admin/customers/customers_edit.tpl @@ -6,6 +6,7 @@ $header {$title} +
    diff --git a/templates/Sparkle/admin/plans/plans.tpl b/templates/Sparkle/admin/plans/plans.tpl new file mode 100644 index 00000000..963c1dbf --- /dev/null +++ b/templates/Sparkle/admin/plans/plans.tpl @@ -0,0 +1,59 @@ +$header +
    +
    +

    +   + {$lng['admin']['plans']['plans']} +

    +
    + +
    + +
    + + +
    + {$searchcode} +
    + + + + + + + + + + + + + + + + + + + + + + + + $plans + +
    {$lng['admin']['plans']['name']} {$arrowcode['p.name']}{$lng['admin']['plans']['description']} {$arrowcode['p.description']}{$lng['admin']['admin']} {$arrowcode['a.adminname']}{$lng['admin']['plans']['last_update']} {$arrowcode['p.ts']}{$lng['panel']['options']}
    {$pagingcode}
    +
    + + + + + +
    + +
    +$footer diff --git a/templates/Sparkle/admin/plans/plans_add.tpl b/templates/Sparkle/admin/plans/plans_add.tpl new file mode 100644 index 00000000..ac434237 --- /dev/null +++ b/templates/Sparkle/admin/plans/plans_add.tpl @@ -0,0 +1,26 @@ +$header +
    +
    +

    + {$title}  + {$title} +

    +
    + +
    + +
    + + + + + + + {$plans_add_form} +
    +
    + +
    + +
    +$footer diff --git a/templates/Sparkle/admin/plans/plans_edit.tpl b/templates/Sparkle/admin/plans/plans_edit.tpl new file mode 100644 index 00000000..a812f803 --- /dev/null +++ b/templates/Sparkle/admin/plans/plans_edit.tpl @@ -0,0 +1,27 @@ +$header +
    +
    +

    + {$title}  + {$title} +

    +
    + +
    + +
    + + + + + + + + {$plans_edit_form} +
    +
    + +
    + +
    +$footer diff --git a/templates/Sparkle/admin/plans/plans_plan.tpl b/templates/Sparkle/admin/plans/plans_plan.tpl new file mode 100644 index 00000000..a49c703c --- /dev/null +++ b/templates/Sparkle/admin/plans/plans_plan.tpl @@ -0,0 +1,14 @@ + + {$row['name']} + {$row['description']} + {$row['adminname']} + {$row['ts_format']} + + + {$lng['panel']['edit']} +   + + {$lng['panel']['delete']} + + + diff --git a/templates/Sparkle/assets/js/customers.js b/templates/Sparkle/assets/js/customers.js new file mode 100644 index 00000000..04c7ee89 --- /dev/null +++ b/templates/Sparkle/assets/js/customers.js @@ -0,0 +1,74 @@ +$(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]; + } + } + }; + + /** + * disable unusable php-configuration by customer settings + */ + $('#use_plan').change(function() { + var pid = $(this).val(); + if (pid > 0) { + var sid = getUrlParameter('s'); + $.ajax({ + url: "admin_plans.php?s="+sid+"&page=overview&action=jqGetPlanValues", + type: "POST", + data: { + planid: pid + }, + dataType: "json", + success: function(json) { + for (i in json) { + if (i == 'email_imap' || i == 'email_pop3' || i == 'perlenabled' || i == 'phpenabled' || i == 'dnsenabled') { + /** handle checkboxes **/ + if (json[i] == 1) { + $("input[name='"+i+"']").attr('checked', 'checked'); + } else { + $("input[name='"+i+"']").removeAttr('checked'); + } + } else if (i == 'allowed_phpconfigs') { + /** handle array of values **/ + $("input[name='allowed_phpconfigs[]']").each(function(index) { + $(this).removeAttr('checked'); + for (j in json[i]) { + if ($(this).val() == json[i][j]) { + $(this).attr('checked', 'checked'); + } + } + }); + } else if (json[i] == -1) { + /** handle unlimited checkboxes **/ + $("input[name='" + i + "_ul']").attr('checked', 'checked'); + $("input[name='"+i+"']").prop({ + readonly: true + }); + } else { + /** handle normal value **/ + $("input[name='"+i+"']").val(json[i]); + $("input[name='"+i+"']").prop({ + readonly: false + }); + $("input[name='" + i + "_ul']").removeAttr('checked'); + } + } + }, + error: function(a, b) { + console.log(a, b); + } + }); + } + }); + +}); From 4d7ceb9efe5abd301bfc230b45d4da8c50e98339 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 12 Feb 2018 13:33:29 +0100 Subject: [PATCH 0414/1335] add missing question-string Signed-off-by: Michael Kaufmann (d00p) --- lng/english.lng.php | 1 + lng/german.lng.php | 1 + 2 files changed, 2 insertions(+) diff --git a/lng/english.lng.php b/lng/english.lng.php index df6ce8ce..b8171b15 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -2114,3 +2114,4 @@ $lng['admin']['plans']['plan_details'] = 'Plan details'; $lng['admin']['plans']['add'] = 'Add new plan'; $lng['admin']['plans']['edit'] = 'Edit plan'; $lng['admin']['plans']['use_plan'] = 'Apply plan'; +$lng['question']['plan_reallydelete'] = 'Do you really want to delete the hosting plan %s?'; diff --git a/lng/german.lng.php b/lng/german.lng.php index 90592c13..0055230e 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -1764,3 +1764,4 @@ $lng['admin']['plans']['plan_details'] = 'Plan Details'; $lng['admin']['plans']['add'] = 'Neuen Plan anlegen'; $lng['admin']['plans']['edit'] = 'Plan editieren'; $lng['admin']['plans']['use_plan'] = 'Plan übernehmen'; +$lng['question']['plan_reallydelete'] = 'Wollen Sie den Hosting-Plan "%s" wirklich löschen?'; From de70dbb888634d9eb2a98654e71bfb79ecc5672f Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 12 Feb 2018 14:58:17 +0100 Subject: [PATCH 0415/1335] fix checked-property when applying hosting-plan for allowed-phpconfigs Signed-off-by: Michael Kaufmann (d00p) --- install/updates/froxlor/0.9/update_0.9.inc.php | 1 - .../admin/customer/formfield.customer_add.php | 4 ++-- templates/Sparkle/assets/js/customers.js | 11 ++++++----- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index 81cf8b8e..77ac58e3 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -3912,4 +3912,3 @@ if (isDatabaseVersion('201801260')) { updateToDbVersion('201802120'); } - diff --git a/lib/formfields/admin/customer/formfield.customer_add.php b/lib/formfields/admin/customer/formfield.customer_add.php index c123f161..bba95e12 100644 --- a/lib/formfields/admin/customer/formfield.customer_add.php +++ b/lib/formfields/admin/customer/formfield.customer_add.php @@ -302,9 +302,9 @@ return array( 'values' => $phpconfigs, 'value' => ((int) Settings::Get('system.mod_fcgid') == 1 ? array( Settings::Get('system.mod_fcgid_defaultini') - ) : (int) Settings::Get('phpfpm.enabled') == 1) ? array( + ) : (int) Settings::Get('phpfpm.enabled') == 1 ? array( Settings::Get('phpfpm.defaultini') - ) : array(), + ) : array()), 'is_array' => 1 ), 'perlenabled' => array( diff --git a/templates/Sparkle/assets/js/customers.js b/templates/Sparkle/assets/js/customers.js index 04c7ee89..7863f345 100644 --- a/templates/Sparkle/assets/js/customers.js +++ b/templates/Sparkle/assets/js/customers.js @@ -34,17 +34,18 @@ $(document).ready(function() { if (i == 'email_imap' || i == 'email_pop3' || i == 'perlenabled' || i == 'phpenabled' || i == 'dnsenabled') { /** handle checkboxes **/ if (json[i] == 1) { - $("input[name='"+i+"']").attr('checked', 'checked'); + $("input[name='"+i+"']").prop('checked', true); } else { - $("input[name='"+i+"']").removeAttr('checked'); + $("input[name='"+i+"']").prop('checked', false); } } else if (i == 'allowed_phpconfigs') { /** handle array of values **/ $("input[name='allowed_phpconfigs[]']").each(function(index) { - $(this).removeAttr('checked'); + $(this).prop('checked', false); for (j in json[i]) { if ($(this).val() == json[i][j]) { - $(this).attr('checked', 'checked'); + $(this).prop('checked', true); + break; } } }); @@ -60,7 +61,7 @@ $(document).ready(function() { $("input[name='"+i+"']").prop({ readonly: false }); - $("input[name='" + i + "_ul']").removeAttr('checked'); + $("input[name='" + i + "_ul']").prop('checked', false); } } }, From 372ab5d9c807a0133bcecc9c0faa5d5a98e32b2e Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 13 Feb 2018 08:12:58 +0100 Subject: [PATCH 0416/1335] add domain flag to avoid generation of nginx try_files directive in webroot which is not suitable for some applications; fixes #461 Signed-off-by: Michael Kaufmann (d00p) --- admin_domains.php | 11 +++++++++++ install/froxlor.sql | 3 ++- install/updates/froxlor/0.9/update_0.9.inc.php | 9 +++++++++ .../admin/domains/formfield.domains_add.php | 13 +++++++++++++ .../admin/domains/formfield.domains_edit.php | 15 +++++++++++++++ lib/version.inc.php | 2 +- lng/english.lng.php | 2 ++ lng/german.lng.php | 2 ++ scripts/jobs/cron_tasks.inc.http.30.nginx.php | 6 ++++-- 9 files changed, 59 insertions(+), 4 deletions(-) diff --git a/admin_domains.php b/admin_domains.php index 34418fdc..6dc7cae2 100644 --- a/admin_domains.php +++ b/admin_domains.php @@ -429,6 +429,7 @@ if ($page == 'domains' || $page == 'overview') { } $specialsettings = validate(str_replace("\r\n", "\n", $_POST['specialsettings']), 'specialsettings', '/^[^\0]*$/'); + $notryfiles = isset($_POST['notryfiles']) && (int)$_POST['notryfiles'] == 1 ? 1 : 0; validate($_POST['documentroot'], 'documentroot'); // If path is empty and 'Use domain name as default value for DocumentRoot path' is enabled in settings, @@ -451,6 +452,7 @@ if ($page == 'domains' || $page == 'overview') { $zonefile = ''; $dkim = '1'; $specialsettings = ''; + $notryfiles = '0'; } if ($userinfo['caneditphpsettings'] == '1' || $userinfo['change_serversettings'] == '1') { @@ -801,6 +803,7 @@ if ($page == 'domains' || $page == 'overview') { 'mod_fcgid_starter' => $mod_fcgid_starter, 'mod_fcgid_maxrequests' => $mod_fcgid_maxrequests, 'specialsettings' => $specialsettings, + 'notryfiles' => $notryfiles, 'registration_date' => $registration_date, 'termination_date' => $termination_date, 'issubof' => $issubof, @@ -850,6 +853,7 @@ if ($page == 'domains' || $page == 'overview') { 'openbasedir' => $openbasedir, 'speciallogfile' => $speciallogfile, 'specialsettings' => $specialsettings, + 'notryfiles' => $notryfiles, 'ssl_redirect' => $ssl_redirect, 'add_date' => time(), 'registration_date' => $registration_date, @@ -889,6 +893,7 @@ if ($page == 'domains' || $page == 'overview') { `openbasedir` = :openbasedir, `speciallogfile` = :speciallogfile, `specialsettings` = :specialsettings, + `notryfiles` = :notryfiles, `ssl_redirect` = :ssl_redirect, `add_date` = :add_date, `registration_date` = :registration_date, @@ -1338,6 +1343,7 @@ if ($page == 'domains' || $page == 'overview') { $specialsettings = validate(str_replace("\r\n", "\n", $_POST['specialsettings']), 'specialsettings', '/^[^\0]*$/'); $ssfs = (isset($_POST['specialsettingsforsubdomains']) && intval($_POST['specialsettingsforsubdomains']) == 1) ? 1 : 0; + $notryfiles = isset($_POST['notryfiles']) && (int)$_POST['notryfiles'] == 1 ? 1 : 0; $documentroot = validate($_POST['documentroot'], 'documentroot'); if ($documentroot == '') { @@ -1359,6 +1365,7 @@ if ($page == 'domains' || $page == 'overview') { $dkim = $result['dkim']; $specialsettings = $result['specialsettings']; $ssfs = (empty($specialsettings) ? 0 : 1); + $notryfiles = $result['notryfiles']; $documentroot = $result['documentroot']; } @@ -1655,6 +1662,7 @@ if ($page == 'domains' || $page == 'overview') { 'mod_fcgid_maxrequests' => $mod_fcgid_maxrequests, 'specialsettings' => $specialsettings, 'specialsettingsforsubdomains' => $ssfs, + 'notryfiles' => $notryfiles, 'registration_date' => $registration_date, 'termination_date' => $termination_date, 'issubof' => $issubof, @@ -1697,6 +1705,7 @@ if ($page == 'domains' || $page == 'overview') { $mod_fcgid_starter != $result['mod_fcgid_starter'] || $mod_fcgid_maxrequests != $result['mod_fcgid_maxrequests'] || $specialsettings != $result['specialsettings'] || + $notryfiles != $result['notryfiles'] || $aliasdomain != $result['aliasdomain'] || $issubof != $result['ismainbutsubto'] || $email_only != $result['email_only'] || @@ -1855,6 +1864,7 @@ if ($page == 'domains' || $page == 'overview') { $update_data['mod_fcgid_starter'] = $mod_fcgid_starter; $update_data['mod_fcgid_maxrequests'] = $mod_fcgid_maxrequests; $update_data['specialsettings'] = $specialsettings; + $update_data['notryfiles'] = $notryfiles; $update_data['registration_date'] = $registration_date; $update_data['termination_date'] = $termination_date; $update_data['ismainbutsubto'] = $issubof; @@ -1889,6 +1899,7 @@ if ($page == 'domains' || $page == 'overview') { `mod_fcgid_starter` = :mod_fcgid_starter, `mod_fcgid_maxrequests` = :mod_fcgid_maxrequests, `specialsettings` = :specialsettings, + `notryfiles` = :notryfiles, `registration_date` = :registration_date, `termination_date` = :termination_date, `ismainbutsubto` = :ismainbutsubto, diff --git a/install/froxlor.sql b/install/froxlor.sql index 21c0a32d..a8ab851d 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -260,6 +260,7 @@ CREATE TABLE `panel_domains` ( `hsts_preload` tinyint(1) NOT NULL default '0', `ocsp_stapling` tinyint(1) DEFAULT '0', `http2` tinyint(1) DEFAULT '0', + `notryfiles` tinyint(1) DEFAULT '0', PRIMARY KEY (`id`), KEY `customerid` (`customerid`), KEY `parentdomain` (`parentdomainid`), @@ -687,7 +688,7 @@ opcache.interned_strings_buffer'), ('panel', 'password_special_char', '!?<>§$%+#=@'), ('panel', 'customer_hide_options', ''), ('panel', 'version', '0.9.39.2'), - ('panel', 'db_version', '201802120'); + ('panel', 'db_version', '201802130'); DROP TABLE IF EXISTS `panel_tasks`; diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index 77ac58e3..36bb2e59 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -3912,3 +3912,12 @@ if (isDatabaseVersion('201801260')) { updateToDbVersion('201802120'); } + +if (isDatabaseVersion('201802120')) { + + showUpdateStep("Adding domain field for try_files flag"); + Database::query("ALTER TABLE `" . TABLE_PANEL_DOMAINS . "` ADD `notryfiles` tinyint(1) DEFAULT '0';"); + lastStepStatus(0); + + updateToDbVersion('201802130'); +} diff --git a/lib/formfields/admin/domains/formfield.domains_add.php b/lib/formfields/admin/domains/formfield.domains_add.php index 71116996..7cb4f546 100644 --- a/lib/formfields/admin/domains/formfield.domains_add.php +++ b/lib/formfields/admin/domains/formfield.domains_add.php @@ -131,6 +131,19 @@ return array( 'type' => 'textarea', 'cols' => 60, 'rows' => 12 + ), + 'notryfiles' => array( + 'visible' => (Settings('system.webserver') == 'nginx' && $userinfo['change_serversettings'] == '1'), + 'label' => $lng['admin']['notryfiles']['title'], + 'desc' => $lng['admin']['notryfiles']['description'], + 'type' => 'checkbox', + 'values' => array( + array( + 'label' => $lng['panel']['yes'], + 'value' => '1' + ) + ), + 'value' => array() ) ) ), diff --git a/lib/formfields/admin/domains/formfield.domains_edit.php b/lib/formfields/admin/domains/formfield.domains_edit.php index d37f4806..734619c8 100644 --- a/lib/formfields/admin/domains/formfield.domains_edit.php +++ b/lib/formfields/admin/domains/formfield.domains_edit.php @@ -161,6 +161,21 @@ return array( 'value' => array( '1' ) + ), + 'notryfiles' => array( + 'visible' => (Settings('system.webserver') == 'nginx' && $userinfo['change_serversettings'] == '1'), + 'label' => $lng['admin']['notryfiles']['title'], + 'desc' => $lng['admin']['notryfiles']['description'], + 'type' => 'checkbox', + 'values' => array( + array( + 'label' => $lng['panel']['yes'], + 'value' => '1' + ) + ), + 'value' => array( + $result['notryfiles'] + ) ) ) ), diff --git a/lib/version.inc.php b/lib/version.inc.php index 329a4c90..dac2b1c4 100644 --- a/lib/version.inc.php +++ b/lib/version.inc.php @@ -19,7 +19,7 @@ $version = '0.9.39.2'; // Database version (YYYYMMDDC where C is a daily counter) -$dbversion = '201802120'; +$dbversion = '201802130'; // Distribution branding-tag (used for Debian etc.) $branding = ''; diff --git a/lng/english.lng.php b/lng/english.lng.php index b8171b15..a5d33367 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -2115,3 +2115,5 @@ $lng['admin']['plans']['add'] = 'Add new plan'; $lng['admin']['plans']['edit'] = 'Edit plan'; $lng['admin']['plans']['use_plan'] = 'Apply plan'; $lng['question']['plan_reallydelete'] = 'Do you really want to delete the hosting plan %s?'; +$lng['admin']['notryfiles']['title'] = 'No autogenerated try_files'; +$lng['admin']['notryfiles']['description'] = 'Say yes here if you want to specify a custom try_files directive in specialsettings (needed for some wordpress plugins for example).'; diff --git a/lng/german.lng.php b/lng/german.lng.php index 0055230e..1f6ee8f9 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -1765,3 +1765,5 @@ $lng['admin']['plans']['add'] = 'Neuen Plan anlegen'; $lng['admin']['plans']['edit'] = 'Plan editieren'; $lng['admin']['plans']['use_plan'] = 'Plan übernehmen'; $lng['question']['plan_reallydelete'] = 'Wollen Sie den Hosting-Plan "%s" wirklich löschen?'; +$lng['admin']['notryfiles']['title'] = 'Keine generierte try_files Anweisung'; +$lng['admin']['notryfiles']['description'] = 'Wähle "Ja", wenn eine eigene try_files Direktive in den "eigenen Vhost Einstellungen" angegeben werden soll (z.B. nötig für manche Wordpress Plugins).'; diff --git a/scripts/jobs/cron_tasks.inc.http.30.nginx.php b/scripts/jobs/cron_tasks.inc.http.30.nginx.php index d4d27c77..7f86f605 100644 --- a/scripts/jobs/cron_tasks.inc.http.30.nginx.php +++ b/scripts/jobs/cron_tasks.inc.http.30.nginx.php @@ -916,7 +916,9 @@ class nginx extends HttpConfigBase if ($domain['phpenabled_customer'] == 1 && $domain['phpenabled_vhost'] == '1') { $webroot_text .= "\t" . 'index index.php index.html index.htm;' . "\n"; - $webroot_text .= "\t\t" . 'try_files $uri $uri/ @rewrites;' . "\n"; + if ($domain['notryfiles'] != 1) { + $webroot_text .= "\t\t" . 'try_files $uri $uri/ @rewrites;' . "\n"; + } } else { $webroot_text .= "\t" . 'index index.html index.htm;' . "\n"; } @@ -927,7 +929,7 @@ class nginx extends HttpConfigBase } $webroot_text .= "\t" . '}' . "\n\n"; - if ($domain['phpenabled_customer'] == 1 && $domain['phpenabled_vhost'] == '1') { + if ($domain['phpenabled_customer'] == 1 && $domain['phpenabled_vhost'] == '1' && $domain['notryfiles'] != 1) { $webroot_text .= "\tlocation @rewrites {\n"; $webroot_text .= "\t\trewrite ^ /index.php last;\n"; $webroot_text .= "\t}\n\n"; From 7a664a99909be149fd0a308aa773d4b14b3b910a Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 13 Feb 2018 08:21:45 +0100 Subject: [PATCH 0417/1335] clean up panel_diskspace/panel_diskspace_admins when removing customer/admin Signed-off-by: Michael Kaufmann (d00p) --- admin_admins.php | 5 +++++ admin_customers.php | 2 ++ 2 files changed, 7 insertions(+) diff --git a/admin_admins.php b/admin_admins.php index f0ede7de..4e5a54d5 100644 --- a/admin_admins.php +++ b/admin_admins.php @@ -170,6 +170,11 @@ if ($page == 'admins' "); Database::pexecute($del_stmt, array('adminid' => $id)); + $del_stmt = Database::prepare(" + DELETE FROM `" . TABLE_PANEL_DISKSPACE_ADMINS . "` WHERE `adminid` = :adminid + "); + Database::pexecute($del_stmt, array('adminid' => $id)); + $upd_stmt = Database::prepare(" UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET `adminid` = :userid WHERE `adminid` = :adminid diff --git a/admin_customers.php b/admin_customers.php index ddd139db..1ccb9a19 100644 --- a/admin_customers.php +++ b/admin_customers.php @@ -307,6 +307,8 @@ if ($page == 'customers' Database::pexecute($stmt, array('id' => $id)); $stmt = Database::prepare("DELETE FROM `" . TABLE_PANEL_TRAFFIC . "` WHERE `customerid` = :id"); Database::pexecute($stmt, array('id' => $id)); + $stmt = Database::prepare("DELETE FROM `" . TABLE_PANEL_DISKSPACE . "` WHERE `customerid` = :id"); + Database::pexecute($stmt, array('id' => $id)); $stmt = Database::prepare("DELETE FROM `" . TABLE_MAIL_USERS . "` WHERE `customerid` = :id"); Database::pexecute($stmt, array('id' => $id)); $stmt = Database::prepare("DELETE FROM `" . TABLE_MAIL_VIRTUAL . "` WHERE `customerid` = :id"); From 482e8c9a11ac4fa5381d273c9209ff4096e5e561 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 13 Feb 2018 09:06:49 +0100 Subject: [PATCH 0418/1335] set version to 0.9.39.3 for maintenance release and upcoming debian package so the new hosting-plan feature is included Signed-off-by: Michael Kaufmann (d00p) --- install/froxlor.sql | 2 +- install/updates/froxlor/0.9/update_0.9.inc.php | 6 ++++++ lib/version.inc.php | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/install/froxlor.sql b/install/froxlor.sql index a8ab851d..e484024d 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -687,7 +687,7 @@ opcache.interned_strings_buffer'), ('panel', 'password_special_char_required', '0'), ('panel', 'password_special_char', '!?<>§$%+#=@'), ('panel', 'customer_hide_options', ''), - ('panel', 'version', '0.9.39.2'), + ('panel', 'version', '0.9.39.3'), ('panel', 'db_version', '201802130'); diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index 36bb2e59..b7be4833 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -3921,3 +3921,9 @@ if (isDatabaseVersion('201802120')) { updateToDbVersion('201802130'); } + +if (isFroxlorVersion('0.9.39.2')) { + + showUpdateStep("Updating from 0.9.39.2 to 0.9.39.3", false); + updateToVersion('0.9.39.3'); +} diff --git a/lib/version.inc.php b/lib/version.inc.php index dac2b1c4..b8e701dc 100644 --- a/lib/version.inc.php +++ b/lib/version.inc.php @@ -16,7 +16,7 @@ */ // Main version variable -$version = '0.9.39.2'; +$version = '0.9.39.3'; // Database version (YYYYMMDDC where C is a daily counter) $dbversion = '201802130'; From fc0c796b68dc368b0fd3902bcebd2e5f78b8433c Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 13 Feb 2018 10:58:06 +0100 Subject: [PATCH 0419/1335] avoid undefined variables/indexes Signed-off-by: Michael Kaufmann (d00p) --- admin_plans.php | 31 ++++++++++++++++++++++++- templates/Sparkle/admin/plans/plans.tpl | 2 +- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/admin_plans.php b/admin_plans.php index 6ff53949..d37f04cd 100644 --- a/admin_plans.php +++ b/admin_plans.php @@ -244,6 +244,11 @@ if ($page == '' || $page == 'overview') { } } + // dummy to avoid unknown variables + $language_options = null; + $gender_options = null; + $hosting_plans = null; + $plans_add_data = include_once dirname(__FILE__) . '/lib/formfields/admin/plans/formfield.plans_add.php'; $cust_add_data = include_once dirname(__FILE__) . '/lib/formfields/admin/customer/formfield.customer_add.php'; // unset unneeded stuff @@ -462,7 +467,31 @@ if ($page == '' || $page == 'overview') { ); } } - + + // dummy to avoid unknown variables + $result['loginname'] = null; + $result['documentroot'] = null; + $result['standardsubdomain'] = null; + $result['deactivated'] = null; + $language_options = null; + $result['firstname'] = null; + $gender_options = null; + $result['company'] = null; + $result['street'] = null; + $result['zipcode'] = null; + $result['city'] = null; + $result['phone'] = null; + $result['fax'] = null; + $result['email'] = null; + $result['customernumber'] = null; + $result['custom_notes'] = null; + $result['custom_notes_show'] = null; + $hosting_plans = null; + $result['imap'] = null; + $result['pop3'] = null; + $admin_select_cnt = null; + $admin_select = null; + $plans_edit_data = include_once dirname(__FILE__) . '/lib/formfields/admin/plans/formfield.plans_edit.php'; $cust_edit_data = include_once dirname(__FILE__) . '/lib/formfields/admin/customer/formfield.customer_edit.php'; // unset unneeded stuff diff --git a/templates/Sparkle/admin/plans/plans.tpl b/templates/Sparkle/admin/plans/plans.tpl index 963c1dbf..ed9c4432 100644 --- a/templates/Sparkle/admin/plans/plans.tpl +++ b/templates/Sparkle/admin/plans/plans.tpl @@ -26,7 +26,7 @@ $header {$lng['admin']['plans']['name']} {$arrowcode['p.name']} {$lng['admin']['plans']['description']} {$arrowcode['p.description']} - {$lng['admin']['admin']} {$arrowcode['a.adminname']} + {$lng['admin']['admin']} {$arrowcode['adminname']} {$lng['admin']['plans']['last_update']} {$arrowcode['p.ts']} {$lng['panel']['options']} From 27f8c8b4384a1a5fb0a9bd8cda3d24ff562ee0e5 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 13 Feb 2018 11:49:15 +0100 Subject: [PATCH 0420/1335] show domains of disabled customers also as disabled Signed-off-by: Michael Kaufmann (d00p) --- admin_domains.php | 2 +- templates/Sparkle/admin/domains/domains_domain.tpl | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/admin_domains.php b/admin_domains.php index 6dc7cae2..f731d59c 100644 --- a/admin_domains.php +++ b/admin_domains.php @@ -55,7 +55,7 @@ if ($page == 'domains' || $page == 'overview') { $syshostname = "AND `d`.`id` <> " . Settings::Get('system.hostname_id'); } $result_stmt = Database::prepare(" - SELECT `d`.*, `c`.`loginname`, `c`.`name`, `c`.`firstname`, `c`.`company`, `c`.`standardsubdomain`, `ad`.`id` AS `aliasdomainid`, `ad`.`domain` AS `aliasdomain` + SELECT `d`.*, `c`.`loginname`, `c`.`deactivated`, `c`.`name`, `c`.`firstname`, `c`.`company`, `c`.`standardsubdomain`, `ad`.`id` AS `aliasdomainid`, `ad`.`domain` AS `aliasdomain` FROM `" . TABLE_PANEL_DOMAINS . "` `d` LEFT JOIN `" . TABLE_PANEL_CUSTOMERS . "` `c` USING(`customerid`) LEFT JOIN `" . TABLE_PANEL_DOMAINS . "` `ad` ON `d`.`aliasdomain`=`ad`.`id` diff --git a/templates/Sparkle/admin/domains/domains_domain.tpl b/templates/Sparkle/admin/domains/domains_domain.tpl index 4ccf717c..8e9140c5 100644 --- a/templates/Sparkle/admin/domains/domains_domain.tpl +++ b/templates/Sparkle/admin/domains/domains_domain.tpl @@ -1,8 +1,8 @@ - + - + class="disabled"> {$row['domain']} From 6befe85656d96327e8597f623fc190a12c12db3e Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 13 Feb 2018 12:23:05 +0100 Subject: [PATCH 0421/1335] fix add/edit domain due to wrong access to settings; setting version to 0.9.39.4 for bugfix release Signed-off-by: Michael Kaufmann (d00p) --- install/froxlor.sql | 2 +- install/updates/froxlor/0.9/update_0.9.inc.php | 6 ++++++ lib/formfields/admin/domains/formfield.domains_add.php | 2 +- lib/formfields/admin/domains/formfield.domains_edit.php | 2 +- lib/version.inc.php | 2 +- 5 files changed, 10 insertions(+), 4 deletions(-) diff --git a/install/froxlor.sql b/install/froxlor.sql index e484024d..034d561e 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -687,7 +687,7 @@ opcache.interned_strings_buffer'), ('panel', 'password_special_char_required', '0'), ('panel', 'password_special_char', '!?<>§$%+#=@'), ('panel', 'customer_hide_options', ''), - ('panel', 'version', '0.9.39.3'), + ('panel', 'version', '0.9.39.4'), ('panel', 'db_version', '201802130'); diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index b7be4833..ca951369 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -3927,3 +3927,9 @@ if (isFroxlorVersion('0.9.39.2')) { showUpdateStep("Updating from 0.9.39.2 to 0.9.39.3", false); updateToVersion('0.9.39.3'); } + +if (isFroxlorVersion('0.9.39.3')) { + + showUpdateStep("Updating from 0.9.39.3 to 0.9.39.4", false); + updateToVersion('0.9.39.4'); +} diff --git a/lib/formfields/admin/domains/formfield.domains_add.php b/lib/formfields/admin/domains/formfield.domains_add.php index 7cb4f546..a18018b7 100644 --- a/lib/formfields/admin/domains/formfield.domains_add.php +++ b/lib/formfields/admin/domains/formfield.domains_add.php @@ -133,7 +133,7 @@ return array( 'rows' => 12 ), 'notryfiles' => array( - 'visible' => (Settings('system.webserver') == 'nginx' && $userinfo['change_serversettings'] == '1'), + 'visible' => (Settings::Get('system.webserver') == 'nginx' && $userinfo['change_serversettings'] == '1'), 'label' => $lng['admin']['notryfiles']['title'], 'desc' => $lng['admin']['notryfiles']['description'], 'type' => 'checkbox', diff --git a/lib/formfields/admin/domains/formfield.domains_edit.php b/lib/formfields/admin/domains/formfield.domains_edit.php index 734619c8..5e791da7 100644 --- a/lib/formfields/admin/domains/formfield.domains_edit.php +++ b/lib/formfields/admin/domains/formfield.domains_edit.php @@ -163,7 +163,7 @@ return array( ) ), 'notryfiles' => array( - 'visible' => (Settings('system.webserver') == 'nginx' && $userinfo['change_serversettings'] == '1'), + 'visible' => (Settings::Get('system.webserver') == 'nginx' && $userinfo['change_serversettings'] == '1'), 'label' => $lng['admin']['notryfiles']['title'], 'desc' => $lng['admin']['notryfiles']['description'], 'type' => 'checkbox', diff --git a/lib/version.inc.php b/lib/version.inc.php index b8e701dc..06e38b36 100644 --- a/lib/version.inc.php +++ b/lib/version.inc.php @@ -16,7 +16,7 @@ */ // Main version variable -$version = '0.9.39.3'; +$version = '0.9.39.4'; // Database version (YYYYMMDDC where C is a daily counter) $dbversion = '201802130'; From edd5ef0ca047b132863a922183ef1c28f26788fe Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Wed, 14 Feb 2018 07:40:39 +0100 Subject: [PATCH 0422/1335] fix correct display of selected checkbox imap/pop3 when editing hosting plan Signed-off-by: Michael Kaufmann (d00p) --- admin_plans.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/admin_plans.php b/admin_plans.php index d37f04cd..eaee22b2 100644 --- a/admin_plans.php +++ b/admin_plans.php @@ -468,6 +468,9 @@ if ($page == '' || $page == 'overview') { } } + $result['imap'] = $result['email_imap']; + $result['pop3'] = $result['email_pop3']; + // dummy to avoid unknown variables $result['loginname'] = null; $result['documentroot'] = null; @@ -487,8 +490,6 @@ if ($page == '' || $page == 'overview') { $result['custom_notes'] = null; $result['custom_notes_show'] = null; $hosting_plans = null; - $result['imap'] = null; - $result['pop3'] = null; $admin_select_cnt = null; $admin_select = null; From 5c30961d3c0a3dc9262b09f39b56e52f4e902694 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Wed, 14 Feb 2018 13:31:39 +0100 Subject: [PATCH 0423/1335] set version to 0.9.39.5 for bugfix release (imap/pop3 ticks not enabled if active in hosting plan when editing hosting plan) Signed-off-by: Michael Kaufmann (d00p) --- install/froxlor.sql | 2 +- install/updates/froxlor/0.9/update_0.9.inc.php | 6 ++++++ lib/version.inc.php | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/install/froxlor.sql b/install/froxlor.sql index 034d561e..870e30d4 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -687,7 +687,7 @@ opcache.interned_strings_buffer'), ('panel', 'password_special_char_required', '0'), ('panel', 'password_special_char', '!?<>§$%+#=@'), ('panel', 'customer_hide_options', ''), - ('panel', 'version', '0.9.39.4'), + ('panel', 'version', '0.9.39.5'), ('panel', 'db_version', '201802130'); diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index ca951369..0abab0fe 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -3933,3 +3933,9 @@ if (isFroxlorVersion('0.9.39.3')) { showUpdateStep("Updating from 0.9.39.3 to 0.9.39.4", false); updateToVersion('0.9.39.4'); } + +if (isFroxlorVersion('0.9.39.4')) { + + showUpdateStep("Updating from 0.9.39.4 to 0.9.39.5", false); + updateToVersion('0.9.39.5'); +} diff --git a/lib/version.inc.php b/lib/version.inc.php index 06e38b36..49935fb7 100644 --- a/lib/version.inc.php +++ b/lib/version.inc.php @@ -16,7 +16,7 @@ */ // Main version variable -$version = '0.9.39.4'; +$version = '0.9.39.5'; // Database version (YYYYMMDDC where C is a daily counter) $dbversion = '201802130'; From 6f91bece17d5568c72c3e0001a22d6528bd8caeb Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 15 Feb 2018 07:44:54 +0100 Subject: [PATCH 0424/1335] fix postfix config for postfix/courier on precise and trusty, fixes #516 Signed-off-by: Michael Kaufmann (d00p) --- lib/configfiles/precise.xml | 2 +- lib/configfiles/trusty.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/configfiles/precise.xml b/lib/configfiles/precise.xml index 9983959d..806d86e6 100644 --- a/lib/configfiles/precise.xml +++ b/lib/configfiles/precise.xml @@ -726,7 +726,7 @@ smtpd_sasl_local_domain = $myhostname broken_sasl_auth_clients = yes # Virtual delivery settings -virtual_mailbox_base = +virtual_mailbox_base = / virtual_mailbox_maps = mysql:/etc/postfix/mysql-virtual_mailbox_maps.cf virtual_mailbox_domains = mysql:/etc/postfix/mysql-virtual_mailbox_domains.cf virtual_alias_maps = mysql:/etc/postfix/mysql-virtual_alias_maps.cf diff --git a/lib/configfiles/trusty.xml b/lib/configfiles/trusty.xml index 2df05eeb..0dc4c61f 100644 --- a/lib/configfiles/trusty.xml +++ b/lib/configfiles/trusty.xml @@ -761,7 +761,7 @@ smtpd_sasl_local_domain = $myhostname broken_sasl_auth_clients = yes # Virtual delivery settings -virtual_mailbox_base = +virtual_mailbox_base = / virtual_mailbox_maps = mysql:/etc/postfix/mysql-virtual_mailbox_maps.cf virtual_mailbox_domains = mysql:/etc/postfix/mysql-virtual_mailbox_domains.cf virtual_alias_maps = mysql:/etc/postfix/mysql-virtual_alias_maps.cf From dd371c72a2c9a3241f04cfc7d364bb9223137d4e Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 15 Feb 2018 07:47:35 +0100 Subject: [PATCH 0425/1335] start api implementation Signed-off-by: Michael Kaufmann (d00p) --- api.php | 69 ++++++++++ install/froxlor.sql | 15 ++ lib/classes/api/abstract.ApiCommand.php | 129 ++++++++++++++++++ lib/classes/api/api_includes.inc.php | 6 + lib/classes/api/class.FroxlorRPC.php | 100 ++++++++++++++ lib/classes/database/class.Database.php | 14 +- .../output/function.standard_error.php | 5 +- lib/functions/validate/function.validate.php | 4 +- .../validate/function.validate_ip.php | 10 +- 9 files changed, 339 insertions(+), 13 deletions(-) create mode 100644 api.php create mode 100644 lib/classes/api/abstract.ApiCommand.php create mode 100644 lib/classes/api/api_includes.inc.php create mode 100644 lib/classes/api/class.FroxlorRPC.php diff --git a/api.php b/api.php new file mode 100644 index 00000000..42bfaae4 --- /dev/null +++ b/api.php @@ -0,0 +1,69 @@ +$method(); +} catch (Exception $e) { + json_response($e->getCode(), $e->getMessage()); +} + +exit(); + +/** + * output json result + * + * @param int $status + * @param string $status_message + * @param mixed $data + * + * @return void + */ +function json_response($status, $status_message, $data = null) +{ + header("HTTP/1.1 " . $status); + + $response['status'] = $status; + $response['status_message'] = $status_message; + $response['data'] = $data; + + $json_response = json_encode($response, JSON_PRETTY_PRINT); + echo $json_response; + exit(); +} diff --git a/install/froxlor.sql b/install/froxlor.sql index 870e30d4..5a262c55 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -1042,3 +1042,18 @@ CREATE TABLE `panel_plans` ( KEY adminid (adminid) ) ENGINE=MyISAM CHARSET=utf8 COLLATE=utf8_general_ci; + +DROP TABLE IF EXISTS `api_keys`; +CREATE TABLE `api_keys` ( + `id` int(11) NOT NULL auto_increment, + `adminid` int(11) NOT NULL default '0', + `customerid` int(11) NOT NULL default '0', + `apikey` varchar(500) NOT NULL default '', + `secret` varchar(500) NOT NULL default '', + `allowed_from` text NOT NULL, + `valid_until` int(15) NOT NULL default '0', + PRIMARY KEY (id), + KEY adminid (adminid), + KEY customerid (customerid) +) ENGINE=MyISAM CHARSET=utf8 COLLATE=utf8_general_ci; + diff --git a/lib/classes/api/abstract.ApiCommand.php b/lib/classes/api/abstract.ApiCommand.php new file mode 100644 index 00000000..43a5b688 --- /dev/null +++ b/lib/classes/api/abstract.ApiCommand.php @@ -0,0 +1,129 @@ +cmd_params = $params; + if (! empty($header)) { + $this->readUserData($header); + } elseif (! empty($userinfo)) { + $this->user_data = $userinfo; + $this->is_admin = ($userinfo['adminsession'] == 1 && $userinfo['adminid'] > 0) ? true : false; + } else { + throw new Exception("Invalid user data", 500); + } + $this->logger = FroxlorLogger::getInstanceOf($this->user_data); + } + + public static function getLocal($userinfo = null, $params = null) + { + return new static(null, $params, $userinfo); + } + + /** + * admin flag + * + * @return boolean + */ + protected function isAdmin() + { + return $this->is_admin; + } + + /** + * return field from user-table + * + * @param string $detail + * + * @return string + */ + protected function getUserDetail($detail = null) + { + return (isset($this->user_data[$detail]) ? $this->user_data[$detail] : null); + } + + /** + * receive field from parameter-list + * + * @param string $param + * + * @throws Exception + * @return mixed + */ + protected function getParam($param = null) + { + if (isset($this->cmd_params[$param])) { + return $this->cmd_params[$param]; + } + return null; + } + + /** + * return logger instance + * + * @return FroxlorLogger + */ + protected function logger() + { + return $this->logger; + } + + protected function response($status, $status_message, $data = null) + { + header("HTTP/1.1 " . $status); + + $response['status'] = $status; + $response['status_message'] = $status_message; + $response['data'] = $data; + + $json_response = json_encode($response, JSON_PRETTY_PRINT); + return $json_response; + } + + public abstract function list(); + + public abstract function get(); + + public abstract function add(); + + public abstract function update(); + + public abstract function delete(); + + private function readUserData($header = null) + { + $sel_stmt = Database::prepare("SELECT * FROM `api_keys` WHERE `apikey` = :ak AND `secret` = :as"); + $result = Database::pexecute_first($sel_stmt, array( + 'ak' => $header['apikey'], + 'as' => $header['secret'] + ), true, true); + if ($result) { + // admin or customer? + if ($result['customerid'] == 0) { + $this->is_admin = true; + $table = 'panel_admins'; + $key = "adminid"; + } else { + $this->is_admin = false; + $table = 'panel_customers'; + $key = "customerid"; + } + $sel_stmt = Database::prepare("SELECT * FROM `" . $table . "` WHERE `" . $key . "` = :id"); + $this->user_data = Database::pexecute_first($sel_stmt, array( + 'id' => ($this->is_admin ? $result['adminid'] : $result['customerid']) + ), true, true); + return true; + } + throw new Exception("Invalid API credentials", 400); + } +} diff --git a/lib/classes/api/api_includes.inc.php b/lib/classes/api/api_includes.inc.php new file mode 100644 index 00000000..09b3cd84 --- /dev/null +++ b/lib/classes/api/api_includes.inc.php @@ -0,0 +1,6 @@ + $key, + 'as' => $secret + ), true, true); + if ($result) { + if ($result['apikey'] == $key && $result['secret'] == $secret && ($result['valid_until'] == -1 || $result['valid_until'] >= time())) { + if (!empty($result['allowed_from'])) { + $ip_list = explode(",", $result['allowed_from']); + $access_ip = $_SERVER['REMOTE_ADDR']; + // @fixme finish me + } + return true; + } + } + throw new Exception("Invalid authorization credentials", 400); + } + + /** + * validates the given command + * + * @param array $body + * + * @throws Exception + * @return boolean + */ + private static function validateBody($request) + { + // check body + if (! isset($request['body']) || empty($request['body'])) { + throw new Exception("Invalid request body", 400); + } + + // check command exists + if (! isset($request['body']['command']) || empty($request['body']['command'])) { + throw new Exception("No command given", 400); + } + + $command = explode(".", $request['body']['command']); + + if (count($command) != 2) { + throw new Exception("Invalid command", 400); + } + // simply check for file-existance, as we do not want to use our autoloader because this way + // it will recognize non-api classes+methods as valid commands + $apiclass = FROXLOR_INSTALL_DIR . '/lib/classes/api/commands/class.' . $command[0] . '.php'; + if (! file_exists($apiclass) || ! @method_exists($command[0], $command[1])) { + // there will be an exception from the autoloader for class_exists hence the try-catch-block + throw new Exception("Unknown command", 400); + } + return array( + 'command' => array( + 'class' => $command[0], + 'method' => $command[1] + ), + 'params' => isset($request['body']['params']) ? $request['body']['params'] : null + ); + } +} diff --git a/lib/classes/database/class.Database.php b/lib/classes/database/class.Database.php index 3174c2b3..7f6c7d46 100644 --- a/lib/classes/database/class.Database.php +++ b/lib/classes/database/class.Database.php @@ -67,11 +67,11 @@ class Database { * @param array $params (optional) * @param bool $showerror suppress errordisplay (default true) */ - public static function pexecute(&$stmt, $params = null, $showerror = true) { + public static function pexecute(&$stmt, $params = null, $showerror = true, $json_response = false) { try { $stmt->execute($params); } catch (PDOException $e) { - self::_showerror($e, $showerror); + self::_showerror($e, $showerror, $json_response); } } @@ -86,8 +86,8 @@ class Database { * * @return array */ - public static function pexecute_first(&$stmt, $params = null, $showerror = true) { - self::pexecute($stmt, $params, $showerror); + public static function pexecute_first(&$stmt, $params = null, $showerror = true, $json_response = false) { + self::pexecute($stmt, $params, $showerror, $json_response); return $stmt->fetch(PDO::FETCH_ASSOC); } @@ -309,7 +309,7 @@ class Database { * @param PDOException $error * @param bool $showerror if set to false, the error will be logged but we go on */ - private static function _showerror($error, $showerror = true) { + private static function _showerror($error, $showerror = true, $json_response = false) { global $userinfo, $theme, $linker; // include userdata.inc.php @@ -367,6 +367,10 @@ class Database { @fwrite($errlog, "|TRACE\n".$error_trace."\n"); @fclose($errlog); + if ($showerror && $json_response) { + throw new Exception($error_message, 500); + } + if ($showerror) { if (empty($sql['debug'])) { $error_trace = ''; diff --git a/lib/functions/output/function.standard_error.php b/lib/functions/output/function.standard_error.php index 03fb272f..9e8bd7fb 100644 --- a/lib/functions/output/function.standard_error.php +++ b/lib/functions/output/function.standard_error.php @@ -25,7 +25,7 @@ * @author Florian Lippert * @author Ron Brand */ -function standard_error($errors = '', $replacer = '') { +function standard_error($errors = '', $replacer = '', $throw_exception = false) { global $userinfo, $s, $header, $footer, $lng, $theme; @@ -60,6 +60,9 @@ function standard_error($errors = '', $replacer = '') { } } + if ($throw_exception) { + throw new Exception($error); + } eval("echo \"" . getTemplate('misc/error', '1') . "\";"); exit; } diff --git a/lib/functions/validate/function.validate.php b/lib/functions/validate/function.validate.php index eddb8c3c..355f48e1 100644 --- a/lib/functions/validate/function.validate.php +++ b/lib/functions/validate/function.validate.php @@ -31,7 +31,7 @@ * */ -function validate($str, $fieldname, $pattern = '', $lng = '', $emptydefault = array()) { +function validate($str, $fieldname, $pattern = '', $lng = '', $emptydefault = array(), $throw_exception = false) { global $log, $theme; @@ -74,6 +74,6 @@ function validate($str, $fieldname, $pattern = '', $lng = '', $emptydefault = ar $lng = 'stringformaterror'; } - standard_error($lng, $fieldname); + standard_error($lng, $fieldname, $throw_exception); exit; } diff --git a/lib/functions/validate/function.validate_ip.php b/lib/functions/validate/function.validate_ip.php index f3caa492..2a30150b 100644 --- a/lib/functions/validate/function.validate_ip.php +++ b/lib/functions/validate/function.validate_ip.php @@ -23,7 +23,7 @@ * @return mixed ip address on success, standard_error on failure * @deprecated use validate_ip2 */ -function validate_ip($ip, $return_bool = false, $lng = 'invalidip') { +function validate_ip($ip, $return_bool = false, $lng = 'invalidip', $throw_exception = false) { if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false && filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) === false @@ -32,7 +32,7 @@ function validate_ip($ip, $return_bool = false, $lng = 'invalidip') { if ($return_bool) { return false; } else { - standard_error($lng, $ip); + standard_error($lng, $ip, $throw_exception); exit(); } } else { @@ -53,7 +53,7 @@ function validate_ip($ip, $return_bool = false, $lng = 'invalidip') { * * @return string|bool ip address on success, false on failure */ -function validate_ip2($ip, $return_bool = false, $lng = 'invalidip', $allow_localhost = false, $allow_priv = false, $allow_cidr = false) { +function validate_ip2($ip, $return_bool = false, $lng = 'invalidip', $allow_localhost = false, $allow_priv = false, $allow_cidr = false, $throw_exception = false) { $cidr = ""; if ($allow_cidr) { @@ -69,7 +69,7 @@ function validate_ip2($ip, $return_bool = false, $lng = 'invalidip', $allow_loca if ($return_bool) { return false; } else { - standard_error($lng, $ip); + standard_error($lng, $ip, $throw_exception); exit(); } } @@ -91,7 +91,7 @@ function validate_ip2($ip, $return_bool = false, $lng = 'invalidip', $allow_loca if ($return_bool) { return false; } else { - standard_error($lng, $ip); + standard_error($lng, $ip, $throw_exception); exit(); } } From 4663f8c6ec1d30b4155bb742842ecfb234a7bb54 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 15 Feb 2018 07:48:03 +0100 Subject: [PATCH 0426/1335] converted IpsAndPorts to API Signed-off-by: Michael Kaufmann (d00p) --- admin_ipsandports.php | 435 ++++-------------- .../api/commands/class.IpsAndPorts.php | 393 ++++++++++++++++ 2 files changed, 470 insertions(+), 358 deletions(-) create mode 100644 lib/classes/api/commands/class.IpsAndPorts.php diff --git a/admin_ipsandports.php b/admin_ipsandports.php index eddcf429..c9165618 100644 --- a/admin_ipsandports.php +++ b/admin_ipsandports.php @@ -16,27 +16,24 @@ * @package Panel * */ - define('AREA', 'admin'); require './lib/init.php'; if (isset($_POST['id'])) { $id = intval($_POST['id']); -} elseif(isset($_GET['id'])) { +} elseif (isset($_GET['id'])) { $id = intval($_GET['id']); } -if ($page == 'ipsandports' - || $page == 'overview' -) { +if ($page == 'ipsandports' || $page == 'overview') { // Do not display attributes that are not used by the current webserver $websrv = Settings::Get('system.webserver'); $is_nginx = ($websrv == 'nginx'); $is_apache = ($websrv == 'apache2'); $is_apache24 = $is_apache && (Settings::Get('system.apache24') === '1'); - + if ($action == '') { - + $log->logAction(ADM_ACTION, LOG_NOTICE, "viewed admin_ipsandports"); $fields = array( 'ip' => $lng['admin']['ipsandports']['ip'], @@ -53,384 +50,106 @@ if ($page == 'ipsandports' $pagingcode = $paging->getHtmlPagingCode($filename . '?page=' . $page . '&s=' . $s); $i = 0; $count = 0; - + while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) { - + if ($paging->checkDisplay($i)) { $row = htmlentities_array($row); if (filter_var($row['ip'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { $row['ip'] = '[' . $row['ip'] . ']'; } eval("\$ipsandports.=\"" . getTemplate("ipsandports/ipsandports_ipandport") . "\";"); - $count++; + $count ++; } - $i++; + $i ++; } - eval("echo \"" . getTemplate("ipsandports/ipsandports") . "\";"); - - } elseif($action == 'delete' - && $id != 0 - ) { - $result_stmt = Database::prepare("SELECT `id`, `ip`, `port` FROM `" . TABLE_PANEL_IPSANDPORTS . "` WHERE `id` = :id"); - $result = Database::pexecute_first($result_stmt, array('id' => $id)); - - if (isset($result['id']) - && $result['id'] == $id - ) { - $result_checkdomain_stmt = Database::prepare(" - SELECT `id_domain` as `id` FROM `" . TABLE_DOMAINTOIP . "` WHERE `id_ipandports` = :id" - ); - $result_checkdomain = Database::pexecute_first($result_checkdomain_stmt, array('id' => $id)); - - if ($result_checkdomain['id'] == '') { - if (!in_array($result['id'], explode(',', Settings::Get('system.defaultip')))) { - - $result_sameipotherport_stmt = Database::prepare(" - SELECT `id` FROM `" . TABLE_PANEL_IPSANDPORTS . "` - WHERE `ip` = :ip AND `id` <> :id" - ); - $result_sameipotherport = Database::pexecute_first($result_sameipotherport_stmt, array('id' => $id, 'ip' => $result['ip'])); - - if (($result['ip'] != Settings::Get('system.ipaddress')) - || ($result['ip'] == Settings::Get('system.ipaddress') - && $result_sameipotherport['id'] != '') - ) { - $result_stmt = Database::prepare(" - SELECT `ip`, `port` FROM `" . TABLE_PANEL_IPSANDPORTS . "` - WHERE `id` = :id" - ); - $result = Database::pexecute_first($result_stmt, array('id' => $id)); - - if ($result['ip'] != '') { - - if (isset($_POST['send']) - && $_POST['send'] == 'send' - ) { - $del_stmt = Database::prepare(" - DELETE FROM `" . TABLE_PANEL_IPSANDPORTS . "` - WHERE `id` = :id" - ); - Database::pexecute($del_stmt, array('id' => $id)); - - // also, remove connections to domains (multi-stack) - $del_stmt = Database::prepare(" - DELETE FROM `".TABLE_DOMAINTOIP."` WHERE `id_ipandports` = :id" - ); - Database::pexecute($del_stmt, array('id' => $id)); - - $log->logAction(ADM_ACTION, LOG_WARNING, "deleted IP/port '" . $result['ip'] . ":" . $result['port'] . "'"); - inserttask('1'); - // Using nameserver, insert a task which rebuilds the server config - inserttask('4'); - - redirectTo($filename, array('page' => $page, 's' => $s)); - - } else { - ask_yesno('admin_ip_reallydelete', $filename, array('id' => $id, 'page' => $page, 'action' => $action), $result['ip'] . ':' . $result['port']); - } - } - } else { - standard_error('cantdeletesystemip'); - } - } else { - standard_error('cantdeletedefaultip'); + } elseif ($action == 'delete' && $id != 0) { + try { + $json_result = IpsAndPorts::getLocal($userinfo, array( + 'id' => $id + ))->get(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); + } + $result = json_decode($json_result, true)['data']; + + if (isset($result['id']) && $result['id'] == $id) { + if (isset($_POST['send']) && $_POST['send'] == 'send') { + + try { + IpsAndPorts::getLocal($userinfo, array( + 'id' => $id + ))->delete(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); } + + redirectTo($filename, array( + 'page' => $page, + 's' => $s + )); } else { - standard_error('ipstillhasdomains'); + ask_yesno('admin_ip_reallydelete', $filename, array( + 'id' => $id, + 'page' => $page, + 'action' => $action + ), $result['ip'] . ':' . $result['port']); } } - - } elseif($action == 'add') { - - if (isset($_POST['send']) - && $_POST['send'] == 'send' - ) { - - $ip = validate_ip($_POST['ip']); - $port = validate($_POST['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', 'myport')); - $listen_statement = isset($_POST['listen_statement']) ? 1 : 0; - $namevirtualhost_statement = isset($_POST['namevirtualhost_statement']) ? 1 : 0; - $vhostcontainer = isset($_POST['vhostcontainer']) ? 1 : 0; - $specialsettings = validate(str_replace("\r\n", "\n", $_POST['specialsettings']), 'specialsettings', '/^[^\0]*$/'); - $vhostcontainer_servername_statement = isset($_POST['vhostcontainer_servername_statement']) ? 1 : 0; - $default_vhostconf_domain = validate(str_replace("\r\n", "\n", $_POST['default_vhostconf_domain']), 'default_vhostconf_domain', '/^[^\0]*$/'); - $docroot = validate($_POST['docroot'], 'docroot'); - - if ((int)Settings::Get('system.use_ssl') == 1) { - $ssl = isset($_POST['ssl']) ? intval($_POST['ssl']) : 0; - $ssl_cert_file = validate($_POST['ssl_cert_file'], 'ssl_cert_file'); - $ssl_key_file = validate($_POST['ssl_key_file'], 'ssl_key_file'); - $ssl_ca_file = validate($_POST['ssl_ca_file'], 'ssl_ca_file'); - $ssl_cert_chainfile = validate($_POST['ssl_cert_chainfile'], 'ssl_cert_chainfile'); - } else { - $ssl = 0; - $ssl_cert_file = ''; - $ssl_key_file = ''; - $ssl_ca_file = ''; - $ssl_cert_chainfile = ''; + } elseif ($action == 'add') { + if (isset($_POST['send']) && $_POST['send'] == 'send') { + try { + IpsAndPorts::getLocal($userinfo, $_POST)->add(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); } - - if ($listen_statement != '1') { - $listen_statement = '0'; - } - - if ($namevirtualhost_statement != '1') { - $namevirtualhost_statement = '0'; - } - - if ($vhostcontainer != '1') { - $vhostcontainer = '0'; - } - - if ($vhostcontainer_servername_statement != '1') { - $vhostcontainer_servername_statement = '0'; - } - - if ($ssl != '1') { - $ssl = '0'; - } - - if ($ssl_cert_file != '') { - $ssl_cert_file = makeCorrectFile($ssl_cert_file); - } - - if ($ssl_key_file != '') { - $ssl_key_file = makeCorrectFile($ssl_key_file); - } - - if ($ssl_ca_file != '') { - $ssl_ca_file = makeCorrectFile($ssl_ca_file); - } - - if ($ssl_cert_chainfile != '') { - $ssl_cert_chainfile = makeCorrectFile($ssl_cert_chainfile); - } - - if (strlen(trim($docroot)) > 0) { - $docroot = makeCorrectDir($docroot); - } else { - $docroot = ''; - } - - $result_checkfordouble_stmt = Database::prepare(" - SELECT `id` FROM `" . TABLE_PANEL_IPSANDPORTS . "` - WHERE `ip` = :ip AND `port` = :port" - ); - $result_checkfordouble = Database::pexecute_first($result_checkfordouble_stmt, array('ip' => $ip, 'port' => $port)); - - if ($result_checkfordouble['id'] != '') { - standard_error('myipnotdouble'); - } else { - $ins_stmt = Database::prepare(" - INSERT INTO `" . TABLE_PANEL_IPSANDPORTS . "` - SET - `ip` = :ip, `port` = :port, `listen_statement` = :ls, - `namevirtualhost_statement` = :nvhs, `vhostcontainer` = :vhc, - `vhostcontainer_servername_statement` = :vhcss, - `specialsettings` = :ss, `ssl` = :ssl, - `ssl_cert_file` = :ssl_cert, `ssl_key_file` = :ssl_key, - `ssl_ca_file` = :ssl_ca, `ssl_cert_chainfile` = :ssl_chain, - `default_vhostconf_domain` = :dvhd, `docroot` = :docroot; - "); - $ins_data = array( - 'ip' => $ip, - 'port' => $port, - 'ls' => $listen_statement, - 'nvhs' => $namevirtualhost_statement, - 'vhc' => $vhostcontainer, - 'vhcss' => $vhostcontainer_servername_statement, - 'ss' => $specialsettings, - 'ssl' => $ssl, - 'ssl_cert' => $ssl_cert_file, - 'ssl_key' => $ssl_key_file, - 'ssl_ca' => $ssl_ca_file, - 'ssl_chain' => $ssl_cert_chainfile, - 'dvhd' => $default_vhostconf_domain, - 'docroot' => $docroot - ); - Database::pexecute($ins_stmt, $ins_data); - - if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { - $ip = '[' . $ip . ']'; - } - - $log->logAction(ADM_ACTION, LOG_WARNING, "added IP/port '" . $ip . ":" . $port . "'"); - inserttask('1'); - // Using nameserver, insert a task which rebuilds the server config - inserttask('4'); - redirectTo($filename, Array('page' => $page, 's' => $s)); - } - + redirectTo($filename, array( + 'page' => $page, + 's' => $s + )); } else { - - $ipsandports_add_data = include_once dirname(__FILE__).'/lib/formfields/admin/ipsandports/formfield.ipsandports_add.php'; + + $ipsandports_add_data = include_once dirname(__FILE__) . '/lib/formfields/admin/ipsandports/formfield.ipsandports_add.php'; $ipsandports_add_form = htmlform::genHTMLForm($ipsandports_add_data); - + $title = $ipsandports_add_data['ipsandports_add']['title']; $image = $ipsandports_add_data['ipsandports_add']['image']; - + eval("echo \"" . getTemplate("ipsandports/ipsandports_add") . "\";"); } - - } elseif($action == 'edit' - && $id != 0 - ) { - $result_stmt = Database::prepare(" - SELECT * FROM `" . TABLE_PANEL_IPSANDPORTS . "` WHERE `id` = :id" - ); - $result = Database::pexecute_first($result_stmt, array('id' => $id)); - + } elseif ($action == 'edit' && $id != 0) { + try { + $json_result = IpsAndPorts::getLocal($userinfo, array( + 'id' => $id + ))->get(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); + } + $result = json_decode($json_result, true)['data']; + if ($result['ip'] != '') { - - if (isset($_POST['send']) - && $_POST['send'] == 'send' - ) { - - $ip = validate_ip($_POST['ip']); - $port = validate($_POST['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', 'myport')); - $listen_statement = isset($_POST['listen_statement']) ? 1 : 0; - $namevirtualhost_statement = isset($_POST['namevirtualhost_statement']) ? 1 : 0; - $vhostcontainer = isset($_POST['vhostcontainer']) ? 1 : 0; - $specialsettings = validate(str_replace("\r\n", "\n", $_POST['specialsettings']), 'specialsettings', '/^[^\0]*$/'); - $vhostcontainer_servername_statement = isset($_POST['vhostcontainer_servername_statement']) ? 1 : 0; - $default_vhostconf_domain = validate(str_replace("\r\n", "\n", $_POST['default_vhostconf_domain']), 'default_vhostconf_domain', '/^[^\0]*$/'); - $docroot = validate($_POST['docroot'], 'docroot'); - - $result_checkfordouble_stmt = Database::prepare(" - SELECT `id` FROM `" . TABLE_PANEL_IPSANDPORTS . "` - WHERE `ip` = :ip AND `port` = :port" - ); - $result_checkfordouble = Database::pexecute_first($result_checkfordouble_stmt, array('ip' => $ip, 'port' => $port)); - - $result_sameipotherport_stmt = Database::prepare(" - SELECT `id` FROM `" . TABLE_PANEL_IPSANDPORTS . "` - WHERE `ip` = :ip AND `id` <> :id" - ); - $result_sameipotherport = Database::pexecute_first($result_sameipotherport_stmt, array('ip' => $ip, 'id' => $id)); - - if ((int)Settings::Get('system.use_ssl') == 1 - && isset($_POST['ssl']) - && $_POST['ssl'] != 0 - ) { - $ssl = 1; - $ssl_cert_file = validate($_POST['ssl_cert_file'], 'ssl_cert_file'); - $ssl_key_file = validate($_POST['ssl_key_file'], 'ssl_key_file'); - $ssl_ca_file = validate($_POST['ssl_ca_file'], 'ssl_ca_file'); - $ssl_cert_chainfile = validate($_POST['ssl_cert_chainfile'], 'ssl_cert_chainfile'); - } else { - $ssl = 0; - $ssl_cert_file = ''; - $ssl_key_file = ''; - $ssl_ca_file = ''; - $ssl_cert_chainfile = ''; - } - - if ($listen_statement != '1') { - $listen_statement = '0'; - } - - if ($namevirtualhost_statement != '1') { - $namevirtualhost_statement = '0'; - } - - if ($vhostcontainer != '1') { - $vhostcontainer = '0'; - } - - if ($vhostcontainer_servername_statement != '1') { - $vhostcontainer_servername_statement = '0'; - } - - if ($ssl != '1') { - $ssl = '0'; - } - - if ($ssl_cert_file != '') { - $ssl_cert_file = makeCorrectFile($ssl_cert_file); - } - - if ($ssl_key_file != '') { - $ssl_key_file = makeCorrectFile($ssl_key_file); - } - - if ($ssl_ca_file != '') { - $ssl_ca_file = makeCorrectFile($ssl_ca_file); - } - - if ($ssl_cert_chainfile != '') { - $ssl_cert_chainfile = makeCorrectFile($ssl_cert_chainfile); - } - - if (strlen(trim($docroot)) > 0) { - $docroot = makeCorrectDir($docroot); - } else { - $docroot = ''; - } - - if ($result['ip'] != $ip - && $result['ip'] == Settings::Get('system.ipaddress') - && $result_sameipotherport['id'] == '' - ) { - standard_error('cantchangesystemip'); - - } elseif($result_checkfordouble['id'] != '' - && $result_checkfordouble['id'] != $id - ) { - standard_error('myipnotdouble'); - - } else { - - $upd_stmt = Database::prepare(" - UPDATE `" . TABLE_PANEL_IPSANDPORTS . "` - SET - `ip` = :ip, `port` = :port, `listen_statement` = :ls, - `namevirtualhost_statement` = :nvhs, `vhostcontainer` = :vhc, - `vhostcontainer_servername_statement` = :vhcss, - `specialsettings` = :ss, `ssl` = :ssl, - `ssl_cert_file` = :ssl_cert, `ssl_key_file` = :ssl_key, - `ssl_ca_file` = :ssl_ca, `ssl_cert_chainfile` = :ssl_chain, - `default_vhostconf_domain` = :dvhd, `docroot` = :docroot - WHERE `id` = :id; - "); - $upd_data = array( - 'ip' => $ip, - 'port' => $port, - 'ls' => $listen_statement, - 'nvhs' => $namevirtualhost_statement, - 'vhc' => $vhostcontainer, - 'vhcss' => $vhostcontainer_servername_statement, - 'ss' => $specialsettings, - 'ssl' => $ssl, - 'ssl_cert' => $ssl_cert_file, - 'ssl_key' => $ssl_key_file, - 'ssl_ca' => $ssl_ca_file, - 'ssl_chain' => $ssl_cert_chainfile, - 'dvhd' => $default_vhostconf_domain, - 'docroot' => $docroot, - 'id' => $id - ); - Database::pexecute($upd_stmt, $upd_data); - - $log->logAction(ADM_ACTION, LOG_WARNING, "changed IP/port from '" . $result['ip'] . ":" . $result['port'] . "' to '" . $ip . ":" . $port . "'"); - inserttask('1'); - - // Using nameserver, insert a task which rebuilds the server config - inserttask('4'); - - redirectTo($filename, Array('page' => $page, 's' => $s)); - } - + + if (isset($_POST['send']) && $_POST['send'] == 'send') { + try { + IpsAndPorts::getLocal($userinfo, $_POST)->update(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); + } + redirectTo($filename, array( + 'page' => $page, + 's' => $s + )); } else { - + $result = htmlentities_array($result); - - $ipsandports_edit_data = include_once dirname(__FILE__).'/lib/formfields/admin/ipsandports/formfield.ipsandports_edit.php'; + + $ipsandports_edit_data = include_once dirname(__FILE__) . '/lib/formfields/admin/ipsandports/formfield.ipsandports_edit.php'; $ipsandports_edit_form = htmlform::genHTMLForm($ipsandports_edit_data); - + $title = $ipsandports_edit_data['ipsandports_edit']['title']; $image = $ipsandports_edit_data['ipsandports_edit']['image']; - + eval("echo \"" . getTemplate("ipsandports/ipsandports_edit") . "\";"); } } diff --git a/lib/classes/api/commands/class.IpsAndPorts.php b/lib/classes/api/commands/class.IpsAndPorts.php new file mode 100644 index 00000000..502d68a1 --- /dev/null +++ b/lib/classes/api/commands/class.IpsAndPorts.php @@ -0,0 +1,393 @@ +isAdmin() && $this->getUserDetail('change_serversettings')) { + $this->logger()->logAction(ADM_ACTION, LOG_NOTICE, "[API] list ips and ports"); + $result_stmt = Database::prepare(" + SELECT * FROM `" . TABLE_PANEL_IPSANDPORTS . "` ORDER BY `ip` ASC, `port` ASC + "); + Database::pexecute($result_stmt); + $result = array(); + while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) { + $result[] = $row; + } + return $this->response(200, "successfull", array( + 'count' => count($result), + 'list' => $result + )); + } + throw new Exception("Not allowed to execute given command.", 403); + } + + public function get() + { + if ($this->isAdmin() && $this->getUserDetail('change_serversettings')) { + $id = $this->getParam('id'); + $this->logger()->logAction(ADM_ACTION, LOG_NOTICE, "[API] get ip and port #" . $id); + $result_stmt = Database::prepare(" + SELECT * FROM `" . TABLE_PANEL_IPSANDPORTS . "` WHERE `id` = :id + "); + $result = Database::pexecute_first($result_stmt, array( + 'id' => $id + ), true, true); + if ($result) { + return $this->response(200, "successfull", $result); + } + throw new Exception("IP/port with id #" . $id . " could not be found"); + } + throw new Exception("Not allowed to execute given command.", 403); + } + + public function add() + { + if ($this->isAdmin() && $this->getUserDetail('change_serversettings')) { + $ip = validate_ip2($this->getParam('ip'), false, 'invalidip', false, false, false, true); + $port = validate($this->getParam('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', + 'myport' + ), array(), true); + $listen_statement = ! empty($this->getParam('listen_statement')) ? 1 : 0; + $namevirtualhost_statement = ! empty($this->getParam('namevirtualhost_statement')) ? 1 : 0; + $vhostcontainer = ! empty($this->getParam('vhostcontainer')) ? 1 : 0; + $specialsettings = validate(str_replace("\r\n", "\n", $this->getParam('specialsettings')), 'specialsettings', '/^[^\0]*$/', '', array(), true); + $vhostcontainer_servername_statement = ! empty($this->getParam('vhostcontainer_servername_statement')) ? 1 : 0; + $default_vhostconf_domain = validate(str_replace("\r\n", "\n", $this->getParam('default_vhostconf_domain')), 'default_vhostconf_domain', '/^[^\0]*$/', '', array(), true); + $docroot = validate($this->getParam('docroot'), 'docroot', '', '', array(), true); + + if ((int) Settings::Get('system.use_ssl') == 1) { + $ssl = ! empty($this->getParam('ssl')) ? intval($this->getParam('ssl')) : 0; + $ssl_cert_file = validate($this->getParam('ssl_cert_file'), 'ssl_cert_file', '', '', array(), true); + $ssl_key_file = validate($this->getParam('ssl_key_file'), 'ssl_key_file', '', '', array(), true); + $ssl_ca_file = validate($this->getParam('ssl_ca_file'), 'ssl_ca_file', '', '', array(), true); + $ssl_cert_chainfile = validate($this->getParam('ssl_cert_chainfile'), 'ssl_cert_chainfile', '', '', array(), true); + } else { + $ssl = 0; + $ssl_cert_file = ''; + $ssl_key_file = ''; + $ssl_ca_file = ''; + $ssl_cert_chainfile = ''; + } + + if ($listen_statement != '1') { + $listen_statement = '0'; + } + + if ($namevirtualhost_statement != '1') { + $namevirtualhost_statement = '0'; + } + + if ($vhostcontainer != '1') { + $vhostcontainer = '0'; + } + + if ($vhostcontainer_servername_statement != '1') { + $vhostcontainer_servername_statement = '0'; + } + + if ($ssl != '1') { + $ssl = '0'; + } + + if ($ssl_cert_file != '') { + $ssl_cert_file = makeCorrectFile($ssl_cert_file); + } + + if ($ssl_key_file != '') { + $ssl_key_file = makeCorrectFile($ssl_key_file); + } + + if ($ssl_ca_file != '') { + $ssl_ca_file = makeCorrectFile($ssl_ca_file); + } + + if ($ssl_cert_chainfile != '') { + $ssl_cert_chainfile = makeCorrectFile($ssl_cert_chainfile); + } + + if (strlen(trim($docroot)) > 0) { + $docroot = makeCorrectDir($docroot); + } else { + $docroot = ''; + } + + $result_checkfordouble_stmt = Database::prepare(" + SELECT `id` FROM `" . TABLE_PANEL_IPSANDPORTS . "` + WHERE `ip` = :ip AND `port` = :port"); + $result_checkfordouble = Database::pexecute_first($result_checkfordouble_stmt, array( + 'ip' => $ip, + 'port' => $port + )); + + if ($result_checkfordouble['id'] != '') { + standard_error('myipnotdouble', '', true); + } + + $ins_stmt = Database::prepare(" + INSERT INTO `" . TABLE_PANEL_IPSANDPORTS . "` + SET + `ip` = :ip, `port` = :port, `listen_statement` = :ls, + `namevirtualhost_statement` = :nvhs, `vhostcontainer` = :vhc, + `vhostcontainer_servername_statement` = :vhcss, + `specialsettings` = :ss, `ssl` = :ssl, + `ssl_cert_file` = :ssl_cert, `ssl_key_file` = :ssl_key, + `ssl_ca_file` = :ssl_ca, `ssl_cert_chainfile` = :ssl_chain, + `default_vhostconf_domain` = :dvhd, `docroot` = :docroot; + "); + $ins_data = array( + 'ip' => $ip, + 'port' => $port, + 'ls' => $listen_statement, + 'nvhs' => $namevirtualhost_statement, + 'vhc' => $vhostcontainer, + 'vhcss' => $vhostcontainer_servername_statement, + 'ss' => $specialsettings, + 'ssl' => $ssl, + 'ssl_cert' => $ssl_cert_file, + 'ssl_key' => $ssl_key_file, + 'ssl_ca' => $ssl_ca_file, + 'ssl_chain' => $ssl_cert_chainfile, + 'dvhd' => $default_vhostconf_domain, + 'docroot' => $docroot + ); + Database::pexecute($ins_stmt, $ins_data); + $ins_data['id'] = Database::lastInsertId(); + + inserttask('1'); + // Using nameserver, insert a task which rebuilds the server config + inserttask('4'); + + if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { + $ip = '[' . $ip . ']'; + } + $this->logger()->logAction(ADM_ACTION, LOG_WARNING, "[API] added IP/port '" . $ip . ":" . $port . "'"); + return $this->response(200, "successfull", $ins_data); + } + throw new Exception("Not allowed to execute given command.", 403); + } + + public function update() + { + if ($this->isAdmin() && $this->getUserDetail('change_serversettings')) { + $id = $this->getParam('id'); + + $result_stmt = Database::prepare(" + SELECT * FROM `" . TABLE_PANEL_IPSANDPORTS . "` WHERE `id` = :id + "); + $result = Database::pexecute_first($result_stmt, array( + 'id' => $id + ), true, true); + + $ip = validate_ip2($this->getParam('ip'), false, 'invalidip', false, false, false, true); + $port = validate($this->getParam('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', + 'myport' + ), array(), true); + $listen_statement = ! empty($this->getParam('listen_statement')) ? 1 : 0; + $namevirtualhost_statement = ! empty($this->getParam('namevirtualhost_statement')) ? 1 : 0; + $vhostcontainer = ! empty($this->getParam('vhostcontainer')) ? 1 : 0; + $specialsettings = validate(str_replace("\r\n", "\n", $this->getParam('specialsettings')), 'specialsettings', '/^[^\0]*$/', '', array(), true); + $vhostcontainer_servername_statement = ! empty($this->getParam('vhostcontainer_servername_statement')) ? 1 : 0; + $default_vhostconf_domain = validate(str_replace("\r\n", "\n", $this->getParam('default_vhostconf_domain')), 'default_vhostconf_domain', '/^[^\0]*$/', '', array(), true); + $docroot = validate($this->getParam('docroot'), 'docroot', '', '', array(), true); + + if ((int) Settings::Get('system.use_ssl') == 1) { + $ssl = ! empty($this->getParam('ssl')) ? intval($this->getParam('ssl')) : 0; + $ssl_cert_file = validate($this->getParam('ssl_cert_file'), 'ssl_cert_file', '', '', array(), true); + $ssl_key_file = validate($this->getParam('ssl_key_file'), 'ssl_key_file', '', '', array(), true); + $ssl_ca_file = validate($this->getParam('ssl_ca_file'), 'ssl_ca_file', '', '', array(), true); + $ssl_cert_chainfile = validate($this->getParam('ssl_cert_chainfile'), 'ssl_cert_chainfile', '', '', array(), true); + } else { + $ssl = 0; + $ssl_cert_file = ''; + $ssl_key_file = ''; + $ssl_ca_file = ''; + $ssl_cert_chainfile = ''; + } + + $result_checkfordouble_stmt = Database::prepare(" + SELECT `id` FROM `" . TABLE_PANEL_IPSANDPORTS . "` + WHERE `ip` = :ip AND `port` = :port + "); + $result_checkfordouble = Database::pexecute_first($result_checkfordouble_stmt, array( + 'ip' => $ip, + 'port' => $port + )); + + $result_sameipotherport_stmt = Database::prepare(" + SELECT `id` FROM `" . TABLE_PANEL_IPSANDPORTS . "` + WHERE `ip` = :ip AND `id` <> :id + "); + $result_sameipotherport = Database::pexecute_first($result_sameipotherport_stmt, array( + 'ip' => $ip, + 'id' => $id + )); + + if ($listen_statement != '1') { + $listen_statement = '0'; + } + + if ($namevirtualhost_statement != '1') { + $namevirtualhost_statement = '0'; + } + + if ($vhostcontainer != '1') { + $vhostcontainer = '0'; + } + + if ($vhostcontainer_servername_statement != '1') { + $vhostcontainer_servername_statement = '0'; + } + + if ($ssl != '1') { + $ssl = '0'; + } + + if ($ssl_cert_file != '') { + $ssl_cert_file = makeCorrectFile($ssl_cert_file); + } + + if ($ssl_key_file != '') { + $ssl_key_file = makeCorrectFile($ssl_key_file); + } + + if ($ssl_ca_file != '') { + $ssl_ca_file = makeCorrectFile($ssl_ca_file); + } + + if ($ssl_cert_chainfile != '') { + $ssl_cert_chainfile = makeCorrectFile($ssl_cert_chainfile); + } + + if (strlen(trim($docroot)) > 0) { + $docroot = makeCorrectDir($docroot); + } else { + $docroot = ''; + } + + if ($result['ip'] != $ip && $result['ip'] == Settings::Get('system.ipaddress') && $result_sameipotherport['id'] == '') { + standard_error('cantchangesystemip', '', true); + } elseif ($result_checkfordouble['id'] != '' && $result_checkfordouble['id'] != $id) { + standard_error('myipnotdouble', '', true); + } else { + + $upd_stmt = Database::prepare(" + UPDATE `" . TABLE_PANEL_IPSANDPORTS . "` + SET + `ip` = :ip, `port` = :port, `listen_statement` = :ls, + `namevirtualhost_statement` = :nvhs, `vhostcontainer` = :vhc, + `vhostcontainer_servername_statement` = :vhcss, + `specialsettings` = :ss, `ssl` = :ssl, + `ssl_cert_file` = :ssl_cert, `ssl_key_file` = :ssl_key, + `ssl_ca_file` = :ssl_ca, `ssl_cert_chainfile` = :ssl_chain, + `default_vhostconf_domain` = :dvhd, `docroot` = :docroot + WHERE `id` = :id; + "); + $upd_data = array( + 'ip' => $ip, + 'port' => $port, + 'ls' => $listen_statement, + 'nvhs' => $namevirtualhost_statement, + 'vhc' => $vhostcontainer, + 'vhcss' => $vhostcontainer_servername_statement, + 'ss' => $specialsettings, + 'ssl' => $ssl, + 'ssl_cert' => $ssl_cert_file, + 'ssl_key' => $ssl_key_file, + 'ssl_ca' => $ssl_ca_file, + 'ssl_chain' => $ssl_cert_chainfile, + 'dvhd' => $default_vhostconf_domain, + 'docroot' => $docroot, + 'id' => $id + ); + Database::pexecute($upd_stmt, $upd_data); + + inserttask('1'); + // Using nameserver, insert a task which rebuilds the server config + inserttask('4'); + + $this->logger()->logAction(ADM_ACTION, LOG_WARNING, "[API] changed IP/port from '" . $result['ip'] . ":" . $result['port'] . "' to '" . $ip . ":" . $port . "'"); + return $this->response(200, "successfull", $upd_data); + } + } + throw new Exception("Not allowed to execute given command.", 403); + } + + public function delete() + { + if ($this->isAdmin() && $this->getUserDetail('change_serversettings')) { + $id = $this->getParam('id'); + + $result_stmt = Database::prepare(" + SELECT * FROM `" . TABLE_PANEL_IPSANDPORTS . "` WHERE `id` = :id + "); + $result = Database::pexecute_first($result_stmt, array( + 'id' => $id + ), true, true); + + $result_checkdomain_stmt = Database::prepare(" + SELECT `id_domain` as `id` FROM `" . TABLE_DOMAINTOIP . "` WHERE `id_ipandports` = :id + "); + $result_checkdomain = Database::pexecute_first($result_checkdomain_stmt, array( + 'id' => $id + ), true, true); + + if ($result_checkdomain['id'] == '') { + if (! in_array($result['id'], explode(',', Settings::Get('system.defaultip')))) { + + $result_sameipotherport_stmt = Database::prepare(" + SELECT `id` FROM `" . TABLE_PANEL_IPSANDPORTS . "` + WHERE `ip` = :ip AND `id` <> :id"); + $result_sameipotherport = Database::pexecute_first($result_sameipotherport_stmt, array( + 'id' => $id, + 'ip' => $result['ip'] + )); + + if (($result['ip'] != Settings::Get('system.ipaddress')) || ($result['ip'] == Settings::Get('system.ipaddress') && $result_sameipotherport['id'] != '')) { + $result_stmt = Database::prepare(" + SELECT `ip`, `port` FROM `" . TABLE_PANEL_IPSANDPORTS . "` + WHERE `id` = :id"); + $result = Database::pexecute_first($result_stmt, array( + 'id' => $id + )); + if ($result['ip'] != '') { + + $del_stmt = Database::prepare(" + DELETE FROM `" . TABLE_PANEL_IPSANDPORTS . "` + WHERE `id` = :id + "); + Database::pexecute($del_stmt, array( + 'id' => $id + )); + + // also, remove connections to domains (multi-stack) + $del_stmt = Database::prepare(" + DELETE FROM `" . TABLE_DOMAINTOIP . "` WHERE `id_ipandports` = :id + "); + Database::pexecute($del_stmt, array( + 'id' => $id + )); + + inserttask('1'); + // Using nameserver, insert a task which rebuilds the server config + inserttask('4'); + + $this->logger()->logAction(ADM_ACTION, LOG_WARNING, "[API] deleted IP/port '" . $result['ip'] . ":" . $result['port'] . "'"); + return $this->response(200, "successfull", $result); + } + } else { + standard_error('cantdeletesystemip', '', true); + } + } else { + standard_error('cantdeletedefaultip', '', true); + } + } else { + standard_error('ipstillhasdomains', '', true); + } + } + throw new Exception("Not allowed to execute given command.", 403); + } +} From d068477a935d4782225fdbfaeb2468b15e4524e9 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 15 Feb 2018 07:54:21 +0100 Subject: [PATCH 0427/1335] set version to 0.10.0 Signed-off-by: Michael Kaufmann (d00p) --- install/froxlor.sql | 5 ++-- .../updates/froxlor/0.9/update_0.10.inc.php | 28 +++++++++++++++++++ .../updates/froxlor/0.9/update_0.9.inc.php | 26 +++++++++++++++++ install/updatesql.php | 1 + lib/tables.inc.php | 1 + lib/version.inc.php | 4 +-- 6 files changed, 61 insertions(+), 4 deletions(-) create mode 100644 install/updates/froxlor/0.9/update_0.10.inc.php diff --git a/install/froxlor.sql b/install/froxlor.sql index 5a262c55..6a8534a8 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -656,6 +656,7 @@ opcache.interned_strings_buffer'), ('system', 'nssextrausers', '0'), ('system', 'disable_le_selfcheck', '0'), ('system', 'ssl_protocols', 'TLSv1,TLSv1.2'), + ('api', 'enabled', '0'), ('panel', 'decimal_places', '4'), ('panel', 'adminmail', 'admin@SERVERNAME'), ('panel', 'phpmyadmin_url', ''), @@ -687,8 +688,8 @@ opcache.interned_strings_buffer'), ('panel', 'password_special_char_required', '0'), ('panel', 'password_special_char', '!?<>§$%+#=@'), ('panel', 'customer_hide_options', ''), - ('panel', 'version', '0.9.39.5'), - ('panel', 'db_version', '201802130'); + ('panel', 'version', '0.10.0'), + ('panel', 'db_version', '201802150'); DROP TABLE IF EXISTS `panel_tasks`; diff --git a/install/updates/froxlor/0.9/update_0.10.inc.php b/install/updates/froxlor/0.9/update_0.10.inc.php new file mode 100644 index 00000000..7e796501 --- /dev/null +++ b/install/updates/froxlor/0.9/update_0.10.inc.php @@ -0,0 +1,28 @@ + (2010-) + * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt + * @package Install + * + */ +if (!defined('_CRON_UPDATE')) { + if (! defined('AREA') || (defined('AREA') && AREA != 'admin') || ! isset($userinfo['loginname']) || (isset($userinfo['loginname']) && $userinfo['loginname'] == '')) { + header('Location: ../../../../index.php'); + exit(); + } +} + +if (isFroxlorVersion('0.9.39.5')) { + + showUpdateStep("Updating from 0.9.39.5 to 0.10.0", false); + updateToVersion('0.10.0'); +} diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index 0abab0fe..2656d31d 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -3939,3 +3939,29 @@ if (isFroxlorVersion('0.9.39.4')) { showUpdateStep("Updating from 0.9.39.4 to 0.9.39.5", false); updateToVersion('0.9.39.5'); } + +if (isDatabaseVersion('201802130')) { + + showUpdateStep("Adding new api keys table"); + Database::query("DROP TABLE IF EXISTS `api_keys`;"); + $sql = "CREATE TABLE `api_keys` ( + `id` int(11) NOT NULL auto_increment, + `adminid` int(11) NOT NULL default '0', + `customerid` int(11) NOT NULL default '0', + `apikey` varchar(500) NOT NULL default '', + `secret` varchar(500) NOT NULL default '', + `allowed_from` text NOT NULL, + `valid_until` int(15) NOT NULL default '0', + PRIMARY KEY (id), + KEY adminid (adminid), + KEY customerid (customerid) + ) ENGINE=MyISAM CHARSET=utf8 COLLATE=utf8_general_ci;"; + Database::query($sql); + lastStepStatus(0); + + showUpdateStep("Adding new api settings"); + Settings::AddNew('api.enabled', 0); + lastStepStatus(0); + + updateToDbVersion('201802150'); +} diff --git a/install/updatesql.php b/install/updatesql.php index 4cc7417f..55d954f4 100644 --- a/install/updatesql.php +++ b/install/updatesql.php @@ -55,6 +55,7 @@ if(!isFroxlor()) { if (isFroxlor()) { include_once (makeCorrectFile(dirname(__FILE__).'/updates/froxlor/0.9/update_0.9.inc.php')); + include_once (makeCorrectFile(dirname(__FILE__).'/updates/froxlor/0.10/update_0.10.inc.php')); // Check Froxlor - database integrity (only happens after all updates are done, so we know the db-layout is okay) showUpdateStep("Checking database integrity"); diff --git a/lib/tables.inc.php b/lib/tables.inc.php index 207612f0..2a6dc924 100644 --- a/lib/tables.inc.php +++ b/lib/tables.inc.php @@ -53,5 +53,6 @@ define('TABLE_DOMAINTOIP', 'panel_domaintoip'); define('TABLE_DOMAIN_DNS', 'domain_dns_entries'); define('TABLE_PANEL_FPMDAEMONS', 'panel_fpmdaemons'); define('TABLE_PANEL_PLANS', 'panel_plans'); +define('TABLE_API_KEYS', 'api_keys'); require dirname(__FILE__).'/version.inc.php'; diff --git a/lib/version.inc.php b/lib/version.inc.php index 49935fb7..8870fe02 100644 --- a/lib/version.inc.php +++ b/lib/version.inc.php @@ -16,10 +16,10 @@ */ // Main version variable -$version = '0.9.39.5'; +$version = '0.10.0'; // Database version (YYYYMMDDC where C is a daily counter) -$dbversion = '201802130'; +$dbversion = '201802150'; // Distribution branding-tag (used for Debian etc.) $branding = ''; From 2c1f76e6a44c665e37cce5b06f031564e4a502f7 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 15 Feb 2018 07:56:27 +0100 Subject: [PATCH 0428/1335] definetly require json extension now Signed-off-by: Michael Kaufmann (d00p) --- install/lib/class.FroxlorInstall.php | 19 ++++++++++--------- install/lng/english.lng.php | 1 - install/lng/german.lng.php | 1 - 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/install/lib/class.FroxlorInstall.php b/install/lib/class.FroxlorInstall.php index 128a7104..757af606 100644 --- a/install/lib/class.FroxlorInstall.php +++ b/install/lib/class.FroxlorInstall.php @@ -1013,7 +1013,17 @@ class FroxlorInstall } else { $content .= $this->_status_message('green', $this->_lng['requirements']['installed']); } + + // check for json extension + $content .= $this->_status_message('begin', $this->_lng['requirements']['phpjson']); + if (! extension_loaded('json')) { + $content .= $this->_status_message('red', $this->_lng['requirements']['notinstalled']); + $_die = true; + } else { + $content .= $this->_status_message('green', $this->_lng['requirements']['installed']); + } + // check for bcmath extension $content .= $this->_status_message('begin', $this->_lng['requirements']['phpbcmath']); @@ -1032,15 +1042,6 @@ class FroxlorInstall $content .= $this->_status_message('green', $this->_lng['requirements']['installed']); } - // check for json extension - $content .= $this->_status_message('begin', $this->_lng['requirements']['phpjson']); - - if (! extension_loaded('json')) { - $content .= $this->_status_message('orange', $this->_lng['requirements']['notinstalled'] . "
    " . $this->_lng['requirements']['jsondescription']); - } else { - $content .= $this->_status_message('green', $this->_lng['requirements']['installed']); - } - // check for open_basedir $content .= $this->_status_message('begin', $this->_lng['requirements']['openbasedir']); $php_ob = @ini_get("open_basedir"); diff --git a/install/lng/english.lng.php b/install/lng/english.lng.php index 8651bb83..a4cea2dc 100644 --- a/install/lng/english.lng.php +++ b/install/lng/english.lng.php @@ -38,7 +38,6 @@ $lng['requirements']['phpzip'] = 'PHP zip-extension...'; $lng['requirements']['phpjson'] = 'PHP json-extension...'; $lng['requirements']['bcmathdescription'] = 'Traffic-calculation related functions will not work correctly!'; $lng['requirements']['zipdescription'] = 'The auto-update feature requires the zip extension.'; -$lng['requirements']['jsondescription'] = 'The settings import/export feature requires the json extension.'; $lng['requirements']['openbasedir'] = 'open_basedir...'; $lng['requirements']['openbasedirenabled'] = 'Froxlor will not work properly with open_basedir enabled. Please disable open_basedir for Froxlor in the coresponding php.ini'; $lng['requirements']['diedbecauseofrequirements'] = 'Cannot install Froxlor without these requirements! Try to fix them and retry.'; diff --git a/install/lng/german.lng.php b/install/lng/german.lng.php index d18e94c0..4e4638db 100644 --- a/install/lng/german.lng.php +++ b/install/lng/german.lng.php @@ -38,7 +38,6 @@ $lng['requirements']['phpzip'] = 'PHP zip-Erweiterung...'; $lng['requirements']['phpjson'] = 'PHP json-Erweiterung...'; $lng['requirements']['bcmathdescription'] = 'Traffic-Berechnungs bezogene Funktionen stehen nicht vollständig zur Verfügung!'; $lng['requirements']['zipdescription'] = 'Die Auto-Update Funktion benötigt die zip Erweiterung.'; -$lng['requirements']['jsondescription'] = 'Die Einstellungen Import/Export Funktion benötigt die json Erweiterung.'; $lng['requirements']['openbasedir'] = 'open_basedir genutzt wird...'; $lng['requirements']['openbasedirenabled'] = 'Froxlor wird mit aktiviertem open_basedir nicht vollständig funktionieren. Bitte deaktivieren Sie open_basedir für Froxlor in der entsprechenden php.ini'; $lng['requirements']['diedbecauseofrequirements'] = 'Kann Froxlor ohne diese Voraussetzungen nicht installieren! Beheben Sie die angezeigten Probleme und versuchen Sie es erneut.'; From a82d5cf7642d70935d6b98a6287b95cccf228e20 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 15 Feb 2018 11:37:38 +0100 Subject: [PATCH 0429/1335] minor fixes Signed-off-by: Michael Kaufmann (d00p) --- api.php | 7 +--- lib/classes/api/abstract.ApiCommand.php | 40 ++++++++++++++++--- .../api/commands/class.IpsAndPorts.php | 12 +++--- 3 files changed, 42 insertions(+), 17 deletions(-) diff --git a/api.php b/api.php index 42bfaae4..c792d18b 100644 --- a/api.php +++ b/api.php @@ -13,10 +13,7 @@ if (Settings::Get('api.enabled') != 1) { header("Content-Type:application/json"); // get our request -$request = isset($_GET['request']) ? $_GET['request'] : null; -if (empty($request)) { - $request = isset($_POST['request']) ? $_POST['request'] : null; -} +$request = @file_get_contents('php://input'); // check if present if (empty($request)) { @@ -63,7 +60,7 @@ function json_response($status, $status_message, $data = null) $response['status_message'] = $status_message; $response['data'] = $data; - $json_response = json_encode($response, JSON_PRETTY_PRINT); + $json_response = json_encode($response, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT); echo $json_response; exit(); } diff --git a/lib/classes/api/abstract.ApiCommand.php b/lib/classes/api/abstract.ApiCommand.php index 43a5b688..dba62676 100644 --- a/lib/classes/api/abstract.ApiCommand.php +++ b/lib/classes/api/abstract.ApiCommand.php @@ -52,20 +52,50 @@ abstract class ApiCommand return (isset($this->user_data[$detail]) ? $this->user_data[$detail] : null); } + /** + * return user-data array + * + * @return array + */ + protected function getUserData() + { + return $this->user_data; + } + /** * receive field from parameter-list * * @param string $param - * + * @param mixed $default + * set if param is not found + * * @throws Exception * @return mixed */ - protected function getParam($param = null) + protected function getParam($param = null, $default = null) { if (isset($this->cmd_params[$param])) { return $this->cmd_params[$param]; } - return null; + return $default; + } + + /** + * update value of parameter + * + * @param string $param + * @param mixed $value + * + * @throws Exception + * @return boolean + */ + protected function updateParam($param, $value = null) + { + if (isset($this->cmd_params[$param])) { + $this->cmd_params[$param] = $value; + return true; + } + throw new Exception("Unable to update parameter '" . $param . "' as it does not exist", 500); } /** @@ -86,7 +116,7 @@ abstract class ApiCommand $response['status_message'] = $status_message; $response['data'] = $data; - $json_response = json_encode($response, JSON_PRETTY_PRINT); + $json_response = json_encode($response, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT); return $json_response; } @@ -95,7 +125,7 @@ abstract class ApiCommand public abstract function get(); public abstract function add(); - + public abstract function update(); public abstract function delete(); diff --git a/lib/classes/api/commands/class.IpsAndPorts.php b/lib/classes/api/commands/class.IpsAndPorts.php index 502d68a1..7aa13794 100644 --- a/lib/classes/api/commands/class.IpsAndPorts.php +++ b/lib/classes/api/commands/class.IpsAndPorts.php @@ -321,16 +321,14 @@ class IpsAndPorts extends ApiCommand if ($this->isAdmin() && $this->getUserDetail('change_serversettings')) { $id = $this->getParam('id'); - $result_stmt = Database::prepare(" - SELECT * FROM `" . TABLE_PANEL_IPSANDPORTS . "` WHERE `id` = :id - "); - $result = Database::pexecute_first($result_stmt, array( + $json_result = IpsAndPorts::getLocal($this->getUserData(), array( 'id' => $id - ), true, true); + ))->get(); + $result = json_decode($json_result, true)['data']; $result_checkdomain_stmt = Database::prepare(" - SELECT `id_domain` as `id` FROM `" . TABLE_DOMAINTOIP . "` WHERE `id_ipandports` = :id - "); + SELECT `id_domain` as `id` FROM `" . TABLE_DOMAINTOIP . "` WHERE `id_ipandports` = :id + "); $result_checkdomain = Database::pexecute_first($result_checkdomain_stmt, array( 'id' => $id ), true, true); From 0fc2fbaf09d76bc202584f1457e1807e4951a500 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 15 Feb 2018 12:59:34 +0100 Subject: [PATCH 0430/1335] add language strings (english only currently) Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/api_includes.inc.php | 35 ++++++++++ .../api/commands/class.IpsAndPorts.php | 68 +++++++++---------- .../output/function.standard_error.php | 2 +- 3 files changed, 70 insertions(+), 35 deletions(-) diff --git a/lib/classes/api/api_includes.inc.php b/lib/classes/api/api_includes.inc.php index 09b3cd84..322502ef 100644 --- a/lib/classes/api/api_includes.inc.php +++ b/lib/classes/api/api_includes.inc.php @@ -4,3 +4,38 @@ if (! defined('FROXLOR_INSTALL_DIR')) { require_once FROXLOR_INSTALL_DIR . '/lib/tables.inc.php'; require_once FROXLOR_INSTALL_DIR . '/lib/functions.php'; } + +// query the whole table +$result_stmt = Database::query("SELECT * FROM `" . TABLE_PANEL_LANGUAGE . "`"); + +$langs = array(); +// presort languages +while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) { + $langs[$row['language']][] = $row; + // check for row[iso] cause older froxlor + // versions didn't have that and it will + // lead to a lot of undfined variables + // before the admin can even update + if (isset($row['iso'])) { + $iso[$row['iso']] = $row['language']; + } +} + +// set default language before anything else to +// ensure that we can display messages +$language = Settings::Get('panel.standardlanguage'); + +// include every english language file we can get +foreach ($langs['English'] as $key => $value) { + include_once makeSecurePath($value['file']); +} + +// now include the selected language if its not english +if ($language != 'English') { + foreach ($langs[$language] as $key => $value) { + include_once makeSecurePath($value['file']); + } +} + +// last but not least include language references file +include_once makeSecurePath('lng/lng_references.php'); diff --git a/lib/classes/api/commands/class.IpsAndPorts.php b/lib/classes/api/commands/class.IpsAndPorts.php index 7aa13794..309f15b2 100644 --- a/lib/classes/api/commands/class.IpsAndPorts.php +++ b/lib/classes/api/commands/class.IpsAndPorts.php @@ -175,31 +175,31 @@ class IpsAndPorts extends ApiCommand $id = $this->getParam('id'); $result_stmt = Database::prepare(" - SELECT * FROM `" . TABLE_PANEL_IPSANDPORTS . "` WHERE `id` = :id - "); + SELECT * FROM `" . TABLE_PANEL_IPSANDPORTS . "` WHERE `id` = :id + "); $result = Database::pexecute_first($result_stmt, array( 'id' => $id ), true, true); - $ip = validate_ip2($this->getParam('ip'), false, 'invalidip', false, false, false, true); - $port = validate($this->getParam('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( + $ip = validate_ip2($this->getParam('ip', $result['ip']), false, 'invalidip', false, false, false, true); + $port = validate($this->getParam('port', $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', 'myport' ), array(), true); - $listen_statement = ! empty($this->getParam('listen_statement')) ? 1 : 0; - $namevirtualhost_statement = ! empty($this->getParam('namevirtualhost_statement')) ? 1 : 0; - $vhostcontainer = ! empty($this->getParam('vhostcontainer')) ? 1 : 0; - $specialsettings = validate(str_replace("\r\n", "\n", $this->getParam('specialsettings')), 'specialsettings', '/^[^\0]*$/', '', array(), true); - $vhostcontainer_servername_statement = ! empty($this->getParam('vhostcontainer_servername_statement')) ? 1 : 0; - $default_vhostconf_domain = validate(str_replace("\r\n", "\n", $this->getParam('default_vhostconf_domain')), 'default_vhostconf_domain', '/^[^\0]*$/', '', array(), true); - $docroot = validate($this->getParam('docroot'), 'docroot', '', '', array(), true); + $listen_statement = $this->getParam('listen_statement', $result['listen_statement']); + $namevirtualhost_statement = $this->getParam('namevirtualhost_statement', $result['namevirtualhost_statement']); + $vhostcontainer = $this->getParam('vhostcontainer', $result['vhostcontainer']); + $specialsettings = validate(str_replace("\r\n", "\n", $this->getParam('specialsettings', $result['specialsettings'])), 'specialsettings', '/^[^\0]*$/', '', array(), true); + $vhostcontainer_servername_statement = $this->getParam('vhostcontainer_servername_statement', $result['vhostcontainer_servername_statement']); + $default_vhostconf_domain = validate(str_replace("\r\n", "\n", $this->getParam('default_vhostconf_domain', $result['default_vhostconf_domain'])), 'default_vhostconf_domain', '/^[^\0]*$/', '', array(), true); + $docroot = validate($this->getParam('docroot', $result['docroot']), 'docroot', '', '', array(), true); if ((int) Settings::Get('system.use_ssl') == 1) { - $ssl = ! empty($this->getParam('ssl')) ? intval($this->getParam('ssl')) : 0; - $ssl_cert_file = validate($this->getParam('ssl_cert_file'), 'ssl_cert_file', '', '', array(), true); - $ssl_key_file = validate($this->getParam('ssl_key_file'), 'ssl_key_file', '', '', array(), true); - $ssl_ca_file = validate($this->getParam('ssl_ca_file'), 'ssl_ca_file', '', '', array(), true); - $ssl_cert_chainfile = validate($this->getParam('ssl_cert_chainfile'), 'ssl_cert_chainfile', '', '', array(), true); + $ssl = $this->getParam('ssl', $result['ssl']); + $ssl_cert_file = validate($this->getParam('ssl_cert_file', $result['ssl_cert_file']), 'ssl_cert_file', '', '', array(), true); + $ssl_key_file = validate($this->getParam('ssl_key_file', $result['ssl_key_file']), 'ssl_key_file', '', '', array(), true); + $ssl_ca_file = validate($this->getParam('ssl_ca_file', $result['ssl_ca_file']), 'ssl_ca_file', '', '', array(), true); + $ssl_cert_chainfile = validate($this->getParam('ssl_cert_chainfile', $result['ssl_cert_chainfile']), 'ssl_cert_chainfile', '', '', array(), true); } else { $ssl = 0; $ssl_cert_file = ''; @@ -209,22 +209,22 @@ class IpsAndPorts extends ApiCommand } $result_checkfordouble_stmt = Database::prepare(" - SELECT `id` FROM `" . TABLE_PANEL_IPSANDPORTS . "` - WHERE `ip` = :ip AND `port` = :port - "); + SELECT `id` FROM `" . TABLE_PANEL_IPSANDPORTS . "` + WHERE `ip` = :ip AND `port` = :port + "); $result_checkfordouble = Database::pexecute_first($result_checkfordouble_stmt, array( 'ip' => $ip, 'port' => $port )); $result_sameipotherport_stmt = Database::prepare(" - SELECT `id` FROM `" . TABLE_PANEL_IPSANDPORTS . "` - WHERE `ip` = :ip AND `id` <> :id - "); + SELECT `id` FROM `" . TABLE_PANEL_IPSANDPORTS . "` + WHERE `ip` = :ip AND `id` <> :id + "); $result_sameipotherport = Database::pexecute_first($result_sameipotherport_stmt, array( 'ip' => $ip, 'id' => $id - )); + ), true, true); if ($listen_statement != '1') { $listen_statement = '0'; @@ -275,17 +275,17 @@ class IpsAndPorts extends ApiCommand } else { $upd_stmt = Database::prepare(" - UPDATE `" . TABLE_PANEL_IPSANDPORTS . "` - SET - `ip` = :ip, `port` = :port, `listen_statement` = :ls, - `namevirtualhost_statement` = :nvhs, `vhostcontainer` = :vhc, - `vhostcontainer_servername_statement` = :vhcss, - `specialsettings` = :ss, `ssl` = :ssl, - `ssl_cert_file` = :ssl_cert, `ssl_key_file` = :ssl_key, - `ssl_ca_file` = :ssl_ca, `ssl_cert_chainfile` = :ssl_chain, - `default_vhostconf_domain` = :dvhd, `docroot` = :docroot - WHERE `id` = :id; - "); + UPDATE `" . TABLE_PANEL_IPSANDPORTS . "` + SET + `ip` = :ip, `port` = :port, `listen_statement` = :ls, + `namevirtualhost_statement` = :nvhs, `vhostcontainer` = :vhc, + `vhostcontainer_servername_statement` = :vhcss, + `specialsettings` = :ss, `ssl` = :ssl, + `ssl_cert_file` = :ssl_cert, `ssl_key_file` = :ssl_key, + `ssl_ca_file` = :ssl_ca, `ssl_cert_chainfile` = :ssl_chain, + `default_vhostconf_domain` = :dvhd, `docroot` = :docroot + WHERE `id` = :id; + "); $upd_data = array( 'ip' => $ip, 'port' => $port, diff --git a/lib/functions/output/function.standard_error.php b/lib/functions/output/function.standard_error.php index 9e8bd7fb..9bcb3397 100644 --- a/lib/functions/output/function.standard_error.php +++ b/lib/functions/output/function.standard_error.php @@ -61,7 +61,7 @@ function standard_error($errors = '', $replacer = '', $throw_exception = false) } if ($throw_exception) { - throw new Exception($error); + throw new Exception($error, 400); } eval("echo \"" . getTemplate('misc/error', '1') . "\";"); exit; From df5fb963c16e5365fcea32d73df818abb3f5c385 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 15 Feb 2018 13:03:30 +0100 Subject: [PATCH 0431/1335] make language strings the language the user uses Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/abstract.ApiCommand.php | 37 +++++++++++++++++++++++++ lib/classes/api/api_includes.inc.php | 35 +---------------------- 2 files changed, 38 insertions(+), 34 deletions(-) diff --git a/lib/classes/api/abstract.ApiCommand.php b/lib/classes/api/abstract.ApiCommand.php index dba62676..d11347fc 100644 --- a/lib/classes/api/abstract.ApiCommand.php +++ b/lib/classes/api/abstract.ApiCommand.php @@ -13,6 +13,8 @@ abstract class ApiCommand public function __construct($header = null, $params = null, $userinfo = null) { + global $lng; + $this->cmd_params = $params; if (! empty($header)) { $this->readUserData($header); @@ -23,6 +25,41 @@ abstract class ApiCommand throw new Exception("Invalid user data", 500); } $this->logger = FroxlorLogger::getInstanceOf($this->user_data); + + // query the whole table + $result_stmt = Database::query("SELECT * FROM `" . TABLE_PANEL_LANGUAGE . "`"); + + $langs = array(); + // presort languages + while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) { + $langs[$row['language']][] = $row; + } + + // set default language before anything else to + // ensure that we can display messages + $language = Settings::Get('panel.standardlanguage'); + + if (isset($this->user_data['language']) && isset($languages[$this->user_data['language']])) { + // default: use language from session, #277 + $language = $this->user_data['language']; + } elseif (isset($this->user_data['def_language'])) { + $language = $this->user_data['def_language']; + } + + // include every english language file we can get + foreach ($langs['English'] as $key => $value) { + include_once makeSecurePath($value['file']); + } + + // now include the selected language if its not english + if ($language != 'English') { + foreach ($langs[$language] as $key => $value) { + include_once makeSecurePath($value['file']); + } + } + + // last but not least include language references file + include_once makeSecurePath(FROXLOR_INSTALL_DIR . '/lng/lng_references.php'); } public static function getLocal($userinfo = null, $params = null) diff --git a/lib/classes/api/api_includes.inc.php b/lib/classes/api/api_includes.inc.php index 322502ef..0e0eb0ad 100644 --- a/lib/classes/api/api_includes.inc.php +++ b/lib/classes/api/api_includes.inc.php @@ -5,37 +5,4 @@ if (! defined('FROXLOR_INSTALL_DIR')) { require_once FROXLOR_INSTALL_DIR . '/lib/functions.php'; } -// query the whole table -$result_stmt = Database::query("SELECT * FROM `" . TABLE_PANEL_LANGUAGE . "`"); - -$langs = array(); -// presort languages -while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) { - $langs[$row['language']][] = $row; - // check for row[iso] cause older froxlor - // versions didn't have that and it will - // lead to a lot of undfined variables - // before the admin can even update - if (isset($row['iso'])) { - $iso[$row['iso']] = $row['language']; - } -} - -// set default language before anything else to -// ensure that we can display messages -$language = Settings::Get('panel.standardlanguage'); - -// include every english language file we can get -foreach ($langs['English'] as $key => $value) { - include_once makeSecurePath($value['file']); -} - -// now include the selected language if its not english -if ($language != 'English') { - foreach ($langs[$language] as $key => $value) { - include_once makeSecurePath($value['file']); - } -} - -// last but not least include language references file -include_once makeSecurePath('lng/lng_references.php'); +$lng = array(); From 81602f17bec500db6ac6e05abd34f1a447ae062c Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 15 Feb 2018 14:44:48 +0100 Subject: [PATCH 0432/1335] add Domains api module Signed-off-by: Michael Kaufmann (d00p) --- admin_domains.php | 2125 +++----------------- lib/classes/api/commands/class.Domains.php | 1635 +++++++++++++++ 2 files changed, 1889 insertions(+), 1871 deletions(-) create mode 100644 lib/classes/api/commands/class.Domains.php diff --git a/admin_domains.php b/admin_domains.php index f731d59c..89c61f4b 100644 --- a/admin_domains.php +++ b/admin_domains.php @@ -35,9 +35,9 @@ if ($page == 'domains' || $page == 'overview') { } $countcustomers = Database::pexecute_first($stmt, $params); $countcustomers = (int) $countcustomers['countcustomers']; - + if ($action == '') { - + $log->logAction(ADM_ACTION, LOG_NOTICE, "viewed admin_domains"); $fields = array( 'd.domain' => $lng['domains']['domainname'], @@ -49,17 +49,12 @@ if ($page == 'domains' || $page == 'overview') { ); $paging = new paging($userinfo, TABLE_PANEL_DOMAINS, $fields); $domains = ""; - $syshostname = ""; - if (Settings::Get('system.hostname_id')) - { - $syshostname = "AND `d`.`id` <> " . Settings::Get('system.hostname_id'); - } $result_stmt = Database::prepare(" SELECT `d`.*, `c`.`loginname`, `c`.`deactivated`, `c`.`name`, `c`.`firstname`, `c`.`company`, `c`.`standardsubdomain`, `ad`.`id` AS `aliasdomainid`, `ad`.`domain` AS `aliasdomain` FROM `" . TABLE_PANEL_DOMAINS . "` `d` LEFT JOIN `" . TABLE_PANEL_CUSTOMERS . "` `c` USING(`customerid`) LEFT JOIN `" . TABLE_PANEL_DOMAINS . "` `ad` ON `d`.`aliasdomain`=`ad`.`id` - WHERE `d`.`parentdomainid`='0' " . $syshostname . ($userinfo['customers_see_all'] ? '' : " AND `d`.`adminid` = :adminid ") . " " . $paging->getSqlWhere(true) . " " . $paging->getSqlOrderBy() . " " . $paging->getSqlLimit()); + WHERE `d`.`parentdomainid`='0' " . ($userinfo['customers_see_all'] ? '' : " AND `d`.`adminid` = :adminid ") . " " . $paging->getSqlWhere(true) . " " . $paging->getSqlOrderBy() . " " . $paging->getSqlLimit()); $params = array(); if ($userinfo['customers_see_all'] == '0') { $params['adminid'] = $userinfo['adminid']; @@ -72,17 +67,17 @@ if ($page == 'domains' || $page == 'overview') { $searchcode = $paging->getHtmlSearchCode($lng); $pagingcode = $paging->getHtmlPagingCode($filename . '?page=' . $page . '&s=' . $s); $domain_array = array(); - + while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) { - + formatDomainEntry($row, $idna_convert); - + if (! isset($domain_array[$row['domain']])) { $domain_array[$row['domain']] = $row; } else { $domain_array[$row['domain']] = array_merge($row, $domain_array[$row['domain']]); } - + if (isset($row['aliasdomainid']) && $row['aliasdomainid'] != null && isset($row['aliasdomain']) && $row['aliasdomain'] != '') { if (! isset($domain_array[$row['aliasdomain']])) { $domain_array[$row['aliasdomain']] = array(); @@ -91,7 +86,7 @@ if ($page == 'domains' || $page == 'overview') { $domain_array[$row['aliasdomain']]['domainalias'] = $row['domain']; } } - + /** * We need ksort/krsort here to make sure idna-domains are also sorted correctly */ @@ -100,26 +95,11 @@ if ($page == 'domains' || $page == 'overview') { } elseif ($paging->sortfield == 'd.domain' && $paging->sortorder == 'desc') { krsort($domain_array); } - - // show froxlor hostname as first entry - if (Settings::Get('system.hostname_id')) - { - $syshost_stmt = Database::prepare("SELECT * FROM `" . TABLE_PANEL_DOMAINS . "` WHERE `id` = :did"); - $row = Database::pexecute_first($syshost_stmt, array( - 'did' => Settings::Get('system.hostname_id') - )); - formatDomainEntry($row, $idna_convert); - $row['customername'] = 'Froxlor hostname'; - $row['loginname'] = null; - $row['termination_css'] = 'domain-hostname'; - $row['ipandport'] = str_replace("\n", "
    ", $row['ipandport']); - eval("\$domains.=\"" . getTemplate("domains/domains_domain") . "\";"); - } - + $i = 0; $count = 0; foreach ($domain_array as $row) { - + if (isset($row['domain']) && $row['domain'] != '' && $paging->checkDisplay($i)) { $row['customername'] = getCorrectFullUserDetails($row); $row = htmlentities_array($row); @@ -130,154 +110,41 @@ if ($page == 'domains' || $page == 'overview') { } $i ++; } - + $domainscount = $numrows_domains; - + // Display the list eval("echo \"" . getTemplate("domains/domains") . "\";"); } elseif ($action == 'delete' && $id != 0) { - - $result_stmt = Database::prepare(" - SELECT `d`.* FROM `" . TABLE_PANEL_DOMAINS . "` `d`, `" . TABLE_PANEL_CUSTOMERS . "` `c` - WHERE `d`.`id` = :id AND `d`.`id` <> `c`.`standardsubdomain`" . ($userinfo['customers_see_all'] ? '' : " AND `d`.`adminid` = :adminid")); - $params = array( - 'id' => $id - ); - if ($userinfo['customers_see_all'] == '0') { - $params['adminid'] = $userinfo['adminid']; + + try { + $json_result = Domains::getLocal($userinfo, array( + 'id' => $id, + 'no_std_subdomain' => true + ))->get(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); } - $result = Database::pexecute_first($result_stmt, $params); - + $result = json_decode($json_result, true)['data']; + $alias_check_stmt = Database::prepare(" SELECT COUNT(`id`) AS `count` FROM `" . TABLE_PANEL_DOMAINS . "` WHERE `aliasdomain`= :id"); $alias_check = Database::pexecute_first($alias_check_stmt, array( 'id' => $id )); - - if ($result['domain'] != '' && $alias_check['count'] == 0) { - if (isset($_POST['send']) && $_POST['send'] == 'send') { - // check for deletion of main-domains which are logically subdomains, #329 - $rsd_sql = ''; - $remove_subbutmain_domains = isset($_POST['delete_userfiles']) ? 1 : 0; - if ($remove_subbutmain_domains == 1) { - $rsd_sql .= " OR `ismainbutsubto` = :id"; + + if ($result['domain'] != '') { + if (isset($_POST['send']) && $_POST['send'] == 'send' && $alias_check['count'] == 0) { + + try { + Domains::getLocal($userinfo, array_merge(array( + 'id' => $id + ), $_POST))->delete(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); } - - $subresult_stmt = Database::prepare(" - SELECT `id` FROM `" . TABLE_PANEL_DOMAINS . "` - WHERE (`id` = :id OR `parentdomainid` = :id " . $rsd_sql . ")"); - Database::pexecute($subresult_stmt, array( - 'id' => $id - )); - $idString = array(); - $paramString = array(); - while ($subRow = $subresult_stmt->fetch(PDO::FETCH_ASSOC)) { - $idString[] = "`domainid` = :domain_" . (int) $subRow['id']; - $paramString['domain_' . $subRow['id']] = $subRow['id']; - } - - $idString = implode(' OR ', $idString); - - if ($idString != '') { - $del_stmt = Database::prepare(" - DELETE FROM `" . TABLE_MAIL_USERS . "` WHERE " . $idString); - Database::pexecute($del_stmt, $paramString); - $del_stmt = Database::prepare(" - DELETE FROM `" . TABLE_MAIL_VIRTUAL . "` WHERE " . $idString); - Database::pexecute($del_stmt, $paramString); - $log->logAction(ADM_ACTION, LOG_NOTICE, "deleted domain/s from mail-tables"); - } - - // if mainbutsubto-domains are not to be deleted, re-assign the (ismainbutsubto value of the main - // domain which is being deleted) as their new ismainbutsubto value - if ($remove_subbutmain_domains !== 1) { - $upd_stmt = Database::prepare(" - UPDATE `" . TABLE_PANEL_DOMAINS . "` SET - `ismainbutsubto` = :newIsMainButSubtoValue - WHERE `ismainbutsubto` = :deletedMainDomainId - "); - Database::pexecute($upd_stmt, array( - 'newIsMainButSubtoValue' => $result['ismainbutsubto'], - 'deletedMainDomainId' => $id, - )); - } - - $del_stmt = Database::prepare(" - DELETE FROM `" . TABLE_PANEL_DOMAINS . "` - WHERE `id` = :id OR `parentdomainid` = :id " . $rsd_sql); - Database::pexecute($del_stmt, array( - 'id' => $id - )); - - $deleted_domains = $del_stmt->rowCount(); - - $upd_stmt = Database::prepare(" - UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET - `subdomains_used` = `subdomains_used` - :domaincount - WHERE `customerid` = :customerid"); - Database::pexecute($upd_stmt, array( - 'domaincount' => ($deleted_domains - 1), - 'customerid' => $result['customerid'] - )); - - $upd_stmt = Database::prepare(" - UPDATE `" . TABLE_PANEL_ADMINS . "` SET - `domains_used` = `domains_used` - 1 - WHERE `adminid` = :adminid"); - Database::pexecute($upd_stmt, array( - 'adminid' => $userinfo['adminid'] - )); - - $upd_stmt = Database::prepare(" - UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET - `standardsubdomain` = '0' - WHERE `standardsubdomain` = :id AND `customerid` = :customerid"); - Database::pexecute($upd_stmt, array( - 'id' => $result['id'], - 'customerid' => $result['customerid'] - )); - - $del_stmt = Database::prepare(" - DELETE FROM `" . TABLE_DOMAINTOIP . "` - WHERE `id_domain` = :domainid"); - Database::pexecute($del_stmt, array( - 'domainid' => $id - )); - - $del_stmt = Database::prepare(" - DELETE FROM `" . TABLE_PANEL_DOMAINREDIRECTS . "` - WHERE `did` = :domainid"); - Database::pexecute($del_stmt, array( - 'domainid' => $id - )); - - // remove certificate from domain_ssl_settings, fixes #1596 - $del_stmt = Database::prepare(" - DELETE FROM `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` - WHERE `domainid` = :domainid"); - Database::pexecute($del_stmt, array( - 'domainid' => $id - )); - - // remove possible existing DNS entries - $del_stmt = Database::prepare(" - DELETE FROM `" . TABLE_DOMAIN_DNS . "` - WHERE `domain_id` = :domainid - "); - Database::pexecute($del_stmt, array( - 'domainid' => $id - )); - - triggerLetsEncryptCSRForAliasDestinationDomain($result['aliasdomain'], $log); - - $log->logAction(ADM_ACTION, LOG_INFO, "deleted domain/subdomains (#" . $result['id'] . ")"); - updateCounters(); - inserttask('1'); - - // Using nameserver, insert a task which rebuilds the server config - inserttask('4'); - + redirectTo($filename, array( 'page' => $page, 's' => $s @@ -285,7 +152,7 @@ if ($page == 'domains' || $page == 'overview') { } elseif ($alias_check['count'] > 0) { standard_error('domains_cantdeletedomainwithaliases'); } else { - + $showcheck = false; if (domainHasMainSubDomains($id)) { $showcheck = true; @@ -298,850 +165,196 @@ if ($page == 'domains' || $page == 'overview') { } } } elseif ($action == 'add') { - - if ($userinfo['domains_used'] < $userinfo['domains'] || $userinfo['domains'] == '-1') { - if (isset($_POST['send']) && $_POST['send'] == 'send') { - - if ($_POST['domain'] == Settings::Get('system.hostname')) { - standard_error('admin_domain_emailsystemhostname'); - } - - if (substr($_POST['domain'], 0, 4) == 'xn--') { - standard_error('domain_nopunycode'); - } - - $domain = $idna_convert->encode(preg_replace(array( - '/\:(\d)+$/', - '/^https?\:\/\//' - ), '', validate($_POST['domain'], 'domain'))); - - // Check whether domain validation is enabled and if, validate the domain - if (Settings::Get('system.validate_domain') && ! validateDomain($domain)) { - standard_error(array( - 'stringiswrong', - 'mydomain' - )); - } - - $subcanemaildomain = intval($_POST['subcanemaildomain']); - - $isemaildomain = 0; - if (isset($_POST['isemaildomain'])) { - $isemaildomain = intval($_POST['isemaildomain']); - } - - $email_only = 0; - if (isset($_POST['email_only'])) { - $email_only = intval($_POST['email_only']); - } - - $serveraliasoption = 0; - if (isset($_POST['selectserveralias'])) { - $serveraliasoption = intval($_POST['selectserveralias']); - } - - $speciallogfile = 0; - if (isset($_POST['speciallogfile'])) { - $speciallogfile = intval($_POST['speciallogfile']); - } - - $aliasdomain = intval($_POST['alias']); - $issubof = intval($_POST['issubof']); - $customerid = intval($_POST['customerid']); - $customer_stmt = Database::prepare(" - SELECT * FROM `" . TABLE_PANEL_CUSTOMERS . "` - WHERE `customerid` = :customerid " . ($userinfo['customers_see_all'] ? '' : " AND `adminid` = :adminid")); - $params = array( - 'customerid' => $customerid - ); - if ($userinfo['customers_see_all'] == '0') { - $params['adminid'] = $userinfo['adminid']; - } - $customer = Database::pexecute_first($customer_stmt, $params); - - if (empty($customer) || $customer['customerid'] != $customerid) { - standard_error('customerdoesntexist'); - } - - if ($userinfo['customers_see_all'] == '1') { - - $adminid = intval($_POST['adminid']); - $admin_stmt = Database::prepare(" - SELECT * FROM `" . TABLE_PANEL_ADMINS . "` - WHERE `adminid` = :adminid AND (`domains_used` < `domains` OR `domains` = '-1')"); - $admin = Database::pexecute_first($admin_stmt, array( - 'adminid' => $adminid - )); - - if (empty($admin) || $admin['adminid'] != $adminid) { - standard_error('admindoesntexist'); - } - } else { - $adminid = $userinfo['adminid']; - $admin = $userinfo; - } - - // set default path if admin/reseller has "change_serversettings == false" but we still - // need to respect the documentroot_use_default_value - setting - $path_suffix = ''; - if (Settings::Get('system.documentroot_use_default_value') == 1) { - $path_suffix = '/' . $domain; - } - $documentroot = makeCorrectDir($customer['documentroot'] . $path_suffix); - - $registration_date = trim($_POST['registration_date']); - $registration_date = validate($registration_date, 'registration_date', '/^(19|20)\d\d[-](0[1-9]|1[012])[-](0[1-9]|[12][0-9]|3[01])$/', '', array( - '0000-00-00', - '0', - '' - )); - if ($registration_date == '0000-00-00') { - $registration_date = null; - } - - $termination_date = trim($_POST['termination_date']); - $termination_date = validate($termination_date, 'termination_date', '/^(19|20)\d\d[-](0[1-9]|1[012])[-](0[1-9]|[12][0-9]|3[01])$/', '', array( - '0000-00-00', - '0', - '' - )); - if ($termination_date == '0000-00-00') { - $termination_date = null; - } - - if ($userinfo['change_serversettings'] == '1') { - - $caneditdomain = isset($_POST['caneditdomain']) ? intval($_POST['caneditdomain']) : 0; - - $isbinddomain = '0'; - $zonefile = ''; - if (Settings::Get('system.bind_enable') == '1') { - if (isset($_POST['isbinddomain'])) { - $isbinddomain = intval($_POST['isbinddomain']); - } - $zonefile = validate($_POST['zonefile'], 'zonefile'); - } - - if (isset($_POST['dkim'])) { - $dkim = intval($_POST['dkim']); - } else { - $dkim = '1'; - } - - $specialsettings = validate(str_replace("\r\n", "\n", $_POST['specialsettings']), 'specialsettings', '/^[^\0]*$/'); - $notryfiles = isset($_POST['notryfiles']) && (int)$_POST['notryfiles'] == 1 ? 1 : 0; - validate($_POST['documentroot'], 'documentroot'); - - // If path is empty and 'Use domain name as default value for DocumentRoot path' is enabled in settings, - // set default path to subdomain or domain name - if (isset($_POST['documentroot']) && $_POST['documentroot'] != '') { - if (substr($_POST['documentroot'], 0, 1) != '/' && ! preg_match('/^https?\:\/\//', $_POST['documentroot'])) { - $documentroot .= '/' . $_POST['documentroot']; - } else { - $documentroot = $_POST['documentroot']; - } - } elseif (isset($_POST['documentroot']) && ($_POST['documentroot'] == '') && (Settings::Get('system.documentroot_use_default_value') == 1)) { - $documentroot = makeCorrectDir($customer['documentroot'] . '/' . $domain); - } - } else { - $isbinddomain = '0'; - if (Settings::Get('system.bind_enable') == '1') { - $isbinddomain = '1'; - } - $caneditdomain = '1'; - $zonefile = ''; - $dkim = '1'; - $specialsettings = ''; - $notryfiles = '0'; - } - - if ($userinfo['caneditphpsettings'] == '1' || $userinfo['change_serversettings'] == '1') { - - $phpenabled = isset($_POST['phpenabled']) ? intval($_POST['phpenabled']) : 0; - $openbasedir = isset($_POST['openbasedir']) ? intval($_POST['openbasedir']) : 0; - - if ((int) Settings::Get('system.mod_fcgid') == 1 || (int) Settings::Get('phpfpm.enabled') == 1) { - $phpsettingid = (int) $_POST['phpsettingid']; - $phpsettingid_check_stmt = Database::prepare(" - SELECT * FROM `" . TABLE_PANEL_PHPCONFIGS . "` - WHERE `id` = :phpsettingid"); - $phpsettingid_check = Database::pexecute_first($phpsettingid_check_stmt, array( - 'phpsettingid' => $phpsettingid - )); - - if (! isset($phpsettingid_check['id']) || $phpsettingid_check['id'] == '0' || $phpsettingid_check['id'] != $phpsettingid) { - standard_error('phpsettingidwrong'); - } - - if ((int) Settings::Get('system.mod_fcgid') == 1) { - $mod_fcgid_starter = validate($_POST['mod_fcgid_starter'], 'mod_fcgid_starter', '/^[0-9]*$/', '', array( - '-1', - '' - )); - $mod_fcgid_maxrequests = validate($_POST['mod_fcgid_maxrequests'], 'mod_fcgid_maxrequests', '/^[0-9]*$/', '', array( - '-1', - '' - )); - } else { - $mod_fcgid_starter = '-1'; - $mod_fcgid_maxrequests = '-1'; - } - } else { - - if ((int) Settings::Get('phpfpm.enabled') == 1) { - $phpsettingid = Settings::Get('phpfpm.defaultini'); - } else { - $phpsettingid = Settings::Get('system.mod_fcgid_defaultini'); - } - $mod_fcgid_starter = '-1'; - $mod_fcgid_maxrequests = '-1'; - } - } else { - - $phpenabled = '1'; - $openbasedir = '1'; - - if ((int) Settings::Get('phpfpm.enabled') == 1) { - $phpsettingid = Settings::Get('phpfpm.defaultini'); - } else { - $phpsettingid = Settings::Get('system.mod_fcgid_defaultini'); - } - $mod_fcgid_starter = '-1'; - $mod_fcgid_maxrequests = '-1'; - } - - if ($userinfo['ip'] != "-1") { - $admin_ip_stmt = Database::prepare(" - SELECT `id`, `ip`, `port` FROM `" . TABLE_PANEL_IPSANDPORTS . "` - WHERE `id` = :id ORDER BY `ip`, `port` ASC"); - $admin_ip = Database::pexecute_first($admin_ip_stmt, array( - 'id' => $userinfo['ip'] - )); - $additional_ip_condition = " AND `ip` = :adminip "; - $aip_param = array( - 'adminip' => $admin_ip['ip'] - ); - } else { - $additional_ip_condition = ''; - $aip_param = array(); - } - - $ipandports = array(); - if (isset($_POST['ipandport']) && ! is_array($_POST['ipandport'])) { - $_POST['ipandport'] = unserialize($_POST['ipandport']); - } - - if (isset($_POST['ipandport']) && is_array($_POST['ipandport'])) { - foreach ($_POST['ipandport'] as $ipandport) { - $ipandport = intval($ipandport); - $ipandport_check_stmt = Database::prepare(" - SELECT `id`, `ip`, `port` FROM `" . TABLE_PANEL_IPSANDPORTS . "` - WHERE `id` = :id " . $additional_ip_condition); - $ip_params = null; - $ip_params = array_merge(array( - 'id' => $ipandport - ), $aip_param); - $ipandport_check = Database::pexecute_first($ipandport_check_stmt, $ip_params); - - if (! isset($ipandport_check['id']) || $ipandport_check['id'] == '0' || $ipandport_check['id'] != $ipandport) { - standard_error('ipportdoesntexist'); - } else { - $ipandports[] = $ipandport; - } - } - } - - if (Settings::Get('system.use_ssl') == "1" && isset($_POST['ssl_ipandport'])) { - $ssl_redirect = 0; - if (isset($_POST['ssl_redirect'])) { - $ssl_redirect = (int) $_POST['ssl_redirect']; - } - - $letsencrypt = 0; - if (isset($_POST['letsencrypt'])) { - $letsencrypt = (int) $_POST['letsencrypt']; - } - - $ssl_ipandports = array(); - if (isset($_POST['ssl_ipandport']) && ! is_array($_POST['ssl_ipandport'])) { - $_POST['ssl_ipandport'] = unserialize($_POST['ssl_ipandport']); - } - - // Verify SSL-Ports - if (isset($_POST['ssl_ipandport']) && is_array($_POST['ssl_ipandport'])) { - foreach ($_POST['ssl_ipandport'] as $ssl_ipandport) { - if (trim($ssl_ipandport) == "") - continue; - // fix if no ssl-ip/port is checked - if (trim($ssl_ipandport) < 1) - continue; - $ssl_ipandport = intval($ssl_ipandport); - $ssl_ipandport_check_stmt = Database::prepare(" - SELECT `id`, `ip`, `port` FROM `" . TABLE_PANEL_IPSANDPORTS . "` - WHERE `id` = :id " . $additional_ip_condition); - $ip_params = null; - $ip_params = array_merge(array( - 'id' => $ssl_ipandport - ), $aip_param); - $ssl_ipandport_check = Database::pexecute_first($ssl_ipandport_check_stmt, $ip_params); - - if (! isset($ssl_ipandport_check['id']) || $ssl_ipandport_check['id'] == '0' || $ssl_ipandport_check['id'] != $ssl_ipandport) { - standard_error('ipportdoesntexist'); - } else { - $ssl_ipandports[] = $ssl_ipandport; - } - } - - $http2 = isset($_POST['http2']) && (int)$_POST['http2'] == 1 ? 1 : 0; - - // HSTS - $hsts_maxage = isset($_POST['hsts_maxage']) ? (int)$_POST['hsts_maxage'] : 0; - $hsts_sub = isset($_POST['hsts_sub']) && (int)$_POST['hsts_sub'] == 1 ? 1 : 0; - $hsts_preload = isset($_POST['hsts_preload']) && (int)$_POST['hsts_preload'] == 1 ? 1 : 0; - - // OCSP stapling - $ocsp_stapling = isset($_POST['ocsp_stapling']) && (int)$_POST['ocsp_stapling'] == 1 ? 1 : 0; - - } else { - $ssl_redirect = 0; - $letsencrypt = 0; - $http2 = 0; - // we need this for the serialize - // if ssl is disabled or no ssl-ip/port exists - $ssl_ipandports[] = - 1; - - // HSTS - $hsts_maxage = 0; - $hsts_sub = 0; - $hsts_preload = 0; - - // OCSP stapling - $ocsp_stapling = 0; - } - } else { - $ssl_redirect = 0; - $letsencrypt = 0; - $http2 = 0; - // we need this for the serialize - // if ssl is disabled or no ssl-ip/port exists - $ssl_ipandports[] = - 1; - - // HSTS - $hsts_maxage = 0; - $hsts_sub = 0; - $hsts_preload = 0; - - // OCSP stapling - $ocsp_stapling = 0; - } - - // We can't enable let's encrypt for wildcard - domains if using acme-v1 - if ($serveraliasoption == '0' && $letsencrypt == '1' && Settings::Get('system.leapiversion') == '1') { - standard_error('nowildcardwithletsencrypt'); - } - // if using acme-v2 we cannot issue wildcard-certificates - // because they currently only support the dns-01 challenge - if ($serveraliasoption == '0' && $letsencrypt == '1' && Settings::Get('system.leapiversion') == '2') { - standard_error('nowildcardwithletsencryptv2'); - } - - // Temporarily deactivate ssl_redirect until Let's Encrypt certificate was generated - if ($ssl_redirect > 0 && $letsencrypt == 1) { - $ssl_redirect = 2; - } - - if (! preg_match('/^https?\:\/\//', $documentroot)) { - if (strstr($documentroot, ":") !== false) { - standard_error('pathmaynotcontaincolon'); - } else { - $documentroot = makeCorrectDir($documentroot); - } - } - - $domain_check_stmt = Database::prepare(" - SELECT `id`, `domain` FROM `" . TABLE_PANEL_DOMAINS . "` - WHERE `domain` = :domain"); - $domain_check = Database::pexecute_first($domain_check_stmt, array( - 'domain' => strtolower($domain) - )); - - $aliasdomain_check = array( - 'id' => 0 - ); - - if ($aliasdomain != 0) { - // Overwrite given ipandports with these of the "main" domain - $ipandports = array(); - $ssl_ipandports = array(); - $origipresult_stmt = Database::prepare(" - SELECT `id_ipandports` FROM `" . TABLE_DOMAINTOIP . "` - WHERE `id_domain` = :id"); - Database::pexecute($origipresult_stmt, array( - 'id' => $aliasdomain - )); - $ipdata_stmt = Database::prepare("SELECT * FROM `" . TABLE_PANEL_IPSANDPORTS . "` WHERE `id` = :ipid"); - while ($origip = $origipresult_stmt->fetch(PDO::FETCH_ASSOC)) { - $_origip_tmp = Database::pexecute_first($ipdata_stmt, array( - 'ipid' => $origip['id_ipandports'] - )); - if ($_origip_tmp['ssl'] == 0) { - $ipandports[] = $origip['id_ipandports']; - } else { - $ssl_ipandports[] = $origip['id_ipandports']; - } - } - - if (count($ssl_ipandports) == 0) { - // we need this for the serialize - // if ssl is disabled or no ssl-ip/port exists - $ssl_ipandports[] = - 1; - } - - $aliasdomain_check_stmt = Database::prepare(" - SELECT `d`.`id` FROM `" . TABLE_PANEL_DOMAINS . "` `d`, `" . TABLE_PANEL_CUSTOMERS . "` `c` - WHERE `d`.`customerid` = :customerid - AND `d`.`aliasdomain` IS NULL AND `d`.`id` <> `c`.`standardsubdomain` - AND `c`.`customerid` = :customerid - AND `d`.`id` = :aliasdomainid"); - $alias_params = array( - 'customerid' => $customerid, - 'aliasdomainid' => $aliasdomain - ); - $aliasdomain_check = Database::pexecute_first($aliasdomain_check_stmt, $alias_params); - } - - if (count($ipandports) == 0) { - standard_error('noipportgiven'); - } - - if ($phpenabled != '1') { - $phpenabled = '0'; - } - - if ($openbasedir != '1') { - $openbasedir = '0'; - } - - if ($speciallogfile != '1') { - $speciallogfile = '0'; - } - - if ($isbinddomain != '1') { - $isbinddomain = '0'; - } - - if ($isemaildomain != '1') { - $isemaildomain = '0'; - } - - if ($email_only == '1') { - $isemaildomain = '1'; - } else { - $email_only = '0'; - } - - if ($subcanemaildomain != '1' && $subcanemaildomain != '2' && $subcanemaildomain != '3') { - $subcanemaildomain = '0'; - } - - if ($dkim != '1') { - $dkim = '0'; - } - - if ($serveraliasoption != '1' && $serveraliasoption != '2') { - $serveraliasoption = '0'; - } - - if ($caneditdomain != '1') { - $caneditdomain = '0'; - } - - if ($issubof <= '0') { - $issubof = '0'; - } - - if ($domain == '') { - standard_error(array( - 'stringisempty', - 'mydomain' - )); - } elseif ($documentroot == '') { - standard_error(array( - 'stringisempty', - 'mydocumentroot' - )); - } elseif ($customerid == 0) { - standard_error('adduserfirst'); - } elseif (strtolower($domain_check['domain']) == strtolower($domain)) { - standard_error('domainalreadyexists', $idna_convert->decode($domain)); - } elseif ($aliasdomain_check['id'] != $aliasdomain) { - standard_error('domainisaliasorothercustomer'); - } else { - $params = array( - 'page' => $page, - 'action' => $action, - 'domain' => $domain, - 'customerid' => $customerid, - 'adminid' => $adminid, - 'documentroot' => $documentroot, - 'alias' => $aliasdomain, - 'isbinddomain' => $isbinddomain, - 'isemaildomain' => $isemaildomain, - 'email_only' => $email_only, - 'subcanemaildomain' => $subcanemaildomain, - 'caneditdomain' => $caneditdomain, - 'zonefile' => $zonefile, - 'dkim' => $dkim, - 'speciallogfile' => $speciallogfile, - 'selectserveralias' => $serveraliasoption, - 'ipandport' => serialize($ipandports), - 'ssl_redirect' => $ssl_redirect, - 'ssl_ipandport' => serialize($ssl_ipandports), - 'phpenabled' => $phpenabled, - 'openbasedir' => $openbasedir, - 'phpsettingid' => $phpsettingid, - 'mod_fcgid_starter' => $mod_fcgid_starter, - 'mod_fcgid_maxrequests' => $mod_fcgid_maxrequests, - 'specialsettings' => $specialsettings, - 'notryfiles' => $notryfiles, - 'registration_date' => $registration_date, - 'termination_date' => $termination_date, - 'issubof' => $issubof, - 'letsencrypt' => $letsencrypt, - 'http2' => $http2, - 'hsts_maxage' => $hsts_maxage, - 'hsts_sub' => $hsts_sub, - 'hsts_preload' => $hsts_preload, - 'ocsp_stapling' => $ocsp_stapling - ); - - $security_questions = array( - 'reallydisablesecuritysetting' => ($openbasedir == '0' && $userinfo['change_serversettings'] == '1'), - 'reallydocrootoutofcustomerroot' => (substr($documentroot, 0, strlen($customer['documentroot'])) != $customer['documentroot'] && ! preg_match('/^https?\:\/\//', $documentroot)) - ); - $question_nr = 1; - foreach ($security_questions as $question_name => $question_launch) { - if ($question_launch !== false) { - $params[$question_name] = $question_name; - - if (! isset($_POST[$question_name]) || $_POST[$question_name] != $question_name) { - ask_yesno('admin_domain_' . $question_name, $filename, $params, $question_nr); - } - } - $question_nr ++; - } - - $wwwserveralias = ($serveraliasoption == '1') ? '1' : '0'; - $iswildcarddomain = ($serveraliasoption == '0') ? '1' : '0'; - - $ins_data = array( - 'domain' => $domain, - 'customerid' => $customerid, - 'adminid' => $adminid, - 'documentroot' => $documentroot, - 'aliasdomain' => ($aliasdomain != 0 ? $aliasdomain : null), - 'zonefile' => $zonefile, - 'dkim' => $dkim, - 'wwwserveralias' => $wwwserveralias, - 'iswildcarddomain' => $iswildcarddomain, - 'isbinddomain' => $isbinddomain, - 'isemaildomain' => $isemaildomain, - 'email_only' => $email_only, - 'subcanemaildomain' => $subcanemaildomain, - 'caneditdomain' => $caneditdomain, - 'phpenabled' => $phpenabled, - 'openbasedir' => $openbasedir, - 'speciallogfile' => $speciallogfile, - 'specialsettings' => $specialsettings, - 'notryfiles' => $notryfiles, - 'ssl_redirect' => $ssl_redirect, - 'add_date' => time(), - 'registration_date' => $registration_date, - 'termination_date' => $termination_date, - 'phpsettingid' => $phpsettingid, - 'mod_fcgid_starter' => $mod_fcgid_starter, - 'mod_fcgid_maxrequests' => $mod_fcgid_maxrequests, - 'ismainbutsubto' => $issubof, - 'letsencrypt' => $letsencrypt, - 'http2' => $http2, - 'hsts' => $hsts_maxage, - 'hsts_sub' => $hsts_sub, - 'hsts_preload' => $hsts_preload, - 'ocsp_stapling' => $ocsp_stapling - ); - - $ins_stmt = Database::prepare(" - INSERT INTO `" . TABLE_PANEL_DOMAINS . "` SET - `domain` = :domain, - `customerid` = :customerid, - `adminid` = :adminid, - `documentroot` = :documentroot, - `aliasdomain` = :aliasdomain, - `zonefile` = :zonefile, - `dkim` = :dkim, - `dkim_id` = '0', - `dkim_privkey` = '', - `dkim_pubkey` = '', - `wwwserveralias` = :wwwserveralias, - `iswildcarddomain` = :iswildcarddomain, - `isbinddomain` = :isbinddomain, - `isemaildomain` = :isemaildomain, - `email_only` = :email_only, - `subcanemaildomain` = :subcanemaildomain, - `caneditdomain` = :caneditdomain, - `phpenabled` = :phpenabled, - `openbasedir` = :openbasedir, - `speciallogfile` = :speciallogfile, - `specialsettings` = :specialsettings, - `notryfiles` = :notryfiles, - `ssl_redirect` = :ssl_redirect, - `add_date` = :add_date, - `registration_date` = :registration_date, - `termination_date` = :termination_date, - `phpsettingid` = :phpsettingid, - `mod_fcgid_starter` = :mod_fcgid_starter, - `mod_fcgid_maxrequests` = :mod_fcgid_maxrequests, - `ismainbutsubto` = :ismainbutsubto, - `letsencrypt` = :letsencrypt, - `http2` = :http2, - `hsts` = :hsts, - `hsts_sub` = :hsts_sub, - `hsts_preload` = :hsts_preload, - `ocsp_stapling` = :ocsp_stapling - "); - Database::pexecute($ins_stmt, $ins_data); - $domainid = Database::lastInsertId(); - - $upd_stmt = Database::prepare(" - UPDATE `" . TABLE_PANEL_ADMINS . "` SET `domains_used` = `domains_used` + 1 - WHERE `adminid` = :adminid"); - Database::pexecute($upd_stmt, array( - 'adminid' => $adminid - )); - - $ins_stmt = Database::prepare(" - INSERT INTO `" . TABLE_DOMAINTOIP . "` SET - `id_domain` = :domainid, - `id_ipandports` = :ipandportsid - "); - - foreach ($ipandports as $ipportid) { - $ins_data = array( - 'domainid' => $domainid, - 'ipandportsid' => $ipportid - ); - Database::pexecute($ins_stmt, $ins_data); - } - - foreach ($ssl_ipandports as $ssl_ipportid) { - if ($ssl_ipportid > 0) { - $ins_data = array( - 'domainid' => $domainid, - 'ipandportsid' => $ssl_ipportid - ); - Database::pexecute($ins_stmt, $ins_data); - } - } - - triggerLetsEncryptCSRForAliasDestinationDomain($aliasdomain, $log); - - $log->logAction(ADM_ACTION, LOG_INFO, "added domain '" . $domain . "'"); - inserttask('1'); - - // Using nameserver, insert a task which rebuilds the server config - inserttask('4'); - - redirectTo($filename, array( - 'page' => $page, - 's' => $s - )); - } - } else { - - $customers = makeoption($lng['panel']['please_choose'], 0, 0, true); - $result_customers_stmt = Database::prepare(" + + if (isset($_POST['send']) && $_POST['send'] == 'send') { + try { + Domains::getLocal($userinfo, $_POST)->add(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); + } + redirectTo($filename, array( + 'page' => $page, + 's' => $s + )); + } else { + + $customers = makeoption($lng['panel']['please_choose'], 0, 0, true); + $result_customers_stmt = Database::prepare(" SELECT `customerid`, `loginname`, `name`, `firstname`, `company` FROM `" . TABLE_PANEL_CUSTOMERS . "` " . ($userinfo['customers_see_all'] ? '' : " WHERE `adminid` = '" . (int) $userinfo['adminid'] . "' ") . " ORDER BY COALESCE(NULLIF(`name`,''), `company`) ASC"); - $params = array(); - if ($userinfo['customers_see_all'] == '0') { - $params['adminid'] = $userinfo['adminid']; - } - Database::pexecute($result_customers_stmt, $params); - - while ($row_customer = $result_customers_stmt->fetch(PDO::FETCH_ASSOC)) { - $customers .= makeoption(getCorrectFullUserDetails($row_customer) . ' (' . $row_customer['loginname'] . ')', $row_customer['customerid']); - } - - $admins = ''; - if ($userinfo['customers_see_all'] == '1') { - - $result_admins_stmt = Database::query(" + $params = array(); + if ($userinfo['customers_see_all'] == '0') { + $params['adminid'] = $userinfo['adminid']; + } + Database::pexecute($result_customers_stmt, $params); + + while ($row_customer = $result_customers_stmt->fetch(PDO::FETCH_ASSOC)) { + $customers .= makeoption(getCorrectFullUserDetails($row_customer) . ' (' . $row_customer['loginname'] . ')', $row_customer['customerid']); + } + + $admins = ''; + if ($userinfo['customers_see_all'] == '1') { + + $result_admins_stmt = Database::query(" SELECT `adminid`, `loginname`, `name` FROM `" . TABLE_PANEL_ADMINS . "` WHERE `domains_used` < `domains` OR `domains` = '-1' ORDER BY `name` ASC"); - - while ($row_admin = $result_admins_stmt->fetch(PDO::FETCH_ASSOC)) { - $admins .= makeoption(getCorrectFullUserDetails($row_admin) . ' (' . $row_admin['loginname'] . ')', $row_admin['adminid'], $userinfo['adminid']); - } + + while ($row_admin = $result_admins_stmt->fetch(PDO::FETCH_ASSOC)) { + $admins .= makeoption(getCorrectFullUserDetails($row_admin) . ' (' . $row_admin['loginname'] . ')', $row_admin['adminid'], $userinfo['adminid']); } - - if ($userinfo['ip'] == "-1") { - $result_ipsandports_stmt = Database::query(" + } + + if ($userinfo['ip'] == "-1") { + $result_ipsandports_stmt = Database::query(" SELECT `id`, `ip`, `port` FROM `" . TABLE_PANEL_IPSANDPORTS . "` WHERE `ssl`='0' ORDER BY `ip`, `port` ASC "); - $result_ssl_ipsandports_stmt = Database::query(" + $result_ssl_ipsandports_stmt = Database::query(" SELECT `id`, `ip`, `port` FROM `" . TABLE_PANEL_IPSANDPORTS . "` WHERE `ssl`='1' ORDER BY `ip`, `port` ASC "); - } else { - $admin_ip_stmt = Database::prepare(" + } else { + $admin_ip_stmt = Database::prepare(" SELECT `id`, `ip`, `port` FROM `" . TABLE_PANEL_IPSANDPORTS . "` WHERE `id` = :ipid ORDER BY `ip`, `port` ASC "); - $admin_ip = Database::pexecute_first($admin_ip_stmt, array( - 'ipid' => $userinfo['ip'] - )); - - $result_ipsandports_stmt = Database::prepare(" + $admin_ip = Database::pexecute_first($admin_ip_stmt, array( + 'ipid' => $userinfo['ip'] + )); + + $result_ipsandports_stmt = Database::prepare(" SELECT `id`, `ip`, `port` FROM `" . TABLE_PANEL_IPSANDPORTS . "` WHERE `ssl`='0' AND `ip` = :ipid ORDER BY `ip`, `port` ASC "); - Database::pexecute($result_ipsandports_stmt, array( - 'ipid' => $admin_ip['ip'] - )); - - $result_ssl_ipsandports_stmt = Database::prepare(" + Database::pexecute($result_ipsandports_stmt, array( + 'ipid' => $admin_ip['ip'] + )); + + $result_ssl_ipsandports_stmt = Database::prepare(" SELECT `id`, `ip`, `port` FROM `" . TABLE_PANEL_IPSANDPORTS . "` WHERE `ssl`='1' AND `ip` = :ipid ORDER BY `ip`, `port` ASC "); - Database::pexecute($result_ssl_ipsandports_stmt, array( - 'ipid' => $admin_ip['ip'] - )); + Database::pexecute($result_ssl_ipsandports_stmt, array( + 'ipid' => $admin_ip['ip'] + )); + } + + // Build array holding all IPs and Ports available to this admin + $ipsandports = array(); + while ($row_ipandport = $result_ipsandports_stmt->fetch(PDO::FETCH_ASSOC)) { + + if (filter_var($row_ipandport['ip'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { + $row_ipandport['ip'] = '[' . $row_ipandport['ip'] . ']'; } - - // Build array holding all IPs and Ports available to this admin - $ipsandports = array(); - while ($row_ipandport = $result_ipsandports_stmt->fetch(PDO::FETCH_ASSOC)) { - - if (filter_var($row_ipandport['ip'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { - $row_ipandport['ip'] = '[' . $row_ipandport['ip'] . ']'; - } - - $ipsandports[] = array( - 'label' => $row_ipandport['ip'] . ':' . $row_ipandport['port'] . '
    ', - 'value' => $row_ipandport['id'] - ); + + $ipsandports[] = array( + 'label' => $row_ipandport['ip'] . ':' . $row_ipandport['port'] . '
    ', + 'value' => $row_ipandport['id'] + ); + } + + $ssl_ipsandports = array(); + while ($row_ssl_ipandport = $result_ssl_ipsandports_stmt->fetch(PDO::FETCH_ASSOC)) { + + if (filter_var($row_ssl_ipandport['ip'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { + $row_ssl_ipandport['ip'] = '[' . $row_ssl_ipandport['ip'] . ']'; } - - $ssl_ipsandports = array(); - while ($row_ssl_ipandport = $result_ssl_ipsandports_stmt->fetch(PDO::FETCH_ASSOC)) { - - if (filter_var($row_ssl_ipandport['ip'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { - $row_ssl_ipandport['ip'] = '[' . $row_ssl_ipandport['ip'] . ']'; - } - - $ssl_ipsandports[] = array( - 'label' => $row_ssl_ipandport['ip'] . ':' . $row_ssl_ipandport['port'] . '
    ', - 'value' => $row_ssl_ipandport['id'] - ); - } - - $standardsubdomains = array(); - $result_standardsubdomains_stmt = Database::query(" + + $ssl_ipsandports[] = array( + 'label' => $row_ssl_ipandport['ip'] . ':' . $row_ssl_ipandport['port'] . '
    ', + 'value' => $row_ssl_ipandport['id'] + ); + } + + $standardsubdomains = array(); + $result_standardsubdomains_stmt = Database::query(" SELECT `id` FROM `" . TABLE_PANEL_DOMAINS . "` `d`, `" . TABLE_PANEL_CUSTOMERS . "` `c` WHERE `d`.`id` = `c`.`standardsubdomain` "); - - while ($row_standardsubdomain = $result_standardsubdomains_stmt->fetch(PDO::FETCH_ASSOC)) { - $standardsubdomains[] = $row_standardsubdomain['id']; - } - - if (count($standardsubdomains) > 0) { - $standardsubdomains = " AND `d`.`id` NOT IN (" . join(',', $standardsubdomains) . ") "; - } else { - $standardsubdomains = ''; - } - - $domains = makeoption($lng['domains']['noaliasdomain'], 0, NULL, true); - $result_domains_stmt = Database::prepare(" + + while ($row_standardsubdomain = $result_standardsubdomains_stmt->fetch(PDO::FETCH_ASSOC)) { + $standardsubdomains[] = $row_standardsubdomain['id']; + } + + if (count($standardsubdomains) > 0) { + $standardsubdomains = " AND `d`.`id` NOT IN (" . join(',', $standardsubdomains) . ") "; + } else { + $standardsubdomains = ''; + } + + $domains = makeoption($lng['domains']['noaliasdomain'], 0, NULL, true); + $result_domains_stmt = Database::prepare(" SELECT `d`.`id`, `d`.`domain`, `c`.`loginname` FROM `" . TABLE_PANEL_DOMAINS . "` `d`, `" . TABLE_PANEL_CUSTOMERS . "` `c` WHERE `d`.`aliasdomain` IS NULL AND `d`.`parentdomainid` = 0" . $standardsubdomains . ($userinfo['customers_see_all'] ? '' : " AND `d`.`adminid` = :adminid") . " AND `d`.`customerid`=`c`.`customerid` ORDER BY `loginname`, `domain` ASC "); - $params = array(); - if ($userinfo['customers_see_all'] == '0') { - $params['adminid'] = $userinfo['adminid']; - } - Database::pexecute($result_domains_stmt, $params); - - while ($row_domain = $result_domains_stmt->fetch(PDO::FETCH_ASSOC)) { - $domains .= makeoption($idna_convert->decode($row_domain['domain']) . ' (' . $row_domain['loginname'] . ')', $row_domain['id']); - } - - $subtodomains = makeoption($lng['domains']['nosubtomaindomain'], 0, NULL, true); - $result_domains_stmt = Database::prepare(" + $params = array(); + if ($userinfo['customers_see_all'] == '0') { + $params['adminid'] = $userinfo['adminid']; + } + Database::pexecute($result_domains_stmt, $params); + + while ($row_domain = $result_domains_stmt->fetch(PDO::FETCH_ASSOC)) { + $domains .= makeoption($idna_convert->decode($row_domain['domain']) . ' (' . $row_domain['loginname'] . ')', $row_domain['id']); + } + + $subtodomains = makeoption($lng['domains']['nosubtomaindomain'], 0, NULL, true); + $result_domains_stmt = Database::prepare(" SELECT `d`.`id`, `d`.`domain`, `c`.`loginname` FROM `" . TABLE_PANEL_DOMAINS . "` `d`, `" . TABLE_PANEL_CUSTOMERS . "` `c` WHERE `d`.`aliasdomain` IS NULL AND `d`.`parentdomainid` = 0 AND `d`.`ismainbutsubto` = 0 " . $standardsubdomains . ($userinfo['customers_see_all'] ? '' : " AND `d`.`adminid` = :adminid") . " AND `d`.`customerid`=`c`.`customerid` ORDER BY `loginname`, `domain` ASC "); - // params from above still valid - Database::pexecute($result_domains_stmt, $params); - - while ($row_domain = $result_domains_stmt->fetch(PDO::FETCH_ASSOC)) { - $subtodomains .= makeoption($idna_convert->decode($row_domain['domain']) . ' (' . $row_domain['loginname'] . ')', $row_domain['id']); - } - - $phpconfigs = ''; - $configs = Database::query(" + // params from above still valid + Database::pexecute($result_domains_stmt, $params); + + while ($row_domain = $result_domains_stmt->fetch(PDO::FETCH_ASSOC)) { + $subtodomains .= makeoption($idna_convert->decode($row_domain['domain']) . ' (' . $row_domain['loginname'] . ')', $row_domain['id']); + } + + $phpconfigs = ''; + $configs = Database::query(" SELECT c.*, fc.description as interpreter FROM `" . TABLE_PANEL_PHPCONFIGS . "` c LEFT JOIN `" . TABLE_PANEL_FPMDAEMONS . "` fc ON fc.id = c.fpmsettingid "); - - while ($row = $configs->fetch(PDO::FETCH_ASSOC)) { - if ((int) Settings::Get('phpfpm.enabled') == 1) { - $phpconfigs .= makeoption($row['description'] . " [".$row['interpreter']."]", $row['id'], Settings::Get('phpfpm.defaultini'), true, true); - } else { - $phpconfigs .= makeoption($row['description'], $row['id'], Settings::Get('system.mod_fcgid_defaultini'), true, true); - } + + while ($row = $configs->fetch(PDO::FETCH_ASSOC)) { + if ((int) Settings::Get('phpfpm.enabled') == 1) { + $phpconfigs .= makeoption($row['description'] . " [" . $row['interpreter'] . "]", $row['id'], Settings::Get('phpfpm.defaultini'), true, true); + } else { + $phpconfigs .= makeoption($row['description'], $row['id'], Settings::Get('system.mod_fcgid_defaultini'), true, true); } - - // create serveralias options - $serveraliasoptions = ""; - $serveraliasoptions .= makeoption($lng['domains']['serveraliasoption_wildcard'], '0', '0', true, true); - $serveraliasoptions .= makeoption($lng['domains']['serveraliasoption_www'], '1', '0', true, true); - $serveraliasoptions .= makeoption($lng['domains']['serveraliasoption_none'], '2', '0', true, true); - - $subcanemaildomain = makeoption($lng['admin']['subcanemaildomain']['never'], '0', '0', true, true); - $subcanemaildomain .= makeoption($lng['admin']['subcanemaildomain']['choosableno'], '1', '0', true, true); - $subcanemaildomain .= makeoption($lng['admin']['subcanemaildomain']['choosableyes'], '2', '0', true, true); - $subcanemaildomain .= makeoption($lng['admin']['subcanemaildomain']['always'], '3', '0', true, true); - - $add_date = date('Y-m-d'); - - $domain_add_data = include_once dirname(__FILE__) . '/lib/formfields/admin/domains/formfield.domains_add.php'; - $domain_add_form = htmlform::genHTMLForm($domain_add_data); - - $title = $domain_add_data['domain_add']['title']; - $image = $domain_add_data['domain_add']['image']; - - eval("echo \"" . getTemplate("domains/domains_add") . "\";"); } + + // create serveralias options + $serveraliasoptions = ""; + $serveraliasoptions .= makeoption($lng['domains']['serveraliasoption_wildcard'], '0', '0', true, true); + $serveraliasoptions .= makeoption($lng['domains']['serveraliasoption_www'], '1', '0', true, true); + $serveraliasoptions .= makeoption($lng['domains']['serveraliasoption_none'], '2', '0', true, true); + + $subcanemaildomain = makeoption($lng['admin']['subcanemaildomain']['never'], '0', '0', true, true); + $subcanemaildomain .= makeoption($lng['admin']['subcanemaildomain']['choosableno'], '1', '0', true, true); + $subcanemaildomain .= makeoption($lng['admin']['subcanemaildomain']['choosableyes'], '2', '0', true, true); + $subcanemaildomain .= makeoption($lng['admin']['subcanemaildomain']['always'], '3', '0', true, true); + + $add_date = date('Y-m-d'); + + $domain_add_data = include_once dirname(__FILE__) . '/lib/formfields/admin/domains/formfield.domains_add.php'; + $domain_add_form = htmlform::genHTMLForm($domain_add_data); + + $title = $domain_add_data['domain_add']['title']; + $image = $domain_add_data['domain_add']['image']; + + eval("echo \"" . getTemplate("domains/domains_add") . "\";"); } } elseif ($action == 'edit' && $id != 0) { - - $result_stmt = Database::prepare(" - SELECT `d`.*, `c`.`customerid` - FROM `" . TABLE_PANEL_DOMAINS . "` `d` - LEFT JOIN `" . TABLE_PANEL_CUSTOMERS . "` `c` USING(`customerid`) - WHERE `d`.`parentdomainid` = '0' - AND `d`.`id` = :id" . ($userinfo['customers_see_all'] ? '' : " AND `d`.`adminid` = :adminid") - ); - $params = array( - 'id' => $id - ); - if ($userinfo['customers_see_all'] == '0') { - $params['adminid'] = $userinfo['adminid']; + + try { + $json_result = Domains::getLocal($userinfo, array( + 'id' => $id + ))->get(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); } - $result = Database::pexecute_first($result_stmt, $params); - + $result = json_decode($json_result, true)['data']; + if ($result['domain'] != '') { - + $subdomains_stmt = Database::prepare(" SELECT COUNT(`id`) AS count FROM `" . TABLE_PANEL_DOMAINS . "` WHERE `parentdomainid` = :resultid @@ -1150,7 +363,7 @@ if ($page == 'domains' || $page == 'overview') { 'resultid' => $result['id'] )); $subdomains = $subdomains['count']; - + $alias_check_stmt = Database::prepare(" SELECT COUNT(`id`) AS count FROM `" . TABLE_PANEL_DOMAINS . "` WHERE `aliasdomain` = :resultid @@ -1159,7 +372,7 @@ if ($page == 'domains' || $page == 'overview') { 'resultid' => $result['id'] )); $alias_check = $alias_check['count']; - + $domain_emails_result_stmt = Database::prepare(" SELECT `email`, `email_full`, `destination`, `popaccountid` AS `number_email_forwarders` FROM `" . TABLE_MAIL_VIRTUAL . "` WHERE `customerid` = :customerid AND `domainid` = :id @@ -1168,876 +381,49 @@ if ($page == 'domains' || $page == 'overview') { 'customerid' => $result['customerid'], 'id' => $result['id'] )); - + $emails = Database::num_rows(); $email_forwarders = 0; $email_accounts = 0; - + while ($domain_emails_row = $domain_emails_result_stmt->fetch(PDO::FETCH_ASSOC)) { - + if ($domain_emails_row['destination'] != '') { - + $domain_emails_row['destination'] = explode(' ', makeCorrectDestination($domain_emails_row['destination'])); $email_forwarders += count($domain_emails_row['destination']); - + if (in_array($domain_emails_row['email_full'], $domain_emails_row['destination'])) { $email_forwarders -= 1; $email_accounts ++; } } } - + $ipsresult_stmt = Database::prepare(" SELECT `id_ipandports` FROM `" . TABLE_DOMAINTOIP . "` WHERE `id_domain` = :id "); Database::pexecute($ipsresult_stmt, array( 'id' => $result['id'] )); - + $usedips = array(); while ($ipsresultrow = $ipsresult_stmt->fetch(PDO::FETCH_ASSOC)) { $usedips[] = $ipsresultrow['id_ipandports']; } - + if (isset($_POST['send']) && $_POST['send'] == 'send') { - - $customer_stmt = Database::prepare(" - SELECT * FROM " . TABLE_PANEL_CUSTOMERS . " WHERE `customerid` = :customerid - "); - $customer = Database::pexecute_first($customer_stmt, array( - 'customerid' => $result['customerid'] - )); - - $customerid = - 1; - if (isset($_POST['customerid'])) { - $customerid = intval($_POST['customerid']); - } - - if ($customerid > 0 && $customerid != $result['customerid'] && Settings::Get('panel.allow_domain_change_customer') == '1') { - - $customer_stmt = Database::prepare(" - SELECT * FROM `" . TABLE_PANEL_CUSTOMERS . "` - WHERE `customerid` = :customerid - AND (`subdomains_used` + :subdomains <= `subdomains` OR `subdomains` = '-1' ) - AND (`emails_used` + :emails <= `emails` OR `emails` = '-1' ) - AND (`email_forwarders_used` + :forwarders <= `email_forwarders` OR `email_forwarders` = '-1' ) - AND (`email_accounts_used` + :accounts <= `email_accounts` OR `email_accounts` = '-1' ) " . ($userinfo['customers_see_all'] ? '' : " AND `adminid` = :adminid")); - - $params = array( - 'customerid' => $customerid, - 'subdomains' => $subdomains, - 'emails' => $emails, - 'forwarders' => $email_forwarders, - 'accounts' => $email_accounts - ); - if ($userinfo['customers_see_all'] == '0') { - $params['adminid'] = $userinfo['adminid']; - } - - $customer = Database::pexecute_first($customer_stmt, $params); - if (empty($customer) || $customer['customerid'] != $customerid) { - standard_error('customerdoesntexist'); - } - } else { - $customerid = $result['customerid']; - } - - $customer_stmt = Database::prepare(" - SELECT * FROM " . TABLE_PANEL_ADMINS . " WHERE `adminid` = :adminid - "); - $admin = Database::pexecute_first($customer_stmt, array( - 'adminid' => $result['adminid'] - )); - - if ($userinfo['customers_see_all'] == '1') { - - $adminid = - 1; - if (isset($_POST['adminid'])) { - $adminid = intval($_POST['adminid']); - } - - if ($adminid > 0 && $adminid != $result['adminid'] && Settings::Get('panel.allow_domain_change_admin') == '1') { - - $admin_stmt = Database::prepare(" - SELECT * FROM `" . TABLE_PANEL_ADMINS . "` - WHERE `adminid` = :adminid AND ( `domains_used` < `domains` OR `domains` = '-1' ) - "); - $admin = Database::pexecute_first($admin_stmt, array( - 'adminid' => $adminid - )); - - if (empty($admin) || $admin['adminid'] != $adminid) { - standard_error('admindoesntexist'); - } - } else { - $adminid = $result['adminid']; - } - } else { - $adminid = $result['adminid']; - } - - $aliasdomain = isset($_POST['alias']) ? intval($_POST['alias']) : 0; - $issubof = intval($_POST['issubof']); - $subcanemaildomain = intval($_POST['subcanemaildomain']); - $caneditdomain = isset($_POST['caneditdomain']) ? intval($_POST['caneditdomain']) : 0; - $registration_date = trim($_POST['registration_date']); - $registration_date = validate($registration_date, 'registration_date', '/^(19|20)\d\d[-](0[1-9]|1[012])[-](0[1-9]|[12][0-9]|3[01])$/', '', array( - '0000-00-00', - '0', - '' - )); - if ($registration_date == '0000-00-00') { - $registration_date = null; - } - $termination_date = trim($_POST['termination_date']); - $termination_date = validate($termination_date, 'termination_date', '/^(19|20)\d\d[-](0[1-9]|1[012])[-](0[1-9]|[12][0-9]|3[01])$/', '', array( - '0000-00-00', - '0', - '' - )); - if ($termination_date == '0000-00-00') { - $termination_date = null; - } - - $isemaildomain = 0; - if (isset($_POST['isemaildomain'])) { - $isemaildomain = intval($_POST['isemaildomain']); - } - - $email_only = 0; - if (isset($_POST['email_only'])) { - $email_only = intval($_POST['email_only']); - } - - $serveraliasoption = '2'; - if ($result['iswildcarddomain'] == '1') { - $serveraliasoption = '0'; - } elseif ($result['wwwserveralias'] == '1') { - $serveraliasoption = '1'; - } - if (isset($_POST['selectserveralias'])) { - $serveraliasoption = intval($_POST['selectserveralias']); - } - - $speciallogfile = 0; - if (isset($_POST['speciallogfile'])) - $speciallogfile = intval($_POST['speciallogfile']); - - if ($userinfo['change_serversettings'] == '1') { - $isbinddomain = $result['isbinddomain']; - $zonefile = $result['zonefile']; - if (Settings::Get('system.bind_enable') == '1') { - if (isset($_POST['isbinddomain'])) { - $isbinddomain = (int) $_POST['isbinddomain']; - } else { - $isbinddomain = 0; - } - $zonefile = validate($_POST['zonefile'], 'zonefile'); - } - - if (Settings::Get('dkim.use_dkim') == '1') { - $dkim = isset($_POST['dkim']) ? 1 : 0; - } else { - $dkim = $result['dkim']; - } - - $specialsettings = validate(str_replace("\r\n", "\n", $_POST['specialsettings']), 'specialsettings', '/^[^\0]*$/'); - $ssfs = (isset($_POST['specialsettingsforsubdomains']) && intval($_POST['specialsettingsforsubdomains']) == 1) ? 1 : 0; - $notryfiles = isset($_POST['notryfiles']) && (int)$_POST['notryfiles'] == 1 ? 1 : 0; - $documentroot = validate($_POST['documentroot'], 'documentroot'); - - if ($documentroot == '') { - // If path is empty and 'Use domain name as default value for DocumentRoot path' is enabled in settings, - // set default path to subdomain or domain name - if (Settings::Get('system.documentroot_use_default_value') == 1) { - $documentroot = makeCorrectDir($customer['documentroot'] . '/' . $result['domain']); - } else { - $documentroot = $customer['documentroot']; - } - } - - if (! preg_match('/^https?\:\/\//', $documentroot) && strstr($documentroot, ":") !== false) { - standard_error('pathmaynotcontaincolon'); - } - } else { - $isbinddomain = $result['isbinddomain']; - $zonefile = $result['zonefile']; - $dkim = $result['dkim']; - $specialsettings = $result['specialsettings']; - $ssfs = (empty($specialsettings) ? 0 : 1); - $notryfiles = $result['notryfiles']; - $documentroot = $result['documentroot']; - } - - $speciallogverified = (isset($_POST['speciallogverified']) ? (int) $_POST['speciallogverified'] : 0); - - if ($userinfo['caneditphpsettings'] == '1' || $userinfo['change_serversettings'] == '1') { - - $phpenabled = isset($_POST['phpenabled']) ? intval($_POST['phpenabled']) : 0; - $openbasedir = isset($_POST['openbasedir']) ? intval($_POST['openbasedir']) : 0; - $phpfs = (isset($_POST['phpsettingsforsubdomains']) && intval($_POST['phpsettingsforsubdomains']) == 1) ? 1 : 0; - - if ((int) Settings::Get('system.mod_fcgid') == 1 || (int) Settings::Get('phpfpm.enabled') == 1) { - $phpsettingid = (int) $_POST['phpsettingid']; - $phpsettingid_check_stmt = Database::prepare(" - SELECT * FROM `" . TABLE_PANEL_PHPCONFIGS . "` WHERE `id` = :phpid - "); - $phpsettingid_check = Database::pexecute_first($phpsettingid_check_stmt, array( - 'phpid' => $phpsettingid - )); - - if (! isset($phpsettingid_check['id']) || $phpsettingid_check['id'] == '0' || $phpsettingid_check['id'] != $phpsettingid) { - standard_error('phpsettingidwrong'); - } - - if ((int) Settings::Get('system.mod_fcgid') == 1) { - $mod_fcgid_starter = validate($_POST['mod_fcgid_starter'], 'mod_fcgid_starter', '/^[0-9]*$/', '', array( - '-1', - '' - )); - $mod_fcgid_maxrequests = validate($_POST['mod_fcgid_maxrequests'], 'mod_fcgid_maxrequests', '/^[0-9]*$/', '', array( - '-1', - '' - )); - } else { - $mod_fcgid_starter = $result['mod_fcgid_starter']; - $mod_fcgid_maxrequests = $result['mod_fcgid_maxrequests']; - } - } else { - $phpsettingid = $result['phpsettingid']; - $phpfs = 1; - $mod_fcgid_starter = $result['mod_fcgid_starter']; - $mod_fcgid_maxrequests = $result['mod_fcgid_maxrequests']; - } - } else { - $phpenabled = $result['phpenabled']; - $openbasedir = $result['openbasedir']; - $phpsettingid = $result['phpsettingid']; - $phpfs = 1; - $mod_fcgid_starter = $result['mod_fcgid_starter']; - $mod_fcgid_maxrequests = $result['mod_fcgid_maxrequests']; - } - - $ipandports = array(); - if (isset($_POST['ipandport']) && ! is_array($_POST['ipandport'])) { - $_POST['ipandport'] = unserialize($_POST['ipandport']); - } - if (isset($_POST['ipandport']) && is_array($_POST['ipandport'])) { - - $ipandport_check_stmt = Database::prepare(" - SELECT `id`, `ip`, `port` FROM `" . TABLE_PANEL_IPSANDPORTS . "` WHERE `id` = :ipandport - "); - foreach ($_POST['ipandport'] as $ipandport) { - if (trim($ipandport) == "") - continue; - $ipandport = intval($ipandport); - $ipandport_check = Database::pexecute_first($ipandport_check_stmt, array( - 'ipandport' => $ipandport - )); - if (! isset($ipandport_check['id']) || $ipandport_check['id'] == '0' || $ipandport_check['id'] != $ipandport) { - standard_error('ipportdoesntexist'); - } else { - $ipandports[] = $ipandport; - } - } - } - - if (Settings::Get('system.use_ssl') == '1' && isset($_POST['ssl_ipandport'])) { - $ssl = 1; // if ssl is set and != 0, it can only be 1 - $ssl_redirect = 0; - if (isset($_POST['ssl_redirect'])) { - $ssl_redirect = (int) $_POST['ssl_redirect']; - } - - $letsencrypt = 0; - if (isset($_POST['letsencrypt'])) { - $letsencrypt = (int) $_POST['letsencrypt']; - } - - $http2 = isset($_POST['http2']) && (int)$_POST['http2'] == 1 ? 1 : 0; - - // HSTS - $hsts_maxage = isset($_POST['hsts_maxage']) ? (int)$_POST['hsts_maxage'] : 0; - $hsts_sub = isset($_POST['hsts_sub']) && (int)$_POST['hsts_sub'] == 1 ? 1 : 0; - $hsts_preload = isset($_POST['hsts_preload']) && (int)$_POST['hsts_preload'] == 1 ? 1 : 0; - - // OCSP stapling - $ocsp_stapling = isset($_POST['ocsp_stapling']) && (int)$_POST['ocsp_stapling'] == 1 ? 1 : 0; - - $ssl_ipandports = array(); - if (isset($_POST['ssl_ipandport']) && ! is_array($_POST['ssl_ipandport'])) { - $_POST['ssl_ipandport'] = unserialize($_POST['ssl_ipandport']); - } - if (isset($_POST['ssl_ipandport']) && is_array($_POST['ssl_ipandport'])) { - - $ssl_ipandport_check_stmt = Database::prepare(" - SELECT `id`, `ip`, `port` FROM `" . TABLE_PANEL_IPSANDPORTS . "` WHERE `id` = :ipandport - "); - foreach ($_POST['ssl_ipandport'] as $ssl_ipandport) { - if (trim($ssl_ipandport) == "") - continue; - // fix if ip/port got de-checked and it was the last one - if (trim($ssl_ipandport) < 1) - continue; - $ssl_ipandport = intval($ssl_ipandport); - $ssl_ipandport_check = Database::pexecute_first($ssl_ipandport_check_stmt, array( - 'ipandport' => $ssl_ipandport - )); - if (! isset($ssl_ipandport_check['id']) || $ssl_ipandport_check['id'] == '0' || $ssl_ipandport_check['id'] != $ssl_ipandport) { - standard_error('ipportdoesntexist'); - } else { - $ssl_ipandports[] = $ssl_ipandport; - } - } - - } else { - $ssl_redirect = 0; - $letsencrypt = 0; - $http2 = 0; - // we need this for the serialize - // if ssl is disabled or no ssl-ip/port exists - $ssl_ipandports[] = - 1; - - // HSTS - $hsts_maxage = 0; - $hsts_sub = 0; - $hsts_preload = 0; - - // OCSP stapling - $ocsp_stapling = 0; - } - } else { - $ssl_redirect = 0; - $letsencrypt = 0; - $http2 = 0; - // we need this for the serialize - // if ssl is disabled or no ssl-ip/port exists - $ssl_ipandports[] = - 1; - - // HSTS - $hsts_maxage = 0; - $hsts_sub = 0; - $hsts_preload = 0; - - // OCSP stapling - $ocsp_stapling = 0; - } - - // We can't enable let's encrypt for wildcard domains when using acme-v1 - if ($serveraliasoption == '0' && $letsencrypt == '1' && Settings::Get('system.leapiversion') == '1') { - standard_error('nowildcardwithletsencrypt'); - } - // if using acme-v2 we cannot issue wildcard-certificates - // because they currently only support the dns-01 challenge - if ($serveraliasoption == '0' && $letsencrypt == '1' && Settings::Get('system.leapiversion') == '2') { - standard_error('nowildcardwithletsencryptv2'); - } - - // Temporarily deactivate ssl_redirect until Let's Encrypt certificate was generated - if ($ssl_redirect > 0 && $letsencrypt == 1 && $result['letsencrypt'] != $letsencrypt) { - $ssl_redirect = 2; - } - - if (! preg_match('/^https?\:\/\//', $documentroot)) { - $documentroot = makeCorrectDir($documentroot); - } - - if ($phpenabled != '1') { - $phpenabled = '0'; - } - - if ($openbasedir != '1') { - $openbasedir = '0'; - } - - if ($isbinddomain != '1') { - $isbinddomain = '0'; - } - - if ($isemaildomain != '1') { - $isemaildomain = '0'; - } - - if ($email_only == '1') { - $isemaildomain = '1'; - } else { - $email_only = '0'; - } - - if ($subcanemaildomain != '1' && $subcanemaildomain != '2' && $subcanemaildomain != '3') { - $subcanemaildomain = '0'; - } - - if ($dkim != '1') { - $dkim = '0'; - } - - if ($caneditdomain != '1') { - $caneditdomain = '0'; - } - - $aliasdomain_check = array( - 'id' => 0 - ); - - if ($aliasdomain != 0) { - // Overwrite given ipandports with these of the "main" domain - $ipandports = array(); - $ssl_ipandports = array(); - $origipresult_stmt = Database::prepare(" - SELECT `id_ipandports` FROM `" . TABLE_DOMAINTOIP . "` WHERE `id_domain` = :aliasdomain - "); - Database::pexecute($origipresult_stmt, array( - 'aliasdomain' => $aliasdomain - )); - $ipdata_stmt = Database::prepare("SELECT * FROM `" . TABLE_PANEL_IPSANDPORTS . "` WHERE `id` = :ipid"); - while ($origip = $origipresult_stmt->fetch(PDO::FETCH_ASSOC)) { - $_origip_tmp = Database::pexecute_first($ipdata_stmt, array( - 'ipid' => $origip['id_ipandports'] - )); - if ($_origip_tmp['ssl'] == 0) { - $ipandports[] = $origip['id_ipandports']; - } else { - $ssl_ipandports[] = $origip['id_ipandports']; - } - } - - if (count($ssl_ipandports) == 0) { - // we need this for the serialize - // if ssl is disabled or no ssl-ip/port exists - $ssl_ipandports[] = - 1; - } - - $aliasdomain_check_stmt = Database::prepare(" - SELECT `d`.`id` FROM `" . TABLE_PANEL_DOMAINS . "` `d`, `" . TABLE_PANEL_CUSTOMERS . "` `c` - WHERE `d`.`customerid` = :customerid - AND `d`.`aliasdomain` IS NULL AND `d`.`id` <> `c`.`standardsubdomain` - AND `c`.`customerid` = :customerid - AND `d`.`id` = :aliasdomain - "); - $aliasdomain_check = Database::pexecute_first($aliasdomain_check_stmt, array( - 'customerid' => $customerid, - 'aliasdomain' => $aliasdomain - )); - } - - if (count($ipandports) == 0) { - standard_error('noipportgiven'); - } - - if ($aliasdomain_check['id'] != $aliasdomain) { - standard_error('domainisaliasorothercustomer'); - } - - if ($issubof <= '0') { - $issubof = '0'; - } - - if ($serveraliasoption != '1' && $serveraliasoption != '2') { - $serveraliasoption = '0'; - } - - $params = array( - 'id' => $id, - 'page' => $page, - 'action' => $action, - 'customerid' => $customerid, - 'adminid' => $adminid, - 'documentroot' => $documentroot, - 'alias' => $aliasdomain, - 'isbinddomain' => $isbinddomain, - 'isemaildomain' => $isemaildomain, - 'email_only' => $email_only, - 'subcanemaildomain' => $subcanemaildomain, - 'caneditdomain' => $caneditdomain, - 'zonefile' => $zonefile, - 'dkim' => $dkim, - 'selectserveralias' => $serveraliasoption, - 'ssl_redirect' => $ssl_redirect, - 'phpenabled' => $phpenabled, - 'openbasedir' => $openbasedir, - 'phpsettingid' => $phpsettingid, - 'phpsettingsforsubdomains' => $phpfs, - 'mod_fcgid_starter' => $mod_fcgid_starter, - 'mod_fcgid_maxrequests' => $mod_fcgid_maxrequests, - 'specialsettings' => $specialsettings, - 'specialsettingsforsubdomains' => $ssfs, - 'notryfiles' => $notryfiles, - 'registration_date' => $registration_date, - 'termination_date' => $termination_date, - 'issubof' => $issubof, - 'speciallogfile' => $speciallogfile, - 'speciallogverified' => $speciallogverified, - 'ipandport' => serialize($ipandports), - 'ssl_ipandport' => serialize($ssl_ipandports), - 'letsencrypt' => $letsencrypt, - 'http2' => $http2, - 'hsts_maxage' => $hsts_maxage, - 'hsts_sub' => $hsts_sub, - 'hsts_preload' => $hsts_preload, - 'ocsp_stapling' => $ocsp_stapling - ); - - $security_questions = array( - 'reallydisablesecuritysetting' => ($openbasedir == '0' && $userinfo['change_serversettings'] == '1'), - 'reallydocrootoutofcustomerroot' => (substr($documentroot, 0, strlen($customer['documentroot'])) != $customer['documentroot'] && ! preg_match('/^https?\:\/\//', $documentroot)) - ); - foreach ($security_questions as $question_name => $question_launch) { - if ($question_launch !== false) { - $params[$question_name] = $question_name; - if (! isset($_POST[$question_name]) || $_POST[$question_name] != $question_name) { - ask_yesno('admin_domain_' . $question_name, $filename, $params); - } - } - } - - $wwwserveralias = ($serveraliasoption == '1') ? '1' : '0'; - $iswildcarddomain = ($serveraliasoption == '0') ? '1' : '0'; - - if ( - $documentroot != $result['documentroot'] || - $ssl_redirect != $result['ssl_redirect'] || - $wwwserveralias != $result['wwwserveralias'] || - $iswildcarddomain != $result['iswildcarddomain'] || - $phpenabled != $result['phpenabled'] || - $openbasedir != $result['openbasedir'] || - $phpsettingid != $result['phpsettingid'] || - $mod_fcgid_starter != $result['mod_fcgid_starter'] || - $mod_fcgid_maxrequests != $result['mod_fcgid_maxrequests'] || - $specialsettings != $result['specialsettings'] || - $notryfiles != $result['notryfiles'] || - $aliasdomain != $result['aliasdomain'] || - $issubof != $result['ismainbutsubto'] || - $email_only != $result['email_only'] || - ($speciallogfile != $result['speciallogfile'] && $speciallogverified == '1') || - $letsencrypt != $result['letsencrypt'] || - $http2 != $result['http2'] || - $hsts_maxage != $result['hsts'] || - $hsts_sub != $result['hsts_sub'] || - $hsts_preload != $result['hsts_preload'] || - $ocsp_stapling != $result['ocsp_stapling'] - ) { - inserttask('1'); - } - - if ($speciallogfile != $result['speciallogfile'] && $speciallogverified != '1') { - $speciallogfile = $result['speciallogfile']; - } - - if ($isbinddomain != $result['isbinddomain'] || $zonefile != $result['zonefile'] || $dkim != $result['dkim']) { - inserttask('4'); - } - - if ($isemaildomain == '0' && $result['isemaildomain'] == '1') { - $del_stmt = Database::prepare(" - DELETE FROM `" . TABLE_MAIL_USERS . "` WHERE `domainid` = :id - "); - Database::pexecute($del_stmt, array( - 'id' => $id - )); - - $del_stmt = Database::prepare(" - DELETE FROM `" . TABLE_MAIL_VIRTUAL . "` WHERE `domainid` = :id - "); - Database::pexecute($del_stmt, array( - 'id' => $id - )); - $log->logAction(ADM_ACTION, LOG_NOTICE, "deleted domain #" . $id . " from mail-tables"); - } - - // check whether LE has been disabled, so we remove the certificate - if ($letsencrypt == '0' && $result['letsencrypt'] == '1') { - $del_stmt = Database::prepare(" - DELETE FROM `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` WHERE `domainid` = :id - "); - Database::pexecute($del_stmt, array( - 'id' => $id - )); - } - - $updatechildren = ''; - - if ($subcanemaildomain == '0' && $result['subcanemaildomain'] != '0') { - $updatechildren = ", `isemaildomain` = '0' "; - } elseif ($subcanemaildomain == '3' && $result['subcanemaildomain'] != '3') { - $updatechildren = ", `isemaildomain` = '1' "; - } - - if ($customerid != $result['customerid'] && Settings::Get('panel.allow_domain_change_customer') == '1') { - $upd_data = array( - 'customerid' => $customerid, - 'domainid' => $result['id'] - ); - $upd_stmt = Database::prepare(" - UPDATE `" . TABLE_MAIL_USERS . "` SET `customerid` = :customerid WHERE `domainid` = :domainid - "); - Database::pexecute($upd_stmt, $upd_data); - $upd_stmt = Database::prepare(" - UPDATE `" . TABLE_MAIL_VIRTUAL . "` SET `customerid` = :customerid WHERE `domainid` = :domainid - "); - Database::pexecute($upd_stmt, $upd_data); - $upd_data = array( - 'subdomains' => $subdomains, - 'emails' => $emails, - 'forwarders' => $email_forwarders, - 'accounts' => $email_accounts - ); - $upd_data['customerid'] = $customerid; - $upd_stmt = Database::prepare(" - UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET - `subdomains_used` = `subdomains_used` + :subdomains, - `emails_used` = `emails_used` + :emails, - `email_forwarders_used` = `email_forwarders_used` + :forwarders, - `email_accounts_used` = `email_accounts_used` + :accounts - WHERE `customerid` = :customerid - "); - Database::pexecute($upd_stmt, $upd_data); - - $upd_data['customerid'] = $result['customerid']; - $upd_stmt = Database::prepare(" - UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET - `subdomains_used` = `subdomains_used` - :subdomains, - `emails_used` = `emails_used` - :emails, - `email_forwarders_used` = `email_forwarders_used` - :forwarders, - `email_accounts_used` = `email_accounts_used` - :accounts - WHERE `customerid` = :customerid - "); - Database::pexecute($upd_stmt, $upd_data); - } - - if ($adminid != $result['adminid'] && Settings::Get('panel.allow_domain_change_admin') == '1') { - $upd_stmt = Database::prepare(" - UPDATE `" . TABLE_PANEL_ADMINS . "` SET `domains_used` = `domains_used` + 1 WHERE `adminid` = :adminid - "); - Database::pexecute($upd_stmt, array( - 'adminid' => $adminid - )); - - $upd_stmt = Database::prepare(" - UPDATE `" . TABLE_PANEL_ADMINS . "` SET `domains_used` = `domains_used` - 1 WHERE `adminid` = :adminid - "); - Database::pexecute($upd_stmt, array( - 'adminid' => $result['adminid'] - )); - } - - $_update_data = array(); - - $ssfs = isset($_POST['specialsettingsforsubdomains']) ? 1 : 0; - if ($ssfs == 1) { - $_update_data['specialsettings'] = $specialsettings; - $upd_specialsettings = ", `specialsettings` = :specialsettings "; - } else { - $upd_specialsettings = ''; - unset($_update_data['specialsettings']); - $upd_stmt = Database::prepare(" - UPDATE `" . TABLE_PANEL_DOMAINS . "` SET `specialsettings`='' WHERE `parentdomainid` = :id - "); - Database::pexecute($upd_stmt, array( - 'id' => $id - )); - $log->logAction(ADM_ACTION, LOG_INFO, "removed specialsettings on all subdomains of domain #" . $id); - } - - $wwwserveralias = ($serveraliasoption == '1') ? '1' : '0'; - $iswildcarddomain = ($serveraliasoption == '0') ? '1' : '0'; - - $update_data = array(); - $update_data['customerid'] = $customerid; - $update_data['adminid'] = $adminid; - $update_data['documentroot'] = $documentroot; - $update_data['ssl_redirect'] = $ssl_redirect; - $update_data['aliasdomain'] = ($aliasdomain != 0 && $alias_check == 0) ? $aliasdomain : null; - $update_data['isbinddomain'] = $isbinddomain; - $update_data['isemaildomain'] = $isemaildomain; - $update_data['email_only'] = $email_only; - $update_data['subcanemaildomain'] = $subcanemaildomain; - $update_data['dkim'] = $dkim; - $update_data['caneditdomain'] = $caneditdomain; - $update_data['zonefile'] = $zonefile; - $update_data['wwwserveralias'] = $wwwserveralias; - $update_data['iswildcarddomain'] = $iswildcarddomain; - $update_data['phpenabled'] = $phpenabled; - $update_data['openbasedir'] = $openbasedir; - $update_data['speciallogfile'] = $speciallogfile; - $update_data['phpsettingid'] = $phpsettingid; - $update_data['mod_fcgid_starter'] = $mod_fcgid_starter; - $update_data['mod_fcgid_maxrequests'] = $mod_fcgid_maxrequests; - $update_data['specialsettings'] = $specialsettings; - $update_data['notryfiles'] = $notryfiles; - $update_data['registration_date'] = $registration_date; - $update_data['termination_date'] = $termination_date; - $update_data['ismainbutsubto'] = $issubof; - $update_data['letsencrypt'] = $letsencrypt; - $update_data['http2'] = $http2; - $update_data['hsts'] = $hsts_maxage; - $update_data['hsts_sub'] = $hsts_sub; - $update_data['hsts_preload'] = $hsts_preload; - $update_data['ocsp_stapling'] = $ocsp_stapling; - $update_data['id'] = $id; - - $update_stmt = Database::prepare(" - UPDATE `" . TABLE_PANEL_DOMAINS . "` SET - `customerid` = :customerid, - `adminid` = :adminid, - `documentroot` = :documentroot, - `ssl_redirect` = :ssl_redirect, - `aliasdomain` = :aliasdomain, - `isbinddomain` = :isbinddomain, - `isemaildomain` = :isemaildomain, - `email_only` = :email_only, - `subcanemaildomain` = :subcanemaildomain, - `dkim` = :dkim, - `caneditdomain` = :caneditdomain, - `zonefile` = :zonefile, - `wwwserveralias` = :wwwserveralias, - `iswildcarddomain` = :iswildcarddomain, - `phpenabled` = :phpenabled, - `openbasedir` = :openbasedir, - `speciallogfile` = :speciallogfile, - `phpsettingid` = :phpsettingid, - `mod_fcgid_starter` = :mod_fcgid_starter, - `mod_fcgid_maxrequests` = :mod_fcgid_maxrequests, - `specialsettings` = :specialsettings, - `notryfiles` = :notryfiles, - `registration_date` = :registration_date, - `termination_date` = :termination_date, - `ismainbutsubto` = :ismainbutsubto, - `letsencrypt` = :letsencrypt, - `http2` = :http2, - `hsts` = :hsts, - `hsts_sub` = :hsts_sub, - `hsts_preload` = :hsts_preload, - `ocsp_stapling` = :ocsp_stapling - WHERE `id` = :id - "); - Database::pexecute($update_stmt, $update_data); - - $_update_data['customerid'] = $customerid; - $_update_data['adminid'] = $adminid; - $_update_data['phpenabled'] = $phpenabled; - $_update_data['openbasedir'] = $openbasedir; - $_update_data['mod_fcgid_starter'] = $mod_fcgid_starter; - $_update_data['mod_fcgid_maxrequests'] = $mod_fcgid_maxrequests; - $_update_data['parentdomainid'] = $id; - - // if php config is to be set for all subdomains, check here - $update_phpconfig = ''; - $phpfs = isset($_POST['phpsettingsforsubdomains']) ? 1 : 0; - if ($phpfs == 1) { - $_update_data['phpsettingid'] = $phpsettingid; - $update_phpconfig = ", `phpsettingid` = :phpsettingid"; - } - - // if we have no more ssl-ip's for this domain, - // all its subdomains must have "ssl-redirect = 0" - // and disable let's encrypt - $update_sslredirect = ''; - if (count($ssl_ipandports) == 1 && $ssl_ipandports[0] == - 1) { - $update_sslredirect = ", `ssl_redirect` = '0', `letsencrypt` = '0' "; - } - - $_update_stmt = Database::prepare(" - UPDATE `" . TABLE_PANEL_DOMAINS . "` SET - `customerid` = :customerid, - `adminid` = :adminid, - `phpenabled` = :phpenabled, - `openbasedir` = :openbasedir, - `mod_fcgid_starter` = :mod_fcgid_starter, - `mod_fcgid_maxrequests` = :mod_fcgid_maxrequests - " . $update_phpconfig . $upd_specialsettings . $updatechildren . $update_sslredirect . " - WHERE `parentdomainid` = :parentdomainid - "); - Database::pexecute($_update_stmt, $_update_data); - - // FIXME check how many we got and if the amount of assigned IP's - // has changed so we can insert a config-rebuild task if only - // the ip's of this domain were changed - // -> for now, always insert a rebuild-task - inserttask('1'); - - // Cleanup domain <-> ip mapping - $del_stmt = Database::prepare(" - DELETE FROM `" . TABLE_DOMAINTOIP . "` WHERE `id_domain` = :id - "); - Database::pexecute($del_stmt, array( - 'id' => $id - )); - - $ins_stmt = Database::prepare(" - INSERT INTO `" . TABLE_DOMAINTOIP . "` SET `id_domain` = :domainid, `id_ipandports` = :ipportid - "); - - foreach ($ipandports as $ipportid) { - Database::pexecute($ins_stmt, array( - 'domainid' => $id, - 'ipportid' => $ipportid - )); - } - foreach ($ssl_ipandports as $ssl_ipportid) { - if ($ssl_ipportid > 0) { - Database::pexecute($ins_stmt, array( - 'domainid' => $id, - 'ipportid' => $ssl_ipportid - )); - } - } - - // Cleanup domain <-> ip mapping for subdomains - $domainidsresult_stmt = Database::prepare(" - SELECT `id` FROM `" . TABLE_PANEL_DOMAINS . "` WHERE `parentdomainid` = :id - "); - Database::pexecute($domainidsresult_stmt, array( - 'id' => $id - )); - - while ($row = $domainidsresult_stmt->fetch(PDO::FETCH_ASSOC)) { - - $del_stmt = Database::prepare(" - DELETE FROM `" . TABLE_DOMAINTOIP . "` WHERE `id_domain` = :rowid - "); - Database::pexecute($del_stmt, array( - 'rowid' => $row['id'] - )); - - $ins_stmt = Database::prepare(" - INSERT INTO `" . TABLE_DOMAINTOIP . "` SET - `id_domain` = :rowid, - `id_ipandports` = :ipportid - "); - - foreach ($ipandports as $ipportid) { - Database::pexecute($ins_stmt, array( - 'rowid' => $row['id'], - 'ipportid' => $ipportid - )); - } - foreach ($ssl_ipandports as $ssl_ipportid) { - if ($ssl_ipportid > 0) { - Database::pexecute($ins_stmt, array( - 'rowid' => $row['id'], - 'ipportid' => $ssl_ipportid - )); - } - } - } - if ($result['aliasdomain'] != $aliasdomain) { - // trigger when domain id for alias destination has changed: both for old and new destination - triggerLetsEncryptCSRForAliasDestinationDomain($result['aliasdomain'], $log); - triggerLetsEncryptCSRForAliasDestinationDomain($aliasdomain, $log); - } else - if ($result['wwwserveralias'] != $wwwserveralias || $result['letsencrypt'] != $letsencrypt) { - // or when wwwserveralias or letsencrypt was changed - triggerLetsEncryptCSRForAliasDestinationDomain($aliasdomain, $log); - } - - $log->logAction(ADM_ACTION, LOG_INFO, "edited domain #" . $id); + try { + Domains::getLocal($userinfo, $_POST)->update(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); + } redirectTo($filename, array( 'page' => $page, 's' => $s )); } else { - + if (Settings::Get('panel.allow_domain_change_customer') == '1') { $customers = ''; $result_customers_stmt = Database::prepare(" @@ -2059,7 +445,7 @@ if ($page == 'domains' || $page == 'overview') { $params['adminid'] = $userinfo['adminid']; } Database::pexecute($result_customers_stmt, $params); - + while ($row_customer = $result_customers_stmt->fetch(PDO::FETCH_ASSOC)) { $customers .= makeoption(getCorrectFullUserDetails($row_customer) . ' (' . $row_customer['loginname'] . ')', $row_customer['customerid'], $result['customerid']); } @@ -2073,10 +459,10 @@ if ($page == 'domains' || $page == 'overview') { )); $result['customername'] = getCorrectFullUserDetails($customer) . ' (' . $customer['loginname'] . ')'; } - + if ($userinfo['customers_see_all'] == '1') { if (Settings::Get('panel.allow_domain_change_admin') == '1') { - + $admins = ''; $result_admins_stmt = Database::prepare(" SELECT `adminid`, `loginname`, `name` FROM `" . TABLE_PANEL_ADMINS . "` @@ -2085,7 +471,7 @@ if ($page == 'domains' || $page == 'overview') { Database::pexecute($result_admins_stmt, array( 'adminid' => $result['adminid'] )); - + while ($row_admin = $result_admins_stmt->fetch(PDO::FETCH_ASSOC)) { $admins .= makeoption(getCorrectFullUserDetails($row_admin) . ' (' . $row_admin['loginname'] . ')', $row_admin['adminid'], $result['adminid']); } @@ -2099,10 +485,10 @@ if ($page == 'domains' || $page == 'overview') { $result['adminname'] = getCorrectFullUserDetails($admin) . ' (' . $admin['loginname'] . ')'; } } - + $result['domain'] = $idna_convert->decode($result['domain']); $domains = makeoption($lng['domains']['noaliasdomain'], 0, null, true); - + $result_domains_stmt = Database::prepare(" SELECT `d`.`id`, `d`.`domain` FROM `" . TABLE_PANEL_DOMAINS . "` `d`, `" . TABLE_PANEL_CUSTOMERS . "` `c` WHERE `d`.`aliasdomain` IS NULL AND `d`.`parentdomainid` = '0' AND `d`.`id` <> :id @@ -2113,11 +499,11 @@ if ($page == 'domains' || $page == 'overview') { 'id' => $result['id'], 'customerid' => $result['customerid'] )); - + while ($row_domain = $result_domains_stmt->fetch(PDO::FETCH_ASSOC)) { $domains .= makeoption($idna_convert->decode($row_domain['domain']), $row_domain['id'], $result['aliasdomain']); } - + $subtodomains = makeoption($lng['domains']['nosubtomaindomain'], 0, null, true); $result_domains_stmt = Database::prepare(" SELECT `d`.`id`, `d`.`domain` FROM `" . TABLE_PANEL_DOMAINS . "` `d`, `" . TABLE_PANEL_CUSTOMERS . "` `c` @@ -2132,11 +518,11 @@ if ($page == 'domains' || $page == 'overview') { $params['adminid'] = $userinfo['adminid']; } Database::pexecute($result_domains_stmt, $params); - + while ($row_domain = $result_domains_stmt->fetch(PDO::FETCH_ASSOC)) { $subtodomains .= makeoption($idna_convert->decode($row_domain['domain']), $row_domain['id'], $result['ismainbutsubto']); } - + if ($userinfo['ip'] == "-1") { $result_ipsandports_stmt = Database::query(" SELECT `id`, `ip`, `port` FROM `" . TABLE_PANEL_IPSANDPORTS . "` WHERE `ssl`='0' ORDER BY `ip`, `port` ASC @@ -2151,14 +537,14 @@ if ($page == 'domains' || $page == 'overview') { $admin_ip = Database::pexecute_first($admin_ip_stmt, array( 'ipid' => $userinfo['ip'] )); - + $result_ipsandports_stmt = Database::prepare(" SELECT `id`, `ip`, `port` FROM `" . TABLE_PANEL_IPSANDPORTS . "` WHERE `ssl`='0' AND `ip` = :ipid ORDER BY `ip`, `port` ASC "); Database::pexecute($result_ipsandports_stmt, array( 'ipid' => $admin_ip['ip'] )); - + $result_ssl_ipsandports_stmt = Database::prepare(" SELECT `id`, `ip`, `port` FROM `" . TABLE_PANEL_IPSANDPORTS . "` WHERE `ssl`='1' AND `ip` = :ipid ORDER BY `ip`, `port` ASC "); @@ -2166,7 +552,7 @@ if ($page == 'domains' || $page == 'overview') { 'ipid' => $admin_ip['ip'] )); } - + $ipsandports = array(); while ($row_ipandport = $result_ipsandports_stmt->fetch(PDO::FETCH_ASSOC)) { if (filter_var($row_ipandport['ip'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { @@ -2177,7 +563,7 @@ if ($page == 'domains' || $page == 'overview') { 'value' => $row_ipandport['id'] ); } - + $ssl_ipsandports = array(); while ($row_ssl_ipandport = $result_ssl_ipsandports_stmt->fetch(PDO::FETCH_ASSOC)) { if (filter_var($row_ssl_ipandport['ip'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { @@ -2188,7 +574,7 @@ if ($page == 'domains' || $page == 'overview') { 'value' => $row_ssl_ipandport['id'] ); } - + // create serveralias options $serveraliasoptions = ""; $_value = '2'; @@ -2198,22 +584,22 @@ if ($page == 'domains' || $page == 'overview') { } elseif ($result['wwwserveralias'] == '1') { $_value = '1'; } - + // Fudge the result for ssl_redirect to hide the Let's Encrypt steps $result['temporary_ssl_redirect'] = $result['ssl_redirect']; $result['ssl_redirect'] = ($result['ssl_redirect'] == 0 ? 0 : 1); - + $serveraliasoptions .= makeoption($lng['domains']['serveraliasoption_wildcard'], '0', $_value, true, true); $serveraliasoptions .= makeoption($lng['domains']['serveraliasoption_www'], '1', $_value, true, true); $serveraliasoptions .= makeoption($lng['domains']['serveraliasoption_none'], '2', $_value, true, true); - + $subcanemaildomain = makeoption($lng['admin']['subcanemaildomain']['never'], '0', $result['subcanemaildomain'], true, true); $subcanemaildomain .= makeoption($lng['admin']['subcanemaildomain']['choosableno'], '1', $result['subcanemaildomain'], true, true); $subcanemaildomain .= makeoption($lng['admin']['subcanemaildomain']['choosableyes'], '2', $result['subcanemaildomain'], true, true); $subcanemaildomain .= makeoption($lng['admin']['subcanemaildomain']['always'], '3', $result['subcanemaildomain'], true, true); $speciallogfile = ($result['speciallogfile'] == 1 ? $lng['panel']['yes'] : $lng['panel']['no']); $result['add_date'] = date('Y-m-d', $result['add_date']); - + $phpconfigs = ''; $phpconfigs_result_stmt = Database::query(" SELECT c.*, fc.description as interpreter @@ -2221,31 +607,31 @@ if ($page == 'domains' || $page == 'overview') { LEFT JOIN `" . TABLE_PANEL_FPMDAEMONS . "` fc ON fc.id = c.fpmsettingid "); $c_allowed_configs = getCustomerDetail($result['customerid'], 'allowed_phpconfigs'); - if (!empty($c_allowed_configs)) { + if (! empty($c_allowed_configs)) { $c_allowed_configs = json_decode($c_allowed_configs, true); } else { $c_allowed_configs = array(); } - + while ($phpconfigs_row = $phpconfigs_result_stmt->fetch(PDO::FETCH_ASSOC)) { - $disabled = !empty($c_allowed_configs) && !in_array($phpconfigs_row['id'], $c_allowed_configs); + $disabled = ! empty($c_allowed_configs) && ! in_array($phpconfigs_row['id'], $c_allowed_configs); if ((int) Settings::Get('phpfpm.enabled') == 1) { - $phpconfigs .= makeoption($phpconfigs_row['description'] . " [".$phpconfigs_row['interpreter']."]", $phpconfigs_row['id'], $result['phpsettingid'], true, true, null, $disabled); + $phpconfigs .= makeoption($phpconfigs_row['description'] . " [" . $phpconfigs_row['interpreter'] . "]", $phpconfigs_row['id'], $result['phpsettingid'], true, true, null, $disabled); } else { $phpconfigs .= makeoption($phpconfigs_row['description'], $phpconfigs_row['id'], $result['phpsettingid'], true, true, null, $disabled); } } - + $result = htmlentities_array($result); - + $domain_edit_data = include_once dirname(__FILE__) . '/lib/formfields/admin/domains/formfield.domains_edit.php'; $domain_edit_form = htmlform::genHTMLForm($domain_edit_data); - + $title = $domain_edit_data['domain_edit']['title']; $image = $domain_edit_data['domain_edit']['image']; - + $speciallogwarning = sprintf($lng['admin']['speciallogwarning'], $lng['admin']['delete_statistics']); - + eval("echo \"" . getTemplate("domains/domains_edit") . "\";"); } } @@ -2253,36 +639,35 @@ if ($page == 'domains' || $page == 'overview') { $customerid = intval($_POST['customerid']); $allowed_phpconfigs = getCustomerDetail($customerid, 'allowed_phpconfigs'); - echo !empty($allowed_phpconfigs) ? $allowed_phpconfigs : json_encode(array()); - exit; - + echo ! empty($allowed_phpconfigs) ? $allowed_phpconfigs : json_encode(array()); + exit(); } elseif ($action == 'import') { - + if (isset($_POST['send']) && $_POST['send'] == 'send') { - + $customerid = intval($_POST['customerid']); $separator = validate($_POST['separator'], 'separator'); $offset = (int) validate($_POST['offset'], 'offset', "/[0-9]/i"); - + $file_name = $_FILES['file']['tmp_name']; - + $result = array(); - + try { $bulk = new DomainBulkAction($file_name, $customerid); $result = $bulk->doImport($separator, $offset); } catch (Exception $e) { standard_error('domain_import_error', $e->getMessage()); } - + // @FIXME find a way to display $result['notice'] here somehow, // as it might be important if you've reached your maximum allocation of domains - + // update customer/admin counters updateCounters(false); inserttask('1'); inserttask('4'); - + $result_str = $result['imported'] . ' / ' . $result['all']; standard_success('domain_import_successfully', $result_str, array( 'filename' => $filename, @@ -2299,47 +684,45 @@ if ($page == 'domains' || $page == 'overview') { $params['adminid'] = $userinfo['adminid']; } Database::pexecute($result_customers_stmt, $params); - + while ($row_customer = $result_customers_stmt->fetch(PDO::FETCH_ASSOC)) { $customers .= makeoption(getCorrectFullUserDetails($row_customer) . ' (' . $row_customer['loginname'] . ')', $row_customer['customerid']); } - + $domain_import_data = include_once dirname(__FILE__) . '/lib/formfields/admin/domains/formfield.domains_import.php'; $domain_import_form = htmlform::genHTMLForm($domain_import_data); - + $title = $domain_import_data['domain_import']['title']; $image = $domain_import_data['domain_import']['image']; - + eval("echo \"" . getTemplate("domains/domains_import") . "\";"); } } } elseif ($page == 'domaindnseditor' && Settings::Get('system.dnsenabled') == '1') { - - require_once __DIR__.'/dns_editor.php'; - + + require_once __DIR__ . '/dns_editor.php'; } elseif ($page == 'sslcertificates') { - - require_once __DIR__.'/ssl_certificates.php'; - + + require_once __DIR__ . '/ssl_certificates.php'; } function formatDomainEntry(&$row, &$idna_convert) { $row['domain'] = $idna_convert->decode($row['domain']); $row['aliasdomain'] = $idna_convert->decode($row['aliasdomain']); - + $resultips_stmt = Database::prepare(" SELECT `ips`.* FROM `" . TABLE_DOMAINTOIP . "` AS `dti`, `" . TABLE_PANEL_IPSANDPORTS . "` AS `ips` WHERE `dti`.`id_ipandports` = `ips`.`id` AND `dti`.`id_domain` = :domainid "); - + Database::pexecute($resultips_stmt, array( 'domainid' => $row['id'] )); - + $row['ipandport'] = ''; while ($rowip = $resultips_stmt->fetch(PDO::FETCH_ASSOC)) { - + if (filter_var($rowip['ip'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { $row['ipandport'] .= '[' . $rowip['ip'] . ']:' . $rowip['port'] . "\n"; } else { @@ -2348,12 +731,12 @@ function formatDomainEntry(&$row, &$idna_convert) } $row['ipandport'] = substr($row['ipandport'], 0, - 1); $row['termination_date'] = str_replace("0000-00-00", "", $row['termination_date']); - + $row['termination_css'] = ""; if ($row['termination_date'] != "") { $cdate = strtotime($row['termination_date'] . " 23:59:59"); $today = time(); - + if ($cdate < $today) { $row['termination_css'] = 'domain-expired'; } else { diff --git a/lib/classes/api/commands/class.Domains.php b/lib/classes/api/commands/class.Domains.php new file mode 100644 index 00000000..1f44d4f6 --- /dev/null +++ b/lib/classes/api/commands/class.Domains.php @@ -0,0 +1,1635 @@ +isAdmin()) { + $this->logger()->logAction(ADM_ACTION, LOG_NOTICE, "[API] list domains"); + $result_stmt = Database::prepare(" + SELECT + `d`.*, `c`.`loginname`, `c`.`deactivated`, `c`.`name`, `c`.`firstname`, `c`.`company`, `c`.`standardsubdomain`, + `ad`.`id` AS `aliasdomainid`, `ad`.`domain` AS `aliasdomain` + FROM `" . TABLE_PANEL_DOMAINS . "` `d` + LEFT JOIN `" . TABLE_PANEL_CUSTOMERS . "` `c` USING(`customerid`) + LEFT JOIN `" . TABLE_PANEL_DOMAINS . "` `ad` ON `d`.`aliasdomain`=`ad`.`id` + WHERE `d`.`parentdomainid`='0' " . ($this->getUserDetail('customers_see_all') ? '' : " AND `d`.`adminid` = :adminid ")); + $params = array(); + if ($this->getUserDetail('customers_see_all') == '0') { + $params['adminid'] = $this->getUserDetail('adminid'); + } + Database::pexecute($result_stmt, $params); + $result = array(); + while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) { + $result[] = $row; + } + return $this->response(200, "successfull", array( + 'count' => count($result), + 'list' => $result + )); + } + throw new Exception("Not allowed to execute given command.", 403); + } + + public function get() + { + if ($this->isAdmin()) { + $id = $this->getParam('id'); + $no_std_subdomain = $this->getParam('no_std_subdomain', false); + $this->logger()->logAction(ADM_ACTION, LOG_NOTICE, "[API] get domain #" . $id); + $result_stmt = Database::prepare(" + SELECT `d`.*, `c`.`customerid` + FROM `" . TABLE_PANEL_DOMAINS . "` `d` + LEFT JOIN `" . TABLE_PANEL_CUSTOMERS . "` `c` USING(`customerid`) + WHERE `d`.`parentdomainid` = '0' + AND `d`.`id` = :id" . ($no_std_subdomain ? ' AND `d.`id` <> `c`.`standardsubdomain`' : '') . ($this->getUserDetail('customers_see_all') ? '' : " AND `d`.`adminid` = :adminid")); + $params = array( + 'id' => $id + ); + if ($this->getUserDetail('customers_see_all') == '0') { + $params['adminid'] = $this->getUserDetail('adminid'); + } + $result = Database::pexecute_first($result_stmt, $params, true, true); + if ($result) { + return $this->response(200, "successfull", $result); + } + throw new Exception("Domain with id #" . $id . " could not be found"); + } + throw new Exception("Not allowed to execute given command.", 403); + } + + public function add() + { + if ($this->isAdmin() && $this->getUserDetail('change_serversettings')) { + if ($this->getUserDetail('domains_used') < $this->getUserDetail('domains') || $this->getUserDetail('domains') == '-1') { + + if ($this->getParam('domain') == Settings::Get('system.hostname')) { + standard_error('admin_domain_emailsystemhostname', '', true); + } + + if (substr($this->getParam('domain'), 0, 4) == 'xn--') { + standard_error('domain_nopunycode', '', true); + } + + $idna_convert = new idna_convert_wrapper(); + $domain = $idna_convert->encode(preg_replace(array( + '/\:(\d)+$/', + '/^https?\:\/\//' + ), '', validate($this->getParam('domain'), 'domain'))); + + // Check whether domain validation is enabled and if, validate the domain + if (Settings::Get('system.validate_domain') && ! validateDomain($domain)) { + standard_error(array( + 'stringiswrong', + 'mydomain' + ), '', true); + } + + $subcanemaildomain = $this->getParam('subcanemaildomain', 0); + $isemaildomain = $this->getParam('isemaildomain', 0); + $email_only = $this->getParam('email_only', 0); + $serveraliasoption = $this->getParam('selectserveralias', 0); + $speciallogfile = $this->getParam('speciallogfile', 0); + + $aliasdomain = intval($this->getParam('alias')); + $issubof = intval($this->getParam('issubof')); + $customerid = intval($this->getParam('customerid')); + $customer_stmt = Database::prepare(" + SELECT * FROM `" . TABLE_PANEL_CUSTOMERS . "` + WHERE `customerid` = :customerid " . ($this->getUserDetail('customers_see_all') ? '' : " AND `adminid` = :adminid")); + $params = array( + 'customerid' => $customerid + ); + if ($this->getUserDetail('customers_see_all') == '0') { + $params['adminid'] = $this->getUserDetail('adminid'); + } + $customer = Database::pexecute_first($customer_stmt, $params, true, true); + + if (empty($customer) || $customer['customerid'] != $customerid) { + standard_error('customerdoesntexist', '', true); + } + + if ($this->getUserDetail('customers_see_all') == '1') { + + $adminid = intval($this->getParam('adminid')); + $admin_stmt = Database::prepare(" + SELECT * FROM `" . TABLE_PANEL_ADMINS . "` + WHERE `adminid` = :adminid AND (`domains_used` < `domains` OR `domains` = '-1')"); + $admin = Database::pexecute_first($admin_stmt, array( + 'adminid' => $adminid + ), true, true); + + if (empty($admin) || $admin['adminid'] != $adminid) { + standard_error('admindoesntexist', '', true); + } + } else { + $adminid = $this->getUserDetail('adminid'); + $admin = $this->getUserData(); + } + + // set default path if admin/reseller has "change_serversettings == false" but we still + // need to respect the documentroot_use_default_value - setting + $path_suffix = ''; + if (Settings::Get('system.documentroot_use_default_value') == 1) { + $path_suffix = '/' . $domain; + } + $documentroot = makeCorrectDir($customer['documentroot'] . $path_suffix); + + $registration_date = trim($this->getParam('registration_date', '')); + $registration_date = validate($registration_date, 'registration_date', '/^(19|20)\d\d[-](0[1-9]|1[012])[-](0[1-9]|[12][0-9]|3[01])$/', '', array( + '0000-00-00', + '0', + '' + ), true); + if ($registration_date == '0000-00-00') { + $registration_date = null; + } + + $termination_date = trim($this->getParam('termination_date', '')); + $termination_date = validate($termination_date, 'termination_date', '/^(19|20)\d\d[-](0[1-9]|1[012])[-](0[1-9]|[12][0-9]|3[01])$/', '', array( + '0000-00-00', + '0', + '' + ), true); + if ($termination_date == '0000-00-00') { + $termination_date = null; + } + + if ($this->getUserDetail('change_serversettings') == '1') { + + $caneditdomain = $this->getParam('caneditdomain', 0); + + $isbinddomain = '0'; + $zonefile = ''; + if (Settings::Get('system.bind_enable') == '1') { + $isbinddomain = $this->getParam('isbinddomain', 0); + $zonefile = validate($this->getParam('zonefile', ''), 'zonefile', '', '', array(), true); + } + + $dkim = intval($this->getParam('dkim', 0)); + + $specialsettings = validate(str_replace("\r\n", "\n", $this->getParam('specialsettings', '')), 'specialsettings', '/^[^\0]*$/', '', array(), true); + $notryfiles = $this->getParam('notryfiles', 0); + validate($this->getParam('documentroot', ''), 'documentroot', '', '', array(), true); + + // If path is empty and 'Use domain name as default value for DocumentRoot path' is enabled in settings, + // set default path to subdomain or domain name + if ($this->getParam('documentroot', '') != '') { + if (substr($this->getParam('documentroot'), 0, 1) != '/' && ! preg_match('/^https?\:\/\//', $this->getParam('documentroot'))) { + $documentroot .= '/' . $this->getParam('documentroot'); + } else { + $documentroot = $this->getParam('documentroot'); + } + } elseif ($this->getParam('documentroot', '') == '' && Settings::Get('system.documentroot_use_default_value') == 1) { + $documentroot = makeCorrectDir($customer['documentroot'] . '/' . $domain); + } + } else { + $isbinddomain = '0'; + if (Settings::Get('system.bind_enable') == '1') { + $isbinddomain = '1'; + } + $caneditdomain = '1'; + $zonefile = ''; + $dkim = '0'; + $specialsettings = ''; + $notryfiles = '0'; + } + + if ($this->getUserDetail('caneditphpsettings') == '1' || $this->getUserDetail('change_serversettings') == '1') { + + $phpenabled = $this->getParam('phpenabled', 0); + $openbasedir = $this->getParam('openbasedir', 0); + + if ((int) Settings::Get('system.mod_fcgid') == 1 || (int) Settings::Get('phpfpm.enabled') == 1) { + $phpsettingid = $this->getParam('phpsettingid', 1); + $phpsettingid_check_stmt = Database::prepare(" + SELECT * FROM `" . TABLE_PANEL_PHPCONFIGS . "` + WHERE `id` = :phpsettingid"); + $phpsettingid_check = Database::pexecute_first($phpsettingid_check_stmt, array( + 'phpsettingid' => $phpsettingid + ), true, true); + + if (! isset($phpsettingid_check['id']) || $phpsettingid_check['id'] == '0' || $phpsettingid_check['id'] != $phpsettingid) { + standard_error('phpsettingidwrong', '', true); + } + + if ((int) Settings::Get('system.mod_fcgid') == 1) { + $mod_fcgid_starter = validate($this->getParam('mod_fcgid_starter', - 1), 'mod_fcgid_starter', '/^[0-9]*$/', '', array( + '-1', + '' + ), true); + $mod_fcgid_maxrequests = validate($this->getParam('mod_fcgid_maxrequests', - 1), 'mod_fcgid_maxrequests', '/^[0-9]*$/', '', array( + '-1', + '' + ), true); + } else { + $mod_fcgid_starter = '-1'; + $mod_fcgid_maxrequests = '-1'; + } + } else { + + if ((int) Settings::Get('phpfpm.enabled') == 1) { + $phpsettingid = Settings::Get('phpfpm.defaultini'); + } else { + $phpsettingid = Settings::Get('system.mod_fcgid_defaultini'); + } + $mod_fcgid_starter = '-1'; + $mod_fcgid_maxrequests = '-1'; + } + } else { + + $phpenabled = '1'; + $openbasedir = '1'; + + if ((int) Settings::Get('phpfpm.enabled') == 1) { + $phpsettingid = Settings::Get('phpfpm.defaultini'); + } else { + $phpsettingid = Settings::Get('system.mod_fcgid_defaultini'); + } + $mod_fcgid_starter = '-1'; + $mod_fcgid_maxrequests = '-1'; + } + + if ($this->getUserDetail('ip') != "-1") { + $admin_ip_stmt = Database::prepare(" + SELECT `id`, `ip`, `port` FROM `" . TABLE_PANEL_IPSANDPORTS . "` + WHERE `id` = :id ORDER BY `ip`, `port` ASC"); + $admin_ip = Database::pexecute_first($admin_ip_stmt, array( + 'id' => $this->getUserDetail('ip') + ), true, true); + $additional_ip_condition = " AND `ip` = :adminip "; + $aip_param = array( + 'adminip' => $admin_ip['ip'] + ); + } else { + $additional_ip_condition = ''; + $aip_param = array(); + } + + $ipandports = array(); + if (! empty($this->getParam('ipandport')) && ! is_array($this->getParam('ipandport'))) { + $this->updateParam('ipandport', unserialize($this->getParam('ipandport'))); + } + + if (! empty($this->getParam('ipandport')) && is_array($this->getParam('ipandport'))) { + foreach ($this->getParam('ipandport') as $ipandport) { + $ipandport = intval($ipandport); + $ipandport_check_stmt = Database::prepare(" + SELECT `id`, `ip`, `port` FROM `" . TABLE_PANEL_IPSANDPORTS . "` + WHERE `id` = :id " . $additional_ip_condition); + $ip_params = null; + $ip_params = array_merge(array( + 'id' => $ipandport + ), $aip_param); + $ipandport_check = Database::pexecute_first($ipandport_check_stmt, $ip_params, true, true); + + if (! isset($ipandport_check['id']) || $ipandport_check['id'] == '0' || $ipandport_check['id'] != $ipandport) { + standard_error('ipportdoesntexist', '', true); + } else { + $ipandports[] = $ipandport; + } + } + } + + if (Settings::Get('system.use_ssl') == "1" && ! empty($this->getParam('ssl_ipandport'))) { + $ssl_redirect = $this->getParam('ssl_redirect', 0); + $letsencrypt = $this->getParam('letsencrypt', 0); + + $ssl_ipandports = array(); + if (! empty($this->getParam('ssl_ipandport')) && ! is_array($this->getParam('ssl_ipandport'))) { + $this->updateParam('ssl_ipandport', unserialize($this->getParam('ssl_ipandport'))); + } + + // Verify SSL-Ports + if (! empty($this->getParam('ssl_ipandport')) && is_array($this->getParam('ssl_ipandport'))) { + foreach ($this->getParam('ssl_ipandport') as $ssl_ipandport) { + if (trim($ssl_ipandport) == "") { + continue; + } + // fix if no ssl-ip/port is checked + if (trim($ssl_ipandport) < 1) { + continue; + } + $ssl_ipandport = intval($ssl_ipandport); + $ssl_ipandport_check_stmt = Database::prepare(" + SELECT `id`, `ip`, `port` FROM `" . TABLE_PANEL_IPSANDPORTS . "` + WHERE `id` = :id " . $additional_ip_condition); + $ip_params = null; + $ip_params = array_merge(array( + 'id' => $ssl_ipandport + ), $aip_param); + $ssl_ipandport_check = Database::pexecute_first($ssl_ipandport_check_stmt, $ip_params, true, true); + + if (! isset($ssl_ipandport_check['id']) || $ssl_ipandport_check['id'] == '0' || $ssl_ipandport_check['id'] != $ssl_ipandport) { + standard_error('ipportdoesntexist', '', true); + } else { + $ssl_ipandports[] = $ssl_ipandport; + } + } + + $http2 = $this->getParam('http2', 0); + // HSTS + $hsts_maxage = $this->getParam('hsts_maxage', 0); + $hsts_sub = $this->getParam('hsts_sub', 0); + $hsts_preload = $this->getParam('hsts_preload', 0); + // OCSP stapling + $ocsp_stapling = $this->getParam('ocsp_stapling', 0); + } else { + $ssl_redirect = 0; + $letsencrypt = 0; + $http2 = 0; + // we need this for the serialize + // if ssl is disabled or no ssl-ip/port exists + $ssl_ipandports[] = - 1; + + // HSTS + $hsts_maxage = 0; + $hsts_sub = 0; + $hsts_preload = 0; + + // OCSP stapling + $ocsp_stapling = 0; + } + } else { + $ssl_redirect = 0; + $letsencrypt = 0; + $http2 = 0; + // we need this for the serialize + // if ssl is disabled or no ssl-ip/port exists + $ssl_ipandports[] = - 1; + + // HSTS + $hsts_maxage = 0; + $hsts_sub = 0; + $hsts_preload = 0; + + // OCSP stapling + $ocsp_stapling = 0; + } + + // We can't enable let's encrypt for wildcard - domains if using acme-v1 + if ($serveraliasoption == '0' && $letsencrypt == '1' && Settings::Get('system.leapiversion') == '1') { + standard_error('nowildcardwithletsencrypt', '', true); + } + // if using acme-v2 we cannot issue wildcard-certificates + // because they currently only support the dns-01 challenge + if ($serveraliasoption == '0' && $letsencrypt == '1' && Settings::Get('system.leapiversion') == '2') { + standard_error('nowildcardwithletsencryptv2', '', true); + } + + // Temporarily deactivate ssl_redirect until Let's Encrypt certificate was generated + if ($ssl_redirect > 0 && $letsencrypt == 1) { + $ssl_redirect = 2; + } + + if (! preg_match('/^https?\:\/\//', $documentroot)) { + if (strstr($documentroot, ":") !== false) { + standard_error('pathmaynotcontaincolon', '', true); + } else { + $documentroot = makeCorrectDir($documentroot); + } + } + + $domain_check_stmt = Database::prepare(" + SELECT `id`, `domain` FROM `" . TABLE_PANEL_DOMAINS . "` + WHERE `domain` = :domain"); + $domain_check = Database::pexecute_first($domain_check_stmt, array( + 'domain' => strtolower($domain) + ), true, true); + $aliasdomain_check = array( + 'id' => 0 + ); + + if ($aliasdomain != 0) { + // Overwrite given ipandports with these of the "main" domain + $ipandports = array(); + $ssl_ipandports = array(); + $origipresult_stmt = Database::prepare(" + SELECT `id_ipandports` FROM `" . TABLE_DOMAINTOIP . "` + WHERE `id_domain` = :id"); + Database::pexecute($origipresult_stmt, array( + 'id' => $aliasdomain + ), true, true); + $ipdata_stmt = Database::prepare("SELECT * FROM `" . TABLE_PANEL_IPSANDPORTS . "` WHERE `id` = :ipid"); + while ($origip = $origipresult_stmt->fetch(PDO::FETCH_ASSOC)) { + $_origip_tmp = Database::pexecute_first($ipdata_stmt, array( + 'ipid' => $origip['id_ipandports'] + ), true, true); + if ($_origip_tmp['ssl'] == 0) { + $ipandports[] = $origip['id_ipandports']; + } else { + $ssl_ipandports[] = $origip['id_ipandports']; + } + } + + if (count($ssl_ipandports) == 0) { + // we need this for the serialize + // if ssl is disabled or no ssl-ip/port exists + $ssl_ipandports[] = - 1; + } + + $aliasdomain_check_stmt = Database::prepare(" + SELECT `d`.`id` FROM `" . TABLE_PANEL_DOMAINS . "` `d`, `" . TABLE_PANEL_CUSTOMERS . "` `c` + WHERE `d`.`customerid` = :customerid + AND `d`.`aliasdomain` IS NULL AND `d`.`id` <> `c`.`standardsubdomain` + AND `c`.`customerid` = :customerid + AND `d`.`id` = :aliasdomainid"); + $alias_params = array( + 'customerid' => $customerid, + 'aliasdomainid' => $aliasdomain + ); + $aliasdomain_check = Database::pexecute_first($aliasdomain_check_stmt, $alias_params, true, true); + } + + if (count($ipandports) == 0) { + standard_error('noipportgiven', '', true); + } + + if ($phpenabled != '1') { + $phpenabled = '0'; + } + + if ($openbasedir != '1') { + $openbasedir = '0'; + } + + if ($speciallogfile != '1') { + $speciallogfile = '0'; + } + + if ($isbinddomain != '1') { + $isbinddomain = '0'; + } + + if ($isemaildomain != '1') { + $isemaildomain = '0'; + } + + if ($email_only == '1') { + $isemaildomain = '1'; + } else { + $email_only = '0'; + } + + if ($subcanemaildomain != '1' && $subcanemaildomain != '2' && $subcanemaildomain != '3') { + $subcanemaildomain = '0'; + } + + if ($dkim != '1') { + $dkim = '0'; + } + + if ($serveraliasoption != '1' && $serveraliasoption != '2') { + $serveraliasoption = '0'; + } + + if ($caneditdomain != '1') { + $caneditdomain = '0'; + } + + if ($issubof <= '0') { + $issubof = '0'; + } + + if ($domain == '') { + standard_error(array( + 'stringisempty', + 'mydomain' + ), '', true); + } elseif ($documentroot == '') { + standard_error(array( + 'stringisempty', + 'mydocumentroot' + ), '', true); + } elseif ($customerid == 0) { + standard_error('adduserfirst', '', true); + } elseif (strtolower($domain_check['domain']) == strtolower($domain)) { + standard_error('domainalreadyexists', $idna_convert->decode($domain), true); + } elseif ($aliasdomain_check['id'] != $aliasdomain) { + standard_error('domainisaliasorothercustomer', '', true); + } else { + + /** + * + * @todo how to handle security questions now? + * + * $params = array( + * 'page' => $page, + * 'action' => $action, + * 'domain' => $domain, + * 'customerid' => $customerid, + * 'adminid' => $adminid, + * 'documentroot' => $documentroot, + * 'alias' => $aliasdomain, + * 'isbinddomain' => $isbinddomain, + * 'isemaildomain' => $isemaildomain, + * 'email_only' => $email_only, + * 'subcanemaildomain' => $subcanemaildomain, + * 'caneditdomain' => $caneditdomain, + * 'zonefile' => $zonefile, + * 'dkim' => $dkim, + * 'speciallogfile' => $speciallogfile, + * 'selectserveralias' => $serveraliasoption, + * 'ipandport' => serialize($ipandports), + * 'ssl_redirect' => $ssl_redirect, + * 'ssl_ipandport' => serialize($ssl_ipandports), + * 'phpenabled' => $phpenabled, + * 'openbasedir' => $openbasedir, + * 'phpsettingid' => $phpsettingid, + * 'mod_fcgid_starter' => $mod_fcgid_starter, + * 'mod_fcgid_maxrequests' => $mod_fcgid_maxrequests, + * 'specialsettings' => $specialsettings, + * 'notryfiles' => $notryfiles, + * 'registration_date' => $registration_date, + * 'termination_date' => $termination_date, + * 'issubof' => $issubof, + * 'letsencrypt' => $letsencrypt, + * 'http2' => $http2, + * 'hsts_maxage' => $hsts_maxage, + * 'hsts_sub' => $hsts_sub, + * 'hsts_preload' => $hsts_preload, + * 'ocsp_stapling' => $ocsp_stapling + * ); + * + * $security_questions = array( + * 'reallydisablesecuritysetting' => ($openbasedir == '0' && $userinfo['change_serversettings'] == '1'), + * 'reallydocrootoutofcustomerroot' => (substr($documentroot, 0, strlen($customer['documentroot'])) != $customer['documentroot'] && ! preg_match('/^https?\:\/\//', $documentroot)) + * ); + * $question_nr = 1; + * foreach ($security_questions as $question_name => $question_launch) { + * if ($question_launch !== false) { + * $params[$question_name] = $question_name; + * + * if (! isset($_POST[$question_name]) || $_POST[$question_name] != $question_name) { + * ask_yesno('admin_domain_' . $question_name, $filename, $params, $question_nr); + * } + * } + * $question_nr ++; + * } + */ + + $wwwserveralias = ($serveraliasoption == '1') ? '1' : '0'; + $iswildcarddomain = ($serveraliasoption == '0') ? '1' : '0'; + + $ins_data = array( + 'domain' => $domain, + 'customerid' => $customerid, + 'adminid' => $adminid, + 'documentroot' => $documentroot, + 'aliasdomain' => ($aliasdomain != 0 ? $aliasdomain : null), + 'zonefile' => $zonefile, + 'dkim' => $dkim, + 'wwwserveralias' => $wwwserveralias, + 'iswildcarddomain' => $iswildcarddomain, + 'isbinddomain' => $isbinddomain, + 'isemaildomain' => $isemaildomain, + 'email_only' => $email_only, + 'subcanemaildomain' => $subcanemaildomain, + 'caneditdomain' => $caneditdomain, + 'phpenabled' => $phpenabled, + 'openbasedir' => $openbasedir, + 'speciallogfile' => $speciallogfile, + 'specialsettings' => $specialsettings, + 'notryfiles' => $notryfiles, + 'ssl_redirect' => $ssl_redirect, + 'add_date' => time(), + 'registration_date' => $registration_date, + 'termination_date' => $termination_date, + 'phpsettingid' => $phpsettingid, + 'mod_fcgid_starter' => $mod_fcgid_starter, + 'mod_fcgid_maxrequests' => $mod_fcgid_maxrequests, + 'ismainbutsubto' => $issubof, + 'letsencrypt' => $letsencrypt, + 'http2' => $http2, + 'hsts' => $hsts_maxage, + 'hsts_sub' => $hsts_sub, + 'hsts_preload' => $hsts_preload, + 'ocsp_stapling' => $ocsp_stapling + ); + + $ins_stmt = Database::prepare(" + INSERT INTO `" . TABLE_PANEL_DOMAINS . "` SET + `domain` = :domain, + `customerid` = :customerid, + `adminid` = :adminid, + `documentroot` = :documentroot, + `aliasdomain` = :aliasdomain, + `zonefile` = :zonefile, + `dkim` = :dkim, + `dkim_id` = '0', + `dkim_privkey` = '', + `dkim_pubkey` = '', + `wwwserveralias` = :wwwserveralias, + `iswildcarddomain` = :iswildcarddomain, + `isbinddomain` = :isbinddomain, + `isemaildomain` = :isemaildomain, + `email_only` = :email_only, + `subcanemaildomain` = :subcanemaildomain, + `caneditdomain` = :caneditdomain, + `phpenabled` = :phpenabled, + `openbasedir` = :openbasedir, + `speciallogfile` = :speciallogfile, + `specialsettings` = :specialsettings, + `notryfiles` = :notryfiles, + `ssl_redirect` = :ssl_redirect, + `add_date` = :add_date, + `registration_date` = :registration_date, + `termination_date` = :termination_date, + `phpsettingid` = :phpsettingid, + `mod_fcgid_starter` = :mod_fcgid_starter, + `mod_fcgid_maxrequests` = :mod_fcgid_maxrequests, + `ismainbutsubto` = :ismainbutsubto, + `letsencrypt` = :letsencrypt, + `http2` = :http2, + `hsts` = :hsts, + `hsts_sub` = :hsts_sub, + `hsts_preload` = :hsts_preload, + `ocsp_stapling` = :ocsp_stapling + "); + Database::pexecute($ins_stmt, $ins_data, true, true); + $domainid = Database::lastInsertId(); + + $upd_stmt = Database::prepare(" + UPDATE `" . TABLE_PANEL_ADMINS . "` SET `domains_used` = `domains_used` + 1 + WHERE `adminid` = :adminid"); + Database::pexecute($upd_stmt, array( + 'adminid' => $adminid + ), true, true); + + $ins_stmt = Database::prepare(" + INSERT INTO `" . TABLE_DOMAINTOIP . "` SET + `id_domain` = :domainid, + `id_ipandports` = :ipandportsid + "); + + foreach ($ipandports as $ipportid) { + $ins_data = array( + 'domainid' => $domainid, + 'ipandportsid' => $ipportid + ); + Database::pexecute($ins_stmt, $ins_data, true, true); + } + + foreach ($ssl_ipandports as $ssl_ipportid) { + if ($ssl_ipportid > 0) { + $ins_data = array( + 'domainid' => $domainid, + 'ipandportsid' => $ssl_ipportid + ); + Database::pexecute($ins_stmt, $ins_data, true, true); + } + } + + triggerLetsEncryptCSRForAliasDestinationDomain($aliasdomain, $this->logger()); + + inserttask('1'); + // Using nameserver, insert a task which rebuilds the server config + inserttask('4'); + + $this->logger()->logAction(ADM_ACTION, LOG_WARNING, "[API] added domain '" . $domain . "'"); + return $this->response(200, "successfull", $ins_data); + } + } + throw new Exception("No more resources available", 406); + } + throw new Exception("Not allowed to execute given command.", 403); + } + + public function update() + { + if ($this->isAdmin() && $this->getUserDetail('change_serversettings')) { + $id = $this->getParam('id'); + + $json_result = Domains::getLocal($this->getUserData(), array( + 'id' => $id, + 'no_std_subdomain' => true + ))->get(); + $result = json_decode($json_result, true)['data']; + + $customer_stmt = Database::prepare(" + SELECT * FROM " . TABLE_PANEL_CUSTOMERS . " WHERE `customerid` = :customerid + "); + $customer = Database::pexecute_first($customer_stmt, array( + 'customerid' => $result['customerid'] + )); + + $customerid = $this->getParam('customerid', $result['customerid']); + + if ($customerid > 0 && $customerid != $result['customerid'] && Settings::Get('panel.allow_domain_change_customer') == '1') { + + $customer_stmt = Database::prepare(" + SELECT * FROM `" . TABLE_PANEL_CUSTOMERS . "` + WHERE `customerid` = :customerid + AND (`subdomains_used` + :subdomains <= `subdomains` OR `subdomains` = '-1' ) + AND (`emails_used` + :emails <= `emails` OR `emails` = '-1' ) + AND (`email_forwarders_used` + :forwarders <= `email_forwarders` OR `email_forwarders` = '-1' ) + AND (`email_accounts_used` + :accounts <= `email_accounts` OR `email_accounts` = '-1' ) " . ($this->getUserDetail('customers_see_all') ? '' : " AND `adminid` = :adminid")); + + $params = array( + 'customerid' => $customerid, + 'subdomains' => $subdomains, + 'emails' => $emails, + 'forwarders' => $email_forwarders, + 'accounts' => $email_accounts + ); + if ($this->getUserDetail('customers_see_all') == '0') { + $params['adminid'] = $this->getUserDetail('adminid'); + } + + $customer = Database::pexecute_first($customer_stmt, $params, true, true); + if (empty($customer) || $customer['customerid'] != $customerid) { + standard_error('customerdoesntexist', '', true); + } + } else { + $customerid = $result['customerid']; + } + + $customer_stmt = Database::prepare(" + SELECT * FROM " . TABLE_PANEL_ADMINS . " WHERE `adminid` = :adminid + "); + $admin = Database::pexecute_first($customer_stmt, array( + 'adminid' => $result['adminid'] + ), true, true); + + if ($this->getUserDetail('customers_see_all') == '1') { + + $adminid = $this->getParam('adminid', $result['adminid']); + + if ($adminid > 0 && $adminid != $result['adminid'] && Settings::Get('panel.allow_domain_change_admin') == '1') { + + $admin_stmt = Database::prepare(" + SELECT * FROM `" . TABLE_PANEL_ADMINS . "` + WHERE `adminid` = :adminid AND ( `domains_used` < `domains` OR `domains` = '-1' ) + "); + $admin = Database::pexecute_first($admin_stmt, array( + 'adminid' => $adminid + ), true, true); + + if (empty($admin) || $admin['adminid'] != $adminid) { + standard_error('admindoesntexist', '', true); + } + } else { + $adminid = $result['adminid']; + } + } else { + $adminid = $result['adminid']; + } + + $aliasdomain = $this->getParam('alias', $result['aliasdomain']); + $issubof = $this->getParam('issubof', $result['ismainbutsubto']); + $subcanemaildomain = $this->getParam('subcanemaildomain', $result['subcanemaildomain']); + $caneditdomain = $this->getParam('caneditdomain', $result['caneditdomain']); + $registration_date = $this->getParam('registration_date', $result['registration_date']); + $registration_date = validate($registration_date, 'registration_date', '/^(19|20)\d\d[-](0[1-9]|1[012])[-](0[1-9]|[12][0-9]|3[01])$/', '', array( + '0000-00-00', + '0', + '' + ), true); + if ($registration_date == '0000-00-00') { + $registration_date = null; + } + $termination_date = $this->getParam('termination_date', $result['termination_date']); + $termination_date = validate($termination_date, 'termination_date', '/^(19|20)\d\d[-](0[1-9]|1[012])[-](0[1-9]|[12][0-9]|3[01])$/', '', array( + '0000-00-00', + '0', + '' + ), true); + if ($termination_date == '0000-00-00') { + $termination_date = null; + } + + $isemaildomain = $this->getParam('isemaildomain', $result['isemaildomain']); + $email_only = $this->getParam('email_only', $result['email_only']); + + $serveraliasoption = '2'; + if ($result['iswildcarddomain'] == '1') { + $serveraliasoption = '0'; + } elseif ($result['wwwserveralias'] == '1') { + $serveraliasoption = '1'; + } + if (! empty($this->getParam('selectserveralias'))) { + $serveraliasoption = intval($this->getParam('selectserveralias')); + } + + $speciallogfile = $this->getParam('speciallogfile', $result['speciallogfile']); + + if ($this->getUserDetail('change_serversettings') == '1') { + $isbinddomain = $result['isbinddomain']; + $zonefile = $result['zonefile']; + if (Settings::Get('system.bind_enable') == '1') { + $isbinddomain = $this->getParam('isbinddomain', $result['isbinddomain']); + $zonefile = validate($this->getParam('zonefile', $result['zonefile']), 'zonefile', '', '', array(), true); + } + + if (Settings::Get('dkim.use_dkim') == '1') { + $dkim = $this->getParam('dkim', $result['dkim']); + } else { + $dkim = $result['dkim']; + } + + $specialsettings = validate(str_replace("\r\n", "\n", $this->getParam('specialsettings', $result['specialsettings'])), 'specialsettings', '/^[^\0]*$/', '', array(), true); + $ssfs = $this->getParam('specialsettingsforsubdomains', 0); + $notryfiles = $this->getParam('notryfiles', $result['notryfiles']); + $documentroot = validate($this->getParam('documentroot', $result['documentroot']), 'documentroot', '', '', array(), true); + + if ($documentroot == '') { + // If path is empty and 'Use domain name as default value for DocumentRoot path' is enabled in settings, + // set default path to subdomain or domain name + if (Settings::Get('system.documentroot_use_default_value') == 1) { + $documentroot = makeCorrectDir($customer['documentroot'] . '/' . $result['domain']); + } else { + $documentroot = $customer['documentroot']; + } + } + + if (! preg_match('/^https?\:\/\//', $documentroot) && strstr($documentroot, ":") !== false) { + standard_error('pathmaynotcontaincolon', '', true); + } + } else { + $isbinddomain = $result['isbinddomain']; + $zonefile = $result['zonefile']; + $dkim = $result['dkim']; + $specialsettings = $result['specialsettings']; + $ssfs = (empty($specialsettings) ? 0 : 1); + $notryfiles = $result['notryfiles']; + $documentroot = $result['documentroot']; + } + + // @TODO unsure whether this will still work + $speciallogverified = $this->getParam('speciallogverified', 0); + + if ($this->getUserDetail('caneditphpsettings') == '1' || $this->getUserDetail('change_serversettings') == '1') { + + $phpenabled = $this->getParam('phpenabled', $result['phpenabled']); + $openbasedir = $this->getParam('openbasedir', $result['openbasedir']); + $phpfs = $this->getParam('phpsettingsforsubdomains', 0); + + if ((int) Settings::Get('system.mod_fcgid') == 1 || (int) Settings::Get('phpfpm.enabled') == 1) { + $phpsettingid = $this->getParam('phpsettingid', $result['phpsettingid']); + $phpsettingid_check_stmt = Database::prepare(" + SELECT * FROM `" . TABLE_PANEL_PHPCONFIGS . "` WHERE `id` = :phpid + "); + $phpsettingid_check = Database::pexecute_first($phpsettingid_check_stmt, array( + 'phpid' => $phpsettingid + ), true, true); + + if (! isset($phpsettingid_check['id']) || $phpsettingid_check['id'] == '0' || $phpsettingid_check['id'] != $phpsettingid) { + standard_error('phpsettingidwrong', '', true); + } + + if ((int) Settings::Get('system.mod_fcgid') == 1) { + $mod_fcgid_starter = validate($this->getParam('mod_fcgid_starter', $result['mod_fcgid_starter']), 'mod_fcgid_starter', '/^[0-9]*$/', '', array( + '-1', + '' + ), true); + $mod_fcgid_maxrequests = validate($this->getParam('mod_fcgid_maxrequests', $result['mod_fcgid_maxrequests']), 'mod_fcgid_maxrequests', '/^[0-9]*$/', '', array( + '-1', + '' + ), true); + } else { + $mod_fcgid_starter = $result['mod_fcgid_starter']; + $mod_fcgid_maxrequests = $result['mod_fcgid_maxrequests']; + } + } else { + $phpsettingid = $result['phpsettingid']; + $phpfs = 1; + $mod_fcgid_starter = $result['mod_fcgid_starter']; + $mod_fcgid_maxrequests = $result['mod_fcgid_maxrequests']; + } + } else { + $phpenabled = $result['phpenabled']; + $openbasedir = $result['openbasedir']; + $phpsettingid = $result['phpsettingid']; + $phpfs = 1; + $mod_fcgid_starter = $result['mod_fcgid_starter']; + $mod_fcgid_maxrequests = $result['mod_fcgid_maxrequests']; + } + + $ipandports = array(); + if (! empty($this->getParam('ipandport')) && ! is_array($this->getParam('ipandport'))) { + $this->updateParam('ipandport', unserialize($this->getParam('ipandport'))); + } + + if (! empty($this->getParam('ipandport')) && is_array($this->getParam('ipandport'))) { + $ipandport_check_stmt = Database::prepare(" + SELECT `id`, `ip`, `port` FROM `" . TABLE_PANEL_IPSANDPORTS . "` WHERE `id` = :ipandport + "); + foreach ($this->getParam('ipandport') as $ipandport) { + if (trim($ipandport) == "") { + continue; + } + $ipandport = intval($ipandport); + $ipandport_check = Database::pexecute_first($ipandport_check_stmt, array( + 'ipandport' => $ipandport + ), true, true); + if (! isset($ipandport_check['id']) || $ipandport_check['id'] == '0' || $ipandport_check['id'] != $ipandport) { + standard_error('ipportdoesntexist', '', true); + } else { + $ipandports[] = $ipandport; + } + } + } + + if (Settings::Get('system.use_ssl') == '1' && ! empty($this->getParam('ssl_ipandport'))) { + $ssl = 1; // if ssl is set and != 0, it can only be 1 + $ssl_redirect = $this->getParam('ssl_redirect', $result['ssl_redirect']); + $letsencrypt = $this->getParam('letsencrypt', $result['letsencrypt']); + + $ssl_ipandports = array(); + if (! empty($this->getParam('ssl_ipandport')) && ! is_array($this->getParam('ssl_ipandport'))) { + $this->updateParam('ssl_ipandport', unserialize($this->getParam('ssl_ipandport'))); + } + if (! empty($this->getParam('ssl_ipandport')) && is_array($this->getParam('ssl_ipandport'))) { + $ssl_ipandport_check_stmt = Database::prepare(" + SELECT `id`, `ip`, `port` FROM `" . TABLE_PANEL_IPSANDPORTS . "` WHERE `id` = :ipandport + "); + foreach ($this->getParam('ssl_ipandport') as $ssl_ipandport) { + if (trim($ssl_ipandport) == "") { + continue; + } + // fix if ip/port got de-checked and it was the last one + if (trim($ssl_ipandport) < 1) { + continue; + } + $ssl_ipandport = intval($ssl_ipandport); + $ssl_ipandport_check = Database::pexecute_first($ssl_ipandport_check_stmt, array( + 'ipandport' => $ssl_ipandport + ), true, true); + if (! isset($ssl_ipandport_check['id']) || $ssl_ipandport_check['id'] == '0' || $ssl_ipandport_check['id'] != $ssl_ipandport) { + standard_error('ipportdoesntexist', '', true); + } else { + $ssl_ipandports[] = $ssl_ipandport; + } + } + + $http2 = $this->getParam('http2', $result['http2']); + // HSTS + $hsts_maxage = $this->getParam('hsts_maxage', $result['hsts_maxage']); + $hsts_sub = $this->getParam('hsts_sub', $result['hsts_sub']); + $hsts_preload = $this->getParam('hsts_preload', $result['hsts_preload']); + // OCSP stapling + $ocsp_stapling = $this->getParam('ocsp_stapling', $result['ocsp_stapling']); + } else { + $ssl_redirect = 0; + $letsencrypt = 0; + $http2 = 0; + // we need this for the serialize + // if ssl is disabled or no ssl-ip/port exists + $ssl_ipandports[] = - 1; + + // HSTS + $hsts_maxage = 0; + $hsts_sub = 0; + $hsts_preload = 0; + + // OCSP stapling + $ocsp_stapling = 0; + } + } else { + $ssl_redirect = 0; + $letsencrypt = 0; + $http2 = 0; + // we need this for the serialize + // if ssl is disabled or no ssl-ip/port exists + $ssl_ipandports[] = - 1; + + // HSTS + $hsts_maxage = 0; + $hsts_sub = 0; + $hsts_preload = 0; + + // OCSP stapling + $ocsp_stapling = 0; + } + + // We can't enable let's encrypt for wildcard domains when using acme-v1 + if ($serveraliasoption == '0' && $letsencrypt == '1' && Settings::Get('system.leapiversion') == '1') { + standard_error('nowildcardwithletsencrypt', '', true); + } + // if using acme-v2 we cannot issue wildcard-certificates + // because they currently only support the dns-01 challenge + if ($serveraliasoption == '0' && $letsencrypt == '1' && Settings::Get('system.leapiversion') == '2') { + standard_error('nowildcardwithletsencryptv2', '', true); + } + + // Temporarily deactivate ssl_redirect until Let's Encrypt certificate was generated + if ($ssl_redirect > 0 && $letsencrypt == 1 && $result['letsencrypt'] != $letsencrypt) { + $ssl_redirect = 2; + } + + if (! preg_match('/^https?\:\/\//', $documentroot)) { + $documentroot = makeCorrectDir($documentroot); + } + + if ($phpenabled != '1') { + $phpenabled = '0'; + } + + if ($openbasedir != '1') { + $openbasedir = '0'; + } + + if ($isbinddomain != '1') { + $isbinddomain = '0'; + } + + if ($isemaildomain != '1') { + $isemaildomain = '0'; + } + + if ($email_only == '1') { + $isemaildomain = '1'; + } else { + $email_only = '0'; + } + + if ($subcanemaildomain != '1' && $subcanemaildomain != '2' && $subcanemaildomain != '3') { + $subcanemaildomain = '0'; + } + + if ($dkim != '1') { + $dkim = '0'; + } + + if ($caneditdomain != '1') { + $caneditdomain = '0'; + } + + $aliasdomain_check = array( + 'id' => 0 + ); + + if ($aliasdomain != 0) { + // Overwrite given ipandports with these of the "main" domain + $ipandports = array(); + $ssl_ipandports = array(); + $origipresult_stmt = Database::prepare(" + SELECT `id_ipandports` FROM `" . TABLE_DOMAINTOIP . "` WHERE `id_domain` = :aliasdomain + "); + Database::pexecute($origipresult_stmt, array( + 'aliasdomain' => $aliasdomain + ), true, true); + $ipdata_stmt = Database::prepare("SELECT * FROM `" . TABLE_PANEL_IPSANDPORTS . "` WHERE `id` = :ipid"); + while ($origip = $origipresult_stmt->fetch(PDO::FETCH_ASSOC)) { + $_origip_tmp = Database::pexecute_first($ipdata_stmt, array( + 'ipid' => $origip['id_ipandports'] + ), true, true); + if ($_origip_tmp['ssl'] == 0) { + $ipandports[] = $origip['id_ipandports']; + } else { + $ssl_ipandports[] = $origip['id_ipandports']; + } + } + + if (count($ssl_ipandports) == 0) { + // we need this for the serialize + // if ssl is disabled or no ssl-ip/port exists + $ssl_ipandports[] = - 1; + } + + $aliasdomain_check_stmt = Database::prepare(" + SELECT `d`.`id` FROM `" . TABLE_PANEL_DOMAINS . "` `d`, `" . TABLE_PANEL_CUSTOMERS . "` `c` + WHERE `d`.`customerid` = :customerid + AND `d`.`aliasdomain` IS NULL AND `d`.`id` <> `c`.`standardsubdomain` + AND `c`.`customerid` = :customerid + AND `d`.`id` = :aliasdomain + "); + $aliasdomain_check = Database::pexecute_first($aliasdomain_check_stmt, array( + 'customerid' => $customerid, + 'aliasdomain' => $aliasdomain + ), true, true); + } + + if (count($ipandports) == 0) { + standard_error('noipportgiven', '', true); + } + + if ($aliasdomain_check['id'] != $aliasdomain) { + standard_error('domainisaliasorothercustomer', '', true); + } + + if ($issubof <= '0') { + $issubof = '0'; + } + + if ($serveraliasoption != '1' && $serveraliasoption != '2') { + $serveraliasoption = '0'; + } + + /** + * + * @todo how to handle security questions now? + * + * $params = array( + * 'id' => $id, + * 'page' => $page, + * 'action' => $action, + * 'customerid' => $customerid, + * 'adminid' => $adminid, + * 'documentroot' => $documentroot, + * 'alias' => $aliasdomain, + * 'isbinddomain' => $isbinddomain, + * 'isemaildomain' => $isemaildomain, + * 'email_only' => $email_only, + * 'subcanemaildomain' => $subcanemaildomain, + * 'caneditdomain' => $caneditdomain, + * 'zonefile' => $zonefile, + * 'dkim' => $dkim, + * 'selectserveralias' => $serveraliasoption, + * 'ssl_redirect' => $ssl_redirect, + * 'phpenabled' => $phpenabled, + * 'openbasedir' => $openbasedir, + * 'phpsettingid' => $phpsettingid, + * 'phpsettingsforsubdomains' => $phpfs, + * 'mod_fcgid_starter' => $mod_fcgid_starter, + * 'mod_fcgid_maxrequests' => $mod_fcgid_maxrequests, + * 'specialsettings' => $specialsettings, + * 'specialsettingsforsubdomains' => $ssfs, + * 'notryfiles' => $notryfiles, + * 'registration_date' => $registration_date, + * 'termination_date' => $termination_date, + * 'issubof' => $issubof, + * 'speciallogfile' => $speciallogfile, + * 'speciallogverified' => $speciallogverified, + * 'ipandport' => serialize($ipandports), + * 'ssl_ipandport' => serialize($ssl_ipandports), + * 'letsencrypt' => $letsencrypt, + * 'http2' => $http2, + * 'hsts_maxage' => $hsts_maxage, + * 'hsts_sub' => $hsts_sub, + * 'hsts_preload' => $hsts_preload, + * 'ocsp_stapling' => $ocsp_stapling + * ); + * + * $security_questions = array( + * 'reallydisablesecuritysetting' => ($openbasedir == '0' && $userinfo['change_serversettings'] == '1'), + * 'reallydocrootoutofcustomerroot' => (substr($documentroot, 0, strlen($customer['documentroot'])) != $customer['documentroot'] && ! preg_match('/^https?\:\/\//', $documentroot)) + * ); + * foreach ($security_questions as $question_name => $question_launch) { + * if ($question_launch !== false) { + * $params[$question_name] = $question_name; + * if (! isset($_POST[$question_name]) || $_POST[$question_name] != $question_name) { + * ask_yesno('admin_domain_' . $question_name, $filename, $params); + * } + * } + * } + */ + + $wwwserveralias = ($serveraliasoption == '1') ? '1' : '0'; + $iswildcarddomain = ($serveraliasoption == '0') ? '1' : '0'; + + if ($documentroot != $result['documentroot'] || $ssl_redirect != $result['ssl_redirect'] || $wwwserveralias != $result['wwwserveralias'] || $iswildcarddomain != $result['iswildcarddomain'] || $phpenabled != $result['phpenabled'] || $openbasedir != $result['openbasedir'] || $phpsettingid != $result['phpsettingid'] || $mod_fcgid_starter != $result['mod_fcgid_starter'] || $mod_fcgid_maxrequests != $result['mod_fcgid_maxrequests'] || $specialsettings != $result['specialsettings'] || $notryfiles != $result['notryfiles'] || $aliasdomain != $result['aliasdomain'] || $issubof != $result['ismainbutsubto'] || $email_only != $result['email_only'] || ($speciallogfile != $result['speciallogfile'] && $speciallogverified == '1') || $letsencrypt != $result['letsencrypt'] || $http2 != $result['http2'] || $hsts_maxage != $result['hsts'] || $hsts_sub != $result['hsts_sub'] || $hsts_preload != $result['hsts_preload'] || $ocsp_stapling != $result['ocsp_stapling']) { + inserttask('1'); + } + + if ($speciallogfile != $result['speciallogfile'] && $speciallogverified != '1') { + $speciallogfile = $result['speciallogfile']; + } + + if ($isbinddomain != $result['isbinddomain'] || $zonefile != $result['zonefile'] || $dkim != $result['dkim']) { + inserttask('4'); + } + + if ($isemaildomain == '0' && $result['isemaildomain'] == '1') { + $del_stmt = Database::prepare(" + DELETE FROM `" . TABLE_MAIL_USERS . "` WHERE `domainid` = :id + "); + Database::pexecute($del_stmt, array( + 'id' => $id + ), true, true); + + $del_stmt = Database::prepare(" + DELETE FROM `" . TABLE_MAIL_VIRTUAL . "` WHERE `domainid` = :id + "); + Database::pexecute($del_stmt, array( + 'id' => $id + ), true, true); + $this->logger()->logAction(ADM_ACTION, LOG_NOTICE, "[API] deleted domain #" . $id . " from mail-tables as is-email-domain was set to 0"); + } + + // check whether LE has been disabled, so we remove the certificate + if ($letsencrypt == '0' && $result['letsencrypt'] == '1') { + $del_stmt = Database::prepare(" + DELETE FROM `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` WHERE `domainid` = :id + "); + Database::pexecute($del_stmt, array( + 'id' => $id + ), true, true); + } + + $updatechildren = ''; + + if ($subcanemaildomain == '0' && $result['subcanemaildomain'] != '0') { + $updatechildren = ", `isemaildomain` = '0' "; + } elseif ($subcanemaildomain == '3' && $result['subcanemaildomain'] != '3') { + $updatechildren = ", `isemaildomain` = '1' "; + } + + if ($customerid != $result['customerid'] && Settings::Get('panel.allow_domain_change_customer') == '1') { + $upd_data = array( + 'customerid' => $customerid, + 'domainid' => $result['id'] + ); + $upd_stmt = Database::prepare(" + UPDATE `" . TABLE_MAIL_USERS . "` SET `customerid` = :customerid WHERE `domainid` = :domainid + "); + Database::pexecute($upd_stmt, $upd_data, true, true); + $upd_stmt = Database::prepare(" + UPDATE `" . TABLE_MAIL_VIRTUAL . "` SET `customerid` = :customerid WHERE `domainid` = :domainid + "); + Database::pexecute($upd_stmt, $upd_data, true, true); + $upd_data = array( + 'subdomains' => $subdomains, + 'emails' => $emails, + 'forwarders' => $email_forwarders, + 'accounts' => $email_accounts + ); + $upd_data['customerid'] = $customerid; + $upd_stmt = Database::prepare(" + UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET + `subdomains_used` = `subdomains_used` + :subdomains, + `emails_used` = `emails_used` + :emails, + `email_forwarders_used` = `email_forwarders_used` + :forwarders, + `email_accounts_used` = `email_accounts_used` + :accounts + WHERE `customerid` = :customerid + "); + Database::pexecute($upd_stmt, $upd_data, true, true); + + $upd_data['customerid'] = $result['customerid']; + $upd_stmt = Database::prepare(" + UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET + `subdomains_used` = `subdomains_used` - :subdomains, + `emails_used` = `emails_used` - :emails, + `email_forwarders_used` = `email_forwarders_used` - :forwarders, + `email_accounts_used` = `email_accounts_used` - :accounts + WHERE `customerid` = :customerid + "); + Database::pexecute($upd_stmt, $upd_data, true, true); + } + + if ($adminid != $result['adminid'] && Settings::Get('panel.allow_domain_change_admin') == '1') { + $upd_stmt = Database::prepare(" + UPDATE `" . TABLE_PANEL_ADMINS . "` SET `domains_used` = `domains_used` + 1 WHERE `adminid` = :adminid + "); + Database::pexecute($upd_stmt, array( + 'adminid' => $adminid + ), true, true); + + $upd_stmt = Database::prepare(" + UPDATE `" . TABLE_PANEL_ADMINS . "` SET `domains_used` = `domains_used` - 1 WHERE `adminid` = :adminid + "); + Database::pexecute($upd_stmt, array( + 'adminid' => $result['adminid'] + ), true, true); + } + + $_update_data = array(); + + $ssfs = $this->getParam('specialsettingsforsubdomains', 0); + if ($ssfs == 1) { + $_update_data['specialsettings'] = $specialsettings; + $upd_specialsettings = ", `specialsettings` = :specialsettings "; + } else { + $upd_specialsettings = ''; + unset($_update_data['specialsettings']); + $upd_stmt = Database::prepare(" + UPDATE `" . TABLE_PANEL_DOMAINS . "` SET `specialsettings`='' WHERE `parentdomainid` = :id + "); + Database::pexecute($upd_stmt, array( + 'id' => $id + ), true, true); + $this->logger()->logAction(ADM_ACTION, LOG_INFO, "[API] removed specialsettings on all subdomains of domain #" . $id); + } + + $wwwserveralias = ($serveraliasoption == '1') ? '1' : '0'; + $iswildcarddomain = ($serveraliasoption == '0') ? '1' : '0'; + + $update_data = array(); + $update_data['customerid'] = $customerid; + $update_data['adminid'] = $adminid; + $update_data['documentroot'] = $documentroot; + $update_data['ssl_redirect'] = $ssl_redirect; + $update_data['aliasdomain'] = ($aliasdomain != 0 && $alias_check == 0) ? $aliasdomain : null; + $update_data['isbinddomain'] = $isbinddomain; + $update_data['isemaildomain'] = $isemaildomain; + $update_data['email_only'] = $email_only; + $update_data['subcanemaildomain'] = $subcanemaildomain; + $update_data['dkim'] = $dkim; + $update_data['caneditdomain'] = $caneditdomain; + $update_data['zonefile'] = $zonefile; + $update_data['wwwserveralias'] = $wwwserveralias; + $update_data['iswildcarddomain'] = $iswildcarddomain; + $update_data['phpenabled'] = $phpenabled; + $update_data['openbasedir'] = $openbasedir; + $update_data['speciallogfile'] = $speciallogfile; + $update_data['phpsettingid'] = $phpsettingid; + $update_data['mod_fcgid_starter'] = $mod_fcgid_starter; + $update_data['mod_fcgid_maxrequests'] = $mod_fcgid_maxrequests; + $update_data['specialsettings'] = $specialsettings; + $update_data['notryfiles'] = $notryfiles; + $update_data['registration_date'] = $registration_date; + $update_data['termination_date'] = $termination_date; + $update_data['ismainbutsubto'] = $issubof; + $update_data['letsencrypt'] = $letsencrypt; + $update_data['http2'] = $http2; + $update_data['hsts'] = $hsts_maxage; + $update_data['hsts_sub'] = $hsts_sub; + $update_data['hsts_preload'] = $hsts_preload; + $update_data['ocsp_stapling'] = $ocsp_stapling; + $update_data['id'] = $id; + + $update_stmt = Database::prepare(" + UPDATE `" . TABLE_PANEL_DOMAINS . "` SET + `customerid` = :customerid, + `adminid` = :adminid, + `documentroot` = :documentroot, + `ssl_redirect` = :ssl_redirect, + `aliasdomain` = :aliasdomain, + `isbinddomain` = :isbinddomain, + `isemaildomain` = :isemaildomain, + `email_only` = :email_only, + `subcanemaildomain` = :subcanemaildomain, + `dkim` = :dkim, + `caneditdomain` = :caneditdomain, + `zonefile` = :zonefile, + `wwwserveralias` = :wwwserveralias, + `iswildcarddomain` = :iswildcarddomain, + `phpenabled` = :phpenabled, + `openbasedir` = :openbasedir, + `speciallogfile` = :speciallogfile, + `phpsettingid` = :phpsettingid, + `mod_fcgid_starter` = :mod_fcgid_starter, + `mod_fcgid_maxrequests` = :mod_fcgid_maxrequests, + `specialsettings` = :specialsettings, + `notryfiles` = :notryfiles, + `registration_date` = :registration_date, + `termination_date` = :termination_date, + `ismainbutsubto` = :ismainbutsubto, + `letsencrypt` = :letsencrypt, + `http2` = :http2, + `hsts` = :hsts, + `hsts_sub` = :hsts_sub, + `hsts_preload` = :hsts_preload, + `ocsp_stapling` = :ocsp_stapling + WHERE `id` = :id + "); + Database::pexecute($update_stmt, $update_data, true, true); + + $_update_data['customerid'] = $customerid; + $_update_data['adminid'] = $adminid; + $_update_data['phpenabled'] = $phpenabled; + $_update_data['openbasedir'] = $openbasedir; + $_update_data['mod_fcgid_starter'] = $mod_fcgid_starter; + $_update_data['mod_fcgid_maxrequests'] = $mod_fcgid_maxrequests; + $_update_data['parentdomainid'] = $id; + + // if php config is to be set for all subdomains, check here + $update_phpconfig = ''; + $phpfs = $this->getParam('phpsettingsforsubdomains', 0); + if ($phpfs == 1) { + $_update_data['phpsettingid'] = $phpsettingid; + $update_phpconfig = ", `phpsettingid` = :phpsettingid"; + } + + // if we have no more ssl-ip's for this domain, + // all its subdomains must have "ssl-redirect = 0" + // and disable let's encrypt + $update_sslredirect = ''; + if (count($ssl_ipandports) == 1 && $ssl_ipandports[0] == - 1) { + $update_sslredirect = ", `ssl_redirect` = '0', `letsencrypt` = '0' "; + } + + $_update_stmt = Database::prepare(" + UPDATE `" . TABLE_PANEL_DOMAINS . "` SET + `customerid` = :customerid, + `adminid` = :adminid, + `phpenabled` = :phpenabled, + `openbasedir` = :openbasedir, + `mod_fcgid_starter` = :mod_fcgid_starter, + `mod_fcgid_maxrequests` = :mod_fcgid_maxrequests + " . $update_phpconfig . $upd_specialsettings . $updatechildren . $update_sslredirect . " + WHERE `parentdomainid` = :parentdomainid + "); + Database::pexecute($_update_stmt, $_update_data, true, true); + + // FIXME check how many we got and if the amount of assigned IP's + // has changed so we can insert a config-rebuild task if only + // the ip's of this domain were changed + // -> for now, always insert a rebuild-task + inserttask('1'); + + // Cleanup domain <-> ip mapping + $del_stmt = Database::prepare(" + DELETE FROM `" . TABLE_DOMAINTOIP . "` WHERE `id_domain` = :id + "); + Database::pexecute($del_stmt, array( + 'id' => $id + ), true, true); + + $ins_stmt = Database::prepare(" + INSERT INTO `" . TABLE_DOMAINTOIP . "` SET `id_domain` = :domainid, `id_ipandports` = :ipportid + "); + + foreach ($ipandports as $ipportid) { + Database::pexecute($ins_stmt, array( + 'domainid' => $id, + 'ipportid' => $ipportid + ), true, true); + } + foreach ($ssl_ipandports as $ssl_ipportid) { + if ($ssl_ipportid > 0) { + Database::pexecute($ins_stmt, array( + 'domainid' => $id, + 'ipportid' => $ssl_ipportid + ), true, true); + } + } + + // Cleanup domain <-> ip mapping for subdomains + $domainidsresult_stmt = Database::prepare(" + SELECT `id` FROM `" . TABLE_PANEL_DOMAINS . "` WHERE `parentdomainid` = :id + "); + Database::pexecute($domainidsresult_stmt, array( + 'id' => $id + ), true, true); + + while ($row = $domainidsresult_stmt->fetch(PDO::FETCH_ASSOC)) { + + $del_stmt = Database::prepare(" + DELETE FROM `" . TABLE_DOMAINTOIP . "` WHERE `id_domain` = :rowid + "); + Database::pexecute($del_stmt, array( + 'rowid' => $row['id'] + ), true, true); + + $ins_stmt = Database::prepare(" + INSERT INTO `" . TABLE_DOMAINTOIP . "` SET + `id_domain` = :rowid, + `id_ipandports` = :ipportid + "); + + foreach ($ipandports as $ipportid) { + Database::pexecute($ins_stmt, array( + 'rowid' => $row['id'], + 'ipportid' => $ipportid + ), true, true); + } + foreach ($ssl_ipandports as $ssl_ipportid) { + if ($ssl_ipportid > 0) { + Database::pexecute($ins_stmt, array( + 'rowid' => $row['id'], + 'ipportid' => $ssl_ipportid + ), true, true); + } + } + } + if ($result['aliasdomain'] != $aliasdomain) { + // trigger when domain id for alias destination has changed: both for old and new destination + triggerLetsEncryptCSRForAliasDestinationDomain($result['aliasdomain'], $this->logger()); + triggerLetsEncryptCSRForAliasDestinationDomain($aliasdomain, $this->logger()); + } else if ($result['wwwserveralias'] != $wwwserveralias || $result['letsencrypt'] != $letsencrypt) { + // or when wwwserveralias or letsencrypt was changed + triggerLetsEncryptCSRForAliasDestinationDomain($aliasdomain, $this->logger()); + } + + $this->logger()->logAction(ADM_ACTION, LOG_WARNING, "[API] updated domain '" . $result['domain'] . "'"); + return $this->response(200, "successfull", $update_data); + } + throw new Exception("Not allowed to execute given command.", 403); + } + + public function delete() + { + if ($this->isAdmin()) { + $id = $this->getParam('id'); + + $json_result = Domains::getLocal($this->getUserData(), array( + 'id' => $id, + 'no_std_subdomain' => true + ))->get(); + $result = json_decode($json_result, true)['data']; + + // check for deletion of main-domains which are logically subdomains, #329 + $rsd_sql = ''; + $remove_subbutmain_domains = $this->getParam('delete_userfiles', 0) ? 1 : 0; + if ($remove_subbutmain_domains == 1) { + $rsd_sql .= " OR `ismainbutsubto` = :id"; + } + + $subresult_stmt = Database::prepare(" + SELECT `id` FROM `" . TABLE_PANEL_DOMAINS . "` + WHERE (`id` = :id OR `parentdomainid` = :id " . $rsd_sql . ")"); + Database::pexecute($subresult_stmt, array( + 'id' => $id + ), true, true); + $idString = array(); + $paramString = array(); + while ($subRow = $subresult_stmt->fetch(PDO::FETCH_ASSOC)) { + $idString[] = "`domainid` = :domain_" . (int) $subRow['id']; + $paramString['domain_' . $subRow['id']] = $subRow['id']; + } + $idString = implode(' OR ', $idString); + + if ($idString != '') { + $del_stmt = Database::prepare(" + DELETE FROM `" . TABLE_MAIL_USERS . "` WHERE " . $idString); + Database::pexecute($del_stmt, $paramString, true, true); + $del_stmt = Database::prepare(" + DELETE FROM `" . TABLE_MAIL_VIRTUAL . "` WHERE " . $idString); + Database::pexecute($del_stmt, $paramString, true, true); + $this->logger()->logAction(ADM_ACTION, LOG_NOTICE, "[API] deleted domain/s from mail-tables"); + } + + // if mainbutsubto-domains are not to be deleted, re-assign the (ismainbutsubto value of the main + // domain which is being deleted) as their new ismainbutsubto value + if ($remove_subbutmain_domains !== 1) { + $upd_stmt = Database::prepare(" + UPDATE `" . TABLE_PANEL_DOMAINS . "` SET + `ismainbutsubto` = :newIsMainButSubtoValue + WHERE `ismainbutsubto` = :deletedMainDomainId + "); + Database::pexecute($upd_stmt, array( + 'newIsMainButSubtoValue' => $result['ismainbutsubto'], + 'deletedMainDomainId' => $id + ), true, true); + } + + $del_stmt = Database::prepare(" + DELETE FROM `" . TABLE_PANEL_DOMAINS . "` + WHERE `id` = :id OR `parentdomainid` = :id " . $rsd_sql); + Database::pexecute($del_stmt, array( + 'id' => $id + ), true, true); + + $deleted_domains = $del_stmt->rowCount(); + + $upd_stmt = Database::prepare(" + UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET + `subdomains_used` = `subdomains_used` - :domaincount + WHERE `customerid` = :customerid"); + Database::pexecute($upd_stmt, array( + 'domaincount' => ($deleted_domains - 1), + 'customerid' => $result['customerid'] + ), true, true); + + $upd_stmt = Database::prepare(" + UPDATE `" . TABLE_PANEL_ADMINS . "` SET + `domains_used` = `domains_used` - 1 + WHERE `adminid` = :adminid"); + Database::pexecute($upd_stmt, array( + 'adminid' => $this->getUserDetail('adminid') + ), true, true); + + $upd_stmt = Database::prepare(" + UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET + `standardsubdomain` = '0' + WHERE `standardsubdomain` = :id AND `customerid` = :customerid"); + Database::pexecute($upd_stmt, array( + 'id' => $result['id'], + 'customerid' => $result['customerid'] + ), true, true); + + $del_stmt = Database::prepare(" + DELETE FROM `" . TABLE_DOMAINTOIP . "` + WHERE `id_domain` = :domainid"); + Database::pexecute($del_stmt, array( + 'domainid' => $id + ), true, true); + + $del_stmt = Database::prepare(" + DELETE FROM `" . TABLE_PANEL_DOMAINREDIRECTS . "` + WHERE `did` = :domainid"); + Database::pexecute($del_stmt, array( + 'domainid' => $id + ), true, true); + + // remove certificate from domain_ssl_settings, fixes #1596 + $del_stmt = Database::prepare(" + DELETE FROM `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` + WHERE `domainid` = :domainid"); + Database::pexecute($del_stmt, array( + 'domainid' => $id + ), true, true); + + // remove possible existing DNS entries + $del_stmt = Database::prepare(" + DELETE FROM `" . TABLE_DOMAIN_DNS . "` + WHERE `domain_id` = :domainid + "); + Database::pexecute($del_stmt, array( + 'domainid' => $id + ), true, true); + + triggerLetsEncryptCSRForAliasDestinationDomain($result['aliasdomain'], $this->logger()); + + $this->logger()->logAction(ADM_ACTION, LOG_INFO, "[API] deleted domain/subdomains (#" . $result['id'] . ")"); + updateCounters(); + inserttask('1'); + // Using nameserver, insert a task which rebuilds the server config + inserttask('4'); + return $this->response(200, "successfull", $result); + } + throw new Exception("Not allowed to execute given command.", 403); + } +} From 350e3d733a79e87c6b13768609cb6af48d85ae3c Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Fri, 16 Feb 2018 09:22:34 +0100 Subject: [PATCH 0433/1335] do not check for options if field is disabled, unset enabled-ownvhost flags for fcgid/fpm if the corresponding other one is activated; fixes #518 Signed-off-by: Michael Kaufmann (d00p) --- .../option/function.validateFormFieldOption.php | 2 +- .../validate/function.checkFcgidPhpFpm.php | 13 +++++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/lib/functions/formfields/option/function.validateFormFieldOption.php b/lib/functions/formfields/option/function.validateFormFieldOption.php index cbda0408..01b4be5f 100644 --- a/lib/functions/formfields/option/function.validateFormFieldOption.php +++ b/lib/functions/formfields/option/function.validateFormFieldOption.php @@ -34,7 +34,7 @@ function validateFormFieldOption($fieldname, $fielddata, $newfieldvalue) $returnvalue = isset($fielddata['option_options'][$newfieldvalue]); } - if($returnvalue === true) + if($returnvalue === true || $fielddata['visible'] == false) { return true; } diff --git a/lib/functions/validate/function.checkFcgidPhpFpm.php b/lib/functions/validate/function.checkFcgidPhpFpm.php index c0431f0d..add4e3df 100644 --- a/lib/functions/validate/function.checkFcgidPhpFpm.php +++ b/lib/functions/validate/function.checkFcgidPhpFpm.php @@ -24,12 +24,14 @@ function checkFcgidPhpFpm($fieldname, $fielddata, $newfieldvalue, $allnewfieldva 'system_mod_fcgid_enabled' => array( 'other_post_field' => 'system_phpfpm_enabled', 'other_enabled' => 'phpfpm.enabled', - 'other_enabled_lng' => 'phpfpmstillenabled' + 'other_enabled_lng' => 'phpfpmstillenabled', + 'deactivate' => array('phpfpm.enabled_ownvhost' => 0) ), 'system_phpfpm_enabled' => array( 'other_post_field' => 'system_mod_fcgid_enabled', 'other_enabled' => 'system.mod_fcgid', - 'other_enabled_lng' => 'fcgidstillenabled' + 'other_enabled_lng' => 'fcgidstillenabled', + 'deactivate' => array('system.mod_fcgid_ownvhost' => 0) ) ); @@ -56,6 +58,13 @@ function checkFcgidPhpFpm($fieldname, $fielddata, $newfieldvalue, $allnewfieldva } } } + if (in_array(FORMFIELDS_PLAUSIBILITY_CHECK_OK, $returnvalue)) { + // be sure to deactivate the other one for the froxlor-vhost + // to avoid having a settings-deadlock + foreach ($check_array[$fieldname]['deactivate'] as $setting => $value) { + Settings::Set($setting, $value, true); + } + } } return $returnvalue; From 2da2912c9c686ba06957133929e21d28f1b702db Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Fri, 16 Feb 2018 10:56:27 +0100 Subject: [PATCH 0434/1335] set update-check-urls to api-version; started working on Customers-ApiCommand Signed-off-by: Michael Kaufmann (d00p) --- admin_autoupdate.php | 2 +- admin_index.php | 2 +- .../froxlor/{0.9 => 0.10}/update_0.10.inc.php | 0 lib/classes/api/commands/class.Customers.php | 98 +++++++++++++++++++ .../api/commands/class.IpsAndPorts.php | 10 +- 5 files changed, 104 insertions(+), 8 deletions(-) rename install/updates/froxlor/{0.9 => 0.10}/update_0.10.inc.php (100%) create mode 100644 lib/classes/api/commands/class.Customers.php diff --git a/admin_autoupdate.php b/admin_autoupdate.php index 701e8011..ec3242fe 100644 --- a/admin_autoupdate.php +++ b/admin_autoupdate.php @@ -21,7 +21,7 @@ define('AREA', 'admin'); require './lib/init.php'; // define update-uri -define('UPDATE_URI', "https://version.froxlor.org/Froxlor/legacy/" . $version); +define('UPDATE_URI', "https://version.froxlor.org/Froxlor/api/" . $version); define('RELEASE_URI', "https://autoupdate.froxlor.org/froxlor-{version}.zip"); define('CHECKSUM_URI', "https://autoupdate.froxlor.org/froxlor-{version}.zip.sha256"); diff --git a/admin_index.php b/admin_index.php index 4650a24e..174f65a0 100644 --- a/admin_index.php +++ b/admin_index.php @@ -85,7 +85,7 @@ if ($page == 'overview') { if ((isset($_GET['lookfornewversion']) && $_GET['lookfornewversion'] == 'yes') || (isset($lookfornewversion) && $lookfornewversion == 'yes') ) { - $update_check_uri = 'http://version.froxlor.org/Froxlor/legacy/' . $version; + $update_check_uri = 'http://version.froxlor.org/Froxlor/api/' . $version; $latestversion = HttpClient::urlGet($update_check_uri); $latestversion = explode('|', $latestversion); diff --git a/install/updates/froxlor/0.9/update_0.10.inc.php b/install/updates/froxlor/0.10/update_0.10.inc.php similarity index 100% rename from install/updates/froxlor/0.9/update_0.10.inc.php rename to install/updates/froxlor/0.10/update_0.10.inc.php diff --git a/lib/classes/api/commands/class.Customers.php b/lib/classes/api/commands/class.Customers.php new file mode 100644 index 00000000..2d30d2e7 --- /dev/null +++ b/lib/classes/api/commands/class.Customers.php @@ -0,0 +1,98 @@ +isAdmin()) { + $this->logger()->logAction(ADM_ACTION, LOG_NOTICE, "[API] list customers"); + $result_stmt = Database::prepare(" + SELECT `c`.*, `a`.`loginname` AS `adminname` + FROM `" . TABLE_PANEL_CUSTOMERS . "` `c`, `" . TABLE_PANEL_ADMINS . "` `a` + WHERE " . ($this->getUserDetail('customers_see_all') ? '' : " `c`.`adminid` = :adminid AND ") . " + `c`.`adminid` = `a`.`adminid` + "); + $params = array(); + if ($this->getUserDetail('customers_see_all') == '0') { + $params = array( + 'adminid' => $this->getUserDetail('adminid') + ); + } + Database::pexecute($result_stmt, $params, true, true); + $result = array(); + while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) { + $result[] = $row; + } + return $this->response(200, "successfull", array( + 'count' => count($result), + 'list' => $result + )); + } + throw new Exception("Not allowed to execute given command.", 403); + } + + public function get() + { + if ($this->isAdmin()) { + $id = $this->getParam('id'); + $this->logger()->logAction(ADM_ACTION, LOG_NOTICE, "[API] get customer #" . $id); + $result_stmt = Database::prepare(" + SELECT * FROM `" . TABLE_PANEL_CUSTOMERS . "` + WHERE `customerid` = :id" . ($this->getUserDetail('customers_see_all') ? '' : " AND `adminid` = :adminid")); + $params = array( + 'id' => $id + ); + if ($this->getUserDetail('customers_see_all') == '0') { + $params['adminid'] = $this->getUserDetail('adminid'); + } + $result = Database::pexecute_first($result_stmt, $params, true, true); + if ($result) { + return $this->response(200, "successfull", $result); + } + throw new Exception("Customer with id #" . $id . " could not be found"); + } + throw new Exception("Not allowed to execute given command.", 403); + } + + public function add() + { + if ($this->isAdmin()) { + $this->logger()->logAction(ADM_ACTION, LOG_WARNING, "[API] added customer '" . $loginname . "'"); + return $this->response(200, "successfull", $ins_data); + } + throw new Exception("Not allowed to execute given command.", 403); + } + + public function update() + { + if ($this->isAdmin()) { + $id = $this->getParam('id'); + + $json_result = Customers::getLocal($this->getUserData(), array( + 'id' => $id + ))->get(); + $result = json_decode($json_result, true)['data']; + + $this->logger()->logAction(ADM_ACTION, LOG_WARNING, "[API] changed customer '" . $result['loginname'] . "'"); + return $this->response(200, "successfull", $upd_data); + } + throw new Exception("Not allowed to execute given command.", 403); + } + + public function delete() + { + if ($this->isAdmin()) { + $id = $this->getParam('id'); + + $json_result = Customers::getLocal($this->getUserData(), array( + 'id' => $id + ))->get(); + $result = json_decode($json_result, true)['data']; + + $this->logger()->logAction(ADM_ACTION, LOG_WARNING, "[API] deleted customer '" . $result['loginname'] . "'"); + return $this->response(200, "successfull", $result); + } + throw new Exception("Not allowed to execute given command.", 403); + } +} diff --git a/lib/classes/api/commands/class.IpsAndPorts.php b/lib/classes/api/commands/class.IpsAndPorts.php index 309f15b2..94d27115 100644 --- a/lib/classes/api/commands/class.IpsAndPorts.php +++ b/lib/classes/api/commands/class.IpsAndPorts.php @@ -10,7 +10,7 @@ class IpsAndPorts extends ApiCommand $result_stmt = Database::prepare(" SELECT * FROM `" . TABLE_PANEL_IPSANDPORTS . "` ORDER BY `ip` ASC, `port` ASC "); - Database::pexecute($result_stmt); + Database::pexecute($result_stmt, null, true, true); $result = array(); while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) { $result[] = $row; @@ -174,12 +174,10 @@ class IpsAndPorts extends ApiCommand if ($this->isAdmin() && $this->getUserDetail('change_serversettings')) { $id = $this->getParam('id'); - $result_stmt = Database::prepare(" - SELECT * FROM `" . TABLE_PANEL_IPSANDPORTS . "` WHERE `id` = :id - "); - $result = Database::pexecute_first($result_stmt, array( + $json_result = IpsAndPorts::getLocal($this->getUserData(), array( 'id' => $id - ), true, true); + ))->get(); + $result = json_decode($json_result, true)['data']; $ip = validate_ip2($this->getParam('ip', $result['ip']), false, 'invalidip', false, false, false, true); $port = validate($this->getParam('port', $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( From 1e45da2410ebe8f97c8032478a874f0e9cd0a0ad Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Fri, 16 Feb 2018 13:27:31 +0100 Subject: [PATCH 0435/1335] more work on Customer-ApiCommand Signed-off-by: Michael Kaufmann (d00p) --- admin_customers.php | 994 +++--------------- lib/classes/api/abstract.ApiCommand.php | 54 +- lib/classes/api/commands/class.Customers.php | 827 ++++++++++++++- lib/classes/api/commands/class.Domains.php | 6 +- .../validate/function.validatePassword.php | 26 +- 5 files changed, 1018 insertions(+), 889 deletions(-) diff --git a/admin_customers.php b/admin_customers.php index 1ccb9a19..7e0aae18 100644 --- a/admin_customers.php +++ b/admin_customers.php @@ -152,16 +152,14 @@ if ($page == 'customers' } elseif($action == 'su' && $id != 0 ) { - $result_stmt = Database::prepare(" - SELECT * FROM `" . TABLE_PANEL_CUSTOMERS . "` - WHERE `customerid` = :id" . - ($userinfo['customers_see_all'] ? '' : " AND `adminid` = :adminid") - ); - $params = array('id' => $id); - if ($userinfo['customers_see_all'] == '0') { - $params['adminid'] = $userinfo['adminid']; + try { + $json_result = Customers::getLocal($userinfo, array( + 'id' => $id + ))->get(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); } - $result = Database::pexecute_first($result_stmt, $params); + $result = json_decode($json_result, true)['data']; $destination_user = $result['loginname']; @@ -212,891 +210,133 @@ if ($page == 'customers' } elseif($action == 'unlock' && $id != 0 ) { - $result_stmt = Database::prepare(" - SELECT * FROM `" . TABLE_PANEL_CUSTOMERS . "` - WHERE `customerid` = :id" . - ($userinfo['customers_see_all'] ? '' : " AND `adminid` = :adminid") - ); - $result_data = array('id' => $id); - if ($userinfo['customers_see_all'] == '0') { - $result_data['adminid'] = $userinfo['adminid']; + try { + $json_result = Customers::getLocal($userinfo, array( + 'id' => $id + ))->get(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); } - $result = Database::pexecute_first($result_stmt, $result_data); + $result = json_decode($json_result, true)['data']; - if ($result['loginname'] != '') { - - if (isset($_POST['send']) - && $_POST['send'] == 'send' - ) { - $result_stmt = Database::prepare(" - UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET - `loginfail_count` = '0' - WHERE `customerid`= :id" - ); - Database::pexecute($result_stmt, array('id' => $id)); - redirectTo($filename, array('page' => $page, 's' => $s)); - - } else { - ask_yesno('customer_reallyunlock', $filename, array('id' => $id, 'page' => $page, 'action' => $action), $result['loginname']); + if (isset($_POST['send']) + && $_POST['send'] == 'send' + ) { + try { + $json_result = Customers::getLocal($userinfo, array( + 'id' => $id + ))->unlock(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); } + redirectTo($filename, array('page' => $page, 's' => $s)); + } else { + ask_yesno('customer_reallyunlock', $filename, array('id' => $id, 'page' => $page, 'action' => $action), $result['loginname']); } } elseif ($action == 'delete' && $id != 0 ) { - $result_stmt = Database::prepare(" - SELECT * FROM `" . TABLE_PANEL_CUSTOMERS . "` - WHERE `customerid` = :id" . - ($userinfo['customers_see_all'] ? '' : " AND `adminid` = :adminid") - ); - $params = array('id' => $id); - if ($userinfo['customers_see_all'] == '0') { - $params['adminid'] = $userinfo['adminid']; + try { + $json_result = Customers::getLocal($userinfo, array( + 'id' => $id + ))->get(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); } - $result = Database::pexecute_first($result_stmt, $params); + $result = json_decode($json_result, true)['data']; - if ($result['loginname'] != '') { - - if (isset($_POST['send']) - && $_POST['send'] == 'send' - ) { - $databases_stmt = Database::prepare(" - SELECT * FROM `" . TABLE_PANEL_DATABASES . "` - WHERE `customerid` = :id ORDER BY `dbserver`" - ); - Database::pexecute($databases_stmt, array('id' => $id)); - Database::needRoot(true); - $last_dbserver = 0; - - $dbm = new DbManager($log); - - while ($row_database = $databases_stmt->fetch(PDO::FETCH_ASSOC)) { - - if ($last_dbserver != $row_database['dbserver']) { - Database::needRoot(true, $row_database['dbserver']); - $dbm->getManager()->flushPrivileges(); - $last_dbserver = $row_database['dbserver']; - } - - $dbm->getManager()->deleteDatabase($row_database['databasename']); - } - - $dbm->getManager()->flushPrivileges(); - Database::needRoot(false); - $stmt = Database::prepare("DELETE FROM `" . TABLE_PANEL_CUSTOMERS . "` WHERE `customerid` = :id"); - Database::pexecute($stmt, array('id' => $id)); - $stmt = Database::prepare("DELETE FROM `" . TABLE_PANEL_DATABASES . "` WHERE `customerid` = :id"); - Database::pexecute($stmt, array('id' => $id)); - // first gather all domain-id's to clean up panel_domaintoip and dns-entries accordingly - $did_stmt = Database::prepare("SELECT `id` FROM `".TABLE_PANEL_DOMAINS."` WHERE `customerid` = :id"); - Database::pexecute($did_stmt, array('id' => $id)); - while ($row = $did_stmt->fetch(PDO::FETCH_ASSOC)) { - $stmt = Database::prepare("DELETE FROM `" . TABLE_DOMAINTOIP . "` WHERE `id_domain` = :did"); - Database::pexecute($stmt, array('did' => $row['id'])); - $stmt = Database::prepare("DELETE FROM `" . TABLE_DOMAIN_DNS . "` WHERE `domain_id` = :did"); - Database::pexecute($stmt, array('did' => $row['id'])); - } - $stmt = Database::prepare("DELETE FROM `" . TABLE_PANEL_DOMAINS . "` WHERE `customerid` = :id"); - Database::pexecute($stmt, array('id' => $id)); - $domains_deleted = $stmt->rowCount(); - $stmt = Database::prepare("DELETE FROM `" . TABLE_PANEL_HTPASSWDS . "` WHERE `customerid` = :id"); - Database::pexecute($stmt, array('id' => $id)); - $stmt = Database::prepare("DELETE FROM `" . TABLE_PANEL_HTACCESS . "` WHERE `customerid` = :id"); - Database::pexecute($stmt, array('id' => $id)); - $stmt = Database::prepare("DELETE FROM `" . TABLE_PANEL_SESSIONS . "` WHERE `userid` = :id AND `adminsession` = '0'"); - Database::pexecute($stmt, array('id' => $id)); - $stmt = Database::prepare("DELETE FROM `" . TABLE_PANEL_TRAFFIC . "` WHERE `customerid` = :id"); - Database::pexecute($stmt, array('id' => $id)); - $stmt = Database::prepare("DELETE FROM `" . TABLE_PANEL_DISKSPACE . "` WHERE `customerid` = :id"); - Database::pexecute($stmt, array('id' => $id)); - $stmt = Database::prepare("DELETE FROM `" . TABLE_MAIL_USERS . "` WHERE `customerid` = :id"); - Database::pexecute($stmt, array('id' => $id)); - $stmt = Database::prepare("DELETE FROM `" . TABLE_MAIL_VIRTUAL . "` WHERE `customerid` = :id"); - Database::pexecute($stmt, array('id' => $id)); - $result2_stmt = Database::prepare("SELECT `username` FROM `" . TABLE_FTP_USERS . "` WHERE `customerid` = :id"); - Database::pexecute($result2_stmt, array('id' => $id)); - while ($row = $result2_stmt->fetch(PDO::FETCH_ASSOC)) { - $stmt = Database::prepare("DELETE FROM `" . TABLE_FTP_QUOTATALLIES . "` WHERE `name` = :name"); - Database::pexecute($stmt, array('name' => $row['username'])); - } - $stmt = Database::prepare("DELETE FROM `" . TABLE_FTP_GROUPS . "` WHERE `customerid` = :id"); - Database::pexecute($stmt, array('id' => $id)); - $stmt = Database::prepare("DELETE FROM `" . TABLE_FTP_USERS . "` WHERE `customerid` = :id"); - Database::pexecute($stmt, array('id' => $id)); - - // Delete all waiting "create user" -tasks for this user, #276 - // Note: the WHERE selects part of a serialized array, but it should be safe this way - $del_stmt = Database::prepare(" - DELETE FROM `" . TABLE_PANEL_TASKS . "` - WHERE `type` = '2' AND `data` LIKE :loginname" - ); - Database::pexecute($del_stmt, array('loginname' => "%:{$result['loginname']};%")); - - $admin_update_query = "UPDATE `" . TABLE_PANEL_ADMINS . "` SET `customers_used` = `customers_used` - 1 "; - $admin_update_query.= ", `domains_used` = `domains_used` - 0" . (int)($domains_deleted - $result['subdomains_used']); - - if ($result['mysqls'] != '-1') { - $admin_update_query.= ", `mysqls_used` = `mysqls_used` - 0" . (int)$result['mysqls']; - } - - if ($result['emails'] != '-1') { - $admin_update_query.= ", `emails_used` = `emails_used` - 0" . (int)$result['emails']; - } - - if ($result['email_accounts'] != '-1') { - $admin_update_query.= ", `email_accounts_used` = `email_accounts_used` - 0" . (int)$result['email_accounts']; - } - - if ($result['email_forwarders'] != '-1') { - $admin_update_query.= ", `email_forwarders_used` = `email_forwarders_used` - 0" . (int)$result['email_forwarders']; - } - - if ($result['email_quota'] != '-1') { - $admin_update_query.= ", `email_quota_used` = `email_quota_used` - 0" . (int)$result['email_quota']; - } - - if ($result['subdomains'] != '-1') { - $admin_update_query.= ", `subdomains_used` = `subdomains_used` - 0" . (int)$result['subdomains']; - } - - if ($result['ftps'] != '-1') { - $admin_update_query.= ", `ftps_used` = `ftps_used` - 0" . (int)$result['ftps']; - } - - if ($result['tickets'] != '-1') { - $admin_update_query.= ", `tickets_used` = `tickets_used` - 0" . (int)$result['tickets']; - } - - if (($result['diskspace'] / 1024) != '-1') { - $admin_update_query.= ", `diskspace_used` = `diskspace_used` - 0" . (int)$result['diskspace']; - } - - $admin_update_query.= " WHERE `adminid` = '" . (int)$result['adminid'] . "'"; - Database::query($admin_update_query); - $log->logAction(ADM_ACTION, LOG_INFO, "deleted user '" . $result['loginname'] . "'"); - inserttask('1'); - - // Using nameserver, insert a task which rebuilds the server config - inserttask('4'); - - if (isset($_POST['delete_userfiles']) - && (int)$_POST['delete_userfiles'] == 1 - ) { - inserttask('6', $result['loginname']); - } - - // Using filesystem - quota, insert a task which cleans the filesystem - quota - inserttask('10'); - - /* - * move old tickets to archive - */ - $tickets = ticket::customerHasTickets($id); - if ($tickets !== false && isset($tickets[0])) { - foreach ($tickets as $ticket) { - $now = time(); - $mainticket = ticket::getInstanceOf($userinfo, (int)$ticket); - $mainticket->Set('lastchange', $now, true, true); - $mainticket->Set('lastreplier', '1', true, true); - $mainticket->Set('status', '3', true, true); - $mainticket->Update(); - $mainticket->Archive(); - $log->logAction(ADM_ACTION, LOG_NOTICE, "archived ticket '" . $mainticket->Get('subject') . "'"); - } - } - redirectTo($filename, array('page' => $page, 's' => $s)); - - } else { - ask_yesno_withcheckbox('admin_customer_reallydelete', 'admin_customer_alsoremovefiles', $filename, array('id' => $id, 'page' => $page, 'action' => $action), $result['loginname']); + if (isset($_POST['send']) + && $_POST['send'] == 'send' + ) { + try { + $json_result = Customers::getLocal($userinfo, array( + 'id' => $id, + 'delete_userfiles' => (isset($_POST['delete_userfiles']) ? (int)$_POST['delete_userfiles'] : 0) + ))->delete(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); } + redirectTo($filename, array('page' => $page, 's' => $s)); + + } else { + ask_yesno_withcheckbox('admin_customer_reallydelete', 'admin_customer_alsoremovefiles', $filename, array('id' => $id, 'page' => $page, 'action' => $action), $result['loginname']); } } elseif($action == 'add') { - if ($userinfo['customers_used'] < $userinfo['customers'] - || $userinfo['customers'] == '-1' + if (isset($_POST['send']) + && $_POST['send'] == 'send' ) { - if (isset($_POST['send']) - && $_POST['send'] == 'send' - ) { - $name = validate($_POST['name'], 'name'); - $firstname = validate($_POST['firstname'], 'first name'); - $company = validate($_POST['company'], 'company'); - $street = validate($_POST['street'], 'street'); - $zipcode = validate($_POST['zipcode'], 'zipcode', '/^[0-9 \-A-Z]*$/'); - $city = validate($_POST['city'], 'city'); - $phone = validate($_POST['phone'], 'phone', '/^[0-9\- \+\(\)\/]*$/'); - $fax = validate($_POST['fax'], 'fax', '/^[0-9\- \+\(\)\/]*$/'); - $email = $idna_convert->encode(validate($_POST['email'], 'email')); - $customernumber = validate($_POST['customernumber'], 'customer number', '/^[A-Za-z0-9 \-]*$/Di'); - $def_language = validate($_POST['def_language'], 'default language'); - $gender = intval_ressource($_POST['gender']); - - $custom_notes = validate(str_replace("\r\n", "\n", $_POST['custom_notes']), 'custom_notes', '/^[^\0]*$/'); - $custom_notes_show = 0; - if (isset($_POST['custom_notes_show'])) { - $custom_notes_show = intval_ressource($_POST['custom_notes_show']); - } - - $diskspace = intval_ressource($_POST['diskspace']); - if (isset($_POST['diskspace_ul'])) { - $diskspace = - 1; - } - - $traffic = doubleval_ressource($_POST['traffic']); - if (isset($_POST['traffic_ul'])) { - $traffic = - 1; - } - - $subdomains = intval_ressource($_POST['subdomains']); - if (isset($_POST['subdomains_ul'])) { - $subdomains = - 1; - } - - $emails = intval_ressource($_POST['emails']); - if (isset($_POST['emails_ul'])) { - $emails = - 1; - } - - $email_accounts = intval_ressource($_POST['email_accounts']); - if (isset($_POST['email_accounts_ul'])) { - $email_accounts = - 1; - } - - $email_forwarders = intval_ressource($_POST['email_forwarders']); - if (isset($_POST['email_forwarders_ul'])) { - $email_forwarders = - 1; - } - - if (Settings::Get('system.mail_quota_enabled') == '1') { - $email_quota = validate($_POST['email_quota'], 'email_quota', '/^\d+$/', 'vmailquotawrong', array('0', '')); - if (isset($_POST['email_quota_ul'])) { - $email_quota = - 1; - } - } else { - $email_quota = - 1; - } - - $email_imap = 0; - if (isset($_POST['email_imap'])) { - $email_imap = intval_ressource($_POST['email_imap']); - } - - $email_pop3 = 0; - if (isset($_POST['email_pop3'])) { - $email_pop3 = intval_ressource($_POST['email_pop3']); - } - - $ftps = 0; - if (isset($_POST['ftps'])) { - $ftps = intval_ressource($_POST['ftps']); - } - if (isset($_POST['ftps_ul'])) { - $ftps = - 1; - } - - $tickets = (Settings::Get('ticket.enabled') == 1 ? intval_ressource($_POST['tickets']) : 0); - if (isset($_POST['tickets_ul']) - && Settings::Get('ticket.enabled') == '1' - ) { - $tickets = - 1; - } - - $mysqls = intval_ressource($_POST['mysqls']); - if (isset($_POST['mysqls_ul'])) { - $mysqls = - 1; - } - - $createstdsubdomain = 0; - if(isset($_POST['createstdsubdomain'])) { - $createstdsubdomain = intval($_POST['createstdsubdomain']); - } - - $password = validate($_POST['new_customer_password'], 'password'); - // only check if not empty, - // cause empty == generate password automatically - if ($password != '') { - $password = validatePassword($password); - } - - // gender out of range? [0,2] - if ($gender < 0 || $gender > 2) { - $gender = 0; - } - - $sendpassword = 0; - if (isset($_POST['sendpassword'])) { - $sendpassword = intval($_POST['sendpassword']); - } - - $phpenabled = 0; - if (isset($_POST['phpenabled'])) { - $phpenabled = intval($_POST['phpenabled']); - } - - $allowed_phpconfigs = array(); - if (isset($_POST['allowed_phpconfigs']) && is_array($_POST['allowed_phpconfigs'])) { - foreach ($_POST['allowed_phpconfigs'] as $allowed_phpconfig) { - $allowed_phpconfig = intval($allowed_phpconfig); - $allowed_phpconfigs[] = $allowed_phpconfig; - } - } - - $perlenabled = 0; - if (isset($_POST['perlenabled'])) { - $perlenabled = intval($_POST['perlenabled']); - } - - $dnsenabled = 0; - if (isset($_POST['dnsenabled'])) { - $dnsenabled = intval($_POST['dnsenabled']); - } - - $store_defaultindex = 0; - if (isset($_POST['store_defaultindex'])) { - $store_defaultindex = intval($_POST['store_defaultindex']); - } - - $diskspace = $diskspace * 1024; - $traffic = $traffic * 1024 * 1024; - - if (((($userinfo['diskspace_used'] + $diskspace) > $userinfo['diskspace']) && ($userinfo['diskspace'] / 1024) != '-1') - || ((($userinfo['mysqls_used'] + $mysqls) > $userinfo['mysqls']) && $userinfo['mysqls'] != '-1') - || ((($userinfo['emails_used'] + $emails) > $userinfo['emails']) && $userinfo['emails'] != '-1') - || ((($userinfo['email_accounts_used'] + $email_accounts) > $userinfo['email_accounts']) && $userinfo['email_accounts'] != '-1') - || ((($userinfo['email_forwarders_used'] + $email_forwarders) > $userinfo['email_forwarders']) && $userinfo['email_forwarders'] != '-1') - || ((($userinfo['email_quota_used'] + $email_quota) > $userinfo['email_quota']) && $userinfo['email_quota'] != '-1' && Settings::Get('system.mail_quota_enabled') == '1') - || ((($userinfo['ftps_used'] + $ftps) > $userinfo['ftps']) && $userinfo['ftps'] != '-1') - || ((($userinfo['tickets_used'] + $tickets) > $userinfo['tickets']) && $userinfo['tickets'] != '-1') - || ((($userinfo['subdomains_used'] + $subdomains) > $userinfo['subdomains']) && $userinfo['subdomains'] != '-1') - || (($diskspace / 1024) == '-1' && ($userinfo['diskspace'] / 1024) != '-1') - || ($mysqls == '-1' && $userinfo['mysqls'] != '-1') - || ($emails == '-1' && $userinfo['emails'] != '-1') - || ($email_accounts == '-1' && $userinfo['email_accounts'] != '-1') - || ($email_forwarders == '-1' && $userinfo['email_forwarders'] != '-1') - || ($email_quota == '-1' && $userinfo['email_quota'] != '-1' && Settings::Get('system.mail_quota_enabled') == '1') - || ($ftps == '-1' && $userinfo['ftps'] != '-1') - || ($tickets == '-1' && $userinfo['tickets'] != '-1') - || ($subdomains == '-1' && $userinfo['subdomains'] != '-1') - ) { - standard_error('youcantallocatemorethanyouhave'); - } - - // Either $name and $firstname or the $company must be inserted - if ($name == '' && $company == '') { - standard_error(array('stringisempty', 'myname')); - - } elseif($firstname == '' && $company == '') { - standard_error(array('stringisempty', 'myfirstname')); - - } elseif($email == '') { - standard_error(array('stringisempty', 'emailadd')); - - } elseif(!validateEmail($email)) { - standard_error('emailiswrong', $email); - - } else { - - if (isset($_POST['new_loginname']) - && $_POST['new_loginname'] != '' - ) { - $accountnumber = intval(Settings::Get('system.lastaccountnumber')); - $loginname = validate($_POST['new_loginname'], 'loginname', '/^[a-z][a-z0-9\-_]+$/i'); - - // Accounts which match systemaccounts are not allowed, filtering them - if (preg_match('/^' . preg_quote(Settings::Get('customer.accountprefix'), '/') . '([0-9]+)/', $loginname)) { - standard_error('loginnameissystemaccount', Settings::Get('customer.accountprefix')); - } - - // Additional filtering for Bug #962 - if (function_exists('posix_getpwnam') - && !in_array("posix_getpwnam", explode(",", ini_get('disable_functions'))) - && posix_getpwnam($loginname) - ) { - standard_error('loginnameissystemaccount', Settings::Get('customer.accountprefix')); - } - - } else { - $accountnumber = intval(Settings::Get('system.lastaccountnumber')) + 1; - $loginname = Settings::Get('customer.accountprefix') . $accountnumber; - } - - // Check if the account already exists - $loginname_check_stmt = Database::prepare(" - SELECT `loginname` FROM `" . TABLE_PANEL_CUSTOMERS . "` WHERE `loginname` = :loginname" - ); - $loginname_check = Database::pexecute_first($loginname_check_stmt, array('loginname' => $loginname)); - - $loginname_check_admin_stmt = Database::prepare(" - SELECT `loginname` FROM `" . TABLE_PANEL_ADMINS . "` WHERE `loginname` = :loginname" - ); - $loginname_check_admin = Database::pexecute_first($loginname_check_admin_stmt, array('loginname' => $loginname)); - - if (strtolower($loginname_check['loginname']) == strtolower($loginname) - || strtolower($loginname_check_admin['loginname']) == strtolower($loginname) - ) { - standard_error('loginnameexists', $loginname); - - } elseif (!validateUsername($loginname, Settings::Get('panel.unix_names'), 14 - strlen(Settings::Get('customer.mysqlprefix')))) { - if (strlen($loginname) > 14 - strlen(Settings::Get('customer.mysqlprefix'))) { - standard_error('loginnameiswrong2', 14 - strlen(Settings::Get('customer.mysqlprefix'))); - } else { - standard_error('loginnameiswrong', $loginname); - } - } - - $guid = intval(Settings::Get('system.lastguid')) + 1; - $documentroot = makeCorrectDir(Settings::Get('system.documentroot_prefix') . '/' . $loginname); - - if (file_exists($documentroot)) { - standard_error('documentrootexists', $documentroot); - } - - if ($createstdsubdomain != '1') { - $createstdsubdomain = '0'; - } - - if ($phpenabled != '0') { - $phpenabled = '1'; - } - - if ($perlenabled != '0') { - $perlenabled = '1'; - } - - if ($dnsenabled != '0') { - $dnsenabled = '1'; - } - - if ($password == '') { - $password = generatePassword(); - } - - $_theme = Settings::Get('panel.default_theme'); - - $ins_data = array( - 'adminid' => $userinfo['adminid'], - 'loginname' => $loginname, - 'passwd' => makeCryptPassword($password), - 'name' => $name, - 'firstname' => $firstname, - 'gender' => $gender, - 'company' => $company, - 'street' => $street, - 'zipcode' => $zipcode, - 'city' => $city, - 'phone' => $phone, - 'fax' => $fax, - 'email' => $email, - 'customerno' => $customernumber, - 'lang' => $def_language, - 'docroot' => $documentroot, - 'guid' => $guid, - 'diskspace' => $diskspace, - 'traffic' => $traffic, - 'subdomains' => $subdomains, - 'emails' => $emails, - 'email_accounts' => $email_accounts, - 'email_forwarders' => $email_forwarders, - 'email_quota' => $email_quota, - 'ftps' => $ftps, - 'tickets' => $tickets, - 'mysqls' => $mysqls, - 'phpenabled' => $phpenabled, - 'allowed_phpconfigs' => empty($allowed_phpconfigs) ? "" : json_encode($allowed_phpconfigs), - 'imap' => $email_imap, - 'pop3' => $email_pop3, - 'perlenabled' => $perlenabled, - 'dnsenabled' => $dnsenabled, - 'theme' => $_theme, - 'custom_notes' => $custom_notes, - 'custom_notes_show' => $custom_notes_show - ); - - $ins_stmt = Database::prepare(" - INSERT INTO `" . TABLE_PANEL_CUSTOMERS . "` SET - `adminid` = :adminid, - `loginname` = :loginname, - `password` = :passwd, - `name` = :name, - `firstname` = :firstname, - `gender` = :gender, - `company` = :company, - `street` = :street, - `zipcode` = :zipcode, - `city` = :city, - `phone` = :phone, - `fax` = :fax, - `email` = :email, - `customernumber` = :customerno, - `def_language` = :lang, - `documentroot` = :docroot, - `guid` = :guid, - `diskspace` = :diskspace, - `traffic` = :traffic, - `subdomains` = :subdomains, - `emails` = :emails, - `email_accounts` = :email_accounts, - `email_forwarders` = :email_forwarders, - `email_quota` = :email_quota, - `ftps` = :ftps, - `tickets` = :tickets, - `mysqls` = :mysqls, - `standardsubdomain` = '0', - `phpenabled` = :phpenabled, - `allowed_phpconfigs` = :allowed_phpconfigs, - `imap` = :imap, - `pop3` = :pop3, - `perlenabled` = :perlenabled, - `dnsenabled` = :dnsenabled, - `theme` = :theme, - `custom_notes` = :custom_notes, - `custom_notes_show` = :custom_notes_show" - ); - Database::pexecute($ins_stmt, $ins_data); - - $customerid = Database::lastInsertId(); - - $admin_update_query = "UPDATE `" . TABLE_PANEL_ADMINS . "` SET `customers_used` = `customers_used` + 1"; - - if ($mysqls != '-1') { - $admin_update_query.= ", `mysqls_used` = `mysqls_used` + 0" . (int)$mysqls; - } - - if ($emails != '-1') { - $admin_update_query.= ", `emails_used` = `emails_used` + 0" . (int)$emails; - } - - if ($email_accounts != '-1') { - $admin_update_query.= ", `email_accounts_used` = `email_accounts_used` + 0" . (int)$email_accounts; - } - - if ($email_forwarders != '-1') { - $admin_update_query.= ", `email_forwarders_used` = `email_forwarders_used` + 0" . (int)$email_forwarders; - } - - if ($email_quota != '-1') { - $admin_update_query.= ", `email_quota_used` = `email_quota_used` + 0" . (int)$email_quota; - } - - if ($subdomains != '-1') { - $admin_update_query.= ", `subdomains_used` = `subdomains_used` + 0" . (int)$subdomains; - } - - if ($ftps != '-1') { - $admin_update_query.= ", `ftps_used` = `ftps_used` + 0" . (int)$ftps; - } - - if ($tickets != '-1' - && Settings::Get('ticket.enabled') == 1 - ) { - $admin_update_query.= ", `tickets_used` = `tickets_used` + 0" . (int)$tickets; - } - - if (($diskspace / 1024) != '-1') { - $admin_update_query.= ", `diskspace_used` = `diskspace_used` + 0" . (int)$diskspace; - } - - $admin_update_query.= " WHERE `adminid` = '" . (int)$userinfo['adminid'] . "'"; - Database::query($admin_update_query); - - $upd_stmt = Database::prepare(" - UPDATE `" . TABLE_PANEL_SETTINGS . "` SET - `value` = :guid - WHERE `settinggroup` = 'system' AND `varname` = 'lastguid'" - ); - Database::pexecute($upd_stmt, array('guid' => $guid)); - - if ($accountnumber != intval(Settings::Get('system.lastaccountnumber'))) { - $upd_stmt = Database::prepare(" - UPDATE `" . TABLE_PANEL_SETTINGS . "` SET - `value` = :accno - WHERE `settinggroup` = 'system' AND `varname` = 'lastaccountnumber'" - ); - Database::pexecute($upd_stmt, array('accno' => $accountnumber)); - } - - $log->logAction(ADM_ACTION, LOG_INFO, "added user '" . $loginname . "'"); - inserttask('2', $loginname, $guid, $guid, $store_defaultindex); - - // Using filesystem - quota, insert a task which cleans the filesystem - quota - inserttask('10'); - - // Add htpasswd for the webalizer stats - if (CRYPT_STD_DES == 1) { - $saltfordescrypt = substr(md5(uniqid(microtime(), 1)), 4, 2); - $htpasswdPassword = crypt($password, $saltfordescrypt); - } else { - $htpasswdPassword = crypt($password); - } - - $ins_stmt = Database::prepare(" - INSERT INTO `" . TABLE_PANEL_HTPASSWDS . "` SET - `customerid` = :customerid, - `username` = :username, - `password` = :passwd, - `path` = :path" - ); - $ins_data = array( - 'customerid' => $customerid, - 'username' => $loginname, - 'passwd' => $htpasswdPassword - ); - - if (Settings::Get('system.awstats_enabled') == '1') { - $ins_data['path'] = makeCorrectDir($documentroot . '/awstats/'); - $log->logAction(ADM_ACTION, LOG_NOTICE, "automatically added awstats htpasswd for user '" . $loginname . "'"); - } else { - $ins_data['path'] = makeCorrectDir($documentroot . '/webalizer/'); - $log->logAction(ADM_ACTION, LOG_NOTICE, "automatically added webalizer htpasswd for user '" . $loginname . "'"); - } - Database::pexecute($ins_stmt, $ins_data); - - inserttask('1'); - $cryptPassword = makeCryptPassword($password); - // FTP-User - $ins_stmt = Database::prepare(" - INSERT INTO `" . TABLE_FTP_USERS . "` SET `customerid` = :customerid, `username` = :username, `description` = :desc, - `password` = :passwd, `homedir` = :homedir, `login_enabled` = 'y', `uid` = :guid, `gid` = :guid" - ); - $ins_data = array( - 'customerid' => $customerid, - 'username' => $loginname, - 'passwd' => $cryptPassword, - 'homedir' => $documentroot, - 'guid' => $guid, - 'desc' => "Default" - ); - Database::pexecute($ins_stmt, $ins_data); - // FTP-Group - $ins_stmt = Database::prepare(" - INSERT INTO `" . TABLE_FTP_GROUPS . "` SET `customerid` = :customerid, `groupname` = :groupname, `gid` = :guid, `members` = :members" - ); - $ins_data = array( - 'customerid' => $customerid, - 'groupname' => $loginname, - 'guid' => $guid, - 'members' => $loginname.','.Settings::Get('system.httpuser') - ); - - // also, add froxlor-local user to ftp-group (if exists!) to - // allow access to customer-directories from within the panel, which - // is necessary when pathedit = Dropdown - if ((int)Settings::Get('system.mod_fcgid_ownvhost') == 1 || (int)Settings::Get('phpfpm.enabled_ownvhost') == 1) { - if ((int)Settings::Get('system.mod_fcgid') == 1) { - $local_user = Settings::Get('system.mod_fcgid_httpuser'); - } else { - $local_user = Settings::Get('phpfpm.vhost_httpuser'); - } - // check froxlor-local user membership in ftp-group - // without this check addition may duplicate user in list if httpuser == local_user - if (strpos($ins_data['members'], $local_user) == false) { - $ins_data['members'] .= ','.$local_user; - } - } - - Database::pexecute($ins_stmt, $ins_data); - - // FTP-Quotatallies - $ins_stmt = Database::prepare(" - INSERT INTO `" . TABLE_FTP_QUOTATALLIES . "` SET `name` = :name, `quota_type` = 'user', `bytes_in_used` = '0', - `bytes_out_used` = '0', `bytes_xfer_used` = '0', `files_in_used` = '0', `files_out_used` = '0', `files_xfer_used` = '0'" - ); - Database::pexecute($ins_stmt, array('name' => $loginname)); - $log->logAction(ADM_ACTION, LOG_NOTICE, "automatically added ftp-account for user '" . $loginname . "'"); - - $_stdsubdomain = ''; - - if ($createstdsubdomain == '1') { - - if (Settings::Get('system.stdsubdomain') !== null - && Settings::Get('system.stdsubdomain') != '' - ) { - $_stdsubdomain = $loginname . '.' . Settings::Get('system.stdsubdomain'); - } else { - $_stdsubdomain = $loginname . '.' . Settings::Get('system.hostname'); - } - - $ins_data = array( - 'domain' => $_stdsubdomain, - 'customerid' => $customerid, - 'adminid' => $userinfo['adminid'], - 'docroot' => $documentroot, - 'adddate' => time(), - 'phpenabled' => $phpenabled - ); - $ins_stmt = Database::prepare(" - INSERT INTO `" . TABLE_PANEL_DOMAINS . "` SET - `domain` = :domain, - `customerid` = :customerid, - `adminid` = :adminid, - `parentdomainid` = '0', - `documentroot` = :docroot, - `zonefile` = '', - `isemaildomain` = '0', - `caneditdomain` = '0', - `openbasedir` = '1', - `speciallogfile` = '0', - `specialsettings` = '', - `dkim_id` = '0', - `dkim_privkey` = '', - `dkim_pubkey` = '', - `phpenabled` = :phpenabled, - `add_date` = :adddate" - ); - Database::pexecute($ins_stmt, $ins_data); - $domainid = Database::lastInsertId(); - - // set ip <-> domain connection - $defaultips = explode(',', Settings::Get('system.defaultip')); - $ins_stmt = Database::prepare(" - INSERT INTO `" . TABLE_DOMAINTOIP . "` SET `id_domain` = :domainid, `id_ipandports` = :ipid" - ); - foreach ($defaultips as $defaultip) { - Database::pexecute($ins_stmt, array('domainid' => $domainid, 'ipid' => $defaultip)); - } - - $upd_stmt = Database::prepare(" - UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET `standardsubdomain` = :domainid WHERE `customerid` = :customerid" - ); - Database::pexecute($upd_stmt, array('domainid' => $domainid, 'customerid' => $customerid)); - $log->logAction(ADM_ACTION, LOG_NOTICE, "automatically added standardsubdomain for user '" . $loginname . "'"); - inserttask('1'); - } - - if ($sendpassword == '1') { - - $srv_hostname = Settings::Get('system.hostname'); - if (Settings::Get('system.froxlordirectlyviahostname') == '0') { - $srv_hostname .= '/froxlor'; - } - - $srv_ip_stmt = Database::prepare(" - SELECT ip, port FROM `".TABLE_PANEL_IPSANDPORTS."` - WHERE `id` = :defaultip - "); - $default_ips = Settings::Get('system.defaultip'); - $default_ips = explode(',', $default_ips); - $srv_ip = Database::pexecute_first($srv_ip_stmt, array('defaultip' => reset($default_ips))); - - $replace_arr = array( - 'FIRSTNAME' => $firstname, - 'NAME' => $name, - 'COMPANY' => $company, - 'SALUTATION' => getCorrectUserSalutation(array('firstname' => $firstname, 'name' => $name, 'company' => $company)), - 'USERNAME' => $loginname, - 'PASSWORD' => $password, - 'SERVER_HOSTNAME' => $srv_hostname, - 'SERVER_IP' => isset($srv_ip['ip']) ? $srv_ip['ip'] : '', - 'SERVER_PORT' => isset($srv_ip['port']) ? $srv_ip['port'] : '', - 'DOMAINNAME' => $_stdsubdomain - ); - - // Get mail templates from database; the ones from 'admin' are fetched for fallback - $result_stmt = Database::prepare(" - SELECT `value` FROM `" . TABLE_PANEL_TEMPLATES . "` - WHERE `adminid` = :adminid AND `language` = :deflang AND `templategroup` = 'mails' AND `varname` = 'createcustomer_subject'" - ); - $result = Database::pexecute_first($result_stmt, array('adminid' => $userinfo['adminid'], 'deflang' => $def_language)); - $mail_subject = html_entity_decode(replace_variables((($result['value'] != '') ? $result['value'] : $lng['mails']['createcustomer']['subject']), $replace_arr)); - - $result_stmt = Database::prepare(" - SELECT `value` FROM `" . TABLE_PANEL_TEMPLATES . "` - WHERE `adminid` = :adminid AND `language` = :deflang AND `templategroup` = 'mails' AND `varname` = 'createcustomer_mailbody'" - ); - $result = Database::pexecute_first($result_stmt, array('adminid' => $userinfo['adminid'], 'deflang' => $def_language)); - $mail_body = html_entity_decode(replace_variables((($result['value'] != '') ? $result['value'] : $lng['mails']['createcustomer']['mailbody']), $replace_arr)); - - $_mailerror = false; - try { - $mail->Subject = $mail_subject; - $mail->AltBody = $mail_body; - $mail->MsgHTML(str_replace("\n", "
    ", $mail_body)); - $mail->AddAddress($email, getCorrectUserSalutation(array('firstname' => $firstname, 'name' => $name, 'company' => $company))); - $mail->Send(); - } catch(phpmailerException $e) { - $mailerr_msg = $e->errorMessage(); - $_mailerror = true; - } catch (Exception $e) { - $mailerr_msg = $e->getMessage(); - $_mailerror = true; - } - - if ($_mailerror) { - $log->logAction(ADM_ACTION, LOG_ERR, "Error sending mail: " . $mailerr_msg); - standard_error('errorsendingmail', $email); - } - - $mail->ClearAddresses(); - $log->logAction(ADM_ACTION, LOG_NOTICE, "automatically sent password to user '" . $loginname . "'"); - } - redirectTo($filename, array('page' => $page, 's' => $s)); - } - - } else { - $language_options = ''; - - foreach ($languages as $language_file => $language_name) { - $language_options.= makeoption($language_name, $language_file, Settings::Get('panel.standardlanguage'), true); - } - - $diskspace_ul = makecheckbox('diskspace_ul', $lng['customer']['unlimited'], '-1', false, '0', true, true); - $traffic_ul = makecheckbox('traffic_ul', $lng['customer']['unlimited'], '-1', false, '0', true, true); - $subdomains_ul = makecheckbox('subdomains_ul', $lng['customer']['unlimited'], '-1', false, '0', true, true); - $emails_ul = makecheckbox('emails_ul', $lng['customer']['unlimited'], '-1', false, '0', true, true); - $email_accounts_ul = makecheckbox('email_accounts_ul', $lng['customer']['unlimited'], '-1', false, '0', true, true); - $email_forwarders_ul = makecheckbox('email_forwarders_ul', $lng['customer']['unlimited'], '-1', false, '0', true, true); - $email_quota_ul = makecheckbox('email_quota_ul', $lng['customer']['unlimited'], '-1', false, '0', true, true); - $ftps_ul = makecheckbox('ftps_ul', $lng['customer']['unlimited'], '-1', false, '0', true, true); - $tickets_ul = makecheckbox('tickets_ul', $lng['customer']['unlimited'], '-1', false, '0', true, true); - $mysqls_ul = makecheckbox('mysqls_ul', $lng['customer']['unlimited'], '-1', false, '0', true, true); - - $gender_options = makeoption($lng['gender']['undef'], 0, true, true, true); - $gender_options .= makeoption($lng['gender']['male'], 1, null, true, true); - $gender_options .= makeoption($lng['gender']['female'], 2, null, true, true); - - $phpconfigs = array(); - $configs = Database::query(" - SELECT c.*, fc.description as interpreter - FROM `" . TABLE_PANEL_PHPCONFIGS . "` c - LEFT JOIN `" . TABLE_PANEL_FPMDAEMONS . "` fc ON fc.id = c.fpmsettingid - "); - while ($row = $configs->fetch(PDO::FETCH_ASSOC)) { - if ((int) Settings::Get('phpfpm.enabled') == 1) { - $phpconfigs[] = array( - 'label' => $row['description'] . " [".$row['interpreter']."]
    ", - 'value' => $row['id'] - ); - } else { - $phpconfigs[] = array( - 'label' => $row['description']."
    ", - 'value' => $row['id'] - ); - } - } - - // hosting plans - $hosting_plans = ""; - $plans = Database::query(" - SELECT * - FROM `" . TABLE_PANEL_PLANS . "` - ORDER BY name ASC - "); - if (Database::num_rows() > 0){ - $hosting_plans .= makeoption("---", 0, 0, true, true); - } - while ($row = $plans->fetch(PDO::FETCH_ASSOC)) { - $hosting_plans .= makeoption($row['name'], $row['id'], 0, true, true); - } - - $customer_add_data = include_once dirname(__FILE__).'/lib/formfields/admin/customer/formfield.customer_add.php'; - $customer_add_form = htmlform::genHTMLForm($customer_add_data); - - $title = $customer_add_data['customer_add']['title']; - $image = $customer_add_data['customer_add']['image']; - - eval("echo \"" . getTemplate("customers/customers_add") . "\";"); + try { + Customers::getLocal($userinfo, $_POST)->add(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); } + redirectTo($filename, array('page' => $page, 's' => $s)); + } else { + $language_options = ''; + + foreach ($languages as $language_file => $language_name) { + $language_options.= makeoption($language_name, $language_file, Settings::Get('panel.standardlanguage'), true); + } + + $diskspace_ul = makecheckbox('diskspace_ul', $lng['customer']['unlimited'], '-1', false, '0', true, true); + $traffic_ul = makecheckbox('traffic_ul', $lng['customer']['unlimited'], '-1', false, '0', true, true); + $subdomains_ul = makecheckbox('subdomains_ul', $lng['customer']['unlimited'], '-1', false, '0', true, true); + $emails_ul = makecheckbox('emails_ul', $lng['customer']['unlimited'], '-1', false, '0', true, true); + $email_accounts_ul = makecheckbox('email_accounts_ul', $lng['customer']['unlimited'], '-1', false, '0', true, true); + $email_forwarders_ul = makecheckbox('email_forwarders_ul', $lng['customer']['unlimited'], '-1', false, '0', true, true); + $email_quota_ul = makecheckbox('email_quota_ul', $lng['customer']['unlimited'], '-1', false, '0', true, true); + $ftps_ul = makecheckbox('ftps_ul', $lng['customer']['unlimited'], '-1', false, '0', true, true); + $tickets_ul = makecheckbox('tickets_ul', $lng['customer']['unlimited'], '-1', false, '0', true, true); + $mysqls_ul = makecheckbox('mysqls_ul', $lng['customer']['unlimited'], '-1', false, '0', true, true); + + $gender_options = makeoption($lng['gender']['undef'], 0, true, true, true); + $gender_options .= makeoption($lng['gender']['male'], 1, null, true, true); + $gender_options .= makeoption($lng['gender']['female'], 2, null, true, true); + + $phpconfigs = array(); + $configs = Database::query(" + SELECT c.*, fc.description as interpreter + FROM `" . TABLE_PANEL_PHPCONFIGS . "` c + LEFT JOIN `" . TABLE_PANEL_FPMDAEMONS . "` fc ON fc.id = c.fpmsettingid + "); + while ($row = $configs->fetch(PDO::FETCH_ASSOC)) { + if ((int) Settings::Get('phpfpm.enabled') == 1) { + $phpconfigs[] = array( + 'label' => $row['description'] . " [".$row['interpreter']."]
    ", + 'value' => $row['id'] + ); + } else { + $phpconfigs[] = array( + 'label' => $row['description']."
    ", + 'value' => $row['id'] + ); + } + } + + // hosting plans + $hosting_plans = ""; + $plans = Database::query(" + SELECT * + FROM `" . TABLE_PANEL_PLANS . "` + ORDER BY name ASC + "); + if (Database::num_rows() > 0){ + $hosting_plans .= makeoption("---", 0, 0, true, true); + } + while ($row = $plans->fetch(PDO::FETCH_ASSOC)) { + $hosting_plans .= makeoption($row['name'], $row['id'], 0, true, true); + } + + $customer_add_data = include_once dirname(__FILE__).'/lib/formfields/admin/customer/formfield.customer_add.php'; + $customer_add_form = htmlform::genHTMLForm($customer_add_data); + + $title = $customer_add_data['customer_add']['title']; + $image = $customer_add_data['customer_add']['image']; + + eval("echo \"" . getTemplate("customers/customers_add") . "\";"); } } elseif($action == 'edit' diff --git a/lib/classes/api/abstract.ApiCommand.php b/lib/classes/api/abstract.ApiCommand.php index d11347fc..10e053a8 100644 --- a/lib/classes/api/abstract.ApiCommand.php +++ b/lib/classes/api/abstract.ApiCommand.php @@ -9,6 +9,8 @@ abstract class ApiCommand private $logger = null; + private $mail = null; + private $cmd_params = null; public function __construct($header = null, $params = null, $userinfo = null) @@ -20,12 +22,18 @@ abstract class ApiCommand $this->readUserData($header); } elseif (! empty($userinfo)) { $this->user_data = $userinfo; - $this->is_admin = ($userinfo['adminsession'] == 1 && $userinfo['adminid'] > 0) ? true : false; + $this->is_admin = (isset($userinfo['adminsession']) && $userinfo['adminsession'] == 1 && $userinfo['adminid'] > 0) ? true : false; } else { throw new Exception("Invalid user data", 500); } $this->logger = FroxlorLogger::getInstanceOf($this->user_data); + $this->initLang(); + $this->initMail(); + } + + private function initLang() + { // query the whole table $result_stmt = Database::query("SELECT * FROM `" . TABLE_PANEL_LANGUAGE . "`"); @@ -62,6 +70,37 @@ abstract class ApiCommand include_once makeSecurePath(FROXLOR_INSTALL_DIR . '/lng/lng_references.php'); } + private function initMail() + { + /** + * Initialize the mailingsystem + */ + $this->mail = new PHPMailer(true); + $this->mail->CharSet = "UTF-8"; + + if (Settings::Get('system.mail_use_smtp')) { + $this->mail->isSMTP(); + $this->mail->Host = Settings::Get('system.mail_smtp_host'); + $this->mail->SMTPAuth = Settings::Get('system.mail_smtp_auth') == '1' ? true : false; + $this->mail->Username = Settings::Get('system.mail_smtp_user'); + $this->mail->Password = Settings::Get('system.mail_smtp_passwd'); + if (Settings::Get('system.mail_smtp_usetls')) { + $this->mail->SMTPSecure = 'tls'; + } else { + $this->mail->SMTPAutoTLS = false; + } + $this->mail->Port = Settings::Get('system.mail_smtp_port'); + } + + if (PHPMailer::ValidateAddress(Settings::Get('panel.adminmail')) !== false) { + // set return-to address and custom sender-name, see #76 + $this->mail->SetFrom(Settings::Get('panel.adminmail'), Settings::Get('panel.adminmail_defname')); + if (Settings::Get('panel.adminmail_return') != '') { + $this->mail->AddReplyTo(Settings::Get('panel.adminmail_return'), Settings::Get('panel.adminmail_defname')); + } + } + } + public static function getLocal($userinfo = null, $params = null) { return new static(null, $params, $userinfo); @@ -145,6 +184,16 @@ abstract class ApiCommand return $this->logger; } + /** + * return mailer instance + * + * @return PHPMailer + */ + protected function mailer() + { + return $this->mail; + } + protected function response($status, $status_message, $data = null) { header("HTTP/1.1 " . $status); @@ -189,6 +238,9 @@ abstract class ApiCommand $this->user_data = Database::pexecute_first($sel_stmt, array( 'id' => ($this->is_admin ? $result['adminid'] : $result['customerid']) ), true, true); + if ($this->is_admin) { + $this->user_data['adminsession'] = 1; + } return true; } throw new Exception("Invalid API credentials", 400); diff --git a/lib/classes/api/commands/class.Customers.php b/lib/classes/api/commands/class.Customers.php index 2d30d2e7..8e72e50a 100644 --- a/lib/classes/api/commands/class.Customers.php +++ b/lib/classes/api/commands/class.Customers.php @@ -57,9 +57,596 @@ class Customers extends ApiCommand public function add() { + global $lng; + if ($this->isAdmin()) { - $this->logger()->logAction(ADM_ACTION, LOG_WARNING, "[API] added customer '" . $loginname . "'"); - return $this->response(200, "successfull", $ins_data); + if ($this->getUserDetail('customers_used') < $this->getUserDetail('customers') || $this->getUserDetail('customers') == '-1') { + + $idna_convert = new idna_convert_wrapper(); + $name = validate($this->getParam('name'), 'name', '', '', array(), true); + $firstname = validate($this->getParam('firstname'), 'first name', '', '', array(), true); + $company = validate($this->getParam('company'), 'company', '', '', array(), true); + $street = validate($this->getParam('street'), 'street', '', '', array(), true); + $zipcode = validate($this->getParam('zipcode'), 'zipcode', '/^[0-9 \-A-Z]*$/', '', array(), true); + $city = validate($this->getParam('city'), 'city', '', '', array(), true); + $phone = validate($this->getParam('phone'), 'phone', '/^[0-9\- \+\(\)\/]*$/', '', array(), true); + $fax = validate($this->getParam('fax'), 'fax', '/^[0-9\- \+\(\)\/]*$/', '', array(), true); + $email = $idna_convert->encode(validate($this->getParam('email'), 'email', '', '', array(), true)); + $customernumber = validate($this->getParam('customernumber'), 'customer number', '/^[A-Za-z0-9 \-]*$/Di', '', array(), true); + $def_language = validate($this->getParam('def_language'), 'default language', '', '', array(), true); + $gender = intval_ressource($this->getParam('gender', 0)); + + $custom_notes = validate(str_replace("\r\n", "\n", $this->getParam('custom_notes', '')), 'custom_notes', '/^[^\0]*$/', '', array(), true); + $custom_notes_show = $this->getParam('custom_notes_show', 0); + + $diskspace = intval_ressource($this->getParam('diskspace', 0)); + if ($this->getParam('diskspace_ul', 0) == 1) { + $diskspace = - 1; + } + + $traffic = doubleval_ressource($this->getParam('traffic', 0)); + if ($this->getParam('traffic_ul', 0) == 1) { + $traffic = - 1; + } + + $subdomains = intval_ressource($this->getParam('subdomains', 0)); + if ($this->getParam('subdomains_ul', 0) == 1) { + $subdomains = - 1; + } + + $emails = intval_ressource($this->getParam('emails', 0)); + if ($this->getParam('emails_ul', 0) == 1) { + $emails = - 1; + } + + $email_accounts = intval_ressource($this->getParam('email_accounts', 0)); + if ($this->getParam('email_accounts_ul', 0) == 1) { + $email_accounts = - 1; + } + + $email_forwarders = intval_ressource($this->getParam('email_forwarders', 0)); + if ($this->getParam('email_forwarders_ul', 0) == 1) { + $email_forwarders = - 1; + } + + if (Settings::Get('system.mail_quota_enabled') == '1') { + $email_quota = validate($this->getParam('email_quota', 0), 'email_quota', '/^\d+$/', 'vmailquotawrong', array( + '0', + '' + ), true); + if ($this->getParam('email_quota_ul', 0) == 1) { + $email_quota = - 1; + } + } else { + $email_quota = - 1; + } + + $email_imap = $this->getParam('email_imap', 0); + $email_pop3 = $this->getParam('email_pop3', 0); + + $ftps = intval_ressource($this->getParam('ftps', 0)); + if ($this->getParam('ftps_ul', 0) == 1) { + $ftps = - 1; + } + + if (Settings::Get('ticket.enabled') == '1') { + $tickets = intval_ressource($this->getParam('tickets', 0)); + if ($this->getParam('tickets_ul', 0) == 1) { + $tickets = - 1; + } + } else { + $tickets = - 1; + } + + $mysqls = intval_ressource($this->getParam('mysqls', 0)); + if ($this->getParam('mysqls_ul', 0) == 1) { + $mysqls = - 1; + } + + $createstdsubdomain = $this->getParam('createstdsubdomain', 0); + + $password = validate($this->getParam('new_customer_password', ''), 'password', '', '', array(), true); + // only check if not empty, + // cause empty == generate password automatically + if ($password != '') { + $password = validatePassword($password, true); + } + + // gender out of range? [0,2] + if ($gender < 0 || $gender > 2) { + $gender = 0; + } + + $sendpassword = $this->getParam('sendpassword', 0); + $phpenabled = $this->getParam('phpenabled', 0); + + $allowed_phpconfigs = array(); + if (! empty($this->getParam('allowed_phpconfigs', array())) && is_array($this->getParam('allowed_phpconfigs'))) { + foreach ($this->getParam('allowed_phpconfigs') as $allowed_phpconfig) { + $allowed_phpconfig = intval($allowed_phpconfig); + $allowed_phpconfigs[] = $allowed_phpconfig; + } + } + + $perlenabled = $this->getParam('perlenabled', 0); + $dnsenabled = $this->getParam('dnsenabled', 0); + $store_defaultindex = $this->getParam('store_defaultindex', 0); + + $diskspace = $diskspace * 1024; + $traffic = $traffic * 1024 * 1024; + + if (((($this->getUserDetail('diskspace_used') + $diskspace) > $this->getUserDetail('diskspace')) && ($this->getUserDetail('diskspace') / 1024) != '-1') || ((($this->getUserDetail('mysqls_used') + $mysqls) > $this->getUserDetail('mysqls')) && $this->getUserDetail('mysqls') != '-1') || ((($this->getUserDetail('emails_used') + $emails) > $this->getUserDetail('emails')) && $this->getUserDetail('emails') != '-1') || ((($this->getUserDetail('email_accounts_used') + $email_accounts) > $this->getUserDetail('email_accounts')) && $this->getUserDetail('email_accounts') != '-1') || ((($this->getUserDetail('email_forwarders_used') + $email_forwarders) > $this->getUserDetail('email_forwarders')) && $this->getUserDetail('email_forwarders') != '-1') || ((($this->getUserDetail('email_quota_used') + $email_quota) > $this->getUserDetail('email_quota')) && $this->getUserDetail('email_quota') != '-1' && Settings::Get('system.mail_quota_enabled') == '1') || ((($this->getUserDetail('ftps_used') + $ftps) > $this->getUserDetail('ftps')) && $this->getUserDetail('ftps') != '-1') || ((($this->getUserDetail('tickets_used') + $tickets) > $this->getUserDetail('tickets')) && $this->getUserDetail('tickets') != '-1') || ((($this->getUserDetail('subdomains_used') + $subdomains) > $this->getUserDetail('subdomains')) && $this->getUserDetail('subdomains') != '-1') || (($diskspace / 1024) == '-1' && ($this->getUserDetail('diskspace') / 1024) != '-1') || ($mysqls == '-1' && $this->getUserDetail('mysqls') != '-1') || ($emails == '-1' && $this->getUserDetail('emails') != '-1') || ($email_accounts == '-1' && $this->getUserDetail('email_accounts') != '-1') || ($email_forwarders == '-1' && $this->getUserDetail('email_forwarders') != '-1') || ($email_quota == '-1' && $this->getUserDetail('email_quota') != '-1' && Settings::Get('system.mail_quota_enabled') == '1') || ($ftps == '-1' && $this->getUserDetail('ftps') != '-1') || ($tickets == '-1' && $this->getUserDetail('tickets') != '-1') || ($subdomains == '-1' && $this->getUserDetail('subdomains') != '-1')) { + standard_error('youcantallocatemorethanyouhave', '', true); + } + + // Either $name and $firstname or the $company must be inserted + if ($name == '' && $company == '') { + standard_error(array( + 'stringisempty', + 'myname' + )); + } elseif ($firstname == '' && $company == '') { + standard_error(array( + 'stringisempty', + 'myfirstname' + ), '', true); + } elseif ($email == '') { + standard_error(array( + 'stringisempty', + 'emailadd' + ), '', true); + } elseif (! validateEmail($email)) { + standard_error('emailiswrong', $email, true); + } else { + + if ($this->getParam('new_loginname', '') != '') { + $accountnumber = intval(Settings::Get('system.lastaccountnumber')); + $loginname = validate($this->getParam('new_loginname'), 'loginname', '/^[a-z][a-z0-9\-_]+$/i', '', array(), true); + + // Accounts which match systemaccounts are not allowed, filtering them + if (preg_match('/^' . preg_quote(Settings::Get('customer.accountprefix'), '/') . '([0-9]+)/', $loginname)) { + standard_error('loginnameissystemaccount', Settings::Get('customer.accountprefix'), true); + } + + // Additional filtering for Bug #962 + if (function_exists('posix_getpwnam') && ! in_array("posix_getpwnam", explode(",", ini_get('disable_functions'))) && posix_getpwnam($loginname)) { + standard_error('loginnameissystemaccount', Settings::Get('customer.accountprefix'), true); + } + } else { + $accountnumber = intval(Settings::Get('system.lastaccountnumber')) + 1; + $loginname = Settings::Get('customer.accountprefix') . $accountnumber; + } + + // Check if the account already exists + $loginname_check_stmt = Database::prepare(" + SELECT `loginname` FROM `" . TABLE_PANEL_CUSTOMERS . "` WHERE `loginname` = :loginname + "); + $loginname_check = Database::pexecute_first($loginname_check_stmt, array( + 'loginname' => $loginname + ), true, true); + + $loginname_check_admin_stmt = Database::prepare(" + SELECT `loginname` FROM `" . TABLE_PANEL_ADMINS . "` WHERE `loginname` = :loginname + "); + $loginname_check_admin = Database::pexecute_first($loginname_check_admin_stmt, array( + 'loginname' => $loginname + ), true, true); + + if (strtolower($loginname_check['loginname']) == strtolower($loginname) || strtolower($loginname_check_admin['loginname']) == strtolower($loginname)) { + standard_error('loginnameexists', $loginname, true); + } elseif (! validateUsername($loginname, Settings::Get('panel.unix_names'), 14 - strlen(Settings::Get('customer.mysqlprefix')))) { + if (strlen($loginname) > 14 - strlen(Settings::Get('customer.mysqlprefix'))) { + standard_error('loginnameiswrong2', 14 - strlen(Settings::Get('customer.mysqlprefix')), true); + } else { + standard_error('loginnameiswrong', $loginname, true); + } + } + + $guid = intval(Settings::Get('system.lastguid')) + 1; + $documentroot = makeCorrectDir(Settings::Get('system.documentroot_prefix') . '/' . $loginname); + + if (file_exists($documentroot)) { + standard_error('documentrootexists', $documentroot, true); + } + + if ($createstdsubdomain != '1') { + $createstdsubdomain = '0'; + } + + if ($phpenabled != '0') { + $phpenabled = '1'; + } + + if ($perlenabled != '0') { + $perlenabled = '1'; + } + + if ($dnsenabled != '0') { + $dnsenabled = '1'; + } + + if ($password == '') { + $password = generatePassword(); + } + + $_theme = Settings::Get('panel.default_theme'); + + $ins_data = array( + 'adminid' => $this->getUserDetail('adminid'), + 'loginname' => $loginname, + 'passwd' => makeCryptPassword($password), + 'name' => $name, + 'firstname' => $firstname, + 'gender' => $gender, + 'company' => $company, + 'street' => $street, + 'zipcode' => $zipcode, + 'city' => $city, + 'phone' => $phone, + 'fax' => $fax, + 'email' => $email, + 'customerno' => $customernumber, + 'lang' => $def_language, + 'docroot' => $documentroot, + 'guid' => $guid, + 'diskspace' => $diskspace, + 'traffic' => $traffic, + 'subdomains' => $subdomains, + 'emails' => $emails, + 'email_accounts' => $email_accounts, + 'email_forwarders' => $email_forwarders, + 'email_quota' => $email_quota, + 'ftps' => $ftps, + 'tickets' => $tickets, + 'mysqls' => $mysqls, + 'phpenabled' => $phpenabled, + 'allowed_phpconfigs' => empty($allowed_phpconfigs) ? "" : json_encode($allowed_phpconfigs), + 'imap' => $email_imap, + 'pop3' => $email_pop3, + 'perlenabled' => $perlenabled, + 'dnsenabled' => $dnsenabled, + 'theme' => $_theme, + 'custom_notes' => $custom_notes, + 'custom_notes_show' => $custom_notes_show + ); + + $ins_stmt = Database::prepare(" + INSERT INTO `" . TABLE_PANEL_CUSTOMERS . "` SET + `adminid` = :adminid, + `loginname` = :loginname, + `password` = :passwd, + `name` = :name, + `firstname` = :firstname, + `gender` = :gender, + `company` = :company, + `street` = :street, + `zipcode` = :zipcode, + `city` = :city, + `phone` = :phone, + `fax` = :fax, + `email` = :email, + `customernumber` = :customerno, + `def_language` = :lang, + `documentroot` = :docroot, + `guid` = :guid, + `diskspace` = :diskspace, + `traffic` = :traffic, + `subdomains` = :subdomains, + `emails` = :emails, + `email_accounts` = :email_accounts, + `email_forwarders` = :email_forwarders, + `email_quota` = :email_quota, + `ftps` = :ftps, + `tickets` = :tickets, + `mysqls` = :mysqls, + `standardsubdomain` = '0', + `phpenabled` = :phpenabled, + `allowed_phpconfigs` = :allowed_phpconfigs, + `imap` = :imap, + `pop3` = :pop3, + `perlenabled` = :perlenabled, + `dnsenabled` = :dnsenabled, + `theme` = :theme, + `custom_notes` = :custom_notes, + `custom_notes_show` = :custom_notes_show + "); + Database::pexecute($ins_stmt, $ins_data, true, true); + + $customerid = Database::lastInsertId(); + $ins_data['customerid'] = $customerid; + + // update admin resource-usage + $admin_update_query = "UPDATE `" . TABLE_PANEL_ADMINS . "` SET `customers_used` = `customers_used` + 1"; + + if ($mysqls != '-1') { + $admin_update_query .= ", `mysqls_used` = `mysqls_used` + 0" . (int) $mysqls; + } + + if ($emails != '-1') { + $admin_update_query .= ", `emails_used` = `emails_used` + 0" . (int) $emails; + } + + if ($email_accounts != '-1') { + $admin_update_query .= ", `email_accounts_used` = `email_accounts_used` + 0" . (int) $email_accounts; + } + + if ($email_forwarders != '-1') { + $admin_update_query .= ", `email_forwarders_used` = `email_forwarders_used` + 0" . (int) $email_forwarders; + } + + if ($email_quota != '-1') { + $admin_update_query .= ", `email_quota_used` = `email_quota_used` + 0" . (int) $email_quota; + } + + if ($subdomains != '-1') { + $admin_update_query .= ", `subdomains_used` = `subdomains_used` + 0" . (int) $subdomains; + } + + if ($ftps != '-1') { + $admin_update_query .= ", `ftps_used` = `ftps_used` + 0" . (int) $ftps; + } + + if ($tickets != '-1' && Settings::Get('ticket.enabled') == 1) { + $admin_update_query .= ", `tickets_used` = `tickets_used` + 0" . (int) $tickets; + } + + if (($diskspace / 1024) != '-1') { + $admin_update_query .= ", `diskspace_used` = `diskspace_used` + 0" . (int) $diskspace; + } + + $admin_update_query .= " WHERE `adminid` = '" . (int) $this->getUserDetail('adminid') . "'"; + Database::query($admin_update_query); + + // update last guid + Settings::Set('system.lastguid', $guid, true); + + if ($accountnumber != intval(Settings::Get('system.lastaccountnumber'))) { + // update last account number + Settings::Set('system.lastaccountnumber', $accountnumber, true); + } + + $this->logger()->logAction(ADM_ACTION, LOG_INFO, "[API] added customer '" . $loginname . "'"); + $customer_ins_data = $ins_data; + unset($ins_data); + + // insert task to create homedir etc. + inserttask('2', $loginname, $guid, $guid, $store_defaultindex); + + // Using filesystem - quota, insert a task which cleans the filesystem - quota + inserttask('10'); + + // Add htpasswd for the webalizer stats + if (CRYPT_STD_DES == 1) { + $saltfordescrypt = substr(md5(uniqid(microtime(), 1)), 4, 2); + $htpasswdPassword = crypt($password, $saltfordescrypt); + } else { + $htpasswdPassword = crypt($password); + } + + $ins_stmt = Database::prepare(" + INSERT INTO `" . TABLE_PANEL_HTPASSWDS . "` SET + `customerid` = :customerid, + `username` = :username, + `password` = :passwd, + `path` = :path + "); + $ins_data = array( + 'customerid' => $customerid, + 'username' => $loginname, + 'passwd' => $htpasswdPassword + ); + + if (Settings::Get('system.awstats_enabled') == '1') { + $ins_data['path'] = makeCorrectDir($documentroot . '/awstats/'); + $this->logger()->logAction(ADM_ACTION, LOG_NOTICE, "[API] automatically added awstats htpasswd for user '" . $loginname . "'"); + } else { + $ins_data['path'] = makeCorrectDir($documentroot . '/webalizer/'); + $this->logger()->logAction(ADM_ACTION, LOG_NOTICE, "[API] automatically added webalizer htpasswd for user '" . $loginname . "'"); + } + Database::pexecute($ins_stmt, $ins_data, true, true); + + inserttask('1'); + $cryptPassword = makeCryptPassword($password); + // add FTP-User + // @fixme use Ftp-ApiCommand later + $ins_stmt = Database::prepare(" + INSERT INTO `" . TABLE_FTP_USERS . "` SET `customerid` = :customerid, `username` = :username, `description` = :desc, + `password` = :passwd, `homedir` = :homedir, `login_enabled` = 'y', `uid` = :guid, `gid` = :guid + "); + $ins_data = array( + 'customerid' => $customerid, + 'username' => $loginname, + 'passwd' => $cryptPassword, + 'homedir' => $documentroot, + 'guid' => $guid, + 'desc' => "Default" + ); + Database::pexecute($ins_stmt, $ins_data, true, true); + // add FTP-Group + // @fixme use Ftp-ApiCommand later + $ins_stmt = Database::prepare(" + INSERT INTO `" . TABLE_FTP_GROUPS . "` SET `customerid` = :customerid, `groupname` = :groupname, `gid` = :guid, `members` = :members + "); + $ins_data = array( + 'customerid' => $customerid, + 'groupname' => $loginname, + 'guid' => $guid, + 'members' => $loginname . ',' . Settings::Get('system.httpuser') + ); + + // also, add froxlor-local user to ftp-group (if exists!) to + // allow access to customer-directories from within the panel, which + // is necessary when pathedit = Dropdown + if ((int) Settings::Get('system.mod_fcgid_ownvhost') == 1 || (int) Settings::Get('phpfpm.enabled_ownvhost') == 1) { + if ((int) Settings::Get('system.mod_fcgid') == 1) { + $local_user = Settings::Get('system.mod_fcgid_httpuser'); + } else { + $local_user = Settings::Get('phpfpm.vhost_httpuser'); + } + // check froxlor-local user membership in ftp-group + // without this check addition may duplicate user in list if httpuser == local_user + if (strpos($ins_data['members'], $local_user) == false) { + $ins_data['members'] .= ',' . $local_user; + } + } + Database::pexecute($ins_stmt, $ins_data, true, true); + + // FTP-Quotatallies + // @fixme use Ftp-ApiCommand later + $ins_stmt = Database::prepare(" + INSERT INTO `" . TABLE_FTP_QUOTATALLIES . "` SET `name` = :name, `quota_type` = 'user', `bytes_in_used` = '0', + `bytes_out_used` = '0', `bytes_xfer_used` = '0', `files_in_used` = '0', `files_out_used` = '0', `files_xfer_used` = '0' + "); + Database::pexecute($ins_stmt, array( + 'name' => $loginname + ), true, true); + $this->logger()->logAction(ADM_ACTION, LOG_NOTICE, "[API] automatically added ftp-account for user '" . $loginname . "'"); + + $_stdsubdomain = ''; + if ($createstdsubdomain == '1') { + if (Settings::Get('system.stdsubdomain') !== null && Settings::Get('system.stdsubdomain') != '') { + $_stdsubdomain = $loginname . '.' . Settings::Get('system.stdsubdomain'); + } else { + $_stdsubdomain = $loginname . '.' . Settings::Get('system.hostname'); + } + + $ins_data = array( + 'domain' => $_stdsubdomain, + 'customerid' => $customerid, + 'adminid' => $this->getUserDetail('adminid'), + 'parentdomainid' => '0', + 'docroot' => $documentroot, + 'adddate' => time(), + 'phpenabled' => $phpenabled, + 'zonefile' => '', + 'isemaildomain' => '0', + 'caneditdomain' => '0', + 'openbasedir' => '1', + 'speciallogfile' => '0', + 'dkim_id' => '0', + 'dkim_privkey' => '', + 'dkim_pubkey' => '', + 'ipandport' => explode(',', Settings::Get('system.defaultip')) + ); + $domainid = - 1; + try { + $std_domain = Domains::getLocal($this->getUserData(), $ins_data)->add(); + $domainid = json_decode($std_domain, true)['data']['id']; + } catch (Exception $e) { + $this->logger()->logAction(ADM_ACTION, LOG_ERR, "[API] Unable to add standard-subdomain: " . $e->getMessage()); + } + + if ($domainid > 0) { + // set ip <-> domain connection + $defaultips = explode(',', Settings::Get('system.defaultip')); + $ins_stmt = Database::prepare(" + INSERT INTO `" . TABLE_DOMAINTOIP . "` SET `id_domain` = :domainid, `id_ipandports` = :ipid + "); + foreach ($defaultips as $defaultip) { + Database::pexecute($ins_stmt, array( + 'domainid' => $domainid, + 'ipid' => $defaultip + ), true, true); + } + + $upd_stmt = Database::prepare(" + UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET `standardsubdomain` = :domainid WHERE `customerid` = :customerid + "); + Database::pexecute($upd_stmt, array( + 'domainid' => $domainid, + 'customerid' => $customerid + ), true, true); + $this->logger()->logAction(ADM_ACTION, LOG_NOTICE, "[API] automatically added standardsubdomain for user '" . $loginname . "'"); + inserttask('1'); + } + } + + if ($sendpassword == '1') { + + $srv_hostname = Settings::Get('system.hostname'); + if (Settings::Get('system.froxlordirectlyviahostname') == '0') { + $srv_hostname .= '/froxlor'; + } + + $srv_ip_stmt = Database::prepare(" + SELECT ip, port FROM `" . TABLE_PANEL_IPSANDPORTS . "` + WHERE `id` = :defaultip + "); + $default_ips = Settings::Get('system.defaultip'); + $default_ips = explode(',', $default_ips); + $srv_ip = Database::pexecute_first($srv_ip_stmt, array( + 'defaultip' => reset($default_ips) + ), true, true); + + $replace_arr = array( + 'FIRSTNAME' => $firstname, + 'NAME' => $name, + 'COMPANY' => $company, + 'SALUTATION' => getCorrectUserSalutation(array( + 'firstname' => $firstname, + 'name' => $name, + 'company' => $company + )), + 'USERNAME' => $loginname, + 'PASSWORD' => $password, + 'SERVER_HOSTNAME' => $srv_hostname, + 'SERVER_IP' => isset($srv_ip['ip']) ? $srv_ip['ip'] : '', + 'SERVER_PORT' => isset($srv_ip['port']) ? $srv_ip['port'] : '', + 'DOMAINNAME' => $_stdsubdomain + ); + + // Get mail templates from database; the ones from 'admin' are fetched for fallback + $result_stmt = Database::prepare(" + SELECT `value` FROM `" . TABLE_PANEL_TEMPLATES . "` + WHERE `adminid` = :adminid AND `language` = :deflang AND `templategroup` = 'mails' AND `varname` = 'createcustomer_subject'"); + $result = Database::pexecute_first($result_stmt, array( + 'adminid' => $this->getUserDetail('adminid'), + 'deflang' => $def_language + ), true, true); + $mail_subject = html_entity_decode(replace_variables((($result['value'] != '') ? $result['value'] : $lng['mails']['createcustomer']['subject']), $replace_arr)); + + $result_stmt = Database::prepare(" + SELECT `value` FROM `" . TABLE_PANEL_TEMPLATES . "` + WHERE `adminid` = :adminid AND `language` = :deflang AND `templategroup` = 'mails' AND `varname` = 'createcustomer_mailbody'"); + $result = Database::pexecute_first($result_stmt, array( + 'adminid' => $this->getUserDetail('adminid'), + 'deflang' => $def_language + ), true, true); + $mail_body = html_entity_decode(replace_variables((($result['value'] != '') ? $result['value'] : $lng['mails']['createcustomer']['mailbody']), $replace_arr)); + + $_mailerror = false; + try { + $this->mailer()->Subject = $mail_subject; + $this->mailer()->AltBody = $mail_body; + $this->mailer()->MsgHTML(str_replace("\n", "
    ", $mail_body)); + $this->mailer()->AddAddress($email, getCorrectUserSalutation(array( + 'firstname' => $firstname, + 'name' => $name, + 'company' => $company + ))); + $this->mailer()->Send(); + } catch (phpmailerException $e) { + $mailerr_msg = $e->errorMessage(); + $_mailerror = true; + } catch (Exception $e) { + $mailerr_msg = $e->getMessage(); + $_mailerror = true; + } + + if ($_mailerror) { + $this->logger()->logAction(ADM_ACTION, LOG_ERR, "[API] Error sending mail: " . $mailerr_msg); + standard_error('errorsendingmail', $email, true); + } + + $this->mailer()->ClearAddresses(); + $this->logger()->logAction(ADM_ACTION, LOG_NOTICE, "[API] automatically sent password to user '" . $loginname . "'"); + } + } + + $this->logger()->logAction(ADM_ACTION, LOG_WARNING, "[API] added customer '" . $loginname . "'"); + return $this->response(200, "successfull", $customer_ins_data); + } + throw new Exception("No more resources available", 406); } throw new Exception("Not allowed to execute given command.", 403); } @@ -89,10 +676,244 @@ class Customers extends ApiCommand 'id' => $id ))->get(); $result = json_decode($json_result, true)['data']; - + + // @fixme use Databases-ApiCommand later + $databases_stmt = Database::prepare(" + SELECT * FROM `" . TABLE_PANEL_DATABASES . "` + WHERE `customerid` = :id ORDER BY `dbserver` + "); + Database::pexecute($databases_stmt, array( + 'id' => $id + )); + Database::needRoot(true); + $last_dbserver = 0; + + $dbm = new DbManager($log); + + while ($row_database = $databases_stmt->fetch(PDO::FETCH_ASSOC)) { + if ($last_dbserver != $row_database['dbserver']) { + Database::needRoot(true, $row_database['dbserver']); + $dbm->getManager()->flushPrivileges(); + $last_dbserver = $row_database['dbserver']; + } + $dbm->getManager()->deleteDatabase($row_database['databasename']); + } + $dbm->getManager()->flushPrivileges(); + Database::needRoot(false); + + // delete customer itself + $stmt = Database::prepare("DELETE FROM `" . TABLE_PANEL_CUSTOMERS . "` WHERE `customerid` = :id"); + Database::pexecute($stmt, array( + 'id' => $id + ), true, true); + + // delete customer databases + $stmt = Database::prepare("DELETE FROM `" . TABLE_PANEL_DATABASES . "` WHERE `customerid` = :id"); + Database::pexecute($stmt, array( + 'id' => $id + ), true, true); + + // first gather all domain-id's to clean up panel_domaintoip and dns-entries accordingly + $did_stmt = Database::prepare("SELECT `id` FROM `" . TABLE_PANEL_DOMAINS . "` WHERE `customerid` = :id"); + Database::pexecute($did_stmt, array( + 'id' => $id + ), true, true); + while ($row = $did_stmt->fetch(PDO::FETCH_ASSOC)) { + // remove domain->ip connection + $stmt = Database::prepare("DELETE FROM `" . TABLE_DOMAINTOIP . "` WHERE `id_domain` = :did"); + Database::pexecute($stmt, array( + 'did' => $row['id'] + ), true, true); + // remove domain->dns entries + $stmt = Database::prepare("DELETE FROM `" . TABLE_DOMAIN_DNS . "` WHERE `domain_id` = :did"); + Database::pexecute($stmt, array( + 'did' => $row['id'] + ), true, true); + } + // remove customer domains + $stmt = Database::prepare("DELETE FROM `" . TABLE_PANEL_DOMAINS . "` WHERE `customerid` = :id"); + Database::pexecute($stmt, array( + 'id' => $id + ), true, true); + $domains_deleted = $stmt->rowCount(); + + // delete htpasswds + $stmt = Database::prepare("DELETE FROM `" . TABLE_PANEL_HTPASSWDS . "` WHERE `customerid` = :id"); + Database::pexecute($stmt, array( + 'id' => $id + ), true, true); + + // delete htaccess options + $stmt = Database::prepare("DELETE FROM `" . TABLE_PANEL_HTACCESS . "` WHERE `customerid` = :id"); + Database::pexecute($stmt, array( + 'id' => $id + ), true, true); + + // delete potential existing sessions + $stmt = Database::prepare("DELETE FROM `" . TABLE_PANEL_SESSIONS . "` WHERE `userid` = :id AND `adminsession` = '0'"); + Database::pexecute($stmt, array( + 'id' => $id + ), true, true); + + // delete traffic information + $stmt = Database::prepare("DELETE FROM `" . TABLE_PANEL_TRAFFIC . "` WHERE `customerid` = :id"); + Database::pexecute($stmt, array( + 'id' => $id + ), true, true); + + // remove diskspace analysis + $stmt = Database::prepare("DELETE FROM `" . TABLE_PANEL_DISKSPACE . "` WHERE `customerid` = :id"); + Database::pexecute($stmt, array( + 'id' => $id + ), true, true); + + // delete mail-accounts + $stmt = Database::prepare("DELETE FROM `" . TABLE_MAIL_USERS . "` WHERE `customerid` = :id"); + Database::pexecute($stmt, array( + 'id' => $id + ), true, true); + + // delete mail-addresses + $stmt = Database::prepare("DELETE FROM `" . TABLE_MAIL_VIRTUAL . "` WHERE `customerid` = :id"); + Database::pexecute($stmt, array( + 'id' => $id + ), true, true); + + // gather ftp-user names + $result2_stmt = Database::prepare("SELECT `username` FROM `" . TABLE_FTP_USERS . "` WHERE `customerid` = :id"); + Database::pexecute($result2_stmt, array( + 'id' => $id + ), true, true); + while ($row = $result2_stmt->fetch(PDO::FETCH_ASSOC)) { + // delete ftp-quotatallies by username + $stmt = Database::prepare("DELETE FROM `" . TABLE_FTP_QUOTATALLIES . "` WHERE `name` = :name"); + Database::pexecute($stmt, array( + 'name' => $row['username'] + ), true, true); + } + + // remove ftp-group + $stmt = Database::prepare("DELETE FROM `" . TABLE_FTP_GROUPS . "` WHERE `customerid` = :id"); + Database::pexecute($stmt, array( + 'id' => $id + ), true, true); + + // remove ftp-users + $stmt = Database::prepare("DELETE FROM `" . TABLE_FTP_USERS . "` WHERE `customerid` = :id"); + Database::pexecute($stmt, array( + 'id' => $id + ), true, true); + + // Delete all waiting "create user" -tasks for this user, #276 + // Note: the WHERE selects part of a serialized array, but it should be safe this way + $del_stmt = Database::prepare(" + DELETE FROM `" . TABLE_PANEL_TASKS . "` + WHERE `type` = '2' AND `data` LIKE :loginname + "); + Database::pexecute($del_stmt, array( + 'loginname' => "%:{$result['loginname']};%" + ), true, true); + + // update admin-resource-usage + $admin_update_query = "UPDATE `" . TABLE_PANEL_ADMINS . "` SET `customers_used` = `customers_used` - 1 "; + $admin_update_query .= ", `domains_used` = `domains_used` - 0" . (int) ($domains_deleted - $result['subdomains_used']); + + if ($result['mysqls'] != '-1') { + $admin_update_query .= ", `mysqls_used` = `mysqls_used` - 0" . (int) $result['mysqls']; + } + + if ($result['emails'] != '-1') { + $admin_update_query .= ", `emails_used` = `emails_used` - 0" . (int) $result['emails']; + } + + if ($result['email_accounts'] != '-1') { + $admin_update_query .= ", `email_accounts_used` = `email_accounts_used` - 0" . (int) $result['email_accounts']; + } + + if ($result['email_forwarders'] != '-1') { + $admin_update_query .= ", `email_forwarders_used` = `email_forwarders_used` - 0" . (int) $result['email_forwarders']; + } + + if ($result['email_quota'] != '-1') { + $admin_update_query .= ", `email_quota_used` = `email_quota_used` - 0" . (int) $result['email_quota']; + } + + if ($result['subdomains'] != '-1') { + $admin_update_query .= ", `subdomains_used` = `subdomains_used` - 0" . (int) $result['subdomains']; + } + + if ($result['ftps'] != '-1') { + $admin_update_query .= ", `ftps_used` = `ftps_used` - 0" . (int) $result['ftps']; + } + + if ($result['tickets'] != '-1') { + $admin_update_query .= ", `tickets_used` = `tickets_used` - 0" . (int) $result['tickets']; + } + + if (($result['diskspace'] / 1024) != '-1') { + $admin_update_query .= ", `diskspace_used` = `diskspace_used` - 0" . (int) $result['diskspace']; + } + + $admin_update_query .= " WHERE `adminid` = '" . (int) $result['adminid'] . "'"; + Database::query($admin_update_query); + + // rebuild configs + inserttask('1'); + + // Using nameserver, insert a task which rebuilds the server config + inserttask('4'); + + if ($this->getParam('delete_userfiles', 0) == 1) { + // insert task to remove the customers files from the filesystem + inserttask('6', $result['loginname']); + } + + // Using filesystem - quota, insert a task which cleans the filesystem - quota + inserttask('10'); + + // move old tickets to archive + $tickets = ticket::customerHasTickets($id); + if ($tickets !== false && isset($tickets[0])) { + foreach ($tickets as $ticket) { + $now = time(); + $mainticket = ticket::getInstanceOf($userinfo, (int) $ticket); + $mainticket->Set('lastchange', $now, true, true); + $mainticket->Set('lastreplier', '1', true, true); + $mainticket->Set('status', '3', true, true); + $mainticket->Update(); + $mainticket->Archive(); + $this->logger()->logAction(ADM_ACTION, LOG_NOTICE, "[API] archived ticket '" . $mainticket->Get('subject') . "'"); + } + } + $this->logger()->logAction(ADM_ACTION, LOG_WARNING, "[API] deleted customer '" . $result['loginname'] . "'"); return $this->response(200, "successfull", $result); } throw new Exception("Not allowed to execute given command.", 403); } + + public function unlock() + { + if ($this->isAdmin()) { + $id = $this->getParam('id'); + + $json_result = Customers::getLocal($this->getUserData(), array( + 'id' => $id + ))->get(); + $result = json_decode($json_result, true)['data']; + + $result_stmt = Database::prepare(" + UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET + `loginfail_count` = '0' + WHERE `customerid`= :id + "); + Database::pexecute($result_stmt, array( + 'id' => $id + ), true, true); + + $this->logger()->logAction(ADM_ACTION, LOG_WARNING, "[API] unlocked customer '" . $result['loginname'] . "'"); + return $this->response(200, "successfull", $result); + } + throw new Exception("Not allowed to execute given command.", 403); + } } diff --git a/lib/classes/api/commands/class.Domains.php b/lib/classes/api/commands/class.Domains.php index 1f44d4f6..ed5db265 100644 --- a/lib/classes/api/commands/class.Domains.php +++ b/lib/classes/api/commands/class.Domains.php @@ -492,6 +492,7 @@ class Domains extends ApiCommand $issubof = '0'; } + $idna_convert = new idna_convert_wrapper(); if ($domain == '') { standard_error(array( 'stringisempty', @@ -649,6 +650,9 @@ class Domains extends ApiCommand "); Database::pexecute($ins_stmt, $ins_data, true, true); $domainid = Database::lastInsertId(); + $ins_data['id'] = $domainid; + $domain_ins_data = $ins_data; + unset($ins_data); $upd_stmt = Database::prepare(" UPDATE `" . TABLE_PANEL_ADMINS . "` SET `domains_used` = `domains_used` + 1 @@ -688,7 +692,7 @@ class Domains extends ApiCommand inserttask('4'); $this->logger()->logAction(ADM_ACTION, LOG_WARNING, "[API] added domain '" . $domain . "'"); - return $this->response(200, "successfull", $ins_data); + return $this->response(200, "successfull", $domain_ins_data); } } throw new Exception("No more resources available", 406); diff --git a/lib/functions/validate/function.validatePassword.php b/lib/functions/validate/function.validatePassword.php index 48922ba7..03d2b0f9 100644 --- a/lib/functions/validate/function.validatePassword.php +++ b/lib/functions/validate/function.validatePassword.php @@ -26,14 +26,16 @@ * * @return string either the password or an errormessage+exit */ -function validatePassword($password = null) { +function validatePassword($password = null, $json_response = false) { if (Settings::Get('panel.password_min_length') > 0) { $password = validate( $password, Settings::Get('panel.password_min_length'), '/^.{'.(int)Settings::Get('panel.password_min_length').',}$/D', - 'notrequiredpasswordlength' + 'notrequiredpasswordlength', + array(), + $json_response ); } @@ -42,7 +44,9 @@ function validatePassword($password = null) { $password, Settings::Get('panel.password_regex'), Settings::Get('panel.password_regex'), - 'notrequiredpasswordcomplexity' + 'notrequiredpasswordcomplexity', + array(), + $json_response ); } else { if (Settings::Get('panel.password_alpha_lower')) { @@ -50,7 +54,9 @@ function validatePassword($password = null) { $password, '/.*[a-z]+.*/', '/.*[a-z]+.*/', - 'notrequiredpasswordcomplexity' + 'notrequiredpasswordcomplexity', + array(), + $json_response ); } if (Settings::Get('panel.password_alpha_upper')) { @@ -58,7 +64,9 @@ function validatePassword($password = null) { $password, '/.*[A-Z]+.*/', '/.*[A-Z]+.*/', - 'notrequiredpasswordcomplexity' + 'notrequiredpasswordcomplexity', + array(), + $json_response ); } if (Settings::Get('panel.password_numeric')) { @@ -66,7 +74,9 @@ function validatePassword($password = null) { $password, '/.*[0-9]+.*/', '/.*[0-9]+.*/', - 'notrequiredpasswordcomplexity' + 'notrequiredpasswordcomplexity', + array(), + $json_response ); } if (Settings::Get('panel.password_special_char_required')) { @@ -74,7 +84,9 @@ function validatePassword($password = null) { $password, '/.*[' . preg_quote(Settings::Get('panel.password_special_char')) . ']+.*/', '/.*[' . preg_quote(Settings::Get('panel.password_special_char')) . ']+.*/', - 'notrequiredpasswordcomplexity' + 'notrequiredpasswordcomplexity', + array(), + $json_response ); } } From 8978dd3a4bdb6b5fc2b7c5e78946c5b0e1b63cea Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Fri, 16 Feb 2018 13:55:12 +0100 Subject: [PATCH 0436/1335] std-subdomain <> ip connection is already handled by Domains::add() Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/commands/class.Customers.php | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/lib/classes/api/commands/class.Customers.php b/lib/classes/api/commands/class.Customers.php index 8e72e50a..cad36cfe 100644 --- a/lib/classes/api/commands/class.Customers.php +++ b/lib/classes/api/commands/class.Customers.php @@ -537,21 +537,9 @@ class Customers extends ApiCommand } if ($domainid > 0) { - // set ip <-> domain connection - $defaultips = explode(',', Settings::Get('system.defaultip')); - $ins_stmt = Database::prepare(" - INSERT INTO `" . TABLE_DOMAINTOIP . "` SET `id_domain` = :domainid, `id_ipandports` = :ipid - "); - foreach ($defaultips as $defaultip) { - Database::pexecute($ins_stmt, array( - 'domainid' => $domainid, - 'ipid' => $defaultip - ), true, true); - } - $upd_stmt = Database::prepare(" - UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET `standardsubdomain` = :domainid WHERE `customerid` = :customerid - "); + UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET `standardsubdomain` = :domainid WHERE `customerid` = :customerid + "); Database::pexecute($upd_stmt, array( 'domainid' => $domainid, 'customerid' => $customerid From 60defd3cdf668bada02e95bbb4b01580a7c4d8bf Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Fri, 16 Feb 2018 14:34:55 +0100 Subject: [PATCH 0437/1335] fix unlimited flags when adding customer; add debug flag to log all api-requests for testing purposes now Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/abstract.ApiCommand.php | 5 + lib/classes/api/commands/class.Customers.php | 136 +++++++++---------- 2 files changed, 73 insertions(+), 68 deletions(-) diff --git a/lib/classes/api/abstract.ApiCommand.php b/lib/classes/api/abstract.ApiCommand.php index 10e053a8..5ad847b3 100644 --- a/lib/classes/api/abstract.ApiCommand.php +++ b/lib/classes/api/abstract.ApiCommand.php @@ -3,6 +3,8 @@ abstract class ApiCommand { + private $debug = true; + private $is_admin = false; private $user_data = null; @@ -27,6 +29,9 @@ abstract class ApiCommand throw new Exception("Invalid user data", 500); } $this->logger = FroxlorLogger::getInstanceOf($this->user_data); + if ($this->debug) { + $this->logger()->logAction(LOG_ERROR, LOG_DEBUG, json_encode($params, JSON_UNESCAPED_SLASHES)); + } $this->initLang(); $this->initMail(); diff --git a/lib/classes/api/commands/class.Customers.php b/lib/classes/api/commands/class.Customers.php index cad36cfe..84525d0c 100644 --- a/lib/classes/api/commands/class.Customers.php +++ b/lib/classes/api/commands/class.Customers.php @@ -80,32 +80,32 @@ class Customers extends ApiCommand $custom_notes_show = $this->getParam('custom_notes_show', 0); $diskspace = intval_ressource($this->getParam('diskspace', 0)); - if ($this->getParam('diskspace_ul', 0) == 1) { + if ($this->getParam('diskspace_ul', 0) == -1) { $diskspace = - 1; } $traffic = doubleval_ressource($this->getParam('traffic', 0)); - if ($this->getParam('traffic_ul', 0) == 1) { + if ($this->getParam('traffic_ul', 0) == -1) { $traffic = - 1; } $subdomains = intval_ressource($this->getParam('subdomains', 0)); - if ($this->getParam('subdomains_ul', 0) == 1) { + if ($this->getParam('subdomains_ul', 0) == -1) { $subdomains = - 1; } $emails = intval_ressource($this->getParam('emails', 0)); - if ($this->getParam('emails_ul', 0) == 1) { + if ($this->getParam('emails_ul', 0) == -1) { $emails = - 1; } $email_accounts = intval_ressource($this->getParam('email_accounts', 0)); - if ($this->getParam('email_accounts_ul', 0) == 1) { + if ($this->getParam('email_accounts_ul', 0) == -1) { $email_accounts = - 1; } $email_forwarders = intval_ressource($this->getParam('email_forwarders', 0)); - if ($this->getParam('email_forwarders_ul', 0) == 1) { + if ($this->getParam('email_forwarders_ul', 0) == -1) { $email_forwarders = - 1; } @@ -114,7 +114,7 @@ class Customers extends ApiCommand '0', '' ), true); - if ($this->getParam('email_quota_ul', 0) == 1) { + if ($this->getParam('email_quota_ul', 0) == -1) { $email_quota = - 1; } } else { @@ -125,13 +125,13 @@ class Customers extends ApiCommand $email_pop3 = $this->getParam('email_pop3', 0); $ftps = intval_ressource($this->getParam('ftps', 0)); - if ($this->getParam('ftps_ul', 0) == 1) { + if ($this->getParam('ftps_ul', 0) == -1) { $ftps = - 1; } if (Settings::Get('ticket.enabled') == '1') { $tickets = intval_ressource($this->getParam('tickets', 0)); - if ($this->getParam('tickets_ul', 0) == 1) { + if ($this->getParam('tickets_ul', 0) == -1) { $tickets = - 1; } } else { @@ -139,7 +139,7 @@ class Customers extends ApiCommand } $mysqls = intval_ressource($this->getParam('mysqls', 0)); - if ($this->getParam('mysqls_ul', 0) == 1) { + if ($this->getParam('mysqls_ul', 0) == -1) { $mysqls = - 1; } @@ -219,15 +219,15 @@ class Customers extends ApiCommand // Check if the account already exists $loginname_check_stmt = Database::prepare(" - SELECT `loginname` FROM `" . TABLE_PANEL_CUSTOMERS . "` WHERE `loginname` = :loginname - "); + SELECT `loginname` FROM `" . TABLE_PANEL_CUSTOMERS . "` WHERE `loginname` = :loginname + "); $loginname_check = Database::pexecute_first($loginname_check_stmt, array( 'loginname' => $loginname ), true, true); $loginname_check_admin_stmt = Database::prepare(" - SELECT `loginname` FROM `" . TABLE_PANEL_ADMINS . "` WHERE `loginname` = :loginname - "); + SELECT `loginname` FROM `" . TABLE_PANEL_ADMINS . "` WHERE `loginname` = :loginname + "); $loginname_check_admin = Database::pexecute_first($loginname_check_admin_stmt, array( 'loginname' => $loginname ), true, true); @@ -311,45 +311,45 @@ class Customers extends ApiCommand ); $ins_stmt = Database::prepare(" - INSERT INTO `" . TABLE_PANEL_CUSTOMERS . "` SET - `adminid` = :adminid, - `loginname` = :loginname, - `password` = :passwd, - `name` = :name, - `firstname` = :firstname, - `gender` = :gender, - `company` = :company, - `street` = :street, - `zipcode` = :zipcode, - `city` = :city, - `phone` = :phone, - `fax` = :fax, - `email` = :email, - `customernumber` = :customerno, - `def_language` = :lang, - `documentroot` = :docroot, - `guid` = :guid, - `diskspace` = :diskspace, - `traffic` = :traffic, - `subdomains` = :subdomains, - `emails` = :emails, - `email_accounts` = :email_accounts, - `email_forwarders` = :email_forwarders, - `email_quota` = :email_quota, - `ftps` = :ftps, - `tickets` = :tickets, - `mysqls` = :mysqls, - `standardsubdomain` = '0', - `phpenabled` = :phpenabled, - `allowed_phpconfigs` = :allowed_phpconfigs, - `imap` = :imap, - `pop3` = :pop3, - `perlenabled` = :perlenabled, - `dnsenabled` = :dnsenabled, - `theme` = :theme, - `custom_notes` = :custom_notes, - `custom_notes_show` = :custom_notes_show - "); + INSERT INTO `" . TABLE_PANEL_CUSTOMERS . "` SET + `adminid` = :adminid, + `loginname` = :loginname, + `password` = :passwd, + `name` = :name, + `firstname` = :firstname, + `gender` = :gender, + `company` = :company, + `street` = :street, + `zipcode` = :zipcode, + `city` = :city, + `phone` = :phone, + `fax` = :fax, + `email` = :email, + `customernumber` = :customerno, + `def_language` = :lang, + `documentroot` = :docroot, + `guid` = :guid, + `diskspace` = :diskspace, + `traffic` = :traffic, + `subdomains` = :subdomains, + `emails` = :emails, + `email_accounts` = :email_accounts, + `email_forwarders` = :email_forwarders, + `email_quota` = :email_quota, + `ftps` = :ftps, + `tickets` = :tickets, + `mysqls` = :mysqls, + `standardsubdomain` = '0', + `phpenabled` = :phpenabled, + `allowed_phpconfigs` = :allowed_phpconfigs, + `imap` = :imap, + `pop3` = :pop3, + `perlenabled` = :perlenabled, + `dnsenabled` = :dnsenabled, + `theme` = :theme, + `custom_notes` = :custom_notes, + `custom_notes_show` = :custom_notes_show + "); Database::pexecute($ins_stmt, $ins_data, true, true); $customerid = Database::lastInsertId(); @@ -424,12 +424,12 @@ class Customers extends ApiCommand } $ins_stmt = Database::prepare(" - INSERT INTO `" . TABLE_PANEL_HTPASSWDS . "` SET - `customerid` = :customerid, - `username` = :username, - `password` = :passwd, - `path` = :path - "); + INSERT INTO `" . TABLE_PANEL_HTPASSWDS . "` SET + `customerid` = :customerid, + `username` = :username, + `password` = :passwd, + `path` = :path + "); $ins_data = array( 'customerid' => $customerid, 'username' => $loginname, @@ -450,9 +450,9 @@ class Customers extends ApiCommand // add FTP-User // @fixme use Ftp-ApiCommand later $ins_stmt = Database::prepare(" - INSERT INTO `" . TABLE_FTP_USERS . "` SET `customerid` = :customerid, `username` = :username, `description` = :desc, - `password` = :passwd, `homedir` = :homedir, `login_enabled` = 'y', `uid` = :guid, `gid` = :guid - "); + INSERT INTO `" . TABLE_FTP_USERS . "` SET `customerid` = :customerid, `username` = :username, `description` = :desc, + `password` = :passwd, `homedir` = :homedir, `login_enabled` = 'y', `uid` = :guid, `gid` = :guid + "); $ins_data = array( 'customerid' => $customerid, 'username' => $loginname, @@ -465,8 +465,8 @@ class Customers extends ApiCommand // add FTP-Group // @fixme use Ftp-ApiCommand later $ins_stmt = Database::prepare(" - INSERT INTO `" . TABLE_FTP_GROUPS . "` SET `customerid` = :customerid, `groupname` = :groupname, `gid` = :guid, `members` = :members - "); + INSERT INTO `" . TABLE_FTP_GROUPS . "` SET `customerid` = :customerid, `groupname` = :groupname, `gid` = :guid, `members` = :members + "); $ins_data = array( 'customerid' => $customerid, 'groupname' => $loginname, @@ -553,13 +553,13 @@ class Customers extends ApiCommand $srv_hostname = Settings::Get('system.hostname'); if (Settings::Get('system.froxlordirectlyviahostname') == '0') { - $srv_hostname .= '/froxlor'; + $srv_hostname .= '/'.basename(FROXLOR_INSTALL_DIR); } $srv_ip_stmt = Database::prepare(" - SELECT ip, port FROM `" . TABLE_PANEL_IPSANDPORTS . "` - WHERE `id` = :defaultip - "); + SELECT ip, port FROM `" . TABLE_PANEL_IPSANDPORTS . "` + WHERE `id` = :defaultip + "); $default_ips = Settings::Get('system.defaultip'); $default_ips = explode(',', $default_ips); $srv_ip = Database::pexecute_first($srv_ip_stmt, array( From 7c961647704c61c9605253af6555375f3bc2c72a Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Fri, 16 Feb 2018 15:39:59 +0100 Subject: [PATCH 0438/1335] fix lng availability; add returncode for not-found messages Signed-off-by: Michael Kaufmann (d00p) --- admin_customers.php | 19 +++++++++---------- lib/classes/api/abstract.ApiCommand.php | 8 +++++--- lib/classes/api/commands/class.Customers.php | 2 +- lib/classes/api/commands/class.Domains.php | 2 +- .../api/commands/class.IpsAndPorts.php | 2 +- 5 files changed, 17 insertions(+), 16 deletions(-) diff --git a/admin_customers.php b/admin_customers.php index 7e0aae18..fc53871b 100644 --- a/admin_customers.php +++ b/admin_customers.php @@ -343,22 +343,21 @@ if ($page == 'customers' && $id != 0 ) { - $result_data = array('id' => $id); - $result_stmt = Database::prepare(" - SELECT * FROM `" . TABLE_PANEL_CUSTOMERS . "` - WHERE `customerid` = :id" . ($userinfo['customers_see_all'] ? '' : " AND `adminid` = :adminid") - ); - if ($userinfo['customers_see_all'] == '0') { - $result_data['adminid'] = $userinfo['adminid']; + try { + $json_result = Customers::getLocal($userinfo, array( + 'id' => $id + ))->get(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); } - $result = Database::pexecute_first($result_stmt, $result_data); + $result = json_decode($json_result, true)['data']; /* * information for moving customer */ $available_admins_stmt = Database::prepare(" - SELECT * FROM `" . TABLE_PANEL_ADMINS . "` - WHERE (`customers` = '-1' OR `customers` > `customers_used`)" + SELECT * FROM `" . TABLE_PANEL_ADMINS . "` + WHERE (`customers` = '-1' OR `customers` > `customers_used`)" ); Database::pexecute($available_admins_stmt); $admin_select = makeoption("-----", 0, true, true, true); diff --git a/lib/classes/api/abstract.ApiCommand.php b/lib/classes/api/abstract.ApiCommand.php index 5ad847b3..25fa60f0 100644 --- a/lib/classes/api/abstract.ApiCommand.php +++ b/lib/classes/api/abstract.ApiCommand.php @@ -29,16 +29,18 @@ abstract class ApiCommand throw new Exception("Invalid user data", 500); } $this->logger = FroxlorLogger::getInstanceOf($this->user_data); - if ($this->debug) { - $this->logger()->logAction(LOG_ERROR, LOG_DEBUG, json_encode($params, JSON_UNESCAPED_SLASHES)); - } $this->initLang(); $this->initMail(); + + if ($this->debug) { + $this->logger()->logAction(LOG_ERROR, LOG_DEBUG, "[API] ".get_called_class().": ".json_encode($params, JSON_UNESCAPED_SLASHES)); + } } private function initLang() { + global $lng; // query the whole table $result_stmt = Database::query("SELECT * FROM `" . TABLE_PANEL_LANGUAGE . "`"); diff --git a/lib/classes/api/commands/class.Customers.php b/lib/classes/api/commands/class.Customers.php index 84525d0c..eb1b1085 100644 --- a/lib/classes/api/commands/class.Customers.php +++ b/lib/classes/api/commands/class.Customers.php @@ -50,7 +50,7 @@ class Customers extends ApiCommand if ($result) { return $this->response(200, "successfull", $result); } - throw new Exception("Customer with id #" . $id . " could not be found"); + throw new Exception("Customer with id #" . $id . " could not be found", 404); } throw new Exception("Not allowed to execute given command.", 403); } diff --git a/lib/classes/api/commands/class.Domains.php b/lib/classes/api/commands/class.Domains.php index ed5db265..2d0eb4dd 100644 --- a/lib/classes/api/commands/class.Domains.php +++ b/lib/classes/api/commands/class.Domains.php @@ -54,7 +54,7 @@ class Domains extends ApiCommand if ($result) { return $this->response(200, "successfull", $result); } - throw new Exception("Domain with id #" . $id . " could not be found"); + throw new Exception("Domain with id #" . $id . " could not be found", 404); } throw new Exception("Not allowed to execute given command.", 403); } diff --git a/lib/classes/api/commands/class.IpsAndPorts.php b/lib/classes/api/commands/class.IpsAndPorts.php index 94d27115..f0b9d8e1 100644 --- a/lib/classes/api/commands/class.IpsAndPorts.php +++ b/lib/classes/api/commands/class.IpsAndPorts.php @@ -37,7 +37,7 @@ class IpsAndPorts extends ApiCommand if ($result) { return $this->response(200, "successfull", $result); } - throw new Exception("IP/port with id #" . $id . " could not be found"); + throw new Exception("IP/port with id #" . $id . " could not be found", 404); } throw new Exception("Not allowed to execute given command.", 403); } From 5afdbae83afd2b3e79a6ef364c8ec5f74685c92e Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Fri, 16 Feb 2018 15:50:31 +0100 Subject: [PATCH 0439/1335] minor phpDoc fixes in Logger classes Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/logger/abstract.AbstractLogger.php | 6 +++--- lib/classes/logger/class.FileLogger.php | 4 ++-- lib/classes/logger/class.FroxlorLogger.php | 4 ++-- lib/classes/logger/class.MysqlLogger.php | 2 +- lib/classes/logger/class.SysLogger.php | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/classes/logger/abstract.AbstractLogger.php b/lib/classes/logger/abstract.AbstractLogger.php index c3a1eeca..cfd01f70 100644 --- a/lib/classes/logger/abstract.AbstractLogger.php +++ b/lib/classes/logger/abstract.AbstractLogger.php @@ -37,19 +37,19 @@ abstract class AbstractLogger { /** * Enable/Disable Logging - * @var logenabled + * @var boolean */ private $logenabled = false; /** * Enable/Disable Cronjob-Logging - * @var logcronjob + * @var boolean */ private $logcronjob = false; /** * Loggin-Severity - * @var severity + * @var int */ private $severity = 1; diff --git a/lib/classes/logger/class.FileLogger.php b/lib/classes/logger/class.FileLogger.php index 47a784c7..5f48c5d7 100644 --- a/lib/classes/logger/class.FileLogger.php +++ b/lib/classes/logger/class.FileLogger.php @@ -30,13 +30,13 @@ class FileLogger extends AbstractLogger { /** * Logfile - * @var logfile + * @var string */ private $logfile = null; /** * Syslogger Objects Array - * @var loggers + * @var FileLogger[] */ static private $loggers = array(); diff --git a/lib/classes/logger/class.FroxlorLogger.php b/lib/classes/logger/class.FroxlorLogger.php index 467dd160..78fae694 100644 --- a/lib/classes/logger/class.FroxlorLogger.php +++ b/lib/classes/logger/class.FroxlorLogger.php @@ -30,13 +30,13 @@ class FroxlorLogger { /** * LogTypes Array - * @var logtypes + * @var array */ static private $logtypes = null; /** * Logger-Object-Array - * @var loggers + * @var FroxlorLogger[] */ static private $loggers = null; diff --git a/lib/classes/logger/class.MysqlLogger.php b/lib/classes/logger/class.MysqlLogger.php index b21ed392..0136b56f 100644 --- a/lib/classes/logger/class.MysqlLogger.php +++ b/lib/classes/logger/class.MysqlLogger.php @@ -30,7 +30,7 @@ class MysqlLogger extends AbstractLogger { /** * Syslogger Objects Array - * @var loggers + * @var MysqlLogger[] */ static private $loggers = array(); diff --git a/lib/classes/logger/class.SysLogger.php b/lib/classes/logger/class.SysLogger.php index f15396e2..42255484 100644 --- a/lib/classes/logger/class.SysLogger.php +++ b/lib/classes/logger/class.SysLogger.php @@ -30,7 +30,7 @@ class SysLogger extends AbstractLogger { /** * Syslogger Objects Array - * @var loggers + * @var SysLogger[] */ static private $loggers = array(); From fd287e7be483509220d1164df0c4a3780cde04a6 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sat, 17 Feb 2018 14:51:04 +0100 Subject: [PATCH 0440/1335] add apache restart command after enabling modules in config-templates Signed-off-by: Michael Kaufmann (d00p) --- install/scripts/config-services.php | 4 ++-- lib/configfiles/jessie.xml | 10 ++++++++++ lib/configfiles/precise.xml | 10 ++++++++++ lib/configfiles/stretch.xml | 10 ++++++++++ lib/configfiles/trusty.xml | 15 +++++++++++++++ lib/configfiles/wheezy.xml | 15 +++++++++++++++ 6 files changed, 62 insertions(+), 2 deletions(-) diff --git a/install/scripts/config-services.php b/install/scripts/config-services.php index 1236fbf5..69d1b654 100755 --- a/install/scripts/config-services.php +++ b/install/scripts/config-services.php @@ -316,7 +316,7 @@ class Action case "file": if (array_key_exists('content', $action)) { CmdLineHandler::printwarn("Creating file '" . $action['name'] . "'"); - file_put_contents($action['name'], strtr($action['content'], $replace_arr)); + file_put_contents($action['name'], trim(strtr($action['content'], $replace_arr))); } elseif (array_key_exists('subcommands', $action)) { foreach ($action['subcommands'] as $fileaction) { if (array_key_exists('execute', $fileaction) && $fileaction['execute'] == "pre") { @@ -325,7 +325,7 @@ class Action exec(strtr($fileaction['content'], $replace_arr)); } elseif ($fileaction['type'] == 'file') { CmdLineHandler::printwarn("Creating file '" . $fileaction['name'] . "'"); - file_put_contents($fileaction['name'], strtr($fileaction['content'], $replace_arr)); + file_put_contents($fileaction['name'], trim(strtr($fileaction['content'], $replace_arr))); } } } diff --git a/lib/configfiles/jessie.xml b/lib/configfiles/jessie.xml index 10ee0a2f..062cdb9b 100644 --- a/lib/configfiles/jessie.xml +++ b/lib/configfiles/jessie.xml @@ -48,6 +48,11 @@ //service[@type='http']/general/commands + + {{settings.system.use_ssl}} + + + {{settings.phpfpm.enabled}} @@ -4753,6 +4758,11 @@ aliases: files + + {{settings.system.webserver}} + + + diff --git a/lib/configfiles/precise.xml b/lib/configfiles/precise.xml index 806d86e6..7017ec3c 100644 --- a/lib/configfiles/precise.xml +++ b/lib/configfiles/precise.xml @@ -49,6 +49,11 @@ //service[@type='http']/general/commands + + {{settings.system.use_ssl}} + + + {{settings.phpfpm.enabled}} @@ -1752,6 +1757,11 @@ aliases: files + + {{settings.system.webserver}} + + + diff --git a/lib/configfiles/stretch.xml b/lib/configfiles/stretch.xml index 7d9eab6b..df0a1cd6 100644 --- a/lib/configfiles/stretch.xml +++ b/lib/configfiles/stretch.xml @@ -48,6 +48,11 @@ //service[@type='http']/general/commands + + {{settings.system.use_ssl}} + + + {{settings.phpfpm.enabled}} @@ -4647,6 +4652,11 @@ aliases: files + + {{settings.system.webserver}} + + + diff --git a/lib/configfiles/trusty.xml b/lib/configfiles/trusty.xml index 0dc4c61f..07788901 100644 --- a/lib/configfiles/trusty.xml +++ b/lib/configfiles/trusty.xml @@ -49,6 +49,11 @@ //service[@type='http']/general/commands + + {{settings.system.use_ssl}} + + + {{settings.phpfpm.enabled}} @@ -85,6 +90,11 @@ Alias "/.well-known/acme-challenge" "{{settings.system.letsencryptchallengepath} //service[@type='http']/general/commands + + {{settings.system.use_ssl}} + + + {{settings.phpfpm.enabled}} @@ -1761,6 +1771,11 @@ aliases: files + + {{settings.system.webserver}} + + + diff --git a/lib/configfiles/wheezy.xml b/lib/configfiles/wheezy.xml index 55ba59d4..17ed9ed9 100644 --- a/lib/configfiles/wheezy.xml +++ b/lib/configfiles/wheezy.xml @@ -49,6 +49,11 @@ //service[@type='http']/general/commands + + {{settings.system.use_ssl}} + + + {{settings.phpfpm.enabled}} @@ -85,6 +90,11 @@ Alias "/.well-known/acme-challenge" "{{settings.system.letsencryptchallengepath} //service[@type='http']/general/commands + + {{settings.system.use_ssl}} + + + {{settings.phpfpm.enabled}} @@ -5561,6 +5571,11 @@ aliases: files + + {{settings.system.webserver}} + + + From 9619abdad7d3ae2edefd1cab199081cd14194d3f Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sat, 17 Feb 2018 16:00:50 +0100 Subject: [PATCH 0441/1335] when importing settings with enabled ssl flag, validate that the target system has ssl enabled ip's to avoid unexpected behaviour, tthx v3ng for testing Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/settings/class.SImExporter.php | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/lib/classes/settings/class.SImExporter.php b/lib/classes/settings/class.SImExporter.php index 428d8beb..10e8aa32 100644 --- a/lib/classes/settings/class.SImExporter.php +++ b/lib/classes/settings/class.SImExporter.php @@ -98,14 +98,28 @@ class SImExporter // when there were changes in the variable-name or similar unset($_data['panel.version']); unset($_data['panel.db_version']); - + // validate we got ssl enabled ips when ssl is enabled + // otherwise deactivate it + if ($_data['system.use_ssl'] == 1) { + $result_ssl_ipsandports_stmt = Database::prepare(" + SELECT COUNT(*) as count_ssl_ip FROM `" . TABLE_PANEL_IPSANDPORTS . "` WHERE `ssl`='1' + "); + $result = Database::pexecute_first($result_ssl_ipsandports_stmt); + if ($result['count_ssl_ip'] <= 0) { + // no ssl-ip -> deactivate + $_data['system.use_ssl'] = 0; + // deactivate other ssl-related settings + $_data['system.leenabled'] = 0; + $_data['system.le_froxlor_enabled'] = 0; + $_data['system.le_froxlor_redirect'] = 0; + } + } // store new data foreach ($_data as $index => $value) { Settings::Set($index, $value); } // save to DB Settings::Flush(); - // all good return true; } From 532551263d65c8e9f483c60745d544df0297fce7 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sun, 18 Feb 2018 10:19:17 +0100 Subject: [PATCH 0442/1335] add new api-module to output list of possible modules/functions Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/abstract.ApiCommand.php | 10 - lib/classes/api/commands/class.Customers.php | 31 ++- lib/classes/api/commands/class.Domains.php | 24 ++- lib/classes/api/commands/class.Froxlor.php | 182 ++++++++++++++++++ .../api/commands/class.IpsAndPorts.php | 23 ++- lib/classes/api/interface.ResourceEntity.php | 15 ++ 6 files changed, 272 insertions(+), 13 deletions(-) create mode 100644 lib/classes/api/commands/class.Froxlor.php create mode 100644 lib/classes/api/interface.ResourceEntity.php diff --git a/lib/classes/api/abstract.ApiCommand.php b/lib/classes/api/abstract.ApiCommand.php index 25fa60f0..ec473fbb 100644 --- a/lib/classes/api/abstract.ApiCommand.php +++ b/lib/classes/api/abstract.ApiCommand.php @@ -213,16 +213,6 @@ abstract class ApiCommand return $json_response; } - public abstract function list(); - - public abstract function get(); - - public abstract function add(); - - public abstract function update(); - - public abstract function delete(); - private function readUserData($header = null) { $sel_stmt = Database::prepare("SELECT * FROM `api_keys` WHERE `apikey` = :ak AND `secret` = :as"); diff --git a/lib/classes/api/commands/class.Customers.php b/lib/classes/api/commands/class.Customers.php index eb1b1085..2065a1d7 100644 --- a/lib/classes/api/commands/class.Customers.php +++ b/lib/classes/api/commands/class.Customers.php @@ -1,8 +1,13 @@ isAdmin()) { @@ -32,6 +37,14 @@ class Customers extends ApiCommand throw new Exception("Not allowed to execute given command.", 403); } + /** + * return a customer entry by id + * + * @param int $id customer-id + * + * @throws Exception + * @return array + */ public function get() { if ($this->isAdmin()) { @@ -655,6 +668,14 @@ class Customers extends ApiCommand throw new Exception("Not allowed to execute given command.", 403); } + /** + * delete a customer entry by id + * + * @param int $id customer-id + * + * @throws Exception + * @return array + */ public function delete() { if ($this->isAdmin()) { @@ -880,6 +901,14 @@ class Customers extends ApiCommand throw new Exception("Not allowed to execute given command.", 403); } + /** + * unlock a locked customer by id + * + * @param int $id customer-id + * + * @throws Exception + * @return array + */ public function unlock() { if ($this->isAdmin()) { diff --git a/lib/classes/api/commands/class.Domains.php b/lib/classes/api/commands/class.Domains.php index 2d0eb4dd..d4f88f84 100644 --- a/lib/classes/api/commands/class.Domains.php +++ b/lib/classes/api/commands/class.Domains.php @@ -1,8 +1,13 @@ isAdmin()) { @@ -32,6 +37,15 @@ class Domains extends ApiCommand throw new Exception("Not allowed to execute given command.", 403); } + /** + * return a domain entry by id + * + * @param int $id domain-id + * @param boolean $no_std_subdomain optional, default false + * + * @throws Exception + * @return array + */ public function get() { if ($this->isAdmin()) { @@ -1503,6 +1517,14 @@ class Domains extends ApiCommand throw new Exception("Not allowed to execute given command.", 403); } + /** + * delete a domain entry by id + * + * @param int $id domain-id + * + * @throws Exception + * @return array + */ public function delete() { if ($this->isAdmin()) { diff --git a/lib/classes/api/commands/class.Froxlor.php b/lib/classes/api/commands/class.Froxlor.php new file mode 100644 index 00000000..01c316e5 --- /dev/null +++ b/lib/classes/api/commands/class.Froxlor.php @@ -0,0 +1,182 @@ +getParam('module'); + + $functions = array(); + if ($module != null) { + // check existence + $this->requireModules($module); + // now get all static functions + $reflection = new \ReflectionClass($module); + $_functions = $reflection->getMethods(\ReflectionMethod::IS_PUBLIC); + foreach ($_functions as $func) { + if ($func->class == $module && $func->isPublic()) { + array_push($functions, array_merge(array( + 'module' => $module, + 'function' => $func->name + ), $this->_getParamListFromDoc($module, $func->name))); + } + } + } else { + // check all the modules + $path = FROXLOR_INSTALL_DIR . '/lib/classes/api/commands/'; + // valid directory? + if (is_dir($path)) { + // create RecursiveIteratorIterator + $its = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($path)); + // check every file + foreach ($its as $fullFileName => $it) { + // does it match the Filename pattern? + $matches = array(); + if (preg_match("/^class\.(.+)\.php$/i", $it->getFilename(), $matches)) { + // check for existence + try { + // set the module to be in our namespace + $mod = $matches[1]; + $this->requireModules($mod); + } catch (Exception $e) { + // @todo log? + continue; + } + // now get all static functions + $reflection = new \ReflectionClass($mod); + $_functions = $reflection->getMethods(\ReflectionMethod::IS_PUBLIC); + foreach ($_functions as $func) { + if ($func->class == $mod && $func->isPublic()) { + array_push($functions, array_merge(array( + 'module' => $matches[1], + 'function' => $func->name + ), $this->_getParamListFromDoc($matches[1], $func->name))); + } + } + } + } + } else { + // yikes - no valid directory to check + throw new Exception("Cannot search directory '" . $path . "'. No such directory.", 500); + } + } + + // return the list + return $this->response(200, "successfull", $functions); + } + + /** + * generate an api-response to list all parameters and the return-value of + * a given module.function-combination + * + * @param string $module + * @param string $function + * + * @throws Exception + * @return array|bool + */ + private function _getParamListFromDoc($module = null, $function = null) + { + try { + // set the module to be in our namespace + $cls = new \ReflectionMethod($module, $function); + $comment = $cls->getDocComment(); + if ($comment == false) { + return array( + 'head' => 'There is no comment-block for "' . $module . '.' . $function . '"' + ); + } + $clines = explode("\n", $comment); + $result = array(); + $result['params'] = array(); + foreach ($clines as $c) { + $c = trim($c); + // check param-section + if (strpos($c, '@param')) { + preg_match('/^\*\s\@param\s(.+)\s(\$\w+)(\s.*)?/', $c, $r); + // cut $ off the parameter-name as it is not wanted in the api-request + $result['params'][] = array( + 'parameter' => substr($r[2], 1), + 'type' => $r[1], + 'desc' => (isset($r[3]) ? trim($r['3']) : '') + ); + } // check return-section + elseif (strpos($c, '@return')) { + preg_match('/^\*\s\@return\s(\w+)(\s.*)?/', $c, $r); + if (! isset($r[0]) || empty($r[0])) { + $r[1] = 'null'; + $r[2] = 'This function has no return value given'; + } + $result['return'] = array( + 'type' => $r[1], + 'desc' => (isset($r[2]) ? trim($r[2]) : '') + ); + } else if (! empty($c) && strpos($c, '@throws') === false) { + if (substr($c, 0, 3) == "/**") + continue; + if (substr($c, 0, 2) == "*/") + continue; + if (substr($c, 0, 1) == "*") + $c = trim(substr($c, 1)); + if (empty($c)) + continue; + if (! isset($result['head']) || empty($result['head'])) { + $result['head'] = $c . " "; + } else { + $result['head'] .= $c . " "; + } + } + } + return $result; + } catch (\ReflectionException $e) { + return array(); + } + } + + /** + * this functions is used to check the availability + * of a given list of modules. + * If either one of + * them are not found, throw an Exception + * + * @param string|array $modules + * + * @throws Exception + */ + private function requireModules($modules = null) + { + if ($modules != null) { + // no array -> create one + if (! is_array($modules)) { + $modules = array( + $modules + ); + } + // check all the modules + foreach ($modules as $module) { + try { + // can we use the class? + if (class_exists($module)) { + continue; + } else { + throw new Exception('The required class "' . $module . '" could not be found but the module-file exists', 404); + } + } catch (Exception $e) { + // The autoloader will throw an Exception + // that the required class could not be found + // but we want a nicer error-message for this here + throw new Exception('The required module "' . $module . '" could not be found', 404); + } + } + } + } +} diff --git a/lib/classes/api/commands/class.IpsAndPorts.php b/lib/classes/api/commands/class.IpsAndPorts.php index f0b9d8e1..9baa0a64 100644 --- a/lib/classes/api/commands/class.IpsAndPorts.php +++ b/lib/classes/api/commands/class.IpsAndPorts.php @@ -1,8 +1,13 @@ isAdmin() && $this->getUserDetail('change_serversettings')) { @@ -23,6 +28,14 @@ class IpsAndPorts extends ApiCommand throw new Exception("Not allowed to execute given command.", 403); } + /** + * return an ip/port entry by id + * + * @param int $id ip-port-id + * + * @throws Exception + * @return array + */ public function get() { if ($this->isAdmin() && $this->getUserDetail('change_serversettings')) { @@ -314,6 +327,14 @@ class IpsAndPorts extends ApiCommand throw new Exception("Not allowed to execute given command.", 403); } + /** + * delete an ip/port entry by id + * + * @param int $id ip-port-id + * + * @throws Exception + * @return array + */ public function delete() { if ($this->isAdmin() && $this->getUserDetail('change_serversettings')) { diff --git a/lib/classes/api/interface.ResourceEntity.php b/lib/classes/api/interface.ResourceEntity.php new file mode 100644 index 00000000..6361eb46 --- /dev/null +++ b/lib/classes/api/interface.ResourceEntity.php @@ -0,0 +1,15 @@ + Date: Mon, 19 Feb 2018 08:59:24 +0100 Subject: [PATCH 0443/1335] enhance ApiCommand::getParam() to specify required and optional parameter Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/abstract.ApiCommand.php | 67 +++- lib/classes/api/commands/class.Customers.php | 95 +++--- lib/classes/api/commands/class.Domains.php | 289 +++++++++--------- .../api/commands/class.IpsAndPorts.php | 55 ++-- 4 files changed, 283 insertions(+), 223 deletions(-) diff --git a/lib/classes/api/abstract.ApiCommand.php b/lib/classes/api/abstract.ApiCommand.php index ec473fbb..d45808be 100644 --- a/lib/classes/api/abstract.ApiCommand.php +++ b/lib/classes/api/abstract.ApiCommand.php @@ -32,9 +32,9 @@ abstract class ApiCommand $this->initLang(); $this->initMail(); - + if ($this->debug) { - $this->logger()->logAction(LOG_ERROR, LOG_DEBUG, "[API] ".get_called_class().": ".json_encode($params, JSON_UNESCAPED_SLASHES)); + $this->logger()->logAction(LOG_ERROR, LOG_DEBUG, "[API] " . get_called_class() . ": " . json_encode($params, JSON_UNESCAPED_SLASHES)); } } @@ -146,21 +146,72 @@ abstract class ApiCommand } /** - * receive field from parameter-list + * get specific parameter from the parameterlist; + * check for existence and != empty if needed. + * Maybe more in the future * * @param string $param + * parameter to get out of the request-parameter list + * @param bool $optional + * default: false * @param mixed $default - * set if param is not found + * value which is returned if optional=true and param is not set * * @throws Exception * @return mixed */ - protected function getParam($param = null, $default = null) + protected function getParam($param = null, $optional = false, $default = '') { - if (isset($this->cmd_params[$param])) { - return $this->cmd_params[$param]; + // does it exist? + if (! isset($this->cmd_params[$param])) { + if ($optional === false) { + // get module + function for better error-messages + $inmod = $this->getModFunctionString(); + throw new Exception('Requested parameter "' . $param . '" could not be found for "' . $inmod . '"', 404); + } + return $default; + } + // is it empty? - test really on string, as value 0 is being seen as empty by php + if ($this->cmd_params[$param] === "") { + if ($optional === false) { + // get module + function for better error-messages + $inmod = $this->getModFunctionString(); + throw new Exception('Requested parameter "' . $param . '" is empty where it should not be for "' . $inmod . '"', 406); + } + return ''; + } + // everything else is fine + return $this->cmd_params[$param]; + } + + /** + * returns "module::function()" for better error-messages (missing parameter etc.) + * makes debugging a whole lot more comfortable + * + * @return string + */ + private function getModFunctionString() + { + $_c = get_called_class(); + + $level = 2; + if (version_compare(PHP_VERSION, "5.4.0", "<")) { + $t = debug_backtrace(); + } else { + $t = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); + } + while (true) { + $c = $t[$level]['class']; + $f = $t[$level]['function']; + if ($c != get_called_class()) { + $level ++; + if ($level > 5) { + break; + } + continue; + } + return $c . ':' . $f; } - return $default; } /** diff --git a/lib/classes/api/commands/class.Customers.php b/lib/classes/api/commands/class.Customers.php index 2065a1d7..820703eb 100644 --- a/lib/classes/api/commands/class.Customers.php +++ b/lib/classes/api/commands/class.Customers.php @@ -76,89 +76,90 @@ class Customers extends ApiCommand implements ResourceEntity if ($this->getUserDetail('customers_used') < $this->getUserDetail('customers') || $this->getUserDetail('customers') == '-1') { $idna_convert = new idna_convert_wrapper(); - $name = validate($this->getParam('name'), 'name', '', '', array(), true); - $firstname = validate($this->getParam('firstname'), 'first name', '', '', array(), true); - $company = validate($this->getParam('company'), 'company', '', '', array(), true); - $street = validate($this->getParam('street'), 'street', '', '', array(), true); - $zipcode = validate($this->getParam('zipcode'), 'zipcode', '/^[0-9 \-A-Z]*$/', '', array(), true); - $city = validate($this->getParam('city'), 'city', '', '', array(), true); - $phone = validate($this->getParam('phone'), 'phone', '/^[0-9\- \+\(\)\/]*$/', '', array(), true); - $fax = validate($this->getParam('fax'), 'fax', '/^[0-9\- \+\(\)\/]*$/', '', array(), true); + $name = validate($this->getParam('name', true, ''), 'name', '', '', array(), true); + $firstname = validate($this->getParam('firstname', true, ''), 'first name', '', '', array(), true); + $company_required = (empty($name) && empty($first)); + $company = validate($this->getParam('company', $company_required, ''), 'company', '', '', array(), true); + $street = validate($this->getParam('street', true, ''), 'street', '', '', array(), true); + $zipcode = validate($this->getParam('zipcode', true, ''), 'zipcode', '/^[0-9 \-A-Z]*$/', '', array(), true); + $city = validate($this->getParam('city', true, ''), 'city', '', '', array(), true); + $phone = validate($this->getParam('phone', true, ''), 'phone', '/^[0-9\- \+\(\)\/]*$/', '', array(), true); + $fax = validate($this->getParam('fax', true, ''), 'fax', '/^[0-9\- \+\(\)\/]*$/', '', array(), true); $email = $idna_convert->encode(validate($this->getParam('email'), 'email', '', '', array(), true)); - $customernumber = validate($this->getParam('customernumber'), 'customer number', '/^[A-Za-z0-9 \-]*$/Di', '', array(), true); - $def_language = validate($this->getParam('def_language'), 'default language', '', '', array(), true); - $gender = intval_ressource($this->getParam('gender', 0)); + $customernumber = validate($this->getParam('customernumber', true, ''), 'customer number', '/^[A-Za-z0-9 \-]*$/Di', '', array(), true); + $def_language = validate($this->getParam('def_language', true, ''), 'default language', '', '', array(), true); + $gender = intval_ressource($this->getParam('gender', true, 0)); - $custom_notes = validate(str_replace("\r\n", "\n", $this->getParam('custom_notes', '')), 'custom_notes', '/^[^\0]*$/', '', array(), true); - $custom_notes_show = $this->getParam('custom_notes_show', 0); + $custom_notes = validate(str_replace("\r\n", "\n", $this->getParam('custom_notes', true, '')), 'custom_notes', '/^[^\0]*$/', '', array(), true); + $custom_notes_show = $this->getParam('custom_notes_show', true, 0); - $diskspace = intval_ressource($this->getParam('diskspace', 0)); - if ($this->getParam('diskspace_ul', 0) == -1) { + $diskspace = intval_ressource($this->getParam('diskspace', true, 0)); + if ($this->getParam('diskspace_ul', true, 0) == -1) { $diskspace = - 1; } - $traffic = doubleval_ressource($this->getParam('traffic', 0)); - if ($this->getParam('traffic_ul', 0) == -1) { + $traffic = doubleval_ressource($this->getParam('traffic', true, 0)); + if ($this->getParam('traffic_ul', true, 0) == -1) { $traffic = - 1; } - $subdomains = intval_ressource($this->getParam('subdomains', 0)); - if ($this->getParam('subdomains_ul', 0) == -1) { + $subdomains = intval_ressource($this->getParam('subdomains', true, 0)); + if ($this->getParam('subdomains_ul', true, 0) == -1) { $subdomains = - 1; } - $emails = intval_ressource($this->getParam('emails', 0)); - if ($this->getParam('emails_ul', 0) == -1) { + $emails = intval_ressource($this->getParam('emails', true, 0)); + if ($this->getParam('emails_ul', true, 0) == -1) { $emails = - 1; } - $email_accounts = intval_ressource($this->getParam('email_accounts', 0)); - if ($this->getParam('email_accounts_ul', 0) == -1) { + $email_accounts = intval_ressource($this->getParam('email_accounts', true, 0)); + if ($this->getParam('email_accounts_ul', true, 0) == -1) { $email_accounts = - 1; } - $email_forwarders = intval_ressource($this->getParam('email_forwarders', 0)); - if ($this->getParam('email_forwarders_ul', 0) == -1) { + $email_forwarders = intval_ressource($this->getParam('email_forwarders', true, 0)); + if ($this->getParam('email_forwarders_ul', true, 0) == -1) { $email_forwarders = - 1; } if (Settings::Get('system.mail_quota_enabled') == '1') { - $email_quota = validate($this->getParam('email_quota', 0), 'email_quota', '/^\d+$/', 'vmailquotawrong', array( + $email_quota = validate($this->getParam('email_quota', true, 0), 'email_quota', '/^\d+$/', 'vmailquotawrong', array( '0', '' ), true); - if ($this->getParam('email_quota_ul', 0) == -1) { + if ($this->getParam('email_quota_ul', true, 0) == -1) { $email_quota = - 1; } } else { $email_quota = - 1; } - $email_imap = $this->getParam('email_imap', 0); - $email_pop3 = $this->getParam('email_pop3', 0); + $email_imap = $this->getParam('email_imap', true, 0); + $email_pop3 = $this->getParam('email_pop3', true, 0); - $ftps = intval_ressource($this->getParam('ftps', 0)); - if ($this->getParam('ftps_ul', 0) == -1) { + $ftps = intval_ressource($this->getParam('ftps', true, 0)); + if ($this->getParam('ftps_ul', true, 0) == -1) { $ftps = - 1; } if (Settings::Get('ticket.enabled') == '1') { - $tickets = intval_ressource($this->getParam('tickets', 0)); - if ($this->getParam('tickets_ul', 0) == -1) { + $tickets = intval_ressource($this->getParam('tickets', true, 0)); + if ($this->getParam('tickets_ul', true, 0) == -1) { $tickets = - 1; } } else { $tickets = - 1; } - $mysqls = intval_ressource($this->getParam('mysqls', 0)); - if ($this->getParam('mysqls_ul', 0) == -1) { + $mysqls = intval_ressource($this->getParam('mysqls', true, 0)); + if ($this->getParam('mysqls_ul', true, 0) == -1) { $mysqls = - 1; } - $createstdsubdomain = $this->getParam('createstdsubdomain', 0); + $createstdsubdomain = $this->getParam('createstdsubdomain', true, 0); - $password = validate($this->getParam('new_customer_password', ''), 'password', '', '', array(), true); + $password = validate($this->getParam('new_customer_password', true, ''), 'password', '', '', array(), true); // only check if not empty, // cause empty == generate password automatically if ($password != '') { @@ -170,20 +171,20 @@ class Customers extends ApiCommand implements ResourceEntity $gender = 0; } - $sendpassword = $this->getParam('sendpassword', 0); - $phpenabled = $this->getParam('phpenabled', 0); + $sendpassword = $this->getParam('sendpassword', true, 0); + $phpenabled = $this->getParam('phpenabled', true, 0); $allowed_phpconfigs = array(); - if (! empty($this->getParam('allowed_phpconfigs', array())) && is_array($this->getParam('allowed_phpconfigs'))) { - foreach ($this->getParam('allowed_phpconfigs') as $allowed_phpconfig) { + if (! empty($this->getParam('allowed_phpconfigs', true, array())) && is_array($this->getParam('allowed_phpconfigs', true, array()))) { + foreach ($this->getParam('allowed_phpconfigs', true, array()) as $allowed_phpconfig) { $allowed_phpconfig = intval($allowed_phpconfig); $allowed_phpconfigs[] = $allowed_phpconfig; } } - $perlenabled = $this->getParam('perlenabled', 0); - $dnsenabled = $this->getParam('dnsenabled', 0); - $store_defaultindex = $this->getParam('store_defaultindex', 0); + $perlenabled = $this->getParam('perlenabled', true, 0); + $dnsenabled = $this->getParam('dnsenabled', true, 0); + $store_defaultindex = $this->getParam('store_defaultindex', true, 0); $diskspace = $diskspace * 1024; $traffic = $traffic * 1024 * 1024; @@ -212,7 +213,7 @@ class Customers extends ApiCommand implements ResourceEntity standard_error('emailiswrong', $email, true); } else { - if ($this->getParam('new_loginname', '') != '') { + if ($this->getParam('new_loginname', true, '') != '') { $accountnumber = intval(Settings::Get('system.lastaccountnumber')); $loginname = validate($this->getParam('new_loginname'), 'loginname', '/^[a-z][a-z0-9\-_]+$/i', '', array(), true); @@ -672,6 +673,7 @@ class Customers extends ApiCommand implements ResourceEntity * delete a customer entry by id * * @param int $id customer-id + * @param bool $delete_userfiles optional, default false * * @throws Exception * @return array @@ -680,6 +682,7 @@ class Customers extends ApiCommand implements ResourceEntity { if ($this->isAdmin()) { $id = $this->getParam('id'); + $delete_userfiles = $this->getParam('delete_userfiles', true, 0); $json_result = Customers::getLocal($this->getUserData(), array( 'id' => $id @@ -872,7 +875,7 @@ class Customers extends ApiCommand implements ResourceEntity // Using nameserver, insert a task which rebuilds the server config inserttask('4'); - if ($this->getParam('delete_userfiles', 0) == 1) { + if ($delete_userfiles == 1) { // insert task to remove the customers files from the filesystem inserttask('6', $result['loginname']); } diff --git a/lib/classes/api/commands/class.Domains.php b/lib/classes/api/commands/class.Domains.php index d4f88f84..481a8ba2 100644 --- a/lib/classes/api/commands/class.Domains.php +++ b/lib/classes/api/commands/class.Domains.php @@ -40,9 +40,11 @@ class Domains extends ApiCommand implements ResourceEntity /** * return a domain entry by id * - * @param int $id domain-id - * @param boolean $no_std_subdomain optional, default false - * + * @param int $id + * domain-id + * @param boolean $no_std_subdomain + * optional, default false + * * @throws Exception * @return array */ @@ -50,7 +52,7 @@ class Domains extends ApiCommand implements ResourceEntity { if ($this->isAdmin()) { $id = $this->getParam('id'); - $no_std_subdomain = $this->getParam('no_std_subdomain', false); + $no_std_subdomain = $this->getParam('no_std_subdomain', true, false); $this->logger()->logAction(ADM_ACTION, LOG_NOTICE, "[API] get domain #" . $id); $result_stmt = Database::prepare(" SELECT `d`.*, `c`.`customerid` @@ -78,11 +80,50 @@ class Domains extends ApiCommand implements ResourceEntity if ($this->isAdmin() && $this->getUserDetail('change_serversettings')) { if ($this->getUserDetail('domains_used') < $this->getUserDetail('domains') || $this->getUserDetail('domains') == '-1') { - if ($this->getParam('domain') == Settings::Get('system.hostname')) { + // parameters + $p_domain = $this->getParam('domain'); + $customerid = intval($this->getParam('customerid')); + $p_ipandports = $this->getParam('ipandport'); + + // optional parameters + $adminid = intval($this->getParam('adminid', true, $this->getUserDetail('adminid'))); + $subcanemaildomain = $this->getParam('subcanemaildomain', true, 0); + $isemaildomain = $this->getParam('isemaildomain', true, 0); + $email_only = $this->getParam('email_only', true, 0); + $serveraliasoption = $this->getParam('selectserveralias', true, 0); + $speciallogfile = $this->getParam('speciallogfile', true, 0); + $aliasdomain = intval($this->getParam('alias', true, 0)); + $issubof = intval($this->getParam('issubof', true, 0)); + $registration_date = trim($this->getParam('registration_date', true, '')); + $termination_date = trim($this->getParam('termination_date', true, '')); + $caneditdomain = $this->getParam('caneditdomain', true, 0); + $isbinddomain = $this->getParam('isbinddomain', true, 0); + $zonefile = $this->getParam('zonefile', true, ''); + $dkim = intval($this->getParam('dkim', true, 0)); + $specialsettings = $this->getParam('specialsettings', true, ''); + $notryfiles = $this->getParam('notryfiles', true, 0); + $documentroot = $this->getParam('documentroot', true, ''); + $phpenabled = $this->getParam('phpenabled', true, 0); + $openbasedir = $this->getParam('openbasedir', true, 0); + $phpsettingid = $this->getParam('phpsettingid', true, 1); + $mod_fcgid_starter = $this->getParam('mod_fcgid_starter', true, - 1); + $mod_fcgid_maxrequests = $this->getParam('mod_fcgid_maxrequests', true, - 1); + $ssl_redirect = $this->getParam('ssl_redirect', true, 0); + $letsencrypt = $this->getParam('letsencrypt', true, 0); + $p_ssl_ipandports = $this->getParam('ssl_ipandport', true, array()); + $http2 = $this->getParam('http2', true, 0); + $hsts_maxage = $this->getParam('hsts_maxage', true, 0); + $hsts_sub = $this->getParam('hsts_sub', true, 0); + $hsts_preload = $this->getParam('hsts_preload', true, 0); + $ocsp_stapling = $this->getParam('ocsp_stapling', true, 0); + + // validation + + if ($p_domain == Settings::Get('system.hostname')) { standard_error('admin_domain_emailsystemhostname', '', true); } - if (substr($this->getParam('domain'), 0, 4) == 'xn--') { + if (substr($p_domain, 0, 4) == 'xn--') { standard_error('domain_nopunycode', '', true); } @@ -90,7 +131,7 @@ class Domains extends ApiCommand implements ResourceEntity $domain = $idna_convert->encode(preg_replace(array( '/\:(\d)+$/', '/^https?\:\/\//' - ), '', validate($this->getParam('domain'), 'domain'))); + ), '', validate($p_domain, 'domain'))); // Check whether domain validation is enabled and if, validate the domain if (Settings::Get('system.validate_domain') && ! validateDomain($domain)) { @@ -100,15 +141,6 @@ class Domains extends ApiCommand implements ResourceEntity ), '', true); } - $subcanemaildomain = $this->getParam('subcanemaildomain', 0); - $isemaildomain = $this->getParam('isemaildomain', 0); - $email_only = $this->getParam('email_only', 0); - $serveraliasoption = $this->getParam('selectserveralias', 0); - $speciallogfile = $this->getParam('speciallogfile', 0); - - $aliasdomain = intval($this->getParam('alias')); - $issubof = intval($this->getParam('issubof')); - $customerid = intval($this->getParam('customerid')); $customer_stmt = Database::prepare(" SELECT * FROM `" . TABLE_PANEL_CUSTOMERS . "` WHERE `customerid` = :customerid " . ($this->getUserDetail('customers_see_all') ? '' : " AND `adminid` = :adminid")); @@ -125,8 +157,6 @@ class Domains extends ApiCommand implements ResourceEntity } if ($this->getUserDetail('customers_see_all') == '1') { - - $adminid = intval($this->getParam('adminid')); $admin_stmt = Database::prepare(" SELECT * FROM `" . TABLE_PANEL_ADMINS . "` WHERE `adminid` = :adminid AND (`domains_used` < `domains` OR `domains` = '-1')"); @@ -150,7 +180,6 @@ class Domains extends ApiCommand implements ResourceEntity } $documentroot = makeCorrectDir($customer['documentroot'] . $path_suffix); - $registration_date = trim($this->getParam('registration_date', '')); $registration_date = validate($registration_date, 'registration_date', '/^(19|20)\d\d[-](0[1-9]|1[012])[-](0[1-9]|[12][0-9]|3[01])$/', '', array( '0000-00-00', '0', @@ -160,7 +189,6 @@ class Domains extends ApiCommand implements ResourceEntity $registration_date = null; } - $termination_date = trim($this->getParam('termination_date', '')); $termination_date = validate($termination_date, 'termination_date', '/^(19|20)\d\d[-](0[1-9]|1[012])[-](0[1-9]|[12][0-9]|3[01])$/', '', array( '0000-00-00', '0', @@ -171,31 +199,23 @@ class Domains extends ApiCommand implements ResourceEntity } if ($this->getUserDetail('change_serversettings') == '1') { - - $caneditdomain = $this->getParam('caneditdomain', 0); - - $isbinddomain = '0'; - $zonefile = ''; if (Settings::Get('system.bind_enable') == '1') { - $isbinddomain = $this->getParam('isbinddomain', 0); - $zonefile = validate($this->getParam('zonefile', ''), 'zonefile', '', '', array(), true); + $zonefile = validate($zonefile, 'zonefile', '', '', array(), true); + } else { + $isbinddomain = 0; + $zonefile = ''; } - $dkim = intval($this->getParam('dkim', 0)); - - $specialsettings = validate(str_replace("\r\n", "\n", $this->getParam('specialsettings', '')), 'specialsettings', '/^[^\0]*$/', '', array(), true); - $notryfiles = $this->getParam('notryfiles', 0); - validate($this->getParam('documentroot', ''), 'documentroot', '', '', array(), true); + $specialsettings = validate(str_replace("\r\n", "\n", $specialsettings), 'specialsettings', '/^[^\0]*$/', '', array(), true); + validate($documentroot, 'documentroot', '', '', array(), true); // If path is empty and 'Use domain name as default value for DocumentRoot path' is enabled in settings, // set default path to subdomain or domain name - if ($this->getParam('documentroot', '') != '') { - if (substr($this->getParam('documentroot'), 0, 1) != '/' && ! preg_match('/^https?\:\/\//', $this->getParam('documentroot'))) { - $documentroot .= '/' . $this->getParam('documentroot'); - } else { - $documentroot = $this->getParam('documentroot'); + if ($documentroot != '') { + if (substr($documentroot, 0, 1) != '/' && ! preg_match('/^https?\:\/\//', $documentroot)) { + $documentroot .= '/' . $documentroot; } - } elseif ($this->getParam('documentroot', '') == '' && Settings::Get('system.documentroot_use_default_value') == 1) { + } elseif ($documentroot == '' && Settings::Get('system.documentroot_use_default_value') == 1) { $documentroot = makeCorrectDir($customer['documentroot'] . '/' . $domain); } } else { @@ -212,11 +232,7 @@ class Domains extends ApiCommand implements ResourceEntity if ($this->getUserDetail('caneditphpsettings') == '1' || $this->getUserDetail('change_serversettings') == '1') { - $phpenabled = $this->getParam('phpenabled', 0); - $openbasedir = $this->getParam('openbasedir', 0); - if ((int) Settings::Get('system.mod_fcgid') == 1 || (int) Settings::Get('phpfpm.enabled') == 1) { - $phpsettingid = $this->getParam('phpsettingid', 1); $phpsettingid_check_stmt = Database::prepare(" SELECT * FROM `" . TABLE_PANEL_PHPCONFIGS . "` WHERE `id` = :phpsettingid"); @@ -229,11 +245,11 @@ class Domains extends ApiCommand implements ResourceEntity } if ((int) Settings::Get('system.mod_fcgid') == 1) { - $mod_fcgid_starter = validate($this->getParam('mod_fcgid_starter', - 1), 'mod_fcgid_starter', '/^[0-9]*$/', '', array( + $mod_fcgid_starter = validate($mod_fcgid_starter, 'mod_fcgid_starter', '/^[0-9]*$/', '', array( '-1', '' ), true); - $mod_fcgid_maxrequests = validate($this->getParam('mod_fcgid_maxrequests', - 1), 'mod_fcgid_maxrequests', '/^[0-9]*$/', '', array( + $mod_fcgid_maxrequests = validate($mod_fcgid_maxrequests, 'mod_fcgid_maxrequests', '/^[0-9]*$/', '', array( '-1', '' ), true); @@ -282,12 +298,12 @@ class Domains extends ApiCommand implements ResourceEntity } $ipandports = array(); - if (! empty($this->getParam('ipandport')) && ! is_array($this->getParam('ipandport'))) { - $this->updateParam('ipandport', unserialize($this->getParam('ipandport'))); + if (! empty($p_ipandport) && ! is_array($p_ipandports)) { + $p_ipandports = unserialize($p_ipandports); } - if (! empty($this->getParam('ipandport')) && is_array($this->getParam('ipandport'))) { - foreach ($this->getParam('ipandport') as $ipandport) { + if (! empty($p_ipandports) && is_array($p_ipandports)) { + foreach ($p_ipandports as $ipandport) { $ipandport = intval($ipandport); $ipandport_check_stmt = Database::prepare(" SELECT `id`, `ip`, `port` FROM `" . TABLE_PANEL_IPSANDPORTS . "` @@ -306,18 +322,16 @@ class Domains extends ApiCommand implements ResourceEntity } } - if (Settings::Get('system.use_ssl') == "1" && ! empty($this->getParam('ssl_ipandport'))) { - $ssl_redirect = $this->getParam('ssl_redirect', 0); - $letsencrypt = $this->getParam('letsencrypt', 0); + if (Settings::Get('system.use_ssl') == "1" && ! empty($p_ssl_ipandports)) { $ssl_ipandports = array(); - if (! empty($this->getParam('ssl_ipandport')) && ! is_array($this->getParam('ssl_ipandport'))) { - $this->updateParam('ssl_ipandport', unserialize($this->getParam('ssl_ipandport'))); + if (! empty($p_ssl_ipandports) && ! is_array($p_ssl_ipandports)) { + $p_ssl_ipandports = unserialize($p_ssl_ipandports); } // Verify SSL-Ports - if (! empty($this->getParam('ssl_ipandport')) && is_array($this->getParam('ssl_ipandport'))) { - foreach ($this->getParam('ssl_ipandport') as $ssl_ipandport) { + if (! empty($p_ssl_ipandports) && is_array($p_ssl_ipandports)) { + foreach ($p_ssl_ipandports as $ssl_ipandport) { if (trim($ssl_ipandport) == "") { continue; } @@ -341,14 +355,6 @@ class Domains extends ApiCommand implements ResourceEntity $ssl_ipandports[] = $ssl_ipandport; } } - - $http2 = $this->getParam('http2', 0); - // HSTS - $hsts_maxage = $this->getParam('hsts_maxage', 0); - $hsts_sub = $this->getParam('hsts_sub', 0); - $hsts_preload = $this->getParam('hsts_preload', 0); - // OCSP stapling - $ocsp_stapling = $this->getParam('ocsp_stapling', 0); } else { $ssl_redirect = 0; $letsencrypt = 0; @@ -717,23 +723,57 @@ class Domains extends ApiCommand implements ResourceEntity public function update() { if ($this->isAdmin() && $this->getUserDetail('change_serversettings')) { + + // parameters $id = $this->getParam('id'); + // get requested domain $json_result = Domains::getLocal($this->getUserData(), array( 'id' => $id, 'no_std_subdomain' => true ))->get(); $result = json_decode($json_result, true)['data']; - $customer_stmt = Database::prepare(" - SELECT * FROM " . TABLE_PANEL_CUSTOMERS . " WHERE `customerid` = :customerid - "); - $customer = Database::pexecute_first($customer_stmt, array( - 'customerid' => $result['customerid'] - )); + // optional parameters + $p_domain = $this->getParam('domain', true, $result['domain']); + $p_ipandports = $this->getParam('ipandport', true, array()); + $customerid = intval($this->getParam('customerid', true, $result['customerid'])); + $adminid = intval($this->getParam('adminid', true, $result['adminid'])); - $customerid = $this->getParam('customerid', $result['customerid']); + $subcanemaildomain = $this->getParam('subcanemaildomain', true, $result['subcanemaildomain']); + $isemaildomain = $this->getParam('isemaildomain', true, $result['isemaildomain']); + $email_only = $this->getParam('email_only', true, $result['email_only']); + $p_serveraliasoption = $this->getParam('selectserveralias', true, - 1); + $speciallogfile = $this->getParam('speciallogfile', true, $result['speciallogfile']); + $speciallogverified = $this->getParam('speciallogverified', true, 0); + $aliasdomain = intval($this->getParam('alias', true, $result['aliasdomain'])); + $issubof = intval($this->getParam('issubof', true, $result['ismainbutsubto'])); + $registration_date = trim($this->getParam('registration_date', true, $result['registration_date'])); + $termination_date = trim($this->getParam('termination_date', true, $result['termination_date'])); + $caneditdomain = $this->getParam('caneditdomain', true, $result['caneditdomain']); + $isbinddomain = $this->getParam('isbinddomain', true, $result['isbinddomain']); + $zonefile = $this->getParam('zonefile', true, $result['zonefile']); + $dkim = intval($this->getParam('dkim', true, $result['dkim'])); + $specialsettings = $this->getParam('specialsettings', true, $result['specialsettings']); + $ssfs = $this->getParam('specialsettingsforsubdomains', true, 0); + $notryfiles = $this->getParam('notryfiles', true, $result['notryfiles']); + $documentroot = $this->getParam('documentroot', true, $result['documentroot']); + $phpenabled = $this->getParam('phpenabled', true, $result['phpenabled']); + $phpfs = $this->getParam('phpsettingsforsubdomains', true, 0); + $openbasedir = $this->getParam('openbasedir', true, $result['openbasedir']); + $phpsettingid = $this->getParam('phpsettingid', true, $result['phpsettingid']); + $mod_fcgid_starter = $this->getParam('mod_fcgid_starter', true, $result['mod_fcgid_starter']); + $mod_fcgid_maxrequests = $this->getParam('mod_fcgid_maxrequests', true, $result['mod_fcgid_maxrequests']); + $ssl_redirect = $this->getParam('ssl_redirect', true, $result['ssl_redirect']); + $letsencrypt = $this->getParam('letsencrypt', true, $result['letsencrypt']); + $p_ssl_ipandports = $this->getParam('ssl_ipandport', true, array()); + $http2 = $this->getParam('http2', true, $result['http2']); + $hsts_maxage = $this->getParam('hsts_maxage', true, $result['hsts_maxage']); + $hsts_sub = $this->getParam('hsts_sub', true, $result['hsts_sub']); + $hsts_preload = $this->getParam('hsts_preload', true, $result['hsts_preload']); + $ocsp_stapling = $this->getParam('ocsp_stapling', true, $result['ocsp_stapling']); + // handle change of customer (move domain from customer to customer) if ($customerid > 0 && $customerid != $result['customerid'] && Settings::Get('panel.allow_domain_change_customer') == '1') { $customer_stmt = Database::prepare(" @@ -755,7 +795,12 @@ class Domains extends ApiCommand implements ResourceEntity $params['adminid'] = $this->getUserDetail('adminid'); } - $customer = Database::pexecute_first($customer_stmt, $params, true, true); + // get domains customer + $json_result = Customers::getLocal($this->getUserData(), array( + 'id' => $result['customerid'] + ))->get(); + $customer = json_decode($json_result, true)['data']; + if (empty($customer) || $customer['customerid'] != $customerid) { standard_error('customerdoesntexist', '', true); } @@ -763,17 +808,9 @@ class Domains extends ApiCommand implements ResourceEntity $customerid = $result['customerid']; } - $customer_stmt = Database::prepare(" - SELECT * FROM " . TABLE_PANEL_ADMINS . " WHERE `adminid` = :adminid - "); - $admin = Database::pexecute_first($customer_stmt, array( - 'adminid' => $result['adminid'] - ), true, true); - + // handle change of admin (move domain from admin to admin) if ($this->getUserDetail('customers_see_all') == '1') { - $adminid = $this->getParam('adminid', $result['adminid']); - if ($adminid > 0 && $adminid != $result['adminid'] && Settings::Get('panel.allow_domain_change_admin') == '1') { $admin_stmt = Database::prepare(" @@ -794,11 +831,6 @@ class Domains extends ApiCommand implements ResourceEntity $adminid = $result['adminid']; } - $aliasdomain = $this->getParam('alias', $result['aliasdomain']); - $issubof = $this->getParam('issubof', $result['ismainbutsubto']); - $subcanemaildomain = $this->getParam('subcanemaildomain', $result['subcanemaildomain']); - $caneditdomain = $this->getParam('caneditdomain', $result['caneditdomain']); - $registration_date = $this->getParam('registration_date', $result['registration_date']); $registration_date = validate($registration_date, 'registration_date', '/^(19|20)\d\d[-](0[1-9]|1[012])[-](0[1-9]|[12][0-9]|3[01])$/', '', array( '0000-00-00', '0', @@ -807,7 +839,6 @@ class Domains extends ApiCommand implements ResourceEntity if ($registration_date == '0000-00-00') { $registration_date = null; } - $termination_date = $this->getParam('termination_date', $result['termination_date']); $termination_date = validate($termination_date, 'termination_date', '/^(19|20)\d\d[-](0[1-9]|1[012])[-](0[1-9]|[12][0-9]|3[01])$/', '', array( '0000-00-00', '0', @@ -817,39 +848,31 @@ class Domains extends ApiCommand implements ResourceEntity $termination_date = null; } - $isemaildomain = $this->getParam('isemaildomain', $result['isemaildomain']); - $email_only = $this->getParam('email_only', $result['email_only']); - $serveraliasoption = '2'; if ($result['iswildcarddomain'] == '1') { $serveraliasoption = '0'; } elseif ($result['wwwserveralias'] == '1') { $serveraliasoption = '1'; } - if (! empty($this->getParam('selectserveralias'))) { - $serveraliasoption = intval($this->getParam('selectserveralias')); + if ($p_serveraliasoption > - 1) { + $serveraliasoption = $p_serveraliasoption; } - $speciallogfile = $this->getParam('speciallogfile', $result['speciallogfile']); - if ($this->getUserDetail('change_serversettings') == '1') { - $isbinddomain = $result['isbinddomain']; - $zonefile = $result['zonefile']; - if (Settings::Get('system.bind_enable') == '1') { - $isbinddomain = $this->getParam('isbinddomain', $result['isbinddomain']); - $zonefile = validate($this->getParam('zonefile', $result['zonefile']), 'zonefile', '', '', array(), true); + + if (Settings::Get('system.bind_enable') != '1') { + $zonefile = validate($zonefile, 'zonefile', '', '', array(), true); + } else { + $isbinddomain = $result['isbinddomain']; + $zonefile = $result['zonefile']; } - if (Settings::Get('dkim.use_dkim') == '1') { - $dkim = $this->getParam('dkim', $result['dkim']); - } else { + if (Settings::Get('dkim.use_dkim') != '1') { $dkim = $result['dkim']; } - $specialsettings = validate(str_replace("\r\n", "\n", $this->getParam('specialsettings', $result['specialsettings'])), 'specialsettings', '/^[^\0]*$/', '', array(), true); - $ssfs = $this->getParam('specialsettingsforsubdomains', 0); - $notryfiles = $this->getParam('notryfiles', $result['notryfiles']); - $documentroot = validate($this->getParam('documentroot', $result['documentroot']), 'documentroot', '', '', array(), true); + $specialsettings = validate(str_replace("\r\n", "\n", $specialsettings), 'specialsettings', '/^[^\0]*$/', '', array(), true); + $documentroot = validate($documentroot, 'documentroot', '', '', array(), true); if ($documentroot == '') { // If path is empty and 'Use domain name as default value for DocumentRoot path' is enabled in settings, @@ -874,17 +897,9 @@ class Domains extends ApiCommand implements ResourceEntity $documentroot = $result['documentroot']; } - // @TODO unsure whether this will still work - $speciallogverified = $this->getParam('speciallogverified', 0); - if ($this->getUserDetail('caneditphpsettings') == '1' || $this->getUserDetail('change_serversettings') == '1') { - $phpenabled = $this->getParam('phpenabled', $result['phpenabled']); - $openbasedir = $this->getParam('openbasedir', $result['openbasedir']); - $phpfs = $this->getParam('phpsettingsforsubdomains', 0); - if ((int) Settings::Get('system.mod_fcgid') == 1 || (int) Settings::Get('phpfpm.enabled') == 1) { - $phpsettingid = $this->getParam('phpsettingid', $result['phpsettingid']); $phpsettingid_check_stmt = Database::prepare(" SELECT * FROM `" . TABLE_PANEL_PHPCONFIGS . "` WHERE `id` = :phpid "); @@ -897,11 +912,11 @@ class Domains extends ApiCommand implements ResourceEntity } if ((int) Settings::Get('system.mod_fcgid') == 1) { - $mod_fcgid_starter = validate($this->getParam('mod_fcgid_starter', $result['mod_fcgid_starter']), 'mod_fcgid_starter', '/^[0-9]*$/', '', array( + $mod_fcgid_starter = validate($mod_fcgid_starter, 'mod_fcgid_starter', '/^[0-9]*$/', '', array( '-1', '' ), true); - $mod_fcgid_maxrequests = validate($this->getParam('mod_fcgid_maxrequests', $result['mod_fcgid_maxrequests']), 'mod_fcgid_maxrequests', '/^[0-9]*$/', '', array( + $mod_fcgid_maxrequests = validate($mod_fcgid_maxrequests, 'mod_fcgid_maxrequests', '/^[0-9]*$/', '', array( '-1', '' ), true); @@ -925,15 +940,15 @@ class Domains extends ApiCommand implements ResourceEntity } $ipandports = array(); - if (! empty($this->getParam('ipandport')) && ! is_array($this->getParam('ipandport'))) { - $this->updateParam('ipandport', unserialize($this->getParam('ipandport'))); + if (! empty($p_ipandports) && ! is_array($p_ipandports)) { + $p_ipandports = unserialize($p_ipandports); } - if (! empty($this->getParam('ipandport')) && is_array($this->getParam('ipandport'))) { + if (! empty($p_ipandports) && is_array($p_ipandports)) { $ipandport_check_stmt = Database::prepare(" SELECT `id`, `ip`, `port` FROM `" . TABLE_PANEL_IPSANDPORTS . "` WHERE `id` = :ipandport "); - foreach ($this->getParam('ipandport') as $ipandport) { + foreach ($p_ipandports as $ipandport) { if (trim($ipandport) == "") { continue; } @@ -949,20 +964,18 @@ class Domains extends ApiCommand implements ResourceEntity } } - if (Settings::Get('system.use_ssl') == '1' && ! empty($this->getParam('ssl_ipandport'))) { + if (Settings::Get('system.use_ssl') == '1' && ! empty($p_ssl_ipandports)) { $ssl = 1; // if ssl is set and != 0, it can only be 1 - $ssl_redirect = $this->getParam('ssl_redirect', $result['ssl_redirect']); - $letsencrypt = $this->getParam('letsencrypt', $result['letsencrypt']); $ssl_ipandports = array(); - if (! empty($this->getParam('ssl_ipandport')) && ! is_array($this->getParam('ssl_ipandport'))) { - $this->updateParam('ssl_ipandport', unserialize($this->getParam('ssl_ipandport'))); + if (! empty($p_ssl_ipandports) && ! is_array($p_ssl_ipandports)) { + $p_ssl_ipandports = unserialize($p_ssl_ipandports); } - if (! empty($this->getParam('ssl_ipandport')) && is_array($this->getParam('ssl_ipandport'))) { + if (! empty($p_ssl_ipandports) && is_array($p_ssl_ipandports)) { $ssl_ipandport_check_stmt = Database::prepare(" SELECT `id`, `ip`, `port` FROM `" . TABLE_PANEL_IPSANDPORTS . "` WHERE `id` = :ipandport "); - foreach ($this->getParam('ssl_ipandport') as $ssl_ipandport) { + foreach ($p_ssl_ipandports as $ssl_ipandport) { if (trim($ssl_ipandport) == "") { continue; } @@ -980,14 +993,6 @@ class Domains extends ApiCommand implements ResourceEntity $ssl_ipandports[] = $ssl_ipandport; } } - - $http2 = $this->getParam('http2', $result['http2']); - // HSTS - $hsts_maxage = $this->getParam('hsts_maxage', $result['hsts_maxage']); - $hsts_sub = $this->getParam('hsts_sub', $result['hsts_sub']); - $hsts_preload = $this->getParam('hsts_preload', $result['hsts_preload']); - // OCSP stapling - $ocsp_stapling = $this->getParam('ocsp_stapling', $result['ocsp_stapling']); } else { $ssl_redirect = 0; $letsencrypt = 0; @@ -1304,7 +1309,6 @@ class Domains extends ApiCommand implements ResourceEntity $_update_data = array(); - $ssfs = $this->getParam('specialsettingsforsubdomains', 0); if ($ssfs == 1) { $_update_data['specialsettings'] = $specialsettings; $upd_specialsettings = ", `specialsettings` = :specialsettings "; @@ -1404,7 +1408,6 @@ class Domains extends ApiCommand implements ResourceEntity // if php config is to be set for all subdomains, check here $update_phpconfig = ''; - $phpfs = $this->getParam('phpsettingsforsubdomains', 0); if ($phpfs == 1) { $_update_data['phpsettingid'] = $phpsettingid; $update_phpconfig = ", `phpsettingid` = :phpsettingid"; @@ -1520,8 +1523,10 @@ class Domains extends ApiCommand implements ResourceEntity /** * delete a domain entry by id * - * @param int $id domain-id - * + * @param int $id + * domain-id + * @param bool $delete_mainsubdomains optional, remove also domains that are subdomains of this domain but added as main domains; default false + * * @throws Exception * @return array */ @@ -1529,7 +1534,8 @@ class Domains extends ApiCommand implements ResourceEntity { if ($this->isAdmin()) { $id = $this->getParam('id'); - + $remove_subbutmain_domains = $this->getParam('delete_mainsubdomains', true, 0); + $json_result = Domains::getLocal($this->getUserData(), array( 'id' => $id, 'no_std_subdomain' => true @@ -1538,11 +1544,10 @@ class Domains extends ApiCommand implements ResourceEntity // check for deletion of main-domains which are logically subdomains, #329 $rsd_sql = ''; - $remove_subbutmain_domains = $this->getParam('delete_userfiles', 0) ? 1 : 0; - if ($remove_subbutmain_domains == 1) { + if ($remove_subbutmain_domains) { $rsd_sql .= " OR `ismainbutsubto` = :id"; } - + $subresult_stmt = Database::prepare(" SELECT `id` FROM `" . TABLE_PANEL_DOMAINS . "` WHERE (`id` = :id OR `parentdomainid` = :id " . $rsd_sql . ")"); diff --git a/lib/classes/api/commands/class.IpsAndPorts.php b/lib/classes/api/commands/class.IpsAndPorts.php index 9baa0a64..17b90d3f 100644 --- a/lib/classes/api/commands/class.IpsAndPorts.php +++ b/lib/classes/api/commands/class.IpsAndPorts.php @@ -58,25 +58,26 @@ class IpsAndPorts extends ApiCommand implements ResourceEntity public function add() { if ($this->isAdmin() && $this->getUserDetail('change_serversettings')) { + $ip = validate_ip2($this->getParam('ip'), false, 'invalidip', false, false, false, true); - $port = validate($this->getParam('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 = 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', 'myport' ), array(), true); - $listen_statement = ! empty($this->getParam('listen_statement')) ? 1 : 0; - $namevirtualhost_statement = ! empty($this->getParam('namevirtualhost_statement')) ? 1 : 0; - $vhostcontainer = ! empty($this->getParam('vhostcontainer')) ? 1 : 0; - $specialsettings = validate(str_replace("\r\n", "\n", $this->getParam('specialsettings')), 'specialsettings', '/^[^\0]*$/', '', array(), true); - $vhostcontainer_servername_statement = ! empty($this->getParam('vhostcontainer_servername_statement')) ? 1 : 0; - $default_vhostconf_domain = validate(str_replace("\r\n", "\n", $this->getParam('default_vhostconf_domain')), 'default_vhostconf_domain', '/^[^\0]*$/', '', array(), true); - $docroot = validate($this->getParam('docroot'), 'docroot', '', '', array(), true); + $listen_statement = ! empty($this->getParam('listen_statement', true, 0)) ? 1 : 0; + $namevirtualhost_statement = ! empty($this->getParam('namevirtualhost_statement', true, 0)) ? 1 : 0; + $vhostcontainer = ! empty($this->getParam('vhostcontainer', true, 0)) ? 1 : 0; + $specialsettings = validate(str_replace("\r\n", "\n", $this->getParam('specialsettings', true, '')), 'specialsettings', '/^[^\0]*$/', '', array(), true); + $vhostcontainer_servername_statement = ! empty($this->getParam('vhostcontainer_servername_statement', true, 1)) ? 1 : 0; + $default_vhostconf_domain = validate(str_replace("\r\n", "\n", $this->getParam('default_vhostconf_domain', true, '')), 'default_vhostconf_domain', '/^[^\0]*$/', '', array(), true); + $docroot = validate($this->getParam('docroot', true, ''), 'docroot', '', '', array(), true); if ((int) Settings::Get('system.use_ssl') == 1) { - $ssl = ! empty($this->getParam('ssl')) ? intval($this->getParam('ssl')) : 0; - $ssl_cert_file = validate($this->getParam('ssl_cert_file'), 'ssl_cert_file', '', '', array(), true); - $ssl_key_file = validate($this->getParam('ssl_key_file'), 'ssl_key_file', '', '', array(), true); - $ssl_ca_file = validate($this->getParam('ssl_ca_file'), 'ssl_ca_file', '', '', array(), true); - $ssl_cert_chainfile = validate($this->getParam('ssl_cert_chainfile'), 'ssl_cert_chainfile', '', '', array(), true); + $ssl = ! empty($this->getParam('ssl', true, 0)) ? intval($this->getParam('ssl', true, 0)) : 0; + $ssl_cert_file = validate($this->getParam('ssl_cert_file', $ssl, ''), 'ssl_cert_file', '', '', array(), true); + $ssl_key_file = validate($this->getParam('ssl_key_file', $ssl, ''), 'ssl_key_file', '', '', array(), true); + $ssl_ca_file = validate($this->getParam('ssl_ca_file', true, ''), 'ssl_ca_file', '', '', array(), true); + $ssl_cert_chainfile = validate($this->getParam('ssl_cert_chainfile', true, ''), 'ssl_cert_chainfile', '', '', array(), true); } else { $ssl = 0; $ssl_cert_file = ''; @@ -192,25 +193,25 @@ class IpsAndPorts extends ApiCommand implements ResourceEntity ))->get(); $result = json_decode($json_result, true)['data']; - $ip = validate_ip2($this->getParam('ip', $result['ip']), false, 'invalidip', false, false, false, true); - $port = validate($this->getParam('port', $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( + $ip = validate_ip2($this->getParam('ip', true, $result['ip']), false, 'invalidip', false, false, false, true); + $port = 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', 'myport' ), array(), true); - $listen_statement = $this->getParam('listen_statement', $result['listen_statement']); - $namevirtualhost_statement = $this->getParam('namevirtualhost_statement', $result['namevirtualhost_statement']); - $vhostcontainer = $this->getParam('vhostcontainer', $result['vhostcontainer']); - $specialsettings = validate(str_replace("\r\n", "\n", $this->getParam('specialsettings', $result['specialsettings'])), 'specialsettings', '/^[^\0]*$/', '', array(), true); - $vhostcontainer_servername_statement = $this->getParam('vhostcontainer_servername_statement', $result['vhostcontainer_servername_statement']); - $default_vhostconf_domain = validate(str_replace("\r\n", "\n", $this->getParam('default_vhostconf_domain', $result['default_vhostconf_domain'])), 'default_vhostconf_domain', '/^[^\0]*$/', '', array(), true); - $docroot = validate($this->getParam('docroot', $result['docroot']), 'docroot', '', '', array(), true); + $listen_statement = $this->getParam('listen_statement', true, $result['listen_statement']); + $namevirtualhost_statement = $this->getParam('namevirtualhost_statement', true, $result['namevirtualhost_statement']); + $vhostcontainer = $this->getParam('vhostcontainer', true, $result['vhostcontainer']); + $specialsettings = validate(str_replace("\r\n", "\n", $this->getParam('specialsettings', true, $result['specialsettings'])), 'specialsettings', '/^[^\0]*$/', '', array(), true); + $vhostcontainer_servername_statement = $this->getParam('vhostcontainer_servername_statement', true, $result['vhostcontainer_servername_statement']); + $default_vhostconf_domain = validate(str_replace("\r\n", "\n", $this->getParam('default_vhostconf_domain',true, $result['default_vhostconf_domain'])), 'default_vhostconf_domain', '/^[^\0]*$/', '', array(), true); + $docroot = validate($this->getParam('docroot', true, $result['docroot']), 'docroot', '', '', array(), true); if ((int) Settings::Get('system.use_ssl') == 1) { - $ssl = $this->getParam('ssl', $result['ssl']); - $ssl_cert_file = validate($this->getParam('ssl_cert_file', $result['ssl_cert_file']), 'ssl_cert_file', '', '', array(), true); - $ssl_key_file = validate($this->getParam('ssl_key_file', $result['ssl_key_file']), 'ssl_key_file', '', '', array(), true); - $ssl_ca_file = validate($this->getParam('ssl_ca_file', $result['ssl_ca_file']), 'ssl_ca_file', '', '', array(), true); - $ssl_cert_chainfile = validate($this->getParam('ssl_cert_chainfile', $result['ssl_cert_chainfile']), 'ssl_cert_chainfile', '', '', array(), true); + $ssl = $this->getParam('ssl', true, $result['ssl']); + $ssl_cert_file = validate($this->getParam('ssl_cert_file', $ssl, $result['ssl_cert_file']), 'ssl_cert_file', '', '', array(), true); + $ssl_key_file = validate($this->getParam('ssl_key_file', $ssl, $result['ssl_key_file']), 'ssl_key_file', '', '', array(), true); + $ssl_ca_file = validate($this->getParam('ssl_ca_file', true, $result['ssl_ca_file']), 'ssl_ca_file', '', '', array(), true); + $ssl_cert_chainfile = validate($this->getParam('ssl_cert_chainfile', true, $result['ssl_cert_chainfile']), 'ssl_cert_chainfile', '', '', array(), true); } else { $ssl = 0; $ssl_cert_file = ''; From 168738b23a6842cf820f70283f00a27c59790297 Mon Sep 17 00:00:00 2001 From: JB1985 Date: Mon, 19 Feb 2018 09:02:34 +0100 Subject: [PATCH 0444/1335] Update german.lng.php Statt Benutze Lets Encrypt => SSL Zertifikat erstellen (Let\'s Encrypt) --- lng/german.lng.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lng/german.lng.php b/lng/german.lng.php index 1f6ee8f9..322ddb14 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -1588,7 +1588,7 @@ $lng['integrity_check']['SubdomainLetsencrypt'] = 'Hauptdomains ohne zugewiesene $lng['admin']['mod_fcgid_umask']['title'] = 'Umask (Standard: 022)'; // Added for let's encrypt -$lng['admin']['letsencrypt']['title'] = 'Benutze Let\'s Encrypt'; +$lng['admin']['letsencrypt']['title'] = 'SSL Zertifikat erstellen (Let\'s Encrypt'); $lng['admin']['letsencrypt']['description'] = 'Holt ein kostenloses Zertifikat von Let\'s Encrypt. Das Zertifikat wird automatisch erstellt und verlängert.
    ACHTUNG: Wenn Wildcards aktiviert sind, wird diese Option automatisch deaktiviert. Dieses Feature befindet sich noch im Test.'; $lng['customer']['letsencrypt']['title'] = 'Benutze Let\'s Encrypt'; $lng['customer']['letsencrypt']['description'] = 'Holt ein kostenloses Zertifikat von Let\'s Encrypt. Das Zertifikat wird automatisch erstellt und verlängert.
    ACHTUNG: Dieses Feature befindet sich noch im Test.'; From 603e14913ba08bd4bf8ae0e0ed29315d092e0460 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 19 Feb 2018 10:45:41 +0100 Subject: [PATCH 0445/1335] finished Customer::update(), untested Signed-off-by: Michael Kaufmann (d00p) --- admin_customers.php | 554 +-------------- lib/classes/api/abstract.ApiCommand.php | 10 + lib/classes/api/commands/class.Customers.php | 638 +++++++++++++++--- lib/classes/api/commands/class.Domains.php | 61 +- lib/classes/api/commands/class.Froxlor.php | 79 ++- .../api/commands/class.IpsAndPorts.php | 28 +- .../output/function.standard_success.php | 39 +- 7 files changed, 720 insertions(+), 689 deletions(-) diff --git a/admin_customers.php b/admin_customers.php index fc53871b..3a8c66f1 100644 --- a/admin_customers.php +++ b/admin_customers.php @@ -375,553 +375,15 @@ if ($page == 'customers' if (isset($_POST['send']) && $_POST['send'] == 'send' ) { - - $name = validate($_POST['name'], 'name'); - $firstname = validate($_POST['firstname'], 'first name'); - $company = validate($_POST['company'], 'company'); - $street = validate($_POST['street'], 'street'); - $zipcode = validate($_POST['zipcode'], 'zipcode', '/^[0-9 \-A-Z]*$/'); - $city = validate($_POST['city'], 'city'); - $phone = validate($_POST['phone'], 'phone', '/^[0-9\- \+\(\)\/]*$/'); - $fax = validate($_POST['fax'], 'fax', '/^[0-9\- \+\(\)\/]*$/'); - $email = $idna_convert->encode(validate($_POST['email'], 'email')); - $customernumber = validate($_POST['customernumber'], 'customer number', '/^[A-Za-z0-9 \-]*$/Di'); - $def_language = validate($_POST['def_language'], 'default language'); - $password = validate($_POST['new_customer_password'], 'new password'); - $gender = intval_ressource($_POST['gender']); - - $move_to_admin = isset($_POST['move_to_admin']) ? intval_ressource($_POST['move_to_admin']) : 0; - - $custom_notes = validate(str_replace("\r\n", "\n", $_POST['custom_notes']), 'custom_notes', '/^[^\0]*$/'); - $custom_notes_show = $result['custom_notes_show']; - if (isset($_POST['custom_notes_show'])) { - $custom_notes_show = intval_ressource($_POST['custom_notes_show']); + try { + Customers::getLocal($userinfo, $_POST)->update(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); } - - $diskspace = intval_ressource($_POST['diskspace']); - if (isset($_POST['diskspace_ul'])) { - $diskspace = - 1; - } - - $traffic = doubleval_ressource($_POST['traffic']); - if (isset($_POST['traffic_ul'])) { - $traffic = - 1; - } - - $subdomains = intval_ressource($_POST['subdomains']); - if (isset($_POST['subdomains_ul'])) { - $subdomains = - 1; - } - - $emails = intval_ressource($_POST['emails']); - if (isset($_POST['emails_ul'])) { - $emails = - 1; - } - - $email_accounts = intval_ressource($_POST['email_accounts']); - if (isset($_POST['email_accounts_ul'])) { - $email_accounts = - 1; - } - - $email_forwarders = intval_ressource($_POST['email_forwarders']); - if (isset($_POST['email_forwarders_ul'])) { - $email_forwarders = - 1; - } - - if (Settings::Get('system.mail_quota_enabled') == '1') { - $email_quota = validate($_POST['email_quota'], 'email_quota', '/^\d+$/', 'vmailquotawrong', array('0', '')); - if (isset($_POST['email_quota_ul'])) { - $email_quota = - 1; - } - } else { - $email_quota = - 1; - } - - $email_imap = 0; - if (isset($_POST['email_imap'])) { - $email_imap = intval_ressource($_POST['email_imap']); - } - - $email_pop3 = 0; - if (isset($_POST['email_pop3'])) { - $email_pop3 = intval_ressource($_POST['email_pop3']); - } - - $ftps = 0; - if (isset($_POST['ftps'])) { - $ftps = intval_ressource($_POST['ftps']); - } - if (isset($_POST['ftps_ul'])) { - $ftps = - 1; - } - - $tickets = (Settings::Get('ticket.enabled') == 1 ? intval_ressource($_POST['tickets']) : 0); - if (isset($_POST['tickets_ul']) - && Settings::Get('ticket.enabled') == '1' - ) { - $tickets = - 1; - } - - // gender out of range? [0,2] - if ($gender < 0 || $gender > 2) { - $gender = 0; - } - - $mysqls = 0; - if (isset($_POST['mysqls'])) { - $mysqls = intval_ressource($_POST['mysqls']); - } - if (isset($_POST['mysqls_ul'])) { - $mysqls = - 1; - } - - $createstdsubdomain = 0; - if (isset($_POST['createstdsubdomain'])) { - $createstdsubdomain = intval($_POST['createstdsubdomain']); - } - - $deactivated = 0; - if (isset($_POST['deactivated'])) { - $deactivated = intval($_POST['deactivated']); - } - - $phpenabled = 0; - if (isset($_POST['phpenabled'])) { - $phpenabled = intval($_POST['phpenabled']); - } - - $allowed_phpconfigs = array(); - if (isset($_POST['allowed_phpconfigs']) && is_array($_POST['allowed_phpconfigs'])) { - foreach ($_POST['allowed_phpconfigs'] as $allowed_phpconfig) { - $allowed_phpconfig = intval($allowed_phpconfig); - $allowed_phpconfigs[] = $allowed_phpconfig; - } - } - - $perlenabled = 0; - if (isset($_POST['perlenabled'])) { - $perlenabled = intval($_POST['perlenabled']); - } - - $dnsenabled = 0; - if (isset($_POST['dnsenabled'])) { - $dnsenabled = intval($_POST['dnsenabled']); - } - - $diskspace = $diskspace * 1024; - $traffic = $traffic * 1024 * 1024; - - if (((($userinfo['diskspace_used'] + $diskspace - $result['diskspace']) > $userinfo['diskspace']) && ($userinfo['diskspace'] / 1024) != '-1') - || ((($userinfo['mysqls_used'] + $mysqls - $result['mysqls']) > $userinfo['mysqls']) && $userinfo['mysqls'] != '-1') - || ((($userinfo['emails_used'] + $emails - $result['emails']) > $userinfo['emails']) && $userinfo['emails'] != '-1') - || ((($userinfo['email_accounts_used'] + $email_accounts - $result['email_accounts']) > $userinfo['email_accounts']) && $userinfo['email_accounts'] != '-1') - || ((($userinfo['email_forwarders_used'] + $email_forwarders - $result['email_forwarders']) > $userinfo['email_forwarders']) && $userinfo['email_forwarders'] != '-1') - || ((($userinfo['email_quota_used'] + $email_quota - $result['email_quota']) > $userinfo['email_quota']) && $userinfo['email_quota'] != '-1' && Settings::Get('system.mail_quota_enabled') == '1') - || ((($userinfo['ftps_used'] + $ftps - $result['ftps']) > $userinfo['ftps']) && $userinfo['ftps'] != '-1') - || ((($userinfo['tickets_used'] + $tickets - $result['tickets']) > $userinfo['tickets']) && $userinfo['tickets'] != '-1') - || ((($userinfo['subdomains_used'] + $subdomains - $result['subdomains']) > $userinfo['subdomains']) && $userinfo['subdomains'] != '-1') - || (($diskspace / 1024) == '-1' && ($userinfo['diskspace'] / 1024) != '-1') - || ($mysqls == '-1' && $userinfo['mysqls'] != '-1') - || ($emails == '-1' && $userinfo['emails'] != '-1') - || ($email_accounts == '-1' && $userinfo['email_accounts'] != '-1') - || ($email_forwarders == '-1' && $userinfo['email_forwarders'] != '-1') - || ($email_quota == '-1' && $userinfo['email_quota'] != '-1' && Settings::Get('system.mail_quota_enabled') == '1') - || ($ftps == '-1' && $userinfo['ftps'] != '-1') - || ($tickets == '-1' && $userinfo['tickets'] != '-1') - || ($subdomains == '-1' && $userinfo['subdomains'] != '-1') - ) { - standard_error('youcantallocatemorethanyouhave'); - } - - // Either $name and $firstname or the $company must be inserted - if ($name == '' && $company == '') { - standard_error(array('stringisempty', 'myname')); - - } elseif($firstname == '' && $company == '') { - standard_error(array('stringisempty', 'myfirstname')); - - } elseif($email == '') { - standard_error(array('stringisempty', 'emailadd')); - - } elseif(!validateEmail($email)) { - standard_error('emailiswrong', $email); - - } else { - - if ($password != '') { - $password = validatePassword($password); - $password = makeCryptPassword($password); - } else { - $password = $result['password']; - } - - if ($createstdsubdomain != '1') { - $createstdsubdomain = '0'; - } - - if ($createstdsubdomain == '1' - && $result['standardsubdomain'] == '0' - ) { - - if (Settings::Get('system.stdsubdomain') !== null - && Settings::Get('system.stdsubdomain') != '' - ) { - $_stdsubdomain = $result['loginname'] . '.' . Settings::Get('system.stdsubdomain'); - } else { - $_stdsubdomain = $result['loginname'] . '.' . Settings::Get('system.hostname'); - } - - $ins_data = array( - 'domain' => $_stdsubdomain, - 'customerid' => $result['customerid'], - 'adminid' => $userinfo['adminid'], - 'docroot' => $result['documentroot'], - 'adddate' => time() - ); - $ins_stmt = Database::prepare(" - INSERT INTO `" . TABLE_PANEL_DOMAINS . "` SET - `domain` = :domain, - `customerid` = :customerid, - `adminid` = :adminid, - `parentdomainid` = '0', - `documentroot` = :docroot, - `zonefile` = '', - `isemaildomain` = '0', - `caneditdomain` = '0', - `openbasedir` = '1', - `speciallogfile` = '0', - `specialsettings` = '', - `add_date` = :adddate" - ); - Database::pexecute($ins_stmt, $ins_data); - $domainid = Database::lastInsertId(); - - // set ip <-> domain connection - $defaultips = explode(',', Settings::Get('system.defaultip')); - $ins_stmt = Database::prepare(" - INSERT INTO `" . TABLE_DOMAINTOIP . "` SET `id_domain` = :domainid, `id_ipandports` = :ipid" - ); - foreach ($defaultips as $defaultip) { - Database::pexecute($ins_stmt, array('domainid' => $domainid, 'ipid' => $defaultip)); - } - - $upd_stmt = Database::prepare(" - UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET `standardsubdomain` = :domainid WHERE `customerid` = :customerid" - ); - Database::pexecute($upd_stmt, array('domainid' => $domainid, 'customerid' => $result['customerid'])); - $log->logAction(ADM_ACTION, LOG_NOTICE, "automatically added standardsubdomain for user '" . $result['loginname'] . "'"); - inserttask('1'); - } - - if ($createstdsubdomain == '0' - && $result['standardsubdomain'] != '0' - ) { - - $del_stmt = Database::prepare("DELETE FROM `" . TABLE_PANEL_DOMAINS . "` WHERE `id` = :stdsub"); - Database::pexecute($del_stmt, array('stdsub' => $result['standardsubdomain'])); - $del_stmt = Database::prepare("DELETE FROM `" . TABLE_DOMAINTOIP . "` WHERE `id_domain` = :stdsub"); - Database::pexecute($del_stmt, array('stdsub' => $result['standardsubdomain'])); - $del_stmt = Database::prepare("UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET `standardsubdomain`= '0' WHERE `customerid` = :customerid"); - Database::pexecute($del_stmt, array('customerid' => $result['customerid'])); - $log->logAction(ADM_ACTION, LOG_NOTICE, "automatically deleted standardsubdomain for user '" . $result['loginname'] . "'"); - inserttask('1'); - } - - if ($deactivated != '1') { - $deactivated = '0'; - } - - if ($phpenabled != '0') { - $phpenabled = '1'; - } - - if ($perlenabled != '0') { - $perlenabled = '1'; - } - - if ($dnsenabled != '0') { - $dnsenabled = '1'; - } - - if ($phpenabled != $result['phpenabled'] - || $perlenabled != $result['perlenabled'] - ) { - inserttask('1'); - } - - // activate/deactivate customer services - if ($deactivated != $result['deactivated']) { - - $yesno = (($deactivated) ? 'N' : 'Y'); - $pop3 = (($deactivated) ? '0' : (int)$result['pop3']); - $imap = (($deactivated) ? '0' : (int)$result['imap']); - - $upd_stmt = Database::prepare(" - UPDATE `" . TABLE_MAIL_USERS . "` SET `postfix`= :yesno, `pop3` = :pop3, `imap` = :imap WHERE `customerid` = :customerid" - ); - Database::pexecute($upd_stmt, array('yesno' => $yesno, 'pop3' => $pop3, 'imap' => $imap, 'customerid' => $id)); - - $upd_stmt = Database::prepare(" - UPDATE `" . TABLE_FTP_USERS . "` SET `login_enabled` = :yesno WHERE `customerid` = :customerid" - ); - Database::pexecute($upd_stmt, array('yesno' => $yesno, 'customerid' => $id)); - - $upd_stmt = Database::prepare(" - UPDATE `" . TABLE_PANEL_DOMAINS . "` SET `deactivated`= :deactivated WHERE `customerid` = :customerid" - ); - Database::pexecute($upd_stmt, array('deactivated' => $deactivated, 'customerid' => $id)); - - // Retrieve customer's databases - $databases_stmt = Database::prepare("SELECT * FROM " . TABLE_PANEL_DATABASES . " WHERE customerid = :customerid ORDER BY `dbserver`"); - Database::pexecute($databases_stmt, array('customerid' => $id)); - - Database::needRoot(true); - $last_dbserver = 0; - - $dbm = new DbManager($log); - - // For each of them - while ($row_database = $databases_stmt->fetch(PDO::FETCH_ASSOC)) { - - if ($last_dbserver != $row_database['dbserver']) { - $dbm->getManager()->flushPrivileges(); - Database::needRoot(true, $row_database['dbserver']); - $last_dbserver = $row_database['dbserver']; - } - - foreach (array_unique(explode(',', Settings::Get('system.mysql_access_host'))) as $mysql_access_host) { - $mysql_access_host = trim($mysql_access_host); - - // Prevent access, if deactivated - if ($deactivated) { - // failsafe if user has been deleted manually (requires MySQL 4.1.2+) - $dbm->getManager()->disableUser($row_database['databasename'], $mysql_access_host); - } else { - // Otherwise grant access - $dbm->getManager()->enableUser($row_database['databasename'], $mysql_access_host); - } - } - } - - // At last flush the new privileges - $dbm->getManager()->flushPrivileges(); - Database::needRoot(false); - - $log->logAction(ADM_ACTION, LOG_INFO, "deactivated user '" . $result['loginname'] . "'"); - inserttask('1'); - } - - // Disable or enable POP3 Login for customers Mail Accounts - if ($email_pop3 != $result['pop3']) { - $upd_stmt = Database::prepare("UPDATE `" . TABLE_MAIL_USERS . "` SET `pop3` = :pop3 WHERE `customerid` = :customerid"); - Database::pexecute($upd_stmt, array('pop3' => $email_pop3, 'customerid' => $id)); - } - - // Disable or enable IMAP Login for customers Mail Accounts - if ($email_imap != $result['imap']) { - $upd_stmt = Database::prepare("UPDATE `" . TABLE_MAIL_USERS . "` SET `imap` = :imap WHERE `customerid` = :customerid"); - Database::pexecute($upd_stmt, array('imap' => $email_imap, 'customerid' => $id)); - } - - $upd_data = array( - 'customerid' => $id, - 'passwd' => $password, - 'name' => $name, - 'firstname' => $firstname, - 'gender' => $gender, - 'company' => $company, - 'street' => $street, - 'zipcode' => $zipcode, - 'city' => $city, - 'phone' => $phone, - 'fax' => $fax, - 'email' => $email, - 'customerno' => $customernumber, - 'lang' => $def_language, - 'diskspace' => $diskspace, - 'traffic' => $traffic, - 'subdomains' => $subdomains, - 'emails' => $emails, - 'email_accounts' => $email_accounts, - 'email_forwarders' => $email_forwarders, - 'email_quota' => $email_quota, - 'ftps' => $ftps, - 'tickets' => $tickets, - 'mysqls' => $mysqls, - 'deactivated' => $deactivated, - 'phpenabled' => $phpenabled, - 'allowed_phpconfigs' => empty($allowed_phpconfigs) ? "" : json_encode($allowed_phpconfigs), - 'imap' => $email_imap, - 'pop3' => $email_pop3, - 'perlenabled' => $perlenabled, - 'dnsenabled' => $dnsenabled, - 'custom_notes' => $custom_notes, - 'custom_notes_show' => $custom_notes_show - ); - $upd_stmt = Database::prepare(" - UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET - `name` = :name, - `firstname` = :firstname, - `gender` = :gender, - `company` = :company, - `street` = :street, - `zipcode` = :zipcode, - `city` = :city, - `phone` = :phone, - `fax` = :fax, - `email` = :email, - `customernumber` = :customerno, - `def_language` = :lang, - `password` = :passwd, - `diskspace` = :diskspace, - `traffic` = :traffic, - `subdomains` = :subdomains, - `emails` = :emails, - `email_accounts` = :email_accounts, - `email_forwarders` = :email_forwarders, - `ftps` = :ftps, - `tickets` = :tickets, - `mysqls` = :mysqls, - `deactivated` = :deactivated, - `phpenabled` = :phpenabled, - `allowed_phpconfigs` = :allowed_phpconfigs, - `email_quota` = :email_quota, - `imap` = :imap, - `pop3` = :pop3, - `perlenabled` = :perlenabled, - `dnsenabled` = :dnsenabled, - `custom_notes` = :custom_notes, - `custom_notes_show` = :custom_notes_show - WHERE `customerid` = :customerid" - ); - Database::pexecute($upd_stmt, $upd_data); - - // Using filesystem - quota, insert a task which cleans the filesystem - quota - inserttask('10'); - - $admin_update_query = "UPDATE `" . TABLE_PANEL_ADMINS . "` SET `customers_used` = `customers_used` "; - - if ($mysqls != '-1' || $result['mysqls'] != '-1') { - $admin_update_query.= ", `mysqls_used` = `mysqls_used` "; - - if ($mysqls != '-1') { - $admin_update_query.= " + 0" . (int)$mysqls . " "; - } - if ($result['mysqls'] != '-1') { - $admin_update_query.= " - 0" . (int)$result['mysqls'] . " "; - } - } - - if($emails != '-1' || $result['emails'] != '-1') { - $admin_update_query.= ", `emails_used` = `emails_used` "; - - if ($emails != '-1') { - $admin_update_query.= " + 0" . (int)$emails . " "; - } - if ($result['emails'] != '-1') { - $admin_update_query.= " - 0" . (int)$result['emails'] . " "; - } - } - - if ($email_accounts != '-1' || $result['email_accounts'] != '-1') { - $admin_update_query.= ", `email_accounts_used` = `email_accounts_used` "; - - if ($email_accounts != '-1') { - $admin_update_query.= " + 0" . (int)$email_accounts . " "; - } - if ($result['email_accounts'] != '-1') { - $admin_update_query.= " - 0" . (int)$result['email_accounts'] . " "; - } - } - - if ($email_forwarders != '-1' || $result['email_forwarders'] != '-1') { - $admin_update_query.= ", `email_forwarders_used` = `email_forwarders_used` "; - - if ($email_forwarders != '-1') { - $admin_update_query.= " + 0" . (int)$email_forwarders . " "; - } - if ($result['email_forwarders'] != '-1') { - $admin_update_query.= " - 0" . (int)$result['email_forwarders'] . " "; - } - } - - if ($email_quota != '-1' || $result['email_quota'] != '-1') { - $admin_update_query.= ", `email_quota_used` = `email_quota_used` "; - - if ($email_quota != '-1') { - $admin_update_query.= " + 0" . (int)$email_quota . " "; - } - if ($result['email_quota'] != '-1') { - $admin_update_query.= " - 0" . (int)$result['email_quota'] . " "; - } - } - - if ($subdomains != '-1' || $result['subdomains'] != '-1') { - $admin_update_query.= ", `subdomains_used` = `subdomains_used` "; - - if ($subdomains != '-1') { - $admin_update_query.= " + 0" . (int)$subdomains . " "; - } - if ($result['subdomains'] != '-1') { - $admin_update_query.= " - 0" . (int)$result['subdomains'] . " "; - } - } - - if ($ftps != '-1' || $result['ftps'] != '-1') { - $admin_update_query.= ", `ftps_used` = `ftps_used` "; - - if ($ftps != '-1') { - $admin_update_query.= " + 0" . (int)$ftps . " "; - } - if ($result['ftps'] != '-1') { - $admin_update_query.= " - 0" . (int)$result['ftps'] . " "; - } - } - - if ($tickets != '-1' || $result['tickets'] != '-1') { - $admin_update_query.= ", `tickets_used` = `tickets_used` "; - - if ($tickets != '-1') { - $admin_update_query.= " + 0" . (int)$tickets . " "; - } - if ($result['tickets'] != '-1') { - $admin_update_query.= " - 0" . (int)$result['tickets'] . " "; - } - } - - if (($diskspace / 1024) != '-1' || ($result['diskspace'] / 1024) != '-1') { - $admin_update_query.= ", `diskspace_used` = `diskspace_used` "; - - if (($diskspace / 1024) != '-1') { - $admin_update_query.= " + 0" . (int)$diskspace . " "; - } - if (($result['diskspace'] / 1024) != '-1') { - $admin_update_query.= " - 0" . (int)$result['diskspace'] . " "; - } - } - - $admin_update_query.= " WHERE `adminid` = '" . (int)$result['adminid'] . "'"; - Database::query($admin_update_query); - $log->logAction(ADM_ACTION, LOG_INFO, "edited user '" . $result['loginname'] . "'"); - - /* - * move customer to another admin/reseller; #1166 - */ - if ($move_to_admin > 0 && $move_to_admin != $result['adminid']) { - $move_result = moveCustomerToAdmin($id, $move_to_admin); - if ($move_result != true) { - standard_error('moveofcustomerfailed', $move_result); - } - } - - $redirect_props = Array( - 'page' => $page, - 's' => $s - ); - - redirectTo($filename, $redirect_props); - } - + redirectTo($filename, array( + 'page' => $page, + 's' => $s + )); } else { $language_options = ''; diff --git a/lib/classes/api/abstract.ApiCommand.php b/lib/classes/api/abstract.ApiCommand.php index d45808be..5a749e63 100644 --- a/lib/classes/api/abstract.ApiCommand.php +++ b/lib/classes/api/abstract.ApiCommand.php @@ -184,6 +184,16 @@ abstract class ApiCommand return $this->cmd_params[$param]; } + protected function getUlParam($param = null, $ul_field = null, $optional = false, $default = 0) + { + $param_value = intval_ressource($this->getParam($param, $optional, $default)); + $ul_field_value = $this->getParam($ul_field, true, 0); + if ($ul_field_value != 0) { + $param_value = - 1; + } + return $param_value; + } + /** * returns "module::function()" for better error-messages (missing parameter etc.) * makes debugging a whole lot more comfortable diff --git a/lib/classes/api/commands/class.Customers.php b/lib/classes/api/commands/class.Customers.php index 820703eb..9f7ed7c4 100644 --- a/lib/classes/api/commands/class.Customers.php +++ b/lib/classes/api/commands/class.Customers.php @@ -1,5 +1,19 @@ (2010-) + * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt + * @package Panel + * + */ class Customers extends ApiCommand implements ResourceEntity { @@ -40,8 +54,9 @@ class Customers extends ApiCommand implements ResourceEntity /** * return a customer entry by id * - * @param int $id customer-id - * + * @param int $id + * customer-id + * * @throws Exception * @return array */ @@ -75,91 +90,72 @@ class Customers extends ApiCommand implements ResourceEntity if ($this->isAdmin()) { if ($this->getUserDetail('customers_used') < $this->getUserDetail('customers') || $this->getUserDetail('customers') == '-1') { - $idna_convert = new idna_convert_wrapper(); - $name = validate($this->getParam('name', true, ''), 'name', '', '', array(), true); - $firstname = validate($this->getParam('firstname', true, ''), 'first name', '', '', array(), true); + // required parameters + $email = $this->getParam('email'); + + // parameters + $name = $this->getParam('name', true, ''); + $firstname = $this->getParam('firstname', true, ''); $company_required = (empty($name) && empty($first)); - $company = validate($this->getParam('company', $company_required, ''), 'company', '', '', array(), true); - $street = validate($this->getParam('street', true, ''), 'street', '', '', array(), true); - $zipcode = validate($this->getParam('zipcode', true, ''), 'zipcode', '/^[0-9 \-A-Z]*$/', '', array(), true); - $city = validate($this->getParam('city', true, ''), 'city', '', '', array(), true); - $phone = validate($this->getParam('phone', true, ''), 'phone', '/^[0-9\- \+\(\)\/]*$/', '', array(), true); - $fax = validate($this->getParam('fax', true, ''), 'fax', '/^[0-9\- \+\(\)\/]*$/', '', array(), true); - $email = $idna_convert->encode(validate($this->getParam('email'), 'email', '', '', array(), true)); - $customernumber = validate($this->getParam('customernumber', true, ''), 'customer number', '/^[A-Za-z0-9 \-]*$/Di', '', array(), true); - $def_language = validate($this->getParam('def_language', true, ''), 'default language', '', '', array(), true); + $company = $this->getParam('company', $company_required, ''); + $street = $this->getParam('street', true, ''); + $zipcode = $this->getParam('zipcode', true, ''); + $city = $this->getParam('city', true, ''); + $phone = $this->getParam('phone', true, ''); + $fax = $this->getParam('fax', true, ''); + $customernumber = $this->getParam('customernumber', true, ''); + $def_language = $this->getParam('def_language', true, ''); $gender = intval_ressource($this->getParam('gender', true, 0)); - - $custom_notes = validate(str_replace("\r\n", "\n", $this->getParam('custom_notes', true, '')), 'custom_notes', '/^[^\0]*$/', '', array(), true); + $custom_notes = $this->getParam('custom_notes', true, ''); $custom_notes_show = $this->getParam('custom_notes_show', true, 0); - $diskspace = intval_ressource($this->getParam('diskspace', true, 0)); - if ($this->getParam('diskspace_ul', true, 0) == -1) { - $diskspace = - 1; - } + $diskspace = $this->getUlParam('diskspace', 'diskspace_ul', true, 0); + $traffic = $this->getUlParam('traffic', 'traffic_ul', true, 0); + $subdomains = $this->getUlParam('subdomains', 'subdomains_ul', true, 0); + $emails = $this->getUlParam('emails', 'emails_ul', true, 0); + $email_accounts = $this->getUlParam('email_accounts', 'email_accounts_ul', true, 0); + $email_forwarders = $this->getUlParam('email_forwarders', 'email_forwarders_ul', true, 0); + $email_quota = $this->getUlParam('email_quota', 'email_quota_ul', true, 0); + $email_imap = $this->getParam('email_imap', true, 0); + $email_pop3 = $this->getParam('email_pop3', true, 0); + $ftps = $this->getUlParam('ftps', 'ftps_ul', true, 0); + $tickets = $this->getUlParam('tickets', 'tickets_ul', true, 0); + $mysqls = $this->getUlParam('mysqls', 'mysqls_ul', true, 0); + $createstdsubdomain = $this->getParam('createstdsubdomain', true, 0); + $password = $this->getParam('new_customer_password', true, ''); + $sendpassword = $this->getParam('sendpassword', true, 0); + $phpenabled = $this->getParam('phpenabled', true, 0); + $p_allowed_phpconfigs = $this->getParam('allowed_phpconfigs', true, array()); + $perlenabled = $this->getParam('perlenabled', true, 0); + $dnsenabled = $this->getParam('dnsenabled', true, 0); + $store_defaultindex = $this->getParam('store_defaultindex', true, 0); + $loginname = $this->getParam('new_loginname', true, ''); - $traffic = doubleval_ressource($this->getParam('traffic', true, 0)); - if ($this->getParam('traffic_ul', true, 0) == -1) { - $traffic = - 1; - } + // validation + $idna_convert = new idna_convert_wrapper(); + $name = validate($name, 'name', '', '', array(), true); + $firstname = validate($firstname, 'first name', '', '', array(), true); + $company = validate($company, 'company', '', '', array(), true); + $street = validate($street, 'street', '', '', array(), true); + $zipcode = validate($zipcode, 'zipcode', '/^[0-9 \-A-Z]*$/', '', array(), true); + $city = validate($city, 'city', '', '', array(), true); + $phone = validate($phone, 'phone', '/^[0-9\- \+\(\)\/]*$/', '', array(), true); + $fax = validate(fax, 'fax', '/^[0-9\- \+\(\)\/]*$/', '', array(), true); + $idna_convert = new idna_convert_wrapper(); + $email = $idna_convert->encode(validate($email, 'email', '', '', array(), true)); + $customernumber = validate($customernumber, 'customer number', '/^[A-Za-z0-9 \-]*$/Di', '', array(), true); + $def_language = validate($def_language, 'default language', '', '', array(), true); + $custom_notes = validate(str_replace("\r\n", "\n", $custom_notes), 'custom_notes', '/^[^\0]*$/', '', array(), true); - $subdomains = intval_ressource($this->getParam('subdomains', true, 0)); - if ($this->getParam('subdomains_ul', true, 0) == -1) { - $subdomains = - 1; - } - - $emails = intval_ressource($this->getParam('emails', true, 0)); - if ($this->getParam('emails_ul', true, 0) == -1) { - $emails = - 1; - } - - $email_accounts = intval_ressource($this->getParam('email_accounts', true, 0)); - if ($this->getParam('email_accounts_ul', true, 0) == -1) { - $email_accounts = - 1; - } - - $email_forwarders = intval_ressource($this->getParam('email_forwarders', true, 0)); - if ($this->getParam('email_forwarders_ul', true, 0) == -1) { - $email_forwarders = - 1; - } - - if (Settings::Get('system.mail_quota_enabled') == '1') { - $email_quota = validate($this->getParam('email_quota', true, 0), 'email_quota', '/^\d+$/', 'vmailquotawrong', array( - '0', - '' - ), true); - if ($this->getParam('email_quota_ul', true, 0) == -1) { - $email_quota = - 1; - } - } else { + if (Settings::Get('system.mail_quota_enabled') != '1') { $email_quota = - 1; } - $email_imap = $this->getParam('email_imap', true, 0); - $email_pop3 = $this->getParam('email_pop3', true, 0); - - $ftps = intval_ressource($this->getParam('ftps', true, 0)); - if ($this->getParam('ftps_ul', true, 0) == -1) { - $ftps = - 1; - } - - if (Settings::Get('ticket.enabled') == '1') { - $tickets = intval_ressource($this->getParam('tickets', true, 0)); - if ($this->getParam('tickets_ul', true, 0) == -1) { - $tickets = - 1; - } - } else { + if (Settings::Get('ticket.enabled') != '1') { $tickets = - 1; } - $mysqls = intval_ressource($this->getParam('mysqls', true, 0)); - if ($this->getParam('mysqls_ul', true, 0) == -1) { - $mysqls = - 1; - } - - $createstdsubdomain = $this->getParam('createstdsubdomain', true, 0); - - $password = validate($this->getParam('new_customer_password', true, ''), 'password', '', '', array(), true); + $password = validate($password, 'password', '', '', array(), true); // only check if not empty, // cause empty == generate password automatically if ($password != '') { @@ -171,21 +167,14 @@ class Customers extends ApiCommand implements ResourceEntity $gender = 0; } - $sendpassword = $this->getParam('sendpassword', true, 0); - $phpenabled = $this->getParam('phpenabled', true, 0); - $allowed_phpconfigs = array(); - if (! empty($this->getParam('allowed_phpconfigs', true, array())) && is_array($this->getParam('allowed_phpconfigs', true, array()))) { - foreach ($this->getParam('allowed_phpconfigs', true, array()) as $allowed_phpconfig) { + if (! empty($p_allowed_phpconfigs) && is_array($p_allowed_phpconfigs)) { + foreach ($p_allowed_phpconfigs as $allowed_phpconfig) { $allowed_phpconfig = intval($allowed_phpconfig); $allowed_phpconfigs[] = $allowed_phpconfig; } } - $perlenabled = $this->getParam('perlenabled', true, 0); - $dnsenabled = $this->getParam('dnsenabled', true, 0); - $store_defaultindex = $this->getParam('store_defaultindex', true, 0); - $diskspace = $diskspace * 1024; $traffic = $traffic * 1024 * 1024; @@ -213,9 +202,9 @@ class Customers extends ApiCommand implements ResourceEntity standard_error('emailiswrong', $email, true); } else { - if ($this->getParam('new_loginname', true, '') != '') { + if ($loginname != '') { $accountnumber = intval(Settings::Get('system.lastaccountnumber')); - $loginname = validate($this->getParam('new_loginname'), 'loginname', '/^[a-z][a-z0-9\-_]+$/i', '', array(), true); + $loginname = validate($loginname, 'loginname', '/^[a-z][a-z0-9\-_]+$/i', '', array(), true); // Accounts which match systemaccounts are not allowed, filtering them if (preg_match('/^' . preg_quote(Settings::Get('customer.accountprefix'), '/') . '([0-9]+)/', $loginname)) { @@ -567,7 +556,7 @@ class Customers extends ApiCommand implements ResourceEntity $srv_hostname = Settings::Get('system.hostname'); if (Settings::Get('system.froxlordirectlyviahostname') == '0') { - $srv_hostname .= '/'.basename(FROXLOR_INSTALL_DIR); + $srv_hostname .= '/' . basename(FROXLOR_INSTALL_DIR); } $srv_ip_stmt = Database::prepare(" @@ -663,8 +652,466 @@ class Customers extends ApiCommand implements ResourceEntity ))->get(); $result = json_decode($json_result, true)['data']; - $this->logger()->logAction(ADM_ACTION, LOG_WARNING, "[API] changed customer '" . $result['loginname'] . "'"); - return $this->response(200, "successfull", $upd_data); + // parameters + $move_to_admin = intval_ressource($this->getParam('move_to_admin', true, 0)); + + $idna_convert = new idna_convert_wrapper(); + $email = $this->getParam('email', true, $idna_convert->decode($result['email'])); + $name = $this->getParam('name', true, $result['name']); + $firstname = $this->getParam('firstname', true, $result['firstname']); + $company_required = (empty($name) && empty($first)); + $company = $this->getParam('company', $company_required, $result['company']); + $street = $this->getParam('street', true, $result['street']); + $zipcode = $this->getParam('zipcode', true, $result['zipcode']); + $city = $this->getParam('city', true, $result['city']); + $phone = $this->getParam('phone', true, $result['phone']); + $fax = $this->getParam('fax', true, $result['fax']); + $customernumber = $this->getParam('customernumber', true, $result['customernumber']); + $def_language = $this->getParam('def_language', true, $result['def_language']); + $gender = intval_ressource($this->getParam('gender', true, $result['gender'])); + $custom_notes = $this->getParam('custom_notes', true, $result['custom_notes']); + $custom_notes_show = $this->getParam('custom_notes_show', true, $result['custom_notes_show']); + + $dec_places = Settings::Get('panel.decimal_places'); + $diskspace = $this->getUlParam('diskspace', 'diskspace_ul', true, round($result['diskspace'] / 1024, $dec_places)); + $traffic = $this->getUlParam('traffic', 'traffic_ul', true, round($result['traffic'] / (1024 * 1024), $dec_places)); + $subdomains = $this->getUlParam('subdomains', 'subdomains_ul', true, $result['subdomains']); + $emails = $this->getUlParam('emails', 'emails_ul', true, $result['emails']); + $email_accounts = $this->getUlParam('email_accounts', 'email_accounts_ul', true, $result['email_accounts']); + $email_forwarders = $this->getUlParam('email_forwarders', 'email_forwarders_ul', true, $result['email_forwarders']); + $email_quota = $this->getUlParam('email_quota', 'email_quota_ul', true, $result['email_quota']); + $email_imap = $this->getParam('email_imap', true, $result['imap']); + $email_pop3 = $this->getParam('email_pop3', true, $result['pop3']); + $ftps = $this->getUlParam('ftps', 'ftps_ul', true, $result['ftps']); + $tickets = $this->getUlParam('tickets', 'tickets_ul', true, $result['tickets']); + $mysqls = $this->getUlParam('mysqls', 'mysqls_ul', true, $result['mysqls']); + $createstdsubdomain = $this->getParam('createstdsubdomain', true, 0); + $password = $this->getParam('new_customer_password', true, ''); + $sendpassword = $this->getParam('sendpassword', true, 0); + $phpenabled = $this->getParam('phpenabled', true, $result['phpenabled']); + $allowed_phpconfigs = $this->getParam('allowed_phpconfigs', true, json_decode($result['allowed_phpconfigs'], true)); + $perlenabled = $this->getParam('perlenabled', true, $result['perlenabled']); + $dnsenabled = $this->getParam('dnsenabled', true, $result['dnsenabled']); + $deactivated = $this->getParam('deactivated', true, $result['deactivated']); + + // validation + $idna_convert = new idna_convert_wrapper(); + $name = validate($name, 'name', '', '', array(), true); + $firstname = validate($firstname, 'first name', '', '', array(), true); + $company = validate($company, 'company', '', '', array(), true); + $street = validate($street, 'street', '', '', array(), true); + $zipcode = validate($zipcode, 'zipcode', '/^[0-9 \-A-Z]*$/', '', array(), true); + $city = validate($city, 'city', '', '', array(), true); + $phone = validate($phone, 'phone', '/^[0-9\- \+\(\)\/]*$/', '', array(), true); + $fax = validate(fax, 'fax', '/^[0-9\- \+\(\)\/]*$/', '', array(), true); + $email = $idna_convert->encode(validate($email, 'email', '', '', array(), true)); + $customernumber = validate($customernumber, 'customer number', '/^[A-Za-z0-9 \-]*$/Di', '', array(), true); + $def_language = validate($def_language, 'default language', '', '', array(), true); + $custom_notes = validate(str_replace("\r\n", "\n", $custom_notes), 'custom_notes', '/^[^\0]*$/', '', array(), true); + + if (Settings::Get('system.mail_quota_enabled') != '1') { + $email_quota = - 1; + } + + if (Settings::Get('ticket.enabled') != '1') { + $tickets = - 1; + } + + // Either $name and $firstname or the $company must be inserted + if ($name == '' && $company == '') { + standard_error(array( + 'stringisempty', + 'myname' + )); + } elseif ($firstname == '' && $company == '') { + standard_error(array( + 'stringisempty', + 'myfirstname' + )); + } elseif ($email == '') { + standard_error(array( + 'stringisempty', + 'emailadd' + )); + } elseif (! validateEmail($email)) { + standard_error('emailiswrong', $email); + } else { + + if ($password != '') { + $password = validatePassword($password); + $password = makeCryptPassword($password); + } else { + $password = $result['password']; + } + + if ($createstdsubdomain != '1') { + $createstdsubdomain = '0'; + } + + if ($createstdsubdomain == '1' && $result['standardsubdomain'] == '0') { + + if (Settings::Get('system.stdsubdomain') !== null && Settings::Get('system.stdsubdomain') != '') { + $_stdsubdomain = $result['loginname'] . '.' . Settings::Get('system.stdsubdomain'); + } else { + $_stdsubdomain = $result['loginname'] . '.' . Settings::Get('system.hostname'); + } + + $ins_data = array( + 'domain' => $_stdsubdomain, + 'customerid' => $result['customerid'], + 'adminid' => $this->getUserDetail('adminid'), + 'parentdomainid' => '0', + 'docroot' => $result['documentroot'], + 'adddate' => time(), + 'phpenabled' => $phpenabled, + 'zonefile' => '', + 'isemaildomain' => '0', + 'caneditdomain' => '0', + 'openbasedir' => '1', + 'speciallogfile' => '0', + 'dkim_id' => '0', + 'dkim_privkey' => '', + 'dkim_pubkey' => '', + 'ipandport' => explode(',', Settings::Get('system.defaultip')) + ); + $domainid = - 1; + try { + $std_domain = Domains::getLocal($this->getUserData(), $ins_data)->add(); + $domainid = json_decode($std_domain, true)['data']['id']; + } catch (Exception $e) { + $this->logger()->logAction(ADM_ACTION, LOG_ERR, "[API] Unable to add standard-subdomain: " . $e->getMessage()); + } + + if ($domainid > 0) { + $upd_stmt = Database::prepare(" + UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET `standardsubdomain` = :domainid WHERE `customerid` = :customerid + "); + Database::pexecute($upd_stmt, array( + 'domainid' => $domainid, + 'customerid' => $result['customerid'] + ), true, true); + $this->logger()->logAction(ADM_ACTION, LOG_NOTICE, "[API] automatically added standardsubdomain for user '" . $result['loginname'] . "'"); + inserttask('1'); + } + } + + if ($createstdsubdomain == '0' && $result['standardsubdomain'] != '0') { + + try { + $std_domain = Domains::getLocal($this->getUserData(), array( + 'id' => $result['standardsubdomain'], + 'is_stdsubdomain' => 1 + ))->delete(); + } catch (Exception $e) { + $this->logger()->logAction(ADM_ACTION, LOG_ERR, "[API] Unable to delete standard-subdomain: " . $e->getMessage()); + } + $log->logAction(ADM_ACTION, LOG_NOTICE, "automatically deleted standardsubdomain for user '" . $result['loginname'] . "'"); + inserttask('1'); + } + + if ($deactivated != '1') { + $deactivated = '0'; + } + + if ($phpenabled != '0') { + $phpenabled = '1'; + } + + if ($perlenabled != '0') { + $perlenabled = '1'; + } + + if ($dnsenabled != '0') { + $dnsenabled = '1'; + } + + if ($phpenabled != $result['phpenabled'] || $perlenabled != $result['perlenabled']) { + inserttask('1'); + } + + // activate/deactivate customer services + if ($deactivated != $result['deactivated']) { + + $yesno = (($deactivated) ? 'N' : 'Y'); + $pop3 = (($deactivated) ? '0' : (int) $result['pop3']); + $imap = (($deactivated) ? '0' : (int) $result['imap']); + + $upd_stmt = Database::prepare(" + UPDATE `" . TABLE_MAIL_USERS . "` SET `postfix`= :yesno, `pop3` = :pop3, `imap` = :imap WHERE `customerid` = :customerid"); + Database::pexecute($upd_stmt, array( + 'yesno' => $yesno, + 'pop3' => $pop3, + 'imap' => $imap, + 'customerid' => $id + )); + + $upd_stmt = Database::prepare(" + UPDATE `" . TABLE_FTP_USERS . "` SET `login_enabled` = :yesno WHERE `customerid` = :customerid"); + Database::pexecute($upd_stmt, array( + 'yesno' => $yesno, + 'customerid' => $id + )); + + $upd_stmt = Database::prepare(" + UPDATE `" . TABLE_PANEL_DOMAINS . "` SET `deactivated`= :deactivated WHERE `customerid` = :customerid"); + Database::pexecute($upd_stmt, array( + 'deactivated' => $deactivated, + 'customerid' => $id + )); + + // Retrieve customer's databases + $databases_stmt = Database::prepare("SELECT * FROM " . TABLE_PANEL_DATABASES . " WHERE customerid = :customerid ORDER BY `dbserver`"); + Database::pexecute($databases_stmt, array( + 'customerid' => $id + )); + + Database::needRoot(true); + $last_dbserver = 0; + + $dbm = new DbManager($log); + + // For each of them + while ($row_database = $databases_stmt->fetch(PDO::FETCH_ASSOC)) { + + if ($last_dbserver != $row_database['dbserver']) { + $dbm->getManager()->flushPrivileges(); + Database::needRoot(true, $row_database['dbserver']); + $last_dbserver = $row_database['dbserver']; + } + + foreach (array_unique(explode(',', Settings::Get('system.mysql_access_host'))) as $mysql_access_host) { + $mysql_access_host = trim($mysql_access_host); + + // Prevent access, if deactivated + if ($deactivated) { + // failsafe if user has been deleted manually (requires MySQL 4.1.2+) + $dbm->getManager()->disableUser($row_database['databasename'], $mysql_access_host); + } else { + // Otherwise grant access + $dbm->getManager()->enableUser($row_database['databasename'], $mysql_access_host); + } + } + } + + // At last flush the new privileges + $dbm->getManager()->flushPrivileges(); + Database::needRoot(false); + + $log->logAction(ADM_ACTION, LOG_INFO, "deactivated user '" . $result['loginname'] . "'"); + inserttask('1'); + } + + // Disable or enable POP3 Login for customers Mail Accounts + if ($email_pop3 != $result['pop3']) { + $upd_stmt = Database::prepare("UPDATE `" . TABLE_MAIL_USERS . "` SET `pop3` = :pop3 WHERE `customerid` = :customerid"); + Database::pexecute($upd_stmt, array( + 'pop3' => $email_pop3, + 'customerid' => $id + )); + } + + // Disable or enable IMAP Login for customers Mail Accounts + if ($email_imap != $result['imap']) { + $upd_stmt = Database::prepare("UPDATE `" . TABLE_MAIL_USERS . "` SET `imap` = :imap WHERE `customerid` = :customerid"); + Database::pexecute($upd_stmt, array( + 'imap' => $email_imap, + 'customerid' => $id + )); + } + + $upd_data = array( + 'customerid' => $id, + 'passwd' => $password, + 'name' => $name, + 'firstname' => $firstname, + 'gender' => $gender, + 'company' => $company, + 'street' => $street, + 'zipcode' => $zipcode, + 'city' => $city, + 'phone' => $phone, + 'fax' => $fax, + 'email' => $email, + 'customerno' => $customernumber, + 'lang' => $def_language, + 'diskspace' => $diskspace, + 'traffic' => $traffic, + 'subdomains' => $subdomains, + 'emails' => $emails, + 'email_accounts' => $email_accounts, + 'email_forwarders' => $email_forwarders, + 'email_quota' => $email_quota, + 'ftps' => $ftps, + 'tickets' => $tickets, + 'mysqls' => $mysqls, + 'deactivated' => $deactivated, + 'phpenabled' => $phpenabled, + 'allowed_phpconfigs' => empty($allowed_phpconfigs) ? "" : json_encode($allowed_phpconfigs), + 'imap' => $email_imap, + 'pop3' => $email_pop3, + 'perlenabled' => $perlenabled, + 'dnsenabled' => $dnsenabled, + 'custom_notes' => $custom_notes, + 'custom_notes_show' => $custom_notes_show + ); + $upd_stmt = Database::prepare(" + UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET + `name` = :name, + `firstname` = :firstname, + `gender` = :gender, + `company` = :company, + `street` = :street, + `zipcode` = :zipcode, + `city` = :city, + `phone` = :phone, + `fax` = :fax, + `email` = :email, + `customernumber` = :customerno, + `def_language` = :lang, + `password` = :passwd, + `diskspace` = :diskspace, + `traffic` = :traffic, + `subdomains` = :subdomains, + `emails` = :emails, + `email_accounts` = :email_accounts, + `email_forwarders` = :email_forwarders, + `ftps` = :ftps, + `tickets` = :tickets, + `mysqls` = :mysqls, + `deactivated` = :deactivated, + `phpenabled` = :phpenabled, + `allowed_phpconfigs` = :allowed_phpconfigs, + `email_quota` = :email_quota, + `imap` = :imap, + `pop3` = :pop3, + `perlenabled` = :perlenabled, + `dnsenabled` = :dnsenabled, + `custom_notes` = :custom_notes, + `custom_notes_show` = :custom_notes_show + WHERE `customerid` = :customerid + "); + Database::pexecute($upd_stmt, $upd_data); + + // Using filesystem - quota, insert a task which cleans the filesystem - quota + inserttask('10'); + + $admin_update_query = "UPDATE `" . TABLE_PANEL_ADMINS . "` SET `customers_used` = `customers_used` "; + + if ($mysqls != '-1' || $result['mysqls'] != '-1') { + $admin_update_query .= ", `mysqls_used` = `mysqls_used` "; + + if ($mysqls != '-1') { + $admin_update_query .= " + 0" . (int) $mysqls . " "; + } + if ($result['mysqls'] != '-1') { + $admin_update_query .= " - 0" . (int) $result['mysqls'] . " "; + } + } + + if ($emails != '-1' || $result['emails'] != '-1') { + $admin_update_query .= ", `emails_used` = `emails_used` "; + + if ($emails != '-1') { + $admin_update_query .= " + 0" . (int) $emails . " "; + } + if ($result['emails'] != '-1') { + $admin_update_query .= " - 0" . (int) $result['emails'] . " "; + } + } + + if ($email_accounts != '-1' || $result['email_accounts'] != '-1') { + $admin_update_query .= ", `email_accounts_used` = `email_accounts_used` "; + + if ($email_accounts != '-1') { + $admin_update_query .= " + 0" . (int) $email_accounts . " "; + } + if ($result['email_accounts'] != '-1') { + $admin_update_query .= " - 0" . (int) $result['email_accounts'] . " "; + } + } + + if ($email_forwarders != '-1' || $result['email_forwarders'] != '-1') { + $admin_update_query .= ", `email_forwarders_used` = `email_forwarders_used` "; + + if ($email_forwarders != '-1') { + $admin_update_query .= " + 0" . (int) $email_forwarders . " "; + } + if ($result['email_forwarders'] != '-1') { + $admin_update_query .= " - 0" . (int) $result['email_forwarders'] . " "; + } + } + + if ($email_quota != '-1' || $result['email_quota'] != '-1') { + $admin_update_query .= ", `email_quota_used` = `email_quota_used` "; + + if ($email_quota != '-1') { + $admin_update_query .= " + 0" . (int) $email_quota . " "; + } + if ($result['email_quota'] != '-1') { + $admin_update_query .= " - 0" . (int) $result['email_quota'] . " "; + } + } + + if ($subdomains != '-1' || $result['subdomains'] != '-1') { + $admin_update_query .= ", `subdomains_used` = `subdomains_used` "; + + if ($subdomains != '-1') { + $admin_update_query .= " + 0" . (int) $subdomains . " "; + } + if ($result['subdomains'] != '-1') { + $admin_update_query .= " - 0" . (int) $result['subdomains'] . " "; + } + } + + if ($ftps != '-1' || $result['ftps'] != '-1') { + $admin_update_query .= ", `ftps_used` = `ftps_used` "; + + if ($ftps != '-1') { + $admin_update_query .= " + 0" . (int) $ftps . " "; + } + if ($result['ftps'] != '-1') { + $admin_update_query .= " - 0" . (int) $result['ftps'] . " "; + } + } + + if ($tickets != '-1' || $result['tickets'] != '-1') { + $admin_update_query .= ", `tickets_used` = `tickets_used` "; + + if ($tickets != '-1') { + $admin_update_query .= " + 0" . (int) $tickets . " "; + } + if ($result['tickets'] != '-1') { + $admin_update_query .= " - 0" . (int) $result['tickets'] . " "; + } + } + + if (($diskspace / 1024) != '-1' || ($result['diskspace'] / 1024) != '-1') { + $admin_update_query .= ", `diskspace_used` = `diskspace_used` "; + + if (($diskspace / 1024) != '-1') { + $admin_update_query .= " + 0" . (int) $diskspace . " "; + } + if (($result['diskspace'] / 1024) != '-1') { + $admin_update_query .= " - 0" . (int) $result['diskspace'] . " "; + } + } + + $admin_update_query .= " WHERE `adminid` = '" . (int) $result['adminid'] . "'"; + Database::query($admin_update_query); + $this->logger()->logAction(ADM_ACTION, LOG_INFO, "[API] edited user '" . $result['loginname'] . "'"); + + /* + * move customer to another admin/reseller; #1166 + */ + if ($move_to_admin > 0 && $move_to_admin != $result['adminid']) { + $move_result = moveCustomerToAdmin($id, $move_to_admin); + if ($move_result != true) { + standard_error('moveofcustomerfailed', $move_result, true); + } + } + + return $this->response(200, "successfull", $upd_data); + } } throw new Exception("Not allowed to execute given command.", 403); } @@ -672,9 +1119,11 @@ class Customers extends ApiCommand implements ResourceEntity /** * delete a customer entry by id * - * @param int $id customer-id - * @param bool $delete_userfiles optional, default false - * + * @param int $id + * customer-id + * @param bool $delete_userfiles + * optional, default false + * * @throws Exception * @return array */ @@ -907,8 +1356,9 @@ class Customers extends ApiCommand implements ResourceEntity /** * unlock a locked customer by id * - * @param int $id customer-id - * + * @param int $id + * customer-id + * * @throws Exception * @return array */ diff --git a/lib/classes/api/commands/class.Domains.php b/lib/classes/api/commands/class.Domains.php index 481a8ba2..c25b4d64 100644 --- a/lib/classes/api/commands/class.Domains.php +++ b/lib/classes/api/commands/class.Domains.php @@ -1,5 +1,19 @@ (2010-) + * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt + * @package Panel + * + */ class Domains extends ApiCommand implements ResourceEntity { @@ -1525,7 +1539,10 @@ class Domains extends ApiCommand implements ResourceEntity * * @param int $id * domain-id - * @param bool $delete_mainsubdomains optional, remove also domains that are subdomains of this domain but added as main domains; default false + * @param bool $delete_mainsubdomains + * optional, remove also domains that are subdomains of this domain but added as main domains; default false + * @param bool $is_stdsubdomain + * optional, default false, specify whether it's a std-subdomain you are deleting as it does not count as subdomain-resource * * @throws Exception * @return array @@ -1534,11 +1551,11 @@ class Domains extends ApiCommand implements ResourceEntity { if ($this->isAdmin()) { $id = $this->getParam('id'); + $is_stdsubdomain = $this->getParam('is_stdsubdomain', true, 0); $remove_subbutmain_domains = $this->getParam('delete_mainsubdomains', true, 0); - + $json_result = Domains::getLocal($this->getUserData(), array( - 'id' => $id, - 'no_std_subdomain' => true + 'id' => $id ))->get(); $result = json_decode($json_result, true)['data']; @@ -1547,7 +1564,7 @@ class Domains extends ApiCommand implements ResourceEntity if ($remove_subbutmain_domains) { $rsd_sql .= " OR `ismainbutsubto` = :id"; } - + $subresult_stmt = Database::prepare(" SELECT `id` FROM `" . TABLE_PANEL_DOMAINS . "` WHERE (`id` = :id OR `parentdomainid` = :id " . $rsd_sql . ")"); @@ -1595,22 +1612,24 @@ class Domains extends ApiCommand implements ResourceEntity $deleted_domains = $del_stmt->rowCount(); - $upd_stmt = Database::prepare(" - UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET - `subdomains_used` = `subdomains_used` - :domaincount - WHERE `customerid` = :customerid"); - Database::pexecute($upd_stmt, array( - 'domaincount' => ($deleted_domains - 1), - 'customerid' => $result['customerid'] - ), true, true); - - $upd_stmt = Database::prepare(" - UPDATE `" . TABLE_PANEL_ADMINS . "` SET - `domains_used` = `domains_used` - 1 - WHERE `adminid` = :adminid"); - Database::pexecute($upd_stmt, array( - 'adminid' => $this->getUserDetail('adminid') - ), true, true); + if ($is_stdsubdomain == 0) { + $upd_stmt = Database::prepare(" + UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET + `subdomains_used` = `subdomains_used` - :domaincount + WHERE `customerid` = :customerid"); + Database::pexecute($upd_stmt, array( + 'domaincount' => ($deleted_domains - 1), + 'customerid' => $result['customerid'] + ), true, true); + + $upd_stmt = Database::prepare(" + UPDATE `" . TABLE_PANEL_ADMINS . "` SET + `domains_used` = `domains_used` - 1 + WHERE `adminid` = :adminid"); + Database::pexecute($upd_stmt, array( + 'adminid' => $this->getUserDetail('adminid') + ), true, true); + } $upd_stmt = Database::prepare(" UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET diff --git a/lib/classes/api/commands/class.Froxlor.php b/lib/classes/api/commands/class.Froxlor.php index 01c316e5..4d6d1ea3 100644 --- a/lib/classes/api/commands/class.Froxlor.php +++ b/lib/classes/api/commands/class.Froxlor.php @@ -1,19 +1,91 @@ (2010-) + * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt + * @package Panel + * + */ class Froxlor extends ApiCommand { + public function checkUpdate() + { + global $version, $branding; + + if ($this->isAdmin() && $this->getUserDetail('change_serversettings')) { + // log our actions + $this->logger()->logAction(ADM_ACTION, LOG_NOTICE, "[API] checking for updates"); + + // check for new version + define('UPDATE_URI', "https://version.froxlor.org/Froxlor/api/" . $version); + $latestversion = HttpClient::urlGet(UPDATE_URI); + $latestversion = explode('|', $latestversion); + + if (is_array($latestversion) && count($latestversion) >= 1) { + $_version = $latestversion[0]; + $_message = isset($latestversion[1]) ? $latestversion[1] : ''; + $_link = isset($latestversion[2]) ? $latestversion[2] : ''; + + // add the branding so debian guys are not gettings confused + // about their version-number + $version_label = $_version . $branding; + $version_link = $_link; + $message_addinfo = $_message; + + // not numeric -> error-message + if (! preg_match('/^((\d+\\.)(\d+\\.)(\d+\\.)?(\d+)?(\-(svn|dev|rc)(\d+))?)$/', $_version)) { + // check for customized version to not output + // "There is a newer version of froxlor" besides the error-message + $isnewerversion = - 1; + } elseif (version_compare2($version, $_version) == - 1) { + // there is a newer version - yay + $isnewerversion = 1; + } else { + // nothing new + $isnewerversion = 0; + } + + // anzeige über version-status mit ggfls. formular + // zum update schritt #1 -> download + if ($isnewerversion == 1) { + $text = 'There is a newer version available: "' . $_version . '" (Your current version is: ' . $version . ')'; + return $this->response(200, "successfull", array( + 'message' => $text, + 'link' => $version_link, + 'additional_info' => $message_addinfo + )); + } elseif ($isnewerversion == 0) { + // all good + standard_success('noupdatesavail', '', array(), true); + } else { + standard_error('customized_version', '', true); + } + } + } + throw new Exception("Not allowed to execute given command.", 403); + } + /** * returns a list of all available api functions * - * @param string $module optional, return list of functions for a specific module + * @param string $module + * optional, return list of functions for a specific module * * @throws Exception * @return array */ public function listFunctions() { - $module = $this->getParam('module'); + $module = $this->getParam('module', true, ''); $functions = array(); if ($module != null) { @@ -87,7 +159,7 @@ class Froxlor extends ApiCommand private function _getParamListFromDoc($module = null, $function = null) { try { - // set the module to be in our namespace + // set the module $cls = new \ReflectionMethod($module, $function); $comment = $cls->getDocComment(); if ($comment == false) { @@ -95,6 +167,7 @@ class Froxlor extends ApiCommand 'head' => 'There is no comment-block for "' . $module . '.' . $function . '"' ); } + $clines = explode("\n", $comment); $result = array(); $result['params'] = array(); diff --git a/lib/classes/api/commands/class.IpsAndPorts.php b/lib/classes/api/commands/class.IpsAndPorts.php index 17b90d3f..89aeb596 100644 --- a/lib/classes/api/commands/class.IpsAndPorts.php +++ b/lib/classes/api/commands/class.IpsAndPorts.php @@ -1,5 +1,19 @@ (2010-) + * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt + * @package Panel + * + */ class IpsAndPorts extends ApiCommand implements ResourceEntity { @@ -31,8 +45,9 @@ class IpsAndPorts extends ApiCommand implements ResourceEntity /** * return an ip/port entry by id * - * @param int $id ip-port-id - * + * @param int $id + * ip-port-id + * * @throws Exception * @return array */ @@ -58,7 +73,7 @@ class IpsAndPorts extends ApiCommand implements ResourceEntity public function add() { if ($this->isAdmin() && $this->getUserDetail('change_serversettings')) { - + $ip = validate_ip2($this->getParam('ip'), false, 'invalidip', false, false, false, true); $port = 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', @@ -203,7 +218,7 @@ class IpsAndPorts extends ApiCommand implements ResourceEntity $vhostcontainer = $this->getParam('vhostcontainer', true, $result['vhostcontainer']); $specialsettings = validate(str_replace("\r\n", "\n", $this->getParam('specialsettings', true, $result['specialsettings'])), 'specialsettings', '/^[^\0]*$/', '', array(), true); $vhostcontainer_servername_statement = $this->getParam('vhostcontainer_servername_statement', true, $result['vhostcontainer_servername_statement']); - $default_vhostconf_domain = validate(str_replace("\r\n", "\n", $this->getParam('default_vhostconf_domain',true, $result['default_vhostconf_domain'])), 'default_vhostconf_domain', '/^[^\0]*$/', '', array(), true); + $default_vhostconf_domain = validate(str_replace("\r\n", "\n", $this->getParam('default_vhostconf_domain', true, $result['default_vhostconf_domain'])), 'default_vhostconf_domain', '/^[^\0]*$/', '', array(), true); $docroot = validate($this->getParam('docroot', true, $result['docroot']), 'docroot', '', '', array(), true); if ((int) Settings::Get('system.use_ssl') == 1) { @@ -331,8 +346,9 @@ class IpsAndPorts extends ApiCommand implements ResourceEntity /** * delete an ip/port entry by id * - * @param int $id ip-port-id - * + * @param int $id + * ip-port-id + * * @throws Exception * @return array */ diff --git a/lib/functions/output/function.standard_success.php b/lib/functions/output/function.standard_success.php index ae996b25..1f319fb8 100644 --- a/lib/functions/output/function.standard_success.php +++ b/lib/functions/output/function.standard_success.php @@ -20,38 +20,39 @@ /** * Prints one ore more errormessages on screen * - * @param array Errormessages - * @param string A %s in the errormessage will be replaced by this string. + * @param + * array Errormessages + * @param + * string A %s in the errormessage will be replaced by this string. * @author Florian Lippert */ - -function standard_success($success_message = '', $replacer = '', $params = array()) +function standard_success($success_message = '', $replacer = '', $params = array(), $throw_exception = false) { global $s, $header, $footer, $lng, $theme; - - if(isset($lng['success'][$success_message])) - { - $success_message = strtr($lng['success'][$success_message], array('%s' => htmlentities($replacer))); + + if (isset($lng['success'][$success_message])) { + $success_message = strtr($lng['success'][$success_message], array( + '%s' => htmlentities($replacer) + )); } - if(is_array($params) && isset($params['filename'])) - { + if ($throw_exception) { + throw new Exception(strip_tags($success_message), 200); + } + + if (is_array($params) && isset($params['filename'])) { $redirect_url = $params['filename'] . '?s=' . $s; unset($params['filename']); - foreach($params as $varname => $value) - { - if($value != '') - { + foreach ($params as $varname => $value) { + if ($value != '') { $redirect_url .= '&' . $varname . '=' . $value; } } - } - else - { + } else { $redirect_url = ''; } - + eval("echo \"" . getTemplate('misc/success', '1') . "\";"); - exit; + exit(); } From 5524ff7cae4bfd48397f0c89e077ffa2f6c05646 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 19 Feb 2018 11:36:01 +0100 Subject: [PATCH 0446/1335] fixes in Customers::update() Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/commands/class.Customers.php | 15 +++++++-------- lib/classes/database/class.DbManager.php | 4 ++-- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/lib/classes/api/commands/class.Customers.php b/lib/classes/api/commands/class.Customers.php index 9f7ed7c4..00a03997 100644 --- a/lib/classes/api/commands/class.Customers.php +++ b/lib/classes/api/commands/class.Customers.php @@ -140,7 +140,7 @@ class Customers extends ApiCommand implements ResourceEntity $zipcode = validate($zipcode, 'zipcode', '/^[0-9 \-A-Z]*$/', '', array(), true); $city = validate($city, 'city', '', '', array(), true); $phone = validate($phone, 'phone', '/^[0-9\- \+\(\)\/]*$/', '', array(), true); - $fax = validate(fax, 'fax', '/^[0-9\- \+\(\)\/]*$/', '', array(), true); + $fax = validate($fax, 'fax', '/^[0-9\- \+\(\)\/]*$/', '', array(), true); $idna_convert = new idna_convert_wrapper(); $email = $idna_convert->encode(validate($email, 'email', '', '', array(), true)); $customernumber = validate($customernumber, 'customer number', '/^[A-Za-z0-9 \-]*$/Di', '', array(), true); @@ -659,8 +659,7 @@ class Customers extends ApiCommand implements ResourceEntity $email = $this->getParam('email', true, $idna_convert->decode($result['email'])); $name = $this->getParam('name', true, $result['name']); $firstname = $this->getParam('firstname', true, $result['firstname']); - $company_required = (empty($name) && empty($first)); - $company = $this->getParam('company', $company_required, $result['company']); + $company = $this->getParam('company', true, $result['company']); $street = $this->getParam('street', true, $result['street']); $zipcode = $this->getParam('zipcode', true, $result['zipcode']); $city = $this->getParam('city', true, $result['city']); @@ -703,7 +702,7 @@ class Customers extends ApiCommand implements ResourceEntity $zipcode = validate($zipcode, 'zipcode', '/^[0-9 \-A-Z]*$/', '', array(), true); $city = validate($city, 'city', '', '', array(), true); $phone = validate($phone, 'phone', '/^[0-9\- \+\(\)\/]*$/', '', array(), true); - $fax = validate(fax, 'fax', '/^[0-9\- \+\(\)\/]*$/', '', array(), true); + $fax = validate($fax, 'fax', '/^[0-9\- \+\(\)\/]*$/', '', array(), true); $email = $idna_convert->encode(validate($email, 'email', '', '', array(), true)); $customernumber = validate($customernumber, 'customer number', '/^[A-Za-z0-9 \-]*$/Di', '', array(), true); $def_language = validate($def_language, 'default language', '', '', array(), true); @@ -805,7 +804,7 @@ class Customers extends ApiCommand implements ResourceEntity } catch (Exception $e) { $this->logger()->logAction(ADM_ACTION, LOG_ERR, "[API] Unable to delete standard-subdomain: " . $e->getMessage()); } - $log->logAction(ADM_ACTION, LOG_NOTICE, "automatically deleted standardsubdomain for user '" . $result['loginname'] . "'"); + $this->logger()->logAction(ADM_ACTION, LOG_NOTICE, "[API] automatically deleted standardsubdomain for user '" . $result['loginname'] . "'"); inserttask('1'); } @@ -868,7 +867,7 @@ class Customers extends ApiCommand implements ResourceEntity Database::needRoot(true); $last_dbserver = 0; - $dbm = new DbManager($log); + $dbm = new DbManager($this->logger()); // For each of them while ($row_database = $databases_stmt->fetch(PDO::FETCH_ASSOC)) { @@ -897,7 +896,7 @@ class Customers extends ApiCommand implements ResourceEntity $dbm->getManager()->flushPrivileges(); Database::needRoot(false); - $log->logAction(ADM_ACTION, LOG_INFO, "deactivated user '" . $result['loginname'] . "'"); + $this->logger()->logAction(ADM_ACTION, LOG_INFO, "[API] deactivated user '" . $result['loginname'] . "'"); inserttask('1'); } @@ -1149,7 +1148,7 @@ class Customers extends ApiCommand implements ResourceEntity Database::needRoot(true); $last_dbserver = 0; - $dbm = new DbManager($log); + $dbm = new DbManager($this->logger()); while ($row_database = $databases_stmt->fetch(PDO::FETCH_ASSOC)) { if ($last_dbserver != $row_database['dbserver']) { diff --git a/lib/classes/database/class.DbManager.php b/lib/classes/database/class.DbManager.php index 5e078026..91d0d0c5 100644 --- a/lib/classes/database/class.DbManager.php +++ b/lib/classes/database/class.DbManager.php @@ -49,7 +49,7 @@ class DbManager { * * @param FroxlorLogger $log */ - public function __construct(&$log = null) { + public function __construct($log = null) { $this->_log = $log; $this->_setManager(); } @@ -124,4 +124,4 @@ class DbManager { // TODO read different dbms from settings later $this->_manager = new DbManagerMySQL($this->_log); } -} \ No newline at end of file +} From 131efc544df95547d5cf5f94fd3a2e76c33c127f Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 19 Feb 2018 12:27:00 +0100 Subject: [PATCH 0447/1335] fix syntax error in lang file, fixes #522 Signed-off-by: Michael Kaufmann (d00p) --- lng/german.lng.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lng/german.lng.php b/lng/german.lng.php index 322ddb14..0924c1ac 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -1588,7 +1588,7 @@ $lng['integrity_check']['SubdomainLetsencrypt'] = 'Hauptdomains ohne zugewiesene $lng['admin']['mod_fcgid_umask']['title'] = 'Umask (Standard: 022)'; // Added for let's encrypt -$lng['admin']['letsencrypt']['title'] = 'SSL Zertifikat erstellen (Let\'s Encrypt'); +$lng['admin']['letsencrypt']['title'] = 'SSL Zertifikat erstellen (Let\'s Encrypt)'; $lng['admin']['letsencrypt']['description'] = 'Holt ein kostenloses Zertifikat von Let\'s Encrypt. Das Zertifikat wird automatisch erstellt und verlängert.
    ACHTUNG: Wenn Wildcards aktiviert sind, wird diese Option automatisch deaktiviert. Dieses Feature befindet sich noch im Test.'; $lng['customer']['letsencrypt']['title'] = 'Benutze Let\'s Encrypt'; $lng['customer']['letsencrypt']['description'] = 'Holt ein kostenloses Zertifikat von Let\'s Encrypt. Das Zertifikat wird automatisch erstellt und verlängert.
    ACHTUNG: Dieses Feature befindet sich noch im Test.'; From 8c6ae4f3a369ee64b6ad2b6552283c67388b4573 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 19 Feb 2018 13:36:15 +0100 Subject: [PATCH 0448/1335] add PhpSettings ApiCommand Signed-off-by: Michael Kaufmann (d00p) --- admin_phpsettings.php | 302 +++------------ .../api/commands/class.PhpSettings.php | 364 ++++++++++++++++++ 2 files changed, 417 insertions(+), 249 deletions(-) create mode 100644 lib/classes/api/commands/class.PhpSettings.php diff --git a/admin_phpsettings.php b/admin_phpsettings.php index 1d922b3f..12b6b853 100644 --- a/admin_phpsettings.php +++ b/admin_phpsettings.php @@ -29,73 +29,29 @@ if ($page == 'overview') { if ($action == '') { + try { + $json_result = PhpSettings::getLocal($userinfo)->list(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); + } + $result = json_decode($json_result, true)['data']; + $tablecontent = ''; $count = 0; - $result = Database::query(" - SELECT c.*, fd.description as fpmdesc - FROM `" . TABLE_PANEL_PHPCONFIGS . "` c - LEFT JOIN `" . TABLE_PANEL_FPMDAEMONS . "` fd ON fd.id = c.fpmsettingid - ORDER BY c.description ASC - "); - - while ($row = $result->fetch(PDO::FETCH_ASSOC)) { - - $domainresult = false; - $query_params = array( - 'id' => $row['id'] - ); - - $query = "SELECT * FROM `" . TABLE_PANEL_DOMAINS . "` - WHERE `phpsettingid` = :id - AND `parentdomainid` = '0'"; - - if ((int) $userinfo['domains_see_all'] == 0) { - $query .= " AND `adminid` = :adminid"; - $query_params['adminid'] = $userinfo['adminid']; - } - - if ((int) Settings::Get('panel.phpconfigs_hidestdsubdomain') == 1) { - $ssdids_res = Database::query(" - SELECT DISTINCT `standardsubdomain` FROM `" . TABLE_PANEL_CUSTOMERS . "` - WHERE `standardsubdomain` > 0 ORDER BY `standardsubdomain` ASC;"); - $ssdids = array(); - while ($ssd = $ssdids_res->fetch(PDO::FETCH_ASSOC)) { - $ssdids[] = $ssd['standardsubdomain']; + if (isset($result['count']) && $result['count'] > 0) { + foreach ($result['list'] as $row) { + if (isset($row['is_default']) && $row['is_default'] == true) { + $row['description'] = "" . $row['description'] . ""; } - if (count($ssdids) > 0) { - $query .= " AND `id` NOT IN (" . implode(', ', $ssdids) . ")"; + $domains = ""; + foreach ($row['domains'] as $configdomain) { + $domains .= $configdomain . "
    "; } + $count++; + eval("\$tablecontent.=\"" . getTemplate("phpconfig/overview_overview") . "\";"); } - - $domainresult_stmt = Database::prepare($query); - Database::pexecute($domainresult_stmt, $query_params); - - $domains = ''; - if (Database::num_rows() > 0) { - while ($row2 = $domainresult_stmt->fetch(PDO::FETCH_ASSOC)) { - $domains .= $row2['domain'] . '
    '; - } - } - - // check whether we use that config as froxor-vhost config - if (Settings::Get('system.mod_fcgid_defaultini_ownvhost') == $row['id'] || Settings::Get('phpfpm.vhost_defaultini') == $row['id']) { - $domains .= Settings::Get('system.hostname'); - } - - if ($domains == '') { - $domains = $lng['admin']['phpsettings']['notused']; - } - - // check whether this is our default config - if ((Settings::Get('system.mod_fcgid') == '1' && Settings::Get('system.mod_fcgid_defaultini') == $row['id']) || (Settings::Get('phpfpm.enabled') == '1' && Settings::Get('phpfpm.defaultini') == $row['id'])) { - $row['description'] = '' . $row['description'] . ''; - } - - $count ++; - eval("\$tablecontent.=\"" . getTemplate("phpconfig/overview_overview") . "\";"); } - $log->logAction(ADM_ACTION, LOG_INFO, "php.ini setting overview has been viewed by '" . $userinfo['loginname'] . "'"); eval("echo \"" . getTemplate("phpconfig/overview") . "\";"); } @@ -104,77 +60,11 @@ if ($page == 'overview') { if ((int) $userinfo['change_serversettings'] == 1) { if (isset($_POST['send']) && $_POST['send'] == 'send') { - $description = validate($_POST['description'], 'description'); - $phpsettings = validate(str_replace("\r\n", "\n", $_POST['phpsettings']), 'phpsettings', '/^[^\0]*$/'); - - if (Settings::Get('system.mod_fcgid') == 1) { - $binary = makeCorrectFile(validate($_POST['binary'], 'binary')); - $file_extensions = validate($_POST['file_extensions'], 'file_extensions', '/^[a-zA-Z0-9\s]*$/'); - $mod_fcgid_starter = validate($_POST['mod_fcgid_starter'], 'mod_fcgid_starter', '/^[0-9]*$/', '', array( - '-1', - '' - )); - $mod_fcgid_maxrequests = validate($_POST['mod_fcgid_maxrequests'], 'mod_fcgid_maxrequests', '/^[0-9]*$/', '', array( - '-1', - '' - )); - $mod_fcgid_umask = validate($_POST['mod_fcgid_umask'], 'mod_fcgid_umask', '/^[0-9]*$/'); - // disable fpm stuff - $fpm_config_id = 1; - $fpm_enableslowlog = 0; - $fpm_reqtermtimeout = 0; - $fpm_reqslowtimeout = 0; - $fpm_pass_authorizationheader = 0; - } elseif (Settings::Get('phpfpm.enabled') == 1) { - $fpm_config_id = intval($_POST['fpmconfig']); - $fpm_enableslowlog = isset($_POST['phpfpm_enable_slowlog']) ? (int) $_POST['phpfpm_enable_slowlog'] : 0; - $fpm_reqtermtimeout = validate($_POST['phpfpm_reqtermtimeout'], 'phpfpm_reqtermtimeout', '/^([0-9]+)(|s|m|h|d)$/'); - $fpm_reqslowtimeout = validate($_POST['phpfpm_reqslowtimeout'], 'phpfpm_reqslowtimeout', '/^([0-9]+)(|s|m|h|d)$/'); - $fpm_pass_authorizationheader = isset($_POST['phpfpm_pass_authorizationheader']) ? (int) $_POST['phpfpm_pass_authorizationheader'] : 0; - // disable fcgid stuff - $binary = '/usr/bin/php-cgi'; - $file_extensions = 'php'; - $mod_fcgid_starter = 0; - $mod_fcgid_maxrequests = 0; - $mod_fcgid_umask = "022"; + try { + PhpSettings::getLocal($userinfo, $_POST)->add(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); } - - if (strlen($description) == 0 || strlen($description) > 50) { - standard_error('descriptioninvalid'); - } - - $ins_stmt = Database::prepare(" - INSERT INTO `" . TABLE_PANEL_PHPCONFIGS . "` SET - `description` = :desc, - `binary` = :binary, - `file_extensions` = :fext, - `mod_fcgid_starter` = :starter, - `mod_fcgid_maxrequests` = :mreq, - `mod_fcgid_umask` = :umask, - `fpm_slowlog` = :fpmslow, - `fpm_reqterm` = :fpmreqterm, - `fpm_reqslow` = :fpmreqslow, - `phpsettings` = :phpsettings, - `fpmsettingid` = :fpmsettingid, - `pass_authorizationheader` = :fpmpassauth"); - $ins_data = array( - 'desc' => $description, - 'binary' => $binary, - 'fext' => $file_extensions, - 'starter' => $mod_fcgid_starter, - 'mreq' => $mod_fcgid_maxrequests, - 'umask' => $mod_fcgid_umask, - 'fpmslow' => $fpm_enableslowlog, - 'fpmreqterm' => $fpm_reqtermtimeout, - 'fpmreqslow' => $fpm_reqslowtimeout, - 'phpsettings' => $phpsettings, - 'fpmsettingid' => $fpm_config_id, - 'fpmpassauth' => $fpm_pass_authorizationheader - ); - Database::pexecute($ins_stmt, $ins_data); - - inserttask('1'); - $log->logAction(ADM_ACTION, LOG_INFO, "php.ini setting with description '" . $description . "' has been created by '" . $userinfo['loginname'] . "'"); redirectTo($filename, array( 'page' => $page, 's' => $s @@ -204,42 +94,23 @@ if ($page == 'overview') { } if ($action == 'delete') { - - $result_stmt = Database::prepare(" - SELECT * FROM `" . TABLE_PANEL_PHPCONFIGS . "` WHERE `id` = :id"); - $result = Database::pexecute_first($result_stmt, array( - 'id' => $id - )); - - if ((Settings::Get('system.mod_fcgid') == '1' && Settings::Get('system.mod_fcgid_defaultini_ownvhost') == $id) || (Settings::Get('phpfpm.enabled') == '1' && Settings::Get('phpfpm.vhost_defaultini') == $id)) { - standard_error('cannotdeletehostnamephpconfig'); - } - - if ((Settings::Get('system.mod_fcgid') == '1' && Settings::Get('system.mod_fcgid_defaultini') == $id) || (Settings::Get('phpfpm.enabled') == '1' && Settings::Get('phpfpm.defaultini') == $id)) { - standard_error('cannotdeletedefaultphpconfig'); + + try { + $json_result = PhpSettings::getLocal($userinfo, array('id' => $id))->get(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); } + $result = json_decode($json_result, true)['data']; if ($result['id'] != 0 && $result['id'] == $id && (int) $userinfo['change_serversettings'] == 1 && $id != 1) // cannot delete the default php.config { if (isset($_POST['send']) && $_POST['send'] == 'send') { - // set php-config to default for all domains using the - // config that is to be deleted - $upd_stmt = Database::prepare(" - UPDATE `" . TABLE_PANEL_DOMAINS . "` SET - `phpsettingid` = '1' WHERE `phpsettingid` = :id"); - Database::pexecute($upd_stmt, array( - 'id' => $id - )); - - $del_stmt = Database::prepare(" - DELETE FROM `" . TABLE_PANEL_PHPCONFIGS . "` WHERE `id` = :id"); - Database::pexecute($del_stmt, array( - 'id' => $id - )); - - inserttask('1'); - $log->logAction(ADM_ACTION, LOG_INFO, "php.ini setting with id #" . (int) $id . " has been deleted by '" . $userinfo['loginname'] . "'"); + try { + PhpSettings::getLocal($userinfo, array('id' => $id))->delete(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); + } redirectTo($filename, array( 'page' => $page, 's' => $s @@ -258,103 +129,36 @@ if ($page == 'overview') { if ($action == 'edit') { - $result_stmt = Database::prepare(" - SELECT * FROM `" . TABLE_PANEL_PHPCONFIGS . "` WHERE `id` = :id"); - $result = Database::pexecute_first($result_stmt, array( - 'id' => $id - )); + try { + $json_result = PhpSettings::getLocal($userinfo, array('id' => $id))->get(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); + } + $result = json_decode($json_result, true)['data']; if ($result['id'] != 0 && $result['id'] == $id && (int) $userinfo['change_serversettings'] == 1) { if (isset($_POST['send']) && $_POST['send'] == 'send') { - $description = validate($_POST['description'], 'description'); - $phpsettings = validate(str_replace("\r\n", "\n", $_POST['phpsettings']), 'phpsettings', '/^[^\0]*$/'); - - if (Settings::Get('system.mod_fcgid') == 1) { - $binary = makeCorrectFile(validate($_POST['binary'], 'binary')); - $file_extensions = validate($_POST['file_extensions'], 'file_extensions', '/^[a-zA-Z0-9\s]*$/'); - $mod_fcgid_starter = validate($_POST['mod_fcgid_starter'], 'mod_fcgid_starter', '/^[0-9]*$/', '', array( - '-1', - '' - )); - $mod_fcgid_maxrequests = validate($_POST['mod_fcgid_maxrequests'], 'mod_fcgid_maxrequests', '/^[0-9]*$/', '', array( - '-1', - '' - )); - $mod_fcgid_umask = validate($_POST['mod_fcgid_umask'], 'mod_fcgid_umask', '/^[0-9]*$/'); - // disable fpm stuff - $fpm_config_id = 1; - $fpm_enableslowlog = 0; - $fpm_reqtermtimeout = 0; - $fpm_reqslowtimeout = 0; - $fpm_pass_authorizationheader = 0; - } elseif (Settings::Get('phpfpm.enabled') == 1) { - $fpm_config_id = intval($_POST['fpmconfig']); - $fpm_enableslowlog = isset($_POST['phpfpm_enable_slowlog']) ? (int) $_POST['phpfpm_enable_slowlog'] : 0; - $fpm_reqtermtimeout = validate($_POST['phpfpm_reqtermtimeout'], 'phpfpm_reqtermtimeout', '/^([0-9]+)(|s|m|h|d)$/'); - $fpm_reqslowtimeout = validate($_POST['phpfpm_reqslowtimeout'], 'phpfpm_reqslowtimeout', '/^([0-9]+)(|s|m|h|d)$/'); - $fpm_pass_authorizationheader = isset($_POST['phpfpm_pass_authorizationheader']) ? (int) $_POST['phpfpm_pass_authorizationheader'] : 0; - // disable fcgid stuff - $binary = '/usr/bin/php-cgi'; - $file_extensions = 'php'; - $mod_fcgid_starter = 0; - $mod_fcgid_maxrequests = 0; - $mod_fcgid_umask = "022"; + try { + PhpSettings::getLocal($userinfo, $_POST)->update(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); } - - if (strlen($description) == 0 || strlen($description) > 50) { - standard_error('descriptioninvalid'); - } - - $upd_stmt = Database::prepare(" - UPDATE `" . TABLE_PANEL_PHPCONFIGS . "` SET - `description` = :desc, - `binary` = :binary, - `file_extensions` = :fext, - `mod_fcgid_starter` = :starter, - `mod_fcgid_maxrequests` = :mreq, - `mod_fcgid_umask` = :umask, - `fpm_slowlog` = :fpmslow, - `fpm_reqterm` = :fpmreqterm, - `fpm_reqslow` = :fpmreqslow, - `phpsettings` = :phpsettings, - `fpmsettingid` = :fpmsettingid, - `pass_authorizationheader` = :fpmpassauth - WHERE `id` = :id"); - $upd_data = array( - 'desc' => $description, - 'binary' => $binary, - 'fext' => $file_extensions, - 'starter' => $mod_fcgid_starter, - 'mreq' => $mod_fcgid_maxrequests, - 'umask' => $mod_fcgid_umask, - 'fpmslow' => $fpm_enableslowlog, - 'fpmreqterm' => $fpm_reqtermtimeout, - 'fpmreqslow' => $fpm_reqslowtimeout, - 'phpsettings' => $phpsettings, - 'fpmsettingid' => $fpm_config_id, - 'fpmpassauth' => $fpm_pass_authorizationheader, - 'id' => $id - ); - Database::pexecute($upd_stmt, $upd_data); - - inserttask('1'); - $log->logAction(ADM_ACTION, LOG_INFO, "php.ini setting with description '" . $description . "' has been changed by '" . $userinfo['loginname'] . "'"); redirectTo($filename, array( 'page' => $page, 's' => $s )); } else { - + $fpmconfigs = ''; $configs = Database::query("SELECT * FROM `" . TABLE_PANEL_FPMDAEMONS . "` ORDER BY `description` ASC"); while ($row = $configs->fetch(PDO::FETCH_ASSOC)) { $fpmconfigs .= makeoption($row['description'], $row['id'], $result['fpmsettingid'], true, true); } - + $phpconfig_edit_data = include_once dirname(__FILE__) . '/lib/formfields/admin/phpconfig/formfield.phpconfig_edit.php'; $phpconfig_edit_form = htmlform::genHTMLForm($phpconfig_edit_data); - + $title = $phpconfig_edit_data['phpconfig_edit']['title']; $image = $phpconfig_edit_data['phpconfig_edit']['image']; @@ -373,7 +177,7 @@ if ($page == 'overview') { $result = Database::query("SELECT * FROM `" . TABLE_PANEL_FPMDAEMONS . "` ORDER BY `description` ASC"); while ($row = $result->fetch(PDO::FETCH_ASSOC)) { - + $query_params = array( 'id' => $row['id'] ); @@ -461,9 +265,9 @@ if ($page == 'overview') { } else { $pm_select = makeoption('static', 'static', 'static', true, true); - $pm_select.= makeoption('dynamic', 'dynamic', 'static', true, true); - $pm_select.= makeoption('ondemand', 'ondemand', 'static', true, true); - + $pm_select .= makeoption('dynamic', 'dynamic', 'static', true, true); + $pm_select .= makeoption('ondemand', 'ondemand', 'static', true, true); + $fpmconfig_add_data = include_once dirname(__FILE__) . '/lib/formfields/admin/phpconfig/formfield.fpmconfig_add.php'; $fpmconfig_add_form = htmlform::genHTMLForm($fpmconfig_add_data); @@ -484,11 +288,11 @@ if ($page == 'overview') { $result = Database::pexecute_first($result_stmt, array( 'id' => $id )); - + if ($id == 1) { standard_error('cannotdeletedefaultphpconfig'); } - + if ($result['id'] != 0 && $result['id'] == $id && (int) $userinfo['change_serversettings'] == 1 && $id != 1) // cannot delete the default php.config { @@ -592,9 +396,9 @@ if ($page == 'overview') { } else { $pm_select = makeoption('static', 'static', $result['pm'], true, true); - $pm_select.= makeoption('dynamic', 'dynamic', $result['pm'], true, true); - $pm_select.= makeoption('ondemand', 'ondemand', $result['pm'], true, true); - + $pm_select .= makeoption('dynamic', 'dynamic', $result['pm'], true, true); + $pm_select .= makeoption('ondemand', 'ondemand', $result['pm'], true, true); + $fpmconfig_edit_data = include_once dirname(__FILE__) . '/lib/formfields/admin/phpconfig/formfield.fpmconfig_edit.php'; $fpmconfig_edit_form = htmlform::genHTMLForm($fpmconfig_edit_data); diff --git a/lib/classes/api/commands/class.PhpSettings.php b/lib/classes/api/commands/class.PhpSettings.php new file mode 100644 index 00000000..f987c1cd --- /dev/null +++ b/lib/classes/api/commands/class.PhpSettings.php @@ -0,0 +1,364 @@ + (2010-) + * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt + * @package Panel + * + */ +class PhpSettings extends ApiCommand implements ResourceEntity +{ + + /** + * lists all php-config entries + * + * @return array count|list + */ + public function list() + { + if ($this->isAdmin()) { + $this->logger()->logAction(ADM_ACTION, LOG_NOTICE, "[API] list php-configs"); + + $result = Database::query(" + SELECT c.*, fd.description as fpmdesc + FROM `" . TABLE_PANEL_PHPCONFIGS . "` c + LEFT JOIN `" . TABLE_PANEL_FPMDAEMONS . "` fd ON fd.id = c.fpmsettingid + ORDER BY c.description ASC + "); + + $phpconfigs = array(); + while ($row = $result->fetch(PDO::FETCH_ASSOC)) { + + $domainresult = false; + $query_params = array( + 'id' => $row['id'] + ); + + $query = "SELECT * FROM `" . TABLE_PANEL_DOMAINS . "` + WHERE `phpsettingid` = :id + AND `parentdomainid` = '0'"; + + if ((int) $this->getUserDetail('domains_see_all') == 0) { + $query .= " AND `adminid` = :adminid"; + $query_params['adminid'] = $this->getUserDetail('adminid'); + } + + if ((int) Settings::Get('panel.phpconfigs_hidestdsubdomain') == 1) { + $ssdids_res = Database::query(" + SELECT DISTINCT `standardsubdomain` FROM `" . TABLE_PANEL_CUSTOMERS . "` + WHERE `standardsubdomain` > 0 ORDER BY `standardsubdomain` ASC;"); + $ssdids = array(); + while ($ssd = $ssdids_res->fetch(PDO::FETCH_ASSOC)) { + $ssdids[] = $ssd['standardsubdomain']; + } + if (count($ssdids) > 0) { + $query .= " AND `id` NOT IN (" . implode(', ', $ssdids) . ")"; + } + } + + $domains = array(); + $domainresult_stmt = Database::prepare($query); + Database::pexecute($domainresult_stmt, $query_params, true, true); + + if (Database::num_rows() > 0) { + while ($row2 = $domainresult_stmt->fetch(PDO::FETCH_ASSOC)) { + $domains[] = $row2['domain']; + } + } + + // check whether we use that config as froxor-vhost config + if (Settings::Get('system.mod_fcgid_defaultini_ownvhost') == $row['id'] || Settings::Get('phpfpm.vhost_defaultini') == $row['id']) { + $domains[] = Settings::Get('system.hostname'); + } + + if (empty($domains)) { + $domains[] = $lng['admin']['phpsettings']['notused']; + } + + // check whether this is our default config + if ((Settings::Get('system.mod_fcgid') == '1' && Settings::Get('system.mod_fcgid_defaultini') == $row['id']) || (Settings::Get('phpfpm.enabled') == '1' && Settings::Get('phpfpm.defaultini') == $row['id'])) { + $row['is_default'] = true; + } + + $row['domains'] = $domains; + $phpconfigs[] = $row; + } + + return $this->response(200, "successfull", array( + 'count' => count($phpconfigs), + 'list' => $phpconfigs + )); + } + throw new Exception("Not allowed to execute given command.", 403); + } + + public function get() + { + if ($this->isAdmin()) { + $id = $this->getParam('id'); + + $result_stmt = Database::prepare(" + SELECT * FROM `" . TABLE_PANEL_PHPCONFIGS . "` WHERE `id` = :id + "); + $result = Database::pexecute_first($result_stmt, array( + 'id' => $id + ), true, true); + if ($result) { + return $this->response(200, "successfull", $result); + } + throw new Exception("php-config with id #" . $id . " could not be found", 404); + } + throw new Exception("Not allowed to execute given command.", 403); + } + + public function add() + { + if ($this->isAdmin() && $this->getUserDetail('change_serversettings') == 1) { + + // required parameter + $description = $this->getParam('description'); + $phpsettings = $this->getParam('phpsettings'); + + if (Settings::Get('system.mod_fcgid') == 1) { + $binary = $this->getParam('binary'); + } elseif (Settings::Get('phpfpm.enabled') == 1) { + $fpm_config_id = intval($this->getParam('fpmconfig')); + } + + // parameters + $file_extensions = $this->getParam('file_extensions', true, 'php'); + $mod_fcgid_starter = $this->getParam('mod_fcgid_starter', true, - 1); + $mod_fcgid_maxrequests = $this->getParam('mod_fcgid_maxrequests', true, - 1); + $mod_fcgid_umask = $this->getParam('mod_fcgid_umask', true, "022"); + $fpm_enableslowlog = $this->getParam('phpfpm_enable_slowlog', true, 0); + $fpm_reqtermtimeout = $this->getParam('phpfpm_reqtermtimeout', true, "60s"); + $fpm_reqslowtimeout = $this->getParam('phpfpm_reqslowtimeout', true, "5s"); + $fpm_pass_authorizationheader = $this->getParam('phpfpm_pass_authorizationheader', true, 0); + + // validation + $description = validate($description, 'description', '', '', array(), true); + $phpsettings = validate(str_replace("\r\n", "\n", $phpsettings), 'phpsettings', '/^[^\0]*$/', '', array(), true); + if (Settings::Get('system.mod_fcgid') == 1) { + $binary = makeCorrectFile(validate($binary, 'binary', '', '', array(), true)); + $file_extensions = validate($file_extensions, 'file_extensions', '/^[a-zA-Z0-9\s]*$/', '', array(), true); + $mod_fcgid_starter = validate($mod_fcgid_starter, 'mod_fcgid_starter', '/^[0-9]*$/', '', array( + '-1', + '' + ), true); + $mod_fcgid_maxrequests = validate($mod_fcgid_maxrequests, 'mod_fcgid_maxrequests', '/^[0-9]*$/', '', array( + '-1', + '' + ), true); + $mod_fcgid_umask = validate($mod_fcgid_umask, 'mod_fcgid_umask', '/^[0-9]*$/', '', array(), true); + // disable fpm stuff + $fpm_config_id = 1; + $fpm_enableslowlog = 0; + $fpm_reqtermtimeout = 0; + $fpm_reqslowtimeout = 0; + $fpm_pass_authorizationheader = 0; + } elseif (Settings::Get('phpfpm.enabled') == 1) { + $fpm_reqtermtimeout = validate($fpm_reqtermtimeout, 'phpfpm_reqtermtimeout', '/^([0-9]+)(|s|m|h|d)$/', '', array(), true); + $fpm_reqslowtimeout = validate($fpm_reqslowtimeout, 'phpfpm_reqslowtimeout', '/^([0-9]+)(|s|m|h|d)$/', '', array(), true); + // disable fcgid stuff + $binary = '/usr/bin/php-cgi'; + $file_extensions = 'php'; + $mod_fcgid_starter = 0; + $mod_fcgid_maxrequests = 0; + $mod_fcgid_umask = "022"; + } + + if (strlen($description) == 0 || strlen($description) > 50) { + standard_error('descriptioninvalid', '', true); + } + + $ins_stmt = Database::prepare(" + INSERT INTO `" . TABLE_PANEL_PHPCONFIGS . "` SET + `description` = :desc, + `binary` = :binary, + `file_extensions` = :fext, + `mod_fcgid_starter` = :starter, + `mod_fcgid_maxrequests` = :mreq, + `mod_fcgid_umask` = :umask, + `fpm_slowlog` = :fpmslow, + `fpm_reqterm` = :fpmreqterm, + `fpm_reqslow` = :fpmreqslow, + `phpsettings` = :phpsettings, + `fpmsettingid` = :fpmsettingid, + `pass_authorizationheader` = :fpmpassauth + "); + $ins_data = array( + 'desc' => $description, + 'binary' => $binary, + 'fext' => $file_extensions, + 'starter' => $mod_fcgid_starter, + 'mreq' => $mod_fcgid_maxrequests, + 'umask' => $mod_fcgid_umask, + 'fpmslow' => $fpm_enableslowlog, + 'fpmreqterm' => $fpm_reqtermtimeout, + 'fpmreqslow' => $fpm_reqslowtimeout, + 'phpsettings' => $phpsettings, + 'fpmsettingid' => $fpm_config_id, + 'fpmpassauth' => $fpm_pass_authorizationheader + ); + Database::pexecute($ins_stmt, $ins_data, true, true); + $ins_data['id'] = Database::lastInsertId(); + + inserttask('1'); + $this->logger()->logAction(ADM_ACTION, LOG_INFO, "[API] php setting with description '" . $description . "' has been created by '" . $this->getUserDetail('loginname') . "'"); + return $this->response(200, "successfull", $ins_data); + } + throw new Exception("Not allowed to execute given command.", 403); + } + + public function update() + { + if ($this->isAdmin() && $this->getUserDetail('change_serversettings') == 1) { + + // required parameter + $id = $this->getParam('id'); + + $json_result = PhpSettings::getLocal($this->getUserData(), array( + 'id' => $id + ))->get(); + $result = json_decode($json_result, true)['data']; + + // parameters + $description = $this->getParam('description', true, $result['description']); + $phpsettings = $this->getParam('phpsettings', true, $result['phpsettings']); + $binary = $this->getParam('binary', true, $result['binary']); + $fpm_config_id = intval($this->getParam('fpmconfig', true, $result['fpmsettingid'])); + $file_extensions = $this->getParam('file_extensions', true, $result['file_extensions']); + $mod_fcgid_starter = $this->getParam('mod_fcgid_starter', true, $result['mod_fcgid_starter']); + $mod_fcgid_maxrequests = $this->getParam('mod_fcgid_maxrequests', true, $result['mod_fcgid_maxrequests']); + $mod_fcgid_umask = $this->getParam('mod_fcgid_umask', true, $result['mod_fcgid_umask']); + $fpm_enableslowlog = $this->getParam('phpfpm_enable_slowlog', true, $result['fpm_slowlog']); + $fpm_reqtermtimeout = $this->getParam('phpfpm_reqtermtimeout', true, $result['fpm_reqterm']); + $fpm_reqslowtimeout = $this->getParam('phpfpm_reqslowtimeout', true, $result['fpm_reqslow']); + $fpm_pass_authorizationheader = $this->getParam('phpfpm_pass_authorizationheader', true, $result['pass_authorizationheader']); + + // validation + $description = validate($description, 'description', '', '', array(), true); + $phpsettings = validate(str_replace("\r\n", "\n", $phpsettings), 'phpsettings', '/^[^\0]*$/', '', array(), true); + if (Settings::Get('system.mod_fcgid') == 1) { + $binary = makeCorrectFile(validate($binary, 'binary', '', '', array(), true)); + $file_extensions = validate($file_extensions, 'file_extensions', '/^[a-zA-Z0-9\s]*$/', '', array(), true); + $mod_fcgid_starter = validate($mod_fcgid_starter, 'mod_fcgid_starter', '/^[0-9]*$/', '', array( + '-1', + '' + ), true); + $mod_fcgid_maxrequests = validate($mod_fcgid_maxrequests, 'mod_fcgid_maxrequests', '/^[0-9]*$/', '', array( + '-1', + '' + ), true); + $mod_fcgid_umask = validate($mod_fcgid_umask, 'mod_fcgid_umask', '/^[0-9]*$/', '', array(), true); + // disable fpm stuff + $fpm_config_id = 1; + $fpm_enableslowlog = 0; + $fpm_reqtermtimeout = 0; + $fpm_reqslowtimeout = 0; + $fpm_pass_authorizationheader = 0; + } elseif (Settings::Get('phpfpm.enabled') == 1) { + $fpm_reqtermtimeout = validate($fpm_reqtermtimeout, 'phpfpm_reqtermtimeout', '/^([0-9]+)(|s|m|h|d)$/', '', array(), true); + $fpm_reqslowtimeout = validate($fpm_reqslowtimeout, 'phpfpm_reqslowtimeout', '/^([0-9]+)(|s|m|h|d)$/', '', array(), true); + // disable fcgid stuff + $binary = '/usr/bin/php-cgi'; + $file_extensions = 'php'; + $mod_fcgid_starter = 0; + $mod_fcgid_maxrequests = 0; + $mod_fcgid_umask = "022"; + } + + if (strlen($description) == 0 || strlen($description) > 50) { + standard_error('descriptioninvalid', '', true); + } + + $upd_stmt = Database::prepare(" + UPDATE `" . TABLE_PANEL_PHPCONFIGS . "` SET + `description` = :desc, + `binary` = :binary, + `file_extensions` = :fext, + `mod_fcgid_starter` = :starter, + `mod_fcgid_maxrequests` = :mreq, + `mod_fcgid_umask` = :umask, + `fpm_slowlog` = :fpmslow, + `fpm_reqterm` = :fpmreqterm, + `fpm_reqslow` = :fpmreqslow, + `phpsettings` = :phpsettings, + `fpmsettingid` = :fpmsettingid, + `pass_authorizationheader` = :fpmpassauth + WHERE `id` = :id + "); + $upd_data = array( + 'desc' => $description, + 'binary' => $binary, + 'fext' => $file_extensions, + 'starter' => $mod_fcgid_starter, + 'mreq' => $mod_fcgid_maxrequests, + 'umask' => $mod_fcgid_umask, + 'fpmslow' => $fpm_enableslowlog, + 'fpmreqterm' => $fpm_reqtermtimeout, + 'fpmreqslow' => $fpm_reqslowtimeout, + 'phpsettings' => $phpsettings, + 'fpmsettingid' => $fpm_config_id, + 'fpmpassauth' => $fpm_pass_authorizationheader, + 'id' => $id + ); + Database::pexecute($upd_stmt, $upd_data, true, true); + + inserttask('1'); + $this->logger()->logAction(ADM_ACTION, LOG_INFO, "[API] php setting with description '" . $description . "' has been updated by '" . $this->getUserDetail('loginname') . "'"); + return $this->response(200, "successfull", $upd_data); + } + throw new Exception("Not allowed to execute given command.", 403); + } + + public function delete() + { + if ($this->isAdmin() && $this->getUserDetail('change_serversettings') == 1) { + $id = $this->getParam('id'); + + $json_result = PhpSettings::getLocal($this->getUserData(), array( + 'id' => $id + ))->get(); + $result = json_decode($json_result, true)['data']; + + if ((Settings::Get('system.mod_fcgid') == '1' && Settings::Get('system.mod_fcgid_defaultini_ownvhost') == $id) || (Settings::Get('phpfpm.enabled') == '1' && Settings::Get('phpfpm.vhost_defaultini') == $id)) { + standard_error('cannotdeletehostnamephpconfig', '', true); + } + + if ((Settings::Get('system.mod_fcgid') == '1' && Settings::Get('system.mod_fcgid_defaultini') == $id) || (Settings::Get('phpfpm.enabled') == '1' && Settings::Get('phpfpm.defaultini') == $id)) { + standard_error('cannotdeletedefaultphpconfig', '', true); + } + + // set php-config to default for all domains using the + // config that is to be deleted + $upd_stmt = Database::prepare(" + UPDATE `" . TABLE_PANEL_DOMAINS . "` SET + `phpsettingid` = '1' WHERE `phpsettingid` = :id + "); + Database::pexecute($upd_stmt, array( + 'id' => $id + ), true, true); + + $del_stmt = Database::prepare(" + DELETE FROM `" . TABLE_PANEL_PHPCONFIGS . "` WHERE `id` = :id + "); + Database::pexecute($del_stmt, array( + 'id' => $id + ), true, true); + + inserttask('1'); + $this->logger()->logAction(ADM_ACTION, LOG_INFO, "[API] php setting '" . $result['description'] . " has been deleted by '" . $this->getUserDetail('loginname') . "'"); + return $this->response(200, "successfull", $result); + } + throw new Exception("Not allowed to execute given command.", 403); + } +} From b2ac1fb593e357b4e523a9ac10115ec71e957c00 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 19 Feb 2018 13:58:39 +0100 Subject: [PATCH 0449/1335] add FpmDaemons ApiCommand Signed-off-by: Michael Kaufmann (d00p) --- admin_phpsettings.php | 186 +++--------- lib/classes/api/commands/class.FpmDaemons.php | 277 ++++++++++++++++++ .../api/commands/class.PhpSettings.php | 2 +- 3 files changed, 317 insertions(+), 148 deletions(-) create mode 100644 lib/classes/api/commands/class.FpmDaemons.php diff --git a/admin_phpsettings.php b/admin_phpsettings.php index 12b6b853..6eee297a 100644 --- a/admin_phpsettings.php +++ b/admin_phpsettings.php @@ -171,38 +171,26 @@ if ($page == 'overview') { } elseif ($page == 'fpmdaemons') { if ($action == '') { + + try { + $json_result = FpmDaemons::getLocal($userinfo)->list(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); + } + $result = json_decode($json_result, true)['data']; $tablecontent = ''; $count = 0; - $result = Database::query("SELECT * FROM `" . TABLE_PANEL_FPMDAEMONS . "` ORDER BY `description` ASC"); - - while ($row = $result->fetch(PDO::FETCH_ASSOC)) { - - $query_params = array( - 'id' => $row['id'] - ); - - $query = "SELECT * FROM `" . TABLE_PANEL_PHPCONFIGS . "` WHERE `fpmsettingid` = :id"; - - $configresult_stmt = Database::prepare($query); - Database::pexecute($configresult_stmt, $query_params); - - $configs = ''; - if (Database::num_rows() > 0) { - while ($row2 = $configresult_stmt->fetch(PDO::FETCH_ASSOC)) { - $configs .= $row2['description'] . '
    '; + if (isset($result['count']) && $result['count'] > 0) { + foreach ($result['list'] as $row) { + $configs = ""; + foreach ($row['configs'] as $configused) { + $configs .= $configused . "
    "; } + $count++; + eval("\$tablecontent.=\"" . getTemplate("phpconfig/fpmdaemons_overview") . "\";"); } - - if ($configs == '') { - $configs = $lng['admin']['phpsettings']['notused']; - } - - $count ++; - eval("\$tablecontent.=\"" . getTemplate("phpconfig/fpmdaemons_overview") . "\";"); } - - $log->logAction(ADM_ACTION, LOG_INFO, "fpm daemons setting overview has been viewed by '" . $userinfo['loginname'] . "'"); eval("echo \"" . getTemplate("phpconfig/fpmdaemons") . "\";"); } @@ -211,53 +199,11 @@ if ($page == 'overview') { if ((int) $userinfo['change_serversettings'] == 1) { if (isset($_POST['send']) && $_POST['send'] == 'send') { - $description = validate($_POST['description'], 'description'); - $reload_cmd = validate($_POST['reload_cmd'], 'reload_cmd'); - $config_dir = validate($_POST['config_dir'], 'config_dir'); - $pm = $_POST['pm']; - $max_children = isset($_POST['max_children']) ? (int) $_POST['max_children'] : 0; - $start_servers = isset($_POST['start_servers']) ? (int) $_POST['start_servers'] : 0; - $min_spare_servers = isset($_POST['min_spare_servers']) ? (int) $_POST['min_spare_servers'] : 0; - $max_spare_servers = isset($_POST['max_spare_servers']) ? (int) $_POST['max_spare_servers'] : 0; - $max_requests = isset($_POST['max_requests']) ? (int) $_POST['max_requests'] : 0; - $idle_timeout = isset($_POST['idle_timeout']) ? (int) $_POST['idle_timeout'] : 0; - $limit_extensions = validate($_POST['limit_extensions'], 'limit_extensions', '/^(\.[a-z]([a-z0-9]+)\ ?)+$/'); - - if (strlen($description) == 0 || strlen($description) > 50) { - standard_error('descriptioninvalid'); + try { + FpmDaemons::getLocal($userinfo, $_POST)->add(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); } - - $ins_stmt = Database::prepare(" - INSERT INTO `" . TABLE_PANEL_FPMDAEMONS . "` SET - `description` = :desc, - `reload_cmd` = :reload_cmd, - `config_dir` = :config_dir, - `pm` = :pm, - `max_children` = :max_children, - `start_servers` = :start_servers, - `min_spare_servers` = :min_spare_servers, - `max_spare_servers` = :max_spare_servers, - `max_requests` = :max_requests, - `idle_timeout` = :idle_timeout, - `limit_extensions` = :limit_extensions - "); - $ins_data = array( - 'desc' => $description, - 'reload_cmd' => $reload_cmd, - 'config_dir' => makeCorrectDir($config_dir), - 'pm' => $pm, - 'max_children' => $max_children, - 'start_servers' => $start_servers, - 'min_spare_servers' => $min_spare_servers, - 'max_spare_servers' => $max_spare_servers, - 'max_requests' => $max_requests, - 'idle_timeout' => $idle_timeout, - 'limit_extensions' => $limit_extensions - ); - Database::pexecute($ins_stmt, $ins_data); - - inserttask('1'); - $log->logAction(ADM_ACTION, LOG_INFO, "fpm-daemon setting with description '" . $description . "' has been created by '" . $userinfo['loginname'] . "'"); redirectTo($filename, array( 'page' => $page, 's' => $s @@ -283,11 +229,12 @@ if ($page == 'overview') { if ($action == 'delete') { - $result_stmt = Database::prepare(" - SELECT * FROM `" . TABLE_PANEL_FPMDAEMONS . "` WHERE `id` = :id"); - $result = Database::pexecute_first($result_stmt, array( - 'id' => $id - )); + try { + $json_result = FpmDaemons::getLocal($userinfo, array('id' => $id))->get(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); + } + $result = json_decode($json_result, true)['data']; if ($id == 1) { standard_error('cannotdeletedefaultphpconfig'); @@ -295,24 +242,12 @@ if ($page == 'overview') { if ($result['id'] != 0 && $result['id'] == $id && (int) $userinfo['change_serversettings'] == 1 && $id != 1) // cannot delete the default php.config { - if (isset($_POST['send']) && $_POST['send'] == 'send') { - // set default fpm daemon config for all php-config that use this config that is to be deleted - $upd_stmt = Database::prepare(" - UPDATE `" . TABLE_PANEL_PHPCONFIGS . "` SET - `fpmsettingid` = '1' WHERE `fpmsettingid` = :id"); - Database::pexecute($upd_stmt, array( - 'id' => $id - )); - - $del_stmt = Database::prepare(" - DELETE FROM `" . TABLE_PANEL_FPMDAEMONS . "` WHERE `id` = :id"); - Database::pexecute($del_stmt, array( - 'id' => $id - )); - - inserttask('1'); - $log->logAction(ADM_ACTION, LOG_INFO, "fpm-daemon setting with id #" . (int) $id . " has been deleted by '" . $userinfo['loginname'] . "'"); + try { + FpmDaemons::getLocal($userinfo, $_POST)->delete(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); + } redirectTo($filename, array( 'page' => $page, 's' => $s @@ -331,64 +266,21 @@ if ($page == 'overview') { if ($action == 'edit') { - $result_stmt = Database::prepare(" - SELECT * FROM `" . TABLE_PANEL_FPMDAEMONS . "` WHERE `id` = :id"); - $result = Database::pexecute_first($result_stmt, array( - 'id' => $id - )); + try { + $json_result = FpmDaemons::getLocal($userinfo, array('id' => $id))->get(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); + } + $result = json_decode($json_result, true)['data']; if ($result['id'] != 0 && $result['id'] == $id && (int) $userinfo['change_serversettings'] == 1) { if (isset($_POST['send']) && $_POST['send'] == 'send') { - $description = validate($_POST['description'], 'description'); - $reload_cmd = validate($_POST['reload_cmd'], 'reload_cmd'); - $config_dir = validate($_POST['config_dir'], 'config_dir'); - $pm = $_POST['pm']; - $max_children = isset($_POST['max_children']) ? (int) $_POST['max_children'] : $result['max_children']; - $start_servers = isset($_POST['start_servers']) ? (int) $_POST['start_servers'] : $result['start_servers']; - $min_spare_servers = isset($_POST['min_spare_servers']) ? (int) $_POST['min_spare_servers'] : $result['min_spare_servers']; - $max_spare_servers = isset($_POST['max_spare_servers']) ? (int) $_POST['max_spare_servers'] : $result['max_spare_servers']; - $max_requests = isset($_POST['max_requests']) ? (int) $_POST['max_requests'] : $result['max_requests']; - $idle_timeout = isset($_POST['idle_timeout']) ? (int) $_POST['idle_timeout'] : $result['idle_timeout']; - $limit_extensions = validate($_POST['limit_extensions'], 'limit_extensions', '/^(\.[a-z]([a-z0-9]+)\ ?)+$/'); - - if (strlen($description) == 0 || strlen($description) > 50) { - standard_error('descriptioninvalid'); + try { + FpmDaemons::getLocal($userinfo, $_POST)->update(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); } - - $upd_stmt = Database::prepare(" - UPDATE `" . TABLE_PANEL_FPMDAEMONS . "` SET - `description` = :desc, - `reload_cmd` = :reload_cmd, - `config_dir` = :config_dir, - `pm` = :pm, - `max_children` = :max_children, - `start_servers` = :start_servers, - `min_spare_servers` = :min_spare_servers, - `max_spare_servers` = :max_spare_servers, - `max_requests` = :max_requests, - `idle_timeout` = :idle_timeout, - `limit_extensions` = :limit_extensions - WHERE `id` = :id - "); - $upd_data = array( - 'desc' => $description, - 'reload_cmd' => $reload_cmd, - 'config_dir' => makeCorrectDir($config_dir), - 'pm' => $pm, - 'max_children' => $max_children, - 'start_servers' => $start_servers, - 'min_spare_servers' => $min_spare_servers, - 'max_spare_servers' => $max_spare_servers, - 'max_requests' => $max_requests, - 'idle_timeout' => $idle_timeout, - 'limit_extensions' => $limit_extensions, - 'id' => $id - ); - Database::pexecute($upd_stmt, $upd_data); - - inserttask('1'); - $log->logAction(ADM_ACTION, LOG_INFO, "fpm-daemon setting with description '" . $description . "' has been changed by '" . $userinfo['loginname'] . "'"); redirectTo($filename, array( 'page' => $page, 's' => $s diff --git a/lib/classes/api/commands/class.FpmDaemons.php b/lib/classes/api/commands/class.FpmDaemons.php new file mode 100644 index 00000000..9d906593 --- /dev/null +++ b/lib/classes/api/commands/class.FpmDaemons.php @@ -0,0 +1,277 @@ + (2010-) + * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt + * @package Panel + * + */ +class FpmDaemons extends ApiCommand implements ResourceEntity +{ + + /** + * lists all php-config entries + * + * @return array count|list + */ + public function list() + { + if ($this->isAdmin()) { + $this->logger()->logAction(ADM_ACTION, LOG_NOTICE, "[API] list fpm-daemons"); + + $result = Database::query(" + SELECT * FROM `" . TABLE_PANEL_FPMDAEMONS . "` ORDER BY `description` ASC + "); + + $fpmdaemons = array(); + while ($row = $result->fetch(PDO::FETCH_ASSOC)) { + + $query_params = array( + 'id' => $row['id'] + ); + + $query = "SELECT * FROM `" . TABLE_PANEL_PHPCONFIGS . "` WHERE `fpmsettingid` = :id"; + + $configresult_stmt = Database::prepare($query); + Database::pexecute($configresult_stmt, $query_params, true, true); + + $configs = array(); + if (Database::num_rows() > 0) { + while ($row2 = $configresult_stmt->fetch(PDO::FETCH_ASSOC)) { + $configs[] = $row2['description']; + } + } + + if (empty($configs)) { + $configs[] = $lng['admin']['phpsettings']['notused']; + } + + $row['configs'] = $configs; + $fpmdaemons[] = $row; + } + + return $this->response(200, "successfull", array( + 'count' => count($fpmdaemons), + 'list' => $fpmdaemons + )); + } + throw new Exception("Not allowed to execute given command.", 403); + } + + public function get() + { + if ($this->isAdmin()) { + $id = $this->getParam('id'); + + $result_stmt = Database::prepare(" + SELECT * FROM `" . TABLE_PANEL_FPMDAEMONS . "` WHERE `id` = :id + "); + $result = Database::pexecute_first($result_stmt, array( + 'id' => $id + ), true, true); + if ($result) { + return $this->response(200, "successfull", $result); + } + throw new Exception("fpm-daemon with id #" . $id . " could not be found", 404); + } + throw new Exception("Not allowed to execute given command.", 403); + } + + public function add() + { + if ($this->isAdmin() && $this->getUserDetail('change_serversettings') == 1) { + + // required parameter + $description = $this->getParam('description'); + $reload_cmd = $this->getParam('reload_cmd'); + $config_dir = $this->getParam('config_dir'); + + // parameters + $pm = $this->getParam('pm', true, 'static'); + $max_children = $this->getParam('max_children', true, 0); + $start_servers = $this->getParam('start_servers', true, 0); + $min_spare_servers = $this->getParam('min_spare_servers', true, 0); + $max_spare_servers = $this->getParam('max_spare_servers', true, 0); + $max_requests = $this->getParam('max_requests', true, 0); + $idle_timeout = $this->getParam('idle_timeout', true, 0); + $limit_extensions = $this->getParam('limit_extensions', true, ''); + + // validation + $description = validate($description, 'description', '', '', array(), true); + $reload_cmd = validate($reload_cmd, 'reload_cmd', '', '', array(), true); + $config_dir = validate($config_dir, 'config_dir', '', '', array(), true); + if (! in_array($pm, array( + 'static', + 'dynamic', + 'ondemand' + ))) { + throw new ErrorException("Unknown process manager", 406); + } + $limit_extensions = validate($limit_extensions, 'limit_extensions', '/^(\.[a-z]([a-z0-9]+)\ ?)+$/', '', array(), true); + + if (strlen($description) == 0 || strlen($description) > 50) { + standard_error('descriptioninvalid', '', true); + } + + $ins_stmt = Database::prepare(" + INSERT INTO `" . TABLE_PANEL_FPMDAEMONS . "` SET + `description` = :desc, + `reload_cmd` = :reload_cmd, + `config_dir` = :config_dir, + `pm` = :pm, + `max_children` = :max_children, + `start_servers` = :start_servers, + `min_spare_servers` = :min_spare_servers, + `max_spare_servers` = :max_spare_servers, + `max_requests` = :max_requests, + `idle_timeout` = :idle_timeout, + `limit_extensions` = :limit_extensions + "); + $ins_data = array( + 'desc' => $description, + 'reload_cmd' => $reload_cmd, + 'config_dir' => makeCorrectDir($config_dir), + 'pm' => $pm, + 'max_children' => $max_children, + 'start_servers' => $start_servers, + 'min_spare_servers' => $min_spare_servers, + 'max_spare_servers' => $max_spare_servers, + 'max_requests' => $max_requests, + 'idle_timeout' => $idle_timeout, + 'limit_extensions' => $limit_extensions + ); + Database::pexecute($ins_stmt, $ins_data); + $ins_data['id'] = Database::lastInsertId(); + + inserttask('1'); + $this->logger()->logAction(ADM_ACTION, LOG_INFO, "[API] fpm-daemon with description '" . $description . "' has been created by '" . $this->getUserDetail('loginname') . "'"); + return $this->response(200, "successfull", $ins_data); + } + throw new Exception("Not allowed to execute given command.", 403); + } + + public function update() + { + if ($this->isAdmin() && $this->getUserDetail('change_serversettings') == 1) { + + // required parameter + $id = $this->getParam('id'); + + $json_result = PhpSettings::getLocal($this->getUserData(), array( + 'id' => $id + ))->get(); + $result = json_decode($json_result, true)['data']; + + // parameters + $description = $this->getParam('description', true, $result['description']); + $reload_cmd = $this->getParam('reload_cmd', true, $result['reload_cmd']); + $config_dir = $this->getParam('config_dir', true, $result['config_dir']); + $pm = $this->getParam('pm', true, $result['pm']); + $max_children = $this->getParam('max_children', true, $result['max_children']); + $start_servers = $this->getParam('start_servers', true, $result['start_servers']); + $min_spare_servers = $this->getParam('min_spare_servers', true, $result['min_spare_servers']); + $max_spare_servers = $this->getParam('max_spare_servers', true, $result['max_spare_servers']); + $max_requests = $this->getParam('max_requests', true, $result['max_requests']); + $idle_timeout = $this->getParam('idle_timeout', true, $result['idle_timeout']); + $limit_extensions = $this->getParam('limit_extensions', true, $result['limit_extensions']); + + // validation + $description = validate($description, 'description', '', '', array(), true); + $reload_cmd = validate($reload_cmd, 'reload_cmd', '', '', array(), true); + $config_dir = validate($config_dir, 'config_dir', '', '', array(), true); + if (! in_array($pm, array( + 'static', + 'dynamic', + 'ondemand' + ))) { + throw new ErrorException("Unknown process manager", 406); + } + $limit_extensions = validate($limit_extensions, 'limit_extensions', '/^(\.[a-z]([a-z0-9]+)\ ?)+$/', '', array(), true); + + if (strlen($description) == 0 || strlen($description) > 50) { + standard_error('descriptioninvalid', '', true); + } + + $upd_stmt = Database::prepare(" + UPDATE `" . TABLE_PANEL_FPMDAEMONS . "` SET + `description` = :desc, + `reload_cmd` = :reload_cmd, + `config_dir` = :config_dir, + `pm` = :pm, + `max_children` = :max_children, + `start_servers` = :start_servers, + `min_spare_servers` = :min_spare_servers, + `max_spare_servers` = :max_spare_servers, + `max_requests` = :max_requests, + `idle_timeout` = :idle_timeout, + `limit_extensions` = :limit_extensions + WHERE `id` = :id + "); + $upd_data = array( + 'desc' => $description, + 'reload_cmd' => $reload_cmd, + 'config_dir' => makeCorrectDir($config_dir), + 'pm' => $pm, + 'max_children' => $max_children, + 'start_servers' => $start_servers, + 'min_spare_servers' => $min_spare_servers, + 'max_spare_servers' => $max_spare_servers, + 'max_requests' => $max_requests, + 'idle_timeout' => $idle_timeout, + 'limit_extensions' => $limit_extensions, + 'id' => $id + ); + Database::pexecute($upd_stmt, $upd_data, true, true); + + inserttask('1'); + $this->logger()->logAction(ADM_ACTION, LOG_INFO, "[API] fpm-daemon with description '" . $description . "' has been updated by '" . $this->getUserDetail('loginname') . "'"); + return $this->response(200, "successfull", $upd_data); + } + throw new Exception("Not allowed to execute given command.", 403); + } + + public function delete() + { + if ($this->isAdmin() && $this->getUserDetail('change_serversettings') == 1) { + $id = $this->getParam('id'); + + if ($id == 1) { + standard_error('cannotdeletedefaultphpconfig', '', true); + } + + $json_result = FpmDaemons::getLocal($this->getUserData(), array( + 'id' => $id + ))->get(); + $result = json_decode($json_result, true)['data']; + + // set default fpm daemon config for all php-config that use this config that is to be deleted + $upd_stmt = Database::prepare(" + UPDATE `" . TABLE_PANEL_PHPCONFIGS . "` SET + `fpmsettingid` = '1' WHERE `fpmsettingid` = :id + "); + Database::pexecute($upd_stmt, array( + 'id' => $id + ), true, true); + + $del_stmt = Database::prepare(" + DELETE FROM `" . TABLE_PANEL_FPMDAEMONS . "` WHERE `id` = :id + "); + Database::pexecute($del_stmt, array( + 'id' => $id + ), true, true); + + inserttask('1'); + $this->logger()->logAction(ADM_ACTION, LOG_INFO, "[API] fpm-daemon setting '" . $result['description'] . "' has been deleted by '" . $this->getUserDetail('loginname') . "'"); + return $this->response(200, "successfull", $result); + } + throw new Exception("Not allowed to execute given command.", 403); + } +} diff --git a/lib/classes/api/commands/class.PhpSettings.php b/lib/classes/api/commands/class.PhpSettings.php index f987c1cd..686da052 100644 --- a/lib/classes/api/commands/class.PhpSettings.php +++ b/lib/classes/api/commands/class.PhpSettings.php @@ -356,7 +356,7 @@ class PhpSettings extends ApiCommand implements ResourceEntity ), true, true); inserttask('1'); - $this->logger()->logAction(ADM_ACTION, LOG_INFO, "[API] php setting '" . $result['description'] . " has been deleted by '" . $this->getUserDetail('loginname') . "'"); + $this->logger()->logAction(ADM_ACTION, LOG_INFO, "[API] php setting '" . $result['description'] . "' has been deleted by '" . $this->getUserDetail('loginname') . "'"); return $this->response(200, "successfull", $result); } throw new Exception("Not allowed to execute given command.", 403); From 033393880d59babc2445a950d8096777603f7ebf Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 20 Feb 2018 14:14:47 +0100 Subject: [PATCH 0450/1335] fix typo in variable name Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/commands/class.Customers.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/classes/api/commands/class.Customers.php b/lib/classes/api/commands/class.Customers.php index 00a03997..2ce4bbae 100644 --- a/lib/classes/api/commands/class.Customers.php +++ b/lib/classes/api/commands/class.Customers.php @@ -96,7 +96,7 @@ class Customers extends ApiCommand implements ResourceEntity // parameters $name = $this->getParam('name', true, ''); $firstname = $this->getParam('firstname', true, ''); - $company_required = (empty($name) && empty($first)); + $company_required = (empty($name) && empty($firstname)); $company = $this->getParam('company', $company_required, ''); $street = $this->getParam('street', true, ''); $zipcode = $this->getParam('zipcode', true, ''); From eabc78c84f087518ec4f7ac39e2f7ba350d6cd09 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 20 Feb 2018 14:21:40 +0100 Subject: [PATCH 0451/1335] enhance check for requirement of company field Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/commands/class.Customers.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/classes/api/commands/class.Customers.php b/lib/classes/api/commands/class.Customers.php index 2ce4bbae..5cbaddb4 100644 --- a/lib/classes/api/commands/class.Customers.php +++ b/lib/classes/api/commands/class.Customers.php @@ -92,11 +92,11 @@ class Customers extends ApiCommand implements ResourceEntity // required parameters $email = $this->getParam('email'); - + // parameters $name = $this->getParam('name', true, ''); $firstname = $this->getParam('firstname', true, ''); - $company_required = (empty($name) && empty($firstname)); + $company_required = (! empty($name) && empty($firstname)) || (empty($name) && ! empty($firstname)) || (empty($name) && empty($firstname)); $company = $this->getParam('company', $company_required, ''); $street = $this->getParam('street', true, ''); $zipcode = $this->getParam('zipcode', true, ''); @@ -654,7 +654,7 @@ class Customers extends ApiCommand implements ResourceEntity // parameters $move_to_admin = intval_ressource($this->getParam('move_to_admin', true, 0)); - + $idna_convert = new idna_convert_wrapper(); $email = $this->getParam('email', true, $idna_convert->decode($result['email'])); $name = $this->getParam('name', true, $result['name']); @@ -670,7 +670,7 @@ class Customers extends ApiCommand implements ResourceEntity $gender = intval_ressource($this->getParam('gender', true, $result['gender'])); $custom_notes = $this->getParam('custom_notes', true, $result['custom_notes']); $custom_notes_show = $this->getParam('custom_notes_show', true, $result['custom_notes_show']); - + $dec_places = Settings::Get('panel.decimal_places'); $diskspace = $this->getUlParam('diskspace', 'diskspace_ul', true, round($result['diskspace'] / 1024, $dec_places)); $traffic = $this->getUlParam('traffic', 'traffic_ul', true, round($result['traffic'] / (1024 * 1024), $dec_places)); @@ -692,7 +692,7 @@ class Customers extends ApiCommand implements ResourceEntity $perlenabled = $this->getParam('perlenabled', true, $result['perlenabled']); $dnsenabled = $this->getParam('dnsenabled', true, $result['dnsenabled']); $deactivated = $this->getParam('deactivated', true, $result['deactivated']); - + // validation $idna_convert = new idna_convert_wrapper(); $name = validate($name, 'name', '', '', array(), true); @@ -707,15 +707,15 @@ class Customers extends ApiCommand implements ResourceEntity $customernumber = validate($customernumber, 'customer number', '/^[A-Za-z0-9 \-]*$/Di', '', array(), true); $def_language = validate($def_language, 'default language', '', '', array(), true); $custom_notes = validate(str_replace("\r\n", "\n", $custom_notes), 'custom_notes', '/^[^\0]*$/', '', array(), true); - + if (Settings::Get('system.mail_quota_enabled') != '1') { $email_quota = - 1; } - + if (Settings::Get('ticket.enabled') != '1') { $tickets = - 1; } - + // Either $name and $firstname or the $company must be inserted if ($name == '' && $company == '') { standard_error(array( From b9653c5abd33c2e0d46bc3abdbe5aae673304306 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 20 Feb 2018 19:11:33 +0100 Subject: [PATCH 0452/1335] fix company-required check for good now :P Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/commands/class.Customers.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/classes/api/commands/class.Customers.php b/lib/classes/api/commands/class.Customers.php index 5cbaddb4..29acb6ff 100644 --- a/lib/classes/api/commands/class.Customers.php +++ b/lib/classes/api/commands/class.Customers.php @@ -97,7 +97,7 @@ class Customers extends ApiCommand implements ResourceEntity $name = $this->getParam('name', true, ''); $firstname = $this->getParam('firstname', true, ''); $company_required = (! empty($name) && empty($firstname)) || (empty($name) && ! empty($firstname)) || (empty($name) && empty($firstname)); - $company = $this->getParam('company', $company_required, ''); + $company = $this->getParam('company', ($company_required ? false : true), ''); $street = $this->getParam('street', true, ''); $zipcode = $this->getParam('zipcode', true, ''); $city = $this->getParam('city', true, ''); From 5437fcdc898cb505348b89c5a3d09a16e29f67d2 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 20 Feb 2018 19:15:55 +0100 Subject: [PATCH 0453/1335] insert tasks to rebuild configs etc. after import of settings, thx to v3ng for noticing Signed-off-by: Michael Kaufmann (d00p) --- admin_settings.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/admin_settings.php b/admin_settings.php index ac8ff85c..8bc17a13 100644 --- a/admin_settings.php +++ b/admin_settings.php @@ -319,6 +319,12 @@ elseif ($page == 'importexport' && $userinfo['change_serversettings'] == '1') } catch(Exception $e) { dynamic_error($e->getMessage()); } + inserttask('1'); + inserttask('10'); + // Using nameserver, insert a task which rebuilds the server config + inserttask('4'); + // cron.d file + inserttask('99'); standard_success('settingsimported', '', array('filename' => 'admin_settings.php')); } dynamic_error("Upload failed"); From d5b9ad345235b4dd6ad6048f5ac1e08c26261fda Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 20 Feb 2018 21:37:06 +0100 Subject: [PATCH 0454/1335] darn, stay php-5.3 compatible, thx greppy Signed-off-by: Michael Kaufmann (d00p) --- scripts/jobs/cron_tasks.inc.http.10.apache.php | 3 ++- scripts/jobs/cron_tasks.inc.http.20.lighttpd.php | 3 ++- scripts/jobs/cron_tasks.inc.http.30.nginx.php | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/scripts/jobs/cron_tasks.inc.http.10.apache.php b/scripts/jobs/cron_tasks.inc.http.10.apache.php index 2a7ab516..e49ef34e 100644 --- a/scripts/jobs/cron_tasks.inc.http.10.apache.php +++ b/scripts/jobs/cron_tasks.inc.http.10.apache.php @@ -66,7 +66,8 @@ class apache extends HttpConfigBase foreach ($restart_cmds as $restart_cmd) { // check whether the config dir is empty (no domains uses this daemon) // so we need to create a dummy - $isDirEmpty = !(new \FilesystemIterator($restart_cmd['config_dir']))->valid(); + $fsi = new \FilesystemIterator($restart_cmd['config_dir']); + $isDirEmpty = !$fsi->valid(); if ($isDirEmpty) { $this->logger->logAction(CRON_ACTION, LOG_INFO, 'apache::reload: fpm config directory "' . $restart_cmd['config_dir'] . '" is empty. Creating dummy.'); phpinterface_fpm::createDummyPool($restart_cmd['config_dir']); diff --git a/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php b/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php index 99232c8c..84b88400 100644 --- a/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php +++ b/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php @@ -66,7 +66,8 @@ class lighttpd extends HttpConfigBase foreach ($restart_cmds as $restart_cmd) { // check whether the config dir is empty (no domains uses this daemon) // so we need to create a dummy - $isDirEmpty = !(new \FilesystemIterator($restart_cmd['config_dir']))->valid(); + $fsi = new \FilesystemIterator($restart_cmd['config_dir']); + $isDirEmpty = !$fsi->valid(); if ($isDirEmpty) { $this->logger->logAction(CRON_ACTION, LOG_INFO, 'lighttpd::reload: fpm config directory "' . $restart_cmd['config_dir'] . '" is empty. Creating dummy.'); phpinterface_fpm::createDummyPool($restart_cmd['config_dir']); diff --git a/scripts/jobs/cron_tasks.inc.http.30.nginx.php b/scripts/jobs/cron_tasks.inc.http.30.nginx.php index 7f86f605..e2eac04d 100644 --- a/scripts/jobs/cron_tasks.inc.http.30.nginx.php +++ b/scripts/jobs/cron_tasks.inc.http.30.nginx.php @@ -81,7 +81,8 @@ class nginx extends HttpConfigBase foreach ($restart_cmds as $restart_cmd) { // check whether the config dir is empty (no domains uses this daemon) // so we need to create a dummy - $isDirEmpty = !(new \FilesystemIterator($restart_cmd['config_dir']))->valid(); + $fsi = new \FilesystemIterator($restart_cmd['config_dir']); + $isDirEmpty = !$fsi->valid(); if ($isDirEmpty) { $this->logger->logAction(CRON_ACTION, LOG_INFO, 'nginx::reload: fpm config directory "' . $restart_cmd['config_dir'] . '" is empty. Creating dummy.'); phpinterface_fpm::createDummyPool($restart_cmd['config_dir']); From 26b9c030b53053761bfe70dc48b280b6e3785748 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Wed, 21 Feb 2018 10:57:46 +0100 Subject: [PATCH 0455/1335] leave default-values for adding std-subdomain when adding customer from the parameters-array; do not require ipandports parameter when adding domain but rather default to system.defaultip if no ipandport is given Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/commands/class.Customers.php | 21 +------------------- lib/classes/api/commands/class.Domains.php | 8 ++++++-- 2 files changed, 7 insertions(+), 22 deletions(-) diff --git a/lib/classes/api/commands/class.Customers.php b/lib/classes/api/commands/class.Customers.php index 29acb6ff..83fbb373 100644 --- a/lib/classes/api/commands/class.Customers.php +++ b/lib/classes/api/commands/class.Customers.php @@ -517,19 +517,9 @@ class Customers extends ApiCommand implements ResourceEntity 'domain' => $_stdsubdomain, 'customerid' => $customerid, 'adminid' => $this->getUserDetail('adminid'), - 'parentdomainid' => '0', 'docroot' => $documentroot, - 'adddate' => time(), 'phpenabled' => $phpenabled, - 'zonefile' => '', - 'isemaildomain' => '0', - 'caneditdomain' => '0', - 'openbasedir' => '1', - 'speciallogfile' => '0', - 'dkim_id' => '0', - 'dkim_privkey' => '', - 'dkim_pubkey' => '', - 'ipandport' => explode(',', Settings::Get('system.defaultip')) + 'openbasedir' => '1' ); $domainid = - 1; try { @@ -759,18 +749,9 @@ class Customers extends ApiCommand implements ResourceEntity 'domain' => $_stdsubdomain, 'customerid' => $result['customerid'], 'adminid' => $this->getUserDetail('adminid'), - 'parentdomainid' => '0', 'docroot' => $result['documentroot'], - 'adddate' => time(), 'phpenabled' => $phpenabled, - 'zonefile' => '', - 'isemaildomain' => '0', - 'caneditdomain' => '0', 'openbasedir' => '1', - 'speciallogfile' => '0', - 'dkim_id' => '0', - 'dkim_privkey' => '', - 'dkim_pubkey' => '', 'ipandport' => explode(',', Settings::Get('system.defaultip')) ); $domainid = - 1; diff --git a/lib/classes/api/commands/class.Domains.php b/lib/classes/api/commands/class.Domains.php index c25b4d64..e3ae5878 100644 --- a/lib/classes/api/commands/class.Domains.php +++ b/lib/classes/api/commands/class.Domains.php @@ -97,9 +97,9 @@ class Domains extends ApiCommand implements ResourceEntity // parameters $p_domain = $this->getParam('domain'); $customerid = intval($this->getParam('customerid')); - $p_ipandports = $this->getParam('ipandport'); // optional parameters + $p_ipandports = $this->getParam('ipandport', true, explode(',', Settings::Get('system.defaultip'))); $adminid = intval($this->getParam('adminid', true, $this->getUserDetail('adminid'))); $subcanemaildomain = $this->getParam('subcanemaildomain', true, 0); $isemaildomain = $this->getParam('isemaildomain', true, 0); @@ -310,7 +310,11 @@ class Domains extends ApiCommand implements ResourceEntity $additional_ip_condition = ''; $aip_param = array(); } - + + if (empty($p_ipandports)) { + throw new Exception("No IPs given, unable to add domain (no default IPs set?", 406); + } + $ipandports = array(); if (! empty($p_ipandport) && ! is_array($p_ipandports)) { $p_ipandports = unserialize($p_ipandports); From 5480fcbf5d172067fec3d20fb0ddda8dbbbf2eed Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Wed, 21 Feb 2018 11:50:34 +0100 Subject: [PATCH 0456/1335] add default-ssl-ip setting Signed-off-by: Michael Kaufmann (d00p) --- actions/admin/settings/120.system.php | 10 ++++ install/froxlor.sql | 3 +- .../updates/froxlor/0.10/update_0.10.inc.php | 11 +++- lib/classes/api/abstract.ApiCommand.php | 14 +++++ lib/classes/api/commands/class.Customers.php | 3 +- lib/classes/api/commands/class.Domains.php | 4 +- .../api/commands/class.IpsAndPorts.php | 2 +- .../integrity/class.IntegrityCheck.php | 4 +- lib/classes/settings/class.SImExporter.php | 1 + .../function.getIpPortCombinations.php | 4 ++ .../function.storeSettingDefaultIp.php | 52 +++++++++++++++++++ lib/version.inc.php | 2 +- lng/english.lng.php | 2 + lng/german.lng.php | 4 +- 14 files changed, 106 insertions(+), 10 deletions(-) diff --git a/actions/admin/settings/120.system.php b/actions/admin/settings/120.system.php index e1b4c5cd..644ee5f4 100644 --- a/actions/admin/settings/120.system.php +++ b/actions/admin/settings/120.system.php @@ -60,6 +60,16 @@ return array( 'default' => '', 'save_method' => 'storeSettingDefaultIp', ), + 'system_defaultsslip' => array( + 'label' => $lng['serversettings']['defaultsslip'], + 'settinggroup' => 'system', + 'varname' => 'defaultsslip', + 'type' => 'option', + 'option_mode' => 'multiple', + 'option_options_method' => 'getSslIpPortCombinations', + 'default' => '', + 'save_method' => 'storeSettingDefaultSslIp', + ), 'system_hostname' => array( 'label' => $lng['serversettings']['hostname'], 'settinggroup' => 'system', diff --git a/install/froxlor.sql b/install/froxlor.sql index 6a8534a8..0f5b1dcd 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -540,6 +540,7 @@ opcache.interned_strings_buffer'), ('system', 'mysql_access_host', 'localhost'), ('system', 'lastcronrun', ''), ('system', 'defaultip', '1'), + ('system', 'defaultsslip', ''), ('system', 'phpappendopenbasedir', '/tmp/'), ('system', 'deactivateddocroot', ''), ('system', 'mailpwcleartext', '1'), @@ -689,7 +690,7 @@ opcache.interned_strings_buffer'), ('panel', 'password_special_char', '!?<>§$%+#=@'), ('panel', 'customer_hide_options', ''), ('panel', 'version', '0.10.0'), - ('panel', 'db_version', '201802150'); + ('panel', 'db_version', '201802210'); DROP TABLE IF EXISTS `panel_tasks`; diff --git a/install/updates/froxlor/0.10/update_0.10.inc.php b/install/updates/froxlor/0.10/update_0.10.inc.php index 7e796501..bb9cf1c9 100644 --- a/install/updates/froxlor/0.10/update_0.10.inc.php +++ b/install/updates/froxlor/0.10/update_0.10.inc.php @@ -14,7 +14,7 @@ * @package Install * */ -if (!defined('_CRON_UPDATE')) { +if (! defined('_CRON_UPDATE')) { if (! defined('AREA') || (defined('AREA') && AREA != 'admin') || ! isset($userinfo['loginname']) || (isset($userinfo['loginname']) && $userinfo['loginname'] == '')) { header('Location: ../../../../index.php'); exit(); @@ -26,3 +26,12 @@ if (isFroxlorVersion('0.9.39.5')) { showUpdateStep("Updating from 0.9.39.5 to 0.10.0", false); updateToVersion('0.10.0'); } + +if (isDatabaseVersion('201802150')) { + + showUpdateStep("Adding new default-ssl-ip setting"); + Settings::AddNew('system.defaultsslip', ''); + lastStepStatus(0); + + updateToDbVersion('201802210'); +} diff --git a/lib/classes/api/abstract.ApiCommand.php b/lib/classes/api/abstract.ApiCommand.php index 5a749e63..abf2eda8 100644 --- a/lib/classes/api/abstract.ApiCommand.php +++ b/lib/classes/api/abstract.ApiCommand.php @@ -184,6 +184,20 @@ abstract class ApiCommand return $this->cmd_params[$param]; } + /** + * get specific parameter which also has and unlimited-field + * + * @param string $param + * parameter to get out of the request-parameter list + * @param string $ul_field + * parameter to get out of the request-parameter list + * @param bool $optional + * default: false + * @param mixed $default + * value which is returned if optional=true and param is not set + * + * @return mixed + */ protected function getUlParam($param = null, $ul_field = null, $optional = false, $default = 0) { $param_value = intval_ressource($this->getParam($param, $optional, $default)); diff --git a/lib/classes/api/commands/class.Customers.php b/lib/classes/api/commands/class.Customers.php index 83fbb373..ecef61f4 100644 --- a/lib/classes/api/commands/class.Customers.php +++ b/lib/classes/api/commands/class.Customers.php @@ -751,8 +751,7 @@ class Customers extends ApiCommand implements ResourceEntity 'adminid' => $this->getUserDetail('adminid'), 'docroot' => $result['documentroot'], 'phpenabled' => $phpenabled, - 'openbasedir' => '1', - 'ipandport' => explode(',', Settings::Get('system.defaultip')) + 'openbasedir' => '1' ); $domainid = - 1; try { diff --git a/lib/classes/api/commands/class.Domains.php b/lib/classes/api/commands/class.Domains.php index e3ae5878..6d4085d3 100644 --- a/lib/classes/api/commands/class.Domains.php +++ b/lib/classes/api/commands/class.Domains.php @@ -124,7 +124,7 @@ class Domains extends ApiCommand implements ResourceEntity $mod_fcgid_maxrequests = $this->getParam('mod_fcgid_maxrequests', true, - 1); $ssl_redirect = $this->getParam('ssl_redirect', true, 0); $letsencrypt = $this->getParam('letsencrypt', true, 0); - $p_ssl_ipandports = $this->getParam('ssl_ipandport', true, array()); + $p_ssl_ipandports = $this->getParam('ssl_ipandport', true, explode(',', Settings::Get('system.defaultsslip'))); $http2 = $this->getParam('http2', true, 0); $hsts_maxage = $this->getParam('hsts_maxage', true, 0); $hsts_sub = $this->getParam('hsts_sub', true, 0); @@ -312,7 +312,7 @@ class Domains extends ApiCommand implements ResourceEntity } if (empty($p_ipandports)) { - throw new Exception("No IPs given, unable to add domain (no default IPs set?", 406); + throw new Exception("No IPs given, unable to add domain (no default IPs set?)", 406); } $ipandports = array(); diff --git a/lib/classes/api/commands/class.IpsAndPorts.php b/lib/classes/api/commands/class.IpsAndPorts.php index 89aeb596..596a0a42 100644 --- a/lib/classes/api/commands/class.IpsAndPorts.php +++ b/lib/classes/api/commands/class.IpsAndPorts.php @@ -370,7 +370,7 @@ class IpsAndPorts extends ApiCommand implements ResourceEntity ), true, true); if ($result_checkdomain['id'] == '') { - if (! in_array($result['id'], explode(',', Settings::Get('system.defaultip')))) { + if (! in_array($result['id'], explode(',', Settings::Get('system.defaultip'))) && ! in_array($result['id'], explode(',', Settings::Get('system.defaultsslip')))) { $result_sameipotherport_stmt = Database::prepare(" SELECT `id` FROM `" . TABLE_PANEL_IPSANDPORTS . "` diff --git a/lib/classes/integrity/class.IntegrityCheck.php b/lib/classes/integrity/class.IntegrityCheck.php index 1f8a3fb7..bc0a7dad 100644 --- a/lib/classes/integrity/class.IntegrityCheck.php +++ b/lib/classes/integrity/class.IntegrityCheck.php @@ -126,10 +126,12 @@ class IntegrityCheck { // Cache all IPs the admins have assigned $adm_stmt = Database::prepare("SELECT `adminid`, `ip` FROM `" . TABLE_PANEL_ADMINS . "` ORDER BY `adminid` ASC"); Database::pexecute($adm_stmt); + $default_ips = explode(',', Settings::Get('system.defaultip')); + $default_ssl_ips = explode(',', Settings::Get('system.defaultsslip')); while ($row = $adm_stmt->fetch(PDO::FETCH_ASSOC)) { if ($row['ip'] < 0 || is_null($row['ip']) || empty($row['ip'])) { // Admin uses default-IP - $admips[$row['adminid']] = explode(',', Settings::Get('system.defaultip')); + $admips[$row['adminid']] = array_merge($default_ips, $default_ssl_ips); } else { $admips[$row['adminid']] = array($row['ip']); } diff --git a/lib/classes/settings/class.SImExporter.php b/lib/classes/settings/class.SImExporter.php index 10e8aa32..366efc64 100644 --- a/lib/classes/settings/class.SImExporter.php +++ b/lib/classes/settings/class.SImExporter.php @@ -48,6 +48,7 @@ class SImExporter 'system.mysql_access_host', 'system.lastcronrun', 'system.defaultip', + 'system.defaultsslip'. 'system.last_tasks_run', 'system.last_archive_run', 'system.leprivatekey', diff --git a/lib/functions/froxlor/function.getIpPortCombinations.php b/lib/functions/froxlor/function.getIpPortCombinations.php index 5d67a4dd..f0043a74 100644 --- a/lib/functions/froxlor/function.getIpPortCombinations.php +++ b/lib/functions/froxlor/function.getIpPortCombinations.php @@ -62,3 +62,7 @@ function getIpPortCombinations($ssl = false) { return $system_ipaddress_array; } + +function getSslIpPortCombinations() { + return getIpPortCombinations(true); +} diff --git a/lib/functions/settings/function.storeSettingDefaultIp.php b/lib/functions/settings/function.storeSettingDefaultIp.php index 22d08d66..5a8994c2 100644 --- a/lib/functions/settings/function.storeSettingDefaultIp.php +++ b/lib/functions/settings/function.storeSettingDefaultIp.php @@ -67,3 +67,55 @@ function storeSettingDefaultIp($fieldname, $fielddata, $newfieldvalue) { return $returnvalue; } + +function storeSettingDefaultSslIp($fieldname, $fielddata, $newfieldvalue) { + $defaultips_old = Settings::Get('system.defaultsslip'); + + $returnvalue = storeSettingField($fieldname, $fielddata, $newfieldvalue); + + if ($returnvalue !== false + && is_array($fielddata) + && isset($fielddata['settinggroup']) + && $fielddata['settinggroup'] == 'system' + && isset($fielddata['varname']) + && $fielddata['varname'] == 'defaultsslip' + ) { + + $customerstddomains_result_stmt = Database::prepare(" + SELECT `standardsubdomain` FROM `" . TABLE_PANEL_CUSTOMERS . "` WHERE `standardsubdomain` <> '0' + "); + Database::pexecute($customerstddomains_result_stmt); + + $ids = array(); + + while ($customerstddomains_row = $customerstddomains_result_stmt->fetch(PDO::FETCH_ASSOC)) { + $ids[] = (int)$customerstddomains_row['standardsubdomain']; + } + + if (count($ids) > 0) { + $defaultips_new = explode(',', $newfieldvalue); + + // Delete the existing mappings linking to default IPs + $del_stmt = Database::prepare(" + DELETE FROM `" . TABLE_DOMAINTOIP . "` + WHERE `id_domain` IN (" . implode(', ', $ids) . ") + AND `id_ipandports` IN (" . $defaultips_old . ", " . $newfieldvalue . ") + "); + Database::pexecute($del_stmt); + + // Insert the new mappings + $ins_stmt = Database::prepare(" + INSERT INTO `" . TABLE_DOMAINTOIP . "` + SET `id_domain` = :domainid, `id_ipandports` = :ipandportid + "); + + foreach ($ids as $id) { + foreach ($defaultips_new as $defaultip_new) { + Database::pexecute($ins_stmt, array('domainid' => $id, 'ipandportid' => $defaultip_new)); + } + } + } + } + + return $returnvalue; +} diff --git a/lib/version.inc.php b/lib/version.inc.php index 8870fe02..29fa7c32 100644 --- a/lib/version.inc.php +++ b/lib/version.inc.php @@ -19,7 +19,7 @@ $version = '0.10.0'; // Database version (YYYYMMDDC where C is a daily counter) -$dbversion = '201802150'; +$dbversion = '201802210'; // Distribution branding-tag (used for Debian etc.) $branding = ''; diff --git a/lng/english.lng.php b/lng/english.lng.php index a5d33367..f9ed0d54 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -436,6 +436,8 @@ $lng['error']['webftpiswrong'] = 'The WebFTP-link is not a valid link.'; $lng['domains']['hasaliasdomains'] = 'Has alias domain(s)'; $lng['serversettings']['defaultip']['title'] = 'Default IP/Port'; $lng['serversettings']['defaultip']['description'] = 'Select all IP-addresses you want to use as default for new domains'; +$lng['serversettings']['defaultsslip']['title'] = 'Default SSL IP/Port'; +$lng['serversettings']['defaultsslip']['description'] = 'Select all ssl-enabled IP-addresses you want to use as default for new domains'; $lng['domains']['statstics'] = 'Usage Statistics'; $lng['panel']['ascending'] = 'ascending'; $lng['panel']['descending'] = 'descending'; diff --git a/lng/german.lng.php b/lng/german.lng.php index 0924c1ac..a4567f2a 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -429,8 +429,10 @@ $lng['error']['phpmyadminiswrong'] = 'Die "phpMyAdmin-URL" ist keine gültige UR $lng['error']['webmailiswrong'] = 'Die "Webmail-URL" ist keine gültige URL.'; $lng['error']['webftpiswrong'] = 'Die "WebFTP-URL" ist keine gültige URL.'; $lng['domains']['hasaliasdomains'] = 'Hat Aliasdomain(s)'; -$lng['serversettings']['defaultip']['title'] = 'Standard-IP/Port'; +$lng['serversettings']['defaultip']['title'] = 'Standard IP/Port'; $lng['serversettings']['defaultip']['description'] = 'Welche IP/Port-Kombination sollen standardmäßig verwendet werden?'; +$lng['serversettings']['defaultsslip']['title'] = 'Standard SSL IP/Port'; +$lng['serversettings']['defaultsslip']['description'] = 'Welche ssl-fähigen IP/Port-Kombination sollen standardmäßig verwendet werden?'; $lng['domains']['statstics'] = 'Statistiken'; $lng['panel']['ascending'] = 'aufsteigend'; $lng['panel']['descending'] = 'absteigend'; From b56414ed0e54633cce72fe8b069637a09c8d8b82 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Wed, 21 Feb 2018 12:16:50 +0100 Subject: [PATCH 0457/1335] add sql-query of last statement to sql-debug for debugging purposes; fix default-ssl-ip setting and allow 'none' value Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/database/class.Database.php | 6 ++-- .../function.getIpPortCombinations.php | 3 +- .../function.storeSettingDefaultIp.php | 30 +++++++++++++------ lng/english.lng.php | 1 + lng/german.lng.php | 1 + 5 files changed, 29 insertions(+), 12 deletions(-) diff --git a/lib/classes/database/class.Database.php b/lib/classes/database/class.Database.php index 7f6c7d46..350e71e3 100644 --- a/lib/classes/database/class.Database.php +++ b/lib/classes/database/class.Database.php @@ -71,7 +71,7 @@ class Database { try { $stmt->execute($params); } catch (PDOException $e) { - self::_showerror($e, $showerror, $json_response); + self::_showerror($e, $showerror, $json_response, $stmt); } } @@ -309,7 +309,7 @@ class Database { * @param PDOException $error * @param bool $showerror if set to false, the error will be logged but we go on */ - private static function _showerror($error, $showerror = true, $json_response = false) { + private static function _showerror($error, $showerror = true, $json_response = false, PDOStatement $stmt = null) { global $userinfo, $theme, $linker; // include userdata.inc.php @@ -374,6 +374,8 @@ class Database { if ($showerror) { if (empty($sql['debug'])) { $error_trace = ''; + } elseif (!is_null($stmt)) { + $error_trace .= "

    ".$stmt->queryString; } // fallback diff --git a/lib/functions/froxlor/function.getIpPortCombinations.php b/lib/functions/froxlor/function.getIpPortCombinations.php index f0043a74..1a0c75f6 100644 --- a/lib/functions/froxlor/function.getIpPortCombinations.php +++ b/lib/functions/froxlor/function.getIpPortCombinations.php @@ -64,5 +64,6 @@ function getIpPortCombinations($ssl = false) { } function getSslIpPortCombinations() { - return getIpPortCombinations(true); + global $lng; + return array('' => $lng['panel']['none_value']) + getIpPortCombinations(true); } diff --git a/lib/functions/settings/function.storeSettingDefaultIp.php b/lib/functions/settings/function.storeSettingDefaultIp.php index 5a8994c2..a3ca6b1f 100644 --- a/lib/functions/settings/function.storeSettingDefaultIp.php +++ b/lib/functions/settings/function.storeSettingDefaultIp.php @@ -95,23 +95,35 @@ function storeSettingDefaultSslIp($fieldname, $fielddata, $newfieldvalue) { if (count($ids) > 0) { $defaultips_new = explode(',', $newfieldvalue); + if (!empty($defaultips_old) && !empty($newfieldvalue)) + { + $in_value = $defaultips_old . ", " . $newfieldvalue; + } elseif (!empty($defaultips_old) && empty($newfieldvalue)) + { + $in_value = $defaultips_old; + } else { + $in_value = $newfieldvalue; + } + // Delete the existing mappings linking to default IPs $del_stmt = Database::prepare(" DELETE FROM `" . TABLE_DOMAINTOIP . "` WHERE `id_domain` IN (" . implode(', ', $ids) . ") - AND `id_ipandports` IN (" . $defaultips_old . ", " . $newfieldvalue . ") + AND `id_ipandports` IN (" . $in_value . ") "); Database::pexecute($del_stmt); - // Insert the new mappings - $ins_stmt = Database::prepare(" - INSERT INTO `" . TABLE_DOMAINTOIP . "` - SET `id_domain` = :domainid, `id_ipandports` = :ipandportid - "); + if (count($defaultips_new) > 0) { + // Insert the new mappings + $ins_stmt = Database::prepare(" + INSERT INTO `" . TABLE_DOMAINTOIP . "` + SET `id_domain` = :domainid, `id_ipandports` = :ipandportid + "); - foreach ($ids as $id) { - foreach ($defaultips_new as $defaultip_new) { - Database::pexecute($ins_stmt, array('domainid' => $id, 'ipandportid' => $defaultip_new)); + foreach ($ids as $id) { + foreach ($defaultips_new as $defaultip_new) { + Database::pexecute($ins_stmt, array('domainid' => $id, 'ipandportid' => $defaultip_new)); + } } } } diff --git a/lng/english.lng.php b/lng/english.lng.php index f9ed0d54..8adc8a40 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -2119,3 +2119,4 @@ $lng['admin']['plans']['use_plan'] = 'Apply plan'; $lng['question']['plan_reallydelete'] = 'Do you really want to delete the hosting plan %s?'; $lng['admin']['notryfiles']['title'] = 'No autogenerated try_files'; $lng['admin']['notryfiles']['description'] = 'Say yes here if you want to specify a custom try_files directive in specialsettings (needed for some wordpress plugins for example).'; +$lng['panel']['none_value'] = 'None'; diff --git a/lng/german.lng.php b/lng/german.lng.php index a4567f2a..888303b1 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -1769,3 +1769,4 @@ $lng['admin']['plans']['use_plan'] = 'Plan übernehmen'; $lng['question']['plan_reallydelete'] = 'Wollen Sie den Hosting-Plan "%s" wirklich löschen?'; $lng['admin']['notryfiles']['title'] = 'Keine generierte try_files Anweisung'; $lng['admin']['notryfiles']['description'] = 'Wähle "Ja", wenn eine eigene try_files Direktive in den "eigenen Vhost Einstellungen" angegeben werden soll (z.B. nötig für manche Wordpress Plugins).'; +$lng['panel']['none_value'] = 'Keine'; From 8310e8554b74c9ea4359b7ae82d9a4cfd4f1ff08 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Wed, 21 Feb 2018 13:14:54 +0100 Subject: [PATCH 0458/1335] enable usage of 'domainname' as an alternative to 'id' for Domains::get() and Domains::delete(); enable usage of 'loginname' as an alternative to 'id' for Customers::get() and Customers::delete() Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/commands/class.Customers.php | 39 ++++++++++++----- lib/classes/api/commands/class.Domains.php | 45 +++++++++++++++----- 2 files changed, 64 insertions(+), 20 deletions(-) diff --git a/lib/classes/api/commands/class.Customers.php b/lib/classes/api/commands/class.Customers.php index ecef61f4..852fafe9 100644 --- a/lib/classes/api/commands/class.Customers.php +++ b/lib/classes/api/commands/class.Customers.php @@ -52,10 +52,12 @@ class Customers extends ApiCommand implements ResourceEntity } /** - * return a customer entry by id + * return a customer entry by either id or loginname * * @param int $id - * customer-id + * optional, the customer-id + * @param string $loginname + * optional, the loginname * * @throws Exception * @return array @@ -63,13 +65,20 @@ class Customers extends ApiCommand implements ResourceEntity public function get() { if ($this->isAdmin()) { - $id = $this->getParam('id'); + $id = $this->getParam('id', true, 0); + $ln_optional = ($id <= 0 ? false : true); + $loginname = $this->getParam('loginname', $ln_optional, ''); + + if ($id <= 0 && empty($loginname)) { + throw new Exception("Either 'id' or 'loginname' parameter must be given", 406); + } + $this->logger()->logAction(ADM_ACTION, LOG_NOTICE, "[API] get customer #" . $id); $result_stmt = Database::prepare(" SELECT * FROM `" . TABLE_PANEL_CUSTOMERS . "` - WHERE `customerid` = :id" . ($this->getUserDetail('customers_see_all') ? '' : " AND `adminid` = :adminid")); + WHERE ".($id > 0 ? "`customerid` = :idln" : "`loginname` = :idln") . ($this->getUserDetail('customers_see_all') ? '' : " AND `adminid` = :adminid")); $params = array( - 'id' => $id + 'idln' => ($id <= 0 ? $loginname : $id) ); if ($this->getUserDetail('customers_see_all') == '0') { $params['adminid'] = $this->getUserDetail('adminid'); @@ -1096,10 +1105,12 @@ class Customers extends ApiCommand implements ResourceEntity } /** - * delete a customer entry by id + * delete a customer entry by either id or loginname * * @param int $id - * customer-id + * optional, the customer-id + * @param string $loginname + * optional, the loginname * @param bool $delete_userfiles * optional, default false * @@ -1109,13 +1120,21 @@ class Customers extends ApiCommand implements ResourceEntity public function delete() { if ($this->isAdmin()) { - $id = $this->getParam('id'); + $id = $this->getParam('id', true, 0); + $ln_optional = ($id <= 0 ? false : true); + $loginname = $this->getParam('loginname', $ln_optional, ''); $delete_userfiles = $this->getParam('delete_userfiles', true, 0); - + + if ($id <= 0 && empty($loginname)) { + throw new Exception("Either 'id' or 'loginname' parameter must be given", 406); + } + $json_result = Customers::getLocal($this->getUserData(), array( - 'id' => $id + 'id' => $id, + 'loginname' => $loginname ))->get(); $result = json_decode($json_result, true)['data']; + $id = $result['customerid']; // @fixme use Databases-ApiCommand later $databases_stmt = Database::prepare(" diff --git a/lib/classes/api/commands/class.Domains.php b/lib/classes/api/commands/class.Domains.php index 6d4085d3..90084e20 100644 --- a/lib/classes/api/commands/class.Domains.php +++ b/lib/classes/api/commands/class.Domains.php @@ -52,10 +52,12 @@ class Domains extends ApiCommand implements ResourceEntity } /** - * return a domain entry by id + * return a domain entry by either id or domainname * * @param int $id - * domain-id + * optional, the domain-id + * @param string $domainname + * optional, the domainname * @param boolean $no_std_subdomain * optional, default false * @@ -65,17 +67,30 @@ class Domains extends ApiCommand implements ResourceEntity public function get() { if ($this->isAdmin()) { - $id = $this->getParam('id'); + $id = $this->getParam('id', true, 0); + $dn_optional = ($id <= 0 ? false : true); + $domainname = $this->getParam('domainname', $dn_optional, ''); $no_std_subdomain = $this->getParam('no_std_subdomain', true, false); $this->logger()->logAction(ADM_ACTION, LOG_NOTICE, "[API] get domain #" . $id); + + if ($id <= 0 && empty($domainname)) { + throw new Exception("Either 'id' or 'domainname' parameter must be given", 406); + } + + // convert possible idn domain to punycode + if (substr($domainname, 0, 4) != 'xn--') { + $idna_convert = new idna_convert_wrapper(); + $domainname = $idna_convert->encode($domainname); + } + $result_stmt = Database::prepare(" SELECT `d`.*, `c`.`customerid` FROM `" . TABLE_PANEL_DOMAINS . "` `d` LEFT JOIN `" . TABLE_PANEL_CUSTOMERS . "` `c` USING(`customerid`) WHERE `d`.`parentdomainid` = '0' - AND `d`.`id` = :id" . ($no_std_subdomain ? ' AND `d.`id` <> `c`.`standardsubdomain`' : '') . ($this->getUserDetail('customers_see_all') ? '' : " AND `d`.`adminid` = :adminid")); + AND ".($id > 0 ? "`d`.`id` = :iddn" : "`d`.`domain` = :iddn") . ($no_std_subdomain ? ' AND `d.`id` <> `c`.`standardsubdomain`' : '') . ($this->getUserDetail('customers_see_all') ? '' : " AND `d`.`adminid` = :adminid")); $params = array( - 'id' => $id + 'iddn' => ($id <= 0 ? $domainname : $id) ); if ($this->getUserDetail('customers_see_all') == '0') { $params['adminid'] = $this->getUserDetail('adminid'); @@ -1539,10 +1554,12 @@ class Domains extends ApiCommand implements ResourceEntity } /** - * delete a domain entry by id + * delete a domain entry by either id or domainname * * @param int $id - * domain-id + * optional, the domain-id + * @param string $domainname + * optional, the domainname * @param bool $delete_mainsubdomains * optional, remove also domains that are subdomains of this domain but added as main domains; default false * @param bool $is_stdsubdomain @@ -1554,14 +1571,22 @@ class Domains extends ApiCommand implements ResourceEntity public function delete() { if ($this->isAdmin()) { - $id = $this->getParam('id'); + $id = $this->getParam('id', true, 0); + $dn_optional = ($id <= 0 ? false : true); + $domainname = $this->getParam('domainname', $dn_optional, ''); $is_stdsubdomain = $this->getParam('is_stdsubdomain', true, 0); $remove_subbutmain_domains = $this->getParam('delete_mainsubdomains', true, 0); - + + if ($id <= 0 && empty($domainname)) { + throw new Exception("Either 'id' or 'domainname' parameter must be given", 406); + } + $json_result = Domains::getLocal($this->getUserData(), array( - 'id' => $id + 'id' => $id, + 'domainname' => $domainname ))->get(); $result = json_decode($json_result, true)['data']; + $id = $result['id']; // check for deletion of main-domains which are logically subdomains, #329 $rsd_sql = ''; From 689ca853c3204d355cb80f045d53b71dac00fc3b Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Wed, 21 Feb 2018 14:38:27 +0100 Subject: [PATCH 0459/1335] minor fixes in Customers and Domains ApiCommands, added list() and get() for Admins-ApiCommand Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/commands/class.Admins.php | 123 +++++++++++++++++++ lib/classes/api/commands/class.Customers.php | 24 ++-- lib/classes/api/commands/class.Domains.php | 19 +-- 3 files changed, 146 insertions(+), 20 deletions(-) create mode 100644 lib/classes/api/commands/class.Admins.php diff --git a/lib/classes/api/commands/class.Admins.php b/lib/classes/api/commands/class.Admins.php new file mode 100644 index 00000000..65d53782 --- /dev/null +++ b/lib/classes/api/commands/class.Admins.php @@ -0,0 +1,123 @@ + (2010-) + * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt + * @package Panel + * + */ +class Admins extends ApiCommand implements ResourceEntity +{ + + /** + * lists all admin entries + * + * @return array count|list + */ + public function list() + { + if ($this->isAdmin() && $this->getUserDetail('change_serversettings') == 1) { + $this->logger()->logAction(ADM_ACTION, LOG_NOTICE, "[API] list admins"); + $result_stmt = Database::prepare(" + SELECT * + FROM `" . TABLE_PANEL_ADMINS . "` + ORDER BY `loginname` ASC + "); + Database::pexecute($result_stmt, null, true, true); + $result = array(); + while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) { + $result[] = $row; + } + return $this->response(200, "successfull", array( + 'count' => count($result), + 'list' => $result + )); + } + throw new Exception("Not allowed to execute given command.", 403); + } + + /** + * return an admin entry by either id or loginname + * + * @param int $id + * optional, the admin-id + * @param string $loginname + * optional, the loginname + * + * @throws Exception + * @return array + */ + public function get() + { + $id = $this->getParam('id', true, 0); + $ln_optional = ($id <= 0 ? false : true); + $loginname = $this->getParam('loginname', $ln_optional, ''); + + if ($id <= 0 && empty($loginname)) { + throw new Exception("Either 'id' or 'loginname' parameter must be given", 406); + } + + if ($this->isAdmin() && ($this->getUserDetail('change_serversettings') == 1 || ($this->getUserDetail('adminid') == $id || $this->getUserDetail('loginname') == $loginname))) { + $result_stmt = Database::prepare(" + SELECT * FROM `" . TABLE_PANEL_ADMINS . "` + WHERE " . ($id > 0 ? "`adminid` = :idln" : "`loginname` = :idln")); + $params = array( + 'idln' => ($id <= 0 ? $loginname : $id) + ); + $result = Database::pexecute_first($result_stmt, $params, true, true); + if ($result) { + $this->logger()->logAction(ADM_ACTION, LOG_NOTICE, "[API] get admin '" . $result['loginname'] . "'"); + return $this->response(200, "successfull", $result); + } + $key = ($id > 0 ? "id #" . $id : "loginname '" . $loginname . "'"); + throw new Exception("Admin with " . $key . " could not be found", 404); + } + throw new Exception("Not allowed to execute given command.", 403); + } + + public function add() + { + } + + public function update() + { + } + + /** + * delete a admin entry by either id or loginname + * + * @param int $id + * optional, the customer-id + * @param string $loginname + * optional, the loginname + * @param bool $delete_userfiles + * optional, default false + * + * @throws Exception + * @return array + */ + public function delete() + { + } + + /** + * unlock a locked admin by id + * + * @param int $id + * customer-id + * + * @throws Exception + * @return array + */ + public function unlock() + { + } +} diff --git a/lib/classes/api/commands/class.Customers.php b/lib/classes/api/commands/class.Customers.php index 852fafe9..015a09c5 100644 --- a/lib/classes/api/commands/class.Customers.php +++ b/lib/classes/api/commands/class.Customers.php @@ -27,10 +27,11 @@ class Customers extends ApiCommand implements ResourceEntity if ($this->isAdmin()) { $this->logger()->logAction(ADM_ACTION, LOG_NOTICE, "[API] list customers"); $result_stmt = Database::prepare(" - SELECT `c`.*, `a`.`loginname` AS `adminname` - FROM `" . TABLE_PANEL_CUSTOMERS . "` `c`, `" . TABLE_PANEL_ADMINS . "` `a` - WHERE " . ($this->getUserDetail('customers_see_all') ? '' : " `c`.`adminid` = :adminid AND ") . " - `c`.`adminid` = `a`.`adminid` + SELECT `c`.*, `a`.`loginname` AS `adminname` + FROM `" . TABLE_PANEL_CUSTOMERS . "` `c`, `" . TABLE_PANEL_ADMINS . "` `a` + WHERE " . ($this->getUserDetail('customers_see_all') ? '' : " `c`.`adminid` = :adminid AND ") . " + `c`.`adminid` = `a`.`adminid` + ORDER BY `c`.`loginname` ASC "); $params = array(); if ($this->getUserDetail('customers_see_all') == '0') { @@ -68,15 +69,14 @@ class Customers extends ApiCommand implements ResourceEntity $id = $this->getParam('id', true, 0); $ln_optional = ($id <= 0 ? false : true); $loginname = $this->getParam('loginname', $ln_optional, ''); - + if ($id <= 0 && empty($loginname)) { throw new Exception("Either 'id' or 'loginname' parameter must be given", 406); } - - $this->logger()->logAction(ADM_ACTION, LOG_NOTICE, "[API] get customer #" . $id); + $result_stmt = Database::prepare(" SELECT * FROM `" . TABLE_PANEL_CUSTOMERS . "` - WHERE ".($id > 0 ? "`customerid` = :idln" : "`loginname` = :idln") . ($this->getUserDetail('customers_see_all') ? '' : " AND `adminid` = :adminid")); + WHERE " . ($id > 0 ? "`customerid` = :idln" : "`loginname` = :idln") . ($this->getUserDetail('customers_see_all') ? '' : " AND `adminid` = :adminid")); $params = array( 'idln' => ($id <= 0 ? $loginname : $id) ); @@ -85,9 +85,11 @@ class Customers extends ApiCommand implements ResourceEntity } $result = Database::pexecute_first($result_stmt, $params, true, true); if ($result) { + $this->logger()->logAction(ADM_ACTION, LOG_NOTICE, "[API] get customer '" . $result['loginname'] . "'"); return $this->response(200, "successfull", $result); } - throw new Exception("Customer with id #" . $id . " could not be found", 404); + $key = ($id > 0 ? "id #" . $id : "loginname '" . $loginname . "'"); + throw new Exception("Customer with " . $key . " could not be found", 404); } throw new Exception("Not allowed to execute given command.", 403); } @@ -1124,11 +1126,11 @@ class Customers extends ApiCommand implements ResourceEntity $ln_optional = ($id <= 0 ? false : true); $loginname = $this->getParam('loginname', $ln_optional, ''); $delete_userfiles = $this->getParam('delete_userfiles', true, 0); - + if ($id <= 0 && empty($loginname)) { throw new Exception("Either 'id' or 'loginname' parameter must be given", 406); } - + $json_result = Customers::getLocal($this->getUserData(), array( 'id' => $id, 'loginname' => $loginname diff --git a/lib/classes/api/commands/class.Domains.php b/lib/classes/api/commands/class.Domains.php index 90084e20..0220095e 100644 --- a/lib/classes/api/commands/class.Domains.php +++ b/lib/classes/api/commands/class.Domains.php @@ -72,23 +72,23 @@ class Domains extends ApiCommand implements ResourceEntity $domainname = $this->getParam('domainname', $dn_optional, ''); $no_std_subdomain = $this->getParam('no_std_subdomain', true, false); $this->logger()->logAction(ADM_ACTION, LOG_NOTICE, "[API] get domain #" . $id); - + if ($id <= 0 && empty($domainname)) { throw new Exception("Either 'id' or 'domainname' parameter must be given", 406); } - + // convert possible idn domain to punycode if (substr($domainname, 0, 4) != 'xn--') { $idna_convert = new idna_convert_wrapper(); $domainname = $idna_convert->encode($domainname); } - + $result_stmt = Database::prepare(" SELECT `d`.*, `c`.`customerid` FROM `" . TABLE_PANEL_DOMAINS . "` `d` LEFT JOIN `" . TABLE_PANEL_CUSTOMERS . "` `c` USING(`customerid`) WHERE `d`.`parentdomainid` = '0' - AND ".($id > 0 ? "`d`.`id` = :iddn" : "`d`.`domain` = :iddn") . ($no_std_subdomain ? ' AND `d.`id` <> `c`.`standardsubdomain`' : '') . ($this->getUserDetail('customers_see_all') ? '' : " AND `d`.`adminid` = :adminid")); + AND " . ($id > 0 ? "`d`.`id` = :iddn" : "`d`.`domain` = :iddn") . ($no_std_subdomain ? ' AND `d.`id` <> `c`.`standardsubdomain`' : '') . ($this->getUserDetail('customers_see_all') ? '' : " AND `d`.`adminid` = :adminid")); $params = array( 'iddn' => ($id <= 0 ? $domainname : $id) ); @@ -99,7 +99,8 @@ class Domains extends ApiCommand implements ResourceEntity if ($result) { return $this->response(200, "successfull", $result); } - throw new Exception("Domain with id #" . $id . " could not be found", 404); + $key = ($id > 0 ? "id #" . $id : "domainname '" . $domainname . "'"); + throw new Exception("Domain with " . $key . " could not be found", 404); } throw new Exception("Not allowed to execute given command.", 403); } @@ -325,11 +326,11 @@ class Domains extends ApiCommand implements ResourceEntity $additional_ip_condition = ''; $aip_param = array(); } - + if (empty($p_ipandports)) { throw new Exception("No IPs given, unable to add domain (no default IPs set?)", 406); } - + $ipandports = array(); if (! empty($p_ipandport) && ! is_array($p_ipandports)) { $p_ipandports = unserialize($p_ipandports); @@ -1576,11 +1577,11 @@ class Domains extends ApiCommand implements ResourceEntity $domainname = $this->getParam('domainname', $dn_optional, ''); $is_stdsubdomain = $this->getParam('is_stdsubdomain', true, 0); $remove_subbutmain_domains = $this->getParam('delete_mainsubdomains', true, 0); - + if ($id <= 0 && empty($domainname)) { throw new Exception("Either 'id' or 'domainname' parameter must be given", 406); } - + $json_result = Domains::getLocal($this->getUserData(), array( 'id' => $id, 'domainname' => $domainname From f8fe4be3efff60de245ce6f6d4309347e198ba7d Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Wed, 21 Feb 2018 14:52:23 +0100 Subject: [PATCH 0460/1335] fix parameters for checkboxes when passed via webinterface Signed-off-by: Michael Kaufmann (d00p) --- templates/Sparkle/formfields/bool.tpl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/templates/Sparkle/formfields/bool.tpl b/templates/Sparkle/formfields/bool.tpl index 7f01b3d1..3ae7e894 100644 --- a/templates/Sparkle/formfields/bool.tpl +++ b/templates/Sparkle/formfields/bool.tpl @@ -1,4 +1,7 @@ {$label} - disabled="disabled"
    type="checkbox" name="{$fieldname}" value="1" checked="checked" /> + + + disabled="disabled" type="checkbox" name="{$fieldname}" value="1" checked="checked" /> + From c93e2678f755714bc857a53f157b198d57f03df7 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 22 Feb 2018 10:47:14 +0100 Subject: [PATCH 0461/1335] make Customers::update() also work with loginname as an alternative to the id Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/commands/class.Admins.php | 39 ++++++++++++++++++-- lib/classes/api/commands/class.Customers.php | 34 +++++++++++++---- 2 files changed, 61 insertions(+), 12 deletions(-) diff --git a/lib/classes/api/commands/class.Admins.php b/lib/classes/api/commands/class.Admins.php index 65d53782..9d23534f 100644 --- a/lib/classes/api/commands/class.Admins.php +++ b/lib/classes/api/commands/class.Admins.php @@ -95,7 +95,7 @@ class Admins extends ApiCommand implements ResourceEntity * delete a admin entry by either id or loginname * * @param int $id - * optional, the customer-id + * optional, the admin-id * @param string $loginname * optional, the loginname * @param bool $delete_userfiles @@ -109,15 +109,46 @@ class Admins extends ApiCommand implements ResourceEntity } /** - * unlock a locked admin by id + * unlock a locked admin by either id or loginname * * @param int $id - * customer-id - * + * optional, the admin-id + * @param string $loginname + * optional, the loginname + * * @throws Exception * @return array */ public function unlock() { + if ($this->isAdmin()) { + $id = $this->getParam('id', true, 0); + $ln_optional = ($id <= 0 ? false : true); + $loginname = $this->getParam('loginname', $ln_optional, ''); + + if ($id <= 0 && empty($loginname)) { + throw new Exception("Either 'id' or 'loginname' parameter must be given", 406); + } + + $json_result = Admins::getLocal($this->getUserData(), array( + 'id' => $id, + 'loginname' => $loginname + ))->get(); + $result = json_decode($json_result, true)['data']; + $id = $result['adminid']; + + $result_stmt = Database::prepare(" + UPDATE `" . TABLE_PANEL_ADMINS . "` SET + `loginfail_count` = '0' + WHERE `adminid`= :id + "); + Database::pexecute($result_stmt, array( + 'id' => $id + ), true, true); + + $this->logger()->logAction(ADM_ACTION, LOG_WARNING, "[API] unlocked admin '" . $result['loginname'] . "'"); + return $this->response(200, "successfull", $result); + } + throw new Exception("Not allowed to execute given command.", 403); } } diff --git a/lib/classes/api/commands/class.Customers.php b/lib/classes/api/commands/class.Customers.php index 015a09c5..4aeadf28 100644 --- a/lib/classes/api/commands/class.Customers.php +++ b/lib/classes/api/commands/class.Customers.php @@ -646,12 +646,20 @@ class Customers extends ApiCommand implements ResourceEntity public function update() { if ($this->isAdmin()) { - $id = $this->getParam('id'); - + $id = $this->getParam('id', true, 0); + $ln_optional = ($id <= 0 ? false : true); + $loginname = $this->getParam('loginname', $ln_optional, ''); + + if ($id <= 0 && empty($loginname)) { + throw new Exception("Either 'id' or 'loginname' parameter must be given", 406); + } + $json_result = Customers::getLocal($this->getUserData(), array( - 'id' => $id + 'id' => $id, + 'loginname' => $loginname ))->get(); $result = json_decode($json_result, true)['data']; + $id = $result['customerid']; // parameters $move_to_admin = intval_ressource($this->getParam('move_to_admin', true, 0)); @@ -1354,10 +1362,12 @@ class Customers extends ApiCommand implements ResourceEntity } /** - * unlock a locked customer by id + * unlock a locked customer by either id or loginname * * @param int $id - * customer-id + * optional, the customer-id + * @param string $loginname + * optional, the loginname * * @throws Exception * @return array @@ -1365,12 +1375,20 @@ class Customers extends ApiCommand implements ResourceEntity public function unlock() { if ($this->isAdmin()) { - $id = $this->getParam('id'); - + $id = $this->getParam('id', true, 0); + $ln_optional = ($id <= 0 ? false : true); + $loginname = $this->getParam('loginname', $ln_optional, ''); + + if ($id <= 0 && empty($loginname)) { + throw new Exception("Either 'id' or 'loginname' parameter must be given", 406); + } + $json_result = Customers::getLocal($this->getUserData(), array( - 'id' => $id + 'id' => $id, + 'loginname' => $loginname ))->get(); $result = json_decode($json_result, true)['data']; + $id = $result['customerid']; $result_stmt = Database::prepare(" UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET From 332e29be24b6e228c0f4efec8ae4d4d5a4657413 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 22 Feb 2018 11:16:55 +0100 Subject: [PATCH 0462/1335] lots of phpdoc; fix Customers::update() Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/abstract.ApiCommand.php | 93 +++++++++++++++++++ lib/classes/api/api_includes.inc.php | 16 ++++ lib/classes/api/commands/class.Admins.php | 3 +- lib/classes/api/commands/class.Customers.php | 30 +++++- lib/classes/api/commands/class.Domains.php | 15 ++- lib/classes/api/commands/class.FpmDaemons.php | 20 +++- lib/classes/api/commands/class.Froxlor.php | 9 +- .../api/commands/class.IpsAndPorts.php | 3 +- .../api/commands/class.PhpSettings.php | 18 +++- lib/classes/api/interface.ResourceEntity.php | 15 +++ 10 files changed, 211 insertions(+), 11 deletions(-) diff --git a/lib/classes/api/abstract.ApiCommand.php b/lib/classes/api/abstract.ApiCommand.php index abf2eda8..1dafc054 100644 --- a/lib/classes/api/abstract.ApiCommand.php +++ b/lib/classes/api/abstract.ApiCommand.php @@ -1,20 +1,76 @@ (2010-) + * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt + * @package API + * @since 0.10.0 + * + */ abstract class ApiCommand { + /** + * debug flag + * + * @var boolean + */ private $debug = true; + /** + * is admin flag + * + * @var boolean + */ private $is_admin = false; + /** + * internal user data array + * + * @var array + */ private $user_data = null; + /** + * logger interface + * + * @var FroxlorLogger + */ private $logger = null; + /** + * mail interface + * + * @var PHPMailer + */ private $mail = null; + /** + * array of parameters passed to the command + * + * @var array + */ private $cmd_params = null; + /** + * + * @param array $header + * optional, passed via API + * @param array $params + * optional, array of parameters (var=>value) for the command + * @param array $userinfo + * optional, passed via WebInterface (instead of $header) + * + * @throws Exception + */ public function __construct($header = null, $params = null, $userinfo = null) { global $lng; @@ -38,6 +94,10 @@ abstract class ApiCommand } } + /** + * initialize global $lng variable to have + * localized strings available for the ApiCommands + */ private function initLang() { global $lng; @@ -77,6 +137,9 @@ abstract class ApiCommand include_once makeSecurePath(FROXLOR_INSTALL_DIR . '/lng/lng_references.php'); } + /** + * initialize mail interface so an API wide mail-object is available + */ private function initMail() { /** @@ -108,6 +171,18 @@ abstract class ApiCommand } } + /** + * returns an instance of the wanted ApiCommand (e.g. + * Customers, Domains, etc); + * this is used widely in the WebInterface + * + * @param array $userinfo + * array of user-data + * @param array $params + * array of parameters for the command + * + * @return ApiCommand + */ public static function getLocal($userinfo = null, $params = null) { return new static(null, $params, $userinfo); @@ -276,6 +351,15 @@ abstract class ApiCommand return $this->mail; } + /** + * return api-compatible response in JSON format and send corresponding http-header + * + * @param int $status + * @param string $status_message + * @param mixed $data + * + * @return string json-encoded response message + */ protected function response($status, $status_message, $data = null) { header("HTTP/1.1 " . $status); @@ -288,6 +372,15 @@ abstract class ApiCommand return $json_response; } + /** + * read user data from database by api-request-header fields + * + * @param array $header + * api-request header + * + * @throws Exception + * @return boolean + */ private function readUserData($header = null) { $sel_stmt = Database::prepare("SELECT * FROM `api_keys` WHERE `apikey` = :ak AND `secret` = :as"); diff --git a/lib/classes/api/api_includes.inc.php b/lib/classes/api/api_includes.inc.php index 0e0eb0ad..f7446b51 100644 --- a/lib/classes/api/api_includes.inc.php +++ b/lib/classes/api/api_includes.inc.php @@ -1,4 +1,20 @@ (2010-) + * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt + * @package API + * @since 0.10.0 + * + */ if (! defined('FROXLOR_INSTALL_DIR')) { define('FROXLOR_INSTALL_DIR', dirname(dirname(dirname(__DIR__)))); require_once FROXLOR_INSTALL_DIR . '/lib/tables.inc.php'; diff --git a/lib/classes/api/commands/class.Admins.php b/lib/classes/api/commands/class.Admins.php index 9d23534f..6a095e0e 100644 --- a/lib/classes/api/commands/class.Admins.php +++ b/lib/classes/api/commands/class.Admins.php @@ -11,7 +11,8 @@ * @copyright (c) the authors * @author Froxlor team (2010-) * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt - * @package Panel + * @package API + * @since 0.10.0 * */ class Admins extends ApiCommand implements ResourceEntity diff --git a/lib/classes/api/commands/class.Customers.php b/lib/classes/api/commands/class.Customers.php index 4aeadf28..bc2fb55b 100644 --- a/lib/classes/api/commands/class.Customers.php +++ b/lib/classes/api/commands/class.Customers.php @@ -11,7 +11,8 @@ * @copyright (c) the authors * @author Froxlor team (2010-) * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt - * @package Panel + * @package API + * @since 0.10.0 * */ class Customers extends ApiCommand implements ResourceEntity @@ -724,7 +725,32 @@ class Customers extends ApiCommand implements ResourceEntity if (Settings::Get('ticket.enabled') != '1') { $tickets = - 1; } - + + $diskspace = $diskspace * 1024; + $traffic = $traffic * 1024 * 1024; + + if (((($this->getUserDetail('diskspace_used') + $diskspace - $result['diskspace']) > $this->getUserDetail('diskspace')) && ($this->getUserDetail('diskspace') / 1024) != '-1') + || ((($this->getUserDetail('mysqls_used') + $mysqls - $result['mysqls']) > $this->getUserDetail('mysqls')) && $this->getUserDetail('mysqls') != '-1') + || ((($this->getUserDetail('emails_used') + $emails - $result['emails']) > $this->getUserDetail('emails')) && $this->getUserDetail('emails') != '-1') + || ((($this->getUserDetail('email_accounts_used') + $email_accounts - $result['email_accounts']) > $this->getUserDetail('email_accounts')) && $this->getUserDetail('email_accounts') != '-1') + || ((($this->getUserDetail('email_forwarders_used') + $email_forwarders - $result['email_forwarders']) > $this->getUserDetail('email_forwarders')) && $this->getUserDetail('email_forwarders') != '-1') + || ((($this->getUserDetail('email_quota_used') + $email_quota - $result['email_quota']) > $this->getUserDetail('email_quota')) && $this->getUserDetail('email_quota') != '-1' && Settings::Get('system.mail_quota_enabled') == '1') + || ((($this->getUserDetail('ftps_used') + $ftps - $result['ftps']) > $this->getUserDetail('ftps')) && $this->getUserDetail('ftps') != '-1') + || ((($this->getUserDetail('tickets_used') + $tickets - $result['tickets']) > $this->getUserDetail('tickets')) && $this->getUserDetail('tickets') != '-1') + || ((($this->getUserDetail('subdomains_used') + $subdomains - $result['subdomains']) > $this->getUserDetail('subdomains')) && $this->getUserDetail('subdomains') != '-1') + || (($diskspace / 1024) == '-1' && ($this->getUserDetail('diskspace') / 1024) != '-1') + || ($mysqls == '-1' && $this->getUserDetail('mysqls') != '-1') + || ($emails == '-1' && $this->getUserDetail('emails') != '-1') + || ($email_accounts == '-1' && $this->getUserDetail('email_accounts') != '-1') + || ($email_forwarders == '-1' && $this->getUserDetail('email_forwarders') != '-1') + || ($email_quota == '-1' && $this->getUserDetail('email_quota') != '-1' && Settings::Get('system.mail_quota_enabled') == '1') + || ($ftps == '-1' && $this->getUserDetail('ftps') != '-1') + || ($tickets == '-1' && $this->getUserDetail('tickets') != '-1') + || ($subdomains == '-1' && $this->getUserDetail('subdomains') != '-1') + ) { + standard_error('youcantallocatemorethanyouhave', '', true); + } + // Either $name and $firstname or the $company must be inserted if ($name == '' && $company == '') { standard_error(array( diff --git a/lib/classes/api/commands/class.Domains.php b/lib/classes/api/commands/class.Domains.php index 0220095e..92866870 100644 --- a/lib/classes/api/commands/class.Domains.php +++ b/lib/classes/api/commands/class.Domains.php @@ -11,7 +11,8 @@ * @copyright (c) the authors * @author Froxlor team (2010-) * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt - * @package Panel + * @package API + * @since 0.10.0 * */ class Domains extends ApiCommand implements ResourceEntity @@ -759,14 +760,22 @@ class Domains extends ApiCommand implements ResourceEntity if ($this->isAdmin() && $this->getUserDetail('change_serversettings')) { // parameters - $id = $this->getParam('id'); - + $id = $this->getParam('id', true, 0); + $dn_optional = ($id <= 0 ? false : true); + $domainname = $this->getParam('domainname', $dn_optional, ''); + + if ($id <= 0 && empty($domainname)) { + throw new Exception("Either 'id' or 'domainname' parameter must be given", 406); + } + // get requested domain $json_result = Domains::getLocal($this->getUserData(), array( 'id' => $id, + 'domainname' => $domainname, 'no_std_subdomain' => true ))->get(); $result = json_decode($json_result, true)['data']; + $id = $result['id']; // optional parameters $p_domain = $this->getParam('domain', true, $result['domain']); diff --git a/lib/classes/api/commands/class.FpmDaemons.php b/lib/classes/api/commands/class.FpmDaemons.php index 9d906593..205d6be8 100644 --- a/lib/classes/api/commands/class.FpmDaemons.php +++ b/lib/classes/api/commands/class.FpmDaemons.php @@ -11,14 +11,15 @@ * @copyright (c) the authors * @author Froxlor team (2010-) * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt - * @package Panel + * @package API + * @since 0.10.0 * */ class FpmDaemons extends ApiCommand implements ResourceEntity { /** - * lists all php-config entries + * lists all fpm-daemon entries * * @return array count|list */ @@ -66,6 +67,13 @@ class FpmDaemons extends ApiCommand implements ResourceEntity throw new Exception("Not allowed to execute given command.", 403); } + /** + * return a fpm-daemon entry by id + * + * @param int $id fpm-daemon-id + * + * @return array + */ public function get() { if ($this->isAdmin()) { @@ -238,6 +246,14 @@ class FpmDaemons extends ApiCommand implements ResourceEntity throw new Exception("Not allowed to execute given command.", 403); } + /** + * delete a fpm-daemon entry by id + * + * @param int $id fpm-daemon-id + * + * @throws Exception + * @return array + */ public function delete() { if ($this->isAdmin() && $this->getUserDetail('change_serversettings') == 1) { diff --git a/lib/classes/api/commands/class.Froxlor.php b/lib/classes/api/commands/class.Froxlor.php index 4d6d1ea3..b005634c 100644 --- a/lib/classes/api/commands/class.Froxlor.php +++ b/lib/classes/api/commands/class.Froxlor.php @@ -11,12 +11,19 @@ * @copyright (c) the authors * @author Froxlor team (2010-) * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt - * @package Panel + * @package API + * @since 0.10.0 * */ class Froxlor extends ApiCommand { + /** + * checks whether there is a newer version of froxlor available + * + * @throws Exception + * @return string + */ public function checkUpdate() { global $version, $branding; diff --git a/lib/classes/api/commands/class.IpsAndPorts.php b/lib/classes/api/commands/class.IpsAndPorts.php index 596a0a42..fe0affa0 100644 --- a/lib/classes/api/commands/class.IpsAndPorts.php +++ b/lib/classes/api/commands/class.IpsAndPorts.php @@ -11,7 +11,8 @@ * @copyright (c) the authors * @author Froxlor team (2010-) * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt - * @package Panel + * @package API + * @since 0.10.0 * */ class IpsAndPorts extends ApiCommand implements ResourceEntity diff --git a/lib/classes/api/commands/class.PhpSettings.php b/lib/classes/api/commands/class.PhpSettings.php index 686da052..5507e454 100644 --- a/lib/classes/api/commands/class.PhpSettings.php +++ b/lib/classes/api/commands/class.PhpSettings.php @@ -11,7 +11,8 @@ * @copyright (c) the authors * @author Froxlor team (2010-) * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt - * @package Panel + * @package API + * @since 0.10.0 * */ class PhpSettings extends ApiCommand implements ResourceEntity @@ -100,6 +101,13 @@ class PhpSettings extends ApiCommand implements ResourceEntity throw new Exception("Not allowed to execute given command.", 403); } + /** + * return a php-config entry by id + * + * @param int $id php-settings-id + * + * @return array + */ public function get() { if ($this->isAdmin()) { @@ -320,6 +328,14 @@ class PhpSettings extends ApiCommand implements ResourceEntity throw new Exception("Not allowed to execute given command.", 403); } + /** + * delete a php-config entry by id + * + * @param int $id php-config-id + * + * @throws Exception + * @return array + */ public function delete() { if ($this->isAdmin() && $this->getUserDetail('change_serversettings') == 1) { diff --git a/lib/classes/api/interface.ResourceEntity.php b/lib/classes/api/interface.ResourceEntity.php index 6361eb46..b10713a8 100644 --- a/lib/classes/api/interface.ResourceEntity.php +++ b/lib/classes/api/interface.ResourceEntity.php @@ -1,5 +1,20 @@ (2010-) + * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt + * @package API + * @since 0.10.0 + * + */ interface ResourceEntity { From c1875132ef3da5b8f535a8c5d18cf34e8c701e25 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 22 Feb 2018 11:34:40 +0100 Subject: [PATCH 0463/1335] fix unchecked-checkbox value passed to API Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/output/class.htmlform.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/classes/output/class.htmlform.php b/lib/classes/output/class.htmlform.php index 6f1c4a3b..c7ded086 100644 --- a/lib/classes/output/class.htmlform.php +++ b/lib/classes/output/class.htmlform.php @@ -283,7 +283,12 @@ class htmlform } } } - $output .= ''; + $output .= ''; } return $output; From a468fe50df22fe82abd34a620a301d856ea4eac6 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 22 Feb 2018 11:42:13 +0100 Subject: [PATCH 0464/1335] filter deactivated users who want to use the API; fix error-output in Customers::update() Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/abstract.ApiCommand.php | 6 ++++++ lib/classes/api/commands/class.Customers.php | 12 ++++++------ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/lib/classes/api/abstract.ApiCommand.php b/lib/classes/api/abstract.ApiCommand.php index 1dafc054..f499140b 100644 --- a/lib/classes/api/abstract.ApiCommand.php +++ b/lib/classes/api/abstract.ApiCommand.php @@ -86,6 +86,12 @@ abstract class ApiCommand } $this->logger = FroxlorLogger::getInstanceOf($this->user_data); + // check whether the user is deactivated + if ($this->getUserDetail('deactivated') == 1) { + $this->logger()->logAction(LOG_ERROR, LOG_INFO, "[API] User '" . $this->getUserDetail('loginnname') . "' tried to use API but is deactivated"); + throw new Exception("Account suspended", 406); + } + $this->initLang(); $this->initMail(); diff --git a/lib/classes/api/commands/class.Customers.php b/lib/classes/api/commands/class.Customers.php index bc2fb55b..bfad0ba9 100644 --- a/lib/classes/api/commands/class.Customers.php +++ b/lib/classes/api/commands/class.Customers.php @@ -199,7 +199,7 @@ class Customers extends ApiCommand implements ResourceEntity standard_error(array( 'stringisempty', 'myname' - )); + ), '', true); } elseif ($firstname == '' && $company == '') { standard_error(array( 'stringisempty', @@ -756,23 +756,23 @@ class Customers extends ApiCommand implements ResourceEntity standard_error(array( 'stringisempty', 'myname' - )); + ), '', true); } elseif ($firstname == '' && $company == '') { standard_error(array( 'stringisempty', 'myfirstname' - )); + ), '', true); } elseif ($email == '') { standard_error(array( 'stringisempty', 'emailadd' - )); + ), '', true); } elseif (! validateEmail($email)) { - standard_error('emailiswrong', $email); + standard_error('emailiswrong', $email, true); } else { if ($password != '') { - $password = validatePassword($password); + $password = validatePassword($password, true); $password = makeCryptPassword($password); } else { $password = $result['password']; From b42a7b1b26d434b41626a3ac2db8637f69e5c2be Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 22 Feb 2018 13:41:28 +0100 Subject: [PATCH 0465/1335] show basic api doc in webinterface (top-menu - options - API help) Signed-off-by: Michael Kaufmann (d00p) --- admin_index.php | 4 + apihelp.php | 112 +++++++++++++++++++++ customer_index.php | 3 + lib/classes/api/commands/class.Froxlor.php | 30 ++++-- lib/navigation/00.froxlor.main.php | 10 ++ lng/english.lng.php | 3 + lng/german.lng.php | 3 + templates/Sparkle/apihelp/index.tpl | 13 +++ templates/Sparkle/header.tpl | 3 + 9 files changed, 171 insertions(+), 10 deletions(-) create mode 100644 apihelp.php create mode 100644 templates/Sparkle/apihelp/index.tpl diff --git a/admin_index.php b/admin_index.php index 174f65a0..959a4632 100644 --- a/admin_index.php +++ b/admin_index.php @@ -51,6 +51,7 @@ if (isset($_POST['id'])) { } if ($page == 'overview') { + $log->logAction(ADM_ACTION, LOG_NOTICE, "viewed admin_index"); $overview_stmt = Database::prepare("SELECT COUNT(*) AS `number_customers`, SUM(`diskspace_used`) AS `diskspace_used`, @@ -404,3 +405,6 @@ if ($page == 'overview') { redirectTo($filename, array('s' => $s)); } } +elseif ($page == 'apihelp' && Settings::Get('api.enabled') == 1) { + require_once __DIR__ . '/apihelp.php'; +} diff --git a/apihelp.php b/apihelp.php new file mode 100644 index 00000000..2f426f3a --- /dev/null +++ b/apihelp.php @@ -0,0 +1,112 @@ + (2018-) + * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt + * @package Panel + * @since 0.10.0 + * + */ + +// This file is being included in admin_index and customer_index +// and therefore does not need to require lib/init.php + +try { + $json_result = Froxlor::getLocal($userinfo)->listFunctions(); +} catch (Exception $e) { + dynamic_error($e->getMessage()); +} +$result = json_decode($json_result, true)['data']; + +// get response data +$m_arr = $result; + +// initialize output-array +$output_arr = array(); + +// check every module +foreach ($m_arr as $module) { + + // initialize module array for sorting + if (! isset($output_arr[$module['module']]) || ! is_array($output_arr[$module['module']])) { + $output_arr[$module['module']] = array(); + } + + // set necessary data + $output_arr[$module['module']][$module['function']] = array( + 'return_type' => (isset($module['return']['type']) && $module['return']['type'] != "" ? $module['return']['type'] : - 1), + 'params_list' => array(), + 'head' => $module['head'] + ); + + if (isset($module['params']) && is_array($module['params'])) { + foreach ($module['params'] as $param) { + $output_arr[$module['module']][$module['function']]['params_list'][] = array( + 'type' => $param['type'], + 'name' => $param['parameter'], + 'desc' => $param['desc'] + ); + } + } +} + +// sort array +ksort($output_arr); + +$apihelp = ""; + +// output ALL the modules +foreach ($output_arr as $module => $functions) { + + // sort by function + ksort($functions); + + // output ALL the functions + foreach ($functions as $function => $funcdata) { + $apihelp .= "
    "; + $apihelp .= "

    " . ($funcdata['return_type'] == - 1 ? "no-return-type" : $funcdata['return_type']) . " "; + $apihelp .= "" . $module . "." . $function . "

    "; + // description + if (strtoupper(substr($funcdata['head'], 0, 4)) == "TODO") + $apihelp .= ""; + $apihelp .= $funcdata['head']; + if (strtoupper(substr($funcdata['head'], 0, 4)) == "TODO") + $apihelp .= ""; + // output ALL the params; + if (count($funcdata['params_list']) > 0) { + $parms = "

    Parameters:
      "; + // separate and format them + foreach ($funcdata['params_list'] as $index => $param) { + $parms .= "
    • "; + // check whether the parameter is optional + if (! empty($param['desc']) && strtolower(substr(trim($param['desc']), 0, 8)) == "optional") { + $parms .= "optional "; + $param['desc'] = substr(trim($param['desc']), 8); + if (substr($param['desc'], 0, 1) == ',') { + $param['desc'] = substr(trim($param['desc']), 1); + } + } + $parms .= "" . (strtolower($param['type']) == 'unknown' ? "unknown" : $param['type']) . " " . $param['name'] . ""; + if (! empty($param['desc'])) { + $parms .= " " . trim($param['desc']); + } + $parms .= "
    • "; + } + $apihelp .= "
    " . $parms; + } + $apihelp .= "


    "; + } + $apihelp .= "
    "; +} + +eval("echo \"" . getTemplate("apihelp/index", 1) . "\";"); diff --git a/customer_index.php b/customer_index.php index bb1305c9..71877c71 100644 --- a/customer_index.php +++ b/customer_index.php @@ -319,3 +319,6 @@ if ($page == 'overview') { redirectTo($filename, array('s' => $s)); } } +elseif ($page == 'apihelp' && Settings::Get('api.enabled') == 1) { + require_once __DIR__ . '/apihelp.php'; +} diff --git a/lib/classes/api/commands/class.Froxlor.php b/lib/classes/api/commands/class.Froxlor.php index b005634c..99a49a28 100644 --- a/lib/classes/api/commands/class.Froxlor.php +++ b/lib/classes/api/commands/class.Froxlor.php @@ -20,7 +20,7 @@ class Froxlor extends ApiCommand /** * checks whether there is a newer version of froxlor available - * + * * @throws Exception * @return string */ @@ -178,6 +178,7 @@ class Froxlor extends ApiCommand $clines = explode("\n", $comment); $result = array(); $result['params'] = array(); + $param_desc = false; foreach ($clines as $c) { $c = trim($c); // check param-section @@ -189,6 +190,7 @@ class Froxlor extends ApiCommand 'type' => $r[1], 'desc' => (isset($r[3]) ? trim($r['3']) : '') ); + $param_desc = true; } // check return-section elseif (strpos($c, '@return')) { preg_match('/^\*\s\@return\s(\w+)(\s.*)?/', $c, $r); @@ -201,18 +203,26 @@ class Froxlor extends ApiCommand 'desc' => (isset($r[2]) ? trim($r[2]) : '') ); } else if (! empty($c) && strpos($c, '@throws') === false) { - if (substr($c, 0, 3) == "/**") + if (substr($c, 0, 3) == "/**") { continue; - if (substr($c, 0, 2) == "*/") + } + if (substr($c, 0, 2) == "*/") { continue; - if (substr($c, 0, 1) == "*") + } + if (substr($c, 0, 1) == "*") { $c = trim(substr($c, 1)); - if (empty($c)) - continue; - if (! isset($result['head']) || empty($result['head'])) { - $result['head'] = $c . " "; - } else { - $result['head'] .= $c . " "; + if (empty($c)) { + continue; + } + if ($param_desc) { + $result['params'][count($result['params']) - 1]['desc'] .= $c; + } else { + if (! isset($result['head']) || empty($result['head'])) { + $result['head'] = $c . " "; + } else { + $result['head'] .= $c . " "; + } + } } } } diff --git a/lib/navigation/00.froxlor.main.php b/lib/navigation/00.froxlor.main.php index a02a3a07..61c7fe6f 100644 --- a/lib/navigation/00.froxlor.main.php +++ b/lib/navigation/00.froxlor.main.php @@ -38,6 +38,11 @@ return array( 'label' => $lng['menue']['main']['changetheme'], 'show_element' => (Settings::Get('panel.allow_theme_change_customer') == true) ), + array( + 'url' => 'customer_index.php?page=apihelp', + 'label' => $lng['menue']['main']['apihelp'], + 'show_element' => (Settings::Get('api.enabled') == true) + ), array( 'url' => 'customer_index.php?action=logout', 'label' => $lng['login']['logout'] @@ -179,6 +184,11 @@ return array( 'label' => $lng['menue']['main']['changetheme'], 'show_element' => (Settings::Get('panel.allow_theme_change_admin') == true) ), + array( + 'url' => 'admin_index.php?page=apihelp', + 'label' => $lng['menue']['main']['apihelp'], + 'show_element' => (Settings::Get('api.enabled') == true) + ), array( 'url' => 'admin_index.php?action=logout', 'label' => $lng['login']['logout'] diff --git a/lng/english.lng.php b/lng/english.lng.php index 8adc8a40..b0f87d35 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -2119,4 +2119,7 @@ $lng['admin']['plans']['use_plan'] = 'Apply plan'; $lng['question']['plan_reallydelete'] = 'Do you really want to delete the hosting plan %s?'; $lng['admin']['notryfiles']['title'] = 'No autogenerated try_files'; $lng['admin']['notryfiles']['description'] = 'Say yes here if you want to specify a custom try_files directive in specialsettings (needed for some wordpress plugins for example).'; + +// added in froxlor 0.10.0 $lng['panel']['none_value'] = 'None'; +$lng['menue']['main']['apihelp'] = 'API help'; diff --git a/lng/german.lng.php b/lng/german.lng.php index 888303b1..4693d860 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -1769,4 +1769,7 @@ $lng['admin']['plans']['use_plan'] = 'Plan übernehmen'; $lng['question']['plan_reallydelete'] = 'Wollen Sie den Hosting-Plan "%s" wirklich löschen?'; $lng['admin']['notryfiles']['title'] = 'Keine generierte try_files Anweisung'; $lng['admin']['notryfiles']['description'] = 'Wähle "Ja", wenn eine eigene try_files Direktive in den "eigenen Vhost Einstellungen" angegeben werden soll (z.B. nötig für manche Wordpress Plugins).'; + +// added in froxlor 0.10.0 $lng['panel']['none_value'] = 'Keine'; +$lng['menue']['main']['apihelp'] = 'API Hilfe'; diff --git a/templates/Sparkle/apihelp/index.tpl b/templates/Sparkle/apihelp/index.tpl new file mode 100644 index 00000000..793a57a8 --- /dev/null +++ b/templates/Sparkle/apihelp/index.tpl @@ -0,0 +1,13 @@ +$header +
    +

    + API help +

    + +
    +
    + {$apihelp} +
    + +
    +$footer diff --git a/templates/Sparkle/header.tpl b/templates/Sparkle/header.tpl index 2909fc6d..d858f52c 100644 --- a/templates/Sparkle/header.tpl +++ b/templates/Sparkle/header.tpl @@ -67,6 +67,9 @@
  • {$lng['panel']['theme']}
  • + +
  • {$lng['menue']['main']['apihelp']}
  • +
  • {$lng['login']['logout']}
  • From dfb5d33a561f803e315bc3868e7548cebe482ba4 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 22 Feb 2018 16:51:56 +0100 Subject: [PATCH 0466/1335] add Admins.add() Signed-off-by: Michael Kaufmann (d00p) --- admin_admins.php | 245 +----------------- lib/classes/api/commands/class.Admins.php | 218 +++++++++++++++- lib/classes/api/commands/class.Customers.php | 34 +-- .../api/commands/class.PhpSettings.php | 8 +- 4 files changed, 237 insertions(+), 268 deletions(-) diff --git a/admin_admins.php b/admin_admins.php index 4e5a54d5..ce0f8488 100644 --- a/admin_admins.php +++ b/admin_admins.php @@ -201,247 +201,12 @@ if ($page == 'admins' if (isset($_POST['send']) && $_POST['send'] == 'send' ) { - - $name = validate($_POST['name'], 'name'); - $email = $idna_convert->encode(validate($_POST['email'], 'email')); - - $custom_notes = validate(str_replace("\r\n", "\n", $_POST['custom_notes']), 'custom_notes', '/^[^\0]*$/'); - $custom_notes_show = 0; - if (isset($_POST['custom_notes_show'])) { - $custom_notes_show = intval_ressource($_POST['custom_notes_show']); + try { + Admins::getLocal($userinfo, $_POST)->add(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); } - - $loginname = validate($_POST['loginname'], 'loginname'); - $password = validate($_POST['admin_password'], 'password'); - $password = validatePassword($password); - $def_language = validate($_POST['def_language'], 'default language'); - - $customers = intval_ressource($_POST['customers']); - if (isset($_POST['customers_ul'])) { - $customers = -1; - } - - $domains = intval_ressource($_POST['domains']); - if (isset($_POST['domains_ul'])) { - $domains = -1; - } - - $subdomains = intval_ressource($_POST['subdomains']); - if (isset($_POST['subdomains_ul'])) { - $subdomains = -1; - } - - $emails = intval_ressource($_POST['emails']); - if (isset($_POST['emails_ul'])) { - $emails = -1; - } - - $email_accounts = intval_ressource($_POST['email_accounts']); - if (isset($_POST['email_accounts_ul'])) { - $email_accounts = -1; - } - - $email_forwarders = intval_ressource($_POST['email_forwarders']); - if (isset($_POST['email_forwarders_ul'])) { - $email_forwarders = -1; - } - - if (Settings::Get('system.mail_quota_enabled') == '1') { - - $email_quota = validate($_POST['email_quota'], 'email_quota', '/^\d+$/', 'vmailquotawrong', array('0', '')); - if (isset($_POST['email_quota_ul'])) { - $email_quota = -1; - } - } else { - $email_quota = -1; - } - - $ftps = intval_ressource($_POST['ftps']); - if (isset($_POST['ftps_ul'])) { - $ftps = -1; - } - - if (Settings::Get('ticket.enabled') == 1) { - - $tickets = intval_ressource($_POST['tickets']); - if (isset($_POST['tickets_ul'])) { - $tickets = -1; - } - } else { - $tickets = 0; - } - - $mysqls = intval_ressource($_POST['mysqls']); - if (isset($_POST['mysqls_ul'])) { - $mysqls = -1; - } - - $customers_see_all = 0; - if (isset($_POST['customers_see_all'])) { - $customers_see_all = intval($_POST['customers_see_all']); - } - - $domains_see_all = 0; - if (isset($_POST['domains_see_all'])) { - $domains_see_all = intval($_POST['domains_see_all']); - } - - $caneditphpsettings = 0; - if (isset($_POST['caneditphpsettings'])) { - $caneditphpsettings = intval($_POST['caneditphpsettings']); - } - - $change_serversettings = 0; - if (isset($_POST['change_serversettings'])) { - $change_serversettings = intval($_POST['change_serversettings']); - } - - $diskspace = intval_ressource($_POST['diskspace']); - if (isset($_POST['diskspace_ul'])) { - $diskspace = -1; - } - - $traffic = doubleval_ressource($_POST['traffic']); - if (isset($_POST['traffic_ul'])) { - $traffic = -1; - } - - $tickets_see_all = 0; - if (isset($_POST['tickets_see_all'])) { - $tickets_see_all = intval($_POST['tickets_see_all']); - } - - $diskspace = $diskspace * 1024; - $traffic = $traffic * 1024 * 1024; - $ipaddress = intval_ressource($_POST['ipaddress']); - - // Check if the account already exists - $loginname_check_stmt = Database::prepare(" - SELECT `loginname` FROM `" . TABLE_PANEL_CUSTOMERS . "` WHERE `loginname` = :login - "); - $loginname_check = Database::pexecute_first($loginname_check_stmt, array('login' => $loginname)); - - $loginname_check_admin_stmt = Database::prepare(" - SELECT `loginname` FROM `" . TABLE_PANEL_ADMINS . "` WHERE `loginname` = :login - "); - $loginname_check_admin = Database::pexecute_first($loginname_check_admin_stmt, array('login' => $loginname)); - - if ($loginname == '') { - standard_error(array('stringisempty', 'myloginname')); - } - elseif (strtolower($loginname_check['loginname']) == strtolower($loginname) - || strtolower($loginname_check_admin['loginname']) == strtolower($loginname) - ) { - standard_error('loginnameexists', $loginname); - } - // Accounts which match systemaccounts are not allowed, filtering them - elseif (preg_match('/^' . preg_quote(Settings::Get('customer.accountprefix'), '/') . '([0-9]+)/', $loginname)) { - standard_error('loginnameissystemaccount', Settings::Get('customer.accountprefix')); - } - elseif (!validateUsername($loginname)) { - standard_error('loginnameiswrong', $loginname); - } - elseif ($name == '') { - standard_error(array('stringisempty', 'myname')); - } - elseif ($email == '') { - standard_error(array('stringisempty', 'emailadd')); - } - elseif ($password == '') { - standard_error(array('stringisempty', 'mypassword')); - } - elseif (!validateEmail($email)) { - standard_error('emailiswrong', $email); - - } else { - - if ($customers_see_all != '1') { - $customers_see_all = '0'; - } - - if ($domains_see_all != '1') { - $domains_see_all = '0'; - } - - if ($caneditphpsettings != '1') { - $caneditphpsettings = '0'; - } - - if ($change_serversettings != '1') { - $change_serversettings = '0'; - } - - if ($tickets_see_all != '1') { - $tickets_see_all = '0'; - } - - $_theme = Settings::Get('panel.default_theme'); - - $ins_data = array( - 'loginname' => $loginname, - 'password' => makeCryptPassword($password), - 'name' => $name, - 'email' => $email, - 'lang' => $def_language, - 'change_serversettings' => $change_serversettings, - 'customers' => $customers, - 'customers_see_all' => $customers_see_all, - 'domains' => $domains, - 'domains_see_all' => $domains_see_all, - 'caneditphpsettings' => $caneditphpsettings, - 'diskspace' => $diskspace, - 'traffic' => $traffic, - 'subdomains' => $subdomains, - 'emails' => $emails, - 'accounts' => $email_accounts, - 'forwarders' => $email_forwarders, - 'quota' => $email_quota, - 'ftps' => $ftps, - 'tickets' => $tickets, - 'tickets_see_all' => $tickets_see_all, - 'mysqls' => $mysqls, - 'ip' => $ipaddress, - 'theme' => $_theme, - 'custom_notes' => $custom_notes, - 'custom_notes_show' => $custom_notes_show - ); - - $ins_stmt = Database::prepare(" - INSERT INTO `" . TABLE_PANEL_ADMINS . "` SET - `loginname` = :loginname, - `password` = :password, - `name` = :name, - `email` = :email, - `def_language` = :lang, - `change_serversettings` = :change_serversettings, - `customers` = :customers, - `customers_see_all` = :customers_see_all, - `domains` = :domains, - `domains_see_all` = :domains_see_all, - `caneditphpsettings` = :caneditphpsettings, - `diskspace` = :diskspace, - `traffic` = :traffic, - `subdomains` = :subdomains, - `emails` = :emails, - `email_accounts` = :accounts, - `email_forwarders` = :forwarders, - `email_quota` = :quota, - `ftps` = :ftps, - `tickets` = :tickets, - `tickets_see_all` = :tickets_see_all, - `mysqls` = :mysqls, - `ip` = :ip, - `theme` = :theme, - `custom_notes` = :custom_notes, - `custom_notes_show` = :custom_notes_show - "); - Database::pexecute($ins_stmt, $ins_data); - - $adminid = Database::lastInsertId(); - $log->logAction(ADM_ACTION, LOG_INFO, "added admin '" . $loginname . "'"); - redirectTo($filename, array('page' => $page, 's' => $s)); - } - + redirectTo($filename, array('page' => $page, 's' => $s)); } else { $language_options = ''; diff --git a/lib/classes/api/commands/class.Admins.php b/lib/classes/api/commands/class.Admins.php index 6a095e0e..68781b59 100644 --- a/lib/classes/api/commands/class.Admins.php +++ b/lib/classes/api/commands/class.Admins.php @@ -86,11 +86,212 @@ class Admins extends ApiCommand implements ResourceEntity public function add() { + if ($this->isAdmin()) { + + // required parameters + $name = $this->getParam('name'); + $email = $this->getParam('email'); + + // parameters + $def_language = $this->getParam('def_language', true, ''); + $custom_notes = $this->getParam('custom_notes', true, ''); + $custom_notes_show = $this->getParam('custom_notes_show', true, 0); + $password = $this->getParam('admin_password', true, ''); + $sendpassword = $this->getParam('sendpassword', true, 0); + $loginname = $this->getParam('new_loginname', true, ''); + + $diskspace = $this->getUlParam('diskspace', 'diskspace_ul', true, 0); + $traffic = $this->getUlParam('traffic', 'traffic_ul', true, 0); + $customers = $this->getUlParam('customers', 'customers_ul', true, 0); + $domains = $this->getUlParam('domains', 'domains_ul', true, 0); + $subdomains = $this->getUlParam('subdomains', 'subdomains_ul', true, 0); + $emails = $this->getUlParam('emails', 'emails_ul', true, 0); + $email_accounts = $this->getUlParam('email_accounts', 'email_accounts_ul', true, 0); + $email_forwarders = $this->getUlParam('email_forwarders', 'email_forwarders_ul', true, 0); + $email_quota = $this->getUlParam('email_quota', 'email_quota_ul', true, 0); + $ftps = $this->getUlParam('ftps', 'ftps_ul', true, 0); + $tickets = $this->getUlParam('tickets', 'tickets_ul', true, 0); + $mysqls = $this->getUlParam('mysqls', 'mysqls_ul', true, 0); + + $customers_see_all = $this->getParam('customers_see_all', true, 0); + $domains_see_all = $this->getParam('domains_see_all', true, 0); + $tickets_see_all = $this->getParam('tickets_see_all', true, 0); + $caneditphpsettings = $this->getParam('caneditphpsettings', true, 0); + $change_serversettings = $this->getParam('change_serversettings', true, 0); + $ipaddress = intval_ressource($this->getParam('ipaddress', true, -1)); + + // validation + $name = validate($name, 'name', '', '', array(), true); + $idna_convert = new idna_convert_wrapper(); + $email = $idna_convert->encode(validate($email, 'email', '', '', array(), true)); + $def_language = validate($def_language, 'default language', '', '', array(), true); + $custom_notes = validate(str_replace("\r\n", "\n", $custom_notes), 'custom_notes', '/^[^\0]*$/', '', array(), true); + + if (Settings::Get('system.mail_quota_enabled') != '1') { + $email_quota = - 1; + } + + if (Settings::Get('ticket.enabled') != '1') { + $tickets = - 1; + } + + $password = validate($password, 'password', '', '', array(), true); + // only check if not empty, + // cause empty == generate password automatically + if ($password != '') { + $password = validatePassword($password, true); + } + + $diskspace = $diskspace * 1024; + $traffic = $traffic * 1024 * 1024; + + // Check if the account already exists + try { + $dup_check_result = Customers::getLocal($this->getUserData(), array( + 'loginname' => $loginname + ))->get(); + $loginname_check = json_decode($dup_check_result, true)['data']; + } catch (Exception $e) { + $loginname_check = array( + 'loginname' => '' + ); + } + + // Check if an admin with the loginname already exists + try { + $dup_check_result = Admins::getLocal($this->getUserData(), array( + 'loginname' => $loginname + ))->get(); + $loginname_check_admin = json_decode($dup_check_result, true)['data']; + } catch (Exception $e) { + $loginname_check_admin = array( + 'loginname' => '' + ); + } + + if ($loginname == '') { + standard_error(array( + 'stringisempty', + 'myloginname' + ), '', true); + } elseif (strtolower($loginname_check['loginname']) == strtolower($loginname) || strtolower($loginname_check_admin['loginname']) == strtolower($loginname)) { + standard_error('loginnameexists', $loginname, true); + } // Accounts which match systemaccounts are not allowed, filtering them + elseif (preg_match('/^' . preg_quote(Settings::Get('customer.accountprefix'), '/') . '([0-9]+)/', $loginname)) { + standard_error('loginnameissystemaccount', Settings::Get('customer.accountprefix'), true); + } elseif (! validateUsername($loginname)) { + standard_error('loginnameiswrong', $loginname, true); + } elseif ($name == '') { + standard_error(array( + 'stringisempty', + 'myname' + ), '', true); + } elseif ($email == '') { + standard_error(array( + 'stringisempty', + 'emailadd' + ), '', true); + } elseif (! validateEmail($email)) { + standard_error('emailiswrong', $email, true); + } else { + + if ($customers_see_all != '1') { + $customers_see_all = '0'; + } + + if ($domains_see_all != '1') { + $domains_see_all = '0'; + } + + if ($caneditphpsettings != '1') { + $caneditphpsettings = '0'; + } + + if ($change_serversettings != '1') { + $change_serversettings = '0'; + } + + if ($tickets_see_all != '1') { + $tickets_see_all = '0'; + } + + if ($password == '') { + $password = generatePassword(); + } + + $_theme = Settings::Get('panel.default_theme'); + + $ins_data = array( + 'loginname' => $loginname, + 'password' => makeCryptPassword($password), + 'name' => $name, + 'email' => $email, + 'lang' => $def_language, + 'change_serversettings' => $change_serversettings, + 'customers' => $customers, + 'customers_see_all' => $customers_see_all, + 'domains' => $domains, + 'domains_see_all' => $domains_see_all, + 'caneditphpsettings' => $caneditphpsettings, + 'diskspace' => $diskspace, + 'traffic' => $traffic, + 'subdomains' => $subdomains, + 'emails' => $emails, + 'accounts' => $email_accounts, + 'forwarders' => $email_forwarders, + 'quota' => $email_quota, + 'ftps' => $ftps, + 'tickets' => $tickets, + 'tickets_see_all' => $tickets_see_all, + 'mysqls' => $mysqls, + 'ip' => $ipaddress, + 'theme' => $_theme, + 'custom_notes' => $custom_notes, + 'custom_notes_show' => $custom_notes_show + ); + + $ins_stmt = Database::prepare(" + INSERT INTO `" . TABLE_PANEL_ADMINS . "` SET + `loginname` = :loginname, + `password` = :password, + `name` = :name, + `email` = :email, + `def_language` = :lang, + `change_serversettings` = :change_serversettings, + `customers` = :customers, + `customers_see_all` = :customers_see_all, + `domains` = :domains, + `domains_see_all` = :domains_see_all, + `caneditphpsettings` = :caneditphpsettings, + `diskspace` = :diskspace, + `traffic` = :traffic, + `subdomains` = :subdomains, + `emails` = :emails, + `email_accounts` = :accounts, + `email_forwarders` = :forwarders, + `email_quota` = :quota, + `ftps` = :ftps, + `tickets` = :tickets, + `tickets_see_all` = :tickets_see_all, + `mysqls` = :mysqls, + `ip` = :ip, + `theme` = :theme, + `custom_notes` = :custom_notes, + `custom_notes_show` = :custom_notes_show + "); + Database::pexecute($ins_stmt, $ins_data, true, true); + + $adminid = Database::lastInsertId(); + $ins_data['adminid'] = $adminid; + $this->logger()->logAction(ADM_ACTION, LOG_WARNING, "[API] added admin '" . $loginname . "'"); + return $this->response(200, "successfull", $admin_ins_data); + } + } + throw new Exception("Not allowed to execute given command.", 403); } public function update() - { - } + {} /** * delete a admin entry by either id or loginname @@ -106,8 +307,7 @@ class Admins extends ApiCommand implements ResourceEntity * @return array */ public function delete() - { - } + {} /** * unlock a locked admin by either id or loginname @@ -116,7 +316,7 @@ class Admins extends ApiCommand implements ResourceEntity * optional, the admin-id * @param string $loginname * optional, the loginname - * + * * @throws Exception * @return array */ @@ -126,18 +326,18 @@ class Admins extends ApiCommand implements ResourceEntity $id = $this->getParam('id', true, 0); $ln_optional = ($id <= 0 ? false : true); $loginname = $this->getParam('loginname', $ln_optional, ''); - + if ($id <= 0 && empty($loginname)) { throw new Exception("Either 'id' or 'loginname' parameter must be given", 406); } - + $json_result = Admins::getLocal($this->getUserData(), array( 'id' => $id, 'loginname' => $loginname ))->get(); $result = json_decode($json_result, true)['data']; $id = $result['adminid']; - + $result_stmt = Database::prepare(" UPDATE `" . TABLE_PANEL_ADMINS . "` SET `loginfail_count` = '0' @@ -146,7 +346,7 @@ class Admins extends ApiCommand implements ResourceEntity Database::pexecute($result_stmt, array( 'id' => $id ), true, true); - + $this->logger()->logAction(ADM_ACTION, LOG_WARNING, "[API] unlocked admin '" . $result['loginname'] . "'"); return $this->response(200, "successfull", $result); } diff --git a/lib/classes/api/commands/class.Customers.php b/lib/classes/api/commands/class.Customers.php index bfad0ba9..d707c7f8 100644 --- a/lib/classes/api/commands/class.Customers.php +++ b/lib/classes/api/commands/class.Customers.php @@ -144,7 +144,6 @@ class Customers extends ApiCommand implements ResourceEntity $loginname = $this->getParam('new_loginname', true, ''); // validation - $idna_convert = new idna_convert_wrapper(); $name = validate($name, 'name', '', '', array(), true); $firstname = validate($firstname, 'first name', '', '', array(), true); $company = validate($company, 'company', '', '', array(), true); @@ -233,20 +232,25 @@ class Customers extends ApiCommand implements ResourceEntity } // Check if the account already exists - $loginname_check_stmt = Database::prepare(" - SELECT `loginname` FROM `" . TABLE_PANEL_CUSTOMERS . "` WHERE `loginname` = :loginname - "); - $loginname_check = Database::pexecute_first($loginname_check_stmt, array( - 'loginname' => $loginname - ), true, true); - - $loginname_check_admin_stmt = Database::prepare(" - SELECT `loginname` FROM `" . TABLE_PANEL_ADMINS . "` WHERE `loginname` = :loginname - "); - $loginname_check_admin = Database::pexecute_first($loginname_check_admin_stmt, array( - 'loginname' => $loginname - ), true, true); - + try { + $dup_check_result = Customers::getLocal($this->getUserData(), array( + 'loginname' => $loginname + ))->get(); + $loginname_check = json_decode($dup_check_result, true)['data']; + } catch (Exception $e) { + $loginname_check = array('loginname' => ''); + } + + // Check if an admin with the loginname already exists + try { + $dup_check_result = Admins::getLocal($this->getUserData(), array( + 'loginname' => $loginname + ))->get(); + $loginname_check_admin = json_decode($dup_check_result, true)['data']; + } catch (Exception $e) { + $loginname_check_admin = array('loginname' => ''); + } + if (strtolower($loginname_check['loginname']) == strtolower($loginname) || strtolower($loginname_check_admin['loginname']) == strtolower($loginname)) { standard_error('loginnameexists', $loginname, true); } elseif (! validateUsername($loginname, Settings::Get('panel.unix_names'), 14 - strlen(Settings::Get('customer.mysqlprefix')))) { diff --git a/lib/classes/api/commands/class.PhpSettings.php b/lib/classes/api/commands/class.PhpSettings.php index 5507e454..8d0a5dd8 100644 --- a/lib/classes/api/commands/class.PhpSettings.php +++ b/lib/classes/api/commands/class.PhpSettings.php @@ -19,7 +19,7 @@ class PhpSettings extends ApiCommand implements ResourceEntity { /** - * lists all php-config entries + * lists all php-setting entries * * @return array count|list */ @@ -102,7 +102,7 @@ class PhpSettings extends ApiCommand implements ResourceEntity } /** - * return a php-config entry by id + * return a php-setting entry by id * * @param int $id php-settings-id * @@ -329,9 +329,9 @@ class PhpSettings extends ApiCommand implements ResourceEntity } /** - * delete a php-config entry by id + * delete a php-setting entry by id * - * @param int $id php-config-id + * @param int $id php-settings-id * * @throws Exception * @return array From d9ec214e17d2c14567b2f8e6d2d6e0e3b6e5c6c5 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 22 Feb 2018 21:05:58 +0100 Subject: [PATCH 0467/1335] secure included webinterface-modules; add settings-functions to Froxlor-ApiCommand Signed-off-by: Michael Kaufmann (d00p) --- apihelp.php | 10 ++- dns_editor.php | 8 +- lib/classes/api/commands/class.Froxlor.php | 80 +++++++++++++++++++ .../filedir/function.makeCorrectDir.php | 35 ++++---- ssl_certificates.php | 8 +- 5 files changed, 112 insertions(+), 29 deletions(-) diff --git a/apihelp.php b/apihelp.php index 2f426f3a..9e9be7a5 100644 --- a/apihelp.php +++ b/apihelp.php @@ -1,6 +1,8 @@ $functions) { $apihelp .= "

    " . ($funcdata['return_type'] == - 1 ? "no-return-type" : $funcdata['return_type']) . " "; $apihelp .= "" . $module . "." . $function . "

    "; // description - if (strtoupper(substr($funcdata['head'], 0, 4)) == "TODO") + if (strtoupper(substr($funcdata['head'], 0, 5)) == "@TODO") $apihelp .= ""; $apihelp .= $funcdata['head']; - if (strtoupper(substr($funcdata['head'], 0, 4)) == "TODO") + if (strtoupper(substr($funcdata['head'], 0, 5)) == "@TODO") $apihelp .= ""; // output ALL the params; if (count($funcdata['params_list']) > 0) { diff --git a/dns_editor.php b/dns_editor.php index 98a4d683..cd720fdc 100644 --- a/dns_editor.php +++ b/dns_editor.php @@ -1,6 +1,8 @@ fetch(PDO::FETCH_ASSOC)) { + $result[] = array( + 'key' => $row['settinggroup'] . '.' . $row['varname'], + 'value' => $row['value'] + ); + } + return $this->response(200, "successfull", array( + 'count' => count($result), + 'list' => $result + )); + } + + /** + * return a setting by settinggroup.varname couple + * + * @param string $key + * settinggroup.varname couple + * + * @throws Exception + * @return string + */ + public function getSetting() + { + if ($this->isAdmin() && $this->getUserDetail('change_serversettings')) { + $setting = $this->getParam('key'); + return $this->response(200, "successfull", Settings::Get($setting)); + } + throw new Exception("Not allowed to execute given command.", 403); + } + + /** + * updates a setting + * + * @param string $key + * settinggroup.varname couple + * @param string $value + * optional the new value, default is '' + * + * @throws Exception + * @return string + */ + public function updateSetting() + { + if ($this->isAdmin() && $this->getUserDetail('change_serversettings')) { + $setting = $this->getParam('key'); + $value = $this->getParam('value', true, ''); + $oldvalue = Settings::Get($setting); + if (is_null($oldvalue)) { + throw new Exception("Setting '" . $setting . "' could not be found"); + } + $this->logger()->logAction(ADM_ACTION, LOG_WARNING, "[API] Changing setting '" . $setting . "' from '" . $oldvalue . "' to '" . $value . "'"); + return $this->response(200, "successfull", Settings::Set($setting, $value, true)); + } + throw new Exception("Not allowed to execute given command.", 403); + } + /** * returns a list of all available api functions * diff --git a/lib/functions/filedir/function.makeCorrectDir.php b/lib/functions/filedir/function.makeCorrectDir.php index dcc91ca2..b3ac5cba 100644 --- a/lib/functions/filedir/function.makeCorrectDir.php +++ b/lib/functions/filedir/function.makeCorrectDir.php @@ -20,26 +20,23 @@ /** * Function which returns a correct dirname, means to add slashes at the beginning and at the end if there weren't some * - * @param string The dirname + * @param string $dir + * The dirname + * * @return string The corrected dirname - * @author Florian Lippert */ -function makeCorrectDir($dir) { - - if (version_compare("5.4.6", PHP_VERSION, ">")) { - assert('is_string($dir) && strlen($dir) > 0 /* $dir does not look like an actual folder name */'); - } else { - assert('is_string($dir) && strlen($dir) > 0', 'Value "' . $dir .'" does not look like an actual folder name'); +function makeCorrectDir($dir) +{ + if (is_string($dir) && strlen($dir) > 0) { + $dir = trim($dir); + if (substr($dir, - 1, 1) != '/') { + $dir .= '/'; + } + if (substr($dir, 0, 1) != '/') { + $dir = '/' . $dir; + } + $dir = makeSecurePath($dir); + return $dir; } - - $dir = trim($dir); - - if (substr($dir, -1, 1) != '/') { - $dir.= '/'; - } - if (substr($dir, 0, 1) != '/') { - $dir = '/' . $dir; - } - $dir = makeSecurePath($dir); - return $dir; + throw new Exception("Cannot validate directory in " . __FUNCTION__ . " which is very dangerous."); } diff --git a/ssl_certificates.php b/ssl_certificates.php index 875b903e..dd0bba10 100644 --- a/ssl_certificates.php +++ b/ssl_certificates.php @@ -1,6 +1,8 @@ Date: Fri, 23 Feb 2018 11:08:24 +0100 Subject: [PATCH 0468/1335] check remote-ip when ip-restriction is set in api_keys table Signed-off-by: Michael Kaufmann (d00p) --- api.php | 8 ++++++-- lib/classes/api/abstract.ApiCommand.php | 6 +++++- lib/classes/api/class.FroxlorRPC.php | 27 +++++++++++++++++++++---- 3 files changed, 34 insertions(+), 7 deletions(-) diff --git a/api.php b/api.php index c792d18b..a5dd6fe3 100644 --- a/api.php +++ b/api.php @@ -52,9 +52,13 @@ exit(); * * @return void */ -function json_response($status, $status_message, $data = null) +function json_response($status, $status_message = '', $data = null) { - header("HTTP/1.1 " . $status); + $resheader = $_SERVER["SERVER_PROTOCOL"] . " " . $status; + if (! empty($status_message)) { + $resheader .= ' ' . $status_message; + } + header($resheader); $response['status'] = $status; $response['status_message'] = $status_message; diff --git a/lib/classes/api/abstract.ApiCommand.php b/lib/classes/api/abstract.ApiCommand.php index f499140b..333985d5 100644 --- a/lib/classes/api/abstract.ApiCommand.php +++ b/lib/classes/api/abstract.ApiCommand.php @@ -368,7 +368,11 @@ abstract class ApiCommand */ protected function response($status, $status_message, $data = null) { - header("HTTP/1.1 " . $status); + $resheader = $_SERVER["SERVER_PROTOCOL"] . " " . $status; + if (! empty($status_message)) { + $resheader .= ' ' . $status_message; + } + header($resheader); $response['status'] = $status; $response['status_message'] = $status_message; diff --git a/lib/classes/api/class.FroxlorRPC.php b/lib/classes/api/class.FroxlorRPC.php index 693d4ee7..07d466fa 100644 --- a/lib/classes/api/class.FroxlorRPC.php +++ b/lib/classes/api/class.FroxlorRPC.php @@ -1,5 +1,20 @@ (2010-) + * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt + * @package API + * @since 0.10.0 + * + */ class FroxlorRPC { @@ -48,13 +63,17 @@ class FroxlorRPC if ($result['apikey'] == $key && $result['secret'] == $secret && ($result['valid_until'] == -1 || $result['valid_until'] >= time())) { if (!empty($result['allowed_from'])) { $ip_list = explode(",", $result['allowed_from']); - $access_ip = $_SERVER['REMOTE_ADDR']; - // @fixme finish me + $ip_list = array_map('inet_pton', $ip_list); + $access_ip = inet_pton($_SERVER['REMOTE_ADDR']); + if (in_array($access_ip, $ip_list)) { + return true; + } + } else { + return true; } - return true; } } - throw new Exception("Invalid authorization credentials", 400); + throw new Exception("Invalid authorization credentials", 403); } /** From 8e0bfe9d09cd8dbc0bc9e063870165c967617784 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Fri, 23 Feb 2018 11:57:29 +0100 Subject: [PATCH 0469/1335] add Admins.update() Signed-off-by: Michael Kaufmann (d00p) --- admin_admins.php | 356 ++----------------- lib/classes/api/commands/class.Admins.php | 326 ++++++++++++++++- lib/classes/api/commands/class.Customers.php | 12 +- 3 files changed, 362 insertions(+), 332 deletions(-) diff --git a/admin_admins.php b/admin_admins.php index ce0f8488..1404aa6d 100644 --- a/admin_admins.php +++ b/admin_admins.php @@ -107,10 +107,14 @@ if ($page == 'admins' } elseif($action == 'su') { - $result_stmt = Database::prepare(" - SELECT * FROM `" . TABLE_PANEL_ADMINS . "` WHERE `adminid` = :adminid - "); - $result = Database::pexecute_first($result_stmt, array('adminid' => $id)); + try { + $json_result = Admins::getLocal($userinfo, array( + 'id' => $id + ))->get(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); + } + $result = json_decode($json_result, true)['data']; $destination_admin = $result['loginname']; if ($destination_admin != '' @@ -147,10 +151,14 @@ if ($page == 'admins' } elseif ($action == 'delete' && $id != 0 ) { - $result_stmt = Database::prepare(" - SELECT * FROM `" . TABLE_PANEL_ADMINS . "` WHERE `adminid` = :adminid - "); - $result = Database::pexecute_first($result_stmt, array('adminid' => $id)); + try { + $json_result = Admins::getLocal($userinfo, array( + 'id' => $id + ))->get(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); + } + $result = json_decode($json_result, true)['data']; if ($result['loginname'] != '') { if ($result['adminid'] == $userinfo['userid']) { @@ -160,37 +168,10 @@ if ($page == 'admins' if (isset($_POST['send']) && $_POST['send'] == 'send' ) { - $del_stmt = Database::prepare(" - DELETE FROM `" . TABLE_PANEL_ADMINS . "` WHERE `adminid` = :adminid - "); - Database::pexecute($del_stmt, array('adminid' => $id)); - - $del_stmt = Database::prepare(" - DELETE FROM `" . TABLE_PANEL_TRAFFIC_ADMINS . "` WHERE `adminid` = :adminid - "); - Database::pexecute($del_stmt, array('adminid' => $id)); - - $del_stmt = Database::prepare(" - DELETE FROM `" . TABLE_PANEL_DISKSPACE_ADMINS . "` WHERE `adminid` = :adminid - "); - Database::pexecute($del_stmt, array('adminid' => $id)); - - $upd_stmt = Database::prepare(" - UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET - `adminid` = :userid WHERE `adminid` = :adminid - "); - Database::pexecute($upd_stmt, array('userid' => $userinfo['userid'], 'adminid' => $id)); - - $upd_stmt = Database::prepare(" - UPDATE `" . TABLE_PANEL_DOMAINS . "` SET - `adminid` = :userid WHERE `adminid` = :adminid - "); - Database::pexecute($upd_stmt, array('userid' => $userinfo['userid'], 'adminid' => $id)); - - $log->logAction(ADM_ACTION, LOG_INFO, "deleted admin '" . $result['loginname'] . "'"); - updateCounters(); + Admins::getLocal($this->getUserData(), array( + 'id' => $id + ))->delete(); redirectTo($filename, array('page' => $page, 's' => $s)); - } else { ask_yesno('admin_admin_reallydelete', $filename, array('id' => $id, 'page' => $page, 'action' => $action), $result['loginname']); } @@ -249,299 +230,26 @@ if ($page == 'admins' && $id != 0 ) { - $result_stmt = Database::prepare(" - SELECT * FROM `" . TABLE_PANEL_ADMINS . "` WHERE `adminid` = :adminid - "); - $result = Database::pexecute_first($result_stmt, array('adminid' => $id)); + try { + $json_result = Admins::getLocal($userinfo, array( + 'id' => $id + ))->get(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); + } + $result = json_decode($json_result, true)['data']; if ($result['loginname'] != '') { if (isset($_POST['send']) && $_POST['send'] == 'send' ) { - $name = validate($_POST['name'], 'name'); - $email = $idna_convert->encode(validate($_POST['email'], 'email')); - - $custom_notes = validate(str_replace("\r\n", "\n", $_POST['custom_notes']), 'custom_notes', '/^[^\0]*$/'); - $custom_notes_show = $result['custom_notes_show']; - if (isset($_POST['custom_notes_show'])) { - $custom_notes_show = intval_ressource($_POST['custom_notes_show']); + try { + Admins::getLocal($userinfo, $_POST)->update(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); } - - if ($result['adminid'] == $userinfo['userid']) { - - $password = ''; - $def_language = $result['def_language']; - $deactivated = $result['deactivated']; - $customers = $result['customers']; - $domains = $result['domains']; - $subdomains = $result['subdomains']; - $emails = $result['emails']; - $email_accounts = $result['email_accounts']; - $email_forwarders = $result['email_forwarders']; - $email_quota = $result['email_quota']; - $ftps = $result['ftps']; - $tickets = $result['tickets']; - $mysqls = $result['mysqls']; - $tickets_see_all = $result['tickets_see_all']; - $customers_see_all = $result['customers_see_all']; - $domains_see_all = $result['domains_see_all']; - $caneditphpsettings = $result['caneditphpsettings']; - $change_serversettings = $result['change_serversettings']; - $diskspace = $result['diskspace']; - $traffic = $result['traffic']; - $ipaddress = $result['ip']; - - } else { - - $password = validate($_POST['admin_password'], 'new password'); - $def_language = validate($_POST['def_language'], 'default language'); - $deactivated = isset($_POST['deactivated']) ? 1 : 0; - - $customers = intval_ressource($_POST['customers']); - if (isset($_POST['customers_ul'])) { - $customers = -1; - } - - $domains = intval_ressource($_POST['domains']); - if (isset($_POST['domains_ul'])) { - $domains = -1; - } - - $subdomains = intval_ressource($_POST['subdomains']); - if (isset($_POST['subdomains_ul'])) { - $subdomains = -1; - } - - $emails = intval_ressource($_POST['emails']); - if (isset($_POST['emails_ul'])) { - $emails = -1; - } - - $email_accounts = intval_ressource($_POST['email_accounts']); - if (isset($_POST['email_accounts_ul'])) { - $email_accounts = -1; - } - - $email_forwarders = intval_ressource($_POST['email_forwarders']); - if (isset($_POST['email_forwarders_ul'])) { - $email_forwarders = -1; - } - - if (Settings::Get('system.mail_quota_enabled') == '1') { - $email_quota = validate($_POST['email_quota'], 'email_quota', '/^\d+$/', 'vmailquotawrong', array('0', '')); - if (isset($_POST['email_quota_ul'])) { - $email_quota = -1; - } - } else { - $email_quota = -1; - } - - $ftps = intval_ressource($_POST['ftps']); - if (isset($_POST['ftps_ul'])) { - $ftps = -1; - } - - if (Settings::Get('ticket.enabled') == 1) { - $tickets = intval_ressource($_POST['tickets']); - if (isset($_POST['tickets_ul'])) { - $tickets = -1; - } - } else { - $tickets = 0; - } - - $mysqls = intval_ressource($_POST['mysqls']); - if (isset($_POST['mysqls_ul'])) { - $mysqls = -1; - } - - $customers_see_all = 0; - if (isset($_POST['customers_see_all'])) { - $customers_see_all = intval($_POST['customers_see_all']); - } - - $domains_see_all = 0; - if (isset($_POST['domains_see_all'])) { - $domains_see_all = intval($_POST['domains_see_all']); - } - - $caneditphpsettings = 0; - if (isset($_POST['caneditphpsettings'])) { - $caneditphpsettings = intval($_POST['caneditphpsettings']); - } - - $change_serversettings = 0; - if (isset($_POST['change_serversettings'])) { - $change_serversettings = isset($_POST['change_serversettings']) ? 1 : 0; - } - - $tickets_see_all = 0; - if (isset($_POST['tickets_see_all'])) { - $tickets_see_all = intval($_POST['tickets_see_all']); - } - - $diskspace = intval($_POST['diskspace']); - if (isset($_POST['diskspace_ul'])) { - $diskspace = -1; - } - - $traffic = doubleval_ressource($_POST['traffic']); - if (isset($_POST['traffic_ul'])) { - $traffic = -1; - } - - $diskspace = $diskspace * 1024; - $traffic = $traffic * 1024 * 1024; - $ipaddress = intval_ressource($_POST['ipaddress']); - } - - if ($name == '') { - standard_error(array('stringisempty', 'myname')); - } elseif($email == '') { - standard_error(array('stringisempty', 'emailadd')); - } elseif(!validateEmail($email)) { - standard_error('emailiswrong', $email); - } else { - if ($password != '') { - $password = validatePassword($password); - $password = makeCryptPassword($password); - } else { - $password = $result['password']; - } - - if ($deactivated != '1') { - $deactivated = '0'; - } - - if ($customers_see_all != '1') { - $customers_see_all = '0'; - } - - if ($domains_see_all != '1') { - $domains_see_all = '0'; - } - - if ($caneditphpsettings != '1') { - $caneditphpsettings = '0'; - } - - if ($change_serversettings != '1') { - $change_serversettings = '0'; - } - - if ($tickets_see_all != '1') { - $tickets_see_all = '0'; - } - - // check if a resource was set to something lower - // than actually used by the admin/reseller - $res_warning = ""; - if ($customers != $result['customers'] && $customers != -1 && $customers < $result['customers_used']) { - $res_warning .= sprintf($lng['error']['setlessthanalreadyused'], 'customers'); - } - if ($domains != $result['domains'] && $domains != -1 && $domains < $result['domains_used']) { - $res_warning .= sprintf($lng['error']['setlessthanalreadyused'], 'domains'); - } - if ($diskspace != $result['diskspace'] && ($diskspace / 1024) != -1 && $diskspace < $result['diskspace_used']) { - $res_warning .= sprintf($lng['error']['setlessthanalreadyused'], 'diskspace'); - } - if ($traffic != $result['traffic'] && ($traffic / 1024 / 1024) != -1 && $traffic < $result['traffic_used']) { - $res_warning .= sprintf($lng['error']['setlessthanalreadyused'], 'traffic'); - } - if ($emails != $result['emails'] && $emails != -1 && $emails < $result['emails_used']) { - $res_warning .= sprintf($lng['error']['setlessthanalreadyused'], 'emails'); - } - if ($email_accounts != $result['email_accounts'] && $email_accounts != -1 && $email_accounts < $result['email_accounts_used']) { - $res_warning .= sprintf($lng['error']['setlessthanalreadyused'], 'email accounts'); - } - if ($email_forwarders != $result['email_forwarders'] && $email_forwarders != -1 && $email_forwarders < $result['email_forwarders_used']) { - $res_warning .= sprintf($lng['error']['setlessthanalreadyused'], 'email forwarders'); - } - if ($email_quota != $result['email_quota'] && $email_quota != -1 && $email_quota < $result['email_quota_used']) { - $res_warning .= sprintf($lng['error']['setlessthanalreadyused'], 'email quota'); - } - if ($ftps != $result['ftps'] && $ftps != -1 && $ftps < $result['ftps_used']) { - $res_warning .= sprintf($lng['error']['setlessthanalreadyused'], 'ftps'); - } - if ($tickets != $result['tickets'] && $tickets != -1 && $tickets < $result['tickets_used']) { - $res_warning .= sprintf($lng['error']['setlessthanalreadyused'], 'tickets'); - } - if ($mysqls != $result['mysqls'] && $mysqls != -1 && $mysqls < $result['mysqls_used']) { - $res_warning .= sprintf($lng['error']['setlessthanalreadyused'], 'mysqls'); - } - - if ($res_warning != "") { - $link = ''; - $error = $res_warning; - eval("echo \"" . getTemplate('misc/error', '1') . "\";"); - exit; - } - - $upd_data = array( - 'password' => $password, - 'name' => $name, - 'email' => $email, - 'lang' => $def_language, - 'change_serversettings' => $change_serversettings, - 'customers' => $customers, - 'customers_see_all' => $customers_see_all, - 'domains' => $domains, - 'domains_see_all' => $domains_see_all, - 'caneditphpsettings' => $caneditphpsettings, - 'diskspace' => $diskspace, - 'traffic' => $traffic, - 'subdomains' => $subdomains, - 'emails' => $emails, - 'accounts' => $email_accounts, - 'forwarders' => $email_forwarders, - 'quota' => $email_quota, - 'ftps' => $ftps, - 'tickets' => $tickets, - 'tickets_see_all' => $tickets_see_all, - 'mysqls' => $mysqls, - 'ip' => $ipaddress, - 'deactivated' => $deactivated, - 'custom_notes' => $custom_notes, - 'custom_notes_show' => $custom_notes_show, - 'adminid' => $id - ); - - $upd_stmt = Database::prepare(" - UPDATE `" . TABLE_PANEL_ADMINS . "` SET - `password` = :password, - `name` = :name, - `email` = :email, - `def_language` = :lang, - `change_serversettings` = :change_serversettings, - `customers` = :customers, - `customers_see_all` = :customers_see_all, - `domains` = :domains, - `domains_see_all` = :domains_see_all, - `caneditphpsettings` = :caneditphpsettings, - `diskspace` = :diskspace, - `traffic` = :traffic, - `subdomains` = :subdomains, - `emails` = :emails, - `email_accounts` = :accounts, - `email_forwarders` = :forwarders, - `email_quota` = :quota, - `ftps` = :ftps, - `tickets` = :tickets, - `tickets_see_all` = :tickets_see_all, - `mysqls` = :mysqls, - `ip` = :ip, - `deactivated` = :deactivated, - `custom_notes` = :custom_notes, - `custom_notes_show` = :custom_notes_show - WHERE `adminid` = :adminid - "); - Database::pexecute($upd_stmt, $upd_data); - - $log->logAction(ADM_ACTION, LOG_INFO, "edited admin '#" . $id . "'"); - redirectTo($filename, array('page' => $page, 's' => $s)); - } - + redirectTo($filename, array('page' => $page, 's' => $s)); } else { $dec_places = Settings::Get('panel.decimal_places'); diff --git a/lib/classes/api/commands/class.Admins.php b/lib/classes/api/commands/class.Admins.php index 68781b59..5779b68c 100644 --- a/lib/classes/api/commands/class.Admins.php +++ b/lib/classes/api/commands/class.Admins.php @@ -86,7 +86,7 @@ class Admins extends ApiCommand implements ResourceEntity public function add() { - if ($this->isAdmin()) { + if ($this->isAdmin() && $this->getUserDetail('change_serversettings') == 1) { // required parameters $name = $this->getParam('name'); @@ -291,7 +291,269 @@ class Admins extends ApiCommand implements ResourceEntity } public function update() - {} + { + if ($this->isAdmin() && $this->getUserDetail('change_serversettings') == 1) { + + $id = $this->getParam('id', true, 0); + $ln_optional = ($id <= 0 ? false : true); + $loginname = $this->getParam('loginname', $ln_optional, ''); + + if ($id <= 0 && empty($loginname)) { + throw new Exception("Either 'id' or 'loginname' parameter must be given", 406); + } + + $json_result = Admins::getLocal($this->getUserData(), array( + 'id' => $id, + 'loginname' => $loginname + ))->get(); + $result = json_decode($json_result, true)['data']; + $id = $result['adminid']; + + // parameters + $name = $this->getParam('name', true, $result['name']); + $idna_convert = new idna_convert_wrapper(); + $email = $this->getParam('email', true, $idna_convert->decode($result['email'])); + $custom_notes = $this->getParam('custom_notes', true, $result['custom_notes']); + $custom_notes_show = $this->getParam('custom_notes_show', true, $result['custom_notes_show']); + $theme = $this->getParam('theme', true, $result['theme']); + + // you cannot edit some of the details of yourself + if ($result['adminid'] == $this->getUserDetail('userid')) { + $password = ''; + $def_language = $result['def_language']; + $deactivated = $result['deactivated']; + $customers = $result['customers']; + $domains = $result['domains']; + $subdomains = $result['subdomains']; + $emails = $result['emails']; + $email_accounts = $result['email_accounts']; + $email_forwarders = $result['email_forwarders']; + $email_quota = $result['email_quota']; + $ftps = $result['ftps']; + $tickets = $result['tickets']; + $mysqls = $result['mysqls']; + $tickets_see_all = $result['tickets_see_all']; + $customers_see_all = $result['customers_see_all']; + $domains_see_all = $result['domains_see_all']; + $caneditphpsettings = $result['caneditphpsettings']; + $change_serversettings = $result['change_serversettings']; + $diskspace = $result['diskspace']; + $traffic = $result['traffic']; + $ipaddress = $result['ip']; + } else { + $password = $this->getParam('admin_password', true, ''); + $def_language = $this->getParam('def_language', true, $result['def_language']); + $deactivated = $this->getParam('deactivated', true, $result['deactivated']); + + $dec_places = Settings::Get('panel.decimal_places'); + $diskspace = $this->getUlParam('diskspace', 'diskspace_ul', true, round($result['diskspace'] / 1024, $dec_places)); + $traffic = $this->getUlParam('traffic', 'traffic_ul', true, round($result['traffic'] / (1024 * 1024), $dec_places)); + $customers = $this->getUlParam('customers', 'customers_ul', true, $result['customers']); + $domains = $this->getUlParam('domains', 'domains_ul', true, $result['domains']); + $subdomains = $this->getUlParam('subdomains', 'subdomains_ul', true, $result['subdomains']); + $emails = $this->getUlParam('emails', 'emails_ul', true, $result['emails']); + $email_accounts = $this->getUlParam('email_accounts', 'email_accounts_ul', true, $result['email_accounts']); + $email_forwarders = $this->getUlParam('email_forwarders', 'email_forwarders_ul', true, $result['email_forwarders']); + $email_quota = $this->getUlParam('email_quota', 'email_quota_ul', true, $result['email_quota']); + $ftps = $this->getUlParam('ftps', 'ftps_ul', true, $result['ftps']); + $tickets = $this->getUlParam('tickets', 'tickets_ul', true, $result['tickets']); + $mysqls = $this->getUlParam('mysqls', 'mysqls_ul', true, $result['mysqls']); + + $customers_see_all = $this->getParam('customers_see_all', true, $result['customers_see_all']); + $domains_see_all = $this->getParam('domains_see_all', true, $result['domains_see_all']); + $tickets_see_all = $this->getParam('tickets_see_all', true, $result['tickets_see_all']); + $caneditphpsettings = $this->getParam('caneditphpsettings', true, $result['caneditphpsettings']); + $change_serversettings = $this->getParam('change_serversettings', true, $result['change_serversettings']); + $ipaddress = intval_ressource($this->getParam('ipaddress', true, $result['ip'])); + + $diskspace = $diskspace * 1024; + $traffic = $traffic * 1024 * 1024; + } + + // validation + $name = validate($name, 'name', '', '', array(), true); + $idna_convert = new idna_convert_wrapper(); + $email = $idna_convert->encode(validate($email, 'email', '', '', array(), true)); + $def_language = validate($def_language, 'default language', '', '', array(), true); + $custom_notes = validate(str_replace("\r\n", "\n", $custom_notes), 'custom_notes', '/^[^\0]*$/', '', array(), true); + $theme = validate($theme, 'theme', '', '', array(), true); + + if (Settings::Get('system.mail_quota_enabled') != '1') { + $email_quota = - 1; + } + + if (Settings::Get('ticket.enabled') != '1') { + $tickets = - 1; + } + + if (empty($theme)) { + $theme = Settings::Get('panel.default_theme'); + } + + $password = validate($password, 'password', '', '', array(), true); + // only check if not empty, + // cause empty == generate password automatically + if ($password != '') { + $password = validatePassword($password, true); + } + + $diskspace = $diskspace * 1024; + $traffic = $traffic * 1024 * 1024; + + if ($name == '') { + standard_error(array( + 'stringisempty', + 'myname' + ), '', true); + } elseif ($email == '') { + standard_error(array( + 'stringisempty', + 'emailadd' + ), '', true); + } elseif (! validateEmail($email)) { + standard_error('emailiswrong', $email, true); + } else { + + if ($deactivated != '1') { + $deactivated = '0'; + } + + if ($customers_see_all != '1') { + $customers_see_all = '0'; + } + + if ($domains_see_all != '1') { + $domains_see_all = '0'; + } + + if ($caneditphpsettings != '1') { + $caneditphpsettings = '0'; + } + + if ($change_serversettings != '1') { + $change_serversettings = '0'; + } + + if ($tickets_see_all != '1') { + $tickets_see_all = '0'; + } + + if ($password != '') { + $password = validatePassword($password, true); + $password = makeCryptPassword($password); + } else { + $password = $result['password']; + } + + // check if a resource was set to something lower + // than actually used by the admin/reseller + $res_warning = ""; + if ($customers != $result['customers'] && $customers != -1 && $customers < $result['customers_used']) { + $res_warning .= sprintf($lng['error']['setlessthanalreadyused'], 'customers'); + } + if ($domains != $result['domains'] && $domains != -1 && $domains < $result['domains_used']) { + $res_warning .= sprintf($lng['error']['setlessthanalreadyused'], 'domains'); + } + if ($diskspace != $result['diskspace'] && ($diskspace / 1024) != -1 && $diskspace < $result['diskspace_used']) { + $res_warning .= sprintf($lng['error']['setlessthanalreadyused'], 'diskspace'); + } + if ($traffic != $result['traffic'] && ($traffic / 1024 / 1024) != -1 && $traffic < $result['traffic_used']) { + $res_warning .= sprintf($lng['error']['setlessthanalreadyused'], 'traffic'); + } + if ($emails != $result['emails'] && $emails != -1 && $emails < $result['emails_used']) { + $res_warning .= sprintf($lng['error']['setlessthanalreadyused'], 'emails'); + } + if ($email_accounts != $result['email_accounts'] && $email_accounts != -1 && $email_accounts < $result['email_accounts_used']) { + $res_warning .= sprintf($lng['error']['setlessthanalreadyused'], 'email accounts'); + } + if ($email_forwarders != $result['email_forwarders'] && $email_forwarders != -1 && $email_forwarders < $result['email_forwarders_used']) { + $res_warning .= sprintf($lng['error']['setlessthanalreadyused'], 'email forwarders'); + } + if ($email_quota != $result['email_quota'] && $email_quota != -1 && $email_quota < $result['email_quota_used']) { + $res_warning .= sprintf($lng['error']['setlessthanalreadyused'], 'email quota'); + } + if ($ftps != $result['ftps'] && $ftps != -1 && $ftps < $result['ftps_used']) { + $res_warning .= sprintf($lng['error']['setlessthanalreadyused'], 'ftps'); + } + if ($tickets != $result['tickets'] && $tickets != -1 && $tickets < $result['tickets_used']) { + $res_warning .= sprintf($lng['error']['setlessthanalreadyused'], 'tickets'); + } + if ($mysqls != $result['mysqls'] && $mysqls != -1 && $mysqls < $result['mysqls_used']) { + $res_warning .= sprintf($lng['error']['setlessthanalreadyused'], 'mysqls'); + } + + if (!empty($res_warning)) { + throw new Exception($res_warning, 406); + } + + $upd_data = array( + 'password' => $password, + 'name' => $name, + 'email' => $email, + 'lang' => $def_language, + 'change_serversettings' => $change_serversettings, + 'customers' => $customers, + 'customers_see_all' => $customers_see_all, + 'domains' => $domains, + 'domains_see_all' => $domains_see_all, + 'caneditphpsettings' => $caneditphpsettings, + 'diskspace' => $diskspace, + 'traffic' => $traffic, + 'subdomains' => $subdomains, + 'emails' => $emails, + 'accounts' => $email_accounts, + 'forwarders' => $email_forwarders, + 'quota' => $email_quota, + 'ftps' => $ftps, + 'tickets' => $tickets, + 'tickets_see_all' => $tickets_see_all, + 'mysqls' => $mysqls, + 'ip' => $ipaddress, + 'deactivated' => $deactivated, + 'custom_notes' => $custom_notes, + 'custom_notes_show' => $custom_notes_show, + 'theme' => $theme, + 'adminid' => $id + ); + + $upd_stmt = Database::prepare(" + UPDATE `" . TABLE_PANEL_ADMINS . "` SET + `password` = :password, + `name` = :name, + `email` = :email, + `def_language` = :lang, + `change_serversettings` = :change_serversettings, + `customers` = :customers, + `customers_see_all` = :customers_see_all, + `domains` = :domains, + `domains_see_all` = :domains_see_all, + `caneditphpsettings` = :caneditphpsettings, + `diskspace` = :diskspace, + `traffic` = :traffic, + `subdomains` = :subdomains, + `emails` = :emails, + `email_accounts` = :accounts, + `email_forwarders` = :forwarders, + `email_quota` = :quota, + `ftps` = :ftps, + `tickets` = :tickets, + `tickets_see_all` = :tickets_see_all, + `mysqls` = :mysqls, + `ip` = :ip, + `deactivated` = :deactivated, + `custom_notes` = :custom_notes, + `custom_notes_show` = :custom_notes_show, + `theme` = :theme + WHERE `adminid` = :adminid + "); + Database::pexecute($upd_stmt, $upd_data, true, true); + + $this->logger()->logAction(ADM_ACTION, LOG_INFO, "[API] edited admin '" . $result['loginname'] . "'"); + return $this->response(200, "successfull", $upd_data); + } + } + throw new Exception("Not allowed to execute given command.", 403); + } /** * delete a admin entry by either id or loginname @@ -300,14 +562,66 @@ class Admins extends ApiCommand implements ResourceEntity * optional, the admin-id * @param string $loginname * optional, the loginname - * @param bool $delete_userfiles - * optional, default false * * @throws Exception * @return array */ public function delete() - {} + { + if ($this->isAdmin() && $this->getUserDetail('change_serversettings') == 1) { + $id = $this->getParam('id', true, 0); + $ln_optional = ($id <= 0 ? false : true); + $loginname = $this->getParam('loginname', $ln_optional, ''); + + if ($id <= 0 && empty($loginname)) { + throw new Exception("Either 'id' or 'loginname' parameter must be given", 406); + } + + $json_result = Admins::getLocal($this->getUserData(), array( + 'id' => $id, + 'loginname' => $loginname + ))->get(); + $result = json_decode($json_result, true)['data']; + $id = $result['adminid']; + + // don't be stupid + if ($id == $this->getUserDetail('userid')) { + standard_error('youcantdeleteyourself', '', true); + } + + $del_stmt = Database::prepare(" + DELETE FROM `" . TABLE_PANEL_ADMINS . "` WHERE `adminid` = :adminid + "); + Database::pexecute($del_stmt, array('adminid' => $id), true, true); + + $del_stmt = Database::prepare(" + DELETE FROM `" . TABLE_PANEL_TRAFFIC_ADMINS . "` WHERE `adminid` = :adminid + "); + Database::pexecute($del_stmt, array('adminid' => $id), true, true); + + $del_stmt = Database::prepare(" + DELETE FROM `" . TABLE_PANEL_DISKSPACE_ADMINS . "` WHERE `adminid` = :adminid + "); + Database::pexecute($del_stmt, array('adminid' => $id), true, true); + + $upd_stmt = Database::prepare(" + UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET + `adminid` = :userid WHERE `adminid` = :adminid + "); + Database::pexecute($upd_stmt, array('userid' => $this->getUserDetail('userid'), 'adminid' => $id), true, true); + + $upd_stmt = Database::prepare(" + UPDATE `" . TABLE_PANEL_DOMAINS . "` SET + `adminid` = :userid WHERE `adminid` = :adminid + "); + Database::pexecute($upd_stmt, array('userid' => $this->getUserDetail('userid'), 'adminid' => $id), true, true); + + $this->logger()->logAction(ADM_ACTION, LOG_WARNING, "[API] deleted admin '" . $result['loginname'] . "'"); + updateCounters(); + return $this->response(200, "successfull", $result); + } + throw new Exception("Not allowed to execute given command.", 403); + } /** * unlock a locked admin by either id or loginname @@ -322,7 +636,7 @@ class Admins extends ApiCommand implements ResourceEntity */ public function unlock() { - if ($this->isAdmin()) { + if ($this->isAdmin() && $this->getUserDetail('change_serversettings') == 1) { $id = $this->getParam('id', true, 0); $ln_optional = ($id <= 0 ? false : true); $loginname = $this->getParam('loginname', $ln_optional, ''); diff --git a/lib/classes/api/commands/class.Customers.php b/lib/classes/api/commands/class.Customers.php index d707c7f8..339631b2 100644 --- a/lib/classes/api/commands/class.Customers.php +++ b/lib/classes/api/commands/class.Customers.php @@ -706,6 +706,7 @@ class Customers extends ApiCommand implements ResourceEntity $perlenabled = $this->getParam('perlenabled', true, $result['perlenabled']); $dnsenabled = $this->getParam('dnsenabled', true, $result['dnsenabled']); $deactivated = $this->getParam('deactivated', true, $result['deactivated']); + $theme = $this->getParam('theme', true, $result['theme']); // validation $idna_convert = new idna_convert_wrapper(); @@ -721,6 +722,7 @@ class Customers extends ApiCommand implements ResourceEntity $customernumber = validate($customernumber, 'customer number', '/^[A-Za-z0-9 \-]*$/Di', '', array(), true); $def_language = validate($def_language, 'default language', '', '', array(), true); $custom_notes = validate(str_replace("\r\n", "\n", $custom_notes), 'custom_notes', '/^[^\0]*$/', '', array(), true); + $theme = validate($theme, 'theme', '', '', array(), true); if (Settings::Get('system.mail_quota_enabled') != '1') { $email_quota = - 1; @@ -730,6 +732,10 @@ class Customers extends ApiCommand implements ResourceEntity $tickets = - 1; } + if (empty($theme)) { + $theme = Settings::Get('panel.default_theme'); + } + $diskspace = $diskspace * 1024; $traffic = $traffic * 1024 * 1024; @@ -980,7 +986,8 @@ class Customers extends ApiCommand implements ResourceEntity 'perlenabled' => $perlenabled, 'dnsenabled' => $dnsenabled, 'custom_notes' => $custom_notes, - 'custom_notes_show' => $custom_notes_show + 'custom_notes_show' => $custom_notes_show, + 'theme' => $theme ); $upd_stmt = Database::prepare(" UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET @@ -1015,7 +1022,8 @@ class Customers extends ApiCommand implements ResourceEntity `perlenabled` = :perlenabled, `dnsenabled` = :dnsenabled, `custom_notes` = :custom_notes, - `custom_notes_show` = :custom_notes_show + `custom_notes_show` = :custom_notes_show, + `theme` = :theme WHERE `customerid` = :customerid "); Database::pexecute($upd_stmt, $upd_data); From 662f537a0de025a4704dce5394968ad0e7d07296 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Fri, 23 Feb 2018 12:48:17 +0100 Subject: [PATCH 0470/1335] fixes in Admins.update(); use ApiCommand for theme-, language- and password-change Signed-off-by: Michael Kaufmann (d00p) --- admin_index.php | 46 +++++++++-------------- customer_index.php | 41 +++++++++----------- lib/classes/api/commands/class.Admins.php | 17 ++------- 3 files changed, 38 insertions(+), 66 deletions(-) diff --git a/admin_index.php b/admin_index.php index 959a4632..6efb31ff 100644 --- a/admin_index.php +++ b/admin_index.php @@ -214,15 +214,11 @@ if ($page == 'overview') { } elseif($new_password != $new_password_confirm) { standard_error('newpasswordconfirmerror'); } else { - $chgpwd_stmt = Database::prepare(" - UPDATE `" . TABLE_PANEL_ADMINS . "` - SET `password`= :newpasswd - WHERE `adminid`= :adminid" - ); - Database::pexecute($chgpwd_stmt, array( - 'newpasswd' => makeCryptPassword($new_password), - 'adminid' => (int)$userinfo['adminid'] - )); + try { + Admins::getLocal($userinfo, array('id' => $userinfo['adminid'], 'admin_password' => $new_password))->update(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); + } $log->logAction(ADM_ACTION, LOG_NOTICE, 'changed password'); redirectTo($filename, Array('s' => $s)); } @@ -238,16 +234,13 @@ if ($page == 'overview') { $def_language = validate($_POST['def_language'], 'default language'); if (isset($languages[$def_language])) { - $lng_stmt = Database::prepare(" - UPDATE `" . TABLE_PANEL_ADMINS . "` - SET `def_language`= :deflng - WHERE `adminid`= :adminid" - ); - Database::pexecute($lng_stmt, array( - 'deflng' => $def_language, - 'adminid' => (int)$userinfo['adminid'] - )); + try { + Admins::getLocal($userinfo, array('id' => $userinfo['adminid'], 'def_language' => $def_language))->update(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); + } + // also update current session $lng_stmt = Database::prepare(" UPDATE `" . TABLE_PANEL_SESSIONS . "` SET `language`= :lng @@ -258,7 +251,6 @@ if ($page == 'overview') { 'hash' => $s )); } - $log->logAction(ADM_ACTION, LOG_NOTICE, "changed his/her default language to '" . $def_language . "'"); redirectTo($filename, array('s' => $s)); @@ -284,17 +276,13 @@ if ($page == 'overview') { && $_POST['send'] == 'send' ) { $theme = validate($_POST['theme'], 'theme'); + try { + Admins::getLocal($userinfo, array('id' => $userinfo['adminid'], 'theme' => $theme))->update(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); + } - $theme_stmt = Database::prepare(" - UPDATE `" . TABLE_PANEL_ADMINS . "` - SET `theme`= :theme - WHERE `adminid`= :adminid" - ); - Database::pexecute($theme_stmt, array( - 'theme' => $theme, - 'adminid' => (int)$userinfo['adminid'] - )); - + // also update current session $theme_stmt = Database::prepare(" UPDATE `" . TABLE_PANEL_SESSIONS . "` SET `theme`= :theme diff --git a/customer_index.php b/customer_index.php index 71877c71..85d02158 100644 --- a/customer_index.php +++ b/customer_index.php @@ -122,15 +122,11 @@ if ($page == 'overview') { standard_error('newpasswordconfirmerror'); } else { // Update user password - $stmt = Database::prepare("UPDATE `" . TABLE_PANEL_CUSTOMERS . "` - SET `password` = :newpassword - WHERE `customerid` = :customerid" - ); - $params = array( - "newpassword" => makeCryptPassword($new_password), - "customerid" => $userinfo['customerid'] - ); - Database::pexecute($stmt, $params); + try { + Customers::getLocal($userinfo, array('id' => $userinfo['customerid'], 'new_customer_password' => $new_password))->update(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); + } $log->logAction(USR_ACTION, LOG_NOTICE, 'changed password'); // Update ftp password @@ -181,21 +177,20 @@ if ($page == 'overview') { if (isset($_POST['send']) && $_POST['send'] == 'send') { $def_language = validate($_POST['def_language'], 'default language'); if (isset($languages[$def_language])) { - $stmt = Database::prepare("UPDATE `" . TABLE_PANEL_CUSTOMERS . "` - SET `def_language` = :lang - WHERE `customerid` = :customerid" - ); - Database::pexecute($stmt, array("lang" => $def_language, "customerid" => $userinfo['customerid'])); + try { + Customers::getLocal($userinfo, array('id' => $userinfo['customerid'], 'def_language' => $def_language))->update(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); + } + // also update current session $stmt = Database::prepare("UPDATE `" . TABLE_PANEL_SESSIONS . "` SET `language` = :lang WHERE `hash` = :hash" ); Database::pexecute($stmt, array("lang" => $def_language, "hash" => $s)); - - $log->logAction(USR_ACTION, LOG_NOTICE, "changed default language to '" . $def_language . "'"); } - + $log->logAction(USR_ACTION, LOG_NOTICE, "changed default language to '" . $def_language . "'"); redirectTo($filename, array('s' => $s)); } else { $default_lang = Settings::Get('panel.standardlanguage'); @@ -213,13 +208,13 @@ if ($page == 'overview') { } elseif ($page == 'change_theme') { if (isset($_POST['send']) && $_POST['send'] == 'send') { $theme = validate($_POST['theme'], 'theme'); + try { + Customers::getLocal($userinfo, array('id' => $userinfo['customerid'], 'theme' => $theme))->update(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); + } - $stmt = Database::prepare("UPDATE `" . TABLE_PANEL_CUSTOMERS . "` - SET `theme` = :theme - WHERE `customerid` = :customerid" - ); - Database::pexecute($stmt, array("theme" => $theme, "customerid" => $userinfo['customerid'])); - + // also update current session $stmt = Database::prepare("UPDATE `" . TABLE_PANEL_SESSIONS . "` SET `theme` = :theme WHERE `hash` = :hash" diff --git a/lib/classes/api/commands/class.Admins.php b/lib/classes/api/commands/class.Admins.php index 5779b68c..4c66592d 100644 --- a/lib/classes/api/commands/class.Admins.php +++ b/lib/classes/api/commands/class.Admins.php @@ -313,14 +313,14 @@ class Admins extends ApiCommand implements ResourceEntity $name = $this->getParam('name', true, $result['name']); $idna_convert = new idna_convert_wrapper(); $email = $this->getParam('email', true, $idna_convert->decode($result['email'])); + $password = $this->getParam('admin_password', true, ''); + $def_language = $this->getParam('def_language', true, $result['def_language']); $custom_notes = $this->getParam('custom_notes', true, $result['custom_notes']); $custom_notes_show = $this->getParam('custom_notes_show', true, $result['custom_notes_show']); $theme = $this->getParam('theme', true, $result['theme']); // you cannot edit some of the details of yourself if ($result['adminid'] == $this->getUserDetail('userid')) { - $password = ''; - $def_language = $result['def_language']; $deactivated = $result['deactivated']; $customers = $result['customers']; $domains = $result['domains']; @@ -341,8 +341,6 @@ class Admins extends ApiCommand implements ResourceEntity $traffic = $result['traffic']; $ipaddress = $result['ip']; } else { - $password = $this->getParam('admin_password', true, ''); - $def_language = $this->getParam('def_language', true, $result['def_language']); $deactivated = $this->getParam('deactivated', true, $result['deactivated']); $dec_places = Settings::Get('panel.decimal_places'); @@ -377,6 +375,7 @@ class Admins extends ApiCommand implements ResourceEntity $def_language = validate($def_language, 'default language', '', '', array(), true); $custom_notes = validate(str_replace("\r\n", "\n", $custom_notes), 'custom_notes', '/^[^\0]*$/', '', array(), true); $theme = validate($theme, 'theme', '', '', array(), true); + $password = validate($password, 'password', '', '', array(), true); if (Settings::Get('system.mail_quota_enabled') != '1') { $email_quota = - 1; @@ -390,16 +389,6 @@ class Admins extends ApiCommand implements ResourceEntity $theme = Settings::Get('panel.default_theme'); } - $password = validate($password, 'password', '', '', array(), true); - // only check if not empty, - // cause empty == generate password automatically - if ($password != '') { - $password = validatePassword($password, true); - } - - $diskspace = $diskspace * 1024; - $traffic = $traffic * 1024 * 1024; - if ($name == '') { standard_error(array( 'stringisempty', From 6409fb2dbedd93c0e0844cf2e0f0397543f235f3 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Fri, 23 Feb 2018 15:17:22 +0100 Subject: [PATCH 0471/1335] started working on Mysqls-ApiCommand Signed-off-by: Michael Kaufmann (d00p) --- customer_mysql.php | 42 ++-- lib/classes/api/commands/class.Mysqls.php | 283 ++++++++++++++++++++++ 2 files changed, 297 insertions(+), 28 deletions(-) create mode 100644 lib/classes/api/commands/class.Mysqls.php diff --git a/customer_mysql.php b/customer_mysql.php index 8367adec..774daf6b 100644 --- a/customer_mysql.php +++ b/customer_mysql.php @@ -93,12 +93,15 @@ if ($page == 'overview') { eval("echo \"" . getTemplate('mysql/mysqls') . "\";"); } elseif ($action == 'delete' && $id != 0) { - $result_stmt = Database::prepare('SELECT `id`, `databasename`, `description`, `dbserver` FROM `' . TABLE_PANEL_DATABASES . '` - WHERE `customerid`="' . (int)$userinfo['customerid'] . '" - AND `id`="' . (int)$id . '"' - ); - Database::pexecute($result_stmt, array("customerid" => $userinfo['customerid'])); - $result = $result_stmt->fetch(PDO::FETCH_ASSOC); + + try { + $json_result = Mysqls::getLocal($userinfo, array( + 'id' => $id + ))->get(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); + } + $result = json_decode($json_result, true)['data']; if (isset($result['databasename']) && $result['databasename'] != '') { @@ -112,28 +115,11 @@ if ($page == 'overview') { } if (isset($_POST['send']) && $_POST['send'] == 'send') { - // Begin root-session - Database::needRoot(true, $result['dbserver']); - $dbm = new DbManager($log); - $dbm->getManager()->deleteDatabase($result['databasename']); - $log->logAction(USR_ACTION, LOG_INFO, "deleted database '" . $result['databasename'] . "'"); - Database::needRoot(false); - // End root-session - - $stmt = Database::prepare("DELETE FROM `" . TABLE_PANEL_DATABASES . "` - WHERE `customerid` = :customerid - AND `id` = :id" - ); - Database::pexecute($stmt, array("customerid" => $userinfo['customerid'], "id" => $id)); - - $resetaccnumber = ($userinfo['mysqls_used'] == '1') ? " , `mysql_lastaccountnumber` = '0' " : ''; - - $stmt = Database::prepare("UPDATE `" . TABLE_PANEL_CUSTOMERS . "` - SET `mysqls_used` = `mysqls_used` - 1 " . $resetaccnumber . " - WHERE `customerid` = :customerid" - ); - Database::pexecute($stmt, array("customerid" => $userinfo['customerid'])); - + try { + Mysqls::getLocal($userinfo, $_POST)->delete(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); + } redirectTo($filename, array('page' => $page, 's' => $s)); } else { $dbnamedesc = $result['databasename']; diff --git a/lib/classes/api/commands/class.Mysqls.php b/lib/classes/api/commands/class.Mysqls.php new file mode 100644 index 00000000..fd2ec191 --- /dev/null +++ b/lib/classes/api/commands/class.Mysqls.php @@ -0,0 +1,283 @@ + (2010-) + * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt + * @package API + * @since 0.10.0 + * + */ +class Mysqls extends ApiCommand implements ResourceEntity +{ + + public function add() + {} + + /** + * return a mysql database entry by either id or dbname + * + * @param int $id + * optional, the database-id + * @param string $dbname + * optional, the databasename + * @param int $dbserver + * optional, specify database-server, default is none + * + * @throws Exception + * @return array + */ + public function get() + { + $id = $this->getParam('id', true, 0); + $dn_optional = ($id <= 0 ? false : true); + $dbname = $this->getParam('dbname', $dn_optional, ''); + $dbserver = $this->getParam('dbserver', true, - 1); + + if ($id <= 0 && empty($dbname)) { + throw new Exception("Either 'id' or 'dbname' parameter must be given", 406); + } + + if ($this->isAdmin()) { + if ($this->getUserDetail('customers_see_all') != 1) { + // if it's a reseller or an admin who cannot see all customers, we need to check + // whether the database belongs to one of his customers + $json_result = Customers::getLocal($this->getUserData())->list(); + $custom_list_result = json_decode($json_result, true)['data']['list']; + $customer_ids = array(); + foreach ($custom_list_result as $customer) { + $customer_ids[] = $customer['customerid']; + } + if (count($customer_ids) > 0) { + $result_stmt = Database::prepare(" + SELECT * FROM `" . TABLE_PANEL_DATABASES . "` + WHERE " . ($id > 0 ? "`id` = :iddn" : "`databasename` = :iddn") . ($dbserver >= 0 ? " AND `dbserver` = :dbserver" : "") . " AND `customerid` IN (:customerids) + "); + $params = array( + 'iddn' => ($id <= 0 ? $dbname : $id), + 'customerids' => implode(", ", $customer_ids) + ); + if ($dbserver >= 0) { + $params['dbserver'] = $dbserver; + } + } else { + throw new Exception("You do not have any customers yet", 406); + } + } else { + $result_stmt = Database::prepare(" + SELECT * FROM `" . TABLE_PANEL_DATABASES . "` + WHERE " . ($id > 0 ? "`id` = :iddn" : "`databasename` = :iddn") . ($dbserver >= 0 ? " AND `dbserver` = :dbserver" : "")); + $params = array( + 'iddn' => ($id <= 0 ? $dbname : $id) + ); + if ($dbserver >= 0) { + $params['dbserver'] = $dbserver; + } + } + } else { + if ($id != $this->getUserDetail('customerid')) { + throw new Exception("You cannot access data of other customers", 401); + } + $result_stmt = Database::prepare(" + SELECT * FROM `" . TABLE_PANEL_DATABASES . "` + WHERE `customerid`= :customerid AND " . ($id > 0 ? "`id` = :iddn" : "`databasename` = :iddn") . ($dbserver >= 0 ? " AND `dbserver` = :dbserver" : "")); + $params = array( + 'customerid' => $this->getUserDetail('customerid'), + 'iddn' => ($id <= 0 ? $dbname : $id) + ); + if ($dbserver >= 0) { + $params['dbserver'] = $dbserver; + } + } + $result = Database::pexecute_first($result_stmt, $params, true, true); + if ($result) { + Database::needRoot(true, $dbserver); + $mbdata_stmt = Database::prepare(" + SELECT SUM(data_length + index_length) as MB FROM information_schema.TABLES + WHERE table_schema = :table_schema + GROUP BY table_schema + "); + Database::pexecute($mbdata_stmt, array( + "table_schema" => $result['databasename'] + ), true, true); + $mbdata = $mbdata_stmt->fetch(PDO::FETCH_ASSOC); + Database::needRoot(false); + $result['size'] = $mbdata['MB']; + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_NOTICE, "[API] get database '" . $result['databasename'] . "'"); + return $this->response(200, "successfull", $result); + } + $key = ($id > 0 ? "id #" . $id : "dbname '" . $dbname . "'"); + throw new Exception("MySQL database with " . $key . " could not be found", 404); + } + + public function update() + {} + + /** + * list all databases, if called from an admin, list all databases of all customers you are allowed to view, or specify id or loginname for one specific customer + * + * @param int $dbserver + * optional, specify dbserver to select from, else use all available + * @param int $customerid + * optional, admin-only, select dbs of a specific customer by id + * @param string $loginname + * optional, admin-only, select dbs of a specific customer by loginname + * + * @return array count|list + */ + public function list() + { + $result = array(); + $dbserver = $this->getParam('dbserver', true, - 1); + if ($this->isAdmin()) { + // if we're an admin, list all databases of all the admins customers + // or optionally for one specific customer identified by id or loginname + $customerid = $this->getParam('customerid', true, 0); + $loginname = $this->getParam('loginname', true, ''); + + if (! empty($customer_id) || ! empty($loginname)) { + $json_result = Customers::getLocal($this->getUserData(), array( + 'id' => $customerid, + 'loginname' => $loginname + ))->get(); + $custom_list_result = array( + json_decode($json_result, true)['data'] + ); + } else { + $json_result = Customers::getLocal($this->getUserData())->list(); + $custom_list_result = json_decode($json_result, true)['data']['list']; + } + $customer_ids = array(); + foreach ($custom_list_result as $customer) { + $customer_ids[] = $customer['customerid']; + } + } else { + $customer_ids = array( + $this->getUserDetail('customerid') + ); + } + $result_stmt = Database::prepare(" + SELECT * FROM `" . TABLE_PANEL_DATABASES . "` + WHERE `customerid`= :customerid AND `dbserver` = :dbserver + "); + if ($dbserver < 0) { + // use all dbservers + $dbservers_stmt = Database::query("SELECT DISTINCT `dbserver` FROM `" . TABLE_PANEL_DATABASES . "`"); + $dbservers = $dbservers_stmt->fetchAll(PDO::FETCH_ASSOC); + } else { + // use specific dbserver + $dbservers = array( + array( + 'dbserver' => $dbserver + ) + ); + } + + foreach ($customer_ids as $customer_id) { + foreach ($dbservers as $_dbserver) { + Database::pexecute($result_stmt, array( + 'customerid' => $customer_id, + 'dbserver' => $_dbserver['dbserver'] + ), true, true); + // Begin root-session + Database::needRoot(true, $_dbserver['dbserver']); + while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) { + $mbdata_stmt = Database::prepare(" + SELECT SUM(data_length + index_length) as MB FROM information_schema.TABLES + WHERE table_schema = :table_schema + GROUP BY table_schema + "); + Database::pexecute($mbdata_stmt, array( + "table_schema" => $row['databasename'] + ), true, true); + $mbdata = $mbdata_stmt->fetch(PDO::FETCH_ASSOC); + $row['size'] = $mbdata['MB']; + $result[] = $row; + } + Database::needRoot(false); + } + } + return $this->response(200, "successfull", array( + 'count' => count($result), + 'list' => $result + )); + } + + /** + * delete a mysql database by either id or dbname + * + * @param int $id + * optional, the database-id + * @param string $dbname + * optional, the databasename + * @param int $dbserver + * optional, specify database-server, default is none + * + * @throws Exception + * @return array + */ + public function delete() + { + $id = $this->getParam('id', true, 0); + $dn_optional = ($id <= 0 ? false : true); + $dbname = $this->getParam('dbname', $dn_optional, ''); + $dbserver = $this->getParam('dbserver', true, - 1); + + if ($id <= 0 && empty($dbname)) { + throw new Exception("Either 'id' or 'dbname' parameter must be given", 406); + } + + $json_result = Mysqls::getLocal($this->getUserData(), array( + 'id' => $id, + 'dbname' => $dbname, + 'dbserver' => $dbserver + ))->get(); + $result = json_decode($json_result, true)['data']; + $id = $result['id']; + + // Begin root-session + Database::needRoot(true, $result['dbserver']); + $dbm = new DbManager($this->logger()); + $dbm->getManager()->deleteDatabase($result['databasename']); + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_WARNING, "[API] deleted database '" . $result['databasename'] . "'"); + Database::needRoot(false); + // End root-session + + // delete from table + $stmt = Database::prepare("DELETE FROM `" . TABLE_PANEL_DATABASES . "` WHERE `id` = :id"); + Database::pexecute($stmt, array( + "id" => $id + ), true, true); + + // get needed customer info to reduce the mysql-usage-counter by one + if ($this->isAdmin()) { + $json_result = Customers::getLocal($this->getUserData(), array( + 'id' => $result['customerid'] + ))->get(); + $customer = json_decode($json_result, true)['data']; + $mysql_used = $customer['mysqls_used']; + $customer_id = $customer['customer_id']; + } else { + $mysql_used = $this->getUserDetail('mysqls_used'); + $customer_id = $this->getUserDetail('customer_id'); + } + // reduce mysql-usage-counter + $resetaccnumber = ($mysql_used == '1') ? " , `mysql_lastaccountnumber` = '0' " : ''; + $stmt = Database::prepare(" + UPDATE `" . TABLE_PANEL_CUSTOMERS . "` + SET `mysqls_used` = `mysqls_used` - 1 " . $resetaccnumber . " + WHERE `customerid` = :customerid + "); + Database::pexecute($stmt, array( + "customerid" => $customer_id + ), true, true); + return $this->response(200, "successfull", $result); + } +} From 831ee221f6bb8e0459f0a33168b4da17b968d989 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Fri, 23 Feb 2018 18:18:31 +0100 Subject: [PATCH 0472/1335] make lng, version, dbversion and branding protected variables of ApiCommand to avoid the need of 'global' statement Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/abstract.ApiCommand.php | 36 ++++++++++++- lib/classes/api/commands/class.Admins.php | 22 ++++---- lib/classes/api/commands/class.Customers.php | 6 +-- lib/classes/api/commands/class.FpmDaemons.php | 2 +- lib/classes/api/commands/class.Froxlor.php | 54 ++++++++++--------- .../api/commands/class.PhpSettings.php | 2 +- 6 files changed, 79 insertions(+), 43 deletions(-) diff --git a/lib/classes/api/abstract.ApiCommand.php b/lib/classes/api/abstract.ApiCommand.php index 333985d5..ee8f1a90 100644 --- a/lib/classes/api/abstract.ApiCommand.php +++ b/lib/classes/api/abstract.ApiCommand.php @@ -60,6 +60,34 @@ abstract class ApiCommand */ private $cmd_params = null; + /** + * language strings array + * + * @var array + */ + protected $lng = null; + + /** + * froxlor version + * + * @var string + */ + protected $version = null; + + /** + * froxlor dbversion + * + * @var int + */ + protected $dbversion = null; + + /** + * froxlor version-branding + * + * @var string + */ + protected $branding = null; + /** * * @param array $header @@ -73,8 +101,11 @@ abstract class ApiCommand */ public function __construct($header = null, $params = null, $userinfo = null) { - global $lng; - + global $lng, $version, $dbversion, $branding; + + $this->version = $version; + $this->dbversion = $dbversion; + $this->branding = $branding; $this->cmd_params = $params; if (! empty($header)) { $this->readUserData($header); @@ -93,6 +124,7 @@ abstract class ApiCommand } $this->initLang(); + $this->lng = $lng; $this->initMail(); if ($this->debug) { diff --git a/lib/classes/api/commands/class.Admins.php b/lib/classes/api/commands/class.Admins.php index 4c66592d..88ee04e1 100644 --- a/lib/classes/api/commands/class.Admins.php +++ b/lib/classes/api/commands/class.Admins.php @@ -438,37 +438,37 @@ class Admins extends ApiCommand implements ResourceEntity // than actually used by the admin/reseller $res_warning = ""; if ($customers != $result['customers'] && $customers != -1 && $customers < $result['customers_used']) { - $res_warning .= sprintf($lng['error']['setlessthanalreadyused'], 'customers'); + $res_warning .= sprintf($this->lng['error']['setlessthanalreadyused'], 'customers'); } if ($domains != $result['domains'] && $domains != -1 && $domains < $result['domains_used']) { - $res_warning .= sprintf($lng['error']['setlessthanalreadyused'], 'domains'); + $res_warning .= sprintf($this->lng['error']['setlessthanalreadyused'], 'domains'); } if ($diskspace != $result['diskspace'] && ($diskspace / 1024) != -1 && $diskspace < $result['diskspace_used']) { - $res_warning .= sprintf($lng['error']['setlessthanalreadyused'], 'diskspace'); + $res_warning .= sprintf($this->lng['error']['setlessthanalreadyused'], 'diskspace'); } if ($traffic != $result['traffic'] && ($traffic / 1024 / 1024) != -1 && $traffic < $result['traffic_used']) { - $res_warning .= sprintf($lng['error']['setlessthanalreadyused'], 'traffic'); + $res_warning .= sprintf($this->lng['error']['setlessthanalreadyused'], 'traffic'); } if ($emails != $result['emails'] && $emails != -1 && $emails < $result['emails_used']) { - $res_warning .= sprintf($lng['error']['setlessthanalreadyused'], 'emails'); + $res_warning .= sprintf($this->lng['error']['setlessthanalreadyused'], 'emails'); } if ($email_accounts != $result['email_accounts'] && $email_accounts != -1 && $email_accounts < $result['email_accounts_used']) { - $res_warning .= sprintf($lng['error']['setlessthanalreadyused'], 'email accounts'); + $res_warning .= sprintf($this->lng['error']['setlessthanalreadyused'], 'email accounts'); } if ($email_forwarders != $result['email_forwarders'] && $email_forwarders != -1 && $email_forwarders < $result['email_forwarders_used']) { - $res_warning .= sprintf($lng['error']['setlessthanalreadyused'], 'email forwarders'); + $res_warning .= sprintf($this->lng['error']['setlessthanalreadyused'], 'email forwarders'); } if ($email_quota != $result['email_quota'] && $email_quota != -1 && $email_quota < $result['email_quota_used']) { - $res_warning .= sprintf($lng['error']['setlessthanalreadyused'], 'email quota'); + $res_warning .= sprintf($this->lng['error']['setlessthanalreadyused'], 'email quota'); } if ($ftps != $result['ftps'] && $ftps != -1 && $ftps < $result['ftps_used']) { - $res_warning .= sprintf($lng['error']['setlessthanalreadyused'], 'ftps'); + $res_warning .= sprintf($this->lng['error']['setlessthanalreadyused'], 'ftps'); } if ($tickets != $result['tickets'] && $tickets != -1 && $tickets < $result['tickets_used']) { - $res_warning .= sprintf($lng['error']['setlessthanalreadyused'], 'tickets'); + $res_warning .= sprintf($this->lng['error']['setlessthanalreadyused'], 'tickets'); } if ($mysqls != $result['mysqls'] && $mysqls != -1 && $mysqls < $result['mysqls_used']) { - $res_warning .= sprintf($lng['error']['setlessthanalreadyused'], 'mysqls'); + $res_warning .= sprintf($this->lng['error']['setlessthanalreadyused'], 'mysqls'); } if (!empty($res_warning)) { diff --git a/lib/classes/api/commands/class.Customers.php b/lib/classes/api/commands/class.Customers.php index 339631b2..26ae4568 100644 --- a/lib/classes/api/commands/class.Customers.php +++ b/lib/classes/api/commands/class.Customers.php @@ -97,8 +97,6 @@ class Customers extends ApiCommand implements ResourceEntity public function add() { - global $lng; - if ($this->isAdmin()) { if ($this->getUserDetail('customers_used') < $this->getUserDetail('customers') || $this->getUserDetail('customers') == '-1') { @@ -600,7 +598,7 @@ class Customers extends ApiCommand implements ResourceEntity 'adminid' => $this->getUserDetail('adminid'), 'deflang' => $def_language ), true, true); - $mail_subject = html_entity_decode(replace_variables((($result['value'] != '') ? $result['value'] : $lng['mails']['createcustomer']['subject']), $replace_arr)); + $mail_subject = html_entity_decode(replace_variables((($result['value'] != '') ? $result['value'] : $this->lng['mails']['createcustomer']['subject']), $replace_arr)); $result_stmt = Database::prepare(" SELECT `value` FROM `" . TABLE_PANEL_TEMPLATES . "` @@ -609,7 +607,7 @@ class Customers extends ApiCommand implements ResourceEntity 'adminid' => $this->getUserDetail('adminid'), 'deflang' => $def_language ), true, true); - $mail_body = html_entity_decode(replace_variables((($result['value'] != '') ? $result['value'] : $lng['mails']['createcustomer']['mailbody']), $replace_arr)); + $mail_body = html_entity_decode(replace_variables((($result['value'] != '') ? $result['value'] : $this->lng['mails']['createcustomer']['mailbody']), $replace_arr)); $_mailerror = false; try { diff --git a/lib/classes/api/commands/class.FpmDaemons.php b/lib/classes/api/commands/class.FpmDaemons.php index 205d6be8..c604e53c 100644 --- a/lib/classes/api/commands/class.FpmDaemons.php +++ b/lib/classes/api/commands/class.FpmDaemons.php @@ -52,7 +52,7 @@ class FpmDaemons extends ApiCommand implements ResourceEntity } if (empty($configs)) { - $configs[] = $lng['admin']['phpsettings']['notused']; + $configs[] = $this->lng['admin']['phpsettings']['notused']; } $row['configs'] = $configs; diff --git a/lib/classes/api/commands/class.Froxlor.php b/lib/classes/api/commands/class.Froxlor.php index 8483e5a5..21c0951e 100644 --- a/lib/classes/api/commands/class.Froxlor.php +++ b/lib/classes/api/commands/class.Froxlor.php @@ -26,14 +26,12 @@ class Froxlor extends ApiCommand */ public function checkUpdate() { - global $version, $branding; - if ($this->isAdmin() && $this->getUserDetail('change_serversettings')) { // log our actions $this->logger()->logAction(ADM_ACTION, LOG_NOTICE, "[API] checking for updates"); // check for new version - define('UPDATE_URI', "https://version.froxlor.org/Froxlor/api/" . $version); + define('UPDATE_URI', "https://version.froxlor.org/Froxlor/api/" . $this->version); $latestversion = HttpClient::urlGet(UPDATE_URI); $latestversion = explode('|', $latestversion); @@ -44,7 +42,7 @@ class Froxlor extends ApiCommand // add the branding so debian guys are not gettings confused // about their version-number - $version_label = $_version . $branding; + $version_label = $_version . $this->branding; $version_link = $_link; $message_addinfo = $_message; @@ -53,7 +51,7 @@ class Froxlor extends ApiCommand // check for customized version to not output // "There is a newer version of froxlor" besides the error-message $isnewerversion = - 1; - } elseif (version_compare2($version, $_version) == - 1) { + } elseif (version_compare2($this->version, $_version) == - 1) { // there is a newer version - yay $isnewerversion = 1; } else { @@ -64,7 +62,7 @@ class Froxlor extends ApiCommand // anzeige über version-status mit ggfls. formular // zum update schritt #1 -> download if ($isnewerversion == 1) { - $text = 'There is a newer version available: "' . $_version . '" (Your current version is: ' . $version . ')'; + $text = 'There is a newer version available: "' . $_version . '" (Your current version is: ' . $this->version . ')'; return $this->response(200, "successfull", array( 'message' => $text, 'link' => $version_link, @@ -82,13 +80,15 @@ class Froxlor extends ApiCommand } /** - * @TODO import settings + * + * @todo import settings */ public function importSettings() {} /** - * @TODO export settings to file + * + * @todo export settings to file */ public function exportSettings() {} @@ -100,21 +100,24 @@ class Froxlor extends ApiCommand */ public function listSettings() { - $sel_stmt = Database::prepare(" - SELECT * FROM `" . TABLE_PANEL_SETTINGS . "` ORDER BY settinggroup ASC, varname ASC - "); - Database::pexecute($sel_stmt, null, true, true); - $result = array(); - while ($row = $sel_stmt->fetch(PDO::FETCH_ASSOC)) { - $result[] = array( - 'key' => $row['settinggroup'] . '.' . $row['varname'], - 'value' => $row['value'] - ); + if ($this->isAdmin() && $this->getUserDetail('change_serversettings')) { + $sel_stmt = Database::prepare(" + SELECT * FROM `" . TABLE_PANEL_SETTINGS . "` ORDER BY settinggroup ASC, varname ASC + "); + Database::pexecute($sel_stmt, null, true, true); + $result = array(); + while ($row = $sel_stmt->fetch(PDO::FETCH_ASSOC)) { + $result[] = array( + 'key' => $row['settinggroup'] . '.' . $row['varname'], + 'value' => $row['value'] + ); + } + return $this->response(200, "successfull", array( + 'count' => count($result), + 'list' => $result + )); } - return $this->response(200, "successfull", array( - 'count' => count($result), - 'list' => $result - )); + throw new Exception("Not allowed to execute given command.", 403); } /** @@ -122,7 +125,7 @@ class Froxlor extends ApiCommand * * @param string $key * settinggroup.varname couple - * + * * @throws Exception * @return string */ @@ -142,12 +145,15 @@ class Froxlor extends ApiCommand * settinggroup.varname couple * @param string $value * optional the new value, default is '' - * + * * @throws Exception * @return string */ public function updateSetting() { + // currently not implemented as it required validation too so no wrong settings are being stored via API + throw new Exception("Not available yet.", 501); + if ($this->isAdmin() && $this->getUserDetail('change_serversettings')) { $setting = $this->getParam('key'); $value = $this->getParam('value', true, ''); diff --git a/lib/classes/api/commands/class.PhpSettings.php b/lib/classes/api/commands/class.PhpSettings.php index 8d0a5dd8..db10ac8d 100644 --- a/lib/classes/api/commands/class.PhpSettings.php +++ b/lib/classes/api/commands/class.PhpSettings.php @@ -81,7 +81,7 @@ class PhpSettings extends ApiCommand implements ResourceEntity } if (empty($domains)) { - $domains[] = $lng['admin']['phpsettings']['notused']; + $domains[] = $this->lng['admin']['phpsettings']['notused']; } // check whether this is our default config From 1c4ecdffbf2cce2f4f2036944bdb9efb38958bdc Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Fri, 23 Feb 2018 18:24:10 +0100 Subject: [PATCH 0473/1335] use correct dbserver for getting mysql-size info in Mysqls.get() Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/commands/class.Mysqls.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/classes/api/commands/class.Mysqls.php b/lib/classes/api/commands/class.Mysqls.php index fd2ec191..e55c6651 100644 --- a/lib/classes/api/commands/class.Mysqls.php +++ b/lib/classes/api/commands/class.Mysqls.php @@ -98,7 +98,7 @@ class Mysqls extends ApiCommand implements ResourceEntity } $result = Database::pexecute_first($result_stmt, $params, true, true); if ($result) { - Database::needRoot(true, $dbserver); + Database::needRoot(true, $result['dbserver']); $mbdata_stmt = Database::prepare(" SELECT SUM(data_length + index_length) as MB FROM information_schema.TABLES WHERE table_schema = :table_schema From 344ca827e42b5dd1bea6beb75a26509459cd26a0 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Fri, 23 Feb 2018 19:46:57 +0100 Subject: [PATCH 0474/1335] beautify api-help in webinterface Signed-off-by: Michael Kaufmann (d00p) --- apihelp.php | 43 +- templates/Sparkle/apihelp/index.tpl | 5 - templates/Sparkle/assets/css/main.css | 1368 ++++++++++++++----------- 3 files changed, 811 insertions(+), 605 deletions(-) diff --git a/apihelp.php b/apihelp.php index 9e9be7a5..deee08d9 100644 --- a/apihelp.php +++ b/apihelp.php @@ -73,42 +73,57 @@ foreach ($output_arr as $module => $functions) { // sort by function ksort($functions); + $apihelp .= "

    ".$module."



    "; + // output ALL the functions foreach ($functions as $function => $funcdata) { - $apihelp .= "
    "; - $apihelp .= "

    " . ($funcdata['return_type'] == - 1 ? "no-return-type" : $funcdata['return_type']) . " "; - $apihelp .= "" . $module . "." . $function . "

    "; + $apihelp .= "
    "; + $apihelp .= "

    ".$module." - "; // description - if (strtoupper(substr($funcdata['head'], 0, 5)) == "@TODO") + if (strtoupper(substr($funcdata['head'], 0, 5)) == "@TODO") { $apihelp .= ""; + } $apihelp .= $funcdata['head']; - if (strtoupper(substr($funcdata['head'], 0, 5)) == "@TODO") + if (strtoupper(substr($funcdata['head'], 0, 5)) == "@TODO") { $apihelp .= ""; + } + $apihelp .= "

    "; + $apihelp .= "Command"." "; + $apihelp .= "".$module.".".$function."
    "; + // output ALL the params; if (count($funcdata['params_list']) > 0) { - $parms = "

    Parameters:
      "; + $parms = "
      Parameter
      "; + $parms .= ""; + $parms .= ""; + $parms .= ""; // separate and format them foreach ($funcdata['params_list'] as $index => $param) { - $parms .= "
    • "; + $parms .= "
    • "; + $parms .= ""; + $parms .= ""; } - $apihelp .= "" . $parms; + $parms .= "
      FieldTypeDescription
      ";
       				// check whether the parameter is optional
       				if (! empty($param['desc']) && strtolower(substr(trim($param['desc']), 0, 8)) == "optional") {
      -					$parms .= "optional ";
      +					$parms .= "".$param['name']."";
       					$param['desc'] = substr(trim($param['desc']), 8);
       					if (substr($param['desc'], 0, 1) == ',') {
       						$param['desc'] = substr(trim($param['desc']), 1);
       					}
      +				} else {
      +					$parms .= "".$param['name']."";
       				}
      -				$parms .= "" . (strtolower($param['type']) == 'unknown' ? "unknown" : $param['type']) . " " . $param['name'] . "";
      +				$parms .= "
      " . (strtolower($param['type']) == 'unknown' ? "unknown" : $param['type']).""; if (! empty($param['desc'])) { - $parms .= " " . trim($param['desc']); + $parms .= trim($param['desc']); } - $parms .= "
    • "; + $parms .= "
    • "; + $apihelp .= $parms; } - $apihelp .= "


    "; + $apihelp .= "
    Returns " . ($funcdata['return_type'] == - 1 ? "no-return-type" : $funcdata['return_type']); + $apihelp .= "
    "; } - $apihelp .= "
    "; } eval("echo \"" . getTemplate("apihelp/index", 1) . "\";"); diff --git a/templates/Sparkle/apihelp/index.tpl b/templates/Sparkle/apihelp/index.tpl index 793a57a8..867008dc 100644 --- a/templates/Sparkle/apihelp/index.tpl +++ b/templates/Sparkle/apihelp/index.tpl @@ -1,11 +1,6 @@ $header
    -

    - API help -

    -
    -
    {$apihelp}
    diff --git a/templates/Sparkle/assets/css/main.css b/templates/Sparkle/assets/css/main.css index 3d3f27bc..83a93250 100644 --- a/templates/Sparkle/assets/css/main.css +++ b/templates/Sparkle/assets/css/main.css @@ -1,98 +1,126 @@ @charset "UTF-8"; /* RESET */ -html,body,div,ul,ol,li,dl,dt,dd,h1,h2,h3,h4,h5,h6,pre,form,p,blockquote,fieldset,input { margin:0; padding:0; } -h1,h2,h3,h4,h5,h6,pre,code,address,caption,cite,code,em,strong,th { font-size:1em; font-weight:400; font-style:normal; } -ul,ol { list-style:none; } -fieldset,img { border:none; } -caption,th { text-align:left; } -table { border-collapse:collapse; border-spacing:0; } -article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section { display:block; } +html, body, div, ul, ol, li, dl, dt, dd, h1, h2, h3, h4, h5, h6, pre, + form, p, blockquote, fieldset, input { + margin: 0; + padding: 0; +} + +h1, h2, h3, h4, h5, h6, pre, code, address, caption, cite, code, em, + strong, th { + font-size: 1em; + font-weight: 400; + font-style: normal; +} + +ul, ol { + list-style: none; +} + +fieldset, img { + border: none; +} + +caption, th { + text-align: left; +} + +table { + border-collapse: collapse; + border-spacing: 0; +} + +article, aside, details, figcaption, figure, footer, header, hgroup, + menu, nav, section { + display: block; +} /* TYPE */ -html,body { - font:12px/18px 'Lucida Grande','Lucida Sans Unicode',Helvetica,Arial,Verdana,sans-serif; +html, body { + font: 12px/18px 'Lucida Grande', 'Lucida Sans Unicode', Helvetica, Arial, + Verdana, sans-serif; background-color: #f5f5f5; - color:#444; + color: #444; -webkit-font-smoothing: subpixel-antialiased; } body { - margin:0; - padding:0; + margin: 0; + padding: 0; } strong { - font-weight:600; + font-weight: 600; } .content { - background-color:#f7f8fa; - margin-top:53px; - min-width:100%; - border-bottom:1px solid #b6c0cd; + background-color: #f7f8fa; + margin-top: 53px; + min-width: 100%; + border-bottom: 1px solid #b6c0cd; } /* * main container */ .main { - margin-left:230px; - padding:30px; - background-color:#fff; - border-left:1px solid #b6c0cd; - margin-bottom:0; + margin-left: 230px; + padding: 30px; + background-color: #fff; + border-left: 1px solid #b6c0cd; + margin-bottom: 0; } .dark { - background:#f0f2f4; - border-bottom:1px solid #d1d5d8; + background: #f0f2f4; + border-bottom: 1px solid #d1d5d8; } header img { - padding:10px 0 10px 10px; + padding: 10px 0 10px 10px; } img.small { - height:30px; + height: 30px; } img.responsive { max-width: 100%; - height: auto; + height: auto; } h1 { - display:none; + display: none; font-size: 2em; } -h2,h3 { - margin:0 0 10px; - padding:0; - font-weight:700; +h2, h3 { + margin: 0 0 10px; + padding: 0; + font-weight: 700; } h2 { - font-size:24px; - font-weight:400; + font-size: 24px; + font-weight: 400; } h3 { - font-size:16px; + font-size: 16px; } h4 { - font-size:13px; + font-size: 13px; } img { - border:0; - vertical-align:middle; + border: 0; + vertical-align: middle; text-decoration: none; } td a { - text-decoration:none; + text-decoration: none; } .bradius { @@ -100,390 +128,395 @@ td a { } .topheader { - background:#f0f2f4; - background:rgba(240,242,244,0.85098); - top:0; - width:100%; - padding:2px 0 0 5px; - position:fixed; - z-index:100; + background: #f0f2f4; + background: rgba(240, 242, 244, 0.85098); + top: 0; + width: 100%; + padding: 2px 0 0 5px; + position: fixed; + z-index: 100; border-bottom: 1px solid #ddd; } .topheader_navigation { - float:right; - margin:17px 50px 0 0; + float: right; + margin: 17px 50px 0 0; } /* TOPHEADER NAV */ ul.topheadernav { - list-style-type:none; - font-size:12px; + list-style-type: none; + font-size: 12px; } ul.topheadernav li { - padding:0; - margin-left:50px; - float:left; - position:relative; + padding: 0; + margin-left: 50px; + float: left; + position: relative; } ul.topheadernav li a { - display:block; - text-decoration:none; - color:#0f3e4e; + display: block; + text-decoration: none; + color: #0f3e4e; } ul.topheadernav li a:hover { - color:#111; + color: #111; } ul.topheadernav li ul { - display:none; - background-color:#eee; - padding:5px; - box-shadow:0 1px 3px rgba(0,0,0,0.35); - margin-left:0; - border-radius:3px; + display: none; + background-color: #eee; + padding: 5px; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.35); + margin-left: 0; + border-radius: 3px; } ul.topheadernav li:hover ul { - display:block; - position:absolute; + display: block; + position: absolute; } ul.topheadernav li ul li { - font-size:11px; - margin-left:0; + font-size: 11px; + margin-left: 0; } ul.topheadernav li ul li a:hover { - color:#111; + color: #111; } .topheadernav img { - padding:0; - margin:-4px 0 0; + padding: 0; + margin: -4px 0 0; } + .topheadernav a.logoutlink { color: #cc0000; } .countbubble { - display:block; - font-size:9px; - color:#fff; - background-color:#d90000; - position:absolute; - padding:3px; - line-height:9px; - border-radius:3px; - right:-6px; - bottom:-4px; + display: block; + font-size: 9px; + color: #fff; + background-color: #d90000; + position: absolute; + padding: 3px; + line-height: 9px; + border-radius: 3px; + right: -6px; + bottom: -4px; } /* FOOTER */ footer { - clear:both; - text-align:center; - color:#888; - font-size:10px!important; - padding:10px 0; - bottom:0; + clear: both; + text-align: center; + color: #888; + font-size: 10px !important; + padding: 10px 0; + bottom: 0; } -footer a,footer a:active,footer a:visited { - color:#888; +footer a, footer a:active, footer a:visited { + color: #888; } footer img { - margin:0 2px 3px 0; - height:13px; + margin: 0 2px 3px 0; + height: 13px; } .login, .errorbox { - background-color:#fff; - margin:9%; - margin-left:auto; - margin-right:auto; - margin-bottom:12px; - width:500px; - box-shadow:rgba(0,0,0,0.34902) 0 1px 3px 0; + background-color: #fff; + margin: 9%; + margin-left: auto; + margin-right: auto; + margin-bottom: 12px; + width: 500px; + box-shadow: rgba(0, 0, 0, 0.34902) 0 1px 3px 0; } -.login div.warningcontainer, .login div.errorcontainer, .login div.successcontainer { - margin:10px!important; +.login div.warningcontainer, .login div.errorcontainer, .login div.successcontainer + { + margin: 10px !important; } .errorbox { - width:800px; + width: 800px; } .installsec { - margin-top:10px; - padding:0; - text-align:left; + margin-top: 10px; + padding: 0; + text-align: left; } .loginsec, .errorsec { - margin-top:10px; - padding:10px 0; - text-align:center; + margin-top: 10px; + padding: 10px 0; + text-align: center; } .errorsec { - padding:10px; + padding: 10px; } .loginsec form { - width:400px; - margin:0 auto; - text-align:left; + width: 400px; + margin: 0 auto; + text-align: left; } .loginsec fieldset { - border:0; - float:left; - clear:left; - width:100%; - margin:0 0 10px; - padding:0; + border: 0; + float: left; + clear: left; + width: 100%; + margin: 0 0 10px; + padding: 0; } .loginsec legend { - display:none; + display: none; } .loginsec label { - float:left; - width:10em; - margin-right:1em; - margin-top:6px; - text-align:right; + float: left; + width: 10em; + margin-right: 1em; + margin-top: 6px; + text-align: right; } .loginsec input[type="text"], .loginsec input[type="password"] { - width:183px; + width: 183px; } .loginsec select { - width:213px; + width: 213px; } p.submit { - text-align:right; - padding-right:46px; + text-align: right; + padding-right: 46px; } -.loginsec aside,.errorsec aside { - border-top:1px solid #d1d5d8; - clear:both; - float:none; - width:auto; - text-align:left; - padding:10px 10px 0; +.loginsec aside, .errorsec aside { + border-top: 1px solid #d1d5d8; + clear: both; + float: none; + width: auto; + text-align: left; + padding: 10px 10px 0; } aside.right { - text-align:right; + text-align: right; } .messagewrapper { - width:650px; - margin:0 auto; - padding:120px 0 0; - overflow:hidden; + width: 650px; + margin: 0 auto; + padding: 120px 0 0; + overflow: hidden; } .messagewrapperfull { - width:100%; - margin:0 auto; - padding:0; - overflow:hidden; + width: 100%; + margin: 0 auto; + padding: 0; + overflow: hidden; } .overviewsearch { - float:right; - font-size:80%; - text-align:right; + float: right; + font-size: 80%; + text-align: right; } .overviewsearch input[type="text"] { - width:150px; + width: 150px; } -.overviewsearch + table { - clear:right; +.overviewsearch+table { + clear: right; } .overviewadd { - padding:10px; - font-weight:700; + padding: 10px; + font-weight: 700; } /* * error message display */ .errorcontainer { - background:url(../img/icons/error_big.png) 15px 15px no-repeat rgb(242, 222, 222); - border:1px solid #ffc2ca; + background: url(../img/icons/error_big.png) 15px 15px no-repeat + rgb(242, 222, 222); + border: 1px solid #ffc2ca; padding: 15px 15px 15px 60px; - margin:10px 0; - overflow:hidden; - box-shadow:0 0 0 #000; + margin: 10px 0; + overflow: hidden; + box-shadow: 0 0 0 #000; border-radius: 4px; - color:rgb(169, 68, 66); + color: rgb(169, 68, 66); } .errortitle { - font-weight:700; + font-weight: 700; } .error { - font-weight:400!important; + font-weight: 400 !important; } /* * warning message display */ -.warningcontainer,.ui-dialog { - background:url(../img/icons/warning_big.png) 10px center no-repeat #fffecc; - border:1px solid #f3c37e; - padding:10px 10px 10px 68px!important; - margin:10px 0!important; - text-align:left!important; - overflow:hidden; - box-shadow:0 0 0 #000; +.warningcontainer, .ui-dialog { + background: url(../img/icons/warning_big.png) 10px center no-repeat + #fffecc; + border: 1px solid #f3c37e; + padding: 10px 10px 10px 68px !important; + margin: 10px 0 !important; + text-align: left !important; + overflow: hidden; + box-shadow: 0 0 0 #000; } .ui-dialog { - padding:10px!important; + padding: 10px !important; } -.warningtitle,.ui-dialog-titlebar { - font-weight:700; - color:#D57D00; +.warningtitle, .ui-dialog-titlebar { + font-weight: 700; + color: #D57D00; } -.warning,.ui-dialog-content { - color:#D57D00!important; +.warning, .ui-dialog-content { + color: #D57D00 !important; } /* * success message display */ .successcontainer { - background:url(../img/icons/ok_big.png) 10px center no-repeat #E2F9E3; - border:1px solid #9C9; - padding:10px 10px 10px 68px!important; - margin:10px 0!important; - text-align:left!important; - overflow:hidden; - box-shadow:0 0 0 #000; + background: url(../img/icons/ok_big.png) 10px center no-repeat #E2F9E3; + border: 1px solid #9C9; + padding: 10px 10px 10px 68px !important; + margin: 10px 0 !important; + text-align: left !important; + overflow: hidden; + box-shadow: 0 0 0 #000; } .successtitle { - font-weight:700; - color:#060!important; + font-weight: 700; + color: #060 !important; } .success { - font-weight:400!important; + font-weight: 400 !important; } /* * neutral/info message display */ .neutralcontainer { - background:url(../img/icons/info_big.png) 10px center no-repeat #d2eaf6; - border:1px solid #b7d8ed; - padding:10px 10px 10px 68px!important; - margin:10px 0!important; - text-align:left!important; - overflow:hidden; - box-shadow:0 0 0 #000; + background: url(../img/icons/info_big.png) 10px center no-repeat #d2eaf6; + border: 1px solid #b7d8ed; + padding: 10px 10px 10px 68px !important; + margin: 10px 0 !important; + text-align: left !important; + overflow: hidden; + box-shadow: 0 0 0 #000; } .neutraltitle { - font-weight:700; - color:#3188c1!important; + font-weight: 700; + color: #3188c1 !important; } .neutral { - font-weight:400!important; - color:#3188c1!important; + font-weight: 400 !important; + color: #3188c1 !important; } /* std hyperlink */ -a,a:active,a:visited { - color:#176fa1; - text-decoration:none; +a, a:active, a:visited { + color: #176fa1; + text-decoration: none; } a:hover { - text-decoration:underline; + text-decoration: underline; } a.active { - font-weight:700; + font-weight: 700; } /* navigation */ nav { - float:left; - width:230px; - background-color:#f7f8fa; - min-height:500px; + float: left; + width: 230px; + background-color: #f7f8fa; + min-height: 500px; padding-top: 10px; } nav div:first-child { - display:none; + display: none; } nav div:nth-child(2) { - border-top:0!important; + border-top: 0 !important; } .menuelement { - margin:0 15px; - padding:15px 0 15px 5px; - border-bottom:1px solid #e1e7f0; - border-top:1px solid #fff; + margin: 0 15px; + padding: 15px 0 15px 5px; + border-bottom: 1px solid #e1e7f0; + border-top: 1px solid #fff; } nav div:last-child { - border-bottom:0!important; + border-bottom: 0 !important; } .menuelement h4 { - background:transparent url(../img/icons/tag_blue.png) no-repeat center left; - font-weight:700; - margin:0; - padding:0 0 0 20px; - color:#626976; + background: transparent url(../img/icons/tag_blue.png) no-repeat center + left; + font-weight: 700; + margin: 0; + padding: 0 0 0 20px; + color: #626976; } .menuelement h4 a { - color:#626976; + color: #626976; } .menuelement ul { - list-style:none; - margin:3px 0 0; - padding:0; + list-style: none; + margin: 3px 0 0; + padding: 0; } .menuelement ul li { - margin:2px 0 2px 20px; - padding:0; + margin: 2px 0 2px 20px; + padding: 0; } .noborder { - width:100%; - border-spacing:0; - border-collapse:separate; - border:0; + width: 100%; + border-spacing: 0; + border-collapse: separate; + border: 0; } /* TABLES */ @@ -502,13 +535,14 @@ table thead th, table th { font-weight: 700; } -table tr.section:not(:first-child) th { +table tr.section:not (:first-child ) th { border-top: 1px solid #d1d5d8; } table tbody tr td { - border-bottom:1px solid #f1f2f3; + border-bottom: 1px solid #f1f2f3; } + table tbody tr:last-child td { border-bottom: none; } @@ -518,16 +552,16 @@ table.hl tbody tr:hover { } table tfoot tr td { - height:25px; - border-top:1px solid #d1d5d8; - background-color:#f2f8fa; + height: 25px; + border-top: 1px solid #d1d5d8; + background-color: #f2f8fa; padding-right: 0px; } table td { padding: 5px 10px; height: 25px; - min-height: 25px; + min-height: 25px; } table th.right, table td.right { @@ -537,45 +571,50 @@ table th.right, table td.right { table.tiny { width: 400px; } + table.middle { width: 600px; } + table.full { width: 100%; } + table.center { margin: 0 auto; } + table tr.top { vertical-align: top; } + tr.disabled td, tr.disabled td a { - color:#cfcfcf; + color: #cfcfcf; } /* ADMIN/CUSTOMER BARS */ .overviewcustomerextras { - line-height:15px; - font-size:10px; - width:250px; - padding-top:3px; - padding-bottom:3px; + line-height: 15px; + font-size: 10px; + width: 250px; + padding-top: 3px; + padding-bottom: 3px; } .overviewcustomerextras span { - width:60px; - float:left; + width: 60px; + float: left; } /* INPUT ELEMENTS */ input { - background:#fff url(../img/icons/text_align_left.png) no-repeat 5px 4px; + background: #fff url(../img/icons/text_align_left.png) no-repeat 5px 4px; color: #333; - padding:1px 4px 2px 24px; - height:23px; - border:1px solid #d9d9d9; - margin-bottom:5px; - border-radius:3px; + padding: 1px 4px 2px 24px; + height: 23px; + border: 1px solid #d9d9d9; + margin-bottom: 5px; + border-radius: 3px; } input[disabled], input[readonly] { @@ -584,274 +623,277 @@ input[disabled], input[readonly] { } textarea { - background:#fff url(../img/icons/text_align_left.png) no-repeat 5px 4px; + background: #fff url(../img/icons/text_align_left.png) no-repeat 5px 4px; color: #333; - padding:4px 4px 2px 24px; - border:1px solid #d9d9d9; + padding: 4px 4px 2px 24px; + border: 1px solid #d9d9d9; margin: 5px 0 5px 0; - border-radius:3px; + border-radius: 3px; } -input[type="text"],input[type="password"],input[type="text"] { - width:400px; +input[type="text"], input[type="password"], input[type="text"] { + width: 400px; margin-top: 5px; } input[type="password"] { - background:#fff url(../img/icons/lock.png) no-repeat 5px 4px; + background: #fff url(../img/icons/lock.png) no-repeat 5px 4px; } input[class="small"] { - width:auto; + width: auto; margin-top: 5px; } - /* * BUTTONS */ -input[type="button"],input[type="submit"],input[type="reset"],input[type="file"] { - margin:0 5px; - padding:5px 14px; - outline:0; - border:0; - background-color:#eee; - min-width:80px; - height:28px; - background-image:none; - border-width:0; +input[type="button"], input[type="submit"], input[type="reset"], input[type="file"] + { + margin: 0 5px; + padding: 5px 14px; + outline: 0; + border: 0; + background-color: #eee; + min-width: 80px; + height: 28px; + background-image: none; + border-width: 0; } -.loginsec input[type="button"],.loginsec input[type="submit"],.loginsec input[type="reset"] { - margin:0 1px; +.loginsec input[type="button"], .loginsec input[type="submit"], + .loginsec input[type="reset"] { + margin: 0 1px; } -input[type="button"]:hover,input[type="submit"]:hover,input[type="reset"]:hover { - color:#333; - background-color:#dcdcdc; +input[type="button"]:hover, input[type="submit"]:hover, input[type="reset"]:hover + { + color: #333; + background-color: #dcdcdc; } -input[type="button"]:active,input[type="submit"]:active,input[type="reset"]:active { - -webkit-box-shadow:inset 0 1px 8px rgba(0,0,0,0.25); - -moz-box-shadow:inset 0 1px 8px rgba(0,0,0,0.25); - box-shadow:inset 0 1px 8px rgba(0,0,0,0.25); - color:#fff!important; +input[type="button"]:active, input[type="submit"]:active, input[type="reset"]:active + { + -webkit-box-shadow: inset 0 1px 8px rgba(0, 0, 0, 0.25); + -moz-box-shadow: inset 0 1px 8px rgba(0, 0, 0, 0.25); + box-shadow: inset 0 1px 8px rgba(0, 0, 0, 0.25); + color: #fff !important; } -input[type="submit"],input[class="yesbutton"] { - color:#fff; - background-color:#35aa47; +input[type="submit"], input[class="yesbutton"] { + color: #fff; + background-color: #35aa47; } -input[type="submit"]:hover,input[class="yesbutton"]:hover { - color:#fff; - background-color:#1d943b; +input[type="submit"]:hover, input[class="yesbutton"]:hover { + color: #fff; + background-color: #1d943b; } -input[class="submit"]:active,input[class="yesbutton"]:active { - background-color:#35aa47; +input[class="submit"]:active, input[class="yesbutton"]:active { + background-color: #35aa47; } -input[class="nobutton"],input[type="reset"] { - color:#fff; - background-color:#d84a38; +input[class="nobutton"], input[type="reset"] { + color: #fff; + background-color: #d84a38; } input[type="file"] { - background-color:#FFFFFF; - padding-left:0px; + background-color: #FFFFFF; + padding-left: 0px; } -input[class="nobutton"]:hover,input[type="reset"]:hover { - color:#fff; - background-color:#c53727; +input[class="nobutton"]:hover, input[type="reset"]:hover { + color: #fff; + background-color: #c53727; } -input[class="nobutton"]:active,input[type="reset"]:active { - background-color:#dd4b39; +input[class="nobutton"]:active, input[type="reset"]:active { + background-color: #dd4b39; } input[type="checkbox"] { - background:#dae7ee; - padding:0; - margin:0 5px 0 0; - vertical-align:middle; + background: #dae7ee; + padding: 0; + margin: 0 5px 0 0; + vertical-align: middle; /* Fix Safari-Bug */ height: auto; } select { - padding:6px 4px 7px 24px; + padding: 6px 4px 7px 24px; color: #333; - border:1px solid #d9d9d9; - margin:5px 5px 5px 0; - border-radius:3px; + border: 1px solid #d9d9d9; + margin: 5px 5px 5px 0; + border-radius: 3px; background: url(../img/icons/down.png) no-repeat 9px; - -webkit-appearance:none; - -moz-appearance:none; - appearance:none; - min-width:170px; - text-indent:0.01px; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + min-width: 170px; + text-indent: 0.01px; text-overflow: ''; } select[multiple="multiple"] { - height:auto; - background-image:none; - padding:4px; + height: auto; + background-image: none; + padding: 4px; } .customer_add { - margin-top:15px; + margin-top: 15px; } .dboarditem { margin-bottom: 20px; - border:1px solid #d1d5d8; + border: 1px solid #d1d5d8; border-radius: 3px; width: 100%; } .dboarditemfull { - position:relative; - overflow:hidden; - width:100%; - margin-top:10px; - margin-bottom:10px; - padding:0; - border:1px solid #d1d5d8; + position: relative; + overflow: hidden; + width: 100%; + margin-top: 10px; + margin-bottom: 10px; + padding: 0; + border: 1px solid #d1d5d8; } -.dboarditem table,.dboarditemfull table { - width:100%; - border:0; +.dboarditem table, .dboarditemfull table { + width: 100%; + border: 0; } -.dboarditem th,.dboarditemfull th { - border-bottom:1px solid #d1d5d8; - height:25px!important; - padding:5px 0 5px 8px; - font-weight:700; +.dboarditem th, .dboarditemfull th { + border-bottom: 1px solid #d1d5d8; + height: 25px !important; + padding: 5px 0 5px 8px; + font-weight: 700; } -.dboarditem td,.dboarditemfull td { - border-right:0; - border-bottom:1px solid #f1f2f3; - padding:4px 0 4px 8px; +.dboarditem td, .dboarditemfull td { + border-right: 0; + border-bottom: 1px solid #f1f2f3; + padding: 4px 0 4px 8px; } .cronjobtask li { - background-image:url(../img/icons/clock.png); - background-repeat:no-repeat; - background-position:0 1px; - padding-left:18px; + background-image: url(../img/icons/clock.png); + background-repeat: no-repeat; + background-position: 0 1px; + padding-left: 18px; } .overviewheading { - vertical-align:top; - line-height:36px; - height:36px; + vertical-align: top; + line-height: 36px; + height: 36px; } .overviewheading h3 { - display:inline; + display: inline; } /* PROGRESS BAR */ .progress { - height:15px; - width:150px; - margin:2px 0 2px 10px; - overflow:hidden; - background-color:#f7f7f7; - -webkit-border-radius:3px; - -moz-border-radius:3px; - border-radius:3px; - -webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1); - -moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1); - box-shadow:inset 0 1px 2px rgba(0,0,0,0.1); - text-align:center; - color:#999; + height: 15px; + width: 150px; + margin: 2px 0 2px 10px; + overflow: hidden; + background-color: #f7f7f7; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); + -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); + text-align: center; + color: #999; } .progress .bar { - width:1px; - height:18px; - font-size:12px; - color:#fff; - text-align:center; - text-shadow:0 -1px 0 rgba(0,0,0,0.25); - background-color:#0e90d2; - -webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15); - -moz-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15); - box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15); - -webkit-transition:width 6s ease; - -moz-transition:width 6s ease; - -ms-transition:width 6s ease; - -o-transition:width 6s ease; - transition:width 6s ease; + width: 1px; + height: 18px; + font-size: 12px; + color: #fff; + text-align: center; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #0e90d2; + -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); + -moz-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); + -webkit-transition: width 6s ease; + -moz-transition: width 6s ease; + -ms-transition: width 6s ease; + -o-transition: width 6s ease; + transition: width 6s ease; } .progress-danger .bar { - background-color:#dd514c; + background-color: #dd514c; } .progress-warn .bar { - background-color:#e6b64e; + background-color: #e6b64e; } .maintable { - width:90%; + width: 90%; } .update_progess { - padding:2em; - text-align:left; + padding: 2em; + text-align: left; } .preconfig { - text-align:left; - margin-top:20px; - margin-bottom:5px; - margin-right:15px; - margin-left:15px; + text-align: left; + margin-top: 20px; + margin-bottom: 5px; + margin-right: 15px; + margin-left: 15px; } .preconfigitem { - padding:.15em; - border-bottom:1px solid #ccc; + padding: .15em; + border-bottom: 1px solid #ccc; } .preconfdesc { - display:block; - margin-bottom:.5em; - font-size:120%; + display: block; + margin-bottom: .5em; + font-size: 120%; } .strikethrough { - text-decoration:line-through; + text-decoration: line-through; } label.nobr { - display:inline; - padding:0; - margin:0 10px 0 0; + display: inline; + padding: 0; + margin: 0 10px 0 0; } .scrollup { - width:40px; - height:40px; - opacity:.3; - position:fixed; - bottom:50px; - left:95px; - display:none; - text-indent:-9999px; - background:url(../img/top.png) no-repeat; + width: 40px; + height: 40px; + opacity: .3; + position: fixed; + bottom: 50px; + left: 95px; + display: none; + text-indent: -9999px; + background: url(../img/top.png) no-repeat; } .nowrap { - white-space:nowrap; + white-space: nowrap; } .trafficchart { @@ -862,26 +904,26 @@ label.nobr { /* CANVAS STUFF */ .canvasitems { - position:relative; - overflow:hidden; - width:100%; - margin-top:0; - margin-bottom:10px; - padding:0 0 0 10px; + position: relative; + overflow: hidden; + width: 100%; + margin-top: 0; + margin-bottom: 10px; + padding: 0 0 0 10px; } .canvasbox { - width:130px; - margin:10px 20px 10px 0; - text-align:center; - float:left; - height:150px; - line-height:normal; + width: 130px; + margin: 10px 20px 10px 0; + text-align: center; + float: left; + height: 150px; + line-height: normal; } .canvasbox canvas { - width:120px; - margin-bottom:5px; + width: 120px; + margin-bottom: 5px; } /* NEWSFEED @@ -921,15 +963,15 @@ label.nobr { color:gray; }*/ .newsfeed { - margin: 0; - padding: 0; - list-style: none; + margin: 0; + padding: 0; + list-style: none; } .newsfeed li { - margin-bottom: 10px; - padding-bottom: 5px; - border-bottom: 1px dotted #999; + margin-bottom: 10px; + padding-bottom: 5px; + border-bottom: 1px dotted #999; } .newsfeed li:last-child { @@ -939,118 +981,121 @@ label.nobr { } .newsfeed li.left .newsfeed-body { - margin-left: 60px; + margin-left: 60px; } .newsfeed li.right .newsfeed-body { - margin-right: 60px; + margin-right: 60px; } .newsfeed li .newsfeed-body p { - margin: 0; + margin: 0; } -.panel .slidedown .glyphicon, -.newsfeed .glyphicon { - margin-right: 5px; +.panel .slidedown .glyphicon, .newsfeed .glyphicon { + margin-right: 5px; } .newsfeed-panel .panel-body { - height: 350px; - overflow-y: scroll; + height: 350px; + overflow-y: scroll; } /* TIPPER */ .tipper-positioner { - left:-99999px; - position:absolute; + left: -99999px; + position: absolute; pointer-events: none; - top:-99999px; + top: -99999px; } .tipper-positioner .tipper-wrapper { - position:relative; + position: relative; } .tipper-positioner .tipper-content { - background:rgba(0,0,0,0.85); - border-radius:3px; - color:#fff; - display:block; - font-family:sans-serif; - font-size:11px; - margin:0; - padding:4px 8px; - white-space:nowrap; + background: rgba(0, 0, 0, 0.85); + border-radius: 3px; + color: #fff; + display: block; + font-family: sans-serif; + font-size: 11px; + margin: 0; + padding: 4px 8px; + white-space: nowrap; } .tipper-positioner .tipper-caret { - background:url(../img/tipper.png) no-repeat; - display:block; - height:11px; - margin:0; - overflow:hidden; - position:absolute; - width:5px; + background: url(../img/tipper.png) no-repeat; + display: block; + height: 11px; + margin: 0; + overflow: hidden; + position: absolute; + width: 5px; } .tipper-positioner.right { - box-shadow:1px 0 3px rgba(0,0,0,0.25); + box-shadow: 1px 0 3px rgba(0, 0, 0, 0.25); } .tipper-positioner.right .tipper-caret { - background-position:left center; - left:-5px; - top:0; + background-position: left center; + left: -5px; + top: 0; } .tipper-positioner.left { - box-shadow:-1px 0 3px rgba(0,0,0,0.25); + box-shadow: -1px 0 3px rgba(0, 0, 0, 0.25); } .tipper-positioner.left .tipper-caret { - background-position:right center; - right:-5px; - top:0; + background-position: right center; + right: -5px; + top: 0; } -.tipper-positioner.top .tipper-caret,.tipper-positioner.bottom .tipper-caret { - display:block; - float:none; - height:5px; - margin:0 auto; - width:11px; +.tipper-positioner.top .tipper-caret, .tipper-positioner.bottom .tipper-caret + { + display: block; + float: none; + height: 5px; + margin: 0 auto; + width: 11px; } .tipper-positioner.top { - box-shadow:0 -1px 3px rgba(0,0,0,0.25); + box-shadow: 0 -1px 3px rgba(0, 0, 0, 0.25); } .tipper-positioner.top .tipper-caret { - background-position:center bottom; - bottom:-5px; - left:0; + background-position: center bottom; + bottom: -5px; + left: 0; } .tipper-positioner.bottom { - box-shadow:0 1px 3px rgba(0,0,0,0.25); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25); } .tipper-positioner.bottom .tipper-caret { - background-position:center top; - top:-5px; - left:0; + background-position: center top; + top: -5px; + left: 0; } .red { color: #ff0000; } + .green { color: green; } + .orange { color: orange; } + .blue { color: blue; } @@ -1058,19 +1103,24 @@ label.nobr { .phpinfo, .overflow { overflow: scroll; } + .phperror { margin-top: 50px; margin-bottom: -55px; } + .clear { clear: both; } + .hidden { display: none; } + div.left { float: left; } + div.right { float: right; } @@ -1090,16 +1140,18 @@ div.right { .grid-u { display: inline-block; - zoom: 1; *display: inline; + zoom: 1; + *display: inline; letter-spacing: normal; word-spacing: normal; vertical-align: top; text-rendering: auto; } -.grid-u-1,.grid-u-1-2 { +.grid-u-1, .grid-u-1-2 { display: inline-block; - zoom: 1; *display: inline; + zoom: 1; + *display: inline; letter-spacing: normal; word-spacing: normal; vertical-align: top; @@ -1107,7 +1159,7 @@ div.right { } .grid-u-1 { - display:block; + display: block; } .grid-u-1-2 { @@ -1122,347 +1174,466 @@ div.right { .tablesorter-header-inner { margin-left: 15px; } + table thead th.tablesorter-headerUnSorted { - background-image: url(data:image/gif;base64,R0lGODlhFQAJAIAAACMtMP///yH5BAEAAAEALAAAAAAVAAkAAAIXjI+AywnaYnhUMoqt3gZXPmVg94yJVQAAOw==); + background-image: + url(data:image/gif;base64,R0lGODlhFQAJAIAAACMtMP///yH5BAEAAAEALAAAAAAVAAkAAAIXjI+AywnaYnhUMoqt3gZXPmVg94yJVQAAOw==); background-repeat: no-repeat; background-position: left center; } + table thead th.tablesorter-headerAsc { - background-image: url(data:image/gif;base64,R0lGODlhFQAEAIAAACMtMP///yH5BAEAAAEALAAAAAAVAAQAAAINjI8Bya2wnINUMopZAQA7); + background-image: + url(data:image/gif;base64,R0lGODlhFQAEAIAAACMtMP///yH5BAEAAAEALAAAAAAVAAQAAAINjI8Bya2wnINUMopZAQA7); background-repeat: no-repeat; background-position: left center; } + table thead th.tablesorter-headerDesc { - background-image: url(data:image/gif;base64,R0lGODlhFQAEAIAAACMtMP///yH5BAEAAAEALAAAAAAVAAQAAAINjB+gC+jP2ptn0WskLQA7); + background-image: + url(data:image/gif;base64,R0lGODlhFQAEAIAAACMtMP///yH5BAEAAAEALAAAAAAVAAQAAAINjB+gC+jP2ptn0WskLQA7); background-repeat: no-repeat; background-position: left center; } /* PROGRESS */ .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="0"] { - width: 0%; + width: 0%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="1"] { - width: 1%; + width: 1%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="2"] { - width: 2%; + width: 2%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="3"] { - width: 3%; + width: 3%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="4"] { - width: 4%; + width: 4%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="5"] { - width: 5%; + width: 5%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="6"] { - width: 6%; + width: 6%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="7"] { - width: 7%; + width: 7%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="8"] { - width: 8%; + width: 8%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="9"] { - width: 9%; + width: 9%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="10"] { - width: 10%; + width: 10%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="11"] { - width: 11%; + width: 11%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="12"] { - width: 12%; + width: 12%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="13"] { - width: 13%; + width: 13%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="14"] { - width: 14%; + width: 14%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="15"] { - width: 15%; + width: 15%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="16"] { - width: 16%; + width: 16%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="17"] { - width: 17%; + width: 17%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="18"] { - width: 18%; + width: 18%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="19"] { - width: 19%; + width: 19%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="20"] { - width: 20%; + width: 20%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="21"] { - width: 21%; + width: 21%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="22"] { - width: 22%; + width: 22%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="23"] { - width: 23%; + width: 23%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="24"] { - width: 24%; + width: 24%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="25"] { - width: 25%; + width: 25%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="26"] { - width: 26%; + width: 26%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="27"] { - width: 27%; + width: 27%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="28"] { - width: 28%; + width: 28%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="29"] { - width: 29%; + width: 29%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="30"] { - width: 30%; + width: 30%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="31"] { - width: 31%; + width: 31%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="32"] { - width: 32%; + width: 32%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="33"] { - width: 33%; + width: 33%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="34"] { - width: 34%; + width: 34%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="35"] { - width: 35%; + width: 35%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="36"] { - width: 36%; + width: 36%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="37"] { - width: 37%; + width: 37%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="38"] { - width: 38%; + width: 38%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="39"] { - width: 39%; + width: 39%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="40"] { - width: 40%; + width: 40%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="41"] { - width: 41%; + width: 41%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="42"] { - width: 42%; + width: 42%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="43"] { - width: 43%; + width: 43%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="44"] { - width: 44%; + width: 44%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="45"] { - width: 45%; + width: 45%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="46"] { - width: 46%; + width: 46%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="47"] { - width: 47%; + width: 47%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="48"] { - width: 48%; + width: 48%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="49"] { - width: 49%; + width: 49%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="50"] { - width: 50%; + width: 50%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="51"] { - width: 51%; + width: 51%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="52"] { - width: 52%; + width: 52%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="53"] { - width: 53%; + width: 53%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="54"] { - width: 54%; + width: 54%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="55"] { - width: 55%; + width: 55%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="56"] { - width: 56%; + width: 56%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="57"] { - width: 57%; + width: 57%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="58"] { - width: 58%; + width: 58%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="59"] { - width: 59%; + width: 59%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="60"] { - width: 60%; + width: 60%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="61"] { - width: 61%; + width: 61%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="62"] { - width: 62%; + width: 62%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="63"] { - width: 63%; + width: 63%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="64"] { - width: 64%; + width: 64%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="65"] { - width: 65%; + width: 65%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="66"] { - width: 66%; + width: 66%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="67"] { - width: 67%; + width: 67%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="68"] { - width: 68%; + width: 68%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="69"] { - width: 69%; + width: 69%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="70"] { - width: 70%; + width: 70%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="71"] { - width: 71%; + width: 71%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="72"] { - width: 72%; + width: 72%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="73"] { - width: 73%; + width: 73%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="74"] { - width: 74%; + width: 74%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="75"] { - width: 75%; + width: 75%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="76"] { - width: 76%; + width: 76%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="77"] { - width: 77%; + width: 77%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="78"] { - width: 78%; + width: 78%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="79"] { - width: 79%; + width: 79%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="80"] { - width: 80%; + width: 80%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="81"] { - width: 81%; + width: 81%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="82"] { - width: 82%; + width: 82%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="83"] { - width: 83%; + width: 83%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="84"] { - width: 84%; + width: 84%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="85"] { - width: 85%; + width: 85%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="86"] { - width: 86%; + width: 86%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="87"] { - width: 87%; + width: 87%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="88"] { - width: 88%; + width: 88%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="89"] { - width: 89%; + width: 89%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="90"] { - width: 90%; + width: 90%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="91"] { - width: 91%; + width: 91%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="92"] { - width: 92%; + width: 92%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="93"] { - width: 93%; + width: 93%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="94"] { - width: 94%; + width: 94%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="95"] { - width: 95%; + width: 95%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="96"] { - width: 96%; + width: 96%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="97"] { - width: 97%; + width: 97%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="98"] { - width: 98%; + width: 98%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="99"] { - width: 99%; + width: 99%; } + .bar[aria-valuemax="100"][aria-valuemin="0"][aria-valuenow="100"] { - width: 100%; + width: 100%; } .update-step { - margin-left: 5em; - font-weight: bold; + margin-left: 5em; + font-weight: bold; } -.update-step-ok { color: #1dcd00; } -.update-step-warn { color: #db7100; } -.update-step-err { color: #ff0000; } -.update-step-unknown { color: #000000; } +.update-step-ok { + color: #1dcd00; +} -.align-top { vertical-align:top; } +.update-step-warn { + color: #db7100; +} + +.update-step-err { + color: #ff0000; +} + +.update-step-unknown { + color: #000000; +} + +.align-top { + vertical-align: top; +} .code-block { - width: 500px; - border: 1px solid #ccc; - padding: 4px; + width: 500px; + border: 1px solid #ccc; + padding: 4px; } .notes_block { - display: none; + display: none; } .nolbr { @@ -1477,9 +1648,10 @@ table thead th.tablesorter-headerDesc { } .shell, .filecontent { - font-family: Consolas,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New, monospace; + font-family: Consolas, Monaco, Lucida Console, Liberation Mono, + DejaVu Sans Mono, Bitstream Vera Sans Mono, Courier New, monospace; border: 1px solid #d1d5d8; - border-radius: 3px; + border-radius: 3px; padding: 10px; background-image: none; -webkit-box-sizing: border-box; @@ -1494,7 +1666,7 @@ table thead th.tablesorter-headerDesc { } fieldset.file { - border:1px solid #d1d5d8; + border: 1px solid #d1d5d8; border-radius: 3px; padding: 5px; margin-bottom: 10px; @@ -1535,3 +1707,27 @@ td.size-20 { td.size-50 { width: 50%; } + +.well { + min-height: 20px; + padding: 19px; + margin-bottom: 20px; + background-color: #f5f5f5; + border: 1px solid #e3e3e3; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05); +} + +.label { + border-radius: 0; + text-shadow: none; + font-size: 14px; + font-weight: normal; + padding: 3px 5px 3px; + background-color: #abbac3 !important +} + +.label[class*="span"][class*="arrow"] { + min-height: 0 +} From 81d6a856d9e3d38ab0b2474aad855a37a04847f4 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Fri, 23 Feb 2018 19:51:06 +0100 Subject: [PATCH 0475/1335] forgot to hit save :P Signed-off-by: Michael Kaufmann (d00p) --- templates/Sparkle/assets/css/main.css | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/templates/Sparkle/assets/css/main.css b/templates/Sparkle/assets/css/main.css index 83a93250..5e61b727 100644 --- a/templates/Sparkle/assets/css/main.css +++ b/templates/Sparkle/assets/css/main.css @@ -1723,9 +1723,10 @@ td.size-50 { border-radius: 0; text-shadow: none; font-size: 14px; - font-weight: normal; + font-weight: bold; padding: 3px 5px 3px; - background-color: #abbac3 !important + background-color: #abbac3 !important; + color: #fff !important; } .label[class*="span"][class*="arrow"] { From 9a61a56732466368762dbc18d34e3441aead5d04 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sat, 24 Feb 2018 10:57:52 +0100 Subject: [PATCH 0476/1335] enhance phpdoc and add @access to specify which usergroup can use the ApiCommands; add --import-settings parameter to config-services.php CLI script to gain even more automatism when setting up Signed-off-by: Michael Kaufmann (d00p) --- apihelp.php | 27 +++-- install/scripts/config-services.php | 24 ++++ lib/classes/api/commands/class.Admins.php | 30 ++++- lib/classes/api/commands/class.Customers.php | 105 ++++++++++-------- lib/classes/api/commands/class.FpmDaemons.php | 21 ++++ lib/classes/api/commands/class.Froxlor.php | 24 +++- .../api/commands/class.IpsAndPorts.php | 24 +++- lib/classes/api/commands/class.Mysqls.php | 3 + .../api/commands/class.PhpSettings.php | 21 ++++ 9 files changed, 217 insertions(+), 62 deletions(-) diff --git a/apihelp.php b/apihelp.php index deee08d9..bcc3169b 100644 --- a/apihelp.php +++ b/apihelp.php @@ -1,7 +1,7 @@ listFunctions(); } catch (Exception $e) { @@ -48,7 +48,8 @@ foreach ($m_arr as $module) { $output_arr[$module['module']][$module['function']] = array( 'return_type' => (isset($module['return']['type']) && $module['return']['type'] != "" ? $module['return']['type'] : - 1), 'params_list' => array(), - 'head' => $module['head'] + 'head' => $module['head'], + 'access' => isset($module['access']) ? $module['access'] : null ); if (isset($module['params']) && is_array($module['params'])) { @@ -73,12 +74,12 @@ foreach ($output_arr as $module => $functions) { // sort by function ksort($functions); - $apihelp .= "

    ".$module."



    "; + $apihelp .= "

    " . $module . "



    "; // output ALL the functions foreach ($functions as $function => $funcdata) { $apihelp .= "
    "; - $apihelp .= "

    ".$module." - "; + $apihelp .= "

    " . $module . " - "; // description if (strtoupper(substr($funcdata['head'], 0, 5)) == "@TODO") { $apihelp .= ""; @@ -88,8 +89,12 @@ foreach ($output_arr as $module => $functions) { $apihelp .= ""; } $apihelp .= "

    "; - $apihelp .= "Command"." "; - $apihelp .= "".$module.".".$function."
    "; + $apihelp .= "Command" . " "; + $apihelp .= "" . $module . "." . $function . "
    "; + if (isset($funcdata['access']['groups']) && ! empty($funcdata['access']['groups'])) { + $apihelp .= "
    Access: "; + $apihelp .= $funcdata['access']['groups'] . "
    "; + } // output ALL the params; if (count($funcdata['params_list']) > 0) { @@ -102,15 +107,15 @@ foreach ($output_arr as $module => $functions) { $parms .= "
    ";
     				// check whether the parameter is optional
     				if (! empty($param['desc']) && strtolower(substr(trim($param['desc']), 0, 8)) == "optional") {
    -					$parms .= "".$param['name']."";
    +					$parms .= "" . $param['name'] . "";
     					$param['desc'] = substr(trim($param['desc']), 8);
     					if (substr($param['desc'], 0, 1) == ',') {
     						$param['desc'] = substr(trim($param['desc']), 1);
     					}
     				} else {
    -					$parms .= "".$param['name']."";
    +					$parms .= "" . $param['name'] . "";
     				}
    -				$parms .= "
    " . (strtolower($param['type']) == 'unknown' ? "unknown" : $param['type']).""; + $parms .= "" . (strtolower($param['type']) == 'unknown' ? "unknown" : $param['type']) . ""; $parms .= ""; if (! empty($param['desc'])) { $parms .= trim($param['desc']); diff --git a/install/scripts/config-services.php b/install/scripts/config-services.php index 69d1b654..1799ec4e 100755 --- a/install/scripts/config-services.php +++ b/install/scripts/config-services.php @@ -43,6 +43,7 @@ class ConfigServicesCmd extends CmdLineHandler public static $params = array( 'create', 'apply', + 'import-settings', 'daemon', 'list-daemons', 'froxlor-dir', @@ -68,6 +69,10 @@ class ConfigServicesCmd extends CmdLineHandler self::println("--daemon\t\tWhen running --apply you can specify a daemon. This will be the only service that gets configured"); self::println("\t\t\tExample: --apply=/path/to/my-config.json --daemon=apache24"); self::println(""); + self::println(""); + self::println("--import-settings\tImport settings from another froxlor installation. This should be done prior to running --apply or alternatively in the same command together."); + self::println("\t\t\tExample: --import-settings=/path/to/Froxlor_settings-[version]-[dbversion]-[date].json"); + self::println(""); self::println("--froxlor-dir\t\tpath to froxlor installation"); self::println("\t\t\tExample: --froxlor-dir=/var/www/froxlor/"); self::println(""); @@ -115,10 +120,15 @@ class Action require FROXLOR_INSTALL_DIR . '/lib/tables.inc.php'; require FROXLOR_INSTALL_DIR . '/lib/functions.php'; require FROXLOR_INSTALL_DIR . '/lib/classes/settings/class.Settings.php'; + require FROXLOR_INSTALL_DIR . '/lib/classes/settings/class.SImExporter.php'; require FROXLOR_INSTALL_DIR . '/lib/classes/config/class.ConfigParser.php'; require FROXLOR_INSTALL_DIR . '/lib/classes/config/class.ConfigService.php'; require FROXLOR_INSTALL_DIR . '/lib/classes/config/class.ConfigDaemon.php'; + if (array_key_exists("import-settings", $this->_args)) { + $this->_importSettings(); + } + if (array_key_exists("create", $this->_args)) { $this->_createConfig(); } elseif (array_key_exists("apply", $this->_args)) { @@ -128,6 +138,20 @@ class Action } } + private function _importSettings() + { + if (! is_file($this->_args["import-settings"])) { + throw new Exception("Given settings file is not a file"); + } elseif (! file_exists($this->_args["import-settings"])) { + throw new Exception("Given settings file cannot be found ('" . $this->_args["import-settings"] . "')"); + } elseif (! is_readable($this->_args["import-settings"])) { + throw new Exception("Given settings file cannot be read ('" . $this->_args["import-settings"] . "')"); + } + $imp_content = file_get_contents($this->_args["import-settings"]); + SImExporter::import($imp_content); + CmdLineHandler::printsucc("Successfully imported settings from '" . $this->_args["import-settings"] . "'"); + } + private function _createConfig() { $_daemons_config = array( diff --git a/lib/classes/api/commands/class.Admins.php b/lib/classes/api/commands/class.Admins.php index 88ee04e1..a37a894c 100644 --- a/lib/classes/api/commands/class.Admins.php +++ b/lib/classes/api/commands/class.Admins.php @@ -21,6 +21,8 @@ class Admins extends ApiCommand implements ResourceEntity /** * lists all admin entries * + * @access admin + * @throws Exception * @return array count|list */ public function list() @@ -52,7 +54,8 @@ class Admins extends ApiCommand implements ResourceEntity * optional, the admin-id * @param string $loginname * optional, the loginname - * + * + * @access admin * @throws Exception * @return array */ @@ -84,6 +87,13 @@ class Admins extends ApiCommand implements ResourceEntity throw new Exception("Not allowed to execute given command.", 403); } + /** + * create a new admin user + * + * @access admin + * @throws Exception + * @return array + */ public function add() { if ($this->isAdmin() && $this->getUserDetail('change_serversettings') == 1) { @@ -290,6 +300,18 @@ class Admins extends ApiCommand implements ResourceEntity throw new Exception("Not allowed to execute given command.", 403); } + /** + * update an admin user by given id or loginname + * + * @param int $id + * optional, the admin-id + * @param string $loginname + * optional, the loginname + * + * @access admin + * @throws Exception + * @return array + */ public function update() { if ($this->isAdmin() && $this->getUserDetail('change_serversettings') == 1) { @@ -551,7 +573,8 @@ class Admins extends ApiCommand implements ResourceEntity * optional, the admin-id * @param string $loginname * optional, the loginname - * + * + * @access admin * @throws Exception * @return array */ @@ -619,7 +642,8 @@ class Admins extends ApiCommand implements ResourceEntity * optional, the admin-id * @param string $loginname * optional, the loginname - * + * + * @access admin * @throws Exception * @return array */ diff --git a/lib/classes/api/commands/class.Customers.php b/lib/classes/api/commands/class.Customers.php index 26ae4568..424bdf92 100644 --- a/lib/classes/api/commands/class.Customers.php +++ b/lib/classes/api/commands/class.Customers.php @@ -21,6 +21,7 @@ class Customers extends ApiCommand implements ResourceEntity /** * lists all customer entries * + * @access admin * @return array count|list */ public function list() @@ -61,20 +62,21 @@ class Customers extends ApiCommand implements ResourceEntity * @param string $loginname * optional, the loginname * + * @access admin, customer * @throws Exception * @return array */ public function get() { + $id = $this->getParam('id', true, 0); + $ln_optional = ($id <= 0 ? false : true); + $loginname = $this->getParam('loginname', $ln_optional, ''); + + if ($id <= 0 && empty($loginname)) { + throw new Exception("Either 'id' or 'loginname' parameter must be given", 406); + } + if ($this->isAdmin()) { - $id = $this->getParam('id', true, 0); - $ln_optional = ($id <= 0 ? false : true); - $loginname = $this->getParam('loginname', $ln_optional, ''); - - if ($id <= 0 && empty($loginname)) { - throw new Exception("Either 'id' or 'loginname' parameter must be given", 406); - } - $result_stmt = Database::prepare(" SELECT * FROM `" . TABLE_PANEL_CUSTOMERS . "` WHERE " . ($id > 0 ? "`customerid` = :idln" : "`loginname` = :idln") . ($this->getUserDetail('customers_see_all') ? '' : " AND `adminid` = :adminid")); @@ -84,17 +86,32 @@ class Customers extends ApiCommand implements ResourceEntity if ($this->getUserDetail('customers_see_all') == '0') { $params['adminid'] = $this->getUserDetail('adminid'); } - $result = Database::pexecute_first($result_stmt, $params, true, true); - if ($result) { - $this->logger()->logAction(ADM_ACTION, LOG_NOTICE, "[API] get customer '" . $result['loginname'] . "'"); - return $this->response(200, "successfull", $result); + } else { + if (($id > 0 && $id != $this->getUserDetail('customerid')) || ! empty($loginname) && $loginname != $this->getUserDetail('loginname')) { + throw new Exception("You cannot access data of other customers", 401); } - $key = ($id > 0 ? "id #" . $id : "loginname '" . $loginname . "'"); - throw new Exception("Customer with " . $key . " could not be found", 404); + $result_stmt = Database::prepare(" + SELECT * FROM `" . TABLE_PANEL_CUSTOMERS . "` + WHERE " . ($id > 0 ? "`customerid` = :idln" : "`loginname` = :idln")); + $params = array( + 'idln' => ($id <= 0 ? $loginname : $id) + ); } - throw new Exception("Not allowed to execute given command.", 403); + $result = Database::pexecute_first($result_stmt, $params, true, true); + if ($result) { + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_NOTICE, "[API] get customer '" . $result['loginname'] . "'"); + return $this->response(200, "successfull", $result); + } + $key = ($id > 0 ? "id #" . $id : "loginname '" . $loginname . "'"); + throw new Exception("Customer with " . $key . " could not be found", 404); } + /** + * create a new customer with default ftp-user and standard-subdomain (if wanted) + * + * @access admin + * @return array + */ public function add() { if ($this->isAdmin()) { @@ -236,7 +253,9 @@ class Customers extends ApiCommand implements ResourceEntity ))->get(); $loginname_check = json_decode($dup_check_result, true)['data']; } catch (Exception $e) { - $loginname_check = array('loginname' => ''); + $loginname_check = array( + 'loginname' => '' + ); } // Check if an admin with the loginname already exists @@ -246,7 +265,9 @@ class Customers extends ApiCommand implements ResourceEntity ))->get(); $loginname_check_admin = json_decode($dup_check_result, true)['data']; } catch (Exception $e) { - $loginname_check_admin = array('loginname' => ''); + $loginname_check_admin = array( + 'loginname' => '' + ); } if (strtolower($loginname_check['loginname']) == strtolower($loginname) || strtolower($loginname_check_admin['loginname']) == strtolower($loginname)) { @@ -646,17 +667,29 @@ class Customers extends ApiCommand implements ResourceEntity throw new Exception("Not allowed to execute given command.", 403); } + /** + * update customer entry by either id or loginname + * + * @param int $id + * optional, the customer-id + * @param string $loginname + * optional, the loginname + * + * @access admin, customer + * @throws Exception + * @return array + */ public function update() { if ($this->isAdmin()) { $id = $this->getParam('id', true, 0); $ln_optional = ($id <= 0 ? false : true); $loginname = $this->getParam('loginname', $ln_optional, ''); - + if ($id <= 0 && empty($loginname)) { throw new Exception("Either 'id' or 'loginname' parameter must be given", 406); } - + $json_result = Customers::getLocal($this->getUserData(), array( 'id' => $id, 'loginname' => $loginname @@ -729,36 +762,18 @@ class Customers extends ApiCommand implements ResourceEntity if (Settings::Get('ticket.enabled') != '1') { $tickets = - 1; } - + if (empty($theme)) { $theme = Settings::Get('panel.default_theme'); } - + $diskspace = $diskspace * 1024; $traffic = $traffic * 1024 * 1024; - - if (((($this->getUserDetail('diskspace_used') + $diskspace - $result['diskspace']) > $this->getUserDetail('diskspace')) && ($this->getUserDetail('diskspace') / 1024) != '-1') - || ((($this->getUserDetail('mysqls_used') + $mysqls - $result['mysqls']) > $this->getUserDetail('mysqls')) && $this->getUserDetail('mysqls') != '-1') - || ((($this->getUserDetail('emails_used') + $emails - $result['emails']) > $this->getUserDetail('emails')) && $this->getUserDetail('emails') != '-1') - || ((($this->getUserDetail('email_accounts_used') + $email_accounts - $result['email_accounts']) > $this->getUserDetail('email_accounts')) && $this->getUserDetail('email_accounts') != '-1') - || ((($this->getUserDetail('email_forwarders_used') + $email_forwarders - $result['email_forwarders']) > $this->getUserDetail('email_forwarders')) && $this->getUserDetail('email_forwarders') != '-1') - || ((($this->getUserDetail('email_quota_used') + $email_quota - $result['email_quota']) > $this->getUserDetail('email_quota')) && $this->getUserDetail('email_quota') != '-1' && Settings::Get('system.mail_quota_enabled') == '1') - || ((($this->getUserDetail('ftps_used') + $ftps - $result['ftps']) > $this->getUserDetail('ftps')) && $this->getUserDetail('ftps') != '-1') - || ((($this->getUserDetail('tickets_used') + $tickets - $result['tickets']) > $this->getUserDetail('tickets')) && $this->getUserDetail('tickets') != '-1') - || ((($this->getUserDetail('subdomains_used') + $subdomains - $result['subdomains']) > $this->getUserDetail('subdomains')) && $this->getUserDetail('subdomains') != '-1') - || (($diskspace / 1024) == '-1' && ($this->getUserDetail('diskspace') / 1024) != '-1') - || ($mysqls == '-1' && $this->getUserDetail('mysqls') != '-1') - || ($emails == '-1' && $this->getUserDetail('emails') != '-1') - || ($email_accounts == '-1' && $this->getUserDetail('email_accounts') != '-1') - || ($email_forwarders == '-1' && $this->getUserDetail('email_forwarders') != '-1') - || ($email_quota == '-1' && $this->getUserDetail('email_quota') != '-1' && Settings::Get('system.mail_quota_enabled') == '1') - || ($ftps == '-1' && $this->getUserDetail('ftps') != '-1') - || ($tickets == '-1' && $this->getUserDetail('tickets') != '-1') - || ($subdomains == '-1' && $this->getUserDetail('subdomains') != '-1') - ) { + + if (((($this->getUserDetail('diskspace_used') + $diskspace - $result['diskspace']) > $this->getUserDetail('diskspace')) && ($this->getUserDetail('diskspace') / 1024) != '-1') || ((($this->getUserDetail('mysqls_used') + $mysqls - $result['mysqls']) > $this->getUserDetail('mysqls')) && $this->getUserDetail('mysqls') != '-1') || ((($this->getUserDetail('emails_used') + $emails - $result['emails']) > $this->getUserDetail('emails')) && $this->getUserDetail('emails') != '-1') || ((($this->getUserDetail('email_accounts_used') + $email_accounts - $result['email_accounts']) > $this->getUserDetail('email_accounts')) && $this->getUserDetail('email_accounts') != '-1') || ((($this->getUserDetail('email_forwarders_used') + $email_forwarders - $result['email_forwarders']) > $this->getUserDetail('email_forwarders')) && $this->getUserDetail('email_forwarders') != '-1') || ((($this->getUserDetail('email_quota_used') + $email_quota - $result['email_quota']) > $this->getUserDetail('email_quota')) && $this->getUserDetail('email_quota') != '-1' && Settings::Get('system.mail_quota_enabled') == '1') || ((($this->getUserDetail('ftps_used') + $ftps - $result['ftps']) > $this->getUserDetail('ftps')) && $this->getUserDetail('ftps') != '-1') || ((($this->getUserDetail('tickets_used') + $tickets - $result['tickets']) > $this->getUserDetail('tickets')) && $this->getUserDetail('tickets') != '-1') || ((($this->getUserDetail('subdomains_used') + $subdomains - $result['subdomains']) > $this->getUserDetail('subdomains')) && $this->getUserDetail('subdomains') != '-1') || (($diskspace / 1024) == '-1' && ($this->getUserDetail('diskspace') / 1024) != '-1') || ($mysqls == '-1' && $this->getUserDetail('mysqls') != '-1') || ($emails == '-1' && $this->getUserDetail('emails') != '-1') || ($email_accounts == '-1' && $this->getUserDetail('email_accounts') != '-1') || ($email_forwarders == '-1' && $this->getUserDetail('email_forwarders') != '-1') || ($email_quota == '-1' && $this->getUserDetail('email_quota') != '-1' && Settings::Get('system.mail_quota_enabled') == '1') || ($ftps == '-1' && $this->getUserDetail('ftps') != '-1') || ($tickets == '-1' && $this->getUserDetail('tickets') != '-1') || ($subdomains == '-1' && $this->getUserDetail('subdomains') != '-1')) { standard_error('youcantallocatemorethanyouhave', '', true); } - + // Either $name and $firstname or the $company must be inserted if ($name == '' && $company == '') { standard_error(array( @@ -1160,6 +1175,7 @@ class Customers extends ApiCommand implements ResourceEntity * @param bool $delete_userfiles * optional, default false * + * @access admin * @throws Exception * @return array */ @@ -1405,6 +1421,7 @@ class Customers extends ApiCommand implements ResourceEntity * @param string $loginname * optional, the loginname * + * @access admin * @throws Exception * @return array */ @@ -1414,11 +1431,11 @@ class Customers extends ApiCommand implements ResourceEntity $id = $this->getParam('id', true, 0); $ln_optional = ($id <= 0 ? false : true); $loginname = $this->getParam('loginname', $ln_optional, ''); - + if ($id <= 0 && empty($loginname)) { throw new Exception("Either 'id' or 'loginname' parameter must be given", 406); } - + $json_result = Customers::getLocal($this->getUserData(), array( 'id' => $id, 'loginname' => $loginname diff --git a/lib/classes/api/commands/class.FpmDaemons.php b/lib/classes/api/commands/class.FpmDaemons.php index c604e53c..fd7b30fa 100644 --- a/lib/classes/api/commands/class.FpmDaemons.php +++ b/lib/classes/api/commands/class.FpmDaemons.php @@ -21,6 +21,8 @@ class FpmDaemons extends ApiCommand implements ResourceEntity /** * lists all fpm-daemon entries * + * @access admin + * @throws Exception * @return array count|list */ public function list() @@ -72,6 +74,8 @@ class FpmDaemons extends ApiCommand implements ResourceEntity * * @param int $id fpm-daemon-id * + * @access admin + * @throws Exception * @return array */ public function get() @@ -93,6 +97,13 @@ class FpmDaemons extends ApiCommand implements ResourceEntity throw new Exception("Not allowed to execute given command.", 403); } + /** + * create a new fpm-daemon entry + * + * @access admin + * @throws Exception + * @return array + */ public function add() { if ($this->isAdmin() && $this->getUserDetail('change_serversettings') == 1) { @@ -166,6 +177,15 @@ class FpmDaemons extends ApiCommand implements ResourceEntity throw new Exception("Not allowed to execute given command.", 403); } + /** + * update a fpm-daemon entry by given id + * + * @param int $id + * + * @access admin + * @throws Exception + * @return array + */ public function update() { if ($this->isAdmin() && $this->getUserDetail('change_serversettings') == 1) { @@ -251,6 +271,7 @@ class FpmDaemons extends ApiCommand implements ResourceEntity * * @param int $id fpm-daemon-id * + * @access admin * @throws Exception * @return array */ diff --git a/lib/classes/api/commands/class.Froxlor.php b/lib/classes/api/commands/class.Froxlor.php index 21c0951e..2f61e20c 100644 --- a/lib/classes/api/commands/class.Froxlor.php +++ b/lib/classes/api/commands/class.Froxlor.php @@ -21,6 +21,7 @@ class Froxlor extends ApiCommand /** * checks whether there is a newer version of froxlor available * + * @access admin * @throws Exception * @return string */ @@ -82,6 +83,8 @@ class Froxlor extends ApiCommand /** * * @todo import settings + * + * @access admin */ public function importSettings() {} @@ -89,6 +92,8 @@ class Froxlor extends ApiCommand /** * * @todo export settings to file + * + * @access admin */ public function exportSettings() {} @@ -96,6 +101,8 @@ class Froxlor extends ApiCommand /** * return a list of all settings * + * @access admin + * @throws Exception * @return array count|list */ public function listSettings() @@ -126,6 +133,7 @@ class Froxlor extends ApiCommand * @param string $key * settinggroup.varname couple * + * @access admin * @throws Exception * @return string */ @@ -146,6 +154,7 @@ class Froxlor extends ApiCommand * @param string $value * optional the new value, default is '' * + * @access admin * @throws Exception * @return string */ @@ -153,7 +162,7 @@ class Froxlor extends ApiCommand { // currently not implemented as it required validation too so no wrong settings are being stored via API throw new Exception("Not available yet.", 501); - + if ($this->isAdmin() && $this->getUserDetail('change_serversettings')) { $setting = $this->getParam('key'); $value = $this->getParam('value', true, ''); @@ -173,6 +182,7 @@ class Froxlor extends ApiCommand * @param string $module * optional, return list of functions for a specific module * + * @access admin, customer * @throws Exception * @return array */ @@ -260,7 +270,7 @@ class Froxlor extends ApiCommand 'head' => 'There is no comment-block for "' . $module . '.' . $function . '"' ); } - + $clines = explode("\n", $comment); $result = array(); $result['params'] = array(); @@ -277,6 +287,15 @@ class Froxlor extends ApiCommand 'desc' => (isset($r[3]) ? trim($r['3']) : '') ); $param_desc = true; + } // check access-section + elseif (strpos($c, '@access')) { + preg_match('/^\*\s\@access\s(.*)/', $c, $r); + if (! isset($r[0]) || empty($r[0])) { + $r[1] = 'This function has no restrictions'; + } + $result['access'] = array( + 'groups' => (isset($r[1]) ? trim($r[1]) : '') + ); } // check return-section elseif (strpos($c, '@return')) { preg_match('/^\*\s\@return\s(\w+)(\s.*)?/', $c, $r); @@ -312,6 +331,7 @@ class Froxlor extends ApiCommand } } } + $result['head'] =trim($result['head']); return $result; } catch (\ReflectionException $e) { return array(); diff --git a/lib/classes/api/commands/class.IpsAndPorts.php b/lib/classes/api/commands/class.IpsAndPorts.php index fe0affa0..32ab79a3 100644 --- a/lib/classes/api/commands/class.IpsAndPorts.php +++ b/lib/classes/api/commands/class.IpsAndPorts.php @@ -21,6 +21,8 @@ class IpsAndPorts extends ApiCommand implements ResourceEntity /** * lists all ip/port entries * + * @access admin + * @throws Exception * @return array count|list */ public function list() @@ -48,7 +50,8 @@ class IpsAndPorts extends ApiCommand implements ResourceEntity * * @param int $id * ip-port-id - * + * + * @access admin * @throws Exception * @return array */ @@ -71,6 +74,13 @@ class IpsAndPorts extends ApiCommand implements ResourceEntity throw new Exception("Not allowed to execute given command.", 403); } + /** + * create a new ip/port entry + * + * @access admin + * @throws Exception + * @return array + */ public function add() { if ($this->isAdmin() && $this->getUserDetail('change_serversettings')) { @@ -199,6 +209,15 @@ class IpsAndPorts extends ApiCommand implements ResourceEntity throw new Exception("Not allowed to execute given command.", 403); } + /** + * update ip/port entry by given id + * + * @param int $id + * + * @access admin + * @throws ErrorException + * @return array + */ public function update() { if ($this->isAdmin() && $this->getUserDetail('change_serversettings')) { @@ -349,7 +368,8 @@ class IpsAndPorts extends ApiCommand implements ResourceEntity * * @param int $id * ip-port-id - * + * + * @access admin * @throws Exception * @return array */ diff --git a/lib/classes/api/commands/class.Mysqls.php b/lib/classes/api/commands/class.Mysqls.php index e55c6651..b7faee12 100644 --- a/lib/classes/api/commands/class.Mysqls.php +++ b/lib/classes/api/commands/class.Mysqls.php @@ -31,6 +31,7 @@ class Mysqls extends ApiCommand implements ResourceEntity * @param int $dbserver * optional, specify database-server, default is none * + * @access admin, customer * @throws Exception * @return array */ @@ -130,6 +131,7 @@ class Mysqls extends ApiCommand implements ResourceEntity * @param string $loginname * optional, admin-only, select dbs of a specific customer by loginname * + * @access admin, customer * @return array count|list */ public function list() @@ -220,6 +222,7 @@ class Mysqls extends ApiCommand implements ResourceEntity * @param int $dbserver * optional, specify database-server, default is none * + * @access admin, customer * @throws Exception * @return array */ diff --git a/lib/classes/api/commands/class.PhpSettings.php b/lib/classes/api/commands/class.PhpSettings.php index db10ac8d..6d26a560 100644 --- a/lib/classes/api/commands/class.PhpSettings.php +++ b/lib/classes/api/commands/class.PhpSettings.php @@ -21,6 +21,8 @@ class PhpSettings extends ApiCommand implements ResourceEntity /** * lists all php-setting entries * + * @access admin + * @throws Exception * @return array count|list */ public function list() @@ -106,6 +108,8 @@ class PhpSettings extends ApiCommand implements ResourceEntity * * @param int $id php-settings-id * + * @access admin + * @throws Exception * @return array */ public function get() @@ -127,6 +131,13 @@ class PhpSettings extends ApiCommand implements ResourceEntity throw new Exception("Not allowed to execute given command.", 403); } + /** + * add new php-settings entry + * + * @access admin + * @throws Exception + * @return array + */ public function add() { if ($this->isAdmin() && $this->getUserDetail('change_serversettings') == 1) { @@ -226,6 +237,15 @@ class PhpSettings extends ApiCommand implements ResourceEntity throw new Exception("Not allowed to execute given command.", 403); } + /** + * update a php-setting entry by given id + * + * @param int $id + * + * @access admin + * @throws Exception:: + * @return array + */ public function update() { if ($this->isAdmin() && $this->getUserDetail('change_serversettings') == 1) { @@ -333,6 +353,7 @@ class PhpSettings extends ApiCommand implements ResourceEntity * * @param int $id php-settings-id * + * @access admin * @throws Exception * @return array */ From b5ebe48715758db9c4a918837fb951db296ac296 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sat, 24 Feb 2018 11:01:29 +0100 Subject: [PATCH 0477/1335] forgot to save this one Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/commands/class.Domains.php | 27 ++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/lib/classes/api/commands/class.Domains.php b/lib/classes/api/commands/class.Domains.php index 92866870..b363c19e 100644 --- a/lib/classes/api/commands/class.Domains.php +++ b/lib/classes/api/commands/class.Domains.php @@ -21,6 +21,8 @@ class Domains extends ApiCommand implements ResourceEntity /** * lists all domain entries * + * @access admin + * @throws Exception * @return array count|list */ public function list() @@ -61,7 +63,8 @@ class Domains extends ApiCommand implements ResourceEntity * optional, the domainname * @param boolean $no_std_subdomain * optional, default false - * + * + * @access admin * @throws Exception * @return array */ @@ -106,6 +109,13 @@ class Domains extends ApiCommand implements ResourceEntity throw new Exception("Not allowed to execute given command.", 403); } + /** + * add new domain entry + * + * @access admin + * @throws Exception + * @return array + */ public function add() { if ($this->isAdmin() && $this->getUserDetail('change_serversettings')) { @@ -755,6 +765,18 @@ class Domains extends ApiCommand implements ResourceEntity throw new Exception("Not allowed to execute given command.", 403); } + /** + * update domain entry by either id or domainname + * + * @param int $id + * optional, the domain-id + * @param string $domainname + * optional, the domainname + * + * @access admin + * @throws Exception + * @return array + */ public function update() { if ($this->isAdmin() && $this->getUserDetail('change_serversettings')) { @@ -1574,7 +1596,8 @@ class Domains extends ApiCommand implements ResourceEntity * optional, remove also domains that are subdomains of this domain but added as main domains; default false * @param bool $is_stdsubdomain * optional, default false, specify whether it's a std-subdomain you are deleting as it does not count as subdomain-resource - * + * + * @access admin * @throws Exception * @return array */ From d2aaf84effdab2de53d18d4af303bbefc7c34993 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sat, 24 Feb 2018 12:13:47 +0100 Subject: [PATCH 0478/1335] added Mysqls.add Signed-off-by: Michael Kaufmann (d00p) --- apihelp.php | 3 +- lib/classes/api/commands/class.Mysqls.php | 210 +++++++++++++++++++++- 2 files changed, 211 insertions(+), 2 deletions(-) diff --git a/apihelp.php b/apihelp.php index bcc3169b..4fa6eee4 100644 --- a/apihelp.php +++ b/apihelp.php @@ -47,6 +47,7 @@ foreach ($m_arr as $module) { // set necessary data $output_arr[$module['module']][$module['function']] = array( 'return_type' => (isset($module['return']['type']) && $module['return']['type'] != "" ? $module['return']['type'] : - 1), + 'return_desc' => (isset($module['return']['desc']) && $module['return']['desc'] != "" ? $module['return']['desc'] : - 1), 'params_list' => array(), 'head' => $module['head'], 'access' => isset($module['access']) ? $module['access'] : null @@ -126,7 +127,7 @@ foreach ($output_arr as $module => $functions) { $parms .= ""; $apihelp .= $parms; } - $apihelp .= "
    Returns " . ($funcdata['return_type'] == - 1 ? "no-return-type" : $funcdata['return_type']); + $apihelp .= "
    Returns " . ($funcdata['return_type'] == - 1 ? "no-return-type" : $funcdata['return_type']) . ($funcdata['return_desc'] == - 1 ? "" : " ".$funcdata['return_desc']); $apihelp .= "

    "; } } diff --git a/lib/classes/api/commands/class.Mysqls.php b/lib/classes/api/commands/class.Mysqls.php index b7faee12..eed6b999 100644 --- a/lib/classes/api/commands/class.Mysqls.php +++ b/lib/classes/api/commands/class.Mysqls.php @@ -18,8 +18,207 @@ class Mysqls extends ApiCommand implements ResourceEntity { + /** + * add a new mysql-database + * + * @param string $mysql_password + * password for the created database and database-user + * @param int $mysql_server + * optional, default is 0 + * @param string $description + * optional, description for database + * @param bool $sendinfomail + * optional, send created resource-information to customer, default: false + * @param int $customer_id + * required when called as admin, not needed when called as customer + * + * @access admin, customer + * @throws Exception + * @return array + */ public function add() - {} + { + if ($this->getUserDetail('mysqls_used') < $this->getUserDetail('mysqls') || $this->getUserDetail('mysqls') == '-1') { + + // required paramters + $password = $this->getParam('mysql_password'); + + // parameters + $dbserver = $this->getParam('mysql_server', true, 0); + $databasedescription = $this->getParam('description', true, ''); + $sendinfomail = $this->getParam('sendinfomail', true, 0); + + // validation + $password = validate($password, 'password', '', '', array(), true); + $password = validatePassword($password, true); + $databasedescription = validate(trim($databasedescription), 'description', '', '', array(), true); + + // validate whether the dbserver exists + $dbserver = validate($dbserver, html_entity_decode($this->lng['mysql']['mysql_server']), '', '', 0, true); + Database::needRoot(true, $dbserver); + Database::needSqlData(); + $sql_root = Database::getSqlData(); + Database::needRoot(false); + if (! isset($sql_root) || ! is_array($sql_root)) { + throw new ErrorException("Database server with index #" . $dbserver . " is unknown", 404); + } + + if ($password == '') { + standard_error(array( + 'stringisempty', + 'mysql_password' + ), '', true); + } + + if ($sendinfomail != 1) { + $sendinfomail = 0; + } + + // get needed customer info to reduce the mysql-usage-counter by one + if ($this->isAdmin()) { + // get customer id + $customer_id = $this->getParam('customer_id'); + $json_result = Customers::getLocal($this->getUserData(), array( + 'id' => $result['customerid'] + ))->get(); + $customer = json_decode($json_result, true)['data']; + // check whether the customer has enough resources to get the database added + if ($customer['mysqls_used'] >= $customer['mysqls'] && $customer['mysqls'] != '-1') { + throw new Exception("Customer has no more resources available", 406); + } + } else { + $customer_id = $this->getUserDetail('customer_id'); + } + + $newdb_params = array( + 'loginname' => ($this->isAdmin() ? $customer['loginname'] : $this->getUserDetail('loginname')), + 'mysql_lastaccountnumber' => ($this->isAdmin() ? $customer['mysql_lastaccountnumber'] : $this->getUserDetail('mysql_lastaccountnumber')) + ); + // create database, user, set permissions, etc.pp. + $dbm = new DbManager($this->logger()); + $username = $dbm->createDatabase($newdb_params['loginname'], $password, $newdb_params['mysql_lastaccountnumber']); + + // we've checked against the password in dbm->createDatabase + if ($username == false) { + standard_error('passwordshouldnotbeusername', '', true); + } + + // add database info to froxlor + $stmt = Database::prepare(" + INSERT INTO `" . TABLE_PANEL_DATABASES . "` + SET + `customerid` = :customerid, + `databasename` = :databasename, + `description` = :description, + `dbserver` = :dbserver + "); + $params = array( + "customerid" => ($this->isAdmin() ? $customer['customerid'] : $this->getUserDetail('customerid')), + "databasename" => $username, + "description" => $databasedescription, + "dbserver" => $dbserver + ); + Database::pexecute($stmt, $params, true, true); + $databaseid = Database::lastInsertId(); + $params['id'] = $databaseid; + + // update customer usage + $stmt = Database::prepare(" + UPDATE `" . TABLE_PANEL_CUSTOMERS . "` + SET `mysqls_used` = `mysqls_used` + 1, `mysql_lastaccountnumber` = `mysql_lastaccountnumber` + 1 + WHERE `customerid` = :customerid + "); + Database::pexecute($stmt, array( + "customerid" => ($this->isAdmin() ? $customer['customerid'] : $this->getUserDetail('customerid')) + ), true, true); + + // update admin usage + $stmt = Database::prepare(" + UPDATE `" . TABLE_PANEL_ADMINS . "` + SET `mysqls_used` = `mysqls_used` + 1 + WHERE `adminid` = :adminid + "); + Database::pexecute($stmt, array( + "adminid" => $this->getUserDetail('adminid') + ), true, true); + + // send info-mail? + if ($sendinfomail == 1) { + $pma = $this->lng['admin']['notgiven']; + if (Settings::Get('panel.phpmyadmin_url') != '') { + $pma = Settings::Get('panel.phpmyadmin_url'); + } + + Database::needRoot(true, $dbserver); + Database::needSqlData(); + $sql_root = Database::getSqlData(); + Database::needRoot(false); + $userinfo = ($this->isAdmin() ? $customer : $this->getUserData()); + + $replace_arr = array( + 'SALUTATION' => getCorrectUserSalutation($userinfo), + 'CUST_NAME' => getCorrectUserSalutation($userinfo), // < keep this for compatibility + 'DB_NAME' => $username, + 'DB_PASS' => $password, + 'DB_DESC' => $databasedescription, + 'DB_SRV' => $sql_root['host'], + 'PMA_URI' => $pma + ); + + $def_language = $userinfo['def_language']; + $result_stmt = Database::prepare(" + SELECT `value` FROM `" . TABLE_PANEL_TEMPLATES . "` + WHERE `adminid` = :adminid + AND `language` = :lang + AND `templategroup`='mails' + AND `varname`='new_database_by_customer_subject' + "); + $result = Database::pexecute_first($result_stmt, array( + "adminid" => $userinfo['adminid'], + "lang" => $def_language + ), true, true); + $mail_subject = html_entity_decode(replace_variables((($result['value'] != '') ? $result['value'] : $this->lng['mails']['new_database_by_customer']['subject']), $replace_arr)); + + $result_stmt = Database::prepare(" + SELECT `value` FROM `" . TABLE_PANEL_TEMPLATES . "` + WHERE `adminid`= :adminid + AND `language`= :lang + AND `templategroup` = 'mails' + AND `varname` = 'new_database_by_customer_mailbody' + "); + $result = Database::pexecute_first($result_stmt, array( + "adminid" => $userinfo['adminid'], + "lang" => $def_language + )); + $mail_body = html_entity_decode(replace_variables((($result['value'] != '') ? $result['value'] : $this->lng['mails']['new_database_by_customer']['mailbody']), $replace_arr)); + + $_mailerror = false; + try { + $this->mail->Subject = $mail_subject; + $this->mail->AltBody = $mail_body; + $this->mail->MsgHTML(str_replace("\n", "
    ", $mail_body)); + $this->mail->AddAddress($userinfo['email'], getCorrectUserSalutation($userinfo)); + $this->mail->Send(); + } catch (phpmailerException $e) { + $mailerr_msg = $e->errorMessage(); + $_mailerror = true; + } catch (Exception $e) { + $mailerr_msg = $e->getMessage(); + $_mailerror = true; + } + + if ($_mailerror) { + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_ERR, "[API] Error sending mail: " . $mailerr_msg); + standard_error('errorsendingmail', $userinfo['email'], true); + } + + $this->mail->ClearAddresses(); + } + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_WARNING, "[API] added mysql-database '" . $username . "'"); + return $this->response(200, "successfull", $params); + } + throw new Exception("No more resources available", 406); + } /** * return a mysql database entry by either id or dbname @@ -85,6 +284,8 @@ class Mysqls extends ApiCommand implements ResourceEntity } else { if ($id != $this->getUserDetail('customerid')) { throw new Exception("You cannot access data of other customers", 401); + } elseif (Settings::IsInList('panel.customer_hide_options', 'mysql')) { + throw new Exception("You cannot access this resource", 405); } $result_stmt = Database::prepare(" SELECT * FROM `" . TABLE_PANEL_DATABASES . "` @@ -161,6 +362,9 @@ class Mysqls extends ApiCommand implements ResourceEntity $customer_ids[] = $customer['customerid']; } } else { + if (Settings::IsInList('panel.customer_hide_options', 'mysql')) { + throw new Exception("You cannot access this resource", 405); + } $customer_ids = array( $this->getUserDetail('customerid') ); @@ -237,6 +441,10 @@ class Mysqls extends ApiCommand implements ResourceEntity throw new Exception("Either 'id' or 'dbname' parameter must be given", 406); } + if ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'mysql')) { + throw new Exception("You cannot access this resource", 405); + } + $json_result = Mysqls::getLocal($this->getUserData(), array( 'id' => $id, 'dbname' => $dbname, From 0bf430e0c13e3559e16db976ff021fdfb45d63b0 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sat, 24 Feb 2018 15:02:49 +0100 Subject: [PATCH 0479/1335] let customers edit password, def-language and theme in Customers.edit Signed-off-by: Michael Kaufmann (d00p) --- customer_mysql.php | 146 +--- lib/classes/api/commands/class.Customers.php | 673 ++++++++++--------- 2 files changed, 376 insertions(+), 443 deletions(-) diff --git a/customer_mysql.php b/customer_mysql.php index 774daf6b..9e1b0b8e 100644 --- a/customer_mysql.php +++ b/customer_mysql.php @@ -132,134 +132,12 @@ if ($page == 'overview') { } elseif ($action == 'add') { if ($userinfo['mysqls_used'] < $userinfo['mysqls'] || $userinfo['mysqls'] == '-1') { if (isset($_POST['send']) && $_POST['send'] == 'send') { - $password = validate($_POST['mysql_password'], 'password'); - $password = validatePassword($password); - - $sendinfomail = isset($_POST['sendinfomail']) ? 1 : 0; - if ($sendinfomail != 1) { - $sendinfomail = 0; - } - - if ($password == '') { - standard_error(array('stringisempty', 'mypassword')); - } else { - $dbserver = 0; - $dbservers_stmt = Database::query("SELECT COUNT(DISTINCT `dbserver`) as numservers FROM `".TABLE_PANEL_DATABASES."`"); - $_dbserver = $dbservers_stmt->fetch(PDO::FETCH_ASSOC); - $count_mysqlservers = $_dbserver['numservers']; - if ($count_mysqlservers > 1) { - $dbserver = validate($_POST['mysql_server'], html_entity_decode($lng['mysql']['mysql_server']), '', '', 0); - Database::needRoot(true, $dbserver); - Database::needSqlData(); - $sql_root = Database::getSqlData(); - Database::needRoot(false); - if (!isset($sql_root) || !is_array($sql_root)) { - $dbserver = 0; - } - } - - // validate description before actual adding the database, #1052 - $databasedescription = validate(trim($_POST['description']), 'description'); - - // create database, user, set permissions, etc.pp. - $dbm = new DbManager($log); - $username = $dbm->createDatabase( - $userinfo['loginname'], - $password, - $userinfo['mysql_lastaccountnumber'] - ); - - // we've checked against the password in dbm->createDatabase - if ($username == false) { - standard_error('passwordshouldnotbeusername'); - } - - // Statement modified for Database description -- PH 2004-11-29 - $stmt = Database::prepare('INSERT INTO `' . TABLE_PANEL_DATABASES . '` - (`customerid`, `databasename`, `description`, `dbserver`) - VALUES (:customerid, :databasename, :description, :dbserver)' - ); - $params = array( - "customerid" => $userinfo['customerid'], - "databasename" => $username, - "description" => $databasedescription, - "dbserver" => $dbserver - ); - Database::pexecute($stmt, $params); - - $stmt = Database::prepare('UPDATE `' . TABLE_PANEL_CUSTOMERS . '` - SET `mysqls_used` = `mysqls_used` + 1, `mysql_lastaccountnumber` = `mysql_lastaccountnumber` + 1 - WHERE `customerid` = :customerid' - ); - Database::pexecute($stmt, array("customerid" => $userinfo['customerid'])); - - if ($sendinfomail == 1) { - $pma = $lng['admin']['notgiven']; - if (Settings::Get('panel.phpmyadmin_url') != '') { - $pma = Settings::Get('panel.phpmyadmin_url'); - } - - Database::needRoot(true, $dbserver); - Database::needSqlData(); - $sql_root = Database::getSqlData(); - Database::needRoot(false); - - $replace_arr = array( - 'SALUTATION' => getCorrectUserSalutation($userinfo), - 'CUST_NAME' => getCorrectUserSalutation($userinfo), // < keep this for compatibility - 'DB_NAME' => $username, - 'DB_PASS' => $password, - 'DB_DESC' => $databasedescription, - 'DB_SRV' => $sql_root['host'], - 'PMA_URI' => $pma - ); - - $def_language = $userinfo['def_language']; - $result_stmt = Database::prepare("SELECT `value` FROM `" . TABLE_PANEL_TEMPLATES . "` - WHERE `adminid` = :adminid - AND `language` = :lang - AND `templategroup`='mails' - AND `varname`='new_database_by_customer_subject'" - ); - Database::pexecute($result_stmt, array("adminid" => $userinfo['adminid'], "lang" => $def_language)); - $result = $result_stmt->fetch(PDO::FETCH_ASSOC); - $mail_subject = html_entity_decode(replace_variables((($result['value'] != '') ? $result['value'] : $lng['mails']['new_database_by_customer']['subject']), $replace_arr)); - - $result_stmt = Database::prepare("SELECT `value` FROM `" . TABLE_PANEL_TEMPLATES . "` - WHERE `adminid`= :adminid - AND `language`= :lang - AND `templategroup` = 'mails' - AND `varname` = 'new_database_by_customer_mailbody'" - ); - Database::pexecute($result_stmt, array("adminid" => $userinfo['adminid'], "lang" => $def_language)); - $result = $result_stmt->fetch(PDO::FETCH_ASSOC); - $mail_body = html_entity_decode(replace_variables((($result['value'] != '') ? $result['value'] : $lng['mails']['new_database_by_customer']['mailbody']), $replace_arr)); - - $_mailerror = false; - try { - $mail->Subject = $mail_subject; - $mail->AltBody = $mail_body; - $mail->MsgHTML(str_replace("\n", "
    ", $mail_body)); - $mail->AddAddress($userinfo['email'], getCorrectUserSalutation($userinfo)); - $mail->Send(); - } catch(phpmailerException $e) { - $mailerr_msg = $e->errorMessage(); - $_mailerror = true; - } catch (Exception $e) { - $mailerr_msg = $e->getMessage(); - $_mailerror = true; - } - - if ($_mailerror) { - $log->logAction(USR_ACTION, LOG_ERR, "Error sending mail: " . $mailerr_msg); - standard_error('errorsendingmail', $userinfo['email']); - } - - $mail->ClearAddresses(); - } - - redirectTo($filename, array('page' => $page, 's' => $s)); + try { + Mysqls::getLocal($userinfo, $_POST)->add(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); } + redirectTo($filename, array('page' => $page, 's' => $s)); } else { $dbservers_stmt = Database::query("SELECT DISTINCT `dbserver` FROM `".TABLE_PANEL_DATABASES."`"); @@ -284,12 +162,14 @@ if ($page == 'overview') { } } } elseif ($action == 'edit' && $id != 0) { - $result_stmt = Database::prepare("SELECT `id`, `databasename`, `description`, `dbserver` FROM `" . TABLE_PANEL_DATABASES . "` - WHERE `customerid` = :customerid - AND `id` = :id" - ); - Database::pexecute($result_stmt, array("customerid" => $userinfo['customerid'], "id" => $id)); - $result = $result_stmt->fetch(PDO::FETCH_ASSOC); + try { + $json_result = Mysqls::getLocal($userinfo, array( + 'id' => $id + ))->get(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); + } + $result = json_decode($json_result, true)['data']; if (isset($result['databasename']) && $result['databasename'] != '') { if (!isset($sql_root[$result['dbserver']]) || !is_array($sql_root[$result['dbserver']])) { diff --git a/lib/classes/api/commands/class.Customers.php b/lib/classes/api/commands/class.Customers.php index 424bdf92..4bea264a 100644 --- a/lib/classes/api/commands/class.Customers.php +++ b/lib/classes/api/commands/class.Customers.php @@ -71,11 +71,11 @@ class Customers extends ApiCommand implements ResourceEntity $id = $this->getParam('id', true, 0); $ln_optional = ($id <= 0 ? false : true); $loginname = $this->getParam('loginname', $ln_optional, ''); - + if ($id <= 0 && empty($loginname)) { throw new Exception("Either 'id' or 'loginname' parameter must be given", 406); } - + if ($this->isAdmin()) { $result_stmt = Database::prepare(" SELECT * FROM `" . TABLE_PANEL_CUSTOMERS . "` @@ -257,7 +257,7 @@ class Customers extends ApiCommand implements ResourceEntity 'loginname' => '' ); } - + // Check if an admin with the loginname already exists try { $dup_check_result = Admins::getLocal($this->getUserData(), array( @@ -269,7 +269,7 @@ class Customers extends ApiCommand implements ResourceEntity 'loginname' => '' ); } - + if (strtolower($loginname_check['loginname']) == strtolower($loginname) || strtolower($loginname_check_admin['loginname']) == strtolower($loginname)) { standard_error('loginnameexists', $loginname, true); } elseif (! validateUsername($loginname, Settings::Get('panel.unix_names'), 14 - strlen(Settings::Get('customer.mysqlprefix')))) { @@ -674,7 +674,7 @@ class Customers extends ApiCommand implements ResourceEntity * optional, the customer-id * @param string $loginname * optional, the loginname - * + * * @access admin, customer * @throws Exception * @return array @@ -697,47 +697,94 @@ class Customers extends ApiCommand implements ResourceEntity $result = json_decode($json_result, true)['data']; $id = $result['customerid']; - // parameters - $move_to_admin = intval_ressource($this->getParam('move_to_admin', true, 0)); - - $idna_convert = new idna_convert_wrapper(); - $email = $this->getParam('email', true, $idna_convert->decode($result['email'])); - $name = $this->getParam('name', true, $result['name']); - $firstname = $this->getParam('firstname', true, $result['firstname']); - $company = $this->getParam('company', true, $result['company']); - $street = $this->getParam('street', true, $result['street']); - $zipcode = $this->getParam('zipcode', true, $result['zipcode']); - $city = $this->getParam('city', true, $result['city']); - $phone = $this->getParam('phone', true, $result['phone']); - $fax = $this->getParam('fax', true, $result['fax']); - $customernumber = $this->getParam('customernumber', true, $result['customernumber']); - $def_language = $this->getParam('def_language', true, $result['def_language']); - $gender = intval_ressource($this->getParam('gender', true, $result['gender'])); - $custom_notes = $this->getParam('custom_notes', true, $result['custom_notes']); - $custom_notes_show = $this->getParam('custom_notes_show', true, $result['custom_notes_show']); - - $dec_places = Settings::Get('panel.decimal_places'); - $diskspace = $this->getUlParam('diskspace', 'diskspace_ul', true, round($result['diskspace'] / 1024, $dec_places)); - $traffic = $this->getUlParam('traffic', 'traffic_ul', true, round($result['traffic'] / (1024 * 1024), $dec_places)); - $subdomains = $this->getUlParam('subdomains', 'subdomains_ul', true, $result['subdomains']); - $emails = $this->getUlParam('emails', 'emails_ul', true, $result['emails']); - $email_accounts = $this->getUlParam('email_accounts', 'email_accounts_ul', true, $result['email_accounts']); - $email_forwarders = $this->getUlParam('email_forwarders', 'email_forwarders_ul', true, $result['email_forwarders']); - $email_quota = $this->getUlParam('email_quota', 'email_quota_ul', true, $result['email_quota']); - $email_imap = $this->getParam('email_imap', true, $result['imap']); - $email_pop3 = $this->getParam('email_pop3', true, $result['pop3']); - $ftps = $this->getUlParam('ftps', 'ftps_ul', true, $result['ftps']); - $tickets = $this->getUlParam('tickets', 'tickets_ul', true, $result['tickets']); - $mysqls = $this->getUlParam('mysqls', 'mysqls_ul', true, $result['mysqls']); - $createstdsubdomain = $this->getParam('createstdsubdomain', true, 0); - $password = $this->getParam('new_customer_password', true, ''); - $sendpassword = $this->getParam('sendpassword', true, 0); - $phpenabled = $this->getParam('phpenabled', true, $result['phpenabled']); - $allowed_phpconfigs = $this->getParam('allowed_phpconfigs', true, json_decode($result['allowed_phpconfigs'], true)); - $perlenabled = $this->getParam('perlenabled', true, $result['perlenabled']); - $dnsenabled = $this->getParam('dnsenabled', true, $result['dnsenabled']); - $deactivated = $this->getParam('deactivated', true, $result['deactivated']); - $theme = $this->getParam('theme', true, $result['theme']); + if ($this->isAdmin()) { + // parameters + $move_to_admin = intval_ressource($this->getParam('move_to_admin', true, 0)); + + $idna_convert = new idna_convert_wrapper(); + $email = $this->getParam('email', true, $idna_convert->decode($result['email'])); + $name = $this->getParam('name', true, $result['name']); + $firstname = $this->getParam('firstname', true, $result['firstname']); + $company = $this->getParam('company', true, $result['company']); + $street = $this->getParam('street', true, $result['street']); + $zipcode = $this->getParam('zipcode', true, $result['zipcode']); + $city = $this->getParam('city', true, $result['city']); + $phone = $this->getParam('phone', true, $result['phone']); + $fax = $this->getParam('fax', true, $result['fax']); + $customernumber = $this->getParam('customernumber', true, $result['customernumber']); + $def_language = $this->getParam('def_language', true, $result['def_language']); + $gender = intval_ressource($this->getParam('gender', true, $result['gender'])); + $custom_notes = $this->getParam('custom_notes', true, $result['custom_notes']); + $custom_notes_show = $this->getParam('custom_notes_show', true, $result['custom_notes_show']); + + $dec_places = Settings::Get('panel.decimal_places'); + $diskspace = $this->getUlParam('diskspace', 'diskspace_ul', true, round($result['diskspace'] / 1024, $dec_places)); + $traffic = $this->getUlParam('traffic', 'traffic_ul', true, round($result['traffic'] / (1024 * 1024), $dec_places)); + $subdomains = $this->getUlParam('subdomains', 'subdomains_ul', true, $result['subdomains']); + $emails = $this->getUlParam('emails', 'emails_ul', true, $result['emails']); + $email_accounts = $this->getUlParam('email_accounts', 'email_accounts_ul', true, $result['email_accounts']); + $email_forwarders = $this->getUlParam('email_forwarders', 'email_forwarders_ul', true, $result['email_forwarders']); + $email_quota = $this->getUlParam('email_quota', 'email_quota_ul', true, $result['email_quota']); + $email_imap = $this->getParam('email_imap', true, $result['imap']); + $email_pop3 = $this->getParam('email_pop3', true, $result['pop3']); + $ftps = $this->getUlParam('ftps', 'ftps_ul', true, $result['ftps']); + $tickets = $this->getUlParam('tickets', 'tickets_ul', true, $result['tickets']); + $mysqls = $this->getUlParam('mysqls', 'mysqls_ul', true, $result['mysqls']); + $createstdsubdomain = $this->getParam('createstdsubdomain', true, 0); + $password = $this->getParam('new_customer_password', true, ''); + $sendpassword = $this->getParam('sendpassword', true, 0); + $phpenabled = $this->getParam('phpenabled', true, $result['phpenabled']); + $allowed_phpconfigs = $this->getParam('allowed_phpconfigs', true, json_decode($result['allowed_phpconfigs'], true)); + $perlenabled = $this->getParam('perlenabled', true, $result['perlenabled']); + $dnsenabled = $this->getParam('dnsenabled', true, $result['dnsenabled']); + $deactivated = $this->getParam('deactivated', true, $result['deactivated']); + $theme = $this->getParam('theme', true, $result['theme']); + } else { + // allowed parameters + $def_language = $this->getParam('def_language', true, $result['def_language']); + $password = $this->getParam('new_customer_password', true, ''); + $theme = $this->getParam('theme', true, $result['theme']); + + // unchangeable parameters for customers + $move_to_admin = 0; + $idna_convert = new idna_convert_wrapper(); + $email = $idna_convert->decode($result['email']); + $name = $result['name']; + $firstname = $result['firstname']; + $company = $result['company']; + $street = $result['street']; + $zipcode = $result['zipcode']; + $city = $result['city']; + $phone = $result['phone']; + $fax = $result['fax']; + $customernumber = $result['customernumber']; + $gender = $result['gender']; + $custom_notes = $result['custom_notes']; + $custom_notes_show = $result['custom_notes_show']; + + $dec_places = Settings::Get('panel.decimal_places'); + $diskspace = round($result['diskspace'] / 1024, $dec_places); + $traffic = round($result['traffic'] / (1024 * 1024), $dec_places); + $subdomains = $result['subdomains']; + $emails = $result['emails']; + $email_accounts = $result['email_accounts']; + $email_forwarders = $result['email_forwarders']; + $email_quota = $result['email_quota']; + $email_imap = $result['imap']; + $email_pop3 = $result['pop3']; + $ftps = $result['ftps']; + $tickets = $result['tickets']; + $mysqls = $result['mysqls']; + // if we got one, it's true so none will be generated (it exists already) + // if not, none will be generated + $createstdsubdomain = ($result['standardsubdomain'] > 0 ? 1 : 0); + $sendpassword = 0; + $phpenabled = $result['phpenabled']; + $allowed_phpconfigs = json_decode($result['allowed_phpconfigs'], true); + $perlenabled = $result['perlenabled']; + $dnsenabled = $result['dnsenabled']; + $deactivated = $result['deactivated']; + } // validation $idna_convert = new idna_convert_wrapper(); @@ -770,277 +817,282 @@ class Customers extends ApiCommand implements ResourceEntity $diskspace = $diskspace * 1024; $traffic = $traffic * 1024 * 1024; - if (((($this->getUserDetail('diskspace_used') + $diskspace - $result['diskspace']) > $this->getUserDetail('diskspace')) && ($this->getUserDetail('diskspace') / 1024) != '-1') || ((($this->getUserDetail('mysqls_used') + $mysqls - $result['mysqls']) > $this->getUserDetail('mysqls')) && $this->getUserDetail('mysqls') != '-1') || ((($this->getUserDetail('emails_used') + $emails - $result['emails']) > $this->getUserDetail('emails')) && $this->getUserDetail('emails') != '-1') || ((($this->getUserDetail('email_accounts_used') + $email_accounts - $result['email_accounts']) > $this->getUserDetail('email_accounts')) && $this->getUserDetail('email_accounts') != '-1') || ((($this->getUserDetail('email_forwarders_used') + $email_forwarders - $result['email_forwarders']) > $this->getUserDetail('email_forwarders')) && $this->getUserDetail('email_forwarders') != '-1') || ((($this->getUserDetail('email_quota_used') + $email_quota - $result['email_quota']) > $this->getUserDetail('email_quota')) && $this->getUserDetail('email_quota') != '-1' && Settings::Get('system.mail_quota_enabled') == '1') || ((($this->getUserDetail('ftps_used') + $ftps - $result['ftps']) > $this->getUserDetail('ftps')) && $this->getUserDetail('ftps') != '-1') || ((($this->getUserDetail('tickets_used') + $tickets - $result['tickets']) > $this->getUserDetail('tickets')) && $this->getUserDetail('tickets') != '-1') || ((($this->getUserDetail('subdomains_used') + $subdomains - $result['subdomains']) > $this->getUserDetail('subdomains')) && $this->getUserDetail('subdomains') != '-1') || (($diskspace / 1024) == '-1' && ($this->getUserDetail('diskspace') / 1024) != '-1') || ($mysqls == '-1' && $this->getUserDetail('mysqls') != '-1') || ($emails == '-1' && $this->getUserDetail('emails') != '-1') || ($email_accounts == '-1' && $this->getUserDetail('email_accounts') != '-1') || ($email_forwarders == '-1' && $this->getUserDetail('email_forwarders') != '-1') || ($email_quota == '-1' && $this->getUserDetail('email_quota') != '-1' && Settings::Get('system.mail_quota_enabled') == '1') || ($ftps == '-1' && $this->getUserDetail('ftps') != '-1') || ($tickets == '-1' && $this->getUserDetail('tickets') != '-1') || ($subdomains == '-1' && $this->getUserDetail('subdomains') != '-1')) { - standard_error('youcantallocatemorethanyouhave', '', true); + if ($this->isAdmin()) { + if (((($this->getUserDetail('diskspace_used') + $diskspace - $result['diskspace']) > $this->getUserDetail('diskspace')) && ($this->getUserDetail('diskspace') / 1024) != '-1') || ((($this->getUserDetail('mysqls_used') + $mysqls - $result['mysqls']) > $this->getUserDetail('mysqls')) && $this->getUserDetail('mysqls') != '-1') || ((($this->getUserDetail('emails_used') + $emails - $result['emails']) > $this->getUserDetail('emails')) && $this->getUserDetail('emails') != '-1') || ((($this->getUserDetail('email_accounts_used') + $email_accounts - $result['email_accounts']) > $this->getUserDetail('email_accounts')) && $this->getUserDetail('email_accounts') != '-1') || ((($this->getUserDetail('email_forwarders_used') + $email_forwarders - $result['email_forwarders']) > $this->getUserDetail('email_forwarders')) && $this->getUserDetail('email_forwarders') != '-1') || ((($this->getUserDetail('email_quota_used') + $email_quota - $result['email_quota']) > $this->getUserDetail('email_quota')) && $this->getUserDetail('email_quota') != '-1' && Settings::Get('system.mail_quota_enabled') == '1') || ((($this->getUserDetail('ftps_used') + $ftps - $result['ftps']) > $this->getUserDetail('ftps')) && $this->getUserDetail('ftps') != '-1') || ((($this->getUserDetail('tickets_used') + $tickets - $result['tickets']) > $this->getUserDetail('tickets')) && $this->getUserDetail('tickets') != '-1') || ((($this->getUserDetail('subdomains_used') + $subdomains - $result['subdomains']) > $this->getUserDetail('subdomains')) && $this->getUserDetail('subdomains') != '-1') || (($diskspace / 1024) == '-1' && ($this->getUserDetail('diskspace') / 1024) != '-1') || ($mysqls == '-1' && $this->getUserDetail('mysqls') != '-1') || ($emails == '-1' && $this->getUserDetail('emails') != '-1') || ($email_accounts == '-1' && $this->getUserDetail('email_accounts') != '-1') || ($email_forwarders == '-1' && $this->getUserDetail('email_forwarders') != '-1') || ($email_quota == '-1' && $this->getUserDetail('email_quota') != '-1' && Settings::Get('system.mail_quota_enabled') == '1') || ($ftps == '-1' && $this->getUserDetail('ftps') != '-1') || ($tickets == '-1' && $this->getUserDetail('tickets') != '-1') || ($subdomains == '-1' && $this->getUserDetail('subdomains') != '-1')) { + standard_error('youcantallocatemorethanyouhave', '', true); + } + + // Either $name and $firstname or the $company must be inserted + if ($name == '' && $company == '') { + standard_error(array( + 'stringisempty', + 'myname' + ), '', true); + } elseif ($firstname == '' && $company == '') { + standard_error(array( + 'stringisempty', + 'myfirstname' + ), '', true); + } elseif ($email == '') { + standard_error(array( + 'stringisempty', + 'emailadd' + ), '', true); + } elseif (! validateEmail($email)) { + standard_error('emailiswrong', $email, true); + } } - // Either $name and $firstname or the $company must be inserted - if ($name == '' && $company == '') { - standard_error(array( - 'stringisempty', - 'myname' - ), '', true); - } elseif ($firstname == '' && $company == '') { - standard_error(array( - 'stringisempty', - 'myfirstname' - ), '', true); - } elseif ($email == '') { - standard_error(array( - 'stringisempty', - 'emailadd' - ), '', true); - } elseif (! validateEmail($email)) { - standard_error('emailiswrong', $email, true); + if ($password != '') { + $password = validatePassword($password, true); + $password = makeCryptPassword($password); } else { + $password = $result['password']; + } + + if ($createstdsubdomain != '1') { + $createstdsubdomain = '0'; + } + + if ($createstdsubdomain == '1' && $result['standardsubdomain'] == '0') { - if ($password != '') { - $password = validatePassword($password, true); - $password = makeCryptPassword($password); + if (Settings::Get('system.stdsubdomain') !== null && Settings::Get('system.stdsubdomain') != '') { + $_stdsubdomain = $result['loginname'] . '.' . Settings::Get('system.stdsubdomain'); } else { - $password = $result['password']; + $_stdsubdomain = $result['loginname'] . '.' . Settings::Get('system.hostname'); } - if ($createstdsubdomain != '1') { - $createstdsubdomain = '0'; + $ins_data = array( + 'domain' => $_stdsubdomain, + 'customerid' => $result['customerid'], + 'adminid' => $this->getUserDetail('adminid'), + 'docroot' => $result['documentroot'], + 'phpenabled' => $phpenabled, + 'openbasedir' => '1' + ); + $domainid = - 1; + try { + $std_domain = Domains::getLocal($this->getUserData(), $ins_data)->add(); + $domainid = json_decode($std_domain, true)['data']['id']; + } catch (Exception $e) { + $this->logger()->logAction(ADM_ACTION, LOG_ERR, "[API] Unable to add standard-subdomain: " . $e->getMessage()); } - if ($createstdsubdomain == '1' && $result['standardsubdomain'] == '0') { - - if (Settings::Get('system.stdsubdomain') !== null && Settings::Get('system.stdsubdomain') != '') { - $_stdsubdomain = $result['loginname'] . '.' . Settings::Get('system.stdsubdomain'); - } else { - $_stdsubdomain = $result['loginname'] . '.' . Settings::Get('system.hostname'); - } - - $ins_data = array( - 'domain' => $_stdsubdomain, - 'customerid' => $result['customerid'], - 'adminid' => $this->getUserDetail('adminid'), - 'docroot' => $result['documentroot'], - 'phpenabled' => $phpenabled, - 'openbasedir' => '1' - ); - $domainid = - 1; - try { - $std_domain = Domains::getLocal($this->getUserData(), $ins_data)->add(); - $domainid = json_decode($std_domain, true)['data']['id']; - } catch (Exception $e) { - $this->logger()->logAction(ADM_ACTION, LOG_ERR, "[API] Unable to add standard-subdomain: " . $e->getMessage()); - } - - if ($domainid > 0) { - $upd_stmt = Database::prepare(" + if ($domainid > 0) { + $upd_stmt = Database::prepare(" UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET `standardsubdomain` = :domainid WHERE `customerid` = :customerid "); - Database::pexecute($upd_stmt, array( - 'domainid' => $domainid, - 'customerid' => $result['customerid'] - ), true, true); - $this->logger()->logAction(ADM_ACTION, LOG_NOTICE, "[API] automatically added standardsubdomain for user '" . $result['loginname'] . "'"); - inserttask('1'); - } - } - - if ($createstdsubdomain == '0' && $result['standardsubdomain'] != '0') { - - try { - $std_domain = Domains::getLocal($this->getUserData(), array( - 'id' => $result['standardsubdomain'], - 'is_stdsubdomain' => 1 - ))->delete(); - } catch (Exception $e) { - $this->logger()->logAction(ADM_ACTION, LOG_ERR, "[API] Unable to delete standard-subdomain: " . $e->getMessage()); - } - $this->logger()->logAction(ADM_ACTION, LOG_NOTICE, "[API] automatically deleted standardsubdomain for user '" . $result['loginname'] . "'"); + Database::pexecute($upd_stmt, array( + 'domainid' => $domainid, + 'customerid' => $result['customerid'] + ), true, true); + $this->logger()->logAction(ADM_ACTION, LOG_NOTICE, "[API] automatically added standardsubdomain for user '" . $result['loginname'] . "'"); inserttask('1'); } + } + + if ($createstdsubdomain == '0' && $result['standardsubdomain'] != '0') { - if ($deactivated != '1') { - $deactivated = '0'; + try { + $std_domain = Domains::getLocal($this->getUserData(), array( + 'id' => $result['standardsubdomain'], + 'is_stdsubdomain' => 1 + ))->delete(); + } catch (Exception $e) { + $this->logger()->logAction(ADM_ACTION, LOG_ERR, "[API] Unable to delete standard-subdomain: " . $e->getMessage()); } + $this->logger()->logAction(ADM_ACTION, LOG_NOTICE, "[API] automatically deleted standardsubdomain for user '" . $result['loginname'] . "'"); + inserttask('1'); + } + + if ($deactivated != '1') { + $deactivated = '0'; + } + + if ($phpenabled != '0') { + $phpenabled = '1'; + } + + if ($perlenabled != '0') { + $perlenabled = '1'; + } + + if ($dnsenabled != '0') { + $dnsenabled = '1'; + } + + if ($phpenabled != $result['phpenabled'] || $perlenabled != $result['perlenabled']) { + inserttask('1'); + } + + // activate/deactivate customer services + if ($deactivated != $result['deactivated']) { - if ($phpenabled != '0') { - $phpenabled = '1'; - } + $yesno = (($deactivated) ? 'N' : 'Y'); + $pop3 = (($deactivated) ? '0' : (int) $result['pop3']); + $imap = (($deactivated) ? '0' : (int) $result['imap']); - if ($perlenabled != '0') { - $perlenabled = '1'; - } - - if ($dnsenabled != '0') { - $dnsenabled = '1'; - } - - if ($phpenabled != $result['phpenabled'] || $perlenabled != $result['perlenabled']) { - inserttask('1'); - } - - // activate/deactivate customer services - if ($deactivated != $result['deactivated']) { - - $yesno = (($deactivated) ? 'N' : 'Y'); - $pop3 = (($deactivated) ? '0' : (int) $result['pop3']); - $imap = (($deactivated) ? '0' : (int) $result['imap']); - - $upd_stmt = Database::prepare(" - UPDATE `" . TABLE_MAIL_USERS . "` SET `postfix`= :yesno, `pop3` = :pop3, `imap` = :imap WHERE `customerid` = :customerid"); - Database::pexecute($upd_stmt, array( - 'yesno' => $yesno, - 'pop3' => $pop3, - 'imap' => $imap, - 'customerid' => $id - )); - - $upd_stmt = Database::prepare(" - UPDATE `" . TABLE_FTP_USERS . "` SET `login_enabled` = :yesno WHERE `customerid` = :customerid"); - Database::pexecute($upd_stmt, array( - 'yesno' => $yesno, - 'customerid' => $id - )); - - $upd_stmt = Database::prepare(" - UPDATE `" . TABLE_PANEL_DOMAINS . "` SET `deactivated`= :deactivated WHERE `customerid` = :customerid"); - Database::pexecute($upd_stmt, array( - 'deactivated' => $deactivated, - 'customerid' => $id - )); - - // Retrieve customer's databases - $databases_stmt = Database::prepare("SELECT * FROM " . TABLE_PANEL_DATABASES . " WHERE customerid = :customerid ORDER BY `dbserver`"); - Database::pexecute($databases_stmt, array( - 'customerid' => $id - )); - - Database::needRoot(true); - $last_dbserver = 0; - - $dbm = new DbManager($this->logger()); - - // For each of them - while ($row_database = $databases_stmt->fetch(PDO::FETCH_ASSOC)) { - - if ($last_dbserver != $row_database['dbserver']) { - $dbm->getManager()->flushPrivileges(); - Database::needRoot(true, $row_database['dbserver']); - $last_dbserver = $row_database['dbserver']; - } - - foreach (array_unique(explode(',', Settings::Get('system.mysql_access_host'))) as $mysql_access_host) { - $mysql_access_host = trim($mysql_access_host); - - // Prevent access, if deactivated - if ($deactivated) { - // failsafe if user has been deleted manually (requires MySQL 4.1.2+) - $dbm->getManager()->disableUser($row_database['databasename'], $mysql_access_host); - } else { - // Otherwise grant access - $dbm->getManager()->enableUser($row_database['databasename'], $mysql_access_host); - } - } - } - - // At last flush the new privileges - $dbm->getManager()->flushPrivileges(); - Database::needRoot(false); - - $this->logger()->logAction(ADM_ACTION, LOG_INFO, "[API] deactivated user '" . $result['loginname'] . "'"); - inserttask('1'); - } - - // Disable or enable POP3 Login for customers Mail Accounts - if ($email_pop3 != $result['pop3']) { - $upd_stmt = Database::prepare("UPDATE `" . TABLE_MAIL_USERS . "` SET `pop3` = :pop3 WHERE `customerid` = :customerid"); - Database::pexecute($upd_stmt, array( - 'pop3' => $email_pop3, - 'customerid' => $id - )); - } - - // Disable or enable IMAP Login for customers Mail Accounts - if ($email_imap != $result['imap']) { - $upd_stmt = Database::prepare("UPDATE `" . TABLE_MAIL_USERS . "` SET `imap` = :imap WHERE `customerid` = :customerid"); - Database::pexecute($upd_stmt, array( - 'imap' => $email_imap, - 'customerid' => $id - )); - } - - $upd_data = array( - 'customerid' => $id, - 'passwd' => $password, - 'name' => $name, - 'firstname' => $firstname, - 'gender' => $gender, - 'company' => $company, - 'street' => $street, - 'zipcode' => $zipcode, - 'city' => $city, - 'phone' => $phone, - 'fax' => $fax, - 'email' => $email, - 'customerno' => $customernumber, - 'lang' => $def_language, - 'diskspace' => $diskspace, - 'traffic' => $traffic, - 'subdomains' => $subdomains, - 'emails' => $emails, - 'email_accounts' => $email_accounts, - 'email_forwarders' => $email_forwarders, - 'email_quota' => $email_quota, - 'ftps' => $ftps, - 'tickets' => $tickets, - 'mysqls' => $mysqls, - 'deactivated' => $deactivated, - 'phpenabled' => $phpenabled, - 'allowed_phpconfigs' => empty($allowed_phpconfigs) ? "" : json_encode($allowed_phpconfigs), - 'imap' => $email_imap, - 'pop3' => $email_pop3, - 'perlenabled' => $perlenabled, - 'dnsenabled' => $dnsenabled, - 'custom_notes' => $custom_notes, - 'custom_notes_show' => $custom_notes_show, - 'theme' => $theme - ); $upd_stmt = Database::prepare(" - UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET - `name` = :name, - `firstname` = :firstname, - `gender` = :gender, - `company` = :company, - `street` = :street, - `zipcode` = :zipcode, - `city` = :city, - `phone` = :phone, - `fax` = :fax, - `email` = :email, - `customernumber` = :customerno, - `def_language` = :lang, - `password` = :passwd, - `diskspace` = :diskspace, - `traffic` = :traffic, - `subdomains` = :subdomains, - `emails` = :emails, - `email_accounts` = :email_accounts, - `email_forwarders` = :email_forwarders, - `ftps` = :ftps, - `tickets` = :tickets, - `mysqls` = :mysqls, - `deactivated` = :deactivated, - `phpenabled` = :phpenabled, - `allowed_phpconfigs` = :allowed_phpconfigs, - `email_quota` = :email_quota, - `imap` = :imap, - `pop3` = :pop3, - `perlenabled` = :perlenabled, - `dnsenabled` = :dnsenabled, - `custom_notes` = :custom_notes, - `custom_notes_show` = :custom_notes_show, - `theme` = :theme - WHERE `customerid` = :customerid + UPDATE `" . TABLE_MAIL_USERS . "` SET `postfix`= :yesno, `pop3` = :pop3, `imap` = :imap WHERE `customerid` = :customerid "); - Database::pexecute($upd_stmt, $upd_data); + Database::pexecute($upd_stmt, array( + 'yesno' => $yesno, + 'pop3' => $pop3, + 'imap' => $imap, + 'customerid' => $id + )); + $upd_stmt = Database::prepare(" + UPDATE `" . TABLE_FTP_USERS . "` SET `login_enabled` = :yesno WHERE `customerid` = :customerid + "); + Database::pexecute($upd_stmt, array( + 'yesno' => $yesno, + 'customerid' => $id + )); + + $upd_stmt = Database::prepare(" + UPDATE `" . TABLE_PANEL_DOMAINS . "` SET `deactivated`= :deactivated WHERE `customerid` = :customerid"); + Database::pexecute($upd_stmt, array( + 'deactivated' => $deactivated, + 'customerid' => $id + )); + + // Retrieve customer's databases + $databases_stmt = Database::prepare("SELECT * FROM " . TABLE_PANEL_DATABASES . " WHERE customerid = :customerid ORDER BY `dbserver`"); + Database::pexecute($databases_stmt, array( + 'customerid' => $id + )); + + Database::needRoot(true); + $last_dbserver = 0; + + $dbm = new DbManager($this->logger()); + + // For each of them + while ($row_database = $databases_stmt->fetch(PDO::FETCH_ASSOC)) { + + if ($last_dbserver != $row_database['dbserver']) { + $dbm->getManager()->flushPrivileges(); + Database::needRoot(true, $row_database['dbserver']); + $last_dbserver = $row_database['dbserver']; + } + + foreach (array_unique(explode(',', Settings::Get('system.mysql_access_host'))) as $mysql_access_host) { + $mysql_access_host = trim($mysql_access_host); + + // Prevent access, if deactivated + if ($deactivated) { + // failsafe if user has been deleted manually (requires MySQL 4.1.2+) + $dbm->getManager()->disableUser($row_database['databasename'], $mysql_access_host); + } else { + // Otherwise grant access + $dbm->getManager()->enableUser($row_database['databasename'], $mysql_access_host); + } + } + } + + // At last flush the new privileges + $dbm->getManager()->flushPrivileges(); + Database::needRoot(false); + + $this->logger()->logAction(ADM_ACTION, LOG_INFO, "[API] deactivated user '" . $result['loginname'] . "'"); + inserttask('1'); + } + + // Disable or enable POP3 Login for customers Mail Accounts + if ($email_pop3 != $result['pop3']) { + $upd_stmt = Database::prepare("UPDATE `" . TABLE_MAIL_USERS . "` SET `pop3` = :pop3 WHERE `customerid` = :customerid"); + Database::pexecute($upd_stmt, array( + 'pop3' => $email_pop3, + 'customerid' => $id + )); + } + + // Disable or enable IMAP Login for customers Mail Accounts + if ($email_imap != $result['imap']) { + $upd_stmt = Database::prepare("UPDATE `" . TABLE_MAIL_USERS . "` SET `imap` = :imap WHERE `customerid` = :customerid"); + Database::pexecute($upd_stmt, array( + 'imap' => $email_imap, + 'customerid' => $id + )); + } + + $upd_data = array( + 'customerid' => $id, + 'passwd' => $password, + 'name' => $name, + 'firstname' => $firstname, + 'gender' => $gender, + 'company' => $company, + 'street' => $street, + 'zipcode' => $zipcode, + 'city' => $city, + 'phone' => $phone, + 'fax' => $fax, + 'email' => $email, + 'customerno' => $customernumber, + 'lang' => $def_language, + 'diskspace' => $diskspace, + 'traffic' => $traffic, + 'subdomains' => $subdomains, + 'emails' => $emails, + 'email_accounts' => $email_accounts, + 'email_forwarders' => $email_forwarders, + 'email_quota' => $email_quota, + 'ftps' => $ftps, + 'tickets' => $tickets, + 'mysqls' => $mysqls, + 'deactivated' => $deactivated, + 'phpenabled' => $phpenabled, + 'allowed_phpconfigs' => empty($allowed_phpconfigs) ? "" : json_encode($allowed_phpconfigs), + 'imap' => $email_imap, + 'pop3' => $email_pop3, + 'perlenabled' => $perlenabled, + 'dnsenabled' => $dnsenabled, + 'custom_notes' => $custom_notes, + 'custom_notes_show' => $custom_notes_show, + 'theme' => $theme + ); + $upd_stmt = Database::prepare(" + UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET + `name` = :name, + `firstname` = :firstname, + `gender` = :gender, + `company` = :company, + `street` = :street, + `zipcode` = :zipcode, + `city` = :city, + `phone` = :phone, + `fax` = :fax, + `email` = :email, + `customernumber` = :customerno, + `def_language` = :lang, + `password` = :passwd, + `diskspace` = :diskspace, + `traffic` = :traffic, + `subdomains` = :subdomains, + `emails` = :emails, + `email_accounts` = :email_accounts, + `email_forwarders` = :email_forwarders, + `ftps` = :ftps, + `tickets` = :tickets, + `mysqls` = :mysqls, + `deactivated` = :deactivated, + `phpenabled` = :phpenabled, + `allowed_phpconfigs` = :allowed_phpconfigs, + `email_quota` = :email_quota, + `imap` = :imap, + `pop3` = :pop3, + `perlenabled` = :perlenabled, + `dnsenabled` = :dnsenabled, + `custom_notes` = :custom_notes, + `custom_notes_show` = :custom_notes_show, + `theme` = :theme + WHERE `customerid` = :customerid + "); + Database::pexecute($upd_stmt, $upd_data); + + if ($this->isAdmin()) { // Using filesystem - quota, insert a task which cleans the filesystem - quota inserttask('10'); @@ -1147,20 +1199,21 @@ class Customers extends ApiCommand implements ResourceEntity $admin_update_query .= " WHERE `adminid` = '" . (int) $result['adminid'] . "'"; Database::query($admin_update_query); - $this->logger()->logAction(ADM_ACTION, LOG_INFO, "[API] edited user '" . $result['loginname'] . "'"); - - /* - * move customer to another admin/reseller; #1166 - */ - if ($move_to_admin > 0 && $move_to_admin != $result['adminid']) { - $move_result = moveCustomerToAdmin($id, $move_to_admin); - if ($move_result != true) { - standard_error('moveofcustomerfailed', $move_result, true); - } - } - - return $this->response(200, "successfull", $upd_data); } + + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_INFO, "[API] edited user '" . $result['loginname'] . "'"); + + /* + * move customer to another admin/reseller; #1166 + */ + if ($move_to_admin > 0 && $move_to_admin != $result['adminid']) { + $move_result = moveCustomerToAdmin($id, $move_to_admin); + if ($move_result != true) { + standard_error('moveofcustomerfailed', $move_result, true); + } + } + + return $this->response(200, "successfull", $upd_data); } throw new Exception("Not allowed to execute given command.", 403); } From 87912a9e072b360f9df2066753b9572ac01398c4 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sat, 24 Feb 2018 16:06:53 +0100 Subject: [PATCH 0480/1335] refactored moveCustomerToAdmin() function to Customers.move ApiCommand Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/commands/class.Customers.php | 80 ++++++++++++++++++- lib/classes/api/commands/class.Domains.php | 2 +- .../froxlor/function.moveCustomerToAdmin.php | 62 -------------- 3 files changed, 79 insertions(+), 65 deletions(-) delete mode 100644 lib/functions/froxlor/function.moveCustomerToAdmin.php diff --git a/lib/classes/api/commands/class.Customers.php b/lib/classes/api/commands/class.Customers.php index 4bea264a..d3a762e4 100644 --- a/lib/classes/api/commands/class.Customers.php +++ b/lib/classes/api/commands/class.Customers.php @@ -1207,12 +1207,16 @@ class Customers extends ApiCommand implements ResourceEntity * move customer to another admin/reseller; #1166 */ if ($move_to_admin > 0 && $move_to_admin != $result['adminid']) { - $move_result = moveCustomerToAdmin($id, $move_to_admin); + $json_result = Customers::getLocal($this->getUserData(), array( + 'id' => $result['customerid'], + 'adminid' => $move_to_admin + ))->move(); + $move_result = json_decode($json_result, true)['data']; if ($move_result != true) { standard_error('moveofcustomerfailed', $move_result, true); } } - + return $this->response(200, "successfull", $upd_data); } throw new Exception("Not allowed to execute given command.", 403); @@ -1510,4 +1514,76 @@ class Customers extends ApiCommand implements ResourceEntity } throw new Exception("Not allowed to execute given command.", 403); } + + /** + * Function to move a given customer to a given admin/reseller + * and update all its references accordingly + * + * @param int $id + * customer-id + * @param int $adminid + * target-admin-id + * + * @access admin + * @throws Exception + * @return bool true on success, error-message on failure + */ + public function move() + { + if ($this->isAdmin() && $this->getUserDetail('change_serversettings') == 1) { + $id = $this->getParam('id'); + $adminid = $this->getParam('adminid'); + + // get customer + $json_result = Admins::getLocal($this->getUserData(), array( + 'id' => $id + ))->get(); + $c_result = json_decode($json_result, true)['data']; + + // check if target-admin is the current admin + if ($adminid == $c_result['adminid']) { + throw new Exception("Cannot move customer to the same admin/reseller as he currently is assigned to", 406); + } + + // get target admin + $json_result = Customers::getLocal($this->getUserData(), array( + 'id' => $adminid + ))->get(); + $a_result = json_decode($json_result, true)['data']; + + // Update customer entry + $updCustomer_stmt = Database::prepare(" + UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET `adminid` = :adminid WHERE `customerid` = :cid + "); + Database::pexecute($updCustomer_stmt, array( + 'adminid' => $adminid, + 'cid' => $id + ), true, true); + + // Update customer-domains + $updDomains_stmt = Database::prepare(" + UPDATE `" . TABLE_PANEL_DOMAINS . "` SET `adminid` = :adminid WHERE `customerid` = :cid + "); + Database::pexecute($updDomains_stmt, array( + 'adminid' => $adminid, + 'cid' => $id + ), true, true); + + // Update customer-tickets + $updTickets_stmt = Database::prepare(" + UPDATE `" . TABLE_PANEL_TICKETS . "` SET `adminid` = :adminid WHERE `customerid` = :cid + "); + Database::pexecute($updTickets_stmt, array( + 'adminid' => $adminid, + 'cid' => $id + ), true, true); + + // now, recalculate the resource-usage for the old and the new admin + updateCounters(false); + + $log->logAction(ADM_ACTION, LOG_INFO, "[API] moved user '" . $c_result['loginname'] . "' from admin/reseller '" . $c_result['adminname'] . " to admin/reseller '" . $a_result['loginname'] . "'"); + return $this->response(200, "successfull", true); + } + throw new Exception("Not allowed to execute given command.", 403); + } } diff --git a/lib/classes/api/commands/class.Domains.php b/lib/classes/api/commands/class.Domains.php index b363c19e..1e135c2c 100644 --- a/lib/classes/api/commands/class.Domains.php +++ b/lib/classes/api/commands/class.Domains.php @@ -61,7 +61,7 @@ class Domains extends ApiCommand implements ResourceEntity * optional, the domain-id * @param string $domainname * optional, the domainname - * @param boolean $no_std_subdomain + * @param bool $no_std_subdomain * optional, default false * * @access admin diff --git a/lib/functions/froxlor/function.moveCustomerToAdmin.php b/lib/functions/froxlor/function.moveCustomerToAdmin.php deleted file mode 100644 index 0c2018bc..00000000 --- a/lib/functions/froxlor/function.moveCustomerToAdmin.php +++ /dev/null @@ -1,62 +0,0 @@ - $id - ) ); - - $log->logAction(ADM_ACTION, LOG_INFO, "moved user #" . $id . " from admin/reseller #".$cAdmin['adminid']." to admin/reseller #".$adminid); - - // Update customer entry - $updCustomer_stmt = Database::prepare ( " - UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET `adminid` = :adminid WHERE `customerid` = :cid - " ); - Database::pexecute ( $updCustomer_stmt, array ( - 'adminid' => $adminid, - 'cid' => $id - ) ); - - // Update customer-domains - $updDomains_stmt = Database::prepare ( " - UPDATE `" . TABLE_PANEL_DOMAINS . "` SET `adminid` = :adminid WHERE `customerid` = :cid - " ); - Database::pexecute ( $updDomains_stmt, array ( - 'adminid' => $adminid, - 'cid' => $id - ) ); - - // Update customer-tickets - $updTickets_stmt = Database::prepare ( " - UPDATE `" . TABLE_PANEL_TICKETS . "` SET `adminid` = :adminid WHERE `customerid` = :cid - " ); - Database::pexecute ( $updTickets_stmt, array ( - 'adminid' => $adminid, - 'cid' => $id - ) ); - - // now, recalculate the resource-usage for the old and the new admin - updateCounters ( false ); - - return true; -} From bda652f9477b419eca47003e7449405dc5b31030 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sat, 24 Feb 2018 16:26:47 +0100 Subject: [PATCH 0481/1335] tiny fixes in Domains.add and Domains.update Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/commands/class.Domains.php | 33 ++++++++-------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/lib/classes/api/commands/class.Domains.php b/lib/classes/api/commands/class.Domains.php index 1e135c2c..ad879bb6 100644 --- a/lib/classes/api/commands/class.Domains.php +++ b/lib/classes/api/commands/class.Domains.php @@ -181,18 +181,12 @@ class Domains extends ApiCommand implements ResourceEntity 'mydomain' ), '', true); } - - $customer_stmt = Database::prepare(" - SELECT * FROM `" . TABLE_PANEL_CUSTOMERS . "` - WHERE `customerid` = :customerid " . ($this->getUserDetail('customers_see_all') ? '' : " AND `adminid` = :adminid")); - $params = array( - 'customerid' => $customerid - ); - if ($this->getUserDetail('customers_see_all') == '0') { - $params['adminid'] = $this->getUserDetail('adminid'); - } - $customer = Database::pexecute_first($customer_stmt, $params, true, true); - + + $json_result = Customers::getLocal($this->getUserData(), array( + 'id' => $customerid + ))->get(); + $customer = json_decode($json_result, true)['data']; + if (empty($customer) || $customer['customerid'] != $customerid) { standard_error('customerdoesntexist', '', true); } @@ -859,14 +853,9 @@ class Domains extends ApiCommand implements ResourceEntity if ($this->getUserDetail('customers_see_all') == '0') { $params['adminid'] = $this->getUserDetail('adminid'); } - - // get domains customer - $json_result = Customers::getLocal($this->getUserData(), array( - 'id' => $result['customerid'] - ))->get(); - $customer = json_decode($json_result, true)['data']; - - if (empty($customer) || $customer['customerid'] != $customerid) { + + $result = Database::pexecute_first($customer_stmt, $params, true, true); + if (empty($result) || $result['customerid'] != $customerid) { standard_error('customerdoesntexist', '', true); } } else { @@ -943,9 +932,9 @@ class Domains extends ApiCommand implements ResourceEntity // If path is empty and 'Use domain name as default value for DocumentRoot path' is enabled in settings, // set default path to subdomain or domain name if (Settings::Get('system.documentroot_use_default_value') == 1) { - $documentroot = makeCorrectDir($customer['documentroot'] . '/' . $result['domain']); + $documentroot = makeCorrectDir($result['documentroot'] . '/' . $result['domain']); } else { - $documentroot = $customer['documentroot']; + $documentroot = $result['documentroot']; } } From 20aac1ccd4b8f2cc86e6cd294f004de84c9cc7db Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sat, 24 Feb 2018 16:46:56 +0100 Subject: [PATCH 0482/1335] finish Mysqls.update, untested Signed-off-by: Michael Kaufmann (d00p) --- admin_admins.php | 1 - customer_mysql.php | 44 +----- lib/classes/api/commands/class.Mysqls.php | 164 +++++++++++++++++++--- 3 files changed, 145 insertions(+), 64 deletions(-) diff --git a/admin_admins.php b/admin_admins.php index 1404aa6d..49784818 100644 --- a/admin_admins.php +++ b/admin_admins.php @@ -229,7 +229,6 @@ if ($page == 'admins' } elseif($action == 'edit' && $id != 0 ) { - try { $json_result = Admins::getLocal($userinfo, array( 'id' => $id diff --git a/customer_mysql.php b/customer_mysql.php index 9e1b0b8e..5407b172 100644 --- a/customer_mysql.php +++ b/customer_mysql.php @@ -172,48 +172,12 @@ if ($page == 'overview') { $result = json_decode($json_result, true)['data']; if (isset($result['databasename']) && $result['databasename'] != '') { - if (!isset($sql_root[$result['dbserver']]) || !is_array($sql_root[$result['dbserver']])) { - $result['dbserver'] = 0; - } - if (isset($_POST['send']) && $_POST['send'] == 'send') { - // Only change Password if it is set, do nothing if it is empty! -- PH 2004-11-29 - $password = validate($_POST['mysql_password'], 'password'); - if ($password != '') { - // validate password - $password = validatePassword($password); - - if ($password == $result['databasename']) { - standard_error('passwordshouldnotbeusername'); - } - - // Begin root-session - Database::needRoot(true); - foreach (array_map('trim', explode(',', Settings::Get('system.mysql_access_host'))) as $mysql_access_host) { - $stmt = Database::prepare("SET PASSWORD FOR :dbname@:host = PASSWORD(:password)"); - $params = array( - "dbname" => $result['databasename'], - "host" => $mysql_access_host, - "password" => $password - ); - Database::pexecute($stmt, $params); - } - - $stmt = Database::prepare("FLUSH PRIVILEGES"); - Database::pexecute($stmt); - Database::needRoot(false); - // End root-session + try { + $json_result = Mysqls::getLocal($userinfo, $_POST)->update(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); } - - // Update the Database description -- PH 2004-11-29 - $log->logAction(USR_ACTION, LOG_INFO, "edited database '" . $result['databasename'] . "'"); - $databasedescription = validate($_POST['description'], 'description'); - $stmt = Database::prepare("UPDATE `" . TABLE_PANEL_DATABASES . "` - SET `description` = :desc - WHERE `customerid` = :customerid - AND `id` = :id" - ); - Database::pexecute($stmt, array("desc" => $databasedescription, "customerid" => $userinfo['customerid'], "id" => $id)); redirectTo($filename, array('page' => $page, 's' => $s)); } else { diff --git a/lib/classes/api/commands/class.Mysqls.php b/lib/classes/api/commands/class.Mysqls.php index eed6b999..71558a1e 100644 --- a/lib/classes/api/commands/class.Mysqls.php +++ b/lib/classes/api/commands/class.Mysqls.php @@ -31,7 +31,7 @@ class Mysqls extends ApiCommand implements ResourceEntity * optional, send created resource-information to customer, default: false * @param int $customer_id * required when called as admin, not needed when called as customer - * + * * @access admin, customer * @throws Exception * @return array @@ -39,20 +39,20 @@ class Mysqls extends ApiCommand implements ResourceEntity public function add() { if ($this->getUserDetail('mysqls_used') < $this->getUserDetail('mysqls') || $this->getUserDetail('mysqls') == '-1') { - + // required paramters $password = $this->getParam('mysql_password'); - + // parameters $dbserver = $this->getParam('mysql_server', true, 0); $databasedescription = $this->getParam('description', true, ''); $sendinfomail = $this->getParam('sendinfomail', true, 0); - + // validation $password = validate($password, 'password', '', '', array(), true); $password = validatePassword($password, true); $databasedescription = validate(trim($databasedescription), 'description', '', '', array(), true); - + // validate whether the dbserver exists $dbserver = validate($dbserver, html_entity_decode($this->lng['mysql']['mysql_server']), '', '', 0, true); Database::needRoot(true, $dbserver); @@ -62,18 +62,18 @@ class Mysqls extends ApiCommand implements ResourceEntity if (! isset($sql_root) || ! is_array($sql_root)) { throw new ErrorException("Database server with index #" . $dbserver . " is unknown", 404); } - + if ($password == '') { standard_error(array( 'stringisempty', 'mysql_password' ), '', true); } - + if ($sendinfomail != 1) { $sendinfomail = 0; } - + // get needed customer info to reduce the mysql-usage-counter by one if ($this->isAdmin()) { // get customer id @@ -89,7 +89,7 @@ class Mysqls extends ApiCommand implements ResourceEntity } else { $customer_id = $this->getUserDetail('customer_id'); } - + $newdb_params = array( 'loginname' => ($this->isAdmin() ? $customer['loginname'] : $this->getUserDetail('loginname')), 'mysql_lastaccountnumber' => ($this->isAdmin() ? $customer['mysql_lastaccountnumber'] : $this->getUserDetail('mysql_lastaccountnumber')) @@ -97,12 +97,12 @@ class Mysqls extends ApiCommand implements ResourceEntity // create database, user, set permissions, etc.pp. $dbm = new DbManager($this->logger()); $username = $dbm->createDatabase($newdb_params['loginname'], $password, $newdb_params['mysql_lastaccountnumber']); - + // we've checked against the password in dbm->createDatabase if ($username == false) { standard_error('passwordshouldnotbeusername', '', true); } - + // add database info to froxlor $stmt = Database::prepare(" INSERT INTO `" . TABLE_PANEL_DATABASES . "` @@ -121,7 +121,7 @@ class Mysqls extends ApiCommand implements ResourceEntity Database::pexecute($stmt, $params, true, true); $databaseid = Database::lastInsertId(); $params['id'] = $databaseid; - + // update customer usage $stmt = Database::prepare(" UPDATE `" . TABLE_PANEL_CUSTOMERS . "` @@ -131,7 +131,7 @@ class Mysqls extends ApiCommand implements ResourceEntity Database::pexecute($stmt, array( "customerid" => ($this->isAdmin() ? $customer['customerid'] : $this->getUserDetail('customerid')) ), true, true); - + // update admin usage $stmt = Database::prepare(" UPDATE `" . TABLE_PANEL_ADMINS . "` @@ -141,20 +141,20 @@ class Mysqls extends ApiCommand implements ResourceEntity Database::pexecute($stmt, array( "adminid" => $this->getUserDetail('adminid') ), true, true); - + // send info-mail? if ($sendinfomail == 1) { $pma = $this->lng['admin']['notgiven']; if (Settings::Get('panel.phpmyadmin_url') != '') { $pma = Settings::Get('panel.phpmyadmin_url'); } - + Database::needRoot(true, $dbserver); Database::needSqlData(); $sql_root = Database::getSqlData(); Database::needRoot(false); $userinfo = ($this->isAdmin() ? $customer : $this->getUserData()); - + $replace_arr = array( 'SALUTATION' => getCorrectUserSalutation($userinfo), 'CUST_NAME' => getCorrectUserSalutation($userinfo), // < keep this for compatibility @@ -164,7 +164,7 @@ class Mysqls extends ApiCommand implements ResourceEntity 'DB_SRV' => $sql_root['host'], 'PMA_URI' => $pma ); - + $def_language = $userinfo['def_language']; $result_stmt = Database::prepare(" SELECT `value` FROM `" . TABLE_PANEL_TEMPLATES . "` @@ -178,7 +178,7 @@ class Mysqls extends ApiCommand implements ResourceEntity "lang" => $def_language ), true, true); $mail_subject = html_entity_decode(replace_variables((($result['value'] != '') ? $result['value'] : $this->lng['mails']['new_database_by_customer']['subject']), $replace_arr)); - + $result_stmt = Database::prepare(" SELECT `value` FROM `" . TABLE_PANEL_TEMPLATES . "` WHERE `adminid`= :adminid @@ -191,7 +191,7 @@ class Mysqls extends ApiCommand implements ResourceEntity "lang" => $def_language )); $mail_body = html_entity_decode(replace_variables((($result['value'] != '') ? $result['value'] : $this->lng['mails']['new_database_by_customer']['mailbody']), $replace_arr)); - + $_mailerror = false; try { $this->mail->Subject = $mail_subject; @@ -206,12 +206,12 @@ class Mysqls extends ApiCommand implements ResourceEntity $mailerr_msg = $e->getMessage(); $_mailerror = true; } - + if ($_mailerror) { $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_ERR, "[API] Error sending mail: " . $mailerr_msg); standard_error('errorsendingmail', $userinfo['email'], true); } - + $this->mail->ClearAddresses(); } $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_WARNING, "[API] added mysql-database '" . $username . "'"); @@ -319,8 +319,126 @@ class Mysqls extends ApiCommand implements ResourceEntity throw new Exception("MySQL database with " . $key . " could not be found", 404); } + /** + * update a mysql database entry by either id or dbname + * + * @param int $id + * optional, the database-id + * @param string $dbname + * optional, the databasename + * @param int $dbserver + * optional, specify database-server, default is none + * @param string $mysql_password + * optional, update password for the database + * @param string $description + * optional, description for database + * + * @access admin, customer + * @throws Exception + * @return array + */ public function update() - {} + { + $id = $this->getParam('id', true, 0); + $dn_optional = ($id <= 0 ? false : true); + $dbname = $this->getParam('dbname', $dn_optional, ''); + $dbserver = $this->getParam('dbserver', true, - 1); + + if ($id <= 0 && empty($dbname)) { + throw new Exception("Either 'id' or 'dbname' parameter must be given", 406); + } + + if ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'mysql')) { + throw new Exception("You cannot access this resource", 405); + } + + $json_result = Mysqls::getLocal($this->getUserData(), array( + 'id' => $id, + 'dbname' => $dbname, + 'dbserver' => $dbserver + ))->get(); + $result = json_decode($json_result, true)['data']; + $id = $result['id']; + + // paramters + $password = $this->getParam('mysql_password', true, ''); + $databasedescription = $this->getParam('description', true, ''); + + // validation + $password = validate($password, 'password', '', '', array(), true); + $password = validatePassword($password, true); + $databasedescription = validate(trim($databasedescription), 'description', '', '', array(), true); + + // validate whether the dbserver exists + $dbserver = validate($dbserver, html_entity_decode($this->lng['mysql']['mysql_server']), '', '', 0, true); + Database::needRoot(true, $dbserver); + Database::needSqlData(); + $sql_root = Database::getSqlData(); + Database::needRoot(false); + if (! isset($sql_root) || ! is_array($sql_root)) { + throw new ErrorException("Database server with index #" . $dbserver . " is unknown", 404); + } + + if ($sendinfomail != 1) { + $sendinfomail = 0; + } + + // get needed customer info to reduce the mysql-usage-counter by one + if ($this->isAdmin()) { + // get customer id + $customer_id = $this->getParam('customer_id'); + $json_result = Customers::getLocal($this->getUserData(), array( + 'id' => $result['customerid'] + ))->get(); + $customer = json_decode($json_result, true)['data']; + // check whether the customer has enough resources to get the database added + if ($customer['mysqls_used'] >= $customer['mysqls'] && $customer['mysqls'] != '-1') { + throw new Exception("Customer has no more resources available", 406); + } + } else { + $customer_id = $this->getUserDetail('customer_id'); + } + + if ($password != '') { + // validate password + $password = validatePassword($password, true); + + if ($password == $result['databasename']) { + standard_error('passwordshouldnotbeusername', '', true); + } + + // Begin root-session + Database::needRoot(true, $result['dbserver']); + foreach (array_map('trim', explode(',', Settings::Get('system.mysql_access_host'))) as $mysql_access_host) { + $stmt = Database::prepare("SET PASSWORD FOR :dbname@:host = PASSWORD(:password)"); + $params = array( + "dbname" => $result['databasename'], + "host" => $mysql_access_host, + "password" => $password + ); + Database::pexecute($stmt, $params, true, true); + } + + $stmt = Database::prepare("FLUSH PRIVILEGES"); + Database::pexecute($stmt, null, true, true); + Database::needRoot(false); + // End root-session + } + $stmt = Database::prepare(" + UPDATE `" . TABLE_PANEL_DATABASES . "` + SET `description` = :desc + WHERE `customerid` = :customerid + AND `id` = :id + "); + $params = array( + "desc" => $databasedescription, + "customerid" => ($this->isAdmin() ? $customer['customerid'] : $this->getUserDetail('customerid')), + "id" => $id + ); + Database::pexecute($stmt, $params, true, true); + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_WARNING, "[API] updated mysql-database '" . $result['databasename'] . "'"); + return $this->response(200, "successfull", $params); + } /** * list all databases, if called from an admin, list all databases of all customers you are allowed to view, or specify id or loginname for one specific customer @@ -444,7 +562,7 @@ class Mysqls extends ApiCommand implements ResourceEntity if ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'mysql')) { throw new Exception("You cannot access this resource", 405); } - + $json_result = Mysqls::getLocal($this->getUserData(), array( 'id' => $id, 'dbname' => $dbname, From e5a1b504d7675c060e2fd283d14acdfbf0b2f073 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sat, 24 Feb 2018 16:52:14 +0100 Subject: [PATCH 0483/1335] consistency replace dbserver with mysql_server everywhere Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/commands/class.Mysqls.php | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/classes/api/commands/class.Mysqls.php b/lib/classes/api/commands/class.Mysqls.php index 71558a1e..77d8d28f 100644 --- a/lib/classes/api/commands/class.Mysqls.php +++ b/lib/classes/api/commands/class.Mysqls.php @@ -227,7 +227,7 @@ class Mysqls extends ApiCommand implements ResourceEntity * optional, the database-id * @param string $dbname * optional, the databasename - * @param int $dbserver + * @param int $mysql_server * optional, specify database-server, default is none * * @access admin, customer @@ -239,7 +239,7 @@ class Mysqls extends ApiCommand implements ResourceEntity $id = $this->getParam('id', true, 0); $dn_optional = ($id <= 0 ? false : true); $dbname = $this->getParam('dbname', $dn_optional, ''); - $dbserver = $this->getParam('dbserver', true, - 1); + $dbserver = $this->getParam('mysql_server', true, - 1); if ($id <= 0 && empty($dbname)) { throw new Exception("Either 'id' or 'dbname' parameter must be given", 406); @@ -326,7 +326,7 @@ class Mysqls extends ApiCommand implements ResourceEntity * optional, the database-id * @param string $dbname * optional, the databasename - * @param int $dbserver + * @param int $mysql_server * optional, specify database-server, default is none * @param string $mysql_password * optional, update password for the database @@ -342,7 +342,7 @@ class Mysqls extends ApiCommand implements ResourceEntity $id = $this->getParam('id', true, 0); $dn_optional = ($id <= 0 ? false : true); $dbname = $this->getParam('dbname', $dn_optional, ''); - $dbserver = $this->getParam('dbserver', true, - 1); + $dbserver = $this->getParam('mysql_server', true, - 1); if ($id <= 0 && empty($dbname)) { throw new Exception("Either 'id' or 'dbname' parameter must be given", 406); @@ -355,7 +355,7 @@ class Mysqls extends ApiCommand implements ResourceEntity $json_result = Mysqls::getLocal($this->getUserData(), array( 'id' => $id, 'dbname' => $dbname, - 'dbserver' => $dbserver + 'mysql_server' => $dbserver ))->get(); $result = json_decode($json_result, true)['data']; $id = $result['id']; @@ -443,7 +443,7 @@ class Mysqls extends ApiCommand implements ResourceEntity /** * list all databases, if called from an admin, list all databases of all customers you are allowed to view, or specify id or loginname for one specific customer * - * @param int $dbserver + * @param int $mysql_server * optional, specify dbserver to select from, else use all available * @param int $customerid * optional, admin-only, select dbs of a specific customer by id @@ -456,7 +456,7 @@ class Mysqls extends ApiCommand implements ResourceEntity public function list() { $result = array(); - $dbserver = $this->getParam('dbserver', true, - 1); + $dbserver = $this->getParam('mysql_server', true, - 1); if ($this->isAdmin()) { // if we're an admin, list all databases of all the admins customers // or optionally for one specific customer identified by id or loginname @@ -541,7 +541,7 @@ class Mysqls extends ApiCommand implements ResourceEntity * optional, the database-id * @param string $dbname * optional, the databasename - * @param int $dbserver + * @param int $mysql_server * optional, specify database-server, default is none * * @access admin, customer @@ -553,7 +553,7 @@ class Mysqls extends ApiCommand implements ResourceEntity $id = $this->getParam('id', true, 0); $dn_optional = ($id <= 0 ? false : true); $dbname = $this->getParam('dbname', $dn_optional, ''); - $dbserver = $this->getParam('dbserver', true, - 1); + $dbserver = $this->getParam('mysql_server', true, - 1); if ($id <= 0 && empty($dbname)) { throw new Exception("Either 'id' or 'dbname' parameter must be given", 406); @@ -566,7 +566,7 @@ class Mysqls extends ApiCommand implements ResourceEntity $json_result = Mysqls::getLocal($this->getUserData(), array( 'id' => $id, 'dbname' => $dbname, - 'dbserver' => $dbserver + 'mysql_server' => $dbserver ))->get(); $result = json_decode($json_result, true)['data']; $id = $result['id']; From ca07621de77389b62adbe04b894df2aa99d0d99f Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sat, 24 Feb 2018 20:12:57 +0100 Subject: [PATCH 0484/1335] reduce mysql-usage counter for admins too when deleting a mysql-database Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/commands/class.Mysqls.php | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/lib/classes/api/commands/class.Mysqls.php b/lib/classes/api/commands/class.Mysqls.php index 77d8d28f..7b72a65c 100644 --- a/lib/classes/api/commands/class.Mysqls.php +++ b/lib/classes/api/commands/class.Mysqls.php @@ -378,11 +378,7 @@ class Mysqls extends ApiCommand implements ResourceEntity if (! isset($sql_root) || ! is_array($sql_root)) { throw new ErrorException("Database server with index #" . $dbserver . " is unknown", 404); } - - if ($sendinfomail != 1) { - $sendinfomail = 0; - } - + // get needed customer info to reduce the mysql-usage-counter by one if ($this->isAdmin()) { // get customer id @@ -436,6 +432,7 @@ class Mysqls extends ApiCommand implements ResourceEntity "id" => $id ); Database::pexecute($stmt, $params, true, true); + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_WARNING, "[API] updated mysql-database '" . $result['databasename'] . "'"); return $this->response(200, "successfull", $params); } @@ -607,6 +604,16 @@ class Mysqls extends ApiCommand implements ResourceEntity Database::pexecute($stmt, array( "customerid" => $customer_id ), true, true); + // update admin usage + $stmt = Database::prepare(" + UPDATE `" . TABLE_PANEL_ADMINS . "` + SET `mysqls_used` = `mysqls_used` - 1 + WHERE `adminid` = :adminid + "); + Database::pexecute($stmt, array( + "adminid" => ($this->isAdmin() ? $customer['adminid'] : $this->getUserDetail('adminid')), + ), true, true); + return $this->response(200, "successfull", $result); } } From 2f30d85d325486cb04825c56ee94af6c63e6db37 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sat, 24 Feb 2018 20:52:21 +0100 Subject: [PATCH 0485/1335] minor changes in ApiCommand; added Ftps.get ApiCommand Signed-off-by: Michael Kaufmann (d00p) --- api.php | 2 +- customer_ftp.php | 28 ++++--- lib/classes/api/abstract.ApiCommand.php | 9 ++- lib/classes/api/commands/class.Ftps.php | 98 +++++++++++++++++++++++++ 4 files changed, 121 insertions(+), 16 deletions(-) create mode 100644 lib/classes/api/commands/class.Ftps.php diff --git a/api.php b/api.php index a5dd6fe3..6dc25ff3 100644 --- a/api.php +++ b/api.php @@ -56,7 +56,7 @@ function json_response($status, $status_message = '', $data = null) { $resheader = $_SERVER["SERVER_PROTOCOL"] . " " . $status; if (! empty($status_message)) { - $resheader .= ' ' . $status_message; + $resheader .= ' ' . str_replace("\n", " ", $status_message); } header($resheader); diff --git a/customer_ftp.php b/customer_ftp.php index 46cd71f4..e40886d5 100644 --- a/customer_ftp.php +++ b/customer_ftp.php @@ -79,12 +79,14 @@ if ($page == 'overview') { eval("echo \"" . getTemplate('ftp/accounts') . "\";"); } elseif ($action == 'delete' && $id != 0) { - $result_stmt = Database::prepare("SELECT `id`, `username`, `homedir`, `up_count`, `up_bytes`, `down_count`, `down_bytes` FROM `" . TABLE_FTP_USERS . "` - WHERE `customerid` = :customerid - AND `id` = :id" - ); - Database::pexecute($result_stmt, array("customerid" => $userinfo['customerid'], "id" => $id)); - $result = $result_stmt->fetch(PDO::FETCH_ASSOC); + try { + $json_result = Ftps::getLocal($userinfo, array( + 'id' => $id + ))->get(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); + } + $result = json_decode($json_result, true)['data']; if (isset($result['username']) && $result['username'] != $userinfo['loginname']) { if (isset($_POST['send']) && $_POST['send'] == 'send') { @@ -369,12 +371,14 @@ if ($page == 'overview') { } } } elseif ($action == 'edit' && $id != 0) { - $result_stmt = Database::prepare("SELECT `id`, `username`, `description`, `homedir`, `uid`, `gid`, `shell` FROM `" . TABLE_FTP_USERS . "` - WHERE `customerid` = :customerid - AND `id` = :id" - ); - Database::pexecute($result_stmt, array("customerid" => $userinfo['customerid'], "id" => $id)); - $result = $result_stmt->fetch(PDO::FETCH_ASSOC); + try { + $json_result = Ftps::getLocal($userinfo, array( + 'id' => $id + ))->get(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); + } + $result = json_decode($json_result, true)['data']; if (isset($result['username']) && $result['username'] != '') { if (isset($_POST['send']) && $_POST['send'] == 'send') { diff --git a/lib/classes/api/abstract.ApiCommand.php b/lib/classes/api/abstract.ApiCommand.php index ee8f1a90..8ce98e8d 100644 --- a/lib/classes/api/abstract.ApiCommand.php +++ b/lib/classes/api/abstract.ApiCommand.php @@ -402,7 +402,7 @@ abstract class ApiCommand { $resheader = $_SERVER["SERVER_PROTOCOL"] . " " . $status; if (! empty($status_message)) { - $resheader .= ' ' . $status_message; + $resheader .= ' ' . str_replace("\n", " ", $status_message); } header($resheader); @@ -432,14 +432,17 @@ abstract class ApiCommand ), true, true); if ($result) { // admin or customer? - if ($result['customerid'] == 0) { + if ($result['customerid'] == 0 && $result['adminid'] > 0) { $this->is_admin = true; $table = 'panel_admins'; $key = "adminid"; - } else { + } elseif ($result['customerid'] > 0 && $result['adminid'] > 0) { $this->is_admin = false; $table = 'panel_customers'; $key = "customerid"; + } else { + // neither adminid is > 0 nor customerid is > 0 - sorry man, no way + throw new Exception("Invalid API credentials", 400); } $sel_stmt = Database::prepare("SELECT * FROM `" . $table . "` WHERE `" . $key . "` = :id"); $this->user_data = Database::pexecute_first($sel_stmt, array( diff --git a/lib/classes/api/commands/class.Ftps.php b/lib/classes/api/commands/class.Ftps.php new file mode 100644 index 00000000..6b27c3fb --- /dev/null +++ b/lib/classes/api/commands/class.Ftps.php @@ -0,0 +1,98 @@ + (2010-) + * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt + * @package API + * @since 0.10.0 + * + */ +class Ftps extends ApiCommand implements ResourceEntity +{ + + public function add() + {} + + /** + * return a ftp-user entry by either id or username + * + * @param int $id + * optional, the customer-id + * @param string $username + * optional, the username + * + * @access admin, customer + * @throws Exception + * @return array + */ + public function get() + { + $id = $this->getParam('id', true, 0); + $un_optional = ($id <= 0 ? false : true); + $username = $this->getParam('username', $un_optional, ''); + + if ($id <= 0 && empty($username)) { + throw new Exception("Either 'id' or 'username' parameter must be given", 406); + } + + $params = array(); + if ($this->isAdmin()) { + if ($this->getUserDetail('customers_see_all') != 1) { + // if it's a reseller or an admin who cannot see all customers, we need to check + // whether the database belongs to one of his customers + $json_result = Customers::getLocal($this->getUserData())->list(); + $custom_list_result = json_decode($json_result, true)['data']['list']; + $customer_ids = array(); + foreach ($custom_list_result as $customer) { + $customer_ids[] = $customer['customerid']; + } + $result_stmt = Database::prepare(" + SELECT * FROM `" . TABLE_FTP_USERS . "` + WHERE `customerid` IN (:customerid) + AND (`id` = :idun OR `username` = :idun) + "); + $params['customerid'] = implode(", ", $customer_ids); + } else { + $result_stmt = Database::prepare(" + SELECT * FROM `" . TABLE_FTP_USERS . "` + WHERE (`id` = :idun OR `username` = :idun) + "); + } + } else { + if (Settings::IsInList('panel.customer_hide_options', 'ftp')) { + throw new Exception("You cannot access this resource", 405); + } + $result_stmt = Database::prepare(" + SELECT * FROM `" . TABLE_FTP_USERS . "` + WHERE `customerid` = :customerid + AND (`id` = :idun OR `username` = :idun) + "); + $params['customerid'] = $this->getUserDetail('customerid'); + } + $params['idun'] = ($id <= 0 ? $username : $id); + $result = Database::pexecute_first($result_stmt, $params, true, true); + if ($result) { + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_NOTICE, "[API] get ftp-user '" . $result['username'] . "'"); + return $this->response(200, "successfull", $result); + } + $key = ($id > 0 ? "id #" . $id : "username '" . $username . "'"); + throw new Exception("FTP user with " . $key . " could not be found", 404); + } + + public function update() + {} + + public function list() + {} + + public function delete() + {} +} From 243b68cc371a8d4fa83dff58e0724336f40795ff Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sun, 25 Feb 2018 11:47:28 +0100 Subject: [PATCH 0486/1335] minor changes for testing Signed-off-by: Michael Kaufmann (d00p) --- api.php | 10 ++++++---- lib/classes/api/abstract.ApiCommand.php | 16 +++++++++------- lib/classes/api/commands/class.Ftps.php | 2 +- lib/functions.php | 5 +++++ 4 files changed, 21 insertions(+), 12 deletions(-) diff --git a/api.php b/api.php index 6dc25ff3..db419d30 100644 --- a/api.php +++ b/api.php @@ -54,11 +54,13 @@ exit(); */ function json_response($status, $status_message = '', $data = null) { - $resheader = $_SERVER["SERVER_PROTOCOL"] . " " . $status; - if (! empty($status_message)) { - $resheader .= ' ' . str_replace("\n", " ", $status_message); + if (isset($_SERVER["SERVER_PROTOCOL"]) && ! empty($_SERVER["SERVER_PROTOCOL"])) { + $resheader = $_SERVER["SERVER_PROTOCOL"] . " " . $status; + if (! empty($status_message)) { + $resheader .= ' ' . str_replace("\n", " ", $status_message); + } + header($resheader); } - header($resheader); $response['status'] = $status; $response['status_message'] = $status_message; diff --git a/lib/classes/api/abstract.ApiCommand.php b/lib/classes/api/abstract.ApiCommand.php index 8ce98e8d..82fd38c8 100644 --- a/lib/classes/api/abstract.ApiCommand.php +++ b/lib/classes/api/abstract.ApiCommand.php @@ -116,7 +116,7 @@ abstract class ApiCommand throw new Exception("Invalid user data", 500); } $this->logger = FroxlorLogger::getInstanceOf($this->user_data); - + // check whether the user is deactivated if ($this->getUserDetail('deactivated') == 1) { $this->logger()->logAction(LOG_ERROR, LOG_INFO, "[API] User '" . $this->getUserDetail('loginnname') . "' tried to use API but is deactivated"); @@ -161,13 +161,13 @@ abstract class ApiCommand // include every english language file we can get foreach ($langs['English'] as $key => $value) { - include_once makeSecurePath($value['file']); + include_once makeSecurePath(FROXLOR_INSTALL_DIR . '/' . $value['file']); } // now include the selected language if its not english if ($language != 'English') { foreach ($langs[$language] as $key => $value) { - include_once makeSecurePath($value['file']); + include_once makeSecurePath(FROXLOR_INSTALL_DIR . '/' . $value['file']); } } @@ -400,11 +400,13 @@ abstract class ApiCommand */ protected function response($status, $status_message, $data = null) { - $resheader = $_SERVER["SERVER_PROTOCOL"] . " " . $status; - if (! empty($status_message)) { - $resheader .= ' ' . str_replace("\n", " ", $status_message); + if (isset($_SERVER["SERVER_PROTOCOL"]) && ! empty($_SERVER["SERVER_PROTOCOL"])) { + $resheader = $_SERVER["SERVER_PROTOCOL"] . " " . $status; + if (! empty($status_message)) { + $resheader .= ' ' . str_replace("\n", " ", $status_message); + } + header($resheader); } - header($resheader); $response['status'] = $status; $response['status_message'] = $status_message; diff --git a/lib/classes/api/commands/class.Ftps.php b/lib/classes/api/commands/class.Ftps.php index 6b27c3fb..352302e3 100644 --- a/lib/classes/api/commands/class.Ftps.php +++ b/lib/classes/api/commands/class.Ftps.php @@ -45,7 +45,7 @@ class Ftps extends ApiCommand implements ResourceEntity $params = array(); if ($this->isAdmin()) { - if ($this->getUserDetail('customers_see_all') != 1) { + if ($this->getUserDetail('customers_see_all') == false) { // if it's a reseller or an admin who cannot see all customers, we need to check // whether the database belongs to one of his customers $json_result = Customers::getLocal($this->getUserData())->list(); diff --git a/lib/functions.php b/lib/functions.php index ebfd5bb6..5a15cac8 100644 --- a/lib/functions.php +++ b/lib/functions.php @@ -100,6 +100,11 @@ class Autoloader { return true; } + // don't load anything from a namespace, it's not our responsibility + if (strpos($class, "\\") !== false) { + return true; + } + // now iterate through the paths foreach ($paths as $path) { // valid directory? From a222114d0a41de43f80a76e9573e1a14fd40d4f4 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sun, 25 Feb 2018 15:02:58 +0100 Subject: [PATCH 0487/1335] remove unnecessary parameter-checks as they will never happen; make Customers.update callable for customers Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/commands/class.Admins.php | 28 +- lib/classes/api/commands/class.Customers.php | 954 +++++++++--------- lib/classes/api/commands/class.Domains.php | 24 +- lib/classes/api/commands/class.Ftps.php | 8 +- lib/classes/api/commands/class.Mysqls.php | 24 +- .../output/function.standard_error.php | 2 +- 6 files changed, 488 insertions(+), 552 deletions(-) diff --git a/lib/classes/api/commands/class.Admins.php b/lib/classes/api/commands/class.Admins.php index a37a894c..6c0de7ae 100644 --- a/lib/classes/api/commands/class.Admins.php +++ b/lib/classes/api/commands/class.Admins.php @@ -63,12 +63,8 @@ class Admins extends ApiCommand implements ResourceEntity { $id = $this->getParam('id', true, 0); $ln_optional = ($id <= 0 ? false : true); - $loginname = $this->getParam('loginname', $ln_optional, ''); - - if ($id <= 0 && empty($loginname)) { - throw new Exception("Either 'id' or 'loginname' parameter must be given", 406); - } - + $loginname = trim($this->getParam('loginname', $ln_optional, '')); + if ($this->isAdmin() && ($this->getUserDetail('change_serversettings') == 1 || ($this->getUserDetail('adminid') == $id || $this->getUserDetail('loginname') == $loginname))) { $result_stmt = Database::prepare(" SELECT * FROM `" . TABLE_PANEL_ADMINS . "` @@ -318,11 +314,7 @@ class Admins extends ApiCommand implements ResourceEntity $id = $this->getParam('id', true, 0); $ln_optional = ($id <= 0 ? false : true); - $loginname = $this->getParam('loginname', $ln_optional, ''); - - if ($id <= 0 && empty($loginname)) { - throw new Exception("Either 'id' or 'loginname' parameter must be given", 406); - } + $loginname = trim($this->getParam('loginname', $ln_optional, '')); $json_result = Admins::getLocal($this->getUserData(), array( 'id' => $id, @@ -583,11 +575,7 @@ class Admins extends ApiCommand implements ResourceEntity if ($this->isAdmin() && $this->getUserDetail('change_serversettings') == 1) { $id = $this->getParam('id', true, 0); $ln_optional = ($id <= 0 ? false : true); - $loginname = $this->getParam('loginname', $ln_optional, ''); - - if ($id <= 0 && empty($loginname)) { - throw new Exception("Either 'id' or 'loginname' parameter must be given", 406); - } + $loginname = trim($this->getParam('loginname', $ln_optional, '')); $json_result = Admins::getLocal($this->getUserData(), array( 'id' => $id, @@ -652,12 +640,8 @@ class Admins extends ApiCommand implements ResourceEntity if ($this->isAdmin() && $this->getUserDetail('change_serversettings') == 1) { $id = $this->getParam('id', true, 0); $ln_optional = ($id <= 0 ? false : true); - $loginname = $this->getParam('loginname', $ln_optional, ''); - - if ($id <= 0 && empty($loginname)) { - throw new Exception("Either 'id' or 'loginname' parameter must be given", 406); - } - + $loginname = trim($this->getParam('loginname', $ln_optional, '')); + $json_result = Admins::getLocal($this->getUserData(), array( 'id' => $id, 'loginname' => $loginname diff --git a/lib/classes/api/commands/class.Customers.php b/lib/classes/api/commands/class.Customers.php index d3a762e4..e827d32a 100644 --- a/lib/classes/api/commands/class.Customers.php +++ b/lib/classes/api/commands/class.Customers.php @@ -70,11 +70,7 @@ class Customers extends ApiCommand implements ResourceEntity { $id = $this->getParam('id', true, 0); $ln_optional = ($id <= 0 ? false : true); - $loginname = $this->getParam('loginname', $ln_optional, ''); - - if ($id <= 0 && empty($loginname)) { - throw new Exception("Either 'id' or 'loginname' parameter must be given", 406); - } + $loginname = trim($this->getParam('loginname', $ln_optional, '')); if ($this->isAdmin()) { $result_stmt = Database::prepare(" @@ -131,7 +127,7 @@ class Customers extends ApiCommand implements ResourceEntity $phone = $this->getParam('phone', true, ''); $fax = $this->getParam('fax', true, ''); $customernumber = $this->getParam('customernumber', true, ''); - $def_language = $this->getParam('def_language', true, ''); + $def_language = $this->getParam('def_language', true, Settings::Get('panel.standardlanguage')); $gender = intval_ressource($this->getParam('gender', true, 0)); $custom_notes = $this->getParam('custom_notes', true, ''); $custom_notes_show = $this->getParam('custom_notes_show', true, 0); @@ -681,379 +677,373 @@ class Customers extends ApiCommand implements ResourceEntity */ public function update() { + $id = $this->getParam('id', true, 0); + $ln_optional = ($id <= 0 ? false : true); + $loginname = trim($this->getParam('loginname', $ln_optional, '')); + + $json_result = Customers::getLocal($this->getUserData(), array( + 'id' => $id, + 'loginname' => $loginname + ))->get(); + $result = json_decode($json_result, true)['data']; + $id = $result['customerid']; + if ($this->isAdmin()) { - $id = $this->getParam('id', true, 0); - $ln_optional = ($id <= 0 ? false : true); - $loginname = $this->getParam('loginname', $ln_optional, ''); + // parameters + $move_to_admin = intval_ressource($this->getParam('move_to_admin', true, 0)); - if ($id <= 0 && empty($loginname)) { - throw new Exception("Either 'id' or 'loginname' parameter must be given", 406); - } - - $json_result = Customers::getLocal($this->getUserData(), array( - 'id' => $id, - 'loginname' => $loginname - ))->get(); - $result = json_decode($json_result, true)['data']; - $id = $result['customerid']; - - if ($this->isAdmin()) { - // parameters - $move_to_admin = intval_ressource($this->getParam('move_to_admin', true, 0)); - - $idna_convert = new idna_convert_wrapper(); - $email = $this->getParam('email', true, $idna_convert->decode($result['email'])); - $name = $this->getParam('name', true, $result['name']); - $firstname = $this->getParam('firstname', true, $result['firstname']); - $company = $this->getParam('company', true, $result['company']); - $street = $this->getParam('street', true, $result['street']); - $zipcode = $this->getParam('zipcode', true, $result['zipcode']); - $city = $this->getParam('city', true, $result['city']); - $phone = $this->getParam('phone', true, $result['phone']); - $fax = $this->getParam('fax', true, $result['fax']); - $customernumber = $this->getParam('customernumber', true, $result['customernumber']); - $def_language = $this->getParam('def_language', true, $result['def_language']); - $gender = intval_ressource($this->getParam('gender', true, $result['gender'])); - $custom_notes = $this->getParam('custom_notes', true, $result['custom_notes']); - $custom_notes_show = $this->getParam('custom_notes_show', true, $result['custom_notes_show']); - - $dec_places = Settings::Get('panel.decimal_places'); - $diskspace = $this->getUlParam('diskspace', 'diskspace_ul', true, round($result['diskspace'] / 1024, $dec_places)); - $traffic = $this->getUlParam('traffic', 'traffic_ul', true, round($result['traffic'] / (1024 * 1024), $dec_places)); - $subdomains = $this->getUlParam('subdomains', 'subdomains_ul', true, $result['subdomains']); - $emails = $this->getUlParam('emails', 'emails_ul', true, $result['emails']); - $email_accounts = $this->getUlParam('email_accounts', 'email_accounts_ul', true, $result['email_accounts']); - $email_forwarders = $this->getUlParam('email_forwarders', 'email_forwarders_ul', true, $result['email_forwarders']); - $email_quota = $this->getUlParam('email_quota', 'email_quota_ul', true, $result['email_quota']); - $email_imap = $this->getParam('email_imap', true, $result['imap']); - $email_pop3 = $this->getParam('email_pop3', true, $result['pop3']); - $ftps = $this->getUlParam('ftps', 'ftps_ul', true, $result['ftps']); - $tickets = $this->getUlParam('tickets', 'tickets_ul', true, $result['tickets']); - $mysqls = $this->getUlParam('mysqls', 'mysqls_ul', true, $result['mysqls']); - $createstdsubdomain = $this->getParam('createstdsubdomain', true, 0); - $password = $this->getParam('new_customer_password', true, ''); - $sendpassword = $this->getParam('sendpassword', true, 0); - $phpenabled = $this->getParam('phpenabled', true, $result['phpenabled']); - $allowed_phpconfigs = $this->getParam('allowed_phpconfigs', true, json_decode($result['allowed_phpconfigs'], true)); - $perlenabled = $this->getParam('perlenabled', true, $result['perlenabled']); - $dnsenabled = $this->getParam('dnsenabled', true, $result['dnsenabled']); - $deactivated = $this->getParam('deactivated', true, $result['deactivated']); - $theme = $this->getParam('theme', true, $result['theme']); - } else { - // allowed parameters - $def_language = $this->getParam('def_language', true, $result['def_language']); - $password = $this->getParam('new_customer_password', true, ''); - $theme = $this->getParam('theme', true, $result['theme']); - - // unchangeable parameters for customers - $move_to_admin = 0; - $idna_convert = new idna_convert_wrapper(); - $email = $idna_convert->decode($result['email']); - $name = $result['name']; - $firstname = $result['firstname']; - $company = $result['company']; - $street = $result['street']; - $zipcode = $result['zipcode']; - $city = $result['city']; - $phone = $result['phone']; - $fax = $result['fax']; - $customernumber = $result['customernumber']; - $gender = $result['gender']; - $custom_notes = $result['custom_notes']; - $custom_notes_show = $result['custom_notes_show']; - - $dec_places = Settings::Get('panel.decimal_places'); - $diskspace = round($result['diskspace'] / 1024, $dec_places); - $traffic = round($result['traffic'] / (1024 * 1024), $dec_places); - $subdomains = $result['subdomains']; - $emails = $result['emails']; - $email_accounts = $result['email_accounts']; - $email_forwarders = $result['email_forwarders']; - $email_quota = $result['email_quota']; - $email_imap = $result['imap']; - $email_pop3 = $result['pop3']; - $ftps = $result['ftps']; - $tickets = $result['tickets']; - $mysqls = $result['mysqls']; - // if we got one, it's true so none will be generated (it exists already) - // if not, none will be generated - $createstdsubdomain = ($result['standardsubdomain'] > 0 ? 1 : 0); - $sendpassword = 0; - $phpenabled = $result['phpenabled']; - $allowed_phpconfigs = json_decode($result['allowed_phpconfigs'], true); - $perlenabled = $result['perlenabled']; - $dnsenabled = $result['dnsenabled']; - $deactivated = $result['deactivated']; - } - - // validation $idna_convert = new idna_convert_wrapper(); - $name = validate($name, 'name', '', '', array(), true); - $firstname = validate($firstname, 'first name', '', '', array(), true); - $company = validate($company, 'company', '', '', array(), true); - $street = validate($street, 'street', '', '', array(), true); - $zipcode = validate($zipcode, 'zipcode', '/^[0-9 \-A-Z]*$/', '', array(), true); - $city = validate($city, 'city', '', '', array(), true); - $phone = validate($phone, 'phone', '/^[0-9\- \+\(\)\/]*$/', '', array(), true); - $fax = validate($fax, 'fax', '/^[0-9\- \+\(\)\/]*$/', '', array(), true); - $email = $idna_convert->encode(validate($email, 'email', '', '', array(), true)); - $customernumber = validate($customernumber, 'customer number', '/^[A-Za-z0-9 \-]*$/Di', '', array(), true); - $def_language = validate($def_language, 'default language', '', '', array(), true); - $custom_notes = validate(str_replace("\r\n", "\n", $custom_notes), 'custom_notes', '/^[^\0]*$/', '', array(), true); - $theme = validate($theme, 'theme', '', '', array(), true); + $email = $this->getParam('email', true, $idna_convert->decode($result['email'])); + $name = $this->getParam('name', true, $result['name']); + $firstname = $this->getParam('firstname', true, $result['firstname']); + $company = $this->getParam('company', true, $result['company']); + $street = $this->getParam('street', true, $result['street']); + $zipcode = $this->getParam('zipcode', true, $result['zipcode']); + $city = $this->getParam('city', true, $result['city']); + $phone = $this->getParam('phone', true, $result['phone']); + $fax = $this->getParam('fax', true, $result['fax']); + $customernumber = $this->getParam('customernumber', true, $result['customernumber']); + $def_language = $this->getParam('def_language', true, $result['def_language']); + $gender = intval_ressource($this->getParam('gender', true, $result['gender'])); + $custom_notes = $this->getParam('custom_notes', true, $result['custom_notes']); + $custom_notes_show = $this->getParam('custom_notes_show', true, $result['custom_notes_show']); - if (Settings::Get('system.mail_quota_enabled') != '1') { - $email_quota = - 1; + $dec_places = Settings::Get('panel.decimal_places'); + $diskspace = $this->getUlParam('diskspace', 'diskspace_ul', true, round($result['diskspace'] / 1024, $dec_places)); + $traffic = $this->getUlParam('traffic', 'traffic_ul', true, round($result['traffic'] / (1024 * 1024), $dec_places)); + $subdomains = $this->getUlParam('subdomains', 'subdomains_ul', true, $result['subdomains']); + $emails = $this->getUlParam('emails', 'emails_ul', true, $result['emails']); + $email_accounts = $this->getUlParam('email_accounts', 'email_accounts_ul', true, $result['email_accounts']); + $email_forwarders = $this->getUlParam('email_forwarders', 'email_forwarders_ul', true, $result['email_forwarders']); + $email_quota = $this->getUlParam('email_quota', 'email_quota_ul', true, $result['email_quota']); + $email_imap = $this->getParam('email_imap', true, $result['imap']); + $email_pop3 = $this->getParam('email_pop3', true, $result['pop3']); + $ftps = $this->getUlParam('ftps', 'ftps_ul', true, $result['ftps']); + $tickets = $this->getUlParam('tickets', 'tickets_ul', true, $result['tickets']); + $mysqls = $this->getUlParam('mysqls', 'mysqls_ul', true, $result['mysqls']); + $createstdsubdomain = $this->getParam('createstdsubdomain', true, 0); + $password = $this->getParam('new_customer_password', true, ''); + $sendpassword = $this->getParam('sendpassword', true, 0); + $phpenabled = $this->getParam('phpenabled', true, $result['phpenabled']); + $allowed_phpconfigs = $this->getParam('allowed_phpconfigs', true, json_decode($result['allowed_phpconfigs'], true)); + $perlenabled = $this->getParam('perlenabled', true, $result['perlenabled']); + $dnsenabled = $this->getParam('dnsenabled', true, $result['dnsenabled']); + $deactivated = $this->getParam('deactivated', true, $result['deactivated']); + $theme = $this->getParam('theme', true, $result['theme']); + } else { + // allowed parameters + $def_language = $this->getParam('def_language', true, $result['def_language']); + $password = $this->getParam('new_customer_password', true, ''); + $theme = $this->getParam('theme', true, $result['theme']); + + // unchangeable parameters for customers + $move_to_admin = 0; + $idna_convert = new idna_convert_wrapper(); + $email = $idna_convert->decode($result['email']); + $name = $result['name']; + $firstname = $result['firstname']; + $company = $result['company']; + $street = $result['street']; + $zipcode = $result['zipcode']; + $city = $result['city']; + $phone = $result['phone']; + $fax = $result['fax']; + $customernumber = $result['customernumber']; + $gender = $result['gender']; + $custom_notes = $result['custom_notes']; + $custom_notes_show = $result['custom_notes_show']; + + $dec_places = Settings::Get('panel.decimal_places'); + $diskspace = round($result['diskspace'] / 1024, $dec_places); + $traffic = round($result['traffic'] / (1024 * 1024), $dec_places); + $subdomains = $result['subdomains']; + $emails = $result['emails']; + $email_accounts = $result['email_accounts']; + $email_forwarders = $result['email_forwarders']; + $email_quota = $result['email_quota']; + $email_imap = $result['imap']; + $email_pop3 = $result['pop3']; + $ftps = $result['ftps']; + $tickets = $result['tickets']; + $mysqls = $result['mysqls']; + // if we got one, it's true so none will be generated (it exists already) + // if not, none will be generated + $createstdsubdomain = ($result['standardsubdomain'] > 0 ? 1 : 0); + $sendpassword = 0; + $phpenabled = $result['phpenabled']; + $allowed_phpconfigs = json_decode($result['allowed_phpconfigs'], true); + $perlenabled = $result['perlenabled']; + $dnsenabled = $result['dnsenabled']; + $deactivated = $result['deactivated']; + } + + // validation + $idna_convert = new idna_convert_wrapper(); + $name = validate($name, 'name', '', '', array(), true); + $firstname = validate($firstname, 'first name', '', '', array(), true); + $company = validate($company, 'company', '', '', array(), true); + $street = validate($street, 'street', '', '', array(), true); + $zipcode = validate($zipcode, 'zipcode', '/^[0-9 \-A-Z]*$/', '', array(), true); + $city = validate($city, 'city', '', '', array(), true); + $phone = validate($phone, 'phone', '/^[0-9\- \+\(\)\/]*$/', '', array(), true); + $fax = validate($fax, 'fax', '/^[0-9\- \+\(\)\/]*$/', '', array(), true); + $email = $idna_convert->encode(validate($email, 'email', '', '', array(), true)); + $customernumber = validate($customernumber, 'customer number', '/^[A-Za-z0-9 \-]*$/Di', '', array(), true); + $def_language = validate($def_language, 'default language', '', '', array(), true); + $custom_notes = validate(str_replace("\r\n", "\n", $custom_notes), 'custom_notes', '/^[^\0]*$/', '', array(), true); + $theme = validate($theme, 'theme', '', '', array(), true); + + if (Settings::Get('system.mail_quota_enabled') != '1') { + $email_quota = - 1; + } + + if (Settings::Get('ticket.enabled') != '1') { + $tickets = - 1; + } + + if (empty($theme)) { + $theme = Settings::Get('panel.default_theme'); + } + + $diskspace = $diskspace * 1024; + $traffic = $traffic * 1024 * 1024; + + if ($this->isAdmin()) { + if (((($this->getUserDetail('diskspace_used') + $diskspace - $result['diskspace']) > $this->getUserDetail('diskspace')) && ($this->getUserDetail('diskspace') / 1024) != '-1') || ((($this->getUserDetail('mysqls_used') + $mysqls - $result['mysqls']) > $this->getUserDetail('mysqls')) && $this->getUserDetail('mysqls') != '-1') || ((($this->getUserDetail('emails_used') + $emails - $result['emails']) > $this->getUserDetail('emails')) && $this->getUserDetail('emails') != '-1') || ((($this->getUserDetail('email_accounts_used') + $email_accounts - $result['email_accounts']) > $this->getUserDetail('email_accounts')) && $this->getUserDetail('email_accounts') != '-1') || ((($this->getUserDetail('email_forwarders_used') + $email_forwarders - $result['email_forwarders']) > $this->getUserDetail('email_forwarders')) && $this->getUserDetail('email_forwarders') != '-1') || ((($this->getUserDetail('email_quota_used') + $email_quota - $result['email_quota']) > $this->getUserDetail('email_quota')) && $this->getUserDetail('email_quota') != '-1' && Settings::Get('system.mail_quota_enabled') == '1') || ((($this->getUserDetail('ftps_used') + $ftps - $result['ftps']) > $this->getUserDetail('ftps')) && $this->getUserDetail('ftps') != '-1') || ((($this->getUserDetail('tickets_used') + $tickets - $result['tickets']) > $this->getUserDetail('tickets')) && $this->getUserDetail('tickets') != '-1') || ((($this->getUserDetail('subdomains_used') + $subdomains - $result['subdomains']) > $this->getUserDetail('subdomains')) && $this->getUserDetail('subdomains') != '-1') || (($diskspace / 1024) == '-1' && ($this->getUserDetail('diskspace') / 1024) != '-1') || ($mysqls == '-1' && $this->getUserDetail('mysqls') != '-1') || ($emails == '-1' && $this->getUserDetail('emails') != '-1') || ($email_accounts == '-1' && $this->getUserDetail('email_accounts') != '-1') || ($email_forwarders == '-1' && $this->getUserDetail('email_forwarders') != '-1') || ($email_quota == '-1' && $this->getUserDetail('email_quota') != '-1' && Settings::Get('system.mail_quota_enabled') == '1') || ($ftps == '-1' && $this->getUserDetail('ftps') != '-1') || ($tickets == '-1' && $this->getUserDetail('tickets') != '-1') || ($subdomains == '-1' && $this->getUserDetail('subdomains') != '-1')) { + standard_error('youcantallocatemorethanyouhave', '', true); } - if (Settings::Get('ticket.enabled') != '1') { - $tickets = - 1; + // Either $name and $firstname or the $company must be inserted + if ($name == '' && $company == '') { + standard_error(array( + 'stringisempty', + 'myname' + ), '', true); + } elseif ($firstname == '' && $company == '') { + standard_error(array( + 'stringisempty', + 'myfirstname' + ), '', true); + } elseif ($email == '') { + standard_error(array( + 'stringisempty', + 'emailadd' + ), '', true); + } elseif (! validateEmail($email)) { + standard_error('emailiswrong', $email, true); } + } + + if ($password != '') { + $password = validatePassword($password, true); + $password = makeCryptPassword($password); + } else { + $password = $result['password']; + } + + if ($createstdsubdomain != '1') { + $createstdsubdomain = '0'; + } + + if ($createstdsubdomain == '1' && $result['standardsubdomain'] == '0') { - if (empty($theme)) { - $theme = Settings::Get('panel.default_theme'); - } - - $diskspace = $diskspace * 1024; - $traffic = $traffic * 1024 * 1024; - - if ($this->isAdmin()) { - if (((($this->getUserDetail('diskspace_used') + $diskspace - $result['diskspace']) > $this->getUserDetail('diskspace')) && ($this->getUserDetail('diskspace') / 1024) != '-1') || ((($this->getUserDetail('mysqls_used') + $mysqls - $result['mysqls']) > $this->getUserDetail('mysqls')) && $this->getUserDetail('mysqls') != '-1') || ((($this->getUserDetail('emails_used') + $emails - $result['emails']) > $this->getUserDetail('emails')) && $this->getUserDetail('emails') != '-1') || ((($this->getUserDetail('email_accounts_used') + $email_accounts - $result['email_accounts']) > $this->getUserDetail('email_accounts')) && $this->getUserDetail('email_accounts') != '-1') || ((($this->getUserDetail('email_forwarders_used') + $email_forwarders - $result['email_forwarders']) > $this->getUserDetail('email_forwarders')) && $this->getUserDetail('email_forwarders') != '-1') || ((($this->getUserDetail('email_quota_used') + $email_quota - $result['email_quota']) > $this->getUserDetail('email_quota')) && $this->getUserDetail('email_quota') != '-1' && Settings::Get('system.mail_quota_enabled') == '1') || ((($this->getUserDetail('ftps_used') + $ftps - $result['ftps']) > $this->getUserDetail('ftps')) && $this->getUserDetail('ftps') != '-1') || ((($this->getUserDetail('tickets_used') + $tickets - $result['tickets']) > $this->getUserDetail('tickets')) && $this->getUserDetail('tickets') != '-1') || ((($this->getUserDetail('subdomains_used') + $subdomains - $result['subdomains']) > $this->getUserDetail('subdomains')) && $this->getUserDetail('subdomains') != '-1') || (($diskspace / 1024) == '-1' && ($this->getUserDetail('diskspace') / 1024) != '-1') || ($mysqls == '-1' && $this->getUserDetail('mysqls') != '-1') || ($emails == '-1' && $this->getUserDetail('emails') != '-1') || ($email_accounts == '-1' && $this->getUserDetail('email_accounts') != '-1') || ($email_forwarders == '-1' && $this->getUserDetail('email_forwarders') != '-1') || ($email_quota == '-1' && $this->getUserDetail('email_quota') != '-1' && Settings::Get('system.mail_quota_enabled') == '1') || ($ftps == '-1' && $this->getUserDetail('ftps') != '-1') || ($tickets == '-1' && $this->getUserDetail('tickets') != '-1') || ($subdomains == '-1' && $this->getUserDetail('subdomains') != '-1')) { - standard_error('youcantallocatemorethanyouhave', '', true); - } - - // Either $name and $firstname or the $company must be inserted - if ($name == '' && $company == '') { - standard_error(array( - 'stringisempty', - 'myname' - ), '', true); - } elseif ($firstname == '' && $company == '') { - standard_error(array( - 'stringisempty', - 'myfirstname' - ), '', true); - } elseif ($email == '') { - standard_error(array( - 'stringisempty', - 'emailadd' - ), '', true); - } elseif (! validateEmail($email)) { - standard_error('emailiswrong', $email, true); - } - } - - if ($password != '') { - $password = validatePassword($password, true); - $password = makeCryptPassword($password); + if (Settings::Get('system.stdsubdomain') !== null && Settings::Get('system.stdsubdomain') != '') { + $_stdsubdomain = $result['loginname'] . '.' . Settings::Get('system.stdsubdomain'); } else { - $password = $result['password']; + $_stdsubdomain = $result['loginname'] . '.' . Settings::Get('system.hostname'); } - if ($createstdsubdomain != '1') { - $createstdsubdomain = '0'; + $ins_data = array( + 'domain' => $_stdsubdomain, + 'customerid' => $result['customerid'], + 'adminid' => $this->getUserDetail('adminid'), + 'docroot' => $result['documentroot'], + 'phpenabled' => $phpenabled, + 'openbasedir' => '1' + ); + $domainid = - 1; + try { + $std_domain = Domains::getLocal($this->getUserData(), $ins_data)->add(); + $domainid = json_decode($std_domain, true)['data']['id']; + } catch (Exception $e) { + $this->logger()->logAction(ADM_ACTION, LOG_ERR, "[API] Unable to add standard-subdomain: " . $e->getMessage()); } - if ($createstdsubdomain == '1' && $result['standardsubdomain'] == '0') { - - if (Settings::Get('system.stdsubdomain') !== null && Settings::Get('system.stdsubdomain') != '') { - $_stdsubdomain = $result['loginname'] . '.' . Settings::Get('system.stdsubdomain'); - } else { - $_stdsubdomain = $result['loginname'] . '.' . Settings::Get('system.hostname'); - } - - $ins_data = array( - 'domain' => $_stdsubdomain, - 'customerid' => $result['customerid'], - 'adminid' => $this->getUserDetail('adminid'), - 'docroot' => $result['documentroot'], - 'phpenabled' => $phpenabled, - 'openbasedir' => '1' - ); - $domainid = - 1; - try { - $std_domain = Domains::getLocal($this->getUserData(), $ins_data)->add(); - $domainid = json_decode($std_domain, true)['data']['id']; - } catch (Exception $e) { - $this->logger()->logAction(ADM_ACTION, LOG_ERR, "[API] Unable to add standard-subdomain: " . $e->getMessage()); - } - - if ($domainid > 0) { - $upd_stmt = Database::prepare(" + if ($domainid > 0) { + $upd_stmt = Database::prepare(" UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET `standardsubdomain` = :domainid WHERE `customerid` = :customerid "); - Database::pexecute($upd_stmt, array( - 'domainid' => $domainid, - 'customerid' => $result['customerid'] - ), true, true); - $this->logger()->logAction(ADM_ACTION, LOG_NOTICE, "[API] automatically added standardsubdomain for user '" . $result['loginname'] . "'"); - inserttask('1'); - } - } - - if ($createstdsubdomain == '0' && $result['standardsubdomain'] != '0') { - - try { - $std_domain = Domains::getLocal($this->getUserData(), array( - 'id' => $result['standardsubdomain'], - 'is_stdsubdomain' => 1 - ))->delete(); - } catch (Exception $e) { - $this->logger()->logAction(ADM_ACTION, LOG_ERR, "[API] Unable to delete standard-subdomain: " . $e->getMessage()); - } - $this->logger()->logAction(ADM_ACTION, LOG_NOTICE, "[API] automatically deleted standardsubdomain for user '" . $result['loginname'] . "'"); + Database::pexecute($upd_stmt, array( + 'domainid' => $domainid, + 'customerid' => $result['customerid'] + ), true, true); + $this->logger()->logAction(ADM_ACTION, LOG_NOTICE, "[API] automatically added standardsubdomain for user '" . $result['loginname'] . "'"); inserttask('1'); } - - if ($deactivated != '1') { - $deactivated = '0'; + } + + if ($createstdsubdomain == '0' && $result['standardsubdomain'] != '0') { + try { + $std_domain = Domains::getLocal($this->getUserData(), array( + 'id' => $result['standardsubdomain'], + 'is_stdsubdomain' => 1 + ))->delete(); + } catch (Exception $e) { + $this->logger()->logAction(ADM_ACTION, LOG_ERR, "[API] Unable to delete standard-subdomain: " . $e->getMessage()); } + $this->logger()->logAction(ADM_ACTION, LOG_NOTICE, "[API] automatically deleted standardsubdomain for user '" . $result['loginname'] . "'"); + inserttask('1'); + } + + if ($deactivated != '1') { + $deactivated = '0'; + } + + if ($phpenabled != '0') { + $phpenabled = '1'; + } + + if ($perlenabled != '0') { + $perlenabled = '1'; + } + + if ($dnsenabled != '0') { + $dnsenabled = '1'; + } + + if ($phpenabled != $result['phpenabled'] || $perlenabled != $result['perlenabled']) { + inserttask('1'); + } + + // activate/deactivate customer services + if ($deactivated != $result['deactivated']) { - if ($phpenabled != '0') { - $phpenabled = '1'; - } + $yesno = (($deactivated) ? 'N' : 'Y'); + $pop3 = (($deactivated) ? '0' : (int) $result['pop3']); + $imap = (($deactivated) ? '0' : (int) $result['imap']); - if ($perlenabled != '0') { - $perlenabled = '1'; - } - - if ($dnsenabled != '0') { - $dnsenabled = '1'; - } - - if ($phpenabled != $result['phpenabled'] || $perlenabled != $result['perlenabled']) { - inserttask('1'); - } - - // activate/deactivate customer services - if ($deactivated != $result['deactivated']) { - - $yesno = (($deactivated) ? 'N' : 'Y'); - $pop3 = (($deactivated) ? '0' : (int) $result['pop3']); - $imap = (($deactivated) ? '0' : (int) $result['imap']); - - $upd_stmt = Database::prepare(" + $upd_stmt = Database::prepare(" UPDATE `" . TABLE_MAIL_USERS . "` SET `postfix`= :yesno, `pop3` = :pop3, `imap` = :imap WHERE `customerid` = :customerid "); - Database::pexecute($upd_stmt, array( - 'yesno' => $yesno, - 'pop3' => $pop3, - 'imap' => $imap, - 'customerid' => $id - )); - - $upd_stmt = Database::prepare(" + Database::pexecute($upd_stmt, array( + 'yesno' => $yesno, + 'pop3' => $pop3, + 'imap' => $imap, + 'customerid' => $id + )); + + $upd_stmt = Database::prepare(" UPDATE `" . TABLE_FTP_USERS . "` SET `login_enabled` = :yesno WHERE `customerid` = :customerid "); - Database::pexecute($upd_stmt, array( - 'yesno' => $yesno, - 'customerid' => $id - )); - - $upd_stmt = Database::prepare(" + Database::pexecute($upd_stmt, array( + 'yesno' => $yesno, + 'customerid' => $id + )); + + $upd_stmt = Database::prepare(" UPDATE `" . TABLE_PANEL_DOMAINS . "` SET `deactivated`= :deactivated WHERE `customerid` = :customerid"); - Database::pexecute($upd_stmt, array( - 'deactivated' => $deactivated, - 'customerid' => $id - )); - - // Retrieve customer's databases - $databases_stmt = Database::prepare("SELECT * FROM " . TABLE_PANEL_DATABASES . " WHERE customerid = :customerid ORDER BY `dbserver`"); - Database::pexecute($databases_stmt, array( - 'customerid' => $id - )); - - Database::needRoot(true); - $last_dbserver = 0; - - $dbm = new DbManager($this->logger()); - - // For each of them - while ($row_database = $databases_stmt->fetch(PDO::FETCH_ASSOC)) { - - if ($last_dbserver != $row_database['dbserver']) { - $dbm->getManager()->flushPrivileges(); - Database::needRoot(true, $row_database['dbserver']); - $last_dbserver = $row_database['dbserver']; - } - - foreach (array_unique(explode(',', Settings::Get('system.mysql_access_host'))) as $mysql_access_host) { - $mysql_access_host = trim($mysql_access_host); - - // Prevent access, if deactivated - if ($deactivated) { - // failsafe if user has been deleted manually (requires MySQL 4.1.2+) - $dbm->getManager()->disableUser($row_database['databasename'], $mysql_access_host); - } else { - // Otherwise grant access - $dbm->getManager()->enableUser($row_database['databasename'], $mysql_access_host); - } + Database::pexecute($upd_stmt, array( + 'deactivated' => $deactivated, + 'customerid' => $id + )); + + // Retrieve customer's databases + $databases_stmt = Database::prepare("SELECT * FROM " . TABLE_PANEL_DATABASES . " WHERE customerid = :customerid ORDER BY `dbserver`"); + Database::pexecute($databases_stmt, array( + 'customerid' => $id + )); + + Database::needRoot(true); + $last_dbserver = 0; + + $dbm = new DbManager($this->logger()); + + // For each of them + while ($row_database = $databases_stmt->fetch(PDO::FETCH_ASSOC)) { + + if ($last_dbserver != $row_database['dbserver']) { + $dbm->getManager()->flushPrivileges(); + Database::needRoot(true, $row_database['dbserver']); + $last_dbserver = $row_database['dbserver']; + } + + foreach (array_unique(explode(',', Settings::Get('system.mysql_access_host'))) as $mysql_access_host) { + $mysql_access_host = trim($mysql_access_host); + + // Prevent access, if deactivated + if ($deactivated) { + // failsafe if user has been deleted manually (requires MySQL 4.1.2+) + $dbm->getManager()->disableUser($row_database['databasename'], $mysql_access_host); + } else { + // Otherwise grant access + $dbm->getManager()->enableUser($row_database['databasename'], $mysql_access_host); } } - - // At last flush the new privileges - $dbm->getManager()->flushPrivileges(); - Database::needRoot(false); - - $this->logger()->logAction(ADM_ACTION, LOG_INFO, "[API] deactivated user '" . $result['loginname'] . "'"); - inserttask('1'); } + + // At last flush the new privileges + $dbm->getManager()->flushPrivileges(); + Database::needRoot(false); - // Disable or enable POP3 Login for customers Mail Accounts - if ($email_pop3 != $result['pop3']) { - $upd_stmt = Database::prepare("UPDATE `" . TABLE_MAIL_USERS . "` SET `pop3` = :pop3 WHERE `customerid` = :customerid"); - Database::pexecute($upd_stmt, array( - 'pop3' => $email_pop3, - 'customerid' => $id - )); - } - - // Disable or enable IMAP Login for customers Mail Accounts - if ($email_imap != $result['imap']) { - $upd_stmt = Database::prepare("UPDATE `" . TABLE_MAIL_USERS . "` SET `imap` = :imap WHERE `customerid` = :customerid"); - Database::pexecute($upd_stmt, array( - 'imap' => $email_imap, - 'customerid' => $id - )); - } - - $upd_data = array( - 'customerid' => $id, - 'passwd' => $password, - 'name' => $name, - 'firstname' => $firstname, - 'gender' => $gender, - 'company' => $company, - 'street' => $street, - 'zipcode' => $zipcode, - 'city' => $city, - 'phone' => $phone, - 'fax' => $fax, - 'email' => $email, - 'customerno' => $customernumber, - 'lang' => $def_language, - 'diskspace' => $diskspace, - 'traffic' => $traffic, - 'subdomains' => $subdomains, - 'emails' => $emails, - 'email_accounts' => $email_accounts, - 'email_forwarders' => $email_forwarders, - 'email_quota' => $email_quota, - 'ftps' => $ftps, - 'tickets' => $tickets, - 'mysqls' => $mysqls, - 'deactivated' => $deactivated, - 'phpenabled' => $phpenabled, - 'allowed_phpconfigs' => empty($allowed_phpconfigs) ? "" : json_encode($allowed_phpconfigs), - 'imap' => $email_imap, + $this->logger()->logAction(ADM_ACTION, LOG_INFO, "[API] deactivated user '" . $result['loginname'] . "'"); + inserttask('1'); + } + + // Disable or enable POP3 Login for customers Mail Accounts + if ($email_pop3 != $result['pop3']) { + $upd_stmt = Database::prepare("UPDATE `" . TABLE_MAIL_USERS . "` SET `pop3` = :pop3 WHERE `customerid` = :customerid"); + Database::pexecute($upd_stmt, array( 'pop3' => $email_pop3, - 'perlenabled' => $perlenabled, - 'dnsenabled' => $dnsenabled, - 'custom_notes' => $custom_notes, - 'custom_notes_show' => $custom_notes_show, - 'theme' => $theme - ); - $upd_stmt = Database::prepare(" + 'customerid' => $id + )); + } + + // Disable or enable IMAP Login for customers Mail Accounts + if ($email_imap != $result['imap']) { + $upd_stmt = Database::prepare("UPDATE `" . TABLE_MAIL_USERS . "` SET `imap` = :imap WHERE `customerid` = :customerid"); + Database::pexecute($upd_stmt, array( + 'imap' => $email_imap, + 'customerid' => $id + )); + } + + $upd_data = array( + 'customerid' => $id, + 'passwd' => $password, + 'name' => $name, + 'firstname' => $firstname, + 'gender' => $gender, + 'company' => $company, + 'street' => $street, + 'zipcode' => $zipcode, + 'city' => $city, + 'phone' => $phone, + 'fax' => $fax, + 'email' => $email, + 'customerno' => $customernumber, + 'lang' => $def_language, + 'diskspace' => $diskspace, + 'traffic' => $traffic, + 'subdomains' => $subdomains, + 'emails' => $emails, + 'email_accounts' => $email_accounts, + 'email_forwarders' => $email_forwarders, + 'email_quota' => $email_quota, + 'ftps' => $ftps, + 'tickets' => $tickets, + 'mysqls' => $mysqls, + 'deactivated' => $deactivated, + 'phpenabled' => $phpenabled, + 'allowed_phpconfigs' => empty($allowed_phpconfigs) ? "" : json_encode($allowed_phpconfigs), + 'imap' => $email_imap, + 'pop3' => $email_pop3, + 'perlenabled' => $perlenabled, + 'dnsenabled' => $dnsenabled, + 'custom_notes' => $custom_notes, + 'custom_notes_show' => $custom_notes_show, + 'theme' => $theme + ); + $upd_stmt = Database::prepare(" UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET `name` = :name, `firstname` = :firstname, @@ -1090,136 +1080,134 @@ class Customers extends ApiCommand implements ResourceEntity `theme` = :theme WHERE `customerid` = :customerid "); - Database::pexecute($upd_stmt, $upd_data); + Database::pexecute($upd_stmt, $upd_data); + + if ($this->isAdmin()) { + // Using filesystem - quota, insert a task which cleans the filesystem - quota + inserttask('10'); - if ($this->isAdmin()) { - // Using filesystem - quota, insert a task which cleans the filesystem - quota - inserttask('10'); - - $admin_update_query = "UPDATE `" . TABLE_PANEL_ADMINS . "` SET `customers_used` = `customers_used` "; - - if ($mysqls != '-1' || $result['mysqls'] != '-1') { - $admin_update_query .= ", `mysqls_used` = `mysqls_used` "; - - if ($mysqls != '-1') { - $admin_update_query .= " + 0" . (int) $mysqls . " "; - } - if ($result['mysqls'] != '-1') { - $admin_update_query .= " - 0" . (int) $result['mysqls'] . " "; - } - } - - if ($emails != '-1' || $result['emails'] != '-1') { - $admin_update_query .= ", `emails_used` = `emails_used` "; - - if ($emails != '-1') { - $admin_update_query .= " + 0" . (int) $emails . " "; - } - if ($result['emails'] != '-1') { - $admin_update_query .= " - 0" . (int) $result['emails'] . " "; - } - } - - if ($email_accounts != '-1' || $result['email_accounts'] != '-1') { - $admin_update_query .= ", `email_accounts_used` = `email_accounts_used` "; - - if ($email_accounts != '-1') { - $admin_update_query .= " + 0" . (int) $email_accounts . " "; - } - if ($result['email_accounts'] != '-1') { - $admin_update_query .= " - 0" . (int) $result['email_accounts'] . " "; - } - } - - if ($email_forwarders != '-1' || $result['email_forwarders'] != '-1') { - $admin_update_query .= ", `email_forwarders_used` = `email_forwarders_used` "; - - if ($email_forwarders != '-1') { - $admin_update_query .= " + 0" . (int) $email_forwarders . " "; - } - if ($result['email_forwarders'] != '-1') { - $admin_update_query .= " - 0" . (int) $result['email_forwarders'] . " "; - } - } - - if ($email_quota != '-1' || $result['email_quota'] != '-1') { - $admin_update_query .= ", `email_quota_used` = `email_quota_used` "; - - if ($email_quota != '-1') { - $admin_update_query .= " + 0" . (int) $email_quota . " "; - } - if ($result['email_quota'] != '-1') { - $admin_update_query .= " - 0" . (int) $result['email_quota'] . " "; - } - } - - if ($subdomains != '-1' || $result['subdomains'] != '-1') { - $admin_update_query .= ", `subdomains_used` = `subdomains_used` "; - - if ($subdomains != '-1') { - $admin_update_query .= " + 0" . (int) $subdomains . " "; - } - if ($result['subdomains'] != '-1') { - $admin_update_query .= " - 0" . (int) $result['subdomains'] . " "; - } - } - - if ($ftps != '-1' || $result['ftps'] != '-1') { - $admin_update_query .= ", `ftps_used` = `ftps_used` "; - - if ($ftps != '-1') { - $admin_update_query .= " + 0" . (int) $ftps . " "; - } - if ($result['ftps'] != '-1') { - $admin_update_query .= " - 0" . (int) $result['ftps'] . " "; - } - } - - if ($tickets != '-1' || $result['tickets'] != '-1') { - $admin_update_query .= ", `tickets_used` = `tickets_used` "; - - if ($tickets != '-1') { - $admin_update_query .= " + 0" . (int) $tickets . " "; - } - if ($result['tickets'] != '-1') { - $admin_update_query .= " - 0" . (int) $result['tickets'] . " "; - } - } - - if (($diskspace / 1024) != '-1' || ($result['diskspace'] / 1024) != '-1') { - $admin_update_query .= ", `diskspace_used` = `diskspace_used` "; - - if (($diskspace / 1024) != '-1') { - $admin_update_query .= " + 0" . (int) $diskspace . " "; - } - if (($result['diskspace'] / 1024) != '-1') { - $admin_update_query .= " - 0" . (int) $result['diskspace'] . " "; - } - } - - $admin_update_query .= " WHERE `adminid` = '" . (int) $result['adminid'] . "'"; - Database::query($admin_update_query); - } + $admin_update_query = "UPDATE `" . TABLE_PANEL_ADMINS . "` SET `customers_used` = `customers_used` "; - $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_INFO, "[API] edited user '" . $result['loginname'] . "'"); - - /* - * move customer to another admin/reseller; #1166 - */ - if ($move_to_admin > 0 && $move_to_admin != $result['adminid']) { - $json_result = Customers::getLocal($this->getUserData(), array( - 'id' => $result['customerid'], - 'adminid' => $move_to_admin - ))->move(); - $move_result = json_decode($json_result, true)['data']; - if ($move_result != true) { - standard_error('moveofcustomerfailed', $move_result, true); + if ($mysqls != '-1' || $result['mysqls'] != '-1') { + $admin_update_query .= ", `mysqls_used` = `mysqls_used` "; + + if ($mysqls != '-1') { + $admin_update_query .= " + 0" . (int) $mysqls . " "; + } + if ($result['mysqls'] != '-1') { + $admin_update_query .= " - 0" . (int) $result['mysqls'] . " "; } } - return $this->response(200, "successfull", $upd_data); + if ($emails != '-1' || $result['emails'] != '-1') { + $admin_update_query .= ", `emails_used` = `emails_used` "; + + if ($emails != '-1') { + $admin_update_query .= " + 0" . (int) $emails . " "; + } + if ($result['emails'] != '-1') { + $admin_update_query .= " - 0" . (int) $result['emails'] . " "; + } + } + + if ($email_accounts != '-1' || $result['email_accounts'] != '-1') { + $admin_update_query .= ", `email_accounts_used` = `email_accounts_used` "; + + if ($email_accounts != '-1') { + $admin_update_query .= " + 0" . (int) $email_accounts . " "; + } + if ($result['email_accounts'] != '-1') { + $admin_update_query .= " - 0" . (int) $result['email_accounts'] . " "; + } + } + + if ($email_forwarders != '-1' || $result['email_forwarders'] != '-1') { + $admin_update_query .= ", `email_forwarders_used` = `email_forwarders_used` "; + + if ($email_forwarders != '-1') { + $admin_update_query .= " + 0" . (int) $email_forwarders . " "; + } + if ($result['email_forwarders'] != '-1') { + $admin_update_query .= " - 0" . (int) $result['email_forwarders'] . " "; + } + } + + if ($email_quota != '-1' || $result['email_quota'] != '-1') { + $admin_update_query .= ", `email_quota_used` = `email_quota_used` "; + + if ($email_quota != '-1') { + $admin_update_query .= " + 0" . (int) $email_quota . " "; + } + if ($result['email_quota'] != '-1') { + $admin_update_query .= " - 0" . (int) $result['email_quota'] . " "; + } + } + + if ($subdomains != '-1' || $result['subdomains'] != '-1') { + $admin_update_query .= ", `subdomains_used` = `subdomains_used` "; + + if ($subdomains != '-1') { + $admin_update_query .= " + 0" . (int) $subdomains . " "; + } + if ($result['subdomains'] != '-1') { + $admin_update_query .= " - 0" . (int) $result['subdomains'] . " "; + } + } + + if ($ftps != '-1' || $result['ftps'] != '-1') { + $admin_update_query .= ", `ftps_used` = `ftps_used` "; + + if ($ftps != '-1') { + $admin_update_query .= " + 0" . (int) $ftps . " "; + } + if ($result['ftps'] != '-1') { + $admin_update_query .= " - 0" . (int) $result['ftps'] . " "; + } + } + + if ($tickets != '-1' || $result['tickets'] != '-1') { + $admin_update_query .= ", `tickets_used` = `tickets_used` "; + + if ($tickets != '-1') { + $admin_update_query .= " + 0" . (int) $tickets . " "; + } + if ($result['tickets'] != '-1') { + $admin_update_query .= " - 0" . (int) $result['tickets'] . " "; + } + } + + if (($diskspace / 1024) != '-1' || ($result['diskspace'] / 1024) != '-1') { + $admin_update_query .= ", `diskspace_used` = `diskspace_used` "; + + if (($diskspace / 1024) != '-1') { + $admin_update_query .= " + 0" . (int) $diskspace . " "; + } + if (($result['diskspace'] / 1024) != '-1') { + $admin_update_query .= " - 0" . (int) $result['diskspace'] . " "; + } + } + + $admin_update_query .= " WHERE `adminid` = '" . (int) $result['adminid'] . "'"; + Database::query($admin_update_query); } - throw new Exception("Not allowed to execute given command.", 403); + + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_INFO, "[API] edited user '" . $result['loginname'] . "'"); + + /* + * move customer to another admin/reseller; #1166 + */ + if ($move_to_admin > 0 && $move_to_admin != $result['adminid']) { + $json_result = Customers::getLocal($this->getUserData(), array( + 'id' => $result['customerid'], + 'adminid' => $move_to_admin + ))->move(); + $move_result = json_decode($json_result, true)['data']; + if ($move_result != true) { + standard_error('moveofcustomerfailed', $move_result, true); + } + } + + return $this->response(200, "successfull", $upd_data); } /** @@ -1241,13 +1229,9 @@ class Customers extends ApiCommand implements ResourceEntity if ($this->isAdmin()) { $id = $this->getParam('id', true, 0); $ln_optional = ($id <= 0 ? false : true); - $loginname = $this->getParam('loginname', $ln_optional, ''); + $loginname = trim($this->getParam('loginname', $ln_optional, '')); $delete_userfiles = $this->getParam('delete_userfiles', true, 0); - if ($id <= 0 && empty($loginname)) { - throw new Exception("Either 'id' or 'loginname' parameter must be given", 406); - } - $json_result = Customers::getLocal($this->getUserData(), array( 'id' => $id, 'loginname' => $loginname @@ -1487,11 +1471,7 @@ class Customers extends ApiCommand implements ResourceEntity if ($this->isAdmin()) { $id = $this->getParam('id', true, 0); $ln_optional = ($id <= 0 ? false : true); - $loginname = $this->getParam('loginname', $ln_optional, ''); - - if ($id <= 0 && empty($loginname)) { - throw new Exception("Either 'id' or 'loginname' parameter must be given", 406); - } + $loginname = trim($this->getParam('loginname', $ln_optional, '')); $json_result = Customers::getLocal($this->getUserData(), array( 'id' => $id, @@ -1539,18 +1519,18 @@ class Customers extends ApiCommand implements ResourceEntity 'id' => $id ))->get(); $c_result = json_decode($json_result, true)['data']; - + // check if target-admin is the current admin if ($adminid == $c_result['adminid']) { throw new Exception("Cannot move customer to the same admin/reseller as he currently is assigned to", 406); } - + // get target admin $json_result = Customers::getLocal($this->getUserData(), array( 'id' => $adminid ))->get(); $a_result = json_decode($json_result, true)['data']; - + // Update customer entry $updCustomer_stmt = Database::prepare(" UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET `adminid` = :adminid WHERE `customerid` = :cid @@ -1580,7 +1560,7 @@ class Customers extends ApiCommand implements ResourceEntity // now, recalculate the resource-usage for the old and the new admin updateCounters(false); - + $log->logAction(ADM_ACTION, LOG_INFO, "[API] moved user '" . $c_result['loginname'] . "' from admin/reseller '" . $c_result['adminname'] . " to admin/reseller '" . $a_result['loginname'] . "'"); return $this->response(200, "successfull", true); } diff --git a/lib/classes/api/commands/class.Domains.php b/lib/classes/api/commands/class.Domains.php index ad879bb6..31ca5e63 100644 --- a/lib/classes/api/commands/class.Domains.php +++ b/lib/classes/api/commands/class.Domains.php @@ -73,14 +73,9 @@ class Domains extends ApiCommand implements ResourceEntity if ($this->isAdmin()) { $id = $this->getParam('id', true, 0); $dn_optional = ($id <= 0 ? false : true); - $domainname = $this->getParam('domainname', $dn_optional, ''); + $domainname = trim($this->getParam('domainname', $dn_optional, '')); $no_std_subdomain = $this->getParam('no_std_subdomain', true, false); - $this->logger()->logAction(ADM_ACTION, LOG_NOTICE, "[API] get domain #" . $id); - - if ($id <= 0 && empty($domainname)) { - throw new Exception("Either 'id' or 'domainname' parameter must be given", 406); - } - + // convert possible idn domain to punycode if (substr($domainname, 0, 4) != 'xn--') { $idna_convert = new idna_convert_wrapper(); @@ -101,6 +96,7 @@ class Domains extends ApiCommand implements ResourceEntity } $result = Database::pexecute_first($result_stmt, $params, true, true); if ($result) { + $this->logger()->logAction(ADM_ACTION, LOG_NOTICE, "[API] get domain '" . $result['domain'] . "'"); return $this->response(200, "successfull", $result); } $key = ($id > 0 ? "id #" . $id : "domainname '" . $domainname . "'"); @@ -778,11 +774,7 @@ class Domains extends ApiCommand implements ResourceEntity // parameters $id = $this->getParam('id', true, 0); $dn_optional = ($id <= 0 ? false : true); - $domainname = $this->getParam('domainname', $dn_optional, ''); - - if ($id <= 0 && empty($domainname)) { - throw new Exception("Either 'id' or 'domainname' parameter must be given", 406); - } + $domainname = trim($this->getParam('domainname', $dn_optional, '')); // get requested domain $json_result = Domains::getLocal($this->getUserData(), array( @@ -1595,14 +1587,10 @@ class Domains extends ApiCommand implements ResourceEntity if ($this->isAdmin()) { $id = $this->getParam('id', true, 0); $dn_optional = ($id <= 0 ? false : true); - $domainname = $this->getParam('domainname', $dn_optional, ''); + $domainname = trim($this->getParam('domainname', $dn_optional, '')); $is_stdsubdomain = $this->getParam('is_stdsubdomain', true, 0); $remove_subbutmain_domains = $this->getParam('delete_mainsubdomains', true, 0); - - if ($id <= 0 && empty($domainname)) { - throw new Exception("Either 'id' or 'domainname' parameter must be given", 406); - } - + $json_result = Domains::getLocal($this->getUserData(), array( 'id' => $id, 'domainname' => $domainname diff --git a/lib/classes/api/commands/class.Ftps.php b/lib/classes/api/commands/class.Ftps.php index 352302e3..e0150ab4 100644 --- a/lib/classes/api/commands/class.Ftps.php +++ b/lib/classes/api/commands/class.Ftps.php @@ -37,12 +37,8 @@ class Ftps extends ApiCommand implements ResourceEntity { $id = $this->getParam('id', true, 0); $un_optional = ($id <= 0 ? false : true); - $username = $this->getParam('username', $un_optional, ''); - - if ($id <= 0 && empty($username)) { - throw new Exception("Either 'id' or 'username' parameter must be given", 406); - } - + $username = trim($this->getParam('username', $un_optional, '')); + $params = array(); if ($this->isAdmin()) { if ($this->getUserDetail('customers_see_all') == false) { diff --git a/lib/classes/api/commands/class.Mysqls.php b/lib/classes/api/commands/class.Mysqls.php index 7b72a65c..5fa12a1e 100644 --- a/lib/classes/api/commands/class.Mysqls.php +++ b/lib/classes/api/commands/class.Mysqls.php @@ -238,13 +238,9 @@ class Mysqls extends ApiCommand implements ResourceEntity { $id = $this->getParam('id', true, 0); $dn_optional = ($id <= 0 ? false : true); - $dbname = $this->getParam('dbname', $dn_optional, ''); + $dbname = trim($this->getParam('dbname', $dn_optional, '')); $dbserver = $this->getParam('mysql_server', true, - 1); - - if ($id <= 0 && empty($dbname)) { - throw new Exception("Either 'id' or 'dbname' parameter must be given", 406); - } - + if ($this->isAdmin()) { if ($this->getUserDetail('customers_see_all') != 1) { // if it's a reseller or an admin who cannot see all customers, we need to check @@ -341,13 +337,9 @@ class Mysqls extends ApiCommand implements ResourceEntity { $id = $this->getParam('id', true, 0); $dn_optional = ($id <= 0 ? false : true); - $dbname = $this->getParam('dbname', $dn_optional, ''); + $dbname = trim($this->getParam('dbname', $dn_optional, '')); $dbserver = $this->getParam('mysql_server', true, - 1); - - if ($id <= 0 && empty($dbname)) { - throw new Exception("Either 'id' or 'dbname' parameter must be given", 406); - } - + if ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'mysql')) { throw new Exception("You cannot access this resource", 405); } @@ -549,13 +541,9 @@ class Mysqls extends ApiCommand implements ResourceEntity { $id = $this->getParam('id', true, 0); $dn_optional = ($id <= 0 ? false : true); - $dbname = $this->getParam('dbname', $dn_optional, ''); + $dbname = trim($this->getParam('dbname', $dn_optional, '')); $dbserver = $this->getParam('mysql_server', true, - 1); - - if ($id <= 0 && empty($dbname)) { - throw new Exception("Either 'id' or 'dbname' parameter must be given", 406); - } - + if ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'mysql')) { throw new Exception("You cannot access this resource", 405); } diff --git a/lib/functions/output/function.standard_error.php b/lib/functions/output/function.standard_error.php index 9bcb3397..089d2343 100644 --- a/lib/functions/output/function.standard_error.php +++ b/lib/functions/output/function.standard_error.php @@ -61,7 +61,7 @@ function standard_error($errors = '', $replacer = '', $throw_exception = false) } if ($throw_exception) { - throw new Exception($error, 400); + throw new Exception(strip_tags($error), 400); } eval("echo \"" . getTemplate('misc/error', '1') . "\";"); exit; From 0958d07f2343d8fc98e52b5756996a9dc6f70af4 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sun, 25 Feb 2018 16:49:24 +0100 Subject: [PATCH 0488/1335] fixes in Admins and Customers ApiCommand Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/abstract.ApiCommand.php | 16 ++++++--- lib/classes/api/commands/class.Admins.php | 34 +++++++------------- lib/classes/api/commands/class.Customers.php | 30 ++++++----------- 3 files changed, 33 insertions(+), 47 deletions(-) diff --git a/lib/classes/api/abstract.ApiCommand.php b/lib/classes/api/abstract.ApiCommand.php index 82fd38c8..afdba0c5 100644 --- a/lib/classes/api/abstract.ApiCommand.php +++ b/lib/classes/api/abstract.ApiCommand.php @@ -102,7 +102,7 @@ abstract class ApiCommand public function __construct($header = null, $params = null, $userinfo = null) { global $lng, $version, $dbversion, $branding; - + $this->version = $version; $this->dbversion = $dbversion; $this->branding = $branding; @@ -116,13 +116,13 @@ abstract class ApiCommand throw new Exception("Invalid user data", 500); } $this->logger = FroxlorLogger::getInstanceOf($this->user_data); - + // check whether the user is deactivated if ($this->getUserDetail('deactivated') == 1) { $this->logger()->logAction(LOG_ERROR, LOG_INFO, "[API] User '" . $this->getUserDetail('loginnname') . "' tried to use API but is deactivated"); throw new Exception("Account suspended", 406); } - + $this->initLang(); $this->lng = $lng; $this->initMail(); @@ -166,8 +166,14 @@ abstract class ApiCommand // now include the selected language if its not english if ($language != 'English') { - foreach ($langs[$language] as $key => $value) { - include_once makeSecurePath(FROXLOR_INSTALL_DIR . '/' . $value['file']); + if (isset($langs[$language])) { + foreach ($langs[$language] as $key => $value) { + include_once makeSecurePath(FROXLOR_INSTALL_DIR . '/' . $value['file']); + } + } else { + if ($this->debug) { + $this->logger()->logAction(LOG_ERROR, LOG_DEBUG, "[API] unable to include user-language '" . $language . "'. Not found in database.", 404); + } } } diff --git a/lib/classes/api/commands/class.Admins.php b/lib/classes/api/commands/class.Admins.php index 6c0de7ae..36c1005b 100644 --- a/lib/classes/api/commands/class.Admins.php +++ b/lib/classes/api/commands/class.Admins.php @@ -99,7 +99,7 @@ class Admins extends ApiCommand implements ResourceEntity $email = $this->getParam('email'); // parameters - $def_language = $this->getParam('def_language', true, ''); + $def_language = $this->getParam('def_language', true, Settings::Get('panel.standardlanguage')); $custom_notes = $this->getParam('custom_notes', true, ''); $custom_notes_show = $this->getParam('custom_notes_show', true, 0); $password = $this->getParam('admin_password', true, ''); @@ -152,28 +152,18 @@ class Admins extends ApiCommand implements ResourceEntity $traffic = $traffic * 1024 * 1024; // Check if the account already exists - try { - $dup_check_result = Customers::getLocal($this->getUserData(), array( - 'loginname' => $loginname - ))->get(); - $loginname_check = json_decode($dup_check_result, true)['data']; - } catch (Exception $e) { - $loginname_check = array( - 'loginname' => '' - ); - } + // do not check via api as we skip any permission checks for this task + $loginname_check_stmt = Database::prepare(" + SELECT `loginname` FROM `" . TABLE_PANEL_CUSTOMERS . "` WHERE `loginname` = :login + "); + $loginname_check = Database::pexecute_first($loginname_check_stmt, array('login' => $loginname), true, true); // Check if an admin with the loginname already exists - try { - $dup_check_result = Admins::getLocal($this->getUserData(), array( - 'loginname' => $loginname - ))->get(); - $loginname_check_admin = json_decode($dup_check_result, true)['data']; - } catch (Exception $e) { - $loginname_check_admin = array( - 'loginname' => '' - ); - } + // do not check via api as we skip any permission checks for this task + $loginname_check_admin_stmt = Database::prepare(" + SELECT `loginname` FROM `" . TABLE_PANEL_ADMINS . "` WHERE `loginname` = :login + "); + $loginname_check_admin = Database::pexecute_first($loginname_check_admin_stmt, array('login' => $loginname), true, true); if ($loginname == '') { standard_error(array( @@ -290,7 +280,7 @@ class Admins extends ApiCommand implements ResourceEntity $adminid = Database::lastInsertId(); $ins_data['adminid'] = $adminid; $this->logger()->logAction(ADM_ACTION, LOG_WARNING, "[API] added admin '" . $loginname . "'"); - return $this->response(200, "successfull", $admin_ins_data); + return $this->response(200, "successfull", $ins_data); } } throw new Exception("Not allowed to execute given command.", 403); diff --git a/lib/classes/api/commands/class.Customers.php b/lib/classes/api/commands/class.Customers.php index e827d32a..2b978684 100644 --- a/lib/classes/api/commands/class.Customers.php +++ b/lib/classes/api/commands/class.Customers.php @@ -243,28 +243,18 @@ class Customers extends ApiCommand implements ResourceEntity } // Check if the account already exists - try { - $dup_check_result = Customers::getLocal($this->getUserData(), array( - 'loginname' => $loginname - ))->get(); - $loginname_check = json_decode($dup_check_result, true)['data']; - } catch (Exception $e) { - $loginname_check = array( - 'loginname' => '' - ); - } + // do not check via api as we skip any permission checks for this task + $loginname_check_stmt = Database::prepare(" + SELECT `loginname` FROM `" . TABLE_PANEL_CUSTOMERS . "` WHERE `loginname` = :login + "); + $loginname_check = Database::pexecute_first($loginname_check_stmt, array('login' => $loginname), true, true); // Check if an admin with the loginname already exists - try { - $dup_check_result = Admins::getLocal($this->getUserData(), array( - 'loginname' => $loginname - ))->get(); - $loginname_check_admin = json_decode($dup_check_result, true)['data']; - } catch (Exception $e) { - $loginname_check_admin = array( - 'loginname' => '' - ); - } + // do not check via api as we skip any permission checks for this task + $loginname_check_admin_stmt = Database::prepare(" + SELECT `loginname` FROM `" . TABLE_PANEL_ADMINS . "` WHERE `loginname` = :login + "); + $loginname_check_admin = Database::pexecute_first($loginname_check_admin_stmt, array('login' => $loginname), true, true); if (strtolower($loginname_check['loginname']) == strtolower($loginname) || strtolower($loginname_check_admin['loginname']) == strtolower($loginname)) { standard_error('loginnameexists', $loginname, true); From f32a1921c5ea513d4eae39ef59e1142f7f073845 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sun, 25 Feb 2018 17:03:49 +0100 Subject: [PATCH 0489/1335] re-read admin/customer when adding/updating so we return the fields from the table, not the placeholders of the prepared-statement Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/commands/class.Admins.php | 19 +++++++++++++++--- lib/classes/api/commands/class.Customers.php | 21 ++++++++++++++------ 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/lib/classes/api/commands/class.Admins.php b/lib/classes/api/commands/class.Admins.php index 36c1005b..382ff0c0 100644 --- a/lib/classes/api/commands/class.Admins.php +++ b/lib/classes/api/commands/class.Admins.php @@ -280,7 +280,13 @@ class Admins extends ApiCommand implements ResourceEntity $adminid = Database::lastInsertId(); $ins_data['adminid'] = $adminid; $this->logger()->logAction(ADM_ACTION, LOG_WARNING, "[API] added admin '" . $loginname . "'"); - return $this->response(200, "successfull", $ins_data); + + // get all admin-data for return-array + $json_result = Admins::getLocal($this->getUserData(), array( + 'id' => $adminid + ))->get(); + $result = json_decode($json_result, true)['data']; + return $this->response(200, "successfull", $result); } } throw new Exception("Not allowed to execute given command.", 403); @@ -540,9 +546,14 @@ class Admins extends ApiCommand implements ResourceEntity WHERE `adminid` = :adminid "); Database::pexecute($upd_stmt, $upd_data, true, true); - $this->logger()->logAction(ADM_ACTION, LOG_INFO, "[API] edited admin '" . $result['loginname'] . "'"); - return $this->response(200, "successfull", $upd_data); + + // get all admin-data for return-array + $json_result = Admins::getLocal($this->getUserData(), array( + 'id' => $adminid + ))->get(); + $result = json_decode($json_result, true)['data']; + return $this->response(200, "successfull", $result); } } throw new Exception("Not allowed to execute given command.", 403); @@ -647,6 +658,8 @@ class Admins extends ApiCommand implements ResourceEntity Database::pexecute($result_stmt, array( 'id' => $id ), true, true); + // set the new value for result-array + $result['loginfail_count'] = 0; $this->logger()->logAction(ADM_ACTION, LOG_WARNING, "[API] unlocked admin '" . $result['loginname'] . "'"); return $this->response(200, "successfull", $result); diff --git a/lib/classes/api/commands/class.Customers.php b/lib/classes/api/commands/class.Customers.php index 2b978684..e6b49ec1 100644 --- a/lib/classes/api/commands/class.Customers.php +++ b/lib/classes/api/commands/class.Customers.php @@ -428,9 +428,8 @@ class Customers extends ApiCommand implements ResourceEntity // update last account number Settings::Set('system.lastaccountnumber', $accountnumber, true); } - + $this->logger()->logAction(ADM_ACTION, LOG_INFO, "[API] added customer '" . $loginname . "'"); - $customer_ins_data = $ins_data; unset($ins_data); // insert task to create homedir etc. @@ -644,9 +643,13 @@ class Customers extends ApiCommand implements ResourceEntity $this->logger()->logAction(ADM_ACTION, LOG_NOTICE, "[API] automatically sent password to user '" . $loginname . "'"); } } - $this->logger()->logAction(ADM_ACTION, LOG_WARNING, "[API] added customer '" . $loginname . "'"); - return $this->response(200, "successfull", $customer_ins_data); + + $json_result = Customers::getLocal($this->getUserData(), array( + 'loginname' => $loginname + ))->get(); + $result = json_decode($json_result, true)['data']; + return $this->response(200, "successfull", $result); } throw new Exception("No more resources available", 406); } @@ -1196,8 +1199,12 @@ class Customers extends ApiCommand implements ResourceEntity standard_error('moveofcustomerfailed', $move_result, true); } } - - return $this->response(200, "successfull", $upd_data); + + $json_result = Customers::getLocal($this->getUserData(), array( + 'loginname' => $result['customerid'] + ))->get(); + $result = json_decode($json_result, true)['data']; + return $this->response(200, "successfull", $result); } /** @@ -1478,6 +1485,8 @@ class Customers extends ApiCommand implements ResourceEntity Database::pexecute($result_stmt, array( 'id' => $id ), true, true); + // set the new value for result-array + $result['loginfail_count'] = 0; $this->logger()->logAction(ADM_ACTION, LOG_WARNING, "[API] unlocked customer '" . $result['loginname'] . "'"); return $this->response(200, "successfull", $result); From e66dde2e646f11630d5c5a806ec45ea8bbae2c9c Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sun, 25 Feb 2018 17:08:30 +0100 Subject: [PATCH 0490/1335] id <> loginname, grrr Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/commands/class.Customers.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/classes/api/commands/class.Customers.php b/lib/classes/api/commands/class.Customers.php index e6b49ec1..8813adc2 100644 --- a/lib/classes/api/commands/class.Customers.php +++ b/lib/classes/api/commands/class.Customers.php @@ -1201,7 +1201,7 @@ class Customers extends ApiCommand implements ResourceEntity } $json_result = Customers::getLocal($this->getUserData(), array( - 'loginname' => $result['customerid'] + 'id' => $result['customerid'] ))->get(); $result = json_decode($json_result, true)['data']; return $this->response(200, "successfull", $result); From 2b366c8f23317ccab87f873af999147e7d05dd5e Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sun, 25 Feb 2018 18:22:15 +0100 Subject: [PATCH 0491/1335] add field for fullchain to be stored in ssl-certificates-table; create fullchain file if given (it's not used by froxlor); do not generate/renew certificates for disabled customers domains Signed-off-by: Michael Kaufmann (d00p) --- install/froxlor.sql | 3 ++- install/updates/froxlor/0.9/update_0.9.inc.php | 9 +++++++++ lib/classes/webserver/class.DomainSSL.php | 4 ++++ lib/version.inc.php | 2 +- scripts/jobs/cron_letsencrypt.php | 3 +++ scripts/jobs/cron_letsencrypt_v2.php | 3 +++ 6 files changed, 22 insertions(+), 2 deletions(-) diff --git a/install/froxlor.sql b/install/froxlor.sql index 870e30d4..0876cffd 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -688,7 +688,7 @@ opcache.interned_strings_buffer'), ('panel', 'password_special_char', '!?<>§$%+#=@'), ('panel', 'customer_hide_options', ''), ('panel', 'version', '0.9.39.5'), - ('panel', 'db_version', '201802130'); + ('panel', 'db_version', '201802250'); DROP TABLE IF EXISTS `panel_tasks`; @@ -1004,6 +1004,7 @@ CREATE TABLE IF NOT EXISTS `domain_ssl_settings` ( `ssl_ca_file` mediumtext, `ssl_cert_chainfile` mediumtext, `ssl_csr_file` mediumtext, + `ssl_fullchain_file` mediumtext, `expirationdate` datetime DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM CHARSET=utf8 COLLATE=utf8_general_ci; diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index 0abab0fe..0e9c09e6 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -3939,3 +3939,12 @@ if (isFroxlorVersion('0.9.39.4')) { showUpdateStep("Updating from 0.9.39.4 to 0.9.39.5", false); updateToVersion('0.9.39.5'); } + +if (isDatabaseVersion('201802130')) { + + showUpdateStep("Adding fullchain field to ssl certificates"); + Database::query("ALTER TABLE `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` ADD `ssl_fullchain_file` mediumtext AFTER `ssl_csr_file`;"); + lastStepStatus(0); + + updateToDbVersion('201802250'); +} diff --git a/lib/classes/webserver/class.DomainSSL.php b/lib/classes/webserver/class.DomainSSL.php index 82ae9fcd..73399bfa 100644 --- a/lib/classes/webserver/class.DomainSSL.php +++ b/lib/classes/webserver/class.DomainSSL.php @@ -89,6 +89,10 @@ class DomainSSL { $ssl_files['ssl_cert_chainfile'] = makeCorrectFile($sslcertpath.'/'.$domain['domain'].'_chain.pem'); } } + // will only be generated to be used externally, froxlor does not need this + if ($dom_certs['ssl_fullchain_file'] != '') { + $ssl_files['ssl_fullchain_file'] = makeCorrectFile($sslcertpath.'/'.$domain['domain'].'_fullchain.pem'); + } // create them on the filesystem foreach ($ssl_files as $type => $filename) { if ($filename != '') { diff --git a/lib/version.inc.php b/lib/version.inc.php index 49935fb7..9ad32b3a 100644 --- a/lib/version.inc.php +++ b/lib/version.inc.php @@ -19,7 +19,7 @@ $version = '0.9.39.5'; // Database version (YYYYMMDDC where C is a daily counter) -$dbversion = '201802130'; +$dbversion = '201802250'; // Distribution branding-tag (used for Debian etc.) $branding = ''; diff --git a/scripts/jobs/cron_letsencrypt.php b/scripts/jobs/cron_letsencrypt.php index b25b9a8d..98ee24ce 100644 --- a/scripts/jobs/cron_letsencrypt.php +++ b/scripts/jobs/cron_letsencrypt.php @@ -60,6 +60,7 @@ $certificates_stmt = Database::query(" 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 @@ -92,6 +93,7 @@ $updcert_stmt = Database::prepare(" `ssl_ca_file` = :ca, `ssl_cert_chainfile` = :chain, `ssl_csr_file` = :csr, + `ssl_fullchain_file` = :fullchain, `expirationdate` = :expirationdate "); @@ -182,6 +184,7 @@ if (Settings::Get('system.le_froxlor_enabled') == '1') { 'ca' => $return['chain'], 'chain' => $return['chain'], 'csr' => $return['csr'], + 'fullchain' => $return['fullchain'], 'expirationdate' => date('Y-m-d H:i:s', $newcert['validTo_time_t']) )); diff --git a/scripts/jobs/cron_letsencrypt_v2.php b/scripts/jobs/cron_letsencrypt_v2.php index 6b1148d3..6e27361b 100644 --- a/scripts/jobs/cron_letsencrypt_v2.php +++ b/scripts/jobs/cron_letsencrypt_v2.php @@ -55,6 +55,7 @@ $certificates_stmt = Database::query(" 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 @@ -88,6 +89,7 @@ $updcert_stmt = Database::prepare(" `ssl_ca_file` = :ca, `ssl_cert_chainfile` = :chain, `ssl_csr_file` = :csr, + `ssl_fullchain_file` = :fullchain, `expirationdate` = :expirationdate "); @@ -178,6 +180,7 @@ if (Settings::Get('system.le_froxlor_enabled') == '1') { 'ca' => $return['chain'], 'chain' => $return['chain'], 'csr' => $return['csr'], + 'fullchain' => $return['fullchain'], 'expirationdate' => date('Y-m-d H:i:s', $newcert['validTo_time_t']) )); From 5c330505ea6a508ebdd7d9ca13d20586dc208efd Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sun, 25 Feb 2018 20:47:36 +0100 Subject: [PATCH 0492/1335] correct Admins.update and Admins.delete Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/commands/class.Admins.php | 576 +++++++++++----------- 1 file changed, 297 insertions(+), 279 deletions(-) diff --git a/lib/classes/api/commands/class.Admins.php b/lib/classes/api/commands/class.Admins.php index 382ff0c0..d6af8258 100644 --- a/lib/classes/api/commands/class.Admins.php +++ b/lib/classes/api/commands/class.Admins.php @@ -54,7 +54,7 @@ class Admins extends ApiCommand implements ResourceEntity * optional, the admin-id * @param string $loginname * optional, the loginname - * + * * @access admin * @throws Exception * @return array @@ -64,7 +64,7 @@ class Admins extends ApiCommand implements ResourceEntity $id = $this->getParam('id', true, 0); $ln_optional = ($id <= 0 ? false : true); $loginname = trim($this->getParam('loginname', $ln_optional, '')); - + if ($this->isAdmin() && ($this->getUserDetail('change_serversettings') == 1 || ($this->getUserDetail('adminid') == $id || $this->getUserDetail('loginname') == $loginname))) { $result_stmt = Database::prepare(" SELECT * FROM `" . TABLE_PANEL_ADMINS . "` @@ -93,11 +93,11 @@ class Admins extends ApiCommand implements ResourceEntity public function add() { if ($this->isAdmin() && $this->getUserDetail('change_serversettings') == 1) { - + // required parameters $name = $this->getParam('name'); $email = $this->getParam('email'); - + // parameters $def_language = $this->getParam('def_language', true, Settings::Get('panel.standardlanguage')); $custom_notes = $this->getParam('custom_notes', true, ''); @@ -105,7 +105,7 @@ class Admins extends ApiCommand implements ResourceEntity $password = $this->getParam('admin_password', true, ''); $sendpassword = $this->getParam('sendpassword', true, 0); $loginname = $this->getParam('new_loginname', true, ''); - + $diskspace = $this->getUlParam('diskspace', 'diskspace_ul', true, 0); $traffic = $this->getUlParam('traffic', 'traffic_ul', true, 0); $customers = $this->getUlParam('customers', 'customers_ul', true, 0); @@ -118,53 +118,57 @@ class Admins extends ApiCommand implements ResourceEntity $ftps = $this->getUlParam('ftps', 'ftps_ul', true, 0); $tickets = $this->getUlParam('tickets', 'tickets_ul', true, 0); $mysqls = $this->getUlParam('mysqls', 'mysqls_ul', true, 0); - + $customers_see_all = $this->getParam('customers_see_all', true, 0); $domains_see_all = $this->getParam('domains_see_all', true, 0); $tickets_see_all = $this->getParam('tickets_see_all', true, 0); $caneditphpsettings = $this->getParam('caneditphpsettings', true, 0); $change_serversettings = $this->getParam('change_serversettings', true, 0); - $ipaddress = intval_ressource($this->getParam('ipaddress', true, -1)); - + $ipaddress = intval_ressource($this->getParam('ipaddress', true, - 1)); + // validation $name = validate($name, 'name', '', '', array(), true); $idna_convert = new idna_convert_wrapper(); $email = $idna_convert->encode(validate($email, 'email', '', '', array(), true)); $def_language = validate($def_language, 'default language', '', '', array(), true); $custom_notes = validate(str_replace("\r\n", "\n", $custom_notes), 'custom_notes', '/^[^\0]*$/', '', array(), true); - + if (Settings::Get('system.mail_quota_enabled') != '1') { $email_quota = - 1; } - + if (Settings::Get('ticket.enabled') != '1') { $tickets = - 1; } - + $password = validate($password, 'password', '', '', array(), true); // only check if not empty, // cause empty == generate password automatically if ($password != '') { $password = validatePassword($password, true); } - + $diskspace = $diskspace * 1024; $traffic = $traffic * 1024 * 1024; - + // Check if the account already exists // do not check via api as we skip any permission checks for this task $loginname_check_stmt = Database::prepare(" SELECT `loginname` FROM `" . TABLE_PANEL_CUSTOMERS . "` WHERE `loginname` = :login "); - $loginname_check = Database::pexecute_first($loginname_check_stmt, array('login' => $loginname), true, true); - + $loginname_check = Database::pexecute_first($loginname_check_stmt, array( + 'login' => $loginname + ), true, true); + // Check if an admin with the loginname already exists // do not check via api as we skip any permission checks for this task $loginname_check_admin_stmt = Database::prepare(" SELECT `loginname` FROM `" . TABLE_PANEL_ADMINS . "` WHERE `loginname` = :login "); - $loginname_check_admin = Database::pexecute_first($loginname_check_admin_stmt, array('login' => $loginname), true, true); - + $loginname_check_admin = Database::pexecute_first($loginname_check_admin_stmt, array( + 'login' => $loginname + ), true, true); + if ($loginname == '') { standard_error(array( 'stringisempty', @@ -190,33 +194,33 @@ class Admins extends ApiCommand implements ResourceEntity } elseif (! validateEmail($email)) { standard_error('emailiswrong', $email, true); } else { - + if ($customers_see_all != '1') { $customers_see_all = '0'; } - + if ($domains_see_all != '1') { $domains_see_all = '0'; } - + if ($caneditphpsettings != '1') { $caneditphpsettings = '0'; } - + if ($change_serversettings != '1') { $change_serversettings = '0'; } - + if ($tickets_see_all != '1') { $tickets_see_all = '0'; } - + if ($password == '') { $password = generatePassword(); } - + $_theme = Settings::Get('panel.default_theme'); - + $ins_data = array( 'loginname' => $loginname, 'password' => makeCryptPassword($password), @@ -245,7 +249,7 @@ class Admins extends ApiCommand implements ResourceEntity 'custom_notes' => $custom_notes, 'custom_notes_show' => $custom_notes_show ); - + $ins_stmt = Database::prepare(" INSERT INTO `" . TABLE_PANEL_ADMINS . "` SET `loginname` = :loginname, @@ -276,11 +280,11 @@ class Admins extends ApiCommand implements ResourceEntity `custom_notes_show` = :custom_notes_show "); Database::pexecute($ins_stmt, $ins_data, true, true); - + $adminid = Database::lastInsertId(); $ins_data['adminid'] = $adminid; $this->logger()->logAction(ADM_ACTION, LOG_WARNING, "[API] added admin '" . $loginname . "'"); - + // get all admin-data for return-array $json_result = Admins::getLocal($this->getUserData(), array( 'id' => $adminid @@ -299,261 +303,263 @@ class Admins extends ApiCommand implements ResourceEntity * optional, the admin-id * @param string $loginname * optional, the loginname - * + * * @access admin * @throws Exception * @return array */ public function update() { - if ($this->isAdmin() && $this->getUserDetail('change_serversettings') == 1) { - + if ($this->isAdmin()) { + $id = $this->getParam('id', true, 0); $ln_optional = ($id <= 0 ? false : true); $loginname = trim($this->getParam('loginname', $ln_optional, '')); - + $json_result = Admins::getLocal($this->getUserData(), array( 'id' => $id, 'loginname' => $loginname ))->get(); $result = json_decode($json_result, true)['data']; $id = $result['adminid']; - - // parameters - $name = $this->getParam('name', true, $result['name']); - $idna_convert = new idna_convert_wrapper(); - $email = $this->getParam('email', true, $idna_convert->decode($result['email'])); - $password = $this->getParam('admin_password', true, ''); - $def_language = $this->getParam('def_language', true, $result['def_language']); - $custom_notes = $this->getParam('custom_notes', true, $result['custom_notes']); - $custom_notes_show = $this->getParam('custom_notes_show', true, $result['custom_notes_show']); - $theme = $this->getParam('theme', true, $result['theme']); - - // you cannot edit some of the details of yourself - if ($result['adminid'] == $this->getUserDetail('userid')) { - $deactivated = $result['deactivated']; - $customers = $result['customers']; - $domains = $result['domains']; - $subdomains = $result['subdomains']; - $emails = $result['emails']; - $email_accounts = $result['email_accounts']; - $email_forwarders = $result['email_forwarders']; - $email_quota = $result['email_quota']; - $ftps = $result['ftps']; - $tickets = $result['tickets']; - $mysqls = $result['mysqls']; - $tickets_see_all = $result['tickets_see_all']; - $customers_see_all = $result['customers_see_all']; - $domains_see_all = $result['domains_see_all']; - $caneditphpsettings = $result['caneditphpsettings']; - $change_serversettings = $result['change_serversettings']; - $diskspace = $result['diskspace']; - $traffic = $result['traffic']; - $ipaddress = $result['ip']; - } else { - $deactivated = $this->getParam('deactivated', true, $result['deactivated']); - - $dec_places = Settings::Get('panel.decimal_places'); - $diskspace = $this->getUlParam('diskspace', 'diskspace_ul', true, round($result['diskspace'] / 1024, $dec_places)); - $traffic = $this->getUlParam('traffic', 'traffic_ul', true, round($result['traffic'] / (1024 * 1024), $dec_places)); - $customers = $this->getUlParam('customers', 'customers_ul', true, $result['customers']); - $domains = $this->getUlParam('domains', 'domains_ul', true, $result['domains']); - $subdomains = $this->getUlParam('subdomains', 'subdomains_ul', true, $result['subdomains']); - $emails = $this->getUlParam('emails', 'emails_ul', true, $result['emails']); - $email_accounts = $this->getUlParam('email_accounts', 'email_accounts_ul', true, $result['email_accounts']); - $email_forwarders = $this->getUlParam('email_forwarders', 'email_forwarders_ul', true, $result['email_forwarders']); - $email_quota = $this->getUlParam('email_quota', 'email_quota_ul', true, $result['email_quota']); - $ftps = $this->getUlParam('ftps', 'ftps_ul', true, $result['ftps']); - $tickets = $this->getUlParam('tickets', 'tickets_ul', true, $result['tickets']); - $mysqls = $this->getUlParam('mysqls', 'mysqls_ul', true, $result['mysqls']); - - $customers_see_all = $this->getParam('customers_see_all', true, $result['customers_see_all']); - $domains_see_all = $this->getParam('domains_see_all', true, $result['domains_see_all']); - $tickets_see_all = $this->getParam('tickets_see_all', true, $result['tickets_see_all']); - $caneditphpsettings = $this->getParam('caneditphpsettings', true, $result['caneditphpsettings']); - $change_serversettings = $this->getParam('change_serversettings', true, $result['change_serversettings']); - $ipaddress = intval_ressource($this->getParam('ipaddress', true, $result['ip'])); - - $diskspace = $diskspace * 1024; - $traffic = $traffic * 1024 * 1024; - } - - // validation - $name = validate($name, 'name', '', '', array(), true); - $idna_convert = new idna_convert_wrapper(); - $email = $idna_convert->encode(validate($email, 'email', '', '', array(), true)); - $def_language = validate($def_language, 'default language', '', '', array(), true); - $custom_notes = validate(str_replace("\r\n", "\n", $custom_notes), 'custom_notes', '/^[^\0]*$/', '', array(), true); - $theme = validate($theme, 'theme', '', '', array(), true); - $password = validate($password, 'password', '', '', array(), true); - - if (Settings::Get('system.mail_quota_enabled') != '1') { - $email_quota = - 1; - } - - if (Settings::Get('ticket.enabled') != '1') { - $tickets = - 1; - } - - if (empty($theme)) { - $theme = Settings::Get('panel.default_theme'); - } - - if ($name == '') { - standard_error(array( - 'stringisempty', - 'myname' - ), '', true); - } elseif ($email == '') { - standard_error(array( - 'stringisempty', - 'emailadd' - ), '', true); - } elseif (! validateEmail($email)) { - standard_error('emailiswrong', $email, true); - } else { - - if ($deactivated != '1') { - $deactivated = '0'; - } - - if ($customers_see_all != '1') { - $customers_see_all = '0'; - } - - if ($domains_see_all != '1') { - $domains_see_all = '0'; - } - - if ($caneditphpsettings != '1') { - $caneditphpsettings = '0'; - } - - if ($change_serversettings != '1') { - $change_serversettings = '0'; - } - - if ($tickets_see_all != '1') { - $tickets_see_all = '0'; - } - - if ($password != '') { - $password = validatePassword($password, true); - $password = makeCryptPassword($password); + + if ($this->getUserDetail('change_serversettings') == 1 || $result['adminid'] == $this->getUserDetail('adminid')) { + // parameters + $name = $this->getParam('name', true, $result['name']); + $idna_convert = new idna_convert_wrapper(); + $email = $this->getParam('email', true, $idna_convert->decode($result['email'])); + $password = $this->getParam('admin_password', true, ''); + $def_language = $this->getParam('def_language', true, $result['def_language']); + $custom_notes = $this->getParam('custom_notes', true, $result['custom_notes']); + $custom_notes_show = $this->getParam('custom_notes_show', true, $result['custom_notes_show']); + $theme = $this->getParam('theme', true, $result['theme']); + + // you cannot edit some of the details of yourself + if ($result['adminid'] == $this->getUserDetail('adminid')) { + $deactivated = $result['deactivated']; + $customers = $result['customers']; + $domains = $result['domains']; + $subdomains = $result['subdomains']; + $emails = $result['emails']; + $email_accounts = $result['email_accounts']; + $email_forwarders = $result['email_forwarders']; + $email_quota = $result['email_quota']; + $ftps = $result['ftps']; + $tickets = $result['tickets']; + $mysqls = $result['mysqls']; + $tickets_see_all = $result['tickets_see_all']; + $customers_see_all = $result['customers_see_all']; + $domains_see_all = $result['domains_see_all']; + $caneditphpsettings = $result['caneditphpsettings']; + $change_serversettings = $result['change_serversettings']; + $diskspace = $result['diskspace']; + $traffic = $result['traffic']; + $ipaddress = $result['ip']; } else { - $password = $result['password']; + $deactivated = $this->getParam('deactivated', true, $result['deactivated']); + + $dec_places = Settings::Get('panel.decimal_places'); + $diskspace = $this->getUlParam('diskspace', 'diskspace_ul', true, round($result['diskspace'] / 1024, $dec_places)); + $traffic = $this->getUlParam('traffic', 'traffic_ul', true, round($result['traffic'] / (1024 * 1024), $dec_places)); + $customers = $this->getUlParam('customers', 'customers_ul', true, $result['customers']); + $domains = $this->getUlParam('domains', 'domains_ul', true, $result['domains']); + $subdomains = $this->getUlParam('subdomains', 'subdomains_ul', true, $result['subdomains']); + $emails = $this->getUlParam('emails', 'emails_ul', true, $result['emails']); + $email_accounts = $this->getUlParam('email_accounts', 'email_accounts_ul', true, $result['email_accounts']); + $email_forwarders = $this->getUlParam('email_forwarders', 'email_forwarders_ul', true, $result['email_forwarders']); + $email_quota = $this->getUlParam('email_quota', 'email_quota_ul', true, $result['email_quota']); + $ftps = $this->getUlParam('ftps', 'ftps_ul', true, $result['ftps']); + $tickets = $this->getUlParam('tickets', 'tickets_ul', true, $result['tickets']); + $mysqls = $this->getUlParam('mysqls', 'mysqls_ul', true, $result['mysqls']); + + $customers_see_all = $this->getParam('customers_see_all', true, $result['customers_see_all']); + $domains_see_all = $this->getParam('domains_see_all', true, $result['domains_see_all']); + $tickets_see_all = $this->getParam('tickets_see_all', true, $result['tickets_see_all']); + $caneditphpsettings = $this->getParam('caneditphpsettings', true, $result['caneditphpsettings']); + $change_serversettings = $this->getParam('change_serversettings', true, $result['change_serversettings']); + $ipaddress = intval_ressource($this->getParam('ipaddress', true, $result['ip'])); + + $diskspace = $diskspace * 1024; + $traffic = $traffic * 1024 * 1024; } - - // check if a resource was set to something lower - // than actually used by the admin/reseller - $res_warning = ""; - if ($customers != $result['customers'] && $customers != -1 && $customers < $result['customers_used']) { - $res_warning .= sprintf($this->lng['error']['setlessthanalreadyused'], 'customers'); + + // validation + $name = validate($name, 'name', '', '', array(), true); + $idna_convert = new idna_convert_wrapper(); + $email = $idna_convert->encode(validate($email, 'email', '', '', array(), true)); + $def_language = validate($def_language, 'default language', '', '', array(), true); + $custom_notes = validate(str_replace("\r\n", "\n", $custom_notes), 'custom_notes', '/^[^\0]*$/', '', array(), true); + $theme = validate($theme, 'theme', '', '', array(), true); + $password = validate($password, 'password', '', '', array(), true); + + if (Settings::Get('system.mail_quota_enabled') != '1') { + $email_quota = - 1; } - if ($domains != $result['domains'] && $domains != -1 && $domains < $result['domains_used']) { - $res_warning .= sprintf($this->lng['error']['setlessthanalreadyused'], 'domains'); + + if (Settings::Get('ticket.enabled') != '1') { + $tickets = - 1; } - if ($diskspace != $result['diskspace'] && ($diskspace / 1024) != -1 && $diskspace < $result['diskspace_used']) { - $res_warning .= sprintf($this->lng['error']['setlessthanalreadyused'], 'diskspace'); + + if (empty($theme)) { + $theme = Settings::Get('panel.default_theme'); } - if ($traffic != $result['traffic'] && ($traffic / 1024 / 1024) != -1 && $traffic < $result['traffic_used']) { - $res_warning .= sprintf($this->lng['error']['setlessthanalreadyused'], 'traffic'); + + if ($name == '') { + standard_error(array( + 'stringisempty', + 'myname' + ), '', true); + } elseif ($email == '') { + standard_error(array( + 'stringisempty', + 'emailadd' + ), '', true); + } elseif (! validateEmail($email)) { + standard_error('emailiswrong', $email, true); + } else { + + if ($deactivated != '1') { + $deactivated = '0'; + } + + if ($customers_see_all != '1') { + $customers_see_all = '0'; + } + + if ($domains_see_all != '1') { + $domains_see_all = '0'; + } + + if ($caneditphpsettings != '1') { + $caneditphpsettings = '0'; + } + + if ($change_serversettings != '1') { + $change_serversettings = '0'; + } + + if ($tickets_see_all != '1') { + $tickets_see_all = '0'; + } + + if ($password != '') { + $password = validatePassword($password, true); + $password = makeCryptPassword($password); + } else { + $password = $result['password']; + } + + // check if a resource was set to something lower + // than actually used by the admin/reseller + $res_warning = ""; + if ($customers != $result['customers'] && $customers != - 1 && $customers < $result['customers_used']) { + $res_warning .= sprintf($this->lng['error']['setlessthanalreadyused'], 'customers'); + } + if ($domains != $result['domains'] && $domains != - 1 && $domains < $result['domains_used']) { + $res_warning .= sprintf($this->lng['error']['setlessthanalreadyused'], 'domains'); + } + if ($diskspace != $result['diskspace'] && ($diskspace / 1024) != - 1 && $diskspace < $result['diskspace_used']) { + $res_warning .= sprintf($this->lng['error']['setlessthanalreadyused'], 'diskspace'); + } + if ($traffic != $result['traffic'] && ($traffic / 1024 / 1024) != - 1 && $traffic < $result['traffic_used']) { + $res_warning .= sprintf($this->lng['error']['setlessthanalreadyused'], 'traffic'); + } + if ($emails != $result['emails'] && $emails != - 1 && $emails < $result['emails_used']) { + $res_warning .= sprintf($this->lng['error']['setlessthanalreadyused'], 'emails'); + } + if ($email_accounts != $result['email_accounts'] && $email_accounts != - 1 && $email_accounts < $result['email_accounts_used']) { + $res_warning .= sprintf($this->lng['error']['setlessthanalreadyused'], 'email accounts'); + } + if ($email_forwarders != $result['email_forwarders'] && $email_forwarders != - 1 && $email_forwarders < $result['email_forwarders_used']) { + $res_warning .= sprintf($this->lng['error']['setlessthanalreadyused'], 'email forwarders'); + } + if ($email_quota != $result['email_quota'] && $email_quota != - 1 && $email_quota < $result['email_quota_used']) { + $res_warning .= sprintf($this->lng['error']['setlessthanalreadyused'], 'email quota'); + } + if ($ftps != $result['ftps'] && $ftps != - 1 && $ftps < $result['ftps_used']) { + $res_warning .= sprintf($this->lng['error']['setlessthanalreadyused'], 'ftps'); + } + if ($tickets != $result['tickets'] && $tickets != - 1 && $tickets < $result['tickets_used']) { + $res_warning .= sprintf($this->lng['error']['setlessthanalreadyused'], 'tickets'); + } + if ($mysqls != $result['mysqls'] && $mysqls != - 1 && $mysqls < $result['mysqls_used']) { + $res_warning .= sprintf($this->lng['error']['setlessthanalreadyused'], 'mysqls'); + } + + if (! empty($res_warning)) { + throw new Exception($res_warning, 406); + } + + $upd_data = array( + 'password' => $password, + 'name' => $name, + 'email' => $email, + 'lang' => $def_language, + 'change_serversettings' => $change_serversettings, + 'customers' => $customers, + 'customers_see_all' => $customers_see_all, + 'domains' => $domains, + 'domains_see_all' => $domains_see_all, + 'caneditphpsettings' => $caneditphpsettings, + 'diskspace' => $diskspace, + 'traffic' => $traffic, + 'subdomains' => $subdomains, + 'emails' => $emails, + 'accounts' => $email_accounts, + 'forwarders' => $email_forwarders, + 'quota' => $email_quota, + 'ftps' => $ftps, + 'tickets' => $tickets, + 'tickets_see_all' => $tickets_see_all, + 'mysqls' => $mysqls, + 'ip' => $ipaddress, + 'deactivated' => $deactivated, + 'custom_notes' => $custom_notes, + 'custom_notes_show' => $custom_notes_show, + 'theme' => $theme, + 'adminid' => $id + ); + + $upd_stmt = Database::prepare(" + UPDATE `" . TABLE_PANEL_ADMINS . "` SET + `password` = :password, + `name` = :name, + `email` = :email, + `def_language` = :lang, + `change_serversettings` = :change_serversettings, + `customers` = :customers, + `customers_see_all` = :customers_see_all, + `domains` = :domains, + `domains_see_all` = :domains_see_all, + `caneditphpsettings` = :caneditphpsettings, + `diskspace` = :diskspace, + `traffic` = :traffic, + `subdomains` = :subdomains, + `emails` = :emails, + `email_accounts` = :accounts, + `email_forwarders` = :forwarders, + `email_quota` = :quota, + `ftps` = :ftps, + `tickets` = :tickets, + `tickets_see_all` = :tickets_see_all, + `mysqls` = :mysqls, + `ip` = :ip, + `deactivated` = :deactivated, + `custom_notes` = :custom_notes, + `custom_notes_show` = :custom_notes_show, + `theme` = :theme + WHERE `adminid` = :adminid + "); + Database::pexecute($upd_stmt, $upd_data, true, true); + $this->logger()->logAction(ADM_ACTION, LOG_INFO, "[API] edited admin '" . $result['loginname'] . "'"); + + // get all admin-data for return-array + $json_result = Admins::getLocal($this->getUserData(), array( + 'id' => $adminid + ))->get(); + $result = json_decode($json_result, true)['data']; + return $this->response(200, "successfull", $result); } - if ($emails != $result['emails'] && $emails != -1 && $emails < $result['emails_used']) { - $res_warning .= sprintf($this->lng['error']['setlessthanalreadyused'], 'emails'); - } - if ($email_accounts != $result['email_accounts'] && $email_accounts != -1 && $email_accounts < $result['email_accounts_used']) { - $res_warning .= sprintf($this->lng['error']['setlessthanalreadyused'], 'email accounts'); - } - if ($email_forwarders != $result['email_forwarders'] && $email_forwarders != -1 && $email_forwarders < $result['email_forwarders_used']) { - $res_warning .= sprintf($this->lng['error']['setlessthanalreadyused'], 'email forwarders'); - } - if ($email_quota != $result['email_quota'] && $email_quota != -1 && $email_quota < $result['email_quota_used']) { - $res_warning .= sprintf($this->lng['error']['setlessthanalreadyused'], 'email quota'); - } - if ($ftps != $result['ftps'] && $ftps != -1 && $ftps < $result['ftps_used']) { - $res_warning .= sprintf($this->lng['error']['setlessthanalreadyused'], 'ftps'); - } - if ($tickets != $result['tickets'] && $tickets != -1 && $tickets < $result['tickets_used']) { - $res_warning .= sprintf($this->lng['error']['setlessthanalreadyused'], 'tickets'); - } - if ($mysqls != $result['mysqls'] && $mysqls != -1 && $mysqls < $result['mysqls_used']) { - $res_warning .= sprintf($this->lng['error']['setlessthanalreadyused'], 'mysqls'); - } - - if (!empty($res_warning)) { - throw new Exception($res_warning, 406); - } - - $upd_data = array( - 'password' => $password, - 'name' => $name, - 'email' => $email, - 'lang' => $def_language, - 'change_serversettings' => $change_serversettings, - 'customers' => $customers, - 'customers_see_all' => $customers_see_all, - 'domains' => $domains, - 'domains_see_all' => $domains_see_all, - 'caneditphpsettings' => $caneditphpsettings, - 'diskspace' => $diskspace, - 'traffic' => $traffic, - 'subdomains' => $subdomains, - 'emails' => $emails, - 'accounts' => $email_accounts, - 'forwarders' => $email_forwarders, - 'quota' => $email_quota, - 'ftps' => $ftps, - 'tickets' => $tickets, - 'tickets_see_all' => $tickets_see_all, - 'mysqls' => $mysqls, - 'ip' => $ipaddress, - 'deactivated' => $deactivated, - 'custom_notes' => $custom_notes, - 'custom_notes_show' => $custom_notes_show, - 'theme' => $theme, - 'adminid' => $id - ); - - $upd_stmt = Database::prepare(" - UPDATE `" . TABLE_PANEL_ADMINS . "` SET - `password` = :password, - `name` = :name, - `email` = :email, - `def_language` = :lang, - `change_serversettings` = :change_serversettings, - `customers` = :customers, - `customers_see_all` = :customers_see_all, - `domains` = :domains, - `domains_see_all` = :domains_see_all, - `caneditphpsettings` = :caneditphpsettings, - `diskspace` = :diskspace, - `traffic` = :traffic, - `subdomains` = :subdomains, - `emails` = :emails, - `email_accounts` = :accounts, - `email_forwarders` = :forwarders, - `email_quota` = :quota, - `ftps` = :ftps, - `tickets` = :tickets, - `tickets_see_all` = :tickets_see_all, - `mysqls` = :mysqls, - `ip` = :ip, - `deactivated` = :deactivated, - `custom_notes` = :custom_notes, - `custom_notes_show` = :custom_notes_show, - `theme` = :theme - WHERE `adminid` = :adminid - "); - Database::pexecute($upd_stmt, $upd_data, true, true); - $this->logger()->logAction(ADM_ACTION, LOG_INFO, "[API] edited admin '" . $result['loginname'] . "'"); - - // get all admin-data for return-array - $json_result = Admins::getLocal($this->getUserData(), array( - 'id' => $adminid - ))->get(); - $result = json_decode($json_result, true)['data']; - return $this->response(200, "successfull", $result); } } throw new Exception("Not allowed to execute given command.", 403); @@ -566,7 +572,7 @@ class Admins extends ApiCommand implements ResourceEntity * optional, the admin-id * @param string $loginname * optional, the loginname - * + * * @access admin * @throws Exception * @return array @@ -577,46 +583,58 @@ class Admins extends ApiCommand implements ResourceEntity $id = $this->getParam('id', true, 0); $ln_optional = ($id <= 0 ? false : true); $loginname = trim($this->getParam('loginname', $ln_optional, '')); - + $json_result = Admins::getLocal($this->getUserData(), array( 'id' => $id, 'loginname' => $loginname ))->get(); $result = json_decode($json_result, true)['data']; $id = $result['adminid']; - + // don't be stupid - if ($id == $this->getUserDetail('userid')) { + if ($id == $this->getUserDetail('adminid')) { standard_error('youcantdeleteyourself', '', true); } - + $del_stmt = Database::prepare(" DELETE FROM `" . TABLE_PANEL_ADMINS . "` WHERE `adminid` = :adminid "); - Database::pexecute($del_stmt, array('adminid' => $id), true, true); - + Database::pexecute($del_stmt, array( + 'adminid' => $id + ), true, true); + $del_stmt = Database::prepare(" DELETE FROM `" . TABLE_PANEL_TRAFFIC_ADMINS . "` WHERE `adminid` = :adminid "); - Database::pexecute($del_stmt, array('adminid' => $id), true, true); - + Database::pexecute($del_stmt, array( + 'adminid' => $id + ), true, true); + $del_stmt = Database::prepare(" DELETE FROM `" . TABLE_PANEL_DISKSPACE_ADMINS . "` WHERE `adminid` = :adminid "); - Database::pexecute($del_stmt, array('adminid' => $id), true, true); - + Database::pexecute($del_stmt, array( + 'adminid' => $id + ), true, true); + $upd_stmt = Database::prepare(" UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET `adminid` = :userid WHERE `adminid` = :adminid "); - Database::pexecute($upd_stmt, array('userid' => $this->getUserDetail('userid'), 'adminid' => $id), true, true); - + Database::pexecute($upd_stmt, array( + 'userid' => $this->getUserDetail('adminid'), + 'adminid' => $id + ), true, true); + $upd_stmt = Database::prepare(" UPDATE `" . TABLE_PANEL_DOMAINS . "` SET `adminid` = :userid WHERE `adminid` = :adminid "); - Database::pexecute($upd_stmt, array('userid' => $this->getUserDetail('userid'), 'adminid' => $id), true, true); - + Database::pexecute($upd_stmt, array( + 'userid' => $this->getUserDetail('adminid'), + 'adminid' => $id + ), true, true); + $this->logger()->logAction(ADM_ACTION, LOG_WARNING, "[API] deleted admin '" . $result['loginname'] . "'"); updateCounters(); return $this->response(200, "successfull", $result); @@ -631,7 +649,7 @@ class Admins extends ApiCommand implements ResourceEntity * optional, the admin-id * @param string $loginname * optional, the loginname - * + * * @access admin * @throws Exception * @return array @@ -642,7 +660,7 @@ class Admins extends ApiCommand implements ResourceEntity $id = $this->getParam('id', true, 0); $ln_optional = ($id <= 0 ? false : true); $loginname = trim($this->getParam('loginname', $ln_optional, '')); - + $json_result = Admins::getLocal($this->getUserData(), array( 'id' => $id, 'loginname' => $loginname From ae42e87a64059a42d82d389e822a73ecd255332c Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sun, 25 Feb 2018 21:26:51 +0100 Subject: [PATCH 0493/1335] fix in Admins.update; code-format Customers ApiCommand Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/commands/class.Admins.php | 2 +- lib/classes/api/commands/class.Customers.php | 80 ++++++++++---------- 2 files changed, 43 insertions(+), 39 deletions(-) diff --git a/lib/classes/api/commands/class.Admins.php b/lib/classes/api/commands/class.Admins.php index d6af8258..9c1f9407 100644 --- a/lib/classes/api/commands/class.Admins.php +++ b/lib/classes/api/commands/class.Admins.php @@ -555,7 +555,7 @@ class Admins extends ApiCommand implements ResourceEntity // get all admin-data for return-array $json_result = Admins::getLocal($this->getUserData(), array( - 'id' => $adminid + 'id' => $result['adminid'] ))->get(); $result = json_decode($json_result, true)['data']; return $this->response(200, "successfull", $result); diff --git a/lib/classes/api/commands/class.Customers.php b/lib/classes/api/commands/class.Customers.php index 8813adc2..b74dbd61 100644 --- a/lib/classes/api/commands/class.Customers.php +++ b/lib/classes/api/commands/class.Customers.php @@ -247,14 +247,18 @@ class Customers extends ApiCommand implements ResourceEntity $loginname_check_stmt = Database::prepare(" SELECT `loginname` FROM `" . TABLE_PANEL_CUSTOMERS . "` WHERE `loginname` = :login "); - $loginname_check = Database::pexecute_first($loginname_check_stmt, array('login' => $loginname), true, true); + $loginname_check = Database::pexecute_first($loginname_check_stmt, array( + 'login' => $loginname + ), true, true); // Check if an admin with the loginname already exists // do not check via api as we skip any permission checks for this task $loginname_check_admin_stmt = Database::prepare(" SELECT `loginname` FROM `" . TABLE_PANEL_ADMINS . "` WHERE `loginname` = :login "); - $loginname_check_admin = Database::pexecute_first($loginname_check_admin_stmt, array('login' => $loginname), true, true); + $loginname_check_admin = Database::pexecute_first($loginname_check_admin_stmt, array( + 'login' => $loginname + ), true, true); if (strtolower($loginname_check['loginname']) == strtolower($loginname) || strtolower($loginname_check_admin['loginname']) == strtolower($loginname)) { standard_error('loginnameexists', $loginname, true); @@ -428,7 +432,7 @@ class Customers extends ApiCommand implements ResourceEntity // update last account number Settings::Set('system.lastaccountnumber', $accountnumber, true); } - + $this->logger()->logAction(ADM_ACTION, LOG_INFO, "[API] added customer '" . $loginname . "'"); unset($ins_data); @@ -644,7 +648,7 @@ class Customers extends ApiCommand implements ResourceEntity } } $this->logger()->logAction(ADM_ACTION, LOG_WARNING, "[API] added customer '" . $loginname . "'"); - + $json_result = Customers::getLocal($this->getUserData(), array( 'loginname' => $loginname ))->get(); @@ -673,14 +677,14 @@ class Customers extends ApiCommand implements ResourceEntity $id = $this->getParam('id', true, 0); $ln_optional = ($id <= 0 ? false : true); $loginname = trim($this->getParam('loginname', $ln_optional, '')); - + $json_result = Customers::getLocal($this->getUserData(), array( 'id' => $id, 'loginname' => $loginname ))->get(); $result = json_decode($json_result, true)['data']; $id = $result['customerid']; - + if ($this->isAdmin()) { // parameters $move_to_admin = intval_ressource($this->getParam('move_to_admin', true, 0)); @@ -745,7 +749,7 @@ class Customers extends ApiCommand implements ResourceEntity $gender = $result['gender']; $custom_notes = $result['custom_notes']; $custom_notes_show = $result['custom_notes_show']; - + $dec_places = Settings::Get('panel.decimal_places'); $diskspace = round($result['diskspace'] / 1024, $dec_places); $traffic = round($result['traffic'] / (1024 * 1024), $dec_places); @@ -769,7 +773,7 @@ class Customers extends ApiCommand implements ResourceEntity $dnsenabled = $result['dnsenabled']; $deactivated = $result['deactivated']; } - + // validation $idna_convert = new idna_convert_wrapper(); $name = validate($name, 'name', '', '', array(), true); @@ -785,22 +789,22 @@ class Customers extends ApiCommand implements ResourceEntity $def_language = validate($def_language, 'default language', '', '', array(), true); $custom_notes = validate(str_replace("\r\n", "\n", $custom_notes), 'custom_notes', '/^[^\0]*$/', '', array(), true); $theme = validate($theme, 'theme', '', '', array(), true); - + if (Settings::Get('system.mail_quota_enabled') != '1') { $email_quota = - 1; } - + if (Settings::Get('ticket.enabled') != '1') { $tickets = - 1; } - + if (empty($theme)) { $theme = Settings::Get('panel.default_theme'); } - + $diskspace = $diskspace * 1024; $traffic = $traffic * 1024 * 1024; - + if ($this->isAdmin()) { if (((($this->getUserDetail('diskspace_used') + $diskspace - $result['diskspace']) > $this->getUserDetail('diskspace')) && ($this->getUserDetail('diskspace') / 1024) != '-1') || ((($this->getUserDetail('mysqls_used') + $mysqls - $result['mysqls']) > $this->getUserDetail('mysqls')) && $this->getUserDetail('mysqls') != '-1') || ((($this->getUserDetail('emails_used') + $emails - $result['emails']) > $this->getUserDetail('emails')) && $this->getUserDetail('emails') != '-1') || ((($this->getUserDetail('email_accounts_used') + $email_accounts - $result['email_accounts']) > $this->getUserDetail('email_accounts')) && $this->getUserDetail('email_accounts') != '-1') || ((($this->getUserDetail('email_forwarders_used') + $email_forwarders - $result['email_forwarders']) > $this->getUserDetail('email_forwarders')) && $this->getUserDetail('email_forwarders') != '-1') || ((($this->getUserDetail('email_quota_used') + $email_quota - $result['email_quota']) > $this->getUserDetail('email_quota')) && $this->getUserDetail('email_quota') != '-1' && Settings::Get('system.mail_quota_enabled') == '1') || ((($this->getUserDetail('ftps_used') + $ftps - $result['ftps']) > $this->getUserDetail('ftps')) && $this->getUserDetail('ftps') != '-1') || ((($this->getUserDetail('tickets_used') + $tickets - $result['tickets']) > $this->getUserDetail('tickets')) && $this->getUserDetail('tickets') != '-1') || ((($this->getUserDetail('subdomains_used') + $subdomains - $result['subdomains']) > $this->getUserDetail('subdomains')) && $this->getUserDetail('subdomains') != '-1') || (($diskspace / 1024) == '-1' && ($this->getUserDetail('diskspace') / 1024) != '-1') || ($mysqls == '-1' && $this->getUserDetail('mysqls') != '-1') || ($emails == '-1' && $this->getUserDetail('emails') != '-1') || ($email_accounts == '-1' && $this->getUserDetail('email_accounts') != '-1') || ($email_forwarders == '-1' && $this->getUserDetail('email_forwarders') != '-1') || ($email_quota == '-1' && $this->getUserDetail('email_quota') != '-1' && Settings::Get('system.mail_quota_enabled') == '1') || ($ftps == '-1' && $this->getUserDetail('ftps') != '-1') || ($tickets == '-1' && $this->getUserDetail('tickets') != '-1') || ($subdomains == '-1' && $this->getUserDetail('subdomains') != '-1')) { standard_error('youcantallocatemorethanyouhave', '', true); @@ -826,18 +830,18 @@ class Customers extends ApiCommand implements ResourceEntity standard_error('emailiswrong', $email, true); } } - + if ($password != '') { $password = validatePassword($password, true); $password = makeCryptPassword($password); } else { $password = $result['password']; } - + if ($createstdsubdomain != '1') { $createstdsubdomain = '0'; } - + if ($createstdsubdomain == '1' && $result['standardsubdomain'] == '0') { if (Settings::Get('system.stdsubdomain') !== null && Settings::Get('system.stdsubdomain') != '') { @@ -874,7 +878,7 @@ class Customers extends ApiCommand implements ResourceEntity inserttask('1'); } } - + if ($createstdsubdomain == '0' && $result['standardsubdomain'] != '0') { try { $std_domain = Domains::getLocal($this->getUserData(), array( @@ -887,27 +891,27 @@ class Customers extends ApiCommand implements ResourceEntity $this->logger()->logAction(ADM_ACTION, LOG_NOTICE, "[API] automatically deleted standardsubdomain for user '" . $result['loginname'] . "'"); inserttask('1'); } - + if ($deactivated != '1') { $deactivated = '0'; } - + if ($phpenabled != '0') { $phpenabled = '1'; } - + if ($perlenabled != '0') { $perlenabled = '1'; } - + if ($dnsenabled != '0') { $dnsenabled = '1'; } - + if ($phpenabled != $result['phpenabled'] || $perlenabled != $result['perlenabled']) { inserttask('1'); } - + // activate/deactivate customer services if ($deactivated != $result['deactivated']) { @@ -924,7 +928,7 @@ class Customers extends ApiCommand implements ResourceEntity 'imap' => $imap, 'customerid' => $id )); - + $upd_stmt = Database::prepare(" UPDATE `" . TABLE_FTP_USERS . "` SET `login_enabled` = :yesno WHERE `customerid` = :customerid "); @@ -932,37 +936,37 @@ class Customers extends ApiCommand implements ResourceEntity 'yesno' => $yesno, 'customerid' => $id )); - + $upd_stmt = Database::prepare(" UPDATE `" . TABLE_PANEL_DOMAINS . "` SET `deactivated`= :deactivated WHERE `customerid` = :customerid"); Database::pexecute($upd_stmt, array( 'deactivated' => $deactivated, 'customerid' => $id )); - + // Retrieve customer's databases $databases_stmt = Database::prepare("SELECT * FROM " . TABLE_PANEL_DATABASES . " WHERE customerid = :customerid ORDER BY `dbserver`"); Database::pexecute($databases_stmt, array( 'customerid' => $id )); - + Database::needRoot(true); $last_dbserver = 0; - + $dbm = new DbManager($this->logger()); - + // For each of them while ($row_database = $databases_stmt->fetch(PDO::FETCH_ASSOC)) { - + if ($last_dbserver != $row_database['dbserver']) { $dbm->getManager()->flushPrivileges(); Database::needRoot(true, $row_database['dbserver']); $last_dbserver = $row_database['dbserver']; } - + foreach (array_unique(explode(',', Settings::Get('system.mysql_access_host'))) as $mysql_access_host) { $mysql_access_host = trim($mysql_access_host); - + // Prevent access, if deactivated if ($deactivated) { // failsafe if user has been deleted manually (requires MySQL 4.1.2+) @@ -973,7 +977,7 @@ class Customers extends ApiCommand implements ResourceEntity } } } - + // At last flush the new privileges $dbm->getManager()->flushPrivileges(); Database::needRoot(false); @@ -981,7 +985,7 @@ class Customers extends ApiCommand implements ResourceEntity $this->logger()->logAction(ADM_ACTION, LOG_INFO, "[API] deactivated user '" . $result['loginname'] . "'"); inserttask('1'); } - + // Disable or enable POP3 Login for customers Mail Accounts if ($email_pop3 != $result['pop3']) { $upd_stmt = Database::prepare("UPDATE `" . TABLE_MAIL_USERS . "` SET `pop3` = :pop3 WHERE `customerid` = :customerid"); @@ -990,7 +994,7 @@ class Customers extends ApiCommand implements ResourceEntity 'customerid' => $id )); } - + // Disable or enable IMAP Login for customers Mail Accounts if ($email_imap != $result['imap']) { $upd_stmt = Database::prepare("UPDATE `" . TABLE_MAIL_USERS . "` SET `imap` = :imap WHERE `customerid` = :customerid"); @@ -999,7 +1003,7 @@ class Customers extends ApiCommand implements ResourceEntity 'customerid' => $id )); } - + $upd_data = array( 'customerid' => $id, 'passwd' => $password, @@ -1074,7 +1078,7 @@ class Customers extends ApiCommand implements ResourceEntity WHERE `customerid` = :customerid "); Database::pexecute($upd_stmt, $upd_data); - + if ($this->isAdmin()) { // Using filesystem - quota, insert a task which cleans the filesystem - quota inserttask('10'); @@ -1199,7 +1203,7 @@ class Customers extends ApiCommand implements ResourceEntity standard_error('moveofcustomerfailed', $move_result, true); } } - + $json_result = Customers::getLocal($this->getUserData(), array( 'id' => $result['customerid'] ))->get(); From 4a1decf359a921778376146052d2499a8332e148 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 26 Feb 2018 07:51:44 +0100 Subject: [PATCH 0494/1335] do not update fields of customer a customer cannot even change; unset custom_notes when admin of customer set custom_notes_show to 0 Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/commands/class.Customers.php | 468 +++++++++---------- 1 file changed, 225 insertions(+), 243 deletions(-) diff --git a/lib/classes/api/commands/class.Customers.php b/lib/classes/api/commands/class.Customers.php index b74dbd61..529f401a 100644 --- a/lib/classes/api/commands/class.Customers.php +++ b/lib/classes/api/commands/class.Customers.php @@ -95,6 +95,10 @@ class Customers extends ApiCommand implements ResourceEntity } $result = Database::pexecute_first($result_stmt, $params, true, true); if ($result) { + // check whether the admin does not want the customer to see the notes + if (! $this->isAdmin() && $result['custom_notes_show'] != 1) { + $result['custom_notes'] = ""; + } $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_NOTICE, "[API] get customer '" . $result['loginname'] . "'"); return $this->response(200, "successfull", $result); } @@ -732,62 +736,24 @@ class Customers extends ApiCommand implements ResourceEntity $def_language = $this->getParam('def_language', true, $result['def_language']); $password = $this->getParam('new_customer_password', true, ''); $theme = $this->getParam('theme', true, $result['theme']); - - // unchangeable parameters for customers - $move_to_admin = 0; - $idna_convert = new idna_convert_wrapper(); - $email = $idna_convert->decode($result['email']); - $name = $result['name']; - $firstname = $result['firstname']; - $company = $result['company']; - $street = $result['street']; - $zipcode = $result['zipcode']; - $city = $result['city']; - $phone = $result['phone']; - $fax = $result['fax']; - $customernumber = $result['customernumber']; - $gender = $result['gender']; - $custom_notes = $result['custom_notes']; - $custom_notes_show = $result['custom_notes_show']; - - $dec_places = Settings::Get('panel.decimal_places'); - $diskspace = round($result['diskspace'] / 1024, $dec_places); - $traffic = round($result['traffic'] / (1024 * 1024), $dec_places); - $subdomains = $result['subdomains']; - $emails = $result['emails']; - $email_accounts = $result['email_accounts']; - $email_forwarders = $result['email_forwarders']; - $email_quota = $result['email_quota']; - $email_imap = $result['imap']; - $email_pop3 = $result['pop3']; - $ftps = $result['ftps']; - $tickets = $result['tickets']; - $mysqls = $result['mysqls']; - // if we got one, it's true so none will be generated (it exists already) - // if not, none will be generated - $createstdsubdomain = ($result['standardsubdomain'] > 0 ? 1 : 0); - $sendpassword = 0; - $phpenabled = $result['phpenabled']; - $allowed_phpconfigs = json_decode($result['allowed_phpconfigs'], true); - $perlenabled = $result['perlenabled']; - $dnsenabled = $result['dnsenabled']; - $deactivated = $result['deactivated']; } // validation - $idna_convert = new idna_convert_wrapper(); - $name = validate($name, 'name', '', '', array(), true); - $firstname = validate($firstname, 'first name', '', '', array(), true); - $company = validate($company, 'company', '', '', array(), true); - $street = validate($street, 'street', '', '', array(), true); - $zipcode = validate($zipcode, 'zipcode', '/^[0-9 \-A-Z]*$/', '', array(), true); - $city = validate($city, 'city', '', '', array(), true); - $phone = validate($phone, 'phone', '/^[0-9\- \+\(\)\/]*$/', '', array(), true); - $fax = validate($fax, 'fax', '/^[0-9\- \+\(\)\/]*$/', '', array(), true); - $email = $idna_convert->encode(validate($email, 'email', '', '', array(), true)); - $customernumber = validate($customernumber, 'customer number', '/^[A-Za-z0-9 \-]*$/Di', '', array(), true); + if ($this->isAdmin()) { + $idna_convert = new idna_convert_wrapper(); + $name = validate($name, 'name', '', '', array(), true); + $firstname = validate($firstname, 'first name', '', '', array(), true); + $company = validate($company, 'company', '', '', array(), true); + $street = validate($street, 'street', '', '', array(), true); + $zipcode = validate($zipcode, 'zipcode', '/^[0-9 \-A-Z]*$/', '', array(), true); + $city = validate($city, 'city', '', '', array(), true); + $phone = validate($phone, 'phone', '/^[0-9\- \+\(\)\/]*$/', '', array(), true); + $fax = validate($fax, 'fax', '/^[0-9\- \+\(\)\/]*$/', '', array(), true); + $email = $idna_convert->encode(validate($email, 'email', '', '', array(), true)); + $customernumber = validate($customernumber, 'customer number', '/^[A-Za-z0-9 \-]*$/Di', '', array(), true); + $custom_notes = validate(str_replace("\r\n", "\n", $custom_notes), 'custom_notes', '/^[^\0]*$/', '', array(), true); + } $def_language = validate($def_language, 'default language', '', '', array(), true); - $custom_notes = validate(str_replace("\r\n", "\n", $custom_notes), 'custom_notes', '/^[^\0]*$/', '', array(), true); $theme = validate($theme, 'theme', '', '', array(), true); if (Settings::Get('system.mail_quota_enabled') != '1') { @@ -802,10 +768,11 @@ class Customers extends ApiCommand implements ResourceEntity $theme = Settings::Get('panel.default_theme'); } - $diskspace = $diskspace * 1024; - $traffic = $traffic * 1024 * 1024; - if ($this->isAdmin()) { + + $diskspace = $diskspace * 1024; + $traffic = $traffic * 1024 * 1024; + if (((($this->getUserDetail('diskspace_used') + $diskspace - $result['diskspace']) > $this->getUserDetail('diskspace')) && ($this->getUserDetail('diskspace') / 1024) != '-1') || ((($this->getUserDetail('mysqls_used') + $mysqls - $result['mysqls']) > $this->getUserDetail('mysqls')) && $this->getUserDetail('mysqls') != '-1') || ((($this->getUserDetail('emails_used') + $emails - $result['emails']) > $this->getUserDetail('emails')) && $this->getUserDetail('emails') != '-1') || ((($this->getUserDetail('email_accounts_used') + $email_accounts - $result['email_accounts']) > $this->getUserDetail('email_accounts')) && $this->getUserDetail('email_accounts') != '-1') || ((($this->getUserDetail('email_forwarders_used') + $email_forwarders - $result['email_forwarders']) > $this->getUserDetail('email_forwarders')) && $this->getUserDetail('email_forwarders') != '-1') || ((($this->getUserDetail('email_quota_used') + $email_quota - $result['email_quota']) > $this->getUserDetail('email_quota')) && $this->getUserDetail('email_quota') != '-1' && Settings::Get('system.mail_quota_enabled') == '1') || ((($this->getUserDetail('ftps_used') + $ftps - $result['ftps']) > $this->getUserDetail('ftps')) && $this->getUserDetail('ftps') != '-1') || ((($this->getUserDetail('tickets_used') + $tickets - $result['tickets']) > $this->getUserDetail('tickets')) && $this->getUserDetail('tickets') != '-1') || ((($this->getUserDetail('subdomains_used') + $subdomains - $result['subdomains']) > $this->getUserDetail('subdomains')) && $this->getUserDetail('subdomains') != '-1') || (($diskspace / 1024) == '-1' && ($this->getUserDetail('diskspace') / 1024) != '-1') || ($mysqls == '-1' && $this->getUserDetail('mysqls') != '-1') || ($emails == '-1' && $this->getUserDetail('emails') != '-1') || ($email_accounts == '-1' && $this->getUserDetail('email_accounts') != '-1') || ($email_forwarders == '-1' && $this->getUserDetail('email_forwarders') != '-1') || ($email_quota == '-1' && $this->getUserDetail('email_quota') != '-1' && Settings::Get('system.mail_quota_enabled') == '1') || ($ftps == '-1' && $this->getUserDetail('ftps') != '-1') || ($tickets == '-1' && $this->getUserDetail('tickets') != '-1') || ($subdomains == '-1' && $this->getUserDetail('subdomains') != '-1')) { standard_error('youcantallocatemorethanyouhave', '', true); } @@ -838,210 +805,224 @@ class Customers extends ApiCommand implements ResourceEntity $password = $result['password']; } - if ($createstdsubdomain != '1') { - $createstdsubdomain = '0'; - } - - if ($createstdsubdomain == '1' && $result['standardsubdomain'] == '0') { - - if (Settings::Get('system.stdsubdomain') !== null && Settings::Get('system.stdsubdomain') != '') { - $_stdsubdomain = $result['loginname'] . '.' . Settings::Get('system.stdsubdomain'); - } else { - $_stdsubdomain = $result['loginname'] . '.' . Settings::Get('system.hostname'); + if ($this->isAdmin()) { + if ($createstdsubdomain != '1') { + $createstdsubdomain = '0'; } - $ins_data = array( - 'domain' => $_stdsubdomain, - 'customerid' => $result['customerid'], - 'adminid' => $this->getUserDetail('adminid'), - 'docroot' => $result['documentroot'], - 'phpenabled' => $phpenabled, - 'openbasedir' => '1' - ); - $domainid = - 1; - try { - $std_domain = Domains::getLocal($this->getUserData(), $ins_data)->add(); - $domainid = json_decode($std_domain, true)['data']['id']; - } catch (Exception $e) { - $this->logger()->logAction(ADM_ACTION, LOG_ERR, "[API] Unable to add standard-subdomain: " . $e->getMessage()); - } - - if ($domainid > 0) { - $upd_stmt = Database::prepare(" + if ($createstdsubdomain == '1' && $result['standardsubdomain'] == '0') { + + if (Settings::Get('system.stdsubdomain') !== null && Settings::Get('system.stdsubdomain') != '') { + $_stdsubdomain = $result['loginname'] . '.' . Settings::Get('system.stdsubdomain'); + } else { + $_stdsubdomain = $result['loginname'] . '.' . Settings::Get('system.hostname'); + } + + $ins_data = array( + 'domain' => $_stdsubdomain, + 'customerid' => $result['customerid'], + 'adminid' => $this->getUserDetail('adminid'), + 'docroot' => $result['documentroot'], + 'phpenabled' => $phpenabled, + 'openbasedir' => '1' + ); + $domainid = - 1; + try { + $std_domain = Domains::getLocal($this->getUserData(), $ins_data)->add(); + $domainid = json_decode($std_domain, true)['data']['id']; + } catch (Exception $e) { + $this->logger()->logAction(ADM_ACTION, LOG_ERR, "[API] Unable to add standard-subdomain: " . $e->getMessage()); + } + + if ($domainid > 0) { + $upd_stmt = Database::prepare(" UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET `standardsubdomain` = :domainid WHERE `customerid` = :customerid "); - Database::pexecute($upd_stmt, array( - 'domainid' => $domainid, - 'customerid' => $result['customerid'] - ), true, true); - $this->logger()->logAction(ADM_ACTION, LOG_NOTICE, "[API] automatically added standardsubdomain for user '" . $result['loginname'] . "'"); + Database::pexecute($upd_stmt, array( + 'domainid' => $domainid, + 'customerid' => $result['customerid'] + ), true, true); + $this->logger()->logAction(ADM_ACTION, LOG_NOTICE, "[API] automatically added standardsubdomain for user '" . $result['loginname'] . "'"); + inserttask('1'); + } + } + + if ($createstdsubdomain == '0' && $result['standardsubdomain'] != '0') { + try { + $std_domain = Domains::getLocal($this->getUserData(), array( + 'id' => $result['standardsubdomain'], + 'is_stdsubdomain' => 1 + ))->delete(); + } catch (Exception $e) { + $this->logger()->logAction(ADM_ACTION, LOG_ERR, "[API] Unable to delete standard-subdomain: " . $e->getMessage()); + } + $this->logger()->logAction(ADM_ACTION, LOG_NOTICE, "[API] automatically deleted standardsubdomain for user '" . $result['loginname'] . "'"); inserttask('1'); } - } - - if ($createstdsubdomain == '0' && $result['standardsubdomain'] != '0') { - try { - $std_domain = Domains::getLocal($this->getUserData(), array( - 'id' => $result['standardsubdomain'], - 'is_stdsubdomain' => 1 - ))->delete(); - } catch (Exception $e) { - $this->logger()->logAction(ADM_ACTION, LOG_ERR, "[API] Unable to delete standard-subdomain: " . $e->getMessage()); + + if ($deactivated != '1') { + $deactivated = '0'; } - $this->logger()->logAction(ADM_ACTION, LOG_NOTICE, "[API] automatically deleted standardsubdomain for user '" . $result['loginname'] . "'"); - inserttask('1'); - } - - if ($deactivated != '1') { - $deactivated = '0'; - } - - if ($phpenabled != '0') { - $phpenabled = '1'; - } - - if ($perlenabled != '0') { - $perlenabled = '1'; - } - - if ($dnsenabled != '0') { - $dnsenabled = '1'; - } - - if ($phpenabled != $result['phpenabled'] || $perlenabled != $result['perlenabled']) { - inserttask('1'); - } - - // activate/deactivate customer services - if ($deactivated != $result['deactivated']) { - $yesno = (($deactivated) ? 'N' : 'Y'); - $pop3 = (($deactivated) ? '0' : (int) $result['pop3']); - $imap = (($deactivated) ? '0' : (int) $result['imap']); + if ($phpenabled != '0') { + $phpenabled = '1'; + } - $upd_stmt = Database::prepare(" + if ($perlenabled != '0') { + $perlenabled = '1'; + } + + if ($dnsenabled != '0') { + $dnsenabled = '1'; + } + + if ($phpenabled != $result['phpenabled'] || $perlenabled != $result['perlenabled']) { + inserttask('1'); + } + + // activate/deactivate customer services + if ($deactivated != $result['deactivated']) { + + $yesno = (($deactivated) ? 'N' : 'Y'); + $pop3 = (($deactivated) ? '0' : (int) $result['pop3']); + $imap = (($deactivated) ? '0' : (int) $result['imap']); + + $upd_stmt = Database::prepare(" UPDATE `" . TABLE_MAIL_USERS . "` SET `postfix`= :yesno, `pop3` = :pop3, `imap` = :imap WHERE `customerid` = :customerid "); - Database::pexecute($upd_stmt, array( - 'yesno' => $yesno, - 'pop3' => $pop3, - 'imap' => $imap, - 'customerid' => $id - )); - - $upd_stmt = Database::prepare(" + Database::pexecute($upd_stmt, array( + 'yesno' => $yesno, + 'pop3' => $pop3, + 'imap' => $imap, + 'customerid' => $id + )); + + $upd_stmt = Database::prepare(" UPDATE `" . TABLE_FTP_USERS . "` SET `login_enabled` = :yesno WHERE `customerid` = :customerid "); - Database::pexecute($upd_stmt, array( - 'yesno' => $yesno, - 'customerid' => $id - )); - - $upd_stmt = Database::prepare(" + Database::pexecute($upd_stmt, array( + 'yesno' => $yesno, + 'customerid' => $id + )); + + $upd_stmt = Database::prepare(" UPDATE `" . TABLE_PANEL_DOMAINS . "` SET `deactivated`= :deactivated WHERE `customerid` = :customerid"); - Database::pexecute($upd_stmt, array( - 'deactivated' => $deactivated, - 'customerid' => $id - )); - - // Retrieve customer's databases - $databases_stmt = Database::prepare("SELECT * FROM " . TABLE_PANEL_DATABASES . " WHERE customerid = :customerid ORDER BY `dbserver`"); - Database::pexecute($databases_stmt, array( - 'customerid' => $id - )); - - Database::needRoot(true); - $last_dbserver = 0; - - $dbm = new DbManager($this->logger()); - - // For each of them - while ($row_database = $databases_stmt->fetch(PDO::FETCH_ASSOC)) { + Database::pexecute($upd_stmt, array( + 'deactivated' => $deactivated, + 'customerid' => $id + )); - if ($last_dbserver != $row_database['dbserver']) { - $dbm->getManager()->flushPrivileges(); - Database::needRoot(true, $row_database['dbserver']); - $last_dbserver = $row_database['dbserver']; - } + // Retrieve customer's databases + $databases_stmt = Database::prepare("SELECT * FROM " . TABLE_PANEL_DATABASES . " WHERE customerid = :customerid ORDER BY `dbserver`"); + Database::pexecute($databases_stmt, array( + 'customerid' => $id + )); - foreach (array_unique(explode(',', Settings::Get('system.mysql_access_host'))) as $mysql_access_host) { - $mysql_access_host = trim($mysql_access_host); + Database::needRoot(true); + $last_dbserver = 0; + + $dbm = new DbManager($this->logger()); + + // For each of them + while ($row_database = $databases_stmt->fetch(PDO::FETCH_ASSOC)) { - // Prevent access, if deactivated - if ($deactivated) { - // failsafe if user has been deleted manually (requires MySQL 4.1.2+) - $dbm->getManager()->disableUser($row_database['databasename'], $mysql_access_host); - } else { - // Otherwise grant access - $dbm->getManager()->enableUser($row_database['databasename'], $mysql_access_host); + if ($last_dbserver != $row_database['dbserver']) { + $dbm->getManager()->flushPrivileges(); + Database::needRoot(true, $row_database['dbserver']); + $last_dbserver = $row_database['dbserver']; + } + + foreach (array_unique(explode(',', Settings::Get('system.mysql_access_host'))) as $mysql_access_host) { + $mysql_access_host = trim($mysql_access_host); + + // Prevent access, if deactivated + if ($deactivated) { + // failsafe if user has been deleted manually (requires MySQL 4.1.2+) + $dbm->getManager()->disableUser($row_database['databasename'], $mysql_access_host); + } else { + // Otherwise grant access + $dbm->getManager()->enableUser($row_database['databasename'], $mysql_access_host); + } } } + + // At last flush the new privileges + $dbm->getManager()->flushPrivileges(); + Database::needRoot(false); + + $this->logger()->logAction(ADM_ACTION, LOG_INFO, "[API] deactivated user '" . $result['loginname'] . "'"); + inserttask('1'); } - // At last flush the new privileges - $dbm->getManager()->flushPrivileges(); - Database::needRoot(false); + // Disable or enable POP3 Login for customers Mail Accounts + if ($email_pop3 != $result['pop3']) { + $upd_stmt = Database::prepare("UPDATE `" . TABLE_MAIL_USERS . "` SET `pop3` = :pop3 WHERE `customerid` = :customerid"); + Database::pexecute($upd_stmt, array( + 'pop3' => $email_pop3, + 'customerid' => $id + )); + } - $this->logger()->logAction(ADM_ACTION, LOG_INFO, "[API] deactivated user '" . $result['loginname'] . "'"); - inserttask('1'); - } - - // Disable or enable POP3 Login for customers Mail Accounts - if ($email_pop3 != $result['pop3']) { - $upd_stmt = Database::prepare("UPDATE `" . TABLE_MAIL_USERS . "` SET `pop3` = :pop3 WHERE `customerid` = :customerid"); - Database::pexecute($upd_stmt, array( - 'pop3' => $email_pop3, - 'customerid' => $id - )); - } - - // Disable or enable IMAP Login for customers Mail Accounts - if ($email_imap != $result['imap']) { - $upd_stmt = Database::prepare("UPDATE `" . TABLE_MAIL_USERS . "` SET `imap` = :imap WHERE `customerid` = :customerid"); - Database::pexecute($upd_stmt, array( - 'imap' => $email_imap, - 'customerid' => $id - )); + // Disable or enable IMAP Login for customers Mail Accounts + if ($email_imap != $result['imap']) { + $upd_stmt = Database::prepare("UPDATE `" . TABLE_MAIL_USERS . "` SET `imap` = :imap WHERE `customerid` = :customerid"); + Database::pexecute($upd_stmt, array( + 'imap' => $email_imap, + 'customerid' => $id + )); + } } $upd_data = array( 'customerid' => $id, 'passwd' => $password, - 'name' => $name, - 'firstname' => $firstname, - 'gender' => $gender, - 'company' => $company, - 'street' => $street, - 'zipcode' => $zipcode, - 'city' => $city, - 'phone' => $phone, - 'fax' => $fax, - 'email' => $email, - 'customerno' => $customernumber, 'lang' => $def_language, - 'diskspace' => $diskspace, - 'traffic' => $traffic, - 'subdomains' => $subdomains, - 'emails' => $emails, - 'email_accounts' => $email_accounts, - 'email_forwarders' => $email_forwarders, - 'email_quota' => $email_quota, - 'ftps' => $ftps, - 'tickets' => $tickets, - 'mysqls' => $mysqls, - 'deactivated' => $deactivated, - 'phpenabled' => $phpenabled, - 'allowed_phpconfigs' => empty($allowed_phpconfigs) ? "" : json_encode($allowed_phpconfigs), - 'imap' => $email_imap, - 'pop3' => $email_pop3, - 'perlenabled' => $perlenabled, - 'dnsenabled' => $dnsenabled, - 'custom_notes' => $custom_notes, - 'custom_notes_show' => $custom_notes_show, 'theme' => $theme ); - $upd_stmt = Database::prepare(" - UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET + + if ($this->isAdmin()) { + $admin_upd_data = array( + 'name' => $name, + 'firstname' => $firstname, + 'gender' => $gender, + 'company' => $company, + 'street' => $street, + 'zipcode' => $zipcode, + 'city' => $city, + 'phone' => $phone, + 'fax' => $fax, + 'email' => $email, + 'customerno' => $customernumber, + 'diskspace' => $diskspace, + 'traffic' => $traffic, + 'subdomains' => $subdomains, + 'emails' => $emails, + 'email_accounts' => $email_accounts, + 'email_forwarders' => $email_forwarders, + 'email_quota' => $email_quota, + 'ftps' => $ftps, + 'tickets' => $tickets, + 'mysqls' => $mysqls, + 'deactivated' => $deactivated, + 'phpenabled' => $phpenabled, + 'allowed_phpconfigs' => empty($allowed_phpconfigs) ? "" : json_encode($allowed_phpconfigs), + 'imap' => $email_imap, + 'pop3' => $email_pop3, + 'perlenabled' => $perlenabled, + 'dnsenabled' => $dnsenabled, + 'custom_notes' => $custom_notes, + 'custom_notes_show' => $custom_notes_show + ); + $upd_data = $upd_data + $admin_upd_data; + } + + $upd_query = "UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET + `def_language` = :lang, + `password` = :passwd, + `theme` = :theme"; + + if ($this->isAdmin()) { + $admin_upd_query = ", `name` = :name, `firstname` = :firstname, `gender` = :gender, @@ -1053,8 +1034,6 @@ class Customers extends ApiCommand implements ResourceEntity `fax` = :fax, `email` = :email, `customernumber` = :customerno, - `def_language` = :lang, - `password` = :passwd, `diskspace` = :diskspace, `traffic` = :traffic, `subdomains` = :subdomains, @@ -1073,10 +1052,11 @@ class Customers extends ApiCommand implements ResourceEntity `perlenabled` = :perlenabled, `dnsenabled` = :dnsenabled, `custom_notes` = :custom_notes, - `custom_notes_show` = :custom_notes_show, - `theme` = :theme - WHERE `customerid` = :customerid - "); + `custom_notes_show` = :custom_notes_show"; + $upd_query .= $admin_upd_query; + } + $upd_query .= " WHERE `customerid` = :customerid"; + $upd_stmt = Database::prepare($upd_query); Database::pexecute($upd_stmt, $upd_data); if ($this->isAdmin()) { @@ -1193,14 +1173,16 @@ class Customers extends ApiCommand implements ResourceEntity /* * move customer to another admin/reseller; #1166 */ - if ($move_to_admin > 0 && $move_to_admin != $result['adminid']) { - $json_result = Customers::getLocal($this->getUserData(), array( - 'id' => $result['customerid'], - 'adminid' => $move_to_admin - ))->move(); - $move_result = json_decode($json_result, true)['data']; - if ($move_result != true) { - standard_error('moveofcustomerfailed', $move_result, true); + if ($this->isAdmin()) { + if ($move_to_admin > 0 && $move_to_admin != $result['adminid']) { + $json_result = Customers::getLocal($this->getUserData(), array( + 'id' => $result['customerid'], + 'adminid' => $move_to_admin + ))->move(); + $move_result = json_decode($json_result, true)['data']; + if ($move_result != true) { + standard_error('moveofcustomerfailed', $move_result, true); + } } } From 592c9ed0b9d42ffb9000ab0be41f39cbf989cc13 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 26 Feb 2018 08:21:56 +0100 Subject: [PATCH 0495/1335] automatically trim() all parameters given Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/abstract.ApiCommand.php | 15 ++++++++++++++ lib/classes/api/commands/class.Admins.php | 8 ++++---- lib/classes/api/commands/class.Customers.php | 21 +++++--------------- lib/classes/api/commands/class.Domains.php | 14 ++++++------- lib/classes/api/commands/class.Ftps.php | 2 +- lib/classes/api/commands/class.Mysqls.php | 6 +++--- 6 files changed, 35 insertions(+), 31 deletions(-) diff --git a/lib/classes/api/abstract.ApiCommand.php b/lib/classes/api/abstract.ApiCommand.php index afdba0c5..c11938bb 100644 --- a/lib/classes/api/abstract.ApiCommand.php +++ b/lib/classes/api/abstract.ApiCommand.php @@ -106,6 +106,10 @@ abstract class ApiCommand $this->version = $version; $this->dbversion = $dbversion; $this->branding = $branding; + + if (! is_null($params)) { + $params = $this->trimArray($params); + } $this->cmd_params = $params; if (! empty($header)) { $this->readUserData($header); @@ -463,4 +467,15 @@ abstract class ApiCommand } throw new Exception("Invalid API credentials", 400); } + + private function trimArray($input) + { + if (! is_array($input)) { + return trim($input); + } + return array_map(array( + $this, + 'trimArray' + ), $input); + } } diff --git a/lib/classes/api/commands/class.Admins.php b/lib/classes/api/commands/class.Admins.php index 9c1f9407..12f648e3 100644 --- a/lib/classes/api/commands/class.Admins.php +++ b/lib/classes/api/commands/class.Admins.php @@ -63,7 +63,7 @@ class Admins extends ApiCommand implements ResourceEntity { $id = $this->getParam('id', true, 0); $ln_optional = ($id <= 0 ? false : true); - $loginname = trim($this->getParam('loginname', $ln_optional, '')); + $loginname = $this->getParam('loginname', $ln_optional, ''); if ($this->isAdmin() && ($this->getUserDetail('change_serversettings') == 1 || ($this->getUserDetail('adminid') == $id || $this->getUserDetail('loginname') == $loginname))) { $result_stmt = Database::prepare(" @@ -314,7 +314,7 @@ class Admins extends ApiCommand implements ResourceEntity $id = $this->getParam('id', true, 0); $ln_optional = ($id <= 0 ? false : true); - $loginname = trim($this->getParam('loginname', $ln_optional, '')); + $loginname = $this->getParam('loginname', $ln_optional, ''); $json_result = Admins::getLocal($this->getUserData(), array( 'id' => $id, @@ -582,7 +582,7 @@ class Admins extends ApiCommand implements ResourceEntity if ($this->isAdmin() && $this->getUserDetail('change_serversettings') == 1) { $id = $this->getParam('id', true, 0); $ln_optional = ($id <= 0 ? false : true); - $loginname = trim($this->getParam('loginname', $ln_optional, '')); + $loginname = $this->getParam('loginname', $ln_optional, ''); $json_result = Admins::getLocal($this->getUserData(), array( 'id' => $id, @@ -659,7 +659,7 @@ class Admins extends ApiCommand implements ResourceEntity if ($this->isAdmin() && $this->getUserDetail('change_serversettings') == 1) { $id = $this->getParam('id', true, 0); $ln_optional = ($id <= 0 ? false : true); - $loginname = trim($this->getParam('loginname', $ln_optional, '')); + $loginname = $this->getParam('loginname', $ln_optional, ''); $json_result = Admins::getLocal($this->getUserData(), array( 'id' => $id, diff --git a/lib/classes/api/commands/class.Customers.php b/lib/classes/api/commands/class.Customers.php index 529f401a..de370b9c 100644 --- a/lib/classes/api/commands/class.Customers.php +++ b/lib/classes/api/commands/class.Customers.php @@ -70,7 +70,7 @@ class Customers extends ApiCommand implements ResourceEntity { $id = $this->getParam('id', true, 0); $ln_optional = ($id <= 0 ? false : true); - $loginname = trim($this->getParam('loginname', $ln_optional, '')); + $loginname = $this->getParam('loginname', $ln_optional, ''); if ($this->isAdmin()) { $result_stmt = Database::prepare(" @@ -208,18 +208,7 @@ class Customers extends ApiCommand implements ResourceEntity standard_error('youcantallocatemorethanyouhave', '', true); } - // Either $name and $firstname or the $company must be inserted - if ($name == '' && $company == '') { - standard_error(array( - 'stringisempty', - 'myname' - ), '', true); - } elseif ($firstname == '' && $company == '') { - standard_error(array( - 'stringisempty', - 'myfirstname' - ), '', true); - } elseif ($email == '') { + if ($email == '') { standard_error(array( 'stringisempty', 'emailadd' @@ -680,7 +669,7 @@ class Customers extends ApiCommand implements ResourceEntity { $id = $this->getParam('id', true, 0); $ln_optional = ($id <= 0 ? false : true); - $loginname = trim($this->getParam('loginname', $ln_optional, '')); + $loginname = $this->getParam('loginname', $ln_optional, ''); $json_result = Customers::getLocal($this->getUserData(), array( 'id' => $id, @@ -1212,7 +1201,7 @@ class Customers extends ApiCommand implements ResourceEntity if ($this->isAdmin()) { $id = $this->getParam('id', true, 0); $ln_optional = ($id <= 0 ? false : true); - $loginname = trim($this->getParam('loginname', $ln_optional, '')); + $loginname = $this->getParam('loginname', $ln_optional, ''); $delete_userfiles = $this->getParam('delete_userfiles', true, 0); $json_result = Customers::getLocal($this->getUserData(), array( @@ -1454,7 +1443,7 @@ class Customers extends ApiCommand implements ResourceEntity if ($this->isAdmin()) { $id = $this->getParam('id', true, 0); $ln_optional = ($id <= 0 ? false : true); - $loginname = trim($this->getParam('loginname', $ln_optional, '')); + $loginname = $this->getParam('loginname', $ln_optional, ''); $json_result = Customers::getLocal($this->getUserData(), array( 'id' => $id, diff --git a/lib/classes/api/commands/class.Domains.php b/lib/classes/api/commands/class.Domains.php index 31ca5e63..c9539d6d 100644 --- a/lib/classes/api/commands/class.Domains.php +++ b/lib/classes/api/commands/class.Domains.php @@ -73,7 +73,7 @@ class Domains extends ApiCommand implements ResourceEntity if ($this->isAdmin()) { $id = $this->getParam('id', true, 0); $dn_optional = ($id <= 0 ? false : true); - $domainname = trim($this->getParam('domainname', $dn_optional, '')); + $domainname = $this->getParam('domainname', $dn_optional, ''); $no_std_subdomain = $this->getParam('no_std_subdomain', true, false); // convert possible idn domain to punycode @@ -131,8 +131,8 @@ class Domains extends ApiCommand implements ResourceEntity $speciallogfile = $this->getParam('speciallogfile', true, 0); $aliasdomain = intval($this->getParam('alias', true, 0)); $issubof = intval($this->getParam('issubof', true, 0)); - $registration_date = trim($this->getParam('registration_date', true, '')); - $termination_date = trim($this->getParam('termination_date', true, '')); + $registration_date = $this->getParam('registration_date', true, ''); + $termination_date = $this->getParam('termination_date', true, ''); $caneditdomain = $this->getParam('caneditdomain', true, 0); $isbinddomain = $this->getParam('isbinddomain', true, 0); $zonefile = $this->getParam('zonefile', true, ''); @@ -774,7 +774,7 @@ class Domains extends ApiCommand implements ResourceEntity // parameters $id = $this->getParam('id', true, 0); $dn_optional = ($id <= 0 ? false : true); - $domainname = trim($this->getParam('domainname', $dn_optional, '')); + $domainname = $this->getParam('domainname', $dn_optional, ''); // get requested domain $json_result = Domains::getLocal($this->getUserData(), array( @@ -799,8 +799,8 @@ class Domains extends ApiCommand implements ResourceEntity $speciallogverified = $this->getParam('speciallogverified', true, 0); $aliasdomain = intval($this->getParam('alias', true, $result['aliasdomain'])); $issubof = intval($this->getParam('issubof', true, $result['ismainbutsubto'])); - $registration_date = trim($this->getParam('registration_date', true, $result['registration_date'])); - $termination_date = trim($this->getParam('termination_date', true, $result['termination_date'])); + $registration_date = $this->getParam('registration_date', true, $result['registration_date']); + $termination_date = $this->getParam('termination_date', true, $result['termination_date']); $caneditdomain = $this->getParam('caneditdomain', true, $result['caneditdomain']); $isbinddomain = $this->getParam('isbinddomain', true, $result['isbinddomain']); $zonefile = $this->getParam('zonefile', true, $result['zonefile']); @@ -1587,7 +1587,7 @@ class Domains extends ApiCommand implements ResourceEntity if ($this->isAdmin()) { $id = $this->getParam('id', true, 0); $dn_optional = ($id <= 0 ? false : true); - $domainname = trim($this->getParam('domainname', $dn_optional, '')); + $domainname = $this->getParam('domainname', $dn_optional, ''); $is_stdsubdomain = $this->getParam('is_stdsubdomain', true, 0); $remove_subbutmain_domains = $this->getParam('delete_mainsubdomains', true, 0); diff --git a/lib/classes/api/commands/class.Ftps.php b/lib/classes/api/commands/class.Ftps.php index e0150ab4..0766e58c 100644 --- a/lib/classes/api/commands/class.Ftps.php +++ b/lib/classes/api/commands/class.Ftps.php @@ -37,7 +37,7 @@ class Ftps extends ApiCommand implements ResourceEntity { $id = $this->getParam('id', true, 0); $un_optional = ($id <= 0 ? false : true); - $username = trim($this->getParam('username', $un_optional, '')); + $username = $this->getParam('username', $un_optional, ''); $params = array(); if ($this->isAdmin()) { diff --git a/lib/classes/api/commands/class.Mysqls.php b/lib/classes/api/commands/class.Mysqls.php index 5fa12a1e..68e24b61 100644 --- a/lib/classes/api/commands/class.Mysqls.php +++ b/lib/classes/api/commands/class.Mysqls.php @@ -238,7 +238,7 @@ class Mysqls extends ApiCommand implements ResourceEntity { $id = $this->getParam('id', true, 0); $dn_optional = ($id <= 0 ? false : true); - $dbname = trim($this->getParam('dbname', $dn_optional, '')); + $dbname = $this->getParam('dbname', $dn_optional, ''); $dbserver = $this->getParam('mysql_server', true, - 1); if ($this->isAdmin()) { @@ -337,7 +337,7 @@ class Mysqls extends ApiCommand implements ResourceEntity { $id = $this->getParam('id', true, 0); $dn_optional = ($id <= 0 ? false : true); - $dbname = trim($this->getParam('dbname', $dn_optional, '')); + $dbname = $this->getParam('dbname', $dn_optional, ''); $dbserver = $this->getParam('mysql_server', true, - 1); if ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'mysql')) { @@ -541,7 +541,7 @@ class Mysqls extends ApiCommand implements ResourceEntity { $id = $this->getParam('id', true, 0); $dn_optional = ($id <= 0 ? false : true); - $dbname = trim($this->getParam('dbname', $dn_optional, '')); + $dbname = $this->getParam('dbname', $dn_optional, ''); $dbserver = $this->getParam('mysql_server', true, - 1); if ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'mysql')) { From c9256c0020b675ac3f376a4d079a93c7c5b3ce1d Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 26 Feb 2018 09:01:13 +0100 Subject: [PATCH 0496/1335] add 'adminname' to result in Customers.get; fix Customers.move and return customer-data there instead of just true Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/commands/class.Customers.php | 28 +++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/lib/classes/api/commands/class.Customers.php b/lib/classes/api/commands/class.Customers.php index de370b9c..40823efd 100644 --- a/lib/classes/api/commands/class.Customers.php +++ b/lib/classes/api/commands/class.Customers.php @@ -74,8 +74,9 @@ class Customers extends ApiCommand implements ResourceEntity if ($this->isAdmin()) { $result_stmt = Database::prepare(" - SELECT * FROM `" . TABLE_PANEL_CUSTOMERS . "` - WHERE " . ($id > 0 ? "`customerid` = :idln" : "`loginname` = :idln") . ($this->getUserDetail('customers_see_all') ? '' : " AND `adminid` = :adminid")); + SELECT `c`.*, `a`.`loginname` AS `adminname` + FROM `" . TABLE_PANEL_CUSTOMERS . "` `c`, `" . TABLE_PANEL_ADMINS . "` `a` + WHERE " . ($id > 0 ? "`c`.`customerid` = :idln" : "`c`.`loginname` = :idln") . ($this->getUserDetail('customers_see_all') ? '' : " AND `c`.`adminid` = :adminid"). " AND `c`.`adminid` = `a`.`adminid`"); $params = array( 'idln' => ($id <= 0 ? $loginname : $id) ); @@ -1480,19 +1481,22 @@ class Customers extends ApiCommand implements ResourceEntity * * @access admin * @throws Exception - * @return bool true on success, error-message on failure + * @return array */ public function move() { if ($this->isAdmin() && $this->getUserDetail('change_serversettings') == 1) { - $id = $this->getParam('id'); $adminid = $this->getParam('adminid'); + $id = $this->getParam('id', true, 0); + $ln_optional = ($id <= 0 ? false : true); + $loginname = $this->getParam('loginname', $ln_optional, ''); - // get customer - $json_result = Admins::getLocal($this->getUserData(), array( - 'id' => $id + $json_result = Customers::getLocal($this->getUserData(), array( + 'id' => $id, + 'loginname' => $loginname ))->get(); $c_result = json_decode($json_result, true)['data']; + $id = $c_result['customerid']; // check if target-admin is the current admin if ($adminid == $c_result['adminid']) { @@ -1500,7 +1504,7 @@ class Customers extends ApiCommand implements ResourceEntity } // get target admin - $json_result = Customers::getLocal($this->getUserData(), array( + $json_result = Admins::getLocal($this->getUserData(), array( 'id' => $adminid ))->get(); $a_result = json_decode($json_result, true)['data']; @@ -1535,8 +1539,12 @@ class Customers extends ApiCommand implements ResourceEntity // now, recalculate the resource-usage for the old and the new admin updateCounters(false); - $log->logAction(ADM_ACTION, LOG_INFO, "[API] moved user '" . $c_result['loginname'] . "' from admin/reseller '" . $c_result['adminname'] . " to admin/reseller '" . $a_result['loginname'] . "'"); - return $this->response(200, "successfull", true); + $this->logger()->logAction(ADM_ACTION, LOG_INFO, "[API] moved user '" . $c_result['loginname'] . "' from admin/reseller '" . $c_result['adminname'] . " to admin/reseller '" . $a_result['loginname'] . "'"); + $json_result = Customers::getLocal($this->getUserData(), array( + 'id' => $c_result['customerid'] + ))->get(); + $result = json_decode($json_result, true)['data']; + return $this->response(200, "successfull", $result); } throw new Exception("Not allowed to execute given command.", 403); } From 6191ee6fba27d30da2ae6d5e14265955d68bf50d Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 26 Feb 2018 09:36:58 +0100 Subject: [PATCH 0497/1335] add Ftps.list and Ftps.delete Signed-off-by: Michael Kaufmann (d00p) --- customer_ftp.php | 63 +------- lib/classes/api/commands/class.Ftps.php | 183 +++++++++++++++++++++- lib/classes/api/commands/class.Mysqls.php | 3 +- 3 files changed, 186 insertions(+), 63 deletions(-) diff --git a/customer_ftp.php b/customer_ftp.php index e40886d5..bd6cbe11 100644 --- a/customer_ftp.php +++ b/customer_ftp.php @@ -90,66 +90,11 @@ if ($page == 'overview') { if (isset($result['username']) && $result['username'] != $userinfo['loginname']) { if (isset($_POST['send']) && $_POST['send'] == 'send') { - $stmt = Database::prepare("UPDATE `" . TABLE_FTP_USERS . "` - SET `up_count` = `up_count` + :up_count, - `up_bytes` = `up_bytes` + :up_bytes, - `down_count` = `down_count` + :down_count, - `down_bytes` = `down_bytes` + :down_bytes - WHERE `username` = :username" - ); - $params = array( - "up_count" => $result['up_count'], - "up_bytes" => $result['up_bytes'], - "down_count" => $result['down_count'], - "down_bytes" => $result['down_bytes'], - "username" => $userinfo['loginname'] - ); - Database::pexecute($stmt, $params); - - $result_stmt = Database::prepare("SELECT `username`, `homedir` FROM `" . TABLE_FTP_USERS . "` - WHERE `customerid` = :customerid - AND `id` = :id" - ); - Database::pexecute($result_stmt, array("customerid" => $userinfo['customerid'], "id" => $id)); - $result = $result_stmt->fetch(PDO::FETCH_ASSOC); - - $stmt = Database::prepare("DELETE FROM `" . TABLE_FTP_QUOTATALLIES . "` WHERE `name` = :name"); - Database::pexecute($stmt, array("name" => $result['username'])); - - $stmt = Database::prepare("DELETE FROM `" . TABLE_FTP_USERS . "` - WHERE `customerid` = :customerid - AND `id` = :id" - ); - Database::pexecute($stmt, array("customerid" => $userinfo['customerid'], "id" => $id)); - - $stmt = Database::prepare(" - UPDATE `" . TABLE_FTP_GROUPS . "` SET - `members` = REPLACE(`members`, :username,'') - WHERE `customerid` = :customerid - "); - Database::pexecute($stmt, array("username" => ",".$result['username'], "customerid" => $userinfo['customerid'])); - - $log->logAction(USR_ACTION, LOG_INFO, "deleted ftp-account '" . $result['username'] . "'"); - - $resetaccnumber = ($userinfo['ftps_used'] == '1') ? " , `ftp_lastaccountnumber`='0'" : ''; - - // refs #293 - if (isset($_POST['delete_userfiles']) && (int)$_POST['delete_userfiles'] == 1) { - inserttask('8', $userinfo['loginname'], $result['homedir']); - } else { - if (Settings::Get('system.nssextrausers') == 1) - { - // this is used so that the libnss-extrausers cron is fired - inserttask(5); - } + try { + Ftps::getLocal($userinfo, $_POST)->delete(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); } - - $stmt = Database::prepare("UPDATE `" . TABLE_PANEL_CUSTOMERS . "` - SET `ftps_used` = `ftps_used` - 1 $resetaccnumber - WHERE `customerid` = :customerid" - ); - Database::pexecute($stmt, array("customerid" => $userinfo['customerid'])); - redirectTo($filename, array('page' => $page, 's' => $s)); } else { ask_yesno_withcheckbox('ftp_reallydelete', 'admin_customer_alsoremoveftphomedir', $filename, array('id' => $id, 'page' => $page, 'action' => $action), $result['username']); diff --git a/lib/classes/api/commands/class.Ftps.php b/lib/classes/api/commands/class.Ftps.php index 0766e58c..7005ab99 100644 --- a/lib/classes/api/commands/class.Ftps.php +++ b/lib/classes/api/commands/class.Ftps.php @@ -38,7 +38,7 @@ class Ftps extends ApiCommand implements ResourceEntity $id = $this->getParam('id', true, 0); $un_optional = ($id <= 0 ? false : true); $username = $this->getParam('username', $un_optional, ''); - + $params = array(); if ($this->isAdmin()) { if ($this->getUserDetail('customers_see_all') == false) { @@ -86,9 +86,186 @@ class Ftps extends ApiCommand implements ResourceEntity public function update() {} + /** + * list all ftp-users, if called from an admin, list all ftp-users of all customers you are allowed to view, or specify id or loginname for one specific customer + * + * @param int $customerid + * optional, admin-only, select ftp-users of a specific customer by id + * @param string $loginname + * optional, admin-only, select ftp-users of a specific customer by loginname + * + * @access admin, customer + * @throws Exception + * @return array count|list + */ public function list() - {} + { + if ($this->isAdmin()) { + // if we're an admin, list all ftp-users of all the admins customers + // or optionally for one specific customer identified by id or loginname + $customerid = $this->getParam('customerid', true, 0); + $loginname = $this->getParam('loginname', true, ''); + + if (! empty($customer_id) || ! empty($loginname)) { + $json_result = Customers::getLocal($this->getUserData(), array( + 'id' => $customerid, + 'loginname' => $loginname + ))->get(); + $custom_list_result = array( + json_decode($json_result, true)['data'] + ); + } else { + $json_result = Customers::getLocal($this->getUserData())->list(); + $custom_list_result = json_decode($json_result, true)['data']['list']; + } + $customer_ids = array(); + foreach ($custom_list_result as $customer) { + $customer_ids[] = $customer['customerid']; + } + } else { + if (Settings::IsInList('panel.customer_hide_options', 'ftp')) { + throw new Exception("You cannot access this resource", 405); + } + $customer_ids = array( + $this->getUserDetail('customerid') + ); + } + $result = array(); + $params['customerid'] = implode(", ", $customer_ids); + $result_stmt = Database::prepare(" + SELECT * FROM `" . TABLE_FTP_USERS . "` + WHERE `customerid` IN (:customerid) + "); + Database::pexecute($result_stmt, $params); + while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) { + $result[] = $row; + } + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_NOTICE, "[API] list ftp-users"); + return $this->response(200, "successfull", array( + 'count' => count($result), + 'list' => $result + )); + } + /** + * delete a ftp-user by either id or username + * + * @param int $id + * optional, the ftp-user-id + * @param string $username + * optional, the username + * @param bool $delete_userfiles + * optional, default false + * + * @access admin, customer + * @throws Exception + * @return array + */ public function delete() - {} + { + $id = $this->getParam('id', true, 0); + $un_optional = ($id <= 0 ? false : true); + $username = $this->getParam('username', $un_optional, ''); + $delete_userfiles = $this->getParam('delete_userfiles', true, 0); + + if ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'ftp')) { + throw new Exception("You cannot access this resource", 405); + } + + // get ftp-user + $json_result = Ftps::getLocal($this->getUserData(), array( + 'id' => $id, + 'username' => $username + ))->get(); + $result = json_decode($json_result, true)['data']; + $id = $result['id']; + + if ($this->isAdmin()) { + // get customer-data + $json_result = Customers::getLocal($this->getUserData(), array( + 'id' => $result['customerid'] + ))->get(); + $customer_data = json_decode($json_result, true)['data']; + } else { + $customer_data = $this->getUserData(); + } + + // add usage of this ftp-user to main-ftp user of customer if different + if ($result['username'] != $customer_data['loginname']) { + $stmt = Database::prepare("UPDATE `" . TABLE_FTP_USERS . "` + SET `up_count` = `up_count` + :up_count, + `up_bytes` = `up_bytes` + :up_bytes, + `down_count` = `down_count` + :down_count, + `down_bytes` = `down_bytes` + :down_bytes + WHERE `username` = :username + "); + $params = array( + "up_count" => $result['up_count'], + "up_bytes" => $result['up_bytes'], + "down_count" => $result['down_count'], + "down_bytes" => $result['down_bytes'], + "username" => $customer_data['loginname'] + ); + Database::pexecute($stmt, $params, true, true); + } + + // remove all quotatallies + $stmt = Database::prepare("DELETE FROM `" . TABLE_FTP_QUOTATALLIES . "` WHERE `name` = :name"); + Database::pexecute($stmt, array( + "name" => $result['username'] + ), true, tue); + + // remove user itself + $stmt = Database::prepare(" + DELETE FROM `" . TABLE_FTP_USERS . "` WHERE `customerid` = :customerid AND `id` = :id + "); + Database::pexecute($stmt, array( + "customerid" => $customer_data['customerid'], + "id" => $id + ), true, true); + + // update ftp-groups + $stmt = Database::prepare(" + UPDATE `" . TABLE_FTP_GROUPS . "` SET + `members` = REPLACE(`members`, :username,'') + WHERE `customerid` = :customerid + "); + Database::pexecute($stmt, array( + "username" => "," . $result['username'], + "customerid" => $customer_data['customerid'] + ), true, true); + + $log->logAction(USR_ACTION, LOG_INFO, "deleted ftp-account '" . $result['username'] . "'"); + + // refs #293 + if ($delete_userfiles == 1) { + inserttask('8', $customer_data['loginname'], $result['homedir']); + } else { + if (Settings::Get('system.nssextrausers') == 1) { + // this is used so that the libnss-extrausers cron is fired + inserttask(5); + } + } + + // decrease ftp-user usage for customer + $resetaccnumber = ($customer_data['ftps_used'] == '1') ? " , `ftp_lastaccountnumber`='0'" : ''; + $stmt = Database::prepare("UPDATE `" . TABLE_PANEL_CUSTOMERS . "` + SET `ftps_used` = `ftps_used` - 1 $resetaccnumber + WHERE `customerid` = :customerid"); + Database::pexecute($stmt, array( + "customerid" => $customer_data['customerid'] + ), true, true); + // update admin usage + $stmt = Database::prepare(" + UPDATE `" . TABLE_PANEL_ADMINS . "` + SET `mysqls_used` = `mysqls_used` - 1 + WHERE `adminid` = :adminid + "); + Database::pexecute($stmt, array( + "adminid" => ($this->isAdmin() ? $customer_data['adminid'] : $this->getUserDetail('adminid')) + ), true, true); + + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_WARNING, "[API] deleted ftp-user '" . $result['username'] . "'"); + return $this->response(200, "successfull", $result); + } } diff --git a/lib/classes/api/commands/class.Mysqls.php b/lib/classes/api/commands/class.Mysqls.php index 68e24b61..ad2ea24c 100644 --- a/lib/classes/api/commands/class.Mysqls.php +++ b/lib/classes/api/commands/class.Mysqls.php @@ -440,6 +440,7 @@ class Mysqls extends ApiCommand implements ResourceEntity * optional, admin-only, select dbs of a specific customer by loginname * * @access admin, customer + * @throws Exception * @return array count|list */ public function list() @@ -560,7 +561,6 @@ class Mysqls extends ApiCommand implements ResourceEntity Database::needRoot(true, $result['dbserver']); $dbm = new DbManager($this->logger()); $dbm->getManager()->deleteDatabase($result['databasename']); - $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_WARNING, "[API] deleted database '" . $result['databasename'] . "'"); Database::needRoot(false); // End root-session @@ -602,6 +602,7 @@ class Mysqls extends ApiCommand implements ResourceEntity "adminid" => ($this->isAdmin() ? $customer['adminid'] : $this->getUserDetail('adminid')), ), true, true); + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_WARNING, "[API] deleted database '" . $result['databasename'] . "'"); return $this->response(200, "successfull", $result); } } From ceb8619552f2d135ca83741a0d35b8c9e3b5495c Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 26 Feb 2018 12:15:48 +0100 Subject: [PATCH 0498/1335] preparations for assign-multiple-ips-to-an-admin in Api, not in webinterface yet Signed-off-by: Michael Kaufmann (d00p) --- install/froxlor.sql | 2 +- .../updates/froxlor/0.10/update_0.10.inc.php | 14 +++++++ lib/classes/api/commands/class.Admins.php | 10 ++--- .../api/commands/class.IpsAndPorts.php | 41 +++++++++++++------ 4 files changed, 48 insertions(+), 19 deletions(-) diff --git a/install/froxlor.sql b/install/froxlor.sql index 8fb1e8e8..cf40bfc3 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -95,7 +95,7 @@ CREATE TABLE `panel_admins` ( `name` varchar(255) NOT NULL default '', `email` varchar(255) NOT NULL default '', `def_language` varchar(255) NOT NULL default '', - `ip` tinyint(4) NOT NULL default '-1', + `ip` varchar(500) NOT NULL default '-1', `customers` int(15) NOT NULL default '0', `customers_used` int(15) NOT NULL default '0', `customers_see_all` tinyint(1) NOT NULL default '0', diff --git a/install/updates/froxlor/0.10/update_0.10.inc.php b/install/updates/froxlor/0.10/update_0.10.inc.php index 1748ecc3..25b35a6f 100644 --- a/install/updates/froxlor/0.10/update_0.10.inc.php +++ b/install/updates/froxlor/0.10/update_0.10.inc.php @@ -51,4 +51,18 @@ if (isFroxlorVersion('0.10.0')) { showUpdateStep("Adding new default-ssl-ip setting"); Settings::AddNew('system.defaultsslip', ''); lastStepStatus(0); + + showUpdateStep("Altering admin ip's field to allow multiple ip addresses"); + // get all admins for updating the new field + $sel_stmt = Database::prepare("SELECT adminid, ip FROM `panel_admins`"); + Database::pexecute($sel_stmt); + $all_admins = $sel_stmt->fetchAll(PDO::FETCH_ASSOC); + Database::query("ALTER TABLE `panel_admins` MODIFY `ip` varchar(500) NOT NULL default '-1';"); + $upd_stmt = Database::prepare("UPDATE `panel_admins` SET `ip` = :ip WHERE `adminid` = :adminid"); + foreach ($all_admins as $adm) { + if ($admin['ip'] != -1) { + Database::pexecute($upd_stmt, array('ip' => json_encode($adm['ip']), 'adminid' => $adm['adminid'])); + } + } + lastStepStatus(0); } diff --git a/lib/classes/api/commands/class.Admins.php b/lib/classes/api/commands/class.Admins.php index 12f648e3..f5780ea3 100644 --- a/lib/classes/api/commands/class.Admins.php +++ b/lib/classes/api/commands/class.Admins.php @@ -124,7 +124,7 @@ class Admins extends ApiCommand implements ResourceEntity $tickets_see_all = $this->getParam('tickets_see_all', true, 0); $caneditphpsettings = $this->getParam('caneditphpsettings', true, 0); $change_serversettings = $this->getParam('change_serversettings', true, 0); - $ipaddress = intval_ressource($this->getParam('ipaddress', true, - 1)); + $ipaddress = $this->getParam('ipaddress', true, -1); // validation $name = validate($name, 'name', '', '', array(), true); @@ -244,7 +244,7 @@ class Admins extends ApiCommand implements ResourceEntity 'tickets' => $tickets, 'tickets_see_all' => $tickets_see_all, 'mysqls' => $mysqls, - 'ip' => $ipaddress, + 'ip' => empty($ipaddress) ? "" : (is_array($ipaddress) && $ipaddress > 0 ? json_encode($ipaddress) : -1), 'theme' => $_theme, 'custom_notes' => $custom_notes, 'custom_notes_show' => $custom_notes_show @@ -354,7 +354,7 @@ class Admins extends ApiCommand implements ResourceEntity $change_serversettings = $result['change_serversettings']; $diskspace = $result['diskspace']; $traffic = $result['traffic']; - $ipaddress = $result['ip']; + $ipaddress = ($result['ip'] != -1 ? json_decode($result['ip'], true) : -1); } else { $deactivated = $this->getParam('deactivated', true, $result['deactivated']); @@ -377,7 +377,7 @@ class Admins extends ApiCommand implements ResourceEntity $tickets_see_all = $this->getParam('tickets_see_all', true, $result['tickets_see_all']); $caneditphpsettings = $this->getParam('caneditphpsettings', true, $result['caneditphpsettings']); $change_serversettings = $this->getParam('change_serversettings', true, $result['change_serversettings']); - $ipaddress = intval_ressource($this->getParam('ipaddress', true, $result['ip'])); + $ipaddress = $this->getParam('ipaddress', true, ($result['ip'] != -1 ? json_decode($result['ip'], true) : -1)); $diskspace = $diskspace * 1024; $traffic = $traffic * 1024 * 1024; @@ -512,7 +512,7 @@ class Admins extends ApiCommand implements ResourceEntity 'tickets' => $tickets, 'tickets_see_all' => $tickets_see_all, 'mysqls' => $mysqls, - 'ip' => $ipaddress, + 'ip' => empty($ipaddress) ? "" : (is_array($ipaddress) && $ipaddress > 0 ? json_encode($ipaddress) : -1), 'deactivated' => $deactivated, 'custom_notes' => $custom_notes, 'custom_notes_show' => $custom_notes_show, diff --git a/lib/classes/api/commands/class.IpsAndPorts.php b/lib/classes/api/commands/class.IpsAndPorts.php index 32ab79a3..a2bb599f 100644 --- a/lib/classes/api/commands/class.IpsAndPorts.php +++ b/lib/classes/api/commands/class.IpsAndPorts.php @@ -27,10 +27,14 @@ class IpsAndPorts extends ApiCommand implements ResourceEntity */ public function list() { - if ($this->isAdmin() && $this->getUserDetail('change_serversettings')) { + if ($this->isAdmin() && ($this->getUserDetail('change_serversettings') || ! empty($this->getUserDetail('ip')))) { $this->logger()->logAction(ADM_ACTION, LOG_NOTICE, "[API] list ips and ports"); + $ip_where = ""; + if (!empty($this->getUserDetail('ip')) && $this->getUserDetail('ip') != -1) { + $ip_where = "WHERE `id` IN (".implode(", ", json_decode($this->getUserDetail('ip'), true)).")"; + } $result_stmt = Database::prepare(" - SELECT * FROM `" . TABLE_PANEL_IPSANDPORTS . "` ORDER BY `ip` ASC, `port` ASC + SELECT * FROM `" . TABLE_PANEL_IPSANDPORTS . "` " . $ip_where . " ORDER BY `ip` ASC, `port` ASC "); Database::pexecute($result_stmt, null, true, true); $result = array(); @@ -50,16 +54,21 @@ class IpsAndPorts extends ApiCommand implements ResourceEntity * * @param int $id * ip-port-id - * + * * @access admin * @throws Exception * @return array */ public function get() { - if ($this->isAdmin() && $this->getUserDetail('change_serversettings')) { + if ($this->isAdmin() && ($this->getUserDetail('change_serversettings') || ! empty($this->getUserDetail('ip')))) { $id = $this->getParam('id'); - $this->logger()->logAction(ADM_ACTION, LOG_NOTICE, "[API] get ip and port #" . $id); + if (!empty($this->getUserDetail('ip')) && $this->getUserDetail('ip') != -1) { + $allowed_ips = json_decode($this->getUserDetail('ip'), true); + if (!in_array($id, $allowed_ips)) { + throw new Exception("You cannot access this resource", 405); + } + } $result_stmt = Database::prepare(" SELECT * FROM `" . TABLE_PANEL_IPSANDPORTS . "` WHERE `id` = :id "); @@ -67,6 +76,7 @@ class IpsAndPorts extends ApiCommand implements ResourceEntity 'id' => $id ), true, true); if ($result) { + $this->logger()->logAction(ADM_ACTION, LOG_NOTICE, "[API] get ip " . $result['ip'] . " " . $result['port']); return $this->response(200, "successfull", $result); } throw new Exception("IP/port with id #" . $id . " could not be found", 404); @@ -204,7 +214,12 @@ class IpsAndPorts extends ApiCommand implements ResourceEntity $ip = '[' . $ip . ']'; } $this->logger()->logAction(ADM_ACTION, LOG_WARNING, "[API] added IP/port '" . $ip . ":" . $port . "'"); - return $this->response(200, "successfull", $ins_data); + // get ip for return-array + $json_result = IpsAndPorts::getLocal($this->getUserData(), array( + 'id' => $ins_data['id'] + ))->get(); + $result = json_decode($json_result, true)['data']; + return $this->response(200, "successfull", $result); } throw new Exception("Not allowed to execute given command.", 403); } @@ -220,7 +235,7 @@ class IpsAndPorts extends ApiCommand implements ResourceEntity */ public function update() { - if ($this->isAdmin() && $this->getUserDetail('change_serversettings')) { + if ($this->isAdmin() && ($this->getUserDetail('change_serversettings') || ! empty($this->getUserDetail('ip')))) { $id = $this->getParam('id'); $json_result = IpsAndPorts::getLocal($this->getUserData(), array( @@ -368,7 +383,7 @@ class IpsAndPorts extends ApiCommand implements ResourceEntity * * @param int $id * ip-port-id - * + * * @access admin * @throws Exception * @return array @@ -411,17 +426,17 @@ class IpsAndPorts extends ApiCommand implements ResourceEntity if ($result['ip'] != '') { $del_stmt = Database::prepare(" - DELETE FROM `" . TABLE_PANEL_IPSANDPORTS . "` - WHERE `id` = :id - "); + DELETE FROM `" . TABLE_PANEL_IPSANDPORTS . "` + WHERE `id` = :id + "); Database::pexecute($del_stmt, array( 'id' => $id )); // also, remove connections to domains (multi-stack) $del_stmt = Database::prepare(" - DELETE FROM `" . TABLE_DOMAINTOIP . "` WHERE `id_ipandports` = :id - "); + DELETE FROM `" . TABLE_DOMAINTOIP . "` WHERE `id_ipandports` = :id + "); Database::pexecute($del_stmt, array( 'id' => $id )); From b0f355ba2f426ed7ccfc956dc4f6c08d9c82bf76 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 26 Feb 2018 15:49:41 +0100 Subject: [PATCH 0499/1335] fixed in Mysqls.add, added Ftps.add Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/commands/class.Ftps.php | 264 +++++++++++++++++++++- lib/classes/api/commands/class.Mysqls.php | 9 +- 2 files changed, 270 insertions(+), 3 deletions(-) diff --git a/lib/classes/api/commands/class.Ftps.php b/lib/classes/api/commands/class.Ftps.php index 7005ab99..595a5f78 100644 --- a/lib/classes/api/commands/class.Ftps.php +++ b/lib/classes/api/commands/class.Ftps.php @@ -18,8 +18,270 @@ class Ftps extends ApiCommand implements ResourceEntity { + /** + * add a new ftp-user + * + * @param string $ftp_password + * password for the created database and database-user + * @param string $path + * destination path + * @param string $ftp_description + * optional, description for ftp-user + * @param bool $sendinfomail + * optional, send created resource-information to customer, default: false + * @param string $shell + * optional, default /bin/false (not changeable when deactivated) + * @param string $ftp_username + * optional if customer.ftpatdomain is allowed, specify an username + * @param string $ftp_domain + * optional if customer.ftpatdomain is allowed, specify a domain (customer must be owner) + * @param int $customer_id + * required when called as admin, not needed when called as customer + * + * @access admin, customer + * @throws Exception + * @return array + */ public function add() - {} + { + if ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'ftp')) { + throw new Exception("You cannot access this resource", 405); + } + + if ($this->getUserDetail('ftps_used') < $this->getUserDetail('ftps') || $this->getUserDetail('ftps') == '-1') { + + // required paramters + $path = $this->getParam('path'); + $password = $this->getParam('ftp_password'); + + // parameters + $description = $this->getParam('ftp_description', true, ''); + $sendinfomail = $this->getParam('sendinfomail', true, 0); + $shell = $this->getParam('shell', true, '/bin/false'); + + $ftpusername = $this->getParam('ftp_username', true, ''); + $ftpdomain = $this->getParam('ftp_domain', true, ''); + + // validation + $password = validate($password, 'password', '', '', array(), true); + $password = validatePassword($password, true); + $description = validate(trim($description), 'description', '', '', array(), true); + + if (Settings::Get('system.allow_customer_shell') == '1') { + $shell = validate(trim($shell), 'shell', '', '', array(), true); + } else { + $shell = "/bin/false"; + } + + if (Settings::Get('customer.ftpatdomain') == '1') { + $ftpusername = validate(trim($ftpusername), 'username', '/^[a-zA-Z0-9][a-zA-Z0-9\-_]+\$?$/', '', array(), true); + $idna_convert = new idna_convert_wrapper(); + $ftpdomain = $idna_convert->encode(validate($ftpdomain, 'domain', '', '', array(), true)); + } + + $params = array(); + // get needed customer info to reduce the mysql-usage-counter by one + if ($this->isAdmin()) { + // get customer id + $customer_id = $this->getParam('customer_id'); + $json_result = Customers::getLocal($this->getUserData(), array( + 'id' => $customer_id + ))->get(); + $customer = json_decode($json_result, true)['data']; + // check whether the customer has enough resources to get the database added + if ($customer['ftps_used'] >= $customer['ftps'] && $customer['ftps'] != '-1') { + throw new Exception("Customer has no more resources available", 406); + } + } else { + $customer_id = $this->getUserDetail('customer_id'); + $customer = $this->getUserData(); + } + + if ($sendinfomail != 1) { + $sendinfomail = 0; + } + + if (Settings::Get('customer.ftpatdomain') == '1') { + if ($ftpusername == '') { + standard_error(array( + 'stringisempty', + 'username' + ), '', true); + } + $ftpdomain_check_stmt = Database::prepare("SELECT `id`, `domain`, `customerid` FROM `" . TABLE_PANEL_DOMAINS . "` + WHERE `domain` = :domain + AND `customerid` = :customerid"); + $ftpdomain_check = Database::pexecute_first($ftpdomain_check_stmt, array( + "domain" => $ftpdomain, + "customerid" => $customer_id + ), true, true); + + if ($ftpdomain_check && $ftpdomain_check['domain'] != $ftpdomain) { + standard_error('maindomainnonexist', $domain, true); + } + $username = $ftpusername . "@" . $ftpdomain; + } else { + $username = $customer['loginname'] . Settings::Get('customer.ftpprefix') . (intval($customer['ftp_lastaccountnumber']) + 1); + } + + $username_check_stmt = Database::prepare(" + SELECT * FROM `" . TABLE_FTP_USERS . "` WHERE `username` = :username + "); + $username_check = Database::pexecute_first($username_check_stmt, array( + "username" => $username + ), true, true); + + if (! empty($username_check) && $username_check['username'] = $username) { + standard_error('usernamealreadyexists', $username, true); + } elseif ($password == '') { + standard_error(array( + 'stringisempty', + 'mypassword' + ), '', true); + } elseif ($username == $password) { + standard_error('passwordshouldnotbeusername', '', true); + } else { + $path = makeCorrectDir($customer['documentroot'] . '/' . $path); + $cryptPassword = makeCryptPassword($password); + + $stmt = Database::prepare("INSERT INTO `" . TABLE_FTP_USERS . "` + (`customerid`, `username`, `description`, `password`, `homedir`, `login_enabled`, `uid`, `gid`, `shell`) + VALUES (:customerid, :username, :description, :password, :homedir, 'y', :guid, :guid, :shell)"); + $params = array( + "customerid" => $customer_id, + "username" => $username, + "description" => $description, + "password" => $cryptPassword, + "homedir" => $path, + "guid" => $customer['guid'], + "shell" => $shell + ); + Database::pexecute($stmt, $params, true, true); + $ftp_userid = Database::lastInsertId(); + + $result_stmt = Database::prepare(" + SELECT `bytes_in_used` FROM `" . TABLE_FTP_QUOTATALLIES . "` WHERE `name` = :name + "); + Database::pexecute($result_stmt, array( + "name" => $customer['loginname'] + ), true, true); + + while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) { + $stmt = Database::prepare("INSERT INTO `" . TABLE_FTP_QUOTATALLIES . "` + (`name`, `quota_type`, `bytes_in_used`, `bytes_out_used`, `bytes_xfer_used`, `files_in_used`, `files_out_used`, `files_xfer_used`) + VALUES (:name, 'user', :bytes_in_used, '0', '0', '0', '0', '0') + "); + Database::pexecute($stmt, array( + "name" => $username, + "bytes_in_used" => $row['bytes_in_used'] + ), true, true); + } + + $stmt = Database::prepare(" + UPDATE `" . TABLE_FTP_GROUPS . "` + SET `members` = CONCAT_WS(',',`members`, :username) + WHERE `customerid`= :customerid AND `gid`= :guid + "); + $params = array( + "username" => $username, + "customerid" => $customer_id, + "guid" => $customer['guid'] + ); + Database::pexecute($stmt, $params, true, true); + + $stmt = Database::prepare(" + UPDATE `" . TABLE_PANEL_CUSTOMERS . "` + SET `ftps_used` = `ftps_used` + 1, + `ftp_lastaccountnumber` = `ftp_lastaccountnumber` + 1 + WHERE `customerid` = :customerid + "); + Database::pexecute($stmt, array( + "customerid" => $customer_id + ), true, true); + + $stmt = Database::prepare(" + UPDATE `" . TABLE_PANEL_ADMINS . "` + SET `ftps_used` = `ftps_used` + 1 + WHERE `adminid` = :adminid + "); + Database::pexecute($stmt, array( + "adminid" => $customer['adminid'] + ), true, true); + + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_INFO, "[API] added ftp-account '" . $username . " (" . $path . ")'"); + inserttask(5); + + if ($sendinfomail == 1) { + $replace_arr = array( + 'SALUTATION' => getCorrectUserSalutation($customer), + 'CUST_NAME' => getCorrectUserSalutation($customer), // < keep this for compatibility + 'USR_NAME' => $username, + 'USR_PASS' => $password, + 'USR_PATH' => makeCorrectDir(str_replace($customer['documentroot'], "/", $path)) + ); + + $def_language = $customer['def_language']; + $result_stmt = Database::prepare(" + SELECT `value` FROM `" . TABLE_PANEL_TEMPLATES . "` + WHERE `adminid` = :adminid + AND `language` = :lang + AND `templategroup`='mails' + AND `varname`='new_ftpaccount_by_customer_subject' + "); + Database::pexecute($result_stmt, array( + "adminid" => $customer['adminid'], + "lang" => $def_language + )); + $result = $result_stmt->fetch(PDO::FETCH_ASSOC); + $mail_subject = html_entity_decode(replace_variables((($result['value'] != '') ? $result['value'] : $this->lng['mails']['new_ftpaccount_by_customer']['subject']), $replace_arr)); + + $def_language = $customer['def_language']; + $result_stmt = Database::prepare(" + SELECT `value` FROM `" . TABLE_PANEL_TEMPLATES . "` + WHERE `adminid` = :adminid + AND `language` = :lang + AND `templategroup`='mails' + AND `varname`='new_ftpaccount_by_customer_mailbody'"); + Database::pexecute($result_stmt, array( + "adminid" => $customer['adminid'], + "lang" => $def_language + )); + $result = $result_stmt->fetch(PDO::FETCH_ASSOC); + $mail_body = html_entity_decode(replace_variables((($result['value'] != '') ? $result['value'] : $this->lng['mails']['new_ftpaccount_by_customer']['mailbody']), $replace_arr)); + + $_mailerror = false; + try { + $mail->Subject = $mail_subject; + $mail->AltBody = $mail_body; + $mail->MsgHTML(str_replace("\n", "
    ", $mail_body)); + $mail->AddAddress($customer['email'], getCorrectUserSalutation($customer)); + $mail->Send(); + } catch (phpmailerException $e) { + $mailerr_msg = $e->errorMessage(); + $_mailerror = true; + } catch (Exception $e) { + $mailerr_msg = $e->getMessage(); + $_mailerror = true; + } + + if ($_mailerror) { + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_ERR, "[API] Error sending mail: " . $mailerr_msg); + standard_error('errorsendingmail', $customer['email']); + } + + $mail->ClearAddresses(); + } + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_WARNING, "[API] added ftp-user '" . $username . "'"); + + $json_result = Ftps::getLocal($this->getUserData(), array( + 'username' => $username + ))->get(); + $result = json_decode($json_result, true)['data']; + return $this->response(200, "successfull", $result); + } + } + throw new Exception("No more resources available", 406); + } /** * return a ftp-user entry by either id or username diff --git a/lib/classes/api/commands/class.Mysqls.php b/lib/classes/api/commands/class.Mysqls.php index ad2ea24c..2b4c6649 100644 --- a/lib/classes/api/commands/class.Mysqls.php +++ b/lib/classes/api/commands/class.Mysqls.php @@ -79,7 +79,7 @@ class Mysqls extends ApiCommand implements ResourceEntity // get customer id $customer_id = $this->getParam('customer_id'); $json_result = Customers::getLocal($this->getUserData(), array( - 'id' => $result['customerid'] + 'id' => $customer_id ))->get(); $customer = json_decode($json_result, true)['data']; // check whether the customer has enough resources to get the database added @@ -215,7 +215,12 @@ class Mysqls extends ApiCommand implements ResourceEntity $this->mail->ClearAddresses(); } $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_WARNING, "[API] added mysql-database '" . $username . "'"); - return $this->response(200, "successfull", $params); + + $json_result = Mysqls::getLocal($this->getUserData(), array( + 'dbname' => $username + ))->get(); + $result = json_decode($json_result, true)['data']; + return $this->response(200, "successfull", $result); } throw new Exception("No more resources available", 406); } From 55ec20be10d0030b53541791cb974094be9aef46 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 26 Feb 2018 15:55:49 +0100 Subject: [PATCH 0500/1335] use Ftps.add in webinterface Signed-off-by: Michael Kaufmann (d00p) --- customer_ftp.php | 168 +----------------------- lib/classes/api/commands/class.Ftps.php | 2 +- 2 files changed, 7 insertions(+), 163 deletions(-) diff --git a/customer_ftp.php b/customer_ftp.php index bd6cbe11..474ea057 100644 --- a/customer_ftp.php +++ b/customer_ftp.php @@ -104,169 +104,13 @@ if ($page == 'overview') { } } elseif ($action == 'add') { if ($userinfo['ftps_used'] < $userinfo['ftps'] || $userinfo['ftps'] == '-1') { - if (isset($_POST['send']) - && $_POST['send'] == 'send') { - $description = validate($_POST['ftp_description'], 'description'); - // @FIXME use a good path-validating regex here (refs #1231) - $path = validate($_POST['path'], 'path'); - $password = validate($_POST['ftp_password'], 'password'); - $password = validatePassword($password); - $shell = "/bin/false"; - if (Settings::Get('system.allow_customer_shell') == '1') { - $shell = isset($_POST['shell']) ? validate($_POST['shell'], 'shell') : '/bin/false'; - } - - $sendinfomail = isset($_POST['sendinfomail']) ? 1 : 0; - if ($sendinfomail != 1) { - $sendinfomail = 0; - } - - if (Settings::Get('customer.ftpatdomain') == '1') { - $ftpusername = validate($_POST['ftp_username'], 'username', '/^[a-zA-Z0-9][a-zA-Z0-9\-_]+\$?$/'); - if ($ftpusername == '') { - standard_error(array('stringisempty', 'username')); - } - $ftpdomain = $idna_convert->encode(validate($_POST['ftp_domain'], 'domain')); - $ftpdomain_check_stmt = Database::prepare("SELECT `id`, `domain`, `customerid` FROM `" . TABLE_PANEL_DOMAINS . "` - WHERE `domain` = :domain - AND `customerid` = :customerid" - ); - Database::pexecute($ftpdomain_check_stmt, array("domain" => $ftpdomain, "customerid" => $userinfo['customerid'])); - $ftpdomain_check = $ftpdomain_check_stmt->fetch(PDO::FETCH_ASSOC); - - if ($ftpdomain_check['domain'] != $ftpdomain) { - standard_error('maindomainnonexist', $domain); - } - $username = $ftpusername . "@" . $ftpdomain; - } else { - $username = $userinfo['loginname'] . Settings::Get('customer.ftpprefix') . (intval($userinfo['ftp_lastaccountnumber']) + 1); - } - - $username_check_stmt = Database::prepare("SELECT * FROM `" . TABLE_FTP_USERS . "` - WHERE `username` = :username" - ); - Database::pexecute($username_check_stmt, array("username" => $username)); - $username_check = $username_check_stmt->fetch(PDO::FETCH_ASSOC); - - if (!empty($username_check) && $username_check['username'] = $username) { - standard_error('usernamealreadyexists', $username); - } elseif ($password == '') { - standard_error(array('stringisempty', 'mypassword')); - } elseif ($path == '') { - standard_error('patherror'); - } elseif ($username == $password) { - standard_error('passwordshouldnotbeusername'); - } else { - $path = makeCorrectDir($userinfo['documentroot'] . '/' . $path); - - $cryptPassword = makeCryptPassword($password); - - $stmt = Database::prepare("INSERT INTO `" . TABLE_FTP_USERS . "` - (`customerid`, `username`, `description`, `password`, `homedir`, `login_enabled`, `uid`, `gid`, `shell`) - VALUES (:customerid, :username, :description, :password, :homedir, 'y', :guid, :guid, :shell)" - ); - $params = array( - "customerid" => $userinfo['customerid'], - "username" => $username, - "description" => $description, - "password" => $cryptPassword, - "homedir" => $path, - "guid" => $userinfo['guid'], - "shell" => $shell - ); - Database::pexecute($stmt, $params); - - $result_stmt = Database::prepare("SELECT `bytes_in_used` FROM `" . TABLE_FTP_QUOTATALLIES . "` - WHERE `name` = :name" - ); - Database::pexecute($result_stmt, array("name" => $userinfo['loginname'])); - - while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) { - $stmt = Database::prepare("INSERT INTO `" . TABLE_FTP_QUOTATALLIES . "` - (`name`, `quota_type`, `bytes_in_used`, `bytes_out_used`, `bytes_xfer_used`, `files_in_used`, `files_out_used`, `files_xfer_used`) - VALUES (:name, 'user', :bytes_in_used, '0', '0', '0', '0', '0')" - ); - Database::pexecute($stmt, array("name" => $username, "bytes_in_used" => $row['bytes_in_used'])); - } - - $stmt = Database::prepare("UPDATE `" . TABLE_FTP_GROUPS . "` - SET `members` = CONCAT_WS(',',`members`, :username) - WHERE `customerid`= :customerid - AND `gid`= :guid" - ); - $params = array( - "username" => $username, - "customerid" => $userinfo['customerid'], - "guid" => $userinfo['guid'] - ); - Database::pexecute($stmt, $params); - - $stmt = Database::prepare("UPDATE `" . TABLE_PANEL_CUSTOMERS . "` - SET `ftps_used` = `ftps_used` + 1, - `ftp_lastaccountnumber` = `ftp_lastaccountnumber` + 1 - WHERE `customerid` = :customerid" - ); - Database::pexecute($stmt, array("customerid" => $userinfo['customerid'])); - - $log->logAction(USR_ACTION, LOG_INFO, "added ftp-account '" . $username . " (" . $path . ")'"); - inserttask(5); - - if ($sendinfomail == 1) { - $replace_arr = array( - 'SALUTATION' => getCorrectUserSalutation($userinfo), - 'CUST_NAME' => getCorrectUserSalutation($userinfo), // < keep this for compatibility - 'USR_NAME' => $username, - 'USR_PASS' => $password, - 'USR_PATH' => makeCorrectDir(str_replace($userinfo['documentroot'], "/", $path)) - ); - - $def_language = $userinfo['def_language']; - $result_stmt = Database::prepare("SELECT `value` FROM `" . TABLE_PANEL_TEMPLATES . "` - WHERE `adminid` = :adminid - AND `language` = :lang - AND `templategroup`='mails' - AND `varname`='new_ftpaccount_by_customer_subject'" - ); - Database::pexecute($result_stmt, array("adminid" => $userinfo['adminid'], "lang" => $def_language)); - $result = $result_stmt->fetch(PDO::FETCH_ASSOC); - $mail_subject = html_entity_decode(replace_variables((($result['value'] != '') ? $result['value'] : $lng['mails']['new_ftpaccount_by_customer']['subject']), $replace_arr)); - - $def_language = $userinfo['def_language']; - $result_stmt = Database::prepare("SELECT `value` FROM `" . TABLE_PANEL_TEMPLATES . "` - WHERE `adminid` = :adminid - AND `language` = :lang - AND `templategroup`='mails' - AND `varname`='new_ftpaccount_by_customer_mailbody'" - ); - Database::pexecute($result_stmt, array("adminid" => $userinfo['adminid'], "lang" => $def_language)); - $result = $result_stmt->fetch(PDO::FETCH_ASSOC); - $mail_body = html_entity_decode(replace_variables((($result['value'] != '') ? $result['value'] : $lng['mails']['new_ftpaccount_by_customer']['mailbody']), $replace_arr)); - - $_mailerror = false; - try { - $mail->Subject = $mail_subject; - $mail->AltBody = $mail_body; - $mail->MsgHTML(str_replace("\n", "
    ", $mail_body)); - $mail->AddAddress($userinfo['email'], getCorrectUserSalutation($userinfo)); - $mail->Send(); - } catch(phpmailerException $e) { - $mailerr_msg = $e->errorMessage(); - $_mailerror = true; - } catch (Exception $e) { - $mailerr_msg = $e->getMessage(); - $_mailerror = true; - } - - if ($_mailerror) { - $log->logAction(USR_ACTION, LOG_ERR, "Error sending mail: " . $mailerr_msg); - standard_error('errorsendingmail', $userinfo['email']); - } - - $mail->ClearAddresses(); - } - - redirectTo($filename, array('page' => $page, 's' => $s)); + if (isset($_POST['send']) && $_POST['send'] == 'send') { + try { + Ftps::getLocal($userinfo, $_POST)->add(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); } + redirectTo($filename, array('page' => $page, 's' => $s)); } else { $pathSelect = makePathfield($userinfo['documentroot'], $userinfo['guid'], $userinfo['guid'], '/'); diff --git a/lib/classes/api/commands/class.Ftps.php b/lib/classes/api/commands/class.Ftps.php index 595a5f78..b9a0ea34 100644 --- a/lib/classes/api/commands/class.Ftps.php +++ b/lib/classes/api/commands/class.Ftps.php @@ -24,7 +24,7 @@ class Ftps extends ApiCommand implements ResourceEntity * @param string $ftp_password * password for the created database and database-user * @param string $path - * destination path + * destination path relative to the customers-homedir * @param string $ftp_description * optional, description for ftp-user * @param bool $sendinfomail From 257855b43bfd01bf06c7df465aaf2a136aed7b7c Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 26 Feb 2018 16:00:54 +0100 Subject: [PATCH 0501/1335] fix typo in field-value for ApiCommand::getUserDetail() Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/commands/class.Ftps.php | 2 +- lib/classes/api/commands/class.Mysqls.php | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/classes/api/commands/class.Ftps.php b/lib/classes/api/commands/class.Ftps.php index b9a0ea34..7f9ba35b 100644 --- a/lib/classes/api/commands/class.Ftps.php +++ b/lib/classes/api/commands/class.Ftps.php @@ -93,7 +93,7 @@ class Ftps extends ApiCommand implements ResourceEntity throw new Exception("Customer has no more resources available", 406); } } else { - $customer_id = $this->getUserDetail('customer_id'); + $customer_id = $this->getUserDetail('customerid'); $customer = $this->getUserData(); } diff --git a/lib/classes/api/commands/class.Mysqls.php b/lib/classes/api/commands/class.Mysqls.php index 2b4c6649..5811477f 100644 --- a/lib/classes/api/commands/class.Mysqls.php +++ b/lib/classes/api/commands/class.Mysqls.php @@ -87,7 +87,7 @@ class Mysqls extends ApiCommand implements ResourceEntity throw new Exception("Customer has no more resources available", 406); } } else { - $customer_id = $this->getUserDetail('customer_id'); + $customer_id = $this->getUserDetail('customerid'); } $newdb_params = array( @@ -389,7 +389,7 @@ class Mysqls extends ApiCommand implements ResourceEntity throw new Exception("Customer has no more resources available", 406); } } else { - $customer_id = $this->getUserDetail('customer_id'); + $customer_id = $this->getUserDetail('customerid'); } if ($password != '') { @@ -585,7 +585,7 @@ class Mysqls extends ApiCommand implements ResourceEntity $customer_id = $customer['customer_id']; } else { $mysql_used = $this->getUserDetail('mysqls_used'); - $customer_id = $this->getUserDetail('customer_id'); + $customer_id = $this->getUserDetail('customerid'); } // reduce mysql-usage-counter $resetaccnumber = ($mysql_used == '1') ? " , `mysql_lastaccountnumber` = '0' " : ''; From c093783904da43f1903da91df52f87db541e1e56 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 26 Feb 2018 16:02:09 +0100 Subject: [PATCH 0502/1335] forgot the mailobject :P Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/commands/class.Ftps.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/classes/api/commands/class.Ftps.php b/lib/classes/api/commands/class.Ftps.php index 7f9ba35b..863e3c7f 100644 --- a/lib/classes/api/commands/class.Ftps.php +++ b/lib/classes/api/commands/class.Ftps.php @@ -251,11 +251,11 @@ class Ftps extends ApiCommand implements ResourceEntity $_mailerror = false; try { - $mail->Subject = $mail_subject; - $mail->AltBody = $mail_body; - $mail->MsgHTML(str_replace("\n", "
    ", $mail_body)); - $mail->AddAddress($customer['email'], getCorrectUserSalutation($customer)); - $mail->Send(); + $this->mail->Subject = $mail_subject; + $this->mail->AltBody = $mail_body; + $this->mail->MsgHTML(str_replace("\n", "
    ", $mail_body)); + $this->mail->AddAddress($customer['email'], getCorrectUserSalutation($customer)); + $this->mail->Send(); } catch (phpmailerException $e) { $mailerr_msg = $e->errorMessage(); $_mailerror = true; @@ -269,7 +269,7 @@ class Ftps extends ApiCommand implements ResourceEntity standard_error('errorsendingmail', $customer['email']); } - $mail->ClearAddresses(); + $this->mail->ClearAddresses(); } $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_WARNING, "[API] added ftp-user '" . $username . "'"); From 4fad0def1e817edba28c67ddf5405ed5d176f17f Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 26 Feb 2018 18:02:18 +0100 Subject: [PATCH 0503/1335] fix mailer variable and fix typo in Ftps.add and Ftps.delete Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/commands/class.Ftps.php | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/lib/classes/api/commands/class.Ftps.php b/lib/classes/api/commands/class.Ftps.php index 863e3c7f..226de773 100644 --- a/lib/classes/api/commands/class.Ftps.php +++ b/lib/classes/api/commands/class.Ftps.php @@ -251,11 +251,11 @@ class Ftps extends ApiCommand implements ResourceEntity $_mailerror = false; try { - $this->mail->Subject = $mail_subject; - $this->mail->AltBody = $mail_body; - $this->mail->MsgHTML(str_replace("\n", "
    ", $mail_body)); - $this->mail->AddAddress($customer['email'], getCorrectUserSalutation($customer)); - $this->mail->Send(); + $this->mailer()->Subject = $mail_subject; + $this->mailer()->AltBody = $mail_body; + $this->mailer()->MsgHTML(str_replace("\n", "
    ", $mail_body)); + $this->mailer()->AddAddress($customer['email'], getCorrectUserSalutation($customer)); + $this->mailer()->Send(); } catch (phpmailerException $e) { $mailerr_msg = $e->errorMessage(); $_mailerror = true; @@ -266,10 +266,10 @@ class Ftps extends ApiCommand implements ResourceEntity if ($_mailerror) { $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_ERR, "[API] Error sending mail: " . $mailerr_msg); - standard_error('errorsendingmail', $customer['email']); + standard_error('errorsendingmail', $customer['email'], true); } - $this->mail->ClearAddresses(); + $this->mailer()->ClearAddresses(); } $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_WARNING, "[API] added ftp-user '" . $username . "'"); @@ -475,7 +475,7 @@ class Ftps extends ApiCommand implements ResourceEntity $stmt = Database::prepare("DELETE FROM `" . TABLE_FTP_QUOTATALLIES . "` WHERE `name` = :name"); Database::pexecute($stmt, array( "name" => $result['username'] - ), true, tue); + ), true, true); // remove user itself $stmt = Database::prepare(" @@ -496,9 +496,7 @@ class Ftps extends ApiCommand implements ResourceEntity "username" => "," . $result['username'], "customerid" => $customer_data['customerid'] ), true, true); - - $log->logAction(USR_ACTION, LOG_INFO, "deleted ftp-account '" . $result['username'] . "'"); - + // refs #293 if ($delete_userfiles == 1) { inserttask('8', $customer_data['loginname'], $result['homedir']); From 4d89f614e33582408a1e7261a2aea7a810d12d0e Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 26 Feb 2018 18:08:43 +0100 Subject: [PATCH 0504/1335] remove unnecessary checks as getParam() validates the existance already Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/commands/class.Customers.php | 25 ++++---------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/lib/classes/api/commands/class.Customers.php b/lib/classes/api/commands/class.Customers.php index 40823efd..2d1dce24 100644 --- a/lib/classes/api/commands/class.Customers.php +++ b/lib/classes/api/commands/class.Customers.php @@ -208,13 +208,8 @@ class Customers extends ApiCommand implements ResourceEntity if (((($this->getUserDetail('diskspace_used') + $diskspace) > $this->getUserDetail('diskspace')) && ($this->getUserDetail('diskspace') / 1024) != '-1') || ((($this->getUserDetail('mysqls_used') + $mysqls) > $this->getUserDetail('mysqls')) && $this->getUserDetail('mysqls') != '-1') || ((($this->getUserDetail('emails_used') + $emails) > $this->getUserDetail('emails')) && $this->getUserDetail('emails') != '-1') || ((($this->getUserDetail('email_accounts_used') + $email_accounts) > $this->getUserDetail('email_accounts')) && $this->getUserDetail('email_accounts') != '-1') || ((($this->getUserDetail('email_forwarders_used') + $email_forwarders) > $this->getUserDetail('email_forwarders')) && $this->getUserDetail('email_forwarders') != '-1') || ((($this->getUserDetail('email_quota_used') + $email_quota) > $this->getUserDetail('email_quota')) && $this->getUserDetail('email_quota') != '-1' && Settings::Get('system.mail_quota_enabled') == '1') || ((($this->getUserDetail('ftps_used') + $ftps) > $this->getUserDetail('ftps')) && $this->getUserDetail('ftps') != '-1') || ((($this->getUserDetail('tickets_used') + $tickets) > $this->getUserDetail('tickets')) && $this->getUserDetail('tickets') != '-1') || ((($this->getUserDetail('subdomains_used') + $subdomains) > $this->getUserDetail('subdomains')) && $this->getUserDetail('subdomains') != '-1') || (($diskspace / 1024) == '-1' && ($this->getUserDetail('diskspace') / 1024) != '-1') || ($mysqls == '-1' && $this->getUserDetail('mysqls') != '-1') || ($emails == '-1' && $this->getUserDetail('emails') != '-1') || ($email_accounts == '-1' && $this->getUserDetail('email_accounts') != '-1') || ($email_forwarders == '-1' && $this->getUserDetail('email_forwarders') != '-1') || ($email_quota == '-1' && $this->getUserDetail('email_quota') != '-1' && Settings::Get('system.mail_quota_enabled') == '1') || ($ftps == '-1' && $this->getUserDetail('ftps') != '-1') || ($tickets == '-1' && $this->getUserDetail('tickets') != '-1') || ($subdomains == '-1' && $this->getUserDetail('subdomains') != '-1')) { standard_error('youcantallocatemorethanyouhave', '', true); } - - if ($email == '') { - standard_error(array( - 'stringisempty', - 'emailadd' - ), '', true); - } elseif (! validateEmail($email)) { + + if (! validateEmail($email)) { standard_error('emailiswrong', $email, true); } else { @@ -687,7 +682,8 @@ class Customers extends ApiCommand implements ResourceEntity $email = $this->getParam('email', true, $idna_convert->decode($result['email'])); $name = $this->getParam('name', true, $result['name']); $firstname = $this->getParam('firstname', true, $result['firstname']); - $company = $this->getParam('company', true, $result['company']); + $company_required = (! empty($name) && empty($firstname)) || (empty($name) && ! empty($firstname)) || (empty($name) && empty($firstname)); + $company = $this->getParam('company', ($company_required ? false : true), $result['company']); $street = $this->getParam('street', true, $result['street']); $zipcode = $this->getParam('zipcode', true, $result['zipcode']); $city = $this->getParam('city', true, $result['city']); @@ -767,18 +763,7 @@ class Customers extends ApiCommand implements ResourceEntity standard_error('youcantallocatemorethanyouhave', '', true); } - // Either $name and $firstname or the $company must be inserted - if ($name == '' && $company == '') { - standard_error(array( - 'stringisempty', - 'myname' - ), '', true); - } elseif ($firstname == '' && $company == '') { - standard_error(array( - 'stringisempty', - 'myfirstname' - ), '', true); - } elseif ($email == '') { + if ($email == '') { standard_error(array( 'stringisempty', 'emailadd' From 9a4359e010acacf745e354eff3e1737e5f815c3a Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 26 Feb 2018 19:56:03 +0100 Subject: [PATCH 0505/1335] fix IpsAndPorts.delete Signed-off-by: Michael Kaufmann (d00p) --- .../api/commands/class.IpsAndPorts.php | 68 +++++++++---------- lng/english.lng.php | 2 +- 2 files changed, 35 insertions(+), 35 deletions(-) diff --git a/lib/classes/api/commands/class.IpsAndPorts.php b/lib/classes/api/commands/class.IpsAndPorts.php index a2bb599f..81976d39 100644 --- a/lib/classes/api/commands/class.IpsAndPorts.php +++ b/lib/classes/api/commands/class.IpsAndPorts.php @@ -372,7 +372,12 @@ class IpsAndPorts extends ApiCommand implements ResourceEntity inserttask('4'); $this->logger()->logAction(ADM_ACTION, LOG_WARNING, "[API] changed IP/port from '" . $result['ip'] . ":" . $result['port'] . "' to '" . $ip . ":" . $port . "'"); - return $this->response(200, "successfull", $upd_data); + + $json_result = IpsAndPorts::getLocal($this->getUserData(), array( + 'id' => $result['id'] + ))->get(); + $result = json_decode($json_result, true)['data']; + return $this->response(200, "successfull", $result); } } throw new Exception("Not allowed to execute given command.", 403); @@ -397,7 +402,7 @@ class IpsAndPorts extends ApiCommand implements ResourceEntity 'id' => $id ))->get(); $result = json_decode($json_result, true)['data']; - + $result_checkdomain_stmt = Database::prepare(" SELECT `id_domain` as `id` FROM `" . TABLE_DOMAINTOIP . "` WHERE `id_ipandports` = :id "); @@ -405,9 +410,12 @@ class IpsAndPorts extends ApiCommand implements ResourceEntity 'id' => $id ), true, true); - if ($result_checkdomain['id'] == '') { + if (empty($result_checkdomain)) { if (! in_array($result['id'], explode(',', Settings::Get('system.defaultip'))) && ! in_array($result['id'], explode(',', Settings::Get('system.defaultsslip')))) { - + + // check whether there is the same IP with a different port + // in case this ip-address is the system.ipaddress and therefore + // when there is one - we have an alternative $result_sameipotherport_stmt = Database::prepare(" SELECT `id` FROM `" . TABLE_PANEL_IPSANDPORTS . "` WHERE `ip` = :ip AND `id` <> :id"); @@ -417,37 +425,29 @@ class IpsAndPorts extends ApiCommand implements ResourceEntity )); if (($result['ip'] != Settings::Get('system.ipaddress')) || ($result['ip'] == Settings::Get('system.ipaddress') && $result_sameipotherport['id'] != '')) { - $result_stmt = Database::prepare(" - SELECT `ip`, `port` FROM `" . TABLE_PANEL_IPSANDPORTS . "` - WHERE `id` = :id"); - $result = Database::pexecute_first($result_stmt, array( + + $del_stmt = Database::prepare(" + DELETE FROM `" . TABLE_PANEL_IPSANDPORTS . "` + WHERE `id` = :id + "); + Database::pexecute($del_stmt, array( 'id' => $id - )); - if ($result['ip'] != '') { - - $del_stmt = Database::prepare(" - DELETE FROM `" . TABLE_PANEL_IPSANDPORTS . "` - WHERE `id` = :id - "); - Database::pexecute($del_stmt, array( - 'id' => $id - )); - - // also, remove connections to domains (multi-stack) - $del_stmt = Database::prepare(" - DELETE FROM `" . TABLE_DOMAINTOIP . "` WHERE `id_ipandports` = :id - "); - Database::pexecute($del_stmt, array( - 'id' => $id - )); - - inserttask('1'); - // Using nameserver, insert a task which rebuilds the server config - inserttask('4'); - - $this->logger()->logAction(ADM_ACTION, LOG_WARNING, "[API] deleted IP/port '" . $result['ip'] . ":" . $result['port'] . "'"); - return $this->response(200, "successfull", $result); - } + ), true, true); + + // also, remove connections to domains (multi-stack) + $del_stmt = Database::prepare(" + DELETE FROM `" . TABLE_DOMAINTOIP . "` WHERE `id_ipandports` = :id + "); + Database::pexecute($del_stmt, array( + 'id' => $id + ), true, true); + + inserttask('1'); + // Using nameserver, insert a task which rebuilds the server config + inserttask('4'); + + $this->logger()->logAction(ADM_ACTION, LOG_WARNING, "[API] deleted IP/port '" . $result['ip'] . ":" . $result['port'] . "'"); + return $this->response(200, "successfull", $result); } else { standard_error('cantdeletesystemip', '', true); } diff --git a/lng/english.lng.php b/lng/english.lng.php index b0f87d35..1fa929f3 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -388,7 +388,7 @@ $lng['mysql']['description'] = 'Here you can create and change your MySQL-databa $lng['serversettings']['paging']['title'] = 'Entries per page'; $lng['serversettings']['paging']['description'] = 'How many entries shall be shown on one page? (0 = disable paging)'; $lng['error']['ipstillhasdomains'] = 'The IP/Port combination you want to delete still has domains assigned to it, please reassign those to other IP/Port combinations before deleting this IP/Port combination.'; -$lng['error']['cantdeletedefaultip'] = 'You cannot delete the default reseller IP/Port combination, please make another IP/Port combination default for resellers before deleting this IP/Port combination.'; +$lng['error']['cantdeletedefaultip'] = 'You cannot delete the default IP/Port combination, please make another IP/Port combination default for before deleting this IP/Port combination.'; $lng['error']['cantdeletesystemip'] = 'You cannot delete the last system IP, either create a new IP/Port combination for the system IP or change the system IP.'; $lng['error']['myipaddress'] = '\'IP\''; $lng['error']['myport'] = '\'Port\''; From 2599f61b3213cbf3b8683e8aa0e97c963496bcd7 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 26 Feb 2018 22:35:04 +0100 Subject: [PATCH 0506/1335] add jenkins build.xml and more; added first unit-tests Signed-off-by: Michael Kaufmann (d00p) --- build.xml | 233 +++++++++++++++ phpcs.xml | 15 + phpdox.xml | 13 + phpmd.xml | 24 ++ phpunit.xml | 39 +++ tests/Admins/AdminsTest.php | 238 +++++++++++++++ tests/Customers/CustomersTest.php | 407 ++++++++++++++++++++++++++ tests/Ftps/FtpsTest.php | 107 +++++++ tests/Global/FroxlorRpcTest.php | 117 ++++++++ tests/IpsAndPorts/IpsAndPortsTest.php | 276 +++++++++++++++++ tests/bootstrap.php | 111 +++++++ 11 files changed, 1580 insertions(+) create mode 100644 build.xml create mode 100644 phpcs.xml create mode 100644 phpdox.xml create mode 100644 phpmd.xml create mode 100644 phpunit.xml create mode 100644 tests/Admins/AdminsTest.php create mode 100644 tests/Customers/CustomersTest.php create mode 100644 tests/Ftps/FtpsTest.php create mode 100644 tests/Global/FroxlorRpcTest.php create mode 100644 tests/IpsAndPorts/IpsAndPortsTest.php create mode 100644 tests/bootstrap.php diff --git a/build.xml b/build.xml new file mode 100644 index 00000000..99f202d8 --- /dev/null +++ b/build.xml @@ -0,0 +1,233 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/phpcs.xml b/phpcs.xml new file mode 100644 index 00000000..757d1e74 --- /dev/null +++ b/phpcs.xml @@ -0,0 +1,15 @@ + + + PSR2 with tabs instead of spaces. + + + + + + + + + + + + diff --git a/phpdox.xml b/phpdox.xml new file mode 100644 index 00000000..813dbd4a --- /dev/null +++ b/phpdox.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/phpmd.xml b/phpmd.xml new file mode 100644 index 00000000..0f1aae49 --- /dev/null +++ b/phpmd.xml @@ -0,0 +1,24 @@ + + + + froxlor ruleset. + + + + + + + + + + + + + + + + + diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 00000000..89f83835 --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,39 @@ + + + + + + + tests/Global + tests/Admins + tests/IpsAndPorts + tests/Customers + tests/Ftps + + + + + + + + + + + + + ./lib/classes/api + + ./lib/classes/api/api_includes.inc.php + ./lib/classes/api/interface.ResourceEntity.php + + + + + diff --git a/tests/Admins/AdminsTest.php b/tests/Admins/AdminsTest.php new file mode 100644 index 00000000..15c81e3c --- /dev/null +++ b/tests/Admins/AdminsTest.php @@ -0,0 +1,238 @@ + 'reseller', + 'email' => 'testreseller@froxlor.org', + 'name' => 'Testreseller', + 'admin_password' => 'h0lYmo1y', + 'diskspace' => - 1, + 'traffic' => - 1, + 'customers' => 15, + 'domains' => 15, + 'subdomains' => 15, + 'emails' => - 1, + 'email_accounts' => 15, + 'email_forwarders' => 15, + 'email_imap' => 1, + 'email_pop3' => 0, + 'ftps' => 15, + 'tickets' => 15, + 'mysqls' => 15, + 'sendpassword' => 0, + 'phpenabled' => 1, + 'ip' => array() + ]; + + $json_result = Admins::getLocal($admin_userdata, $data)->add(); + $result = json_decode($json_result, true)['data']; + $admin_id = $result['adminid']; + + // get admin and check results + $json_result = Admins::getLocal($admin_userdata, array( + 'id' => $admin_id + ))->get(); + $result = json_decode($json_result, true)['data']; + + $this->assertEquals('reseller', $result['loginname']); + $this->assertEquals('testreseller@froxlor.org', $result['email']); + $this->assertEquals(0, $result['customers_see_all']); + } + + public function testAdminAdminsAddNotAllowed() + { + global $admin_userdata; + $testadmin_userdata = $admin_userdata; + $testadmin_userdata['adminsession'] = 0; + + $this->expectExceptionCode(403); + $this->expectExceptionMessage("Not allowed to execute given command."); + Admins::getLocal($testadmin_userdata, array())->add(); + } + + public function testAdminAdminsGetNotFound() + { + global $admin_userdata; + + $this->expectExceptionCode(404); + $this->expectExceptionMessage("Admin with loginname 'unknown' could not be found"); + // get unknown admin + Admins::getLocal($admin_userdata, array( + 'loginname' => 'unknown' + ))->get(); + } + + public function testAdminAdminsList() + { + global $admin_userdata; + + $json_result = Admins::getLocal($admin_userdata)->list(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals(2, $result['count']); + } + + public function testResellerAdminsGet() + { + global $admin_userdata; + // get reseller + $json_result = Admins::getLocal($admin_userdata, array( + 'loginname' => 'reseller' + ))->get(); + $reseller_userdata = json_decode($json_result, true)['data']; + $reseller_userdata['adminsession'] = 1; + + // try to read superadmin with an access-less reseller account + $this->expectException(Exception::class); + $this->expectExceptionCode(403); + + $json_result = Admins::getLocal($reseller_userdata, array( + 'loginname' => 'admin' + ))->get(); + } + + public function testResellerAdminsListNotAllowed() + { + global $admin_userdata; + // get reseller + $json_result = Admins::getLocal($admin_userdata, array( + 'loginname' => 'reseller' + ))->get(); + $reseller_userdata = json_decode($json_result, true)['data']; + $reseller_userdata['adminsession'] = 1; + + $this->expectExceptionCode(403); + $this->expectExceptionMessage("Not allowed to execute given command."); + + Admins::getLocal($reseller_userdata)->list(); + } + + public function testAdminAdminsUnlock() + { + global $admin_userdata; + // update reseller to have correct test-data + Database::query("UPDATE `" . TABLE_PANEL_ADMINS . "` SET `loginfail_count` = '5' WHERE `loginname` = 'reseller'"); + $json_result = Admins::getLocal($admin_userdata, array( + 'loginname' => 'reseller' + ))->unlock(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals(0, $result['loginfail_count']); + } + + public function testAdminAdminsUnlockNotAllowed() + { + global $admin_userdata; + $testadmin_userdata = $admin_userdata; + $testadmin_userdata['adminsession'] = 0; + + $this->expectExceptionCode(403); + $this->expectExceptionMessage("Not allowed to execute given command."); + Admins::getLocal($testadmin_userdata, array( + 'loginname' => 'admin' + ))->unlock(); + } + + public function testAdminAdminsDeleteMyOwn() + { + global $admin_userdata; + $this->expectExceptionMessage("You cannot delete yourself for security reasons."); + Admins::getLocal($admin_userdata, array( + 'loginname' => 'admin' + ))->delete(); + } + + public function testAdminAdminsDeleteReseller() + { + global $admin_userdata; + + // add test reseller + $data = [ + 'new_loginname' => 'resellertest', + 'email' => 'testreseller@froxlor.org', + 'name' => 'Testreseller', + 'admin_password' => 'h0lYmo1y' + ]; + + $json_result = Admins::getLocal($admin_userdata, $data)->add(); + + $json_result = Admins::getLocal($admin_userdata, array( + 'loginname' => 'resellertest' + ))->delete(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals('resellertest', $result['loginname']); + } + + public function testResellerAdminsDelete() + { + global $admin_userdata; + // get reseller + $json_result = Admins::getLocal($admin_userdata, array( + 'loginname' => 'reseller' + ))->get(); + $reseller_userdata = json_decode($json_result, true)['data']; + $reseller_userdata['adminsession'] = 1; + + $this->expectExceptionCode(403); + $this->expectExceptionMessage("Not allowed to execute given command."); + Admins::getLocal($reseller_userdata, array( + 'loginname' => 'admin' + ))->delete(); + } + + public function testAdminAdminsEditMyOwn() + { + global $admin_userdata; + // update admin to have correct test-data + Database::query("UPDATE `" . TABLE_PANEL_ADMINS . "` SET `theme` = 'Froxlor', `def_language` = 'Deutsch' WHERE `loginname` = 'admin'"); + $json_result = Admins::getLocal($admin_userdata, array( + 'loginname' => 'admin', + 'theme' => '', + 'def_language' => 'English' + ))->update(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals('Sparkle', $result['theme']); + $this->assertEquals('English', $result['def_language']); + } + + public function testAdminAdminsEdit() + { + global $admin_userdata; + // update admin to have correct test-data + Database::query("UPDATE `" . TABLE_PANEL_ADMINS . "` SET `deactivated` = '0' WHERE `loginname` = 'reseller'"); + $json_result = Admins::getLocal($admin_userdata, array( + 'loginname' => 'reseller', + 'deactivated' => '1' + ))->update(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals(1, $result['deactivated']); + + // reactivate + Admins::getLocal($admin_userdata, array( + 'loginname' => 'reseller', + 'deactivated' => '0' + ))->update(); + } + + public function testAdminsAdminsEditNotAllowed() + { + global $admin_userdata; + $testadmin_userdata = $admin_userdata; + $testadmin_userdata['adminsession'] = 0; + + $this->expectExceptionCode(403); + $this->expectExceptionMessage("Not allowed to execute given command."); + Admins::getLocal($testadmin_userdata, array( + 'loginname' => 'admin' + ))->update(); + } +} diff --git a/tests/Customers/CustomersTest.php b/tests/Customers/CustomersTest.php new file mode 100644 index 00000000..6e2c354c --- /dev/null +++ b/tests/Customers/CustomersTest.php @@ -0,0 +1,407 @@ + 'test1', + 'email' => 'test@froxlor.org', + 'firstname' => 'Test', + 'name' => 'Testman', + 'customernumber' => 1337, + 'diskspace' => - 1, + 'traffic' => - 1, + 'subdomains' => 15, + 'emails' => - 1, + 'email_accounts' => 15, + 'email_forwarders' => 15, + 'email_imap' => 1, + 'email_pop3' => 0, + 'ftps' => 15, + 'tickets' => 15, + 'mysqls' => 15, + 'createstdsubdomain' => 1, + 'new_customer_password' => 'h0lYmo1y', + 'sendpassword' => 1, + 'phpenabled' => 1, + 'store_defaultindex' => 1, + 'custom_notes' => 'secret', + 'custom_notes_show' => 0, + 'gender' => 5, + 'allowed_phpconfigs' => array(1) + ]; + + $json_result = Customers::getLocal($admin_userdata, $data)->add(); + $result = json_decode($json_result, true)['data']; + $customer_id = $result['customerid']; + + // get customer and check results + $json_result = Customers::getLocal($admin_userdata, array( + 'id' => $customer_id + ))->get(); + $result = json_decode($json_result, true)['data']; + + $this->assertEquals(1, $result['customerid']); + $this->assertEquals('test@froxlor.org', $result['email']); + $this->assertEquals(1337, $result['customernumber']); + $this->assertEquals(15, $result['subdomains']); + $this->assertEquals('secret', $result['custom_notes']); + } + + public function testAdminCustomersAddEmptyMail() + { + global $admin_userdata; + + $data = [ + 'new_loginname' => 'test2', + 'email' => ' ', + 'firstname' => 'Test2', + 'name' => 'Testman2' + ]; + + $this->expectExceptionMessage('Requested parameter "email" is empty where it should not be for "Customers:add"'); + Customers::getLocal($admin_userdata, $data)->add(); + } + + public function testAdminCustomersAddInvalidMail() + { + global $admin_userdata; + + $data = [ + 'new_loginname' => 'test2', + 'email' => 'test.froxlor.org', + 'firstname' => 'Test2', + 'name' => 'Testman2' + ]; + + $this->expectExceptionMessage("Email-address test.froxlor.org contains invalid characters or is incomplete"); + Customers::getLocal($admin_userdata, $data)->add(); + } + + /** + * @depends testAdminCustomersAdd + */ + public function testAdminCustomersList() + { + global $admin_userdata; + + $json_result = Customers::getLocal($admin_userdata)->list(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals(1, $result['count']); + } + + /** + * @depends testAdminCustomersAdd + */ + public function testResellerCustomersList() + { + global $admin_userdata; + // get reseller + $json_result = Admins::getLocal($admin_userdata, array( + 'loginname' => 'reseller' + ))->get(); + $reseller_userdata = json_decode($json_result, true)['data']; + $reseller_userdata['adminsession'] = 1; + $json_result = Customers::getLocal($reseller_userdata)->list(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals(0, $result['count']); + } + + /** + * @depends testAdminCustomersAdd + */ + public function testCustomerCustomersList() + { + global $admin_userdata; + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'id' => 1 + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + + $this->expectExceptionCode(403); + $this->expectExceptionMessage("Not allowed to execute given command."); + + $json_result = Customers::getLocal($customer_userdata)->list(); + + } + + /** + * @depends testAdminCustomersAdd + */ + public function testCustomerCustomersGet() + { + global $admin_userdata; + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'id' => 1 + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + + $json_result = Customers::getLocal($customer_userdata, array( + 'id' => $customer_userdata['customerid'] + ))->get(); + $result = json_decode($json_result, true)['data']; + + $this->assertEquals(1, $result['customerid']); + $this->assertEquals('test@froxlor.org', $result['email']); + $this->assertEquals(1337, $result['customernumber']); + $this->assertEquals(15, $result['subdomains']); + $this->assertEquals('Sparkle', $result['theme']); + $this->assertEquals('', $result['custom_notes']); + } + + public function testAdminCustomersGetNotFound() + { + global $admin_userdata; + $this->expectExceptionCode(404); + $this->expectExceptionMessage("Customer with id #999 could not be found"); + Customers::getLocal($admin_userdata, array( + 'id' => 999 + ))->get(); + } + + /** + * @depends testAdminCustomersAdd + */ + public function testCustomerCustomersGetForeign() + { + global $admin_userdata; + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'id' => 1 + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + + $this->expectException(Exception::class); + $this->expectExceptionCode(401); + + Customers::getLocal($customer_userdata, array( + 'id' => 2 + ))->get(); + } + + /** + * @depends testAdminCustomersAdd + */ + public function testAdminCustomerUpdateDeactivate() + { + global $admin_userdata; + // get customer + Customers::getLocal($admin_userdata, array( + 'id' => 1, + 'deactivated' => 1 + ))->update(); + + // get customer and check results + $json_result = Customers::getLocal($admin_userdata, array( + 'id' => 1 + ))->get(); + $result = json_decode($json_result, true)['data']; + + $this->assertEquals(1, $result['customerid']); + $this->assertEquals(1, $result['deactivated']); + // standard-subdomains will be removed on deactivation + $this->assertEquals(0, $result['standardsubdomain']); + } + + /** + * @depends testAdminCustomersAdd + */ + public function testCustomerCustomersGetWhenDeactivated() + { + global $admin_userdata; + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'id' => 1 + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + + $this->expectException(Exception::class); + $this->expectExceptionCode(406); + $this->expectExceptionMessage("Account suspended"); + + Customers::getLocal($customer_userdata, array( + 'id' => $customer_userdata['customerid'] + ))->get(); + } + + /** + * @depends testAdminCustomersAdd + */ + public function testCustomerCustomersUpdate() + { + global $admin_userdata; + + // reactivate customer + // get customer + Customers::getLocal($admin_userdata, array( + 'id' => 1, + 'deactivated' => 0 + ))->update(); + + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'id' => 1 + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + + Customers::getLocal($customer_userdata, array( + 'id' => $customer_userdata['customerid'], + 'def_language' => 'English', + 'theme' => 'Froxlor', + 'new_customer_password' => 'h0lYmo1y2' + ))->update(); + + $json_result = Customers::getLocal($customer_userdata, array( + 'id' => $customer_userdata['customerid'] + ))->get(); + $result = json_decode($json_result, true)['data']; + + $this->assertEquals('Froxlor', $result['theme']); + $this->assertEquals('English', $result['def_language']); + } + + /** + * @depends testAdminCustomersAdd + */ + public function testResellerCustomersUpdateAllocateMore() + { + global $admin_userdata; + // get reseller + $json_result = Admins::getLocal($admin_userdata, array( + 'loginname' => 'reseller' + ))->get(); + $reseller_userdata = json_decode($json_result, true)['data']; + $reseller_userdata['adminsession'] = 1; + + $this->expectExceptionMessage("You cannot allocate more resources than you own for yourself."); + // add new customer + $data = [ + 'new_loginname' => 'test2', + 'email' => 'test2@froxlor.org', + 'firstname' => 'Test', + 'name' => 'Testman', + 'customernumber' => 1338, + 'subdomains' => -1, + 'new_customer_password' => 'h0lYmo1y' + ]; + Customers::getLocal($reseller_userdata, $data)->add(); + } + + public function testCustomerCustomersDelete() + { + global $admin_userdata; + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + $this->expectExceptionCode(403); + $this->expectExceptionMessage("Not allowed to execute given command."); + Customers::getLocal($customer_userdata, array('loginname' => 'test1'))->delete(); + } + + public function testResellerCustomersDeleteNotOwned() + { + global $admin_userdata; + // get reseller + $json_result = Admins::getLocal($admin_userdata, array( + 'loginname' => 'reseller' + ))->get(); + $reseller_userdata = json_decode($json_result, true)['data']; + $reseller_userdata['adminsession'] = 1; + $this->expectExceptionCode(404); + Customers::getLocal($reseller_userdata, array('loginname' => 'test1'))->delete(); + } + + public function testAdminCustomersDelete() + { + global $admin_userdata; + // add new customer + $data = [ + 'new_loginname' => 'test2', + 'email' => 'test2@froxlor.org', + 'firstname' => 'Test', + 'name' => 'Testman', + 'customernumber' => 1338, + 'new_customer_password' => 'h0lYmo1y' + ]; + Customers::getLocal($admin_userdata, $data)->add(); + $json_result = Customers::getLocal($admin_userdata, array('loginname' => 'test2'))->delete(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals('test2', $result['loginname']); + } + + public function testAdminCustomersUnlock() + { + global $admin_userdata; + // update customer to have correct test-data + Database::query("UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET `loginfail_count` = '5' WHERE `loginname` = 'test1'"); + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->unlock(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals(0, $result['loginfail_count']); + } + + public function testAdminCustomersUnlockNotAllowed() + { + global $admin_userdata; + $testadmin_userdata = $admin_userdata; + $testadmin_userdata['adminsession'] = 0; + + $this->expectExceptionCode(403); + $this->expectExceptionMessage("Not allowed to execute given command."); + Customers::getLocal($testadmin_userdata, array( + 'loginname' => 'test1' + ))->unlock(); + } + + public function testAdminCustomersMoveNotAllowed() + { + global $admin_userdata; + $testadmin_userdata = $admin_userdata; + $testadmin_userdata['change_serversettings'] = 0; + + $this->expectExceptionCode(403); + $this->expectExceptionMessage("Not allowed to execute given command."); + Customers::getLocal($testadmin_userdata, array( + 'loginname' => 'test1', + 'adminid' => 1 + ))->move(); + } + + public function testAdminCustomersMoveTargetIsSource() + { + global $admin_userdata; + $this->expectExceptionCode(406); + $this->expectExceptionMessage("Cannot move customer to the same admin/reseller as he currently is assigned to"); + Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1', + 'adminid' => 1 + ))->move(); + } + + public function testAdminCustomersMove() + { + global $admin_userdata; + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1', + 'adminid' => 2 + ))->move(); + $result = json_decode($json_result, true)['data']; + + $this->assertEquals(2, $result['adminid']); + } + +} diff --git a/tests/Ftps/FtpsTest.php b/tests/Ftps/FtpsTest.php new file mode 100644 index 00000000..2b2165ee --- /dev/null +++ b/tests/Ftps/FtpsTest.php @@ -0,0 +1,107 @@ + 1 + ))->get(); + $result = json_decode($json_result, true)['data']; + + // should be the ftp user of the first added customr 'test1' + $this->assertEquals('test1', $result['username']); + } + + public function testCustomerFtpsGetId() + { + global $admin_userdata; + + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'id' => 1 + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + + $json_result = Ftps::getLocal($customer_userdata, array( + 'id' => 1 + ))->get(); + $result = json_decode($json_result, true)['data']; + + // should be the ftp user of the first added customr 'test1' + $this->assertEquals('test1', $result['username']); + } + + public function testCustomerFtpsGetOtherId() + { + global $admin_userdata; + + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'id' => 1 + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + + $this->expectExceptionCode(404); + + Ftps::getLocal($customer_userdata, array( + 'id' => 10 + ))->get(); + } + + public function testAdminFtpsList() + { + global $admin_userdata; + + $json_result = Ftps::getLocal($admin_userdata)->list(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals(1, $result['count']); + } + + public function testAdminFtpsListSpecificCustomer() + { + global $admin_userdata; + + $json_result = Ftps::getLocal($admin_userdata, array('loginname' => 'test1'))->list(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals(1, $result['count']); + $this->assertEquals('test1', $result['list'][0]['username']); + } + + public function testResellerFtpsList() + { + global $admin_userdata; + // get reseller + $json_result = Admins::getLocal($admin_userdata, array( + 'loginname' => 'reseller' + ))->get(); + $reseller_userdata = json_decode($json_result, true)['data']; + $reseller_userdata['adminsession'] = 1; + $json_result = Ftps::getLocal($reseller_userdata)->list(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals(1, $result['count']); + $this->assertEquals('test1', $result['list'][0]['username']); + } + + public function testCustomerFtpsList() + { + global $admin_userdata; + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + $json_result = Ftps::getLocal($customer_userdata)->list(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals(1, $result['count']); + $this->assertEquals('test1', $result['list'][0]['username']); + } +} diff --git a/tests/Global/FroxlorRpcTest.php b/tests/Global/FroxlorRpcTest.php new file mode 100644 index 00000000..6befd9c4 --- /dev/null +++ b/tests/Global/FroxlorRpcTest.php @@ -0,0 +1,117 @@ +expectExceptionCode(400); + $this->expectExceptionMessage("Invalid request header"); + FroxlorRPC::validateRequest(array()); + } + + public function testNoCredentialsGiven() + { + $this->expectExceptionCode(400); + $this->expectExceptionMessage("No authorization credentials given"); + FroxlorRPC::validateRequest(array( + 'header' => 'asd' + )); + } + + public function testValidateAuthInvalid() + { + $this->expectExceptionCode(403); + $this->expectExceptionMessage("Invalid authorization credentials"); + FroxlorRPC::validateRequest(array( + 'header' => [ + 'apikey' => 'asd', + 'secret' => 'asd' + ] + )); + } + + public function testValidateAuthAllowFromInvalid() + { + $_SERVER['REMOTE_ADDR'] = '127.0.0.1'; + Database::query("UPDATE `api_keys` SET `allowed_from` = '123.123.123.123';"); + $this->expectExceptionCode(403); + $this->expectExceptionMessage("Invalid authorization credentials"); + FroxlorRPC::validateRequest(array( + 'header' => [ + 'apikey' => 'test', + 'secret' => 'test' + ] + )); + } + + public function testInvalidRequestBody() + { + Database::query("UPDATE `api_keys` SET `allowed_from` = '';"); + $this->expectExceptionCode(400); + $this->expectExceptionMessage("Invalid request body"); + FroxlorRPC::validateRequest(array( + 'header' => [ + 'apikey' => 'test', + 'secret' => 'test' + ] + )); + } + + public function testNoCommandGiven() + { + $this->expectExceptionCode(400); + $this->expectExceptionMessage("No command given"); + FroxlorRPC::validateRequest(array( + 'header' => [ + 'apikey' => 'test', + 'secret' => 'test' + ], + 'body' => 'asd' + )); + } + + public function testInvalidCommandGiven() + { + $this->expectExceptionCode(400); + $this->expectExceptionMessage("Invalid command"); + FroxlorRPC::validateRequest(array( + 'header' => [ + 'apikey' => 'test', + 'secret' => 'test' + ], + 'body' => ['command' => 'Froxlor'] + )); + } + + public function testUnknownCommandGiven() + { + $this->expectExceptionCode(400); + $this->expectExceptionMessage("Unknown command"); + FroxlorRPC::validateRequest(array( + 'header' => [ + 'apikey' => 'test', + 'secret' => 'test' + ], + 'body' => ['command' => 'SomeModule.cmd'] + )); + } + + public function testCommandOk() + { + $result = FroxlorRPC::validateRequest(array( + 'header' => [ + 'apikey' => 'test', + 'secret' => 'test' + ], + 'body' => ['command' => 'Froxlor.listFunctions'] + )); + $this->assertEquals('Froxlor', $result['command']['class']); + $this->assertEquals('listFunctions', $result['command']['method']); + $this->assertNull($result['params']); + } +} diff --git a/tests/IpsAndPorts/IpsAndPortsTest.php b/tests/IpsAndPorts/IpsAndPortsTest.php new file mode 100644 index 00000000..95590a17 --- /dev/null +++ b/tests/IpsAndPorts/IpsAndPortsTest.php @@ -0,0 +1,276 @@ +list(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals(1, $result['count']); + $this->assertEquals('82.149.225.46', $result['list'][0]['ip']); + } + + public function testResellerIpsAndPortsListHasNone() + { + global $admin_userdata; + // update reseller to allow no ip access + $json_result = Admins::getLocal($admin_userdata, array( + 'loginname' => 'reseller', + 'ipaddress' => array() + ))->update(); + $reseller_userdata = json_decode($json_result, true)['data']; + $reseller_userdata['adminsession'] = 1; + $this->expectExceptionCode(403); + $this->expectExceptionMessage("Not allowed to execute given command."); + $json_result = IpsAndPorts::getLocal($reseller_userdata)->list(); + } + + public function testAdminIpsAndPortsAdd() + { + global $admin_userdata; + $data = [ + 'ip' => '82.149.225.47' + ]; + $json_result = IpsAndPorts::getLocal($admin_userdata, $data)->add(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals(2, $result['id']); + $this->assertEquals(80, $result['port']); + } + + /** + * @depends testAdminIpsAndPortsAdd + */ + public function testAdminIpsAndPortsAddExists() + { + global $admin_userdata; + $this->expectExceptionMessage("This IP/Port combination already exists."); + $data = [ + 'ip' => '82.149.225.47' + ]; + IpsAndPorts::getLocal($admin_userdata, $data)->add(); + } + + public function testAdminIpsAndPortsAddIpv6() + { + global $admin_userdata; + $data = [ + 'ip' => '2a01:440:1:12:82:149:225:46', + 'docroot' => '/var/www/html' + ]; + $json_result = IpsAndPorts::getLocal($admin_userdata, $data)->add(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals(3, $result['id']); + $this->assertEquals('/var/www/html/', $result['docroot']); + } + + public function testAdminIpsAndPortsGetNotFound() + { + global $admin_userdata; + $this->expectExceptionCode(404); + $this->expectExceptionMessage("IP/port with id #999 could not be found"); + IpsAndPorts::getLocal($admin_userdata, array('id' => 999))->get(); + } + + /** + * @depends testAdminIpsAndPortsAdd + */ + public function testResellerIpsAndPortsList() + { + global $admin_userdata; + // update reseller to allow ip access to ip id #2 + $json_result = Admins::getLocal($admin_userdata, array( + 'loginname' => 'reseller', + 'ipaddress' => array(2) + ))->update(); + $reseller_userdata = json_decode($json_result, true)['data']; + $reseller_userdata['adminsession'] = 1; + $json_result = IpsAndPorts::getLocal($reseller_userdata)->list(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals(1, $result['count']); + $this->assertEquals('82.149.225.47', $result['list'][0]['ip']); + } + + /** + * @depends testResellerIpsAndPortsList + */ + public function testResellerIpsAndPortsGet() + { + global $admin_userdata; + // update reseller to allow ip access to ip id #2 + $json_result = Admins::getLocal($admin_userdata, array( + 'loginname' => 'reseller' + ))->get(); + $reseller_userdata = json_decode($json_result, true)['data']; + $reseller_userdata['adminsession'] = 1; + $json_result = IpsAndPorts::getLocal($reseller_userdata, array('id' => 2))->get(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals('82.149.225.47', $result['ip']); + } + + /** + * @depends testResellerIpsAndPortsList + */ + public function testResellerIpsAndPortsGetRestrictedNotOwned() + { + global $admin_userdata; + // update reseller to allow ip access to ip id #2 + $json_result = Admins::getLocal($admin_userdata, array( + 'loginname' => 'reseller' + ))->get(); + $reseller_userdata = json_decode($json_result, true)['data']; + $reseller_userdata['adminsession'] = 1; + $this->expectExceptionCode(405); + $this->expectExceptionMessage("You cannot access this resource"); + IpsAndPorts::getLocal($reseller_userdata, array('id' => 1))->get(); + } + + public function testResellerIpsAndPortsAdd() + { + global $admin_userdata; + // update reseller to allow ip access to ip id #2 + $json_result = Admins::getLocal($admin_userdata, array( + 'loginname' => 'reseller' + ))->get(); + $reseller_userdata = json_decode($json_result, true)['data']; + $reseller_userdata['adminsession'] = 1; + $this->expectExceptionCode(403); + $this->expectExceptionMessage("Not allowed to execute given command."); + $data = [ + 'ip' => '82.149.225.48' + ]; + IpsAndPorts::getLocal($reseller_userdata, $data)->add(); + } + + public function testCustomerIpsAndPortsGetNotAllowed() + { + global $admin_userdata; + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + $this->expectExceptionCode(403); + $this->expectExceptionMessage("Not allowed to execute given command."); + IpsAndPorts::getLocal($customer_userdata, array('id' => 1))->get(); + } + + public function testAdminIpsAndPortsEdit() + { + global $admin_userdata; + $data = [ + 'id' => 1, + 'listen_statement' => 1, + 'docroot' => '/var/www/html' + ]; + $json_result = IpsAndPorts::getLocal($admin_userdata, $data)->update(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals(1, $result['listen_statement']); + $this->assertEquals('/var/www/html/', $result['docroot']); + } + + public function testResellerIpsAndPortsEditNotAllowed() + { + global $admin_userdata; + // get reseller + $json_result = Admins::getLocal($admin_userdata, array( + 'loginname' => 'reseller' + ))->get(); + $reseller_userdata = json_decode($json_result, true)['data']; + $reseller_userdata['adminsession'] = 1; + $data = [ + 'id' => 1, + 'listen_statement' => 0 + ]; + $this->expectExceptionCode(405); + $this->expectExceptionMessage("You cannot access this resource"); + IpsAndPorts::getLocal($reseller_userdata, $data)->update(); + } + + public function testCustomerIpsAndPortsEditNotAllowed() + { + global $admin_userdata; + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + $data = [ + 'id' => 1, + 'listen_statement' => 0 + ]; + $this->expectExceptionCode(403); + $this->expectExceptionMessage("Not allowed to execute given command."); + IpsAndPorts::getLocal($customer_userdata, $data)->update(); + } + + public function testAdminIpsAndPortsEditCantChangeSystemIp() + { + global $admin_userdata; + $data = [ + 'id' => 1, + 'ip' => '123.123.123.123' + ]; + $this->expectExceptionMessage("You cannot change the last system IP, either create another new IP/Port combination for the system IP or change the system IP."); + $json_result = IpsAndPorts::getLocal($admin_userdata, $data)->update(); + } + + public function testResellerIpsAndPortsEditNoDuplicate() + { + global $admin_userdata; + $json_result = Admins::getLocal($admin_userdata, array( + 'loginname' => 'reseller' + ))->get(); + $reseller_userdata = json_decode($json_result, true)['data']; + $reseller_userdata['adminsession'] = 1; + $data = [ + 'id' => 2, + 'ip' => '82.149.225.46' + ]; + $this->expectExceptionMessage("This IP/Port combination already exists."); + IpsAndPorts::getLocal($reseller_userdata, $data)->update(); + } + + public function testAdminIpsAndPortsDeleteCantDeleteDefaultIp() + { + global $admin_userdata; + $data = [ + 'id' => 1 + ]; + $this->expectExceptionMessage("You cannot delete the default IP/Port combination, please make another IP/Port combination default for before deleting this IP/Port combination."); + IpsAndPorts::getLocal($admin_userdata, $data)->delete(); + } + + public function testAdminIpsAndPortsDelete() + { + global $admin_userdata; + $data = [ + 'id' => 2 + ]; + $json_result = IpsAndPorts::getLocal($admin_userdata, $data)->delete(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals('82.149.225.47', $result['ip']); + } + + public function testResellerIpsAndPortsDeleteNotAllowed() + { + global $admin_userdata; + $json_result = Admins::getLocal($admin_userdata, array( + 'loginname' => 'reseller' + ))->get(); + $reseller_userdata = json_decode($json_result, true)['data']; + $reseller_userdata['adminsession'] = 1; + $data = [ + 'id' => 1 + ]; + $this->expectExceptionCode(403); + $this->expectExceptionMessage("Not allowed to execute given command."); + IpsAndPorts::getLocal($reseller_userdata, $data)->delete(); + } +} diff --git a/tests/bootstrap.php b/tests/bootstrap.php new file mode 100644 index 00000000..31001195 --- /dev/null +++ b/tests/bootstrap.php @@ -0,0 +1,111 @@ + Date: Mon, 26 Feb 2018 22:39:06 +0100 Subject: [PATCH 0507/1335] use correct order Signed-off-by: Michael Kaufmann (d00p) --- phpunit.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phpunit.xml b/phpunit.xml index 89f83835..a924f199 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -10,8 +10,8 @@ tests/Global tests/Admins - tests/IpsAndPorts tests/Customers + tests/IpsAndPorts tests/Ftps From 309b70c0f84db9eac80ac4f294fe7da3133a6901 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 27 Feb 2018 08:23:19 +0100 Subject: [PATCH 0508/1335] completed Ftps-ApiCommand Signed-off-by: Michael Kaufmann (d00p) --- customer_ftp.php | 71 +--------- lib/classes/api/commands/class.Ftps.php | 123 ++++++++++++++-- lib/classes/api/commands/class.Mysqls.php | 1 - tests/Ftps/FtpsTest.php | 163 ++++++++++++++++++++++ 4 files changed, 281 insertions(+), 77 deletions(-) diff --git a/customer_ftp.php b/customer_ftp.php index 474ea057..b2131b07 100644 --- a/customer_ftp.php +++ b/customer_ftp.php @@ -171,74 +171,11 @@ if ($page == 'overview') { if (isset($result['username']) && $result['username'] != '') { if (isset($_POST['send']) && $_POST['send'] == 'send') { - // @FIXME use a good path-validating regex here (refs #1231) - $path = validate($_POST['path'], 'path'); - - $shell = "/bin/false"; - if (Settings::Get('system.allow_customer_shell') == '1') { - $shell = isset($_POST['shell']) ? validate($_POST['shell'], 'shell') : '/bin/false'; + try { + Ftps::getLocal($userinfo, $_POST)->update(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); } - - $_setnewpass = false; - if (isset($_POST['ftp_password']) && $_POST['ftp_password'] != '') { - $password = validate($_POST['ftp_password'], 'password'); - $password = validatePassword($password); - $_setnewpass = true; - } - - if ($_setnewpass) { - if ($password == '') { - standard_error(array('stringisempty', 'mypassword')); - } elseif ($result['username'] == $password) { - standard_error('passwordshouldnotbeusername'); - } - $log->logAction(USR_ACTION, LOG_INFO, "updated ftp-account password for '" . $result['username'] . "'"); - $cryptPassword = makeCryptPassword($password); - - $stmt = Database::prepare("UPDATE `" . TABLE_FTP_USERS . "` - SET `password` = :password - WHERE `customerid` = :customerid - AND `id` = :id" - ); - Database::pexecute($stmt, array("customerid" => $userinfo['customerid'], "id" => $id, "password" => $cryptPassword)); - } - - if ($path != '') { - $path = makeCorrectDir($userinfo['documentroot'] . '/' . $path); - - if ($path != $result['homedir']) { - if (!file_exists($path)) { - // it's the task for "new ftp" but that will - // create all directories and correct their permissions - inserttask(5); - } - - $log->logAction(USR_ACTION, LOG_INFO, "updated ftp-account homdir for '" . $result['username'] . "'"); - - $stmt = Database::prepare("UPDATE `" . TABLE_FTP_USERS . "` - SET `homedir` = :homedir - WHERE `customerid` = :customerid - AND `id` = :id" - ); - $params = array( - "homedir" => $path, - "customerid" => $userinfo['customerid'], - "id" => $id - ); - Database::pexecute($stmt, $params); - } - } - - $log->logAction(USR_ACTION, LOG_INFO, "edited ftp-account '" . $result['username'] . "'"); - inserttask(5); - $description = validate($_POST['ftp_description'], 'description'); - $stmt = Database::prepare("UPDATE `" . TABLE_FTP_USERS . "` - SET `description` = :desc, `shell` = :shell - WHERE `customerid` = :customerid - AND `id` = :id" - ); - Database::pexecute($stmt, array("desc" => $description, "shell" => $shell, "customerid" => $userinfo['customerid'], "id" => $id)); - redirectTo($filename, array('page' => $page, 's' => $s)); } else { if (strpos($result['homedir'], $userinfo['documentroot']) === 0) { diff --git a/lib/classes/api/commands/class.Ftps.php b/lib/classes/api/commands/class.Ftps.php index 226de773..a723080e 100644 --- a/lib/classes/api/commands/class.Ftps.php +++ b/lib/classes/api/commands/class.Ftps.php @@ -80,7 +80,7 @@ class Ftps extends ApiCommand implements ResourceEntity } $params = array(); - // get needed customer info to reduce the mysql-usage-counter by one + // get needed customer info to reduce the ftp-user-counter by one if ($this->isAdmin()) { // get customer id $customer_id = $this->getParam('customer_id'); @@ -88,7 +88,7 @@ class Ftps extends ApiCommand implements ResourceEntity 'id' => $customer_id ))->get(); $customer = json_decode($json_result, true)['data']; - // check whether the customer has enough resources to get the database added + // check whether the customer has enough resources to get the ftp-user added if ($customer['ftps_used'] >= $customer['ftps'] && $customer['ftps'] != '-1') { throw new Exception("Customer has no more resources available", 406); } @@ -133,11 +133,6 @@ class Ftps extends ApiCommand implements ResourceEntity if (! empty($username_check) && $username_check['username'] = $username) { standard_error('usernamealreadyexists', $username, true); - } elseif ($password == '') { - standard_error(array( - 'stringisempty', - 'mypassword' - ), '', true); } elseif ($username == $password) { standard_error('passwordshouldnotbeusername', '', true); } else { @@ -346,7 +341,117 @@ class Ftps extends ApiCommand implements ResourceEntity } public function update() - {} + { + if ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'ftp')) { + throw new Exception("You cannot access this resource", 405); + } + + $id = $this->getParam('id', true, 0); + $un_optional = ($id <= 0 ? false : true); + $username = $this->getParam('username', $un_optional, ''); + + $json_result = Ftps::getLocal($this->getUserData(), array( + 'id' => $id, + 'username' => $username + ))->get(); + $result = json_decode($json_result, true)['data']; + $id = $result['id']; + + // parameters + $path = $this->getParam('path', true, ''); + $password = $this->getParam('ftp_password', true, ''); + $description = $this->getParam('ftp_description', true, $result['description']); + $shell = $this->getParam('shell', true, $result['shell']); + + // validation + $password = validate($password, 'password', '', '', array(), true); + $description = validate(trim($description), 'description', '', '', array(), true); + + if (Settings::Get('system.allow_customer_shell') == '1') { + $shell = validate(trim($shell), 'shell', '', '', array(), true); + } else { + $shell = "/bin/false"; + } + + $params = array(); + // get needed customer info to reduce the ftp-user-counter by one + if ($this->isAdmin()) { + // get customer id + $customer_id = $this->getParam('customer_id'); + $json_result = Customers::getLocal($this->getUserData(), array( + 'id' => $customer_id + ))->get(); + $customer = json_decode($json_result, true)['data']; + } else { + $customer_id = $this->getUserDetail('customerid'); + $customer = $this->getUserData(); + } + + // password update? + if ($password != '') { + // validate password + $password = validatePassword($password, true); + + if ($password == $result['username']) { + standard_error('passwordshouldnotbeusername', '', true); + } + $cryptPassword = makeCryptPassword($password); + + $stmt = Database::prepare("UPDATE `" . TABLE_FTP_USERS . "` + SET `password` = :password + WHERE `customerid` = :customerid + AND `id` = :id + "); + Database::pexecute($stmt, array( + "customerid" => $customer['customerid'], + "id" => $id, + "password" => $cryptPassword + ), true, true); + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_INFO, "[API] updated ftp-account password for '" . $result['username'] . "'"); + } + + // path update? + if ($path != '') { + $path = makeCorrectDir($customer['documentroot'] . '/' . $path); + + if ($path != $result['homedir']) { + $stmt = Database::prepare("UPDATE `" . TABLE_FTP_USERS . "` + SET `homedir` = :homedir + WHERE `customerid` = :customerid + AND `id` = :id + "); + Database::pexecute($stmt, array( + "homedir" => $path, + "customerid" => $customer['customerid'], + "id" => $id + ), true, true); + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_INFO, "[API] updated ftp-account homdir for '" . $result['username'] . "'"); + } + } + // it's the task for "new ftp" but that will + // create all directories and correct their permissions + inserttask(5); + + $stmt = Database::prepare(" + UPDATE `" . TABLE_FTP_USERS . "` + SET `description` = :desc, `shell` = :shell + WHERE `customerid` = :customerid + AND `id` = :id + "); + Database::pexecute($stmt, array( + "desc" => $description, + "shell" => $shell, + "customerid" => $customer['customerid'], + "id" => $id + ), true, true); + + $json_result = Ftps::getLocal($this->getUserData(), array( + 'username' => $result['username'] + ))->get(); + $result = json_decode($json_result, true)['data']; + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_NOTICE, "[API] updated ftp-user '" . $result['username'] . "'"); + return $this->response(200, "successfull", $result); + } /** * list all ftp-users, if called from an admin, list all ftp-users of all customers you are allowed to view, or specify id or loginname for one specific customer @@ -496,7 +601,7 @@ class Ftps extends ApiCommand implements ResourceEntity "username" => "," . $result['username'], "customerid" => $customer_data['customerid'] ), true, true); - + // refs #293 if ($delete_userfiles == 1) { inserttask('8', $customer_data['loginname'], $result['homedir']); diff --git a/lib/classes/api/commands/class.Mysqls.php b/lib/classes/api/commands/class.Mysqls.php index 5811477f..0d043d6d 100644 --- a/lib/classes/api/commands/class.Mysqls.php +++ b/lib/classes/api/commands/class.Mysqls.php @@ -363,7 +363,6 @@ class Mysqls extends ApiCommand implements ResourceEntity // validation $password = validate($password, 'password', '', '', array(), true); - $password = validatePassword($password, true); $databasedescription = validate(trim($databasedescription), 'description', '', '', array(), true); // validate whether the dbserver exists diff --git a/tests/Ftps/FtpsTest.php b/tests/Ftps/FtpsTest.php index 2b2165ee..d9d185a3 100644 --- a/tests/Ftps/FtpsTest.php +++ b/tests/Ftps/FtpsTest.php @@ -21,6 +21,22 @@ class FtpsTest extends TestCase $this->assertEquals('test1', $result['username']); } + public function testResellerFtpsGetId() + { + global $admin_userdata; + // get reseller + $json_result = Admins::getLocal($admin_userdata, array( + 'loginname' => 'reseller' + ))->get(); + $reseller_userdata = json_decode($json_result, true)['data']; + $reseller_userdata['adminsession'] = 1; + $json_result = Ftps::getLocal($reseller_userdata, array( + 'id' => 1 + ))->get(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals('test1', $result['username']); + } + public function testCustomerFtpsGetId() { global $admin_userdata; @@ -104,4 +120,151 @@ class FtpsTest extends TestCase $this->assertEquals(1, $result['count']); $this->assertEquals('test1', $result['list'][0]['username']); } + + public function testCustomerFtpsAdd() + { + global $admin_userdata; + + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + + $data = [ + 'ftp_password' => 'h4xXx0r', + 'path' => '/', + 'ftp_description' => 'testing', + 'sendinfomail' => 1 + ]; + $json_result = Ftps::getLocal($customer_userdata, $data)->add(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals($customer_userdata['documentroot'], $result['homedir']); + } + + public function testCustomerFtpsAddNoMoreResources() + { + global $admin_userdata; + + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data'];# + + $customer_userdata['ftps_used'] = 100; + + $this->expectExceptionCode(406); + $this->expectExceptionMessage('No more resources available'); + $json_result = Ftps::getLocal($customer_userdata)->add(); + } + + public function testAdminFtpsAddCustomerRequired() + { + global $admin_userdata; + + $data = [ + 'ftp_password' => 'h4xXx0r', + 'path' => '/', + 'ftp_description' => 'testing', + 'sendinfomail' => 1 + ]; + + $this->expectExceptionCode(404); + $this->expectExceptionMessage('Requested parameter "customer_id" could not be found for "Ftps:add"'); + $json_result = Ftps::getLocal($admin_userdata, $data)->add(); + } + + public function testCustomerFtpsEdit() + { + global $admin_userdata; + + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + + $data = [ + 'username' => 'test1ftp1', + 'ftp_password' => 'h4xXx0r2', + 'path' => '/subfolder', + 'ftp_description' => 'testing2' + ]; + $json_result = Ftps::getLocal($customer_userdata, $data)->update(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals($customer_userdata['documentroot'].'subfolder/', $result['homedir']); + $this->assertEquals('testing2', $result['description']); + } + + public function testAdminFtpsEdit() + { + global $admin_userdata; + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + $data = [ + 'username' => 'test1ftp1', + 'customer_id' => 1, + 'ftp_password' => 'h4xXx0r2', + 'path' => '/anotherfolder', + 'ftp_description' => 'testing3' + ]; + $json_result = Ftps::getLocal($admin_userdata, $data)->update(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals($customer_userdata['documentroot'].'anotherfolder/', $result['homedir']); + $this->assertEquals('testing3', $result['description']); + } + + public function testAdminFtpsAdd() + { + global $admin_userdata; + + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + + $data = [ + 'customer_id' => $customer_userdata['customerid'], + 'ftp_password' => 'h4xXx0r', + 'path' => '/', + 'ftp_description' => 'testing', + 'sendinfomail' => 1 + ]; + $json_result = Ftps::getLocal($admin_userdata, $data)->add(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals($customer_userdata['documentroot'], $result['homedir']); + } + + public function testCustomerFtpsDelete() + { + global $admin_userdata; + + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + $data = [ + 'username' => 'test1ftp1' + ]; + $json_result = Ftps::getLocal($customer_userdata, $data)->delete(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals('test1ftp1', $result['username']); + } + + public function testAdminFtpsDelete() + { + global $admin_userdata; + $data = [ + 'username' => 'test1ftp2' + ]; + $json_result = Ftps::getLocal($admin_userdata, $data)->delete(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals('test1ftp2', $result['username']); + } } From a2172329cd731cf97a35c6c5beaee5b7cf2ae8d5 Mon Sep 17 00:00:00 2001 From: Michael Kaufmann Date: Tue, 27 Feb 2018 08:50:54 +0100 Subject: [PATCH 0509/1335] Update README.md added CI build-status --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 33cb17cb..87c802ec 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +[![Build Status](http://services.nutime.de:8081/job/froxlor-0.10/lastBuild/buildStatus)](http://services.nutime.de:8081/job/froxlor-0.10/lastBuild) + # Froxlor The server administration software for your needs. From b46d3a3769315ac55fe672b7246d2d4f17a8b5d4 Mon Sep 17 00:00:00 2001 From: Michael Kaufmann Date: Tue, 27 Feb 2018 08:59:49 +0100 Subject: [PATCH 0510/1335] Update README.md new test :) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 87c802ec..ffb2726e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![Build Status](http://services.nutime.de:8081/job/froxlor-0.10/lastBuild/buildStatus)](http://services.nutime.de:8081/job/froxlor-0.10/lastBuild) +[![Build Status](http://services.nutime.de:8081/job/froxlor-0.10/badge/icon)](http://services.nutime.de:8081/job/froxlor-0.10/) # Froxlor From b097c19c0aa807f78d20bcc3b590fd8b4c27390b Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 27 Feb 2018 09:30:57 +0100 Subject: [PATCH 0511/1335] correct phpcs config Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/commands/class.Domains.php | 2 +- lib/classes/api/commands/class.Froxlor.php | 23 +++++++----- phpcs.xml | 11 +++--- tests/Customers/CustomersTest.php | 41 +++++++++++++--------- 4 files changed, 43 insertions(+), 34 deletions(-) diff --git a/lib/classes/api/commands/class.Domains.php b/lib/classes/api/commands/class.Domains.php index c9539d6d..b3e433e3 100644 --- a/lib/classes/api/commands/class.Domains.php +++ b/lib/classes/api/commands/class.Domains.php @@ -1555,7 +1555,7 @@ class Domains extends ApiCommand implements ResourceEntity // trigger when domain id for alias destination has changed: both for old and new destination triggerLetsEncryptCSRForAliasDestinationDomain($result['aliasdomain'], $this->logger()); triggerLetsEncryptCSRForAliasDestinationDomain($aliasdomain, $this->logger()); - } else if ($result['wwwserveralias'] != $wwwserveralias || $result['letsencrypt'] != $letsencrypt) { + } elseif ($result['wwwserveralias'] != $wwwserveralias || $result['letsencrypt'] != $letsencrypt) { // or when wwwserveralias or letsencrypt was changed triggerLetsEncryptCSRForAliasDestinationDomain($aliasdomain, $this->logger()); } diff --git a/lib/classes/api/commands/class.Froxlor.php b/lib/classes/api/commands/class.Froxlor.php index 2f61e20c..d10a96b5 100644 --- a/lib/classes/api/commands/class.Froxlor.php +++ b/lib/classes/api/commands/class.Froxlor.php @@ -83,20 +83,24 @@ class Froxlor extends ApiCommand /** * * @todo import settings - * + * * @access admin */ public function importSettings() - {} + { + throw new Exception("Not available yet.", 501); + } /** * * @todo export settings to file - * + * * @access admin */ public function exportSettings() - {} + { + throw new Exception("Not available yet.", 501); + } /** * return a list of all settings @@ -202,7 +206,7 @@ class Froxlor extends ApiCommand array_push($functions, array_merge(array( 'module' => $module, 'function' => $func->name - ), $this->_getParamListFromDoc($module, $func->name))); + ), $this->getParamListFromDoc($module, $func->name))); } } } else { @@ -234,7 +238,7 @@ class Froxlor extends ApiCommand array_push($functions, array_merge(array( 'module' => $matches[1], 'function' => $func->name - ), $this->_getParamListFromDoc($matches[1], $func->name))); + ), $this->getParamListFromDoc($matches[1], $func->name))); } } } @@ -259,7 +263,7 @@ class Froxlor extends ApiCommand * @throws Exception * @return array|bool */ - private function _getParamListFromDoc($module = null, $function = null) + private function getParamListFromDoc($module = null, $function = null) { try { // set the module @@ -307,7 +311,8 @@ class Froxlor extends ApiCommand 'type' => $r[1], 'desc' => (isset($r[2]) ? trim($r[2]) : '') ); - } else if (! empty($c) && strpos($c, '@throws') === false) { + } + elseif (! empty($c) && strpos($c, '@throws') === false) { if (substr($c, 0, 3) == "/**") { continue; } @@ -331,7 +336,7 @@ class Froxlor extends ApiCommand } } } - $result['head'] =trim($result['head']); + $result['head'] = trim($result['head']); return $result; } catch (\ReflectionException $e) { return array(); diff --git a/phpcs.xml b/phpcs.xml index 757d1e74..e2a1b208 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -1,15 +1,12 @@ PSR2 with tabs instead of spaces. + + - - - - - - - + + diff --git a/tests/Customers/CustomersTest.php b/tests/Customers/CustomersTest.php index 6e2c354c..c90dd6c4 100644 --- a/tests/Customers/CustomersTest.php +++ b/tests/Customers/CustomersTest.php @@ -37,7 +37,9 @@ class CustomersTest extends TestCase 'custom_notes' => 'secret', 'custom_notes_show' => 0, 'gender' => 5, - 'allowed_phpconfigs' => array(1) + 'allowed_phpconfigs' => array( + 1 + ) ]; $json_result = Customers::getLocal($admin_userdata, $data)->add(); @@ -67,7 +69,7 @@ class CustomersTest extends TestCase 'firstname' => 'Test2', 'name' => 'Testman2' ]; - + $this->expectExceptionMessage('Requested parameter "email" is empty where it should not be for "Customers:add"'); Customers::getLocal($admin_userdata, $data)->add(); } @@ -127,12 +129,11 @@ class CustomersTest extends TestCase 'id' => 1 ))->get(); $customer_userdata = json_decode($json_result, true)['data']; - + $this->expectExceptionCode(403); $this->expectExceptionMessage("Not allowed to execute given command."); - + $json_result = Customers::getLocal($customer_userdata)->list(); - } /** @@ -248,7 +249,7 @@ class CustomersTest extends TestCase 'id' => 1, 'deactivated' => 0 ))->update(); - + // get customer $json_result = Customers::getLocal($admin_userdata, array( 'id' => 1 @@ -283,7 +284,7 @@ class CustomersTest extends TestCase ))->get(); $reseller_userdata = json_decode($json_result, true)['data']; $reseller_userdata['adminsession'] = 1; - + $this->expectExceptionMessage("You cannot allocate more resources than you own for yourself."); // add new customer $data = [ @@ -292,12 +293,12 @@ class CustomersTest extends TestCase 'firstname' => 'Test', 'name' => 'Testman', 'customernumber' => 1338, - 'subdomains' => -1, + 'subdomains' => - 1, 'new_customer_password' => 'h0lYmo1y' ]; Customers::getLocal($reseller_userdata, $data)->add(); } - + public function testCustomerCustomersDelete() { global $admin_userdata; @@ -308,7 +309,9 @@ class CustomersTest extends TestCase $customer_userdata = json_decode($json_result, true)['data']; $this->expectExceptionCode(403); $this->expectExceptionMessage("Not allowed to execute given command."); - Customers::getLocal($customer_userdata, array('loginname' => 'test1'))->delete(); + Customers::getLocal($customer_userdata, array( + 'loginname' => 'test1' + ))->delete(); } public function testResellerCustomersDeleteNotOwned() @@ -321,7 +324,9 @@ class CustomersTest extends TestCase $reseller_userdata = json_decode($json_result, true)['data']; $reseller_userdata['adminsession'] = 1; $this->expectExceptionCode(404); - Customers::getLocal($reseller_userdata, array('loginname' => 'test1'))->delete(); + Customers::getLocal($reseller_userdata, array( + 'loginname' => 'test1' + ))->delete(); } public function testAdminCustomersDelete() @@ -337,11 +342,13 @@ class CustomersTest extends TestCase 'new_customer_password' => 'h0lYmo1y' ]; Customers::getLocal($admin_userdata, $data)->add(); - $json_result = Customers::getLocal($admin_userdata, array('loginname' => 'test2'))->delete(); + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test2' + ))->delete(); $result = json_decode($json_result, true)['data']; $this->assertEquals('test2', $result['loginname']); } - + public function testAdminCustomersUnlock() { global $admin_userdata; @@ -353,7 +360,7 @@ class CustomersTest extends TestCase $result = json_decode($json_result, true)['data']; $this->assertEquals(0, $result['loginfail_count']); } - + public function testAdminCustomersUnlockNotAllowed() { global $admin_userdata; @@ -366,7 +373,7 @@ class CustomersTest extends TestCase 'loginname' => 'test1' ))->unlock(); } - + public function testAdminCustomersMoveNotAllowed() { global $admin_userdata; @@ -380,7 +387,7 @@ class CustomersTest extends TestCase 'adminid' => 1 ))->move(); } - + public function testAdminCustomersMoveTargetIsSource() { global $admin_userdata; @@ -403,5 +410,5 @@ class CustomersTest extends TestCase $this->assertEquals(2, $result['adminid']); } - } + From 392db944a2050a80c8cf009a34815bf70729dc2b Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 27 Feb 2018 10:34:37 +0100 Subject: [PATCH 0512/1335] started work in SubDomains-ApiCommand; minor fixes Signed-off-by: Michael Kaufmann (d00p) --- admin_domains.php | 4 +- customer_domains.php | 80 +---- lib/classes/api/commands/class.Mysqls.php | 4 +- lib/classes/api/commands/class.Subdomains.php | 301 ++++++++++++++++++ tests/Customers/CustomersTest.php | 1 - 5 files changed, 316 insertions(+), 74 deletions(-) create mode 100644 lib/classes/api/commands/class.Subdomains.php diff --git a/admin_domains.php b/admin_domains.php index 89c61f4b..47190b56 100644 --- a/admin_domains.php +++ b/admin_domains.php @@ -138,9 +138,7 @@ if ($page == 'domains' || $page == 'overview') { if (isset($_POST['send']) && $_POST['send'] == 'send' && $alias_check['count'] == 0) { try { - Domains::getLocal($userinfo, array_merge(array( - 'id' => $id - ), $_POST))->delete(); + Domains::getLocal($userinfo, $_POST)->delete(); } catch (Exception $e) { dynamic_error($e->getMessage()); } diff --git a/customer_domains.php b/customer_domains.php index 23bbb07f..fff60977 100644 --- a/customer_domains.php +++ b/customer_domains.php @@ -176,79 +176,25 @@ if ($page == 'overview') { eval("echo \"" . getTemplate("domains/domainlist") . "\";"); } elseif ($action == 'delete' && $id != 0) { - $stmt = Database::prepare("SELECT `id`, `customerid`, `domain`, `documentroot`, `isemaildomain`, `parentdomainid`, `aliasdomain` FROM `" . TABLE_PANEL_DOMAINS . "` - WHERE `customerid` = :customerid - AND `id` = :id" - ); - Database::pexecute($stmt, array("customerid" => $userinfo['customerid'], "id" => $id)); - $result = $stmt->fetch(PDO::FETCH_ASSOC); + try { + $json_result = SubDomains::getLocal($userinfo, array( + 'id' => $id + ))->get(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); + } + $result = json_decode($json_result, true)['data']; $alias_stmt = Database::prepare("SELECT COUNT(`id`) AS `count` FROM `" . TABLE_PANEL_DOMAINS . "` WHERE `aliasdomain` = :aliasdomain"); - Database::pexecute($alias_stmt, array("aliasdomain" => $id)); - $alias_check = $alias_stmt->fetch(PDO::FETCH_ASSOC); + $alias_check = Database::pexecute_first($alias_stmt, array("aliasdomain" => $id)); if (isset($result['parentdomainid']) && $result['parentdomainid'] != '0' && $alias_check['count'] == 0) { if (isset($_POST['send']) && $_POST['send'] == 'send') { - if ($result['isemaildomain'] == '1') { - $emails_stmt = Database::prepare("SELECT COUNT(`id`) AS `count` FROM `" . TABLE_MAIL_VIRTUAL . "` - WHERE `customerid` = :customerid - AND `domainid` = :domainid" - ); - Database::pexecute($emails_stmt, array("customerid" => $userinfo['customerid'], "domainid" => $id)); - $emails = $emails_stmt->fetch(PDO::FETCH_ASSOC); - - if ($emails['count'] != '0') { - standard_error('domains_cantdeletedomainwithemail'); - } + try { + SubDomains::getLocal($userinfo, $_POST)->delete(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); } - - triggerLetsEncryptCSRForAliasDestinationDomain($result['aliasdomain'], $log); - - $log->logAction(USR_ACTION, LOG_INFO, "deleted subdomain '" . $idna_convert->decode($result['domain']) . "'"); - $stmt = Database::prepare("DELETE FROM `" . TABLE_PANEL_DOMAINS . "` WHERE - `customerid` = :customerid - AND `id` = :id" - ); - Database::pexecute($stmt, array("customerid" => $userinfo['customerid'], "id" => $id)); - - $stmt = Database::prepare("UPDATE `" . TABLE_PANEL_CUSTOMERS . "` - SET `subdomains_used` = `subdomains_used` - 1 - WHERE `customerid` = :customerid" - ); - Database::pexecute($stmt, array("customerid" => $userinfo['customerid'])); - - // remove connections to ips and domainredirects - $del_stmt = Database::prepare(" - DELETE FROM `" . TABLE_DOMAINTOIP . "` - WHERE `id_domain` = :domainid" - ); - Database::pexecute($del_stmt, array('domainid' => $id)); - - $del_stmt = Database::prepare(" - DELETE FROM `" . TABLE_PANEL_DOMAINREDIRECTS . "` - WHERE `did` = :domainid" - ); - Database::pexecute($del_stmt, array('domainid' => $id)); - - // remove certificate from domain_ssl_settings, fixes #1596 - $del_stmt = Database::prepare(" - DELETE FROM `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` - WHERE `domainid` = :domainid" - ); - Database::pexecute($del_stmt, array('domainid' => $id)); - - // remove possible existing DNS entries - $del_stmt = Database::prepare(" - DELETE FROM `" . TABLE_DOMAIN_DNS . "` - WHERE `domain_id` = :domainid - "); - Database::pexecute($del_stmt, array('domainid' => $id)); - - inserttask('1'); - - // Using nameserver, insert a task which rebuilds the server config - inserttask('4'); - redirectTo($filename, array('page' => $page, 's' => $s)); } else { ask_yesno('domains_reallydelete', $filename, array('id' => $id, 'page' => $page, 'action' => $action), $idna_convert->decode($result['domain'])); diff --git a/lib/classes/api/commands/class.Mysqls.php b/lib/classes/api/commands/class.Mysqls.php index 0d043d6d..b9482284 100644 --- a/lib/classes/api/commands/class.Mysqls.php +++ b/lib/classes/api/commands/class.Mysqls.php @@ -283,9 +283,7 @@ class Mysqls extends ApiCommand implements ResourceEntity } } } else { - if ($id != $this->getUserDetail('customerid')) { - throw new Exception("You cannot access data of other customers", 401); - } elseif (Settings::IsInList('panel.customer_hide_options', 'mysql')) { + if (Settings::IsInList('panel.customer_hide_options', 'mysql')) { throw new Exception("You cannot access this resource", 405); } $result_stmt = Database::prepare(" diff --git a/lib/classes/api/commands/class.Subdomains.php b/lib/classes/api/commands/class.Subdomains.php new file mode 100644 index 00000000..8f88b676 --- /dev/null +++ b/lib/classes/api/commands/class.Subdomains.php @@ -0,0 +1,301 @@ + (2010-) + * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt + * @package API + * @since 0.10.0 + * + */ +class Subdomains extends ApiCommand implements ResourceEntity +{ + + public function add() + {} + + /** + * return a subdomain entry by either id or domainname + * + * @param int $id + * optional, the domain-id + * @param string $domainname + * optional, the domainname + * + * @access admin, customer + * @throws Exception + * @return array + */ + public function get() + { + $id = $this->getParam('id', true, 0); + $dn_optional = ($id <= 0 ? false : true); + $domainname = $this->getParam('domainname', $dn_optional, ''); + + // convert possible idn domain to punycode + if (substr($domainname, 0, 4) != 'xn--') { + $idna_convert = new idna_convert_wrapper(); + $domainname = $idna_convert->encode($domainname); + } + + if ($this->isAdmin()) { + if ($this->getUserDetail('customers_see_all') != 1) { + // if it's a reseller or an admin who cannot see all customers, we need to check + // whether the database belongs to one of his customers + $json_result = Customers::getLocal($this->getUserData())->list(); + $custom_list_result = json_decode($json_result, true)['data']['list']; + $customer_ids = array(); + foreach ($custom_list_result as $customer) { + $customer_ids[] = $customer['customerid']; + } + if (count($customer_ids) > 0) { + $result_stmt = Database::prepare(" + SELECT * FROM `" . TABLE_PANEL_DOMAINS . "` + WHERE " . ($id > 0 ? "`id` = :iddn" : "`domain` = :iddn") . " AND `customerid` IN (:customerids) + "); + $params = array( + 'iddn' => ($id <= 0 ? $domainname : $id), + 'customerids' => implode(", ", $customer_ids) + ); + } else { + throw new Exception("You do not have any customers yet", 406); + } + } else { + $result_stmt = Database::prepare(" + SELECT * FROM `" . TABLE_PANEL_DOMAINS . "` + WHERE " . ($id > 0 ? "`id` = :iddn" : "`databasename` = :iddn")); + $params = array( + 'iddn' => ($id <= 0 ? $domainname : $id) + ); + } + } else { + if (Settings::IsInList('panel.customer_hide_options', 'domains')) { + throw new Exception("You cannot access this resource", 405); + } + $result_stmt = Database::prepare(" + SELECT `id`, `customerid`, `domain`, `documentroot`, `isemaildomain`, `parentdomainid`, `aliasdomain` FROM `" . TABLE_PANEL_DOMAINS . "` + WHERE `customerid`= :customerid AND " . ($id > 0 ? "`id` = :iddn" : "`databasename` = :iddn")); + $params = array( + 'customerid' => $this->getUserDetail('customerid'), + 'iddn' => ($id <= 0 ? $domainname : $id) + ); + } + $result = Database::pexecute_first($result_stmt, $params, true, true); + if ($result) { + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_NOTICE, "[API] get subdomain '" . $result['domain'] . "'"); + return $this->response(200, "successfull", $result); + } + $key = ($id > 0 ? "id #" . $id : "domainname '" . $domainname . "'"); + throw new Exception("Subdomain with " . $key . " could not be found", 404); + } + + public function update() + {} + + public function list() + { + if ($this->isAdmin()) { + // if we're an admin, list all databases of all the admins customers + // or optionally for one specific customer identified by id or loginname + $customerid = $this->getParam('customerid', true, 0); + $loginname = $this->getParam('loginname', true, ''); + + if (! empty($customer_id) || ! empty($loginname)) { + $json_result = Customers::getLocal($this->getUserData(), array( + 'id' => $customerid, + 'loginname' => $loginname + ))->get(); + $custom_list_result = array( + json_decode($json_result, true)['data'] + ); + } else { + $json_result = Customers::getLocal($this->getUserData())->list(); + $custom_list_result = json_decode($json_result, true)['data']['list']; + } + $customer_ids = array(); + $customer_stdsubs = array(); + foreach ($custom_list_result as $customer) { + $customer_ids[] = $customer['customerid']; + $customer_stdsubs[$customer['customerid']] = $customer['standardsubdomain']; + } + } else { + if (Settings::IsInList('panel.customer_hide_options', 'domains')) { + throw new Exception("You cannot access this resource", 405); + } + $customer_ids = array( + $this->getUserDetail('customerid') + ); + $customer_stdsubs = array( + $this->getUserDetail('customerid') => $this->getUserDetail('standardsubdomain') + ); + } + + // prepare select statement + $domains_stmt = Database::prepare(" + SELECT `d`.`id`, `d`.`customerid`, `d`.`domain`, `d`.`documentroot`, `d`.`isbinddomain`, `d`.`isemaildomain`, `d`.`caneditdomain`, `d`.`iswildcarddomain`, `d`.`parentdomainid`, `d`.`letsencrypt`, `d`.`termination_date`, `ad`.`id` AS `aliasdomainid`, `ad`.`domain` AS `aliasdomain`, `da`.`id` AS `domainaliasid`, `da`.`domain` AS `domainalias` + FROM `" . TABLE_PANEL_DOMAINS . "` `d` + LEFT JOIN `" . TABLE_PANEL_DOMAINS . "` `ad` ON `d`.`aliasdomain`=`ad`.`id` + LEFT JOIN `" . TABLE_PANEL_DOMAINS . "` `da` ON `da`.`aliasdomain`=`d`.`id` + WHERE `d`.`customerid`= :customerid + AND `d`.`email_only`='0' + AND `d`.`id` <> :standardsubdomain + "); + + $result = array(); + foreach ($customer_ids as $customer_id) { + Database::pexecute($domains_stmt, array( + "customerid" => $customer_id, + "standardsubdomain" => $customer_stdsubs[$customer_id] + ), true, true); + while ($row = $domains_stmt->fetch(PDO::FETCH_ASSOC)) { + $result[] = $row; + } + } + return $this->response(200, "successfull", array( + 'count' => count($result), + 'list' => $result + )); + } + + /** + * delete a subdomain by either id or domainname + * + * @param int $id + * optional, the domain-id + * @param string $domainname + * optional, the domainname + * + * @access admin, customer + * @throws Exception + * @return array + */ + public function delete() + { + $id = $this->getParam('id', true, 0); + $dn_optional = ($id <= 0 ? false : true); + $domainname = $this->getParam('domainname', $dn_optional, ''); + + if ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'domains')) { + throw new Exception("You cannot access this resource", 405); + } + + $json_result = SubDomains::getLocal($this->getUserData(), array( + 'id' => $id, + 'domainname' => $domainname + ))->get(); + $result = json_decode($json_result, true)['data']; + $id = $result['id']; + + // get needed customer info to reduce the subdomain-usage-counter by one + if ($this->isAdmin()) { + $json_result = Customers::getLocal($this->getUserData(), array( + 'id' => $result['customerid'] + ))->get(); + $customer = json_decode($json_result, true)['data']; + $subdomains_used = $customer['subdomains_used']; + $customer_id = $customer['customer_id']; + } else { + if ($result['caneditdomain'] == 0) { + throw new Exception("You cannot edit this resource", 405); + } + $subdomains_used = $this->getUserDetail('subdomains_used'); + $customer_id = $this->getUserDetail('customerid'); + } + + if ($result['isemaildomain'] == '1') { + // check for e-mail addresses + $emails_stmt = Database::prepare(" + SELECT COUNT(`id`) AS `count` FROM `" . TABLE_MAIL_VIRTUAL . "` + WHERE `customerid` = :customerid AND `domainid` = :domainid + "); + $emails = Database::pexecute_first($emails_stmt, array( + "customerid" => $customer_id, + "domainid" => $id + ), true, true); + + if ($emails['count'] != '0') { + standard_error('domains_cantdeletedomainwithemail', '', true); + } + } + + triggerLetsEncryptCSRForAliasDestinationDomain($result['aliasdomain'], $this->logger()); + + // delete domain from table + $stmt = Database::prepare(" + DELETE FROM `" . TABLE_PANEL_DOMAINS . "` WHERE `customerid` = :customerid AND `id` = :id + "); + Database::pexecute($stmt, array( + "customerid" => $customer_id, + "id" => $id + ), true, true); + + // remove connections to ips and domainredirects + $del_stmt = Database::prepare(" + DELETE FROM `" . TABLE_DOMAINTOIP . "` + WHERE `id_domain` = :domainid + "); + Database::pexecute($del_stmt, array( + 'domainid' => $id + ), true, true); + + // remove redirect-codes + $del_stmt = Database::prepare(" + DELETE FROM `" . TABLE_PANEL_DOMAINREDIRECTS . "` + WHERE `did` = :domainid + "); + Database::pexecute($del_stmt, array( + 'domainid' => $id + ), true, true); + + // remove certificate from domain_ssl_settings, fixes #1596 + $del_stmt = Database::prepare(" + DELETE FROM `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` + WHERE `domainid` = :domainid + "); + Database::pexecute($del_stmt, array( + 'domainid' => $id + ), true, true); + + // remove possible existing DNS entries + $del_stmt = Database::prepare(" + DELETE FROM `" . TABLE_DOMAIN_DNS . "` + WHERE `domain_id` = :domainid + "); + Database::pexecute($del_stmt, array( + 'domainid' => $id + ), true, true); + + inserttask('1'); + // Using nameserver, insert a task which rebuilds the server config + inserttask('4'); + + // reduce subdomain-usage-counter + $stmt = Database::prepare(" + UPDATE `" . TABLE_PANEL_CUSTOMERS . "` + SET `subdomains_used` = `subdomains_used` - 1 + WHERE `customerid` = :customerid + "); + Database::pexecute($stmt, array( + "customerid" => $customer_id + ), true, true); + // update admin usage + $stmt = Database::prepare(" + UPDATE `" . TABLE_PANEL_ADMINS . "` + SET `subdomains_used` = `subdomains_used` - 1 + WHERE `adminid` = :adminid + "); + Database::pexecute($stmt, array( + "adminid" => ($this->isAdmin() ? $customer['adminid'] : $this->getUserDetail('adminid')) + ), true, true); + + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_WARNING, "[API] deleted subdomain '" . $result['domain'] . "'"); + return $this->response(200, "successfull", $result); + } +} diff --git a/tests/Customers/CustomersTest.php b/tests/Customers/CustomersTest.php index c90dd6c4..338c6bef 100644 --- a/tests/Customers/CustomersTest.php +++ b/tests/Customers/CustomersTest.php @@ -411,4 +411,3 @@ class CustomersTest extends TestCase $this->assertEquals(2, $result['adminid']); } } - From 6318e5514b61b21e8a9d2befbdd26bd9b8aa2cc8 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 27 Feb 2018 10:54:03 +0100 Subject: [PATCH 0513/1335] ignore some more checkstyle rules Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/commands/class.Subdomains.php | 8 ++++++-- phpcs.xml | 4 ++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/classes/api/commands/class.Subdomains.php b/lib/classes/api/commands/class.Subdomains.php index 8f88b676..cfec3569 100644 --- a/lib/classes/api/commands/class.Subdomains.php +++ b/lib/classes/api/commands/class.Subdomains.php @@ -19,7 +19,9 @@ class Subdomains extends ApiCommand implements ResourceEntity { public function add() - {} + { + throw new Exception("Not available yet.", 501); + } /** * return a subdomain entry by either id or domainname @@ -97,7 +99,9 @@ class Subdomains extends ApiCommand implements ResourceEntity } public function update() - {} + { + throw new Exception("Not available yet.", 501); + } public function list() { diff --git a/phpcs.xml b/phpcs.xml index e2a1b208..032051b6 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -4,9 +4,13 @@ + + + + From a038e35e458a132edc2256e530c4c336db671a3a Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 27 Feb 2018 15:54:07 +0100 Subject: [PATCH 0514/1335] added Subdomains.add; minor fixes and enhancements Signed-off-by: Michael Kaufmann (d00p) --- customer_domains.php | 227 +------------ lib/classes/api/abstract.ApiCommand.php | 36 +- lib/classes/api/commands/class.Admins.php | 36 +- lib/classes/api/commands/class.Customers.php | 34 +- lib/classes/api/commands/class.Froxlor.php | 2 +- lib/classes/api/commands/class.Ftps.php | 42 +-- lib/classes/api/commands/class.Mysqls.php | 46 +-- lib/classes/api/commands/class.Subdomains.php | 311 +++++++++++++++++- 8 files changed, 421 insertions(+), 313 deletions(-) diff --git a/customer_domains.php b/customer_domains.php index fff60977..e48c48f1 100644 --- a/customer_domains.php +++ b/customer_domains.php @@ -205,229 +205,12 @@ if ($page == 'overview') { } elseif ($action == 'add') { if ($userinfo['subdomains_used'] < $userinfo['subdomains'] || $userinfo['subdomains'] == '-1') { if (isset($_POST['send']) && $_POST['send'] == 'send') { - - if (substr($_POST['subdomain'], 0, 4) == 'xn--') { - standard_error('domain_nopunycode'); - } - - $subdomain = $idna_convert->encode(preg_replace(array('/\:(\d)+$/', '/^https?\:\/\//'), '', validate($_POST['subdomain'], 'subdomain', '', 'subdomainiswrong'))); - $domain = $_POST['domain']; - $domain_stmt = Database::prepare("SELECT * FROM `" . TABLE_PANEL_DOMAINS . "` - WHERE `domain` = :domain - AND `customerid` = :customerid - AND `parentdomainid` = '0' - AND `email_only` = '0' - AND `caneditdomain` = '1'" - ); - $domain_check = Database::pexecute_first($domain_stmt, array("domain" => $domain, "customerid" => $userinfo['customerid'])); - - $completedomain = $subdomain . '.' . $domain; - - if (Settings::Get('system.validate_domain') && ! validateDomain($completedomain)) { - standard_error(array( - 'stringiswrong', - 'mydomain' - )); - } - - if ($completedomain == Settings::Get('system.hostname')) { - standard_error('admin_domain_emailsystemhostname'); - } - - $completedomain_stmt = Database::prepare("SELECT * FROM `" . TABLE_PANEL_DOMAINS . "` - WHERE `domain` = :domain - AND `customerid` = :customerid - AND `email_only` = '0' - AND `caneditdomain` = '1'" - ); - $completedomain_check = Database::pexecute_first($completedomain_stmt, array("domain" => $completedomain, "customerid" => $userinfo['customerid'])); - - $aliasdomain = intval($_POST['alias']); - $aliasdomain_check = array('id' => 0); - $_doredirect = false; - - if ($aliasdomain != 0) { - // also check ip/port combination to be the same, #176 - $aliasdomain_stmt = Database::prepare("SELECT `d`.`id` FROM `" . TABLE_PANEL_DOMAINS . "` `d` , `" . TABLE_PANEL_CUSTOMERS . "` `c` , `".TABLE_DOMAINTOIP."` `dip` - WHERE `d`.`aliasdomain` IS NULL - AND `d`.`id` = :id - AND `c`.`standardsubdomain` <> `d`.`id` - AND `d`.`customerid` = :customerid - AND `c`.`customerid` = `d`.`customerid` - AND `d`.`id` = `dip`.`id_domain` - AND `dip`.`id_ipandports` - IN (SELECT `id_ipandports` FROM `".TABLE_DOMAINTOIP."` - WHERE `id_domain` = :id ) - GROUP BY `d`.`domain` - ORDER BY `d`.`domain` ASC;" - ); - $aliasdomain_check = Database::pexecute_first($aliasdomain_stmt, array("id" => $aliasdomain, "customerid" => $userinfo['customerid'])); - triggerLetsEncryptCSRForAliasDestinationDomain($aliasdomain, $log); - } - - if (isset($_POST['url']) && $_POST['url'] != '' && validateUrl($_POST['url'])) { - $path = $_POST['url']; - $_doredirect = true; - } else { - $path = validate($_POST['path'], 'path'); - } - - if (!preg_match('/^https?\:\/\//', $path) || !validateUrl($path)) { - if (strstr($path, ":") !== FALSE) { - standard_error('pathmaynotcontaincolon'); - } - // If path is empty or '/' and 'Use domain name as default value for DocumentRoot path' is enabled in settings, - // set default path to subdomain or domain name - if ((($path == '') || ($path == '/')) && Settings::Get('system.documentroot_use_default_value') == 1) { - $path = makeCorrectDir($userinfo['documentroot'] . '/' . $completedomain); - } else { - $path = makeCorrectDir($userinfo['documentroot'] . '/' . $path); - } - } else { - $_doredirect = true; - } - - $openbasedir_path = '0'; - if (isset($_POST['openbasedir_path']) && $_POST['openbasedir_path'] == '1') { - $openbasedir_path = '1'; - } - - $ssl_redirect = '0'; - if (isset($_POST['ssl_redirect']) && $_POST['ssl_redirect'] == '1') { - // a ssl-redirect only works if there actually is a - // ssl ip/port assigned to the domain - if (domainHasSslIpPort($domain_check['id']) == true) { - $ssl_redirect = '1'; - $_doredirect = true; - } else { - standard_error('sslredirectonlypossiblewithsslipport'); - } - } - - $letsencrypt = '0'; - if (isset($_POST['letsencrypt']) && $_POST['letsencrypt'] == '1') { - // let's encrypt only works if there actually is a - // ssl ip/port assigned to the domain - if (domainHasSslIpPort($domain_check['id']) == true) { - $letsencrypt = '1'; - } else { - standard_error('letsencryptonlypossiblewithsslipport'); - } - } - - // Temporarily deactivate ssl_redirect until Let's Encrypt certificate was generated - if ($ssl_redirect > 0 && $letsencrypt == 1) { - $ssl_redirect = 2; - } - - // HSTS - $hsts_maxage = isset($_POST['hsts_maxage']) ? (int)$_POST['hsts_maxage'] : 0; - $hsts_sub = isset($_POST['hsts_sub']) && (int)$_POST['hsts_sub'] == 1 ? 1 : 0; - $hsts_preload = isset($_POST['hsts_preload']) && (int)$_POST['hsts_preload'] == 1 ? 1 : 0; - - if ($path == '') { - standard_error('patherror'); - } elseif ($subdomain == '') { - standard_error(array('stringisempty', 'domainname')); - } elseif ($subdomain == 'www' && $domain_check['wwwserveralias'] == '1') { - standard_error('wwwnotallowed'); - } elseif ($domain == '') { - standard_error('domaincantbeempty'); - } elseif (strtolower($completedomain_check['domain']) == strtolower($completedomain)) { - standard_error('domainexistalready', $completedomain); - } elseif (strtolower($domain_check['domain']) != strtolower($domain)) { - standard_error('maindomainnonexist', $domain); - } elseif ($aliasdomain_check['id'] != $aliasdomain) { - standard_error('domainisaliasorothercustomer'); - } else { - // get the phpsettingid from parentdomain, #107 - $phpsid_stmt = Database::prepare("SELECT `phpsettingid` FROM `".TABLE_PANEL_DOMAINS."` - WHERE `id` = :id" - ); - Database::pexecute($phpsid_stmt, array("id" => $domain_check['id'])); - $phpsid_result = $phpsid_stmt->fetch(PDO::FETCH_ASSOC); - - if (!isset($phpsid_result['phpsettingid']) || (int)$phpsid_result['phpsettingid'] <= 0) { - // assign default config - $phpsid_result['phpsettingid'] = 1; - } - // check whether the customer has chosen its own php-config - if (isset($_POST['phpsettingid']) && intval($_POST['phpsettingid']) != $phpsid_result['phpsettingid']) { - $phpsid_result['phpsettingid'] = intval($_POST['phpsettingid']); - } - - $stmt = Database::prepare("INSERT INTO `" . TABLE_PANEL_DOMAINS . "` SET - `customerid` = :customerid, - `domain` = :domain, - `documentroot` = :documentroot, - `aliasdomain` = :aliasdomain, - `parentdomainid` = :parentdomainid, - `wwwserveralias` = :wwwserveralias, - `isemaildomain` = :isemaildomain, - `iswildcarddomain` = :iswildcarddomain, - `phpenabled` = :phpenabled, - `openbasedir` = :openbasedir, - `openbasedir_path` = :openbasedir_path, - `speciallogfile` = :speciallogfile, - `specialsettings` = :specialsettings, - `ssl_redirect` = :ssl_redirect, - `phpsettingid` = :phpsettingid, - `letsencrypt` = :letsencrypt, - `hsts` = :hsts, - `hsts_sub` = :hsts_sub, - `hsts_preload` = :hsts_preload" - ); - $params = array( - "customerid" => $userinfo['customerid'], - "domain" => $completedomain, - "documentroot" => $path, - "aliasdomain" => $aliasdomain != 0 ? $aliasdomain : null, - "parentdomainid" => $domain_check['id'], - "wwwserveralias" => $domain_check['wwwserveralias'] == '1' ? '1' : '0', - "iswildcarddomain" => $domain_check['iswildcarddomain'] == '1' ? '1' : '0', - "isemaildomain" => $domain_check['subcanemaildomain'] == '3' ? '1' : '0', - "openbasedir" => $domain_check['openbasedir'], - "openbasedir_path" => $openbasedir_path, - "phpenabled" => $domain_check['phpenabled'], - "speciallogfile" => $domain_check['speciallogfile'], - "specialsettings" => $domain_check['specialsettings'], - "ssl_redirect" => $ssl_redirect, - "phpsettingid" => $phpsid_result['phpsettingid'], - "letsencrypt" => $letsencrypt, - "hsts" => $hsts_maxage, - "hsts_sub" => $hsts_sub, - "hsts_preload" => $hsts_preload - ); - Database::pexecute($stmt, $params); - - if ($_doredirect) { - $did = Database::lastInsertId(); - $redirect = isset($_POST['redirectcode']) ? (int)$_POST['redirectcode'] : Settings::Get('customredirect.default'); - addRedirectToDomain($did, $redirect); - } - - $stmt = Database::prepare("INSERT INTO `".TABLE_DOMAINTOIP."` - (`id_domain`, `id_ipandports`) - SELECT LAST_INSERT_ID(), `id_ipandports` - FROM `".TABLE_DOMAINTOIP."` - WHERE `id_domain` = :id_domain" - ); - Database::pexecute($stmt, array("id_domain" => $domain_check['id'])); - - $stmt = Database::prepare("UPDATE `" . TABLE_PANEL_CUSTOMERS . "` - SET `subdomains_used` = `subdomains_used` + 1 - WHERE `customerid` = :customerid" - ); - Database::pexecute($stmt, array("customerid" => $userinfo['customerid'])); - - $log->logAction(USR_ACTION, LOG_INFO, "added subdomain '" . $completedomain . "'"); - inserttask('1'); - - // Using nameserver, insert a task which rebuilds the server config - inserttask('4'); - - redirectTo($filename, array('page' => $page, 's' => $s)); + try { + SubDomains::getLocal($userinfo, $_POST)->add(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); } + redirectTo($filename, array('page' => $page, 's' => $s)); } else { $stmt = Database::prepare("SELECT `id`, `domain`, `documentroot`, `ssl_redirect`,`isemaildomain`,`letsencrypt` FROM `" . TABLE_PANEL_DOMAINS . "` WHERE `customerid` = :customerid diff --git a/lib/classes/api/abstract.ApiCommand.php b/lib/classes/api/abstract.ApiCommand.php index c11938bb..34d62e3b 100644 --- a/lib/classes/api/abstract.ApiCommand.php +++ b/lib/classes/api/abstract.ApiCommand.php @@ -339,25 +339,25 @@ abstract class ApiCommand */ private function getModFunctionString() { - $_c = get_called_class(); + $_class = get_called_class(); $level = 2; if (version_compare(PHP_VERSION, "5.4.0", "<")) { - $t = debug_backtrace(); + $trace = debug_backtrace(); } else { - $t = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); + $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); } while (true) { - $c = $t[$level]['class']; - $f = $t[$level]['function']; - if ($c != get_called_class()) { + $class = $trace[$level]['class']; + $func = $trace[$level]['function']; + if ($class != $_class) { $level ++; if ($level > 5) { break; } continue; } - return $c . ':' . $f; + return $class . ':' . $func; } } @@ -426,6 +426,28 @@ abstract class ApiCommand return $json_response; } + /** + * increase/decrease a resource field for customers/admins + * + * @param string $table + * @param string $keyfield + * @param int $key + * @param string $op + * @param string $resource + * @param string $extra + */ + protected static function updateResourceUsage($table = null, $keyfield = null, $key = null, $op = '+', $resource = null, $extra = null) + { + $stmt = Database::prepare(" + UPDATE `" . $table . "` + SET `" . $resource . "` = `" . $resource . "` " . $op . " 1 " . $extra . " + WHERE `" . $keyfield . "` = :key + "); + Database::pexecute($stmt, array( + 'key' => $key + ), true, true); + } + /** * read user data from database by api-request-header fields * diff --git a/lib/classes/api/commands/class.Admins.php b/lib/classes/api/commands/class.Admins.php index f5780ea3..11361603 100644 --- a/lib/classes/api/commands/class.Admins.php +++ b/lib/classes/api/commands/class.Admins.php @@ -124,7 +124,7 @@ class Admins extends ApiCommand implements ResourceEntity $tickets_see_all = $this->getParam('tickets_see_all', true, 0); $caneditphpsettings = $this->getParam('caneditphpsettings', true, 0); $change_serversettings = $this->getParam('change_serversettings', true, 0); - $ipaddress = $this->getParam('ipaddress', true, -1); + $ipaddress = $this->getParam('ipaddress', true, - 1); // validation $name = validate($name, 'name', '', '', array(), true); @@ -244,7 +244,7 @@ class Admins extends ApiCommand implements ResourceEntity 'tickets' => $tickets, 'tickets_see_all' => $tickets_see_all, 'mysqls' => $mysqls, - 'ip' => empty($ipaddress) ? "" : (is_array($ipaddress) && $ipaddress > 0 ? json_encode($ipaddress) : -1), + 'ip' => empty($ipaddress) ? "" : (is_array($ipaddress) && $ipaddress > 0 ? json_encode($ipaddress) : - 1), 'theme' => $_theme, 'custom_notes' => $custom_notes, 'custom_notes_show' => $custom_notes_show @@ -354,7 +354,7 @@ class Admins extends ApiCommand implements ResourceEntity $change_serversettings = $result['change_serversettings']; $diskspace = $result['diskspace']; $traffic = $result['traffic']; - $ipaddress = ($result['ip'] != -1 ? json_decode($result['ip'], true) : -1); + $ipaddress = ($result['ip'] != - 1 ? json_decode($result['ip'], true) : - 1); } else { $deactivated = $this->getParam('deactivated', true, $result['deactivated']); @@ -377,7 +377,7 @@ class Admins extends ApiCommand implements ResourceEntity $tickets_see_all = $this->getParam('tickets_see_all', true, $result['tickets_see_all']); $caneditphpsettings = $this->getParam('caneditphpsettings', true, $result['caneditphpsettings']); $change_serversettings = $this->getParam('change_serversettings', true, $result['change_serversettings']); - $ipaddress = $this->getParam('ipaddress', true, ($result['ip'] != -1 ? json_decode($result['ip'], true) : -1)); + $ipaddress = $this->getParam('ipaddress', true, ($result['ip'] != - 1 ? json_decode($result['ip'], true) : - 1)); $diskspace = $diskspace * 1024; $traffic = $traffic * 1024 * 1024; @@ -512,7 +512,7 @@ class Admins extends ApiCommand implements ResourceEntity 'tickets' => $tickets, 'tickets_see_all' => $tickets_see_all, 'mysqls' => $mysqls, - 'ip' => empty($ipaddress) ? "" : (is_array($ipaddress) && $ipaddress > 0 ? json_encode($ipaddress) : -1), + 'ip' => empty($ipaddress) ? "" : (is_array($ipaddress) && $ipaddress > 0 ? json_encode($ipaddress) : - 1), 'deactivated' => $deactivated, 'custom_notes' => $custom_notes, 'custom_notes_show' => $custom_notes_show, @@ -684,4 +684,30 @@ class Admins extends ApiCommand implements ResourceEntity } throw new Exception("Not allowed to execute given command.", 403); } + + /** + * increase resource-usage + * + * @param int $customerid + * @param string $resource + * @param string $extra + * optional, default empty + */ + public static function increaseUsage($adminid = 0, $resource = null, $extra = '') + { + self::updateResourceUsage(TABLE_PANEL_ADMINS, 'adminid', $adminid, '+', $resource, $extra); + } + + /** + * decrease resource-usage + * + * @param int $customerid + * @param string $resource + * @param string $extra + * optional, default empty + */ + public static function decreaseUsage($adminid = 0, $resource = null, $extra = '') + { + self::updateResourceUsage(TABLE_PANEL_ADMINS, 'adminid', $adminid, '-', $resource, $extra); + } } diff --git a/lib/classes/api/commands/class.Customers.php b/lib/classes/api/commands/class.Customers.php index 2d1dce24..d2a102af 100644 --- a/lib/classes/api/commands/class.Customers.php +++ b/lib/classes/api/commands/class.Customers.php @@ -76,7 +76,7 @@ class Customers extends ApiCommand implements ResourceEntity $result_stmt = Database::prepare(" SELECT `c`.*, `a`.`loginname` AS `adminname` FROM `" . TABLE_PANEL_CUSTOMERS . "` `c`, `" . TABLE_PANEL_ADMINS . "` `a` - WHERE " . ($id > 0 ? "`c`.`customerid` = :idln" : "`c`.`loginname` = :idln") . ($this->getUserDetail('customers_see_all') ? '' : " AND `c`.`adminid` = :adminid"). " AND `c`.`adminid` = `a`.`adminid`"); + WHERE " . ($id > 0 ? "`c`.`customerid` = :idln" : "`c`.`loginname` = :idln") . ($this->getUserDetail('customers_see_all') ? '' : " AND `c`.`adminid` = :adminid") . " AND `c`.`adminid` = `a`.`adminid`"); $params = array( 'idln' => ($id <= 0 ? $loginname : $id) ); @@ -208,7 +208,7 @@ class Customers extends ApiCommand implements ResourceEntity if (((($this->getUserDetail('diskspace_used') + $diskspace) > $this->getUserDetail('diskspace')) && ($this->getUserDetail('diskspace') / 1024) != '-1') || ((($this->getUserDetail('mysqls_used') + $mysqls) > $this->getUserDetail('mysqls')) && $this->getUserDetail('mysqls') != '-1') || ((($this->getUserDetail('emails_used') + $emails) > $this->getUserDetail('emails')) && $this->getUserDetail('emails') != '-1') || ((($this->getUserDetail('email_accounts_used') + $email_accounts) > $this->getUserDetail('email_accounts')) && $this->getUserDetail('email_accounts') != '-1') || ((($this->getUserDetail('email_forwarders_used') + $email_forwarders) > $this->getUserDetail('email_forwarders')) && $this->getUserDetail('email_forwarders') != '-1') || ((($this->getUserDetail('email_quota_used') + $email_quota) > $this->getUserDetail('email_quota')) && $this->getUserDetail('email_quota') != '-1' && Settings::Get('system.mail_quota_enabled') == '1') || ((($this->getUserDetail('ftps_used') + $ftps) > $this->getUserDetail('ftps')) && $this->getUserDetail('ftps') != '-1') || ((($this->getUserDetail('tickets_used') + $tickets) > $this->getUserDetail('tickets')) && $this->getUserDetail('tickets') != '-1') || ((($this->getUserDetail('subdomains_used') + $subdomains) > $this->getUserDetail('subdomains')) && $this->getUserDetail('subdomains') != '-1') || (($diskspace / 1024) == '-1' && ($this->getUserDetail('diskspace') / 1024) != '-1') || ($mysqls == '-1' && $this->getUserDetail('mysqls') != '-1') || ($emails == '-1' && $this->getUserDetail('emails') != '-1') || ($email_accounts == '-1' && $this->getUserDetail('email_accounts') != '-1') || ($email_forwarders == '-1' && $this->getUserDetail('email_forwarders') != '-1') || ($email_quota == '-1' && $this->getUserDetail('email_quota') != '-1' && Settings::Get('system.mail_quota_enabled') == '1') || ($ftps == '-1' && $this->getUserDetail('ftps') != '-1') || ($tickets == '-1' && $this->getUserDetail('tickets') != '-1') || ($subdomains == '-1' && $this->getUserDetail('subdomains') != '-1')) { standard_error('youcantallocatemorethanyouhave', '', true); } - + if (! validateEmail($email)) { standard_error('emailiswrong', $email, true); } else { @@ -755,10 +755,10 @@ class Customers extends ApiCommand implements ResourceEntity } if ($this->isAdmin()) { - + $diskspace = $diskspace * 1024; $traffic = $traffic * 1024 * 1024; - + if (((($this->getUserDetail('diskspace_used') + $diskspace - $result['diskspace']) > $this->getUserDetail('diskspace')) && ($this->getUserDetail('diskspace') / 1024) != '-1') || ((($this->getUserDetail('mysqls_used') + $mysqls - $result['mysqls']) > $this->getUserDetail('mysqls')) && $this->getUserDetail('mysqls') != '-1') || ((($this->getUserDetail('emails_used') + $emails - $result['emails']) > $this->getUserDetail('emails')) && $this->getUserDetail('emails') != '-1') || ((($this->getUserDetail('email_accounts_used') + $email_accounts - $result['email_accounts']) > $this->getUserDetail('email_accounts')) && $this->getUserDetail('email_accounts') != '-1') || ((($this->getUserDetail('email_forwarders_used') + $email_forwarders - $result['email_forwarders']) > $this->getUserDetail('email_forwarders')) && $this->getUserDetail('email_forwarders') != '-1') || ((($this->getUserDetail('email_quota_used') + $email_quota - $result['email_quota']) > $this->getUserDetail('email_quota')) && $this->getUserDetail('email_quota') != '-1' && Settings::Get('system.mail_quota_enabled') == '1') || ((($this->getUserDetail('ftps_used') + $ftps - $result['ftps']) > $this->getUserDetail('ftps')) && $this->getUserDetail('ftps') != '-1') || ((($this->getUserDetail('tickets_used') + $tickets - $result['tickets']) > $this->getUserDetail('tickets')) && $this->getUserDetail('tickets') != '-1') || ((($this->getUserDetail('subdomains_used') + $subdomains - $result['subdomains']) > $this->getUserDetail('subdomains')) && $this->getUserDetail('subdomains') != '-1') || (($diskspace / 1024) == '-1' && ($this->getUserDetail('diskspace') / 1024) != '-1') || ($mysqls == '-1' && $this->getUserDetail('mysqls') != '-1') || ($emails == '-1' && $this->getUserDetail('emails') != '-1') || ($email_accounts == '-1' && $this->getUserDetail('email_accounts') != '-1') || ($email_forwarders == '-1' && $this->getUserDetail('email_forwarders') != '-1') || ($email_quota == '-1' && $this->getUserDetail('email_quota') != '-1' && Settings::Get('system.mail_quota_enabled') == '1') || ($ftps == '-1' && $this->getUserDetail('ftps') != '-1') || ($tickets == '-1' && $this->getUserDetail('tickets') != '-1') || ($subdomains == '-1' && $this->getUserDetail('subdomains') != '-1')) { standard_error('youcantallocatemorethanyouhave', '', true); } @@ -1533,4 +1533,30 @@ class Customers extends ApiCommand implements ResourceEntity } throw new Exception("Not allowed to execute given command.", 403); } + + /** + * increase resource-usage + * + * @param int $customerid + * @param string $resource + * @param string $extra + * optional, default empty + */ + public static function increaseUsage($customerid = 0, $resource = null, $extra = '') + { + self::updateResourceUsage(TABLE_PANEL_CUSTOMERS, 'customerid', $customerid, '+', $resource, $extra); + } + + /** + * decrease resource-usage + * + * @param int $customerid + * @param string $resource + * @param string $extra + * optional, default empty + */ + public static function decreaseUsage($customerid = 0, $resource = null, $extra = '') + { + self::updateResourceUsage(TABLE_PANEL_CUSTOMERS, 'customerid', $customerid, '-', $resource, $extra); + } } diff --git a/lib/classes/api/commands/class.Froxlor.php b/lib/classes/api/commands/class.Froxlor.php index d10a96b5..3e902101 100644 --- a/lib/classes/api/commands/class.Froxlor.php +++ b/lib/classes/api/commands/class.Froxlor.php @@ -311,7 +311,7 @@ class Froxlor extends ApiCommand 'type' => $r[1], 'desc' => (isset($r[2]) ? trim($r[2]) : '') ); - } + } // check throws-section elseif (! empty($c) && strpos($c, '@throws') === false) { if (substr($c, 0, 3) == "/**") { continue; diff --git a/lib/classes/api/commands/class.Ftps.php b/lib/classes/api/commands/class.Ftps.php index a723080e..c98a226c 100644 --- a/lib/classes/api/commands/class.Ftps.php +++ b/lib/classes/api/commands/class.Ftps.php @@ -183,26 +183,14 @@ class Ftps extends ApiCommand implements ResourceEntity "guid" => $customer['guid'] ); Database::pexecute($stmt, $params, true, true); + + // update customer usage + Customers::increaseUsage($customer_id, 'ftps_used'); + Customers::increaseUsage($customer_id, 'ftp_lastaccountnumber'); - $stmt = Database::prepare(" - UPDATE `" . TABLE_PANEL_CUSTOMERS . "` - SET `ftps_used` = `ftps_used` + 1, - `ftp_lastaccountnumber` = `ftp_lastaccountnumber` + 1 - WHERE `customerid` = :customerid - "); - Database::pexecute($stmt, array( - "customerid" => $customer_id - ), true, true); - - $stmt = Database::prepare(" - UPDATE `" . TABLE_PANEL_ADMINS . "` - SET `ftps_used` = `ftps_used` + 1 - WHERE `adminid` = :adminid - "); - Database::pexecute($stmt, array( - "adminid" => $customer['adminid'] - ), true, true); - + // update admin usage + Admins::increaseUsage($customer['adminid'], 'ftps_used'); + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_INFO, "[API] added ftp-account '" . $username . " (" . $path . ")'"); inserttask(5); @@ -614,21 +602,9 @@ class Ftps extends ApiCommand implements ResourceEntity // decrease ftp-user usage for customer $resetaccnumber = ($customer_data['ftps_used'] == '1') ? " , `ftp_lastaccountnumber`='0'" : ''; - $stmt = Database::prepare("UPDATE `" . TABLE_PANEL_CUSTOMERS . "` - SET `ftps_used` = `ftps_used` - 1 $resetaccnumber - WHERE `customerid` = :customerid"); - Database::pexecute($stmt, array( - "customerid" => $customer_data['customerid'] - ), true, true); + Customers::decreaseUsage($customer_id, 'ftps_used', $resetaccnumber); // update admin usage - $stmt = Database::prepare(" - UPDATE `" . TABLE_PANEL_ADMINS . "` - SET `mysqls_used` = `mysqls_used` - 1 - WHERE `adminid` = :adminid - "); - Database::pexecute($stmt, array( - "adminid" => ($this->isAdmin() ? $customer_data['adminid'] : $this->getUserDetail('adminid')) - ), true, true); + Admins::decreaseUsage(($this->isAdmin() ? $customer_data['adminid'] : $this->getUserDetail('adminid')), 'ftps_used'); $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_WARNING, "[API] deleted ftp-user '" . $result['username'] . "'"); return $this->response(200, "successfull", $result); diff --git a/lib/classes/api/commands/class.Mysqls.php b/lib/classes/api/commands/class.Mysqls.php index b9482284..8022562d 100644 --- a/lib/classes/api/commands/class.Mysqls.php +++ b/lib/classes/api/commands/class.Mysqls.php @@ -62,14 +62,7 @@ class Mysqls extends ApiCommand implements ResourceEntity if (! isset($sql_root) || ! is_array($sql_root)) { throw new ErrorException("Database server with index #" . $dbserver . " is unknown", 404); } - - if ($password == '') { - standard_error(array( - 'stringisempty', - 'mysql_password' - ), '', true); - } - + if ($sendinfomail != 1) { $sendinfomail = 0; } @@ -123,24 +116,11 @@ class Mysqls extends ApiCommand implements ResourceEntity $params['id'] = $databaseid; // update customer usage - $stmt = Database::prepare(" - UPDATE `" . TABLE_PANEL_CUSTOMERS . "` - SET `mysqls_used` = `mysqls_used` + 1, `mysql_lastaccountnumber` = `mysql_lastaccountnumber` + 1 - WHERE `customerid` = :customerid - "); - Database::pexecute($stmt, array( - "customerid" => ($this->isAdmin() ? $customer['customerid'] : $this->getUserDetail('customerid')) - ), true, true); + Customers::increaseUsage(($this->isAdmin() ? $customer['customerid'] : $this->getUserDetail('customerid')), 'mysqls_used'); + Customers::increaseUsage(($this->isAdmin() ? $customer['customerid'] : $this->getUserDetail('customerid')), 'mysql_lastaccountnumber'); // update admin usage - $stmt = Database::prepare(" - UPDATE `" . TABLE_PANEL_ADMINS . "` - SET `mysqls_used` = `mysqls_used` + 1 - WHERE `adminid` = :adminid - "); - Database::pexecute($stmt, array( - "adminid" => $this->getUserDetail('adminid') - ), true, true); + Admins::increaseUsage($this->getUserDetail('adminid'), 'mysqls_used'); // send info-mail? if ($sendinfomail == 1) { @@ -586,23 +566,9 @@ class Mysqls extends ApiCommand implements ResourceEntity } // reduce mysql-usage-counter $resetaccnumber = ($mysql_used == '1') ? " , `mysql_lastaccountnumber` = '0' " : ''; - $stmt = Database::prepare(" - UPDATE `" . TABLE_PANEL_CUSTOMERS . "` - SET `mysqls_used` = `mysqls_used` - 1 " . $resetaccnumber . " - WHERE `customerid` = :customerid - "); - Database::pexecute($stmt, array( - "customerid" => $customer_id - ), true, true); + Customers::decreaseUsage($customer_id, 'mysqls_used', $resetaccnumber); // update admin usage - $stmt = Database::prepare(" - UPDATE `" . TABLE_PANEL_ADMINS . "` - SET `mysqls_used` = `mysqls_used` - 1 - WHERE `adminid` = :adminid - "); - Database::pexecute($stmt, array( - "adminid" => ($this->isAdmin() ? $customer['adminid'] : $this->getUserDetail('adminid')), - ), true, true); + Admins::decreaseUsage(($this->isAdmin() ? $customer['adminid'] : $this->getUserDetail('adminid')), 'mysqls_used'); $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_WARNING, "[API] deleted database '" . $result['databasename'] . "'"); return $this->response(200, "successfull", $result); diff --git a/lib/classes/api/commands/class.Subdomains.php b/lib/classes/api/commands/class.Subdomains.php index cfec3569..7b496e78 100644 --- a/lib/classes/api/commands/class.Subdomains.php +++ b/lib/classes/api/commands/class.Subdomains.php @@ -18,9 +18,318 @@ class Subdomains extends ApiCommand implements ResourceEntity { + /** + * add a new subdomain + * + * @param string $subdomain + * part before domain.tld to create as subdomain + * @param string $domain + * domainname of main-domain + * @param int $alias + * optional, domain-id of a domain that the new domain should be an alias of + * @param string $path + * optional, destination path relative to the customers-homedir, default is customers-homedir + * @param string $url + * optional, overwrites path value with an URL to generate a redirect, alternatively use the path parameter also for URLs + * @param string $openbasedir_path + * optional, either 0 for customers-homedir or 1 for domains-docroot + * @param int $phpsettingid + * optional, php-settings-id, if empty the $domain value is used + * @param int $redirectcode + * optional, redirect-code-id from TABLE_PANEL_REDIRECTCODES + * @param bool $ssl_redirect + * optional, whether to generate a https-redirect or not, default false; requires SSL to be enabled + * @param bool $letsencrypt + * optional, whether to generate a Let's Encrypt certificate for this domain, default false; requires SSL to be enabled + * @param int $customer_id + * required when called as admin, not needed when called as customer + * + * @access admin, customer + * @throws Exception + * @return array + */ public function add() { - throw new Exception("Not available yet.", 501); + if ($this->getUserDetail('subdomains_used') < $this->getUserDetail('subdomains') || $this->getUserDetail('subdomains') == '-1') { + // parameters + $subdomain = $this->getParam('subdomain'); + $domain = $this->getParam('domain'); + + // optional parameters + $aliasdomain = $this->getParam('alias', true, 0); + $path = $this->getParam('path', true, ''); + $url = $this->getParam('url', true, ''); + $openbasedir_path = $this->getParam('openbasedir_path', true, 1); + $phpsettingid = $this->getParam('phpsettingid', true, 0); + $redirectcode = $this->getParam('redirectcode', true, Settings::Get('customredirect.default')); + if (Settings::Get('system.use_ssl')) { + $ssl_redirect = $this->getParam('ssl_redirect', true, 0); + $letsencrypt = $this->getParam('letsencrypt', true, 0); + $hsts_maxage = $this->getParam('hsts_maxage', true, 0); + $hsts_sub = $this->getParam('hsts_sub', true, 0); + $hsts_preload = $this->getParam('hsts_preload', true, 0); + } else { + $ssl_redirect = 0; + $letsencrypt = 0; + $hsts_maxage = 0; + $hsts_sub = 0; + $hsts_preload = 0; + } + + // get needed customer info to reduce the mysql-usage-counter by one + if ($this->isAdmin()) { + // get customer id + $customer_id = $this->getParam('customer_id'); + $json_result = Customers::getLocal($this->getUserData(), array( + 'id' => $customer_id + ))->get(); + $customer = json_decode($json_result, true)['data']; + // check whether the customer has enough resources to get the subdomain added + if ($customer['subdomains_used'] >= $customer['subdomains'] && $customer['subdomains'] != '-1') { + throw new Exception("Customer has no more resources available", 406); + } + } else { + $customer_id = $this->getUserDetail('customerid'); + $customer = $this->getUserData(); + } + + // validation + if (substr($subdomain, 0, 4) == 'xn--') { + standard_error('domain_nopunycode', '', true); + } + + $idna_convert = new idna_convert_wrapper(); + $subdomain = $idna_convert->encode(preg_replace(array( + '/\:(\d)+$/', + '/^https?\:\/\//' + ), '', validate($subdomain, 'subdomain', '', 'subdomainiswrong', array(), true))); + + // merge the two parts together + $completedomain = $subdomain . '.' . $domain; + + if (Settings::Get('system.validate_domain') && ! validateDomain($completedomain)) { + standard_error(array( + 'stringiswrong', + 'mydomain' + ), '', true); + } + if ($completedomain == Settings::Get('system.hostname')) { + standard_error('admin_domain_emailsystemhostname', '', true); + } + + // check whether the domain already exists + try { + $json_result = SubDomains::getLocal($this->getUserData(), array( + 'domainname' => $completedomain + ))->get(); + // no exception so far - domain exists + standard_error('domainexistalready', $completedomain, true); + } catch (Exception $e) { + // all good, domain does not exist + } + + // alias domain checked? + if ($aliasdomain != 0) { + // also check ip/port combination to be the same, #176 + $aliasdomain_stmt = Database::prepare(" + SELECT `d`.`id` FROM `" . TABLE_PANEL_DOMAINS . "` `d` , `" . TABLE_PANEL_CUSTOMERS . "` `c` , `" . TABLE_DOMAINTOIP . "` `dip` + WHERE `d`.`aliasdomain` IS NULL + AND `d`.`id` = :id + AND `c`.`standardsubdomain` <> `d`.`id` + AND `d`.`customerid` = :customerid + AND `c`.`customerid` = `d`.`customerid` + AND `d`.`id` = `dip`.`id_domain` + AND `dip`.`id_ipandports` + IN (SELECT `id_ipandports` FROM `" . TABLE_DOMAINTOIP . "` WHERE `id_domain` = :id ) + GROUP BY `d`.`domain` + ORDER BY `d`.`domain` ASC + "); + $aliasdomain_check = Database::pexecute_first($aliasdomain_stmt, array( + "id" => $aliasdomain, + "customerid" => $customer_id + ), true, true); + if ($aliasdomain_check['id'] != $aliasdomain) { + standard_error('domainisaliasorothercustomer', '', true); + } + triggerLetsEncryptCSRForAliasDestinationDomain($aliasdomain, $this->logger()); + } + + // check whether an URL was specified + $_doredirect = false; + if (! empty($url) && validateUrl($url)) { + $path = $url; + $_doredirect = true; + } else { + $path = validate($path, 'path', '', '', array(), true); + } + + // check whether path is a real path + if (! preg_match('/^https?\:\/\//', $path) || ! validateUrl($path)) { + if (strstr($path, ":") !== false) { + standard_error('pathmaynotcontaincolon', '', true); + } + // If path is empty or '/' and 'Use domain name as default value for DocumentRoot path' is enabled in settings, + // set default path to subdomain or domain name + if ((($path == '') || ($path == '/')) && Settings::Get('system.documentroot_use_default_value') == 1) { + $path = makeCorrectDir($customer['documentroot'] . '/' . $completedomain); + } else { + $path = makeCorrectDir($customer['documentroot'] . '/' . $path); + } + } else { + // no it's not, create a redirect + $_doredirect = true; + } + + if ($openbasedir_path != 1) { + $openbasedir_path = 0; + } + + // get main domain for various checks + $domain_stmt = Database::prepare(" + SELECT * FROM `" . TABLE_PANEL_DOMAINS . "` + WHERE `domain` = :domain + AND `customerid` = :customerid + AND `parentdomainid` = '0' + AND `email_only` = '0' + AND `caneditdomain` = '1' + "); + $domain_check = Database::pexecute_first($domain_stmt, array( + "domain" => $domain, + "customerid" => $customer['customerid'] + ), true, true); + + if (! $domain_check) { + // the given main-domain + standard_error('maindomainnonexist', $domain, true); + } elseif ($subdomain == 'www' && $domain_check['wwwserveralias'] == '1') { + // you cannot add 'www' as subdomain when the maindomain generates a www-alias + standard_error('wwwnotallowed', '', true); + } elseif (strtolower($completedomain_check['domain']) == strtolower($completedomain)) { + // the domain does already exist as main-domain + standard_error('domainexistalready', $completedomain, true); + } + + if ($ssl_redirect != 0) { + // a ssl-redirect only works if there actually is a + // ssl ip/port assigned to the domain + if (domainHasSslIpPort($domain_check['id']) == true) { + $ssl_redirect = '1'; + $_doredirect = true; + } else { + standard_error('sslredirectonlypossiblewithsslipport', '', true); + } + } + + if ($letsencrypt != 0) { + // let's encrypt only works if there actually is a + // ssl ip/port assigned to the domain + if (domainHasSslIpPort($domain_check['id']) == true) { + $letsencrypt = '1'; + } else { + standard_error('letsencryptonlypossiblewithsslipport', '', true); + } + } + + // Temporarily deactivate ssl_redirect until Let's Encrypt certificate was generated + if ($ssl_redirect > 0 && $letsencrypt == 1) { + $ssl_redirect = 2; + } + + // get the phpsettingid from parentdomain, #107 + $phpsid_stmt = Database::prepare(" + SELECT `phpsettingid` FROM `" . TABLE_PANEL_DOMAINS . "` WHERE `id` = :id + "); + $phpsid_result = Database::pexecute_first($phpsid_stmt, array( + "id" => $domain_check['id'] + ), true, true); + + if (! isset($phpsid_result['phpsettingid']) || (int) $phpsid_result['phpsettingid'] <= 0) { + // assign default config + $phpsid_result['phpsettingid'] = 1; + } + // check whether the customer has chosen its own php-config + if ($phpsettingid > 0 && $phpsettingid != $phpsid_result['phpsettingid']) { + $phpsid_result['phpsettingid'] = intval($phpsettingid); + } + + // acutall insert domain + $stmt = Database::prepare(" + INSERT INTO `" . TABLE_PANEL_DOMAINS . "` SET + `customerid` = :customerid, + `domain` = :domain, + `documentroot` = :documentroot, + `aliasdomain` = :aliasdomain, + `parentdomainid` = :parentdomainid, + `wwwserveralias` = :wwwserveralias, + `isemaildomain` = :isemaildomain, + `iswildcarddomain` = :iswildcarddomain, + `phpenabled` = :phpenabled, + `openbasedir` = :openbasedir, + `openbasedir_path` = :openbasedir_path, + `speciallogfile` = :speciallogfile, + `specialsettings` = :specialsettings, + `ssl_redirect` = :ssl_redirect, + `phpsettingid` = :phpsettingid, + `letsencrypt` = :letsencrypt, + `hsts` = :hsts, + `hsts_sub` = :hsts_sub, + `hsts_preload` = :hsts_preload + "); + $params = array( + "customerid" => $customer['customerid'], + "domain" => $completedomain, + "documentroot" => $path, + "aliasdomain" => $aliasdomain != 0 ? $aliasdomain : null, + "parentdomainid" => $domain_check['id'], + "wwwserveralias" => $domain_check['wwwserveralias'] == '1' ? '1' : '0', + "iswildcarddomain" => $domain_check['iswildcarddomain'] == '1' ? '1' : '0', + "isemaildomain" => $domain_check['subcanemaildomain'] == '3' ? '1' : '0', + "openbasedir" => $domain_check['openbasedir'], + "openbasedir_path" => $openbasedir_path, + "phpenabled" => $domain_check['phpenabled'], + "speciallogfile" => $domain_check['speciallogfile'], + "specialsettings" => $domain_check['specialsettings'], + "ssl_redirect" => $ssl_redirect, + "phpsettingid" => $phpsid_result['phpsettingid'], + "letsencrypt" => $letsencrypt, + "hsts" => $hsts_maxage, + "hsts_sub" => $hsts_sub, + "hsts_preload" => $hsts_preload + ); + Database::pexecute($stmt, $params, true, true); + $subdomain_id = Database::lastInsertId(); + + $stmt = Database::prepare(" + INSERT INTO `" . TABLE_DOMAINTOIP . "` + (`id_domain`, `id_ipandports`) + SELECT LAST_INSERT_ID(), `id_ipandports` + FROM `" . TABLE_DOMAINTOIP . "` + WHERE `id_domain` = :id_domain + "); + Database::pexecute($stmt, array( + "id_domain" => $domain_check['id'] + )); + + if ($_doredirect) { + addRedirectToDomain($subdomain_id, $redirectcode); + } + + inserttask('1'); + // Using nameserver, insert a task which rebuilds the server config + inserttask('4'); + + Customers::increaseUsage($customer['customerid'], 'subdomains_used'); + Admins::increaseUsage(($this->isAdmin() ? $customer['adminid'] : $this->getUserDetail('adminid')), 'subdomains_used'); + + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_INFO, "[API] added subdomain '" . $completedomain . "'"); + + $json_result = Subdomains::getLocal($this->getUserData(), array( + 'id' => $subdomain_id + ))->get(); + $result = json_decode($json_result, true)['data']; + return $this->response(200, "successfull", $result); + } + throw new Exception("No more resources available", 406); } /** From 3e0b55141602215eda49de59f1b45be2b504769e Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 27 Feb 2018 15:56:05 +0100 Subject: [PATCH 0515/1335] fix copy'n'paste fail Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/commands/class.Ftps.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/classes/api/commands/class.Ftps.php b/lib/classes/api/commands/class.Ftps.php index c98a226c..42cf0705 100644 --- a/lib/classes/api/commands/class.Ftps.php +++ b/lib/classes/api/commands/class.Ftps.php @@ -602,7 +602,7 @@ class Ftps extends ApiCommand implements ResourceEntity // decrease ftp-user usage for customer $resetaccnumber = ($customer_data['ftps_used'] == '1') ? " , `ftp_lastaccountnumber`='0'" : ''; - Customers::decreaseUsage($customer_id, 'ftps_used', $resetaccnumber); + Customers::decreaseUsage($customer_data['customerid'], 'ftps_used', $resetaccnumber); // update admin usage Admins::decreaseUsage(($this->isAdmin() ? $customer_data['adminid'] : $this->getUserDetail('adminid')), 'ftps_used'); From 75bc6d32ab10030b2879870c779e93de6b395ac0 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 27 Feb 2018 17:10:52 +0100 Subject: [PATCH 0516/1335] minor fixes in SubDomains.add; first Unit-Tests for SubDomains-ApiCommand Signed-off-by: Michael Kaufmann (d00p) --- ...ss.Subdomains.php => class.SubDomains.php} | 31 +++++---- phpunit.xml | 1 + tests/SubDomains/SubDomainsTest.php | 64 +++++++++++++++++++ 3 files changed, 84 insertions(+), 12 deletions(-) rename lib/classes/api/commands/{class.Subdomains.php => class.SubDomains.php} (97%) create mode 100644 tests/SubDomains/SubDomainsTest.php diff --git a/lib/classes/api/commands/class.Subdomains.php b/lib/classes/api/commands/class.SubDomains.php similarity index 97% rename from lib/classes/api/commands/class.Subdomains.php rename to lib/classes/api/commands/class.SubDomains.php index 7b496e78..cade0a69 100644 --- a/lib/classes/api/commands/class.Subdomains.php +++ b/lib/classes/api/commands/class.SubDomains.php @@ -15,7 +15,7 @@ * @since 0.10.0 * */ -class Subdomains extends ApiCommand implements ResourceEntity +class SubDomains extends ApiCommand implements ResourceEntity { /** @@ -31,7 +31,7 @@ class Subdomains extends ApiCommand implements ResourceEntity * optional, destination path relative to the customers-homedir, default is customers-homedir * @param string $url * optional, overwrites path value with an URL to generate a redirect, alternatively use the path parameter also for URLs - * @param string $openbasedir_path + * @param int $openbasedir_path * optional, either 0 for customers-homedir or 1 for domains-docroot * @param int $phpsettingid * optional, php-settings-id, if empty the $domain value is used @@ -116,18 +116,25 @@ class Subdomains extends ApiCommand implements ResourceEntity if ($completedomain == Settings::Get('system.hostname')) { standard_error('admin_domain_emailsystemhostname', '', true); } - + // check whether the domain already exists - try { - $json_result = SubDomains::getLocal($this->getUserData(), array( - 'domainname' => $completedomain - ))->get(); + $completedomain_stmt = Database::prepare(" + SELECT * FROM `" . TABLE_PANEL_DOMAINS . "` + WHERE `domain` = :domain + AND `customerid` = :customerid + AND `email_only` = '0' + AND `caneditdomain` = '1' + "); + $completedomain_check = Database::pexecute_first($completedomain_stmt, array( + "domain" => $completedomain, + "customerid" => $customer_id + ), true, true); + + if ($completedomain_check) { // no exception so far - domain exists standard_error('domainexistalready', $completedomain, true); - } catch (Exception $e) { - // all good, domain does not exist } - + // alias domain checked? if ($aliasdomain != 0) { // also check ip/port combination to be the same, #176 @@ -381,7 +388,7 @@ class Subdomains extends ApiCommand implements ResourceEntity } else { $result_stmt = Database::prepare(" SELECT * FROM `" . TABLE_PANEL_DOMAINS . "` - WHERE " . ($id > 0 ? "`id` = :iddn" : "`databasename` = :iddn")); + WHERE " . ($id > 0 ? "`id` = :iddn" : "`domainname` = :iddn")); $params = array( 'iddn' => ($id <= 0 ? $domainname : $id) ); @@ -392,7 +399,7 @@ class Subdomains extends ApiCommand implements ResourceEntity } $result_stmt = Database::prepare(" SELECT `id`, `customerid`, `domain`, `documentroot`, `isemaildomain`, `parentdomainid`, `aliasdomain` FROM `" . TABLE_PANEL_DOMAINS . "` - WHERE `customerid`= :customerid AND " . ($id > 0 ? "`id` = :iddn" : "`databasename` = :iddn")); + WHERE `customerid`= :customerid AND " . ($id > 0 ? "`id` = :iddn" : "`domainname` = :iddn")); $params = array( 'customerid' => $this->getUserDetail('customerid'), 'iddn' => ($id <= 0 ? $domainname : $id) diff --git a/phpunit.xml b/phpunit.xml index a924f199..aac6ff31 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -11,6 +11,7 @@ tests/Global tests/Admins tests/Customers + tests/SubDomains tests/IpsAndPorts tests/Ftps diff --git a/tests/SubDomains/SubDomainsTest.php b/tests/SubDomains/SubDomainsTest.php new file mode 100644 index 00000000..6388dc21 --- /dev/null +++ b/tests/SubDomains/SubDomainsTest.php @@ -0,0 +1,64 @@ + 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + + $data = [ + 'subdomain' => 'xn--asd', + 'domain' => 'unknown.froxlor.org' + ]; + $this->expectExceptionMessage('You must not specify punycode (IDNA). The domain will automatically be converted'); + SubDomains::getLocal($customer_userdata, $data)->add(); + } + + public function testCustomerSubDomainsAddMainDomainUnknown() + { + global $admin_userdata; + + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + + $data = [ + 'subdomain' => 'wohoo', + 'domain' => 'unknown.froxlor.org' + ]; + $this->expectExceptionMessage('The main-domain unknown.froxlor.org does not exist.'); + SubDomains::getLocal($customer_userdata, $data)->add(); + } + + public function testCustomerSubDomainsAddInvalidDomain() + { + global $admin_userdata; + + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + + $data = [ + 'subdomain' => '#+?', + 'domain' => 'unknown.froxlor.org' + ]; + $this->expectExceptionMessage("Wrong Input in Field 'Domain'"); + SubDomains::getLocal($customer_userdata, $data)->add(); + } +} From 2bf5e90a7746e56742becf503d861aabacf29fb0 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 27 Feb 2018 18:07:43 +0100 Subject: [PATCH 0517/1335] add testsuite parameter to phpunit to respect our required test-order; minor fixes in Domains- and SubDomains Command Signed-off-by: Michael Kaufmann (d00p) --- build.xml | 4 ++++ lib/classes/api/commands/class.Domains.php | 8 ++++++-- lib/classes/api/commands/class.SubDomains.php | 20 +++---------------- phpunit.xml | 3 ++- 4 files changed, 15 insertions(+), 20 deletions(-) diff --git a/build.xml b/build.xml index 99f202d8..10713128 100644 --- a/build.xml +++ b/build.xml @@ -194,6 +194,8 @@ taskname="phpunit"> + + @@ -205,6 +207,8 @@ + + diff --git a/lib/classes/api/commands/class.Domains.php b/lib/classes/api/commands/class.Domains.php index b3e433e3..c68099eb 100644 --- a/lib/classes/api/commands/class.Domains.php +++ b/lib/classes/api/commands/class.Domains.php @@ -706,7 +706,6 @@ class Domains extends ApiCommand implements ResourceEntity Database::pexecute($ins_stmt, $ins_data, true, true); $domainid = Database::lastInsertId(); $ins_data['id'] = $domainid; - $domain_ins_data = $ins_data; unset($ins_data); $upd_stmt = Database::prepare(" @@ -747,7 +746,12 @@ class Domains extends ApiCommand implements ResourceEntity inserttask('4'); $this->logger()->logAction(ADM_ACTION, LOG_WARNING, "[API] added domain '" . $domain . "'"); - return $this->response(200, "successfull", $domain_ins_data); + + $json_result = Domains::getLocal($this->getUserData(), array( + 'domainname' => $domain + ))->get(); + $result = json_decode($json_result, true)['data']; + return $this->response(200, "successfull", $result); } } throw new Exception("No more resources available", 406); diff --git a/lib/classes/api/commands/class.SubDomains.php b/lib/classes/api/commands/class.SubDomains.php index cade0a69..35d8e684 100644 --- a/lib/classes/api/commands/class.SubDomains.php +++ b/lib/classes/api/commands/class.SubDomains.php @@ -330,7 +330,7 @@ class SubDomains extends ApiCommand implements ResourceEntity $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_INFO, "[API] added subdomain '" . $completedomain . "'"); - $json_result = Subdomains::getLocal($this->getUserData(), array( + $json_result = SubDomains::getLocal($this->getUserData(), array( 'id' => $subdomain_id ))->get(); $result = json_decode($json_result, true)['data']; @@ -597,23 +597,9 @@ class SubDomains extends ApiCommand implements ResourceEntity inserttask('4'); // reduce subdomain-usage-counter - $stmt = Database::prepare(" - UPDATE `" . TABLE_PANEL_CUSTOMERS . "` - SET `subdomains_used` = `subdomains_used` - 1 - WHERE `customerid` = :customerid - "); - Database::pexecute($stmt, array( - "customerid" => $customer_id - ), true, true); + Customers::decreaseUsage($customer_id, 'subdomains_used'); // update admin usage - $stmt = Database::prepare(" - UPDATE `" . TABLE_PANEL_ADMINS . "` - SET `subdomains_used` = `subdomains_used` - 1 - WHERE `adminid` = :adminid - "); - Database::pexecute($stmt, array( - "adminid" => ($this->isAdmin() ? $customer['adminid'] : $this->getUserDetail('adminid')) - ), true, true); + Admins::decreaseUsage(($this->isAdmin() ? $customer['adminid'] : $this->getUserDetail('adminid')), 'subdomains_used'); $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_WARNING, "[API] deleted subdomain '" . $result['domain'] . "'"); return $this->response(200, "successfull", $result); diff --git a/phpunit.xml b/phpunit.xml index aac6ff31..391c3a47 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -11,8 +11,9 @@ tests/Global tests/Admins tests/Customers - tests/SubDomains tests/IpsAndPorts + tests/Domains + tests/SubDomains tests/Ftps From 02616d3080af1e922fdeb747d1cb6690a52224f2 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 1 Mar 2018 16:46:47 +0100 Subject: [PATCH 0518/1335] minor fixes and first tests for Domains-ApiCommand Signed-off-by: Michael Kaufmann (d00p) --- install/scripts/config-services.php | 1 - lib/classes/api/commands/class.Domains.php | 8 -- .../function.getIpPortCombinations.php | 11 ++- tests/Domains/DomainsTest.php | 87 +++++++++++++++++++ 4 files changed, 92 insertions(+), 15 deletions(-) create mode 100644 tests/Domains/DomainsTest.php diff --git a/install/scripts/config-services.php b/install/scripts/config-services.php index 1799ec4e..c0c84aec 100755 --- a/install/scripts/config-services.php +++ b/install/scripts/config-services.php @@ -69,7 +69,6 @@ class ConfigServicesCmd extends CmdLineHandler self::println("--daemon\t\tWhen running --apply you can specify a daemon. This will be the only service that gets configured"); self::println("\t\t\tExample: --apply=/path/to/my-config.json --daemon=apache24"); self::println(""); - self::println(""); self::println("--import-settings\tImport settings from another froxlor installation. This should be done prior to running --apply or alternatively in the same command together."); self::println("\t\t\tExample: --import-settings=/path/to/Froxlor_settings-[version]-[dbversion]-[date].json"); self::println(""); diff --git a/lib/classes/api/commands/class.Domains.php b/lib/classes/api/commands/class.Domains.php index c68099eb..5db78a46 100644 --- a/lib/classes/api/commands/class.Domains.php +++ b/lib/classes/api/commands/class.Domains.php @@ -183,10 +183,6 @@ class Domains extends ApiCommand implements ResourceEntity ))->get(); $customer = json_decode($json_result, true)['data']; - if (empty($customer) || $customer['customerid'] != $customerid) { - standard_error('customerdoesntexist', '', true); - } - if ($this->getUserDetail('customers_see_all') == '1') { $admin_stmt = Database::prepare(" SELECT * FROM `" . TABLE_PANEL_ADMINS . "` @@ -194,10 +190,6 @@ class Domains extends ApiCommand implements ResourceEntity $admin = Database::pexecute_first($admin_stmt, array( 'adminid' => $adminid ), true, true); - - if (empty($admin) || $admin['adminid'] != $adminid) { - standard_error('admindoesntexist', '', true); - } } else { $adminid = $this->getUserDetail('adminid'); $admin = $this->getUserData(); diff --git a/lib/functions/froxlor/function.getIpPortCombinations.php b/lib/functions/froxlor/function.getIpPortCombinations.php index 1a0c75f6..b0a2346f 100644 --- a/lib/functions/froxlor/function.getIpPortCombinations.php +++ b/lib/functions/froxlor/function.getIpPortCombinations.php @@ -26,13 +26,12 @@ function getIpPortCombinations($ssl = false) { if ($userinfo['ip'] != '-1') { $admin_ip_stmt = Database::prepare(" - SELECT `id`, `ip`, `port` FROM `" . TABLE_PANEL_IPSANDPORTS . "` WHERE `id` = :ipid + SELECT `id`, `ip`, `port` FROM `" . TABLE_PANEL_IPSANDPORTS . "` WHERE `id` = IN (:ipid) "); - $admin_ip = Database::pexecute_first($admin_ip_stmt, array('ipid' => $userinfo['ip'])); - - $additional_conditions_array[] = "`ip` = :adminip"; - $additional_conditions_params['adminip'] = $admin_ip['ip']; - $admin_ip = null; + $myips = implode(",", json_decode($userinfo['ip'], true)); + Database::pexecute($admin_ip_stmt, array('ipid' => $myips)); + $additional_conditions_array[] = "`ip` IN (:adminips)"; + $additional_conditions_params['adminips'] = $myips; } if ($ssl !== null) { diff --git a/tests/Domains/DomainsTest.php b/tests/Domains/DomainsTest.php new file mode 100644 index 00000000..a196c7ef --- /dev/null +++ b/tests/Domains/DomainsTest.php @@ -0,0 +1,87 @@ + 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + $data = [ + 'domain' => 'test.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'].'test.local/', $result['documentroot']); + } + + /** + * @depends testAdminDomainsAdd + */ + public function testAdminDomainsList() + { + global $admin_userdata; + $json_result = Domains::getLocal($admin_userdata)->list(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals(1, $result['count']); + $this->assertEquals('test.local', $result['list'][0]['domain']); + } + + public function testAdminDomainsAddSysHostname() + { + global $admin_userdata; + $data = [ + 'domain' => 'dev.froxlor.org', + 'customerid' => 1 + ]; + $this->expectExceptionMessage('The server-hostname cannot be used as customer-domain.'); + $json_result = Domains::getLocal($admin_userdata, $data)->add(); + } + + public function testAdminDomainsAddNoPunycode() + { + global $admin_userdata; + $data = [ + 'domain' => 'xn--asdasd.tld', + 'customerid' => 1 + ]; + $this->expectExceptionMessage('You must not specify punycode (IDNA). The domain will automatically be converted'); + $json_result = Domains::getLocal($admin_userdata, $data)->add(); + } + + public function testAdminDomainsAddInvalidDomain() + { + global $admin_userdata; + $data = [ + 'domain' => 'dom?*ain.tld', + 'customerid' => 1 + ]; + $this->expectExceptionMessage("Wrong Input in Field 'Domain'"); + $json_result = Domains::getLocal($admin_userdata, $data)->add(); + } + + /** + * @depends testAdminDomainsList + */ + public function testAdminDomainsDelete() + { + global $admin_userdata; + $data = [ + 'domainname' => 'test.local', + 'delete_mainsubdomains' => 1 + ]; + $json_result = Domains::getLocal($admin_userdata, $data)->delete(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals('test.local', $result['domain']); + } +} From 601d16b17c7fa1f4c3de923762191a15bdf44cce Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 1 Mar 2018 19:41:57 +0100 Subject: [PATCH 0519/1335] minor fixes in Domains.add; added more tests Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/commands/class.Domains.php | 7 ++- tests/Domains/DomainsTest.php | 60 ++++++++++++++++++++++ 2 files changed, 65 insertions(+), 2 deletions(-) diff --git a/lib/classes/api/commands/class.Domains.php b/lib/classes/api/commands/class.Domains.php index 5db78a46..b4cce7fe 100644 --- a/lib/classes/api/commands/class.Domains.php +++ b/lib/classes/api/commands/class.Domains.php @@ -114,7 +114,7 @@ class Domains extends ApiCommand implements ResourceEntity */ public function add() { - if ($this->isAdmin() && $this->getUserDetail('change_serversettings')) { + if ($this->isAdmin()) { if ($this->getUserDetail('domains_used') < $this->getUserDetail('domains') || $this->getUserDetail('domains') == '-1') { // parameters @@ -325,7 +325,10 @@ class Domains extends ApiCommand implements ResourceEntity } $ipandports = array(); - if (! empty($p_ipandport) && ! is_array($p_ipandports)) { + if (! empty($p_ipandports) && is_numeric($p_ipandports)) { + $p_ipandports = array($p_ipandports); + } + if (! empty($p_ipandports) && ! is_array($p_ipandports)) { $p_ipandports = unserialize($p_ipandports); } diff --git a/tests/Domains/DomainsTest.php b/tests/Domains/DomainsTest.php index a196c7ef..269c5ba4 100644 --- a/tests/Domains/DomainsTest.php +++ b/tests/Domains/DomainsTest.php @@ -37,6 +37,66 @@ class DomainsTest extends TestCase $this->assertEquals('test.local', $result['list'][0]['domain']); } + /** + * @depends testAdminDomainsAdd + */ + public function testResellerDomainsList() + { + global $admin_userdata; + // get reseller + $json_result = Admins::getLocal($admin_userdata, array( + 'loginname' => 'reseller' + ))->get(); + $reseller_userdata = json_decode($json_result, true)['data']; + $reseller_userdata['adminsession'] = 1; + $json_result = Domains::getLocal($reseller_userdata)->list(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals(0, $result['count']); + } + + public function testResellerDomainsAddWithCanEditPhpSettingsDefaultIp() + { + global $admin_userdata; + // get reseller + $json_result = Admins::getLocal($admin_userdata, array( + 'loginname' => 'reseller' + ))->get(); + $reseller_userdata = json_decode($json_result, true)['data']; + $reseller_userdata['adminsession'] = 1; + $reseller_userdata['caneditphpsettings'] = 1; + $data = [ + 'domain' => 'test2.local', + 'customerid' => 1 + ]; + // the reseller is not allowed to use the default ip/port + $this->expectExceptionMessage("The ip/port combination you have chosen doesn't exist."); + Domains::getLocal($reseller_userdata, $data)->add(); + } + + public function testResellerDomainsAddWithCanEditPhpSettingsAllowedIp() + { + global $admin_userdata; + // first, allow reseller access to ip #3 + Admins::getLocal($admin_userdata, array( + 'loginname' => 'reseller', + 'ipaddress' => 3 + ))->update(); + // get reseller + $json_result = Admins::getLocal($admin_userdata, array( + 'loginname' => 'reseller' + ))->get(); + $reseller_userdata = json_decode($json_result, true)['data']; + $reseller_userdata['adminsession'] = 1; + $data = [ + 'domain' => 'test2.local', + 'customerid' => 1, + 'ipandport' => 3 + ]; + $json_result = Domains::getLocal($reseller_userdata, $data)->add(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals('test2.local', $result['domain']); + } + public function testAdminDomainsAddSysHostname() { global $admin_userdata; From aeb8655cc3f00075a2296486051d18186d7ac542 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 1 Mar 2018 19:59:27 +0100 Subject: [PATCH 0520/1335] fix sql query in Domains.get; minor fixes in Domains.update; first unit-test for Domains.update Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/commands/class.Domains.php | 19 +++++++++++++++++-- tests/Domains/DomainsTest.php | 17 ++++++++++++++++- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/lib/classes/api/commands/class.Domains.php b/lib/classes/api/commands/class.Domains.php index b4cce7fe..89596c96 100644 --- a/lib/classes/api/commands/class.Domains.php +++ b/lib/classes/api/commands/class.Domains.php @@ -87,7 +87,7 @@ class Domains extends ApiCommand implements ResourceEntity FROM `" . TABLE_PANEL_DOMAINS . "` `d` LEFT JOIN `" . TABLE_PANEL_CUSTOMERS . "` `c` USING(`customerid`) WHERE `d`.`parentdomainid` = '0' - AND " . ($id > 0 ? "`d`.`id` = :iddn" : "`d`.`domain` = :iddn") . ($no_std_subdomain ? ' AND `d.`id` <> `c`.`standardsubdomain`' : '') . ($this->getUserDetail('customers_see_all') ? '' : " AND `d`.`adminid` = :adminid")); + AND " . ($id > 0 ? "`d`.`id` = :iddn" : "`d`.`domain` = :iddn") . ($no_std_subdomain ? ' AND `d`.`id` <> `c`.`standardsubdomain`' : '') . ($this->getUserDetail('customers_see_all') ? '' : " AND `d`.`adminid` = :adminid")); $params = array( 'iddn' => ($id <= 0 ? $domainname : $id) ); @@ -818,7 +818,7 @@ class Domains extends ApiCommand implements ResourceEntity $letsencrypt = $this->getParam('letsencrypt', true, $result['letsencrypt']); $p_ssl_ipandports = $this->getParam('ssl_ipandport', true, array()); $http2 = $this->getParam('http2', true, $result['http2']); - $hsts_maxage = $this->getParam('hsts_maxage', true, $result['hsts_maxage']); + $hsts_maxage = $this->getParam('hsts_maxage', true, $result['hsts']); $hsts_sub = $this->getParam('hsts_sub', true, $result['hsts_sub']); $hsts_preload = $this->getParam('hsts_preload', true, $result['hsts_preload']); $ocsp_stapling = $this->getParam('ocsp_stapling', true, $result['ocsp_stapling']); @@ -985,6 +985,9 @@ class Domains extends ApiCommand implements ResourceEntity } $ipandports = array(); + if (! empty($p_ipandports) && is_numeric($p_ipandports)) { + $p_ipandports = array($p_ipandports); + } if (! empty($p_ipandports) && ! is_array($p_ipandports)) { $p_ipandports = unserialize($p_ipandports); } @@ -1007,6 +1010,18 @@ class Domains extends ApiCommand implements ResourceEntity $ipandports[] = $ipandport; } } + } else { + // set currently used ip's + $ipsresult_stmt = Database::prepare(" + SELECT `id_ipandports` FROM `" . TABLE_DOMAINTOIP . "` WHERE `id_domain` = :id + "); + Database::pexecute($ipsresult_stmt, array( + 'id' => $result['id'] + )); + $usedips = array(); + while ($ipsresultrow = $ipsresult_stmt->fetch(PDO::FETCH_ASSOC)) { + $ipandports[] = $ipsresultrow['id_ipandports']; + } } if (Settings::Get('system.use_ssl') == '1' && ! empty($p_ssl_ipandports)) { diff --git a/tests/Domains/DomainsTest.php b/tests/Domains/DomainsTest.php index 269c5ba4..825e0af9 100644 --- a/tests/Domains/DomainsTest.php +++ b/tests/Domains/DomainsTest.php @@ -131,7 +131,22 @@ class DomainsTest extends TestCase } /** - * @depends testAdminDomainsList + * @depends testAdminDomainsAdd + */ + public function testAdminDomainsUpdate() + { + global $admin_userdata; + $data = [ + 'domainname' => 'test.local', + 'email_only' => 1 + ]; + $json_result = Domains::getLocal($admin_userdata, $data)->update(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals(1, $result['email_only']); + } + + /** + * @depends testAdminDomainsUpdate */ public function testAdminDomainsDelete() { From 594512404f1fd20e2f7126c4aab5614c6871ca58 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Fri, 2 Mar 2018 15:24:46 +0100 Subject: [PATCH 0521/1335] optimized CustomersTest and DomainsTest; minor fixes in SubDomains-ApiCommand; added more tests for SubDomains-Command Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/commands/class.SubDomains.php | 8 +- tests/Customers/CustomersTest.php | 8 -- tests/Domains/DomainsTest.php | 4 +- tests/SubDomains/SubDomainsTest.php | 114 ++++++++++++++++++ 4 files changed, 120 insertions(+), 14 deletions(-) diff --git a/lib/classes/api/commands/class.SubDomains.php b/lib/classes/api/commands/class.SubDomains.php index 35d8e684..e5dcbd71 100644 --- a/lib/classes/api/commands/class.SubDomains.php +++ b/lib/classes/api/commands/class.SubDomains.php @@ -388,7 +388,7 @@ class SubDomains extends ApiCommand implements ResourceEntity } else { $result_stmt = Database::prepare(" SELECT * FROM `" . TABLE_PANEL_DOMAINS . "` - WHERE " . ($id > 0 ? "`id` = :iddn" : "`domainname` = :iddn")); + WHERE " . ($id > 0 ? "`id` = :iddn" : "`domain` = :iddn")); $params = array( 'iddn' => ($id <= 0 ? $domainname : $id) ); @@ -398,8 +398,8 @@ class SubDomains extends ApiCommand implements ResourceEntity throw new Exception("You cannot access this resource", 405); } $result_stmt = Database::prepare(" - SELECT `id`, `customerid`, `domain`, `documentroot`, `isemaildomain`, `parentdomainid`, `aliasdomain` FROM `" . TABLE_PANEL_DOMAINS . "` - WHERE `customerid`= :customerid AND " . ($id > 0 ? "`id` = :iddn" : "`domainname` = :iddn")); + SELECT `id`, `customerid`, `domain`, `documentroot`, `isemaildomain`, `parentdomainid`, `aliasdomain`, `caneditdomain` FROM `" . TABLE_PANEL_DOMAINS . "` + WHERE `customerid`= :customerid AND " . ($id > 0 ? "`id` = :iddn" : "`domain` = :iddn")); $params = array( 'customerid' => $this->getUserDetail('customerid'), 'iddn' => ($id <= 0 ? $domainname : $id) @@ -427,7 +427,7 @@ class SubDomains extends ApiCommand implements ResourceEntity $customerid = $this->getParam('customerid', true, 0); $loginname = $this->getParam('loginname', true, ''); - if (! empty($customer_id) || ! empty($loginname)) { + if (! empty($customerid) || ! empty($loginname)) { $json_result = Customers::getLocal($this->getUserData(), array( 'id' => $customerid, 'loginname' => $loginname diff --git a/tests/Customers/CustomersTest.php b/tests/Customers/CustomersTest.php index 338c6bef..b87145d1 100644 --- a/tests/Customers/CustomersTest.php +++ b/tests/Customers/CustomersTest.php @@ -44,14 +44,6 @@ class CustomersTest extends TestCase $json_result = Customers::getLocal($admin_userdata, $data)->add(); $result = json_decode($json_result, true)['data']; - $customer_id = $result['customerid']; - - // get customer and check results - $json_result = Customers::getLocal($admin_userdata, array( - 'id' => $customer_id - ))->get(); - $result = json_decode($json_result, true)['data']; - $this->assertEquals(1, $result['customerid']); $this->assertEquals('test@froxlor.org', $result['email']); $this->assertEquals(1337, $result['customernumber']); diff --git a/tests/Domains/DomainsTest.php b/tests/Domains/DomainsTest.php index 825e0af9..8cc00c4b 100644 --- a/tests/Domains/DomainsTest.php +++ b/tests/Domains/DomainsTest.php @@ -116,7 +116,7 @@ class DomainsTest extends TestCase 'customerid' => 1 ]; $this->expectExceptionMessage('You must not specify punycode (IDNA). The domain will automatically be converted'); - $json_result = Domains::getLocal($admin_userdata, $data)->add(); + Domains::getLocal($admin_userdata, $data)->add(); } public function testAdminDomainsAddInvalidDomain() @@ -127,7 +127,7 @@ class DomainsTest extends TestCase 'customerid' => 1 ]; $this->expectExceptionMessage("Wrong Input in Field 'Domain'"); - $json_result = Domains::getLocal($admin_userdata, $data)->add(); + Domains::getLocal($admin_userdata, $data)->add(); } /** diff --git a/tests/SubDomains/SubDomainsTest.php b/tests/SubDomains/SubDomainsTest.php index 6388dc21..26e0a542 100644 --- a/tests/SubDomains/SubDomainsTest.php +++ b/tests/SubDomains/SubDomainsTest.php @@ -8,6 +8,45 @@ use PHPUnit\Framework\TestCase; */ class SubDomainsTest extends TestCase { + public function testCustomerSubDomainsAdd() + { + global $admin_userdata; + + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + + $data = [ + 'subdomain' => 'mysub', + 'domain' => 'test2.local' + ]; + $json_result = SubDomains::getLocal($customer_userdata, $data)->add(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals('mysub.test2.local', $result['domain']); + } + + public function testResellerSubDomainsAdd() + { + global $admin_userdata; + // get reseller + $json_result = Admins::getLocal($admin_userdata, array( + 'loginname' => 'reseller' + ))->get(); + $reseller_userdata = json_decode($json_result, true)['data']; + $reseller_userdata['adminsession'] = 1; + + $data = [ + 'subdomain' => 'mysub2', + 'domain' => 'test2.local', + 'customer_id' => 1 + ]; + $json_result = SubDomains::getLocal($reseller_userdata, $data)->add(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals('mysub2.test2.local', $result['domain']); + } + public function testCustomerSubDomainsAddNoPunycode() { global $admin_userdata; @@ -61,4 +100,79 @@ class SubDomainsTest extends TestCase $this->expectExceptionMessage("Wrong Input in Field 'Domain'"); SubDomains::getLocal($customer_userdata, $data)->add(); } + + /** + * @depends testCustomerSubDomainsAdd + */ + public function testAdminSubDomainsGet() + { + global $admin_userdata; + + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + + $data = [ + 'domainname' => 'mysub.test2.local' + ]; + $json_result = SubDomains::getLocal($admin_userdata, $data)->get(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals('mysub.test2.local', $result['domain']); + $this->assertEquals(1, $result['customerid']); + } + + public function testCustomerSubDomainsList() + { + global $admin_userdata; + + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + $json_result = SubDomains::getLocal($customer_userdata)->list(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals(3, $result['count']); + } + + public function testResellerSubDomainsList() + { + global $admin_userdata; + // get reseller + $json_result = Admins::getLocal($admin_userdata, array( + 'loginname' => 'reseller' + ))->get(); + $reseller_userdata = json_decode($json_result, true)['data']; + $reseller_userdata['adminsession'] = 1; + $json_result = SubDomains::getLocal($reseller_userdata)->list(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals(3, $result['count']); + } + + public function testAdminSubDomainsListWithCustomer() + { + global $admin_userdata; + $json_result = SubDomains::getLocal($admin_userdata, ['loginname' => 'test1'])->list(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals(3, $result['count']); + } + + /** + * @depends testCustomerSubDomainsList + */ + public function testCustomerSubDomainsDelete() + { + global $admin_userdata; + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + $json_result = SubDomains::getLocal($customer_userdata, ['domainname' => 'mysub.test2.local'])->delete(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals('mysub.test2.local', $result['domain']); + $this->assertEquals($customer_userdata['customerid'], $result['customerid']); + } } From cadb6618ec2b8da2f2a194090e3aa9588defa1c4 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Fri, 2 Mar 2018 17:22:47 +0100 Subject: [PATCH 0522/1335] list() is a reserved php keyword, changed ApiCommand::list() to ApiCommand::listing() Signed-off-by: Michael Kaufmann (d00p) --- admin_phpsettings.php | 4 ++-- lib/classes/api/commands/class.Admins.php | 2 +- lib/classes/api/commands/class.Customers.php | 2 +- lib/classes/api/commands/class.Domains.php | 2 +- lib/classes/api/commands/class.FpmDaemons.php | 2 +- lib/classes/api/commands/class.Ftps.php | 6 +++--- lib/classes/api/commands/class.IpsAndPorts.php | 2 +- lib/classes/api/commands/class.Mysqls.php | 6 +++--- lib/classes/api/commands/class.PhpSettings.php | 2 +- lib/classes/api/commands/class.SubDomains.php | 6 +++--- lib/classes/api/interface.ResourceEntity.php | 2 +- phpdox.xml | 4 ++-- tests/Admins/AdminsTest.php | 4 ++-- tests/Customers/CustomersTest.php | 6 +++--- tests/Domains/DomainsTest.php | 4 ++-- tests/Ftps/FtpsTest.php | 8 ++++---- tests/IpsAndPorts/IpsAndPortsTest.php | 6 +++--- tests/SubDomains/SubDomainsTest.php | 6 +++--- 18 files changed, 37 insertions(+), 37 deletions(-) diff --git a/admin_phpsettings.php b/admin_phpsettings.php index 6eee297a..6192fb42 100644 --- a/admin_phpsettings.php +++ b/admin_phpsettings.php @@ -30,7 +30,7 @@ if ($page == 'overview') { if ($action == '') { try { - $json_result = PhpSettings::getLocal($userinfo)->list(); + $json_result = PhpSettings::getLocal($userinfo)->listing(); } catch (Exception $e) { dynamic_error($e->getMessage()); } @@ -173,7 +173,7 @@ if ($page == 'overview') { if ($action == '') { try { - $json_result = FpmDaemons::getLocal($userinfo)->list(); + $json_result = FpmDaemons::getLocal($userinfo)->listing(); } catch (Exception $e) { dynamic_error($e->getMessage()); } diff --git a/lib/classes/api/commands/class.Admins.php b/lib/classes/api/commands/class.Admins.php index 11361603..6a6b8300 100644 --- a/lib/classes/api/commands/class.Admins.php +++ b/lib/classes/api/commands/class.Admins.php @@ -25,7 +25,7 @@ class Admins extends ApiCommand implements ResourceEntity * @throws Exception * @return array count|list */ - public function list() + public function listing() { if ($this->isAdmin() && $this->getUserDetail('change_serversettings') == 1) { $this->logger()->logAction(ADM_ACTION, LOG_NOTICE, "[API] list admins"); diff --git a/lib/classes/api/commands/class.Customers.php b/lib/classes/api/commands/class.Customers.php index d2a102af..4191a835 100644 --- a/lib/classes/api/commands/class.Customers.php +++ b/lib/classes/api/commands/class.Customers.php @@ -24,7 +24,7 @@ class Customers extends ApiCommand implements ResourceEntity * @access admin * @return array count|list */ - public function list() + public function listing() { if ($this->isAdmin()) { $this->logger()->logAction(ADM_ACTION, LOG_NOTICE, "[API] list customers"); diff --git a/lib/classes/api/commands/class.Domains.php b/lib/classes/api/commands/class.Domains.php index 89596c96..3ea17c58 100644 --- a/lib/classes/api/commands/class.Domains.php +++ b/lib/classes/api/commands/class.Domains.php @@ -25,7 +25,7 @@ class Domains extends ApiCommand implements ResourceEntity * @throws Exception * @return array count|list */ - public function list() + public function listing() { if ($this->isAdmin()) { $this->logger()->logAction(ADM_ACTION, LOG_NOTICE, "[API] list domains"); diff --git a/lib/classes/api/commands/class.FpmDaemons.php b/lib/classes/api/commands/class.FpmDaemons.php index fd7b30fa..cd04990d 100644 --- a/lib/classes/api/commands/class.FpmDaemons.php +++ b/lib/classes/api/commands/class.FpmDaemons.php @@ -25,7 +25,7 @@ class FpmDaemons extends ApiCommand implements ResourceEntity * @throws Exception * @return array count|list */ - public function list() + public function listing() { if ($this->isAdmin()) { $this->logger()->logAction(ADM_ACTION, LOG_NOTICE, "[API] list fpm-daemons"); diff --git a/lib/classes/api/commands/class.Ftps.php b/lib/classes/api/commands/class.Ftps.php index 42cf0705..f2909440 100644 --- a/lib/classes/api/commands/class.Ftps.php +++ b/lib/classes/api/commands/class.Ftps.php @@ -289,7 +289,7 @@ class Ftps extends ApiCommand implements ResourceEntity if ($this->getUserDetail('customers_see_all') == false) { // if it's a reseller or an admin who cannot see all customers, we need to check // whether the database belongs to one of his customers - $json_result = Customers::getLocal($this->getUserData())->list(); + $json_result = Customers::getLocal($this->getUserData())->listing(); $custom_list_result = json_decode($json_result, true)['data']['list']; $customer_ids = array(); foreach ($custom_list_result as $customer) { @@ -453,7 +453,7 @@ class Ftps extends ApiCommand implements ResourceEntity * @throws Exception * @return array count|list */ - public function list() + public function listing() { if ($this->isAdmin()) { // if we're an admin, list all ftp-users of all the admins customers @@ -470,7 +470,7 @@ class Ftps extends ApiCommand implements ResourceEntity json_decode($json_result, true)['data'] ); } else { - $json_result = Customers::getLocal($this->getUserData())->list(); + $json_result = Customers::getLocal($this->getUserData())->listing(); $custom_list_result = json_decode($json_result, true)['data']['list']; } $customer_ids = array(); diff --git a/lib/classes/api/commands/class.IpsAndPorts.php b/lib/classes/api/commands/class.IpsAndPorts.php index 81976d39..f02ded1c 100644 --- a/lib/classes/api/commands/class.IpsAndPorts.php +++ b/lib/classes/api/commands/class.IpsAndPorts.php @@ -25,7 +25,7 @@ class IpsAndPorts extends ApiCommand implements ResourceEntity * @throws Exception * @return array count|list */ - public function list() + public function listing() { if ($this->isAdmin() && ($this->getUserDetail('change_serversettings') || ! empty($this->getUserDetail('ip')))) { $this->logger()->logAction(ADM_ACTION, LOG_NOTICE, "[API] list ips and ports"); diff --git a/lib/classes/api/commands/class.Mysqls.php b/lib/classes/api/commands/class.Mysqls.php index 8022562d..f783b34d 100644 --- a/lib/classes/api/commands/class.Mysqls.php +++ b/lib/classes/api/commands/class.Mysqls.php @@ -230,7 +230,7 @@ class Mysqls extends ApiCommand implements ResourceEntity if ($this->getUserDetail('customers_see_all') != 1) { // if it's a reseller or an admin who cannot see all customers, we need to check // whether the database belongs to one of his customers - $json_result = Customers::getLocal($this->getUserData())->list(); + $json_result = Customers::getLocal($this->getUserData())->listing(); $custom_list_result = json_decode($json_result, true)['data']['list']; $customer_ids = array(); foreach ($custom_list_result as $customer) { @@ -425,7 +425,7 @@ class Mysqls extends ApiCommand implements ResourceEntity * @throws Exception * @return array count|list */ - public function list() + public function listing() { $result = array(); $dbserver = $this->getParam('mysql_server', true, - 1); @@ -444,7 +444,7 @@ class Mysqls extends ApiCommand implements ResourceEntity json_decode($json_result, true)['data'] ); } else { - $json_result = Customers::getLocal($this->getUserData())->list(); + $json_result = Customers::getLocal($this->getUserData())->listing(); $custom_list_result = json_decode($json_result, true)['data']['list']; } $customer_ids = array(); diff --git a/lib/classes/api/commands/class.PhpSettings.php b/lib/classes/api/commands/class.PhpSettings.php index 6d26a560..cc930714 100644 --- a/lib/classes/api/commands/class.PhpSettings.php +++ b/lib/classes/api/commands/class.PhpSettings.php @@ -25,7 +25,7 @@ class PhpSettings extends ApiCommand implements ResourceEntity * @throws Exception * @return array count|list */ - public function list() + public function listing() { if ($this->isAdmin()) { $this->logger()->logAction(ADM_ACTION, LOG_NOTICE, "[API] list php-configs"); diff --git a/lib/classes/api/commands/class.SubDomains.php b/lib/classes/api/commands/class.SubDomains.php index e5dcbd71..eb1ea0d7 100644 --- a/lib/classes/api/commands/class.SubDomains.php +++ b/lib/classes/api/commands/class.SubDomains.php @@ -367,7 +367,7 @@ class SubDomains extends ApiCommand implements ResourceEntity if ($this->getUserDetail('customers_see_all') != 1) { // if it's a reseller or an admin who cannot see all customers, we need to check // whether the database belongs to one of his customers - $json_result = Customers::getLocal($this->getUserData())->list(); + $json_result = Customers::getLocal($this->getUserData())->listing(); $custom_list_result = json_decode($json_result, true)['data']['list']; $customer_ids = array(); foreach ($custom_list_result as $customer) { @@ -419,7 +419,7 @@ class SubDomains extends ApiCommand implements ResourceEntity throw new Exception("Not available yet.", 501); } - public function list() + public function listing() { if ($this->isAdmin()) { // if we're an admin, list all databases of all the admins customers @@ -436,7 +436,7 @@ class SubDomains extends ApiCommand implements ResourceEntity json_decode($json_result, true)['data'] ); } else { - $json_result = Customers::getLocal($this->getUserData())->list(); + $json_result = Customers::getLocal($this->getUserData())->listing(); $custom_list_result = json_decode($json_result, true)['data']['list']; } $customer_ids = array(); diff --git a/lib/classes/api/interface.ResourceEntity.php b/lib/classes/api/interface.ResourceEntity.php index b10713a8..59d6c398 100644 --- a/lib/classes/api/interface.ResourceEntity.php +++ b/lib/classes/api/interface.ResourceEntity.php @@ -18,7 +18,7 @@ interface ResourceEntity { - public function list(); + public function listing(); public function get(); diff --git a/phpdox.xml b/phpdox.xml index 813dbd4a..a090c9ec 100644 --- a/phpdox.xml +++ b/phpdox.xml @@ -1,6 +1,6 @@ - - + + diff --git a/tests/Admins/AdminsTest.php b/tests/Admins/AdminsTest.php index 15c81e3c..c6a63308 100644 --- a/tests/Admins/AdminsTest.php +++ b/tests/Admins/AdminsTest.php @@ -77,7 +77,7 @@ class AdminsTest extends TestCase { global $admin_userdata; - $json_result = Admins::getLocal($admin_userdata)->list(); + $json_result = Admins::getLocal($admin_userdata)->listing(); $result = json_decode($json_result, true)['data']; $this->assertEquals(2, $result['count']); } @@ -114,7 +114,7 @@ class AdminsTest extends TestCase $this->expectExceptionCode(403); $this->expectExceptionMessage("Not allowed to execute given command."); - Admins::getLocal($reseller_userdata)->list(); + Admins::getLocal($reseller_userdata)->listing(); } public function testAdminAdminsUnlock() diff --git a/tests/Customers/CustomersTest.php b/tests/Customers/CustomersTest.php index b87145d1..3d7592c6 100644 --- a/tests/Customers/CustomersTest.php +++ b/tests/Customers/CustomersTest.php @@ -88,7 +88,7 @@ class CustomersTest extends TestCase { global $admin_userdata; - $json_result = Customers::getLocal($admin_userdata)->list(); + $json_result = Customers::getLocal($admin_userdata)->listing(); $result = json_decode($json_result, true)['data']; $this->assertEquals(1, $result['count']); } @@ -105,7 +105,7 @@ class CustomersTest extends TestCase ))->get(); $reseller_userdata = json_decode($json_result, true)['data']; $reseller_userdata['adminsession'] = 1; - $json_result = Customers::getLocal($reseller_userdata)->list(); + $json_result = Customers::getLocal($reseller_userdata)->listing(); $result = json_decode($json_result, true)['data']; $this->assertEquals(0, $result['count']); } @@ -125,7 +125,7 @@ class CustomersTest extends TestCase $this->expectExceptionCode(403); $this->expectExceptionMessage("Not allowed to execute given command."); - $json_result = Customers::getLocal($customer_userdata)->list(); + $json_result = Customers::getLocal($customer_userdata)->listing(); } /** diff --git a/tests/Domains/DomainsTest.php b/tests/Domains/DomainsTest.php index 8cc00c4b..ffdaf42f 100644 --- a/tests/Domains/DomainsTest.php +++ b/tests/Domains/DomainsTest.php @@ -31,7 +31,7 @@ class DomainsTest extends TestCase public function testAdminDomainsList() { global $admin_userdata; - $json_result = Domains::getLocal($admin_userdata)->list(); + $json_result = Domains::getLocal($admin_userdata)->listing(); $result = json_decode($json_result, true)['data']; $this->assertEquals(1, $result['count']); $this->assertEquals('test.local', $result['list'][0]['domain']); @@ -49,7 +49,7 @@ class DomainsTest extends TestCase ))->get(); $reseller_userdata = json_decode($json_result, true)['data']; $reseller_userdata['adminsession'] = 1; - $json_result = Domains::getLocal($reseller_userdata)->list(); + $json_result = Domains::getLocal($reseller_userdata)->listing(); $result = json_decode($json_result, true)['data']; $this->assertEquals(0, $result['count']); } diff --git a/tests/Ftps/FtpsTest.php b/tests/Ftps/FtpsTest.php index d9d185a3..f4dc2ed2 100644 --- a/tests/Ftps/FtpsTest.php +++ b/tests/Ftps/FtpsTest.php @@ -77,7 +77,7 @@ class FtpsTest extends TestCase { global $admin_userdata; - $json_result = Ftps::getLocal($admin_userdata)->list(); + $json_result = Ftps::getLocal($admin_userdata)->listing(); $result = json_decode($json_result, true)['data']; $this->assertEquals(1, $result['count']); } @@ -86,7 +86,7 @@ class FtpsTest extends TestCase { global $admin_userdata; - $json_result = Ftps::getLocal($admin_userdata, array('loginname' => 'test1'))->list(); + $json_result = Ftps::getLocal($admin_userdata, array('loginname' => 'test1'))->listing(); $result = json_decode($json_result, true)['data']; $this->assertEquals(1, $result['count']); $this->assertEquals('test1', $result['list'][0]['username']); @@ -101,7 +101,7 @@ class FtpsTest extends TestCase ))->get(); $reseller_userdata = json_decode($json_result, true)['data']; $reseller_userdata['adminsession'] = 1; - $json_result = Ftps::getLocal($reseller_userdata)->list(); + $json_result = Ftps::getLocal($reseller_userdata)->listing(); $result = json_decode($json_result, true)['data']; $this->assertEquals(1, $result['count']); $this->assertEquals('test1', $result['list'][0]['username']); @@ -115,7 +115,7 @@ class FtpsTest extends TestCase 'loginname' => 'test1' ))->get(); $customer_userdata = json_decode($json_result, true)['data']; - $json_result = Ftps::getLocal($customer_userdata)->list(); + $json_result = Ftps::getLocal($customer_userdata)->listing(); $result = json_decode($json_result, true)['data']; $this->assertEquals(1, $result['count']); $this->assertEquals('test1', $result['list'][0]['username']); diff --git a/tests/IpsAndPorts/IpsAndPortsTest.php b/tests/IpsAndPorts/IpsAndPortsTest.php index 95590a17..9b3ec579 100644 --- a/tests/IpsAndPorts/IpsAndPortsTest.php +++ b/tests/IpsAndPorts/IpsAndPortsTest.php @@ -11,7 +11,7 @@ class IpsAndPortsTest extends TestCase public function testAdminIpsAndPortsList() { global $admin_userdata; - $json_result = IpsAndPorts::getLocal($admin_userdata)->list(); + $json_result = IpsAndPorts::getLocal($admin_userdata)->listing(); $result = json_decode($json_result, true)['data']; $this->assertEquals(1, $result['count']); $this->assertEquals('82.149.225.46', $result['list'][0]['ip']); @@ -29,7 +29,7 @@ class IpsAndPortsTest extends TestCase $reseller_userdata['adminsession'] = 1; $this->expectExceptionCode(403); $this->expectExceptionMessage("Not allowed to execute given command."); - $json_result = IpsAndPorts::getLocal($reseller_userdata)->list(); + $json_result = IpsAndPorts::getLocal($reseller_userdata)->listing(); } public function testAdminIpsAndPortsAdd() @@ -91,7 +91,7 @@ class IpsAndPortsTest extends TestCase ))->update(); $reseller_userdata = json_decode($json_result, true)['data']; $reseller_userdata['adminsession'] = 1; - $json_result = IpsAndPorts::getLocal($reseller_userdata)->list(); + $json_result = IpsAndPorts::getLocal($reseller_userdata)->listing(); $result = json_decode($json_result, true)['data']; $this->assertEquals(1, $result['count']); $this->assertEquals('82.149.225.47', $result['list'][0]['ip']); diff --git a/tests/SubDomains/SubDomainsTest.php b/tests/SubDomains/SubDomainsTest.php index 26e0a542..1e77e43a 100644 --- a/tests/SubDomains/SubDomainsTest.php +++ b/tests/SubDomains/SubDomainsTest.php @@ -132,7 +132,7 @@ class SubDomainsTest extends TestCase 'loginname' => 'test1' ))->get(); $customer_userdata = json_decode($json_result, true)['data']; - $json_result = SubDomains::getLocal($customer_userdata)->list(); + $json_result = SubDomains::getLocal($customer_userdata)->listing(); $result = json_decode($json_result, true)['data']; $this->assertEquals(3, $result['count']); } @@ -146,7 +146,7 @@ class SubDomainsTest extends TestCase ))->get(); $reseller_userdata = json_decode($json_result, true)['data']; $reseller_userdata['adminsession'] = 1; - $json_result = SubDomains::getLocal($reseller_userdata)->list(); + $json_result = SubDomains::getLocal($reseller_userdata)->listing(); $result = json_decode($json_result, true)['data']; $this->assertEquals(3, $result['count']); } @@ -154,7 +154,7 @@ class SubDomainsTest extends TestCase public function testAdminSubDomainsListWithCustomer() { global $admin_userdata; - $json_result = SubDomains::getLocal($admin_userdata, ['loginname' => 'test1'])->list(); + $json_result = SubDomains::getLocal($admin_userdata, ['loginname' => 'test1'])->listing(); $result = json_decode($json_result, true)['data']; $this->assertEquals(3, $result['count']); } From abb94c9189186c4729b15d85c8ca830724f557ac Mon Sep 17 00:00:00 2001 From: JB1985 Date: Fri, 2 Mar 2018 17:28:30 +0100 Subject: [PATCH 0523/1335] Update german.lng.php MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Übersetzung Änderung von Lets Encrypt --- lng/german.lng.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lng/german.lng.php b/lng/german.lng.php index 0924c1ac..d5b1ef2e 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -1590,7 +1590,7 @@ $lng['admin']['mod_fcgid_umask']['title'] = 'Umask (Standard: 022)'; // Added for let's encrypt $lng['admin']['letsencrypt']['title'] = 'SSL Zertifikat erstellen (Let\'s Encrypt)'; $lng['admin']['letsencrypt']['description'] = 'Holt ein kostenloses Zertifikat von Let\'s Encrypt. Das Zertifikat wird automatisch erstellt und verlängert.
    ACHTUNG: Wenn Wildcards aktiviert sind, wird diese Option automatisch deaktiviert. Dieses Feature befindet sich noch im Test.'; -$lng['customer']['letsencrypt']['title'] = 'Benutze Let\'s Encrypt'; +$lng['customer']['letsencrypt']['title'] = 'SSL Zertifikat erstellen (Let\'s Encrypt)'; $lng['customer']['letsencrypt']['description'] = 'Holt ein kostenloses Zertifikat von Let\'s Encrypt. Das Zertifikat wird automatisch erstellt und verlängert.
    ACHTUNG: Dieses Feature befindet sich noch im Test.'; $lng['error']['sslredirectonlypossiblewithsslipport'] = 'Die Nutzung von Let\'s Encrypt ist nur möglich, wenn die Domain mindestens eine IP/Port - Kombination mit aktiviertem SSL zugewiesen hat.'; $lng['error']['nowildcardwithletsencrypt'] = 'Let\'s Encrypt kann in ACME v1 nicht mit Wildcard-Domains umgehen. Bitte den ServerAlias auf WWW setzen oder deaktivieren'; From 1605d2af07ebd1003b29035ec967331a3cd06e1d Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Fri, 2 Mar 2018 18:25:06 +0100 Subject: [PATCH 0524/1335] enhance phpdox config Signed-off-by: Michael Kaufmann (d00p) --- build.xml | 2 +- phpdox.xml | 28 +++++++++++++++++++++++++--- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/build.xml b/build.xml index 10713128..25d8b2ab 100644 --- a/build.xml +++ b/build.xml @@ -98,7 +98,7 @@ - + diff --git a/phpdox.xml b/phpdox.xml index a090c9ec..d191037d 100644 --- a/phpdox.xml +++ b/phpdox.xml @@ -1,10 +1,32 @@ - - + + - + + + + + + + + + + + + + + + + + + + From 20eaa7bc08b953cdd22af4aa320c54cefb4079ea Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Fri, 2 Mar 2018 19:36:56 +0100 Subject: [PATCH 0525/1335] fix missing sql-prepared-statement parameter, fixes #528 Signed-off-by: Michael Kaufmann (d00p) --- scripts/jobs/cron_letsencrypt.php | 1 + scripts/jobs/cron_letsencrypt_v2.php | 1 + 2 files changed, 2 insertions(+) diff --git a/scripts/jobs/cron_letsencrypt.php b/scripts/jobs/cron_letsencrypt.php index 98ee24ce..acfceae0 100644 --- a/scripts/jobs/cron_letsencrypt.php +++ b/scripts/jobs/cron_letsencrypt.php @@ -260,6 +260,7 @@ foreach ($certrows as $certrow) { 'ca' => $return['chain'], 'chain' => $return['chain'], 'csr' => $return['csr'], + 'fullchain' => $return['fullchain'], 'expirationdate' => date('Y-m-d H:i:s', $newcert['validTo_time_t']) )); diff --git a/scripts/jobs/cron_letsencrypt_v2.php b/scripts/jobs/cron_letsencrypt_v2.php index 6e27361b..c56a3e4e 100644 --- a/scripts/jobs/cron_letsencrypt_v2.php +++ b/scripts/jobs/cron_letsencrypt_v2.php @@ -264,6 +264,7 @@ foreach ($certrows as $certrow) { 'ca' => $return['chain'], 'chain' => $return['chain'], 'csr' => $return['csr'], + 'fullchain' => $return['fullchain'], 'expirationdate' => date('Y-m-d H:i:s', $newcert['validTo_time_t']) )); From 7e81b0bb5a0720f3a6450d87629a5411c7d3149f Mon Sep 17 00:00:00 2001 From: Michael Zangl Date: Sat, 3 Mar 2018 11:18:28 +0100 Subject: [PATCH 0526/1335] Froxlor installer: Use same file path for chmod. --- install/lib/class.FroxlorInstall.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/install/lib/class.FroxlorInstall.php b/install/lib/class.FroxlorInstall.php index 128a7104..1cbbd7ca 100644 --- a/install/lib/class.FroxlorInstall.php +++ b/install/lib/class.FroxlorInstall.php @@ -322,11 +322,12 @@ class FroxlorInstall $userdata .= "?>"; // test if we can store the userdata.inc.php in ../lib - if ($fp = @fopen(dirname(dirname(dirname(__FILE__))) . '/lib/userdata.inc.php', 'w')) { + $userdata_file = dirname(dirname(dirname(__FILE__))) . '/lib/userdata.inc.php'; + if ($fp = @fopen($userdata_file, 'w')) { $result = @fputs($fp, $userdata, strlen($userdata)); @fclose($fp); $content .= $this->_status_message('green', 'OK'); - chmod('../lib/userdata.inc.php', 0440); + chmod($userdata_file, 0440); } elseif ($fp = @fopen('/tmp/userdata.inc.php', 'w')) { $result = @fputs($fp, $userdata, strlen($userdata)); @fclose($fp); From 8642254175096d73b80edbf27774c673ee1f11f0 Mon Sep 17 00:00:00 2001 From: Michael Zangl Date: Sat, 3 Mar 2018 11:48:26 +0100 Subject: [PATCH 0527/1335] Fix invalidhostname error message. --- lng/english.lng.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lng/english.lng.php b/lng/english.lng.php index a5d33367..fe156bf9 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -1648,7 +1648,7 @@ $lng['admin']['usedmax'] = 'Used / Max'; $lng['admin']['used'] = 'Used'; $lng['mysql']['size'] = 'Size'; -$lng['error']['invalidhostname'] = 'Hostname can\'t be empty nor can it consist only of whitespaces'; +$lng['error']['invalidhostname'] = 'Hostname needs to be avalid domain. It can\'t be empty nor can it consist only of whitespaces'; $lng['traffic']['http'] = 'HTTP (MiB)'; $lng['traffic']['ftp'] = 'FTP (MiB)'; From 909c983aec4ce6b89d06014cbe36abaeec5490ae Mon Sep 17 00:00:00 2001 From: Michael Zangl Date: Sat, 3 Mar 2018 11:49:58 +0100 Subject: [PATCH 0528/1335] Fix invalidhostname error message (de). --- lng/german.lng.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lng/german.lng.php b/lng/german.lng.php index 0924c1ac..b2dbe82b 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -1375,7 +1375,7 @@ $lng['admin']['usedmax'] = 'Benutzt / Max.'; $lng['admin']['used'] = 'Benutzt'; $lng['mysql']['size'] = 'Datenbankgröße'; -$lng['error']['invalidhostname'] = 'Hostname darf nicht leer sein oder nur aus Leerzeichen bestehen'; +$lng['error']['invalidhostname'] = 'Hostname muss eine gültige Domain sein. Er darf weder leer sein noch nur aus Leerzeichen bestehen'; $lng['traffic']['http'] = 'HTTP (MiB)'; $lng['traffic']['ftp'] = 'FTP (MiB)'; From 826f1378d2be29e00bad5a44d0f20872b9121092 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sat, 3 Mar 2018 15:55:23 +0100 Subject: [PATCH 0529/1335] minor fixes to Domains.update + another unit-test Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/commands/class.Domains.php | 66 +++++++++++++++++++--- tests/Domains/DomainsTest.php | 21 +++++++ 2 files changed, 79 insertions(+), 8 deletions(-) diff --git a/lib/classes/api/commands/class.Domains.php b/lib/classes/api/commands/class.Domains.php index 3ea17c58..9b8e9c98 100644 --- a/lib/classes/api/commands/class.Domains.php +++ b/lib/classes/api/commands/class.Domains.php @@ -822,18 +822,63 @@ class Domains extends ApiCommand implements ResourceEntity $hsts_sub = $this->getParam('hsts_sub', true, $result['hsts_sub']); $hsts_preload = $this->getParam('hsts_preload', true, $result['hsts_preload']); $ocsp_stapling = $this->getParam('ocsp_stapling', true, $result['ocsp_stapling']); + + // count subdomain usage of source-domain + $subdomains_stmt = Database::prepare(" + SELECT COUNT(`id`) AS count FROM `" . TABLE_PANEL_DOMAINS . "` WHERE + `parentdomainid` = :resultid + "); + $subdomains = Database::pexecute_first($subdomains_stmt, array( + 'resultid' => $result['id'] + ), true, true); + $subdomains = $subdomains['count']; + + // count where this domain is alias domain + $alias_check_stmt = Database::prepare(" + SELECT COUNT(`id`) AS count FROM `" . TABLE_PANEL_DOMAINS . "` WHERE + `aliasdomain` = :resultid + "); + $alias_check = Database::pexecute_first($alias_check_stmt, array( + 'resultid' => $result['id'] + ), true, true); + $alias_check = $alias_check['count']; + // count where we are used in email-accounts + $domain_emails_result_stmt = Database::prepare(" + SELECT `email`, `email_full`, `destination`, `popaccountid` AS `number_email_forwarders` + FROM `" . TABLE_MAIL_VIRTUAL . "` WHERE `customerid` = :customerid AND `domainid` = :id + "); + Database::pexecute($domain_emails_result_stmt, array( + 'customerid' => $result['customerid'], + 'id' => $result['id'] + ), true, true); + + $emails = Database::num_rows(); + $email_forwarders = 0; + $email_accounts = 0; + + while ($domain_emails_row = $domain_emails_result_stmt->fetch(PDO::FETCH_ASSOC)) { + if ($domain_emails_row['destination'] != '') { + $domain_emails_row['destination'] = explode(' ', makeCorrectDestination($domain_emails_row['destination'])); + $email_forwarders += count($domain_emails_row['destination']); + if (in_array($domain_emails_row['email_full'], $domain_emails_row['destination'])) { + $email_forwarders -= 1; + $email_accounts ++; + } + } + } + // handle change of customer (move domain from customer to customer) if ($customerid > 0 && $customerid != $result['customerid'] && Settings::Get('panel.allow_domain_change_customer') == '1') { - + // check whether target customer has enough resources $customer_stmt = Database::prepare(" SELECT * FROM `" . TABLE_PANEL_CUSTOMERS . "` WHERE `customerid` = :customerid AND (`subdomains_used` + :subdomains <= `subdomains` OR `subdomains` = '-1' ) AND (`emails_used` + :emails <= `emails` OR `emails` = '-1' ) AND (`email_forwarders_used` + :forwarders <= `email_forwarders` OR `email_forwarders` = '-1' ) - AND (`email_accounts_used` + :accounts <= `email_accounts` OR `email_accounts` = '-1' ) " . ($this->getUserDetail('customers_see_all') ? '' : " AND `adminid` = :adminid")); - + AND (`email_accounts_used` + :accounts <= `email_accounts` OR `email_accounts` = '-1' ) " . ($this->getUserDetail('customers_see_all') ? '' : " AND `adminid` = :adminid") + ); $params = array( 'customerid' => $customerid, 'subdomains' => $subdomains, @@ -844,13 +889,18 @@ class Domains extends ApiCommand implements ResourceEntity if ($this->getUserDetail('customers_see_all') == '0') { $params['adminid'] = $this->getUserDetail('adminid'); } - - $result = Database::pexecute_first($customer_stmt, $params, true, true); - if (empty($result) || $result['customerid'] != $customerid) { + $customer = Database::pexecute_first($customer_stmt, $params, true, true); + if (empty($customer) || $customer['customerid'] != $customerid) { standard_error('customerdoesntexist', '', true); } } else { $customerid = $result['customerid']; + + // get customer + $json_result = Customers::getLocal($this->getUserData(), array( + 'id' => $customerid + ))->get(); + $customer = json_decode($json_result, true)['data']; } // handle change of admin (move domain from admin to admin) @@ -923,9 +973,9 @@ class Domains extends ApiCommand implements ResourceEntity // If path is empty and 'Use domain name as default value for DocumentRoot path' is enabled in settings, // set default path to subdomain or domain name if (Settings::Get('system.documentroot_use_default_value') == 1) { - $documentroot = makeCorrectDir($result['documentroot'] . '/' . $result['domain']); + $documentroot = makeCorrectDir($customer['documentroot'] . '/' . $result['domain']); } else { - $documentroot = $result['documentroot']; + $documentroot = $customer['documentroot']; } } diff --git a/tests/Domains/DomainsTest.php b/tests/Domains/DomainsTest.php index ffdaf42f..67b0fae8 100644 --- a/tests/Domains/DomainsTest.php +++ b/tests/Domains/DomainsTest.php @@ -148,6 +148,26 @@ class DomainsTest extends TestCase /** * @depends testAdminDomainsUpdate */ + public function testAdminDomainsMoveButUnknownCustomer() + { + global $admin_userdata; + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + $data = [ + 'domainname' => 'test.local', + 'customerid' => $customer_userdata['customerid'] + 1 + ]; + Settings::Set('panel.allow_domain_change_customer', 1); + $this->expectExceptionMessage("The customer you have chosen doesn't exist."); + Domains::getLocal($admin_userdata, $data)->update(); + } + + /** + * @depends testAdminDomainsMoveButUnknownCustomer + */ public function testAdminDomainsDelete() { global $admin_userdata; @@ -159,4 +179,5 @@ class DomainsTest extends TestCase $result = json_decode($json_result, true)['data']; $this->assertEquals('test.local', $result['domain']); } + } From 0d7afc5c24b1ad9943d2174a5ae88d58301f3e65 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sat, 3 Mar 2018 21:10:58 +0100 Subject: [PATCH 0530/1335] fixes in move-domain-to-another-customer functionality in Domains.update Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/commands/class.Domains.php | 23 ++++++++++++---- tests/Domains/DomainsTest.php | 32 +++++++++++++++++++++- 2 files changed, 49 insertions(+), 6 deletions(-) diff --git a/lib/classes/api/commands/class.Domains.php b/lib/classes/api/commands/class.Domains.php index 9b8e9c98..775cc8e6 100644 --- a/lib/classes/api/commands/class.Domains.php +++ b/lib/classes/api/commands/class.Domains.php @@ -201,7 +201,7 @@ class Domains extends ApiCommand implements ResourceEntity if (Settings::Get('system.documentroot_use_default_value') == 1) { $path_suffix = '/' . $domain; } - $documentroot = makeCorrectDir($customer['documentroot'] . $path_suffix); + $_documentroot = makeCorrectDir($customer['documentroot'] . $path_suffix); $registration_date = validate($registration_date, 'registration_date', '/^(19|20)\d\d[-](0[1-9]|1[012])[-](0[1-9]|[12][0-9]|3[01])$/', '', array( '0000-00-00', @@ -236,10 +236,10 @@ class Domains extends ApiCommand implements ResourceEntity // set default path to subdomain or domain name if ($documentroot != '') { if (substr($documentroot, 0, 1) != '/' && ! preg_match('/^https?\:\/\//', $documentroot)) { - $documentroot .= '/' . $documentroot; + $documentroot = $_documentroot . '/' . $documentroot; } - } elseif ($documentroot == '' && Settings::Get('system.documentroot_use_default_value') == 1) { - $documentroot = makeCorrectDir($customer['documentroot'] . '/' . $domain); + } else { + $documentroot = $_documentroot; } } else { $isbinddomain = '0'; @@ -251,6 +251,7 @@ class Domains extends ApiCommand implements ResourceEntity $dkim = '0'; $specialsettings = ''; $notryfiles = '0'; + $documentroot = $_documentroot; } if ($this->getUserDetail('caneditphpsettings') == '1' || $this->getUserDetail('change_serversettings') == '1') { @@ -968,7 +969,19 @@ class Domains extends ApiCommand implements ResourceEntity $specialsettings = validate(str_replace("\r\n", "\n", $specialsettings), 'specialsettings', '/^[^\0]*$/', '', array(), true); $documentroot = validate($documentroot, 'documentroot', '', '', array(), true); - + + // when moving customer and no path is specified, update would normally reuse the current document-root + // which would point to the wrong customer, therefore we will re-create that directory + if (!empty($documentroot) && $customerid > 0 && $customerid != $result['customerid'] && Settings::Get('panel.allow_domain_change_customer') == '1') { + if (Settings::Get('system.documentroot_use_default_value') == 1) { + $_documentroot = makeCorrectDir($customer['documentroot'] . '/' . $result['domain']); + } else { + $_documentroot = $customer['documentroot']; + } + // set the customers default docroot + $documentroot = $_documentroot; + } + if ($documentroot == '') { // If path is empty and 'Use domain name as default value for DocumentRoot path' is enabled in settings, // set default path to subdomain or domain name diff --git a/tests/Domains/DomainsTest.php b/tests/Domains/DomainsTest.php index 67b0fae8..c4ee05b8 100644 --- a/tests/Domains/DomainsTest.php +++ b/tests/Domains/DomainsTest.php @@ -165,8 +165,33 @@ class DomainsTest extends TestCase Domains::getLocal($admin_userdata, $data)->update(); } + public function testAdminDomainsMove() + { + global $admin_userdata; + // add new customer + $data = [ + 'new_loginname' => 'test3', + 'email' => 'test3@froxlor.org', + 'firstname' => 'Test', + 'name' => 'Testman', + 'customernumber' => 1339, + 'new_customer_password' => 'h0lYmo1y' + ]; + $json_result = Customers::getLocal($admin_userdata, $data)->add(); + $customer_userdata = json_decode($json_result, true)['data']; + + $data = [ + 'domainname' => 'test.local', + 'customerid' => $customer_userdata['customerid'] + ]; + $json_result =Domains::getLocal($admin_userdata, $data)->update(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals($customer_userdata['customerid'], $result['customerid']); + $this->assertEquals($customer_userdata['documentroot'].'test.local/', $result['documentroot']); + } + /** - * @depends testAdminDomainsMoveButUnknownCustomer + * @depends testAdminDomainsMove */ public function testAdminDomainsDelete() { @@ -178,6 +203,11 @@ class DomainsTest extends TestCase $json_result = Domains::getLocal($admin_userdata, $data)->delete(); $result = json_decode($json_result, true)['data']; $this->assertEquals('test.local', $result['domain']); + + // remove customer again + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test3' + ))->delete(); } } From f30887e3c030fba89557040e010711ffa7021a95 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sat, 3 Mar 2018 21:26:37 +0100 Subject: [PATCH 0531/1335] removed unused local variables Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/commands/class.Admins.php | 1 - lib/classes/api/commands/class.Customers.php | 3 +-- lib/classes/api/commands/class.Domains.php | 4 ---- lib/classes/api/commands/class.Froxlor.php | 4 ++-- lib/classes/api/commands/class.Ftps.php | 8 +++----- lib/classes/api/commands/class.PhpSettings.php | 2 -- tests/Domains/DomainsTest.php | 18 +++++++++--------- 7 files changed, 15 insertions(+), 25 deletions(-) diff --git a/lib/classes/api/commands/class.Admins.php b/lib/classes/api/commands/class.Admins.php index 6a6b8300..5b61b50c 100644 --- a/lib/classes/api/commands/class.Admins.php +++ b/lib/classes/api/commands/class.Admins.php @@ -103,7 +103,6 @@ class Admins extends ApiCommand implements ResourceEntity $custom_notes = $this->getParam('custom_notes', true, ''); $custom_notes_show = $this->getParam('custom_notes_show', true, 0); $password = $this->getParam('admin_password', true, ''); - $sendpassword = $this->getParam('sendpassword', true, 0); $loginname = $this->getParam('new_loginname', true, ''); $diskspace = $this->getUlParam('diskspace', 'diskspace_ul', true, 0); diff --git a/lib/classes/api/commands/class.Customers.php b/lib/classes/api/commands/class.Customers.php index 4191a835..cf26d8aa 100644 --- a/lib/classes/api/commands/class.Customers.php +++ b/lib/classes/api/commands/class.Customers.php @@ -710,7 +710,6 @@ class Customers extends ApiCommand implements ResourceEntity $mysqls = $this->getUlParam('mysqls', 'mysqls_ul', true, $result['mysqls']); $createstdsubdomain = $this->getParam('createstdsubdomain', true, 0); $password = $this->getParam('new_customer_password', true, ''); - $sendpassword = $this->getParam('sendpassword', true, 0); $phpenabled = $this->getParam('phpenabled', true, $result['phpenabled']); $allowed_phpconfigs = $this->getParam('allowed_phpconfigs', true, json_decode($result['allowed_phpconfigs'], true)); $perlenabled = $this->getParam('perlenabled', true, $result['perlenabled']); @@ -1396,7 +1395,7 @@ class Customers extends ApiCommand implements ResourceEntity if ($tickets !== false && isset($tickets[0])) { foreach ($tickets as $ticket) { $now = time(); - $mainticket = ticket::getInstanceOf($userinfo, (int) $ticket); + $mainticket = ticket::getInstanceOf($result, (int) $ticket); $mainticket->Set('lastchange', $now, true, true); $mainticket->Set('lastreplier', '1', true, true); $mainticket->Set('status', '3', true, true); diff --git a/lib/classes/api/commands/class.Domains.php b/lib/classes/api/commands/class.Domains.php index 775cc8e6..fcd71ea6 100644 --- a/lib/classes/api/commands/class.Domains.php +++ b/lib/classes/api/commands/class.Domains.php @@ -786,7 +786,6 @@ class Domains extends ApiCommand implements ResourceEntity $id = $result['id']; // optional parameters - $p_domain = $this->getParam('domain', true, $result['domain']); $p_ipandports = $this->getParam('ipandport', true, array()); $customerid = intval($this->getParam('customerid', true, $result['customerid'])); $adminid = intval($this->getParam('adminid', true, $result['adminid'])); @@ -1081,15 +1080,12 @@ class Domains extends ApiCommand implements ResourceEntity Database::pexecute($ipsresult_stmt, array( 'id' => $result['id'] )); - $usedips = array(); while ($ipsresultrow = $ipsresult_stmt->fetch(PDO::FETCH_ASSOC)) { $ipandports[] = $ipsresultrow['id_ipandports']; } } if (Settings::Get('system.use_ssl') == '1' && ! empty($p_ssl_ipandports)) { - $ssl = 1; // if ssl is set and != 0, it can only be 1 - $ssl_ipandports = array(); if (! empty($p_ssl_ipandports) && ! is_array($p_ssl_ipandports)) { $p_ssl_ipandports = unserialize($p_ssl_ipandports); diff --git a/lib/classes/api/commands/class.Froxlor.php b/lib/classes/api/commands/class.Froxlor.php index 3e902101..9c68a666 100644 --- a/lib/classes/api/commands/class.Froxlor.php +++ b/lib/classes/api/commands/class.Froxlor.php @@ -63,7 +63,7 @@ class Froxlor extends ApiCommand // anzeige über version-status mit ggfls. formular // zum update schritt #1 -> download if ($isnewerversion == 1) { - $text = 'There is a newer version available: "' . $_version . '" (Your current version is: ' . $this->version . ')'; + $text = 'There is a newer version available: "' . $_version . '" (Your current version is: ' . $version_label . ')'; return $this->response(200, "successfull", array( 'message' => $text, 'link' => $version_link, @@ -217,7 +217,7 @@ class Froxlor extends ApiCommand // create RecursiveIteratorIterator $its = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($path)); // check every file - foreach ($its as $fullFileName => $it) { + foreach ($its as $it) { // does it match the Filename pattern? $matches = array(); if (preg_match("/^class\.(.+)\.php$/i", $it->getFilename(), $matches)) { diff --git a/lib/classes/api/commands/class.Ftps.php b/lib/classes/api/commands/class.Ftps.php index f2909440..00e6057b 100644 --- a/lib/classes/api/commands/class.Ftps.php +++ b/lib/classes/api/commands/class.Ftps.php @@ -117,7 +117,7 @@ class Ftps extends ApiCommand implements ResourceEntity ), true, true); if ($ftpdomain_check && $ftpdomain_check['domain'] != $ftpdomain) { - standard_error('maindomainnonexist', $domain, true); + standard_error('maindomainnonexist', $ftpdomain, true); } $username = $ftpusername . "@" . $ftpdomain; } else { @@ -152,7 +152,6 @@ class Ftps extends ApiCommand implements ResourceEntity "shell" => $shell ); Database::pexecute($stmt, $params, true, true); - $ftp_userid = Database::lastInsertId(); $result_stmt = Database::prepare(" SELECT `bytes_in_used` FROM `" . TABLE_FTP_QUOTATALLIES . "` WHERE `name` = :name @@ -360,8 +359,7 @@ class Ftps extends ApiCommand implements ResourceEntity } else { $shell = "/bin/false"; } - - $params = array(); + // get needed customer info to reduce the ftp-user-counter by one if ($this->isAdmin()) { // get customer id @@ -461,7 +459,7 @@ class Ftps extends ApiCommand implements ResourceEntity $customerid = $this->getParam('customerid', true, 0); $loginname = $this->getParam('loginname', true, ''); - if (! empty($customer_id) || ! empty($loginname)) { + if (! empty($customerid) || ! empty($loginname)) { $json_result = Customers::getLocal($this->getUserData(), array( 'id' => $customerid, 'loginname' => $loginname diff --git a/lib/classes/api/commands/class.PhpSettings.php b/lib/classes/api/commands/class.PhpSettings.php index cc930714..e25a9d09 100644 --- a/lib/classes/api/commands/class.PhpSettings.php +++ b/lib/classes/api/commands/class.PhpSettings.php @@ -39,8 +39,6 @@ class PhpSettings extends ApiCommand implements ResourceEntity $phpconfigs = array(); while ($row = $result->fetch(PDO::FETCH_ASSOC)) { - - $domainresult = false; $query_params = array( 'id' => $row['id'] ); diff --git a/tests/Domains/DomainsTest.php b/tests/Domains/DomainsTest.php index c4ee05b8..d2b05651 100644 --- a/tests/Domains/DomainsTest.php +++ b/tests/Domains/DomainsTest.php @@ -8,6 +8,7 @@ use PHPUnit\Framework\TestCase; */ class DomainsTest extends TestCase { + public function testAdminDomainsAdd() { global $admin_userdata; @@ -22,9 +23,9 @@ class DomainsTest extends TestCase ]; $json_result = Domains::getLocal($admin_userdata, $data)->add(); $result = json_decode($json_result, true)['data']; - $this->assertEquals($customer_userdata['documentroot'].'test.local/', $result['documentroot']); + $this->assertEquals($customer_userdata['documentroot'] . 'test.local/', $result['documentroot']); } - + /** * @depends testAdminDomainsAdd */ @@ -107,7 +108,7 @@ class DomainsTest extends TestCase $this->expectExceptionMessage('The server-hostname cannot be used as customer-domain.'); $json_result = Domains::getLocal($admin_userdata, $data)->add(); } - + public function testAdminDomainsAddNoPunycode() { global $admin_userdata; @@ -129,7 +130,7 @@ class DomainsTest extends TestCase $this->expectExceptionMessage("Wrong Input in Field 'Domain'"); Domains::getLocal($admin_userdata, $data)->add(); } - + /** * @depends testAdminDomainsAdd */ @@ -179,15 +180,15 @@ class DomainsTest extends TestCase ]; $json_result = Customers::getLocal($admin_userdata, $data)->add(); $customer_userdata = json_decode($json_result, true)['data']; - + $data = [ 'domainname' => 'test.local', 'customerid' => $customer_userdata['customerid'] ]; - $json_result =Domains::getLocal($admin_userdata, $data)->update(); + $json_result = Domains::getLocal($admin_userdata, $data)->update(); $result = json_decode($json_result, true)['data']; $this->assertEquals($customer_userdata['customerid'], $result['customerid']); - $this->assertEquals($customer_userdata['documentroot'].'test.local/', $result['documentroot']); + $this->assertEquals($customer_userdata['documentroot'] . 'test.local/', $result['documentroot']); } /** @@ -203,11 +204,10 @@ class DomainsTest extends TestCase $json_result = Domains::getLocal($admin_userdata, $data)->delete(); $result = json_decode($json_result, true)['data']; $this->assertEquals('test.local', $result['domain']); - + // remove customer again $json_result = Customers::getLocal($admin_userdata, array( 'loginname' => 'test3' ))->delete(); } - } From 7cc17b9ca591c36a3bc98197574eb6de3ad0ca56 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sat, 3 Mar 2018 21:29:16 +0100 Subject: [PATCH 0532/1335] fix short variables Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/abstract.ApiCommand.php | 6 +++--- lib/classes/api/commands/class.FpmDaemons.php | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/classes/api/abstract.ApiCommand.php b/lib/classes/api/abstract.ApiCommand.php index 34d62e3b..61243365 100644 --- a/lib/classes/api/abstract.ApiCommand.php +++ b/lib/classes/api/abstract.ApiCommand.php @@ -432,15 +432,15 @@ abstract class ApiCommand * @param string $table * @param string $keyfield * @param int $key - * @param string $op + * @param string $operator * @param string $resource * @param string $extra */ - protected static function updateResourceUsage($table = null, $keyfield = null, $key = null, $op = '+', $resource = null, $extra = null) + protected static function updateResourceUsage($table = null, $keyfield = null, $key = null, $operator = '+', $resource = null, $extra = null) { $stmt = Database::prepare(" UPDATE `" . $table . "` - SET `" . $resource . "` = `" . $resource . "` " . $op . " 1 " . $extra . " + SET `" . $resource . "` = `" . $resource . "` " . $operator . " 1 " . $extra . " WHERE `" . $keyfield . "` = :key "); Database::pexecute($stmt, array( diff --git a/lib/classes/api/commands/class.FpmDaemons.php b/lib/classes/api/commands/class.FpmDaemons.php index cd04990d..b42a0c99 100644 --- a/lib/classes/api/commands/class.FpmDaemons.php +++ b/lib/classes/api/commands/class.FpmDaemons.php @@ -114,7 +114,7 @@ class FpmDaemons extends ApiCommand implements ResourceEntity $config_dir = $this->getParam('config_dir'); // parameters - $pm = $this->getParam('pm', true, 'static'); + $pmanager = $this->getParam('pm', true, 'static'); $max_children = $this->getParam('max_children', true, 0); $start_servers = $this->getParam('start_servers', true, 0); $min_spare_servers = $this->getParam('min_spare_servers', true, 0); @@ -127,7 +127,7 @@ class FpmDaemons extends ApiCommand implements ResourceEntity $description = validate($description, 'description', '', '', array(), true); $reload_cmd = validate($reload_cmd, 'reload_cmd', '', '', array(), true); $config_dir = validate($config_dir, 'config_dir', '', '', array(), true); - if (! in_array($pm, array( + if (! in_array($pmanager, array( 'static', 'dynamic', 'ondemand' @@ -158,7 +158,7 @@ class FpmDaemons extends ApiCommand implements ResourceEntity 'desc' => $description, 'reload_cmd' => $reload_cmd, 'config_dir' => makeCorrectDir($config_dir), - 'pm' => $pm, + 'pm' => $pmanager, 'max_children' => $max_children, 'start_servers' => $start_servers, 'min_spare_servers' => $min_spare_servers, @@ -202,7 +202,7 @@ class FpmDaemons extends ApiCommand implements ResourceEntity $description = $this->getParam('description', true, $result['description']); $reload_cmd = $this->getParam('reload_cmd', true, $result['reload_cmd']); $config_dir = $this->getParam('config_dir', true, $result['config_dir']); - $pm = $this->getParam('pm', true, $result['pm']); + $pmanager = $this->getParam('pm', true, $result['pm']); $max_children = $this->getParam('max_children', true, $result['max_children']); $start_servers = $this->getParam('start_servers', true, $result['start_servers']); $min_spare_servers = $this->getParam('min_spare_servers', true, $result['min_spare_servers']); @@ -215,7 +215,7 @@ class FpmDaemons extends ApiCommand implements ResourceEntity $description = validate($description, 'description', '', '', array(), true); $reload_cmd = validate($reload_cmd, 'reload_cmd', '', '', array(), true); $config_dir = validate($config_dir, 'config_dir', '', '', array(), true); - if (! in_array($pm, array( + if (! in_array($pmanager, array( 'static', 'dynamic', 'ondemand' @@ -247,7 +247,7 @@ class FpmDaemons extends ApiCommand implements ResourceEntity 'desc' => $description, 'reload_cmd' => $reload_cmd, 'config_dir' => makeCorrectDir($config_dir), - 'pm' => $pm, + 'pm' => $pmanager, 'max_children' => $max_children, 'start_servers' => $start_servers, 'min_spare_servers' => $min_spare_servers, From bb3fddb08f7b9b3102a16372357ca09981ae02c5 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sat, 3 Mar 2018 21:39:51 +0100 Subject: [PATCH 0533/1335] optimize phpmd config Signed-off-by: Michael Kaufmann (d00p) --- phpmd.xml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/phpmd.xml b/phpmd.xml index 0f1aae49..a34f8f48 100644 --- a/phpmd.xml +++ b/phpmd.xml @@ -9,10 +9,15 @@ - + + + + + + @@ -21,4 +26,11 @@ + + 1 + + + + + From 702b52d13e00c6c705504695a731dedac7280ed6 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sat, 3 Mar 2018 21:42:34 +0100 Subject: [PATCH 0534/1335] forgot to save again Signed-off-by: Michael Kaufmann (d00p) --- phpmd.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phpmd.xml b/phpmd.xml index a34f8f48..e9e42fd5 100644 --- a/phpmd.xml +++ b/phpmd.xml @@ -29,7 +29,7 @@ 1 - + From 91cecf8c1e739132d9ef060ae68ec6679daeeb18 Mon Sep 17 00:00:00 2001 From: Kai Neuwerth Date: Sun, 4 Mar 2018 11:43:50 +0100 Subject: [PATCH 0535/1335] Link domains to HTTPS in certificate list As an admin I want to check if the certificate of a website is working. Therefore I click on the domain in the SSL certificate list and have to prepend "https" to the URL later. In this case I'd link the domain to HTTPS initially. --- templates/Sparkle/ssl_certificates/certs_cert.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/Sparkle/ssl_certificates/certs_cert.tpl b/templates/Sparkle/ssl_certificates/certs_cert.tpl index 250a57f1..e383f54f 100644 --- a/templates/Sparkle/ssl_certificates/certs_cert.tpl +++ b/templates/Sparkle/ssl_certificates/certs_cert.tpl @@ -1,6 +1,6 @@ class="domain-expired"> - {$row['domain']} + {$row['domain']} {$adminCustomerLink} From b07d6ceeaa85cff538a56e1af1947ff46a746fe7 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sun, 4 Mar 2018 12:40:47 +0100 Subject: [PATCH 0536/1335] started UI api-key management Signed-off-by: Michael Kaufmann (d00p) --- admin_index.php | 3 + api_keys.php | 128 +++++++++++++++++++ customer_index.php | 3 + lib/classes/api/commands/class.Admins.php | 34 ++++- lib/classes/api/commands/class.Customers.php | 24 +++- lib/navigation/00.froxlor.main.php | 10 ++ lng/english.lng.php | 3 + lng/german.lng.php | 3 + templates/Sparkle/api_keys/keys_error.tpl | 3 + templates/Sparkle/api_keys/keys_key.tpl | 27 ++++ templates/Sparkle/api_keys/keys_list.tpl | 68 ++++++++++ templates/Sparkle/assets/css/main.css | 4 +- templates/Sparkle/header.tpl | 1 + 13 files changed, 299 insertions(+), 12 deletions(-) create mode 100644 api_keys.php create mode 100644 templates/Sparkle/api_keys/keys_error.tpl create mode 100644 templates/Sparkle/api_keys/keys_key.tpl create mode 100644 templates/Sparkle/api_keys/keys_list.tpl diff --git a/admin_index.php b/admin_index.php index 6efb31ff..8d7f0e48 100644 --- a/admin_index.php +++ b/admin_index.php @@ -393,6 +393,9 @@ if ($page == 'overview') { redirectTo($filename, array('s' => $s)); } } +elseif ($page == 'apikeys' && Settings::Get('api.enabled') == 1) { + require_once __DIR__ . '/api_keys.php'; +} elseif ($page == 'apihelp' && Settings::Get('api.enabled') == 1) { require_once __DIR__ . '/apihelp.php'; } diff --git a/api_keys.php b/api_keys.php new file mode 100644 index 00000000..d04891df --- /dev/null +++ b/api_keys.php @@ -0,0 +1,128 @@ + (2018-) + * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt + * @package Panel + * @since 0.10.0 + * + */ + +// This file is being included in admin_index and customer_index +// and therefore does not need to require lib/init.php + +$log->logAction(USR_ACTION, LOG_NOTICE, "viewed api::api_keys"); + +// select all my (accessable) certificates +$keys_stmt_query = "SELECT ak.*, c.loginname, a.loginname as adminname + FROM `" . TABLE_API_KEYS . "` ak + LEFT JOIN `" . TABLE_PANEL_CUSTOMERS . "` c ON `c`.`customerid` = `ak`.`customerid` + LEFT JOIN `" . TABLE_PANEL_ADMINS . "` a ON `a`.`adminid` = `ak`.`adminid` + WHERE "; + +$qry_params = array(); +if (AREA == 'admin' && $userinfo['customers_see_all'] == '0') { + // admin with only customer-specific permissions + $keys_stmt_query .= "ak.adminid = :adminid "; + $qry_params['adminid'] = $userinfo['adminid']; + $fields = array( + 'a.loginname' => $lng['login']['username'] + ); +} elseif (AREA == 'customer') { + // customer-area + $keys_stmt_query .= "ak.customerid = :cid "; + $qry_params['cid'] = $userinfo['customerid']; + $fields = array( + 'c.loginname' => $lng['login']['username'] + ); +} else { + // admin who can see all customers / reseller / admins + $keys_stmt_query .= "1 "; + $fields = array( + 'a.loginname' => $lng['login']['username'] + ); +} + +$paging = new paging($userinfo, TABLE_API_KEYS, $fields); +$keys_stmt_query .= $paging->getSqlWhere(true) . " " . $paging->getSqlOrderBy() . " " . $paging->getSqlLimit(); + +$keys_stmt = Database::prepare($keys_stmt_query); +Database::pexecute($keys_stmt, $qry_params); +$all_keys = $keys_stmt->fetchAll(PDO::FETCH_ASSOC); +$apikeys = ""; + +if (count($all_keys) == 0) { + $count = 0; + $message = $lng['apikeys']['no_api_keys']; + $sortcode = ""; + $searchcode = ""; + $pagingcode = ""; + eval("\$apikeys.=\"" . getTemplate("api_keys/keys_error", true) . "\";"); +} else { + $count = count($all_keys); + $paging->setEntries($count); + $sortcode = $paging->getHtmlSortCode($lng); + $arrowcode = $paging->getHtmlArrowCode($filename . '?page=' . $page . '&s=' . $s); + $searchcode = $paging->getHtmlSearchCode($lng); + $pagingcode = $paging->getHtmlPagingCode($filename . '?page=' . $page . '&s=' . $s); + + foreach ($all_keys as $idx => $key) { + if ($paging->checkDisplay($idx)) { + + // my own key + $isMyKey = false; + if ($key['adminid'] == $userinfo['adminid'] && (AREA == 'admin' || (AREA == 'customer' && $key['customerid'] == $userinfo['customerid']))) { + // this is mine + $isMyKey = true; + } + + $adminCustomerLink = ""; + if (AREA == 'admin') { + if ($isMyKey) { + $adminCustomerLink = $key['adminname']; + } else { + $adminCustomerLink = ' (' . (empty($key['customerid']) ? $key['adminname'] : $key['loginname']) . ')'; + } + } else { + // customer do not need links + $adminCustomerLink = $key['loginname']; + } + + // escape stuff + $row = htmlentities_array($key); + + // check whether the api key is not valid anymore + $isValid = true; + if ($row['valid_until'] >= 0) { + if ($row['valid_until'] < time()) { + $isValid = false; + } + // format + $row['valid_until'] = date('d.m.Y H:i', $row['valid_until']); + } else { + $row['valid_until'] = "∞"; + } + eval("\$apikeys.=\"" . getTemplate("api_keys/keys_key", true) . "\";"); + } else { + continue; + } + } +} +eval("echo \"" . getTemplate("api_keys/keys_list", true) . "\";"); diff --git a/customer_index.php b/customer_index.php index 85d02158..f1ba35fb 100644 --- a/customer_index.php +++ b/customer_index.php @@ -314,6 +314,9 @@ if ($page == 'overview') { redirectTo($filename, array('s' => $s)); } } +elseif ($page == 'apikeys' && Settings::Get('api.enabled') == 1) { + require_once __DIR__ . '/api_keys.php'; +} elseif ($page == 'apihelp' && Settings::Get('api.enabled') == 1) { require_once __DIR__ . '/apihelp.php'; } diff --git a/lib/classes/api/commands/class.Admins.php b/lib/classes/api/commands/class.Admins.php index 5b61b50c..e100f8ee 100644 --- a/lib/classes/api/commands/class.Admins.php +++ b/lib/classes/api/commands/class.Admins.php @@ -595,27 +595,31 @@ class Admins extends ApiCommand implements ResourceEntity standard_error('youcantdeleteyourself', '', true); } + // delete admin $del_stmt = Database::prepare(" DELETE FROM `" . TABLE_PANEL_ADMINS . "` WHERE `adminid` = :adminid "); Database::pexecute($del_stmt, array( 'adminid' => $id ), true, true); - + + // delete the traffic-usage $del_stmt = Database::prepare(" DELETE FROM `" . TABLE_PANEL_TRAFFIC_ADMINS . "` WHERE `adminid` = :adminid "); Database::pexecute($del_stmt, array( 'adminid' => $id ), true, true); - + + // delete the diskspace usage $del_stmt = Database::prepare(" DELETE FROM `" . TABLE_PANEL_DISKSPACE_ADMINS . "` WHERE `adminid` = :adminid "); Database::pexecute($del_stmt, array( 'adminid' => $id ), true, true); - + + // set admin-id of the old admin's customer to current admins $upd_stmt = Database::prepare(" UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET `adminid` = :userid WHERE `adminid` = :adminid @@ -624,7 +628,8 @@ class Admins extends ApiCommand implements ResourceEntity 'userid' => $this->getUserDetail('adminid'), 'adminid' => $id ), true, true); - + + // set admin-id of the old admin's domains to current admins $upd_stmt = Database::prepare(" UPDATE `" . TABLE_PANEL_DOMAINS . "` SET `adminid` = :userid WHERE `adminid` = :adminid @@ -633,7 +638,26 @@ class Admins extends ApiCommand implements ResourceEntity 'userid' => $this->getUserDetail('adminid'), 'adminid' => $id ), true, true); - + + // delete old admin's api keys if exists (no customer keys) + $upd_stmt = Database::prepare(" + DELETE FROM `" . TABLE_API_KEYS . "` WHERE + `adminid` = :userid AND `customerid` = '0' + "); + Database::pexecute($upd_stmt, array( + 'adminid' => $id + ), true, true); + + // set admin-id of the old admin's api-keys to current admins + $upd_stmt = Database::prepare(" + UPDATE `" . TABLE_API_KEYS . "` SET + `adminid` = :userid WHERE `adminid` = :adminid + "); + Database::pexecute($upd_stmt, array( + 'userid' => $this->getUserDetail('adminid'), + 'adminid' => $id + ), true, true); + $this->logger()->logAction(ADM_ACTION, LOG_WARNING, "[API] deleted admin '" . $result['loginname'] . "'"); updateCounters(); return $this->response(200, "successfull", $result); diff --git a/lib/classes/api/commands/class.Customers.php b/lib/classes/api/commands/class.Customers.php index cf26d8aa..ed1ff384 100644 --- a/lib/classes/api/commands/class.Customers.php +++ b/lib/classes/api/commands/class.Customers.php @@ -857,9 +857,9 @@ class Customers extends ApiCommand implements ResourceEntity // activate/deactivate customer services if ($deactivated != $result['deactivated']) { - $yesno = (($deactivated) ? 'N' : 'Y'); - $pop3 = (($deactivated) ? '0' : (int) $result['pop3']); - $imap = (($deactivated) ? '0' : (int) $result['imap']); + $yesno = ($deactivated ? 'N' : 'Y'); + $pop3 = ($deactivated ? '0' : (int) $result['pop3']); + $imap = ($deactivated ? '0' : (int) $result['imap']); $upd_stmt = Database::prepare(" UPDATE `" . TABLE_MAIL_USERS . "` SET `postfix`= :yesno, `pop3` = :pop3, `imap` = :imap WHERE `customerid` = :customerid @@ -923,8 +923,16 @@ class Customers extends ApiCommand implements ResourceEntity // At last flush the new privileges $dbm->getManager()->flushPrivileges(); Database::needRoot(false); - - $this->logger()->logAction(ADM_ACTION, LOG_INFO, "[API] deactivated user '" . $result['loginname'] . "'"); + + // reactivate/deactivate api-keys + $valid_until = $deactivated ? 0 : - 1; + $stmt = Database::prepare("UPDATE `" . TABLE_API_KEYS . "` SET `valid_until` = :vu WHERE `customerid` = :id"); + Database::pexecute($stmt, array( + 'id' => $id, + 'vu' => $valid_until + ), true, true); + + $this->logger()->logAction(ADM_ACTION, LOG_INFO, "[API] " . ($deactivated ? 'deactivated' : 'reactivated') . " user '" . $result['loginname'] . "'"); inserttask('1'); } @@ -1323,6 +1331,12 @@ class Customers extends ApiCommand implements ResourceEntity 'id' => $id ), true, true); + // remove api-keys + $stmt = Database::prepare("DELETE FROM `" . TABLE_API_KEYS . "` WHERE `customerid` = :id"); + Database::pexecute($stmt, array( + 'id' => $id + ), true, true); + // Delete all waiting "create user" -tasks for this user, #276 // Note: the WHERE selects part of a serialized array, but it should be safe this way $del_stmt = Database::prepare(" diff --git a/lib/navigation/00.froxlor.main.php b/lib/navigation/00.froxlor.main.php index 61c7fe6f..8775effd 100644 --- a/lib/navigation/00.froxlor.main.php +++ b/lib/navigation/00.froxlor.main.php @@ -38,6 +38,11 @@ return array( 'label' => $lng['menue']['main']['changetheme'], 'show_element' => (Settings::Get('panel.allow_theme_change_customer') == true) ), + array( + 'url' => 'customer_index.php?page=apikeys', + 'label' => $lng['menue']['main']['apikeys'], + 'show_element' => (Settings::Get('api.enabled') == true) + ), array( 'url' => 'customer_index.php?page=apihelp', 'label' => $lng['menue']['main']['apihelp'], @@ -184,6 +189,11 @@ return array( 'label' => $lng['menue']['main']['changetheme'], 'show_element' => (Settings::Get('panel.allow_theme_change_admin') == true) ), + array( + 'url' => 'admin_index.php?page=apikeys', + 'label' => $lng['menue']['main']['apikeys'], + 'show_element' => (Settings::Get('api.enabled') == true) + ), array( 'url' => 'admin_index.php?page=apihelp', 'label' => $lng['menue']['main']['apihelp'], diff --git a/lng/english.lng.php b/lng/english.lng.php index 264ba8c3..e0c4e59f 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -2123,3 +2123,6 @@ $lng['admin']['notryfiles']['description'] = 'Say yes here if you want to specif // added in froxlor 0.10.0 $lng['panel']['none_value'] = 'None'; $lng['menue']['main']['apihelp'] = 'API help'; +$lng['menue']['main']['apikeys'] = 'API keys'; +$lng['apikeys']['no_api_keys'] = 'No API keys found'; +$lng['apikeys']['key_add'] = 'Add new key'; diff --git a/lng/german.lng.php b/lng/german.lng.php index 06fac9f6..b6507e87 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -1773,3 +1773,6 @@ $lng['admin']['notryfiles']['description'] = 'Wähle "Ja", wenn eine eigene try_ // added in froxlor 0.10.0 $lng['panel']['none_value'] = 'Keine'; $lng['menue']['main']['apihelp'] = 'API Hilfe'; +$lng['menue']['main']['apikeys'] = 'API Keys'; +$lng['apikeys']['no_api_keys'] = 'Keine API Keys gefunden'; +$lng['apikeys']['key_add'] = 'API Key hinzufügen'; diff --git a/templates/Sparkle/api_keys/keys_error.tpl b/templates/Sparkle/api_keys/keys_error.tpl new file mode 100644 index 00000000..e573c664 --- /dev/null +++ b/templates/Sparkle/api_keys/keys_error.tpl @@ -0,0 +1,3 @@ + + {$message} + diff --git a/templates/Sparkle/api_keys/keys_key.tpl b/templates/Sparkle/api_keys/keys_key.tpl new file mode 100644 index 00000000..12eb43a2 --- /dev/null +++ b/templates/Sparkle/api_keys/keys_key.tpl @@ -0,0 +1,27 @@ +class="primary-entry"> + + {$adminCustomerLink} + + + {$row['apikey']} + + + {$row['secret']} + + + {$row['allowed_from']} + + + + {$row['valid_until']} + + + + + {$lng['panel']['edit']} +   + + {$lng['panel']['delete']} + + + diff --git a/templates/Sparkle/api_keys/keys_list.tpl b/templates/Sparkle/api_keys/keys_list.tpl new file mode 100644 index 00000000..11671ad7 --- /dev/null +++ b/templates/Sparkle/api_keys/keys_list.tpl @@ -0,0 +1,68 @@ + $header +
    +
    +

    +   + {$lng['menue']['main']['apikeys']} +

    +
    + + +
    +
    {$lng['success']['success']}
    +
    + $success_message +
    +
    +
    + +
    + +
    + + + +
    + {$searchcode} +
    + + + + + + + + + + + + + + + + + + + + + + + + + {$apikeys} + +
    UserAPI-keysSecretAllowed fromValid until{$lng['panel']['options']}
    {$pagingcode}
    +
    + + + + +
    +
    +$footer diff --git a/templates/Sparkle/assets/css/main.css b/templates/Sparkle/assets/css/main.css index 5e61b727..85e0dfb0 100644 --- a/templates/Sparkle/assets/css/main.css +++ b/templates/Sparkle/assets/css/main.css @@ -1682,13 +1682,13 @@ fieldset.file { background-color: rgb(242, 222, 222); } -.domain-hostname { +.primary-entry { background-color: rgb(53, 106, 160); color: #ddd; font-weight: bold; } -table.hl tbody tr.domain-hostname:hover { +table.hl tbody tr.primary-entry:hover { background-color: rgb(64, 150, 238); } diff --git a/templates/Sparkle/header.tpl b/templates/Sparkle/header.tpl index d858f52c..ecc5f168 100644 --- a/templates/Sparkle/header.tpl +++ b/templates/Sparkle/header.tpl @@ -68,6 +68,7 @@
  • {$lng['panel']['theme']}
  • +
  • {$lng['menue']['main']['apikeys']}
  • {$lng['menue']['main']['apihelp']}
  • From b664917147dc28bb11492ca01b9c18d8985f2ce0 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sun, 4 Mar 2018 12:44:31 +0100 Subject: [PATCH 0537/1335] fix sql variable in Admins.delete Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/commands/class.Admins.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/classes/api/commands/class.Admins.php b/lib/classes/api/commands/class.Admins.php index e100f8ee..38c2f449 100644 --- a/lib/classes/api/commands/class.Admins.php +++ b/lib/classes/api/commands/class.Admins.php @@ -642,7 +642,7 @@ class Admins extends ApiCommand implements ResourceEntity // delete old admin's api keys if exists (no customer keys) $upd_stmt = Database::prepare(" DELETE FROM `" . TABLE_API_KEYS . "` WHERE - `adminid` = :userid AND `customerid` = '0' + `adminid` = :adminid AND `customerid` = '0' "); Database::pexecute($upd_stmt, array( 'adminid' => $id From cfa07bab47caeae97c5d782f58e1e6f210c6c981 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sun, 4 Mar 2018 18:30:16 +0100 Subject: [PATCH 0538/1335] simplified and wrapped internal api calls Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/abstract.ApiCommand.php | 84 ++++++++++++------- lib/classes/api/commands/class.Admins.php | 29 +++---- lib/classes/api/commands/class.Customers.php | 68 +++++++-------- lib/classes/api/commands/class.Domains.php | 28 +++---- lib/classes/api/commands/class.FpmDaemons.php | 14 ++-- lib/classes/api/commands/class.Ftps.php | 55 ++++++------ .../api/commands/class.IpsAndPorts.php | 24 +++--- lib/classes/api/commands/class.Mysqls.php | 62 +++++++------- .../api/commands/class.PhpSettings.php | 26 +++--- lib/classes/api/commands/class.SubDomains.php | 40 ++++----- 10 files changed, 209 insertions(+), 221 deletions(-) diff --git a/lib/classes/api/abstract.ApiCommand.php b/lib/classes/api/abstract.ApiCommand.php index 61243365..8a09dd32 100644 --- a/lib/classes/api/abstract.ApiCommand.php +++ b/lib/classes/api/abstract.ApiCommand.php @@ -331,36 +331,6 @@ abstract class ApiCommand return $param_value; } - /** - * returns "module::function()" for better error-messages (missing parameter etc.) - * makes debugging a whole lot more comfortable - * - * @return string - */ - private function getModFunctionString() - { - $_class = get_called_class(); - - $level = 2; - if (version_compare(PHP_VERSION, "5.4.0", "<")) { - $trace = debug_backtrace(); - } else { - $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); - } - while (true) { - $class = $trace[$level]['class']; - $func = $trace[$level]['function']; - if ($class != $_class) { - $level ++; - if ($level > 5) { - break; - } - continue; - } - return $class . ':' . $func; - } - } - /** * update value of parameter * @@ -399,6 +369,24 @@ abstract class ApiCommand return $this->mail; } + /** + * call an api-command internally + * + * @param string $module + * @param string $function + * @param array|null $params + * + * @return array + */ + protected function apiCall($command = null, $params = null) + { + $_command = explode(".", $command); + $module = $_command[0]; + $function = $_command[1]; + $json_result = $module::getLocal($this->getUserData(), $params)->{$function}(); + return json_decode($json_result, true)['data']; + } + /** * return api-compatible response in JSON format and send corresponding http-header * @@ -448,6 +436,35 @@ abstract class ApiCommand ), true, true); } + /** + * returns "module::function()" for better error-messages (missing parameter etc.) + * makes debugging a whole lot more comfortable + * + * @return string + */ + private function getModFunctionString() + { + $_class = get_called_class(); + $level = 2; + if (version_compare(PHP_VERSION, "5.4.0", "<")) { + $trace = debug_backtrace(); + } else { + $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); + } + while (true) { + $class = $trace[$level]['class']; + $func = $trace[$level]['function']; + if ($class != $_class) { + $level ++; + if ($level > 5) { + break; + } + continue; + } + return $class . ':' . $func; + } + } + /** * read user data from database by api-request-header fields * @@ -490,6 +507,13 @@ abstract class ApiCommand throw new Exception("Invalid API credentials", 400); } + /** + * run 'trim' function on an array recursively + * + * @param array $input + * + * @return array + */ private function trimArray($input) { if (! is_array($input)) { diff --git a/lib/classes/api/commands/class.Admins.php b/lib/classes/api/commands/class.Admins.php index 38c2f449..9a24c02e 100644 --- a/lib/classes/api/commands/class.Admins.php +++ b/lib/classes/api/commands/class.Admins.php @@ -285,10 +285,9 @@ class Admins extends ApiCommand implements ResourceEntity $this->logger()->logAction(ADM_ACTION, LOG_WARNING, "[API] added admin '" . $loginname . "'"); // get all admin-data for return-array - $json_result = Admins::getLocal($this->getUserData(), array( + $result = $this->apiCall('Admins.get', array( 'id' => $adminid - ))->get(); - $result = json_decode($json_result, true)['data']; + )); return $this->response(200, "successfull", $result); } } @@ -314,12 +313,11 @@ class Admins extends ApiCommand implements ResourceEntity $id = $this->getParam('id', true, 0); $ln_optional = ($id <= 0 ? false : true); $loginname = $this->getParam('loginname', $ln_optional, ''); - - $json_result = Admins::getLocal($this->getUserData(), array( + + $result = $this->apiCall('Admins.get', array( 'id' => $id, 'loginname' => $loginname - ))->get(); - $result = json_decode($json_result, true)['data']; + )); $id = $result['adminid']; if ($this->getUserDetail('change_serversettings') == 1 || $result['adminid'] == $this->getUserDetail('adminid')) { @@ -553,10 +551,9 @@ class Admins extends ApiCommand implements ResourceEntity $this->logger()->logAction(ADM_ACTION, LOG_INFO, "[API] edited admin '" . $result['loginname'] . "'"); // get all admin-data for return-array - $json_result = Admins::getLocal($this->getUserData(), array( + $result = $this->apiCall('Admins.get', array( 'id' => $result['adminid'] - ))->get(); - $result = json_decode($json_result, true)['data']; + )); return $this->response(200, "successfull", $result); } } @@ -582,12 +579,11 @@ class Admins extends ApiCommand implements ResourceEntity $id = $this->getParam('id', true, 0); $ln_optional = ($id <= 0 ? false : true); $loginname = $this->getParam('loginname', $ln_optional, ''); - - $json_result = Admins::getLocal($this->getUserData(), array( + + $result = $this->apiCall('Admins.get', array( 'id' => $id, 'loginname' => $loginname - ))->get(); - $result = json_decode($json_result, true)['data']; + )); $id = $result['adminid']; // don't be stupid @@ -684,11 +680,10 @@ class Admins extends ApiCommand implements ResourceEntity $ln_optional = ($id <= 0 ? false : true); $loginname = $this->getParam('loginname', $ln_optional, ''); - $json_result = Admins::getLocal($this->getUserData(), array( + $result = $this->apiCall('Admins.get', array( 'id' => $id, 'loginname' => $loginname - ))->get(); - $result = json_decode($json_result, true)['data']; + )); $id = $result['adminid']; $result_stmt = Database::prepare(" diff --git a/lib/classes/api/commands/class.Customers.php b/lib/classes/api/commands/class.Customers.php index ed1ff384..fdbd42ce 100644 --- a/lib/classes/api/commands/class.Customers.php +++ b/lib/classes/api/commands/class.Customers.php @@ -536,8 +536,8 @@ class Customers extends ApiCommand implements ResourceEntity ); $domainid = - 1; try { - $std_domain = Domains::getLocal($this->getUserData(), $ins_data)->add(); - $domainid = json_decode($std_domain, true)['data']['id']; + $std_domain = $this->apiCall('Domains.add', $ins_data); + $domainid = $std_domain['id']; } catch (Exception $e) { $this->logger()->logAction(ADM_ACTION, LOG_ERR, "[API] Unable to add standard-subdomain: " . $e->getMessage()); } @@ -638,10 +638,9 @@ class Customers extends ApiCommand implements ResourceEntity } $this->logger()->logAction(ADM_ACTION, LOG_WARNING, "[API] added customer '" . $loginname . "'"); - $json_result = Customers::getLocal($this->getUserData(), array( + $result = $this->apiCall('Customers.get', array( 'loginname' => $loginname - ))->get(); - $result = json_decode($json_result, true)['data']; + )); return $this->response(200, "successfull", $result); } throw new Exception("No more resources available", 406); @@ -666,12 +665,11 @@ class Customers extends ApiCommand implements ResourceEntity $id = $this->getParam('id', true, 0); $ln_optional = ($id <= 0 ? false : true); $loginname = $this->getParam('loginname', $ln_optional, ''); - - $json_result = Customers::getLocal($this->getUserData(), array( + + $result = $this->apiCall('Customers.get', array( 'id' => $id, 'loginname' => $loginname - ))->get(); - $result = json_decode($json_result, true)['data']; + )); $id = $result['customerid']; if ($this->isAdmin()) { @@ -802,8 +800,8 @@ class Customers extends ApiCommand implements ResourceEntity ); $domainid = - 1; try { - $std_domain = Domains::getLocal($this->getUserData(), $ins_data)->add(); - $domainid = json_decode($std_domain, true)['data']['id']; + $std_domain = $this->apiCall('Domains.add', $ins_data); + $domainid = $std_domain['id']; } catch (Exception $e) { $this->logger()->logAction(ADM_ACTION, LOG_ERR, "[API] Unable to add standard-subdomain: " . $e->getMessage()); } @@ -823,10 +821,10 @@ class Customers extends ApiCommand implements ResourceEntity if ($createstdsubdomain == '0' && $result['standardsubdomain'] != '0') { try { - $std_domain = Domains::getLocal($this->getUserData(), array( + $std_domain = $this->apiCall('Domains.delete', array( 'id' => $result['standardsubdomain'], 'is_stdsubdomain' => 1 - ))->delete(); + )); } catch (Exception $e) { $this->logger()->logAction(ADM_ACTION, LOG_ERR, "[API] Unable to delete standard-subdomain: " . $e->getMessage()); } @@ -1157,21 +1155,19 @@ class Customers extends ApiCommand implements ResourceEntity */ if ($this->isAdmin()) { if ($move_to_admin > 0 && $move_to_admin != $result['adminid']) { - $json_result = Customers::getLocal($this->getUserData(), array( + $move_result = $this->apiCall('Customers.move', array( 'id' => $result['customerid'], 'adminid' => $move_to_admin - ))->move(); - $move_result = json_decode($json_result, true)['data']; + )); if ($move_result != true) { standard_error('moveofcustomerfailed', $move_result, true); } } } - - $json_result = Customers::getLocal($this->getUserData(), array( + + $result = $this->apiCall('Customers.get', array( 'id' => $result['customerid'] - ))->get(); - $result = json_decode($json_result, true)['data']; + )); return $this->response(200, "successfull", $result); } @@ -1196,12 +1192,11 @@ class Customers extends ApiCommand implements ResourceEntity $ln_optional = ($id <= 0 ? false : true); $loginname = $this->getParam('loginname', $ln_optional, ''); $delete_userfiles = $this->getParam('delete_userfiles', true, 0); - - $json_result = Customers::getLocal($this->getUserData(), array( + + $result = $this->apiCall('Customers.get', array( 'id' => $id, 'loginname' => $loginname - ))->get(); - $result = json_decode($json_result, true)['data']; + )); $id = $result['customerid']; // @fixme use Databases-ApiCommand later @@ -1443,12 +1438,11 @@ class Customers extends ApiCommand implements ResourceEntity $id = $this->getParam('id', true, 0); $ln_optional = ($id <= 0 ? false : true); $loginname = $this->getParam('loginname', $ln_optional, ''); - - $json_result = Customers::getLocal($this->getUserData(), array( + + $result = $this->apiCall('Customers.get', array( 'id' => $id, 'loginname' => $loginname - ))->get(); - $result = json_decode($json_result, true)['data']; + )); $id = $result['customerid']; $result_stmt = Database::prepare(" @@ -1488,12 +1482,11 @@ class Customers extends ApiCommand implements ResourceEntity $id = $this->getParam('id', true, 0); $ln_optional = ($id <= 0 ? false : true); $loginname = $this->getParam('loginname', $ln_optional, ''); - - $json_result = Customers::getLocal($this->getUserData(), array( + + $c_result = $this->apiCall('Customers.get', array( 'id' => $id, 'loginname' => $loginname - ))->get(); - $c_result = json_decode($json_result, true)['data']; + )); $id = $c_result['customerid']; // check if target-admin is the current admin @@ -1502,10 +1495,9 @@ class Customers extends ApiCommand implements ResourceEntity } // get target admin - $json_result = Admins::getLocal($this->getUserData(), array( + $a_result = $this->apiCall('Admins.get', array( 'id' => $adminid - ))->get(); - $a_result = json_decode($json_result, true)['data']; + )); // Update customer entry $updCustomer_stmt = Database::prepare(" @@ -1538,10 +1530,10 @@ class Customers extends ApiCommand implements ResourceEntity updateCounters(false); $this->logger()->logAction(ADM_ACTION, LOG_INFO, "[API] moved user '" . $c_result['loginname'] . "' from admin/reseller '" . $c_result['adminname'] . " to admin/reseller '" . $a_result['loginname'] . "'"); - $json_result = Customers::getLocal($this->getUserData(), array( + + $result = $this->apiCall('Customers.get', array( 'id' => $c_result['customerid'] - ))->get(); - $result = json_decode($json_result, true)['data']; + )); return $this->response(200, "successfull", $result); } throw new Exception("Not allowed to execute given command.", 403); diff --git a/lib/classes/api/commands/class.Domains.php b/lib/classes/api/commands/class.Domains.php index fcd71ea6..99d29dec 100644 --- a/lib/classes/api/commands/class.Domains.php +++ b/lib/classes/api/commands/class.Domains.php @@ -178,10 +178,9 @@ class Domains extends ApiCommand implements ResourceEntity ), '', true); } - $json_result = Customers::getLocal($this->getUserData(), array( + $customer = $this->apiCall('Customers.get', array( 'id' => $customerid - ))->get(); - $customer = json_decode($json_result, true)['data']; + )); if ($this->getUserDetail('customers_see_all') == '1') { $admin_stmt = Database::prepare(" @@ -743,10 +742,9 @@ class Domains extends ApiCommand implements ResourceEntity $this->logger()->logAction(ADM_ACTION, LOG_WARNING, "[API] added domain '" . $domain . "'"); - $json_result = Domains::getLocal($this->getUserData(), array( + $result = $this->apiCall('Domains.get', array( 'domainname' => $domain - ))->get(); - $result = json_decode($json_result, true)['data']; + )); return $this->response(200, "successfull", $result); } } @@ -777,12 +775,11 @@ class Domains extends ApiCommand implements ResourceEntity $domainname = $this->getParam('domainname', $dn_optional, ''); // get requested domain - $json_result = Domains::getLocal($this->getUserData(), array( + $result = $this->apiCall('Domains.get', array( 'id' => $id, 'domainname' => $domainname, 'no_std_subdomain' => true - ))->get(); - $result = json_decode($json_result, true)['data']; + )); $id = $result['id']; // optional parameters @@ -877,8 +874,7 @@ class Domains extends ApiCommand implements ResourceEntity AND (`subdomains_used` + :subdomains <= `subdomains` OR `subdomains` = '-1' ) AND (`emails_used` + :emails <= `emails` OR `emails` = '-1' ) AND (`email_forwarders_used` + :forwarders <= `email_forwarders` OR `email_forwarders` = '-1' ) - AND (`email_accounts_used` + :accounts <= `email_accounts` OR `email_accounts` = '-1' ) " . ($this->getUserDetail('customers_see_all') ? '' : " AND `adminid` = :adminid") - ); + AND (`email_accounts_used` + :accounts <= `email_accounts` OR `email_accounts` = '-1' ) " . ($this->getUserDetail('customers_see_all') ? '' : " AND `adminid` = :adminid")); $params = array( 'customerid' => $customerid, 'subdomains' => $subdomains, @@ -897,10 +893,9 @@ class Domains extends ApiCommand implements ResourceEntity $customerid = $result['customerid']; // get customer - $json_result = Customers::getLocal($this->getUserData(), array( + $customer = $this->apiCall('Customers.get', array( 'id' => $customerid - ))->get(); - $customer = json_decode($json_result, true)['data']; + )); } // handle change of admin (move domain from admin to admin) @@ -1664,11 +1659,10 @@ class Domains extends ApiCommand implements ResourceEntity $is_stdsubdomain = $this->getParam('is_stdsubdomain', true, 0); $remove_subbutmain_domains = $this->getParam('delete_mainsubdomains', true, 0); - $json_result = Domains::getLocal($this->getUserData(), array( + $result = $this->apiCall('Domains.get', array( 'id' => $id, 'domainname' => $domainname - ))->get(); - $result = json_decode($json_result, true)['data']; + )); $id = $result['id']; // check for deletion of main-domains which are logically subdomains, #329 diff --git a/lib/classes/api/commands/class.FpmDaemons.php b/lib/classes/api/commands/class.FpmDaemons.php index b42a0c99..e740f4cf 100644 --- a/lib/classes/api/commands/class.FpmDaemons.php +++ b/lib/classes/api/commands/class.FpmDaemons.php @@ -192,11 +192,10 @@ class FpmDaemons extends ApiCommand implements ResourceEntity // required parameter $id = $this->getParam('id'); - - $json_result = PhpSettings::getLocal($this->getUserData(), array( + + $result = $this->apiCall('PhpSettings.get', array( 'id' => $id - ))->get(); - $result = json_decode($json_result, true)['data']; + )); // parameters $description = $this->getParam('description', true, $result['description']); @@ -283,11 +282,10 @@ class FpmDaemons extends ApiCommand implements ResourceEntity if ($id == 1) { standard_error('cannotdeletedefaultphpconfig', '', true); } - - $json_result = FpmDaemons::getLocal($this->getUserData(), array( + + $result = $this->apiCall('FpmDaemons.get', array( 'id' => $id - ))->get(); - $result = json_decode($json_result, true)['data']; + )); // set default fpm daemon config for all php-config that use this config that is to be deleted $upd_stmt = Database::prepare(" diff --git a/lib/classes/api/commands/class.Ftps.php b/lib/classes/api/commands/class.Ftps.php index 00e6057b..6411e3f2 100644 --- a/lib/classes/api/commands/class.Ftps.php +++ b/lib/classes/api/commands/class.Ftps.php @@ -84,10 +84,9 @@ class Ftps extends ApiCommand implements ResourceEntity if ($this->isAdmin()) { // get customer id $customer_id = $this->getParam('customer_id'); - $json_result = Customers::getLocal($this->getUserData(), array( + $customer = $this->apiCall('Customers.get', array( 'id' => $customer_id - ))->get(); - $customer = json_decode($json_result, true)['data']; + )); // check whether the customer has enough resources to get the ftp-user added if ($customer['ftps_used'] >= $customer['ftps'] && $customer['ftps'] != '-1') { throw new Exception("Customer has no more resources available", 406); @@ -254,11 +253,10 @@ class Ftps extends ApiCommand implements ResourceEntity $this->mailer()->ClearAddresses(); } $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_WARNING, "[API] added ftp-user '" . $username . "'"); - - $json_result = Ftps::getLocal($this->getUserData(), array( + + $result = $this->apiCall('Ftps.get', array( 'username' => $username - ))->get(); - $result = json_decode($json_result, true)['data']; + )); return $this->response(200, "successfull", $result); } } @@ -288,8 +286,8 @@ class Ftps extends ApiCommand implements ResourceEntity if ($this->getUserDetail('customers_see_all') == false) { // if it's a reseller or an admin who cannot see all customers, we need to check // whether the database belongs to one of his customers - $json_result = Customers::getLocal($this->getUserData())->listing(); - $custom_list_result = json_decode($json_result, true)['data']['list']; + $_custom_list_result = $this->apiCall('Customers.listing'); + $custom_list_result = $_custom_list_result['list']; $customer_ids = array(); foreach ($custom_list_result as $customer) { $customer_ids[] = $customer['customerid']; @@ -336,12 +334,11 @@ class Ftps extends ApiCommand implements ResourceEntity $id = $this->getParam('id', true, 0); $un_optional = ($id <= 0 ? false : true); $username = $this->getParam('username', $un_optional, ''); - - $json_result = Ftps::getLocal($this->getUserData(), array( + + $result = $this->apiCall('Ftps.get', array( 'id' => $id, 'username' => $username - ))->get(); - $result = json_decode($json_result, true)['data']; + )); $id = $result['id']; // parameters @@ -364,10 +361,9 @@ class Ftps extends ApiCommand implements ResourceEntity if ($this->isAdmin()) { // get customer id $customer_id = $this->getParam('customer_id'); - $json_result = Customers::getLocal($this->getUserData(), array( + $customer = $this->apiCall('Customers.get', array( 'id' => $customer_id - ))->get(); - $customer = json_decode($json_result, true)['data']; + )); } else { $customer_id = $this->getUserDetail('customerid'); $customer = $this->getUserData(); @@ -430,11 +426,10 @@ class Ftps extends ApiCommand implements ResourceEntity "customerid" => $customer['customerid'], "id" => $id ), true, true); - - $json_result = Ftps::getLocal($this->getUserData(), array( + + $result = $this->apiCall('Ftps.get', array( 'username' => $result['username'] - ))->get(); - $result = json_decode($json_result, true)['data']; + )); $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_NOTICE, "[API] updated ftp-user '" . $result['username'] . "'"); return $this->response(200, "successfull", $result); } @@ -460,16 +455,16 @@ class Ftps extends ApiCommand implements ResourceEntity $loginname = $this->getParam('loginname', true, ''); if (! empty($customerid) || ! empty($loginname)) { - $json_result = Customers::getLocal($this->getUserData(), array( + $_result = $this->apiCall('Customers.get', array( 'id' => $customerid, 'loginname' => $loginname - ))->get(); + )); $custom_list_result = array( - json_decode($json_result, true)['data'] + $_result ); } else { - $json_result = Customers::getLocal($this->getUserData())->listing(); - $custom_list_result = json_decode($json_result, true)['data']['list']; + $_custom_list_result = $this->apiCall('Customers.listing'); + $custom_list_result = $_custom_list_result['list']; } $customer_ids = array(); foreach ($custom_list_result as $customer) { @@ -526,19 +521,17 @@ class Ftps extends ApiCommand implements ResourceEntity } // get ftp-user - $json_result = Ftps::getLocal($this->getUserData(), array( + $result = $this->apiCall('Ftps.get', array( 'id' => $id, 'username' => $username - ))->get(); - $result = json_decode($json_result, true)['data']; + )); $id = $result['id']; if ($this->isAdmin()) { // get customer-data - $json_result = Customers::getLocal($this->getUserData(), array( + $customer_data = $this->apiCall('Customers.get', array( 'id' => $result['customerid'] - ))->get(); - $customer_data = json_decode($json_result, true)['data']; + )); } else { $customer_data = $this->getUserData(); } diff --git a/lib/classes/api/commands/class.IpsAndPorts.php b/lib/classes/api/commands/class.IpsAndPorts.php index f02ded1c..e6262575 100644 --- a/lib/classes/api/commands/class.IpsAndPorts.php +++ b/lib/classes/api/commands/class.IpsAndPorts.php @@ -215,10 +215,9 @@ class IpsAndPorts extends ApiCommand implements ResourceEntity } $this->logger()->logAction(ADM_ACTION, LOG_WARNING, "[API] added IP/port '" . $ip . ":" . $port . "'"); // get ip for return-array - $json_result = IpsAndPorts::getLocal($this->getUserData(), array( + $result = $this->apiCall('IpsAndPorts.get', array( 'id' => $ins_data['id'] - ))->get(); - $result = json_decode($json_result, true)['data']; + )); return $this->response(200, "successfull", $result); } throw new Exception("Not allowed to execute given command.", 403); @@ -237,11 +236,10 @@ class IpsAndPorts extends ApiCommand implements ResourceEntity { if ($this->isAdmin() && ($this->getUserDetail('change_serversettings') || ! empty($this->getUserDetail('ip')))) { $id = $this->getParam('id'); - - $json_result = IpsAndPorts::getLocal($this->getUserData(), array( + + $result = $this->apiCall('IpsAndPorts.get', array( 'id' => $id - ))->get(); - $result = json_decode($json_result, true)['data']; + )); $ip = validate_ip2($this->getParam('ip', true, $result['ip']), false, 'invalidip', false, false, false, true); $port = 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( @@ -373,10 +371,9 @@ class IpsAndPorts extends ApiCommand implements ResourceEntity $this->logger()->logAction(ADM_ACTION, LOG_WARNING, "[API] changed IP/port from '" . $result['ip'] . ":" . $result['port'] . "' to '" . $ip . ":" . $port . "'"); - $json_result = IpsAndPorts::getLocal($this->getUserData(), array( + $result = $this->apiCall('IpsAndPorts.get', array( 'id' => $result['id'] - ))->get(); - $result = json_decode($json_result, true)['data']; + )); return $this->response(200, "successfull", $result); } } @@ -397,11 +394,10 @@ class IpsAndPorts extends ApiCommand implements ResourceEntity { if ($this->isAdmin() && $this->getUserDetail('change_serversettings')) { $id = $this->getParam('id'); - - $json_result = IpsAndPorts::getLocal($this->getUserData(), array( + + $result = $this->apiCall('IpsAndPorts.get', array( 'id' => $id - ))->get(); - $result = json_decode($json_result, true)['data']; + )); $result_checkdomain_stmt = Database::prepare(" SELECT `id_domain` as `id` FROM `" . TABLE_DOMAINTOIP . "` WHERE `id_ipandports` = :id diff --git a/lib/classes/api/commands/class.Mysqls.php b/lib/classes/api/commands/class.Mysqls.php index f783b34d..55929a80 100644 --- a/lib/classes/api/commands/class.Mysqls.php +++ b/lib/classes/api/commands/class.Mysqls.php @@ -62,7 +62,7 @@ class Mysqls extends ApiCommand implements ResourceEntity if (! isset($sql_root) || ! is_array($sql_root)) { throw new ErrorException("Database server with index #" . $dbserver . " is unknown", 404); } - + if ($sendinfomail != 1) { $sendinfomail = 0; } @@ -71,10 +71,9 @@ class Mysqls extends ApiCommand implements ResourceEntity if ($this->isAdmin()) { // get customer id $customer_id = $this->getParam('customer_id'); - $json_result = Customers::getLocal($this->getUserData(), array( + $customer = $this->apiCall('Customers.get', array( 'id' => $customer_id - ))->get(); - $customer = json_decode($json_result, true)['data']; + )); // check whether the customer has enough resources to get the database added if ($customer['mysqls_used'] >= $customer['mysqls'] && $customer['mysqls'] != '-1') { throw new Exception("Customer has no more resources available", 406); @@ -196,10 +195,9 @@ class Mysqls extends ApiCommand implements ResourceEntity } $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_WARNING, "[API] added mysql-database '" . $username . "'"); - $json_result = Mysqls::getLocal($this->getUserData(), array( + $result = $this->apiCall('Mysqls.get', array( 'dbname' => $username - ))->get(); - $result = json_decode($json_result, true)['data']; + )); return $this->response(200, "successfull", $result); } throw new Exception("No more resources available", 406); @@ -225,13 +223,13 @@ class Mysqls extends ApiCommand implements ResourceEntity $dn_optional = ($id <= 0 ? false : true); $dbname = $this->getParam('dbname', $dn_optional, ''); $dbserver = $this->getParam('mysql_server', true, - 1); - + if ($this->isAdmin()) { if ($this->getUserDetail('customers_see_all') != 1) { // if it's a reseller or an admin who cannot see all customers, we need to check // whether the database belongs to one of his customers - $json_result = Customers::getLocal($this->getUserData())->listing(); - $custom_list_result = json_decode($json_result, true)['data']['list']; + $_custom_list_result = $this->apiCall('Customers.listing'); + $custom_list_result = $_custom_list_result['list']; $customer_ids = array(); foreach ($custom_list_result as $customer) { $customer_ids[] = $customer['customerid']; @@ -311,7 +309,7 @@ class Mysqls extends ApiCommand implements ResourceEntity * optional, update password for the database * @param string $description * optional, description for database - * + * * @access admin, customer * @throws Exception * @return array @@ -322,17 +320,16 @@ class Mysqls extends ApiCommand implements ResourceEntity $dn_optional = ($id <= 0 ? false : true); $dbname = $this->getParam('dbname', $dn_optional, ''); $dbserver = $this->getParam('mysql_server', true, - 1); - + if ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'mysql')) { throw new Exception("You cannot access this resource", 405); } - $json_result = Mysqls::getLocal($this->getUserData(), array( + $result = $this->apiCall('Mysqls.get', array( 'id' => $id, 'dbname' => $dbname, 'mysql_server' => $dbserver - ))->get(); - $result = json_decode($json_result, true)['data']; + )); $id = $result['id']; // paramters @@ -352,15 +349,14 @@ class Mysqls extends ApiCommand implements ResourceEntity if (! isset($sql_root) || ! is_array($sql_root)) { throw new ErrorException("Database server with index #" . $dbserver . " is unknown", 404); } - + // get needed customer info to reduce the mysql-usage-counter by one if ($this->isAdmin()) { // get customer id $customer_id = $this->getParam('customer_id'); - $json_result = Customers::getLocal($this->getUserData(), array( - 'id' => $result['customerid'] - ))->get(); - $customer = json_decode($json_result, true)['data']; + $customer = $this->apiCall('Customers.get', array( + 'id' => $customer_id + )); // check whether the customer has enough resources to get the database added if ($customer['mysqls_used'] >= $customer['mysqls'] && $customer['mysqls'] != '-1') { throw new Exception("Customer has no more resources available", 406); @@ -406,7 +402,7 @@ class Mysqls extends ApiCommand implements ResourceEntity "id" => $id ); Database::pexecute($stmt, $params, true, true); - + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_WARNING, "[API] updated mysql-database '" . $result['databasename'] . "'"); return $this->response(200, "successfull", $params); } @@ -436,16 +432,16 @@ class Mysqls extends ApiCommand implements ResourceEntity $loginname = $this->getParam('loginname', true, ''); if (! empty($customer_id) || ! empty($loginname)) { - $json_result = Customers::getLocal($this->getUserData(), array( + $customer = $this->apiCall('Customers.get', array( 'id' => $customerid, 'loginname' => $loginname - ))->get(); + )); $custom_list_result = array( - json_decode($json_result, true)['data'] + $customer ); } else { - $json_result = Customers::getLocal($this->getUserData())->listing(); - $custom_list_result = json_decode($json_result, true)['data']['list']; + $_custom_list_result = $this->apiCall('Customers.listing'); + $custom_list_result = $_custom_list_result['list']; } $customer_ids = array(); foreach ($custom_list_result as $customer) { @@ -526,17 +522,16 @@ class Mysqls extends ApiCommand implements ResourceEntity $dn_optional = ($id <= 0 ? false : true); $dbname = $this->getParam('dbname', $dn_optional, ''); $dbserver = $this->getParam('mysql_server', true, - 1); - + if ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'mysql')) { throw new Exception("You cannot access this resource", 405); } - - $json_result = Mysqls::getLocal($this->getUserData(), array( + + $result = $this->apiCall('Mysqls.get', array( 'id' => $id, 'dbname' => $dbname, 'mysql_server' => $dbserver - ))->get(); - $result = json_decode($json_result, true)['data']; + )); $id = $result['id']; // Begin root-session @@ -554,10 +549,9 @@ class Mysqls extends ApiCommand implements ResourceEntity // get needed customer info to reduce the mysql-usage-counter by one if ($this->isAdmin()) { - $json_result = Customers::getLocal($this->getUserData(), array( + $customer = $this->apiCall('Customers.get', array( 'id' => $result['customerid'] - ))->get(); - $customer = json_decode($json_result, true)['data']; + )); $mysql_used = $customer['mysqls_used']; $customer_id = $customer['customer_id']; } else { diff --git a/lib/classes/api/commands/class.PhpSettings.php b/lib/classes/api/commands/class.PhpSettings.php index e25a9d09..72d3d2e9 100644 --- a/lib/classes/api/commands/class.PhpSettings.php +++ b/lib/classes/api/commands/class.PhpSettings.php @@ -230,7 +230,11 @@ class PhpSettings extends ApiCommand implements ResourceEntity inserttask('1'); $this->logger()->logAction(ADM_ACTION, LOG_INFO, "[API] php setting with description '" . $description . "' has been created by '" . $this->getUserDetail('loginname') . "'"); - return $this->response(200, "successfull", $ins_data); + + $result = $this->apiCall('PhpSettings.get', array( + 'id' => $ins_data['id'] + )); + return $this->response(200, "successfull", $result); } throw new Exception("Not allowed to execute given command.", 403); } @@ -250,11 +254,10 @@ class PhpSettings extends ApiCommand implements ResourceEntity // required parameter $id = $this->getParam('id'); - - $json_result = PhpSettings::getLocal($this->getUserData(), array( + + $result = $this->apiCall('PhpSettings.get', array( 'id' => $id - ))->get(); - $result = json_decode($json_result, true)['data']; + )); // parameters $description = $this->getParam('description', true, $result['description']); @@ -341,7 +344,11 @@ class PhpSettings extends ApiCommand implements ResourceEntity inserttask('1'); $this->logger()->logAction(ADM_ACTION, LOG_INFO, "[API] php setting with description '" . $description . "' has been updated by '" . $this->getUserDetail('loginname') . "'"); - return $this->response(200, "successfull", $upd_data); + + $result = $this->apiCall('PhpSettings.get', array( + 'id' => $id + )); + return $this->response(200, "successfull", $result); } throw new Exception("Not allowed to execute given command.", 403); } @@ -359,11 +366,10 @@ class PhpSettings extends ApiCommand implements ResourceEntity { if ($this->isAdmin() && $this->getUserDetail('change_serversettings') == 1) { $id = $this->getParam('id'); - - $json_result = PhpSettings::getLocal($this->getUserData(), array( + + $result = $this->apiCall('PhpSettings.get', array( 'id' => $id - ))->get(); - $result = json_decode($json_result, true)['data']; + )); if ((Settings::Get('system.mod_fcgid') == '1' && Settings::Get('system.mod_fcgid_defaultini_ownvhost') == $id) || (Settings::Get('phpfpm.enabled') == '1' && Settings::Get('phpfpm.vhost_defaultini') == $id)) { standard_error('cannotdeletehostnamephpconfig', '', true); diff --git a/lib/classes/api/commands/class.SubDomains.php b/lib/classes/api/commands/class.SubDomains.php index eb1ea0d7..e8994ffa 100644 --- a/lib/classes/api/commands/class.SubDomains.php +++ b/lib/classes/api/commands/class.SubDomains.php @@ -80,10 +80,9 @@ class SubDomains extends ApiCommand implements ResourceEntity if ($this->isAdmin()) { // get customer id $customer_id = $this->getParam('customer_id'); - $json_result = Customers::getLocal($this->getUserData(), array( + $customer = $this->apiCall('Customers.get', array( 'id' => $customer_id - ))->get(); - $customer = json_decode($json_result, true)['data']; + )); // check whether the customer has enough resources to get the subdomain added if ($customer['subdomains_used'] >= $customer['subdomains'] && $customer['subdomains'] != '-1') { throw new Exception("Customer has no more resources available", 406); @@ -329,11 +328,10 @@ class SubDomains extends ApiCommand implements ResourceEntity Admins::increaseUsage(($this->isAdmin() ? $customer['adminid'] : $this->getUserDetail('adminid')), 'subdomains_used'); $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_INFO, "[API] added subdomain '" . $completedomain . "'"); - - $json_result = SubDomains::getLocal($this->getUserData(), array( + + $result = $this->apiCall('SubDomains.get', array( 'id' => $subdomain_id - ))->get(); - $result = json_decode($json_result, true)['data']; + )); return $this->response(200, "successfull", $result); } throw new Exception("No more resources available", 406); @@ -367,8 +365,8 @@ class SubDomains extends ApiCommand implements ResourceEntity if ($this->getUserDetail('customers_see_all') != 1) { // if it's a reseller or an admin who cannot see all customers, we need to check // whether the database belongs to one of his customers - $json_result = Customers::getLocal($this->getUserData())->listing(); - $custom_list_result = json_decode($json_result, true)['data']['list']; + $_custom_list_result = $this->apiCall('Customers.listing'); + $custom_list_result = $_custom_list_result['list']; $customer_ids = array(); foreach ($custom_list_result as $customer) { $customer_ids[] = $customer['customerid']; @@ -428,16 +426,16 @@ class SubDomains extends ApiCommand implements ResourceEntity $loginname = $this->getParam('loginname', true, ''); if (! empty($customerid) || ! empty($loginname)) { - $json_result = Customers::getLocal($this->getUserData(), array( - 'id' => $customerid, + $result = $this->apiCall('Customers.get', array( + 'id' => $id, 'loginname' => $loginname - ))->get(); + )); $custom_list_result = array( - json_decode($json_result, true)['data'] + $result ); } else { - $json_result = Customers::getLocal($this->getUserData())->listing(); - $custom_list_result = json_decode($json_result, true)['data']['list']; + $_custom_list_result = $this->apiCall('Customers.listing'); + $custom_list_result = $_custom_list_result['list']; } $customer_ids = array(); $customer_stdsubs = array(); @@ -505,20 +503,18 @@ class SubDomains extends ApiCommand implements ResourceEntity if ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'domains')) { throw new Exception("You cannot access this resource", 405); } - - $json_result = SubDomains::getLocal($this->getUserData(), array( + + $result = $this->apiCall('SubDomains.get', array( 'id' => $id, 'domainname' => $domainname - ))->get(); - $result = json_decode($json_result, true)['data']; + )); $id = $result['id']; // get needed customer info to reduce the subdomain-usage-counter by one if ($this->isAdmin()) { - $json_result = Customers::getLocal($this->getUserData(), array( + $customer = $this->apiCall('Customers.get', array( 'id' => $result['customerid'] - ))->get(); - $customer = json_decode($json_result, true)['data']; + )); $subdomains_used = $customer['subdomains_used']; $customer_id = $customer['customer_id']; } else { From a869bc58cd9d27ef4dd5ad6d2bb0b8c4160eba22 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sun, 4 Mar 2018 19:47:48 +0100 Subject: [PATCH 0539/1335] fix wrong variable-name in SubDomains.listing Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/commands/class.SubDomains.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/classes/api/commands/class.SubDomains.php b/lib/classes/api/commands/class.SubDomains.php index e8994ffa..d61c75bd 100644 --- a/lib/classes/api/commands/class.SubDomains.php +++ b/lib/classes/api/commands/class.SubDomains.php @@ -427,7 +427,7 @@ class SubDomains extends ApiCommand implements ResourceEntity if (! empty($customerid) || ! empty($loginname)) { $result = $this->apiCall('Customers.get', array( - 'id' => $id, + 'id' => $customerid, 'loginname' => $loginname )); $custom_list_result = array( From ae4a7ff9434c2537cc590be6582deb7d129ea684 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 5 Mar 2018 08:12:08 +0100 Subject: [PATCH 0540/1335] wrap ip-validating in Domains-ApiCommand to reduce duplicate code Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/abstract.ApiCommand.php | 2 +- lib/classes/api/commands/class.Domains.php | 314 ++++++++------------- 2 files changed, 116 insertions(+), 200 deletions(-) diff --git a/lib/classes/api/abstract.ApiCommand.php b/lib/classes/api/abstract.ApiCommand.php index 8a09dd32..156f84a5 100644 --- a/lib/classes/api/abstract.ApiCommand.php +++ b/lib/classes/api/abstract.ApiCommand.php @@ -156,7 +156,7 @@ abstract class ApiCommand // ensure that we can display messages $language = Settings::Get('panel.standardlanguage'); - if (isset($this->user_data['language']) && isset($languages[$this->user_data['language']])) { + if (isset($this->user_data['language']) && isset($langs[$this->user_data['language']])) { // default: use language from session, #277 $language = $this->user_data['language']; } elseif (isset($this->user_data['def_language'])) { diff --git a/lib/classes/api/commands/class.Domains.php b/lib/classes/api/commands/class.Domains.php index 99d29dec..47a9c209 100644 --- a/lib/classes/api/commands/class.Domains.php +++ b/lib/classes/api/commands/class.Domains.php @@ -63,7 +63,7 @@ class Domains extends ApiCommand implements ResourceEntity * optional, the domainname * @param bool $no_std_subdomain * optional, default false - * + * * @access admin * @throws Exception * @return array @@ -75,7 +75,7 @@ class Domains extends ApiCommand implements ResourceEntity $dn_optional = ($id <= 0 ? false : true); $domainname = $this->getParam('domainname', $dn_optional, ''); $no_std_subdomain = $this->getParam('no_std_subdomain', true, false); - + // convert possible idn domain to punycode if (substr($domainname, 0, 4) != 'xn--') { $idna_convert = new idna_convert_wrapper(); @@ -155,7 +155,6 @@ class Domains extends ApiCommand implements ResourceEntity $ocsp_stapling = $this->getParam('ocsp_stapling', true, 0); // validation - if ($p_domain == Settings::Get('system.hostname')) { standard_error('admin_domain_emailsystemhostname', '', true); } @@ -177,11 +176,11 @@ class Domains extends ApiCommand implements ResourceEntity 'mydomain' ), '', true); } - + $customer = $this->apiCall('Customers.get', array( 'id' => $customerid )); - + if ($this->getUserDetail('customers_see_all') == '1') { $admin_stmt = Database::prepare(" SELECT * FROM `" . TABLE_PANEL_ADMINS . "` @@ -304,104 +303,14 @@ class Domains extends ApiCommand implements ResourceEntity $mod_fcgid_maxrequests = '-1'; } - if ($this->getUserDetail('ip') != "-1") { - $admin_ip_stmt = Database::prepare(" - SELECT `id`, `ip`, `port` FROM `" . TABLE_PANEL_IPSANDPORTS . "` - WHERE `id` = :id ORDER BY `ip`, `port` ASC"); - $admin_ip = Database::pexecute_first($admin_ip_stmt, array( - 'id' => $this->getUserDetail('ip') - ), true, true); - $additional_ip_condition = " AND `ip` = :adminip "; - $aip_param = array( - 'adminip' => $admin_ip['ip'] - ); - } else { - $additional_ip_condition = ''; - $aip_param = array(); - } - - if (empty($p_ipandports)) { - throw new Exception("No IPs given, unable to add domain (no default IPs set?)", 406); - } - - $ipandports = array(); - if (! empty($p_ipandports) && is_numeric($p_ipandports)) { - $p_ipandports = array($p_ipandports); - } - if (! empty($p_ipandports) && ! is_array($p_ipandports)) { - $p_ipandports = unserialize($p_ipandports); - } - - if (! empty($p_ipandports) && is_array($p_ipandports)) { - foreach ($p_ipandports as $ipandport) { - $ipandport = intval($ipandport); - $ipandport_check_stmt = Database::prepare(" - SELECT `id`, `ip`, `port` FROM `" . TABLE_PANEL_IPSANDPORTS . "` - WHERE `id` = :id " . $additional_ip_condition); - $ip_params = null; - $ip_params = array_merge(array( - 'id' => $ipandport - ), $aip_param); - $ipandport_check = Database::pexecute_first($ipandport_check_stmt, $ip_params, true, true); - - if (! isset($ipandport_check['id']) || $ipandport_check['id'] == '0' || $ipandport_check['id'] != $ipandport) { - standard_error('ipportdoesntexist', '', true); - } else { - $ipandports[] = $ipandport; - } - } - } - + // check non-ssl IP + $ipandports = $this->validateIpAddresses($p_ipandports); + // check ssl IP + $ssl_ipandports = array(); if (Settings::Get('system.use_ssl') == "1" && ! empty($p_ssl_ipandports)) { - - $ssl_ipandports = array(); - if (! empty($p_ssl_ipandports) && ! is_array($p_ssl_ipandports)) { - $p_ssl_ipandports = unserialize($p_ssl_ipandports); - } - - // Verify SSL-Ports - if (! empty($p_ssl_ipandports) && is_array($p_ssl_ipandports)) { - foreach ($p_ssl_ipandports as $ssl_ipandport) { - if (trim($ssl_ipandport) == "") { - continue; - } - // fix if no ssl-ip/port is checked - if (trim($ssl_ipandport) < 1) { - continue; - } - $ssl_ipandport = intval($ssl_ipandport); - $ssl_ipandport_check_stmt = Database::prepare(" - SELECT `id`, `ip`, `port` FROM `" . TABLE_PANEL_IPSANDPORTS . "` - WHERE `id` = :id " . $additional_ip_condition); - $ip_params = null; - $ip_params = array_merge(array( - 'id' => $ssl_ipandport - ), $aip_param); - $ssl_ipandport_check = Database::pexecute_first($ssl_ipandport_check_stmt, $ip_params, true, true); - - if (! isset($ssl_ipandport_check['id']) || $ssl_ipandport_check['id'] == '0' || $ssl_ipandport_check['id'] != $ssl_ipandport) { - standard_error('ipportdoesntexist', '', true); - } else { - $ssl_ipandports[] = $ssl_ipandport; - } - } - } else { - $ssl_redirect = 0; - $letsencrypt = 0; - $http2 = 0; - // we need this for the serialize - // if ssl is disabled or no ssl-ip/port exists - $ssl_ipandports[] = - 1; - - // HSTS - $hsts_maxage = 0; - $hsts_sub = 0; - $hsts_preload = 0; - - // OCSP stapling - $ocsp_stapling = 0; - } - } else { + $ssl_ipandports = $this->validateIpAddresses($p_ssl_ipandports, true); + } + if (Settings::Get('system.use_ssl') == "0" || empty($ssl_ipandports)) { $ssl_redirect = 0; $letsencrypt = 0; $http2 = 0; @@ -741,7 +650,7 @@ class Domains extends ApiCommand implements ResourceEntity inserttask('4'); $this->logger()->logAction(ADM_ACTION, LOG_WARNING, "[API] added domain '" . $domain . "'"); - + $result = $this->apiCall('Domains.get', array( 'domainname' => $domain )); @@ -760,7 +669,7 @@ class Domains extends ApiCommand implements ResourceEntity * optional, the domain-id * @param string $domainname * optional, the domainname - * + * * @access admin * @throws Exception * @return array @@ -773,7 +682,7 @@ class Domains extends ApiCommand implements ResourceEntity $id = $this->getParam('id', true, 0); $dn_optional = ($id <= 0 ? false : true); $domainname = $this->getParam('domainname', $dn_optional, ''); - + // get requested domain $result = $this->apiCall('Domains.get', array( 'id' => $id, @@ -819,7 +728,7 @@ class Domains extends ApiCommand implements ResourceEntity $hsts_sub = $this->getParam('hsts_sub', true, $result['hsts_sub']); $hsts_preload = $this->getParam('hsts_preload', true, $result['hsts_preload']); $ocsp_stapling = $this->getParam('ocsp_stapling', true, $result['ocsp_stapling']); - + // count subdomain usage of source-domain $subdomains_stmt = Database::prepare(" SELECT COUNT(`id`) AS count FROM `" . TABLE_PANEL_DOMAINS . "` WHERE @@ -829,7 +738,7 @@ class Domains extends ApiCommand implements ResourceEntity 'resultid' => $result['id'] ), true, true); $subdomains = $subdomains['count']; - + // count where this domain is alias domain $alias_check_stmt = Database::prepare(" SELECT COUNT(`id`) AS count FROM `" . TABLE_PANEL_DOMAINS . "` WHERE @@ -849,11 +758,11 @@ class Domains extends ApiCommand implements ResourceEntity 'customerid' => $result['customerid'], 'id' => $result['id'] ), true, true); - + $emails = Database::num_rows(); $email_forwarders = 0; $email_accounts = 0; - + while ($domain_emails_row = $domain_emails_result_stmt->fetch(PDO::FETCH_ASSOC)) { if ($domain_emails_row['destination'] != '') { $domain_emails_row['destination'] = explode(' ', makeCorrectDestination($domain_emails_row['destination'])); @@ -864,7 +773,7 @@ class Domains extends ApiCommand implements ResourceEntity } } } - + // handle change of customer (move domain from customer to customer) if ($customerid > 0 && $customerid != $result['customerid'] && Settings::Get('panel.allow_domain_change_customer') == '1') { // check whether target customer has enough resources @@ -891,7 +800,7 @@ class Domains extends ApiCommand implements ResourceEntity } } else { $customerid = $result['customerid']; - + // get customer $customer = $this->apiCall('Customers.get', array( 'id' => $customerid @@ -963,10 +872,10 @@ class Domains extends ApiCommand implements ResourceEntity $specialsettings = validate(str_replace("\r\n", "\n", $specialsettings), 'specialsettings', '/^[^\0]*$/', '', array(), true); $documentroot = validate($documentroot, 'documentroot', '', '', array(), true); - + // when moving customer and no path is specified, update would normally reuse the current document-root // which would point to the wrong customer, therefore we will re-create that directory - if (!empty($documentroot) && $customerid > 0 && $customerid != $result['customerid'] && Settings::Get('panel.allow_domain_change_customer') == '1') { + if (! empty($documentroot) && $customerid > 0 && $customerid != $result['customerid'] && Settings::Get('panel.allow_domain_change_customer') == '1') { if (Settings::Get('system.documentroot_use_default_value') == 1) { $_documentroot = makeCorrectDir($customer['documentroot'] . '/' . $result['domain']); } else { @@ -975,7 +884,7 @@ class Domains extends ApiCommand implements ResourceEntity // set the customers default docroot $documentroot = $_documentroot; } - + if ($documentroot == '') { // If path is empty and 'Use domain name as default value for DocumentRoot path' is enabled in settings, // set default path to subdomain or domain name @@ -1041,89 +950,14 @@ class Domains extends ApiCommand implements ResourceEntity $mod_fcgid_maxrequests = $result['mod_fcgid_maxrequests']; } - $ipandports = array(); - if (! empty($p_ipandports) && is_numeric($p_ipandports)) { - $p_ipandports = array($p_ipandports); + // check non-ssl IP + $ipandports = $this->validateIpAddresses($p_ipandports, false, $result['id']); + // check ssl IP + $ssl_ipandports = array(); + if (Settings::Get('system.use_ssl') == "1" && ! empty($p_ssl_ipandports)) { + $ssl_ipandports = $this->validateIpAddresses($p_ssl_ipandports, true, $result['id']); } - if (! empty($p_ipandports) && ! is_array($p_ipandports)) { - $p_ipandports = unserialize($p_ipandports); - } - - if (! empty($p_ipandports) && is_array($p_ipandports)) { - $ipandport_check_stmt = Database::prepare(" - SELECT `id`, `ip`, `port` FROM `" . TABLE_PANEL_IPSANDPORTS . "` WHERE `id` = :ipandport - "); - foreach ($p_ipandports as $ipandport) { - if (trim($ipandport) == "") { - continue; - } - $ipandport = intval($ipandport); - $ipandport_check = Database::pexecute_first($ipandport_check_stmt, array( - 'ipandport' => $ipandport - ), true, true); - if (! isset($ipandport_check['id']) || $ipandport_check['id'] == '0' || $ipandport_check['id'] != $ipandport) { - standard_error('ipportdoesntexist', '', true); - } else { - $ipandports[] = $ipandport; - } - } - } else { - // set currently used ip's - $ipsresult_stmt = Database::prepare(" - SELECT `id_ipandports` FROM `" . TABLE_DOMAINTOIP . "` WHERE `id_domain` = :id - "); - Database::pexecute($ipsresult_stmt, array( - 'id' => $result['id'] - )); - while ($ipsresultrow = $ipsresult_stmt->fetch(PDO::FETCH_ASSOC)) { - $ipandports[] = $ipsresultrow['id_ipandports']; - } - } - - if (Settings::Get('system.use_ssl') == '1' && ! empty($p_ssl_ipandports)) { - $ssl_ipandports = array(); - if (! empty($p_ssl_ipandports) && ! is_array($p_ssl_ipandports)) { - $p_ssl_ipandports = unserialize($p_ssl_ipandports); - } - if (! empty($p_ssl_ipandports) && is_array($p_ssl_ipandports)) { - $ssl_ipandport_check_stmt = Database::prepare(" - SELECT `id`, `ip`, `port` FROM `" . TABLE_PANEL_IPSANDPORTS . "` WHERE `id` = :ipandport - "); - foreach ($p_ssl_ipandports as $ssl_ipandport) { - if (trim($ssl_ipandport) == "") { - continue; - } - // fix if ip/port got de-checked and it was the last one - if (trim($ssl_ipandport) < 1) { - continue; - } - $ssl_ipandport = intval($ssl_ipandport); - $ssl_ipandport_check = Database::pexecute_first($ssl_ipandport_check_stmt, array( - 'ipandport' => $ssl_ipandport - ), true, true); - if (! isset($ssl_ipandport_check['id']) || $ssl_ipandport_check['id'] == '0' || $ssl_ipandport_check['id'] != $ssl_ipandport) { - standard_error('ipportdoesntexist', '', true); - } else { - $ssl_ipandports[] = $ssl_ipandport; - } - } - } else { - $ssl_redirect = 0; - $letsencrypt = 0; - $http2 = 0; - // we need this for the serialize - // if ssl is disabled or no ssl-ip/port exists - $ssl_ipandports[] = - 1; - - // HSTS - $hsts_maxage = 0; - $hsts_sub = 0; - $hsts_preload = 0; - - // OCSP stapling - $ocsp_stapling = 0; - } - } else { + if (Settings::Get('system.use_ssl') == "0" || empty($ssl_ipandports)) { $ssl_redirect = 0; $letsencrypt = 0; $http2 = 0; @@ -1139,7 +973,7 @@ class Domains extends ApiCommand implements ResourceEntity // OCSP stapling $ocsp_stapling = 0; } - + // We can't enable let's encrypt for wildcard domains when using acme-v1 if ($serveraliasoption == '0' && $letsencrypt == '1' && Settings::Get('system.leapiversion') == '1') { standard_error('nowildcardwithletsencrypt', '', true); @@ -1645,7 +1479,7 @@ class Domains extends ApiCommand implements ResourceEntity * optional, remove also domains that are subdomains of this domain but added as main domains; default false * @param bool $is_stdsubdomain * optional, default false, specify whether it's a std-subdomain you are deleting as it does not count as subdomain-resource - * + * * @access admin * @throws Exception * @return array @@ -1658,7 +1492,7 @@ class Domains extends ApiCommand implements ResourceEntity $domainname = $this->getParam('domainname', $dn_optional, ''); $is_stdsubdomain = $this->getParam('is_stdsubdomain', true, 0); $remove_subbutmain_domains = $this->getParam('delete_mainsubdomains', true, 0); - + $result = $this->apiCall('Domains.get', array( 'id' => $id, 'domainname' => $domainname @@ -1788,4 +1622,86 @@ class Domains extends ApiCommand implements ResourceEntity } throw new Exception("Not allowed to execute given command.", 403); } + + /** + * validate given ips + * + * @param int|string|array $p_ipsandports + * @param boolean $edit + * default false + * @param boolean $ssl + * default false + * + * @throws Exception + * @return array + */ + private function validateIpAddresses($p_ipandports = null, $ssl = false, $edit_id = 0) + { + // when adding a new domain and no ip is given, we try to use the + // system-default, check here if there is none + // this is not required for ssl-enabled ip's + if ($edit_id <= 0 && ! $ssl && empty($p_ipandports)) { + throw new Exception("No IPs given, unable to add domain (no default IPs set?)", 406); + } + + // convert given value(s) correctly + $ipandports = array(); + if (! empty($p_ipandports) && is_numeric($p_ipandports)) { + $p_ipandports = array( + $p_ipandports + ); + } + if (! empty($p_ipandports) && ! is_array($p_ipandports)) { + $p_ipandports = unserialize($p_ipandports); + } + + // check whether there are ip usage restrictions + $additional_ip_condition = ''; + $aip_param = array(); + if ($this->getUserDetail('ip') != "-1") { + // handle multiple-ip-array + $additional_ip_condition = " AND `ip` IN (:adminips) "; + $aip_param = array( + 'adminips' => implode(",", json_decode($this->getUserDetail('ip'), true)) + ); + } + + if (! empty($p_ipandports) && is_array($p_ipandports)) { + $ipandport_check_stmt = Database::prepare(" + SELECT `id`, `ip`, `port` + FROM `" . TABLE_PANEL_IPSANDPORTS . "` + WHERE `id` = :ipandport " . ($ssl ? " AND `ssl` = '1'" : "") . $additional_ip_condition); + foreach ($p_ipandports as $ipandport) { + if (trim($ipandport) == "") { + continue; + } + // fix if no ip/port is checked + if (trim($ipandport) < 1) { + continue; + } + $ipandport = intval($ipandport); + $ip_params = array_merge(array( + 'ipandport' => $ipandport + ), $aip_param); + $ipandport_check = Database::pexecute_first($ipandport_check_stmt, $ip_params, true, true); + if (! isset($ipandport_check['id']) || $ipandport_check['id'] == '0' || $ipandport_check['id'] != $ipandport) { + standard_error('ipportdoesntexist', '', true); + } else { + $ipandports[] = $ipandport; + } + } + } elseif ($edit_id > 0) { + // set currently used ip's + $ipsresult_stmt = Database::prepare(" + SELECT `id_ipandports` FROM `" . TABLE_DOMAINTOIP . "` WHERE `id_domain` = :id + "); + Database::pexecute($ipsresult_stmt, array( + 'id' => $edit_id + ), true, true); + while ($ipsresultrow = $ipsresult_stmt->fetch(PDO::FETCH_ASSOC)) { + $ipandports[] = $ipsresultrow['id_ipandports']; + } + } + return $ipandports; + } } From b2e2590324d0d6582ed0e0d4cbc92b835e41ef6d Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 5 Mar 2018 12:13:01 +0100 Subject: [PATCH 0541/1335] implemented SubDomains.update; minor fixes and enhancements in Domains-Command and validateUrl-function Signed-off-by: Michael Kaufmann (d00p) --- customer_domains.php | 234 +----------- lib/classes/api/commands/class.Domains.php | 121 +------ lib/classes/api/commands/class.SubDomains.php | 334 +++++++++++++++--- .../validate/function.validateUrl.php | 32 +- tests/Domains/DomainsTest.php | 5 +- tests/SubDomains/SubDomainsTest.php | 84 ++++- 6 files changed, 402 insertions(+), 408 deletions(-) diff --git a/customer_domains.php b/customer_domains.php index e48c48f1..8cc3bf50 100644 --- a/customer_domains.php +++ b/customer_domains.php @@ -299,231 +299,23 @@ if ($page == 'overview') { } } elseif ($action == 'edit' && $id != 0) { - $stmt = Database::prepare("SELECT `d`.*, `pd`.`subcanemaildomain` - FROM `" . TABLE_PANEL_DOMAINS . "` `d`, `" . TABLE_PANEL_DOMAINS . "` `pd` - WHERE `d`.`customerid` = :customerid - AND `d`.`id` = :id - AND ((`d`.`parentdomainid`!='0' - AND `pd`.`id` = `d`.`parentdomainid`) - OR (`d`.`parentdomainid`='0' - AND `pd`.`id` = `d`.`id`)) - AND `d`.`caneditdomain`='1'"); - $result = Database::pexecute_first($stmt, array("customerid" => $userinfo['customerid'], "id" => $id)); - - $alias_stmt = Database::prepare("SELECT COUNT(`id`) AS count FROM `" . TABLE_PANEL_DOMAINS . "` WHERE `aliasdomain`= :aliasdomain"); - $alias_check = Database::pexecute_first($alias_stmt, array("aliasdomain" => $result['id'])); - $alias_check = $alias_check['count']; - $_doredirect = false; + try { + $json_result = SubDomains::getLocal($userinfo, array( + 'id' => $id + ))->get(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); + } + $result = json_decode($json_result, true)['data']; if (isset($result['customerid']) && $result['customerid'] == $userinfo['customerid']) { if (isset($_POST['send']) && $_POST['send'] == 'send') { - if (isset($_POST['url']) && $_POST['url'] != '' && validateUrl($_POST['url'])) { - $path = $_POST['url']; - $_doredirect = true; - } else { - $path = validate($_POST['path'], 'path'); - } - - if (!preg_match('/^https?\:\/\//', $path) || !validateUrl($path)) { - if (strstr($path, ":") !== FALSE) { - standard_error('pathmaynotcontaincolon'); - } - // If path is empty or '/' and 'Use domain name as default value for DocumentRoot path' is enabled in settings, - // set default path to subdomain or domain name - if ((($path == '') || ($path == '/')) && Settings::Get('system.documentroot_use_default_value') == 1) { - $path = makeCorrectDir($userinfo['documentroot'] . '/' . $result['domain']); - } else { - $path = makeCorrectDir($userinfo['documentroot'] . '/' . $path); - } - } else { - $_doredirect = true; - } - - $aliasdomain = isset($_POST['alias']) ? intval($_POST['alias']) : 0; - - if (isset($_POST['selectserveralias'])) { - $iswildcarddomain = ($_POST['selectserveralias'] == '0') ? '1' : '0'; - $wwwserveralias = ($_POST['selectserveralias'] == '1') ? '1' : '0'; - } else { - $iswildcarddomain = $result['iswildcarddomain']; - $wwwserveralias = $result['wwwserveralias']; - } - - if ($result['parentdomainid'] != '0' && ($result['subcanemaildomain'] == '1' || $result['subcanemaildomain'] == '2') && isset($_POST['isemaildomain'])) { - $isemaildomain = intval($_POST['isemaildomain']); - } else { - $isemaildomain = $result['isemaildomain']; - } - - $aliasdomain_check = array('id' => 0); - - if ($aliasdomain != 0) { - $aliasdomain_stmt = Database::prepare("SELECT `id` FROM `" . TABLE_PANEL_DOMAINS . "` `d`,`" . TABLE_PANEL_CUSTOMERS . "` `c` - WHERE `d`.`customerid`= :customerid - AND `d`.`aliasdomain` IS NULL - AND `d`.`id`<>`c`.`standardsubdomain` - AND `c`.`customerid`= :customerid - AND `d`.`id`= :id" - ); - $aliasdomain_check = Database::pexecute_first($aliasdomain_stmt, array("customerid" => $result['customerid'], "id" => $aliasdomain)); - } - - if ($aliasdomain_check['id'] != $aliasdomain) { - standard_error('domainisaliasorothercustomer'); - } - - if (isset($_POST['openbasedir_path']) && $_POST['openbasedir_path'] == '1') { - $openbasedir_path = '1'; - } else { - $openbasedir_path = '0'; - } - - // check whether the customer has chosen its own php-config - if (isset($_POST['phpsettingid'])) { - $phpsettingid = intval($_POST['phpsettingid']); - } else { - $phpsettingid = $result['phpsettingid']; - } - - if (isset($_POST['ssl_redirect']) && $_POST['ssl_redirect'] == '1') { - // a ssl-redirect only works if there actually is a - // ssl ip/port assigned to the domain - if (domainHasSslIpPort($id) == true) { - $ssl_redirect = '1'; - $_doredirect = true; - } else { - standard_error('sslredirectonlypossiblewithsslipport'); - } - } else { - $ssl_redirect = '0'; - } - - if (isset($_POST['letsencrypt']) && $_POST['letsencrypt'] == '1') { - // let's encrypt only works if there actually is a - // ssl ip/port assigned to the domain - if (domainHasSslIpPort($id) == true) { - $letsencrypt = '1'; - } else { - standard_error('letsencryptonlypossiblewithsslipport'); - } - } else { - $letsencrypt = '0'; - } - - // We can't enable let's encrypt for wildcard - domains when using acme-v1 - if ($iswildcarddomain == '1' && $letsencrypt == '1' && Settings::Get('system.leapiversion') == '1') { - standard_error('nowildcardwithletsencrypt'); - } - // if using acme-v2 we cannot issue wildcard-certificates - // because they currently only support the dns-01 challenge - if ($iswildcarddomain == '0' && $letsencrypt == '1' && Settings::Get('system.leapiversion') == '2') { - standard_error('nowildcardwithletsencryptv2'); - } - - // Temporarily deactivate ssl_redirect until Let's Encrypt certificate was generated - if ($ssl_redirect > 0 && $letsencrypt == 1 && $result['letsencrypt'] != $letsencrypt) { - $ssl_redirect = 2; - } - - // HSTS - $hsts_maxage = isset($_POST['hsts_maxage']) ? (int)$_POST['hsts_maxage'] : 0; - $hsts_sub = isset($_POST['hsts_sub']) && (int)$_POST['hsts_sub'] == 1 ? 1 : 0; - $hsts_preload = isset($_POST['hsts_preload']) && (int)$_POST['hsts_preload'] == 1 ? 1 : 0; - - if ($path == '') { - standard_error('patherror'); - } else { - if (($result['isemaildomain'] == '1') && ($isemaildomain == '0')) { - $params = array("customerid" => $userinfo['customerid'], "domainid" => $id); - $stmt = Database::prepare("DELETE FROM `" . TABLE_MAIL_USERS . "` WHERE `customerid`= :customerid AND `domainid`= :domainid"); - Database::pexecute($stmt, $params); - $stmt = Database::prepare("DELETE FROM `" . TABLE_MAIL_VIRTUAL . "` WHERE `customerid`= :customerid AND `domainid`= :domainid"); - Database::pexecute($stmt, $params); - $log->logAction(USR_ACTION, LOG_NOTICE, "automatically deleted mail-table entries for '" . $idna_convert->decode($result['domain']) . "'"); - } - - if ($_doredirect) { - $redirect = isset($_POST['redirectcode']) ? (int)$_POST['redirectcode'] : false; - updateRedirectOfDomain($id, $redirect); - } - - if ($path != $result['documentroot'] - || $isemaildomain != $result['isemaildomain'] - || $wwwserveralias != $result['wwwserveralias'] - || $iswildcarddomain != $result['iswildcarddomain'] - || $aliasdomain != $result['aliasdomain'] - || $openbasedir_path != $result['openbasedir_path'] - || $ssl_redirect != $result['ssl_redirect'] - || $letsencrypt != $result['letsencrypt'] - || $hsts_maxage != $result['hsts'] - || $hsts_sub != $result['hsts_sub'] - || $hsts_preload != $result['hsts_preload'] - || $phpsettingid != $result['phpsettingid'] - ) { - $log->logAction(USR_ACTION, LOG_INFO, "edited domain '" . $idna_convert->decode($result['domain']) . "'"); - - $stmt = Database::prepare("UPDATE `" . TABLE_PANEL_DOMAINS . "` SET - `documentroot`= :documentroot, - `isemaildomain`= :isemaildomain, - `wwwserveralias`= :wwwserveralias, - `iswildcarddomain`= :iswildcarddomain, - `aliasdomain`= :aliasdomain, - `openbasedir_path`= :openbasedir_path, - `ssl_redirect`= :ssl_redirect, - `letsencrypt`= :letsencrypt, - `hsts` = :hsts, - `hsts_sub` = :hsts_sub, - `hsts_preload` = :hsts_preload, - `phpsettingid` = :phpsettingid - WHERE `customerid`= :customerid - AND `id`= :id" - ); - $params = array( - "documentroot" => $path, - "isemaildomain" => $isemaildomain, - "wwwserveralias" => $wwwserveralias, - "iswildcarddomain" => $iswildcarddomain, - "aliasdomain" => ($aliasdomain != 0 && $alias_check == 0) ? $aliasdomain : null, - "openbasedir_path" => $openbasedir_path, - "ssl_redirect" => $ssl_redirect, - "letsencrypt" => $letsencrypt, - "hsts" => $hsts_maxage, - "hsts_sub" => $hsts_sub, - "hsts_preload" => $hsts_preload, - "phpsettingid" => $phpsettingid, - "customerid" => $userinfo['customerid'], - "id" => $id - ); - Database::pexecute($stmt, $params); - - if ($result['aliasdomain'] != $aliasdomain) { - // trigger when domain id for alias destination has changed: both for old and new destination - triggerLetsEncryptCSRForAliasDestinationDomain($result['aliasdomain'], $log); - triggerLetsEncryptCSRForAliasDestinationDomain($aliasdomain, $log); - } elseif ($result['wwwserveralias'] != $wwwserveralias || $result['letsencrypt'] != $letsencrypt) { - // or when wwwserveralias or letsencrypt was changed - triggerLetsEncryptCSRForAliasDestinationDomain($aliasdomain, $log); - } - - // check whether LE has been disabled, so we remove the certificate - if ($letsencrypt == '0' && $result['letsencrypt'] == '1') { - $del_stmt = Database::prepare(" - DELETE FROM `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` WHERE `domainid` = :id - "); - Database::pexecute($del_stmt, array( - 'id' => $id - )); - } - - inserttask('1'); - - // Using nameserver, insert a task which rebuilds the server config - inserttask('4'); - - } - - redirectTo($filename, array('page' => $page, 's' => $s)); + try { + SubDomains::getLocal($userinfo, $_POST)->update(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); } + redirectTo($filename, array('page' => $page, 's' => $s)); } else { $result['domain'] = $idna_convert->decode($result['domain']); diff --git a/lib/classes/api/commands/class.Domains.php b/lib/classes/api/commands/class.Domains.php index 47a9c209..364198a7 100644 --- a/lib/classes/api/commands/class.Domains.php +++ b/lib/classes/api/commands/class.Domains.php @@ -469,66 +469,6 @@ class Domains extends ApiCommand implements ResourceEntity } elseif ($aliasdomain_check['id'] != $aliasdomain) { standard_error('domainisaliasorothercustomer', '', true); } else { - - /** - * - * @todo how to handle security questions now? - * - * $params = array( - * 'page' => $page, - * 'action' => $action, - * 'domain' => $domain, - * 'customerid' => $customerid, - * 'adminid' => $adminid, - * 'documentroot' => $documentroot, - * 'alias' => $aliasdomain, - * 'isbinddomain' => $isbinddomain, - * 'isemaildomain' => $isemaildomain, - * 'email_only' => $email_only, - * 'subcanemaildomain' => $subcanemaildomain, - * 'caneditdomain' => $caneditdomain, - * 'zonefile' => $zonefile, - * 'dkim' => $dkim, - * 'speciallogfile' => $speciallogfile, - * 'selectserveralias' => $serveraliasoption, - * 'ipandport' => serialize($ipandports), - * 'ssl_redirect' => $ssl_redirect, - * 'ssl_ipandport' => serialize($ssl_ipandports), - * 'phpenabled' => $phpenabled, - * 'openbasedir' => $openbasedir, - * 'phpsettingid' => $phpsettingid, - * 'mod_fcgid_starter' => $mod_fcgid_starter, - * 'mod_fcgid_maxrequests' => $mod_fcgid_maxrequests, - * 'specialsettings' => $specialsettings, - * 'notryfiles' => $notryfiles, - * 'registration_date' => $registration_date, - * 'termination_date' => $termination_date, - * 'issubof' => $issubof, - * 'letsencrypt' => $letsencrypt, - * 'http2' => $http2, - * 'hsts_maxage' => $hsts_maxage, - * 'hsts_sub' => $hsts_sub, - * 'hsts_preload' => $hsts_preload, - * 'ocsp_stapling' => $ocsp_stapling - * ); - * - * $security_questions = array( - * 'reallydisablesecuritysetting' => ($openbasedir == '0' && $userinfo['change_serversettings'] == '1'), - * 'reallydocrootoutofcustomerroot' => (substr($documentroot, 0, strlen($customer['documentroot'])) != $customer['documentroot'] && ! preg_match('/^https?\:\/\//', $documentroot)) - * ); - * $question_nr = 1; - * foreach ($security_questions as $question_name => $question_launch) { - * if ($question_launch !== false) { - * $params[$question_name] = $question_name; - * - * if (! isset($_POST[$question_name]) || $_POST[$question_name] != $question_name) { - * ask_yesno('admin_domain_' . $question_name, $filename, $params, $question_nr); - * } - * } - * $question_nr ++; - * } - */ - $wwwserveralias = ($serveraliasoption == '1') ? '1' : '0'; $iswildcarddomain = ($serveraliasoption == '0') ? '1' : '0'; @@ -1087,66 +1027,7 @@ class Domains extends ApiCommand implements ResourceEntity if ($serveraliasoption != '1' && $serveraliasoption != '2') { $serveraliasoption = '0'; } - - /** - * - * @todo how to handle security questions now? - * - * $params = array( - * 'id' => $id, - * 'page' => $page, - * 'action' => $action, - * 'customerid' => $customerid, - * 'adminid' => $adminid, - * 'documentroot' => $documentroot, - * 'alias' => $aliasdomain, - * 'isbinddomain' => $isbinddomain, - * 'isemaildomain' => $isemaildomain, - * 'email_only' => $email_only, - * 'subcanemaildomain' => $subcanemaildomain, - * 'caneditdomain' => $caneditdomain, - * 'zonefile' => $zonefile, - * 'dkim' => $dkim, - * 'selectserveralias' => $serveraliasoption, - * 'ssl_redirect' => $ssl_redirect, - * 'phpenabled' => $phpenabled, - * 'openbasedir' => $openbasedir, - * 'phpsettingid' => $phpsettingid, - * 'phpsettingsforsubdomains' => $phpfs, - * 'mod_fcgid_starter' => $mod_fcgid_starter, - * 'mod_fcgid_maxrequests' => $mod_fcgid_maxrequests, - * 'specialsettings' => $specialsettings, - * 'specialsettingsforsubdomains' => $ssfs, - * 'notryfiles' => $notryfiles, - * 'registration_date' => $registration_date, - * 'termination_date' => $termination_date, - * 'issubof' => $issubof, - * 'speciallogfile' => $speciallogfile, - * 'speciallogverified' => $speciallogverified, - * 'ipandport' => serialize($ipandports), - * 'ssl_ipandport' => serialize($ssl_ipandports), - * 'letsencrypt' => $letsencrypt, - * 'http2' => $http2, - * 'hsts_maxage' => $hsts_maxage, - * 'hsts_sub' => $hsts_sub, - * 'hsts_preload' => $hsts_preload, - * 'ocsp_stapling' => $ocsp_stapling - * ); - * - * $security_questions = array( - * 'reallydisablesecuritysetting' => ($openbasedir == '0' && $userinfo['change_serversettings'] == '1'), - * 'reallydocrootoutofcustomerroot' => (substr($documentroot, 0, strlen($customer['documentroot'])) != $customer['documentroot'] && ! preg_match('/^https?\:\/\//', $documentroot)) - * ); - * foreach ($security_questions as $question_name => $question_launch) { - * if ($question_launch !== false) { - * $params[$question_name] = $question_name; - * if (! isset($_POST[$question_name]) || $_POST[$question_name] != $question_name) { - * ask_yesno('admin_domain_' . $question_name, $filename, $params); - * } - * } - * } - */ - + $wwwserveralias = ($serveraliasoption == '1') ? '1' : '0'; $iswildcarddomain = ($serveraliasoption == '0') ? '1' : '0'; diff --git a/lib/classes/api/commands/class.SubDomains.php b/lib/classes/api/commands/class.SubDomains.php index d61c75bd..6ef6592a 100644 --- a/lib/classes/api/commands/class.SubDomains.php +++ b/lib/classes/api/commands/class.SubDomains.php @@ -62,6 +62,7 @@ class SubDomains extends ApiCommand implements ResourceEntity $openbasedir_path = $this->getParam('openbasedir_path', true, 1); $phpsettingid = $this->getParam('phpsettingid', true, 0); $redirectcode = $this->getParam('redirectcode', true, Settings::Get('customredirect.default')); + $isemaildomain = $this->getParam('isemaildomain', true, 0); if (Settings::Get('system.use_ssl')) { $ssl_redirect = $this->getParam('ssl_redirect', true, 0); $letsencrypt = $this->getParam('letsencrypt', true, 0); @@ -76,7 +77,7 @@ class SubDomains extends ApiCommand implements ResourceEntity $hsts_preload = 0; } - // get needed customer info to reduce the mysql-usage-counter by one + // get needed customer info to reduce the subdomain-usage-counter by one if ($this->isAdmin()) { // get customer id $customer_id = $this->getParam('customer_id'); @@ -115,7 +116,7 @@ class SubDomains extends ApiCommand implements ResourceEntity if ($completedomain == Settings::Get('system.hostname')) { standard_error('admin_domain_emailsystemhostname', '', true); } - + // check whether the domain already exists $completedomain_stmt = Database::prepare(" SELECT * FROM `" . TABLE_PANEL_DOMAINS . "` @@ -128,12 +129,12 @@ class SubDomains extends ApiCommand implements ResourceEntity "domain" => $completedomain, "customerid" => $customer_id ), true, true); - + if ($completedomain_check) { // no exception so far - domain exists standard_error('domainexistalready', $completedomain, true); } - + // alias domain checked? if ($aliasdomain != 0) { // also check ip/port combination to be the same, #176 @@ -160,36 +161,13 @@ class SubDomains extends ApiCommand implements ResourceEntity triggerLetsEncryptCSRForAliasDestinationDomain($aliasdomain, $this->logger()); } - // check whether an URL was specified - $_doredirect = false; - if (! empty($url) && validateUrl($url)) { - $path = $url; - $_doredirect = true; - } else { - $path = validate($path, 'path', '', '', array(), true); - } - - // check whether path is a real path - if (! preg_match('/^https?\:\/\//', $path) || ! validateUrl($path)) { - if (strstr($path, ":") !== false) { - standard_error('pathmaynotcontaincolon', '', true); - } - // If path is empty or '/' and 'Use domain name as default value for DocumentRoot path' is enabled in settings, - // set default path to subdomain or domain name - if ((($path == '') || ($path == '/')) && Settings::Get('system.documentroot_use_default_value') == 1) { - $path = makeCorrectDir($customer['documentroot'] . '/' . $completedomain); - } else { - $path = makeCorrectDir($customer['documentroot'] . '/' . $path); - } - } else { - // no it's not, create a redirect - $_doredirect = true; - } + // validate / correct path/url of domain + $path = $this->validateDomainDocumentRoot($path, $url, $customer, $completedomain, $_doredirect); if ($openbasedir_path != 1) { $openbasedir_path = 0; } - + // get main domain for various checks $domain_stmt = Database::prepare(" SELECT * FROM `" . TABLE_PANEL_DOMAINS . "` @@ -214,7 +192,14 @@ class SubDomains extends ApiCommand implements ResourceEntity // the domain does already exist as main-domain standard_error('domainexistalready', $completedomain, true); } - + + // if allowed, check for 'is email domain'-flag + if ($domain_check['subcanemaildomain'] == '1' || $domain_check['subcanemaildomain'] == '2') { + $isemaildomain = intval($isemaildomain); + } else { + $isemaildomain = $domain_check['subcanemaildomain'] == '3' ? 1 : 0; + } + if ($ssl_redirect != 0) { // a ssl-redirect only works if there actually is a // ssl ip/port assigned to the domain @@ -289,7 +274,7 @@ class SubDomains extends ApiCommand implements ResourceEntity "parentdomainid" => $domain_check['id'], "wwwserveralias" => $domain_check['wwwserveralias'] == '1' ? '1' : '0', "iswildcarddomain" => $domain_check['iswildcarddomain'] == '1' ? '1' : '0', - "isemaildomain" => $domain_check['subcanemaildomain'] == '3' ? '1' : '0', + "isemaildomain" => $isemaildomain, "openbasedir" => $domain_check['openbasedir'], "openbasedir_path" => $openbasedir_path, "phpenabled" => $domain_check['phpenabled'], @@ -328,7 +313,7 @@ class SubDomains extends ApiCommand implements ResourceEntity Admins::increaseUsage(($this->isAdmin() ? $customer['adminid'] : $this->getUserDetail('adminid')), 'subdomains_used'); $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_INFO, "[API] added subdomain '" . $completedomain . "'"); - + $result = $this->apiCall('SubDomains.get', array( 'id' => $subdomain_id )); @@ -373,8 +358,10 @@ class SubDomains extends ApiCommand implements ResourceEntity } if (count($customer_ids) > 0) { $result_stmt = Database::prepare(" - SELECT * FROM `" . TABLE_PANEL_DOMAINS . "` - WHERE " . ($id > 0 ? "`id` = :iddn" : "`domain` = :iddn") . " AND `customerid` IN (:customerids) + SELECT d.*, pd.`subcanemaildomain` + FROM `" . TABLE_PANEL_DOMAINS . "` d, `" . TABLE_PANEL_DOMAINS . "` pd + WHERE " . ($id > 0 ? "d.`id` = :iddn" : "d.`domain` = :iddn") . " AND d.`customerid` IN (:customerids) + AND ((d.`parentdomainid`!='0' AND pd.`id` = d.`parentdomainid`) OR (d.`parentdomainid`='0' AND pd.`id` = d.`id`)) "); $params = array( 'iddn' => ($id <= 0 ? $domainname : $id), @@ -385,8 +372,11 @@ class SubDomains extends ApiCommand implements ResourceEntity } } else { $result_stmt = Database::prepare(" - SELECT * FROM `" . TABLE_PANEL_DOMAINS . "` - WHERE " . ($id > 0 ? "`id` = :iddn" : "`domain` = :iddn")); + SELECT d.*, pd.`subcanemaildomain` + FROM `" . TABLE_PANEL_DOMAINS . "` d, `" . TABLE_PANEL_DOMAINS . "` pd + WHERE " . ($id > 0 ? "d.`id` = :iddn" : "d.`domain` = :iddn") . " + AND ((d.`parentdomainid`!='0' AND pd.`id` = d.`parentdomainid`) OR (d.`parentdomainid`='0' AND pd.`id` = d.`id`)) + "); $params = array( 'iddn' => ($id <= 0 ? $domainname : $id) ); @@ -396,8 +386,11 @@ class SubDomains extends ApiCommand implements ResourceEntity throw new Exception("You cannot access this resource", 405); } $result_stmt = Database::prepare(" - SELECT `id`, `customerid`, `domain`, `documentroot`, `isemaildomain`, `parentdomainid`, `aliasdomain`, `caneditdomain` FROM `" . TABLE_PANEL_DOMAINS . "` - WHERE `customerid`= :customerid AND " . ($id > 0 ? "`id` = :iddn" : "`domain` = :iddn")); + SELECT d.*, pd.`subcanemaildomain` + FROM `" . TABLE_PANEL_DOMAINS . "` d, `" . TABLE_PANEL_DOMAINS . "` pd + WHERE d.`customerid`= :customerid AND " . ($id > 0 ? "d.`id` = :iddn" : "d.`domain` = :iddn") . " + AND ((d.`parentdomainid`!='0' AND pd.`id` = d.`parentdomainid`) OR (d.`parentdomainid`='0' AND pd.`id` = d.`id`)) + "); $params = array( 'customerid' => $this->getUserDetail('customerid'), 'iddn' => ($id <= 0 ? $domainname : $id) @@ -414,7 +407,225 @@ class SubDomains extends ApiCommand implements ResourceEntity public function update() { - throw new Exception("Not available yet.", 501); + $id = $this->getParam('id', true, 0); + $dn_optional = ($id <= 0 ? false : true); + $domainname = $this->getParam('domainname', $dn_optional, ''); + + if ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'domains')) { + throw new Exception("You cannot access this resource", 405); + } + + $result = $this->apiCall('SubDomains.get', array( + 'id' => $id, + 'domainname' => $domainname + )); + $id = $result['id']; + + // parameters + $aliasdomain = $this->getParam('alias', true, 0); + $path = $this->getParam('path', true, $result['documentroot']); + $url = $this->getParam('url', true, ''); + // default: 0 = wildcard, 1 = www-alias, 2 = none + $_serveraliasdefault = $result['iswildcarddomain'] == '1' ? 0 : ($result['wwwserveralias'] == '1' ? 1 : 2); + $selectserveralias = $this->getParam('selectserveralias', true, $_serveraliasdefault); + $isemaildomain = $this->getParam('isemaildomain', true, $result['isemaildomain']); + $openbasedir_path = $this->getParam('openbasedir_path', true, $result['openbasedir_path']); + $phpsettingid = $this->getParam('phpsettingid', true, $result['phpsettingid']); + $redirectcode = $this->getParam('redirectcode', true, getDomainRedirectId($id)); + if (Settings::Get('system.use_ssl')) { + $ssl_redirect = $this->getParam('ssl_redirect', true, $result['ssl_redirect']); + $letsencrypt = $this->getParam('letsencrypt', true, $result['letsencrypt']); + $hsts_maxage = $this->getParam('hsts_maxage', true, $result['hsts']); + $hsts_sub = $this->getParam('hsts_sub', true, $result['hsts_sub']); + $hsts_preload = $this->getParam('hsts_preload', true, $result['hsts_preload']); + } else { + $ssl_redirect = 0; + $letsencrypt = 0; + $hsts_maxage = 0; + $hsts_sub = 0; + $hsts_preload = 0; + } + + // get needed customer info to reduce the subdomain-usage-counter by one + if ($this->isAdmin()) { + // get customer id + $customer_id = $this->getParam('customer_id'); + $customer = $this->apiCall('Customers.get', array( + 'id' => $customer_id + )); + // check whether the customer has enough resources to get the subdomain added + if ($customer['subdomains_used'] >= $customer['subdomains'] && $customer['subdomains'] != '-1') { + throw new Exception("Customer has no more resources available", 406); + } + } else { + $customer_id = $this->getUserDetail('customerid'); + $customer = $this->getUserData(); + } + + $alias_stmt = Database::prepare("SELECT COUNT(`id`) AS count FROM `" . TABLE_PANEL_DOMAINS . "` WHERE `aliasdomain`= :aliasdomain"); + $alias_check = Database::pexecute_first($alias_stmt, array( + "aliasdomain" => $result['id'] + )); + $alias_check = $alias_check['count']; + + // alias domain checked? + if ($aliasdomain != 0) { + $aliasdomain_stmt = Database::prepare(" + SELECT `id` FROM `" . TABLE_PANEL_DOMAINS . "` `d`,`" . TABLE_PANEL_CUSTOMERS . "` `c` + WHERE `d`.`customerid`= :customerid + AND `d`.`aliasdomain` IS NULL + AND `d`.`id`<>`c`.`standardsubdomain` + AND `c`.`customerid`= :customerid + AND `d`.`id`= :id + "); + $aliasdomain_check = Database::pexecute_first($aliasdomain_stmt, array( + "id" => $aliasdomain, + "customerid" => $customer_id + ), true, true); + if ($aliasdomain_check['id'] != $aliasdomain) { + standard_error('domainisaliasorothercustomer', '', true); + } + triggerLetsEncryptCSRForAliasDestinationDomain($aliasdomain, $this->logger()); + } + + // validate / correct path/url of domain + $path = $this->validateDomainDocumentRoot($path, $url, $customer, $result['domain'], $_doredirect); + + // set alias-fields according to selected alias mode + $iswildcarddomain = ($selectserveralias == '0') ? '1' : '0'; + $wwwserveralias = ($selectserveralias == '1') ? '1' : '0'; + + // if allowed, check for 'is email domain'-flag + if ($result['parentdomainid'] != '0' && ($result['subcanemaildomain'] == '1' || $result['subcanemaildomain'] == '2') && $isemaildomain != $result['isemaildomain']) { + $isemaildomain = intval($isemaildomain); + } else { + $isemaildomain = $result['subcanemaildomain'] == '3' ? 1 : 0; + } + + // check changes of openbasedir-path variable + if ($openbasedir_path != 1) { + $openbasedir_path = 0; + } + + if ($ssl_redirect != 0) { + // a ssl-redirect only works if there actually is a + // ssl ip/port assigned to the domain + if (domainHasSslIpPort($result['id']) == true) { + $ssl_redirect = '1'; + $_doredirect = true; + } else { + standard_error('sslredirectonlypossiblewithsslipport', '', true); + } + } + + if ($letsencrypt != 0) { + // let's encrypt only works if there actually is a + // ssl ip/port assigned to the domain + if (domainHasSslIpPort($result['id']) == true) { + $letsencrypt = '1'; + } else { + standard_error('letsencryptonlypossiblewithsslipport', '', true); + } + } + + // We can't enable let's encrypt for wildcard - domains when using acme-v1 + if ($iswildcarddomain == '1' && $letsencrypt == '1' && Settings::Get('system.leapiversion') == '1') { + standard_error('nowildcardwithletsencrypt'); + } + // if using acme-v2 we cannot issue wildcard-certificates + // because they currently only support the dns-01 challenge + if ($iswildcarddomain == '0' && $letsencrypt == '1' && Settings::Get('system.leapiversion') == '2') { + standard_error('nowildcardwithletsencryptv2'); + } + + // Temporarily deactivate ssl_redirect until Let's Encrypt certificate was generated + if ($ssl_redirect > 0 && $letsencrypt == 1 && $result['letsencrypt'] != $letsencrypt) { + $ssl_redirect = 2; + } + + // is-email-domain flag changed - remove mail accounts and mail-addresses + if (($result['isemaildomain'] == '1') && $isemaildomain == '0') { + $params = array( + "customerid" => $customer['customerid'], + "domainid" => $id + ); + $stmt = Database::prepare("DELETE FROM `" . TABLE_MAIL_USERS . "` WHERE `customerid`= :customerid AND `domainid`= :domainid"); + Database::pexecute($stmt, $params, true, true); + $stmt = Database::prepare("DELETE FROM `" . TABLE_MAIL_VIRTUAL . "` WHERE `customerid`= :customerid AND `domainid`= :domainid"); + Database::pexecute($stmt, $params, true, true); + $idna_convert = new idna_convert_wrapper(); + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_NOTICE, "[API] automatically deleted mail-table entries for '" . $idna_convert->decode($result['domain']) . "'"); + } + + // handle redirect + if ($_doredirect) { + updateRedirectOfDomain($id, $redirectcode); + } + + if ($path != $result['documentroot'] || $isemaildomain != $result['isemaildomain'] || $wwwserveralias != $result['wwwserveralias'] || $iswildcarddomain != $result['iswildcarddomain'] || $aliasdomain != $result['aliasdomain'] || $openbasedir_path != $result['openbasedir_path'] || $ssl_redirect != $result['ssl_redirect'] || $letsencrypt != $result['letsencrypt'] || $hsts_maxage != $result['hsts'] || $hsts_sub != $result['hsts_sub'] || $hsts_preload != $result['hsts_preload'] || $phpsettingid != $result['phpsettingid']) { + $stmt = Database::prepare(" + UPDATE `" . TABLE_PANEL_DOMAINS . "` SET + `documentroot`= :documentroot, + `isemaildomain`= :isemaildomain, + `wwwserveralias`= :wwwserveralias, + `iswildcarddomain`= :iswildcarddomain, + `aliasdomain`= :aliasdomain, + `openbasedir_path`= :openbasedir_path, + `ssl_redirect`= :ssl_redirect, + `letsencrypt`= :letsencrypt, + `hsts` = :hsts, + `hsts_sub` = :hsts_sub, + `hsts_preload` = :hsts_preload, + `phpsettingid` = :phpsettingid + WHERE `customerid`= :customerid AND `id`= :id + "); + $params = array( + "documentroot" => $path, + "isemaildomain" => $isemaildomain, + "wwwserveralias" => $wwwserveralias, + "iswildcarddomain" => $iswildcarddomain, + "aliasdomain" => ($aliasdomain != 0 && $alias_check == 0) ? $aliasdomain : null, + "openbasedir_path" => $openbasedir_path, + "ssl_redirect" => $ssl_redirect, + "letsencrypt" => $letsencrypt, + "hsts" => $hsts_maxage, + "hsts_sub" => $hsts_sub, + "hsts_preload" => $hsts_preload, + "phpsettingid" => $phpsettingid, + "customerid" => $customer['customerid'], + "id" => $id + ); + Database::pexecute($stmt, $params, true, true); + + if ($result['aliasdomain'] != $aliasdomain) { + // trigger when domain id for alias destination has changed: both for old and new destination + triggerLetsEncryptCSRForAliasDestinationDomain($result['aliasdomain'], $this->logger()); + triggerLetsEncryptCSRForAliasDestinationDomain($aliasdomain, $this->logger()); + } elseif ($result['wwwserveralias'] != $wwwserveralias || $result['letsencrypt'] != $letsencrypt) { + // or when wwwserveralias or letsencrypt was changed + triggerLetsEncryptCSRForAliasDestinationDomain($aliasdomain, $this->logger()); + } + + // check whether LE has been disabled, so we remove the certificate + if ($letsencrypt == '0' && $result['letsencrypt'] == '1') { + $del_stmt = Database::prepare(" + DELETE FROM `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` WHERE `domainid` = :id + "); + Database::pexecute($del_stmt, array( + 'id' => $id + ), true, true); + } + + inserttask('1'); + inserttask('4'); + + $idna_convert = new idna_convert_wrapper(); + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_INFO, "[API] edited domain '" . $idna_convert->decode($result['domain']) . "'"); + } + $result = $this->apiCall('SubDomains.get', array( + 'id' => $id + )); + return $this->response(200, "successfull", $result); } public function listing() @@ -503,7 +714,7 @@ class SubDomains extends ApiCommand implements ResourceEntity if ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'domains')) { throw new Exception("You cannot access this resource", 405); } - + $result = $this->apiCall('SubDomains.get', array( 'id' => $id, 'domainname' => $domainname @@ -600,4 +811,45 @@ class SubDomains extends ApiCommand implements ResourceEntity $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_WARNING, "[API] deleted subdomain '" . $result['domain'] . "'"); return $this->response(200, "successfull", $result); } + + /** + * validate given path and replace with url if given and valid + * + * @param string $path + * @param string $url + * @param array $customer + * @param string $completedomain + * @param boolean $_doredirect + * + * @return string validated path + */ + private function validateDomainDocumentRoot($path = null, $url = null, $customer = null, $completedomain = null, &$_doredirect = false) + { + // check whether an URL was specified + $_doredirect = false; + if (! empty($url) && validateUrl($url)) { + $path = $url; + $_doredirect = true; + } else { + $path = validate($path, 'path', '', '', array(), true); + } + + // check whether path is a real path + if (! preg_match('/^https?\:\/\//', $path) || ! validateUrl($path)) { + if (strstr($path, ":") !== false) { + standard_error('pathmaynotcontaincolon', '', true); + } + // If path is empty or '/' and 'Use domain name as default value for DocumentRoot path' is enabled in settings, + // set default path to subdomain or domain name + if ((($path == '') || ($path == '/')) && Settings::Get('system.documentroot_use_default_value') == 1) { + $path = makeCorrectDir($customer['documentroot'] . '/' . $completedomain); + } else { + $path = makeCorrectDir($customer['documentroot'] . '/' . $path); + } + } else { + // no it's not, create a redirect + $_doredirect = true; + } + return $path; + } } diff --git a/lib/functions/validate/function.validateUrl.php b/lib/functions/validate/function.validateUrl.php index 0515e48d..680a7a4d 100644 --- a/lib/functions/validate/function.validateUrl.php +++ b/lib/functions/validate/function.validateUrl.php @@ -20,53 +20,49 @@ /** * Returns whether a URL is in a correct format or not * - * @param string URL to be tested + * @param + * string URL to be tested * @return bool * @author Christian Hoffmann * @author Froxlor team (2010-) - * + * */ -function validateUrl($url) { - - global $idna_convert, $theme; - - if (strtolower(substr($url, 0, 7)) != "http://" - && strtolower(substr($url, 0, 8)) != "https://" - ) { +function validateUrl($url) +{ + if (strtolower(substr($url, 0, 7)) != "http://" && strtolower(substr($url, 0, 8)) != "https://") { $url = 'http://' . $url; } - + // needs converting try { + $idna_convert = new idna_convert_wrapper(); $url = $idna_convert->encode($url); } catch (Exception $e) { return false; } - + $pattern = "/^https?:\/\/[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,4}(\:[0-9]+)?\/?(.+)?$/i"; if (preg_match($pattern, $url)) { return true; } - + // not an fqdn - if (strtolower(substr($url, 0, 7)) == "http://" - || strtolower(substr($url, 0, 8)) == "https://" - ) { + if (strtolower(substr($url, 0, 7)) == "http://" || strtolower(substr($url, 0, 8)) == "https://") { if (strtolower(substr($url, 0, 7)) == "http://") { $ip = strtolower(substr($url, 7)); } - + if (strtolower(substr($url, 0, 8)) == "https://") { $ip = strtolower(substr($url, 8)); } - + $ip = substr($ip, 0, strpos($ip, '/')); // possible : in IP (when a port is given), #1173 // but only if there actually IS ONE if (strpos($ip, ':') !== false) { $ip = substr($ip, 0, strpos($ip, ':')); } - + if (validate_ip($ip, true) !== false) { return true; } else { diff --git a/tests/Domains/DomainsTest.php b/tests/Domains/DomainsTest.php index d2b05651..ed13d5ff 100644 --- a/tests/Domains/DomainsTest.php +++ b/tests/Domains/DomainsTest.php @@ -91,11 +91,14 @@ class DomainsTest extends TestCase $data = [ 'domain' => 'test2.local', 'customerid' => 1, - 'ipandport' => 3 + 'ipandport' => 3, + 'isemaildomain' => 1, + 'subcanemaildomain' => 2 ]; $json_result = Domains::getLocal($reseller_userdata, $data)->add(); $result = json_decode($json_result, true)['data']; $this->assertEquals('test2.local', $result['domain']); + $this->assertEquals(2, $result['subcanemaildomain']); } public function testAdminDomainsAddSysHostname() diff --git a/tests/SubDomains/SubDomainsTest.php b/tests/SubDomains/SubDomainsTest.php index 1e77e43a..f930ca5e 100644 --- a/tests/SubDomains/SubDomainsTest.php +++ b/tests/SubDomains/SubDomainsTest.php @@ -8,6 +8,7 @@ use PHPUnit\Framework\TestCase; */ class SubDomainsTest extends TestCase { + public function testCustomerSubDomainsAdd() { global $admin_userdata; @@ -46,7 +47,7 @@ class SubDomainsTest extends TestCase $result = json_decode($json_result, true)['data']; $this->assertEquals('mysub2.test2.local', $result['domain']); } - + public function testCustomerSubDomainsAddNoPunycode() { global $admin_userdata; @@ -100,7 +101,7 @@ class SubDomainsTest extends TestCase $this->expectExceptionMessage("Wrong Input in Field 'Domain'"); SubDomains::getLocal($customer_userdata, $data)->add(); } - + /** * @depends testCustomerSubDomainsAdd */ @@ -122,7 +123,72 @@ class SubDomainsTest extends TestCase $this->assertEquals('mysub.test2.local', $result['domain']); $this->assertEquals(1, $result['customerid']); } - + + /** + * @depends testCustomerSubDomainsAdd + */ + public function testAdminSubDomainsGetMainDomain() + { + global $admin_userdata; + + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + + $data = [ + 'domainname' => 'test2.local' + ]; + $json_result = SubDomains::getLocal($admin_userdata, $data)->get(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals('test2.local', $result['domain']); + $this->assertEquals(1, $result['customerid']); + } + + /** + * @depends testCustomerSubDomainsAdd + */ + public function testAdminSubDomainsUpdate() + { + global $admin_userdata; + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + $data = [ + 'domainname' => 'mysub.test2.local', + 'path' => 'mysub.test2.local', + 'isemaildomain' => 1, + 'customer_id' => $customer_userdata['customerid'] + ]; + $json_result = SubDomains::getLocal($admin_userdata, $data)->update(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals($customer_userdata['documentroot'] . 'mysub.test2.local/', $result['documentroot']); + } + + /** + * @depends testAdminSubDomainsUpdate + */ + public function testCustomerSubDomainsUpdate() + { + global $admin_userdata; + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + $data = [ + 'domainname' => 'mysub.test2.local', + 'url' => 'https://www.froxlor.org/', + 'isemaildomain' => 0, + ]; + $json_result = SubDomains::getLocal($customer_userdata, $data)->update(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals('https://www.froxlor.org/', $result['documentroot']); + } + public function testCustomerSubDomainsList() { global $admin_userdata; @@ -136,7 +202,7 @@ class SubDomainsTest extends TestCase $result = json_decode($json_result, true)['data']; $this->assertEquals(3, $result['count']); } - + public function testResellerSubDomainsList() { global $admin_userdata; @@ -154,11 +220,13 @@ class SubDomainsTest extends TestCase public function testAdminSubDomainsListWithCustomer() { global $admin_userdata; - $json_result = SubDomains::getLocal($admin_userdata, ['loginname' => 'test1'])->listing(); + $json_result = SubDomains::getLocal($admin_userdata, [ + 'loginname' => 'test1' + ])->listing(); $result = json_decode($json_result, true)['data']; $this->assertEquals(3, $result['count']); } - + /** * @depends testCustomerSubDomainsList */ @@ -170,7 +238,9 @@ class SubDomainsTest extends TestCase 'loginname' => 'test1' ))->get(); $customer_userdata = json_decode($json_result, true)['data']; - $json_result = SubDomains::getLocal($customer_userdata, ['domainname' => 'mysub.test2.local'])->delete(); + $json_result = SubDomains::getLocal($customer_userdata, [ + 'domainname' => 'mysub.test2.local' + ])->delete(); $result = json_decode($json_result, true)['data']; $this->assertEquals('mysub.test2.local', $result['domain']); $this->assertEquals($customer_userdata['customerid'], $result['customerid']); From f5ec759d998fba8f65a06dc4c31c5a5b08c58197 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 5 Mar 2018 14:21:36 +0100 Subject: [PATCH 0542/1335] added Certificates-ApiCommand Signed-off-by: Michael Kaufmann (d00p) --- customer_domains.php | 86 +---- .../api/commands/class.Certificates.php | 325 ++++++++++++++++++ lib/classes/api/commands/class.SubDomains.php | 21 +- ssl_certificates.php | 31 +- 4 files changed, 357 insertions(+), 106 deletions(-) create mode 100644 lib/classes/api/commands/class.Certificates.php diff --git a/customer_domains.php b/customer_domains.php index 8cc3bf50..c97f6b07 100644 --- a/customer_domains.php +++ b/customer_domains.php @@ -447,87 +447,16 @@ if ($page == 'overview') { if ($action == '' || $action == 'view') { if (isset($_POST['send']) && $_POST['send'] == 'send') { - - $ssl_cert_file = isset($_POST['ssl_cert_file']) ? $_POST['ssl_cert_file'] : ''; - $ssl_key_file = isset($_POST['ssl_key_file']) ? $_POST['ssl_key_file'] : ''; - $ssl_ca_file = isset($_POST['ssl_ca_file']) ? $_POST['ssl_ca_file'] : ''; - $ssl_cert_chainfile = isset($_POST['ssl_cert_chainfile']) ? $_POST['ssl_cert_chainfile'] : ''; $do_insert = isset($_POST['do_insert']) ? (($_POST['do_insert'] == 1) ? true : false) : false; - - if ($ssl_cert_file != '' && $ssl_key_file == '') { - standard_error('sslcertificateismissingprivatekey'); - } - - $do_verify = true; - - // no cert-file given -> forget everything - if ($ssl_cert_file == '') { - $ssl_key_file = ''; - $ssl_ca_file = ''; - $ssl_cert_chainfile = ''; - $do_verify = false; - } - - // verify certificate content - if ($do_verify) { - // array openssl_x509_parse ( mixed $x509cert [, bool $shortnames = true ] ) - // openssl_x509_parse() returns information about the supplied x509cert, including fields such as - // subject name, issuer name, purposes, valid from and valid to dates etc. - $cert_content = openssl_x509_parse($ssl_cert_file); - - if (is_array($cert_content) && isset($cert_content['subject']) && isset($cert_content['subject']['CN'])) { - // bool openssl_x509_check_private_key ( mixed $cert , mixed $key ) - // Checks whether the given key is the private key that corresponds to cert. - if (openssl_x509_check_private_key($ssl_cert_file, $ssl_key_file) === false) { - standard_error('sslcertificateinvalidcertkeypair'); - } - - // check optional stuff - if ($ssl_ca_file != '') { - $ca_content = openssl_x509_parse($ssl_ca_file); - if (!is_array($ca_content)) { - // invalid - standard_error('sslcertificateinvalidca'); - } - } - if ($ssl_cert_chainfile != '') { - $chain_content = openssl_x509_parse($ssl_cert_chainfile); - if (!is_array($chain_content)) { - // invalid - standard_error('sslcertificateinvalidchain'); - } - } + try { + if ($do_insert) { + Certificates::getLocal($userinfo, $_POST)->add(); } else { - standard_error('sslcertificateinvalidcert'); + Certificates::getLocal($userinfo, $_POST)->update(); } + } catch (Exception $e) { + dynamic_error($e->getMessage()); } - - // Add/Update database entry - $qrystart = "UPDATE "; - $qrywhere = "WHERE "; - if ($do_insert) { - $qrystart = "INSERT INTO "; - $qrywhere = ", "; - } - $stmt = Database::prepare($qrystart." `".TABLE_PANEL_DOMAIN_SSL_SETTINGS."` SET - `ssl_cert_file` = :ssl_cert_file, - `ssl_key_file` = :ssl_key_file, - `ssl_ca_file` = :ssl_ca_file, - `ssl_cert_chainfile` = :ssl_cert_chainfile - ".$qrywhere." `domainid`= :domainid" - ); - $params = array( - "ssl_cert_file" => $ssl_cert_file, - "ssl_key_file" => $ssl_key_file, - "ssl_ca_file" => $ssl_ca_file, - "ssl_cert_chainfile" => $ssl_cert_chainfile, - "domainid" => $id - ); - Database::pexecute($stmt, $params); - - // insert task to re-generate webserver-configs (#1260) - inserttask('1'); - // back to domain overview redirectTo($filename, array('page' => 'domains', 's' => $s)); } @@ -535,8 +464,7 @@ if ($page == 'overview') { $stmt = Database::prepare("SELECT * FROM `".TABLE_PANEL_DOMAIN_SSL_SETTINGS."` WHERE `domainid`= :domainid" ); - Database::pexecute($stmt, array("domainid" => $id)); - $result = $stmt->fetch(PDO::FETCH_ASSOC); + $result = Database::pexecute_first($stmt, array("domainid" => $id)); $do_insert = false; // if no entry can be found, behave like we have empty values diff --git a/lib/classes/api/commands/class.Certificates.php b/lib/classes/api/commands/class.Certificates.php new file mode 100644 index 00000000..fb17a709 --- /dev/null +++ b/lib/classes/api/commands/class.Certificates.php @@ -0,0 +1,325 @@ + (2010-) + * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt + * @package API + * @since 0.10.0 + * + */ +class Certificates extends ApiCommand implements ResourceEntity +{ + + /** + * add new ssl-certificate entry for given domain by either id or domainname + * + * @param int $id + * optional, the domain-id + * @param string $domainname + * optional, the domainname + * @param string $ssl_cert_file + * @param string $ssl_key_file + * @param string $ssl_ca_file + * optional + * @param string $ssl_cert_chainfile + * optional + * + * @access admin, customer + * @throws Exception + * @return array + */ + public function add() + { + $domainid = $this->getParam('domainid', true, 0); + $dn_optional = ($domainid <= 0 ? false : true); + $domainname = $this->getParam('domainname', $dn_optional, ''); + + if ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'domains')) { + throw new Exception("You cannot access this resource", 405); + } + + $domain = $this->apiCall('SubDomains.get', array( + 'id' => $domainid, + 'domainname' => $domainname + )); + // parameters + $ssl_cert_file = $this->getParam('ssl_cert_file'); + $ssl_key_file = $this->getParam('ssl_key_file'); + $ssl_ca_file = $this->getParam('ssl_ca_file', true, ''); + $ssl_cert_chainfile = $this->getParam('ssl_cert_chainfile', true, ''); + $this->addOrUpdateCertificate($domain['id'], $ssl_cert_file, $ssl_key_file, $ssl_ca_file, $ssl_cert_chainfile, true); + $idna_convert = new idna_convert_wrapper(); + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_INFO, "[API] added ssl-certificate for '" . $domain['domain'] . "'"); + $result = $this->apiCall('Certificates.get', array( + 'id' => $domain['id'] + )); + return $this->response(200, "successfull", $result); + } + + /** + * return ssl-certificate entry for given domain by either id or domainname + * + * @param int $id + * optional, the domain-id + * @param string $domainname + * optional, the domainname + * + * @access admin, customer + * @throws Exception + * @return array + */ + public function get() + { + $id = $this->getParam('id', true, 0); + $dn_optional = ($id <= 0 ? false : true); + $domainname = $this->getParam('domainname', $dn_optional, ''); + + if ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'domains')) { + throw new Exception("You cannot access this resource", 405); + } + + $domain = $this->apiCall('SubDomains.get', array( + 'id' => $id, + 'domainname' => $domainname + )); + $domainid = $domain['id']; + + $stmt = Database::prepare("SELECT * FROM `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` WHERE `domainid`= :domainid"); + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_INFO, "[API] get ssl-certificate for '" . $domain['domain'] . "'"); + $result = Database::pexecute_first($stmt, array( + "domainid" => $domainid + )); + return $this->response(200, "successfull", $result); + } + + /** + * update ssl-certificate entry for given domain by either id or domainname + * + * @param int $id + * optional, the domain-id + * @param string $domainname + * optional, the domainname + * + * @access admin, customer + * @throws Exception + * @return array + */ + public function update() + { + $id = $this->getParam('id', true, 0); + $dn_optional = ($id <= 0 ? false : true); + $domainname = $this->getParam('domainname', $dn_optional, ''); + + if ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'domains')) { + throw new Exception("You cannot access this resource", 405); + } + + $domain = $this->apiCall('SubDomains.get', array( + 'id' => $id, + 'domainname' => $domainname + )); + + // parameters + $ssl_cert_file = $this->getParam('ssl_cert_file'); + $ssl_key_file = $this->getParam('ssl_key_file'); + $ssl_ca_file = $this->getParam('ssl_ca_file', true, ''); + $ssl_cert_chainfile = $this->getParam('ssl_cert_chainfile', true, ''); + $this->addOrUpdateCertificate($domain['id'], $ssl_cert_file, $ssl_key_file, $ssl_ca_file, $ssl_cert_chainfile, false); + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_INFO, "[API] updated ssl-certificate for '" . $domain['domain'] . "'"); + $result = $this->apiCall('Certificates.get', array( + 'id' => $domain['id'] + )); + return $this->response(200, "successfull", $result); + } + + /** + * lists all certificate entries + * + * @access admin, customer + * @throws Exception + * @return array count|list + */ + public function listing() + { + // select all my (accessable) certificates + $certs_stmt_query = "SELECT s.*, d.domain, d.letsencrypt, c.customerid, c.loginname + FROM `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` s + LEFT JOIN `" . TABLE_PANEL_DOMAINS . "` d ON `d`.`id` = `s`.`domainid` + LEFT JOIN `" . TABLE_PANEL_CUSTOMERS . "` c ON `c`.`customerid` = `d`.`customerid` + WHERE "; + + $qry_params = array(); + + if ($this->isAdmin() && $this->getUserDetail('customers_see_all') == '0') { + // admin with only customer-specific permissions + $certs_stmt_query .= "d.adminid = :adminid "; + $qry_params['adminid'] = $this->getUserDetail('adminid'); + } elseif ($this->isAdmin() == false) { + // customer-area + $certs_stmt_query .= "d.customerid = :cid "; + $qry_params['cid'] = $this->getUserDetail('customerid'); + } else { + $certs_stmt_query .= "1 "; + } + $certs_stmt = Database::prepare($certs_stmt_query); + Database::pexecute($certs_stmt, $qry_params, true, true); + $result = array(); + while ($cert = $certs_stmt->fetch(PDO::FETCH_ASSOC)) { + // respect froxlor-hostname + if ($cert['domainid'] == 0) { + $cert['domain'] = Settings::Get('system.hostname'); + $cert['letsencrypt'] = Settings::Get('system.le_froxlor_enabled'); + $cert['loginname'] = 'froxlor.panel'; + } + $result[] = $cert; + } + return $this->response(200, "successfull", array( + 'count' => count($result), + 'list' => $result + )); + } + + /** + * delete certificates entry by id + * + * @param int $id + * + * @return array + */ + public function delete() + { + $id = $this->getParam('id'); + + $chk = ($this->isAdmin() && $this->getUserDetail('customers_see_all') == '1') ? true : false; + if ($this->isAdmin() == false) { + $chk_stmt = Database::prepare(" + SELECT d.domain FROM `" . TABLE_PANEL_DOMAINS . "` d + LEFT JOIN `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` s ON s.domainid = d.id + WHERE s.`id` = :id AND d.`customerid` = :cid + "); + $chk = Database::pexecute_first($chk_stmt, array( + 'id' => $id, + 'cid' => $this->getUserDetail('customerid') + )); + } elseif ($this->isAdmin() && $this->getUserDetail('customers_see_all') == '0') { + $chk_stmt = Database::prepare(" + SELECT d.domain FROM `" . TABLE_PANEL_DOMAINS . "` d + LEFT JOIN `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` s ON s.domainid = d.id + WHERE s.`id` = :id AND d.`adminid` = :aid + "); + $chk = Database::pexecute_first($chk_stmt, array( + 'id' => $id, + 'aid' => $this->getUserDetail('adminid') + )); + } + if ($chk !== false) { + // additional access check by trying to get the certificate + $result = $this->apiCall('Certificates.get', array( + 'domainname' => $chk['domain'] + )); + $del_stmt = Database::prepare("DELETE FROM `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` WHERE id = :id"); + Database::pexecute($del_stmt, array( + 'id' => $id + )); + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_INFO, "[API] removed ssl-certificate for '" . $chk['domain'] . "'"); + return $this->response(200, "successfull", $result); + } + } + + /** + * insert or update certificates entry + * + * @param int $domainid + * @param string $ssl_cert_file + * @param string $ssl_key_file + * @param string $ssl_ca_file + * @param string $ssl_cert_chainfile + * @param boolean $do_insert + * optional default false + * + * @return boolean + */ + private function addOrUpdateCertificate($domainid = 0, $ssl_cert_file = '', $ssl_key_file = '', $ssl_ca_file = '', $ssl_cert_chainfile = '', $do_insert = false) + { + if ($ssl_cert_file != '' && $ssl_key_file == '') { + standard_error('sslcertificateismissingprivatekey', '', true); + } + + $do_verify = true; + // no cert-file given -> forget everything + if ($ssl_cert_file == '') { + $ssl_key_file = ''; + $ssl_ca_file = ''; + $ssl_cert_chainfile = ''; + $do_verify = false; + } + + // verify certificate content + if ($do_verify) { + // array openssl_x509_parse ( mixed $x509cert [, bool $shortnames = true ] ) + // openssl_x509_parse() returns information about the supplied x509cert, including fields such as + // subject name, issuer name, purposes, valid from and valid to dates etc. + $cert_content = openssl_x509_parse($ssl_cert_file); + + if (is_array($cert_content) && isset($cert_content['subject']) && isset($cert_content['subject']['CN'])) { + // bool openssl_x509_check_private_key ( mixed $cert , mixed $key ) + // Checks whether the given key is the private key that corresponds to cert. + if (openssl_x509_check_private_key($ssl_cert_file, $ssl_key_file) === false) { + standard_error('sslcertificateinvalidcertkeypair', '', true); + } + + // check optional stuff + if ($ssl_ca_file != '') { + $ca_content = openssl_x509_parse($ssl_ca_file); + if (! is_array($ca_content)) { + // invalid + standard_error('sslcertificateinvalidca', '', true); + } + } + if ($ssl_cert_chainfile != '') { + $chain_content = openssl_x509_parse($ssl_cert_chainfile); + if (! is_array($chain_content)) { + // invalid + standard_error('sslcertificateinvalidchain', '', true); + } + } + } else { + standard_error('sslcertificateinvalidcert', '', true); + } + } + + // Add/Update database entry + $qrystart = "UPDATE "; + $qrywhere = "WHERE "; + if ($do_insert) { + $qrystart = "INSERT INTO "; + $qrywhere = ", "; + } + $stmt = Database::prepare($qrystart . " `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` SET + `ssl_cert_file` = :ssl_cert_file, + `ssl_key_file` = :ssl_key_file, + `ssl_ca_file` = :ssl_ca_file, + `ssl_cert_chainfile` = :ssl_cert_chainfile + " . $qrywhere . " `domainid`= :domainid + "); + $params = array( + "ssl_cert_file" => $ssl_cert_file, + "ssl_key_file" => $ssl_key_file, + "ssl_ca_file" => $ssl_ca_file, + "ssl_cert_chainfile" => $ssl_cert_chainfile, + "domainid" => $domainid + ); + Database::pexecute($stmt, $params, true, true); + // insert task to re-generate webserver-configs (#1260) + inserttask('1'); + return true; + } +} diff --git a/lib/classes/api/commands/class.SubDomains.php b/lib/classes/api/commands/class.SubDomains.php index 6ef6592a..1e2e1cbf 100644 --- a/lib/classes/api/commands/class.SubDomains.php +++ b/lib/classes/api/commands/class.SubDomains.php @@ -405,6 +405,18 @@ class SubDomains extends ApiCommand implements ResourceEntity throw new Exception("Subdomain with " . $key . " could not be found", 404); } + /** + * update subdomain entry by either id or domainname + * + * @param int $id + * optional, the domain-id + * @param string $domainname + * optional, the domainname + * + * @access admin, customer + * @throws Exception + * @return array + */ public function update() { $id = $this->getParam('id', true, 0); @@ -414,7 +426,7 @@ class SubDomains extends ApiCommand implements ResourceEntity if ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'domains')) { throw new Exception("You cannot access this resource", 405); } - + $result = $this->apiCall('SubDomains.get', array( 'id' => $id, 'domainname' => $domainname @@ -628,6 +640,13 @@ class SubDomains extends ApiCommand implements ResourceEntity return $this->response(200, "successfull", $result); } + /** + * lists all subdomain entries + * + * @access admin, customer + * @throws Exception + * @return array count|list + */ public function listing() { if ($this->isAdmin()) { diff --git a/ssl_certificates.php b/ssl_certificates.php index dd0bba10..eb08f534 100644 --- a/ssl_certificates.php +++ b/ssl_certificates.php @@ -22,40 +22,19 @@ if (! defined('AREA')) { // This file is being included in admin_domains and customer_domains // and therefore does not need to require lib/init.php -$del_stmt = Database::prepare("DELETE FROM `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` WHERE id = :id"); $success_message = ""; // do the delete and then just show a success-message and the certificates list again if ($action == 'delete') { $id = isset($_GET['id']) ? (int) $_GET['id'] : 0; if ($id > 0) { - $chk = (AREA == 'admin' && $userinfo['customers_see_all'] == '1') ? true : false; - if (AREA == 'customer') { - $chk_stmt = Database::prepare(" - SELECT d.domain FROM `" . TABLE_PANEL_DOMAINS . "` d - LEFT JOIN `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` s ON s.domainid = d.id - WHERE s.`id` = :id AND d.`customerid` = :cid - "); - $chk = Database::pexecute_first($chk_stmt, array( - 'id' => $id, - 'cid' => $userinfo['customerid'] - )); - } elseif (AREA == 'admin' && $userinfo['customers_see_all'] == '0') { - $chk_stmt = Database::prepare(" - SELECT d.domain FROM `" . TABLE_PANEL_DOMAINS . "` d - LEFT JOIN `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` s ON s.domainid = d.id - WHERE s.`id` = :id AND d.`adminid` = :aid - "); - $chk = Database::pexecute_first($chk_stmt, array( - 'id' => $id, - 'aid' => $userinfo['adminid'] - )); - } - if ($chk !== false) { - Database::pexecute($del_stmt, array( + try { + $json_result = Certificates::getLocal($userinfo, array( 'id' => $id - )); + ))->delete(); $success_message = sprintf($lng['domains']['ssl_certificate_removed'], $id); + } catch (Exception $e) { + dynamic_error($e->getMessage()); } } } From 864331d3713c3ee5fccf0a529b87b80cd29e72cb Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 5 Mar 2018 23:35:00 +0100 Subject: [PATCH 0543/1335] code reduction; added unit-tests for Certificates-Command; minor fixes here and there Signed-off-by: Michael Kaufmann (d00p) --- install/lib/class.FroxlorInstall.php | 2 +- lib/classes/api/abstract.ApiCommand.php | 43 ++++ .../api/commands/class.Certificates.php | 19 +- lib/classes/api/commands/class.Ftps.php | 33 +-- lib/classes/api/commands/class.Mysqls.php | 31 +-- lib/classes/api/commands/class.SubDomains.php | 2 + phpunit.xml | 1 + tests/Certificates/CertificatesTest.php | 194 ++++++++++++++++++ tests/Domains/DomainsTest.php | 6 +- tests/IpsAndPorts/IpsAndPortsTest.php | 20 +- tests/bootstrap.php | 14 ++ 11 files changed, 285 insertions(+), 80 deletions(-) create mode 100644 tests/Certificates/CertificatesTest.php diff --git a/install/lib/class.FroxlorInstall.php b/install/lib/class.FroxlorInstall.php index 4cc136a5..86e50f78 100644 --- a/install/lib/class.FroxlorInstall.php +++ b/install/lib/class.FroxlorInstall.php @@ -995,7 +995,7 @@ class FroxlorInstall $content .= $this->_status_message('green', $this->_lng['requirements']['installed']); } - // check for bstring-extension + // check for mbstring-extension $content .= $this->_status_message('begin', $this->_lng['requirements']['phpmbstring']); if (! extension_loaded('mbstring')) { diff --git a/lib/classes/api/abstract.ApiCommand.php b/lib/classes/api/abstract.ApiCommand.php index 156f84a5..e3677f9a 100644 --- a/lib/classes/api/abstract.ApiCommand.php +++ b/lib/classes/api/abstract.ApiCommand.php @@ -414,6 +414,49 @@ abstract class ApiCommand return $json_response; } + /** + * returns an array of customers the current user can access + * + * @param string $customer_hide_option optional, when called as customer, some options might be hidden due to the panel.customer_hide_options ettings + * + * @throws Exception + * @return array + */ + protected function getAllowedCustomerIds($customer_hide_option = '') + { + $customer_ids = array(); + if ($this->isAdmin()) { + // if we're an admin, list all ftp-users of all the admins customers + // or optionally for one specific customer identified by id or loginname + $customerid = $this->getParam('customerid', true, 0); + $loginname = $this->getParam('loginname', true, ''); + + if (! empty($customerid) || ! empty($loginname)) { + $_result = $this->apiCall('Customers.get', array( + 'id' => $customerid, + 'loginname' => $loginname + )); + $custom_list_result = array( + $_result + ); + } else { + $_custom_list_result = $this->apiCall('Customers.listing'); + $custom_list_result = $_custom_list_result['list']; + } + foreach ($custom_list_result as $customer) { + $customer_ids[] = $customer['customerid']; + } + } else { + if (!empty($customer_hide_option) && Settings::IsInList('panel.customer_hide_options', $customer_hide_option)) { + throw new Exception("You cannot access this resource", 405); + } + $customer_ids = array( + $this->getUserDetail('customerid') + ); + } + return $customer_ids; + } + /** * increase/decrease a resource field for customers/admins * diff --git a/lib/classes/api/commands/class.Certificates.php b/lib/classes/api/commands/class.Certificates.php index fb17a709..c49869ec 100644 --- a/lib/classes/api/commands/class.Certificates.php +++ b/lib/classes/api/commands/class.Certificates.php @@ -50,18 +50,27 @@ class Certificates extends ApiCommand implements ResourceEntity 'id' => $domainid, 'domainname' => $domainname )); + $domainid = $domain['id']; + // parameters $ssl_cert_file = $this->getParam('ssl_cert_file'); $ssl_key_file = $this->getParam('ssl_key_file'); $ssl_ca_file = $this->getParam('ssl_ca_file', true, ''); $ssl_cert_chainfile = $this->getParam('ssl_cert_chainfile', true, ''); - $this->addOrUpdateCertificate($domain['id'], $ssl_cert_file, $ssl_key_file, $ssl_ca_file, $ssl_cert_chainfile, true); - $idna_convert = new idna_convert_wrapper(); - $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_INFO, "[API] added ssl-certificate for '" . $domain['domain'] . "'"); + + // validate whether the domain does not already have an entry $result = $this->apiCall('Certificates.get', array( - 'id' => $domain['id'] + 'id' => $domainid )); - return $this->response(200, "successfull", $result); + if (empty($result)) { + $this->addOrUpdateCertificate($domain['id'], $ssl_cert_file, $ssl_key_file, $ssl_ca_file, $ssl_cert_chainfile, true); + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_INFO, "[API] added ssl-certificate for '" . $domain['domain'] . "'"); + $result = $this->apiCall('Certificates.get', array( + 'id' => $domain['id'] + )); + return $this->response(200, "successfull", $result); + } + throw new Exception("Domain '" . $domain['domain'] . "' already has a certificate. Did you mean to call update?", 406); } /** diff --git a/lib/classes/api/commands/class.Ftps.php b/lib/classes/api/commands/class.Ftps.php index 6411e3f2..6f3223be 100644 --- a/lib/classes/api/commands/class.Ftps.php +++ b/lib/classes/api/commands/class.Ftps.php @@ -448,43 +448,14 @@ class Ftps extends ApiCommand implements ResourceEntity */ public function listing() { - if ($this->isAdmin()) { - // if we're an admin, list all ftp-users of all the admins customers - // or optionally for one specific customer identified by id or loginname - $customerid = $this->getParam('customerid', true, 0); - $loginname = $this->getParam('loginname', true, ''); - - if (! empty($customerid) || ! empty($loginname)) { - $_result = $this->apiCall('Customers.get', array( - 'id' => $customerid, - 'loginname' => $loginname - )); - $custom_list_result = array( - $_result - ); - } else { - $_custom_list_result = $this->apiCall('Customers.listing'); - $custom_list_result = $_custom_list_result['list']; - } - $customer_ids = array(); - foreach ($custom_list_result as $customer) { - $customer_ids[] = $customer['customerid']; - } - } else { - if (Settings::IsInList('panel.customer_hide_options', 'ftp')) { - throw new Exception("You cannot access this resource", 405); - } - $customer_ids = array( - $this->getUserDetail('customerid') - ); - } + $customer_ids = $this->getAllowedCustomerIds('ftp'); $result = array(); $params['customerid'] = implode(", ", $customer_ids); $result_stmt = Database::prepare(" SELECT * FROM `" . TABLE_FTP_USERS . "` WHERE `customerid` IN (:customerid) "); - Database::pexecute($result_stmt, $params); + Database::pexecute($result_stmt, $params, true, true); while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) { $result[] = $row; } diff --git a/lib/classes/api/commands/class.Mysqls.php b/lib/classes/api/commands/class.Mysqls.php index 55929a80..dfb1cd55 100644 --- a/lib/classes/api/commands/class.Mysqls.php +++ b/lib/classes/api/commands/class.Mysqls.php @@ -425,36 +425,7 @@ class Mysqls extends ApiCommand implements ResourceEntity { $result = array(); $dbserver = $this->getParam('mysql_server', true, - 1); - if ($this->isAdmin()) { - // if we're an admin, list all databases of all the admins customers - // or optionally for one specific customer identified by id or loginname - $customerid = $this->getParam('customerid', true, 0); - $loginname = $this->getParam('loginname', true, ''); - - if (! empty($customer_id) || ! empty($loginname)) { - $customer = $this->apiCall('Customers.get', array( - 'id' => $customerid, - 'loginname' => $loginname - )); - $custom_list_result = array( - $customer - ); - } else { - $_custom_list_result = $this->apiCall('Customers.listing'); - $custom_list_result = $_custom_list_result['list']; - } - $customer_ids = array(); - foreach ($custom_list_result as $customer) { - $customer_ids[] = $customer['customerid']; - } - } else { - if (Settings::IsInList('panel.customer_hide_options', 'mysql')) { - throw new Exception("You cannot access this resource", 405); - } - $customer_ids = array( - $this->getUserDetail('customerid') - ); - } + $customer_ids = $this->getAllowedCustomerIds('mysql'); $result_stmt = Database::prepare(" SELECT * FROM `" . TABLE_PANEL_DATABASES . "` WHERE `customerid`= :customerid AND `dbserver` = :dbserver diff --git a/lib/classes/api/commands/class.SubDomains.php b/lib/classes/api/commands/class.SubDomains.php index 1e2e1cbf..ba257882 100644 --- a/lib/classes/api/commands/class.SubDomains.php +++ b/lib/classes/api/commands/class.SubDomains.php @@ -247,6 +247,7 @@ class SubDomains extends ApiCommand implements ResourceEntity $stmt = Database::prepare(" INSERT INTO `" . TABLE_PANEL_DOMAINS . "` SET `customerid` = :customerid, + `adminid` = :adminid, `domain` = :domain, `documentroot` = :documentroot, `aliasdomain` = :aliasdomain, @@ -268,6 +269,7 @@ class SubDomains extends ApiCommand implements ResourceEntity "); $params = array( "customerid" => $customer['customerid'], + "adminid" => $customer['adminid'], "domain" => $completedomain, "documentroot" => $path, "aliasdomain" => $aliasdomain != 0 ? $aliasdomain : null, diff --git a/phpunit.xml b/phpunit.xml index 391c3a47..efedaf51 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -14,6 +14,7 @@ tests/IpsAndPorts tests/Domains tests/SubDomains + tests/Certificates tests/Ftps diff --git a/tests/Certificates/CertificatesTest.php b/tests/Certificates/CertificatesTest.php new file mode 100644 index 00000000..85a307a4 --- /dev/null +++ b/tests/Certificates/CertificatesTest.php @@ -0,0 +1,194 @@ +generateKey(); + $json_result = Certificates::getLocal($admin_userdata, array( + 'domainname' => 'test2.local', + 'ssl_cert_file' => $certdata['cert'], + 'ssl_key_file' => $certdata['key'] + ))->add(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals(3, $result['domainid']); + } + + public function testResellerCertificatesAddAgain() + { + global $admin_userdata; + // get reseller + $json_result = Admins::getLocal($admin_userdata, array( + 'loginname' => 'reseller' + ))->get(); + $reseller_userdata = json_decode($json_result, true)['data']; + $reseller_userdata['adminsession'] = 1; + + $certdata = $this->generateKey(); + $this->expectExceptionCode(406); + $this->expectExceptionMessage("Domain 'test2.local' already has a certificate. Did you mean to call update?"); + $json_result = Certificates::getLocal($reseller_userdata, array( + 'domainname' => 'test2.local', + 'ssl_cert_file' => $certdata['cert'], + 'ssl_key_file' => $certdata['key'] + ))->add(); + } + + public function testCustomerCertificatesAdd() + { + global $admin_userdata; + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + + $certdata = $this->generateKey(); + $json_result = Certificates::getLocal($customer_userdata, array( + 'domainname' => 'mysub2.test2.local', + 'ssl_cert_file' => $certdata['cert'], + 'ssl_key_file' => $certdata['key'] + ))->add(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals(5, $result['domainid']); + } + + public function testAdminCertificatesList() + { + global $admin_userdata; + + $json_result = Certificates::getLocal($admin_userdata)->listing(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals(2, $result['count']); + } + + public function testResellerCertificatesList() + { + global $admin_userdata; + // get reseller + $json_result = Admins::getLocal($admin_userdata, array( + 'loginname' => 'reseller' + ))->get(); + $reseller_userdata = json_decode($json_result, true)['data']; + $reseller_userdata['adminsession'] = 1; + + $json_result = Certificates::getLocal($reseller_userdata)->listing(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals(2, $result['count']); + } + + public function testCustomerCertificatesList() + { + global $admin_userdata; + + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + $json_result = Certificates::getLocal($customer_userdata)->listing(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals(2, $result['count']); + } + + public function testAdminCertificatesUpdate() + { + global $admin_userdata; + + $certdata = $this->generateKey(); + $json_result = Certificates::getLocal($admin_userdata, array( + 'domainname' => 'test2.local', + 'ssl_cert_file' => $certdata['cert'], + 'ssl_key_file' => $certdata['key'] + ))->update(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals(3, $result['domainid']); + $this->assertEquals(str_replace("\n", "", $certdata['cert']), str_replace("\n", "", $result['ssl_cert_file'])); + } + + public function testCustomerCertificatesUpdate() + { + global $admin_userdata; + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + + $certdata = $this->generateKey(); + $json_result = Certificates::getLocal($customer_userdata, array( + 'domainname' => 'mysub2.test2.local', + 'ssl_cert_file' => $certdata['cert'], + 'ssl_key_file' => $certdata['key'] + ))->update(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals(5, $result['domainid']); + $this->assertEquals(str_replace("\n", "", $certdata['cert']), str_replace("\n", "", $result['ssl_cert_file'])); + } + + /** + * @depends testAdminCertificatesUpdate + */ + public function testCustomerCertificatesDelete() + { + global $admin_userdata; + + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + $json_result = Certificates::getLocal($customer_userdata, array( + 'id' => 1 + ))->delete(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals(3, $result['domainid']); + } + + private function generateKey() + { + $dn = array( + "countryName" => "DE", + "stateOrProvinceName" => "Hessen", + "localityName" => "Frankfurt", + "organizationName" => "Froxlor", + "organizationalUnitName" => "Testing", + "commonName" => "test2.local", + "emailAddress" => "team@froxlor.org" + ); + + // generate key pair + $privkey = openssl_pkey_new(array( + "private_key_bits" => 2048, + "private_key_type" => OPENSSL_KEYTYPE_RSA + )); + + // generate csr + $csr = openssl_csr_new($dn, $privkey, array( + 'digest_alg' => 'sha256' + )); + + // generate self-signed certificate + $sscert = openssl_csr_sign($csr, null, $privkey, 365, array( + 'digest_alg' => 'sha256' + )); + + // export + openssl_csr_export($csr, $csrout); + openssl_x509_export($sscert, $certout); + openssl_pkey_export($privkey, $pkeyout, null); + + return array( + 'cert' => $certout, + 'key' => $pkeyout + ); + } +} diff --git a/tests/Domains/DomainsTest.php b/tests/Domains/DomainsTest.php index ed13d5ff..964b29e4 100644 --- a/tests/Domains/DomainsTest.php +++ b/tests/Domains/DomainsTest.php @@ -77,10 +77,10 @@ class DomainsTest extends TestCase public function testResellerDomainsAddWithCanEditPhpSettingsAllowedIp() { global $admin_userdata; - // first, allow reseller access to ip #3 + // first, allow reseller access to ip #4 Admins::getLocal($admin_userdata, array( 'loginname' => 'reseller', - 'ipaddress' => 3 + 'ipaddress' => 4 ))->update(); // get reseller $json_result = Admins::getLocal($admin_userdata, array( @@ -91,7 +91,7 @@ class DomainsTest extends TestCase $data = [ 'domain' => 'test2.local', 'customerid' => 1, - 'ipandport' => 3, + 'ipandport' => 4, 'isemaildomain' => 1, 'subcanemaildomain' => 2 ]; diff --git a/tests/IpsAndPorts/IpsAndPortsTest.php b/tests/IpsAndPorts/IpsAndPortsTest.php index 9b3ec579..94d32f27 100644 --- a/tests/IpsAndPorts/IpsAndPortsTest.php +++ b/tests/IpsAndPorts/IpsAndPortsTest.php @@ -13,7 +13,7 @@ class IpsAndPortsTest extends TestCase global $admin_userdata; $json_result = IpsAndPorts::getLocal($admin_userdata)->listing(); $result = json_decode($json_result, true)['data']; - $this->assertEquals(1, $result['count']); + $this->assertEquals(2, $result['count']); $this->assertEquals('82.149.225.46', $result['list'][0]['ip']); } @@ -40,7 +40,7 @@ class IpsAndPortsTest extends TestCase ]; $json_result = IpsAndPorts::getLocal($admin_userdata, $data)->add(); $result = json_decode($json_result, true)['data']; - $this->assertEquals(2, $result['id']); + $this->assertEquals(3, $result['id']); $this->assertEquals(80, $result['port']); } @@ -66,7 +66,7 @@ class IpsAndPortsTest extends TestCase ]; $json_result = IpsAndPorts::getLocal($admin_userdata, $data)->add(); $result = json_decode($json_result, true)['data']; - $this->assertEquals(3, $result['id']); + $this->assertEquals(4, $result['id']); $this->assertEquals('/var/www/html/', $result['docroot']); } @@ -84,10 +84,10 @@ class IpsAndPortsTest extends TestCase public function testResellerIpsAndPortsList() { global $admin_userdata; - // update reseller to allow ip access to ip id #2 + // update reseller to allow ip access to ip id #3 $json_result = Admins::getLocal($admin_userdata, array( 'loginname' => 'reseller', - 'ipaddress' => array(2) + 'ipaddress' => array(3) ))->update(); $reseller_userdata = json_decode($json_result, true)['data']; $reseller_userdata['adminsession'] = 1; @@ -109,7 +109,7 @@ class IpsAndPortsTest extends TestCase ))->get(); $reseller_userdata = json_decode($json_result, true)['data']; $reseller_userdata['adminsession'] = 1; - $json_result = IpsAndPorts::getLocal($reseller_userdata, array('id' => 2))->get(); + $json_result = IpsAndPorts::getLocal($reseller_userdata, array('id' => 3))->get(); $result = json_decode($json_result, true)['data']; $this->assertEquals('82.149.225.47', $result['ip']); } @@ -120,7 +120,7 @@ class IpsAndPortsTest extends TestCase public function testResellerIpsAndPortsGetRestrictedNotOwned() { global $admin_userdata; - // update reseller to allow ip access to ip id #2 + // get reseller $json_result = Admins::getLocal($admin_userdata, array( 'loginname' => 'reseller' ))->get(); @@ -134,7 +134,7 @@ class IpsAndPortsTest extends TestCase public function testResellerIpsAndPortsAdd() { global $admin_userdata; - // update reseller to allow ip access to ip id #2 + // get reseller $json_result = Admins::getLocal($admin_userdata, array( 'loginname' => 'reseller' ))->get(); @@ -230,7 +230,7 @@ class IpsAndPortsTest extends TestCase $reseller_userdata = json_decode($json_result, true)['data']; $reseller_userdata['adminsession'] = 1; $data = [ - 'id' => 2, + 'id' => 3, 'ip' => '82.149.225.46' ]; $this->expectExceptionMessage("This IP/Port combination already exists."); @@ -251,7 +251,7 @@ class IpsAndPortsTest extends TestCase { global $admin_userdata; $data = [ - 'id' => 2 + 'id' => 3 ]; $json_result = IpsAndPorts::getLocal($admin_userdata, $data)->delete(); $result = json_decode($json_result, true)['data']; diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 31001195..fcf14947 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -103,6 +103,20 @@ Database::query("INSERT INTO `" . TABLE_PANEL_IPSANDPORTS . "` SET $defaultip = Database::lastInsertId(); Settings::Set('system.defaultip', $defaultip, true); +// add ssl ip (system default) +Database::query("INSERT INTO `" . TABLE_PANEL_IPSANDPORTS . "` SET + `ip` = '82.149.225.56', + `port` = '443', + `listen_statement` = '0', + `namevirtualhost_statement` = '0', + `vhostcontainer` = '1', + `vhostcontainer_servername_statement` = '1', + `specialsettings` = '', + `ssl` = '1' +"); +$defaultip = Database::lastInsertId(); +Settings::Set('system.defaultsslip', $defaultip, true); + // get userdata of admin 'admin' $sel_stmt = Database::prepare("SELECT * FROM `" . TABLE_PANEL_ADMINS . "` WHERE `adminid` = '1'"); $admin_userdata = Database::pexecute_first($sel_stmt); From 0c43c5d2b5fa77c51e533813dd0862968e6504d3 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 6 Mar 2018 10:52:57 +0100 Subject: [PATCH 0544/1335] backport config-services --import-settings parameter from 0.10.0 Signed-off-by: Michael Kaufmann (d00p) --- install/scripts/config-services.php | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/install/scripts/config-services.php b/install/scripts/config-services.php index 69d1b654..c0c84aec 100755 --- a/install/scripts/config-services.php +++ b/install/scripts/config-services.php @@ -43,6 +43,7 @@ class ConfigServicesCmd extends CmdLineHandler public static $params = array( 'create', 'apply', + 'import-settings', 'daemon', 'list-daemons', 'froxlor-dir', @@ -68,6 +69,9 @@ class ConfigServicesCmd extends CmdLineHandler self::println("--daemon\t\tWhen running --apply you can specify a daemon. This will be the only service that gets configured"); self::println("\t\t\tExample: --apply=/path/to/my-config.json --daemon=apache24"); self::println(""); + self::println("--import-settings\tImport settings from another froxlor installation. This should be done prior to running --apply or alternatively in the same command together."); + self::println("\t\t\tExample: --import-settings=/path/to/Froxlor_settings-[version]-[dbversion]-[date].json"); + self::println(""); self::println("--froxlor-dir\t\tpath to froxlor installation"); self::println("\t\t\tExample: --froxlor-dir=/var/www/froxlor/"); self::println(""); @@ -115,10 +119,15 @@ class Action require FROXLOR_INSTALL_DIR . '/lib/tables.inc.php'; require FROXLOR_INSTALL_DIR . '/lib/functions.php'; require FROXLOR_INSTALL_DIR . '/lib/classes/settings/class.Settings.php'; + require FROXLOR_INSTALL_DIR . '/lib/classes/settings/class.SImExporter.php'; require FROXLOR_INSTALL_DIR . '/lib/classes/config/class.ConfigParser.php'; require FROXLOR_INSTALL_DIR . '/lib/classes/config/class.ConfigService.php'; require FROXLOR_INSTALL_DIR . '/lib/classes/config/class.ConfigDaemon.php'; + if (array_key_exists("import-settings", $this->_args)) { + $this->_importSettings(); + } + if (array_key_exists("create", $this->_args)) { $this->_createConfig(); } elseif (array_key_exists("apply", $this->_args)) { @@ -128,6 +137,20 @@ class Action } } + private function _importSettings() + { + if (! is_file($this->_args["import-settings"])) { + throw new Exception("Given settings file is not a file"); + } elseif (! file_exists($this->_args["import-settings"])) { + throw new Exception("Given settings file cannot be found ('" . $this->_args["import-settings"] . "')"); + } elseif (! is_readable($this->_args["import-settings"])) { + throw new Exception("Given settings file cannot be read ('" . $this->_args["import-settings"] . "')"); + } + $imp_content = file_get_contents($this->_args["import-settings"]); + SImExporter::import($imp_content); + CmdLineHandler::printsucc("Successfully imported settings from '" . $this->_args["import-settings"] . "'"); + } + private function _createConfig() { $_daemons_config = array( From a621fd3b09a5d5703d7594761df18c573dac8a78 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 6 Mar 2018 11:05:26 +0100 Subject: [PATCH 0545/1335] run cronjob at the end of config-services script (when using --apply), thx v3ng Signed-off-by: Michael Kaufmann (d00p) --- install/scripts/config-services.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/install/scripts/config-services.php b/install/scripts/config-services.php index c0c84aec..3b5d1821 100755 --- a/install/scripts/config-services.php +++ b/install/scripts/config-services.php @@ -357,6 +357,9 @@ class Action } } } + // run cronjob at the end to ensure configs are all up to date + exec('php ' . FROXLOR_INSTALL_DIR . '/scripts/froxlor_master_cronjob.php --force'); + // and done CmdLineHandler::printsucc("All services have been configured"); } else { CmdLineHandler::printerr("Unable to decode given JSON file"); From 893fd0774c2eac148a3adf709103c2f0e8506957 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 6 Mar 2018 12:26:41 +0100 Subject: [PATCH 0546/1335] implement deleting of api-keys via webinterface Signed-off-by: Michael Kaufmann (d00p) --- api_keys.php | 38 ++++++++++++++++++++++++++++++++++++++ lng/english.lng.php | 1 + lng/german.lng.php | 1 + 3 files changed, 40 insertions(+) diff --git a/api_keys.php b/api_keys.php index d04891df..c1d75215 100644 --- a/api_keys.php +++ b/api_keys.php @@ -23,6 +23,44 @@ if (! defined('AREA')) { // This file is being included in admin_index and customer_index // and therefore does not need to require lib/init.php +$del_stmt = Database::prepare("DELETE FROM `" . TABLE_API_KEYS . "` WHERE id = :id"); +$success_message = ""; + +// do the delete and then just show a success-message and the certificates list again +if ($action == 'delete') { + $id = isset($_GET['id']) ? (int) $_GET['id'] : 0; + if ($id > 0) { + $chk = (AREA == 'admin' && $userinfo['customers_see_all'] == '1') ? true : false; + if (AREA == 'customer') { + $chk_stmt = Database::prepare(" + SELECT c.customerid FROM `" . TABLE_PANEL_CUSTOMERS . "` c + LEFT JOIN `" . TABLE_API_KEYS . "` ak ON ak.customerid = c.customerid + WHERE ak.`id` = :id AND c.`customerid` = :cid + "); + $chk = Database::pexecute_first($chk_stmt, array( + 'id' => $id, + 'cid' => $userinfo['customerid'] + )); + } elseif (AREA == 'admin' && $userinfo['customers_see_all'] == '0') { + $chk_stmt = Database::prepare(" + SELECT a.adminid FROM `" . TABLE_PANEL_ADMINS . "` a + LEFT JOIN `" . TABLE_API_KEYS . "` ak ON ak.adminid = a.adminid + WHERE ak.`id` = :id AND a.`adminid` = :aid + "); + $chk = Database::pexecute_first($chk_stmt, array( + 'id' => $id, + 'aid' => $userinfo['adminid'] + )); + } + if ($chk !== false) { + Database::pexecute($del_stmt, array( + 'id' => $id + )); + $success_message = sprintf($lng['apikeys']['apikey_removed'], $id); + } + } +} + $log->logAction(USR_ACTION, LOG_NOTICE, "viewed api::api_keys"); // select all my (accessable) certificates diff --git a/lng/english.lng.php b/lng/english.lng.php index e0c4e59f..507799e7 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -2126,3 +2126,4 @@ $lng['menue']['main']['apihelp'] = 'API help'; $lng['menue']['main']['apikeys'] = 'API keys'; $lng['apikeys']['no_api_keys'] = 'No API keys found'; $lng['apikeys']['key_add'] = 'Add new key'; +$lng['apikeys']['apikey_removed'] = 'The api key with the id #%s has been removed successfully'; diff --git a/lng/german.lng.php b/lng/german.lng.php index b6507e87..07b641cb 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -1776,3 +1776,4 @@ $lng['menue']['main']['apihelp'] = 'API Hilfe'; $lng['menue']['main']['apikeys'] = 'API Keys'; $lng['apikeys']['no_api_keys'] = 'Keine API Keys gefunden'; $lng['apikeys']['key_add'] = 'API Key hinzufügen'; +$lng['apikeys']['apikey_removed'] = 'Der API Key mit der ID #%s wurde erfolgreich gelöscht.'; From a83031504f674a925ba82f545f96c6003ca253c2 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 6 Mar 2018 12:43:24 +0100 Subject: [PATCH 0547/1335] implement generating of api-key for customer Signed-off-by: Michael Kaufmann (d00p) --- api_keys.php | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/api_keys.php b/api_keys.php index c1d75215..c60480b1 100644 --- a/api_keys.php +++ b/api_keys.php @@ -25,10 +25,10 @@ if (! defined('AREA')) { $del_stmt = Database::prepare("DELETE FROM `" . TABLE_API_KEYS . "` WHERE id = :id"); $success_message = ""; +$id = isset($_GET['id']) ? (int) $_GET['id'] : 0; // do the delete and then just show a success-message and the certificates list again if ($action == 'delete') { - $id = isset($_GET['id']) ? (int) $_GET['id'] : 0; if ($id > 0) { $chk = (AREA == 'admin' && $userinfo['customers_see_all'] == '1') ? true : false; if (AREA == 'customer') { @@ -59,6 +59,26 @@ if ($action == 'delete') { $success_message = sprintf($lng['apikeys']['apikey_removed'], $id); } } +} elseif ($action == 'add') { + $ins_stmt = Database::prepare(" + INSERT INTO `" . TABLE_API_KEYS . "` SET + `apikey` = :key, `secret` = :secret, `adminid` = :aid, `customerid` = :cid, `valid_until` = '-1', `allowed_from` = '' + "); + // customer generates for himself, admins will see a customer-select-box + if (AREA == 'customer') { + $key = hash('sha256', openssl_random_pseudo_bytes(64 * 64)); + $secret = hash('sha512', openssl_random_pseudo_bytes(64 * 64 * 4)); + Database::pexecute($ins_stmt, array( + 'key' => $key, + 'secret' => $secret, + 'aid' => $userinfo['adminid'], + 'cid' => $userinfo['customerid'] + )); + redirectTo($filename, array( + 'page' => $page, + 's' => $s + )); + } } $log->logAction(USR_ACTION, LOG_NOTICE, "viewed api::api_keys"); @@ -146,6 +166,10 @@ if (count($all_keys) == 0) { // escape stuff $row = htmlentities_array($key); + // shorten keys + $row['apikey'] = substr($row['apikey'], 0, 20) . '...'; + $row['secret'] = substr($row['secret'], 0, 20) . '...'; + // check whether the api key is not valid anymore $isValid = true; if ($row['valid_until'] >= 0) { From 231159a6c697169bd7740a7d364cd1eb105cfbd0 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 6 Mar 2018 14:02:27 +0100 Subject: [PATCH 0548/1335] added first methods of Emails ApiCommand Signed-off-by: Michael Kaufmann (d00p) --- customer_email.php | 75 +------ lib/classes/api/commands/class.Emails.php | 230 ++++++++++++++++++++++ lib/classes/api/commands/class.Ftps.php | 6 +- phpunit.xml | 1 + tests/Emails/EmailsTest.php | 44 +++++ 5 files changed, 285 insertions(+), 71 deletions(-) create mode 100644 lib/classes/api/commands/class.Emails.php create mode 100644 tests/Emails/EmailsTest.php diff --git a/customer_email.php b/customer_email.php index 02517fc1..efed543a 100644 --- a/customer_email.php +++ b/customer_email.php @@ -205,76 +205,13 @@ if ($page == 'overview') { } elseif ($action == 'add') { if ($userinfo['emails_used'] < $userinfo['emails'] || $userinfo['emails'] == '-1') { if (isset($_POST['send']) && $_POST['send'] == 'send') { - $email_part = $_POST['email_part']; - // domain does not need idna encoding as the value of the select-box is already Punycode - $domain = validate($_POST['domain'], 'domain'); - $stmt = Database::prepare("SELECT `id`, `domain`, `customerid` FROM `" . TABLE_PANEL_DOMAINS . "` - WHERE `domain`= :domain - AND `customerid`= :customerid - AND `isemaildomain`='1' " - ); - $domain_check = Database::pexecute_first($stmt, array("domain" => $domain, "customerid" => $userinfo['customerid'])); - - if (isset($_POST['iscatchall']) && $_POST['iscatchall'] == '1') { - $iscatchall = '1'; - $email = '@' . $domain; - } else { - $iscatchall = '0'; - $email = $email_part . '@' . $domain; - } - - $email_full = $email_part . '@' . $domain; - - if (!validateEmail($email_full)) { - standard_error('emailiswrong', $email_full); - } - - $stmt = Database::prepare("SELECT `id`, `email`, `email_full`, `iscatchall`, `destination`, `customerid` FROM `" . TABLE_MAIL_VIRTUAL . "` - WHERE (`email` = :email - OR `email_full` = :emailfull ) - AND `customerid`= :cid" - ); - $params = array( - "email" => $email, - "emailfull" => $email_full, - "cid" => $userinfo['customerid'] - ); - $email_check = Database::pexecute_first($stmt, $params); - - if ($email == '' || $email_full == '' || $email_part == '') { - standard_error(array('stringisempty', 'emailadd')); - } elseif ($domain == '') { - standard_error('domaincantbeempty'); - } elseif ($domain_check['domain'] != $domain) { - standard_error('maindomainnonexist', $domain); - } elseif (strtolower($email_check['email_full']) == strtolower($email_full)) { - standard_error('emailexistalready', $email_full); - } elseif ($email_check['email'] == $email) { - standard_error('youhavealreadyacatchallforthisdomain'); - } else { - $stmt = Database::prepare("INSERT INTO `" . TABLE_MAIL_VIRTUAL . "` - (`customerid`, `email`, `email_full`, `iscatchall`, `domainid`) - VALUES (:cid, :email, :email_full, :iscatchall, :domainid)" - ); - $params = array( - "cid" => $userinfo['customerid'], - "email" => $email, - "email_full" => $email_full, - "iscatchall" => $iscatchall, - "domainid" => $domain_check['id'] - ); - Database::pexecute($stmt, $params); - - $address_id = Database::lastInsertId(); - $stmt = Database::prepare("UPDATE " . TABLE_PANEL_CUSTOMERS . " - SET `emails_used` = `emails_used` + 1 - WHERE `customerid`= :cid" - ); - Database::pexecute($stmt, array("cid" => $userinfo['customerid'])); - - $log->logAction(USR_ACTION, LOG_INFO, "added email address '" . $email_full . "'"); - redirectTo($filename, array('page' => $page, 'action' => 'edit', 'id' => $address_id, 's' => $s)); + try { + $json_result = Emails::getLocal($userinfo, $_POST)->add(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); } + $result = json_decode($json_result, true)['data']; + redirectTo($filename, array('page' => $page, 'action' => 'edit', 'id' => $result['id'], 's' => $s)); } else { $result_stmt = Database::prepare("SELECT `id`, `domain`, `customerid` FROM `" . TABLE_PANEL_DOMAINS . "` WHERE `customerid`= :cid diff --git a/lib/classes/api/commands/class.Emails.php b/lib/classes/api/commands/class.Emails.php new file mode 100644 index 00000000..ce45ab72 --- /dev/null +++ b/lib/classes/api/commands/class.Emails.php @@ -0,0 +1,230 @@ + (2010-) + * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt + * @package API + * @since 0.10.0 + * + */ +class Emails extends ApiCommand implements ResourceEntity +{ + + /** + * add a new email address + * + * @access admin, customer + * @throws Exception + * @return array + */ + public function add() + { + if ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'email')) { + throw new Exception("You cannot access this resource", 405); + } + + if ($this->getUserDetail('emails_used') < $this->getUserDetail('emails') || $this->getUserDetail('emails') == '-1') { + + // required parameters + $email_part = $this->getParam('email_part'); + $domain = $this->getParam('domain'); + + // parameters + $iscatchall = $this->getParam('iscatchall', true, 0); + + // validation + if (substr($domain, 0, 4) != 'xn--') { + $idna_convert = new idna_convert_wrapper(); + $domain = $idna_convert->encode(validate($domain, 'domain', '', '', array(), true)); + } + + // check domain and whether it's an email-enabled domain + $domain_check = $this->apiCall('SubDomains.get', array( + 'domainname' => $domain + )); + if ($domain_check['isemaildomain'] == 0) { + standard_error('maindomainnonexist', $domain, true); + } + + // check for catchall-flag + if ($iscatchall) { + $iscatchall = '1'; + $email = '@' . $domain; + } else { + $iscatchall = '0'; + $email = $email_part . '@' . $domain; + } + + // full email value + $email_full = $email_part . '@' . $domain; + + // validate it + if (!validateEmail($email_full)) { + standard_error('emailiswrong', $email_full, true); + } + + // get needed customer info to reduce the email-address-counter by one + if ($this->isAdmin()) { + // get customer id + $customer_id = $this->getParam('customer_id'); + $customer = $this->apiCall('Customers.get', array( + 'id' => $customer_id + )); + // check whether the customer has enough resources to get the ftp-user added + if ($customer['emails_used'] >= $customer['emails'] && $customer['emails'] != '-1') { + throw new Exception("Customer has no more resources available", 406); + } + } else { + $customer_id = $this->getUserDetail('customerid'); + $customer = $this->getUserData(); + } + + // duplicate check + $stmt = Database::prepare(" + SELECT `id`, `email`, `email_full`, `iscatchall`, `destination`, `customerid` FROM `" . TABLE_MAIL_VIRTUAL . "` + WHERE (`email` = :email OR `email_full` = :emailfull ) + AND `customerid`= :cid + "); + $params = array( + "email" => $email, + "emailfull" => $email_full, + "cid" => $customer['customerid'] + ); + $email_check = Database::pexecute_first($stmt, $params, true, true); + + if (strtolower($email_check['email_full']) == strtolower($email_full)) { + standard_error('emailexistalready', $email_full, true); + } elseif ($email_check['email'] == $email) { + standard_error('youhavealreadyacatchallforthisdomain', '', true); + } + + $stmt = Database::prepare(" + INSERT INTO `" . TABLE_MAIL_VIRTUAL . "` SET + `customerid` = :cid, + `email` = :email, + `email_full` = :email_full, + `iscatchall` = :iscatchall, + `domainid` = :domainid + "); + $params = array( + "cid" => $customer['customerid'], + "email" => $email, + "email_full" => $email_full, + "iscatchall" => $iscatchall, + "domainid" => $domain_check['id'] + ); + Database::pexecute($stmt, $params, true, true); + $address_id = Database::lastInsertId(); + + // update customer usage + Customers::increaseUsage($customer_id, 'emails_used'); + + // update admin usage + Admins::increaseUsage($customer['adminid'], 'emails_used'); + + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_INFO, "[API] added email address '" . $email_full . "'"); + + $result = $this->apiCall('Emails.get', array( + 'emailaddr' => $email_full + )); + return $this->response(200, "successfull", $result); + } + throw new Exception("No more resources available", 406); + } + + /** + * return a email-address entry by either id or email-address + * + * @param int $id + * optional, the customer-id + * @param string $emailaddr + * optional, the email-address + * + * @access admin, customer + * @throws Exception + * @return array + */ + public function get() + { + $id = $this->getParam('id', true, 0); + $ea_optional = ($id <= 0 ? false : true); + $emailaddr = $this->getParam('emailaddr', $ea_optional, ''); + + $params = array(); + $customer_ids = $this->getAllowedCustomerIds('email'); + $params['customerid'] = implode(", ", $customer_ids); + $params['idea'] = ($id <= 0 ? $emailaddr : $id); + + $result_stmt = Database::prepare("SELECT v.`id`, v.`email`, v.`email_full`, v.`iscatchall`, v.`destination`, v.`customerid`, v.`popaccountid`, u.`quota` + FROM `" . TABLE_MAIL_VIRTUAL . "` v + LEFT JOIN `" . TABLE_MAIL_USERS . "` u ON v.`popaccountid` = u.`id` + WHERE v.`customerid` IN (:customerid) + AND (v.`id`= :idea OR (v.`email` = :idea OR v.`email_full` = :idea)) + "); + $result = Database::pexecute_first($result_stmt, $params, true, true); + if ($result) { + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_NOTICE, "[API] get email address '" . $result['email_full'] . "'"); + return $this->response(200, "successfull", $result); + } + $key = ($id > 0 ? "id #" . $id : "emailaddr '" . $emailaddr . "'"); + throw new Exception("Email address with " . $key . " could not be found", 404); + } + + public function update() + { + } + + /** + * list all email addresses, if called from an admin, list all email addresses of all customers you are allowed to view, or specify id or loginname for one specific customer + * + * @param int $customerid + * optional, admin-only, select ftp-users of a specific customer by id + * @param string $loginname + * optional, admin-only, select ftp-users of a specific customer by loginname + * + * @access admin, customer + * @throws Exception + * @return array count|list + */ + public function listing() + { + $customer_ids = $this->getAllowedCustomerIds('email'); + $result = array(); + $params['customerid'] = implode(", ", $customer_ids); + $result_stmt = Database::prepare(" + SELECT m.`id`, m.`domainid`, m.`email`, m.`email_full`, m.`iscatchall`, u.`quota`, m.`destination`, m.`popaccountid`, d.`domain`, u.`mboxsize` + FROM `" . TABLE_MAIL_VIRTUAL . "` m + LEFT JOIN `" . TABLE_PANEL_DOMAINS . "` d ON (m.`domainid` = d.`id`) + LEFT JOIN `" . TABLE_MAIL_USERS . "` u ON (m.`popaccountid` = u.`id`) + WHERE m.`customerid` IN (:customerid) + "); + Database::pexecute($result_stmt, $params, true, true); + while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) { + $result[] = $row; + } + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_NOTICE, "[API] list email-addresses"); + return $this->response(200, "successfull", array( + 'count' => count($result), + 'list' => $result + )); + } + + /** + * delete an email address by either id or username + * + * @access admin, customer + * @throws Exception + * @return array + */ + public function delete() + { + } +} diff --git a/lib/classes/api/commands/class.Ftps.php b/lib/classes/api/commands/class.Ftps.php index 6f3223be..56ef099b 100644 --- a/lib/classes/api/commands/class.Ftps.php +++ b/lib/classes/api/commands/class.Ftps.php @@ -75,8 +75,10 @@ class Ftps extends ApiCommand implements ResourceEntity if (Settings::Get('customer.ftpatdomain') == '1') { $ftpusername = validate(trim($ftpusername), 'username', '/^[a-zA-Z0-9][a-zA-Z0-9\-_]+\$?$/', '', array(), true); - $idna_convert = new idna_convert_wrapper(); - $ftpdomain = $idna_convert->encode(validate($ftpdomain, 'domain', '', '', array(), true)); + if (substr($ftpdomain, 0, 4) != 'xn--') { + $idna_convert = new idna_convert_wrapper(); + $ftpdomain = $idna_convert->encode(validate($ftpdomain, 'domain', '', '', array(), true)); + } } $params = array(); diff --git a/phpunit.xml b/phpunit.xml index efedaf51..f1754d3b 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -16,6 +16,7 @@ tests/SubDomains tests/Certificates tests/Ftps + tests/Emails diff --git a/tests/Emails/EmailsTest.php b/tests/Emails/EmailsTest.php new file mode 100644 index 00000000..5a07bc1e --- /dev/null +++ b/tests/Emails/EmailsTest.php @@ -0,0 +1,44 @@ + 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + + $data = [ + 'email_part' => 'info', + 'domain' => 'test2.local' + ]; + $json_result = Emails::getLocal($customer_userdata, $data)->add(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals("info@test2.local", $result['email_full']); + $this->assertEquals(0, $result['iscatchall']); + } + + public function testAdminEmailsAdd() + { + global $admin_userdata; + $data = [ + 'email_part' => 'catchall', + 'domain' => 'test2.local', + 'iscatchall' => 1, + 'customer_id' => 1 + ]; + $json_result = Emails::getLocal($admin_userdata, $data)->add(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals("catchall@test2.local", $result['email_full']); + $this->assertEquals(1, $result['iscatchall']); + } +} From cd5cef51e848d122fe5bb90eda208e81d42c02a5 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 6 Mar 2018 21:07:08 +0100 Subject: [PATCH 0549/1335] allow config and settings json file for config-services.php to be downloaded from a remote url Signed-off-by: Michael Kaufmann (d00p) --- install/scripts/config-services.php | 42 ++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/install/scripts/config-services.php b/install/scripts/config-services.php index 3b5d1821..80119178 100755 --- a/install/scripts/config-services.php +++ b/install/scripts/config-services.php @@ -61,7 +61,7 @@ class ConfigServicesCmd extends CmdLineHandler self::println("--create\t\tlets you create a services list configuration for the 'apply' command"); self::println(""); self::println("--apply\t\t\tconfigure your services by given configuration file. To create one run the --create command"); - self::println("\t\t\tExample: --apply=/path/to/my-config.json"); + self::println("\t\t\tExample: --apply=/path/to/my-config.json or --apply=http://domain.tld/my-config.json"); self::println(""); self::println("--list-daemons\t\tOutput the services that are going to be configured using a given config file. No services will be configured."); self::println("\t\t\tExample: --apply=/path/to/my-config.json --list-daemons"); @@ -70,7 +70,7 @@ class ConfigServicesCmd extends CmdLineHandler self::println("\t\t\tExample: --apply=/path/to/my-config.json --daemon=apache24"); self::println(""); self::println("--import-settings\tImport settings from another froxlor installation. This should be done prior to running --apply or alternatively in the same command together."); - self::println("\t\t\tExample: --import-settings=/path/to/Froxlor_settings-[version]-[dbversion]-[date].json"); + self::println("\t\t\tExample: --import-settings=/path/to/Froxlor_settings-[version]-[dbversion]-[date].json or --import-settings=http://domain.tld/Froxlor_settings-[version]-[dbversion]-[date].json"); self::println(""); self::println("--froxlor-dir\t\tpath to froxlor installation"); self::println("\t\t\tExample: --froxlor-dir=/var/www/froxlor/"); @@ -127,7 +127,7 @@ class Action if (array_key_exists("import-settings", $this->_args)) { $this->_importSettings(); } - + if (array_key_exists("create", $this->_args)) { $this->_createConfig(); } elseif (array_key_exists("apply", $this->_args)) { @@ -139,6 +139,15 @@ class Action private function _importSettings() { + if (strtoupper(substr($this->_args["import-settings"], 0, 4)) == 'HTTP') { + echo "Settings file seems to be an URL, trying to download" . PHP_EOL; + $target = "/tmp/froxlor-import-settings-" . time() . ".json"; + if (@file_exists($target)) { + @unlink($target); + } + $this->downloadFile($this->_args["import-settings"], $target); + $this->_args["import-settings"] = $target; + } if (! is_file($this->_args["import-settings"])) { throw new Exception("Given settings file is not a file"); } elseif (! file_exists($this->_args["import-settings"])) { @@ -260,6 +269,15 @@ class Action private function _applyConfig() { + if (strtoupper(substr($this->_args["apply"], 0, 4)) == 'HTTP') { + echo "Config file seems to be an URL, trying to download" . PHP_EOL; + $target = "/tmp/froxlor-config-" . time() . ".json"; + if (@file_exists($target)) { + @unlink($target); + } + $this->downloadFile($this->_args["apply"], $target); + $this->_args["apply"] = $target; + } if (! is_file($this->_args["apply"])) { throw new Exception("Given config file is not a file"); } elseif (! file_exists($this->_args["apply"])) { @@ -443,6 +461,24 @@ class Action } } } + + private function downloadFile($src, $dest) + { + set_time_limit(0); + // This is the file where we save the information + $fp = fopen($dest, 'w+'); + // Here is the file we are downloading, replace spaces with %20 + $ch = curl_init(str_replace(" ", "%20", $src)); + curl_setopt($ch, CURLOPT_TIMEOUT, 50); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + // write curl response to file + curl_setopt($ch, CURLOPT_FILE, $fp); + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); + // get curl response + curl_exec($ch); + curl_close($ch); + fclose($fp); + } } // give control to command line handler From 164650adc3fee251fcd4201bb3ef2e7111a87c45 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 8 Mar 2018 11:49:28 +0100 Subject: [PATCH 0550/1335] added Emails.update() and Emails.delete() commands Signed-off-by: Michael Kaufmann (d00p) --- customer_email.php | 153 ++++--------- lib/classes/api/abstract.ApiCommand.php | 15 +- lib/classes/api/commands/class.Customers.php | 8 +- lib/classes/api/commands/class.Emails.php | 215 +++++++++++++++++-- lib/classes/settings/class.Settings.php | 9 + 5 files changed, 253 insertions(+), 147 deletions(-) diff --git a/customer_email.php b/customer_email.php index efed543a..870f243c 100644 --- a/customer_email.php +++ b/customer_email.php @@ -133,65 +133,24 @@ if ($page == 'overview') { eval("echo \"" . getTemplate("email/emails") . "\";"); } elseif ($action == 'delete' && $id != 0) { - $stmt = Database::prepare("SELECT `id`, `email`, `email_full`, `iscatchall`, `destination`, `customerid`, `popaccountid` FROM `" . TABLE_MAIL_VIRTUAL . "` - WHERE `customerid`= :customerid - AND `id`= :id" - ); - $result = Database::pexecute_first($stmt, array("customerid" => $userinfo['customerid'], "id" => $id)); + try { + $json_result = Emails::getLocal($userinfo, array( + 'id' => $id + ))->get(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); + } + $result = json_decode($json_result, true)['data']; if (isset($result['email']) && $result['email'] != '') { if (isset($_POST['send']) && $_POST['send'] == 'send') { - $update_users_query_addon = ''; - - if ($result['destination'] != '') { - $result['destination'] = explode(' ', $result['destination']); - $number_forwarders = count($result['destination']); - - if ($result['popaccountid'] != 0) { - // Free the Quota used by the email account - if (Settings::Get('system.mail_quota_enabled') == 1) { - $stmt = Database::prepare("SELECT `quota` FROM `" . TABLE_MAIL_USERS . "` - WHERE `customerid`= :customerid - AND `id`= :id" - ); - $res_quota = Database::pexecute_first($stmt, array("customerid" => $userinfo['customerid'], "id" => $result['popaccountid'])); - $update_users_query_addon.= " , `email_quota_used` = `email_quota_used` - " . (int)$res_quota['quota'] . " "; - } - - $stmt = Database::prepare("DELETE FROM `" . TABLE_MAIL_USERS . "` - WHERE `customerid`= :customerid - AND `id`= :id" - ); - Database::pexecute($stmt, array("customerid" => $userinfo['customerid'], "id" => $result['popaccountid'])); - $update_users_query_addon .= " , `email_accounts_used` = `email_accounts_used` - 1 "; - $number_forwarders-= 1; - $log->logAction(USR_ACTION, LOG_INFO, "deleted forwarder for email address '" . $result['email'] . "'"); - } - } else { - $number_forwarders = 0; + try { + Emails::getLocal($userinfo, array( + 'id' => $id + ))->delete(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); } - - if (isset($_POST['delete_userfiles']) - && (int)$_POST['delete_userfiles'] == 1 - ) { - inserttask('7', $userinfo['loginname'], $result['email_full']); - } - - $stmt = Database::prepare("DELETE FROM `" . TABLE_MAIL_VIRTUAL . "` - WHERE `customerid`= :customerid - AND `id`= :id" - ); - Database::pexecute($stmt, array("customerid" => $userinfo['customerid'], "id" => $id)); - - $stmt = Database::prepare("UPDATE `" . TABLE_PANEL_CUSTOMERS . "` - SET `emails_used`=`emails_used` - 1 , - `email_forwarders_used` = `email_forwarders_used` - :nforwarders - $update_users_query_addon - WHERE `customerid`= :customerid" - ); - Database::pexecute($stmt, array("nforwarders" => $number_forwarders, "customerid" => $userinfo['customerid'])); - - $log->logAction(USR_ACTION, LOG_INFO, "deleted email address '" . $result['email'] . "'"); redirectTo($filename, array('page' => $page, 's' => $s)); } else { if ($result['popaccountid'] != '0') { @@ -244,14 +203,14 @@ if ($page == 'overview') { standard_error('allresourcesused'); } } elseif ($action == 'edit' && $id != 0) { - $stmt = Database::prepare("SELECT `v`.`id`, `v`.`email`, `v`.`email_full`, `v`.`iscatchall`, `v`.`destination`, `v`.`customerid`, `v`.`popaccountid`, `u`.`quota` - FROM `" . TABLE_MAIL_VIRTUAL . "` `v` - LEFT JOIN `" . TABLE_MAIL_USERS . "` `u` - ON(`v`.`popaccountid` = `u`.`id`) - WHERE `v`.`customerid`= :cid - AND `v`.`id`= :id" - ); - $result = Database::pexecute_first($stmt, array("cid" => $userinfo['customerid'], "id" => $id)); + try { + $json_result = Emails::getLocal($userinfo, array( + 'id' => $id + ))->get(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); + } + $result = json_decode($json_result, true)['data']; if (isset($result['email']) && $result['email'] != '') { $result['email'] = $idna_convert->decode($result['email']); @@ -289,58 +248,24 @@ if ($page == 'overview') { eval("echo \"" . getTemplate("email/emails_edit") . "\";"); } } elseif ($action == 'togglecatchall' && $id != 0) { - if (Settings::Get('catchall.catchall_enabled') == '1') { - $stmt = Database::prepare("SELECT `id`, `email`, `email_full`, `iscatchall`, `destination`, `customerid`, `popaccountid` FROM `" . TABLE_MAIL_VIRTUAL . "` - WHERE `customerid`= :cid - AND `id`= :id" - ); - $result = Database::pexecute_first($stmt, array("cid" => $userinfo['customerid'], "id" => $id)); - - if (isset($result['email']) && $result['email'] != '') { - if ($result['iscatchall'] == '1') { - $stmt = Database::prepare("UPDATE `" . TABLE_MAIL_VIRTUAL . "` - SET `email` = :email, `iscatchall` = '0' - WHERE `customerid`= :cid - AND `id`= :id" - ); - $params = array( - "email" => $result['email_full'], - "cid" => $userinfo['customerid'], - "id" => $id - ); - Database::pexecute($stmt, $params); - } else { - $email_parts = explode('@', $result['email_full']); - $email = '@' . $email_parts[1]; - $stmt = Database::prepare("SELECT `id`, `email`, `email_full`, `iscatchall`, `destination`, `customerid` FROM `" . TABLE_MAIL_VIRTUAL . "` - WHERE `email`= :email - AND `customerid`= :cid" - ); - $email_check = Database::pexecute_first($stmt, array("email" => $email, "cid" => $userinfo['customerid'])); - - if ($email_check['email'] == $email) { - standard_error('youhavealreadyacatchallforthisdomain'); - } else { - $stmt = Database::prepare("UPDATE `" . TABLE_MAIL_VIRTUAL . "` - SET `email` = :email , `iscatchall` = '1' - WHERE `customerid`= :cid - AND `id`= :id" - ); - $params = array( - "email" => $email, - "cid" => $userinfo['customerid'], - "id" => $id - ); - Database::pexecute($stmt, $params); - $log->logAction(USR_ACTION, LOG_INFO, "edited email address '" . $email . "'"); - } - } - - redirectTo($filename, array('page' => $page, 'action' => 'edit', 'id' => $id, 's' => $s)); - } - } else { - standard_error(array('operationnotpermitted', 'featureisdisabled'), 'Catchall'); + try { + $json_result = Emails::getLocal($userinfo, array( + 'id' => $id + ))->get(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); } + $result = json_decode($json_result, true)['data']; + + try { + Emails::getLocal($userinfo, array( + 'id' => $id, + 'iscatchall' => ($result['iscatchall'] == '1' ? 0 : 1) + ))->update(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); + } + redirectTo($filename, array('page' => $page, 'action' => 'edit', 'id' => $id, 's' => $s)); } } elseif ($page == 'accounts') { if ($action == 'add' && $id != 0) { diff --git a/lib/classes/api/abstract.ApiCommand.php b/lib/classes/api/abstract.ApiCommand.php index e3677f9a..0528a79c 100644 --- a/lib/classes/api/abstract.ApiCommand.php +++ b/lib/classes/api/abstract.ApiCommand.php @@ -106,7 +106,7 @@ abstract class ApiCommand $this->version = $version; $this->dbversion = $dbversion; $this->branding = $branding; - + if (! is_null($params)) { $params = $this->trimArray($params); } @@ -417,8 +417,9 @@ abstract class ApiCommand /** * returns an array of customers the current user can access * - * @param string $customer_hide_option optional, when called as customer, some options might be hidden due to the panel.customer_hide_options ettings - * + * @param string $customer_hide_option + * optional, when called as customer, some options might be hidden due to the panel.customer_hide_options ettings + * * @throws Exception * @return array */ @@ -430,7 +431,7 @@ abstract class ApiCommand // or optionally for one specific customer identified by id or loginname $customerid = $this->getParam('customerid', true, 0); $loginname = $this->getParam('loginname', true, ''); - + if (! empty($customerid) || ! empty($loginname)) { $_result = $this->apiCall('Customers.get', array( 'id' => $customerid, @@ -447,7 +448,7 @@ abstract class ApiCommand $customer_ids[] = $customer['customerid']; } } else { - if (!empty($customer_hide_option) && Settings::IsInList('panel.customer_hide_options', $customer_hide_option)) { + if (! empty($customer_hide_option) && Settings::IsInList('panel.customer_hide_options', $customer_hide_option)) { throw new Exception("You cannot access this resource", 405); } $customer_ids = array( @@ -467,11 +468,11 @@ abstract class ApiCommand * @param string $resource * @param string $extra */ - protected static function updateResourceUsage($table = null, $keyfield = null, $key = null, $operator = '+', $resource = null, $extra = null) + protected static function updateResourceUsage($table = null, $keyfield = null, $key = null, $operator = '+', $resource = null, $extra = null, $step = 1) { $stmt = Database::prepare(" UPDATE `" . $table . "` - SET `" . $resource . "` = `" . $resource . "` " . $operator . " 1 " . $extra . " + SET `" . $resource . "` = `" . $resource . "` " . $operator . " " . (int)$step . " " . $extra . " WHERE `" . $keyfield . "` = :key "); Database::pexecute($stmt, array( diff --git a/lib/classes/api/commands/class.Customers.php b/lib/classes/api/commands/class.Customers.php index fdbd42ce..7291dea3 100644 --- a/lib/classes/api/commands/class.Customers.php +++ b/lib/classes/api/commands/class.Customers.php @@ -1547,9 +1547,9 @@ class Customers extends ApiCommand implements ResourceEntity * @param string $extra * optional, default empty */ - public static function increaseUsage($customerid = 0, $resource = null, $extra = '') + public static function increaseUsage($customerid = 0, $resource = null, $extra = '', $increase_by = 1) { - self::updateResourceUsage(TABLE_PANEL_CUSTOMERS, 'customerid', $customerid, '+', $resource, $extra); + self::updateResourceUsage(TABLE_PANEL_CUSTOMERS, 'customerid', $customerid, '+', $resource, $extra, $increase_by); } /** @@ -1560,8 +1560,8 @@ class Customers extends ApiCommand implements ResourceEntity * @param string $extra * optional, default empty */ - public static function decreaseUsage($customerid = 0, $resource = null, $extra = '') + public static function decreaseUsage($customerid = 0, $resource = null, $extra = '', $decrease_by = 1) { - self::updateResourceUsage(TABLE_PANEL_CUSTOMERS, 'customerid', $customerid, '-', $resource, $extra); + self::updateResourceUsage(TABLE_PANEL_CUSTOMERS, 'customerid', $customerid, '-', $resource, $extra, $decrease_by); } } diff --git a/lib/classes/api/commands/class.Emails.php b/lib/classes/api/commands/class.Emails.php index ce45ab72..8f712c31 100644 --- a/lib/classes/api/commands/class.Emails.php +++ b/lib/classes/api/commands/class.Emails.php @@ -17,7 +17,7 @@ */ class Emails extends ApiCommand implements ResourceEntity { - + /** * add a new email address * @@ -30,7 +30,7 @@ class Emails extends ApiCommand implements ResourceEntity if ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'email')) { throw new Exception("You cannot access this resource", 405); } - + if ($this->getUserDetail('emails_used') < $this->getUserDetail('emails') || $this->getUserDetail('emails') == '-1') { // required parameters @@ -39,13 +39,13 @@ class Emails extends ApiCommand implements ResourceEntity // parameters $iscatchall = $this->getParam('iscatchall', true, 0); - + // validation if (substr($domain, 0, 4) != 'xn--') { $idna_convert = new idna_convert_wrapper(); $domain = $idna_convert->encode(validate($domain, 'domain', '', '', array(), true)); } - + // check domain and whether it's an email-enabled domain $domain_check = $this->apiCall('SubDomains.get', array( 'domainname' => $domain @@ -53,7 +53,11 @@ class Emails extends ApiCommand implements ResourceEntity if ($domain_check['isemaildomain'] == 0) { standard_error('maindomainnonexist', $domain, true); } - + + if (Settings::Get('catchall.catchall_enabled') != '1') { + $iscatchall = 0; + } + // check for catchall-flag if ($iscatchall) { $iscatchall = '1'; @@ -62,15 +66,15 @@ class Emails extends ApiCommand implements ResourceEntity $iscatchall = '0'; $email = $email_part . '@' . $domain; } - + // full email value $email_full = $email_part . '@' . $domain; - + // validate it - if (!validateEmail($email_full)) { + if (! validateEmail($email_full)) { standard_error('emailiswrong', $email_full, true); } - + // get needed customer info to reduce the email-address-counter by one if ($this->isAdmin()) { // get customer id @@ -86,7 +90,7 @@ class Emails extends ApiCommand implements ResourceEntity $customer_id = $this->getUserDetail('customerid'); $customer = $this->getUserData(); } - + // duplicate check $stmt = Database::prepare(" SELECT `id`, `email`, `email_full`, `iscatchall`, `destination`, `customerid` FROM `" . TABLE_MAIL_VIRTUAL . "` @@ -99,13 +103,13 @@ class Emails extends ApiCommand implements ResourceEntity "cid" => $customer['customerid'] ); $email_check = Database::pexecute_first($stmt, $params, true, true); - + if (strtolower($email_check['email_full']) == strtolower($email_full)) { standard_error('emailexistalready', $email_full, true); } elseif ($email_check['email'] == $email) { standard_error('youhavealreadyacatchallforthisdomain', '', true); } - + $stmt = Database::prepare(" INSERT INTO `" . TABLE_MAIL_VIRTUAL . "` SET `customerid` = :cid, @@ -123,15 +127,15 @@ class Emails extends ApiCommand implements ResourceEntity ); Database::pexecute($stmt, $params, true, true); $address_id = Database::lastInsertId(); - + // update customer usage Customers::increaseUsage($customer_id, 'emails_used'); - + // update admin usage Admins::increaseUsage($customer['adminid'], 'emails_used'); - + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_INFO, "[API] added email address '" . $email_full . "'"); - + $result = $this->apiCall('Emails.get', array( 'emailaddr' => $email_full )); @@ -139,7 +143,7 @@ class Emails extends ApiCommand implements ResourceEntity } throw new Exception("No more resources available", 406); } - + /** * return a email-address entry by either id or email-address * @@ -147,7 +151,7 @@ class Emails extends ApiCommand implements ResourceEntity * optional, the customer-id * @param string $emailaddr * optional, the email-address - * + * * @access admin, customer * @throws Exception * @return array @@ -177,11 +181,93 @@ class Emails extends ApiCommand implements ResourceEntity $key = ($id > 0 ? "id #" . $id : "emailaddr '" . $emailaddr . "'"); throw new Exception("Email address with " . $key . " could not be found", 404); } - + + /** + * toggle catchall flag of given email address either by id or email-address + * + * @param int $id + * optional, the customer-id + * @param string $emailaddr + * optional, the email-address + * @param boolean $iscatchall + * optional + * @param int $customerid + * optional, required when called as admin/reseller + * + * @access admin, customer + * @throws Exception + * @return array + */ public function update() { + if ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'email')) { + throw new Exception("You cannot access this resource", 405); + } + + // if enabling catchall is not allowed by settings, we do not need + // to run update() + if (Settings::Get('catchall.catchall_enabled') != '1') { + standard_error(array( + 'operationnotpermitted', + 'featureisdisabled' + ), 'catchall', true); + } + + $id = $this->getParam('id', true, 0); + $ea_optional = ($id <= 0 ? false : true); + $emailaddr = $this->getParam('emailaddr', $ea_optional, ''); + + $result = $this->apiCall('Emails.get', array( + 'id' => $id, + 'emailaddr' => $emailaddr + )); + $id = $result['id']; + + // parameters + $iscatchall = $this->getParam('iscatchall', true, $result['iscatchall']); + + // get needed customer info to reduce the email-address-counter by one + if ($this->isAdmin()) { + // get customer id + $customer_id = $this->getParam('customerid'); + $customer = $this->apiCall('Customers.get', array( + 'id' => $customer_id + )); + } else { + $customer_id = $this->getUserDetail('customerid'); + $customer = $this->getUserData(); + } + + // check for catchall-flag + if ($iscatchall) { + $iscatchall = '1'; + $email_parts = explode('@', $result['email_full']); + $email = '@' . $email_parts[1]; + } else { + $iscatchall = '0'; + $email = $result['email_full']; + } + + $stmt = Database::prepare(" + UPDATE `" . TABLE_MAIL_VIRTUAL . "` + SET `email` = :email , `iscatchall` = :caflag + WHERE `customerid`= :cid AND `id`= :id + "); + $params = array( + "email" => $email, + "caflag" => $iscatchall, + "cid" => $customer['customerid'], + "id" => $id + ); + Database::pexecute($stmt, $params, true, true); + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_INFO, "[API] toggled catchall-flag for email address '" . $result['email_full'] . "'"); + + $result = $this->apiCall('Emails.get', array( + 'emailaddr' => $result['email_full'] + )); + return $this->response(200, "successfull", $result); } - + /** * list all email addresses, if called from an admin, list all email addresses of all customers you are allowed to view, or specify id or loginname for one specific customer * @@ -189,7 +275,7 @@ class Emails extends ApiCommand implements ResourceEntity * optional, admin-only, select ftp-users of a specific customer by id * @param string $loginname * optional, admin-only, select ftp-users of a specific customer by loginname - * + * * @access admin, customer * @throws Exception * @return array count|list @@ -216,15 +302,100 @@ class Emails extends ApiCommand implements ResourceEntity 'list' => $result )); } - + /** * delete an email address by either id or username * + * @param int $id + * optional, the customer-id + * @param string $emailaddr + * optional, the email-address + * @param boolean $delete_userfiles + * optional, delete email data from filesystem, default: no + * @param int $customerid + * optional, required when called as admin/reseller + * * @access admin, customer * @throws Exception * @return array */ public function delete() { + if ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'email')) { + throw new Exception("You cannot access this resource", 405); + } + + $id = $this->getParam('id', true, 0); + $ea_optional = ($id <= 0 ? false : true); + $emailaddr = $this->getParam('emailaddr', $ea_optional, ''); + + $result = $this->apiCall('Emails.get', array( + 'id' => $id, + 'emailaddr' => $emailaddr + )); + $id = $result['id']; + + // parameters + $delete_userfiles = $this->getParam('delete_userfiles', true, 0); + + // get needed customer info to reduce the email-address-counter by one + if ($this->isAdmin()) { + // get customer id + $customer_id = $this->getParam('customerid'); + $customer = $this->apiCall('Customers.get', array( + 'id' => $customer_id + )); + } else { + $customer_id = $this->getUserDetail('customerid'); + $customer = $this->getUserData(); + } + + // check for forwarders + $number_forwarders = 0; + if ($result['destination'] != '') { + $result['destination'] = explode(' ', $result['destination']); + $number_forwarders = count($result['destination']); + Customers::decreaseUsage($customer['customerid'], 'email_forwarders_used', '', $number_forwarders); + Admins::decreaseUsage($customer['customerid'], 'email_forwarders_used', '', $number_forwarders); + } + // check whether this address is an account + if ($result['popaccountid'] != 0) { + // Free the Quota used by the email account + if (Settings::Get('system.mail_quota_enabled') == 1) { + $stmt = Database::prepare("SELECT `quota` FROM `" . TABLE_MAIL_USERS . "` WHERE `customerid`= :customerid AND `id`= :id"); + $res_quota = Database::pexecute_first($stmt, array( + "customerid" => $customer_id, + "id" => $result['popaccountid'] + ), true, true); + Customers::decreaseUsage($customer['customerid'], 'email_quota_used', '', $res_quota['quota']); + Admins::decreaseUsage($customer['customerid'], 'email_quota_used', '', $res_quota['quota']); + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_INFO, "[API] deleted quota entries for email address '" . $result['email_full'] . "'"); + } + // delete account + $stmt = Database::prepare("DELETE FROM `" . TABLE_MAIL_USERS . "` WHERE `customerid`= :customerid AND `id`= :id"); + Database::pexecute($stmt, array( + "customerid" => $customer_id, + "id" => $result['popaccountid'] + ), true, true); + Customers::decreaseUsage($customer['customerid'], 'email_accounts_used'); + Admins::decreaseUsage($customer['customerid'], 'email_accounts_used'); + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_INFO, "[API] deleted email account '" . $result['email_full'] . "'"); + } + + if ($delete_userfiles) { + inserttask('7', $customer['loginname'], $result['email_full']); + } + + // delete address + $stmt = Database::prepare("DELETE FROM `" . TABLE_MAIL_VIRTUAL . "` WHERE `customerid`= :customerid AND `id`= :id"); + Database::pexecute($stmt, array( + "customerid" => $customer_id, + "id" => $id + ), true, true); + Customers::decreaseUsage($customer['customerid'], 'emails_used'); + Admins::decreaseUsage($customer['customerid'], 'emails_used'); + + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_INFO, "[API] deleted email address '" . $result['email_full'] . "'"); + return $this->response(200, "successfull", $result); } } diff --git a/lib/classes/settings/class.Settings.php b/lib/classes/settings/class.Settings.php index e6b8a37b..3ab57ecb 100644 --- a/lib/classes/settings/class.Settings.php +++ b/lib/classes/settings/class.Settings.php @@ -28,6 +28,13 @@ * @author Froxlor team (2010-) * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt * @package Classes + * + * @method static mixed Get ($setting = null) return a setting-value by its group and varname separated by a dot (group.varname) + * @method static boolean Set ($setting = null, $value = null, $instant_save = true) update a setting / set a new value + * @method static boolean IsInList ($setting = null, $entry = null) tests if a setting-value that i s a comma separated list contains an entry + * @method static boolean AddNew ($setting = null, $value = null) add a new setting to the database (mainly used in updater) + * @method static boolean Flush () Store all un-saved changes to the database and re-read in all settings + * @method static void Stash () forget all un-saved changes to settings */ class Settings { @@ -148,6 +155,8 @@ class Settings { * @param string $setting a group and a varname separated by a dot (group.varname) * @param string $value * @param boolean $instant_save + * + * @return bool */ public function pSet($setting = null, $value = null, $instant_save = true) { // check whether the setting exists From 7b52c0c78c2c53507f1700f4429b1a677e0191e2 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 8 Mar 2018 17:03:17 +0100 Subject: [PATCH 0551/1335] fix default value of panel.no_robots settings; add phpdoc for Database-class-methods Signed-off-by: Michael Kaufmann (d00p) --- actions/admin/settings/100.panel.php | 2 +- lib/classes/database/class.Database.php | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/actions/admin/settings/100.panel.php b/actions/admin/settings/100.panel.php index 49308bed..cae04cf9 100644 --- a/actions/admin/settings/100.panel.php +++ b/actions/admin/settings/100.panel.php @@ -71,7 +71,7 @@ return array( 'settinggroup' => 'panel', 'varname' => 'no_robots', 'type' => 'bool', - 'default' => false, + 'default' => true, 'save_method' => 'storeSettingField', ), 'panel_paging' => array( diff --git a/lib/classes/database/class.Database.php b/lib/classes/database/class.Database.php index 3174c2b3..80ef61bc 100644 --- a/lib/classes/database/class.Database.php +++ b/lib/classes/database/class.Database.php @@ -28,6 +28,11 @@ * @author Froxlor team (2010-) * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt * @package Classes + * + * @method static \PDOStatement prepare($statement, array $driver_options = null) Prepares a statement for execution and returns a statement object + * @method static \PDOStatement query ($statement) Executes an SQL statement, returning a result set as a PDOStatement object + * @method static string lastInsertId ($name = null) Returns the ID of the last inserted row or sequence value + * @method static string quote ($string, $parameter_type = null) Quotes a string for use in a query. */ class Database { From b205f8ea5df1cdebd25bef661b6963b30d2872b9 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sun, 11 Mar 2018 10:24:17 +0100 Subject: [PATCH 0552/1335] add EmailFowarders ApiCommand Signed-off-by: Michael Kaufmann (d00p) --- customer_email.php | 86 ++----- lib/classes/api/abstract.ApiCommand.php | 15 +- .../api/commands/class.EmailForwarders.php | 226 ++++++++++++++++++ lib/classes/api/commands/class.Emails.php | 15 +- 4 files changed, 274 insertions(+), 68 deletions(-) create mode 100644 lib/classes/api/commands/class.EmailForwarders.php diff --git a/customer_email.php b/customer_email.php index 870f243c..8be1013c 100644 --- a/customer_email.php +++ b/customer_email.php @@ -658,48 +658,21 @@ if ($page == 'overview') { } elseif ($page == 'forwarders') { if ($action == 'add' && $id != 0) { if ($userinfo['email_forwarders_used'] < $userinfo['email_forwarders'] || $userinfo['email_forwarders'] == '-1') { - $stmt = Database::prepare("SELECT `id`, `email`, `email_full`, `iscatchall`, `destination`, `customerid`, `popaccountid`, `domainid` FROM `" . TABLE_MAIL_VIRTUAL . "` - WHERE `customerid`= :cid - AND `id`= :id" - ); - $result = Database::pexecute_first($stmt, array("cid" => $userinfo['customerid'], "id" => $id)); + try { + $json_result = Emails::getLocal($userinfo, array('id' => $id))->get(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); + } + $result = json_decode($json_result, true)['data']; if (isset($result['email']) && $result['email'] != '') { if (isset($_POST['send']) && $_POST['send'] == 'send') { - $destination = $idna_convert->encode($_POST['destination']); - $result['destination_array'] = explode(' ', $result['destination']); - - if ($destination == '') { - standard_error('destinationnonexist'); - } elseif (!validateEmail($destination)) { - standard_error('destinationiswrong', $destination); - } elseif ($destination == $result['email']) { - standard_error('destinationalreadyexistasmail', $destination); - } elseif (in_array($destination, $result['destination_array'])) { - standard_error('destinationalreadyexist', $destination); - } else { - $result['destination'].= ' ' . $destination; - $stmt = Database::prepare("UPDATE `" . TABLE_MAIL_VIRTUAL . "` - SET `destination` = :dest - WHERE `customerid`= :cid - AND `id`= :id" - ); - $params = array( - "dest" => makeCorrectDestination($result['destination']), - "cid" => $userinfo['customerid'], - "id" => $id - ); - Database::pexecute($stmt, $params); - - $stmt = Database::prepare("UPDATE `" . TABLE_PANEL_CUSTOMERS . "` - SET `email_forwarders_used` = `email_forwarders_used` + 1 - WHERE `customerid`= :cid" - ); - Database::pexecute($stmt, array("cid" => $userinfo['customerid'])); - - $log->logAction(USR_ACTION, LOG_INFO, "added email forwarder for '" . $result['email_full'] . "'"); - redirectTo($filename, array('page' => 'emails', 'action' => 'edit', 'id' => $id, 's' => $s)); + try { + EmailForwarders::getLocal($userinfo, $_POST)->add(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); } + redirectTo($filename, array('page' => 'emails', 'action' => 'edit', 'id' => $id, 's' => $s)); } else { $result['email_full'] = $idna_convert->decode($result['email_full']); $result = htmlentities_array($result); @@ -717,11 +690,12 @@ if ($page == 'overview') { standard_error('allresourcesused'); } } elseif ($action == 'delete' && $id != 0) { - $stmt = Database::prepare("SELECT `id`, `email`, `email_full`, `iscatchall`, `destination`, `customerid`, `popaccountid` FROM `" . TABLE_MAIL_VIRTUAL . "` - WHERE `customerid`='" . (int)$userinfo['customerid'] . "' - AND `id`='" . (int)$id . "'" - ); - $result = Database::pexecute_first($stmt, array("cid" => $userinfo['customerid'])); + try { + $json_result = Emails::getLocal($userinfo, array('id' => $id))->get(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); + } + $result = json_decode($json_result, true)['data']; if (isset($result['destination']) && $result['destination'] != '') { if (isset($_POST['forwarderid'])) { @@ -738,27 +712,11 @@ if ($page == 'overview') { $forwarder = $result['destination'][$forwarderid]; if (isset($_POST['send']) && $_POST['send'] == 'send') { - unset($result['destination'][$forwarderid]); - $result['destination'] = implode(' ', $result['destination']); - $stmt = Database::prepare("UPDATE `" . TABLE_MAIL_VIRTUAL . "` - SET `destination` = :dest - WHERE `customerid`= :cid - AND `id`= :id" - ); - $params = array( - "dest" => makeCorrectDestination($result['destination']), - "cid" => $userinfo['customerid'], - "id" => $id - ); - Database::pexecute($stmt, $params); - - $stmt = Database::prepare("UPDATE `" . TABLE_PANEL_CUSTOMERS . "` - SET `email_forwarders_used` = `email_forwarders_used` - 1 - WHERE `customerid`= :cid" - ); - Database::pexecute($stmt, array("cid" => $userinfo['customerid'])); - - $log->logAction(USR_ACTION, LOG_INFO, "deleted email forwarder for '" . $result['email_full'] . "'"); + try { + EmailForwarders::getLocal($userinfo, $_POST)->delete(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); + } redirectTo($filename, array('page' => 'emails', 'action' => 'edit', 'id' => $id, 's' => $s)); } else { ask_yesno('email_reallydelete_forwarder', $filename, array('id' => $id, 'forwarderid' => $forwarderid, 'page' => $page, 'action' => $action), $idna_convert->decode($result['email_full']) . ' -> ' . $idna_convert->decode($forwarder)); diff --git a/lib/classes/api/abstract.ApiCommand.php b/lib/classes/api/abstract.ApiCommand.php index 0528a79c..da3f0430 100644 --- a/lib/classes/api/abstract.ApiCommand.php +++ b/lib/classes/api/abstract.ApiCommand.php @@ -349,6 +349,16 @@ abstract class ApiCommand throw new Exception("Unable to update parameter '" . $param . "' as it does not exist", 500); } + /** + * return list of all parameters + * + * @return array + */ + protected function getParamList() + { + return $this->cmd_params; + } + /** * return logger instance * @@ -455,6 +465,9 @@ abstract class ApiCommand $this->getUserDetail('customerid') ); } + if (empty($customer_ids)) { + throw new Exception("Required resource unsatisfied.", 405); + } return $customer_ids; } @@ -472,7 +485,7 @@ abstract class ApiCommand { $stmt = Database::prepare(" UPDATE `" . $table . "` - SET `" . $resource . "` = `" . $resource . "` " . $operator . " " . (int)$step . " " . $extra . " + SET `" . $resource . "` = `" . $resource . "` " . $operator . " " . (int) $step . " " . $extra . " WHERE `" . $keyfield . "` = :key "); Database::pexecute($stmt, array( diff --git a/lib/classes/api/commands/class.EmailForwarders.php b/lib/classes/api/commands/class.EmailForwarders.php new file mode 100644 index 00000000..f4658c9b --- /dev/null +++ b/lib/classes/api/commands/class.EmailForwarders.php @@ -0,0 +1,226 @@ + (2010-) + * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt + * @package API + * @since 0.10.0 + * + */ +class EmailForwarders extends ApiCommand implements ResourceEntity +{ + + /** + * add new email-forwarder entry for given email-address by either id or email-address + * + * @param int $id + * optional, the email-address-id + * @param string $emailaddr + * optional, the email-address to add the forwarder for + * @param string $destination + * email-address to add as forwarder + * @param int $customerid + * optional, required when called as admin/reseller + * + * @access admin,customer + * @throws Exception + * @return array + */ + public function add() + { + if ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'email')) { + throw new Exception("You cannot access this resource", 405); + } + + if ($this->getUserDetail('email_forwarders_used') < $this->getUserDetail('email_forwarders') || $this->getUserDetail('email_forwarders') == '-1') { + + // parameter + $id = $this->getParam('id', true, 0); + $ea_optional = ($id <= 0 ? false : true); + $emailaddr = $this->getParam('emailaddr', $ea_optional, ''); + $destination = $this->getParam('destination'); + + // validation + $idna_convert = new idna_convert_wrapper(); + $destination = $idna_convert->encode($destination); + + $result = $this->apiCall('Emails.get', array( + 'id' => $id, + 'emailaddr' => $emailaddr + )); + $id = $result['id']; + + // current destination array + $result['destination_array'] = explode(' ', $result['destination']); + + if (! validateEmail($destination)) { + standard_error('destinationiswrong', $destination, true); + } elseif ($destination == $result['email']) { + standard_error('destinationalreadyexistasmail', $destination, true); + } elseif (in_array($destination, $result['destination_array'])) { + standard_error('destinationalreadyexist', $destination, true); + } + + // get needed customer info to reduce the email-address-counter by one + if ($this->isAdmin()) { + // get customer id + $customer_id = $this->getParam('customerid'); + $customer = $this->apiCall('Customers.get', array( + 'id' => $customer_id + )); + // check whether the customer has enough resources to get the mail-forwarder added + if ($customer['email_forwarders_used'] >= $customer['email_forwarders'] && $customer['email_forwarders'] != '-1') { + throw new Exception("Customer has no more resources available", 406); + } + } else { + $customer_id = $this->getUserDetail('customerid'); + $customer = $this->getUserData(); + } + + // add destination to address + $result['destination'] .= ' ' . $destination; + $stmt = Database::prepare(" + UPDATE `" . TABLE_MAIL_VIRTUAL . "` SET `destination` = :dest + WHERE `customerid`= :cid AND `id`= :id + "); + $params = array( + "dest" => makeCorrectDestination($result['destination']), + "cid" => $customer_id, + "id" => $id + ); + Database::pexecute($stmt, $params, true, true); + + // update customer usage + Customers::increaseUsage($customer_id, 'email_forwarders_used'); + + // update admin usage + Admins::increaseUsage($customer['adminid'], 'email_forwarders_used'); + + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_INFO, "[API] added email forwarder for '" . $result['email_full'] . "'"); + + $result = $this->apiCall('Emails.get', array( + 'emailaddr' => $result['email_full'] + )); + return $this->response(200, "successfull", $result); + } + throw new Exception("No more resources available", 406); + } + + public function get() + { + throw new Exception('You cannot directly get an email forwarder. You need to call Emails.get()', 303); + } + + public function update() + { + throw new Exception('You cannot update an email forwarder. You need to delete the entry and create a new one.', 303); + } + + public function listing() + { + throw new Exception('You cannot directly list email forwarders. You need to call Emails.listing()', 303); + } + + /** + * delete email-forwarder entry for given email-address by either id or email-address and forwarder-id + * + * @param int $id + * optional, the email-address-id + * @param string $emailaddr + * optional, the email-address to add the forwarder for + * @param int $forwarderid + * id of the forwarder to delete + * @param int $customerid + * optional, required when called as admin/reseller + * + * @access admin,customer + * @throws Exception + * @return array + */ + public function delete() + { + if ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'email')) { + throw new Exception("You cannot access this resource", 405); + } + + if ($this->getUserDetail('email_forwarders_used') < $this->getUserDetail('email_forwarders') || $this->getUserDetail('email_forwarders') == '-1') { + + // parameter + $id = $this->getParam('id', true, 0); + $ea_optional = ($id <= 0 ? false : true); + $emailaddr = $this->getParam('emailaddr', $ea_optional, ''); + $forwarderid = $this->getParam('forwarderid'); + + // validation + $result = $this->apiCall('Emails.get', array( + 'id' => $id, + 'emailaddr' => $emailaddr + )); + $id = $result['id']; + + $result['destination'] = explode(' ', $result['destination']); + if (isset($result['destination'][$forwarderid]) && $result['email'] != $result['destination'][$forwarderid]) { + + // get needed customer info to reduce the email-address-counter by one + if ($this->isAdmin()) { + // get customer id + $customer_id = $this->getParam('customer_id'); + $customer = $this->apiCall('Customers.get', array( + 'id' => $customer_id + )); + } else { + $customer_id = $this->getUserDetail('customerid'); + $customer = $this->getUserData(); + } + + // get specific forwarder + $forwarder = $result['destination'][$forwarderid]; + // unset it from array + unset($result['destination'][$forwarderid]); + // rebuild destination-string + $result['destination'] = implode(' ', $result['destination']); + // update in DB + $stmt = Database::prepare(" + UPDATE `" . TABLE_MAIL_VIRTUAL . "` SET `destination` = :dest + WHERE `customerid`= :cid AND `id`= :id + "); + $params = array( + "dest" => makeCorrectDestination($result['destination']), + "cid" => $customer['customerid'], + "id" => $id + ); + Database::pexecute($stmt, $params, true, true); + + $stmt = Database::prepare("UPDATE `" . TABLE_PANEL_CUSTOMERS . "` + SET `email_forwarders_used` = `email_forwarders_used` - 1 + WHERE `customerid`= :cid"); + Database::pexecute($stmt, array( + "cid" => $userinfo['customerid'] + )); + + // update customer usage + Customers::decreaseUsage($customer_id, 'email_forwarders_used'); + + // update admin usage + Admins::decreaseUsage($customer['adminid'], 'email_forwarders_used'); + + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_INFO, "[API] deleted email forwarder for '" . $result['email_full'] . "'"); + + $result = $this->apiCall('Emails.get', array( + 'emailaddr' => $result['email_full'] + )); + return $this->response(200, "successfull", $result); + } + throw new Exception("Unknown forwarder id", 404); + } + throw new Exception("No more resources available", 406); + } +} diff --git a/lib/classes/api/commands/class.Emails.php b/lib/classes/api/commands/class.Emails.php index 8f712c31..b72bfbeb 100644 --- a/lib/classes/api/commands/class.Emails.php +++ b/lib/classes/api/commands/class.Emails.php @@ -21,6 +21,15 @@ class Emails extends ApiCommand implements ResourceEntity /** * add a new email address * + * @param string $email_part + * name of the address before @ + * @param string $domain + * domain-name for the email-address + * @param boolean $iscatchall + * optional, make this address a catchall address, default: no + * @param int $customerid + * optional, required when called as admin/reseller + * * @access admin, customer * @throws Exception * @return array @@ -78,11 +87,11 @@ class Emails extends ApiCommand implements ResourceEntity // get needed customer info to reduce the email-address-counter by one if ($this->isAdmin()) { // get customer id - $customer_id = $this->getParam('customer_id'); + $customer_id = $this->getParam('customerid'); $customer = $this->apiCall('Customers.get', array( 'id' => $customer_id )); - // check whether the customer has enough resources to get the ftp-user added + // check whether the customer has enough resources to get the mail-address added if ($customer['emails_used'] >= $customer['emails'] && $customer['emails'] != '-1') { throw new Exception("Customer has no more resources available", 406); } @@ -148,7 +157,7 @@ class Emails extends ApiCommand implements ResourceEntity * return a email-address entry by either id or email-address * * @param int $id - * optional, the customer-id + * optional, the email-address-id * @param string $emailaddr * optional, the email-address * From 81bd9d945d46e746b57263bb2922506707c32159 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sun, 11 Mar 2018 10:26:05 +0100 Subject: [PATCH 0553/1335] fix parameter customerid for Emails.add() in unit-test Signed-off-by: Michael Kaufmann (d00p) --- tests/Emails/EmailsTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Emails/EmailsTest.php b/tests/Emails/EmailsTest.php index 5a07bc1e..4ba60987 100644 --- a/tests/Emails/EmailsTest.php +++ b/tests/Emails/EmailsTest.php @@ -34,7 +34,7 @@ class MailsTest extends TestCase 'email_part' => 'catchall', 'domain' => 'test2.local', 'iscatchall' => 1, - 'customer_id' => 1 + 'customerid' => 1 ]; $json_result = Emails::getLocal($admin_userdata, $data)->add(); $result = json_decode($json_result, true)['data']; From 349fa7a761029adbaadbb872d1ee9962797d4e9f Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 12 Mar 2018 10:22:50 +0100 Subject: [PATCH 0554/1335] avoid possible undefined index if no issuer-organisation is set in a certificate Signed-off-by: Michael Kaufmann (d00p) --- templates/Sparkle/ssl_certificates/certs_cert.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/Sparkle/ssl_certificates/certs_cert.tpl b/templates/Sparkle/ssl_certificates/certs_cert.tpl index e383f54f..f0755efc 100644 --- a/templates/Sparkle/ssl_certificates/certs_cert.tpl +++ b/templates/Sparkle/ssl_certificates/certs_cert.tpl @@ -8,7 +8,7 @@
    SAN: {$san_list}
    - {$cert_data['issuer']['O']} + {$cert_data['issuer']['O']} {$validFrom} From c920bf6a63edcc2beed16bcca7187c705df42bad Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 12 Mar 2018 10:45:46 +0100 Subject: [PATCH 0555/1335] some code-reduction Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/abstract.ApiCommand.php | 185 ++++-------------- lib/classes/api/abstract.ApiParameter.php | 180 +++++++++++++++++ .../api/commands/class.EmailForwarders.php | 36 +--- lib/classes/api/commands/class.Emails.php | 45 +---- lib/classes/api/commands/class.Ftps.php | 36 +--- lib/classes/api/commands/class.Mysqls.php | 53 ++--- lib/classes/api/commands/class.SubDomains.php | 61 ++---- 7 files changed, 261 insertions(+), 335 deletions(-) create mode 100644 lib/classes/api/abstract.ApiParameter.php diff --git a/lib/classes/api/abstract.ApiCommand.php b/lib/classes/api/abstract.ApiCommand.php index da3f0430..a0c66726 100644 --- a/lib/classes/api/abstract.ApiCommand.php +++ b/lib/classes/api/abstract.ApiCommand.php @@ -15,7 +15,7 @@ * @since 0.10.0 * */ -abstract class ApiCommand +abstract class ApiCommand extends ApiParameter { /** @@ -53,13 +53,6 @@ abstract class ApiCommand */ private $mail = null; - /** - * array of parameters passed to the command - * - * @var array - */ - private $cmd_params = null; - /** * language strings array * @@ -103,14 +96,12 @@ abstract class ApiCommand { global $lng, $version, $dbversion, $branding; + parent::__construct($params); + $this->version = $version; $this->dbversion = $dbversion; $this->branding = $branding; - if (! is_null($params)) { - $params = $this->trimArray($params); - } - $this->cmd_params = $params; if (! empty($header)) { $this->readUserData($header); } elseif (! empty($userinfo)) { @@ -268,97 +259,6 @@ abstract class ApiCommand return $this->user_data; } - /** - * get specific parameter from the parameterlist; - * check for existence and != empty if needed. - * Maybe more in the future - * - * @param string $param - * parameter to get out of the request-parameter list - * @param bool $optional - * default: false - * @param mixed $default - * value which is returned if optional=true and param is not set - * - * @throws Exception - * @return mixed - */ - protected function getParam($param = null, $optional = false, $default = '') - { - // does it exist? - if (! isset($this->cmd_params[$param])) { - if ($optional === false) { - // get module + function for better error-messages - $inmod = $this->getModFunctionString(); - throw new Exception('Requested parameter "' . $param . '" could not be found for "' . $inmod . '"', 404); - } - return $default; - } - // is it empty? - test really on string, as value 0 is being seen as empty by php - if ($this->cmd_params[$param] === "") { - if ($optional === false) { - // get module + function for better error-messages - $inmod = $this->getModFunctionString(); - throw new Exception('Requested parameter "' . $param . '" is empty where it should not be for "' . $inmod . '"', 406); - } - return ''; - } - // everything else is fine - return $this->cmd_params[$param]; - } - - /** - * get specific parameter which also has and unlimited-field - * - * @param string $param - * parameter to get out of the request-parameter list - * @param string $ul_field - * parameter to get out of the request-parameter list - * @param bool $optional - * default: false - * @param mixed $default - * value which is returned if optional=true and param is not set - * - * @return mixed - */ - protected function getUlParam($param = null, $ul_field = null, $optional = false, $default = 0) - { - $param_value = intval_ressource($this->getParam($param, $optional, $default)); - $ul_field_value = $this->getParam($ul_field, true, 0); - if ($ul_field_value != 0) { - $param_value = - 1; - } - return $param_value; - } - - /** - * update value of parameter - * - * @param string $param - * @param mixed $value - * - * @throws Exception - * @return boolean - */ - protected function updateParam($param, $value = null) - { - if (isset($this->cmd_params[$param])) { - $this->cmd_params[$param] = $value; - return true; - } - throw new Exception("Unable to update parameter '" . $param . "' as it does not exist", 500); - } - - /** - * return list of all parameters - * - * @return array - */ - protected function getParamList() - { - return $this->cmd_params; - } - /** * return logger instance * @@ -471,6 +371,38 @@ abstract class ApiCommand return $customer_ids; } + /** + * returns an array of customer data for customer, or by customer-id/loginname for admin/reseller + * + * @param int $customerid + * optional, required if loginname is empty + * @param string $loginname + * optional, required of customerid is empty + * @param string $customer_resource_check + * optional, when called as admin, check the resources of the target customer + * + * @throws Exception + * @return array + */ + protected function getCustomerData($customer_resource_check = '') + { + if ($this->isAdmin()) { + $customerid = $this->getParam('customerid', true, 0); + $loginname = $this->getParam('loginname', true, ''); + $customer = $this->apiCall('Customers.get', array( + 'id' => $customerid, + 'loginname' => $loginname + )); + // check whether the customer has enough resources + if (! empty($customer_resource_check) && $customer[$customer_resource_check . '_used'] >= $customer[$customer_resource_check] && $customer[$customer_resource_check] != '-1') { + throw new Exception("Customer has no more resources available", 406); + } + } else { + $customer = $this->getUserData(); + } + return $customer; + } + /** * increase/decrease a resource field for customers/admins * @@ -493,35 +425,6 @@ abstract class ApiCommand ), true, true); } - /** - * returns "module::function()" for better error-messages (missing parameter etc.) - * makes debugging a whole lot more comfortable - * - * @return string - */ - private function getModFunctionString() - { - $_class = get_called_class(); - $level = 2; - if (version_compare(PHP_VERSION, "5.4.0", "<")) { - $trace = debug_backtrace(); - } else { - $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); - } - while (true) { - $class = $trace[$level]['class']; - $func = $trace[$level]['function']; - if ($class != $_class) { - $level ++; - if ($level > 5) { - break; - } - continue; - } - return $class . ':' . $func; - } - } - /** * read user data from database by api-request-header fields * @@ -563,22 +466,4 @@ abstract class ApiCommand } throw new Exception("Invalid API credentials", 400); } - - /** - * run 'trim' function on an array recursively - * - * @param array $input - * - * @return array - */ - private function trimArray($input) - { - if (! is_array($input)) { - return trim($input); - } - return array_map(array( - $this, - 'trimArray' - ), $input); - } } diff --git a/lib/classes/api/abstract.ApiParameter.php b/lib/classes/api/abstract.ApiParameter.php new file mode 100644 index 00000000..749b4958 --- /dev/null +++ b/lib/classes/api/abstract.ApiParameter.php @@ -0,0 +1,180 @@ + (2010-) + * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt + * @package API + * @since 0.10.0 + * + */ +abstract class ApiParameter +{ + + /** + * array of parameters passed to the command + * + * @var array + */ + private $cmd_params = null; + + /** + * + * @param array $params + * optional, array of parameters (var=>value) for the command + * + * @throws Exception + */ + public function __construct($params = null) + { + if (! is_null($params)) { + $params = $this->trimArray($params); + } + $this->cmd_params = $params; + } + + /** + * get specific parameter from the parameterlist; + * check for existence and != empty if needed. + * Maybe more in the future + * + * @param string $param + * parameter to get out of the request-parameter list + * @param bool $optional + * default: false + * @param mixed $default + * value which is returned if optional=true and param is not set + * + * @throws Exception + * @return mixed + */ + protected function getParam($param = null, $optional = false, $default = '') + { + // does it exist? + if (! isset($this->cmd_params[$param])) { + if ($optional === false) { + // get module + function for better error-messages + $inmod = $this->getModFunctionString(); + throw new Exception('Requested parameter "' . $param . '" could not be found for "' . $inmod . '"', 404); + } + return $default; + } + // is it empty? - test really on string, as value 0 is being seen as empty by php + if ($this->cmd_params[$param] === "") { + if ($optional === false) { + // get module + function for better error-messages + $inmod = $this->getModFunctionString(); + throw new Exception('Requested parameter "' . $param . '" is empty where it should not be for "' . $inmod . '"', 406); + } + return ''; + } + // everything else is fine + return $this->cmd_params[$param]; + } + + /** + * get specific parameter which also has and unlimited-field + * + * @param string $param + * parameter to get out of the request-parameter list + * @param string $ul_field + * parameter to get out of the request-parameter list + * @param bool $optional + * default: false + * @param mixed $default + * value which is returned if optional=true and param is not set + * + * @return mixed + */ + protected function getUlParam($param = null, $ul_field = null, $optional = false, $default = 0) + { + $param_value = intval_ressource($this->getParam($param, $optional, $default)); + $ul_field_value = $this->getParam($ul_field, true, 0); + if ($ul_field_value != 0) { + $param_value = - 1; + } + return $param_value; + } + + /** + * update value of parameter + * + * @param string $param + * @param mixed $value + * + * @throws Exception + * @return boolean + */ + protected function updateParam($param, $value = null) + { + if (isset($this->cmd_params[$param])) { + $this->cmd_params[$param] = $value; + return true; + } + throw new Exception("Unable to update parameter '" . $param . "' as it does not exist", 500); + } + + /** + * return list of all parameters + * + * @return array + */ + protected function getParamList() + { + return $this->cmd_params; + } + + /** + * returns "module::function()" for better error-messages (missing parameter etc.) + * makes debugging a whole lot more comfortable + * + * @return string + */ + private function getModFunctionString() + { + $_class = get_called_class(); + $level = 2; + if (version_compare(PHP_VERSION, "5.4.0", "<")) { + $trace = debug_backtrace(); + } else { + $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); + } + while (true) { + $class = $trace[$level]['class']; + $func = $trace[$level]['function']; + if ($class != $_class) { + $level ++; + if ($level > 5) { + break; + } + continue; + } + return $class . ':' . $func; + } + } + + /** + * run 'trim' function on an array recursively + * + * @param array $input + * + * @return array + */ + private function trimArray($input) + { + if (! is_array($input)) { + return trim($input); + } + return array_map(array( + $this, + 'trimArray' + ), $input); + } +} diff --git a/lib/classes/api/commands/class.EmailForwarders.php b/lib/classes/api/commands/class.EmailForwarders.php index f4658c9b..ee95f349 100644 --- a/lib/classes/api/commands/class.EmailForwarders.php +++ b/lib/classes/api/commands/class.EmailForwarders.php @@ -69,21 +69,8 @@ class EmailForwarders extends ApiCommand implements ResourceEntity standard_error('destinationalreadyexist', $destination, true); } - // get needed customer info to reduce the email-address-counter by one - if ($this->isAdmin()) { - // get customer id - $customer_id = $this->getParam('customerid'); - $customer = $this->apiCall('Customers.get', array( - 'id' => $customer_id - )); - // check whether the customer has enough resources to get the mail-forwarder added - if ($customer['email_forwarders_used'] >= $customer['email_forwarders'] && $customer['email_forwarders'] != '-1') { - throw new Exception("Customer has no more resources available", 406); - } - } else { - $customer_id = $this->getUserDetail('customerid'); - $customer = $this->getUserData(); - } + // get needed customer info to reduce the email-forwarder-counter by one + $customer = $this->getCustomerData('email_forwarders'); // add destination to address $result['destination'] .= ' ' . $destination; @@ -93,13 +80,13 @@ class EmailForwarders extends ApiCommand implements ResourceEntity "); $params = array( "dest" => makeCorrectDestination($result['destination']), - "cid" => $customer_id, + "cid" => $customer['customerid'], "id" => $id ); Database::pexecute($stmt, $params, true, true); // update customer usage - Customers::increaseUsage($customer_id, 'email_forwarders_used'); + Customers::increaseUsage($customer['customerid'], 'email_forwarders_used'); // update admin usage Admins::increaseUsage($customer['adminid'], 'email_forwarders_used'); @@ -169,17 +156,8 @@ class EmailForwarders extends ApiCommand implements ResourceEntity $result['destination'] = explode(' ', $result['destination']); if (isset($result['destination'][$forwarderid]) && $result['email'] != $result['destination'][$forwarderid]) { - // get needed customer info to reduce the email-address-counter by one - if ($this->isAdmin()) { - // get customer id - $customer_id = $this->getParam('customer_id'); - $customer = $this->apiCall('Customers.get', array( - 'id' => $customer_id - )); - } else { - $customer_id = $this->getUserDetail('customerid'); - $customer = $this->getUserData(); - } + // get needed customer info to reduce the email-forwarder-counter by one + $customer = $this->getCustomerData(); // get specific forwarder $forwarder = $result['destination'][$forwarderid]; @@ -207,7 +185,7 @@ class EmailForwarders extends ApiCommand implements ResourceEntity )); // update customer usage - Customers::decreaseUsage($customer_id, 'email_forwarders_used'); + Customers::decreaseUsage($customer['customerid'], 'email_forwarders_used'); // update admin usage Admins::decreaseUsage($customer['adminid'], 'email_forwarders_used'); diff --git a/lib/classes/api/commands/class.Emails.php b/lib/classes/api/commands/class.Emails.php index b72bfbeb..9fe3273e 100644 --- a/lib/classes/api/commands/class.Emails.php +++ b/lib/classes/api/commands/class.Emails.php @@ -85,20 +85,7 @@ class Emails extends ApiCommand implements ResourceEntity } // get needed customer info to reduce the email-address-counter by one - if ($this->isAdmin()) { - // get customer id - $customer_id = $this->getParam('customerid'); - $customer = $this->apiCall('Customers.get', array( - 'id' => $customer_id - )); - // check whether the customer has enough resources to get the mail-address added - if ($customer['emails_used'] >= $customer['emails'] && $customer['emails'] != '-1') { - throw new Exception("Customer has no more resources available", 406); - } - } else { - $customer_id = $this->getUserDetail('customerid'); - $customer = $this->getUserData(); - } + $customer = $this->getCustomerData('emails'); // duplicate check $stmt = Database::prepare(" @@ -138,7 +125,7 @@ class Emails extends ApiCommand implements ResourceEntity $address_id = Database::lastInsertId(); // update customer usage - Customers::increaseUsage($customer_id, 'emails_used'); + Customers::increaseUsage($customer['customerid'], 'emails_used'); // update admin usage Admins::increaseUsage($customer['adminid'], 'emails_used'); @@ -236,16 +223,7 @@ class Emails extends ApiCommand implements ResourceEntity $iscatchall = $this->getParam('iscatchall', true, $result['iscatchall']); // get needed customer info to reduce the email-address-counter by one - if ($this->isAdmin()) { - // get customer id - $customer_id = $this->getParam('customerid'); - $customer = $this->apiCall('Customers.get', array( - 'id' => $customer_id - )); - } else { - $customer_id = $this->getUserDetail('customerid'); - $customer = $this->getUserData(); - } + $customer = $this->getCustomerData(); // check for catchall-flag if ($iscatchall) { @@ -348,16 +326,7 @@ class Emails extends ApiCommand implements ResourceEntity $delete_userfiles = $this->getParam('delete_userfiles', true, 0); // get needed customer info to reduce the email-address-counter by one - if ($this->isAdmin()) { - // get customer id - $customer_id = $this->getParam('customerid'); - $customer = $this->apiCall('Customers.get', array( - 'id' => $customer_id - )); - } else { - $customer_id = $this->getUserDetail('customerid'); - $customer = $this->getUserData(); - } + $customer = $this->getCustomerData(); // check for forwarders $number_forwarders = 0; @@ -373,7 +342,7 @@ class Emails extends ApiCommand implements ResourceEntity if (Settings::Get('system.mail_quota_enabled') == 1) { $stmt = Database::prepare("SELECT `quota` FROM `" . TABLE_MAIL_USERS . "` WHERE `customerid`= :customerid AND `id`= :id"); $res_quota = Database::pexecute_first($stmt, array( - "customerid" => $customer_id, + "customerid" => $customer['customerid'], "id" => $result['popaccountid'] ), true, true); Customers::decreaseUsage($customer['customerid'], 'email_quota_used', '', $res_quota['quota']); @@ -383,7 +352,7 @@ class Emails extends ApiCommand implements ResourceEntity // delete account $stmt = Database::prepare("DELETE FROM `" . TABLE_MAIL_USERS . "` WHERE `customerid`= :customerid AND `id`= :id"); Database::pexecute($stmt, array( - "customerid" => $customer_id, + "customerid" => $customer['customerid'], "id" => $result['popaccountid'] ), true, true); Customers::decreaseUsage($customer['customerid'], 'email_accounts_used'); @@ -398,7 +367,7 @@ class Emails extends ApiCommand implements ResourceEntity // delete address $stmt = Database::prepare("DELETE FROM `" . TABLE_MAIL_VIRTUAL . "` WHERE `customerid`= :customerid AND `id`= :id"); Database::pexecute($stmt, array( - "customerid" => $customer_id, + "customerid" => $customer['customerid'], "id" => $id ), true, true); Customers::decreaseUsage($customer['customerid'], 'emails_used'); diff --git a/lib/classes/api/commands/class.Ftps.php b/lib/classes/api/commands/class.Ftps.php index 56ef099b..09c4f193 100644 --- a/lib/classes/api/commands/class.Ftps.php +++ b/lib/classes/api/commands/class.Ftps.php @@ -83,20 +83,7 @@ class Ftps extends ApiCommand implements ResourceEntity $params = array(); // get needed customer info to reduce the ftp-user-counter by one - if ($this->isAdmin()) { - // get customer id - $customer_id = $this->getParam('customer_id'); - $customer = $this->apiCall('Customers.get', array( - 'id' => $customer_id - )); - // check whether the customer has enough resources to get the ftp-user added - if ($customer['ftps_used'] >= $customer['ftps'] && $customer['ftps'] != '-1') { - throw new Exception("Customer has no more resources available", 406); - } - } else { - $customer_id = $this->getUserDetail('customerid'); - $customer = $this->getUserData(); - } + $customer = $this->getCustomerData('ftps'); if ($sendinfomail != 1) { $sendinfomail = 0; @@ -114,7 +101,7 @@ class Ftps extends ApiCommand implements ResourceEntity AND `customerid` = :customerid"); $ftpdomain_check = Database::pexecute_first($ftpdomain_check_stmt, array( "domain" => $ftpdomain, - "customerid" => $customer_id + "customerid" => $customer['customerid'] ), true, true); if ($ftpdomain_check && $ftpdomain_check['domain'] != $ftpdomain) { @@ -144,7 +131,7 @@ class Ftps extends ApiCommand implements ResourceEntity (`customerid`, `username`, `description`, `password`, `homedir`, `login_enabled`, `uid`, `gid`, `shell`) VALUES (:customerid, :username, :description, :password, :homedir, 'y', :guid, :guid, :shell)"); $params = array( - "customerid" => $customer_id, + "customerid" => $customer['customerid'], "username" => $username, "description" => $description, "password" => $cryptPassword, @@ -179,14 +166,14 @@ class Ftps extends ApiCommand implements ResourceEntity "); $params = array( "username" => $username, - "customerid" => $customer_id, + "customerid" => $customer['customerid'], "guid" => $customer['guid'] ); Database::pexecute($stmt, $params, true, true); // update customer usage - Customers::increaseUsage($customer_id, 'ftps_used'); - Customers::increaseUsage($customer_id, 'ftp_lastaccountnumber'); + Customers::increaseUsage($customer['customerid'], 'ftps_used'); + Customers::increaseUsage($customer['customerid'], 'ftp_lastaccountnumber'); // update admin usage Admins::increaseUsage($customer['adminid'], 'ftps_used'); @@ -360,16 +347,7 @@ class Ftps extends ApiCommand implements ResourceEntity } // get needed customer info to reduce the ftp-user-counter by one - if ($this->isAdmin()) { - // get customer id - $customer_id = $this->getParam('customer_id'); - $customer = $this->apiCall('Customers.get', array( - 'id' => $customer_id - )); - } else { - $customer_id = $this->getUserDetail('customerid'); - $customer = $this->getUserData(); - } + $customer = $this->getCustomerData(); // password update? if ($password != '') { diff --git a/lib/classes/api/commands/class.Mysqls.php b/lib/classes/api/commands/class.Mysqls.php index dfb1cd55..2954660b 100644 --- a/lib/classes/api/commands/class.Mysqls.php +++ b/lib/classes/api/commands/class.Mysqls.php @@ -68,19 +68,7 @@ class Mysqls extends ApiCommand implements ResourceEntity } // get needed customer info to reduce the mysql-usage-counter by one - if ($this->isAdmin()) { - // get customer id - $customer_id = $this->getParam('customer_id'); - $customer = $this->apiCall('Customers.get', array( - 'id' => $customer_id - )); - // check whether the customer has enough resources to get the database added - if ($customer['mysqls_used'] >= $customer['mysqls'] && $customer['mysqls'] != '-1') { - throw new Exception("Customer has no more resources available", 406); - } - } else { - $customer_id = $this->getUserDetail('customerid'); - } + $customer = $this->getCustomerData('mysqls'); $newdb_params = array( 'loginname' => ($this->isAdmin() ? $customer['loginname'] : $this->getUserDetail('loginname')), @@ -105,7 +93,7 @@ class Mysqls extends ApiCommand implements ResourceEntity `dbserver` = :dbserver "); $params = array( - "customerid" => ($this->isAdmin() ? $customer['customerid'] : $this->getUserDetail('customerid')), + "customerid" => $customer['customerid'], "databasename" => $username, "description" => $databasedescription, "dbserver" => $dbserver @@ -115,8 +103,8 @@ class Mysqls extends ApiCommand implements ResourceEntity $params['id'] = $databaseid; // update customer usage - Customers::increaseUsage(($this->isAdmin() ? $customer['customerid'] : $this->getUserDetail('customerid')), 'mysqls_used'); - Customers::increaseUsage(($this->isAdmin() ? $customer['customerid'] : $this->getUserDetail('customerid')), 'mysql_lastaccountnumber'); + Customers::increaseUsage($customer['customerid'], 'mysqls_used'); + Customers::increaseUsage($customer['customerid'], 'mysql_lastaccountnumber'); // update admin usage Admins::increaseUsage($this->getUserDetail('adminid'), 'mysqls_used'); @@ -132,7 +120,7 @@ class Mysqls extends ApiCommand implements ResourceEntity Database::needSqlData(); $sql_root = Database::getSqlData(); Database::needRoot(false); - $userinfo = ($this->isAdmin() ? $customer : $this->getUserData()); + $userinfo = $customer; $replace_arr = array( 'SALUTATION' => getCorrectUserSalutation($userinfo), @@ -351,19 +339,7 @@ class Mysqls extends ApiCommand implements ResourceEntity } // get needed customer info to reduce the mysql-usage-counter by one - if ($this->isAdmin()) { - // get customer id - $customer_id = $this->getParam('customer_id'); - $customer = $this->apiCall('Customers.get', array( - 'id' => $customer_id - )); - // check whether the customer has enough resources to get the database added - if ($customer['mysqls_used'] >= $customer['mysqls'] && $customer['mysqls'] != '-1') { - throw new Exception("Customer has no more resources available", 406); - } - } else { - $customer_id = $this->getUserDetail('customerid'); - } + $customer = $this->getCustomerData(); if ($password != '') { // validate password @@ -398,7 +374,7 @@ class Mysqls extends ApiCommand implements ResourceEntity "); $params = array( "desc" => $databasedescription, - "customerid" => ($this->isAdmin() ? $customer['customerid'] : $this->getUserDetail('customerid')), + "customerid" => $customer['customerid'], "id" => $id ); Database::pexecute($stmt, $params, true, true); @@ -519,19 +495,12 @@ class Mysqls extends ApiCommand implements ResourceEntity ), true, true); // get needed customer info to reduce the mysql-usage-counter by one - if ($this->isAdmin()) { - $customer = $this->apiCall('Customers.get', array( - 'id' => $result['customerid'] - )); - $mysql_used = $customer['mysqls_used']; - $customer_id = $customer['customer_id']; - } else { - $mysql_used = $this->getUserDetail('mysqls_used'); - $customer_id = $this->getUserDetail('customerid'); - } + $customer = $this->getCustomerData(); + $mysql_used = $customer['mysqls_used']; + // reduce mysql-usage-counter $resetaccnumber = ($mysql_used == '1') ? " , `mysql_lastaccountnumber` = '0' " : ''; - Customers::decreaseUsage($customer_id, 'mysqls_used', $resetaccnumber); + Customers::decreaseUsage($customer['customerid'], 'mysqls_used', $resetaccnumber); // update admin usage Admins::decreaseUsage(($this->isAdmin() ? $customer['adminid'] : $this->getUserDetail('adminid')), 'mysqls_used'); diff --git a/lib/classes/api/commands/class.SubDomains.php b/lib/classes/api/commands/class.SubDomains.php index ba257882..306d7ae9 100644 --- a/lib/classes/api/commands/class.SubDomains.php +++ b/lib/classes/api/commands/class.SubDomains.php @@ -78,20 +78,7 @@ class SubDomains extends ApiCommand implements ResourceEntity } // get needed customer info to reduce the subdomain-usage-counter by one - if ($this->isAdmin()) { - // get customer id - $customer_id = $this->getParam('customer_id'); - $customer = $this->apiCall('Customers.get', array( - 'id' => $customer_id - )); - // check whether the customer has enough resources to get the subdomain added - if ($customer['subdomains_used'] >= $customer['subdomains'] && $customer['subdomains'] != '-1') { - throw new Exception("Customer has no more resources available", 406); - } - } else { - $customer_id = $this->getUserDetail('customerid'); - $customer = $this->getUserData(); - } + $customer = $this->getCustomerData('subdomains'); // validation if (substr($subdomain, 0, 4) == 'xn--') { @@ -127,7 +114,7 @@ class SubDomains extends ApiCommand implements ResourceEntity "); $completedomain_check = Database::pexecute_first($completedomain_stmt, array( "domain" => $completedomain, - "customerid" => $customer_id + "customerid" => $customer['customerid'] ), true, true); if ($completedomain_check) { @@ -153,7 +140,7 @@ class SubDomains extends ApiCommand implements ResourceEntity "); $aliasdomain_check = Database::pexecute_first($aliasdomain_stmt, array( "id" => $aliasdomain, - "customerid" => $customer_id + "customerid" => $customer['customerid'] ), true, true); if ($aliasdomain_check['id'] != $aliasdomain) { standard_error('domainisaliasorothercustomer', '', true); @@ -461,20 +448,7 @@ class SubDomains extends ApiCommand implements ResourceEntity } // get needed customer info to reduce the subdomain-usage-counter by one - if ($this->isAdmin()) { - // get customer id - $customer_id = $this->getParam('customer_id'); - $customer = $this->apiCall('Customers.get', array( - 'id' => $customer_id - )); - // check whether the customer has enough resources to get the subdomain added - if ($customer['subdomains_used'] >= $customer['subdomains'] && $customer['subdomains'] != '-1') { - throw new Exception("Customer has no more resources available", 406); - } - } else { - $customer_id = $this->getUserDetail('customerid'); - $customer = $this->getUserData(); - } + $customer = $this->getCustomerData(); $alias_stmt = Database::prepare("SELECT COUNT(`id`) AS count FROM `" . TABLE_PANEL_DOMAINS . "` WHERE `aliasdomain`= :aliasdomain"); $alias_check = Database::pexecute_first($alias_stmt, array( @@ -494,7 +468,7 @@ class SubDomains extends ApiCommand implements ResourceEntity "); $aliasdomain_check = Database::pexecute_first($aliasdomain_stmt, array( "id" => $aliasdomain, - "customerid" => $customer_id + "customerid" => $customer['customerid'] ), true, true); if ($aliasdomain_check['id'] != $aliasdomain) { standard_error('domainisaliasorothercustomer', '', true); @@ -743,20 +717,13 @@ class SubDomains extends ApiCommand implements ResourceEntity $id = $result['id']; // get needed customer info to reduce the subdomain-usage-counter by one - if ($this->isAdmin()) { - $customer = $this->apiCall('Customers.get', array( - 'id' => $result['customerid'] - )); - $subdomains_used = $customer['subdomains_used']; - $customer_id = $customer['customer_id']; - } else { - if ($result['caneditdomain'] == 0) { - throw new Exception("You cannot edit this resource", 405); - } - $subdomains_used = $this->getUserDetail('subdomains_used'); - $customer_id = $this->getUserDetail('customerid'); + $customer = $this->getCustomerData(); + $subdomains_used = $customer['subdomains_used']; + + if (!$this->isAdmin() && $result['caneditdomain'] == 0) { + throw new Exception("You cannot edit this resource", 405); } - + if ($result['isemaildomain'] == '1') { // check for e-mail addresses $emails_stmt = Database::prepare(" @@ -764,7 +731,7 @@ class SubDomains extends ApiCommand implements ResourceEntity WHERE `customerid` = :customerid AND `domainid` = :domainid "); $emails = Database::pexecute_first($emails_stmt, array( - "customerid" => $customer_id, + "customerid" => $customer['customerid'], "domainid" => $id ), true, true); @@ -780,7 +747,7 @@ class SubDomains extends ApiCommand implements ResourceEntity DELETE FROM `" . TABLE_PANEL_DOMAINS . "` WHERE `customerid` = :customerid AND `id` = :id "); Database::pexecute($stmt, array( - "customerid" => $customer_id, + "customerid" => $customer['customerid'], "id" => $id ), true, true); @@ -825,7 +792,7 @@ class SubDomains extends ApiCommand implements ResourceEntity inserttask('4'); // reduce subdomain-usage-counter - Customers::decreaseUsage($customer_id, 'subdomains_used'); + Customers::decreaseUsage($customer['customerid'], 'subdomains_used'); // update admin usage Admins::decreaseUsage(($this->isAdmin() ? $customer['adminid'] : $this->getUserDetail('adminid')), 'subdomains_used'); From ff611fa8dc6c647e40c05ff2987533a766cb79f3 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 12 Mar 2018 10:57:31 +0100 Subject: [PATCH 0556/1335] fix parameter for ApiCommand::getCustomerData() Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/commands/class.Ftps.php | 2 +- lib/classes/api/commands/class.SubDomains.php | 2 +- tests/Admins/AdminsTest.php | 1 + tests/Certificates/CertificatesTest.php | 1 + tests/Customers/CustomersTest.php | 1 + tests/Domains/DomainsTest.php | 1 + tests/Emails/EmailsTest.php | 1 + tests/Ftps/FtpsTest.php | 9 +++++---- tests/IpsAndPorts/IpsAndPortsTest.php | 1 + tests/SubDomains/SubDomainsTest.php | 5 +++-- 10 files changed, 16 insertions(+), 8 deletions(-) diff --git a/lib/classes/api/commands/class.Ftps.php b/lib/classes/api/commands/class.Ftps.php index 09c4f193..5fde2ed3 100644 --- a/lib/classes/api/commands/class.Ftps.php +++ b/lib/classes/api/commands/class.Ftps.php @@ -35,7 +35,7 @@ class Ftps extends ApiCommand implements ResourceEntity * optional if customer.ftpatdomain is allowed, specify an username * @param string $ftp_domain * optional if customer.ftpatdomain is allowed, specify a domain (customer must be owner) - * @param int $customer_id + * @param int $customerid * required when called as admin, not needed when called as customer * * @access admin, customer diff --git a/lib/classes/api/commands/class.SubDomains.php b/lib/classes/api/commands/class.SubDomains.php index 306d7ae9..c122a607 100644 --- a/lib/classes/api/commands/class.SubDomains.php +++ b/lib/classes/api/commands/class.SubDomains.php @@ -41,7 +41,7 @@ class SubDomains extends ApiCommand implements ResourceEntity * optional, whether to generate a https-redirect or not, default false; requires SSL to be enabled * @param bool $letsencrypt * optional, whether to generate a Let's Encrypt certificate for this domain, default false; requires SSL to be enabled - * @param int $customer_id + * @param int $customerid * required when called as admin, not needed when called as customer * * @access admin, customer diff --git a/tests/Admins/AdminsTest.php b/tests/Admins/AdminsTest.php index c6a63308..79b110a2 100644 --- a/tests/Admins/AdminsTest.php +++ b/tests/Admins/AdminsTest.php @@ -3,6 +3,7 @@ use PHPUnit\Framework\TestCase; /** * @covers ApiCommand + * @covers ApiParameter * @covers Admins */ class AdminsTest extends TestCase diff --git a/tests/Certificates/CertificatesTest.php b/tests/Certificates/CertificatesTest.php index 85a307a4..b3924916 100644 --- a/tests/Certificates/CertificatesTest.php +++ b/tests/Certificates/CertificatesTest.php @@ -3,6 +3,7 @@ use PHPUnit\Framework\TestCase; /** * @covers ApiCommand + * @covers ApiParameter * @covers Certificates */ class CertificatesTest extends TestCase diff --git a/tests/Customers/CustomersTest.php b/tests/Customers/CustomersTest.php index 3d7592c6..a127178e 100644 --- a/tests/Customers/CustomersTest.php +++ b/tests/Customers/CustomersTest.php @@ -3,6 +3,7 @@ use PHPUnit\Framework\TestCase; /** * @covers ApiCommand + * @covers ApiParameter * @covers Customers */ class CustomersTest extends TestCase diff --git a/tests/Domains/DomainsTest.php b/tests/Domains/DomainsTest.php index 964b29e4..efc41901 100644 --- a/tests/Domains/DomainsTest.php +++ b/tests/Domains/DomainsTest.php @@ -3,6 +3,7 @@ use PHPUnit\Framework\TestCase; /** * @covers ApiCommand + * @covers ApiParameter * @covers SubDomains * @covers Domains */ diff --git a/tests/Emails/EmailsTest.php b/tests/Emails/EmailsTest.php index 4ba60987..77f71c12 100644 --- a/tests/Emails/EmailsTest.php +++ b/tests/Emails/EmailsTest.php @@ -3,6 +3,7 @@ use PHPUnit\Framework\TestCase; /** * @covers ApiCommand + * @covers ApiParameter * @covers Emails */ class MailsTest extends TestCase diff --git a/tests/Ftps/FtpsTest.php b/tests/Ftps/FtpsTest.php index f4dc2ed2..38389a44 100644 --- a/tests/Ftps/FtpsTest.php +++ b/tests/Ftps/FtpsTest.php @@ -3,6 +3,7 @@ use PHPUnit\Framework\TestCase; /** * @covers ApiCommand + * @covers ApiParameter * @covers Ftps */ class FtpsTest extends TestCase @@ -170,8 +171,8 @@ class FtpsTest extends TestCase 'sendinfomail' => 1 ]; - $this->expectExceptionCode(404); - $this->expectExceptionMessage('Requested parameter "customer_id" could not be found for "Ftps:add"'); + $this->expectExceptionCode(406); + $this->expectExceptionMessage('Requested parameter "loginname" is empty where it should not be for "Customers:get"'); $json_result = Ftps::getLocal($admin_userdata, $data)->add(); } @@ -207,7 +208,7 @@ class FtpsTest extends TestCase $customer_userdata = json_decode($json_result, true)['data']; $data = [ 'username' => 'test1ftp1', - 'customer_id' => 1, + 'customerid' => 1, 'ftp_password' => 'h4xXx0r2', 'path' => '/anotherfolder', 'ftp_description' => 'testing3' @@ -229,7 +230,7 @@ class FtpsTest extends TestCase $customer_userdata = json_decode($json_result, true)['data']; $data = [ - 'customer_id' => $customer_userdata['customerid'], + 'customerid' => $customer_userdata['customerid'], 'ftp_password' => 'h4xXx0r', 'path' => '/', 'ftp_description' => 'testing', diff --git a/tests/IpsAndPorts/IpsAndPortsTest.php b/tests/IpsAndPorts/IpsAndPortsTest.php index 94d32f27..951b22a1 100644 --- a/tests/IpsAndPorts/IpsAndPortsTest.php +++ b/tests/IpsAndPorts/IpsAndPortsTest.php @@ -3,6 +3,7 @@ use PHPUnit\Framework\TestCase; /** * @covers ApiCommand + * @covers ApiParameter * @covers IpsAndPorts */ class IpsAndPortsTest extends TestCase diff --git a/tests/SubDomains/SubDomainsTest.php b/tests/SubDomains/SubDomainsTest.php index f930ca5e..d1aa4af0 100644 --- a/tests/SubDomains/SubDomainsTest.php +++ b/tests/SubDomains/SubDomainsTest.php @@ -3,6 +3,7 @@ use PHPUnit\Framework\TestCase; /** * @covers ApiCommand + * @covers ApiParameter * @covers SubDomains * @covers Domains */ @@ -41,7 +42,7 @@ class SubDomainsTest extends TestCase $data = [ 'subdomain' => 'mysub2', 'domain' => 'test2.local', - 'customer_id' => 1 + 'customerid' => 1 ]; $json_result = SubDomains::getLocal($reseller_userdata, $data)->add(); $result = json_decode($json_result, true)['data']; @@ -161,7 +162,7 @@ class SubDomainsTest extends TestCase 'domainname' => 'mysub.test2.local', 'path' => 'mysub.test2.local', 'isemaildomain' => 1, - 'customer_id' => $customer_userdata['customerid'] + 'customerid' => $customer_userdata['customerid'] ]; $json_result = SubDomains::getLocal($admin_userdata, $data)->update(); $result = json_decode($json_result, true)['data']; From 2e597ef7d944ae8633253fdf5a890c7e8555fa3a Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 12 Mar 2018 11:09:30 +0100 Subject: [PATCH 0557/1335] remove unused local variables Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/commands/class.EmailForwarders.php | 13 ++----------- lib/classes/api/commands/class.Emails.php | 1 - lib/classes/api/commands/class.SubDomains.php | 1 - 3 files changed, 2 insertions(+), 13 deletions(-) diff --git a/lib/classes/api/commands/class.EmailForwarders.php b/lib/classes/api/commands/class.EmailForwarders.php index ee95f349..ad325e92 100644 --- a/lib/classes/api/commands/class.EmailForwarders.php +++ b/lib/classes/api/commands/class.EmailForwarders.php @@ -158,9 +158,7 @@ class EmailForwarders extends ApiCommand implements ResourceEntity // get needed customer info to reduce the email-forwarder-counter by one $customer = $this->getCustomerData(); - - // get specific forwarder - $forwarder = $result['destination'][$forwarderid]; + // unset it from array unset($result['destination'][$forwarderid]); // rebuild destination-string @@ -176,14 +174,7 @@ class EmailForwarders extends ApiCommand implements ResourceEntity "id" => $id ); Database::pexecute($stmt, $params, true, true); - - $stmt = Database::prepare("UPDATE `" . TABLE_PANEL_CUSTOMERS . "` - SET `email_forwarders_used` = `email_forwarders_used` - 1 - WHERE `customerid`= :cid"); - Database::pexecute($stmt, array( - "cid" => $userinfo['customerid'] - )); - + // update customer usage Customers::decreaseUsage($customer['customerid'], 'email_forwarders_used'); diff --git a/lib/classes/api/commands/class.Emails.php b/lib/classes/api/commands/class.Emails.php index 9fe3273e..759cc3b4 100644 --- a/lib/classes/api/commands/class.Emails.php +++ b/lib/classes/api/commands/class.Emails.php @@ -122,7 +122,6 @@ class Emails extends ApiCommand implements ResourceEntity "domainid" => $domain_check['id'] ); Database::pexecute($stmt, $params, true, true); - $address_id = Database::lastInsertId(); // update customer usage Customers::increaseUsage($customer['customerid'], 'emails_used'); diff --git a/lib/classes/api/commands/class.SubDomains.php b/lib/classes/api/commands/class.SubDomains.php index c122a607..9159587f 100644 --- a/lib/classes/api/commands/class.SubDomains.php +++ b/lib/classes/api/commands/class.SubDomains.php @@ -718,7 +718,6 @@ class SubDomains extends ApiCommand implements ResourceEntity // get needed customer info to reduce the subdomain-usage-counter by one $customer = $this->getCustomerData(); - $subdomains_used = $customer['subdomains_used']; if (!$this->isAdmin() && $result['caneditdomain'] == 0) { throw new Exception("You cannot edit this resource", 405); From 6fc8cce8f5cf1156fe27b1d49f228c80cbd42a85 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 12 Mar 2018 13:44:44 +0100 Subject: [PATCH 0558/1335] add EmailAccounts.add; added wrapper function ApiCommand.getMailTemplate() to reduce code-duplication Signed-off-by: Michael Kaufmann (d00p) --- customer_email.php | 210 ++----------------- lib/classes/api/abstract.ApiCommand.php | 28 +++ lib/classes/api/commands/class.Customers.php | 44 ++-- lib/classes/api/commands/class.Emails.php | 7 +- lib/classes/api/commands/class.Ftps.php | 35 +--- lib/classes/api/commands/class.Mysqls.php | 34 +-- 6 files changed, 74 insertions(+), 284 deletions(-) diff --git a/customer_email.php b/customer_email.php index 8be1013c..cf2d043c 100644 --- a/customer_email.php +++ b/customer_email.php @@ -269,210 +269,24 @@ if ($page == 'overview') { } } elseif ($page == 'accounts') { if ($action == 'add' && $id != 0) { - // ensure the int is a positive one - if (isset($_POST['email_quota'])) { - $quota = validate($_POST['email_quota'], 'email_quota', '/^\d+$/', 'vmailquotawrong'); - } - if ($userinfo['email_accounts'] == '-1' || ($userinfo['email_accounts_used'] < $userinfo['email_accounts'])) { - - // check for imap||pop3 == 1, see #1298 - if ($userinfo['imap'] != '1' && $userinfo['pop3'] != '1') { - standard_error('notallowedtouseaccounts'); + try { + $json_result = Emails::getLocal($userinfo, array( + 'id' => $id + ))->get(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); } - - $stmt = Database::prepare(" - SELECT `id`, `email`, `email_full`, `iscatchall`, `destination`, `customerid`, `popaccountid`, `domainid` - FROM `" . TABLE_MAIL_VIRTUAL . "` - WHERE `customerid`= :cid AND `id`= :id - "); - $result = Database::pexecute_first($stmt, array("cid" => $userinfo['customerid'], "id" => $id)); + $result = json_decode($json_result, true)['data']; if (isset($result['email']) && $result['email'] != '' && $result['popaccountid'] == '0') { if (isset($_POST['send']) && $_POST['send'] == 'send') { - $email_full = $result['email_full']; - $username = $idna_convert->decode($email_full); - $password = validate($_POST['email_password'], 'password'); - $password = validatePassword($password); - - if (Settings::Get('panel.sendalternativemail') == 1) { - $alternative_email = $idna_convert->encode(validate($_POST['alternative_email'], 'alternative_email')); - } else { - $alternative_email = ''; - } - - if (Settings::Get('system.mail_quota_enabled') == 1) { - if ($userinfo['email_quota'] != '-1' && ($quota == 0 || ($quota + $userinfo['email_quota_used']) > $userinfo['email_quota'])) { - standard_error('allocatetoomuchquota', $quota); - } - } else { - $quota = 0; - } - - if ($email_full == '') { - standard_error(array('stringisempty', 'emailadd')); - } - elseif ($password == '' && !(Settings::Get('panel.sendalternativemail') == 1 && validateEmail($alternative_email))) { - standard_error(array('stringisempty', 'mypassword')); - } - elseif ($password == $email_full) { - standard_error('passwordshouldnotbeusername'); - } else { - if ($password == '') { - $password = generatePassword(); - } - - $cryptPassword = makeCryptPassword($password); - - $email_user=substr($email_full,0,strrpos($email_full,"@")); - $email_domain=substr($email_full,strrpos($email_full,"@")+1); - $maildirname=trim(Settings::Get('system.vmail_maildirname')); - // Add trailing slash to Maildir if needed - $maildirpath=$maildirname; - if (!empty($maildirname) && substr($maildirname,-1) != "/") { - $maildirpath.="/"; - } - - $stmt = Database::prepare("INSERT INTO `" . TABLE_MAIL_USERS . "` - (`customerid`, `email`, `username`, " . (Settings::Get('system.mailpwcleartext') == '1' ? '`password`, ' : '') . " `password_enc`, `homedir`, `maildir`, `uid`, `gid`, `domainid`, `postfix`, `quota`, `imap`, `pop3`) ". - "VALUES (:cid, :email, :username, " . (Settings::Get('system.mailpwcleartext') == '1' ? ":password, " : '') . ":password_enc, :homedir, :maildir, :uid, :gid, :domainid, 'y', :quota, :imap, :pop3)" - ); - $params = array( - "cid" => $userinfo['customerid'], - "email" => $email_full, - "username" => $username, - "password_enc" => $cryptPassword, - "homedir" => Settings::Get('system.vmail_homedir'), - "maildir" => $userinfo['loginname'] . '/' . $email_domain . "/" . $email_user . "/" . $maildirpath, - "uid" => Settings::Get('system.vmail_uid'), - "gid" => Settings::Get('system.vmail_gid'), - "domainid" => $result['domainid'], - "quota" => $quota, - "imap" => $userinfo['imap'], - "pop3" => $userinfo['pop3'] - ); - if (Settings::Get('system.mailpwcleartext') == '1') { $params["password"] = $password; } - Database::pexecute($stmt, $params); - - $popaccountid = Database::lastInsertId(); - $result['destination'].= ' ' . $email_full; - $stmt = Database::prepare("UPDATE `" . TABLE_MAIL_VIRTUAL . "` - SET `destination` = :destination, - `popaccountid` = :popaccountid - WHERE `customerid`= :cid - AND `id`= :id" - ); - $params = array( - "destination" => makeCorrectDestination($result['destination']), - "popaccountid" => $popaccountid, - "cid" => $userinfo['customerid'], - "id" => $id - ); - Database::pexecute($stmt, $params); - - $stmt = Database::prepare("UPDATE `" . TABLE_PANEL_CUSTOMERS . "` - SET `email_accounts_used`=`email_accounts_used`+1, - `email_quota_used`=`email_quota_used`+ :quota - WHERE `customerid`= :cid" - ); - Database::pexecute($stmt, array("quota" => $quota, "cid" => $userinfo['customerid'])); - - $log->logAction(USR_ACTION, LOG_INFO, "added email account for '" . $email_full . "'"); - $replace_arr = array( - 'EMAIL' => $email_full, - 'USERNAME' => $username, - 'PASSWORD' => $password - ); - - $stmt = Database::prepare("SELECT `name`, `email` FROM `" . TABLE_PANEL_ADMINS . "` WHERE `adminid`= :adminid"); - $admin = Database::pexecute_first($stmt, array("adminid" => $userinfo['adminid'])); - - $stmt = Database::prepare("SELECT `value` FROM `" . TABLE_PANEL_TEMPLATES . "` - WHERE `adminid`= :adminid - AND `language`= :lang - AND `templategroup`= 'mails' - AND `varname`= 'pop_success_subject'" - ); - $result = Database::pexecute_first($stmt, array("adminid" => $userinfo['adminid'], "lang" => $userinfo['def_language'])); - $mail_subject = html_entity_decode(replace_variables((($result['value'] != '') ? $result['value'] : $lng['mails']['pop_success']['subject']), $replace_arr)); - - $stmt = Database::prepare("SELECT `value` FROM `" . TABLE_PANEL_TEMPLATES . "` - WHERE `adminid`= :adminid - AND `language`= :lang - AND `templategroup`= 'mails' - AND `varname`= 'pop_success_mailbody'" - ); - $result = Database::pexecute_first($stmt, array("adminid" => $userinfo['adminid'], "lang" => $userinfo['def_language'])); - $mail_body = html_entity_decode(replace_variables((($result['value'] != '') ? $result['value'] : $lng['mails']['pop_success']['mailbody']), $replace_arr)); - - $_mailerror = false; - try { - $mail->SetFrom($admin['email'], getCorrectUserSalutation($admin)); - $mail->Subject = $mail_subject; - $mail->AltBody = $mail_body; - $mail->MsgHTML(str_replace("\n", "
    ", $mail_body)); - $mail->AddAddress($email_full); - $mail->Send(); - } catch(phpmailerException $e) { - $mailerr_msg = $e->errorMessage(); - $_mailerror = true; - } catch (Exception $e) { - $mailerr_msg = $e->getMessage(); - $_mailerror = true; - } - - if ($_mailerror) { - $log->logAction(USR_ACTION, LOG_ERR, "Error sending mail: " . $mailerr_msg); - standard_error('errorsendingmail', $email_full); - } - - $mail->ClearAddresses(); - - if (validateEmail($alternative_email) && Settings::Get('panel.sendalternativemail') == 1) { - $stmt = Database::prepare("SELECT `value` FROM `" . TABLE_PANEL_TEMPLATES . "` - WHERE `adminid`= :adminid - AND `language`= :lang - AND `templategroup`= 'mails' - AND `varname`= 'pop_success_alternative_subject'" - ); - $result = Database::pexecute_first($stmt, array("adminid" => $userinfo['adminid'], "lang" => $userinfo['def_language'])); - $mail_subject = replace_variables((($result['value'] != '') ? $result['value'] : $lng['mails']['pop_success_alternative']['subject']), $replace_arr); - - $stmt = Database::prepare("SELECT `value` FROM `" . TABLE_PANEL_TEMPLATES . "` - WHERE `adminid`= :adminid - AND `language`= :lang - AND `templategroup`= 'mails' - AND `varname`= 'pop_success_alternative_mailbody'" - ); - $result = Database::pexecute_first($stmt, array("adminid" => $userinfo['adminid'], "lang" => $userinfo['def_language'])); - $mail_body = replace_variables((($result['value'] != '') ? $result['value'] : $lng['mails']['pop_success_alternative']['mailbody']), $replace_arr); - - $_mailerror = false; - try { - $mail->SetFrom($admin['email'], getCorrectUserSalutation($admin)); - $mail->Subject = $mail_subject; - $mail->AltBody = $mail_body; - $mail->MsgHTML(str_replace("\n", "
    ", $mail_body)); - $mail->AddAddress($idna_convert->encode($alternative_email), getCorrectUserSalutation($userinfo)); - $mail->Send(); - } catch(phpmailerException $e) { - $mailerr_msg = $e->errorMessage(); - $_mailerror = true; - } catch (Exception $e) { - $mailerr_msg = $e->getMessage(); - $_mailerror = true; - } - - if ($_mailerror) { - $log->logAction(USR_ACTION, LOG_ERR, "Error sending mail: " . $mailerr_msg); - standard_error(array('errorsendingmail'), $alternative_email); - } - - $mail->ClearAddresses(); - } - - redirectTo($filename, array('page' => 'emails', 'action' => 'edit', 'id' => $id, 's' => $s)); + try { + EmailAccounts::getLocal($userinfo, $_POST)->add(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); } + redirectTo($filename, array('page' => 'emails', 'action' => 'edit', 'id' => $id, 's' => $s)); } else { if (checkMailAccDeletionState($result['email_full'])) { diff --git a/lib/classes/api/abstract.ApiCommand.php b/lib/classes/api/abstract.ApiCommand.php index a0c66726..77b3c58d 100644 --- a/lib/classes/api/abstract.ApiCommand.php +++ b/lib/classes/api/abstract.ApiCommand.php @@ -425,6 +425,34 @@ abstract class ApiCommand extends ApiParameter ), true, true); } + /** + * return email template content from database or global language file if not found in DB + * + * @param array $customerdata + * @param string $group + * @param string $varname + * @param array $replace_arr + * @param string $default + * + * @return string + */ + protected function getMailTemplate($customerdata = null, $group = null, $varname = null, $replace_arr = array(), $default = "") + { + // get template + $stmt = Database::prepare(" + SELECT `value` FROM `" . TABLE_PANEL_TEMPLATES . "` WHERE `adminid`= :adminid + AND `language`= :lang AND `templategroup`= :group AND `varname`= :var + "); + $result = Database::pexecute_first($stmt, array( + "adminid" => $customerdata['adminid'], + "lang" => $customerdata['def_language'], + "group" => $group, + "var" => $varname + ), true, true); + $content = html_entity_decode(replace_variables((($result['value'] != '') ? $result['value'] : $default), $replace_arr)); + return $content; + } + /** * read user data from database by api-request-header fields * diff --git a/lib/classes/api/commands/class.Customers.php b/lib/classes/api/commands/class.Customers.php index 7291dea3..ae02b9c5 100644 --- a/lib/classes/api/commands/class.Customers.php +++ b/lib/classes/api/commands/class.Customers.php @@ -589,25 +589,17 @@ class Customers extends ApiCommand implements ResourceEntity 'DOMAINNAME' => $_stdsubdomain ); - // Get mail templates from database; the ones from 'admin' are fetched for fallback - $result_stmt = Database::prepare(" - SELECT `value` FROM `" . TABLE_PANEL_TEMPLATES . "` - WHERE `adminid` = :adminid AND `language` = :deflang AND `templategroup` = 'mails' AND `varname` = 'createcustomer_subject'"); - $result = Database::pexecute_first($result_stmt, array( + // get template for mail subject + $mail_subject = $this->getMailTemplate(array( 'adminid' => $this->getUserDetail('adminid'), - 'deflang' => $def_language - ), true, true); - $mail_subject = html_entity_decode(replace_variables((($result['value'] != '') ? $result['value'] : $this->lng['mails']['createcustomer']['subject']), $replace_arr)); - - $result_stmt = Database::prepare(" - SELECT `value` FROM `" . TABLE_PANEL_TEMPLATES . "` - WHERE `adminid` = :adminid AND `language` = :deflang AND `templategroup` = 'mails' AND `varname` = 'createcustomer_mailbody'"); - $result = Database::pexecute_first($result_stmt, array( + 'def_language' => $def_language + ), 'mails', 'createcustomer_subject', $replace_arr, $this->lng['mails']['createcustomer']['subject']); + // get template for mail body + $mail_body = $this->getMailTemplate(array( 'adminid' => $this->getUserDetail('adminid'), - 'deflang' => $def_language - ), true, true); - $mail_body = html_entity_decode(replace_variables((($result['value'] != '') ? $result['value'] : $this->lng['mails']['createcustomer']['mailbody']), $replace_arr)); - + 'def_language' => $def_language + ), 'mails', 'createcustomer_mailbody', $replace_arr, $this->lng['mails']['createcustomer']['mailbody']); + $_mailerror = false; try { $this->mailer()->Subject = $mail_subject; @@ -665,7 +657,7 @@ class Customers extends ApiCommand implements ResourceEntity $id = $this->getParam('id', true, 0); $ln_optional = ($id <= 0 ? false : true); $loginname = $this->getParam('loginname', $ln_optional, ''); - + $result = $this->apiCall('Customers.get', array( 'id' => $id, 'loginname' => $loginname @@ -921,7 +913,7 @@ class Customers extends ApiCommand implements ResourceEntity // At last flush the new privileges $dbm->getManager()->flushPrivileges(); Database::needRoot(false); - + // reactivate/deactivate api-keys $valid_until = $deactivated ? 0 : - 1; $stmt = Database::prepare("UPDATE `" . TABLE_API_KEYS . "` SET `valid_until` = :vu WHERE `customerid` = :id"); @@ -929,7 +921,7 @@ class Customers extends ApiCommand implements ResourceEntity 'id' => $id, 'vu' => $valid_until ), true, true); - + $this->logger()->logAction(ADM_ACTION, LOG_INFO, "[API] " . ($deactivated ? 'deactivated' : 'reactivated') . " user '" . $result['loginname'] . "'"); inserttask('1'); } @@ -1164,7 +1156,7 @@ class Customers extends ApiCommand implements ResourceEntity } } } - + $result = $this->apiCall('Customers.get', array( 'id' => $result['customerid'] )); @@ -1192,7 +1184,7 @@ class Customers extends ApiCommand implements ResourceEntity $ln_optional = ($id <= 0 ? false : true); $loginname = $this->getParam('loginname', $ln_optional, ''); $delete_userfiles = $this->getParam('delete_userfiles', true, 0); - + $result = $this->apiCall('Customers.get', array( 'id' => $id, 'loginname' => $loginname @@ -1331,7 +1323,7 @@ class Customers extends ApiCommand implements ResourceEntity Database::pexecute($stmt, array( 'id' => $id ), true, true); - + // Delete all waiting "create user" -tasks for this user, #276 // Note: the WHERE selects part of a serialized array, but it should be safe this way $del_stmt = Database::prepare(" @@ -1438,7 +1430,7 @@ class Customers extends ApiCommand implements ResourceEntity $id = $this->getParam('id', true, 0); $ln_optional = ($id <= 0 ? false : true); $loginname = $this->getParam('loginname', $ln_optional, ''); - + $result = $this->apiCall('Customers.get', array( 'id' => $id, 'loginname' => $loginname @@ -1482,7 +1474,7 @@ class Customers extends ApiCommand implements ResourceEntity $id = $this->getParam('id', true, 0); $ln_optional = ($id <= 0 ? false : true); $loginname = $this->getParam('loginname', $ln_optional, ''); - + $c_result = $this->apiCall('Customers.get', array( 'id' => $id, 'loginname' => $loginname @@ -1530,7 +1522,7 @@ class Customers extends ApiCommand implements ResourceEntity updateCounters(false); $this->logger()->logAction(ADM_ACTION, LOG_INFO, "[API] moved user '" . $c_result['loginname'] . "' from admin/reseller '" . $c_result['adminname'] . " to admin/reseller '" . $a_result['loginname'] . "'"); - + $result = $this->apiCall('Customers.get', array( 'id' => $c_result['customerid'] )); diff --git a/lib/classes/api/commands/class.Emails.php b/lib/classes/api/commands/class.Emails.php index 759cc3b4..d4a36fc6 100644 --- a/lib/classes/api/commands/class.Emails.php +++ b/lib/classes/api/commands/class.Emails.php @@ -332,8 +332,6 @@ class Emails extends ApiCommand implements ResourceEntity if ($result['destination'] != '') { $result['destination'] = explode(' ', $result['destination']); $number_forwarders = count($result['destination']); - Customers::decreaseUsage($customer['customerid'], 'email_forwarders_used', '', $number_forwarders); - Admins::decreaseUsage($customer['customerid'], 'email_forwarders_used', '', $number_forwarders); } // check whether this address is an account if ($result['popaccountid'] != 0) { @@ -357,8 +355,13 @@ class Emails extends ApiCommand implements ResourceEntity Customers::decreaseUsage($customer['customerid'], 'email_accounts_used'); Admins::decreaseUsage($customer['customerid'], 'email_accounts_used'); $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_INFO, "[API] deleted email account '" . $result['email_full'] . "'"); + $number_forwarders --; } + // decrease forwarder counter + Customers::decreaseUsage($customer['customerid'], 'email_forwarders_used', '', $number_forwarders); + Admins::decreaseUsage($customer['customerid'], 'email_forwarders_used', '', $number_forwarders); + if ($delete_userfiles) { inserttask('7', $customer['loginname'], $result['email_full']); } diff --git a/lib/classes/api/commands/class.Ftps.php b/lib/classes/api/commands/class.Ftps.php index 5fde2ed3..30276706 100644 --- a/lib/classes/api/commands/class.Ftps.php +++ b/lib/classes/api/commands/class.Ftps.php @@ -189,36 +189,11 @@ class Ftps extends ApiCommand implements ResourceEntity 'USR_PASS' => $password, 'USR_PATH' => makeCorrectDir(str_replace($customer['documentroot'], "/", $path)) ); - - $def_language = $customer['def_language']; - $result_stmt = Database::prepare(" - SELECT `value` FROM `" . TABLE_PANEL_TEMPLATES . "` - WHERE `adminid` = :adminid - AND `language` = :lang - AND `templategroup`='mails' - AND `varname`='new_ftpaccount_by_customer_subject' - "); - Database::pexecute($result_stmt, array( - "adminid" => $customer['adminid'], - "lang" => $def_language - )); - $result = $result_stmt->fetch(PDO::FETCH_ASSOC); - $mail_subject = html_entity_decode(replace_variables((($result['value'] != '') ? $result['value'] : $this->lng['mails']['new_ftpaccount_by_customer']['subject']), $replace_arr)); - - $def_language = $customer['def_language']; - $result_stmt = Database::prepare(" - SELECT `value` FROM `" . TABLE_PANEL_TEMPLATES . "` - WHERE `adminid` = :adminid - AND `language` = :lang - AND `templategroup`='mails' - AND `varname`='new_ftpaccount_by_customer_mailbody'"); - Database::pexecute($result_stmt, array( - "adminid" => $customer['adminid'], - "lang" => $def_language - )); - $result = $result_stmt->fetch(PDO::FETCH_ASSOC); - $mail_body = html_entity_decode(replace_variables((($result['value'] != '') ? $result['value'] : $this->lng['mails']['new_ftpaccount_by_customer']['mailbody']), $replace_arr)); - + // get template for mail subject + $mail_subject = $this->getMailTemplate($customer, 'mails', 'new_ftpaccount_by_customer_subject', $replace_arr, $this->lng['mails']['new_ftpaccount_by_customer']['subject']); + // get template for mail body + $mail_body = $this->getMailTemplate($customer, 'mails', 'new_ftpaccount_by_customer_mailbody', $replace_arr, $this->lng['mails']['new_ftpaccount_by_customer']['mailbody']); + $_mailerror = false; try { $this->mailer()->Subject = $mail_subject; diff --git a/lib/classes/api/commands/class.Mysqls.php b/lib/classes/api/commands/class.Mysqls.php index 2954660b..a68f57eb 100644 --- a/lib/classes/api/commands/class.Mysqls.php +++ b/lib/classes/api/commands/class.Mysqls.php @@ -131,34 +131,12 @@ class Mysqls extends ApiCommand implements ResourceEntity 'DB_SRV' => $sql_root['host'], 'PMA_URI' => $pma ); - - $def_language = $userinfo['def_language']; - $result_stmt = Database::prepare(" - SELECT `value` FROM `" . TABLE_PANEL_TEMPLATES . "` - WHERE `adminid` = :adminid - AND `language` = :lang - AND `templategroup`='mails' - AND `varname`='new_database_by_customer_subject' - "); - $result = Database::pexecute_first($result_stmt, array( - "adminid" => $userinfo['adminid'], - "lang" => $def_language - ), true, true); - $mail_subject = html_entity_decode(replace_variables((($result['value'] != '') ? $result['value'] : $this->lng['mails']['new_database_by_customer']['subject']), $replace_arr)); - - $result_stmt = Database::prepare(" - SELECT `value` FROM `" . TABLE_PANEL_TEMPLATES . "` - WHERE `adminid`= :adminid - AND `language`= :lang - AND `templategroup` = 'mails' - AND `varname` = 'new_database_by_customer_mailbody' - "); - $result = Database::pexecute_first($result_stmt, array( - "adminid" => $userinfo['adminid'], - "lang" => $def_language - )); - $mail_body = html_entity_decode(replace_variables((($result['value'] != '') ? $result['value'] : $this->lng['mails']['new_database_by_customer']['mailbody']), $replace_arr)); - + + // get template for mail subject + $mail_subject = $this->getMailTemplate($userinfo, 'mails', 'new_database_by_customer_subject', $replace_arr, $this->lng['mails']['new_database_by_customer']['subject']); + // get template for mail body + $mail_body = $this->getMailTemplate($userinfo, 'mails', 'new_database_by_customer_mailbody', $replace_arr, $this->lng['mails']['new_database_by_customer']['mailbody']); + $_mailerror = false; try { $this->mail->Subject = $mail_subject; From fa7bb53d581cc7a3dfbdd3cb6ada6ca8f47029d8 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 12 Mar 2018 14:13:35 +0100 Subject: [PATCH 0559/1335] added EmailAccounts-ApiCommand Signed-off-by: Michael Kaufmann (d00p) --- customer_email.php | 191 +++----- .../api/commands/class.EmailAccounts.php | 440 ++++++++++++++++++ .../api/commands/class.EmailForwarders.php | 92 ++-- 3 files changed, 538 insertions(+), 185 deletions(-) create mode 100644 lib/classes/api/commands/class.EmailAccounts.php diff --git a/customer_email.php b/customer_email.php index cf2d043c..4662656a 100644 --- a/customer_email.php +++ b/customer_email.php @@ -279,72 +279,49 @@ if ($page == 'overview') { } $result = json_decode($json_result, true)['data']; - if (isset($result['email']) && $result['email'] != '' && $result['popaccountid'] == '0') { - if (isset($_POST['send']) && $_POST['send'] == 'send') { - try { - EmailAccounts::getLocal($userinfo, $_POST)->add(); - } catch (Exception $e) { - dynamic_error($e->getMessage()); - } - redirectTo($filename, array('page' => 'emails', 'action' => 'edit', 'id' => $id, 's' => $s)); - } else { - - if (checkMailAccDeletionState($result['email_full'])) { - standard_error(array('mailaccistobedeleted'), $result['email_full']); - } - - $result['email_full'] = $idna_convert->decode($result['email_full']); - $result = htmlentities_array($result); - $quota = Settings::Get('system.mail_quota'); - - $account_add_data = include_once dirname(__FILE__) . '/lib/formfields/customer/email/formfield.emails_addaccount.php'; - $account_add_form = htmlform::genHTMLForm($account_add_data); - - $title = $account_add_data['emails_addaccount']['title']; - $image = $account_add_data['emails_addaccount']['image']; - - eval("echo \"" . getTemplate("email/account_add") . "\";"); + if (isset($_POST['send']) && $_POST['send'] == 'send') { + try { + EmailAccounts::getLocal($userinfo, $_POST)->add(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); } + redirectTo($filename, array('page' => 'emails', 'action' => 'edit', 'id' => $id, 's' => $s)); + } else { + + if (checkMailAccDeletionState($result['email_full'])) { + standard_error(array('mailaccistobedeleted'), $result['email_full']); + } + + $result['email_full'] = $idna_convert->decode($result['email_full']); + $result = htmlentities_array($result); + $quota = Settings::Get('system.mail_quota'); + + $account_add_data = include_once dirname(__FILE__) . '/lib/formfields/customer/email/formfield.emails_addaccount.php'; + $account_add_form = htmlform::genHTMLForm($account_add_data); + + $title = $account_add_data['emails_addaccount']['title']; + $image = $account_add_data['emails_addaccount']['image']; + + eval("echo \"" . getTemplate("email/account_add") . "\";"); } } else { standard_error(array('allresourcesused', 'allocatetoomuchquota'), $quota); } } elseif ($action == 'changepw' && $id != 0) { - $stmt = Database::prepare("SELECT `id`, `email`, `email_full`, `iscatchall`, `destination`, `customerid`, `popaccountid` FROM `" . TABLE_MAIL_VIRTUAL . "` - WHERE `customerid`= :cid - AND `id`= :id" - ); - $result = Database::pexecute_first($stmt, array("cid" => $userinfo['customerid'], "id" => $id)); + try { + $json_result = Emails::getLocal($userinfo, array('id' => $id))->get(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); + } + $result = json_decode($json_result, true)['data']; if (isset($result['popaccountid']) && $result['popaccountid'] != '') { if (isset($_POST['send']) && $_POST['send'] == 'send') { - $password = validate($_POST['email_password'], 'password'); - - if ($password == '') { - standard_error(array('stringisempty', 'mypassword')); + try { + EmailAccounts::getLocal($userinfo, $_POST)->update(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); } - elseif ($password == $result['email_full']) { - standard_error('passwordshouldnotbeusername'); - } - - $password = validatePassword($password); - - $log->logAction(USR_ACTION, LOG_INFO, "changed email password for '" . $result['email_full'] . "'"); - $cryptPassword = makeCryptPassword($password); - $stmt = Database::prepare("UPDATE `" . TABLE_MAIL_USERS . "` - SET " . (Settings::Get('system.mailpwcleartext') == '1' ? "`password` = :password, " : '') . " - `password_enc`= :password_enc - WHERE `customerid`= :cid - AND `id`= :id" - ); - $params = array( - "password_enc" => $cryptPassword, - "cid" => $userinfo['customerid'], - "id" => $result['popaccountid'] - ); - if (Settings::Get('system.mailpwcleartext') == '1') { $params["password"] = $password; } - Database::pexecute($stmt, $params); - redirectTo($filename, array('page' => 'emails', 'action' => 'edit', 'id' => $id, 's' => $s)); } else { $result['email_full'] = $idna_convert->decode($result['email_full']); @@ -360,46 +337,21 @@ if ($page == 'overview') { } } } elseif ($action == 'changequota' && Settings::Get('system.mail_quota_enabled') == '1' && $id != 0) { - $stmt = Database::prepare("SELECT `v`.`id`, `v`.`email`, `v`.`email_full`, `v`.`iscatchall`, `v`.`destination`, `v`.`customerid`, `v`.`popaccountid`, `u`.`quota` - FROM `" . TABLE_MAIL_VIRTUAL . "` `v` - LEFT JOIN `" . TABLE_MAIL_USERS . "` `u` - ON(`v`.`popaccountid` = `u`.`id`) - WHERE `v`.`customerid`= :cid - AND `v`.`id`= :id" - ); - $result = Database::pexecute_first($stmt, array("cid" => $userinfo['customerid'], "id" => $id)); + try { + $json_result = Emails::getLocal($userinfo, array('id' => $id))->get(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); + } + $result = json_decode($json_result, true)['data']; if (isset($result['popaccountid']) && $result['popaccountid'] != '') { if (isset($_POST['send']) && $_POST['send'] == 'send') { - $quota = (int)validate($_POST['email_quota'], 'email_quota', '/^\d+$/', 'vmailquotawrong'); - - if ($userinfo['email_quota'] != '-1' && ($quota == 0 || ($quota + $userinfo['email_quota_used'] - $result['quota']) > $userinfo['email_quota'])) { - standard_error('allocatetoomuchquota', $quota); - } else { - $log->logAction(USR_ACTION, LOG_INFO, "updated quota for email address '" . $result['email'] . "' to " . $quota . " MB"); - $stmt = Database::prepare("UPDATE `" . TABLE_MAIL_USERS . "` - SET `quota` = :quota - WHERE `id` = :id - AND `customerid`= :cid" - ); - $params = array( - "quota" => $quota, - "id" => $result['popaccountid'], - "cid" => $userinfo['customerid'] - ); - Database::pexecute($stmt, $params); - - if ($userinfo['email_quota'] != '-1') { - $new_used_quota = $userinfo['email_quota_used'] + ($quota - $result['quota']); - $stmt = Database::prepare("UPDATE `" . TABLE_PANEL_CUSTOMERS . "` - SET `email_quota_used` = :used - WHERE `customerid` = :cid" - ); - Database::pexecute($stmt, array("used" => $new_used_quota, "cid" => $userinfo['customerid'])); - } - - redirectTo($filename, array('page' => 'emails', 'action' => 'edit', 'id' => $id, 's' => $s)); + try { + EmailAccounts::getLocal($userinfo, $_POST)->update(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); } + redirectTo($filename, array('page' => 'emails', 'action' => 'edit', 'id' => $id, 's' => $s)); } else { $result['email_full'] = $idna_convert->decode($result['email_full']); $result = htmlentities_array($result); @@ -414,55 +366,20 @@ if ($page == 'overview') { } } } elseif ($action == 'delete' && $id != 0) { - $stmt = Database::prepare("SELECT `v`.`id`, `v`.`email`, `v`.`email_full`, `v`.`iscatchall`, `v`.`destination`, `v`.`customerid`, `v`.`popaccountid`, `u`.`quota` - FROM `" . TABLE_MAIL_VIRTUAL . "` `v` - LEFT JOIN `" . TABLE_MAIL_USERS . "` `u` - ON(`v`.`popaccountid` = `u`.`id`) - WHERE `v`.`customerid`='" . (int)$userinfo['customerid'] . "' - AND `v`.`id`='" . (int)$id . "'" - ); - $result = Database::pexecute_first($stmt, array("cid" => $userinfo['customerid'], "id" => $id)); + try { + $json_result = Emails::getLocal($userinfo, array('id' => $id))->get(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); + } + $result = json_decode($json_result, true)['data']; if (isset($result['popaccountid']) && $result['popaccountid'] != '') { if (isset($_POST['send']) && $_POST['send'] == 'send') { - $stmt = Database::prepare("DELETE FROM `" . TABLE_MAIL_USERS . "` - WHERE `customerid`= :cid - AND `id`= :id" - ); - Database::pexecute($stmt, array("cid" => $userinfo['customerid'], "id" => $result['popaccountid'])); - $result['destination'] = str_replace($result['email_full'], '', $result['destination']); - - $stmt = Database::prepare("UPDATE `" . TABLE_MAIL_VIRTUAL . "` - SET `destination` = :dest, - `popaccountid` = '0' - WHERE `customerid`= :cid - AND `id`= :id" - ); - $params = array( - "dest" => makeCorrectDestination($result['destination']), - "cid" => $userinfo['customerid'], - "id" => $id - ); - Database::pexecute($stmt, $params); - - if (Settings::Get('system.mail_quota_enabled') == '1' && $userinfo['email_quota'] != '-1') { - $quota = (int)$result['quota']; - } else { - $quota = 0; + try { + EmailAccounts::getLocal($userinfo, $_POST)->delete(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); } - - if (isset($_POST['delete_userfiles']) && (int)$_POST['delete_userfiles'] == 1) { - inserttask('7', $userinfo['loginname'], $result['email_full']); - } - - $stmt = Database::prepare("UPDATE `" . TABLE_PANEL_CUSTOMERS . "` - SET `email_accounts_used` = `email_accounts_used` - 1, - `email_quota_used` = `email_quota_used` - :quota - WHERE `customerid`= :cid" - ); - Database::pexecute($stmt, array("quota" => $quota, "cid" => $userinfo['customerid'])); - - $log->logAction(USR_ACTION, LOG_INFO, "deleted email account for '" . $result['email_full'] . "'"); redirectTo($filename, array('page' => 'emails', 'action' => 'edit', 'id' => $id, 's' => $s)); } else { ask_yesno_withcheckbox('email_reallydelete_account', 'admin_customer_alsoremovemail', $filename, array('id' => $id, 'page' => $page, 'action' => $action), $idna_convert->decode($result['email_full'])); diff --git a/lib/classes/api/commands/class.EmailAccounts.php b/lib/classes/api/commands/class.EmailAccounts.php new file mode 100644 index 00000000..aea8e70a --- /dev/null +++ b/lib/classes/api/commands/class.EmailAccounts.php @@ -0,0 +1,440 @@ + (2010-) + * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt + * @package API + * @since 0.10.0 + * + */ +class EmailAccounts extends ApiCommand implements ResourceEntity +{ + + public function add() + { + if ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'email')) { + throw new Exception("You cannot access this resource", 405); + } + + if ($this->getUserDetail('email_accounts_used') < $this->getUserDetail('email_accounts') || $this->getUserDetail('email_accounts') == '-1') { + + // parameter + $id = $this->getParam('id', true, 0); + $ea_optional = ($id <= 0 ? false : true); + $emailaddr = $this->getParam('emailaddr', $ea_optional, ''); + $email_password = $this->getParam('email_password'); + $alternative_email = $this->getParam('alternative_email', true, ''); + $quota = $this->getParam('email_quota', true, 0); + + // validation + $quota = validate($quota, 'email_quota', '/^\d+$/', 'vmailquotawrong', array(), true); + + // get needed customer info to reduce the email-account-counter by one + $customer = $this->getCustomerData('email_accounts'); + + // check for imap||pop3 == 1, see #1298 + if ($customer['imap'] != '1' && $customer['pop3'] != '1') { + standard_error('notallowedtouseaccounts', '', true); + } + + // get email address + $result = $this->apiCall('Emails.get', array( + 'id' => $id, + 'emailaddr' => $emailaddr + )); + $id = $result['id']; + + $email_full = $result['email_full']; + $idna_convert = new idna_convert_wrapper(); + $username = $idna_convert->decode($email_full); + $password = validate($email_password, 'password', '', '', array(), true); + $password = validatePassword($password, true); + + if ($result['popaccountid'] != 0) { + throw new Exception("Email address '" . $email_full . "' has already an account assigned.", 406); + } + + if (checkMailAccDeletionState($email_full)) { + standard_error(array( + 'mailaccistobedeleted' + ), $email_full, true); + } + + // alternative email address to send info to + if (Settings::Get('panel.sendalternativemail') == 1) { + $alternative_email = $idna_convert->encode(validate($alternative_email, 'alternative_email', '', '', array(), true)); + if (! validateEmail($alternative_email)) { + standard_error('emailiswrong', $alternative_email, true); + } + } else { + $alternative_email = ''; + } + + // validate quota if enabled + if (Settings::Get('system.mail_quota_enabled') == 1) { + if ($customer['email_quota'] != '-1' && ($quota == 0 || ($quota + $customer['email_quota_used']) > $customer['email_quota'])) { + standard_error('allocatetoomuchquota', $quota, true); + } + } else { + // disable + $quota = 0; + } + + if ($password == $email_full) { + standard_error('passwordshouldnotbeusername', '', true); + } + + // encrypt the password + $cryptPassword = makeCryptPassword($password); + + $email_user = substr($email_full, 0, strrpos($email_full, "@")); + $email_domain = substr($email_full, strrpos($email_full, "@") + 1); + $maildirname = trim(Settings::Get('system.vmail_maildirname')); + // Add trailing slash to Maildir if needed + $maildirpath = $maildirname; + if (! empty($maildirname) && substr($maildirname, - 1) != "/") { + $maildirpath .= "/"; + } + + // insert data + $stmt = Database::prepare("INSERT INTO `" . TABLE_MAIL_USERS . "` SET + `customerid` = :cid, + `email` = :email, + `username` = :username," . (Settings::Get('system.mailpwcleartext') == '1' ? '`password` = :password, ' : '') . " + `password_enc` = :password_end, + `homedir` = :homedir, + `maildir` = :maildir, + `uid` = :uid, + `gid` = :gid, + `domainid` = :domainid, + `postfix` = 'y', + `quota` = :quota, + `imap` = :imap, + `pop3` = :pop3 + "); + $params = array( + "cid" => $customer['customerid'], + "email" => $email_full, + "username" => $username, + "password_enc" => $cryptPassword, + "homedir" => Settings::Get('system.vmail_homedir'), + "maildir" => $customer['loginname'] . '/' . $email_domain . "/" . $email_user . "/" . $maildirpath, + "uid" => Settings::Get('system.vmail_uid'), + "gid" => Settings::Get('system.vmail_gid'), + "domainid" => $result['domainid'], + "quota" => $quota, + "imap" => $customer['imap'], + "pop3" => $customer['pop3'] + ); + if (Settings::Get('system.mailpwcleartext') == '1') { + $params["password"] = $password; + } + Database::pexecute($stmt, $params, true, true); + $popaccountid = Database::lastInsertId(); + + // add email address to its destination field + $result['destination'] .= ' ' . $email_full; + $stmt = Database::prepare(" + UPDATE `" . TABLE_MAIL_VIRTUAL . "` SET `destination` = :destination, `popaccountid` = :popaccountid + WHERE `customerid`= :cid AND `id`= :id + "); + $params = array( + "destination" => makeCorrectDestination($result['destination']), + "popaccountid" => $popaccountid, + "cid" => $customer['customerid'], + "id" => $id + ); + Database::pexecute($stmt, $params, true, true); + + // update customer usage + Customers::increaseUsage($customer['customerid'], 'email_accounts_used'); + Customers::increaseUsage($customer['customerid'], 'email_quota_used', '', $quota); + + // update admin usage + Admins::increaseUsage($customer['adminid'], 'email_accounts_used'); + Admins::increaseUsage($customer['adminid'], 'email_quota_used', '', $quota); + + // replacer array for mail to create account on server + $replace_arr = array( + 'EMAIL' => $email_full, + 'USERNAME' => $username, + 'PASSWORD' => $password + ); + + // get the customers admin + $stmt = Database::prepare("SELECT `name`, `email` FROM `" . TABLE_PANEL_ADMINS . "` WHERE `adminid`= :adminid"); + $admin = Database::pexecute_first($stmt, array( + "adminid" => $customer['adminid'] + )); + + // get template for mail subject + $mail_subject = $this->getMailTemplate($customer, 'mails', 'pop_success_subject', $replace_arr, $this->lng['mails']['pop_success']['subject']); + // get template for mail body + $mail_body = $this->getMailTemplate($customer, 'mails', 'pop_success_mailbody', $replace_arr, $this->lng['mails']['pop_success']['mailbody']); + + $_mailerror = false; + try { + $this->mailer()->SetFrom($admin['email'], getCorrectUserSalutation($admin)); + $this->mailer()->Subject = $mail_subject; + $this->mailer()->AltBody = $mail_body; + $this->mailer()->MsgHTML(str_replace("\n", "
    ", $mail_body)); + $this->mailer()->AddAddress($email_full); + $this->mailer()->Send(); + } catch (phpmailerException $e) { + $mailerr_msg = $e->errorMessage(); + $_mailerror = true; + } catch (Exception $e) { + $mailerr_msg = $e->getMessage(); + $_mailerror = true; + } + + if ($_mailerror) { + $log->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_ERR, "[API] Error sending mail: " . $mailerr_msg); + standard_error('errorsendingmail', $email_full, true); + } + + $this->mailer()->ClearAddresses(); + + // customer wants to send the e-mail to an alternative email address too + if (Settings::Get('panel.sendalternativemail') == 1) { + // get template for mail 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 + $mail_body = $this->getMailTemplate($customer, 'mails', 'pop_success_alternative_mailbody', $replace_arr, $this->lng['mails']['pop_success_alternative']['mailbody']); + + $_mailerror = false; + try { + $this->mailer()->SetFrom($admin['email'], getCorrectUserSalutation($admin)); + $this->mailer()->Subject = $mail_subject; + $this->mailer()->AltBody = $mail_body; + $this->mailer()->MsgHTML(str_replace("\n", "
    ", $mail_body)); + $this->mailer()->AddAddress($idna_convert->encode($alternative_email), getCorrectUserSalutation($customer)); + $this->mailer()->Send(); + } catch (phpmailerException $e) { + $mailerr_msg = $e->errorMessage(); + $_mailerror = true; + } catch (Exception $e) { + $mailerr_msg = $e->getMessage(); + $_mailerror = true; + } + + if ($_mailerror) { + $log->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_ERR, "[API] Error sending mail: " . $mailerr_msg); + standard_error(array( + 'errorsendingmail' + ), $alternative_email, true); + } + + $this->mailer()->ClearAddresses(); + } + + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_INFO, "[API] added email account for '" . $result['email_full'] . "'"); + $result = $this->apiCall('Emails.get', array( + 'emailaddr' => $result['email_full'] + )); + return $this->response(200, "successfull", $result); + } + throw new Exception("No more resources available", 406); + } + + public function get() + { + throw new Exception('You cannot directly get an email forwarder. You need to call Emails.get()', 303); + } + + /** + * update email-account entry for given email-address by either id or email-address + * + * @param int $id + * optional, the email-address-id + * @param string $emailaddr + * optional, the email-address to add the forwarder for + * @param int $customerid + * optional, required when called as admin/reseller + * @param int $email_quota + * optional, update quota + * @param string $email_password + * optional, update password + * + * @access admin,customer + * @throws Exception + * @return array + */ + public function update() + { + if ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'email')) { + throw new Exception("You cannot access this resource", 405); + } + + // parameter + $id = $this->getParam('id', true, 0); + $ea_optional = ($id <= 0 ? false : true); + $emailaddr = $this->getParam('emailaddr', $ea_optional, ''); + + // validation + $result = $this->apiCall('Emails.get', array( + 'id' => $id, + 'emailaddr' => $emailaddr + )); + $id = $result['id']; + + if (empty($result['popaccountid']) || $result['popaccountid'] == 0) { + throw new Exception("Email address '" . $result['email_full'] . "' has no account assigned.", 406); + } + + $email_password = $this->getParam('email_password', true, ''); + $quota = $this->getParam('email_quota', true, $result['quota']); + + // get needed customer info to reduce the email-account-counter by one + $customer = $this->getCustomerData(); + + // validation + $quota = validate($quota, 'email_quota', '/^\d+$/', 'vmailquotawrong', array(), true); + + $upd_query = ""; + $upd_params = array( + "id" => $result['popaccountid'], + "cid" => $customer['customerid'] + ); + if (! empty($password)) { + if ($password == $result['email_full']) { + standard_error('passwordshouldnotbeusername', '', true); + } + $password = validatePassword($password, true); + $cryptPassword = makeCryptPassword($password); + $upd_query .= (Settings::Get('system.mailpwcleartext') == '1' ? "`password` = :password, " : '') . "`password_enc`= :password_enc"; + $upd_params['password_enc'] = $cryptPassword; + if (Settings::Get('system.mailpwcleartext') == '1') { + $upd_params['password'] = $password; + } + } + + if ($quota != $result['quota']) { + if ($customer['email_quota'] != '-1' && ($quota == 0 || ($quota + $customer['email_quota_used'] - $result['quota']) > $customer['email_quota'])) { + standard_error('allocatetoomuchquota', $quota, true); + } + if (! empty($upd_query)) { + $upd_query .= ", "; + } + $upd_query .= "`quota` = :quota"; + $upd_params['quota'] = $quota; + } + + // build update query + if (! empty($upd_query)) { + $upd_stmt = Database::prepare(" + UPDATE `" . TABLE_MAIL_USERS . "` SET " . $upd_query . " WHERE `id` = :id AND `customerid`= :cid + "); + Database::pexecute($upd_stmt, $upd_params, true, true); + } + + if ($customer['email_quota'] != '-1') { + Customers::increaseUsage($customer['customerid'], 'email_quota_used', '', ($quota - $result['quota'])); + Admins::increaseUsage($customer['adminid'], 'email_quota_used', '', ($quota - $result['quota'])); + } + + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_INFO, "[API] updated email account '" . $result['email_full'] . "'"); + $result = $this->apiCall('Emails.get', array( + 'emailaddr' => $result['email_full'] + )); + return $this->response(200, "successfull", $result); + } + + public function listing() + { + throw new Exception('You cannot directly list email forwarders. You need to call Emails.listing()', 303); + } + + /** + * delete email-account entry for given email-address by either id or email-address + * + * @param int $id + * optional, the email-address-id + * @param string $emailaddr + * optional, the email-address to add the forwarder for + * @param bool $delete_userfiles + * optional, default false + * @param int $customerid + * optional, required when called as admin/reseller + * + * @access admin,customer + * @throws Exception + * @return array + */ + public function delete() + { + if ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'email')) { + throw new Exception("You cannot access this resource", 405); + } + + // parameter + $id = $this->getParam('id', true, 0); + $ea_optional = ($id <= 0 ? false : true); + $emailaddr = $this->getParam('emailaddr', $ea_optional, ''); + $delete_userfiles = $this->getParam('delete_userfiles', true, 0); + + // validation + $result = $this->apiCall('Emails.get', array( + 'id' => $id, + 'emailaddr' => $emailaddr + )); + $id = $result['id']; + + if (empty($result['popaccountid']) || $result['popaccountid'] == 0) { + throw new Exception("Email address '" . $result['email_full'] . "' has no account assigned.", 406); + } + + // get needed customer info to reduce the email-account-counter by one + $customer = $this->getCustomerData(); + + // delete entry + $stmt = Database::prepare(" + DELETE FROM `" . TABLE_MAIL_USERS . "` WHERE `customerid`= :cid AND `id`= :id + "); + Database::pexecute($stmt, array( + "cid" => $customer['customerid'], + "id" => $result['popaccountid'] + ), true, true); + + // update mail-virtual entry + $result['destination'] = str_replace($result['email_full'], '', $result['destination']); + + $stmt = Database::prepare(" + UPDATE `" . TABLE_MAIL_VIRTUAL . "` SET `destination` = :dest, `popaccountid` = '0' WHERE `customerid`= :cid AND `id`= :id + "); + $params = array( + "dest" => makeCorrectDestination($result['destination']), + "cid" => $customer['customerid'], + "id" => $id + ); + Database::pexecute($stmt, $params, true, true); + + if (Settings::Get('system.mail_quota_enabled') == '1' && $customer['email_quota'] != '-1') { + $quota = (int) $result['quota']; + } else { + $quota = 0; + } + + if ($delete_userfiles) { + inserttask('7', $customer['loginname'], $result['email_full']); + } + + // decrease usage for customer + Customers::decreaseUsage($customer['customerid'], 'email_accounts_used'); + Customers::decreaseUsage($customer['customerid'], 'email_quota_used', '', $quota); + // decrease admin usage + Admins::decreaseUsage($customer['adminid'], 'email_accounts_used'); + Admins::decreaseUsage($customer['adminid'], 'email_quota_used', '', $quota); + + $log->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_INFO, "[API] deleted email account for '" . $result['email_full'] . "'"); + return $this->response(200, "successfull", $result); + } +} diff --git a/lib/classes/api/commands/class.EmailForwarders.php b/lib/classes/api/commands/class.EmailForwarders.php index ad325e92..e91b96b8 100644 --- a/lib/classes/api/commands/class.EmailForwarders.php +++ b/lib/classes/api/commands/class.EmailForwarders.php @@ -138,58 +138,54 @@ class EmailForwarders extends ApiCommand implements ResourceEntity throw new Exception("You cannot access this resource", 405); } - if ($this->getUserDetail('email_forwarders_used') < $this->getUserDetail('email_forwarders') || $this->getUserDetail('email_forwarders') == '-1') { + // parameter + $id = $this->getParam('id', true, 0); + $ea_optional = ($id <= 0 ? false : true); + $emailaddr = $this->getParam('emailaddr', $ea_optional, ''); + $forwarderid = $this->getParam('forwarderid'); + + // validation + $result = $this->apiCall('Emails.get', array( + 'id' => $id, + 'emailaddr' => $emailaddr + )); + $id = $result['id']; + + $result['destination'] = explode(' ', $result['destination']); + if (isset($result['destination'][$forwarderid]) && $result['email'] != $result['destination'][$forwarderid]) { - // parameter - $id = $this->getParam('id', true, 0); - $ea_optional = ($id <= 0 ? false : true); - $emailaddr = $this->getParam('emailaddr', $ea_optional, ''); - $forwarderid = $this->getParam('forwarderid'); + // get needed customer info to reduce the email-forwarder-counter by one + $customer = $this->getCustomerData(); + + // unset it from array + unset($result['destination'][$forwarderid]); + // rebuild destination-string + $result['destination'] = implode(' ', $result['destination']); + // update in DB + $stmt = Database::prepare(" + UPDATE `" . TABLE_MAIL_VIRTUAL . "` SET `destination` = :dest + WHERE `customerid`= :cid AND `id`= :id + "); + $params = array( + "dest" => makeCorrectDestination($result['destination']), + "cid" => $customer['customerid'], + "id" => $id + ); + Database::pexecute($stmt, $params, true, true); + + // update customer usage + Customers::decreaseUsage($customer['customerid'], 'email_forwarders_used'); + + // update admin usage + Admins::decreaseUsage($customer['adminid'], 'email_forwarders_used'); + + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_INFO, "[API] deleted email forwarder for '" . $result['email_full'] . "'"); - // validation $result = $this->apiCall('Emails.get', array( - 'id' => $id, - 'emailaddr' => $emailaddr + 'emailaddr' => $result['email_full'] )); - $id = $result['id']; - - $result['destination'] = explode(' ', $result['destination']); - if (isset($result['destination'][$forwarderid]) && $result['email'] != $result['destination'][$forwarderid]) { - - // get needed customer info to reduce the email-forwarder-counter by one - $customer = $this->getCustomerData(); - - // unset it from array - unset($result['destination'][$forwarderid]); - // rebuild destination-string - $result['destination'] = implode(' ', $result['destination']); - // update in DB - $stmt = Database::prepare(" - UPDATE `" . TABLE_MAIL_VIRTUAL . "` SET `destination` = :dest - WHERE `customerid`= :cid AND `id`= :id - "); - $params = array( - "dest" => makeCorrectDestination($result['destination']), - "cid" => $customer['customerid'], - "id" => $id - ); - Database::pexecute($stmt, $params, true, true); - - // update customer usage - Customers::decreaseUsage($customer['customerid'], 'email_forwarders_used'); - - // update admin usage - Admins::decreaseUsage($customer['adminid'], 'email_forwarders_used'); - - $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_INFO, "[API] deleted email forwarder for '" . $result['email_full'] . "'"); - - $result = $this->apiCall('Emails.get', array( - 'emailaddr' => $result['email_full'] - )); - return $this->response(200, "successfull", $result); - } - throw new Exception("Unknown forwarder id", 404); + return $this->response(200, "successfull", $result); } - throw new Exception("No more resources available", 406); + throw new Exception("Unknown forwarder id", 404); } } From f0e084ef0eb2230a83a6bc50b4a54ccbd2a6547f Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 12 Mar 2018 14:17:45 +0100 Subject: [PATCH 0560/1335] minor fixes in EmailAccounts.update() Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/commands/class.EmailAccounts.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/classes/api/commands/class.EmailAccounts.php b/lib/classes/api/commands/class.EmailAccounts.php index aea8e70a..1b8216de 100644 --- a/lib/classes/api/commands/class.EmailAccounts.php +++ b/lib/classes/api/commands/class.EmailAccounts.php @@ -290,7 +290,7 @@ class EmailAccounts extends ApiCommand implements ResourceEntity throw new Exception("Email address '" . $result['email_full'] . "' has no account assigned.", 406); } - $email_password = $this->getParam('email_password', true, ''); + $password = $this->getParam('email_password', true, ''); $quota = $this->getParam('email_quota', true, $result['quota']); // get needed customer info to reduce the email-account-counter by one @@ -434,7 +434,7 @@ class EmailAccounts extends ApiCommand implements ResourceEntity Admins::decreaseUsage($customer['adminid'], 'email_accounts_used'); Admins::decreaseUsage($customer['adminid'], 'email_quota_used', '', $quota); - $log->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_INFO, "[API] deleted email account for '" . $result['email_full'] . "'"); + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_INFO, "[API] deleted email account for '" . $result['email_full'] . "'"); return $this->response(200, "successfull", $result); } } From 54deec87d08779d2a472da778ee260bd22287e09 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 12 Mar 2018 15:41:08 +0100 Subject: [PATCH 0561/1335] add a few emails-apicommand unit-tests Signed-off-by: Michael Kaufmann (d00p) --- lng/english.lng.php | 2 +- tests/Emails/EmailsTest.php | 200 +++++++++++++++++++++++++++++++++++- 2 files changed, 200 insertions(+), 2 deletions(-) diff --git a/lng/english.lng.php b/lng/english.lng.php index 507799e7..d514eddc 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -226,7 +226,7 @@ $lng['error']['emailexistalready'] = 'The email-address %s already exists.'; $lng['error']['maindomainnonexist'] = 'The main-domain %s does not exist.'; $lng['error']['destinationnonexist'] = 'Please create your forwarder in the field \'Destination\'.'; $lng['error']['destinationalreadyexistasmail'] = 'The forwarder to %s already exists as active email-address.'; -$lng['error']['destinationalreadyexist'] = 'You have already defined a forwarder to %s .'; +$lng['error']['destinationalreadyexist'] = 'You have already defined a forwarder to "%s"'; $lng['error']['destinationiswrong'] = 'The forwarder %s contains invalid character(s) or is incomplete.'; $lng['error']['ticketnotaccessible'] = 'You cannot access this ticket.'; diff --git a/tests/Emails/EmailsTest.php b/tests/Emails/EmailsTest.php index 77f71c12..73287358 100644 --- a/tests/Emails/EmailsTest.php +++ b/tests/Emails/EmailsTest.php @@ -5,9 +5,12 @@ use PHPUnit\Framework\TestCase; * @covers ApiCommand * @covers ApiParameter * @covers Emails + * @covers EmailForwarders + * @covers EmailAccounts */ class MailsTest extends TestCase { + public function testCustomerEmailsAdd() { global $admin_userdata; @@ -27,7 +30,7 @@ class MailsTest extends TestCase $this->assertEquals("info@test2.local", $result['email_full']); $this->assertEquals(0, $result['iscatchall']); } - + public function testAdminEmailsAdd() { global $admin_userdata; @@ -42,4 +45,199 @@ class MailsTest extends TestCase $this->assertEquals("catchall@test2.local", $result['email_full']); $this->assertEquals(1, $result['iscatchall']); } + + public function testAdminEmailsUpdate() + { + global $admin_userdata; + $data = [ + 'emailaddr' => 'catchall@test2.local', + 'iscatchall' => 0, + 'customerid' => 1 + ]; + $json_result = Emails::getLocal($admin_userdata, $data)->update(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals(0, $result['iscatchall']); + } + + public function testCustomerEmailsUpdate() + { + global $admin_userdata; + + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + + $data = [ + 'emailaddr' => 'catchall@test2.local', + 'iscatchall' => 1 + ]; + $json_result = Emails::getLocal($customer_userdata, $data)->update(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals(1, $result['iscatchall']); + } + + public function testCustomerEmailForwardersAdd() + { + global $admin_userdata; + + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + + $data = [ + 'emailaddr' => 'info@test2.local', + 'destination' => 'other@domain.tld' + ]; + $json_result = EmailForwarders::getLocal($customer_userdata, $data)->add(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals('other@domain.tld', $result['destination']); + } + + public function testCustomerEmailForwardersAddAnother() + { + global $admin_userdata; + + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + + $data = [ + 'emailaddr' => 'info@test2.local', + 'destination' => 'other2@domain.tld' + ]; + $json_result = EmailForwarders::getLocal($customer_userdata, $data)->add(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals('other@domain.tld other2@domain.tld', $result['destination']); + } + + /** + * @depends testCustomerEmailForwardersAdd + */ + public function testCustomerEmailForwardersAddExistingAsMail() + { + global $admin_userdata; + + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + + $data = [ + 'emailaddr' => 'info@test2.local', + 'destination' => 'info@test2.local' + ]; + $this->expectExceptionMessage("The forwarder to info@test2.local already exists as active email-address."); + EmailForwarders::getLocal($customer_userdata, $data)->add(); + } + + /** + * @depends testCustomerEmailForwardersAdd + */ + public function testCustomerEmailForwardersAddExistingAsDestination() + { + global $admin_userdata; + + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + + $data = [ + 'emailaddr' => 'info@test2.local', + 'destination' => 'other@domain.tld' + ]; + $this->expectExceptionMessage('You have already defined a forwarder to "other@domain.tld"'); + EmailForwarders::getLocal($customer_userdata, $data)->add(); + } + + public function testCustomerEmailForwardersAddInvalid() + { + global $admin_userdata; + + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + + $data = [ + 'emailaddr' => 'info@test2.local', + 'destination' => '@domain.com' + ]; + $this->expectExceptionMessage("The forwarder domain.com contains invalid character(s) or is incomplete."); + EmailForwarders::getLocal($customer_userdata, $data)->add(); + } + + public function testAdminEmailForwadersUndefinedGet() + { + global $admin_userdata; + $this->expectExceptionCode(303); + EmailForwarders::getLocal($admin_userdata)->get(); + } + + public function testAdminEmailForwadersUndefinedUpdate() + { + global $admin_userdata; + $this->expectExceptionCode(303); + EmailForwarders::getLocal($admin_userdata)->update(); + } + + public function testAdminEmailForwadersUndefinedListing() + { + global $admin_userdata; + $this->expectExceptionCode(303); + EmailForwarders::getLocal($admin_userdata)->listing(); + } + + /** + * @depends testCustomerEmailForwardersAddAnother + */ + public function testCustomerEmailForwardersDelete() + { + global $admin_userdata; + + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + + $data = [ + 'emailaddr' => 'info@test2.local', + 'forwarderid' => 1 + ]; + $json_result = EmailForwarders::getLocal($customer_userdata, $data)->delete(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals('other@domain.tld', $result['destination']); + } + + /** + * @depends testCustomerEmailForwardersAddAnother + */ + public function testCustomerEmailForwardersDeleteunknown() + { + global $admin_userdata; + + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + + $data = [ + 'emailaddr' => 'info@test2.local', + 'forwarderid' => 1337 + ]; + $this->expectExceptionCode(404); + $this->expectExceptionMessage("Unknown forwarder id"); + EmailForwarders::getLocal($customer_userdata, $data)->delete(); + } } From a7523bbdea360fc14a5c9aeba96238a0983b3549 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 12 Mar 2018 15:52:34 +0100 Subject: [PATCH 0562/1335] add domainid to result-list of Emails.get(); fix typo in EmailAccounts.add(); enhance debugging in Database-class Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/commands/class.EmailAccounts.php | 2 +- lib/classes/api/commands/class.Emails.php | 2 +- lib/classes/database/class.Database.php | 14 +++++++------- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/classes/api/commands/class.EmailAccounts.php b/lib/classes/api/commands/class.EmailAccounts.php index 1b8216de..b4f18b0f 100644 --- a/lib/classes/api/commands/class.EmailAccounts.php +++ b/lib/classes/api/commands/class.EmailAccounts.php @@ -109,7 +109,7 @@ class EmailAccounts extends ApiCommand implements ResourceEntity `customerid` = :cid, `email` = :email, `username` = :username," . (Settings::Get('system.mailpwcleartext') == '1' ? '`password` = :password, ' : '') . " - `password_enc` = :password_end, + `password_enc` = :password_enc, `homedir` = :homedir, `maildir` = :maildir, `uid` = :uid, diff --git a/lib/classes/api/commands/class.Emails.php b/lib/classes/api/commands/class.Emails.php index d4a36fc6..5255cf33 100644 --- a/lib/classes/api/commands/class.Emails.php +++ b/lib/classes/api/commands/class.Emails.php @@ -162,7 +162,7 @@ class Emails extends ApiCommand implements ResourceEntity $params['customerid'] = implode(", ", $customer_ids); $params['idea'] = ($id <= 0 ? $emailaddr : $id); - $result_stmt = Database::prepare("SELECT v.`id`, v.`email`, v.`email_full`, v.`iscatchall`, v.`destination`, v.`customerid`, v.`popaccountid`, u.`quota` + $result_stmt = Database::prepare("SELECT v.`id`, v.`email`, v.`email_full`, v.`iscatchall`, v.`destination`, v.`customerid`, v.`popaccountid`, v.`domainid`, u.`quota` FROM `" . TABLE_MAIL_VIRTUAL . "` v LEFT JOIN `" . TABLE_MAIL_USERS . "` u ON v.`popaccountid` = u.`id` WHERE v.`customerid` IN (:customerid) diff --git a/lib/classes/database/class.Database.php b/lib/classes/database/class.Database.php index 3c7d5cb5..3c1e64c3 100644 --- a/lib/classes/database/class.Database.php +++ b/lib/classes/database/class.Database.php @@ -372,17 +372,17 @@ class Database { @fwrite($errlog, "|TRACE\n".$error_trace."\n"); @fclose($errlog); + if (empty($sql['debug'])) { + $error_trace = ''; + } elseif (!is_null($stmt)) { + $error_trace .= "

    ".$stmt->queryString; + } + if ($showerror && $json_response) { - throw new Exception($error_message, 500); + throw new Exception($error_message.($sql['debug'] ? "\n\n".$error_trace : ''), 500); } if ($showerror) { - if (empty($sql['debug'])) { - $error_trace = ''; - } elseif (!is_null($stmt)) { - $error_trace .= "

    ".$stmt->queryString; - } - // fallback $theme = 'Sparkle'; From 05857985f8b492b2dd591f1e597eed1625c42091 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 12 Mar 2018 21:29:03 +0100 Subject: [PATCH 0563/1335] add more tests for Email-ApiCommands Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/abstract.ApiParameter.php | 18 --- .../api/commands/class.EmailAccounts.php | 22 +-- tests/Emails/EmailsTest.php | 130 +++++++++++++++++- 3 files changed, 143 insertions(+), 27 deletions(-) diff --git a/lib/classes/api/abstract.ApiParameter.php b/lib/classes/api/abstract.ApiParameter.php index 749b4958..c51f4088 100644 --- a/lib/classes/api/abstract.ApiParameter.php +++ b/lib/classes/api/abstract.ApiParameter.php @@ -103,24 +103,6 @@ abstract class ApiParameter return $param_value; } - /** - * update value of parameter - * - * @param string $param - * @param mixed $value - * - * @throws Exception - * @return boolean - */ - protected function updateParam($param, $value = null) - { - if (isset($this->cmd_params[$param])) { - $this->cmd_params[$param] = $value; - return true; - } - throw new Exception("Unable to update parameter '" . $param . "' as it does not exist", 500); - } - /** * return list of all parameters * diff --git a/lib/classes/api/commands/class.EmailAccounts.php b/lib/classes/api/commands/class.EmailAccounts.php index b4f18b0f..ce40ea0d 100644 --- a/lib/classes/api/commands/class.EmailAccounts.php +++ b/lib/classes/api/commands/class.EmailAccounts.php @@ -317,15 +317,20 @@ class EmailAccounts extends ApiCommand implements ResourceEntity } } - if ($quota != $result['quota']) { - if ($customer['email_quota'] != '-1' && ($quota == 0 || ($quota + $customer['email_quota_used'] - $result['quota']) > $customer['email_quota'])) { - standard_error('allocatetoomuchquota', $quota, true); + if (Settings::Get('system.mail_quota_enabled') == 1) { + if ($quota != $result['quota']) { + if ($customer['email_quota'] != '-1' && ($quota == 0 || ($quota + $customer['email_quota_used'] - $result['quota']) > $customer['email_quota'])) { + standard_error('allocatetoomuchquota', $quota, true); + } + if (! empty($upd_query)) { + $upd_query .= ", "; + } + $upd_query .= "`quota` = :quota"; + $upd_params['quota'] = $quota; } - if (! empty($upd_query)) { - $upd_query .= ", "; - } - $upd_query .= "`quota` = :quota"; - $upd_params['quota'] = $quota; + } else { + // disable + $quota = 0; } // build update query @@ -416,6 +421,7 @@ class EmailAccounts extends ApiCommand implements ResourceEntity "id" => $id ); Database::pexecute($stmt, $params, true, true); + $result['popaccountid'] = 0; if (Settings::Get('system.mail_quota_enabled') == '1' && $customer['email_quota'] != '-1') { $quota = (int) $result['quota']; diff --git a/tests/Emails/EmailsTest.php b/tests/Emails/EmailsTest.php index 73287358..5ef1f0d8 100644 --- a/tests/Emails/EmailsTest.php +++ b/tests/Emails/EmailsTest.php @@ -222,7 +222,7 @@ class MailsTest extends TestCase /** * @depends testCustomerEmailForwardersAddAnother */ - public function testCustomerEmailForwardersDeleteunknown() + public function testCustomerEmailForwardersDeleteUnknown() { global $admin_userdata; @@ -240,4 +240,132 @@ class MailsTest extends TestCase $this->expectExceptionMessage("Unknown forwarder id"); EmailForwarders::getLocal($customer_userdata, $data)->delete(); } + + public function testCustomerEmailsListing() + { + global $admin_userdata; + + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + + $json_result = Emails::getLocal($customer_userdata)->listing(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals(2, $result['count']); + $this->assertEquals("info@test2.local", $result['list'][0]['email']); + $this->assertEquals("@test2.local", $result['list'][1]['email']); + } + + public function testCustomerEmailAccountsAdd() + { + global $admin_userdata; + + Settings::Set('panel.sendalternativemail', 1, true); + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + + $data = [ + 'emailaddr' => 'info@test2.local', + 'email_password' => generatePassword(), + 'alternative_email' => 'noone@example.com', + 'email_quota' => 1337 + ]; + $json_result = EmailAccounts::getLocal($customer_userdata, $data)->add(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals(1, $result['popaccountid']); + } + + public function testAdminEmailAccountsUpdate() + { + global $admin_userdata; + + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + + $data = [ + 'emailaddr' => 'info@test2.local', + 'email_password' => generatePassword(), + 'alternative_email' => 'noone@example.com', + 'email_quota' => 1338 + ]; + $json_result = EmailAccounts::getLocal($customer_userdata, $data)->update(); + $result = json_decode($json_result, true)['data']; + // quota is disabled + $this->assertEquals(0, $result['quota']); + } + + public function testAdminEmailAccountsUndefinedGet() + { + global $admin_userdata; + $this->expectExceptionCode(303); + EmailAccounts::getLocal($admin_userdata)->get(); + } + + public function testAdminEmailAccountsUndefinedListing() + { + global $admin_userdata; + $this->expectExceptionCode(303); + EmailAccounts::getLocal($admin_userdata)->listing(); + } + + public function testCustomerEmailAccountsDelete() + { + global $admin_userdata; + + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + + $data = [ + 'emailaddr' => 'info@test2.local', + 'delete_userfiles' => 1 + ]; + $json_result = EmailAccounts::getLocal($customer_userdata, $data)->delete(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals(0, $result['popaccountid']); + } + + public function testCustomerEmailsDelete() + { + global $admin_userdata; + + // remove possible existing delete tasks + Database::query("TRUNCATE `".TABLE_PANEL_TASKS."`"); + + Settings::Set('panel.sendalternativemail', 0, true); + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + + // add account + $data = [ + 'emailaddr' => 'info@test2.local', + 'email_password' => generatePassword(), + 'alternative_email' => 'noone@example.com' + ]; + $json_result = EmailAccounts::getLocal($customer_userdata, $data)->add(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals(2, $result['popaccountid']); + + // now delete the whole address + $data = [ + 'emailaddr' => 'info@test2.local', + 'delete_userfiles' => 1 + ]; + $json_result = Emails::getLocal($customer_userdata, $data)->delete(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals("info@test2.local", $result['email_full']); + } } From 309e613c83c6b09a620e762f28f79ad16d46c2da Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 12 Mar 2018 21:40:19 +0100 Subject: [PATCH 0564/1335] exclude some irritating pmd rules Signed-off-by: Michael Kaufmann (d00p) --- phpmd.xml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/phpmd.xml b/phpmd.xml index e9e42fd5..121e73d6 100644 --- a/phpmd.xml +++ b/phpmd.xml @@ -12,7 +12,9 @@ + + @@ -29,7 +31,14 @@ 1 - + + + + + + 1 + + From 52da7ad40f6bb2ed45c07d57d1267ddb78c8c080 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 12 Mar 2018 21:47:14 +0100 Subject: [PATCH 0565/1335] exclude some irritating pmd rules #2 Signed-off-by: Michael Kaufmann (d00p) --- phpmd.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/phpmd.xml b/phpmd.xml index 121e73d6..946e660e 100644 --- a/phpmd.xml +++ b/phpmd.xml @@ -13,6 +13,7 @@ + @@ -36,9 +37,8 @@ - 1 - + From ae3d95476642e320cb56e13ba9d7e9092bd2bd13 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 12 Mar 2018 22:07:51 +0100 Subject: [PATCH 0566/1335] started work on DirProtections-ApiCommand Signed-off-by: Michael Kaufmann (d00p) --- customer_extras.php | 31 ++- .../api/commands/class.DirProtections.php | 183 ++++++++++++++++++ 2 files changed, 196 insertions(+), 18 deletions(-) create mode 100644 lib/classes/api/commands/class.DirProtections.php diff --git a/customer_extras.php b/customer_extras.php index 5800805d..0bc41701 100644 --- a/customer_extras.php +++ b/customer_extras.php @@ -77,27 +77,22 @@ if ($page == 'overview') { eval("echo \"" . getTemplate("extras/htpasswds") . "\";"); } elseif ($action == 'delete' && $id != 0) { - $result_stmt = Database::prepare("SELECT * FROM `" . TABLE_PANEL_HTPASSWDS . "` - WHERE `customerid`= :customerid - AND `id`= :id"); - Database::pexecute($result_stmt, array( - "customerid" => $userinfo['customerid'], - "id" => $id - )); - $result = $result_stmt->fetch(PDO::FETCH_ASSOC); + try { + $json_result = DirProtections::getLocal($userinfo, array( + 'id' => $id + ))->get(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); + } + $result = json_decode($json_result, true)['data']; if (isset($result['username']) && $result['username'] != '') { if (isset($_POST['send']) && $_POST['send'] == 'send') { - $stmt = Database::prepare("DELETE FROM `" . TABLE_PANEL_HTPASSWDS . "` - WHERE `customerid`= :customerid - AND `id`= :id"); - Database::pexecute($stmt, array( - "customerid" => $userinfo['customerid'], - "id" => $id - )); - - $log->logAction(USR_ACTION, LOG_INFO, "deleted htpasswd for '" . $result['username'] . " (" . $result['path'] . ")'"); - inserttask('1'); + try { + DirProtections::getLocal($userinfo, $_POST)->delete(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); + } redirectTo($filename, array( 'page' => $page, 's' => $s diff --git a/lib/classes/api/commands/class.DirProtections.php b/lib/classes/api/commands/class.DirProtections.php new file mode 100644 index 00000000..6521fe62 --- /dev/null +++ b/lib/classes/api/commands/class.DirProtections.php @@ -0,0 +1,183 @@ + (2010-) + * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt + * @package API + * @since 0.10.0 + * + */ +class DirProtections extends ApiCommand implements ResourceEntity +{ + + public function add() + {} + + /** + * return a directory-protection entry by either id or username + * + * @param int $id + * optional, the customer-id + * @param string $username + * optional, the username + * + * @access admin, customer + * @throws Exception + * @return array + */ + public function get() + { + if ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'extras')) { + throw new Exception("You cannot access this resource", 405); + } + + $id = $this->getParam('id', true, 0); + $un_optional = ($id <= 0 ? false : true); + $username = $this->getParam('username', $un_optional, ''); + + $params = array(); + if ($this->isAdmin()) { + if ($this->getUserDetail('customers_see_all') == false) { + // if it's a reseller or an admin who cannot see all customers, we need to check + // whether the database belongs to one of his customers + $_custom_list_result = $this->apiCall('Customers.listing'); + $custom_list_result = $_custom_list_result['list']; + $customer_ids = array(); + foreach ($custom_list_result as $customer) { + $customer_ids[] = $customer['customerid']; + } + $result_stmt = Database::prepare(" + SELECT * FROM `" . TABLE_PANEL_HTPASSWDS . "` + WHERE `customerid` IN (:customerid) + AND (`id` = :idun OR `username` = :idun) + "); + $params['customerid'] = implode(", ", $customer_ids); + } else { + $result_stmt = Database::prepare(" + SELECT * FROM `" . TABLE_PANEL_HTPASSWDS . "` + WHERE (`id` = :idun OR `username` = :idun) + "); + } + } else { + if (Settings::IsInList('panel.customer_hide_options', 'extras.directoryprotection')) { + throw new Exception("You cannot access this resource", 405); + } + $result_stmt = Database::prepare(" + SELECT * FROM `" . TABLE_PANEL_HTPASSWDS . "` + WHERE `customerid` = :customerid + AND (`id` = :idun OR `username` = :idun) + "); + $params['customerid'] = $this->getUserDetail('customerid'); + } + $params['idun'] = ($id <= 0 ? $username : $id); + $result = Database::pexecute_first($result_stmt, $params, true, true); + if ($result) { + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_NOTICE, "[API] get directory protection for '" . $result['path'] . "'"); + return $this->response(200, "successfull", $result); + } + $key = ($id > 0 ? "id #" . $id : "username '" . $username . "'"); + throw new Exception("Directory protection with " . $key . " could not be found", 404); + } + + public function update() + {} + + /** + * list all directory-protections, if called from an admin, list all directory-protections of all customers you are allowed to view, or specify id or loginname for one specific customer + * + * @param int $customerid + * optional, admin-only, select directory-protections of a specific customer by id + * @param string $loginname + * optional, admin-only, select directory-protections of a specific customer by loginname + * + * @access admin, customer + * @throws Exception + * @return array count|list + */ + public function listing() + { + if ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'extras')) { + throw new Exception("You cannot access this resource", 405); + } + $customer_ids = $this->getAllowedCustomerIds('extras.directoryprotection'); + + $result_stmt = Database::prepare(" + SELECT * FROM `" . TABLE_PANEL_HTPASSWDS . "` + WHERE `customerid` IN (:customerids) + "); + Database::pexecute($result_stmt, array( + "customerids" => $customer_ids + ), true, true); + while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) { + $result[] = $row; + } + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_NOTICE, "[API] list directory-protections"); + return $this->response(200, "successfull", array( + 'count' => count($result), + 'list' => $result + )); + } + + /** + * delete a directory-protection by either id or username + * + * @param int $id + * optional, the ftp-user-id + * @param string $username + * optional, the username + * + * @access admin, customer + * @throws Exception + * @return array + */ + public function delete() + { + if ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'extras')) { + throw new Exception("You cannot access this resource", 405); + } + + $id = $this->getParam('id', true, 0); + $un_optional = ($id <= 0 ? false : true); + $username = $this->getParam('username', $un_optional, ''); + + if ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'extras.directoryprotection')) { + throw new Exception("You cannot access this resource", 405); + } + + // get ftp-user + $result = $this->apiCall('DirProtections.get', array( + 'id' => $id, + 'username' => $username + )); + $id = $result['id']; + + if ($this->isAdmin()) { + // get customer-data + $customer_data = $this->apiCall('Customers.get', array( + 'id' => $result['customerid'] + )); + } else { + $customer_data = $this->getUserData(); + } + + $stmt = Database::prepare(" + DELETE FROM `" . TABLE_PANEL_HTPASSWDS . "` WHERE `customerid`= :customerid AND `id`= :id + "); + Database::pexecute($stmt, array( + "customerid" => $customer_data['customerid'], + "id" => $id + )); + + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_INFO, "[API] deleted htpasswd for '" . $result['username'] . " (" . $result['path'] . ")'"); + inserttask('1'); + return $this->response(200, "successfull", $result); + } +} From 7e47383ee34baf0aef0b6a873b9c150ecb9d5ef7 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 12 Mar 2018 22:12:16 +0100 Subject: [PATCH 0567/1335] ignore NumberOfChildren pmd warning Signed-off-by: Michael Kaufmann (d00p) --- phpmd.xml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/phpmd.xml b/phpmd.xml index 946e660e..2f9d2bf5 100644 --- a/phpmd.xml +++ b/phpmd.xml @@ -7,7 +7,9 @@ froxlor ruleset. - + + + From 38b57117e2a495a911699d4d98c30a5731f4686c Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 13 Mar 2018 10:40:45 +0100 Subject: [PATCH 0568/1335] minor fixes in froxlor-sql file Signed-off-by: Michael Kaufmann (d00p) --- install/froxlor.sql | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/install/froxlor.sql b/install/froxlor.sql index 0876cffd..bdad8357 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -280,11 +280,11 @@ CREATE TABLE `panel_ipsandports` ( `vhostcontainer_servername_statement` tinyint(1) NOT NULL default '0', `specialsettings` text, `ssl` tinyint(4) NOT NULL default '0', - `ssl_cert_file` varchar(255) NOT NULL, - `ssl_key_file` varchar(255) NOT NULL, - `ssl_ca_file` varchar(255) NOT NULL, + `ssl_cert_file` varchar(255) NOT NULL default '', + `ssl_key_file` varchar(255) NOT NULL default '', + `ssl_ca_file` varchar(255) NOT NULL default '', `default_vhostconf_domain` text, - `ssl_cert_chainfile` varchar(255) NOT NULL, + `ssl_cert_chainfile` varchar(255) NOT NULL default '', `docroot` varchar(255) NOT NULL default '', PRIMARY KEY (`id`), UNIQUE KEY `ip_port` (`ip`,`port`) @@ -999,8 +999,8 @@ DROP TABLE IF EXISTS `domain_ssl_settings`; CREATE TABLE IF NOT EXISTS `domain_ssl_settings` ( `id` int(5) NOT NULL auto_increment, `domainid` int(11) NOT NULL, - `ssl_cert_file` mediumtext NOT NULL, - `ssl_key_file` mediumtext NOT NULL, + `ssl_cert_file` mediumtext, + `ssl_key_file` mediumtext, `ssl_ca_file` mediumtext, `ssl_cert_chainfile` mediumtext, `ssl_csr_file` mediumtext, From 3c802038f21dabcc558eb756a59b1bd6e280ede4 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Wed, 14 Mar 2018 11:35:03 +0100 Subject: [PATCH 0569/1335] split touch-command for multiple files into single ones, fixes #535 Signed-off-by: Michael Kaufmann (d00p) --- lib/configfiles/jessie.xml | 4 +++- lib/configfiles/precise.xml | 4 +++- lib/configfiles/stretch.xml | 4 +++- lib/configfiles/trusty.xml | 4 +++- lib/configfiles/wheezy.xml | 4 +++- 5 files changed, 15 insertions(+), 5 deletions(-) diff --git a/lib/configfiles/jessie.xml b/lib/configfiles/jessie.xml index 062cdb9b..fa60dcc2 100644 --- a/lib/configfiles/jessie.xml +++ b/lib/configfiles/jessie.xml @@ -4651,7 +4651,9 @@ aliases: files - + + + - + + + - + + + - + + + - + + + Date: Wed, 14 Mar 2018 11:37:45 +0100 Subject: [PATCH 0570/1335] rename handler php5-fastcgi to php-fastcgi, just cosmetics; fixes #536 Signed-off-by: Michael Kaufmann (d00p) --- scripts/jobs/cron_tasks.inc.http.10.apache.php | 4 ++-- scripts/jobs/cron_tasks.inc.http.15.apache_fcgid.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/jobs/cron_tasks.inc.http.10.apache.php b/scripts/jobs/cron_tasks.inc.http.10.apache.php index e49ef34e..21225125 100644 --- a/scripts/jobs/cron_tasks.inc.http.10.apache.php +++ b/scripts/jobs/cron_tasks.inc.http.10.apache.php @@ -360,8 +360,8 @@ class apache extends HttpConfigBase // start block, cut off last pipe and close block $filesmatch = '('.str_replace(".", "\.", substr($filesmatch, 0, -1)).')'; $this->virtualhosts_data[$vhosts_filename] .= ' '. "\n"; - $this->virtualhosts_data[$vhosts_filename] .= ' AddHandler php5-fastcgi .php' . "\n"; - $this->virtualhosts_data[$vhosts_filename] .= ' Action php5-fastcgi /fastcgiphp' . "\n"; + $this->virtualhosts_data[$vhosts_filename] .= ' AddHandler php-fastcgi .php' . "\n"; + $this->virtualhosts_data[$vhosts_filename] .= ' Action php-fastcgi /fastcgiphp' . "\n"; $this->virtualhosts_data[$vhosts_filename] .= ' Options +ExecCGI' . "\n"; $this->virtualhosts_data[$vhosts_filename] .= ' ' . "\n"; // >=apache-2.4 enabled? diff --git a/scripts/jobs/cron_tasks.inc.http.15.apache_fcgid.php b/scripts/jobs/cron_tasks.inc.http.15.apache_fcgid.php index acf49948..fb20d742 100644 --- a/scripts/jobs/cron_tasks.inc.http.15.apache_fcgid.php +++ b/scripts/jobs/cron_tasks.inc.http.15.apache_fcgid.php @@ -91,8 +91,8 @@ class apache_fcgid extends apache // start block, cut off last pipe and close block $filesmatch = '('.str_replace(".", "\.", substr($filesmatch, 0, -1)).')'; $php_options_text.= ' '. "\n"; - $php_options_text.= ' SetHandler php5-fastcgi'. "\n"; - $php_options_text.= ' Action php5-fastcgi /fastcgiphp' . "\n"; + $php_options_text.= ' SetHandler php-fastcgi'. "\n"; + $php_options_text.= ' Action php-fastcgi /fastcgiphp' . "\n"; $php_options_text.= ' Options +ExecCGI' . "\n"; $php_options_text.= ' ' . "\n"; // >=apache-2.4 enabled? From 616fb77de5176461adc7cde20455e753d662114f Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Wed, 14 Mar 2018 18:13:32 +0100 Subject: [PATCH 0571/1335] check for installed/configured froxlor in api.php and return 404 if not Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/api_includes.inc.php | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/lib/classes/api/api_includes.inc.php b/lib/classes/api/api_includes.inc.php index f7446b51..daea22b7 100644 --- a/lib/classes/api/api_includes.inc.php +++ b/lib/classes/api/api_includes.inc.php @@ -17,6 +17,32 @@ */ if (! defined('FROXLOR_INSTALL_DIR')) { define('FROXLOR_INSTALL_DIR', dirname(dirname(dirname(__DIR__)))); + // ensure that default timezone is set + if (function_exists("date_default_timezone_set") && function_exists("date_default_timezone_get")) { + @date_default_timezone_set(@date_default_timezone_get()); + } + $installed = true; + // check whether the userdata file exists + if (! @file_exists(FROXLOR_INSTALL_DIR . '/lib/userdata.inc.php')) { + $installed = false; + } + // check whether we can read the userdata file + if ($installed && ! @is_readable(FROXLOR_INSTALL_DIR . '/lib/userdata.inc.php')) { + $installed = false; + } + if ($installed) { + // include userdata for content-check + require FROXLOR_INSTALL_DIR . '/lib/userdata.inc.php'; + if (! isset($sql) || ! is_array($sql)) { + $installed = false; + } + } + // do not try to do anything if we have no installed/configured froxlor + if (! $installed) { + header("Status: 404 Not found", 404); + header($_SERVER["SERVER_PROTOCOL"] . " 404 Not found", 404); + exit(); + } require_once FROXLOR_INSTALL_DIR . '/lib/tables.inc.php'; require_once FROXLOR_INSTALL_DIR . '/lib/functions.php'; } From f2809c47ac7810f6ae344154b8bbf42785841227 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Wed, 14 Mar 2018 19:41:12 +0100 Subject: [PATCH 0572/1335] finished DirProtections.add() and some basic tests Signed-off-by: Michael Kaufmann (d00p) --- .../api/commands/class.DirProtections.php | 80 +++++++++++- phpunit.xml | 1 + tests/Customers/CustomersTest.php | 2 +- tests/Extras/ExtrasTest.php | 120 ++++++++++++++++++ 4 files changed, 201 insertions(+), 2 deletions(-) create mode 100644 tests/Extras/ExtrasTest.php diff --git a/lib/classes/api/commands/class.DirProtections.php b/lib/classes/api/commands/class.DirProtections.php index 6521fe62..c8b296a5 100644 --- a/lib/classes/api/commands/class.DirProtections.php +++ b/lib/classes/api/commands/class.DirProtections.php @@ -19,7 +19,85 @@ class DirProtections extends ApiCommand implements ResourceEntity { public function add() - {} + { + if ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'extras')) { + throw new Exception("You cannot access this resource", 405); + } + if ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'extras.directoryprotection')) { + throw new Exception("You cannot access this resource", 405); + } + + // get needed customer info to reduce the email-address-counter by one + $customer = $this->getCustomerData(); + + // required parameters + $path = $this->getParam('path'); + $username = $this->getParam('username'); + $password = $this->getParam('directory_password'); + + // parameters + $authname = $this->getParam('directory_authname', true, ''); + + // validation + $path = makeCorrectDir(validate($path, 'path', '', '', array(), true)); + $path = makeCorrectDir($customer['documentroot'] . '/' . $path); + $username = validate($username, 'username', '/^[a-zA-Z0-9][a-zA-Z0-9\-_]+\$?$/', '', array(), true); + $authname = validate($authname, 'directory_authname', '/^[a-zA-Z0-9][a-zA-Z0-9\-_ ]+\$?$/', '', array(), true); + validate($password, 'password', '', '', array(), true); + + // check for duplicate usernames for the path + $username_path_check_stmt = Database::prepare(" + SELECT `id`, `username`, `path` FROM `" . TABLE_PANEL_HTPASSWDS . "` + WHERE `username`= :username AND `path`= :path AND `customerid`= :customerid + "); + $params = array( + "username" => $username, + "path" => $path, + "customerid" => $customer['customerid'] + ); + $username_path_check = Database::pexecute_first($username_path_check_stmt, $params, true, true); + + // check whether we can used salted passwords + if (CRYPT_STD_DES == 1) { + $saltfordescrypt = substr(md5(uniqid(microtime(), 1)), 4, 2); + $password_enc = crypt($password, $saltfordescrypt); + } else { + $password_enc = crypt($password); + } + + // duplicate check + if ($username_path_check['username'] == $username && $username_path_check['path'] == $path) { + standard_error('userpathcombinationdupe', '', true); + } elseif ($password == $username) { + standard_error('passwordshouldnotbeusername', '', true); + } + + // insert the entry + $stmt = Database::prepare(" + INSERT INTO `" . TABLE_PANEL_HTPASSWDS . "` SET + `customerid` = :customerid, + `username` = :username, + `password` = :password, + `path` = :path, + `authname` = :authname + "); + $params = array( + "customerid" => $customer['customerid'], + "username" => $username, + "password" => $password_enc, + "path" => $path, + "authname" => $authname + ); + Database::pexecute($stmt, $params, true, true); + $id = Database::lastInsertId(); + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_INFO, "[API] added directory-protection for '" . $username . " (" . $path . ")'"); + inserttask('1'); + + $result = $this->apiCall('DirProtections.get', array( + 'id' => $id + )); + return $this->response(200, "successfull", $result); + } /** * return a directory-protection entry by either id or username diff --git a/phpunit.xml b/phpunit.xml index f1754d3b..3f800929 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -17,6 +17,7 @@ tests/Certificates tests/Ftps tests/Emails + tests/Extras diff --git a/tests/Customers/CustomersTest.php b/tests/Customers/CustomersTest.php index a127178e..7ca85690 100644 --- a/tests/Customers/CustomersTest.php +++ b/tests/Customers/CustomersTest.php @@ -268,7 +268,7 @@ class CustomersTest extends TestCase /** * @depends testAdminCustomersAdd */ - public function testResellerCustomersUpdateAllocateMore() + public function testResellerCustomersAddAllocateMore() { global $admin_userdata; // get reseller diff --git a/tests/Extras/ExtrasTest.php b/tests/Extras/ExtrasTest.php new file mode 100644 index 00000000..704f2281 --- /dev/null +++ b/tests/Extras/ExtrasTest.php @@ -0,0 +1,120 @@ + 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + + $data = [ + 'path' => '/test', + 'username' => 'testing', + 'directory_password' => generatePassword(), + 'directory_authname' => 'test1' + ]; + $json_result = DirProtections::getLocal($customer_userdata, $data)->add(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals($customer_userdata['documentroot'] . 'test/', $result['path']); + $this->assertEquals('test1', $result['authname']); + } + + public function testCustomerDirProtectionsAddSameUserPath() + { + global $admin_userdata; + + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + + $data = [ + 'path' => '/test', + 'username' => 'testing', + 'directory_password' => generatePassword(), + 'directory_authname' => 'test2' + ]; + $this->expectExceptionMessage("Combination of username and path already exists"); + DirProtections::getLocal($customer_userdata, $data)->add(); + } + + public function testCustomerDirProtectionsAddPasswordEqualsUsername() + { + global $admin_userdata; + + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + $up = generatePassword(); + $data = [ + 'path' => '/test', + 'username' => $up, + 'directory_password' => $up, + 'directory_authname' => 'test3' + ]; + $this->expectExceptionMessage("The password should not be the same as the username."); + DirProtections::getLocal($customer_userdata, $data)->add(); + } + + /** + * @depends testCustomerDirProtectionsAdd + */ + public function testAdminDirProtectionsGet() + { + global $admin_userdata; + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + $data = [ + 'username' => 'testing', + 'customerid' => 1 + ]; + $json_result = DirProtections::getLocal($admin_userdata, $data)->get(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals($customer_userdata['documentroot'] . 'test/', $result['path']); + $this->assertEquals('test1', $result['authname']); + } + + /** + * @depends testCustomerDirProtectionsAdd + */ + public function testResellerDirProtectionsGet() + { + global $admin_userdata; + // get customer + $json_result = Admins::getLocal($admin_userdata, array( + 'loginname' => 'reseller' + ))->get(); + $reseller_userdata = json_decode($json_result, true)['data']; + $reseller_userdata['adminsession'] = 1; + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + $data = [ + 'username' => 'testing' + ]; + $json_result = DirProtections::getLocal($reseller_userdata, $data)->get(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals($customer_userdata['documentroot'] . 'test/', $result['path']); + $this->assertEquals('test1', $result['authname']); + } +} From 858a9ba6a49704e75adc726425fd1f1c15f082cf Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 15 Mar 2018 11:01:17 +0100 Subject: [PATCH 0573/1335] added DirProtections.update() and various unit-tests Signed-off-by: Michael Kaufmann (d00p) --- customer_extras.php | 146 +++--------------- .../api/commands/class.DirProtections.php | 67 +++++++- tests/Extras/ExtrasTest.php | 69 +++++++++ 3 files changed, 158 insertions(+), 124 deletions(-) diff --git a/customer_extras.php b/customer_extras.php index 0bc41701..104d4b2c 100644 --- a/customer_extras.php +++ b/customer_extras.php @@ -111,74 +111,15 @@ if ($page == 'overview') { } } elseif ($action == 'add') { if (isset($_POST['send']) && $_POST['send'] == 'send') { - $path = makeCorrectDir(validate($_POST['path'], 'path')); - $userpath = $path; - $path = makeCorrectDir($userinfo['documentroot'] . '/' . $path); - $username = validate($_POST['username'], 'username', '/^[a-zA-Z0-9][a-zA-Z0-9\-_]+\$?$/'); - $authname = validate($_POST['directory_authname'], 'directory_authname', '/^[a-zA-Z0-9][a-zA-Z0-9\-_ ]+\$?$/'); - validate($_POST['directory_password'], 'password'); - - $username_path_check_stmt = Database::prepare("SELECT `id`, `username`, `path` FROM `" . TABLE_PANEL_HTPASSWDS . "` - WHERE `username`= :username - AND `path`= :path - AND `customerid`= :customerid"); - $params = array( - "username" => $username, - "path" => $path, - "customerid" => $userinfo['customerid'] - ); - Database::pexecute($username_path_check_stmt, $params); - $username_path_check = $username_path_check_stmt->fetch(PDO::FETCH_ASSOC); - - if (CRYPT_STD_DES == 1) { - $saltfordescrypt = substr(md5(uniqid(microtime(), 1)), 4, 2); - $password = crypt($_POST['directory_password'], $saltfordescrypt); - } else { - $password = crypt($_POST['directory_password']); - } - - if (! $_POST['path']) { - standard_error('invalidpath'); - } - - if ($username == '') { - standard_error(array( - 'stringisempty', - 'myloginname' - )); - } elseif ($username_path_check['username'] == $username && $username_path_check['path'] == $path) { - standard_error('userpathcombinationdupe'); - } elseif ($_POST['directory_password'] == '') { - standard_error(array( - 'stringisempty', - 'mypassword' - )); - } elseif ($path == '') { - standard_error('patherror'); - } elseif ($_POST['directory_password'] == $username) { - standard_error('passwordshouldnotbeusername'); - } else { - $stmt = Database::prepare("INSERT INTO `" . TABLE_PANEL_HTPASSWDS . "` SET - `customerid` = :customerid, - `username` = :username, - `password` = :password, - `path` = :path, - `authname` = :authname"); - $params = array( - "customerid" => $userinfo['customerid'], - "username" => $username, - "password" => $password, - "path" => $path, - "authname" => $authname - ); - Database::pexecute($stmt, $params); - $log->logAction(USR_ACTION, LOG_INFO, "added htpasswd for '" . $username . " (" . $path . ")'"); - inserttask('1'); - redirectTo($filename, array( - 'page' => $page, - 's' => $s - )); + try { + DirProtections::getLocal($userinfo, $_POST)->add(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); } + redirectTo($filename, array( + 'page' => $page, + 's' => $s + )); } else { $pathSelect = makePathfield($userinfo['documentroot'], $userinfo['guid'], $userinfo['guid']); @@ -191,65 +132,26 @@ if ($page == 'overview') { eval("echo \"" . getTemplate("extras/htpasswds_add") . "\";"); } } elseif ($action == 'edit' && $id != 0) { - $result_stmt = Database::prepare("SELECT * FROM `" . TABLE_PANEL_HTPASSWDS . "` - WHERE `customerid`= :customerid - AND `id`= :id"); - Database::pexecute($result_stmt, array( - "customerid" => $userinfo['customerid'], - "id" => $id - )); - $result = $result_stmt->fetch(PDO::FETCH_ASSOC); + try { + $json_result = DirProtections::getLocal($userinfo, array( + 'id' => $id + ))->get(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); + } + $result = json_decode($json_result, true)['data']; if (isset($result['username']) && $result['username'] != '') { if (isset($_POST['send']) && $_POST['send'] == 'send') { - validate($_POST['directory_password'], 'password'); - $authname = validate($_POST['directory_authname'], 'directory_authname', '/^[a-zA-Z0-9][a-zA-Z0-9\-_ ]+\$?$/'); - - if (CRYPT_STD_DES == 1) { - $saltfordescrypt = substr(md5(uniqid(microtime(), 1)), 4, 2); - $password = crypt($_POST['directory_password'], $saltfordescrypt); - } else { - $password = crypt($_POST['directory_password']); - } - - if ($_POST['directory_password'] == $result['username']) { - standard_error('passwordshouldnotbeusername'); - } - - $params = array( - "customerid" => $userinfo['customerid'], - "id" => $id - ); - - $pwd_sql = ''; - if ($_POST['directory_password'] != '') { - $pwd_sql = "`password`= :password "; - $params["password"] = $password; - } - - $auth_sql = ''; - if ($authname != $result['authname']) { - $auth_sql = "`authname`= :authname "; - $params["authname"] = $authname; - } - - if ($pwd_sql != '' || $auth_sql != '') { - if ($pwd_sql != '' && $auth_sql != '') { - $pwd_sql .= ', '; - } - - $stmt = Database::prepare("UPDATE `" . TABLE_PANEL_HTPASSWDS . "` - SET " . $pwd_sql . $auth_sql . " - WHERE `customerid`= :customerid - AND `id`= :id"); - Database::pexecute($stmt, $params); - $log->logAction(USR_ACTION, LOG_INFO, "edited htpasswd for '" . $result['username'] . " (" . $result['path'] . ")'"); - inserttask('1'); - redirectTo($filename, array( - 'page' => $page, - 's' => $s - )); + try { + DirProtections::getLocal($userinfo, $_POST)->update(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); } + redirectTo($filename, array( + 'page' => $page, + 's' => $s + )); } else { if (strpos($result['path'], $userinfo['documentroot']) === 0) { $result['path'] = str_replace($userinfo['documentroot'], "/", $result['path']); diff --git a/lib/classes/api/commands/class.DirProtections.php b/lib/classes/api/commands/class.DirProtections.php index c8b296a5..e654cd4b 100644 --- a/lib/classes/api/commands/class.DirProtections.php +++ b/lib/classes/api/commands/class.DirProtections.php @@ -166,7 +166,70 @@ class DirProtections extends ApiCommand implements ResourceEntity } public function update() - {} + { + $id = $this->getParam('id', true, 0); + $un_optional = ($id <= 0 ? false : true); + $username = $this->getParam('username', $un_optional, ''); + + // validation + $result = $this->apiCall('DirProtections.get', array( + 'id' => $id, + 'username' => $username + )); + $id = $result['id']; + + // parameters + $password = $this->getParam('directory_password', true, ''); + $authname = $this->getParam('directory_authname', true, $result['authname']); + + // get needed customer info + $customer = $this->getCustomerData(); + + // validation + $authname = validate($authname, 'directory_authname', '/^[a-zA-Z0-9][a-zA-Z0-9\-_ ]+\$?$/', '', array(), true); + validate($password, 'password', '', '', array(), true); + + $upd_query = ""; + $upd_params = array( + "id" => $result['id'], + "cid" => $customer['customerid'] + ); + if (! empty($password)) { + if ($password == $result['username']) { + standard_error('passwordshouldnotbeusername', '', true); + } + if (CRYPT_STD_DES == 1) { + $saltfordescrypt = substr(md5(uniqid(microtime(), 1)), 4, 2); + $password_enc = crypt($password, $saltfordescrypt); + } else { + $password_enc = crypt($password); + } + $upd_query .= "`password`= :password_enc"; + $upd_params['password_enc'] = $password_enc; + } + if ($authname != $result['authname']) { + if (! empty($upd_query)) { + $upd_query .= ", "; + } + $upd_query .= "`authname` = :authname"; + $upd_params['authname'] = $authname; + } + + // build update query + if (! empty($upd_query)) { + $upd_stmt = Database::prepare(" + UPDATE `" . TABLE_PANEL_HTPASSWDS . "` SET " . $upd_query . " WHERE `id` = :id AND `customerid`= :cid + "); + Database::pexecute($upd_stmt, $upd_params, true, true); + inserttask('1'); + } + + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_INFO, "[API] updated directory-protection '" . $result['username'] . " (" . $result['path'] . ")'"); + $result = $this->apiCall('DirProtections.get', array( + 'id' => $result['id'] + )); + return $this->response(200, "successfull", $result); + } /** * list all directory-protections, if called from an admin, list all directory-protections of all customers you are allowed to view, or specify id or loginname for one specific customer @@ -192,7 +255,7 @@ class DirProtections extends ApiCommand implements ResourceEntity WHERE `customerid` IN (:customerids) "); Database::pexecute($result_stmt, array( - "customerids" => $customer_ids + "customerids" => implode(', ', $customer_ids) ), true, true); while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) { $result[] = $row; diff --git a/tests/Extras/ExtrasTest.php b/tests/Extras/ExtrasTest.php index 704f2281..5e4f36cf 100644 --- a/tests/Extras/ExtrasTest.php +++ b/tests/Extras/ExtrasTest.php @@ -117,4 +117,73 @@ class ExtrasTest extends TestCase $this->assertEquals($customer_userdata['documentroot'] . 'test/', $result['path']); $this->assertEquals('test1', $result['authname']); } + + /** + * @depends testCustomerDirProtectionsAdd + */ + public function testCustomerDirProtectionsUpdate() + { + global $admin_userdata; + + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + + $json_result = DirProtections::getLocal($customer_userdata, array('id' => 1))->get(); + $data_old = json_decode($json_result, true)['data']; + + $data = [ + 'id' => 1, + 'directory_password' => generatePassword(), + 'directory_authname' => 'test1337' + ]; + $json_result = DirProtections::getLocal($customer_userdata, $data)->update(); + $result = json_decode($json_result, true)['data']; + $this->assertTrue($data_old['password'] != $result['password']); + $this->assertTrue($data_old['authname'] != $result['authname']); + $this->assertEquals('test1337', $result['authname']); + } + + /** + * @depends testCustomerDirProtectionsAdd + */ + public function testCustomerDirProtectionsList() + { + global $admin_userdata; + + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + + $json_result = DirProtections::getLocal($customer_userdata)->listing(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals(2, $result['count']); + $this->assertEquals('test1', $result['list'][0]['username']); + $this->assertEquals('testing', $result['list'][1]['username']); + } + + /** + * @depends testCustomerDirProtectionsList + */ + public function testCustomerDirProtectionsDelete() + { + global $admin_userdata; + + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + + DirProtections::getLocal($customer_userdata, array('username' => 'testing'))->delete(); + + $json_result = DirProtections::getLocal($customer_userdata)->listing(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals(1, $result['count']); + $this->assertEquals('test1', $result['list'][0]['username']); + } } From 7a68dfc45053bca3fdb72e5c8c5b62e8571caad8 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 15 Mar 2018 19:35:56 +0100 Subject: [PATCH 0574/1335] DirProtections and DirOptions stuff Signed-off-by: Michael Kaufmann (d00p) --- customer_extras.php | 176 ++------- lib/classes/api/commands/class.DirOptions.php | 357 ++++++++++++++++++ .../api/commands/class.DirProtections.php | 4 +- .../froxlor/function.CorrectErrorDocument.php | 9 +- 4 files changed, 396 insertions(+), 150 deletions(-) create mode 100644 lib/classes/api/commands/class.DirOptions.php diff --git a/customer_extras.php b/customer_extras.php index 104d4b2c..0124b9b8 100644 --- a/customer_extras.php +++ b/customer_extras.php @@ -223,42 +223,22 @@ if ($page == 'overview') { eval("echo \"" . getTemplate("extras/htaccess") . "\";"); } elseif ($action == 'delete' && $id != 0) { - $result_stmt = Database::prepare("SELECT * FROM `" . TABLE_PANEL_HTACCESS . "` - WHERE `customerid` = :customerid - AND `id` = :id"); - Database::pexecute($result_stmt, array( - "customerid" => $userinfo['customerid'], - "id" => $id - )); - $result = $result_stmt->fetch(PDO::FETCH_ASSOC); + try { + $json_result = DirOptions::getLocal($userinfo, array( + 'id' => $id + ))->get(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); + } + $result = json_decode($json_result, true)['data']; if (isset($result['customerid']) && $result['customerid'] != '' && $result['customerid'] == $userinfo['customerid']) { if (isset($_POST['send']) && $_POST['send'] == 'send') { - // do we have to remove the symlink and folder in suexecpath? - if ((int) Settings::Get('perl.suexecworkaround') == 1) { - $loginname = getCustomerDetail($result['customerid'], 'loginname'); - $suexecpath = makeCorrectDir(Settings::Get('perl.suexecpath') . '/' . $loginname . '/' . md5($result['path']) . '/'); - $perlsymlink = makeCorrectFile($result['path'] . '/cgi-bin'); - // remove symlink - if (file_exists($perlsymlink)) { - safe_exec('rm -f ' . escapeshellarg($perlsymlink)); - $log->logAction(USR_ACTION, LOG_DEBUG, "deleted suexecworkaround symlink '" . $perlsymlink . "'"); - } - // remove folder in suexec-path - if (file_exists($suexecpath)) { - safe_exec('rm -rf ' . escapeshellarg($suexecpath)); - $log->logAction(USR_ACTION, LOG_DEBUG, "deleted suexecworkaround path '" . $suexecpath . "'"); - } + try { + DirOptions::getLocal($userinfo, $_POST)->delete(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); } - $stmt = Database::prepare("DELETE FROM `" . TABLE_PANEL_HTACCESS . "` - WHERE `customerid`= :customerid - AND `id`= :id"); - Database::pexecute($stmt, array( - "customerid" => $userinfo['customerid'], - "id" => $id - )); - $log->logAction(USR_ACTION, LOG_INFO, "deleted htaccess for '" . str_replace($userinfo['documentroot'], '/', $result['path']) . "'"); - inserttask('1'); redirectTo($filename, array( 'page' => $page, 's' => $s @@ -273,74 +253,15 @@ if ($page == 'overview') { } } elseif ($action == 'add') { if (isset($_POST['send']) && $_POST['send'] == 'send') { - $path = makeCorrectDir(validate($_POST['path'], 'path')); - $userpath = $path; - $path = makeCorrectDir($userinfo['documentroot'] . '/' . $path); - $path_dupe_check_stmt = Database::prepare("SELECT `id`, `path` FROM `" . TABLE_PANEL_HTACCESS . "` - WHERE `path`= :path - AND `customerid`= :customerid"); - Database::pexecute($path_dupe_check_stmt, array( - "path" => $path, - "customerid" => $userinfo['customerid'] + try { + DirOptions::getLocal($userinfo, $_POST)->add(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); + } + redirectTo($filename, array( + 'page' => $page, + 's' => $s )); - $path_dupe_check = $path_dupe_check_stmt->fetch(PDO::FETCH_ASSOC); - - if (! $_POST['path']) { - standard_error('invalidpath'); - } - - if (isset($_POST['options_cgi']) && (int) $_POST['options_cgi'] != 0) { - $options_cgi = '1'; - } else { - $options_cgi = '0'; - } - - $error404path = ''; - if (isset($_POST['error404path'])) { - $error404path = correctErrorDocument($_POST['error404path']); - } - - $error403path = ''; - if (isset($_POST['error403path'])) { - $error403path = correctErrorDocument($_POST['error403path']); - } - - $error500path = ''; - if (isset($_POST['error500path'])) { - $error500path = correctErrorDocument($_POST['error500path']); - } - - if ($path_dupe_check['path'] == $path) { - standard_error('errordocpathdupe', $userpath); - } elseif ($path == '') { - standard_error('patherror'); - } else { - $stmt = Database::prepare('INSERT INTO `' . TABLE_PANEL_HTACCESS . '` SET - `customerid` = :customerid, - `path` = :path, - `options_indexes` = :options_indexes, - `error404path` = :error404path, - `error403path` = :error403path, - `error500path` = :error500path, - `options_cgi` = :options_cgi'); - $params = array( - "customerid" => $userinfo['customerid'], - "path" => $path, - "options_indexes" => $_POST['options_indexes'] == '1' ? '1' : '0', - "error403path" => $error403path, - "error404path" => $error404path, - "error500path" => $error500path, - "options_cgi" => $options_cgi - ); - Database::pexecute($stmt, $params); - - $log->logAction(USR_ACTION, LOG_INFO, "added htaccess for '" . $path . "'"); - inserttask('1'); - redirectTo($filename, array( - 'page' => $page, - 's' => $s - )); - } } else { $pathSelect = makePathfield($userinfo['documentroot'], $userinfo['guid'], $userinfo['guid']); $cperlenabled = customerHasPerlEnabled($userinfo['customerid']); @@ -354,55 +275,22 @@ if ($page == 'overview') { eval("echo \"" . getTemplate("extras/htaccess_add") . "\";"); } } elseif (($action == 'edit') && ($id != 0)) { - $result_stmt = Database::prepare("SELECT * FROM `" . TABLE_PANEL_HTACCESS . "` - WHERE `customerid` = :customerid - AND `id` = :id"); - Database::pexecute($result_stmt, array( - "customerid" => $userinfo['customerid'], - "id" => $id - )); - $result = $result_stmt->fetch(PDO::FETCH_ASSOC); + try { + $json_result = DirOptions::getLocal($userinfo, array( + 'id' => $id + ))->get(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); + } + $result = json_decode($json_result, true)['data']; if ((isset($result['customerid'])) && ($result['customerid'] != '') && ($result['customerid'] == $userinfo['customerid'])) { if (isset($_POST['send']) && $_POST['send'] == 'send') { - $option_indexes = intval($_POST['options_indexes']); - $options_cgi = isset($_POST['options_cgi']) ? intval($_POST['options_cgi']) : 0; - - if ($option_indexes != '1') { - $option_indexes = '0'; + try { + DirOptions::getLocal($userinfo, $_POST)->update(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); } - - if ($options_cgi != '1') { - $options_cgi = '0'; - } - - $error404path = correctErrorDocument($_POST['error404path']); - $error403path = correctErrorDocument($_POST['error403path']); - $error500path = correctErrorDocument($_POST['error500path']); - - if (($option_indexes != $result['options_indexes']) || ($error404path != $result['error404path']) || ($error403path != $result['error403path']) || ($error500path != $result['error500path']) || ($options_cgi != $result['options_cgi'])) { - inserttask('1'); - $stmt = Database::prepare("UPDATE `" . TABLE_PANEL_HTACCESS . "` - SET `options_indexes` = :options_indexes, - `error404path` = :error404path, - `error403path` = :error403path, - `error500path` = :error500path, - `options_cgi` = :options_cgi - WHERE `customerid` = :customerid - AND `id` = :id"); - $params = array( - "customerid" => $userinfo['customerid'], - "options_indexes" => $_POST['options_indexes'] == '1' ? '1' : '0', - "error403path" => $error403path, - "error404path" => $error404path, - "error500path" => $error500path, - "options_cgi" => $options_cgi, - "id" => $id - ); - Database::pexecute($stmt, $params); - $log->logAction(USR_ACTION, LOG_INFO, "edited htaccess for '" . str_replace($userinfo['documentroot'], '/', $result['path']) . "'"); - } - redirectTo($filename, array( 'page' => $page, 's' => $s diff --git a/lib/classes/api/commands/class.DirOptions.php b/lib/classes/api/commands/class.DirOptions.php new file mode 100644 index 00000000..8325f9f9 --- /dev/null +++ b/lib/classes/api/commands/class.DirOptions.php @@ -0,0 +1,357 @@ + (2010-) + * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt + * @package API + * @since 0.10.0 + * + */ +class DirOptions extends ApiCommand implements ResourceEntity +{ + + public function add() + { + if ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'extras')) { + throw new Exception("You cannot access this resource", 405); + } + if ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'extras.pathoptions')) { + throw new Exception("You cannot access this resource", 405); + } + + // get needed customer info to reduce the email-address-counter by one + $customer = $this->getCustomerData(); + + // required parameters + $path = $this->getParam('path'); + + // parameters + $options_indexes = $this->getParam('options_indexes', true, 0); + $options_cgi = $this->getParam('options_cgi', true, 0); + $error404path = $this->getParam('error404path', true, ''); + $error403path = $this->getParam('error403path', true, ''); + $error500path = $this->getParam('error500path', true, ''); + + // validation + $path = makeCorrectDir(validate($path, 'path', '', '', array(), true)); + $userpath = $path; + $path = makeCorrectDir($customer['documentroot'] . '/' . $path); + + if ($options_indexes != 0) { + $options_indexes = '1'; + } else { + $options_indexes = '0'; + } + + if ($options_cgi != 0) { + $options_cgi = '1'; + } else { + $options_cgi = '0'; + } + + if (! empty($error404path)) { + $error404path = correctErrorDocument($error404path, true); + } + + if (! empty($error403path)) { + $error403path = correctErrorDocument($error403path, true); + } + + if (! empty($error500path)) { + $error500path = correctErrorDocument($error500path, true); + } + + // check for duplicate path + $path_dupe_check_stmt = Database::prepare(" + SELECT `id`, `path` FROM `" . TABLE_PANEL_HTACCESS . "` + WHERE `path`= :path AND `customerid`= :customerid + "); + $path_dupe_check = Database::pexecute($path_dupe_check_stmt, array( + "path" => $path, + "customerid" => $customer['customerid'] + ), true, true); + + // duplicate check + if ($path_dupe_check['path'] == $path) { + standard_error('errordocpathdupe', $userpath, true); + } + + // insert the entry + $stmt = Database::prepare(' + INSERT INTO `' . TABLE_PANEL_HTACCESS . '` SET + `customerid` = :customerid, + `path` = :path, + `options_indexes` = :options_indexes, + `error404path` = :error404path, + `error403path` = :error403path, + `error500path` = :error500path, + `options_cgi` = :options_cgi + '); + $params = array( + "customerid" => $customer['customerid'], + "path" => $path, + "options_indexes" => $options_indexes, + "error403path" => $error403path, + "error404path" => $error404path, + "error500path" => $error500path, + "options_cgi" => $options_cgi + ); + Database::pexecute($stmt, $params, true, true); + $id = Database::lastInsertId(); + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_INFO, "[API] added directory-protection for '" . $username . " (" . $path . ")'"); + inserttask('1'); + + $result = $this->apiCall('DirOptions.get', array( + 'id' => $id + )); + return $this->response(200, "successfull", $result); + } + + /** + * return a directory-protection entry by either id or username + * + * @param int $id + * optional, the customer-id + * @param string $username + * optional, the username + * + * @access admin, customer + * @throws Exception + * @return array + */ + public function get() + { + if ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'extras')) { + throw new Exception("You cannot access this resource", 405); + } + + $id = $this->getParam('id', true, 0); + + $params = array(); + if ($this->isAdmin()) { + if ($this->getUserDetail('customers_see_all') == false) { + // if it's a reseller or an admin who cannot see all customers, we need to check + // whether the database belongs to one of his customers + $_custom_list_result = $this->apiCall('Customers.listing'); + $custom_list_result = $_custom_list_result['list']; + $customer_ids = array(); + foreach ($custom_list_result as $customer) { + $customer_ids[] = $customer['customerid']; + } + $result_stmt = Database::prepare(" + SELECT * FROM `" . TABLE_PANEL_HTACCESS . "` + WHERE `customerid` IN (:customerid) + AND `id` = :id + "); + $params['customerid'] = implode(", ", $customer_ids); + } else { + $result_stmt = Database::prepare(" + SELECT * FROM `" . TABLE_PANEL_HTACCESS . "` + WHERE `id` = :id + "); + } + } else { + if (Settings::IsInList('panel.customer_hide_options', 'extras.pathoptions')) { + throw new Exception("You cannot access this resource", 405); + } + $result_stmt = Database::prepare(" + SELECT * FROM `" . TABLE_PANEL_HTACCESS . "` + WHERE `customerid` = :customerid + AND `id` = :id + "); + $params['customerid'] = $this->getUserDetail('customerid'); + } + $params['id'] = $id; + $result = Database::pexecute_first($result_stmt, $params, true, true); + if ($result) { + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_NOTICE, "[API] get directory options for '" . $result['path'] . "'"); + return $this->response(200, "successfull", $result); + } + $key = "id #" . $id; + throw new Exception("Directory option with " . $key . " could not be found", 404); + } + + public function update() + { + $id = $this->getParam('id', true, 0); + + // validation + $result = $this->apiCall('DirOptions.get', array( + 'id' => $id + )); + + // get needed customer info to reduce the email-address-counter by one + $customer = $this->getCustomerData(); + + // parameters + $options_indexes = $this->getParam('options_indexes', true, $result['options_indexes']); + $options_cgi = $this->getParam('options_cgi', true, $result['options_cgi']); + $error404path = $this->getParam('error404path', true, $result['error404path']); + $error403path = $this->getParam('error403path', true, $result['error403path']); + $error500path = $this->getParam('error500path', true, $result['error500path']); + + if ($options_indexes != 0) { + $options_indexes = '1'; + } else { + $options_indexes = '0'; + } + + if ($options_cgi != 0) { + $options_cgi = '1'; + } else { + $options_cgi = '0'; + } + + if (! empty($error404path)) { + $error404path = correctErrorDocument($error404path, true); + } + + if (! empty($error403path)) { + $error403path = correctErrorDocument($error403path, true); + } + + if (! empty($error500path)) { + $error500path = correctErrorDocument($error500path, true); + } + + if (($option_indexes != $result['options_indexes']) || ($error404path != $result['error404path']) || ($error403path != $result['error403path']) || ($error500path != $result['error500path']) || ($options_cgi != $result['options_cgi'])) { + inserttask('1'); + $stmt = Database::prepare(" + UPDATE `" . TABLE_PANEL_HTACCESS . "` + SET `options_indexes` = :options_indexes, + `error404path` = :error404path, + `error403path` = :error403path, + `error500path` = :error500path, + `options_cgi` = :options_cgi + WHERE `customerid` = :customerid + AND `id` = :id + "); + $params = array( + "customerid" => $customer['customerid'], + "options_indexes" => $option_indexes, + "error403path" => $error403path, + "error404path" => $error404path, + "error500path" => $error500path, + "options_cgi" => $options_cgi, + "id" => $id + ); + Database::pexecute($stmt, $params, true, true); + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_INFO, "[API] edited directory options for '" . str_replace($customer['documentroot'], '/', $result['path']) . "'"); + } + + $result = $this->apiCall('DirOptions.get', array( + 'id' => $id + )); + return $this->response(200, "successfull", $result); + } + + /** + * list all directory-options, if called from an admin, list all directory-options of all customers you are allowed to view, or specify id or loginname for one specific customer + * + * @param int $customerid + * optional, admin-only, select directory-protections of a specific customer by id + * @param string $loginname + * optional, admin-only, select directory-protections of a specific customer by loginname + * + * @access admin, customer + * @throws Exception + * @return array count|list + */ + public function listing() + { + if ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'extras')) { + throw new Exception("You cannot access this resource", 405); + } + $customer_ids = $this->getAllowedCustomerIds('extras.pathoptions'); + + $result_stmt = Database::prepare(" + SELECT * FROM `" . TABLE_PANEL_HTACCESS . "` + WHERE `customerid` IN (:customerids) + "); + Database::pexecute($result_stmt, array( + "customerids" => implode(', ', $customer_ids) + ), true, true); + while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) { + $result[] = $row; + } + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_NOTICE, "[API] list directory-options"); + return $this->response(200, "successfull", array( + 'count' => count($result), + 'list' => $result + )); + } + + /** + * delete a directory-options by id + * + * @param int $id + * optional, the directory-option-id + * + * @access admin, customer + * @throws Exception + * @return array + */ + public function delete() + { + if ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'extras')) { + throw new Exception("You cannot access this resource", 405); + } + + $id = $this->getParam('id'); + + if ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'extras.pathoptions')) { + throw new Exception("You cannot access this resource", 405); + } + + // get directory-option + $result = $this->apiCall('DirOptions.get', array( + 'id' => $id + )); + + if ($this->isAdmin()) { + // get customer-data + $customer_data = $this->apiCall('Customers.get', array( + 'id' => $result['customerid'] + )); + } else { + $customer_data = $this->getUserData(); + } + + // do we have to remove the symlink and folder in suexecpath? + if ((int) Settings::Get('perl.suexecworkaround') == 1) { + $loginname = $customer_data['loginname']; + $suexecpath = makeCorrectDir(Settings::Get('perl.suexecpath') . '/' . $loginname . '/' . md5($result['path']) . '/'); + $perlsymlink = makeCorrectFile($result['path'] . '/cgi-bin'); + // remove symlink + if (file_exists($perlsymlink)) { + safe_exec('rm -f ' . escapeshellarg($perlsymlink)); + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_DEBUG, "[API] deleted suexecworkaround symlink '" . $perlsymlink . "'"); + } + // remove folder in suexec-path + if (file_exists($suexecpath)) { + safe_exec('rm -rf ' . escapeshellarg($suexecpath)); + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_DEBUG, "[API] deleted suexecworkaround path '" . $suexecpath . "'"); + } + } + $stmt = Database::prepare(" + DELETE FROM `" . TABLE_PANEL_HTACCESS . "` + WHERE `customerid`= :customerid + AND `id`= :id + "); + Database::pexecute($stmt, array( + "customerid" => $customer_data['customerid'], + "id" => $id + )); + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_INFO, "[API] deleted directory-option for '" . str_replace($userinfo['documentroot'], '/', $result['path']) . "'"); + inserttask('1'); + return $this->response(200, "successfull", $result); + } +} diff --git a/lib/classes/api/commands/class.DirProtections.php b/lib/classes/api/commands/class.DirProtections.php index e654cd4b..95a8e1bc 100644 --- a/lib/classes/api/commands/class.DirProtections.php +++ b/lib/classes/api/commands/class.DirProtections.php @@ -271,7 +271,7 @@ class DirProtections extends ApiCommand implements ResourceEntity * delete a directory-protection by either id or username * * @param int $id - * optional, the ftp-user-id + * optional, the directory-protection-id * @param string $username * optional, the username * @@ -293,7 +293,7 @@ class DirProtections extends ApiCommand implements ResourceEntity throw new Exception("You cannot access this resource", 405); } - // get ftp-user + // get directory protection $result = $this->apiCall('DirProtections.get', array( 'id' => $id, 'username' => $username diff --git a/lib/functions/froxlor/function.CorrectErrorDocument.php b/lib/functions/froxlor/function.CorrectErrorDocument.php index 7df4bf37..72c995d4 100644 --- a/lib/functions/froxlor/function.CorrectErrorDocument.php +++ b/lib/functions/froxlor/function.CorrectErrorDocument.php @@ -20,13 +20,14 @@ * refs #267 * * @param string error-document-string + * @param bool $throw_exception * * @return string error-document-string * */ -function correctErrorDocument($errdoc = null) { +function correctErrorDocument($errdoc = null, $throw_exception = false) { - global $idna_convert; + $idna_convert = new idna_convert_wrapper(); if ($errdoc !== null && $errdoc != '') { // not a URL @@ -46,14 +47,14 @@ function correctErrorDocument($errdoc = null) { else { // string won't work for lighty if (Settings::Get('system.webserver') == 'lighttpd') { - standard_error('stringerrordocumentnotvalidforlighty'); + standard_error('stringerrordocumentnotvalidforlighty', '', $throw_exception); } elseif(substr($errdoc, -1) != '"') { $errdoc .= '"'; } } } else { if (Settings::Get('system.webserver') == 'lighttpd') { - standard_error('urlerrordocumentnotvalidforlighty'); + standard_error('urlerrordocumentnotvalidforlighty', '', $throw_exception); } } } From be0099bf01d502943f341eb42f0b613eaac5ebb4 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Fri, 16 Mar 2018 09:18:02 +0100 Subject: [PATCH 0575/1335] add duplicate check to switch-server-ip script Signed-off-by: Michael Kaufmann (d00p) --- install/scripts/switch-server-ip.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/install/scripts/switch-server-ip.php b/install/scripts/switch-server-ip.php index b8fcbe4c..362d1add 100755 --- a/install/scripts/switch-server-ip.php +++ b/install/scripts/switch-server-ip.php @@ -164,6 +164,7 @@ class Action } if (count($ips_to_switch) > 0) { + $check_stmt = Database::prepare("SELECT `id` FROM panel_ipsandports WHERE `ip` = :newip"); $upd_stmt = Database::prepare("UPDATE panel_ipsandports SET `ip` = :newip WHERE `ip` = :oldip"); // system.ipaddress @@ -180,6 +181,13 @@ class Action foreach ($ips_to_switch as $ip_pair) { echo "Switching IP \033[1m" . $ip_pair[0] . "\033[0m to IP \033[1m" . $ip_pair[1] . "\033[0m" . PHP_EOL; + + $ip_check = Database::pexecute_first($check_stmt, array('newip' => $ip_pair[1])); + if ($ip_check) { + CmdLineHandler::printwarn("Note: " . $ip_pair[0] . " not updated to " . $ip_pair[1] . " - IP already exists in froxlor's database"); + continue; + } + Database::pexecute($upd_stmt, array( 'newip' => $ip_pair[1], 'oldip' => $ip_pair[0] @@ -235,6 +243,9 @@ class Action if (! file_exists(FROXLOR_INSTALL_DIR . '/lib/userdata.inc.php')) { throw new Exception("Could not find froxlor's userdata.inc.php file. You should use this script only with a fully installed and setup froxlor system."); } + require FROXLOR_INSTALL_DIR . '/lib/functions/filedir/function.makeSecurePath.php'; + require FROXLOR_INSTALL_DIR . '/lib/functions/filedir/function.makeCorrectDir.php'; + require FROXLOR_INSTALL_DIR . '/lib/functions/filedir/function.makeCorrectFile.php'; require FROXLOR_INSTALL_DIR . '/lib/classes/database/class.Database.php'; } From f5654d5931e96af03060ee3a3c8bf49b54bac90c Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sun, 18 Mar 2018 08:42:22 +0100 Subject: [PATCH 0576/1335] fix var-names in DirOptions-ApiCommand; fix pmd issues Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/abstract.ApiCommand.php | 7 +++++-- lib/classes/api/commands/class.DirOptions.php | 4 ++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/classes/api/abstract.ApiCommand.php b/lib/classes/api/abstract.ApiCommand.php index 77b3c58d..6f1351ca 100644 --- a/lib/classes/api/abstract.ApiCommand.php +++ b/lib/classes/api/abstract.ApiCommand.php @@ -119,7 +119,6 @@ abstract class ApiCommand extends ApiParameter } $this->initLang(); - $this->lng = $lng; $this->initMail(); if ($this->debug) { @@ -133,7 +132,8 @@ abstract class ApiCommand extends ApiParameter */ private function initLang() { - global $lng; + $lng = array(); + // query the whole table $result_stmt = Database::query("SELECT * FROM `" . TABLE_PANEL_LANGUAGE . "`"); @@ -174,6 +174,9 @@ abstract class ApiCommand extends ApiParameter // last but not least include language references file include_once makeSecurePath(FROXLOR_INSTALL_DIR . '/lng/lng_references.php'); + + // set array for ApiCommand + $this->lng = $lng; } /** diff --git a/lib/classes/api/commands/class.DirOptions.php b/lib/classes/api/commands/class.DirOptions.php index 8325f9f9..2bb75e83 100644 --- a/lib/classes/api/commands/class.DirOptions.php +++ b/lib/classes/api/commands/class.DirOptions.php @@ -106,7 +106,7 @@ class DirOptions extends ApiCommand implements ResourceEntity ); Database::pexecute($stmt, $params, true, true); $id = Database::lastInsertId(); - $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_INFO, "[API] added directory-protection for '" . $username . " (" . $path . ")'"); + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_INFO, "[API] added directory-option for '" . $userpath . "'"); inserttask('1'); $result = $this->apiCall('DirOptions.get', array( @@ -350,7 +350,7 @@ class DirOptions extends ApiCommand implements ResourceEntity "customerid" => $customer_data['customerid'], "id" => $id )); - $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_INFO, "[API] deleted directory-option for '" . str_replace($userinfo['documentroot'], '/', $result['path']) . "'"); + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_INFO, "[API] deleted directory-option for '" . str_replace($customer_data['documentroot'], '/', $result['path']) . "'"); inserttask('1'); return $this->response(200, "successfull", $result); } From dfcb7160cbee95c01615d1481b6160dfbf772a68 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sun, 18 Mar 2018 08:45:20 +0100 Subject: [PATCH 0577/1335] fix global lng-array Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/abstract.ApiCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/classes/api/abstract.ApiCommand.php b/lib/classes/api/abstract.ApiCommand.php index 6f1351ca..2806a1d6 100644 --- a/lib/classes/api/abstract.ApiCommand.php +++ b/lib/classes/api/abstract.ApiCommand.php @@ -132,7 +132,7 @@ abstract class ApiCommand extends ApiParameter */ private function initLang() { - $lng = array(); + global $lng; // query the whole table $result_stmt = Database::query("SELECT * FROM `" . TABLE_PANEL_LANGUAGE . "`"); From 715e5f7a642902d6aada70fbd7ddd863faf0f69b Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 19 Mar 2018 10:45:12 +0100 Subject: [PATCH 0578/1335] fix update of domain as admin if domain is a std-subdomain; fix update of mysql-entry; add CustomerBackups-ApiCommand Signed-off-by: Michael Kaufmann (d00p) --- customer_extras.php | 68 +++------ .../api/commands/class.CustomerBackups.php | 139 ++++++++++++++++++ lib/classes/api/commands/class.Domains.php | 3 +- lib/classes/api/commands/class.Mysqls.php | 12 +- 4 files changed, 159 insertions(+), 63 deletions(-) create mode 100644 lib/classes/api/commands/class.CustomerBackups.php diff --git a/customer_extras.php b/customer_extras.php index 0124b9b8..80bb01db 100644 --- a/customer_extras.php +++ b/customer_extras.php @@ -331,74 +331,42 @@ if ($page == 'overview') { { if ($action == 'abort' && isset($_POST['send']) && $_POST['send'] == 'send') { $log->logAction(USR_ACTION, LOG_NOTICE, "customer_extras::backup - aborted scheduled backupjob"); - $entry = isset($_POST['backup_job_entry']) ? (int)$_POST['backup_job_entry'] : 0; - if ($entry > 0) { - $del_stmt = Database::prepare("DELETE FROM `".TABLE_PANEL_TASKS."` WHERE `id` = :tid"); - Database::pexecute($del_stmt, array('tid' => $entry)); - standard_success('backupaborted'); + try { + CustomerBackups::getLocal($userinfo, $_POST)->delete(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); } + standard_success('backupaborted'); redirectTo($filename, array('page' => $page, 'action' => '', 's' => $s)); } if ($action == '') { $log->logAction(USR_ACTION, LOG_NOTICE, "viewed customer_extras::backup"); // check whether there is a backup-job for this customer - $sel_stmt = Database::prepare("SELECT * FROM `".TABLE_PANEL_TASKS."` WHERE `type` = '20'"); - Database::pexecute($sel_stmt); + try { + $json_result = CustomerBackups::getLocal($userinfo)->listing(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); + } + $result = json_decode($json_result, true)['data']; $existing_backupJob = null; - while ($entry = $sel_stmt->fetch()) + if ($result['count'] > 0) { - $data = unserialize($entry['data']); - if ($data['customerid'] == $userinfo['customerid']) { - $existing_backupJob = $entry; - break; - } + $existing_backupJob = array_shift($result['list']); } if (isset($_POST['send']) && $_POST['send'] == 'send') { - - if (! $_POST['path']) { - standard_error('invalidpath'); + try { + CustomerBackups::getLocal($userinfo, $_POST)->add(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); } - - $path = makeCorrectDir(validate($_POST['path'], 'path')); - $path = makeCorrectDir($userinfo['documentroot'] . '/' . $path); - - $backup_dbs = isset($_POST['backup_dbs']) ? intval($_POST['backup_dbs']) : 0; - $backup_mail = isset($_POST['backup_mail']) ? intval($_POST['backup_mail']) : 0; - $backup_web = isset($_POST['backup_web']) ? intval($_POST['backup_web']) : 0; - - if ($backup_dbs != '1') { - $backup_dbs = '0'; - } - - if ($backup_mail != '1') { - $backup_mail = '0'; - } - - if ($backup_web != '1') { - $backup_web = '0'; - } - - $task_data = array( - 'customerid' => $userinfo['customerid'], - 'uid' => $userinfo['guid'], - 'gid' => $userinfo['guid'], - 'loginname' => $userinfo['loginname'], - 'destdir' => $path, - 'backup_dbs' => $backup_dbs, - 'backup_mail' => $backup_mail, - 'backup_web' => $backup_web - ); - // schedule backup job - inserttask('20', $task_data); - standard_success('backupscheduled'); } else { if (!empty($existing_backupJob)) { $action = "abort"; - $row = unserialize($entry['data']); + $row = $existing_backupJob['data']; $row['path'] = makeCorrectDir(str_replace($userinfo['documentroot'], "/", $row['destdir'])); $row['backup_web'] = ($row['backup_web'] == '1') ? $lng['panel']['yes'] : $lng['panel']['no']; $row['backup_mail'] = ($row['backup_mail'] == '1') ? $lng['panel']['yes'] : $lng['panel']['no']; diff --git a/lib/classes/api/commands/class.CustomerBackups.php b/lib/classes/api/commands/class.CustomerBackups.php new file mode 100644 index 00000000..5744a954 --- /dev/null +++ b/lib/classes/api/commands/class.CustomerBackups.php @@ -0,0 +1,139 @@ + (2010-) + * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt + * @package API + * @since 0.10.0 + * + */ +class CustomerBackups extends ApiCommand implements ResourceEntity +{ + + private function validateAccess() + { + if (Settings::Get('system.backupenabled') != 1) { + throw new Exception("You cannot access this resource", 405); + } + if ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'extras')) { + throw new Exception("You cannot access this resource", 405); + } + if ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'extras.backup')) { + throw new Exception("You cannot access this resource", 405); + } + } + + public function add() + { + $this->validateAccess(); + + // required parameter + $path = $this->getParam('path'); + + // parameter + $backup_dbs = $this->getParam('backup_dbs', true, 0); + $backup_mail = $this->getParam('backup_mail', true, 0); + $backup_web = $this->getParam('backup_web', true, 0); + + // get customer data + $customer = $this->getCustomerData(); + + // validation + $path = makeCorrectDir(validate($path, 'path', '', '', array(), true)); + $userpath = $path; + $path = makeCorrectDir($customer['documentroot'] . '/' . $path); + + if ($backup_dbs != '1') { + $backup_dbs = '0'; + } + + if ($backup_mail != '1') { + $backup_mail = '0'; + } + + if ($backup_web != '1') { + $backup_web = '0'; + } + + $task_data = array( + 'customerid' => $customer['customerid'], + 'uid' => $customer['guid'], + 'gid' => $customer['guid'], + 'loginname' => $customer['loginname'], + 'destdir' => $path, + 'backup_dbs' => $backup_dbs, + 'backup_mail' => $backup_mail, + 'backup_web' => $backup_web + ); + // schedule backup job + inserttask('20', $task_data); + + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_NOTICE, "[API] added customer-backup job for '" . $customer['loginname'] . "'. Target directory: " . $userpath); + return $this->response(200, "successfull", $task_data); + } + + public function get() + { + throw new Exception('You cannot get a planned backup. Try CustomerBackups.listing()', 303); + } + + public function update() + { + throw new Exception('You cannot update a planned backup. You need to delete it and re-add it.', 303); + } + + public function listing() + { + $this->validateAccess(); + + $customer_ids = $this->getAllowedCustomerIds('extras.backup'); + + // check whether there is a backup-job for this customer + $sel_stmt = Database::prepare("SELECT * FROM `" . TABLE_PANEL_TASKS . "` WHERE `type` = '20'"); + Database::pexecute($sel_stmt); + $result = array(); + while ($entry = $sel_stmt->fetch(PDO::FETCH_ASSOC)) { + $entry['data'] = unserialize($entry['data']); + if (in_array($entry['data']['customerid'], $customer_ids)) { + $result[] = $entry; + } + } + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_NOTICE, "[API] list customer-backups"); + return $this->response(200, "successfull", array( + 'count' => count($result), + 'list' => $result + )); + } + + public function delete() + { + // get planned backups + $result = $this->apiCall('CustomerBackups.listing', $this->getParamList()); + + $entry = $this->getParam('backup_job_entry'); + $customer_ids = $this->getAllowedCustomerIds('extras.backup'); + + if ($result['count'] > 0 && $entry > 0) { + // prepare statement + $del_stmt = Database::prepare("DELETE FROM `" . TABLE_PANEL_TASKS . "` WHERE `id` = :tid"); + // check for the correct job + foreach ($result['list'] as $backupjob) { + if ($backupjob['id'] == $entry) { + Database::pexecute($del_stmt, array( + 'tid' => $entry + )); + return $this->response(200, "successfull", true); + } + } + } + throw new Exception('Backup job with id #' . $entry . ' could not be found', 404); + } +} diff --git a/lib/classes/api/commands/class.Domains.php b/lib/classes/api/commands/class.Domains.php index 364198a7..76f620c7 100644 --- a/lib/classes/api/commands/class.Domains.php +++ b/lib/classes/api/commands/class.Domains.php @@ -626,8 +626,7 @@ class Domains extends ApiCommand implements ResourceEntity // get requested domain $result = $this->apiCall('Domains.get', array( 'id' => $id, - 'domainname' => $domainname, - 'no_std_subdomain' => true + 'domainname' => $domainname )); $id = $result['id']; diff --git a/lib/classes/api/commands/class.Mysqls.php b/lib/classes/api/commands/class.Mysqls.php index a68f57eb..080b3e1a 100644 --- a/lib/classes/api/commands/class.Mysqls.php +++ b/lib/classes/api/commands/class.Mysqls.php @@ -305,17 +305,7 @@ class Mysqls extends ApiCommand implements ResourceEntity // validation $password = validate($password, 'password', '', '', array(), true); $databasedescription = validate(trim($databasedescription), 'description', '', '', array(), true); - - // validate whether the dbserver exists - $dbserver = validate($dbserver, html_entity_decode($this->lng['mysql']['mysql_server']), '', '', 0, true); - Database::needRoot(true, $dbserver); - Database::needSqlData(); - $sql_root = Database::getSqlData(); - Database::needRoot(false); - if (! isset($sql_root) || ! is_array($sql_root)) { - throw new ErrorException("Database server with index #" . $dbserver . " is unknown", 404); - } - + // get needed customer info to reduce the mysql-usage-counter by one $customer = $this->getCustomerData(); From bf589cdec8e58063824eb139945fe497a797ff47 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 19 Mar 2018 10:52:38 +0100 Subject: [PATCH 0579/1335] forgot to check for customer-id in CustomerBackups.delete() Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/commands/class.CustomerBackups.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/classes/api/commands/class.CustomerBackups.php b/lib/classes/api/commands/class.CustomerBackups.php index 5744a954..92cfd09a 100644 --- a/lib/classes/api/commands/class.CustomerBackups.php +++ b/lib/classes/api/commands/class.CustomerBackups.php @@ -126,7 +126,7 @@ class CustomerBackups extends ApiCommand implements ResourceEntity $del_stmt = Database::prepare("DELETE FROM `" . TABLE_PANEL_TASKS . "` WHERE `id` = :tid"); // check for the correct job foreach ($result['list'] as $backupjob) { - if ($backupjob['id'] == $entry) { + if ($backupjob['id'] == $entry && in_array($backupjob['data']['customerid'], $customer_ids)) { Database::pexecute($del_stmt, array( 'tid' => $entry )); From 975d46044d67a53e7523334cf5eca445c079d839 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 19 Mar 2018 20:38:59 +0100 Subject: [PATCH 0580/1335] added unit-tests for DirOptions Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/commands/class.DirOptions.php | 6 +- tests/Extras/DirOptionsTest.php | 184 ++++++++++++++++++ ...{ExtrasTest.php => DirProtectionsTest.php} | 2 +- 3 files changed, 188 insertions(+), 4 deletions(-) create mode 100644 tests/Extras/DirOptionsTest.php rename tests/Extras/{ExtrasTest.php => DirProtectionsTest.php} (99%) diff --git a/lib/classes/api/commands/class.DirOptions.php b/lib/classes/api/commands/class.DirOptions.php index 2bb75e83..957d1bdd 100644 --- a/lib/classes/api/commands/class.DirOptions.php +++ b/lib/classes/api/commands/class.DirOptions.php @@ -74,7 +74,7 @@ class DirOptions extends ApiCommand implements ResourceEntity SELECT `id`, `path` FROM `" . TABLE_PANEL_HTACCESS . "` WHERE `path`= :path AND `customerid`= :customerid "); - $path_dupe_check = Database::pexecute($path_dupe_check_stmt, array( + $path_dupe_check = Database::pexecute_first($path_dupe_check_stmt, array( "path" => $path, "customerid" => $customer['customerid'] ), true, true); @@ -222,7 +222,7 @@ class DirOptions extends ApiCommand implements ResourceEntity $error500path = correctErrorDocument($error500path, true); } - if (($option_indexes != $result['options_indexes']) || ($error404path != $result['error404path']) || ($error403path != $result['error403path']) || ($error500path != $result['error500path']) || ($options_cgi != $result['options_cgi'])) { + if (($options_indexes != $result['options_indexes']) || ($error404path != $result['error404path']) || ($error403path != $result['error403path']) || ($error500path != $result['error500path']) || ($options_cgi != $result['options_cgi'])) { inserttask('1'); $stmt = Database::prepare(" UPDATE `" . TABLE_PANEL_HTACCESS . "` @@ -236,7 +236,7 @@ class DirOptions extends ApiCommand implements ResourceEntity "); $params = array( "customerid" => $customer['customerid'], - "options_indexes" => $option_indexes, + "options_indexes" => $options_indexes, "error403path" => $error403path, "error404path" => $error404path, "error500path" => $error500path, diff --git a/tests/Extras/DirOptionsTest.php b/tests/Extras/DirOptionsTest.php new file mode 100644 index 00000000..fee67464 --- /dev/null +++ b/tests/Extras/DirOptionsTest.php @@ -0,0 +1,184 @@ + 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + + $data = [ + 'path' => '/test', + 'options_indexes' => 1, + 'options_cgi' => 1, + 'error404path' => '/404.html', + 'error403path' => '/403.html', + 'error500path' => '/500.html' + ]; + $json_result = DirOptions::getLocal($customer_userdata, $data)->add(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals($customer_userdata['documentroot'] . 'test/', $result['path']); + $this->assertEquals('1', $result['options_cgi']); + $this->assertEquals('/403.html', $result['error403path']); + } + + public function testCustomerDirOptionsAddDuplicatePath() + { + global $admin_userdata; + + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + + $data = [ + 'path' => '/test', + 'options_indexes' => 0, + 'options_cgi' => 0, + 'error404path' => '/404a.html', + 'error403path' => '/403a.html', + 'error500path' => '/500a.html' + ]; + $this->expectExceptionMessage("Option for path /test/ already exists"); + DirOptions::getLocal($customer_userdata, $data)->add(); + } + + public function testAdminDirOptionsGet() + { + global $admin_userdata; + + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + + $data = [ + 'id' => 1, + 'loginname' => 'test1' + ]; + $json_result = DirOptions::getLocal($admin_userdata, $data)->get(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals($customer_userdata['documentroot'] . 'test/', $result['path']); + } + + public function testResellerDirOptionsGet() + { + global $admin_userdata; + // get reseller + $json_result = Admins::getLocal($admin_userdata, array( + 'loginname' => 'reseller' + ))->get(); + $reseller_userdata = json_decode($json_result, true)['data']; + $reseller_userdata['adminsession'] = 1; + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + + $data = [ + 'id' => 1, + 'loginname' => 'test1' + ]; + $json_result = DirOptions::getLocal($reseller_userdata, $data)->get(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals($customer_userdata['documentroot'] . 'test/', $result['path']); + } + + public function testCustomerDirOptionsGetNotFound() + { + global $admin_userdata; + + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + + $data = [ + 'id' => 1337 + ]; + $this->expectExceptionMessage("Directory option with id #1337 could not be found"); + DirOptions::getLocal($admin_userdata, $data)->get(); + } + + public function testCustomerDirOptionsUpdate() + { + global $admin_userdata; + + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + + $data = [ + 'id' => 1, + 'options_indexes' => 0, + 'options_cgi' => 0, + 'error403path' => '/403-test.html' + ]; + $json_result = DirOptions::getLocal($customer_userdata, $data)->update(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals($customer_userdata['documentroot'] . 'test/', $result['path']); + $this->assertEquals('0', $result['options_cgi']); + $this->assertEquals('/403-test.html', $result['error403path']); + } + + public function testAdminDirOptionsList() + { + global $admin_userdata; + + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + + $json_result = DirOptions::getLocal($admin_userdata)->listing(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals(1, $result['count']); + $this->assertEquals($customer_userdata['documentroot'] . 'test/', $result['list'][0]['path']); + } + + /** + * @depends testAdminDirOptionsList + */ + public function testCustomerDirOptionsDelete() + { + global $admin_userdata; + + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + + $data = [ + 'id' => 1 + ]; + $json_result = DirOptions::getLocal($customer_userdata, $data)->delete(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals($customer_userdata['documentroot'] . 'test/', $result['path']); + + $data = [ + 'id' => 1 + ]; + $this->expectExceptionMessage("Directory option with id #1 could not be found"); + DirOptions::getLocal($admin_userdata, $data)->get(); + } +} diff --git a/tests/Extras/ExtrasTest.php b/tests/Extras/DirProtectionsTest.php similarity index 99% rename from tests/Extras/ExtrasTest.php rename to tests/Extras/DirProtectionsTest.php index 5e4f36cf..f4046143 100644 --- a/tests/Extras/ExtrasTest.php +++ b/tests/Extras/DirProtectionsTest.php @@ -6,7 +6,7 @@ use PHPUnit\Framework\TestCase; * @covers ApiParameter * @covers DirProtections */ -class ExtrasTest extends TestCase +class DirProtectionsTest extends TestCase { public function testCustomerDirProtectionsAdd() From 190c95bacae679b68265214166c5256045ac6c7e Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 19 Mar 2018 21:25:23 +0100 Subject: [PATCH 0581/1335] created DomainZones ApiCommand Signed-off-by: Michael Kaufmann (d00p) --- dns_editor.php | 256 ++----------- .../api/commands/class.DomainZones.php | 342 ++++++++++++++++++ tests/Extras/DirProtectionsTest.php | 2 +- 3 files changed, 372 insertions(+), 228 deletions(-) create mode 100644 lib/classes/api/commands/class.DomainZones.php diff --git a/dns_editor.php b/dns_editor.php index cd720fdc..7099d0cc 100644 --- a/dns_editor.php +++ b/dns_editor.php @@ -45,234 +45,31 @@ $success_message = ""; // action for adding a new entry if ($action == 'add_record' && ! empty($_POST)) { - - // validation - if (empty($record)) { - $record = "@"; - } - - $record = strtolower($record); - - if ($record != '@' && $record != '*') { - // validate record - if (strpos($record, '--') !== false) { - $errors[] = $lng['error']['domain_nopunycode']; - } else { - // check for wildcard-record - $add_wildcard_again = false; - if (substr($record, 0, 2) == '*.') { - $record = substr($record, 2); - $add_wildcard_again = true; - } - // convert entry - $record = $idna_convert->encode($record); - - if ($add_wildcard_again) { - $record = '*.'.$record; - } - - /* - * see https://redmine.froxlor.org/issues/1697 - * - if ($type != 'SRV' && $type != 'TXT') { - $check_dom = $record . '.example.com'; - if (! validateDomain($check_dom)) { - $errors[] = sprintf($lng['error']['subdomainiswrong'], $idna_convert->decode($record)); - } - } - */ - if (strlen($record) > 63) { - $errors[] = $lng['error']['dns_record_toolong']; - } - } - } - - // TODO regex validate content for invalid characters - - if ($ttl <= 0) { - $ttl = 18000; - } - - if (empty($content)) { - $errors[] = $lng['error']['dns_content_empty']; - } - - // types - if ($type == 'A' && filter_var($content, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) === false) { - $errors[] = $lng['error']['dns_arec_noipv4']; - } elseif ($type == 'AAAA' && filter_var($content, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false) { - $errors[] = $lng['error']['dns_aaaarec_noipv6']; - } elseif ($type == 'MX') { - if ($prio === null || $prio < 0) { - $errors[] = $lng['error']['dns_mx_prioempty']; - } - // check for trailing dot - if (substr($content, - 1) == '.') { - // remove it for checks - $content = substr($content, 0, - 1); - } - if (! validateDomain($content)) { - $errors[] = $lng['error']['dns_mx_needdom']; - } else { - // check whether there is a CNAME-record for the same resource - foreach ($dom_entries as $existing_entries) { - $fqdn = $existing_entries['record'] . '.' . $domain; - if ($existing_entries['type'] == 'CNAME' && $fqdn == $content) { - $errors[] = $lng['error']['dns_mx_noalias']; - break; - } - } - } - // append trailing dot (again) - $content .= '.'; - } elseif ($type == 'CNAME') { - // check for trailing dot - if (substr($content, - 1) == '.') { - // remove it for checks - $content = substr($content, 0, - 1); - } else { - // add domain name - $content .= '.' . $domain; - } - if (! validateDomain($content)) { - $errors[] = $lng['error']['dns_cname_invaliddom']; - } else { - // check whether there are RR-records for the same resource - foreach ($dom_entries as $existing_entries) { - if (($existing_entries['type'] == 'A' || $existing_entries['type'] == 'AAAA' || $existing_entries['type'] == 'MX' || $existing_entries['type'] == 'NS') && $existing_entries['record'] == $record) { - $errors[] = $lng['error']['dns_cname_nomorerr']; - break; - } - } - } - // append trailing dot (again) - $content .= '.'; - } elseif ($type == 'NS') { - // check for trailing dot - if (substr($content, - 1) == '.') { - // remove it for checks - $content = substr($content, 0, - 1); - } - if (! validateDomain($content)) { - $errors[] = $lng['error']['dns_ns_invaliddom']; - } - // append trailing dot (again) - $content .= '.'; - } elseif ($type == 'TXT' && ! empty($content)) { - // check that TXT content is enclosed in " " - $content = encloseTXTContent($content); - } elseif ($type == 'SRV') { - if ($prio === null || $prio < 0) { - $errors[] = $lng['error']['dns_srv_prioempty']; - } - // check only last part of content, as it can look like: - // _service._proto.name. TTL class SRV priority weight port target. - $_split_content = explode(" ", $content); - // SRV content must be [weight] [port] [target] - if (count($_split_content) != 3) { - $errors[] = $lng['error']['dns_srv_invalidcontent']; - } - $target = trim($_split_content[count($_split_content) - 1]); - if ($target != '.') { - // check for trailing dot - if (substr($target, - 1) == '.') { - // remove it for checks - $target = substr($target, 0, - 1); - } - } - if ($target != '.' && ! validateDomain($target)) { - $errors[] = $lng['error']['dns_srv_needdom']; - } else { - // check whether there is a CNAME-record for the same resource - foreach ($dom_entries as $existing_entries) { - $fqdn = $existing_entries['record'] . '.' . $domain; - if ($existing_entries['type'] == 'CNAME' && $fqdn == $target) { - $errors[] = $lng['error']['dns_srv_noalias']; - break; - } - } - } - // append trailing dot if there's none - if (substr($content, - 1) != '.') { - $content .= '.'; - } - } - - $new_entry = array( - 'record' => $record, - 'type' => $type, - 'prio' => $prio, - 'content' => $content, - 'ttl' => $ttl, - 'domain_id' => $domain_id - ); - ksort($new_entry); - - // check for duplicate - foreach ($dom_entries as $existing_entry) { - // compare serialized string of array - $check_entry = $existing_entry; - // new entry has no ID yet - unset($check_entry['id']); - // sort by key - ksort($check_entry); - // format integer fields to real integer (as they are read as string from the DB) - $check_entry['prio'] = (int) $check_entry['prio']; - $check_entry['ttl'] = (int) $check_entry['ttl']; - $check_entry['domain_id'] = (int) $check_entry['domain_id']; - // serialize both - $check_entry = serialize($check_entry); - $new = serialize($new_entry); - // compare - if ($check_entry === $new) { - $errors[] = $lng['error']['dns_duplicate_entry']; - unset($check_entry); - break; - } - } - - if (empty($errors)) { - $ins_stmt = Database::prepare(" - INSERT INTO `" . TABLE_DOMAIN_DNS . "` SET - `record` = :record, - `type` = :type, - `prio` = :prio, - `content` = :content, - `ttl` = :ttl, - `domain_id` = :domain_id - "); - - Database::pexecute($ins_stmt, $new_entry); - - $new_entry_id = Database::lastInsertId(); - - // add temporary to the entries-array (no reread of DB necessary) - $new_entry['id'] = $new_entry_id; - $dom_entries[] = $new_entry; - - // success message (inline) + try { + DomainZones::getLocal($userinfo, array( + 'id' => $domain_id, + 'record' => $record, + 'type' => $type, + 'prio' => $prio, + 'content' => $content, + 'ttl' => $ttl + ))->add(); $success_message = $lng['success']['dns_record_added']; - - $record = ""; - $type = 'A'; - $prio = ""; - $content = ""; - $ttl = ""; - - // re-generate bind configs - inserttask('4'); - } else { - // show $errors - $errors = implode("
    ", $errors); + } catch (Exception $e) { + dynamic_error($e->getMessage()); } } elseif ($action == 'delete') { // remove entry $entry_id = isset($_GET['id']) ? (int) $_GET['id'] : 0; if ($entry_id > 0) { - $del_stmt = Database::prepare("DELETE FROM `" . TABLE_DOMAIN_DNS . "` WHERE `id` = :id"); - Database::pexecute($del_stmt, array( - 'id' => $entry_id - )); + try { + DomainZones::getLocal($userinfo, array( + 'entry_id' => $entry_id, + 'id' => $domain_id + ))->delete(); + } catch (Exception $e) { + dynamic_error($e->getMessage()); + } // remove deleted entry from internal data array (no reread of DB necessary) $_t = $dom_entries; @@ -285,9 +82,6 @@ if ($action == 'add_record' && ! empty($_POST)) { unset($_t); // success message (inline) $success_message = $lng['success']['dns_record_deleted']; - - // re-generate bind configs - inserttask('4'); } } @@ -322,6 +116,14 @@ foreach ($type_select_values as $_type) { eval("\$record_list=\"" . getTemplate("dns_editor/list", true) . "\";"); -$zone = createDomainZone($domain_id); -$zonefile = (string) $zone; +try { + $json_result = DomainZones::getLocal($userinfo, array( + 'id' => $domain_id + ))->get(); +} catch (Exception $e) { + dynamic_error($e->getMessage()); +} +$result = json_decode($json_result, true)['data']; +$zonefile = implode("\n", $result); + eval("echo \"" . getTemplate("dns_editor/index", true) . "\";"); diff --git a/lib/classes/api/commands/class.DomainZones.php b/lib/classes/api/commands/class.DomainZones.php new file mode 100644 index 00000000..dbdacad6 --- /dev/null +++ b/lib/classes/api/commands/class.DomainZones.php @@ -0,0 +1,342 @@ + (2010-) + * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt + * @package API + * @since 0.10.0 + * + */ +class DomainZones extends ApiCommand implements ResourceEntity +{ + + public function add() + { + if (Settings::Get('system.dnsenabled') != '1') { + throw new Exception("DNS server not enabled on this system", 405); + } + + $id = $this->getParam('id', true, 0); + $dn_optional = ($id <= 0 ? false : true); + $domainname = $this->getParam('domainname', $dn_optional, ''); + + // get requested domain + $result = $this->apiCall('SubDomains.get', array( + 'id' => $id, + 'domainname' => $domainname + )); + $id = $result['id']; + + // parameters + $record = $this->getParam('record', true, null); + $type = $this->getParam('type', true, 'A'); + $prio = $this->getParam('prio', true, null); + $content = $this->getParam('content', true, null); + $ttl = $this->getParam('ttl', true, 18000); + + // validation + if ($result['isbinddomain'] != '1') { + standard_error('dns_domain_nodns', '', true); + } + + $idna_convert = new idna_convert_wrapper(); + $domain = $idna_convert->encode($result['domain']); + + // select all entries + $sel_stmt = Database::prepare("SELECT * FROM `" . TABLE_DOMAIN_DNS . "` WHERE domain_id = :did"); + Database::pexecute($sel_stmt, array( + 'did' => $id + ), true, true); + $dom_entries = $sel_stmt->fetchAll(PDO::FETCH_ASSOC); + + // validation + if (empty($record)) { + $record = "@"; + } + + $record = trim(strtolower($record)); + + if ($record != '@' && $record != '*') { + // validate record + if (strpos($record, '--') !== false) { + $errors[] = $this->lng['error']['domain_nopunycode']; + } else { + // check for wildcard-record + $add_wildcard_again = false; + if (substr($record, 0, 2) == '*.') { + $record = substr($record, 2); + $add_wildcard_again = true; + } + // convert entry + $record = $idna_convert->encode($record); + + if ($add_wildcard_again) { + $record = '*.' . $record; + } + + if (strlen($record) > 63) { + $errors[] = $this->lng['error']['dns_record_toolong']; + } + } + } + + // TODO regex validate content for invalid characters + + if ($ttl <= 0) { + $ttl = 18000; + } + + $content = trim($content); + if (empty($content)) { + $errors[] = $this->lng['error']['dns_content_empty']; + } + + // types + if ($type == 'A' && filter_var($content, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) === false) { + $errors[] = $this->lng['error']['dns_arec_noipv4']; + } elseif ($type == 'AAAA' && filter_var($content, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false) { + $errors[] = $this->lng['error']['dns_aaaarec_noipv6']; + } elseif ($type == 'MX') { + if ($prio === null || $prio < 0) { + $errors[] = $this->lng['error']['dns_mx_prioempty']; + } + // check for trailing dot + if (substr($content, - 1) == '.') { + // remove it for checks + $content = substr($content, 0, - 1); + } + if (! validateDomain($content)) { + $errors[] = $this->lng['error']['dns_mx_needdom']; + } else { + // check whether there is a CNAME-record for the same resource + foreach ($dom_entries as $existing_entries) { + $fqdn = $existing_entries['record'] . '.' . $domain; + if ($existing_entries['type'] == 'CNAME' && $fqdn == $content) { + $errors[] = $this->lng['error']['dns_mx_noalias']; + break; + } + } + } + // append trailing dot (again) + $content .= '.'; + } elseif ($type == 'CNAME') { + // check for trailing dot + if (substr($content, - 1) == '.') { + // remove it for checks + $content = substr($content, 0, - 1); + } else { + // add domain name + $content .= '.' . $domain; + } + if (! validateDomain($content)) { + $errors[] = $this->lng['error']['dns_cname_invaliddom']; + } else { + // check whether there are RR-records for the same resource + foreach ($dom_entries as $existing_entries) { + if (($existing_entries['type'] == 'A' || $existing_entries['type'] == 'AAAA' || $existing_entries['type'] == 'MX' || $existing_entries['type'] == 'NS') && $existing_entries['record'] == $record) { + $errors[] = $this->lng['error']['dns_cname_nomorerr']; + break; + } + } + } + // append trailing dot (again) + $content .= '.'; + } elseif ($type == 'NS') { + // check for trailing dot + if (substr($content, - 1) == '.') { + // remove it for checks + $content = substr($content, 0, - 1); + } + if (! validateDomain($content)) { + $errors[] = $this->lng['error']['dns_ns_invaliddom']; + } + // append trailing dot (again) + $content .= '.'; + } elseif ($type == 'TXT' && ! empty($content)) { + // check that TXT content is enclosed in " " + $content = encloseTXTContent($content); + } elseif ($type == 'SRV') { + if ($prio === null || $prio < 0) { + $errors[] = $this->lng['error']['dns_srv_prioempty']; + } + // check only last part of content, as it can look like: + // _service._proto.name. TTL class SRV priority weight port target. + $_split_content = explode(" ", $content); + // SRV content must be [weight] [port] [target] + if (count($_split_content) != 3) { + $errors[] = $this->lng['error']['dns_srv_invalidcontent']; + } + $target = trim($_split_content[count($_split_content) - 1]); + if ($target != '.') { + // check for trailing dot + if (substr($target, - 1) == '.') { + // remove it for checks + $target = substr($target, 0, - 1); + } + } + if ($target != '.' && ! validateDomain($target)) { + $errors[] = $this->lng['error']['dns_srv_needdom']; + } else { + // check whether there is a CNAME-record for the same resource + foreach ($dom_entries as $existing_entries) { + $fqdn = $existing_entries['record'] . '.' . $domain; + if ($existing_entries['type'] == 'CNAME' && $fqdn == $target) { + $errors[] = $this->lng['error']['dns_srv_noalias']; + break; + } + } + } + // append trailing dot if there's none + if (substr($content, - 1) != '.') { + $content .= '.'; + } + } + + $new_entry = array( + 'record' => $record, + 'type' => $type, + 'prio' => $prio, + 'content' => $content, + 'ttl' => $ttl, + 'domain_id' => $id + ); + ksort($new_entry); + + // check for duplicate + foreach ($dom_entries as $existing_entry) { + // compare serialized string of array + $check_entry = $existing_entry; + // new entry has no ID yet + unset($check_entry['id']); + // sort by key + ksort($check_entry); + // format integer fields to real integer (as they are read as string from the DB) + $check_entry['prio'] = (int) $check_entry['prio']; + $check_entry['ttl'] = (int) $check_entry['ttl']; + $check_entry['domain_id'] = (int) $check_entry['domain_id']; + // serialize both + $check_entry = serialize($check_entry); + $new = serialize($new_entry); + // compare + if ($check_entry === $new) { + $errors[] = $this->lng['error']['dns_duplicate_entry']; + unset($check_entry); + break; + } + } + + if (empty($errors)) { + $ins_stmt = Database::prepare(" + INSERT INTO `" . TABLE_DOMAIN_DNS . "` SET + `record` = :record, + `type` = :type, + `prio` = :prio, + `content` = :content, + `ttl` = :ttl, + `domain_id` = :domain_id + "); + Database::pexecute($ins_stmt, $new_entry, true, true); + $new_entry_id = Database::lastInsertId(); + + // add temporary to the entries-array (no reread of DB necessary) + $new_entry['id'] = $new_entry_id; + $dom_entries[] = $new_entry; + + // re-generate bind configs + inserttask('4'); + + $result = $this->apiCall('DomainZones.get', array( + 'id' => $id + )); + return $this->response(200, "successfull", $result); + } + // return $errors + throw new Exception(implode("\n", $errors)); + } + + /** + * return a domain-dns entry by either id or domainname + * + * @param int $id + * optional, the domain-id + * @param string $domainname + * optional, the domainname + * + * @access admin + * @throws Exception + * @return array + */ + public function get() + { + if (Settings::Get('system.dnsenabled') != '1') { + throw new Exception("DNS server not enabled on this system", 405); + } + + $id = $this->getParam('id', true, 0); + $dn_optional = ($id <= 0 ? false : true); + $domainname = $this->getParam('domainname', $dn_optional, ''); + + // get requested domain + $result = $this->apiCall('SubDomains.get', array( + 'id' => $id, + 'domainname' => $domainname + )); + $id = $result['id']; + + if ($result['isbinddomain'] != '1') { + standard_error('dns_domain_nodns', '', true); + } + + $zone = createDomainZone($id); + $zonefile = (string) $zone; + + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_NOTICE, "[API] get dns-zone for '" . $result['domain'] . "'"); + return $this->response(200, "successfull", explode("\n", $zonefile)); + } + + public function update() + { + throw new Exception('You cannot update a dns zone entry. You need to delete it and re-add it.', 303); + } + + public function listing() + { + throw new Exception('You cannot list dns zones. To get all domains use Domains.listing() or SubDomains.listing()', 303); + } + + public function delete() + { + if (Settings::Get('system.dnsenabled') != '1') { + throw new Exception("DNS server not enabled on this system", 405); + } + + $entry_id = $this->getParam('entry_id'); + $id = $this->getParam('id', true, 0); + $dn_optional = ($id <= 0 ? false : true); + $domainname = $this->getParam('domainname', $dn_optional, ''); + + // get requested domain + $result = $this->apiCall('SubDomains.get', array( + 'id' => $id, + 'domainname' => $domainname + )); + $id = $result['id']; + + $del_stmt = Database::prepare("DELETE FROM `" . TABLE_DOMAIN_DNS . "` WHERE `id` = :id AND `domain_id` = :did"); + Database::pexecute($del_stmt, array( + 'id' => $entry_id, + 'did' => $id + ), true, true); + // re-generate bind configs + inserttask('4'); + return $this->response(200, "successfull", true); + } +} diff --git a/tests/Extras/DirProtectionsTest.php b/tests/Extras/DirProtectionsTest.php index f4046143..23c440b6 100644 --- a/tests/Extras/DirProtectionsTest.php +++ b/tests/Extras/DirProtectionsTest.php @@ -98,7 +98,7 @@ class DirProtectionsTest extends TestCase public function testResellerDirProtectionsGet() { global $admin_userdata; - // get customer + // get reseller $json_result = Admins::getLocal($admin_userdata, array( 'loginname' => 'reseller' ))->get(); From 5123b5fccdcfa0a07ef0f2ae98f036d00cebf5f3 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 20 Mar 2018 08:55:14 +0100 Subject: [PATCH 0582/1335] fix error-display in dns_editor.php Signed-off-by: Michael Kaufmann (d00p) --- dns_editor.php | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/dns_editor.php b/dns_editor.php index 7099d0cc..3f731dcd 100644 --- a/dns_editor.php +++ b/dns_editor.php @@ -40,7 +40,7 @@ Database::pexecute($sel_stmt, array( )); $dom_entries = $sel_stmt->fetchAll(PDO::FETCH_ASSOC); -$errors = array(); +$errors = ""; $success_message = ""; // action for adding a new entry @@ -68,20 +68,22 @@ if ($action == 'add_record' && ! empty($_POST)) { 'id' => $domain_id ))->delete(); } catch (Exception $e) { - dynamic_error($e->getMessage()); + $errors = str_replace("\n", "
    ", $e->getMessage()); } - // remove deleted entry from internal data array (no reread of DB necessary) - $_t = $dom_entries; - foreach ($_t as $idx => $entry) { - if ($entry['id'] == $entry_id) { - unset($dom_entries[$idx]); - break; + if (empty($errors)) { + // remove deleted entry from internal data array (no reread of DB necessary) + $_t = $dom_entries; + foreach ($_t as $idx => $entry) { + if ($entry['id'] == $entry_id) { + unset($dom_entries[$idx]); + break; + } } + unset($_t); + // success message (inline) + $success_message = $lng['success']['dns_record_deleted']; } - unset($_t); - // success message (inline) - $success_message = $lng['success']['dns_record_deleted']; } } From bd7f2c26546a252a49c88fe69350aa2546474e63 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Wed, 21 Mar 2018 20:22:43 +0100 Subject: [PATCH 0583/1335] add unit-tests for CustomerBackup-ApiCommand Signed-off-by: Michael Kaufmann (d00p) --- .../api/commands/class.CustomerBackups.php | 5 +- phpunit.xml | 1 + tests/Backup/CustomerBackupsTest.php | 112 ++++++++++++++++++ 3 files changed, 116 insertions(+), 2 deletions(-) create mode 100644 tests/Backup/CustomerBackupsTest.php diff --git a/lib/classes/api/commands/class.CustomerBackups.php b/lib/classes/api/commands/class.CustomerBackups.php index 92cfd09a..46dd10e3 100644 --- a/lib/classes/api/commands/class.CustomerBackups.php +++ b/lib/classes/api/commands/class.CustomerBackups.php @@ -117,10 +117,10 @@ class CustomerBackups extends ApiCommand implements ResourceEntity { // get planned backups $result = $this->apiCall('CustomerBackups.listing', $this->getParamList()); - + $entry = $this->getParam('backup_job_entry'); $customer_ids = $this->getAllowedCustomerIds('extras.backup'); - + if ($result['count'] > 0 && $entry > 0) { // prepare statement $del_stmt = Database::prepare("DELETE FROM `" . TABLE_PANEL_TASKS . "` WHERE `id` = :tid"); @@ -130,6 +130,7 @@ class CustomerBackups extends ApiCommand implements ResourceEntity Database::pexecute($del_stmt, array( 'tid' => $entry )); + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_NOTICE, "[API] deleted planned customer-backup #" . $entry); return $this->response(200, "successfull", true); } } diff --git a/phpunit.xml b/phpunit.xml index 3f800929..33b73837 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -18,6 +18,7 @@ tests/Ftps tests/Emails tests/Extras + tests/Backup diff --git a/tests/Backup/CustomerBackupsTest.php b/tests/Backup/CustomerBackupsTest.php new file mode 100644 index 00000000..210b36eb --- /dev/null +++ b/tests/Backup/CustomerBackupsTest.php @@ -0,0 +1,112 @@ + 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + + $data = [ + 'path' => '/my-backup', + 'backup_dbs' => 1, + 'backup_mail' => 2, + 'backup_web' => 1 + ]; + $json_result = CustomerBackups::getLocal($customer_userdata, $data)->add(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals($customer_userdata['documentroot'] . 'my-backup/', $result['destdir']); + $this->assertEquals('1', $result['backup_dbs']); + $this->assertEquals('0', $result['backup_mail']); + $this->assertEquals('1', $result['backup_web']); + } + + public function testAdminCustomerBackupsGet() + { + global $admin_userdata; + $this->expectExceptionCode(303); + CustomerBackups::getLocal($admin_userdata)->get(); + } + + public function testAdminCustomerBackupsUpdate() + { + global $admin_userdata; + $this->expectExceptionCode(303); + CustomerBackups::getLocal($admin_userdata)->update(); + } + + /** + * + * @depends testCustomerCustomerBackupsAdd + */ + public function testAdminCustomerBackupsListing() + { + global $admin_userdata; + + $json_result = CustomerBackups::getLocal($admin_userdata)->listing(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals(1, $result['count']); + $this->assertEquals('1', $result['list'][0]['data']['backup_dbs']); + $this->assertEquals('0', $result['list'][0]['data']['backup_mail']); + $this->assertEquals('1', $result['list'][0]['data']['backup_web']); + } + + /** + * + * @depends testCustomerCustomerBackupsAdd + */ + public function testCustomerCustomerBackupsDelete() + { + global $admin_userdata; + + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + + $data = [ + 'backup_job_entry' => 1 + ]; + $json_result = CustomerBackups::getLocal($customer_userdata, $data)->delete(); + $result = json_decode($json_result, true)['data']; + $this->assertTrue($result); + } + + /** + * + * @depends testAdminCustomerBackupsListing + */ + public function testCustomerCustomerBackupsDeleteNotFound() + { + global $admin_userdata; + + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + + $data = [ + 'backup_job_entry' => 1337 + ]; + $this->expectExceptionCode(404); + $this->expectExceptionMessage('Backup job with id #1337 could not be found'); + CustomerBackups::getLocal($customer_userdata, $data)->delete(); + } +} From 48d71107790e7426278a5d499732ecaa925e582e Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 22 Mar 2018 14:56:18 +0100 Subject: [PATCH 0584/1335] add first unit tests for DomainZones ApiCommand Signed-off-by: Michael Kaufmann (d00p) --- .../api/commands/class.DomainZones.php | 22 ++++-- lib/classes/api/commands/class.SubDomains.php | 6 +- phpunit.xml | 1 + tests/DomainZones/DomainZonesTest.php | 68 +++++++++++++++++++ tests/Domains/DomainsTest.php | 3 +- 5 files changed, 90 insertions(+), 10 deletions(-) create mode 100644 tests/DomainZones/DomainZonesTest.php diff --git a/lib/classes/api/commands/class.DomainZones.php b/lib/classes/api/commands/class.DomainZones.php index dbdacad6..0a7dcdf3 100644 --- a/lib/classes/api/commands/class.DomainZones.php +++ b/lib/classes/api/commands/class.DomainZones.php @@ -42,8 +42,11 @@ class DomainZones extends ApiCommand implements ResourceEntity $content = $this->getParam('content', true, null); $ttl = $this->getParam('ttl', true, 18000); - // validation - if ($result['isbinddomain'] != '1') { + if ($result['parentdomainid'] != '0') { + throw new Exception("DNS zones can only be generated for the main domain, not for subdomains", 406); + } + + if ($result['subisbinddomain'] != '1') { standard_error('dns_domain_nodns', '', true); } @@ -291,7 +294,11 @@ class DomainZones extends ApiCommand implements ResourceEntity )); $id = $result['id']; - if ($result['isbinddomain'] != '1') { + if ($result['parentdomainid'] != '0') { + throw new Exception("DNS zones can only be generated for the main domain, not for subdomains", 406); + } + + if ($result['subisbinddomain'] != '1') { standard_error('dns_domain_nodns', '', true); } @@ -335,8 +342,11 @@ class DomainZones extends ApiCommand implements ResourceEntity 'id' => $entry_id, 'did' => $id ), true, true); - // re-generate bind configs - inserttask('4'); - return $this->response(200, "successfull", true); + if ($del_stmt->rowCount() > 0) { + // re-generate bind configs + inserttask('4'); + return $this->response(200, "successfull", true); + } + return $this->response(304, "successfull", true); } } diff --git a/lib/classes/api/commands/class.SubDomains.php b/lib/classes/api/commands/class.SubDomains.php index 9159587f..b7be8393 100644 --- a/lib/classes/api/commands/class.SubDomains.php +++ b/lib/classes/api/commands/class.SubDomains.php @@ -347,7 +347,7 @@ class SubDomains extends ApiCommand implements ResourceEntity } if (count($customer_ids) > 0) { $result_stmt = Database::prepare(" - SELECT d.*, pd.`subcanemaildomain` + SELECT d.*, pd.`subcanemaildomain`, pd.`isbinddomain` as subisbinddomain FROM `" . TABLE_PANEL_DOMAINS . "` d, `" . TABLE_PANEL_DOMAINS . "` pd WHERE " . ($id > 0 ? "d.`id` = :iddn" : "d.`domain` = :iddn") . " AND d.`customerid` IN (:customerids) AND ((d.`parentdomainid`!='0' AND pd.`id` = d.`parentdomainid`) OR (d.`parentdomainid`='0' AND pd.`id` = d.`id`)) @@ -361,7 +361,7 @@ class SubDomains extends ApiCommand implements ResourceEntity } } else { $result_stmt = Database::prepare(" - SELECT d.*, pd.`subcanemaildomain` + SELECT d.*, pd.`subcanemaildomain`, pd.`isbinddomain` as subisbinddomain FROM `" . TABLE_PANEL_DOMAINS . "` d, `" . TABLE_PANEL_DOMAINS . "` pd WHERE " . ($id > 0 ? "d.`id` = :iddn" : "d.`domain` = :iddn") . " AND ((d.`parentdomainid`!='0' AND pd.`id` = d.`parentdomainid`) OR (d.`parentdomainid`='0' AND pd.`id` = d.`id`)) @@ -375,7 +375,7 @@ class SubDomains extends ApiCommand implements ResourceEntity throw new Exception("You cannot access this resource", 405); } $result_stmt = Database::prepare(" - SELECT d.*, pd.`subcanemaildomain` + SELECT d.*, pd.`subcanemaildomain`, pd.`isbinddomain` as subisbinddomain FROM `" . TABLE_PANEL_DOMAINS . "` d, `" . TABLE_PANEL_DOMAINS . "` pd WHERE d.`customerid`= :customerid AND " . ($id > 0 ? "d.`id` = :iddn" : "d.`domain` = :iddn") . " AND ((d.`parentdomainid`!='0' AND pd.`id` = d.`parentdomainid`) OR (d.`parentdomainid`='0' AND pd.`id` = d.`id`)) diff --git a/phpunit.xml b/phpunit.xml index 33b73837..ceeb519c 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -19,6 +19,7 @@ tests/Emails tests/Extras tests/Backup + tests/DomainZones diff --git a/tests/DomainZones/DomainZonesTest.php b/tests/DomainZones/DomainZonesTest.php new file mode 100644 index 00000000..3833084f --- /dev/null +++ b/tests/DomainZones/DomainZonesTest.php @@ -0,0 +1,68 @@ + 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + + $data = [ + 'domainname' => 'test2.local' + ]; + $json_result = DomainZones::getLocal($customer_userdata, $data)->get(); + $result = json_decode($json_result, true)['data']; + $this->assertTrue(count($result) > 1); + $this->assertEquals('$ORIGIN test2.local.', $result[1]); + } + + public function testCustomerDomainZonesGetNoSubdomains() + { + global $admin_userdata; + + Settings::Set('system.dnsenabled', 1, true); + + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + + $data = [ + 'domainname' => 'mysub2.test2.local' + ]; + $this->expectExceptionCode(406); + $this->expectExceptionMessage("DNS zones can only be generated for the main domain, not for subdomains"); + DomainZones::getLocal($customer_userdata, $data)->get(); + } + + public function testAdminDomainZonesListing() + { + global $admin_userdata; + $this->expectExceptionCode(303); + DomainZones::getLocal($admin_userdata)->listing(); + } + + public function testAdminDomainZonesUpdate() + { + global $admin_userdata; + $this->expectExceptionCode(303); + DomainZones::getLocal($admin_userdata)->update(); + } +} diff --git a/tests/Domains/DomainsTest.php b/tests/Domains/DomainsTest.php index efc41901..4e899eb2 100644 --- a/tests/Domains/DomainsTest.php +++ b/tests/Domains/DomainsTest.php @@ -68,7 +68,8 @@ class DomainsTest extends TestCase $reseller_userdata['caneditphpsettings'] = 1; $data = [ 'domain' => 'test2.local', - 'customerid' => 1 + 'customerid' => 1, + 'isbinddomain' => 1 ]; // the reseller is not allowed to use the default ip/port $this->expectExceptionMessage("The ip/port combination you have chosen doesn't exist."); From c149cbacf7f13274ecae9ee4ee3743eac3cd2f26 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Fri, 23 Mar 2018 13:35:50 +0100 Subject: [PATCH 0585/1335] more DomainZones unit-tests Signed-off-by: Michael Kaufmann (d00p) --- .../api/commands/class.DomainZones.php | 10 +- tests/DomainZones/DomainZonesTest.php | 410 +++++++++++++++++- 2 files changed, 413 insertions(+), 7 deletions(-) diff --git a/lib/classes/api/commands/class.DomainZones.php b/lib/classes/api/commands/class.DomainZones.php index 0a7dcdf3..db01e93a 100644 --- a/lib/classes/api/commands/class.DomainZones.php +++ b/lib/classes/api/commands/class.DomainZones.php @@ -45,7 +45,7 @@ class DomainZones extends ApiCommand implements ResourceEntity if ($result['parentdomainid'] != '0') { throw new Exception("DNS zones can only be generated for the main domain, not for subdomains", 406); } - + if ($result['subisbinddomain'] != '1') { standard_error('dns_domain_nodns', '', true); } @@ -206,10 +206,10 @@ class DomainZones extends ApiCommand implements ResourceEntity $new_entry = array( 'record' => $record, 'type' => $type, - 'prio' => $prio, + 'prio' => (int) $prio, 'content' => $content, - 'ttl' => $ttl, - 'domain_id' => $id + 'ttl' => (int) $ttl, + 'domain_id' => (int) $id ); ksort($new_entry); @@ -297,7 +297,7 @@ class DomainZones extends ApiCommand implements ResourceEntity if ($result['parentdomainid'] != '0') { throw new Exception("DNS zones can only be generated for the main domain, not for subdomains", 406); } - + if ($result['subisbinddomain'] != '1') { standard_error('dns_domain_nodns', '', true); } diff --git a/tests/DomainZones/DomainZonesTest.php b/tests/DomainZones/DomainZonesTest.php index 3833084f..6e93881a 100644 --- a/tests/DomainZones/DomainZonesTest.php +++ b/tests/DomainZones/DomainZonesTest.php @@ -32,12 +32,13 @@ class DomainZonesTest extends TestCase $this->assertEquals('$ORIGIN test2.local.', $result[1]); } + /** + * @depends testCustomerDomainZonesGet + */ public function testCustomerDomainZonesGetNoSubdomains() { global $admin_userdata; - Settings::Set('system.dnsenabled', 1, true); - // get customer $json_result = Customers::getLocal($admin_userdata, array( 'loginname' => 'test1' @@ -65,4 +66,409 @@ class DomainZonesTest extends TestCase $this->expectExceptionCode(303); DomainZones::getLocal($admin_userdata)->update(); } + + /** + * @depends testCustomerDomainZonesGet + */ + public function testCustomerDomainZonesAddA() + { + global $admin_userdata; + + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + + $data = [ + 'domainname' => 'test2.local', + 'record' => 'www2', + 'type' => 'A', + 'content' => '127.0.0.1' + ]; + $json_result = DomainZones::getLocal($customer_userdata, $data)->add(); + $result = json_decode($json_result, true)['data']; + $this->assertTrue(count($result) > 1); + $found = false; + foreach ($result as $entry) { + if (substr($entry, 0, 4) == 'www2') { + $found = true; + break; + } + } + $this->assertTrue($found); + $this->assertEquals('www2 18000 IN A 127.0.0.1', $entry); + } + + /** + * @depends testCustomerDomainZonesAddA + */ + public function testCustomerDomainZonesAddAInvalid() + { + global $admin_userdata; + + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + + $data = [ + 'domainname' => 'test2.local', + 'record' => 'www3', + 'type' => 'A', + 'content' => 'a.b.c.d', + 'ttl' => -1 + ]; + $this->expectExceptionMessage("No valid IP address for A-record given"); + DomainZones::getLocal($customer_userdata, $data)->add(); + } + + /** + * @depends testCustomerDomainZonesAddA + */ + public function testCustomerDomainZonesAddADuplicate() + { + global $admin_userdata; + + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + + $data = [ + 'domainname' => 'test2.local', + 'record' => 'www2', + 'type' => 'A', + 'content' => '127.0.0.1', + 'ttl' => -1 + ]; + $this->expectExceptionMessage("Record already exists"); + DomainZones::getLocal($customer_userdata, $data)->add(); + } + + /** + * @depends testCustomerDomainZonesGet + */ + public function testCustomerDomainZonesAddAAAA() + { + global $admin_userdata; + + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + + $data = [ + 'domainname' => 'test2.local', + 'record' => 'www3', + 'type' => 'AAAA', + 'content' => '::1' + ]; + $json_result = DomainZones::getLocal($customer_userdata, $data)->add(); + $result = json_decode($json_result, true)['data']; + $this->assertTrue(count($result) > 1); + $found = false; + foreach ($result as $entry) { + if (substr($entry, 0, 4) == 'www3') { + $found = true; + break; + } + } + $this->assertTrue($found); + $this->assertEquals('www3 18000 IN AAAA ::1', $entry); + } + + /** + * @depends testCustomerDomainZonesAddA + */ + public function testCustomerDomainZonesAddAAAAInvalid() + { + global $admin_userdata; + + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + + $data = [ + 'domainname' => 'test2.local', + 'record' => 'www4', + 'type' => 'AAAA', + 'content' => 'z:z123.123', + 'ttl' => -1 + ]; + $this->expectExceptionMessage("No valid IP address for AAAA-record given"); + DomainZones::getLocal($customer_userdata, $data)->add(); + } + + public function testAdminDomainZonesAddMX() + { + global $admin_userdata; + + $data = [ + 'domainname' => 'test2.local', + 'record' => '', + 'type' => 'MX', + 'prio' => 10, + 'content' => 'mail.example.com.' + ]; + $json_result = DomainZones::getLocal($admin_userdata, $data)->add(); + $result = json_decode($json_result, true)['data']; + $this->assertTrue(count($result) > 1); + $found = false; + foreach ($result as $entry) { + if (substr($entry, strlen('mail.example.com.') * -1) == 'mail.example.com.') { + $found = true; + break; + } + } + $this->assertTrue($found); + $this->assertEquals('@ 18000 IN MX 10 mail.example.com.', $entry); + } + + /** + * @depends testAdminDomainZonesAddMX + */ + public function testAdminDomainZonesAddMXNoPrio() + { + global $admin_userdata; + + $data = [ + 'domainname' => 'test2.local', + 'record' => '', + 'type' => 'MX', + 'content' => 'mail.example.com.' + ]; + $this->expectExceptionMessage("Invalid MX priority given"); + DomainZones::getLocal($admin_userdata, $data)->add(); + } + + /** + * @depends testAdminDomainZonesAddMX + */ + public function testAdminDomainZonesAddMXInvalid() + { + global $admin_userdata; + + $data = [ + 'domainname' => 'test2.local', + 'record' => '', + 'type' => 'MX', + 'prio' => 20, + 'content' => 'localhost' + ]; + $this->expectExceptionMessage("The MX content value must be a valid domain-name"); + DomainZones::getLocal($admin_userdata, $data)->add(); + } + + public function testAdminDomainZonesAddCname() + { + global $admin_userdata; + + $data = [ + 'domainname' => 'test2.local', + 'record' => 'db', + 'type' => 'CNAME', + 'content' => 'db.example.com.' + ]; + $json_result = DomainZones::getLocal($admin_userdata, $data)->add(); + $result = json_decode($json_result, true)['data']; + $this->assertTrue(count($result) > 1); + $found = false; + foreach ($result as $entry) { + if (substr($entry, strlen('db.example.com.') * -1) == 'db.example.com.') { + $found = true; + break; + } + } + $this->assertTrue($found); + $this->assertEquals('db 18000 IN CNAME db.example.com.', $entry); + } + + public function testAdminDomainZonesAddCnameLocal() + { + global $admin_userdata; + + $data = [ + 'domainname' => 'test2.local', + 'record' => 'db', + 'type' => 'CNAME', + 'content' => 'db2' + ]; + $json_result = DomainZones::getLocal($admin_userdata, $data)->add(); + $result = json_decode($json_result, true)['data']; + $this->assertTrue(count($result) > 1); + $found = false; + foreach ($result as $entry) { + if (substr($entry, strlen('db2.test2.local.') * -1) == 'db2.test2.local.') { + $found = true; + break; + } + } + $this->assertTrue($found); + $this->assertEquals('db 18000 IN CNAME db2.test2.local.', $entry); + } + + /** + * @depends testAdminDomainZonesAddCname + */ + public function testAdminDomainZonesAddCnameInvalid() + { + global $admin_userdata; + + $data = [ + 'domainname' => 'test2.local', + 'record' => '', + 'type' => 'CNAME', + 'content' => 'localhost.' + ]; + $this->expectExceptionMessage("Invalid domain-name for CNAME record"); + DomainZones::getLocal($admin_userdata, $data)->add(); + } + + public function testAdminDomainZonesAddNS() + { + global $admin_userdata; + + $data = [ + 'domainname' => 'test2.local', + 'record' => '', + 'type' => 'NS', + 'content' => 'ns.example.com.' + ]; + $json_result = DomainZones::getLocal($admin_userdata, $data)->add(); + $result = json_decode($json_result, true)['data']; + $this->assertTrue(count($result) > 1); + $found = false; + foreach ($result as $entry) { + if (substr($entry, strlen('ns.example.com.') * -1) == 'ns.example.com.') { + $found = true; + break; + } + } + $this->assertTrue($found); + $this->assertEquals('@ 18000 IN NS ns.example.com.', $entry); + } + + public function testAdminDomainZonesAddNsInvalid() + { + global $admin_userdata; + + $data = [ + 'domainname' => 'test2.local', + 'record' => '', + 'type' => 'NS', + 'content' => 'localhost.' + ]; + $this->expectExceptionMessage("Invalid domain-name for NS record"); + DomainZones::getLocal($admin_userdata, $data)->add(); + } + + public function testAdminDomainZonesAddTXT() + { + global $admin_userdata; + + $data = [ + 'domainname' => 'test2.local', + 'record' => '_test1', + 'type' => 'TXT', + 'content' => 'aw yeah' + ]; + $json_result = DomainZones::getLocal($admin_userdata, $data)->add(); + $result = json_decode($json_result, true)['data']; + $this->assertTrue(count($result) > 1); + $found = false; + foreach ($result as $entry) { + if (substr($entry, 0, 6) == '_test1') { + $found = true; + break; + } + } + $this->assertTrue($found); + $this->assertEquals('_test1 18000 IN TXT aw yeah', $entry); + } + + public function testAdminDomainZonesAddSRV() + { + global $admin_userdata; + + $data = [ + 'domainname' => 'test2.local', + 'record' => '_test2', + 'type' => 'SRV', + 'prio' => 50, + 'content' => '2 1 srv.example.com.' + ]; + $json_result = DomainZones::getLocal($admin_userdata, $data)->add(); + $result = json_decode($json_result, true)['data']; + $this->assertTrue(count($result) > 1); + $found = false; + foreach ($result as $entry) { + if (substr($entry, 0, 6) == '_test2') { + $found = true; + break; + } + } + $this->assertTrue($found); + $this->assertEquals('_test2 18000 IN SRV 50 2 1 srv.example.com.', $entry); + } + + public function testAdminDomainZonesAddSrvInvalid() + { + global $admin_userdata; + + $data = [ + 'domainname' => 'test2.local', + 'record' => '_test2', + 'type' => 'SRV', + 'prio' => 50, + 'content' => 'srv.example.com.' + ]; + $this->expectExceptionMessage("Invalid SRV content, must contain of fields weight, port and target, e.g.: 5 5060 sipserver.example.com."); + DomainZones::getLocal($admin_userdata, $data)->add(); + } + + public function testCustomerDomainZonesDelete() + { + global $admin_userdata; + + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + + $data = [ + 'domainname' => 'test2.local', + 'entry_id' => 1 + ]; + $json_result = DomainZones::getLocal($customer_userdata, $data)->delete(); + $result = json_decode($json_result, true); + $this->assertTrue($result['data']); + $this->assertEquals(200, $result['status']); + } + + public function testCustomerDomainZonesDeleteUnmodified() + { + global $admin_userdata; + + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + + $data = [ + 'domainname' => 'test2.local', + 'entry_id' => 1337 + ]; + $json_result = DomainZones::getLocal($customer_userdata, $data)->delete(); + $result = json_decode($json_result, true); + $this->assertTrue($result['data']); + $this->assertEquals(304, $result['status']); + } } From 6006b16c9556428568f29eae1c2c63ea87e416a1 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sun, 25 Mar 2018 12:38:57 +0200 Subject: [PATCH 0586/1335] added first test for Mysqls-ApiCommand Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/commands/class.Mysqls.php | 12 ++++----- phpunit.xml | 1 + tests/Customers/CustomersTest.php | 6 ++--- tests/Mysqls/MysqlsTest.php | 33 +++++++++++++++++++++++ tests/bootstrap.php | 4 +++ 5 files changed, 47 insertions(+), 9 deletions(-) create mode 100644 tests/Mysqls/MysqlsTest.php diff --git a/lib/classes/api/commands/class.Mysqls.php b/lib/classes/api/commands/class.Mysqls.php index 080b3e1a..733b977a 100644 --- a/lib/classes/api/commands/class.Mysqls.php +++ b/lib/classes/api/commands/class.Mysqls.php @@ -139,11 +139,11 @@ class Mysqls extends ApiCommand implements ResourceEntity $_mailerror = false; try { - $this->mail->Subject = $mail_subject; - $this->mail->AltBody = $mail_body; - $this->mail->MsgHTML(str_replace("\n", "
    ", $mail_body)); - $this->mail->AddAddress($userinfo['email'], getCorrectUserSalutation($userinfo)); - $this->mail->Send(); + $this->mailer()->Subject = $mail_subject; + $this->mailer()->AltBody = $mail_body; + $this->mailer()->MsgHTML(str_replace("\n", "
    ", $mail_body)); + $this->mailer()->AddAddress($userinfo['email'], getCorrectUserSalutation($userinfo)); + $this->mailer()->Send(); } catch (phpmailerException $e) { $mailerr_msg = $e->errorMessage(); $_mailerror = true; @@ -157,7 +157,7 @@ class Mysqls extends ApiCommand implements ResourceEntity standard_error('errorsendingmail', $userinfo['email'], true); } - $this->mail->ClearAddresses(); + $this->mailer()->ClearAddresses(); } $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_WARNING, "[API] added mysql-database '" . $username . "'"); diff --git a/phpunit.xml b/phpunit.xml index ceeb519c..1ffdbece 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -20,6 +20,7 @@ tests/Extras tests/Backup tests/DomainZones + tests/Mysqls diff --git a/tests/Customers/CustomersTest.php b/tests/Customers/CustomersTest.php index 7ca85690..d9ea0450 100644 --- a/tests/Customers/CustomersTest.php +++ b/tests/Customers/CustomersTest.php @@ -15,7 +15,7 @@ class CustomersTest extends TestCase $data = [ 'new_loginname' => 'test1', - 'email' => 'test@froxlor.org', + 'email' => 'team@froxlor.org', 'firstname' => 'Test', 'name' => 'Testman', 'customernumber' => 1337, @@ -46,7 +46,7 @@ class CustomersTest extends TestCase $json_result = Customers::getLocal($admin_userdata, $data)->add(); $result = json_decode($json_result, true)['data']; $this->assertEquals(1, $result['customerid']); - $this->assertEquals('test@froxlor.org', $result['email']); + $this->assertEquals('team@froxlor.org', $result['email']); $this->assertEquals(1337, $result['customernumber']); $this->assertEquals(15, $result['subdomains']); $this->assertEquals('secret', $result['custom_notes']); @@ -147,7 +147,7 @@ class CustomersTest extends TestCase $result = json_decode($json_result, true)['data']; $this->assertEquals(1, $result['customerid']); - $this->assertEquals('test@froxlor.org', $result['email']); + $this->assertEquals('team@froxlor.org', $result['email']); $this->assertEquals(1337, $result['customernumber']); $this->assertEquals(15, $result['subdomains']); $this->assertEquals('Sparkle', $result['theme']); diff --git a/tests/Mysqls/MysqlsTest.php b/tests/Mysqls/MysqlsTest.php new file mode 100644 index 00000000..b4f78134 --- /dev/null +++ b/tests/Mysqls/MysqlsTest.php @@ -0,0 +1,33 @@ + 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + + $data = [ + 'mysql_password' => generatePassword(), + 'description' => 'testdb', + 'sendinfomail' => true + ]; + $json_result = Mysqls::getLocal($customer_userdata, $data)->add(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals('testdb', $result['description']); + $this->assertEquals(0, $result['dbserver']); + } +} diff --git a/tests/bootstrap.php b/tests/bootstrap.php index fcf14947..1016c4f5 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -25,6 +25,10 @@ file_put_contents($userdata, $userdata_content); // include autoloader / api / etc require dirname(__DIR__) . '/lib/classes/api/api_includes.inc.php'; +Database::needRoot(true); +Database::query("DROP DATABASE IF EXISTS `test1sql1`;"); +Database::needRoot(false); + // clear all tables Database::query("TRUNCATE TABLE `" . TABLE_PANEL_CUSTOMERS . "`;"); Database::query("TRUNCATE TABLE `" . TABLE_PANEL_DOMAINS . "`;"); From c98be3c04fbc7f36fc2d4680d7e98a4843da74cb Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 26 Mar 2018 09:53:09 +0200 Subject: [PATCH 0587/1335] finished unit-tests for Mysqls-ApiCommand Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/commands/class.Mysqls.php | 5 +- tests/Mysqls/MysqlsTest.php | 117 ++++++++++++++++++++++ tests/bootstrap.php | 1 + 3 files changed, 122 insertions(+), 1 deletion(-) diff --git a/lib/classes/api/commands/class.Mysqls.php b/lib/classes/api/commands/class.Mysqls.php index 733b977a..37091ccc 100644 --- a/lib/classes/api/commands/class.Mysqls.php +++ b/lib/classes/api/commands/class.Mysqls.php @@ -348,7 +348,10 @@ class Mysqls extends ApiCommand implements ResourceEntity Database::pexecute($stmt, $params, true, true); $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_WARNING, "[API] updated mysql-database '" . $result['databasename'] . "'"); - return $this->response(200, "successfull", $params); + $result = $this->apiCall('Mysqls.get', array( + 'dbname' => $result['databasename'] + )); + return $this->response(200, "successfull", $result); } /** diff --git a/tests/Mysqls/MysqlsTest.php b/tests/Mysqls/MysqlsTest.php index b4f78134..bd2a15f9 100644 --- a/tests/Mysqls/MysqlsTest.php +++ b/tests/Mysqls/MysqlsTest.php @@ -30,4 +30,121 @@ class MysqlsTest extends TestCase $this->assertEquals('testdb', $result['description']); $this->assertEquals(0, $result['dbserver']); } + + /** + * @depends testCustomerMysqlsAdd + */ + public function testAdminMysqlsGet() + { + global $admin_userdata; + + $json_result = Mysqls::getLocal($admin_userdata, array( + 'dbname' => 'test1sql1' + ))->get(); + $result = json_decode($json_result, true)['data']; + + $this->assertEquals('test1sql1', $result['databasename']); + $this->assertEquals('testdb', $result['description']); + } + + /** + * @depends testCustomerMysqlsAdd + */ + public function testResellerMysqlsGet() + { + global $admin_userdata; + // get reseller + $json_result = Admins::getLocal($admin_userdata, array( + 'loginname' => 'reseller' + ))->get(); + $reseller_userdata = json_decode($json_result, true)['data']; + $reseller_userdata['adminsession'] = 1; + $json_result = Mysqls::getLocal($reseller_userdata, array( + 'dbname' => 'test1sql1' + ))->get(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals('test1sql1', $result['databasename']); + $this->assertEquals('testdb', $result['description']); + } + + public function testCustomerMysqlsGetUnknown() + { + global $admin_userdata; + + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + + $data = [ + 'dbname' => 'test1sql5' + ]; + $this->expectExceptionCode(404); + $this->expectExceptionMessage("MySQL database with dbname 'test1sql5' could not be found"); + Mysqls::getLocal($customer_userdata, $data)->get(); + } + + /** + * @depends testCustomerMysqlsAdd + */ + public function testAdminMysqlsUpdate() + { + global $admin_userdata; + + $json_result = Mysqls::getLocal($admin_userdata, array( + 'dbname' => 'test1sql1' + ))->get(); + $old_db = json_decode($json_result, true)['data']; + + $data = [ + 'dbname' => 'test1sql1', + 'mysql_password' => generatePassword(), + 'description' => 'testdb-upd', + 'loginname' => 'test1' + ]; + $json_result = Mysqls::getLocal($admin_userdata, $data)->update(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals('testdb-upd', $result['description']); + } + + /** + * @depends testCustomerMysqlsAdd + */ + public function testCustomerMysqlsList() + { + global $admin_userdata; + + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + + $json_result = Mysqls::getLocal($customer_userdata)->listing(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals(1, $result['count']); + $this->assertEquals('test1sql1', $result['list'][0]['databasename']); + } + + /** + * @depends testCustomerMysqlsList + */ + public function testCustomerMysqlsDelete() + { + global $admin_userdata; + + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + + $data = [ + 'dbname' => 'test1sql1' + ]; + $json_result = Mysqls::getLocal($customer_userdata, $data)->delete(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals('test1sql1', $result['databasename']); + } } diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 1016c4f5..3d94f45e 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -54,6 +54,7 @@ Database::query("TRUNCATE TABLE `" . TABLE_PANEL_DOMAINREDIRECTS . "`;"); Database::query("TRUNCATE TABLE `" . TABLE_PANEL_ADMINS . "`;"); Database::query("TRUNCATE TABLE `" . TABLE_PANEL_IPSANDPORTS . "`;"); Database::query("TRUNCATE TABLE `" . TABLE_API_KEYS . "`;"); +Database::query("TRUNCATE TABLE `" . TABLE_PANEL_DATABASES . "`;"); // add superadmin Database::query("INSERT INTO `" . TABLE_PANEL_ADMINS . "` SET From efb416ae7c506efa15fcc550a9f28655d84f29f3 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 26 Mar 2018 10:13:51 +0200 Subject: [PATCH 0588/1335] phpdoc for Admins-ApiCommand Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/class.FroxlorRPC.php | 1 - lib/classes/api/commands/class.Admins.php | 165 +++++++++++++++++++-- lib/classes/api/commands/class.Froxlor.php | 2 +- 3 files changed, 157 insertions(+), 11 deletions(-) diff --git a/lib/classes/api/class.FroxlorRPC.php b/lib/classes/api/class.FroxlorRPC.php index 07d466fa..41b7aeef 100644 --- a/lib/classes/api/class.FroxlorRPC.php +++ b/lib/classes/api/class.FroxlorRPC.php @@ -105,7 +105,6 @@ class FroxlorRPC // it will recognize non-api classes+methods as valid commands $apiclass = FROXLOR_INSTALL_DIR . '/lib/classes/api/commands/class.' . $command[0] . '.php'; if (! file_exists($apiclass) || ! @method_exists($command[0], $command[1])) { - // there will be an exception from the autoloader for class_exists hence the try-catch-block throw new Exception("Unknown command", 400); } return array( diff --git a/lib/classes/api/commands/class.Admins.php b/lib/classes/api/commands/class.Admins.php index 9a24c02e..18382d7b 100644 --- a/lib/classes/api/commands/class.Admins.php +++ b/lib/classes/api/commands/class.Admins.php @@ -86,6 +86,77 @@ class Admins extends ApiCommand implements ResourceEntity /** * create a new admin user * + * @param string $name + * @param string $email + * @param string $admin_password + * optional, default auto-generated + * @param string $def_language + * optional, default is system-default language + * @param string $custom_notes + * optional, default empty + * @param bool $custom_notes_show + * optional, default false + * @param int $diskspace + * optional, default 0 + * @param bool $diskspace_ul + * optional, default false + * @param int $traffic + * optional, default 0 + * @param bool $traffic_ul + * optional, default false + * @param int $customers + * optional, default 0 + * @param bool $customers_ul + * optional, default false + * @param int $domains + * optional, default 0 + * @param bool $domains_ul + * optional, default false + * @param int $subdomains + * optional, default 0 + * @param bool $subdomains_ul + * optional, default false + * @param int $emails + * optional, default 0 + * @param bool $emails_ul + * optional, default false + * @param int $email_accounts + * optional, default 0 + * @param bool $email_accounts_ul + * optional, default false + * @param int $email_forwarders + * optional, default 0 + * @param bool $email_forwarders_ul + * optional, default false + * @param int $email_quota + * optional, default 0 + * @param bool $email_quota_ul + * optional, default false + * @param int $ftps + * optional, default 0 + * @param bool $ftps_ul + * optional, default false + * @param int $tickets + * optional, default 0 + * @param bool $tickets_ul + * optional, default false + * @param int $mysqls + * optional, default 0 + * @param bool $mysqls_ul + * optional, default false + * @param bool $customers_see_all + * optional, default false + * @param bool $domains_see_all + * optional, default false + * @param bool $tickets_see_all + * optional, default false + * @param bool $caneditphpsettings + * optional, default false + * @param bool $change_serversettings + * optional, default false + * @param array $ipaddress + * optional, list of ip-address id's; default -1 (all IP's) + * * @access admin * @throws Exception * @return array @@ -301,6 +372,82 @@ class Admins extends ApiCommand implements ResourceEntity * optional, the admin-id * @param string $loginname * optional, the loginname + * @param string $name + * optional + * @param string $email + * optional + * @param string $admin_password + * optional, default auto-generated + * @param string $def_language + * optional, default is system-default language + * @param string $custom_notes + * optional, default empty + * @param string $theme + * optional + * @param bool $deactivated + * optional, default false + * @param bool $custom_notes_show + * optional, default false + * @param int $diskspace + * optional, default 0 + * @param bool $diskspace_ul + * optional, default false + * @param int $traffic + * optional, default 0 + * @param bool $traffic_ul + * optional, default false + * @param int $customers + * optional, default 0 + * @param bool $customers_ul + * optional, default false + * @param int $domains + * optional, default 0 + * @param bool $domains_ul + * optional, default false + * @param int $subdomains + * optional, default 0 + * @param bool $subdomains_ul + * optional, default false + * @param int $emails + * optional, default 0 + * @param bool $emails_ul + * optional, default false + * @param int $email_accounts + * optional, default 0 + * @param bool $email_accounts_ul + * optional, default false + * @param int $email_forwarders + * optional, default 0 + * @param bool $email_forwarders_ul + * optional, default false + * @param int $email_quota + * optional, default 0 + * @param bool $email_quota_ul + * optional, default false + * @param int $ftps + * optional, default 0 + * @param bool $ftps_ul + * optional, default false + * @param int $tickets + * optional, default 0 + * @param bool $tickets_ul + * optional, default false + * @param int $mysqls + * optional, default 0 + * @param bool $mysqls_ul + * optional, default false + * @param bool $customers_see_all + * optional, default false + * @param bool $domains_see_all + * optional, default false + * @param bool $tickets_see_all + * optional, default false + * @param bool $caneditphpsettings + * optional, default false + * @param bool $change_serversettings + * optional, default false + * @param array $ipaddress + * optional, list of ip-address id's; default -1 (all IP's) * * @access admin * @throws Exception @@ -313,7 +460,7 @@ class Admins extends ApiCommand implements ResourceEntity $id = $this->getParam('id', true, 0); $ln_optional = ($id <= 0 ? false : true); $loginname = $this->getParam('loginname', $ln_optional, ''); - + $result = $this->apiCall('Admins.get', array( 'id' => $id, 'loginname' => $loginname @@ -579,7 +726,7 @@ class Admins extends ApiCommand implements ResourceEntity $id = $this->getParam('id', true, 0); $ln_optional = ($id <= 0 ? false : true); $loginname = $this->getParam('loginname', $ln_optional, ''); - + $result = $this->apiCall('Admins.get', array( 'id' => $id, 'loginname' => $loginname @@ -598,7 +745,7 @@ class Admins extends ApiCommand implements ResourceEntity Database::pexecute($del_stmt, array( 'adminid' => $id ), true, true); - + // delete the traffic-usage $del_stmt = Database::prepare(" DELETE FROM `" . TABLE_PANEL_TRAFFIC_ADMINS . "` WHERE `adminid` = :adminid @@ -606,7 +753,7 @@ class Admins extends ApiCommand implements ResourceEntity Database::pexecute($del_stmt, array( 'adminid' => $id ), true, true); - + // delete the diskspace usage $del_stmt = Database::prepare(" DELETE FROM `" . TABLE_PANEL_DISKSPACE_ADMINS . "` WHERE `adminid` = :adminid @@ -614,7 +761,7 @@ class Admins extends ApiCommand implements ResourceEntity Database::pexecute($del_stmt, array( 'adminid' => $id ), true, true); - + // set admin-id of the old admin's customer to current admins $upd_stmt = Database::prepare(" UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET @@ -624,7 +771,7 @@ class Admins extends ApiCommand implements ResourceEntity 'userid' => $this->getUserDetail('adminid'), 'adminid' => $id ), true, true); - + // set admin-id of the old admin's domains to current admins $upd_stmt = Database::prepare(" UPDATE `" . TABLE_PANEL_DOMAINS . "` SET @@ -634,7 +781,7 @@ class Admins extends ApiCommand implements ResourceEntity 'userid' => $this->getUserDetail('adminid'), 'adminid' => $id ), true, true); - + // delete old admin's api keys if exists (no customer keys) $upd_stmt = Database::prepare(" DELETE FROM `" . TABLE_API_KEYS . "` WHERE @@ -643,7 +790,7 @@ class Admins extends ApiCommand implements ResourceEntity Database::pexecute($upd_stmt, array( 'adminid' => $id ), true, true); - + // set admin-id of the old admin's api-keys to current admins $upd_stmt = Database::prepare(" UPDATE `" . TABLE_API_KEYS . "` SET @@ -653,7 +800,7 @@ class Admins extends ApiCommand implements ResourceEntity 'userid' => $this->getUserDetail('adminid'), 'adminid' => $id ), true, true); - + $this->logger()->logAction(ADM_ACTION, LOG_WARNING, "[API] deleted admin '" . $result['loginname'] . "'"); updateCounters(); return $this->response(200, "successfull", $result); diff --git a/lib/classes/api/commands/class.Froxlor.php b/lib/classes/api/commands/class.Froxlor.php index 9c68a666..bcf37499 100644 --- a/lib/classes/api/commands/class.Froxlor.php +++ b/lib/classes/api/commands/class.Froxlor.php @@ -234,7 +234,7 @@ class Froxlor extends ApiCommand $reflection = new \ReflectionClass($mod); $_functions = $reflection->getMethods(\ReflectionMethod::IS_PUBLIC); foreach ($_functions as $func) { - if ($func->class == $mod && $func->isPublic()) { + if ($func->class == $mod && $func->isPublic() && !$func->isStatic()) { array_push($functions, array_merge(array( 'module' => $matches[1], 'function' => $func->name From cb3d5f348885801c4819b0e2423273be1ae94dd4 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 26 Mar 2018 14:26:05 +0200 Subject: [PATCH 0589/1335] unit-test FpmDaemons-ApiCommand Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/commands/class.FpmDaemons.php | 31 +++++-- tests/PhpAndFpm/FpmDaemonsTest.php | 92 +++++++++++++++++++ tests/bootstrap.php | 1 + 3 files changed, 114 insertions(+), 10 deletions(-) create mode 100644 tests/PhpAndFpm/FpmDaemonsTest.php diff --git a/lib/classes/api/commands/class.FpmDaemons.php b/lib/classes/api/commands/class.FpmDaemons.php index e740f4cf..c1aedbbb 100644 --- a/lib/classes/api/commands/class.FpmDaemons.php +++ b/lib/classes/api/commands/class.FpmDaemons.php @@ -72,8 +72,9 @@ class FpmDaemons extends ApiCommand implements ResourceEntity /** * return a fpm-daemon entry by id * - * @param int $id fpm-daemon-id - * + * @param int $id + * fpm-daemon-id + * * @access admin * @throws Exception * @return array @@ -121,7 +122,7 @@ class FpmDaemons extends ApiCommand implements ResourceEntity $max_spare_servers = $this->getParam('max_spare_servers', true, 0); $max_requests = $this->getParam('max_requests', true, 0); $idle_timeout = $this->getParam('idle_timeout', true, 0); - $limit_extensions = $this->getParam('limit_extensions', true, ''); + $limit_extensions = $this->getParam('limit_extensions', true, '.php'); // validation $description = validate($description, 'description', '', '', array(), true); @@ -134,6 +135,9 @@ class FpmDaemons extends ApiCommand implements ResourceEntity ))) { throw new ErrorException("Unknown process manager", 406); } + if (empty($limit_extensions)) { + $limit_extensions = '.php'; + } $limit_extensions = validate($limit_extensions, 'limit_extensions', '/^(\.[a-z]([a-z0-9]+)\ ?)+$/', '', array(), true); if (strlen($description) == 0 || strlen($description) > 50) { @@ -168,11 +172,14 @@ class FpmDaemons extends ApiCommand implements ResourceEntity 'limit_extensions' => $limit_extensions ); Database::pexecute($ins_stmt, $ins_data); - $ins_data['id'] = Database::lastInsertId(); + $id = Database::lastInsertId(); inserttask('1'); $this->logger()->logAction(ADM_ACTION, LOG_INFO, "[API] fpm-daemon with description '" . $description . "' has been created by '" . $this->getUserDetail('loginname') . "'"); - return $this->response(200, "successfull", $ins_data); + $result = $this->apiCall('FpmDaemons.get', array( + 'id' => $id + )); + return $this->response(200, "successfull", $result); } throw new Exception("Not allowed to execute given command.", 403); } @@ -192,8 +199,8 @@ class FpmDaemons extends ApiCommand implements ResourceEntity // required parameter $id = $this->getParam('id'); - - $result = $this->apiCall('PhpSettings.get', array( + + $result = $this->apiCall('FpmDaemons.get', array( 'id' => $id )); @@ -221,6 +228,9 @@ class FpmDaemons extends ApiCommand implements ResourceEntity ))) { throw new ErrorException("Unknown process manager", 406); } + if (empty($limit_extensions)) { + $limit_extensions = '.php'; + } $limit_extensions = validate($limit_extensions, 'limit_extensions', '/^(\.[a-z]([a-z0-9]+)\ ?)+$/', '', array(), true); if (strlen($description) == 0 || strlen($description) > 50) { @@ -268,8 +278,9 @@ class FpmDaemons extends ApiCommand implements ResourceEntity /** * delete a fpm-daemon entry by id * - * @param int $id fpm-daemon-id - * + * @param int $id + * fpm-daemon-id + * * @access admin * @throws Exception * @return array @@ -282,7 +293,7 @@ class FpmDaemons extends ApiCommand implements ResourceEntity if ($id == 1) { standard_error('cannotdeletedefaultphpconfig', '', true); } - + $result = $this->apiCall('FpmDaemons.get', array( 'id' => $id )); diff --git a/tests/PhpAndFpm/FpmDaemonsTest.php b/tests/PhpAndFpm/FpmDaemonsTest.php new file mode 100644 index 00000000..b0491056 --- /dev/null +++ b/tests/PhpAndFpm/FpmDaemonsTest.php @@ -0,0 +1,92 @@ + 'test2 fpm', + 'reload_cmd' => 'service php7.1-fpm reload', + 'config_dir' => '/etc/php/7.1/fpm/pool.d' + ]; + $json_result = FpmDaemons::getLocal($admin_userdata, $data)->add(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals('/etc/php/7.1/fpm/pool.d/', $result['config_dir']); + $this->assertEquals(0, $result['max_children']); + $this->assertEquals('.php', $result['limit_extensions']); + self::$id = $result['id']; + } + + /** + * @depends testAdminFpmDaemonsAdd + */ + public function testAdminFpmDaemonsUpdate() + { + global $admin_userdata; + $data = [ + 'id' => self::$id, + 'description' => 'test2 fpm edit', + 'pm' => 'dynamic', + 'max_children' => '10', + 'start_servers' => '4', + 'limit_extensions' => '.php .php.xml', + ]; + $json_result = FpmDaemons::getLocal($admin_userdata, $data)->update(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals('/etc/php/7.1/fpm/pool.d/', $result['config_dir']); + $this->assertEquals(10, $result['max_children']); + $this->assertEquals('.php .php.xml', $result['limit_extensions']); + } + + /** + * @depends testAdminFpmDaemonsUpdate + */ + public function testAdminFpmDaemonsList() + { + global $admin_userdata; + $json_result = FpmDaemons::getLocal($admin_userdata)->listing(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals(2, $result['count']); + $this->assertEquals('test fpm', $result['list'][0]['description']); + $this->assertEquals('test2 fpm edit', $result['list'][1]['description']); + } + + /** + * @depends testAdminFpmDaemonsList + */ + public function testAdminFpmDaemonsDelete() + { + global $admin_userdata; + $data = [ + 'id' => self::$id + ]; + $json_result = FpmDaemons::getLocal($admin_userdata, $data)->delete(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals('/etc/php/7.1/fpm/pool.d/', $result['config_dir']); + $this->assertEquals(10, $result['max_children']); + $this->assertEquals('.php .php.xml', $result['limit_extensions']); + } + + /** + * @depends testAdminFpmDaemonsDelete + */ + public function testAdminFpmDaemonsDeleteDefaultConfig() + { + global $admin_userdata; + $data = [ + 'id' => 1 + ]; + $this->expectExceptionMessage("This PHP-configuration is set as default and cannot be deleted."); + FpmDaemons::getLocal($admin_userdata, $data)->delete(); + } +} diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 3d94f45e..b5dbbab7 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -55,6 +55,7 @@ Database::query("TRUNCATE TABLE `" . TABLE_PANEL_ADMINS . "`;"); Database::query("TRUNCATE TABLE `" . TABLE_PANEL_IPSANDPORTS . "`;"); Database::query("TRUNCATE TABLE `" . TABLE_API_KEYS . "`;"); Database::query("TRUNCATE TABLE `" . TABLE_PANEL_DATABASES . "`;"); +Database::query("ALTER TABLE `" . TABLE_PANEL_FPMDAEMONS . "` AUTO_INCREMENT=2;"); // add superadmin Database::query("INSERT INTO `" . TABLE_PANEL_ADMINS . "` SET From bf3ae3009f80930a1b294e07966715453b9ffd72 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 26 Mar 2018 14:28:32 +0200 Subject: [PATCH 0590/1335] add tests/PhpAndFpm to phpunit-testsuite Signed-off-by: Michael Kaufmann (d00p) --- phpunit.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/phpunit.xml b/phpunit.xml index 1ffdbece..7c551348 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -21,6 +21,7 @@ tests/Backup tests/DomainZones tests/Mysqls + tests/PhpAndFpm From 45d7307a8feaafa519e896ab1668c876e0cd7c55 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 26 Mar 2018 14:33:43 +0200 Subject: [PATCH 0591/1335] fix phpunit test for FpmDaemonTest Signed-off-by: Michael Kaufmann (d00p) --- tests/PhpAndFpm/FpmDaemonsTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/PhpAndFpm/FpmDaemonsTest.php b/tests/PhpAndFpm/FpmDaemonsTest.php index b0491056..f85ec30e 100644 --- a/tests/PhpAndFpm/FpmDaemonsTest.php +++ b/tests/PhpAndFpm/FpmDaemonsTest.php @@ -57,7 +57,7 @@ class FpmDaemonsTest extends TestCase $json_result = FpmDaemons::getLocal($admin_userdata)->listing(); $result = json_decode($json_result, true)['data']; $this->assertEquals(2, $result['count']); - $this->assertEquals('test fpm', $result['list'][0]['description']); + $this->assertEquals('System default', $result['list'][0]['description']); $this->assertEquals('test2 fpm edit', $result['list'][1]['description']); } From d15e4a827038b0640a974892733a3c83e86199b9 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 27 Mar 2018 14:43:24 +0200 Subject: [PATCH 0592/1335] more unit-tests Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/commands/class.Admins.php | 34 +----- tests/Admins/AdminsTest.php | 75 +++++++++++- tests/Backup/CustomerBackupsTest.php | 73 +++++++++-- tests/Customers/CustomersTest.php | 141 ++++++++++++++++++++++ tests/Emails/EmailsTest.php | 65 ++++++++++ tests/Ftps/FtpsTest.php | 2 + tests/Mysqls/MysqlsTest.php | 2 + tests/SubDomains/SubDomainsTest.php | 2 + 8 files changed, 356 insertions(+), 38 deletions(-) diff --git a/lib/classes/api/commands/class.Admins.php b/lib/classes/api/commands/class.Admins.php index 18382d7b..4aeba76b 100644 --- a/lib/classes/api/commands/class.Admins.php +++ b/lib/classes/api/commands/class.Admins.php @@ -88,6 +88,7 @@ class Admins extends ApiCommand implements ResourceEntity * * @param string $name * @param string $email + * @param string $new_loginname * @param string $admin_password * optional, default auto-generated * @param string $def_language @@ -168,13 +169,13 @@ class Admins extends ApiCommand implements ResourceEntity // required parameters $name = $this->getParam('name'); $email = $this->getParam('email'); - + $loginname = $this->getParam('new_loginname'); + // parameters $def_language = $this->getParam('def_language', true, Settings::Get('panel.standardlanguage')); $custom_notes = $this->getParam('custom_notes', true, ''); $custom_notes_show = $this->getParam('custom_notes_show', true, 0); $password = $this->getParam('admin_password', true, ''); - $loginname = $this->getParam('new_loginname', true, ''); $diskspace = $this->getUlParam('diskspace', 'diskspace_ul', true, 0); $traffic = $this->getUlParam('traffic', 'traffic_ul', true, 0); @@ -239,28 +240,13 @@ class Admins extends ApiCommand implements ResourceEntity 'login' => $loginname ), true, true); - if ($loginname == '') { - standard_error(array( - 'stringisempty', - 'myloginname' - ), '', true); - } elseif (strtolower($loginname_check['loginname']) == strtolower($loginname) || strtolower($loginname_check_admin['loginname']) == strtolower($loginname)) { + if (strtolower($loginname_check['loginname']) == strtolower($loginname) || strtolower($loginname_check_admin['loginname']) == strtolower($loginname)) { standard_error('loginnameexists', $loginname, true); } // Accounts which match systemaccounts are not allowed, filtering them elseif (preg_match('/^' . preg_quote(Settings::Get('customer.accountprefix'), '/') . '([0-9]+)/', $loginname)) { standard_error('loginnameissystemaccount', Settings::Get('customer.accountprefix'), true); } elseif (! validateUsername($loginname)) { standard_error('loginnameiswrong', $loginname, true); - } elseif ($name == '') { - standard_error(array( - 'stringisempty', - 'myname' - ), '', true); - } elseif ($email == '') { - standard_error(array( - 'stringisempty', - 'emailadd' - ), '', true); } elseif (! validateEmail($email)) { standard_error('emailiswrong', $email, true); } else { @@ -548,17 +534,7 @@ class Admins extends ApiCommand implements ResourceEntity $theme = Settings::Get('panel.default_theme'); } - if ($name == '') { - standard_error(array( - 'stringisempty', - 'myname' - ), '', true); - } elseif ($email == '') { - standard_error(array( - 'stringisempty', - 'emailadd' - ), '', true); - } elseif (! validateEmail($email)) { + if (! validateEmail($email)) { standard_error('emailiswrong', $email, true); } else { diff --git a/tests/Admins/AdminsTest.php b/tests/Admins/AdminsTest.php index 79b110a2..da096256 100644 --- a/tests/Admins/AdminsTest.php +++ b/tests/Admins/AdminsTest.php @@ -51,6 +51,78 @@ class AdminsTest extends TestCase $this->assertEquals(0, $result['customers_see_all']); } + /** + * + * @depends testAdminAdminsAdd + */ + public function testAdminAdminsAddLoginnameExists() + { + global $admin_userdata; + + $data = [ + 'new_loginname' => 'reseller', + 'email' => 'testreseller@froxlor.org', + 'name' => 'Testreseller' + ]; + + $this->expectExceptionMessage('Loginname reseller already exists'); + Admins::getLocal($admin_userdata, $data)->add(); + } + + /** + * + * @depends testAdminAdminsAddLoginnameExists + */ + public function testAdminAdminsAddLoginnameIsSystemaccount() + { + global $admin_userdata; + + $data = [ + 'new_loginname' => 'web2', + 'email' => 'testreseller@froxlor.org', + 'name' => 'Testreseller' + ]; + + $this->expectExceptionMessage('You cannot create accounts which are similar to system accounts (as for example begin with "web"). Please enter another account name.'); + Admins::getLocal($admin_userdata, $data)->add(); + } + + /** + * + * @depends testAdminAdminsAddLoginnameIsSystemaccount + */ + public function testAdminAdminsAddLoginnameInvalid() + { + global $admin_userdata; + + $data = [ + 'new_loginname' => 'reslr-', + 'email' => 'testreseller@froxlor.org', + 'name' => 'Testreseller' + ]; + + $this->expectExceptionMessage('Loginname "reslr-" contains illegal characters.'); + Admins::getLocal($admin_userdata, $data)->add(); + } + + /** + * + * @depends testAdminAdminsAddLoginnameIsSystemaccount + */ + public function testAdminAdminsAddLoginnameInvalidEmail() + { + global $admin_userdata; + + $data = [ + 'new_loginname' => 'reslr', + 'email' => 'testreseller.froxlor.org', + 'name' => 'Testreseller' + ]; + + $this->expectExceptionMessage('Email-address testreseller.froxlor.org contains invalid characters or is incomplete'); + Admins::getLocal($admin_userdata, $data)->add(); + } + public function testAdminAdminsAddNotAllowed() { global $admin_userdata; @@ -160,8 +232,7 @@ class AdminsTest extends TestCase $data = [ 'new_loginname' => 'resellertest', 'email' => 'testreseller@froxlor.org', - 'name' => 'Testreseller', - 'admin_password' => 'h0lYmo1y' + 'name' => 'Testreseller' ]; $json_result = Admins::getLocal($admin_userdata, $data)->add(); diff --git a/tests/Backup/CustomerBackupsTest.php b/tests/Backup/CustomerBackupsTest.php index 210b36eb..6a865780 100644 --- a/tests/Backup/CustomerBackupsTest.php +++ b/tests/Backup/CustomerBackupsTest.php @@ -10,10 +10,69 @@ use PHPUnit\Framework\TestCase; class CustomerBackupsTest extends TestCase { + public function testAdminCustomerBackupsNotEnabled() + { + global $admin_userdata; + + Settings::Set('system.backupenabled', 0, true); + + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + $this->expectExceptionCode(405); + $this->expectExceptionMessage("You cannot access this resource"); + CustomerBackups::getLocal($customer_userdata)->add(); + } + + /** + * @depends testAdminCustomerBackupsNotEnabled + */ + public function testAdminCustomerBackupsExtrasHidden() + { + global $admin_userdata; + + Settings::Set('system.backupenabled', 1, true); + Settings::Set('panel.customer_hide_options', 'extras', true); + + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + $this->expectExceptionCode(405); + $this->expectExceptionMessage("You cannot access this resource"); + CustomerBackups::getLocal($customer_userdata)->add(); + } + + /** + * @depends testAdminCustomerBackupsExtrasHidden + */ + public function testAdminCustomerBackupsExtrasBackupHidden() + { + global $admin_userdata; + + Settings::Set('panel.customer_hide_options', 'extras.backup', true); + + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + $this->expectExceptionCode(405); + $this->expectExceptionMessage("You cannot access this resource"); + CustomerBackups::getLocal($customer_userdata)->add(); + } + + /** + * @depends testAdminCustomerBackupsExtrasBackupHidden + */ public function testCustomerCustomerBackupsAdd() { global $admin_userdata; + Settings::Set('panel.customer_hide_options', '', true); Database::query("TRUNCATE TABLE `panel_tasks`;"); // get customer @@ -24,16 +83,16 @@ class CustomerBackupsTest extends TestCase $data = [ 'path' => '/my-backup', - 'backup_dbs' => 1, - 'backup_mail' => 2, - 'backup_web' => 1 + 'backup_dbs' => 2, + 'backup_mail' => 3, + 'backup_web' => 4 ]; $json_result = CustomerBackups::getLocal($customer_userdata, $data)->add(); $result = json_decode($json_result, true)['data']; $this->assertEquals($customer_userdata['documentroot'] . 'my-backup/', $result['destdir']); - $this->assertEquals('1', $result['backup_dbs']); + $this->assertEquals('0', $result['backup_dbs']); $this->assertEquals('0', $result['backup_mail']); - $this->assertEquals('1', $result['backup_web']); + $this->assertEquals('0', $result['backup_web']); } public function testAdminCustomerBackupsGet() @@ -61,9 +120,9 @@ class CustomerBackupsTest extends TestCase $json_result = CustomerBackups::getLocal($admin_userdata)->listing(); $result = json_decode($json_result, true)['data']; $this->assertEquals(1, $result['count']); - $this->assertEquals('1', $result['list'][0]['data']['backup_dbs']); + $this->assertEquals('0', $result['list'][0]['data']['backup_dbs']); $this->assertEquals('0', $result['list'][0]['data']['backup_mail']); - $this->assertEquals('1', $result['list'][0]['data']['backup_web']); + $this->assertEquals('0', $result['list'][0]['data']['backup_web']); } /** diff --git a/tests/Customers/CustomersTest.php b/tests/Customers/CustomersTest.php index d9ea0450..20b3eafb 100644 --- a/tests/Customers/CustomersTest.php +++ b/tests/Customers/CustomersTest.php @@ -2,9 +2,11 @@ use PHPUnit\Framework\TestCase; /** + * * @covers ApiCommand * @covers ApiParameter * @covers Customers + * @covers Admins */ class CustomersTest extends TestCase { @@ -83,6 +85,7 @@ class CustomersTest extends TestCase } /** + * * @depends testAdminCustomersAdd */ public function testAdminCustomersList() @@ -95,6 +98,7 @@ class CustomersTest extends TestCase } /** + * * @depends testAdminCustomersAdd */ public function testResellerCustomersList() @@ -112,6 +116,7 @@ class CustomersTest extends TestCase } /** + * * @depends testAdminCustomersAdd */ public function testCustomerCustomersList() @@ -130,6 +135,7 @@ class CustomersTest extends TestCase } /** + * * @depends testAdminCustomersAdd */ public function testCustomerCustomersGet() @@ -165,6 +171,7 @@ class CustomersTest extends TestCase } /** + * * @depends testAdminCustomersAdd */ public function testCustomerCustomersGetForeign() @@ -185,6 +192,7 @@ class CustomersTest extends TestCase } /** + * * @depends testAdminCustomersAdd */ public function testAdminCustomerUpdateDeactivate() @@ -209,6 +217,7 @@ class CustomersTest extends TestCase } /** + * * @depends testAdminCustomersAdd */ public function testCustomerCustomersGetWhenDeactivated() @@ -230,6 +239,7 @@ class CustomersTest extends TestCase } /** + * * @depends testAdminCustomersAdd */ public function testCustomerCustomersUpdate() @@ -266,6 +276,7 @@ class CustomersTest extends TestCase } /** + * * @depends testAdminCustomersAdd */ public function testResellerCustomersAddAllocateMore() @@ -403,4 +414,134 @@ class CustomersTest extends TestCase $this->assertEquals(2, $result['adminid']); } + + /** + * + * @depends testAdminCustomersMove + */ + public function testAdminCustomersAddLoginnameIsSystemaccount() + { + global $admin_userdata; + + $data = [ + 'new_loginname' => 'web1', + 'email' => 'team@froxlor.org', + 'firstname' => 'Test', + 'name' => 'Testman', + 'customernumber' => 1338, + 'diskspace' => - 1, + 'traffic' => - 1, + 'subdomains' => 15, + 'emails' => - 1, + 'email_accounts' => 15, + 'email_forwarders' => 15, + 'email_imap' => 1, + 'email_pop3' => 0, + 'ftps' => 15, + 'tickets' => 15, + 'mysqls' => 15, + 'createstdsubdomain' => 1, + 'new_customer_password' => 'h0lYmo1y', + 'sendpassword' => 1, + 'phpenabled' => 1, + 'store_defaultindex' => 1, + 'custom_notes' => 'secret', + 'custom_notes_show' => 0, + 'gender' => 5, + 'allowed_phpconfigs' => array( + 1 + ) + ]; + + $this->expectExceptionMessage('You cannot create accounts which are similar to system accounts (as for example begin with "web"). Please enter another account name.'); + Customers::getLocal($admin_userdata, $data)->add(); + } + + /** + * + * @depends testAdminCustomersAddLoginnameIsSystemaccount + */ + public function testAdminCustomersAddAutoLoginname() + { + global $admin_userdata; + + Settings::Set('system.lastaccountnumber', 0, true); + Settings::Set('ticket.enabled', 0, true); + + $data = [ + 'new_loginname' => '', + 'email' => 'team@froxlor.org', + 'firstname' => 'Test2', + 'name' => 'Testman2', + 'customernumber' => 1338, + 'sendpassword' => 0, + 'perlenabled' => 2, + 'dnsenabled' => 4 + ]; + + $json_result = Customers::getLocal($admin_userdata, $data)->add(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals('web1', $result['loginname']); + $this->assertEquals(1338, $result['customernumber']); + } + + /** + * + * @depends testAdminCustomersAddAutoLoginname + */ + public function testAdminCustomersAddLoginnameExists() + { + global $admin_userdata; + + $data = [ + 'new_loginname' => 'test1', + 'email' => 'team@froxlor.org', + 'firstname' => 'Test2', + 'name' => 'Testman2', + 'customernumber' => 1339 + ]; + + $this->expectExceptionMessage('Loginname test1 already exists'); + Customers::getLocal($admin_userdata, $data)->add(); + } + + /** + * + * @depends testAdminCustomersAddLoginnameExists + */ + public function testAdminCustomersAddLoginnameInvalid() + { + global $admin_userdata; + + $data = [ + 'new_loginname' => 'user-', + 'email' => 'team@froxlor.org', + 'firstname' => 'Test2', + 'name' => 'Testman2', + 'customernumber' => 1339 + ]; + + $this->expectExceptionMessage('Loginname "user-" contains illegal characters.'); + Customers::getLocal($admin_userdata, $data)->add(); + } + + /** + * + * @depends testAdminCustomersAddLoginnameExists + */ + public function testAdminCustomersAddLoginnameInvalid2() + { + global $admin_userdata; + + $data = [ + 'new_loginname' => 'useruseruseruseruseruserX', + 'email' => 'team@froxlor.org', + 'firstname' => 'Test2', + 'name' => 'Testman2', + 'customernumber' => 1339 + ]; + + $this->expectExceptionMessage('Loginname contains too many characters. Only ' . (14 - strlen(Settings::Get('customer.mysqlprefix'))) . ' characters are allowed.'); + Customers::getLocal($admin_userdata, $data)->add(); + } } diff --git a/tests/Emails/EmailsTest.php b/tests/Emails/EmailsTest.php index 5ef1f0d8..9cfd1065 100644 --- a/tests/Emails/EmailsTest.php +++ b/tests/Emails/EmailsTest.php @@ -7,6 +7,8 @@ use PHPUnit\Framework\TestCase; * @covers Emails * @covers EmailForwarders * @covers EmailAccounts + * @covers Customers + * @covers Admins */ class MailsTest extends TestCase { @@ -96,11 +98,72 @@ class MailsTest extends TestCase $result = json_decode($json_result, true)['data']; $this->assertEquals('other@domain.tld', $result['destination']); } + + /** + * @depends testCustomerEmailForwardersAdd + */ + public function testCustomerEmailForwardersAddNoMoreResources() + { + global $admin_userdata; + + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + $customer_userdata['email_forwarders_used'] = $customer_userdata['email_forwarders']; + $this->expectExceptionCode(406); + $this->expectExceptionMessage("No more resources available"); + EmailForwarders::getLocal($customer_userdata)->add(); + } + + /** + * @depends testCustomerEmailForwardersAddNoMoreResources + */ + public function testCustomerEmailForwardersAddEmailHidden() + { + global $admin_userdata; + + Settings::Set('panel.customer_hide_options', 'email', true); + + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + $this->expectExceptionCode(405); + $this->expectExceptionMessage("You cannot access this resource"); + EmailForwarders::getLocal($customer_userdata)->add(); + } + /** + * @depends testCustomerEmailForwardersAddEmailHidden + */ + public function testCustomerEmailForwardersDeleteEmailHidden() + { + global $admin_userdata; + + Settings::Set('panel.customer_hide_options', 'email', true); + + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + $this->expectExceptionCode(405); + $this->expectExceptionMessage("You cannot access this resource"); + EmailForwarders::getLocal($customer_userdata)->delete(); + } + + /** + * @depends testCustomerEmailForwardersDeleteEmailHidden + */ public function testCustomerEmailForwardersAddAnother() { global $admin_userdata; + Settings::Set('panel.customer_hide_options', '', true); + // get customer $json_result = Customers::getLocal($admin_userdata, array( 'loginname' => 'test1' @@ -245,6 +308,8 @@ class MailsTest extends TestCase { global $admin_userdata; + Settings::Set('panel.customer_hide_options', '', true); + // get customer $json_result = Customers::getLocal($admin_userdata, array( 'loginname' => 'test1' diff --git a/tests/Ftps/FtpsTest.php b/tests/Ftps/FtpsTest.php index 38389a44..09a3bfcd 100644 --- a/tests/Ftps/FtpsTest.php +++ b/tests/Ftps/FtpsTest.php @@ -5,6 +5,8 @@ use PHPUnit\Framework\TestCase; * @covers ApiCommand * @covers ApiParameter * @covers Ftps + * @covers Customers + * @covers Admins */ class FtpsTest extends TestCase { diff --git a/tests/Mysqls/MysqlsTest.php b/tests/Mysqls/MysqlsTest.php index bd2a15f9..fda7b559 100644 --- a/tests/Mysqls/MysqlsTest.php +++ b/tests/Mysqls/MysqlsTest.php @@ -6,6 +6,8 @@ use PHPUnit\Framework\TestCase; * @covers ApiCommand * @covers ApiParameter * @covers Mysqls + * @covers Customers + * @covers Admins */ class MysqlsTest extends TestCase { diff --git a/tests/SubDomains/SubDomainsTest.php b/tests/SubDomains/SubDomainsTest.php index d1aa4af0..54b9219a 100644 --- a/tests/SubDomains/SubDomainsTest.php +++ b/tests/SubDomains/SubDomainsTest.php @@ -6,6 +6,8 @@ use PHPUnit\Framework\TestCase; * @covers ApiParameter * @covers SubDomains * @covers Domains + * @covers Customers + * @covers Admins */ class SubDomainsTest extends TestCase { From e58192edc259b4560a05882f627431a7cf9a9953 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Thu, 29 Mar 2018 11:11:39 +0200 Subject: [PATCH 0593/1335] show whether curl extension is installed when trying to check for a new version in admin-dashboard Signed-off-by: Michael Kaufmann (d00p) --- admin_index.php | 53 ++++++++++++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 23 deletions(-) diff --git a/admin_index.php b/admin_index.php index 4650a24e..0f88a023 100644 --- a/admin_index.php +++ b/admin_index.php @@ -85,35 +85,42 @@ if ($page == 'overview') { if ((isset($_GET['lookfornewversion']) && $_GET['lookfornewversion'] == 'yes') || (isset($lookfornewversion) && $lookfornewversion == 'yes') ) { - $update_check_uri = 'http://version.froxlor.org/Froxlor/legacy/' . $version; - $latestversion = HttpClient::urlGet($update_check_uri); - $latestversion = explode('|', $latestversion); + if (function_exists('curl_version')) { + $update_check_uri = 'http://version.froxlor.org/Froxlor/legacy/' . $version; + $latestversion = HttpClient::urlGet($update_check_uri); + $latestversion = explode('|', $latestversion); - if (is_array($latestversion) - && count($latestversion) >= 1 - ) { - $_version = $latestversion[0]; - $_message = isset($latestversion[1]) ? $latestversion[1] : ''; - $_link = isset($latestversion[2]) ? $latestversion[2] : htmlspecialchars($filename . '?s=' . urlencode($s) . '&page=' . urlencode($page) . '&lookfornewversion=yes'); + if (is_array($latestversion) + && count($latestversion) >= 1 + ) { + $_version = $latestversion[0]; + $_message = isset($latestversion[1]) ? $latestversion[1] : ''; + $_link = isset($latestversion[2]) ? $latestversion[2] : htmlspecialchars($filename . '?s=' . urlencode($s) . '&page=' . urlencode($page) . '&lookfornewversion=yes'); - // add the branding so debian guys are not gettings confused - // about their version-number - $lookfornewversion_lable = $_version.$branding; - $lookfornewversion_link = $_link; - $lookfornewversion_addinfo = $_message; + // add the branding so debian guys are not gettings confused + // about their version-number + $lookfornewversion_lable = $_version.$branding; + $lookfornewversion_link = $_link; + $lookfornewversion_addinfo = $_message; - // not numeric -> error-message - if (!preg_match('/^((\d+\\.)(\d+\\.)(\d+\\.)?(\d+)?(\-(svn|dev|rc)(\d+))?)$/', $_version)) { - // check for customized version to not output - // "There is a newer version of froxlor" besides the error-message - $isnewerversion = 2; - } elseif (version_compare2($version, $_version) == -1) { - $isnewerversion = 1; + // not numeric -> error-message + if (!preg_match('/^((\d+\\.)(\d+\\.)(\d+\\.)?(\d+)?(\-(svn|dev|rc)(\d+))?)$/', $_version)) { + // check for customized version to not output + // "There is a newer version of froxlor" besides the error-message + $isnewerversion = 2; + } elseif (version_compare2($version, $_version) == -1) { + $isnewerversion = 1; + } else { + $isnewerversion = 0; + } } else { - $isnewerversion = 0; + redirectTo($update_check_uri.'/pretty', NULL, false); } } else { - redirectTo($update_check_uri.'/pretty', NULL, false); + $lookfornewversion_lable = "Version-check not available due to missing php-curl extension"; + $lookfornewversion_link = htmlspecialchars($filename . '?s=' . urlencode($s) . '&page=' . urlencode($page) . '&lookfornewversion=yes'); + $lookfornewversion_addinfo = ''; + $isnewerversion = 0; } } else { $lookfornewversion_lable = $lng['admin']['lookfornewversion']['clickhere']; From 30d39d622d2daef59c9fc97822bd4902dd14d1a3 Mon Sep 17 00:00:00 2001 From: Michael Kaufmann Date: Thu, 29 Mar 2018 13:10:33 +0200 Subject: [PATCH 0594/1335] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 33cb17cb..171035cf 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ https://files.froxlor.org/releases/froxlor-latest.tar.gz [MD5](https://files.fro [HowTo](https://github.com/Froxlor/Froxlor/wiki/Install-froxlor-on-debian) /etc/apt/sources.list.d/froxlor.list -> deb http://debian.froxlor.org {wheezy|jessie} main +> deb http://debian.froxlor.org {wheezy|jessie|stretch} main ### Gentoo repository From 85407abfb4ec5632292bf43534c3d9750403169b Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sun, 1 Apr 2018 09:59:25 +0200 Subject: [PATCH 0595/1335] optimize stats-folder-decision in Customers-ApiCommand Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/commands/class.Customers.php | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/classes/api/commands/class.Customers.php b/lib/classes/api/commands/class.Customers.php index ae02b9c5..6f8da50e 100644 --- a/lib/classes/api/commands/class.Customers.php +++ b/lib/classes/api/commands/class.Customers.php @@ -431,7 +431,7 @@ class Customers extends ApiCommand implements ResourceEntity // Using filesystem - quota, insert a task which cleans the filesystem - quota inserttask('10'); - // Add htpasswd for the webalizer stats + // Add htpasswd for the stats-pages if (CRYPT_STD_DES == 1) { $saltfordescrypt = substr(md5(uniqid(microtime(), 1)), 4, 2); $htpasswdPassword = crypt($password, $saltfordescrypt); @@ -452,13 +452,12 @@ class Customers extends ApiCommand implements ResourceEntity 'passwd' => $htpasswdPassword ); + $stats_folder = 'webalizer'; if (Settings::Get('system.awstats_enabled') == '1') { - $ins_data['path'] = makeCorrectDir($documentroot . '/awstats/'); - $this->logger()->logAction(ADM_ACTION, LOG_NOTICE, "[API] automatically added awstats htpasswd for user '" . $loginname . "'"); - } else { - $ins_data['path'] = makeCorrectDir($documentroot . '/webalizer/'); - $this->logger()->logAction(ADM_ACTION, LOG_NOTICE, "[API] automatically added webalizer htpasswd for user '" . $loginname . "'"); + $stats_folder = 'awstats'; } + $ins_data['path'] = makeCorrectDir($documentroot . '/' . $stats_folder . '/'); + $this->logger()->logAction(ADM_ACTION, LOG_NOTICE, "[API] automatically added ".$stats_folder." htpasswd for user '" . $loginname . "'"); Database::pexecute($ins_stmt, $ins_data, true, true); inserttask('1'); From 3f69c97874076356b9556ee522c499071d5dbc03 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sun, 1 Apr 2018 10:31:38 +0200 Subject: [PATCH 0596/1335] opzimize ApiParameter::getModFunctionString(); corrected FpmDaemons::update(); added a few more unit-tests Signed-off-by: Michael Kaufmann (d00p) --- lib/classes/api/abstract.ApiParameter.php | 38 +++-- lib/classes/api/commands/class.FpmDaemons.php | 5 +- tests/Customers/CustomersTest.php | 3 +- tests/PhpAndFpm/FpmDaemonsTest.php | 150 +++++++++++++++++- 4 files changed, 175 insertions(+), 21 deletions(-) diff --git a/lib/classes/api/abstract.ApiParameter.php b/lib/classes/api/abstract.ApiParameter.php index c51f4088..4bc957d3 100644 --- a/lib/classes/api/abstract.ApiParameter.php +++ b/lib/classes/api/abstract.ApiParameter.php @@ -117,29 +117,33 @@ abstract class ApiParameter * returns "module::function()" for better error-messages (missing parameter etc.) * makes debugging a whole lot more comfortable * + * @param int $level + * depth of backtrace, default 2 + * * @return string */ - private function getModFunctionString() + private function getModFunctionString($level = 1, $max_level = 5, $trace = null) { + // which class called us $_class = get_called_class(); - $level = 2; - if (version_compare(PHP_VERSION, "5.4.0", "<")) { - $trace = debug_backtrace(); - } else { - $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); - } - while (true) { - $class = $trace[$level]['class']; - $func = $trace[$level]['function']; - if ($class != $_class) { - $level ++; - if ($level > 5) { - break; - } - continue; + if (empty($trace)) { + // check php version for backtrace flags + $_traceopts = false; + if (version_compare(PHP_VERSION, "5.3.6", ">")) { + $_traceopts = DEBUG_BACKTRACE_IGNORE_ARGS; } - return $class . ':' . $func; + // get backtrace + $trace = debug_backtrace($_traceopts); } + // check class and function + $class = $trace[$level]['class']; + $func = $trace[$level]['function']; + // is it the one we are looking for? + if ($class != $_class && $level <= $max_level) { + // check one level deeper + return $this->getModFunctionString(++ $level, $max_level, $trace); + } + return $class . ':' . $func; } /** diff --git a/lib/classes/api/commands/class.FpmDaemons.php b/lib/classes/api/commands/class.FpmDaemons.php index c1aedbbb..9e071235 100644 --- a/lib/classes/api/commands/class.FpmDaemons.php +++ b/lib/classes/api/commands/class.FpmDaemons.php @@ -270,7 +270,10 @@ class FpmDaemons extends ApiCommand implements ResourceEntity inserttask('1'); $this->logger()->logAction(ADM_ACTION, LOG_INFO, "[API] fpm-daemon with description '" . $description . "' has been updated by '" . $this->getUserDetail('loginname') . "'"); - return $this->response(200, "successfull", $upd_data); + $result = $this->apiCall('FpmDaemons.get', array( + 'id' => $id + )); + return $this->response(200, "successfull", $result); } throw new Exception("Not allowed to execute given command.", 403); } diff --git a/tests/Customers/CustomersTest.php b/tests/Customers/CustomersTest.php index 20b3eafb..1b99b6fc 100644 --- a/tests/Customers/CustomersTest.php +++ b/tests/Customers/CustomersTest.php @@ -21,7 +21,8 @@ class CustomersTest extends TestCase 'firstname' => 'Test', 'name' => 'Testman', 'customernumber' => 1337, - 'diskspace' => - 1, + 'diskspace' => 0, + 'diskspace_ul' => 1, 'traffic' => - 1, 'subdomains' => 15, 'emails' => - 1, diff --git a/tests/PhpAndFpm/FpmDaemonsTest.php b/tests/PhpAndFpm/FpmDaemonsTest.php index f85ec30e..86ae54db 100644 --- a/tests/PhpAndFpm/FpmDaemonsTest.php +++ b/tests/PhpAndFpm/FpmDaemonsTest.php @@ -17,7 +17,8 @@ class FpmDaemonsTest extends TestCase $data = [ 'description' => 'test2 fpm', 'reload_cmd' => 'service php7.1-fpm reload', - 'config_dir' => '/etc/php/7.1/fpm/pool.d' + 'config_dir' => '/etc/php/7.1/fpm/pool.d', + 'limit_extensions' => '' ]; $json_result = FpmDaemons::getLocal($admin_userdata, $data)->add(); $result = json_decode($json_result, true)['data']; @@ -27,6 +28,33 @@ class FpmDaemonsTest extends TestCase self::$id = $result['id']; } + public function testAdminFpmDaemonsAddUnknownPM() + { + global $admin_userdata; + $data = [ + 'description' => 'test2 fpm', + 'reload_cmd' => 'service php7.3-fpm reload', + 'config_dir' => '/etc/php/7.3/fpm/pool.d', + 'pm' => 'supermegapm' + ]; + $this->expectExceptionCode(406); + $this->expectExceptionMessage("Unknown process manager"); + FpmDaemons::getLocal($admin_userdata, $data)->add(); + } + + public function testAdminFpmDaemonsAddInvalidDesc() + { + // max 50. characters + global $admin_userdata; + $data = [ + 'description' => str_repeat('test', 30), + 'reload_cmd' => 'service php7.3-fpm reload', + 'config_dir' => '/etc/php/7.3/fpm/pool.d' + ]; + $this->expectExceptionMessage("The description is too short, too long or contains illegal characters."); + FpmDaemons::getLocal($admin_userdata, $data)->add(); + } + /** * @depends testAdminFpmDaemonsAdd */ @@ -48,6 +76,51 @@ class FpmDaemonsTest extends TestCase $this->assertEquals('.php .php.xml', $result['limit_extensions']); } + /** + * @depends testAdminFpmDaemonsUpdate + */ + public function testAdminFpmDaemonsUpdate2() + { + global $admin_userdata; + $data = [ + 'id' => self::$id, + 'limit_extensions' => '', + ]; + $json_result = FpmDaemons::getLocal($admin_userdata, $data)->update(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals('.php', $result['limit_extensions']); + } + + /** + * @depends testAdminFpmDaemonsAdd + */ + public function testAdminFpmDaemonsUpdateUnknownPM() + { + global $admin_userdata; + $data = [ + 'id' => self::$id, + 'pm' => 'supermegapm' + ]; + $this->expectExceptionCode(406); + $this->expectExceptionMessage("Unknown process manager"); + FpmDaemons::getLocal($admin_userdata, $data)->update(); + } + + /** + * @depends testAdminFpmDaemonsAdd + */ + public function testAdminFpmDaemonsUpdateInvalidDesc() + { + // max 50. characters + global $admin_userdata; + $data = [ + 'id' => self::$id, + 'description' => str_repeat('test', 30) + ]; + $this->expectExceptionMessage("The description is too short, too long or contains illegal characters."); + FpmDaemons::getLocal($admin_userdata, $data)->update(); + } + /** * @depends testAdminFpmDaemonsUpdate */ @@ -61,6 +134,79 @@ class FpmDaemonsTest extends TestCase $this->assertEquals('test2 fpm edit', $result['list'][1]['description']); } + public function testAdminFpmDaemonsGetNotFound() + { + global $admin_userdata; + $this->expectExceptionCode(404); + $this->expectExceptionMessage("fpm-daemon with id #-1 could not be found"); + FpmDaemons::getLocal($admin_userdata, array('id' => -1))->get(); + } + + public function testCustomerFpmDaemonsAdd() + { + global $admin_userdata; + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + $this->expectExceptionCode(403); + $this->expectExceptionMessage("Not allowed to execute given command."); + FpmDaemons::getLocal($customer_userdata)->add(); + } + + public function testCustomerFpmDaemonsGet() + { + global $admin_userdata; + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + $this->expectExceptionCode(403); + $this->expectExceptionMessage("Not allowed to execute given command."); + FpmDaemons::getLocal($customer_userdata)->get(); + } + + public function testCustomerFpmDaemonsList() + { + global $admin_userdata; + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + $this->expectExceptionCode(403); + $this->expectExceptionMessage("Not allowed to execute given command."); + FpmDaemons::getLocal($customer_userdata)->listing(); + } + + public function testCustomerFpmDaemonsUpdate() + { + global $admin_userdata; + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + $this->expectExceptionCode(403); + $this->expectExceptionMessage("Not allowed to execute given command."); + FpmDaemons::getLocal($customer_userdata)->update(); + } + + public function testCustomerFpmDaemonsDelete() + { + global $admin_userdata; + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + $this->expectExceptionCode(403); + $this->expectExceptionMessage("Not allowed to execute given command."); + FpmDaemons::getLocal($customer_userdata)->delete(); + } + /** * @depends testAdminFpmDaemonsList */ @@ -74,7 +220,7 @@ class FpmDaemonsTest extends TestCase $result = json_decode($json_result, true)['data']; $this->assertEquals('/etc/php/7.1/fpm/pool.d/', $result['config_dir']); $this->assertEquals(10, $result['max_children']); - $this->assertEquals('.php .php.xml', $result['limit_extensions']); + $this->assertEquals('.php', $result['limit_extensions']); } /** From 060115c5e995932146fa932df3970f5643b649a6 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sun, 1 Apr 2018 10:35:55 +0200 Subject: [PATCH 0597/1335] added new ApiParameterTest Signed-off-by: Michael Kaufmann (d00p) --- tests/Global/ApiParameterTest.php | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 tests/Global/ApiParameterTest.php diff --git a/tests/Global/ApiParameterTest.php b/tests/Global/ApiParameterTest.php new file mode 100644 index 00000000..8611baca --- /dev/null +++ b/tests/Global/ApiParameterTest.php @@ -0,0 +1,18 @@ +expectExceptionCode(404); + $this->expectExceptionMessage('Requested parameter "key" could not be found for "Froxlor:getSetting"'); + Froxlor::getLocal($admin_userdata)->getSetting(); + } + +} From a21f3c5a3f92eafd9d96f1cc7ac9705d128b12b4 Mon Sep 17 00:00:00 2001 From: Erik <32094350+z3dm4n@users.noreply.github.com> Date: Tue, 3 Apr 2018 12:59:14 +0200 Subject: [PATCH 0598/1335] added xenial.xml (#533) * added xenial.xml * fixed touch commands as in commit 3c802038f21dabcc558eb756a59b1bd6e280ede4 * changed a2dismod php to a2dismod php7.0 * fixed overlooked a2dismod php to a2dismod php7.0 --- lib/configfiles/xenial.xml | 4680 ++++++++++++++++++++++++++++++++++++ 1 file changed, 4680 insertions(+) create mode 100644 lib/configfiles/xenial.xml diff --git a/lib/configfiles/xenial.xml b/lib/configfiles/xenial.xml new file mode 100644 index 00000000..34877c1f --- /dev/null +++ b/lib/configfiles/xenial.xml @@ -0,0 +1,4680 @@ + + + + + + + + + + + {{settings.system.apacheconf_vhost}} + + + + + {{settings.system.apacheconf_vhost}} + + + + + + + {{settings.system.apacheconf_diroptions}} + + + + + {{settings.system.apacheconf_diroptions}} + + + + + + + + + + + {{settings.system.deactivateddocroot}} + + + + + + + + + //service[@type='http']/general/commands + + + + {{settings.system.use_ssl}} + + + + + {{settings.phpfpm.enabled}} + + + + + {{settings.phpfpm.enabled}} + + + FastCgiIpcDir + + + Require all granted + Require env REDIRECT_STATUS + + +]]> + + + + {{settings.system.leenabled}} + + + Require all granted + +]]> + + + + + + + + + "{{settings.system.letsencryptchallengepath}}/.well-known/acme-challenge/") + +# default listening port for IPv6 falls back to the IPv4 port +include_shell "/usr/share/lighttpd/use-ipv6.pl " + server.port +include_shell "/usr/share/lighttpd/create-mime.assign.pl" +include_shell "/usr/share/lighttpd/include-conf-enabled.pl" +]]> + + + //service[@type='http']/general/commands + + {{settings.system.apacheconf_vhost}} + + > /etc/lighttpd/lighttpd.conf]]> + + + {{settings.system.apacheconf_vhost}} + + > /etc/lighttpd/lighttpd.conf]]> + + + {{settings.system.apacheconf_diroptions}} + + > /etc/lighttpd/lighttpd.conf]]> + + + {{settings.system.apacheconf_diroptions}} + + > /etc/lighttpd/lighttpd.conf]]> + + + + + + + + + + {{settings.phpfpm.enabled}} + + {{settings.system.mod_fcgid}} + + + + + + + + + + + + + {{settings.system.leenabled}} + + + + + + {{settings.phpfpm.enabled}} + + {{settings.system.mod_fcgid}} + + + + + //service[@type='http']/general/commands + + {{settings.phpfpm.enabled}} + + {{settings.system.mod_fcgid}} + + + + + + + + + + + + > /etc/bind/named.conf.local]]> + + + + + + + + + +# add these entries to the list if any speficied: + +################################# +# allow-dnsupdate-from A global setting to allow DNS updates from these IP ranges. +# +# allow-dnsupdate-from=127.0.0.0/8,::1 + +################################# +# allow-recursion List of subnets that are allowed to recurse +# +allow-recursion=127.0.0.1 + +################################# +# also-notify When notifying a domain, also notify these nameservers +# +# also-notify= + +################################# +# any-to-tcp Answer ANY queries with tc=1, shunting to TCP +# +# any-to-tcp=no + +################################# +# cache-ttl Seconds to store packets in the PacketCache +# +# cache-ttl=20 + +################################# +# carbon-interval Number of seconds between carbon (graphite) updates +# +# carbon-interval=30 + +################################# +# carbon-ourname If set, overrides our reported hostname for carbon stats +# +# carbon-ourname= + +################################# +# carbon-server If set, send metrics in carbon (graphite) format to this server +# +# carbon-server= + +################################# +# chroot If set, chroot to this directory for more security +# +# chroot= + +################################# +# config-dir Location of configuration directory (pdns.conf) +# +config-dir=/etc/powerdns + +################################# +# config-name Name of this virtual configuration - will rename the binary image +# +# config-name= + +################################# +# control-console Debugging switch - don't use +# +# control-console=no + +################################# +# daemon Operate as a daemon +# +daemon=yes + +################################# +# default-ksk-algorithms Default KSK algorithms +# +# default-ksk-algorithms=rsasha256 + +################################# +# default-ksk-size Default KSK size (0 means default) +# +# default-ksk-size=0 + +################################# +# default-soa-mail mail address to insert in the SOA record if none set in the backend +# +# default-soa-mail= + +################################# +# default-soa-name name to insert in the SOA record if none set in the backend +# +# default-soa-name=a.misconfigured.powerdns.server + +################################# +# default-ttl Seconds a result is valid if not set otherwise +# +# default-ttl=3600 + +################################# +# default-zsk-algorithms Default ZSK algorithms +# +# default-zsk-algorithms=rsasha256 + +################################# +# default-zsk-size Default ZSK size (0 means default) +# +# default-zsk-size=0 + +################################# +# direct-dnskey Fetch DNSKEY RRs from backend during DNSKEY synthesis +# +# direct-dnskey=no + +################################# +# disable-axfr Disable zonetransfers but do allow TCP queries +# +# disable-axfr=no + +################################# +# disable-axfr-rectify Disable the rectify step during an outgoing AXFR. Only required for regression testing. +# +# disable-axfr-rectify=no + +################################# +# disable-tcp Do not listen to TCP queries +# +# disable-tcp=no + +################################# +# distributor-threads Default number of Distributor (backend) threads to start +# +# distributor-threads=3 + +################################# +# do-ipv6-additional-processing Do AAAA additional processing +# +# do-ipv6-additional-processing=yes + +################################# +# edns-subnet-processing If we should act on EDNS Subnet options +# +# edns-subnet-processing=no + +################################# +# entropy-source If set, read entropy from this file +# +# entropy-source=/dev/urandom + +################################# +# experimental-api-key REST API Static authentication key (required for API use) +# +# experimental-api-key= + +################################# +# experimental-api-readonly If the JSON API should disallow data modification +# +# experimental-api-readonly=no + +################################# +# experimental-dname-processing If we should support DNAME records +# +# experimental-dname-processing=no + +################################# +# experimental-dnsupdate Enable/Disable DNS update (RFC2136) support. Default is no. +# +# experimental-dnsupdate=no + +################################# +# experimental-json-interface If the webserver should serve JSON data +# +# experimental-json-interface=no + +################################# +# experimental-logfile Filename of the log file for JSON parser +# +# experimental-logfile=/var/log/pdns.log + +################################# +# forward-dnsupdate A global setting to allow DNS update packages that are for a Slave domain, to be forwarded to the master. +# +# forward-dnsupdate=yes + +################################# +# guardian Run within a guardian process +# +guardian=yes + +################################# +# include-dir Include *.conf files from this directory +# +# include-dir= + +################################# +# launch Which backends to launch and order to query them in +# +# launch= + +################################# +# load-modules Load this module - supply absolute or relative path +# +# load-modules= + +################################# +# local-address Local IP addresses to which we bind +# +local-address=,127.0.0.1 + +################################# +# local-address-nonexist-fail Fail to start if one or more of the local-address's do not exist on this server +# +# local-address-nonexist-fail=yes + +################################# +# local-ipv6 Local IP address to which we bind +# +# local-ipv6= + +################################# +# local-ipv6-nonexist-fail Fail to start if one or more of the local-ipv6 addresses do not exist on this server +# +# local-ipv6-nonexist-fail=yes + +################################# +# local-port The port on which we listen +# +# local-port=53 + +################################# +# log-dns-details If PDNS should log DNS non-erroneous details +# +# log-dns-details=no + +################################# +# log-dns-queries If PDNS should log all incoming DNS queries +# +# log-dns-queries=no + +################################# +# logging-facility Log under a specific facility +# +# logging-facility= + +################################# +# loglevel Amount of logging. Higher is more. Do not set below 3 +# +# loglevel=4 + +################################# +# lua-prequery-script Lua script with prequery handler +# +# lua-prequery-script= + +################################# +# master Act as a master +# +master=yes + +################################# +# max-cache-entries Maximum number of cache entries +# +# max-cache-entries=1000000 + +################################# +# max-ent-entries Maximum number of empty non-terminals in a zone +# +# max-ent-entries=100000 + +################################# +# max-nsec3-iterations Limit the number of NSEC3 hash iterations +# +# max-nsec3-iterations=500 + +################################# +# max-queue-length Maximum queuelength before considering situation lost +# +# max-queue-length=5000 + +################################# +# max-signature-cache-entries Maximum number of signatures cache entries +# +# max-signature-cache-entries= + +################################# +# max-tcp-connections Maximum number of TCP connections +# +# max-tcp-connections=10 + +################################# +# module-dir Default directory for modules +# +# module-dir=/usr/lib/TRIPLET/pdns + +################################# +# negquery-cache-ttl Seconds to store negative query results in the QueryCache +# +# negquery-cache-ttl=60 + +################################# +# no-shuffle Set this to prevent random shuffling of answers - for regression testing +# +# no-shuffle=off + +################################# +# only-notify Only send AXFR NOTIFY to these IP addresses or netmasks +# +# only-notify=0.0.0.0/0,::/0 + +################################# +# out-of-zone-additional-processing Do out of zone additional processing +# +# out-of-zone-additional-processing=yes + +################################# +# overload-queue-length Maximum queuelength moving to packetcache only +# +# overload-queue-length=0 + +################################# +# pipebackend-abi-version Version of the pipe backend ABI +# +# pipebackend-abi-version=1 + +################################# +# prevent-self-notification Don't send notifications to what we think is ourself +# +# prevent-self-notification=yes + +################################# +# query-cache-ttl Seconds to store query results in the QueryCache +# +# query-cache-ttl=20 + +################################# +# query-local-address Source IP address for sending queries +# +# query-local-address=0.0.0.0 + +################################# +# query-local-address6 Source IPv6 address for sending queries +# +# query-local-address6=:: + +################################# +# query-logging Hint backends that queries should be logged +# +# query-logging=no + +################################# +# queue-limit Maximum number of milliseconds to queue a query +# +# queue-limit=1500 + +################################# +# receiver-threads Default number of receiver threads to start +# +# receiver-threads=1 + +################################# +# recursive-cache-ttl Seconds to store packets for recursive queries in the PacketCache +# +# recursive-cache-ttl=10 + +################################# +# recursor If recursion is desired, IP address of a recursing nameserver +# +# recursor=no + +################################# +# retrieval-threads Number of AXFR-retrieval threads for slave operation +# +# retrieval-threads=2 + +################################# +# reuseport Enable higher performance on compliant kernels by using SO_REUSEPORT allowing each receiver thread to open its own socket +# +# reuseport=no + +################################# +# security-poll-suffix Domain name from which to query security update notifications +# +# security-poll-suffix=secpoll.powerdns.com. + +################################# +# send-root-referral Send out old-fashioned root-referral instead of ServFail in case of no authority +# +# send-root-referral=no + +################################# +# server-id Returned when queried for 'server.id' TXT or NSID, defaults to hostname - disabled or custom +# +# server-id= + +################################# +# setgid If set, change group id to this gid for more security +# +setgid=pdns + +################################# +# setuid If set, change user id to this uid for more security +# +setuid=pdns + +################################# +# signing-threads Default number of signer threads to start +# +# signing-threads=3 + +################################# +# slave Act as a slave +# +# slave=no + +################################# +# slave-cycle-interval Reschedule failed SOA serial checks once every .. seconds +# +# slave-cycle-interval=60 + +################################# +# slave-renotify If we should send out notifications for slaved updates +# +# slave-renotify=no + +################################# +# soa-expire-default Default SOA expire +# +# soa-expire-default=604800 + +################################# +# soa-minimum-ttl Default SOA minimum ttl +# +# soa-minimum-ttl=3600 + +################################# +# soa-refresh-default Default SOA refresh +# +# soa-refresh-default=10800 + +################################# +# soa-retry-default Default SOA retry +# +# soa-retry-default=3600 + +################################# +# socket-dir Where the controlsocket will live +# +# socket-dir=/var/run + +################################# +# tcp-control-address If set, PowerDNS can be controlled over TCP on this address +# +# tcp-control-address= + +################################# +# tcp-control-port If set, PowerDNS can be controlled over TCP on this address +# +# tcp-control-port=53000 + +################################# +# tcp-control-range If set, remote control of PowerDNS is possible over these networks only +# +# tcp-control-range=127.0.0.0/8, 10.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12, ::1/128, fe80::/10 + +################################# +# tcp-control-secret If set, PowerDNS can be controlled over TCP after passing this secret +# +# tcp-control-secret= + +################################# +# traceback-handler Enable the traceback handler (Linux only) +# +# traceback-handler=yes + +################################# +# trusted-notification-proxy IP address of incoming notification proxy +# +# trusted-notification-proxy= + +################################# +# udp-truncation-threshold Maximum UDP response size before we truncate +# +# udp-truncation-threshold=1680 + +################################# +# version-string PowerDNS version in packets - full, anonymous, powerdns or custom +# + +version-string=powerdns +################################# +# webserver Start a webserver for monitoring +# +# webserver=no + +################################# +# webserver-address IP Address of webserver to listen on +# +# webserver-address=127.0.0.1 + +################################# +# webserver-allow-from Webserver access is only allowed from these subnets +# +# webserver-allow-from=0.0.0.0/0,::/0 + +################################# +# webserver-password Password required for accessing the webserver +# +# webserver-password= + +################################# +# webserver-port Port of webserver to listen on +# +# webserver-port=8081 + +################################# +# webserver-print-arguments If the webserver should print arguments +# +# webserver-print-arguments=no + +# include froxlor-bind-specific config +include-dir=/etc/powerdns/froxlor/ +]]> + + + + + + + + + + + + + +# add these entries to the list if any speficied: + +################################# +# allow-dnsupdate-from A global setting to allow DNS updates from these IP ranges. +# +# allow-dnsupdate-from=127.0.0.0/8,::1 + +################################# +# allow-recursion List of subnets that are allowed to recurse +# +allow-recursion=127.0.0.1 + +################################# +# also-notify When notifying a domain, also notify these nameservers +# +# also-notify= + +################################# +# any-to-tcp Answer ANY queries with tc=1, shunting to TCP +# +# any-to-tcp=no + +################################# +# cache-ttl Seconds to store packets in the PacketCache +# +# cache-ttl=20 + +################################# +# carbon-interval Number of seconds between carbon (graphite) updates +# +# carbon-interval=30 + +################################# +# carbon-ourname If set, overrides our reported hostname for carbon stats +# +# carbon-ourname= + +################################# +# carbon-server If set, send metrics in carbon (graphite) format to this server +# +# carbon-server= + +################################# +# chroot If set, chroot to this directory for more security +# +# chroot= + +################################# +# config-dir Location of configuration directory (pdns.conf) +# +config-dir=/etc/powerdns + +################################# +# config-name Name of this virtual configuration - will rename the binary image +# +# config-name= + +################################# +# control-console Debugging switch - don't use +# +# control-console=no + +################################# +# daemon Operate as a daemon +# +daemon=yes + +################################# +# default-ksk-algorithms Default KSK algorithms +# +# default-ksk-algorithms=rsasha256 + +################################# +# default-ksk-size Default KSK size (0 means default) +# +# default-ksk-size=0 + +################################# +# default-soa-mail mail address to insert in the SOA record if none set in the backend +# +# default-soa-mail= + +################################# +# default-soa-name name to insert in the SOA record if none set in the backend +# +# default-soa-name=a.misconfigured.powerdns.server + +################################# +# default-ttl Seconds a result is valid if not set otherwise +# +# default-ttl=3600 + +################################# +# default-zsk-algorithms Default ZSK algorithms +# +# default-zsk-algorithms=rsasha256 + +################################# +# default-zsk-size Default ZSK size (0 means default) +# +# default-zsk-size=0 + +################################# +# direct-dnskey Fetch DNSKEY RRs from backend during DNSKEY synthesis +# +# direct-dnskey=no + +################################# +# disable-axfr Disable zonetransfers but do allow TCP queries +# +# disable-axfr=no + +################################# +# disable-axfr-rectify Disable the rectify step during an outgoing AXFR. Only required for regression testing. +# +# disable-axfr-rectify=no + +################################# +# disable-tcp Do not listen to TCP queries +# +# disable-tcp=no + +################################# +# distributor-threads Default number of Distributor (backend) threads to start +# +# distributor-threads=3 + +################################# +# do-ipv6-additional-processing Do AAAA additional processing +# +# do-ipv6-additional-processing=yes + +################################# +# edns-subnet-processing If we should act on EDNS Subnet options +# +# edns-subnet-processing=no + +################################# +# entropy-source If set, read entropy from this file +# +# entropy-source=/dev/urandom + +################################# +# experimental-api-key REST API Static authentication key (required for API use) +# +# experimental-api-key= + +################################# +# experimental-api-readonly If the JSON API should disallow data modification +# +# experimental-api-readonly=no + +################################# +# experimental-dname-processing If we should support DNAME records +# +# experimental-dname-processing=no + +################################# +# experimental-dnsupdate Enable/Disable DNS update (RFC2136) support. Default is no. +# +# experimental-dnsupdate=no + +################################# +# experimental-json-interface If the webserver should serve JSON data +# +# experimental-json-interface=no + +################################# +# experimental-logfile Filename of the log file for JSON parser +# +# experimental-logfile=/var/log/pdns.log + +################################# +# forward-dnsupdate A global setting to allow DNS update packages that are for a Slave domain, to be forwarded to the master. +# +# forward-dnsupdate=yes + +################################# +# guardian Run within a guardian process +# +guardian=yes + +################################# +# include-dir Include *.conf files from this directory +# +# include-dir= + +################################# +# launch Which backends to launch and order to query them in +# +# launch= +launch=bind + +################################# +# load-modules Load this module - supply absolute or relative path +# +# load-modules= + +################################# +# local-address Local IP addresses to which we bind +# +local-address=,127.0.0.1 + +################################# +# local-address-nonexist-fail Fail to start if one or more of the local-address's do not exist on this server +# +# local-address-nonexist-fail=yes + +################################# +# local-ipv6 Local IP address to which we bind +# +# local-ipv6= + +################################# +# local-ipv6-nonexist-fail Fail to start if one or more of the local-ipv6 addresses do not exist on this server +# +# local-ipv6-nonexist-fail=yes + +################################# +# local-port The port on which we listen +# +# local-port=53 + +################################# +# log-dns-details If PDNS should log DNS non-erroneous details +# +# log-dns-details=no + +################################# +# log-dns-queries If PDNS should log all incoming DNS queries +# +# log-dns-queries=no + +################################# +# logging-facility Log under a specific facility +# +# logging-facility= + +################################# +# loglevel Amount of logging. Higher is more. Do not set below 3 +# +# loglevel=4 + +################################# +# lua-prequery-script Lua script with prequery handler +# +# lua-prequery-script= + +################################# +# master Act as a master +# +master=yes + +################################# +# max-cache-entries Maximum number of cache entries +# +# max-cache-entries=1000000 + +################################# +# max-ent-entries Maximum number of empty non-terminals in a zone +# +# max-ent-entries=100000 + +################################# +# max-nsec3-iterations Limit the number of NSEC3 hash iterations +# +# max-nsec3-iterations=500 + +################################# +# max-queue-length Maximum queuelength before considering situation lost +# +# max-queue-length=5000 + +################################# +# max-signature-cache-entries Maximum number of signatures cache entries +# +# max-signature-cache-entries= + +################################# +# max-tcp-connections Maximum number of TCP connections +# +# max-tcp-connections=10 + +################################# +# module-dir Default directory for modules +# +# module-dir=/usr/lib/TRIPLET/pdns + +################################# +# negquery-cache-ttl Seconds to store negative query results in the QueryCache +# +# negquery-cache-ttl=60 + +################################# +# no-shuffle Set this to prevent random shuffling of answers - for regression testing +# +# no-shuffle=off + +################################# +# only-notify Only send AXFR NOTIFY to these IP addresses or netmasks +# +# only-notify=0.0.0.0/0,::/0 + +################################# +# out-of-zone-additional-processing Do out of zone additional processing +# +# out-of-zone-additional-processing=yes + +################################# +# overload-queue-length Maximum queuelength moving to packetcache only +# +# overload-queue-length=0 + +################################# +# pipebackend-abi-version Version of the pipe backend ABI +# +# pipebackend-abi-version=1 + +################################# +# prevent-self-notification Don't send notifications to what we think is ourself +# +# prevent-self-notification=yes + +################################# +# query-cache-ttl Seconds to store query results in the QueryCache +# +# query-cache-ttl=20 + +################################# +# query-local-address Source IP address for sending queries +# +# query-local-address=0.0.0.0 + +################################# +# query-local-address6 Source IPv6 address for sending queries +# +# query-local-address6=:: + +################################# +# query-logging Hint backends that queries should be logged +# +# query-logging=no + +################################# +# queue-limit Maximum number of milliseconds to queue a query +# +# queue-limit=1500 + +################################# +# receiver-threads Default number of receiver threads to start +# +# receiver-threads=1 + +################################# +# recursive-cache-ttl Seconds to store packets for recursive queries in the PacketCache +# +# recursive-cache-ttl=10 + +################################# +# recursor If recursion is desired, IP address of a recursing nameserver +# +# recursor=no + +################################# +# retrieval-threads Number of AXFR-retrieval threads for slave operation +# +# retrieval-threads=2 + +################################# +# reuseport Enable higher performance on compliant kernels by using SO_REUSEPORT allowing each receiver thread to open its own socket +# +# reuseport=no + +################################# +# security-poll-suffix Domain name from which to query security update notifications +# +# security-poll-suffix=secpoll.powerdns.com. + +################################# +# send-root-referral Send out old-fashioned root-referral instead of ServFail in case of no authority +# +# send-root-referral=no + +################################# +# server-id Returned when queried for 'server.id' TXT or NSID, defaults to hostname - disabled or custom +# +# server-id= + +################################# +# setgid If set, change group id to this gid for more security +# +setgid=pdns + +################################# +# setuid If set, change user id to this uid for more security +# +setuid=pdns + +################################# +# signing-threads Default number of signer threads to start +# +# signing-threads=3 + +################################# +# slave Act as a slave +# +# slave=no + +################################# +# slave-cycle-interval Reschedule failed SOA serial checks once every .. seconds +# +# slave-cycle-interval=60 + +################################# +# slave-renotify If we should send out notifications for slaved updates +# +# slave-renotify=no + +################################# +# soa-expire-default Default SOA expire +# +# soa-expire-default=604800 + +################################# +# soa-minimum-ttl Default SOA minimum ttl +# +# soa-minimum-ttl=3600 + +################################# +# soa-refresh-default Default SOA refresh +# +# soa-refresh-default=10800 + +################################# +# soa-retry-default Default SOA retry +# +# soa-retry-default=3600 + +################################# +# socket-dir Where the controlsocket will live +# +# socket-dir=/var/run + +################################# +# tcp-control-address If set, PowerDNS can be controlled over TCP on this address +# +# tcp-control-address= + +################################# +# tcp-control-port If set, PowerDNS can be controlled over TCP on this address +# +# tcp-control-port=53000 + +################################# +# tcp-control-range If set, remote control of PowerDNS is possible over these networks only +# +# tcp-control-range=127.0.0.0/8, 10.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12, ::1/128, fe80::/10 + +################################# +# tcp-control-secret If set, PowerDNS can be controlled over TCP after passing this secret +# +# tcp-control-secret= + +################################# +# traceback-handler Enable the traceback handler (Linux only) +# +# traceback-handler=yes + +################################# +# trusted-notification-proxy IP address of incoming notification proxy +# +# trusted-notification-proxy= + +################################# +# udp-truncation-threshold Maximum UDP response size before we truncate +# +# udp-truncation-threshold=1680 + +################################# +# version-string PowerDNS version in packets - full, anonymous, powerdns or custom +# + +version-string=powerdns +################################# +# webserver Start a webserver for monitoring +# +# webserver=no + +################################# +# webserver-address IP Address of webserver to listen on +# +# webserver-address=127.0.0.1 + +################################# +# webserver-allow-from Webserver access is only allowed from these subnets +# +# webserver-allow-from=0.0.0.0/0,::/0 + +################################# +# webserver-password Password required for accessing the webserver +# +# webserver-password= + +################################# +# webserver-port Port of webserver to listen on +# +# webserver-port=8081 + +################################# +# webserver-print-arguments If the webserver should print arguments +# +# webserver-print-arguments=no + +# include froxlor-bind-specific config +include-dir=/etc/powerdns/froxlor/ +]]> + + + + + named.conf + +# How often to check for zone changes. See 'Operation' section. +bind-check-interval=180 + +# Uncomment to enable Huffman compression on zone data. +# Currently saves around 20% of memory actually used, but slows down operation. +# bind-enable-huffman +]]> + + + + + + + + + + + + {{settings.system.vmail_gid}} + + + + + {{settings.system.vmail_uid}} + + + + + + + + + + + + + + + + + +password = +dbname = +hosts = +query = SELECT destination FROM mail_virtual WHERE email = '%s' AND trim(destination) <> '' +]]> + + + + +password = +dbname = +hosts = +query = SELECT domain FROM panel_domains WHERE domain = '%s' AND isemaildomain = '1' +]]> + + + + +password = +dbname = +expansion_limit = 1 +hosts = +query = SELECT CONCAT(homedir,maildir) FROM mail_users WHERE email = '%s' +]]> + + + + +password = +dbname = +hosts = +query = SELECT DISTINCT username FROM mail_users WHERE email in ((SELECT mail_virtual.email_full FROM mail_virtual WHERE mail_virtual.email = '%s' UNION SELECT mail_virtual.destination FROM mail_virtual WHERE mail_virtual.email = '%s')); +]]> + + + + +password = +dbname = +expansion_limit = 1 +hosts = +query = SELECT uid FROM mail_users WHERE email = '%s' +]]> + + + + +password = +dbname = +expansion_limit = 1 +hosts = +query = SELECT gid FROM mail_users WHERE email = '%s' +]]> + + + + +]]> + + + + + + + + + + + //service[@type='smtp']/general/commands[@index=1] + + //service[@type='smtp']/general/installs[@index=1] + + //service[@type='smtp']/general/commands[@index=2] + + + + +# SENDING MAIL +# +# The myorigin parameter specifies the domain that locally-posted +# mail appears to come from. The default is to append $myhostname, +# which is fine for small sites. If you run a domain with multiple +# machines, you should (1) change this to $mydomain and (2) set up +# a domain-wide alias database that aliases each user to +# user@that.users.mailhost. +# +# For the sake of consistency between sender and recipient addresses, +# myorigin also specifies the default domain name that is appended +# to recipient addresses that have no @domain part. +# +# Debian GNU/Linux specific: Specifying a file name will cause the +# first line of that file to be used as the name. The Debian default +# is /etc/mailname. +# +#myorigin = /etc/mailname +#myorigin = $myhostname +#myorigin = $mydomain + +# RECEIVING MAIL + +# The inet_interfaces parameter specifies the network interface +# addresses that this mail system receives mail on. By default, +# the software claims all active interfaces on the machine. The +# parameter also controls delivery of mail to user@[ip.address]. +# +# See also the proxy_interfaces parameter, for network addresses that +# are forwarded to us via a proxy or network address translator. +# +# Note: you need to stop/start Postfix when this parameter changes. +# +inet_interfaces = all +#inet_interfaces = $myhostname +#inet_interfaces = $myhostname, localhost + +# The proxy_interfaces parameter specifies the network interface +# addresses that this mail system receives mail on by way of a +# proxy or network address translation unit. This setting extends +# the address list specified with the inet_interfaces parameter. +# +# You must specify your proxy/NAT addresses when your system is a +# backup MX host for other domains, otherwise mail delivery loops +# will happen when the primary MX host is down. +# +#proxy_interfaces = +#proxy_interfaces = 1.2.3.4 + +# The mydestination parameter specifies the list of domains that this +# machine considers itself the final destination for. +# +# These domains are routed to the delivery agent specified with the +# local_transport parameter setting. By default, that is the UNIX +# compatible delivery agent that lookups all recipients in /etc/passwd +# and /etc/aliases or their equivalent. +# +# The default is $myhostname + localhost.$mydomain. On a mail domain +# gateway, you should also include $mydomain. +# +# Do not specify the names of virtual domains - those domains are +# specified elsewhere (see VIRTUAL_README). +# +# Do not specify the names of domains that this machine is backup MX +# host for. Specify those names via the relay_domains settings for +# the SMTP server, or use permit_mx_backup if you are lazy (see +# STANDARD_CONFIGURATION_README). +# +# The local machine is always the final destination for mail addressed +# to user@[the.net.work.address] of an interface that the mail system +# receives mail on (see the inet_interfaces parameter). +# +# Specify a list of host or domain names, /file/name or type:table +# patterns, separated by commas and/or whitespace. A /file/name +# pattern is replaced by its contents; a type:table is matched when +# a name matches a lookup key (the right-hand side is ignored). +# Continue long lines by starting the next line with whitespace. +# +# See also below, section "REJECTING MAIL FOR UNKNOWN LOCAL USERS". +# +#mydestination = $myhostname, localhost.$mydomain, localhost +mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain +#mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain, +# mail.$mydomain, www.$mydomain, ftp.$mydomain + +# REJECTING MAIL FOR UNKNOWN LOCAL USERS +# +# The local_recipient_maps parameter specifies optional lookup tables +# with all names or addresses of users that are local with respect +# to $mydestination, $inet_interfaces or $proxy_interfaces. +# +# If this parameter is defined, then the SMTP server will reject +# mail for unknown local users. This parameter is defined by default. +# +# To turn off local recipient checking in the SMTP server, specify +# local_recipient_maps = (i.e. empty). +# +# The default setting assumes that you use the default Postfix local +# delivery agent for local delivery. You need to update the +# local_recipient_maps setting if: +# +# - You define $mydestination domain recipients in files other than +# /etc/passwd, /etc/aliases, or the $virtual_alias_maps files. +# For example, you define $mydestination domain recipients in +# the $virtual_mailbox_maps files. +# +# - You redefine the local delivery agent in master.cf. +# +# - You redefine the "local_transport" setting in main.cf. +# +# - You use the "luser_relay", "mailbox_transport", or "fallback_transport" +# feature of the Postfix local delivery agent (see local(8)). +# +# Details are described in the LOCAL_RECIPIENT_README file. +# +# Beware: if the Postfix SMTP server runs chrooted, you probably have +# to access the passwd file via the proxymap service, in order to +# overcome chroot restrictions. The alternative, having a copy of +# the system passwd file in the chroot jail is just not practical. +# +# The right-hand side of the lookup tables is conveniently ignored. +# In the left-hand side, specify a bare username, an @domain.tld +# wild-card, or specify a user@domain.tld address. +# +#local_recipient_maps = unix:passwd.byname $alias_maps +#local_recipient_maps = proxy:unix:passwd.byname $alias_maps +#local_recipient_maps = + +# The unknown_local_recipient_reject_code specifies the SMTP server +# response code when a recipient domain matches $mydestination or +# ${proxy,inet}_interfaces, while $local_recipient_maps is non-empty +# and the recipient address or address local-part is not found. +# +# The default setting is 550 (reject mail) but it is safer to start +# with 450 (try again later) until you are certain that your +# local_recipient_maps settings are OK. +# +unknown_local_recipient_reject_code = 550 + +# TRUST AND RELAY CONTROL + +# The mynetworks parameter specifies the list of "trusted" SMTP +# clients that have more privileges than "strangers". +# +# In particular, "trusted" SMTP clients are allowed to relay mail +# through Postfix. See the smtpd_recipient_restrictions parameter +# in postconf(5). +# +# You can specify the list of "trusted" network addresses by hand +# or you can let Postfix do it for you (which is the default). +# +# By default (mynetworks_style = subnet), Postfix "trusts" SMTP +# clients in the same IP subnetworks as the local machine. +# On Linux, this does works correctly only with interfaces specified +# with the "ifconfig" command. +# +# Specify "mynetworks_style = class" when Postfix should "trust" SMTP +# clients in the same IP class A/B/C networks as the local machine. +# Don't do this with a dialup site - it would cause Postfix to "trust" +# your entire provider's network. Instead, specify an explicit +# mynetworks list by hand, as described below. +# +# Specify "mynetworks_style = host" when Postfix should "trust" +# only the local machine. +# +#mynetworks_style = class +#mynetworks_style = subnet +#mynetworks_style = host + +# Alternatively, you can specify the mynetworks list by hand, in +# which case Postfix ignores the mynetworks_style setting. +# +# Specify an explicit list of network/netmask patterns, where the +# mask specifies the number of bits in the network part of a host +# address. +# +# You can also specify the absolute pathname of a pattern file instead +# of listing the patterns here. Specify type:table for table-based lookups +# (the value on the table right-hand side is not used). +# +#mynetworks = 168.100.189.0/28, 127.0.0.0/8 +#mynetworks = $config_directory/mynetworks +#mynetworks = hash:/etc/postfix/network_table +mynetworks = 127.0.0.0/8 + +# The relay_domains parameter restricts what destinations this system will +# relay mail to. See the smtpd_recipient_restrictions description in +# postconf(5) for detailed information. +# +# By default, Postfix relays mail +# - from "trusted" clients (IP address matches $mynetworks) to any destination, +# - from "untrusted" clients to destinations that match $relay_domains or +# subdomains thereof, except addresses with sender-specified routing. +# The default relay_domains value is $mydestination. +# +# In addition to the above, the Postfix SMTP server by default accepts mail +# that Postfix is final destination for: +# - destinations that match $inet_interfaces or $proxy_interfaces, +# - destinations that match $mydestination +# - destinations that match $virtual_alias_domains, +# - destinations that match $virtual_mailbox_domains. +# These destinations do not need to be listed in $relay_domains. +# +# Specify a list of hosts or domains, /file/name patterns or type:name +# lookup tables, separated by commas and/or whitespace. Continue +# long lines by starting the next line with whitespace. A file name +# is replaced by its contents; a type:name table is matched when a +# (parent) domain appears as lookup key. +# +# NOTE: Postfix will not automatically forward mail for domains that +# list this system as their primary or backup MX host. See the +# permit_mx_backup restriction description in postconf(5). +# +#relay_domains = $mydestination + +# INTERNET OR INTRANET + +# The relayhost parameter specifies the default host to send mail to +# when no entry is matched in the optional transport(5) table. When +# no relayhost is given, mail is routed directly to the destination. +# +# On an intranet, specify the organizational domain name. If your +# internal DNS uses no MX records, specify the name of the intranet +# gateway host instead. +# +# In the case of SMTP, specify a domain, host, host:port, [host]:port, +# [address] or [address]:port; the form [host] turns off MX lookups. +# +# If you're connected via UUCP, see also the default_transport parameter. +# +#relayhost = $mydomain +#relayhost = [gateway.my.domain] +#relayhost = [mailserver.isp.tld] +#relayhost = uucphost +#relayhost = [an.ip.add.ress] + +# REJECTING UNKNOWN RELAY USERS +# +# The relay_recipient_maps parameter specifies optional lookup tables +# with all addresses in the domains that match $relay_domains. +# +# If this parameter is defined, then the SMTP server will reject +# mail for unknown relay users. This feature is off by default. +# +# The right-hand side of the lookup tables is conveniently ignored. +# In the left-hand side, specify an @domain.tld wild-card, or specify +# a user@domain.tld address. +# +#relay_recipient_maps = hash:/etc/postfix/relay_recipients + +# INPUT RATE CONTROL +# +# The in_flow_delay configuration parameter implements mail input +# flow control. This feature is turned on by default, although it +# still needs further development (it's disabled on SCO UNIX due +# to an SCO bug). +# +# A Postfix process will pause for $in_flow_delay seconds before +# accepting a new message, when the message arrival rate exceeds the +# message delivery rate. With the default 100 SMTP server process +# limit, this limits the mail inflow to 100 messages a second more +# than the number of messages delivered per second. +# +# Specify 0 to disable the feature. Valid delays are 0..10. +# +#in_flow_delay = 1s + +# ADDRESS REWRITING +# +# The ADDRESS_REWRITING_README document gives information about +# address masquerading or other forms of address rewriting including +# username->Firstname.Lastname mapping. + +# ADDRESS REDIRECTION (VIRTUAL DOMAIN) +# +# The VIRTUAL_README document gives information about the many forms +# of domain hosting that Postfix supports. + +# "USER HAS MOVED" BOUNCE MESSAGES +# +# See the discussion in the ADDRESS_REWRITING_README document. + +# TRANSPORT MAP +# +# See the discussion in the ADDRESS_REWRITING_README document. + +# ALIAS DATABASE +# +# The alias_maps parameter specifies the list of alias databases used +# by the local delivery agent. The default list is system dependent. +# +# On systems with NIS, the default is to search the local alias +# database, then the NIS alias database. See aliases(5) for syntax +# details. +# +# If you change the alias database, run "postalias /etc/aliases" (or +# wherever your system stores the mail alias file), or simply run +# "newaliases" to build the necessary DBM or DB file. +# +# It will take a minute or so before changes become visible. Use +# "postfix reload" to eliminate the delay. +# +#alias_maps = dbm:/etc/aliases +#alias_maps = hash:/etc/aliases +#alias_maps = hash:/etc/aliases, nis:mail.aliases +#alias_maps = netinfo:/aliases + +# The alias_database parameter specifies the alias database(s) that +# are built with "newaliases" or "sendmail -bi". This is a separate +# configuration parameter, because alias_maps (see above) may specify +# tables that are not necessarily all under control by Postfix. +# +#alias_database = dbm:/etc/aliases +#alias_database = dbm:/etc/mail/aliases +#alias_database = hash:/etc/aliases +#alias_database = hash:/etc/aliases, hash:/opt/majordomo/aliases + +# ADDRESS EXTENSIONS (e.g., user+foo) +# +# The recipient_delimiter parameter specifies the separator between +# user names and address extensions (user+foo). See canonical(5), +# local(8), relocated(5) and virtual(5) for the effects this has on +# aliases, canonical, virtual, relocated and .forward file lookups. +# Basically, the software tries user+foo and .forward+foo before +# trying user and .forward. +# +#recipient_delimiter = + + +# DELIVERY TO MAILBOX +# +# The home_mailbox parameter specifies the optional pathname of a +# mailbox file relative to a user's home directory. The default +# mailbox file is /var/spool/mail/user or /var/mail/user. Specify +# "Maildir/" for qmail-style delivery (the / is required). +# +#home_mailbox = Mailbox +#home_mailbox = Maildir/ + +# The mail_spool_directory parameter specifies the directory where +# UNIX-style mailboxes are kept. The default setting depends on the +# system type. +# +#mail_spool_directory = /var/mail +#mail_spool_directory = /var/spool/mail + +# The mailbox_command parameter specifies the optional external +# command to use instead of mailbox delivery. The command is run as +# the recipient with proper HOME, SHELL and LOGNAME environment settings. +# Exception: delivery for root is done as $default_user. +# +# Other environment variables of interest: USER (recipient username), +# EXTENSION (address extension), DOMAIN (domain part of address), +# and LOCAL (the address localpart). +# +# Unlike other Postfix configuration parameters, the mailbox_command +# parameter is not subjected to $parameter substitutions. This is to +# make it easier to specify shell syntax (see example below). +# +# Avoid shell meta characters because they will force Postfix to run +# an expensive shell process. Procmail alone is expensive enough. +# +# IF YOU USE THIS TO DELIVER MAIL SYSTEM-WIDE, YOU MUST SET UP AN +# ALIAS THAT FORWARDS MAIL FOR ROOT TO A REAL USER. +# +mailbox_command = /usr/lib/dovecot/deliver +#mailbox_command = /usr/bin/procmail -a "$EXTENSION" + +# The mailbox_transport specifies the optional transport in master.cf +# to use after processing aliases and .forward files. This parameter +# has precedence over the mailbox_command, fallback_transport and +# luser_relay parameters. +# +# Specify a string of the form transport:nexthop, where transport is +# the name of a mail delivery transport defined in master.cf. The +# :nexthop part is optional. For more details see the sample transport +# configuration file. +# +# NOTE: if you use this feature for accounts not in the UNIX password +# file, then you must update the "local_recipient_maps" setting in +# the main.cf file, otherwise the SMTP server will reject mail for +# non-UNIX accounts with "User unknown in local recipient table". +# +# Cyrus IMAP over LMTP. Specify ``lmtpunix cmd="lmtpd" +# listen="/var/imap/socket/lmtp" prefork=0'' in cyrus.conf. +#mailbox_transport = lmtp:unix:/var/imap/socket/lmtp +# +# Cyrus IMAP via command line. Uncomment the "cyrus...pipe" and +# subsequent line in master.cf. +#mailbox_transport = cyrus + +# The fallback_transport specifies the optional transport in master.cf +# to use for recipients that are not found in the UNIX passwd database. +# This parameter has precedence over the luser_relay parameter. +# +# Specify a string of the form transport:nexthop, where transport is +# the name of a mail delivery transport defined in master.cf. The +# :nexthop part is optional. For more details see the sample transport +# configuration file. +# +# NOTE: if you use this feature for accounts not in the UNIX password +# file, then you must update the "local_recipient_maps" setting in +# the main.cf file, otherwise the SMTP server will reject mail for +# non-UNIX accounts with "User unknown in local recipient table". +# +#fallback_transport = lmtp:unix:/file/name +#fallback_transport = cyrus +#fallback_transport = + +# The luser_relay parameter specifies an optional destination address +# for unknown recipients. By default, mail for unknown@$mydestination, +# unknown@[$inet_interfaces] or unknown@[$proxy_interfaces] is returned +# as undeliverable. +# +# The following expansions are done on luser_relay: $user (recipient +# username), $shell (recipient shell), $home (recipient home directory), +# $recipient (full recipient address), $extension (recipient address +# extension), $domain (recipient domain), $local (entire recipient +# localpart), $recipient_delimiter. Specify ${name?value} or +# ${name:value} to expand value only when $name does (does not) exist. +# +# luser_relay works only for the default Postfix local delivery agent. +# +# NOTE: if you use this feature for accounts not in the UNIX password +# file, then you must specify "local_recipient_maps =" (i.e. empty) in +# the main.cf file, otherwise the SMTP server will reject mail for +# non-UNIX accounts with "User unknown in local recipient table". +# +#luser_relay = $user@other.host +#luser_relay = $local@other.host +#luser_relay = admin+$local + +# JUNK MAIL CONTROLS +# +# The controls listed here are only a very small subset. The file +# SMTPD_ACCESS_README provides an overview. + +# The header_checks parameter specifies an optional table with patterns +# that each logical message header is matched against, including +# headers that span multiple physical lines. +# +# By default, these patterns also apply to MIME headers and to the +# headers of attached messages. With older Postfix versions, MIME and +# attached message headers were treated as body text. +# +# For details, see "man header_checks". +# +#header_checks = regexp:/etc/postfix/header_checks + +# FAST ETRN SERVICE +# +# Postfix maintains per-destination logfiles with information about +# deferred mail, so that mail can be flushed quickly with the SMTP +# "ETRN domain.tld" command, or by executing "sendmail -qRdomain.tld". +# See the ETRN_README document for a detailed description. +# +# The fast_flush_domains parameter controls what destinations are +# eligible for this service. By default, they are all domains that +# this server is willing to relay mail to. +# +#fast_flush_domains = $relay_domains + +# SHOW SOFTWARE VERSION OR NOT +# +# The smtpd_banner parameter specifies the text that follows the 220 +# code in the SMTP server's greeting banner. Some people like to see +# the mail version advertised. By default, Postfix shows no version. +# +# You MUST specify $myhostname at the start of the text. That is an +# RFC requirement. Postfix itself does not care. +# +#smtpd_banner = $myhostname ESMTP $mail_name +#smtpd_banner = $myhostname ESMTP $mail_name ($mail_version) +smtpd_banner = $myhostname ESMTP $mail_name (Debian/GNU) + + +# PARALLEL DELIVERY TO THE SAME DESTINATION +# +# How many parallel deliveries to the same user or domain? With local +# delivery, it does not make sense to do massively parallel delivery +# to the same user, because mailbox updates must happen sequentially, +# and expensive pipelines in .forward files can cause disasters when +# too many are run at the same time. With SMTP deliveries, 10 +# simultaneous connections to the same domain could be sufficient to +# raise eyebrows. +# +# Each message delivery transport has its XXX_destination_concurrency_limit +# parameter. The default is $default_destination_concurrency_limit for +# most delivery transports. For the local delivery agent the default is 2. + +#local_destination_concurrency_limit = 2 +#default_destination_concurrency_limit = 20 + +# DEBUGGING CONTROL +# +# The debug_peer_level parameter specifies the increment in verbose +# logging level when an SMTP client or server host name or address +# matches a pattern in the debug_peer_list parameter. +# +#debug_peer_level = 2 + +# The debug_peer_list parameter specifies an optional list of domain +# or network patterns, /file/name patterns or type:name tables. When +# an SMTP client or server host name or address matches a pattern, +# increase the verbose logging level by the amount specified in the +# debug_peer_level parameter. +# +#debug_peer_list = 127.0.0.1 +#debug_peer_list = some.domain + +# The debugger_command specifies the external command that is executed +# when a Postfix daemon program is run with the -D option. +# +# Use "command .. & sleep 5" so that the debugger can attach before +# the process marches on. If you use an X-based debugger, be sure to +# set up your XAUTHORITY environment variable before starting Postfix. +# +debugger_command = + PATH=/bin:/usr/bin:/usr/local/bin:/usr/X11R6/bin + ddd $daemon_directory/$process_name $process_id & sleep 5 + +# If you can't use X, use this to capture the call stack when a +# daemon crashes. The result is in a file in the configuration +# directory, and is named after the process name and the process ID. +# +# debugger_command = +# PATH=/bin:/usr/bin:/usr/local/bin; export PATH; (echo cont; +# echo where) | gdb $daemon_directory/$process_name $process_id 2>&1 +# >$config_directory/$process_name.$process_id.log & sleep 5 +# +# Another possibility is to run gdb under a detached screen session. +# To attach to the screen sesssion, su root and run "screen -r +# " where uniquely matches one of the detached +# sessions (from "screen -list"). +# +# debugger_command = +# PATH=/bin:/usr/bin:/sbin:/usr/sbin; export PATH; screen +# -dmS $process_name gdb $daemon_directory/$process_name +# $process_id & sleep 1 + +# INSTALL-TIME CONFIGURATION INFORMATION +# +# The following parameters are used when installing a new Postfix version. +# +# sendmail_path: The full pathname of the Postfix sendmail command. +# This is the Sendmail-compatible mail posting interface. +# +sendmail_path = /usr/sbin/sendmail + +# newaliases_path: The full pathname of the Postfix newaliases command. +# This is the Sendmail-compatible command to build alias databases. +# +newaliases_path = /usr/bin/newaliases + +# mailq_path: The full pathname of the Postfix mailq command. This +# is the Sendmail-compatible mail queue listing command. +# +mailq_path = /usr/bin/mailq + +# setgid_group: The group for mail submission and queue management +# commands. This must be a group name with a numerical group ID that +# is not shared with other accounts, not even with the Postfix account. +# +setgid_group = postdrop + +# html_directory: The location of the Postfix HTML documentation. +# +html_directory = no + +# manpage_directory: The location of the Postfix on-line manual pages. +# +manpage_directory = /usr/share/man + +# sample_directory: The location of the Postfix sample configuration files. +# This parameter is obsolete as of Postfix 2.1. +# +sample_directory = /usr/share/doc/postfix + +# readme_directory: The location of the Postfix README files. +# +readme_directory = /usr/share/doc/postfix +inet_protocols = ipv4 + +append_dot_mydomain = no +biff = no +smtpd_helo_required = yes +smtpd_recipient_restrictions = permit_mynetworks, + permit_sasl_authenticated, + reject_unauth_destination, + reject_unauth_pipelining, + reject_non_fqdn_recipient +smtpd_sender_restrictions = permit_mynetworks, + reject_sender_login_mismatch, + permit_sasl_authenticated, + reject_unknown_helo_hostname, + reject_unknown_recipient_domain, + reject_unknown_sender_domain +smtpd_client_restrictions = permit_mynetworks, + permit_sasl_authenticated, + reject_unknown_client_hostname + +# Postfix 2.10 requires this option. Postfix < 2.10 ignores this. +# The option is intentionally left empty. +smtpd_relay_restrictions = + +# Maximum size of Message in bytes (50MB) +message_size_limit = 52428800 + +## SASL Auth Settings +smtpd_sasl_auth_enable = yes +smtpd_sasl_local_domain = $myhostname +broken_sasl_auth_clients = yes +## Dovecot Settings for deliver, SASL Auth and virtual transport +smtpd_sasl_type = dovecot +virtual_transport = dovecot +dovecot_destination_recipient_limit = 1 +smtpd_sasl_path = private/auth + +# Virtual delivery settings +virtual_mailbox_base = / +virtual_mailbox_maps = mysql:/etc/postfix/mysql-virtual_mailbox_maps.cf +virtual_mailbox_domains = mysql:/etc/postfix/mysql-virtual_mailbox_domains.cf +virtual_alias_maps = mysql:/etc/postfix/mysql-virtual_alias_maps.cf +smtpd_sender_login_maps = mysql:/etc/postfix/mysql-virtual_sender_permissions.cf +virtual_uid_maps = static: +virtual_gid_maps = static: + +# Local delivery settings +local_transport = local +alias_maps = $alias_database + +# Default Mailbox size, is set to 0 which means unlimited! +mailbox_size_limit = 0 +virtual_mailbox_limit = 0 + +### TLS settings +### +## TLS for outgoing mails from the server to another server +#smtp_tls_security_level = may +#smtp_tls_note_starttls_offer = yes +## TLS for incoming connections (clients or other mail servers) +#smtpd_tls_security_level = may +#smtpd_tls_cert_file = /etc/ssl/server/.pem +#smtpd_tls_key_file = $smtpd_tls_cert_file +#smtpd_tls_CAfile = /etc/ssl/certs/ca-certificates.crt +#smtpd_tls_loglevel = 1 +#smtpd_tls_received_header = yes +]]> + + + //service[@type='smtp']/general/files[@index=0] + + + + + //service[@type='smtp']/general/commands[@index=3] + + + + + + + + + + + + + to select which instance is used (an alternative +# to -c ). The instance name is also added to Dovecot processes +# in ps output. +#instance_name = dovecot + +# Greeting message for clients. +#login_greeting = Dovecot ready. + +# Space separated list of trusted network ranges. Connections from these +# IPs are allowed to override their IP addresses and ports (for logging and +# for authentication checks). disable_plaintext_auth is also ignored for +# these networks. Typically you'd specify your IMAP proxy servers here. +#login_trusted_networks = + +# Space separated list of login access check sockets (e.g. tcpwrap) +#login_access_sockets = + +# With proxy_maybe=yes if proxy destination matches any of these IPs, don't do +# proxying. This isn't necessary normally, but may be useful if the destination +# IP is e.g. a load balancer's IP. +#auth_proxy_self = + +# Show more verbose process titles (in ps). Currently shows user name and +# IP address. Useful for seeing who are actually using the IMAP processes +# (eg. shared mailboxes or if same uid is used for multiple accounts). +#verbose_proctitle = no + +# Should all processes be killed when Dovecot master process shuts down. +# Setting this to "no" means that Dovecot can be upgraded without +# forcing existing client connections to close (although that could also be +# a problem if the upgrade is e.g. because of a security fix). +#shutdown_clients = yes + +# If non-zero, run mail commands via this many connections to doveadm server, +# instead of running them directly in the same process. +#doveadm_worker_count = 0 +# UNIX socket or host:port used for connecting to doveadm server +#doveadm_socket_path = doveadm-server + +# Space separated list of environment variables that are preserved on Dovecot +# startup and passed down to all of its child processes. You can also give +# key=value pairs to always set specific settings. +#import_environment = TZ + +## +## Dictionary server settings +## + +# Dictionary can be used to store key=value lists. This is used by several +# plugins. The dictionary can be accessed either directly or though a +# dictionary server. The following dict block maps dictionary names to URIs +# when the server is used. These can then be referenced using URIs in format +# "proxy::". + +dict { + #quota = mysql:/etc/dovecot/dovecot-dict-sql.conf.ext + #expire = sqlite:/etc/dovecot/dovecot-dict-sql.conf.ext +} + +# Most of the actual configuration gets included below. The filenames are +# first sorted by their ASCII value and parsed in that order. The 00-prefixes +# in filenames are intended to make it easier to understand the ordering. +!include conf.d/*.conf + +# A config file can also tried to be included without giving an error if +# it's not found: +!include_try local.conf +]]> + + + + dbname= user= password= + +# Default password scheme. +# +# List of supported schemes is in +# http://wiki2.dovecot.org/Authentication/PasswordSchemes +# +default_pass_scheme = CRYPT + +# passdb query to retrieve the password. It can return fields: +# password - The user's password. This field must be returned. +# user - user@domain from the database. Needed with case-insensitive lookups. +# username and domain - An alternative way to represent the "user" field. +# +# The "user" field is often necessary with case-insensitive lookups to avoid +# e.g. "name" and "nAme" logins creating two different mail directories. If +# your user and domain names are in separate fields, you can return "username" +# and "domain" fields instead of "user". +# +# The query can also return other fields which have a special meaning, see +# http://wiki2.dovecot.org/PasswordDatabase/ExtraFields +# +# Commonly used available substitutions (see http://wiki2.dovecot.org/Variables +# for full list): +# %u = entire user@domain +# %n = user part of user@domain +# %d = domain part of user@domain +# +# Note that these can be used only as input to SQL query. If the query outputs +# any of these substitutions, they're not touched. Otherwise it would be +# difficult to have eg. usernames containing '%' characters. +# +# Example: +# password_query = SELECT userid AS user, pw AS password \ +# FROM users WHERE userid = '%u' AND active = 'Y' +# +#password_query = \ +# SELECT username, domain, password \ +# FROM users WHERE username = '%n' AND domain = '%d' + +# userdb query to retrieve the user information. It can return fields: +# uid - System UID (overrides mail_uid setting) +# gid - System GID (overrides mail_gid setting) +# home - Home directory +# mail - Mail location (overrides mail_location setting) +# +# None of these are strictly required. If you use a single UID and GID, and +# home or mail directory fits to a template string, you could use userdb static +# instead. For a list of all fields that can be returned, see +# http://wiki2.dovecot.org/UserDatabase/ExtraFields +# +# Examples: +# user_query = SELECT home, uid, gid FROM users WHERE userid = '%u' +# user_query = SELECT dir AS home, user AS uid, group AS gid FROM users where userid = '%u' +# user_query = SELECT home, 501 AS uid, 501 AS gid FROM users WHERE userid = '%u' +# +#user_query = \ +# SELECT home, uid, gid \ +# FROM users WHERE username = '%n' AND domain = '%d' +user_query = SELECT CONCAT(homedir, maildir) AS home, CONCAT('maildir:', homedir, maildir) AS mail, uid, gid, CONCAT('*:storage=', quota, 'M') as quota_rule FROM mail_users WHERE (username = '%u' OR email = '%u') + +# If you wish to avoid two SQL lookups (passdb + userdb), you can use +# userdb prefetch instead of userdb sql in dovecot.conf. In that case you'll +# also have to return userdb fields in password_query prefixed with "userdb_" +# string. For example: +#password_query = \ +# SELECT userid AS user, password, \ +# home AS userdb_home, uid AS userdb_uid, gid AS userdb_gid \ +# FROM users WHERE userid = '%u' +password_query = SELECT username AS user, password_enc AS password, CONCAT(homedir, maildir) AS userdb_home, uid AS userdb_uid, gid AS userdb_gid, CONCAT('maildir:', homedir, maildir) AS userdb_mail, CONCAT('*:storage=', quota, 'M') as userdb_quota_rule FROM mail_users WHERE (username = '%u' OR email = '%u') AND ((imap = 1 AND '%Ls' = 'imap') OR (pop3 = 1 AND '%Ls' = 'pop3') OR '%Ls' = 'smtp' OR '%Ls' = 'sieve') + +# Query to get a list of all usernames. +#iterate_query = SELECT username AS user FROM users +]]> + + + + to characters. For example "#@/@" means +# that '#' and '/' characters are translated to '@'. +#auth_username_translation = + +# Username formatting before it's looked up from databases. You can use +# the standard variables here, eg. %Lu would lowercase the username, %n would +# drop away the domain if it was given, or "%n-AT-%d" would change the '@' into +# "-AT-". This translation is done after auth_username_translation changes. +#auth_username_format = %Lu + +# If you want to allow master users to log in by specifying the master +# username within the normal username string (ie. not using SASL mechanism's +# support for it), you can specify the separator character here. The format +# is then . UW-IMAP uses "*" as the +# separator, so that could be a good choice. +#auth_master_user_separator = + +# Username to use for users logging in with ANONYMOUS SASL mechanism +#auth_anonymous_username = anonymous + +# Maximum number of dovecot-auth worker processes. They're used to execute +# blocking passdb and userdb queries (eg. MySQL and PAM). They're +# automatically created and destroyed as needed. +#auth_worker_max_count = 30 + +# Host name to use in GSSAPI principal names. The default is to use the +# name returned by gethostname(). Use "$ALL" (with quotes) to allow all keytab +# entries. +#auth_gssapi_hostname = + +# Kerberos keytab to use for the GSSAPI mechanism. Will use the system +# default (usually /etc/krb5.keytab) if not specified. You may need to change +# the auth service to run as root to be able to read this file. +#auth_krb5_keytab = + +# Do NTLM and GSS-SPNEGO authentication using Samba's winbind daemon and +# ntlm_auth helper. +#auth_use_winbind = no + +# Path for Samba's ntlm_auth helper binary. +#auth_winbind_helper_path = /usr/bin/ntlm_auth + +# Time to delay before replying to failed authentications. +#auth_failure_delay = 2 secs + +# Require a valid SSL client certificate or the authentication fails. +#auth_ssl_require_client_cert = no + +# Take the username from client's SSL certificate, using +# X509_NAME_get_text_by_NID() which returns the subject's DN's +# CommonName. +#auth_ssl_username_from_cert = no + +# Space separated list of wanted authentication mechanisms: +# plain login digest-md5 cram-md5 ntlm rpa apop anonymous gssapi otp skey +# gss-spnego +# NOTE: See also disable_plaintext_auth setting. +auth_mechanisms = plain login + +## +## Password and user databases +## + +# +# Password database is used to verify user's password (and nothing more). +# You can have multiple passdbs and userdbs. This is useful if you want to +# allow both system users (/etc/passwd) and virtual users to login without +# duplicating the system users into virtual database. +# +# +# +# User database specifies where mails are located and what user/group IDs +# own them. For single-UID configuration use "static" userdb. +# +# + +#!include auth-deny.conf.ext +#!include auth-master.conf.ext + +#!include auth-system.conf.ext +!include auth-sql.conf.ext +#!include auth-ldap.conf.ext +#!include auth-passwdfile.conf.ext +#!include auth-checkpassword.conf.ext +#!include auth-vpopmail.conf.ext +#!include auth-static.conf.ext +]]> + + + + +# +mail_location = mbox:~/mail:INBOX=/var/mail/%u + +# If you need to set multiple mailbox locations or want to change default +# namespace settings, you can do it by defining namespace sections. +# +# You can have private, shared and public namespaces. Private namespaces +# are for user's personal mails. Shared namespaces are for accessing other +# users' mailboxes that have been shared. Public namespaces are for shared +# mailboxes that are managed by sysadmin. If you create any shared or public +# namespaces you'll typically want to enable ACL plugin also, otherwise all +# users can access all the shared mailboxes, assuming they have permissions +# on filesystem level to do so. +namespace inbox { + # Namespace type: private, shared or public + #type = private + + # Hierarchy separator to use. You should use the same separator for all + # namespaces or some clients get confused. '/' is usually a good one. + # The default however depends on the underlying mail storage format. + #separator = + + # Prefix required to access this namespace. This needs to be different for + # all namespaces. For example "Public/". + #prefix = + + # Physical location of the mailbox. This is in same format as + # mail_location, which is also the default for it. + #location = + + # There can be only one INBOX, and this setting defines which namespace + # has it. + inbox = yes + + # If namespace is hidden, it's not advertised to clients via NAMESPACE + # extension. You'll most likely also want to set list=no. This is mostly + # useful when converting from another server with different namespaces which + # you want to deprecate but still keep working. For example you can create + # hidden namespaces with prefixes "~/mail/", "~%u/mail/" and "mail/". + #hidden = no + + # Show the mailboxes under this namespace with LIST command. This makes the + # namespace visible for clients that don't support NAMESPACE extension. + # "children" value lists child mailboxes, but hides the namespace prefix. + #list = yes + + # Namespace handles its own subscriptions. If set to "no", the parent + # namespace handles them (empty prefix should always have this as "yes") + #subscriptions = yes +} + +# Example shared namespace configuration +#namespace { + #type = shared + #separator = / + + # Mailboxes are visible under "shared/user@domain/" + # %%n, %%d and %%u are expanded to the destination user. + #prefix = shared/%%u/ + + # Mail location for other users' mailboxes. Note that %variables and ~/ + # expands to the logged in user's data. %%n, %%d, %%u and %%h expand to the + # destination user's data. + #location = maildir:%%h/Maildir:INDEX=~/Maildir/shared/%%u + + # Use the default namespace for saving subscriptions. + #subscriptions = no + + # List the shared/ namespace only if there are visible shared mailboxes. + #list = children +#} +# Should shared INBOX be visible as "shared/user" or "shared/user/INBOX"? +#mail_shared_explicit_inbox = no + +# System user and group used to access mails. If you use multiple, userdb +# can override these by returning uid or gid fields. You can use either numbers +# or names. +#mail_uid = +#mail_gid = + +# Group to enable temporarily for privileged operations. Currently this is +# used only with INBOX when either its initial creation or dotlocking fails. +# Typically this is set to "mail" to give access to /var/mail. +#mail_privileged_group = + +# Grant access to these supplementary groups for mail processes. Typically +# these are used to set up access to shared mailboxes. Note that it may be +# dangerous to set these if users can create symlinks (e.g. if "mail" group is +# set here, ln -s /var/mail ~/mail/var could allow a user to delete others' +# mailboxes, or ln -s /secret/shared/box ~/mail/mybox would allow reading it). +mail_access_groups = vmail + +# Allow full filesystem access to clients. There's no access checks other than +# what the operating system does for the active UID/GID. It works with both +# maildir and mboxes, allowing you to prefix mailboxes names with eg. /path/ +# or ~user/. +#mail_full_filesystem_access = no + +# Dictionary for key=value mailbox attributes. Currently used by URLAUTH, but +# soon intended to be used by METADATA as well. +#mail_attribute_dict = + +## +## Mail processes +## + +# Don't use mmap() at all. This is required if you store indexes to shared +# filesystems (NFS or clustered filesystem). +#mmap_disable = no + +# Rely on O_EXCL to work when creating dotlock files. NFS supports O_EXCL +# since version 3, so this should be safe to use nowadays by default. +#dotlock_use_excl = yes + +# When to use fsync() or fdatasync() calls: +# optimized (default): Whenever necessary to avoid losing important data +# always: Useful with e.g. NFS when write()s are delayed +# never: Never use it (best performance, but crashes can lose data) +#mail_fsync = optimized + +# Locking method for index files. Alternatives are fcntl, flock and dotlock. +# Dotlocking uses some tricks which may create more disk I/O than other locking +# methods. NFS users: flock doesn't work, remember to change mmap_disable. +#lock_method = fcntl + +# Directory in which LDA/LMTP temporarily stores incoming mails >128 kB. +#mail_temp_dir = /tmp + +# Valid UID range for users, defaults to 500 and above. This is mostly +# to make sure that users can't log in as daemons or other system users. +# Note that denying root logins is hardcoded to dovecot binary and can't +# be done even if first_valid_uid is set to 0. +#first_valid_uid = 500 +#last_valid_uid = 0 + +# Valid GID range for users, defaults to non-root/wheel. Users having +# non-valid GID as primary group ID aren't allowed to log in. If user +# belongs to supplementary groups with non-valid GIDs, those groups are +# not set. +#first_valid_gid = 1 +#last_valid_gid = 0 + +# Maximum allowed length for mail keyword name. It's only forced when trying +# to create new keywords. +#mail_max_keyword_length = 50 + +# ':' separated list of directories under which chrooting is allowed for mail +# processes (ie. /var/mail will allow chrooting to /var/mail/foo/bar too). +# This setting doesn't affect login_chroot, mail_chroot or auth chroot +# settings. If this setting is empty, "/./" in home dirs are ignored. +# WARNING: Never add directories here which local users can modify, that +# may lead to root exploit. Usually this should be done only if you don't +# allow shell access for users. +#valid_chroot_dirs = + +# Default chroot directory for mail processes. This can be overridden for +# specific users in user database by giving /./ in user's home directory +# (eg. /home/./user chroots into /home). Note that usually there is no real +# need to do chrooting, Dovecot doesn't allow users to access files outside +# their mail directory anyway. If your home directories are prefixed with +# the chroot directory, append "/." to mail_chroot. +#mail_chroot = + +# UNIX socket path to master authentication server to find users. +# This is used by imap (for shared users) and lda. +#auth_socket_path = /var/run/dovecot/auth-userdb + +# Directory where to look up mail plugins. +#mail_plugin_dir = /usr/lib/dovecot/modules + +# Space separated list of plugins to load for all services. Plugins specific to +# IMAP, LDA, etc. are added to this list in their own .conf files. +#mail_plugins = + +## +## Mailbox handling optimizations +## + +# Mailbox list indexes can be used to optimize IMAP STATUS commands. They are +# also required for IMAP NOTIFY extension to be enabled. +#mailbox_list_index = no + +# The minimum number of mails in a mailbox before updates are done to cache +# file. This allows optimizing Dovecot's behavior to do less disk writes at +# the cost of more disk reads. +#mail_cache_min_mail_count = 0 + +# When IDLE command is running, mailbox is checked once in a while to see if +# there are any new mails or other changes. This setting defines the minimum +# time to wait between those checks. Dovecot can also use dnotify, inotify and +# kqueue to find out immediately when changes occur. +#mailbox_idle_check_interval = 30 secs + +# Save mails with CR+LF instead of plain LF. This makes sending those mails +# take less CPU, especially with sendfile() syscall with Linux and FreeBSD. +# But it also creates a bit more disk I/O which may just make it slower. +# Also note that if other software reads the mboxes/maildirs, they may handle +# the extra CRs wrong and cause problems. +#mail_save_crlf = no + +# Max number of mails to keep open and prefetch to memory. This only works with +# some mailbox formats and/or operating systems. +#mail_prefetch_count = 0 + +# How often to scan for stale temporary files and delete them (0 = never). +# These should exist only after Dovecot dies in the middle of saving mails. +#mail_temp_scan_interval = 1w + +## +## Maildir-specific settings +## + +# By default LIST command returns all entries in maildir beginning with a dot. +# Enabling this option makes Dovecot return only entries which are directories. +# This is done by stat()ing each entry, so it causes more disk I/O. +# (For systems setting struct dirent->d_type, this check is free and it's +# done always regardless of this setting) +#maildir_stat_dirs = no + +# When copying a message, do it with hard links whenever possible. This makes +# the performance much better, and it's unlikely to have any side effects. +#maildir_copy_with_hardlinks = yes + +# Assume Dovecot is the only MUA accessing Maildir: Scan cur/ directory only +# when its mtime changes unexpectedly or when we can't find the mail otherwise. +#maildir_very_dirty_syncs = no + +# If enabled, Dovecot doesn't use the S= in the Maildir filenames for +# getting the mail's physical size, except when recalculating Maildir++ quota. +# This can be useful in systems where a lot of the Maildir filenames have a +# broken size. The performance hit for enabling this is very small. +#maildir_broken_filename_sizes = no + +# Always move mails from new/ directory to cur/, even when the \Recent flags +# aren't being reset. +#maildir_empty_new = no + +## +## mbox-specific settings +## + +# Which locking methods to use for locking mbox. There are four available: +# dotlock: Create .lock file. This is the oldest and most NFS-safe +# solution. If you want to use /var/mail/ like directory, the users +# will need write access to that directory. +# dotlock_try: Same as dotlock, but if it fails because of permissions or +# because there isn't enough disk space, just skip it. +# fcntl : Use this if possible. Works with NFS too if lockd is used. +# flock : May not exist in all systems. Doesn't work with NFS. +# lockf : May not exist in all systems. Doesn't work with NFS. +# +# You can use multiple locking methods; if you do the order they're declared +# in is important to avoid deadlocks if other MTAs/MUAs are using multiple +# locking methods as well. Some operating systems don't allow using some of +# them simultaneously. +# +# The Debian value for mbox_write_locks differs from upstream Dovecot. It is +# changed to be compliant with Debian Policy (section 11.6) for NFS safety. +# Dovecot: mbox_write_locks = dotlock fcntl +# Debian: mbox_write_locks = fcntl dotlock +# +#mbox_read_locks = fcntl +#mbox_write_locks = fcntl dotlock + +# Maximum time to wait for lock (all of them) before aborting. +#mbox_lock_timeout = 5 mins + +# If dotlock exists but the mailbox isn't modified in any way, override the +# lock file after this much time. +#mbox_dotlock_change_timeout = 2 mins + +# When mbox changes unexpectedly we have to fully read it to find out what +# changed. If the mbox is large this can take a long time. Since the change +# is usually just a newly appended mail, it'd be faster to simply read the +# new mails. If this setting is enabled, Dovecot does this but still safely +# fallbacks to re-reading the whole mbox file whenever something in mbox isn't +# how it's expected to be. The only real downside to this setting is that if +# some other MUA changes message flags, Dovecot doesn't notice it immediately. +# Note that a full sync is done with SELECT, EXAMINE, EXPUNGE and CHECK +# commands. +#mbox_dirty_syncs = yes + +# Like mbox_dirty_syncs, but don't do full syncs even with SELECT, EXAMINE, +# EXPUNGE or CHECK commands. If this is set, mbox_dirty_syncs is ignored. +#mbox_very_dirty_syncs = no + +# Delay writing mbox headers until doing a full write sync (EXPUNGE and CHECK +# commands and when closing the mailbox). This is especially useful for POP3 +# where clients often delete all mails. The downside is that our changes +# aren't immediately visible to other MUAs. +#mbox_lazy_writes = yes + +# If mbox size is smaller than this (e.g. 100k), don't write index files. +# If an index file already exists it's still read, just not updated. +#mbox_min_index_size = 0 + +# Mail header selection algorithm to use for MD5 POP3 UIDLs when +# pop3_uidl_format=%m. For backwards compatibility we use apop3d inspired +# algorithm, but it fails if the first Received: header isn't unique in all +# mails. An alternative algorithm is "all" that selects all headers. +#mbox_md5 = apop3d + +## +## mdbox-specific settings +## + +# Maximum dbox file size until it's rotated. +#mdbox_rotate_size = 2M + +# Maximum dbox file age until it's rotated. Typically in days. Day begins +# from midnight, so 1d = today, 2d = yesterday, etc. 0 = check disabled. +#mdbox_rotate_interval = 0 + +# When creating new mdbox files, immediately preallocate their size to +# mdbox_rotate_size. This setting currently works only in Linux with some +# filesystems (ext4, xfs). +#mdbox_preallocate_space = no + +## +## Mail attachments +## + +# sdbox and mdbox support saving mail attachments to external files, which +# also allows single instance storage for them. Other backends don't support +# this for now. + +# Directory root where to store mail attachments. Disabled, if empty. +#mail_attachment_dir = + +# Attachments smaller than this aren't saved externally. It's also possible to +# write a plugin to disable saving specific attachments externally. +#mail_attachment_min_size = 128k + +# Filesystem backend to use for saving attachments: +# posix : No SiS done by Dovecot (but this might help FS's own deduplication) +# sis posix : SiS with immediate byte-by-byte comparison during saving +# sis-queue posix : SiS with delayed comparison and deduplication +#mail_attachment_fs = sis posix + +# Hash format to use in attachment filenames. You can add any text and +# variables: %{md4}, %{md5}, %{sha1}, %{sha256}, %{sha512}, %{size}. +# Variables can be truncated, e.g. %{sha256:80} returns only first 80 bits +#mail_attachment_hash = %{sha1} +]]> + + + + + #service_count = 1 + + # Number of processes to always keep waiting for more connections. + #process_min_avail = 0 + + # If you set service_count=0, you probably need to grow this. + #vsz_limit = $default_vsz_limit +} + +service pop3-login { + inet_listener pop3 { + #port = 110 + } + inet_listener pop3s { + #port = 995 + #ssl = yes + } +} + +service lmtp { + unix_listener lmtp { + #mode = 0666 + } + + # Create inet listener only if you can't use the above UNIX socket + #inet_listener lmtp { + # Avoid making LMTP visible for the entire internet + #address = + #port = + #} +} + +service imap { + # Most of the memory goes to mmap()ing files. You may need to increase this + # limit if you have huge mailboxes. + #vsz_limit = $default_vsz_limit + + # Max. number of IMAP processes (connections) + #process_limit = 1024 +} + +service pop3 { + # Max. number of POP3 processes (connections) + #process_limit = 1024 +} + +service auth { + # auth_socket_path points to this userdb socket by default. It's typically + # used by dovecot-lda, doveadm, possibly imap process, etc. Users that have + # full permissions to this socket are able to get a list of all usernames and + # get the results of everyone's userdb lookups. + # + # The default 0666 mode allows anyone to connect to the socket, but the + # userdb lookups will succeed only if the userdb returns an "uid" field that + # matches the caller process's UID. Also if caller's uid or gid matches the + # socket's uid or gid the lookup succeeds. Anything else causes a failure. + # + # To give the caller full permissions to lookup all users, set the mode to + # something else than 0666 and Dovecot lets the kernel enforce the + # permissions (e.g. 0777 allows everyone full permissions). + unix_listener auth-userdb { + #mode = 0666 + #user = + #group = + } + + # Postfix smtp-auth + unix_listener /var/spool/postfix/private/auth { + mode = 0660 + user = postfix + group = postfix + } + + # Exim4 smtp-auth + unix_listener auth-client { + mode = 0660 + user = mail + # group = Debian-exim + } + + # Auth process is run as this user. + #user = $default_internal_user +} + +service auth-worker { + # Auth worker process is run as root by default, so that it can access + # /etc/shadow. If this isn't necessary, the user should be changed to + # $default_internal_user. + #user = root +} + +service dict { + # If dict proxy is used, mail processes should have access to its socket. + # For example: mode=0660, group=vmail and global mail_access_groups=vmail + unix_listener dict { + #mode = 0600 + #user = + #group = + } +} +]]> + + + + +ssl = no + +# PEM encoded X.509 SSL/TLS certificate and private key. They're opened before +# dropping root privileges, so keep the key file unreadable by anyone but +# root. Included doc/mkcert.sh can be used to easily generate self-signed +# certificate, just make sure to update the domains in dovecot-openssl.cnf +#ssl_cert = + + + + . %d expands to recipient domain. +postmaster_address = postmaster@ + +# Hostname to use in various parts of sent mails (e.g. in Message-Id) and +# in LMTP replies. Default is the system's real hostname@domain. +#hostname = + +# If user is over quota, return with temporary failure instead of +# bouncing the mail. +#quota_full_tempfail = no + +# Binary to use for sending mails. +#sendmail_path = /usr/sbin/sendmail + +# If non-empty, send mails via this SMTP host[:port] instead of sendmail. +#submission_host = + +# Subject: header to use for rejection mails. You can use the same variables +# as for rejection_reason below. +#rejection_subject = Rejected: %s + +# Human readable error message for rejection mails. You can use variables: +# %n = CRLF, %r = reason, %s = original subject, %t = recipient +#rejection_reason = Your message to <%t> was automatically rejected:%n%r + +# Delimiter character between local-part and detail in email address. +#recipient_delimiter = + + +# Header where the original recipient address (SMTP's RCPT TO: address) is taken +# from if not available elsewhere. With dovecot-lda -a parameter overrides this. +# A commonly used header for this is X-Original-To. +#lda_original_recipient_header = + +# Should saving a mail to a nonexistent mailbox automatically create it? +#lda_mailbox_autocreate = no + +# Should automatically created mailboxes be also automatically subscribed? +#lda_mailbox_autosubscribe = no + +protocol lda { + # Space separated list of plugins to load (default is global mail_plugins). + mail_plugins = $mail_plugins quota sieve +} +]]> + + + + + + + + + #service_count = 1 + + # Number of processes to always keep waiting for more connections. + #process_min_avail = 0 + + # If you set service_count=0, you probably need to grow this. + #vsz_limit = 64M +#} + +#service managesieve { + # Max. number of ManageSieve processes (connections) + #process_limit = 1024 +#} + +# Service configuration + +protocol sieve { + # Maximum ManageSieve command line length in bytes. ManageSieve usually does + # not involve overly long command lines, so this setting will not normally + # need adjustment + #managesieve_max_line_length = 65536 + + # Maximum number of ManageSieve connections allowed for a user from each IP + # address. + # NOTE: The username is compared case-sensitively. + #mail_max_userip_connections = 10 + + # Space separated list of plugins to load (none known to be useful so far). + # Do NOT try to load IMAP plugins here. + #mail_plugins = + + # MANAGESIEVE logout format string: + # %i - total number of bytes read from client + # %o - total number of bytes sent to client + #managesieve_logout_format = bytes=%i/%o + + # To fool ManageSieve clients that are focused on CMU's timesieved you can + # specify the IMPLEMENTATION capability that Dovecot reports to clients. + # For example: 'Cyrus timsieved v2.2.13' + #managesieve_implementation_string = Dovecot Pigeonhole + + # Explicitly specify the SIEVE and NOTIFY capability reported by the server + # before login. If left unassigned these will be reported dynamically + # according to what the Sieve interpreter supports by default (after login + # this may differ depending on the user). + #managesieve_sieve_capability = + #managesieve_notify_capability = + + # The maximum number of compile errors that are returned to the client upon + # script upload or script verification. + #managesieve_max_compile_errors = 5 + + # Refer to 90-sieve.conf for script quota configuration and configuration of + # Sieve execution limits. +} +]]> + + + + = 2.1.4) : %v.%u +# Dovecot v0.99.x : %v.%u +# tpop3d : %Mf +# +# Note that Outlook 2003 seems to have problems with %v.%u format which was +# Dovecot's default, so if you're building a new server it would be a good +# idea to change this. %08Xu%08Xv should be pretty fail-safe. +# +#pop3_uidl_format = %08Xu%08Xv + +# Permanently save UIDLs sent to POP3 clients, so pop3_uidl_format changes +# won't change those UIDLs. Currently this works only with Maildir. +#pop3_save_uidl = no + +# What to do about duplicate UIDLs if they exist? +# allow: Show duplicates to clients. +# rename: Append a temporary -2, -3, etc. counter after the UIDL. +#pop3_uidl_duplicates = allow + +# This option changes POP3 behavior so that it's not possible to actually +# delete mails via POP3, only hide them from future POP3 sessions. The mails +# will still be counted towards user's quota until actually deleted via IMAP. +# Use e.g. "$POP3Deleted" as the value (it will be visible as IMAP keyword). +# Make sure you can legally archive mails before enabling this setting. +#pop3_deleted_flag = + +# POP3 logout format string: +# %i - total number of bytes read from client +# %o - total number of bytes sent to client +# %t - number of TOP commands +# %p - number of bytes sent to client as a result of TOP command +# %r - number of RETR commands +# %b - number of bytes sent to client as a result of RETR command +# %d - number of deleted messages +# %m - number of messages (before deletion) +# %s - mailbox size in bytes (before deletion) +# %u - old/new UIDL hash. may help finding out if UIDLs changed unexpectedly +#pop3_logout_format = top=%t/%p, retr=%r/%b, del=%d/%m, size=%s + +# Workarounds for various client bugs: +# outlook-no-nuls: +# Outlook and Outlook Express hang if mails contain NUL characters. +# This setting replaces them with 0x80 character. +# oe-ns-eoh: +# Outlook Express and Netscape Mail breaks if end of headers-line is +# missing. This option simply sends it if it's missing. +# The list is space-separated. +#pop3_client_workarounds = + +protocol pop3 { + # Space separated list of plugins to load (default is global mail_plugins). + #mail_plugins = $mail_plugins + + # Maximum number of POP3 connections allowed for a user from each IP address. + # NOTE: The username is compared case-sensitively. + #mail_max_userip_connections = 10 +} +]]> + + + + See sieve_before fore executing scripts before the user's personal + # script. + #sieve_default = /var/lib/dovecot/sieve/default.sieve + + # Directory for :personal include scripts for the include extension. This + # is also where the ManageSieve service stores the user's scripts. + sieve_dir = ~/sieve + + # Directory for :global include scripts for the include extension. + #sieve_global_dir = + + # Path to a script file or a directory containing script files that need to be + # executed before the user's script. If the path points to a directory, all + # the Sieve scripts contained therein (with the proper .sieve extension) are + # executed. The order of execution within a directory is determined by the + # file names, using a normal 8bit per-character comparison. Multiple script + # file or directory paths can be specified by appending an increasing number. + #sieve_before = + #sieve_before2 = + #sieve_before3 = (etc...) + + # Identical to sieve_before, only the specified scripts are executed after the + # user's script (only when keep is still in effect!). Multiple script file or + # directory paths can be specified by appending an increasing number. + #sieve_after = + #sieve_after2 = + #sieve_after2 = (etc...) + + # Which Sieve language extensions are available to users. By default, all + # supported extensions are available, except for deprecated extensions or + # those that are still under development. Some system administrators may want + # to disable certain Sieve extensions or enable those that are not available + # by default. This setting can use '+' and '-' to specify differences relative + # to the default. For example `sieve_extensions = +imapflags' will enable the + # deprecated imapflags extension in addition to all extensions were already + # enabled by default. + #sieve_extensions = +notify +imapflags + + # Which Sieve language extensions are ONLY available in global scripts. This + # can be used to restrict the use of certain Sieve extensions to administrator + # control, for instance when these extensions can cause security concerns. + # This setting has higher precedence than the `sieve_extensions' setting + # (above), meaning that the extensions enabled with this setting are never + # available to the user's personal script no matter what is specified for the + # `sieve_extensions' setting. The syntax of this setting is similar to the + # `sieve_extensions' setting, with the difference that extensions are + # enabled or disabled for exclusive use in global scripts. Currently, no + # extensions are marked as such by default. + #sieve_global_extensions = + + # The Pigeonhole Sieve interpreter can have plugins of its own. Using this + # setting, the used plugins can be specified. Check the Dovecot wiki + # (wiki2.dovecot.org) or the pigeonhole website + # (http://pigeonhole.dovecot.org) for available plugins. + # The sieve_extprograms plugin is included in this release. + #sieve_plugins = + + # The separator that is expected between the :user and :detail + # address parts introduced by the subaddress extension. This may + # also be a sequence of characters (e.g. '--'). The current + # implementation looks for the separator from the left of the + # localpart and uses the first one encountered. The :user part is + # left of the separator and the :detail part is right. This setting + # is also used by Dovecot's LMTP service. + #recipient_delimiter = + + + # The maximum size of a Sieve script. The compiler will refuse to compile any + # script larger than this limit. If set to 0, no limit on the script size is + # enforced. + #sieve_max_script_size = 1M + + # The maximum number of actions that can be performed during a single script + # execution. If set to 0, no limit on the total number of actions is enforced. + #sieve_max_actions = 32 + + # The maximum number of redirect actions that can be performed during a single + # script execution. If set to 0, no redirect actions are allowed. + #sieve_max_redirects = 4 + + # The maximum number of personal Sieve scripts a single user can have. If set + # to 0, no limit on the number of scripts is enforced. + # (Currently only relevant for ManageSieve) + #sieve_quota_max_scripts = 0 + + # The maximum amount of disk storage a single user's scripts may occupy. If + # set to 0, no limit on the used amount of disk storage is enforced. + # (Currently only relevant for ManageSieve) + #sieve_quota_max_storage = 0 +} +]]> + + + + + + + + + + //service[@type='mail']/general/installs[@index=1] + + //service[@type='mail']/general/files[@index=1] + + //service[@type='mail']/general/commands[@index=1] + + + + + + + + + + " +[ -f /etc/ssl/certs/proftpd_ec.crt ] || openssl req -new -x509 -nodes -newkey ec:<(openssl ecparam -name secp521r1) -keyout /etc/ssl/private/proftpd_ec.key -out /etc/ssl/certs/proftpd_ec.crt -days 3650 -subj "/C=US/ST=Some-State/O=Internet Widgits Pty Ltd/CN=" +chmod 0600 /etc/ssl/private/proftpd.key /etc/ssl/private/proftpd_ec.key +]]> + + + + + + + + FTP Server" +ServerType standalone +DeferWelcome off + +MultilineRFC2228 on +DefaultServer on +ShowSymlinks on + +TimeoutNoTransfer 600 +TimeoutStalled 600 +TimeoutIdle 1200 + +DisplayLogin welcome.msg +DisplayChdir .message true +ListOptions "-l" + +DenyFilter \*.*/ + +# Use this to jail all users in their homes +# DefaultRoot ~ + +# Users require a valid shell listed in /etc/shells to login. +# Use this directive to release that constrain. +# RequireValidShell off + +# Port 21 is the standard FTP port. +Port 21 + +# In some cases you have to specify passive ports range to by-pass +# firewall limitations. Ephemeral ports can be used for that, but +# feel free to use a more narrow range. +# PassivePorts 49152 65534 + +# If your host was NATted, this option is useful in order to +# allow passive tranfers to work. You have to use your public +# address and opening the passive ports used on your firewall as well. +# MasqueradeAddress 1.2.3.4 + +# This is useful for masquerading address with dynamic IPs: +# refresh any configured MasqueradeAddress directives every 8 hours + +# DynMasqRefresh 28800 + + +# To prevent DoS attacks, set the maximum number of child processes +# to 30. If you need to allow more than 30 concurrent connections +# at once, simply increase this value. Note that this ONLY works +# in standalone mode, in inetd mode you should use an inetd server +# that allows you to limit maximum number of processes per service +# (such as xinetd) +MaxInstances 30 + +# Set the user and group that the server normally runs at. +User proftpd +Group nogroup + +# Umask 022 is a good standard umask to prevent new files and dirs +# (second parm) from being group and world writable. +Umask 022 022 +# Normally, we want files to be overwriteable. +AllowOverwrite on + +# Uncomment this if you are using NIS or LDAP via NSS to retrieve passwords: +# PersistentPasswd off + +# This is required to use both PAM-based authentication and local passwords +# AuthOrder mod_auth_pam.c* mod_auth_unix.c + +# Be warned: use of this directive impacts CPU average load! +# Uncomment this if you like to see progress and transfer rate with ftpwho +# in downloads. That is not needed for uploads rates. +# +# UseSendFile off + +TransferLog /var/log/proftpd/xferlog +SystemLog /var/log/proftpd/proftpd.log + +# Logging onto /var/log/lastlog is enabled but set to off by default +#UseLastlog on + +# In order to keep log file dates consistent after chroot, use timezone info +# from /etc/localtime. If this is not set, and proftpd is configured to +# chroot (e.g. DefaultRoot or ), it will use the non-daylight +# savings timezone regardless of whether DST is in effect. +#SetEnv TZ :/etc/localtime + + +QuotaEngine on + + + +Ratios off + + + +# Delay engine reduces impact of the so-called Timing Attack described in +# http://www.securityfocus.com/bid/11430/discuss +# It is on by default. + +DelayEngine on + + + +ControlsEngine off +ControlsMaxClients 2 +ControlsLog /var/log/proftpd/controls.log +ControlsInterval 5 +ControlsSocket /var/run/proftpd/proftpd.sock + + + +AdminControlsEngine off + + +# +# Alternative authentication frameworks +# +#Include /etc/proftpd/ldap.conf +Include /etc/proftpd/sql.conf + +# +# This is used for FTPS connections +# +Include /etc/proftpd/tls.conf + +# +# Useful to keep VirtualHost/VirtualRoot directives separated +# +#Include /etc/proftpd/virtuals.conf + +# A basic anonymous configuration, no upload directories. + +# +# User ftp +# Group nogroup +# # We want clients to be able to login with "anonymous" as well as "ftp" +# UserAlias anonymous ftp +# # Cosmetic changes, all files belongs to ftp user +# DirFakeUser on ftp +# DirFakeGroup on ftp +# +# RequireValidShell off +# +# # Limit the maximum number of anonymous logins +# MaxClients 10 +# +# # We want 'welcome.msg' displayed at login, and '.message' displayed +# # in each newly chdired directory. +# DisplayLogin welcome.msg +# DisplayChdir .message +# +# # Limit WRITE everywhere in the anonymous chroot +# +# +# DenyAll +# +# +# +# # Uncomment this if you're brave. +# # +# # # Umask 022 is a good standard umask to prevent new files and dirs +# # # (second parm) from being group and world writable. +# # Umask 022 022 +# # +# # DenyAll +# # +# # +# # AllowAll +# # +# # +# +# + +# Include other custom configuration files +Include /etc/proftpd/conf.d/ +]]> + + + + + + + + + +DefaultRoot ~ +RequireValidShell off +AuthOrder mod_sql.c + +# +# Choose a SQL backend among MySQL or PostgreSQL. +# Both modules are loaded in default configuration, so you have to specify the backend +# or comment out the unused module in /etc/proftpd/modules.conf. +# Use 'mysql' or 'postgres' as possible values. +# +SQLBackend mysql +# +SQLEngine on +SQLAuthenticate on +# +# Use both a crypted or plaintext password +SQLAuthTypes Crypt + +SQLAuthenticate users* groups* + +# +# Connection +SQLConnectInfo @ +# +# Describes both users/groups tables +# +SQLUserInfo ftp_users username password uid gid homedir shell +SQLGroupInfo ftp_groups groupname gid members +# +SQLUserWhereClause "login_enabled = 'y'" + +SQLLog PASS login +SQLNamedQuery login UPDATE "last_login=now(), login_count=login_count+1 WHERE username='%u'" ftp_users + +SQLLog RETR download +SQLNamedQuery download UPDATE "down_count=down_count+1, down_bytes=down_bytes+%b WHERE username='%u'" ftp_users + +SQLLog STOR upload +SQLNamedQuery upload UPDATE "up_count=up_count+1, up_bytes=up_bytes+%b WHERE username='%u'" ftp_users + +QuotaEngine on +QuotaShowQuotas on +QuotaDisplayUnits Mb +QuotaLock /var/lock/ftpd.quotatab.lock +QuotaLimitTable sql:/get-quota-limit +QuotaTallyTable sql:/get-quota-tally/update-quota-tally/insert-quota-tally +SQLNamedQuery get-quota-limit SELECT "ftp_users.username AS name, ftp_quotalimits.quota_type, ftp_quotalimits.per_session, ftp_quotalimits.limit_type, panel_customers.diskspace*1024 AS bytes_in_avail, ftp_quotalimits.bytes_out_avail, ftp_quotalimits.bytes_xfer_avail, ftp_quotalimits.files_in_avail, ftp_quotalimits.files_out_avail, ftp_quotalimits.files_xfer_avail FROM ftp_users, ftp_quotalimits, panel_customers WHERE ftp_users.username = '%{0}' AND panel_customers.loginname = SUBSTRING_INDEX('%{0}', 'ftp', 1) AND quota_type ='%{1}'" +SQLNamedQuery get-quota-tally SELECT "name, quota_type, bytes_in_used,bytes_out_used, bytes_xfer_used, files_in_used, files_out_used,files_xfer_used FROM ftp_quotatallies WHERE name = '%{0}' AND quota_type = '%{1}'" +SQLNamedQuery update-quota-tally UPDATE "bytes_in_used = bytes_in_used + %{0}, bytes_out_used = bytes_out_used + %{1}, bytes_xfer_used = bytes_xfer_used + %{2}, files_in_used = files_in_used + %{3}, files_out_used= files_out_used + %{4}, files_xfer_used = files_xfer_used + %{5} WHERE name= '%{6}' AND quota_type = '%{7}'" ftp_quotatallies +SQLNamedQuery insert-quota-tally INSERT "%{0}, %{1}, %{2}, %{3}, %{4},%{5}, %{6}, %{7}" ftp_quotatallies + + +]]> + + + + +TLSEngine on +TLSLog /var/log/proftpd/tls.log +TLSProtocol TLSv1 TLSv1.1 TLSv1.2 +TLSRSACertificateFile /etc/ssl/certs/proftpd.crt +TLSRSACertificateKeyFile /etc/ssl/private/proftpd.key +TLSECCertificateFile /etc/ssl/certs/proftpd_ec.crt +TLSECCertificateKeyFile /etc/ssl/private/proftpd_ec.key +TLSOptions NoCertRequest NoSessionReuseRequired +TLSVerifyClient off + +# Are clients required to use FTP over TLS when talking to this server? +#TLSRequired on + +# Allow SSL/TLS renegotiations when the client requests them, but +# do not force the renegotations. Some clients do not support +# SSL/TLS renegotiations; when mod_tls forces a renegotiation, these +# clients will close the data connection, or there will be a timeout +# on an idle data connection. +# +#TLSRenegotiate required off + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +# Mandatory : user password. You must have a password. + +MYSQLPassword + + +# Mandatory : database to open. + +MYSQLDatabase + + +# Mandatory : how passwords are stored +# Valid values are : "cleartext", "crypt", "sha1", "md5" and "password" +# ("password" = MySQL password() function) +# You can also use "any" to try "crypt", "sha1", "md5" *and* "password" + +MYSQLCrypt any + + +# In the following directives, parts of the strings are replaced at +# run-time before performing queries : +# +# \L is replaced by the login of the user trying to authenticate. +# \I is replaced by the IP address the user connected to. +# \P is replaced by the port number the user connected to. +# \R is replaced by the IP address the user connected from. +# \D is replaced by the remote IP address, as a long decimal number. +# +# Very complex queries can be performed using these substitution strings, +# especially for virtual hosting. + + +# Query to execute in order to fetch the password + +MYSQLGetPW SELECT password FROM ftp_users WHERE username="\L" AND login_enabled="y" + + +# Query to execute in order to fetch the system user name or uid + +MYSQLGetUID SELECT uid FROM ftp_users WHERE username="\L" AND login_enabled="y" + + +# Optional : default UID - if set this overrides MYSQLGetUID + +#MYSQLDefaultUID 1000 + + +# Query to execute in order to fetch the system user group or gid + +MYSQLGetGID SELECT gid FROM ftp_users WHERE username="\L" AND login_enabled="y" + + +# Optional : default GID - if set this overrides MYSQLGetGID + +#MYSQLDefaultGID 1000 + + +# Query to execute in order to fetch the home directory + +MYSQLGetDir SELECT homedir FROM ftp_users WHERE username="\L" AND login_enabled="y" + + +# Optional : query to get the maximal number of files +# Pure-FTPd must have been compiled with virtual quotas support. + +# MySQLGetQTAFS SELECT QuotaFiles FROM users WHERE User='\L' + + +# Optional : query to get the maximal disk usage (virtual quotas) +# The number should be in Megabytes. +# Pure-FTPd must have been compiled with virtual quotas support. + +MySQLGetQTASZ SELECT panel_customers.diskspace/1024 AS QuotaSize FROM panel_customers, ftp_users WHERE username = "\L" AND panel_customers.loginname = SUBSTRING_INDEX('\L', 'ftp', 1) + + +# Optional : ratios. The server has to be compiled with ratio support. + +# MySQLGetRatioUL SELECT ULRatio FROM users WHERE User='\L' +# MySQLGetRatioDL SELECT DLRatio FROM users WHERE User='\L' + + +# Optional : bandwidth throttling. +# The server has to be compiled with throttling support. +# Values are in KB/s . + +# MySQLGetBandwidthUL SELECT ULBandwidth FROM users WHERE User='\L' +# MySQLGetBandwidthDL SELECT DLBandwidth FROM users WHERE User='\L' + +# Enable ~ expansion. NEVER ENABLE THIS BLINDLY UNLESS : +# 1) You know what you are doing. +# 2) Real and virtual users match. + +# MySQLForceTildeExpansion 1 + + +# If you're using a transactionnal storage engine, you can enable SQL +# transactions to avoid races. Leave this commented if you are using the +# traditional MyIsam engine. + +# MySQLTransactions On +]]> + + + + + + + + + + + + + + + + + + + + + + + scripts/froxlor_master_cronjob.php +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + *.log { + missingok + weekly + rotate 4 + compress + delaycompress + notifempty + create + sharedscripts + postrotate + > /dev/null 2>&1 || true + endscript +} +]]> + + + + + + + + + {{settings.system.mod_fcgid_ownvhost}} + + + + + + + + + + + + + + {{settings.system.webserver}} + + + + + + {{settings.system.webserver}} + + + + + {{settings.phpfpm.enabled_ownvhost}} + + {{settings.phpfpm.vhost_httpuser}} + + + + + + {{settings.system.webserver}} + + {{settings.phpfpm.enabled_ownvhost}} + + + + + {{settings.system.webserver}} + + + + + + + + + + From 2508d855e3e8aaf9af95e43133077758300d3afa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20F=C3=B6rster=20=28Dessa=29?= Date: Tue, 3 Apr 2018 13:12:32 +0200 Subject: [PATCH 0599/1335] deprecate precise, as xenial configs are now available --- lib/configfiles/precise.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/configfiles/precise.xml b/lib/configfiles/precise.xml index 6d7e8b15..2e17ba72 100644 --- a/lib/configfiles/precise.xml +++ b/lib/configfiles/precise.xml @@ -1,6 +1,6 @@ - + From b3d018c506e9f351e8a80cb4da1b71c56f71e52c Mon Sep 17 00:00:00 2001 From: Michael Kaufmann Date: Wed, 25 Apr 2018 12:27:40 +0200 Subject: [PATCH 0600/1335] corrected usage of default redirect code from settings; fixes #546 Signed-off-by: Michael Kaufmann --- lib/functions/output/function.RedirectCode.php | 18 ++++++++++++++---- scripts/jobs/cron_tasks.inc.http.10.apache.php | 2 +- .../jobs/cron_tasks.inc.http.20.lighttpd.php | 2 +- scripts/jobs/cron_tasks.inc.http.30.nginx.php | 2 +- 4 files changed, 17 insertions(+), 7 deletions(-) diff --git a/lib/functions/output/function.RedirectCode.php b/lib/functions/output/function.RedirectCode.php index c5df262c..5a8cd57d 100644 --- a/lib/functions/output/function.RedirectCode.php +++ b/lib/functions/output/function.RedirectCode.php @@ -36,9 +36,11 @@ function getRedirectCodesArray() { * return an array of all enabled redirect-codes * for the settings form * + * @param bool $add_desc optional, default true, add the code-description + * * @return array array of enabled redirect-codes */ -function getRedirectCodes() { +function getRedirectCodes($add_desc = true) { global $lng; @@ -47,7 +49,10 @@ function getRedirectCodes() { $codes = array(); while ($rc = $result_stmt->fetch(PDO::FETCH_ASSOC)) { - $codes[$rc['id']] = $rc['code']. ' ('.$lng['redirect_desc'][$rc['desc']].')'; + $codes[$rc['id']] = $rc['code']; + if ($add_desc) { + $codes[$rc['id']] .= ' ('.$lng['redirect_desc'][$rc['desc']].')'; + } } return $codes; @@ -58,12 +63,17 @@ function getRedirectCodes() { * domain-id * * @param integer $domainid id of the domain - * @param string $default * * @return string redirect-code */ -function getDomainRedirectCode($domainid = 0, $default = '') { +function getDomainRedirectCode($domainid = 0) { + // get system default + $default = '301'; + if (Settings::Get('customredirect.enabled') == '1') { + $all_codes = getRedirectCodes(false); + $default = $all_codes[Settings::Get('customredirect.default')]; + } $code = $default; if ($domainid > 0) { diff --git a/scripts/jobs/cron_tasks.inc.http.10.apache.php b/scripts/jobs/cron_tasks.inc.http.10.apache.php index 21225125..fc436219 100644 --- a/scripts/jobs/cron_tasks.inc.http.10.apache.php +++ b/scripts/jobs/cron_tasks.inc.http.10.apache.php @@ -964,7 +964,7 @@ class apache extends HttpConfigBase $corrected_docroot = $domain['documentroot']; // Get domain's redirect code - $code = getDomainRedirectCode($domain['id'], '301'); + $code = getDomainRedirectCode($domain['id']); $modrew_red = ''; if ($code != '') { $modrew_red = ' [R=' . $code . ';L,NE]'; diff --git a/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php b/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php index 84b88400..661005f6 100644 --- a/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php +++ b/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php @@ -467,7 +467,7 @@ class lighttpd extends HttpConfigBase $uri = $domain['documentroot']; // Get domain's redirect code - $code = getDomainRedirectCode($domain['id'], '301'); + $code = getDomainRedirectCode($domain['id']); $vhost_content .= ' url.redirect-code = ' . $code. "\n"; $vhost_content .= ' url.redirect = (' . "\n"; diff --git a/scripts/jobs/cron_tasks.inc.http.30.nginx.php b/scripts/jobs/cron_tasks.inc.http.30.nginx.php index e2eac04d..a979463b 100644 --- a/scripts/jobs/cron_tasks.inc.http.30.nginx.php +++ b/scripts/jobs/cron_tasks.inc.http.30.nginx.php @@ -486,7 +486,7 @@ class nginx extends HttpConfigBase } // Get domain's redirect code - $code = getDomainRedirectCode($domain['id'], '301'); + $code = getDomainRedirectCode($domain['id']); $vhost_content .= "\t" . 'if ($request_uri !~ ^/.well-known/acme-challenge/\w+$) {' . "\n"; $vhost_content .= "\t\t" . 'return ' . $code .' ' . $uri . '$request_uri;' . "\n"; From 75616cc727a738bd78a6df38199050b09c882f45 Mon Sep 17 00:00:00 2001 From: Michael Kaufmann Date: Wed, 25 Apr 2018 12:27:59 +0200 Subject: [PATCH 0601/1335] fix typo in german language file Signed-off-by: Michael Kaufmann --- lng/german.lng.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lng/german.lng.php b/lng/german.lng.php index 9912b00f..7f44f981 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -1685,7 +1685,7 @@ $lng['serversettings']['panel_customer_hide_options']['description'] = 'Wählen $lng['serversettings']['allow_allow_customer_shell']['title'] = 'Erlaube Kunden für FTP Benutzer eine Shell auszuwählen'; $lng['serversettings']['allow_allow_customer_shell']['description'] = 'Bitte beachten: Shell Zugriff gestattet dem Benutzer verschiedene Programme auf Ihrem System auszuführen. Mit großer Vorsicht verwenden. Bitte aktiviere dies nur wenn WIRKLICH bekannt ist, was das bedeutet!!!'; $lng['serversettings']['available_shells']['title'] = 'Liste der verfügbaren Shells'; -$lng['serversettings']['available_shells']['description'] = 'Komme-getrennte Liste von Shells die der Kunde für seine FTP-Konten wählen kann.

    Hinweis: Die Standard-Shell /bin/false wird immer eine Auswahlmöglichkeit sein (wenn aktiviert), auch wenn diese Einstellung leer ist. Sie ist in jedem Fall der Standardwert für alle FTP-Konten'; +$lng['serversettings']['available_shells']['description'] = 'Komma-getrennte Liste von Shells die der Kunde für seine FTP-Konten wählen kann.

    Hinweis: Die Standard-Shell /bin/false wird immer eine Auswahlmöglichkeit sein (wenn aktiviert), auch wenn diese Einstellung leer ist. Sie ist in jedem Fall der Standardwert für alle FTP-Konten'; $lng['serversettings']['le_froxlor_enabled']['title'] = "Let's Encrypt für den froxlor Vhost verwenden"; $lng['serversettings']['le_froxlor_enabled']['description'] = "Wenn dies aktiviert ist, erstellt froxlor für seinen vhost automatisch ein Let's Encrypt Zertifikat."; $lng['serversettings']['le_froxlor_redirect']['title'] = "SSL-Weiterleitung für den froxlor Vhost aktivieren"; From 84b8cda7acba3e8945cea2d773f7f4531b9ac746 Mon Sep 17 00:00:00 2001 From: Michael Kaufmann Date: Wed, 25 Apr 2018 12:29:30 +0200 Subject: [PATCH 0602/1335] allow usage of up to 255 characters in a txt record, fixes #548 Signed-off-by: Michael Kaufmann --- lib/classes/dns/class.DnsEntry.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/classes/dns/class.DnsEntry.php b/lib/classes/dns/class.DnsEntry.php index a5685741..06e18cbc 100644 --- a/lib/classes/dns/class.DnsEntry.php +++ b/lib/classes/dns/class.DnsEntry.php @@ -43,9 +43,9 @@ class DnsEntry { $_content = $this->content; // check content length for txt records for bind9 (multiline) - if (Settings::Get('system.dns_server') != 'pdns' && $this->type == 'TXT' && strlen($_content) >= 64) { + if (Settings::Get('system.dns_server') != 'pdns' && $this->type == 'TXT' && strlen($_content) >= 255) { // split string - $_contentlines = str_split($_content, 63); + $_contentlines = str_split($_content, 254); // first line $_l = array_shift($_contentlines); // check for starting quote From 67b95a301b023ed6f49e38c7a56d780034762195 Mon Sep 17 00:00:00 2001 From: Michael Kaufmann Date: Sun, 6 May 2018 14:38:18 +0200 Subject: [PATCH 0603/1335] check only for existing .conf files when trying to find out whether a fpm pool config directory is empty; fixes #543 Signed-off-by: Michael Kaufmann --- scripts/jobs/cron_tasks.inc.http.10.apache.php | 5 ++--- scripts/jobs/cron_tasks.inc.http.20.lighttpd.php | 5 ++--- scripts/jobs/cron_tasks.inc.http.30.nginx.php | 5 ++--- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/scripts/jobs/cron_tasks.inc.http.10.apache.php b/scripts/jobs/cron_tasks.inc.http.10.apache.php index fc436219..5f966f38 100644 --- a/scripts/jobs/cron_tasks.inc.http.10.apache.php +++ b/scripts/jobs/cron_tasks.inc.http.10.apache.php @@ -66,9 +66,8 @@ class apache extends HttpConfigBase foreach ($restart_cmds as $restart_cmd) { // check whether the config dir is empty (no domains uses this daemon) // so we need to create a dummy - $fsi = new \FilesystemIterator($restart_cmd['config_dir']); - $isDirEmpty = !$fsi->valid(); - if ($isDirEmpty) { + $_conffiles = glob(makeCorrectFile($restart_cmd['config_dir'] . "/*.conf")); + if ($_conffiles === false || empty($_conffiles)) { $this->logger->logAction(CRON_ACTION, LOG_INFO, 'apache::reload: fpm config directory "' . $restart_cmd['config_dir'] . '" is empty. Creating dummy.'); phpinterface_fpm::createDummyPool($restart_cmd['config_dir']); } diff --git a/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php b/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php index 661005f6..1df73682 100644 --- a/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php +++ b/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php @@ -66,9 +66,8 @@ class lighttpd extends HttpConfigBase foreach ($restart_cmds as $restart_cmd) { // check whether the config dir is empty (no domains uses this daemon) // so we need to create a dummy - $fsi = new \FilesystemIterator($restart_cmd['config_dir']); - $isDirEmpty = !$fsi->valid(); - if ($isDirEmpty) { + $_conffiles = glob(makeCorrectFile($restart_cmd['config_dir'] . "/*.conf")); + if ($_conffiles === false || empty($_conffiles)) { $this->logger->logAction(CRON_ACTION, LOG_INFO, 'lighttpd::reload: fpm config directory "' . $restart_cmd['config_dir'] . '" is empty. Creating dummy.'); phpinterface_fpm::createDummyPool($restart_cmd['config_dir']); } diff --git a/scripts/jobs/cron_tasks.inc.http.30.nginx.php b/scripts/jobs/cron_tasks.inc.http.30.nginx.php index a979463b..f5bb1efe 100644 --- a/scripts/jobs/cron_tasks.inc.http.30.nginx.php +++ b/scripts/jobs/cron_tasks.inc.http.30.nginx.php @@ -81,9 +81,8 @@ class nginx extends HttpConfigBase foreach ($restart_cmds as $restart_cmd) { // check whether the config dir is empty (no domains uses this daemon) // so we need to create a dummy - $fsi = new \FilesystemIterator($restart_cmd['config_dir']); - $isDirEmpty = !$fsi->valid(); - if ($isDirEmpty) { + $_conffiles = glob(makeCorrectFile($restart_cmd['config_dir'] . "/*.conf")); + if ($_conffiles === false || empty($_conffiles)) { $this->logger->logAction(CRON_ACTION, LOG_INFO, 'nginx::reload: fpm config directory "' . $restart_cmd['config_dir'] . '" is empty. Creating dummy.'); phpinterface_fpm::createDummyPool($restart_cmd['config_dir']); } From 07a3f7656807acf776cbf2b10fab12c722d52096 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20F=C3=B6rster=20=28Dessa=29?= Date: Thu, 10 May 2018 16:42:27 +0200 Subject: [PATCH 0604/1335] remove wheezy configs --- lib/configfiles/wheezy.xml | 5588 ------------------------------------ 1 file changed, 5588 deletions(-) delete mode 100644 lib/configfiles/wheezy.xml diff --git a/lib/configfiles/wheezy.xml b/lib/configfiles/wheezy.xml deleted file mode 100644 index 06b6c3a9..00000000 --- a/lib/configfiles/wheezy.xml +++ /dev/null @@ -1,5588 +0,0 @@ - - - - - - - - - - - {{settings.system.apacheconf_vhost}} - - - - - {{settings.system.apacheconf_vhost}} - - - - - - - {{settings.system.apacheconf_diroptions}} - - - - - {{settings.system.apacheconf_diroptions}} - - - - - - - - - - - {{settings.system.deactivateddocroot}} - - - - - - - - - //service[@type='http']/general/commands - - - - {{settings.system.use_ssl}} - - - - - {{settings.phpfpm.enabled}} - - - FastCgiIpcDir - - - Order Deny,Allow - Deny from All - # Prevent accessing this path directly - Allow from env=REDIRECT_STATUS - - -]]> - - - - {{settings.system.leenabled}} - - - Order allow,deny - Allow from all - -]]> - - - - - - - //service[@type='http']/general/commands - - - - {{settings.system.use_ssl}} - - - - - {{settings.phpfpm.enabled}} - - - FastCgiIpcDir - - - Require all granted - Require env REDIRECT_STATUS - - -]]> - - - - {{settings.system.leenabled}} - - - Require all granted - -]]> - - - - - - - - - "{{settings.system.letsencryptchallengepath}}/.well-known/acme-challenge/") - -# default listening port for IPv6 falls back to the IPv4 port -include_shell "/usr/share/lighttpd/use-ipv6.pl " + server.port -include_shell "/usr/share/lighttpd/create-mime.assign.pl" -include_shell "/usr/share/lighttpd/include-conf-enabled.pl" -]]> - - - //service[@type='http']/general/commands - - {{settings.system.apacheconf_vhost}} - - > /etc/lighttpd/lighttpd.conf]]> - - - {{settings.system.apacheconf_vhost}} - - > /etc/lighttpd/lighttpd.conf]]> - - - {{settings.system.apacheconf_diroptions}} - - > /etc/lighttpd/lighttpd.conf]]> - - - {{settings.system.apacheconf_diroptions}} - - > /etc/lighttpd/lighttpd.conf]]> - - - - - - - - - - {{settings.phpfpm.enabled}} - - {{settings.system.mod_fcgid}} - - - - - - - - - - - - - {{settings.system.leenabled}} - - - - - - {{settings.phpfpm.enabled}} - - {{settings.system.mod_fcgid}} - - - - - //service[@type='http']/general/commands - - {{settings.phpfpm.enabled}} - - {{settings.system.mod_fcgid}} - - - - - - - - - - - - > /etc/bind/named.conf.local]]> - - - - - - - - - -# add these entries to the list if any speficied: - -################################# -# allow-recursion List of netmasks that are allowed to recurse -# -allow-recursion=127.0.0.1 - -################################# -# allow-recursion-override Local data even about hosts that don't exist will -# override the internet. (on/off) -# -# allow-recursion-override= - -################################# -# cache-ttl Seconds to store packets in the PacketCache -# -# cache-ttl=20 - -################################# -# chroot If set, chroot to this directory for more security -# -# chroot=/var/spool/powerdns - -################################# -# config-dir Location of configuration directory (pdns.conf) -# -config-dir=/etc/powerdns - -################################# -# config-name Name of this virtual configuration - will rename the binary image -# -# config-name= - -################################# -# control-console Debugging switch - don't use -# -# control-console=no - -################################# -# daemon Operate as a daemon -# -daemon=yes - -################################# -# default-soa-name name to insert in the SOA record if none set in the backend -# -# default-soa-name=a.misconfigured.powerdns.server - -################################# -# disable-axfr Disable zonetransfers but do allow TCP queries -# -disable-axfr=yes - -################################# -# disable-tcp Do not listen to TCP queries -# -# disable-tcp=no - -################################# -# distributor-threads Default number of Distributor (backend) threads to start -# -# distributor-threads=3 - -################################# -# fancy-records Process URL and MBOXFW records -# -# fancy-records=no - -################################# -# guardian Run within a guardian process -# -guardian=yes - -################################# -# launch Which backends to launch and order to query them in -# -# launch= - -################################# -# lazy-recursion Only recurse if question cannot be answered locally -# -lazy-recursion=yes - -################################# -# load-modules Load this module - supply absolute or relative path -# -# load-modules= - -################################# -# local-address Local IP address to which we bind -# -local-address=,127.0.0.1 - -################################# -# local-ipv6 Local IP address to which we bind -# -# local-ipv6= - -################################# -# local-port The port on which we listen -# -local-port=53 - -################################# -# log-dns-details If PDNS should log failed update requests -# -log-dns-details=yes - -################################# -# log-failed-updates If PDNS should log failed update requests -# -# log-failed-updates= - -################################# -# logfile Logfile to use -# -# logfile=/var/log/pdns.log - -################################# -# logging-facility Log under a specific facility -# -# logging-facility= - -################################# -# loglevel Amount of logging. Higher is more. Do not set below 3 -# -# loglevel=4 - -################################# -# master Act as a master -# -master=yes - -################################# -# max-queue-length Maximum queuelength before considering situation lost -# -# max-queue-length=5000 - -################################# -# max-tcp-connections Maximum number of TCP connections -# -# max-tcp-connections=10 - -################################# -# module-dir Default directory for modules -# -module-dir=/usr/lib/powerdns - -################################# -# negquery-cache-ttl Seconds to store packets in the PacketCache -# -# negquery-cache-ttl=60 - -################################# -# out-of-zone-additional-processing Do out of zone additional processing -# -# out-of-zone-additional-processing=no - -################################# -# query-cache-ttl Seconds to store packets in the PacketCache -# -# query-cache-ttl=20 - -################################# -# query-logging Hint backends that queries should be logged -# -# query-logging=no - -################################# -# queue-limit Maximum number of milliseconds to queue a query -# -# queue-limit=1500 - -################################# -# query-local-address The IP address to use as a source address for sending -# queries. -# query-local-address= - -################################# -# receiver-threads Number of receiver threads to launch -# -# receiver-threads=1 - -################################# -# recursive-cache-ttl Seconds to store packets in the PacketCache -# -# recursive-cache-ttl=10 - -################################# -# recursor If recursion is desired, IP address of a recursing nameserver -# -# recursor= - -################################# -# setgid If set, change group id to this gid for more security -# -setgid=pdns - -################################# -# setuid If set, change user id to this uid for more security -# -setuid=pdns - -################################# -# skip-cname Do not perform CNAME indirection for each query -# -# skip-cname=no - -################################# -# slave Act as a slave -# -# slave=no - -################################# -# slave-cycle-interval Reschedule failed SOA serial checks once every .. seconds -# -# slave-cycle-interval=60 - -################################# -# smtpredirector Our smtpredir MX host -# -# smtpredirector=a.misconfigured.powerdns.smtp.server - -################################# -# soa-minimum-ttl Default SOA mininum ttl -# -# soa-minimum-ttl=3600 - -################################# -# soa-refresh-default Default SOA refresh -# -# soa-refresh-default=10800 - -################################# -# soa-retry-default Default SOA retry -# -# soa-retry-default=3600 - -################################# -# soa-expire-default Default SOA expire -# -# soa-expire-default=604800 - -################################# -# soa-serial-offset Make sure that no SOA serial is less than this number -# -# soa-serial-offset=0 - -################################# -# socket-dir Where the controlsocket will live -# -socket-dir=/var/run - -################################# -# strict-rfc-axfrs Perform strictly rfc compliant axfrs (very slow) -# -# strict-rfc-axfrs=no - -################################# -# urlredirector Where we send hosts to that need to be url redirected -# -# urlredirector=127.0.0.1 - -################################# -# use-logfile Use a log file -# -# use-logfile=yes - -################################# -# webserver Start a webserver for monitoring -# -# webserver=no - -################################# -# webserver-address IP Address of webserver to listen on -# -# webserver-address=127.0.0.1 - -################################# -# webserver-password Password required for accessing the webserver -# -# webserver-password= - -################################# -# webserver-port Port of webserver to listen on -# -# webserver-port=8081 - -################################# -# webserver-print-arguments If the webserver should print arguments -# -# webserver-print-arguments=no - -################################# -# wildcard-url Process URL and MBOXFW records -# -# wildcard-url=no - -################################# -# wildcards Honor wildcards in the database -# -# wildcards= - -################################# -# version-string What should PowerDNS return for version -# allowed methods are anonymous / powerdns / full / custom -version-string=powerdns - -# include froxlor-bind-specific config -include-dir=/etc/powerdns/froxlor/ -]]> - - - - - - - - - - - - - -# add these entries to the list if any speficied: - -################################# -# allow-recursion List of netmasks that are allowed to recurse -# -allow-recursion=127.0.0.1 - -################################# -# allow-recursion-override Local data even about hosts that don't exist will -# override the internet. (on/off) -# -# allow-recursion-override= - -################################# -# cache-ttl Seconds to store packets in the PacketCache -# -# cache-ttl=20 - -################################# -# chroot If set, chroot to this directory for more security -# -# chroot=/var/spool/powerdns - -################################# -# config-dir Location of configuration directory (pdns.conf) -# -config-dir=/etc/powerdns - -################################# -# config-name Name of this virtual configuration - will rename the binary image -# -# config-name= - -################################# -# control-console Debugging switch - don't use -# -# control-console=no - -################################# -# daemon Operate as a daemon -# -daemon=yes - -################################# -# default-soa-name name to insert in the SOA record if none set in the backend -# -# default-soa-name=a.misconfigured.powerdns.server - -################################# -# disable-axfr Disable zonetransfers but do allow TCP queries -# -disable-axfr=yes - -################################# -# disable-tcp Do not listen to TCP queries -# -# disable-tcp=no - -################################# -# distributor-threads Default number of Distributor (backend) threads to start -# -# distributor-threads=3 - -################################# -# fancy-records Process URL and MBOXFW records -# -# fancy-records=no - -################################# -# guardian Run within a guardian process -# -guardian=yes - -################################# -# launch Which backends to launch and order to query them in -# -launch=bind - -################################# -# lazy-recursion Only recurse if question cannot be answered locally -# -lazy-recursion=yes - -################################# -# load-modules Load this module - supply absolute or relative path -# -# load-modules= - -################################# -# local-address Local IP address to which we bind -# -local-address=,127.0.0.1 - -################################# -# local-ipv6 Local IP address to which we bind -# -# local-ipv6= - -################################# -# local-port The port on which we listen -# -local-port=53 - -################################# -# log-dns-details If PDNS should log failed update requests -# -log-dns-details=yes - -################################# -# log-failed-updates If PDNS should log failed update requests -# -# log-failed-updates= - -################################# -# logfile Logfile to use -# -# logfile=/var/log/pdns.log - -################################# -# logging-facility Log under a specific facility -# -# logging-facility= - -################################# -# loglevel Amount of logging. Higher is more. Do not set below 3 -# -# loglevel=4 - -################################# -# master Act as a master -# -master=yes - -################################# -# max-queue-length Maximum queuelength before considering situation lost -# -# max-queue-length=5000 - -################################# -# max-tcp-connections Maximum number of TCP connections -# -# max-tcp-connections=10 - -################################# -# module-dir Default directory for modules -# -module-dir=/usr/lib/powerdns - -################################# -# negquery-cache-ttl Seconds to store packets in the PacketCache -# -# negquery-cache-ttl=60 - -################################# -# out-of-zone-additional-processing Do out of zone additional processing -# -# out-of-zone-additional-processing=no - -################################# -# query-cache-ttl Seconds to store packets in the PacketCache -# -# query-cache-ttl=20 - -################################# -# query-logging Hint backends that queries should be logged -# -# query-logging=no - -################################# -# queue-limit Maximum number of milliseconds to queue a query -# -# queue-limit=1500 - -################################# -# query-local-address The IP address to use as a source address for sending -# queries. -# query-local-address= - -################################# -# receiver-threads Number of receiver threads to launch -# -# receiver-threads=1 - -################################# -# recursive-cache-ttl Seconds to store packets in the PacketCache -# -# recursive-cache-ttl=10 - -################################# -# recursor If recursion is desired, IP address of a recursing nameserver -# -# recursor= - -################################# -# setgid If set, change group id to this gid for more security -# -setgid=pdns - -################################# -# setuid If set, change user id to this uid for more security -# -setuid=pdns - -################################# -# skip-cname Do not perform CNAME indirection for each query -# -# skip-cname=no - -################################# -# slave Act as a slave -# -# slave=no - -################################# -# slave-cycle-interval Reschedule failed SOA serial checks once every .. seconds -# -# slave-cycle-interval=60 - -################################# -# smtpredirector Our smtpredir MX host -# -# smtpredirector=a.misconfigured.powerdns.smtp.server - -################################# -# soa-minimum-ttl Default SOA mininum ttl -# -# soa-minimum-ttl=3600 - -################################# -# soa-refresh-default Default SOA refresh -# -# soa-refresh-default=10800 - -################################# -# soa-retry-default Default SOA retry -# -# soa-retry-default=3600 - -################################# -# soa-expire-default Default SOA expire -# -# soa-expire-default=604800 - -################################# -# soa-serial-offset Make sure that no SOA serial is less than this number -# -# soa-serial-offset=0 - -################################# -# socket-dir Where the controlsocket will live -# -socket-dir=/var/run - -################################# -# strict-rfc-axfrs Perform strictly rfc compliant axfrs (very slow) -# -# strict-rfc-axfrs=no - -################################# -# urlredirector Where we send hosts to that need to be url redirected -# -# urlredirector=127.0.0.1 - -################################# -# use-logfile Use a log file -# -# use-logfile=yes - -################################# -# webserver Start a webserver for monitoring -# -# webserver=no - -################################# -# webserver-address IP Address of webserver to listen on -# -# webserver-address=127.0.0.1 - -################################# -# webserver-password Password required for accessing the webserver -# -# webserver-password= - -################################# -# webserver-port Port of webserver to listen on -# -# webserver-port=8081 - -################################# -# webserver-print-arguments If the webserver should print arguments -# -# webserver-print-arguments=no - -################################# -# wildcard-url Process URL and MBOXFW records -# -# wildcard-url=no - -################################# -# wildcards Honor wildcards in the database -# -# wildcards= - -################################# -# version-string What should PowerDNS return for version -# allowed methods are anonymous / powerdns / full / custom -version-string=powerdns - -# include froxlor-bind-specific config -include-dir=/etc/powerdns/froxlor/ -]]> - - - - - named.conf - -# How often to check for zone changes. See 'Operation' section. -bind-check-interval=180 - -# Uncomment to enable Huffman compression on zone data. -# Currently saves around 20% of memory actually used, but slows down operation. -# bind-enable-huffman -]]> - - - - - - - - - - - - {{settings.system.vmail_gid}} - - - - - {{settings.system.vmail_uid}} - - - - - - - - - - - - - - - - - -password = -dbname = -hosts = -query = SELECT destination FROM mail_virtual WHERE email = '%s' AND trim(destination) <> '' -]]> - - - - -password = -dbname = -hosts = -query = SELECT domain FROM panel_domains WHERE domain = '%s' AND isemaildomain = '1' -]]> - - - - -password = -dbname = -expansion_limit = 1 -hosts = -query = SELECT CONCAT(homedir,maildir) FROM mail_users WHERE email = '%s' -]]> - - - - -password = -dbname = -hosts = -query = SELECT DISTINCT username FROM mail_users WHERE email in ((SELECT mail_virtual.email_full FROM mail_virtual WHERE mail_virtual.email = '%s' UNION SELECT mail_virtual.destination FROM mail_virtual WHERE mail_virtual.email = '%s')); -]]> - - - - -password = -dbname = -expansion_limit = 1 -hosts = -query = SELECT uid FROM mail_users WHERE email = '%s' -]]> - - - - -password = -dbname = -expansion_limit = 1 -hosts = -query = SELECT gid FROM mail_users WHERE email = '%s' -]]> - - - - -]]> - - - - - - - - - - - //service[@type='smtp']/general/commands[@index=1] - - //service[@type='smtp']/general/installs[@index=1] - - //service[@type='smtp']/general/commands[@index=2] - - - - -# set myhostname to $mydomain because Froxlor alrady uses a FQDN -myhostname = $mydomain - -# SENDING MAIL -# -# The myorigin parameter specifies the domain that locally-posted -# mail appears to come from. The default is to append $myhostname, -# which is fine for small sites. If you run a domain with multiple -# machines, you should (1) change this to $mydomain and (2) set up -# a domain-wide alias database that aliases each user to -# user@that.users.mailhost. -# -# For the sake of consistency between sender and recipient addresses, -# myorigin also specifies the default domain name that is appended -# to recipient addresses that have no @domain part. -# -# Debian GNU/Linux specific: Specifying a file name will cause the -# first line of that file to be used as the name. The Debian default -# is /etc/mailname. -# -#myorigin = /etc/mailname -#myorigin = $myhostname -#myorigin = $mydomain - -# RECEIVING MAIL - -# The inet_interfaces parameter specifies the network interface -# addresses that this mail system receives mail on. By default, -# the software claims all active interfaces on the machine. The -# parameter also controls delivery of mail to user@[ip.address]. -# -# See also the proxy_interfaces parameter, for network addresses that -# are forwarded to us via a proxy or network address translator. -# -# Note: you need to stop/start Postfix when this parameter changes. -# -inet_interfaces = all -#inet_interfaces = $myhostname -#inet_interfaces = $myhostname, localhost - -# The proxy_interfaces parameter specifies the network interface -# addresses that this mail system receives mail on by way of a -# proxy or network address translation unit. This setting extends -# the address list specified with the inet_interfaces parameter. -# -# You must specify your proxy/NAT addresses when your system is a -# backup MX host for other domains, otherwise mail delivery loops -# will happen when the primary MX host is down. -# -#proxy_interfaces = -#proxy_interfaces = 1.2.3.4 - -# The mydestination parameter specifies the list of domains that this -# machine considers itself the final destination for. -# -# These domains are routed to the delivery agent specified with the -# local_transport parameter setting. By default, that is the UNIX -# compatible delivery agent that lookups all recipients in /etc/passwd -# and /etc/aliases or their equivalent. -# -# The default is $myhostname + localhost.$mydomain. On a mail domain -# gateway, you should also include $mydomain. -# -# Do not specify the names of virtual domains - those domains are -# specified elsewhere (see VIRTUAL_README). -# -# Do not specify the names of domains that this machine is backup MX -# host for. Specify those names via the relay_domains settings for -# the SMTP server, or use permit_mx_backup if you are lazy (see -# STANDARD_CONFIGURATION_README). -# -# The local machine is always the final destination for mail addressed -# to user@[the.net.work.address] of an interface that the mail system -# receives mail on (see the inet_interfaces parameter). -# -# Specify a list of host or domain names, /file/name or type:table -# patterns, separated by commas and/or whitespace. A /file/name -# pattern is replaced by its contents; a type:table is matched when -# a name matches a lookup key (the right-hand side is ignored). -# Continue long lines by starting the next line with whitespace. -# -# See also below, section "REJECTING MAIL FOR UNKNOWN LOCAL USERS". -# -#mydestination = $myhostname, localhost.$mydomain, localhost -mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain -#mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain, -# mail.$mydomain, www.$mydomain, ftp.$mydomain - -# REJECTING MAIL FOR UNKNOWN LOCAL USERS -# -# The local_recipient_maps parameter specifies optional lookup tables -# with all names or addresses of users that are local with respect -# to $mydestination, $inet_interfaces or $proxy_interfaces. -# -# If this parameter is defined, then the SMTP server will reject -# mail for unknown local users. This parameter is defined by default. -# -# To turn off local recipient checking in the SMTP server, specify -# local_recipient_maps = (i.e. empty). -# -# The default setting assumes that you use the default Postfix local -# delivery agent for local delivery. You need to update the -# local_recipient_maps setting if: -# -# - You define $mydestination domain recipients in files other than -# /etc/passwd, /etc/aliases, or the $virtual_alias_maps files. -# For example, you define $mydestination domain recipients in -# the $virtual_mailbox_maps files. -# -# - You redefine the local delivery agent in master.cf. -# -# - You redefine the "local_transport" setting in main.cf. -# -# - You use the "luser_relay", "mailbox_transport", or "fallback_transport" -# feature of the Postfix local delivery agent (see local(8)). -# -# Details are described in the LOCAL_RECIPIENT_README file. -# -# Beware: if the Postfix SMTP server runs chrooted, you probably have -# to access the passwd file via the proxymap service, in order to -# overcome chroot restrictions. The alternative, having a copy of -# the system passwd file in the chroot jail is just not practical. -# -# The right-hand side of the lookup tables is conveniently ignored. -# In the left-hand side, specify a bare username, an @domain.tld -# wild-card, or specify a user@domain.tld address. -# -#local_recipient_maps = unix:passwd.byname $alias_maps -#local_recipient_maps = proxy:unix:passwd.byname $alias_maps -#local_recipient_maps = - -# The unknown_local_recipient_reject_code specifies the SMTP server -# response code when a recipient domain matches $mydestination or -# ${proxy,inet}_interfaces, while $local_recipient_maps is non-empty -# and the recipient address or address local-part is not found. -# -# The default setting is 550 (reject mail) but it is safer to start -# with 450 (try again later) until you are certain that your -# local_recipient_maps settings are OK. -# -unknown_local_recipient_reject_code = 550 - -# TRUST AND RELAY CONTROL - -# The mynetworks parameter specifies the list of "trusted" SMTP -# clients that have more privileges than "strangers". -# -# In particular, "trusted" SMTP clients are allowed to relay mail -# through Postfix. See the smtpd_recipient_restrictions parameter -# in postconf(5). -# -# You can specify the list of "trusted" network addresses by hand -# or you can let Postfix do it for you (which is the default). -# -# By default (mynetworks_style = subnet), Postfix "trusts" SMTP -# clients in the same IP subnetworks as the local machine. -# On Linux, this does works correctly only with interfaces specified -# with the "ifconfig" command. -# -# Specify "mynetworks_style = class" when Postfix should "trust" SMTP -# clients in the same IP class A/B/C networks as the local machine. -# Don't do this with a dialup site - it would cause Postfix to "trust" -# your entire provider's network. Instead, specify an explicit -# mynetworks list by hand, as described below. -# -# Specify "mynetworks_style = host" when Postfix should "trust" -# only the local machine. -# -#mynetworks_style = class -#mynetworks_style = subnet -#mynetworks_style = host - -# Alternatively, you can specify the mynetworks list by hand, in -# which case Postfix ignores the mynetworks_style setting. -# -# Specify an explicit list of network/netmask patterns, where the -# mask specifies the number of bits in the network part of a host -# address. -# -# You can also specify the absolute pathname of a pattern file instead -# of listing the patterns here. Specify type:table for table-based lookups -# (the value on the table right-hand side is not used). -# -#mynetworks = 168.100.189.0/28, 127.0.0.0/8 -#mynetworks = $config_directory/mynetworks -#mynetworks = hash:/etc/postfix/network_table -mynetworks = 127.0.0.0/8 - -# The relay_domains parameter restricts what destinations this system will -# relay mail to. See the smtpd_recipient_restrictions description in -# postconf(5) for detailed information. -# -# By default, Postfix relays mail -# - from "trusted" clients (IP address matches $mynetworks) to any destination, -# - from "untrusted" clients to destinations that match $relay_domains or -# subdomains thereof, except addresses with sender-specified routing. -# The default relay_domains value is $mydestination. -# -# In addition to the above, the Postfix SMTP server by default accepts mail -# that Postfix is final destination for: -# - destinations that match $inet_interfaces or $proxy_interfaces, -# - destinations that match $mydestination -# - destinations that match $virtual_alias_domains, -# - destinations that match $virtual_mailbox_domains. -# These destinations do not need to be listed in $relay_domains. -# -# Specify a list of hosts or domains, /file/name patterns or type:name -# lookup tables, separated by commas and/or whitespace. Continue -# long lines by starting the next line with whitespace. A file name -# is replaced by its contents; a type:name table is matched when a -# (parent) domain appears as lookup key. -# -# NOTE: Postfix will not automatically forward mail for domains that -# list this system as their primary or backup MX host. See the -# permit_mx_backup restriction description in postconf(5). -# -#relay_domains = $mydestination - -# INTERNET OR INTRANET - -# The relayhost parameter specifies the default host to send mail to -# when no entry is matched in the optional transport(5) table. When -# no relayhost is given, mail is routed directly to the destination. -# -# On an intranet, specify the organizational domain name. If your -# internal DNS uses no MX records, specify the name of the intranet -# gateway host instead. -# -# In the case of SMTP, specify a domain, host, host:port, [host]:port, -# [address] or [address]:port; the form [host] turns off MX lookups. -# -# If you're connected via UUCP, see also the default_transport parameter. -# -#relayhost = $mydomain -#relayhost = [gateway.my.domain] -#relayhost = [mailserver.isp.tld] -#relayhost = uucphost -#relayhost = [an.ip.add.ress] - -# REJECTING UNKNOWN RELAY USERS -# -# The relay_recipient_maps parameter specifies optional lookup tables -# with all addresses in the domains that match $relay_domains. -# -# If this parameter is defined, then the SMTP server will reject -# mail for unknown relay users. This feature is off by default. -# -# The right-hand side of the lookup tables is conveniently ignored. -# In the left-hand side, specify an @domain.tld wild-card, or specify -# a user@domain.tld address. -# -#relay_recipient_maps = hash:/etc/postfix/relay_recipients - -# INPUT RATE CONTROL -# -# The in_flow_delay configuration parameter implements mail input -# flow control. This feature is turned on by default, although it -# still needs further development (it's disabled on SCO UNIX due -# to an SCO bug). -# -# A Postfix process will pause for $in_flow_delay seconds before -# accepting a new message, when the message arrival rate exceeds the -# message delivery rate. With the default 100 SMTP server process -# limit, this limits the mail inflow to 100 messages a second more -# than the number of messages delivered per second. -# -# Specify 0 to disable the feature. Valid delays are 0..10. -# -#in_flow_delay = 1s - -# ADDRESS REWRITING -# -# The ADDRESS_REWRITING_README document gives information about -# address masquerading or other forms of address rewriting including -# username->Firstname.Lastname mapping. - -# ADDRESS REDIRECTION (VIRTUAL DOMAIN) -# -# The VIRTUAL_README document gives information about the many forms -# of domain hosting that Postfix supports. - -# "USER HAS MOVED" BOUNCE MESSAGES -# -# See the discussion in the ADDRESS_REWRITING_README document. - -# TRANSPORT MAP -# -# See the discussion in the ADDRESS_REWRITING_README document. - -# ALIAS DATABASE -# -# The alias_maps parameter specifies the list of alias databases used -# by the local delivery agent. The default list is system dependent. -# -# On systems with NIS, the default is to search the local alias -# database, then the NIS alias database. See aliases(5) for syntax -# details. -# -# If you change the alias database, run "postalias /etc/aliases" (or -# wherever your system stores the mail alias file), or simply run -# "newaliases" to build the necessary DBM or DB file. -# -# It will take a minute or so before changes become visible. Use -# "postfix reload" to eliminate the delay. -# -#alias_maps = dbm:/etc/aliases -#alias_maps = hash:/etc/aliases -#alias_maps = hash:/etc/aliases, nis:mail.aliases -#alias_maps = netinfo:/aliases - -# The alias_database parameter specifies the alias database(s) that -# are built with "newaliases" or "sendmail -bi". This is a separate -# configuration parameter, because alias_maps (see above) may specify -# tables that are not necessarily all under control by Postfix. -# -#alias_database = dbm:/etc/aliases -#alias_database = dbm:/etc/mail/aliases -#alias_database = hash:/etc/aliases -#alias_database = hash:/etc/aliases, hash:/opt/majordomo/aliases - -# ADDRESS EXTENSIONS (e.g., user+foo) -# -# The recipient_delimiter parameter specifies the separator between -# user names and address extensions (user+foo). See canonical(5), -# local(8), relocated(5) and virtual(5) for the effects this has on -# aliases, canonical, virtual, relocated and .forward file lookups. -# Basically, the software tries user+foo and .forward+foo before -# trying user and .forward. -# -#recipient_delimiter = + - -# DELIVERY TO MAILBOX -# -# The home_mailbox parameter specifies the optional pathname of a -# mailbox file relative to a user's home directory. The default -# mailbox file is /var/spool/mail/user or /var/mail/user. Specify -# "Maildir/" for qmail-style delivery (the / is required). -# -#home_mailbox = Mailbox -#home_mailbox = Maildir/ - -# The mail_spool_directory parameter specifies the directory where -# UNIX-style mailboxes are kept. The default setting depends on the -# system type. -# -#mail_spool_directory = /var/mail -#mail_spool_directory = /var/spool/mail - -# The mailbox_command parameter specifies the optional external -# command to use instead of mailbox delivery. The command is run as -# the recipient with proper HOME, SHELL and LOGNAME environment settings. -# Exception: delivery for root is done as $default_user. -# -# Other environment variables of interest: USER (recipient username), -# EXTENSION (address extension), DOMAIN (domain part of address), -# and LOCAL (the address localpart). -# -# Unlike other Postfix configuration parameters, the mailbox_command -# parameter is not subjected to $parameter substitutions. This is to -# make it easier to specify shell syntax (see example below). -# -# Avoid shell meta characters because they will force Postfix to run -# an expensive shell process. Procmail alone is expensive enough. -# -# IF YOU USE THIS TO DELIVER MAIL SYSTEM-WIDE, YOU MUST SET UP AN -# ALIAS THAT FORWARDS MAIL FOR ROOT TO A REAL USER. -# -mailbox_command = /usr/lib/dovecot/deliver -#mailbox_command = /usr/bin/procmail -a "$EXTENSION" - -# The mailbox_transport specifies the optional transport in master.cf -# to use after processing aliases and .forward files. This parameter -# has precedence over the mailbox_command, fallback_transport and -# luser_relay parameters. -# -# Specify a string of the form transport:nexthop, where transport is -# the name of a mail delivery transport defined in master.cf. The -# :nexthop part is optional. For more details see the sample transport -# configuration file. -# -# NOTE: if you use this feature for accounts not in the UNIX password -# file, then you must update the "local_recipient_maps" setting in -# the main.cf file, otherwise the SMTP server will reject mail for -# non-UNIX accounts with "User unknown in local recipient table". -# -# Cyrus IMAP over LMTP. Specify ``lmtpunix cmd="lmtpd" -# listen="/var/imap/socket/lmtp" prefork=0'' in cyrus.conf. -#mailbox_transport = lmtp:unix:/var/imap/socket/lmtp -# -# Cyrus IMAP via command line. Uncomment the "cyrus...pipe" and -# subsequent line in master.cf. -#mailbox_transport = cyrus - -# The fallback_transport specifies the optional transport in master.cf -# to use for recipients that are not found in the UNIX passwd database. -# This parameter has precedence over the luser_relay parameter. -# -# Specify a string of the form transport:nexthop, where transport is -# the name of a mail delivery transport defined in master.cf. The -# :nexthop part is optional. For more details see the sample transport -# configuration file. -# -# NOTE: if you use this feature for accounts not in the UNIX password -# file, then you must update the "local_recipient_maps" setting in -# the main.cf file, otherwise the SMTP server will reject mail for -# non-UNIX accounts with "User unknown in local recipient table". -# -#fallback_transport = lmtp:unix:/file/name -#fallback_transport = cyrus -#fallback_transport = - -# The luser_relay parameter specifies an optional destination address -# for unknown recipients. By default, mail for unknown@$mydestination, -# unknown@[$inet_interfaces] or unknown@[$proxy_interfaces] is returned -# as undeliverable. -# -# The following expansions are done on luser_relay: $user (recipient -# username), $shell (recipient shell), $home (recipient home directory), -# $recipient (full recipient address), $extension (recipient address -# extension), $domain (recipient domain), $local (entire recipient -# localpart), $recipient_delimiter. Specify ${name?value} or -# ${name:value} to expand value only when $name does (does not) exist. -# -# luser_relay works only for the default Postfix local delivery agent. -# -# NOTE: if you use this feature for accounts not in the UNIX password -# file, then you must specify "local_recipient_maps =" (i.e. empty) in -# the main.cf file, otherwise the SMTP server will reject mail for -# non-UNIX accounts with "User unknown in local recipient table". -# -#luser_relay = $user@other.host -#luser_relay = $local@other.host -#luser_relay = admin+$local - -# JUNK MAIL CONTROLS -# -# The controls listed here are only a very small subset. The file -# SMTPD_ACCESS_README provides an overview. - -# The header_checks parameter specifies an optional table with patterns -# that each logical message header is matched against, including -# headers that span multiple physical lines. -# -# By default, these patterns also apply to MIME headers and to the -# headers of attached messages. With older Postfix versions, MIME and -# attached message headers were treated as body text. -# -# For details, see "man header_checks". -# -#header_checks = regexp:/etc/postfix/header_checks - -# FAST ETRN SERVICE -# -# Postfix maintains per-destination logfiles with information about -# deferred mail, so that mail can be flushed quickly with the SMTP -# "ETRN domain.tld" command, or by executing "sendmail -qRdomain.tld". -# See the ETRN_README document for a detailed description. -# -# The fast_flush_domains parameter controls what destinations are -# eligible for this service. By default, they are all domains that -# this server is willing to relay mail to. -# -#fast_flush_domains = $relay_domains - -# SHOW SOFTWARE VERSION OR NOT -# -# The smtpd_banner parameter specifies the text that follows the 220 -# code in the SMTP server's greeting banner. Some people like to see -# the mail version advertised. By default, Postfix shows no version. -# -# You MUST specify $myhostname at the start of the text. That is an -# RFC requirement. Postfix itself does not care. -# -#smtpd_banner = $myhostname ESMTP $mail_name -#smtpd_banner = $myhostname ESMTP $mail_name ($mail_version) -smtpd_banner = $myhostname ESMTP $mail_name (Debian/GNU) - - -# PARALLEL DELIVERY TO THE SAME DESTINATION -# -# How many parallel deliveries to the same user or domain? With local -# delivery, it does not make sense to do massively parallel delivery -# to the same user, because mailbox updates must happen sequentially, -# and expensive pipelines in .forward files can cause disasters when -# too many are run at the same time. With SMTP deliveries, 10 -# simultaneous connections to the same domain could be sufficient to -# raise eyebrows. -# -# Each message delivery transport has its XXX_destination_concurrency_limit -# parameter. The default is $default_destination_concurrency_limit for -# most delivery transports. For the local delivery agent the default is 2. - -#local_destination_concurrency_limit = 2 -#default_destination_concurrency_limit = 20 - -# DEBUGGING CONTROL -# -# The debug_peer_level parameter specifies the increment in verbose -# logging level when an SMTP client or server host name or address -# matches a pattern in the debug_peer_list parameter. -# -#debug_peer_level = 2 - -# The debug_peer_list parameter specifies an optional list of domain -# or network patterns, /file/name patterns or type:name tables. When -# an SMTP client or server host name or address matches a pattern, -# increase the verbose logging level by the amount specified in the -# debug_peer_level parameter. -# -#debug_peer_list = 127.0.0.1 -#debug_peer_list = some.domain - -# The debugger_command specifies the external command that is executed -# when a Postfix daemon program is run with the -D option. -# -# Use "command .. & sleep 5" so that the debugger can attach before -# the process marches on. If you use an X-based debugger, be sure to -# set up your XAUTHORITY environment variable before starting Postfix. -# -debugger_command = - PATH=/bin:/usr/bin:/usr/local/bin:/usr/X11R6/bin - ddd $daemon_directory/$process_name $process_id & sleep 5 - -# If you can't use X, use this to capture the call stack when a -# daemon crashes. The result is in a file in the configuration -# directory, and is named after the process name and the process ID. -# -# debugger_command = -# PATH=/bin:/usr/bin:/usr/local/bin; export PATH; (echo cont; -# echo where) | gdb $daemon_directory/$process_name $process_id 2>&1 -# >$config_directory/$process_name.$process_id.log & sleep 5 -# -# Another possibility is to run gdb under a detached screen session. -# To attach to the screen sesssion, su root and run "screen -r -# " where uniquely matches one of the detached -# sessions (from "screen -list"). -# -# debugger_command = -# PATH=/bin:/usr/bin:/sbin:/usr/sbin; export PATH; screen -# -dmS $process_name gdb $daemon_directory/$process_name -# $process_id & sleep 1 - -# INSTALL-TIME CONFIGURATION INFORMATION -# -# The following parameters are used when installing a new Postfix version. -# -# sendmail_path: The full pathname of the Postfix sendmail command. -# This is the Sendmail-compatible mail posting interface. -# -sendmail_path = /usr/sbin/sendmail - -# newaliases_path: The full pathname of the Postfix newaliases command. -# This is the Sendmail-compatible command to build alias databases. -# -newaliases_path = /usr/bin/newaliases - -# mailq_path: The full pathname of the Postfix mailq command. This -# is the Sendmail-compatible mail queue listing command. -# -mailq_path = /usr/bin/mailq - -# setgid_group: The group for mail submission and queue management -# commands. This must be a group name with a numerical group ID that -# is not shared with other accounts, not even with the Postfix account. -# -setgid_group = postdrop - -# html_directory: The location of the Postfix HTML documentation. -# -html_directory = no - -# manpage_directory: The location of the Postfix on-line manual pages. -# -manpage_directory = /usr/share/man - -# sample_directory: The location of the Postfix sample configuration files. -# This parameter is obsolete as of Postfix 2.1. -# -sample_directory = /usr/share/doc/postfix - -# readme_directory: The location of the Postfix README files. -# -readme_directory = /usr/share/doc/postfix -inet_protocols = ipv4 - -append_dot_mydomain = no -biff = no -smtpd_helo_required = yes -smtpd_recipient_restrictions = permit_mynetworks, - permit_sasl_authenticated, - reject_unauth_destination, - reject_unauth_pipelining, - reject_non_fqdn_recipient -smtpd_sender_restrictions = permit_mynetworks, - reject_sender_login_mismatch, - permit_sasl_authenticated, - reject_unknown_helo_hostname, - reject_unknown_recipient_domain, - reject_unknown_sender_domain -smtpd_client_restrictions = permit_mynetworks, - permit_sasl_authenticated, - reject_unknown_client_hostname - -# Postfix 2.10 requires this option. Postfix < 2.10 ignores this. -# The option is intentionally left empty. -smtpd_relay_restrictions = - -# Maximum size of Message in bytes (50MB) -message_size_limit = 52428800 - -## SASL Auth Settings -smtpd_sasl_auth_enable = yes -smtpd_sasl_local_domain = $myhostname -broken_sasl_auth_clients = yes -## Dovecot Settings for deliver, SASL Auth and virtual transport -smtpd_sasl_type = dovecot -virtual_transport = dovecot -dovecot_destination_recipient_limit = 1 -smtpd_sasl_path = private/auth - -# Virtual delivery settings -virtual_mailbox_base = / -virtual_mailbox_maps = mysql:/etc/postfix/mysql-virtual_mailbox_maps.cf -virtual_mailbox_domains = mysql:/etc/postfix/mysql-virtual_mailbox_domains.cf -virtual_alias_maps = mysql:/etc/postfix/mysql-virtual_alias_maps.cf -smtpd_sender_login_maps = mysql:/etc/postfix/mysql-virtual_sender_permissions.cf -virtual_uid_maps = static: -virtual_gid_maps = static: - -# Local delivery settings -local_transport = local -alias_maps = $alias_database - -# Default Mailbox size, is set to 0 which means unlimited! -mailbox_size_limit = 0 -virtual_mailbox_limit = 0 - -### TLS settings -### -## TLS for outgoing mails from the server to another server -#smtp_tls_security_level = may -#smtp_tls_note_starttls_offer = yes -## TLS for incoming connections (clients or other mail servers) -#smtpd_tls_security_level = may -#smtpd_tls_cert_file = /etc/ssl/server/.pem -#smtpd_tls_key_file = $smtpd_tls_cert_file -#smtpd_tls_CAfile = /etc/ssl/certs/ca-certificates.crt -#smtpd_tls_loglevel = 1 -#smtpd_tls_received_header = yes -]]> - - - //service[@type='smtp']/general/files[@index=0] - - - - - //service[@type='smtp']/general/commands[@index=3] - - - - - //service[@type='smtp']/general/commands[@index=1] - - //service[@type='smtp']/general/installs[@index=1] - - - //service[@type='smtp']/general/commands[@index=2] - - - - -# SENDING MAIL -# -# The myorigin parameter specifies the domain that locally-posted -# mail appears to come from. The default is to append $myhostname, -# which is fine for small sites. If you run a domain with multiple -# machines, you should (1) change this to $mydomain and (2) set up -# a domain-wide alias database that aliases each user to -# user@that.users.mailhost. -# -# For the sake of consistency between sender and recipient addresses, -# myorigin also specifies the default domain name that is appended -# to recipient addresses that have no @domain part. -# -# Debian GNU/Linux specific: Specifying a file name will cause the -# first line of that file to be used as the name. The Debian default -# is /etc/mailname. -# -#myorigin = /etc/mailname -#myorigin = $myhostname -#myorigin = $mydomain - -# RECEIVING MAIL - -# The inet_interfaces parameter specifies the network interface -# addresses that this mail system receives mail on. By default, -# the software claims all active interfaces on the machine. The -# parameter also controls delivery of mail to user@[ip.address]. -# -# See also the proxy_interfaces parameter, for network addresses that -# are forwarded to us via a proxy or network address translator. -# -# Note: you need to stop/start Postfix when this parameter changes. -# -inet_interfaces = all -#inet_interfaces = $myhostname -#inet_interfaces = $myhostname, localhost - -# The proxy_interfaces parameter specifies the network interface -# addresses that this mail system receives mail on by way of a -# proxy or network address translation unit. This setting extends -# the address list specified with the inet_interfaces parameter. -# -# You must specify your proxy/NAT addresses when your system is a -# backup MX host for other domains, otherwise mail delivery loops -# will happen when the primary MX host is down. -# -#proxy_interfaces = -#proxy_interfaces = 1.2.3.4 - -# The mydestination parameter specifies the list of domains that this -# machine considers itself the final destination for. -# -# These domains are routed to the delivery agent specified with the -# local_transport parameter setting. By default, that is the UNIX -# compatible delivery agent that lookups all recipients in /etc/passwd -# and /etc/aliases or their equivalent. -# -# The default is $myhostname + localhost.$mydomain. On a mail domain -# gateway, you should also include $mydomain. -# -# Do not specify the names of virtual domains - those domains are -# specified elsewhere (see VIRTUAL_README). -# -# Do not specify the names of domains that this machine is backup MX -# host for. Specify those names via the relay_domains settings for -# the SMTP server, or use permit_mx_backup if you are lazy (see -# STANDARD_CONFIGURATION_README). -# -# The local machine is always the final destination for mail addressed -# to user@[the.net.work.address] of an interface that the mail system -# receives mail on (see the inet_interfaces parameter). -# -# Specify a list of host or domain names, /file/name or type:table -# patterns, separated by commas and/or whitespace. A /file/name -# pattern is replaced by its contents; a type:table is matched when -# a name matches a lookup key (the right-hand side is ignored). -# Continue long lines by starting the next line with whitespace. -# -# See also below, section "REJECTING MAIL FOR UNKNOWN LOCAL USERS". -# -#mydestination = $myhostname, localhost.$mydomain, localhost -mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain -#mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain, -# mail.$mydomain, www.$mydomain, ftp.$mydomain - -# REJECTING MAIL FOR UNKNOWN LOCAL USERS -# -# The local_recipient_maps parameter specifies optional lookup tables -# with all names or addresses of users that are local with respect -# to $mydestination, $inet_interfaces or $proxy_interfaces. -# -# If this parameter is defined, then the SMTP server will reject -# mail for unknown local users. This parameter is defined by default. -# -# To turn off local recipient checking in the SMTP server, specify -# local_recipient_maps = (i.e. empty). -# -# The default setting assumes that you use the default Postfix local -# delivery agent for local delivery. You need to update the -# local_recipient_maps setting if: -# -# - You define $mydestination domain recipients in files other than -# /etc/passwd, /etc/aliases, or the $virtual_alias_maps files. -# For example, you define $mydestination domain recipients in -# the $virtual_mailbox_maps files. -# -# - You redefine the local delivery agent in master.cf. -# -# - You redefine the "local_transport" setting in main.cf. -# -# - You use the "luser_relay", "mailbox_transport", or "fallback_transport" -# feature of the Postfix local delivery agent (see local(8)). -# -# Details are described in the LOCAL_RECIPIENT_README file. -# -# Beware: if the Postfix SMTP server runs chrooted, you probably have -# to access the passwd file via the proxymap service, in order to -# overcome chroot restrictions. The alternative, having a copy of -# the system passwd file in the chroot jail is just not practical. -# -# The right-hand side of the lookup tables is conveniently ignored. -# In the left-hand side, specify a bare username, an @domain.tld -# wild-card, or specify a user@domain.tld address. -# -#local_recipient_maps = unix:passwd.byname $alias_maps -#local_recipient_maps = proxy:unix:passwd.byname $alias_maps -#local_recipient_maps = - -# The unknown_local_recipient_reject_code specifies the SMTP server -# response code when a recipient domain matches $mydestination or -# ${proxy,inet}_interfaces, while $local_recipient_maps is non-empty -# and the recipient address or address local-part is not found. -# -# The default setting is 550 (reject mail) but it is safer to start -# with 450 (try again later) until you are certain that your -# local_recipient_maps settings are OK. -# -unknown_local_recipient_reject_code = 550 - -# TRUST AND RELAY CONTROL - -# The mynetworks parameter specifies the list of "trusted" SMTP -# clients that have more privileges than "strangers". -# -# In particular, "trusted" SMTP clients are allowed to relay mail -# through Postfix. See the smtpd_recipient_restrictions parameter -# in postconf(5). -# -# You can specify the list of "trusted" network addresses by hand -# or you can let Postfix do it for you (which is the default). -# -# By default (mynetworks_style = subnet), Postfix "trusts" SMTP -# clients in the same IP subnetworks as the local machine. -# On Linux, this does works correctly only with interfaces specified -# with the "ifconfig" command. -# -# Specify "mynetworks_style = class" when Postfix should "trust" SMTP -# clients in the same IP class A/B/C networks as the local machine. -# Don't do this with a dialup site - it would cause Postfix to "trust" -# your entire provider's network. Instead, specify an explicit -# mynetworks list by hand, as described below. -# -# Specify "mynetworks_style = host" when Postfix should "trust" -# only the local machine. -# -#mynetworks_style = class -#mynetworks_style = subnet -#mynetworks_style = host - -# Alternatively, you can specify the mynetworks list by hand, in -# which case Postfix ignores the mynetworks_style setting. -# -# Specify an explicit list of network/netmask patterns, where the -# mask specifies the number of bits in the network part of a host -# address. -# -# You can also specify the absolute pathname of a pattern file instead -# of listing the patterns here. Specify type:table for table-based lookups -# (the value on the table right-hand side is not used). -# -#mynetworks = 168.100.189.0/28, 127.0.0.0/8 -#mynetworks = $config_directory/mynetworks -#mynetworks = hash:/etc/postfix/network_table -mynetworks = 127.0.0.0/8 - -# The relay_domains parameter restricts what destinations this system will -# relay mail to. See the smtpd_recipient_restrictions description in -# postconf(5) for detailed information. -# -# By default, Postfix relays mail -# - from "trusted" clients (IP address matches $mynetworks) to any destination, -# - from "untrusted" clients to destinations that match $relay_domains or -# subdomains thereof, except addresses with sender-specified routing. -# The default relay_domains value is $mydestination. -# -# In addition to the above, the Postfix SMTP server by default accepts mail -# that Postfix is final destination for: -# - destinations that match $inet_interfaces or $proxy_interfaces, -# - destinations that match $mydestination -# - destinations that match $virtual_alias_domains, -# - destinations that match $virtual_mailbox_domains. -# These destinations do not need to be listed in $relay_domains. -# -# Specify a list of hosts or domains, /file/name patterns or type:name -# lookup tables, separated by commas and/or whitespace. Continue -# long lines by starting the next line with whitespace. A file name -# is replaced by its contents; a type:name table is matched when a -# (parent) domain appears as lookup key. -# -# NOTE: Postfix will not automatically forward mail for domains that -# list this system as their primary or backup MX host. See the -# permit_mx_backup restriction description in postconf(5). -# -#relay_domains = $mydestination - -# INTERNET OR INTRANET - -# The relayhost parameter specifies the default host to send mail to -# when no entry is matched in the optional transport(5) table. When -# no relayhost is given, mail is routed directly to the destination. -# -# On an intranet, specify the organizational domain name. If your -# internal DNS uses no MX records, specify the name of the intranet -# gateway host instead. -# -# In the case of SMTP, specify a domain, host, host:port, [host]:port, -# [address] or [address]:port; the form [host] turns off MX lookups. -# -# If you're connected via UUCP, see also the default_transport parameter. -# -#relayhost = $mydomain -#relayhost = [gateway.my.domain] -#relayhost = [mailserver.isp.tld] -#relayhost = uucphost -#relayhost = [an.ip.add.ress] - -# REJECTING UNKNOWN RELAY USERS -# -# The relay_recipient_maps parameter specifies optional lookup tables -# with all addresses in the domains that match $relay_domains. -# -# If this parameter is defined, then the SMTP server will reject -# mail for unknown relay users. This feature is off by default. -# -# The right-hand side of the lookup tables is conveniently ignored. -# In the left-hand side, specify an @domain.tld wild-card, or specify -# a user@domain.tld address. -# -#relay_recipient_maps = hash:/etc/postfix/relay_recipients - -# INPUT RATE CONTROL -# -# The in_flow_delay configuration parameter implements mail input -# flow control. This feature is turned on by default, although it -# still needs further development (it's disabled on SCO UNIX due -# to an SCO bug). -# -# A Postfix process will pause for $in_flow_delay seconds before -# accepting a new message, when the message arrival rate exceeds the -# message delivery rate. With the default 100 SMTP server process -# limit, this limits the mail inflow to 100 messages a second more -# than the number of messages delivered per second. -# -# Specify 0 to disable the feature. Valid delays are 0..10. -# -#in_flow_delay = 1s - -# ADDRESS REWRITING -# -# The ADDRESS_REWRITING_README document gives information about -# address masquerading or other forms of address rewriting including -# username->Firstname.Lastname mapping. - -# ADDRESS REDIRECTION (VIRTUAL DOMAIN) -# -# The VIRTUAL_README document gives information about the many forms -# of domain hosting that Postfix supports. - -# "USER HAS MOVED" BOUNCE MESSAGES -# -# See the discussion in the ADDRESS_REWRITING_README document. - -# TRANSPORT MAP -# -# See the discussion in the ADDRESS_REWRITING_README document. - -# ALIAS DATABASE -# -# The alias_maps parameter specifies the list of alias databases used -# by the local delivery agent. The default list is system dependent. -# -# On systems with NIS, the default is to search the local alias -# database, then the NIS alias database. See aliases(5) for syntax -# details. -# -# If you change the alias database, run "postalias /etc/aliases" (or -# wherever your system stores the mail alias file), or simply run -# "newaliases" to build the necessary DBM or DB file. -# -# It will take a minute or so before changes become visible. Use -# "postfix reload" to eliminate the delay. -# -#alias_maps = dbm:/etc/aliases -#alias_maps = hash:/etc/aliases -#alias_maps = hash:/etc/aliases, nis:mail.aliases -#alias_maps = netinfo:/aliases - -# The alias_database parameter specifies the alias database(s) that -# are built with "newaliases" or "sendmail -bi". This is a separate -# configuration parameter, because alias_maps (see above) may specify -# tables that are not necessarily all under control by Postfix. -# -#alias_database = dbm:/etc/aliases -#alias_database = dbm:/etc/mail/aliases -#alias_database = hash:/etc/aliases -#alias_database = hash:/etc/aliases, hash:/opt/majordomo/aliases - -# ADDRESS EXTENSIONS (e.g., user+foo) -# -# The recipient_delimiter parameter specifies the separator between -# user names and address extensions (user+foo). See canonical(5), -# local(8), relocated(5) and virtual(5) for the effects this has on -# aliases, canonical, virtual, relocated and .forward file lookups. -# Basically, the software tries user+foo and .forward+foo before -# trying user and .forward. -# -#recipient_delimiter = + - -# DELIVERY TO MAILBOX -# -# The home_mailbox parameter specifies the optional pathname of a -# mailbox file relative to a user's home directory. The default -# mailbox file is /var/spool/mail/user or /var/mail/user. Specify -# "Maildir/" for qmail-style delivery (the / is required). -# -#home_mailbox = Mailbox -#home_mailbox = Maildir/ - -# The mail_spool_directory parameter specifies the directory where -# UNIX-style mailboxes are kept. The default setting depends on the -# system type. -# -#mail_spool_directory = /var/mail -#mail_spool_directory = /var/spool/mail - -# The mailbox_command parameter specifies the optional external -# command to use instead of mailbox delivery. The command is run as -# the recipient with proper HOME, SHELL and LOGNAME environment settings. -# Exception: delivery for root is done as $default_user. -# -# Other environment variables of interest: USER (recipient username), -# EXTENSION (address extension), DOMAIN (domain part of address), -# and LOCAL (the address localpart). -# -# Unlike other Postfix configuration parameters, the mailbox_command -# parameter is not subjected to $parameter substitutions. This is to -# make it easier to specify shell syntax (see example below). -# -# Avoid shell meta characters because they will force Postfix to run -# an expensive shell process. Procmail alone is expensive enough. -# -# IF YOU USE THIS TO DELIVER MAIL SYSTEM-WIDE, YOU MUST SET UP AN -# ALIAS THAT FORWARDS MAIL FOR ROOT TO A REAL USER. -# -#mailbox_command = /usr/bin/procmail -#mailbox_command = /usr/bin/procmail -a "$EXTENSION" - -# The mailbox_transport specifies the optional transport in master.cf -# to use after processing aliases and .forward files. This parameter -# has precedence over the mailbox_command, fallback_transport and -# luser_relay parameters. -# -# Specify a string of the form transport:nexthop, where transport is -# the name of a mail delivery transport defined in master.cf. The -# :nexthop part is optional. For more details see the sample transport -# configuration file. -# -# NOTE: if you use this feature for accounts not in the UNIX password -# file, then you must update the "local_recipient_maps" setting in -# the main.cf file, otherwise the SMTP server will reject mail for -# non-UNIX accounts with "User unknown in local recipient table". -# -# Cyrus IMAP over LMTP. Specify ``lmtpunix cmd="lmtpd" -# listen="/var/imap/socket/lmtp" prefork=0'' in cyrus.conf. -#mailbox_transport = lmtp:unix:/var/imap/socket/lmtp -# -# Cyrus IMAP via command line. Uncomment the "cyrus...pipe" and -# subsequent line in master.cf. -#mailbox_transport = cyrus - -# The fallback_transport specifies the optional transport in master.cf -# to use for recipients that are not found in the UNIX passwd database. -# This parameter has precedence over the luser_relay parameter. -# -# Specify a string of the form transport:nexthop, where transport is -# the name of a mail delivery transport defined in master.cf. The -# :nexthop part is optional. For more details see the sample transport -# configuration file. -# -# NOTE: if you use this feature for accounts not in the UNIX password -# file, then you must update the "local_recipient_maps" setting in -# the main.cf file, otherwise the SMTP server will reject mail for -# non-UNIX accounts with "User unknown in local recipient table". -# -#fallback_transport = lmtp:unix:/file/name -#fallback_transport = cyrus -#fallback_transport = - -# The luser_relay parameter specifies an optional destination address -# for unknown recipients. By default, mail for unknown@$mydestination, -# unknown@[$inet_interfaces] or unknown@[$proxy_interfaces] is returned -# as undeliverable. -# -# The following expansions are done on luser_relay: $user (recipient -# username), $shell (recipient shell), $home (recipient home directory), -# $recipient (full recipient address), $extension (recipient address -# extension), $domain (recipient domain), $local (entire recipient -# localpart), $recipient_delimiter. Specify ${name?value} or -# ${name:value} to expand value only when $name does (does not) exist. -# -# luser_relay works only for the default Postfix local delivery agent. -# -# NOTE: if you use this feature for accounts not in the UNIX password -# file, then you must specify "local_recipient_maps =" (i.e. empty) in -# the main.cf file, otherwise the SMTP server will reject mail for -# non-UNIX accounts with "User unknown in local recipient table". -# -#luser_relay = $user@other.host -#luser_relay = $local@other.host -#luser_relay = admin+$local - -# JUNK MAIL CONTROLS -# -# The controls listed here are only a very small subset. The file -# SMTPD_ACCESS_README provides an overview. - -# The header_checks parameter specifies an optional table with patterns -# that each logical message header is matched against, including -# headers that span multiple physical lines. -# -# By default, these patterns also apply to MIME headers and to the -# headers of attached messages. With older Postfix versions, MIME and -# attached message headers were treated as body text. -# -# For details, see "man header_checks". -# -#header_checks = regexp:/etc/postfix/header_checks - -# FAST ETRN SERVICE -# -# Postfix maintains per-destination logfiles with information about -# deferred mail, so that mail can be flushed quickly with the SMTP -# "ETRN domain.tld" command, or by executing "sendmail -qRdomain.tld". -# See the ETRN_README document for a detailed description. -# -# The fast_flush_domains parameter controls what destinations are -# eligible for this service. By default, they are all domains that -# this server is willing to relay mail to. -# -#fast_flush_domains = $relay_domains - -# SHOW SOFTWARE VERSION OR NOT -# -# The smtpd_banner parameter specifies the text that follows the 220 -# code in the SMTP server's greeting banner. Some people like to see -# the mail version advertised. By default, Postfix shows no version. -# -# You MUST specify $myhostname at the start of the text. That is an -# RFC requirement. Postfix itself does not care. -# -#smtpd_banner = $myhostname ESMTP $mail_name -#smtpd_banner = $myhostname ESMTP $mail_name ($mail_version) -smtpd_banner = $myhostname ESMTP $mail_name (Debian/GNU) - - -# PARALLEL DELIVERY TO THE SAME DESTINATION -# -# How many parallel deliveries to the same user or domain? With local -# delivery, it does not make sense to do massively parallel delivery -# to the same user, because mailbox updates must happen sequentially, -# and expensive pipelines in .forward files can cause disasters when -# too many are run at the same time. With SMTP deliveries, 10 -# simultaneous connections to the same domain could be sufficient to -# raise eyebrows. -# -# Each message delivery transport has its XXX_destination_concurrency_limit -# parameter. The default is $default_destination_concurrency_limit for -# most delivery transports. For the local delivery agent the default is 2. - -#local_destination_concurrency_limit = 2 -#default_destination_concurrency_limit = 20 - -# DEBUGGING CONTROL -# -# The debug_peer_level parameter specifies the increment in verbose -# logging level when an SMTP client or server host name or address -# matches a pattern in the debug_peer_list parameter. -# -#debug_peer_level = 2 - -# The debug_peer_list parameter specifies an optional list of domain -# or network patterns, /file/name patterns or type:name tables. When -# an SMTP client or server host name or address matches a pattern, -# increase the verbose logging level by the amount specified in the -# debug_peer_level parameter. -# -#debug_peer_list = 127.0.0.1 -#debug_peer_list = some.domain - -# The debugger_command specifies the external command that is executed -# when a Postfix daemon program is run with the -D option. -# -# Use "command .. & sleep 5" so that the debugger can attach before -# the process marches on. If you use an X-based debugger, be sure to -# set up your XAUTHORITY environment variable before starting Postfix. -# -debugger_command = - PATH=/bin:/usr/bin:/usr/local/bin:/usr/X11R6/bin - ddd $daemon_directory/$process_name $process_id & sleep 5 - -# If you can't use X, use this to capture the call stack when a -# daemon crashes. The result is in a file in the configuration -# directory, and is named after the process name and the process ID. -# -# debugger_command = -# PATH=/bin:/usr/bin:/usr/local/bin; export PATH; (echo cont; -# echo where) | gdb $daemon_directory/$process_name $process_id 2>&1 -# >$config_directory/$process_name.$process_id.log & sleep 5 -# -# Another possibility is to run gdb under a detached screen session. -# To attach to the screen sesssion, su root and run "screen -r -# " where uniquely matches one of the detached -# sessions (from "screen -list"). -# -# debugger_command = -# PATH=/bin:/usr/bin:/sbin:/usr/sbin; export PATH; screen -# -dmS $process_name gdb $daemon_directory/$process_name -# $process_id & sleep 1 - -# INSTALL-TIME CONFIGURATION INFORMATION -# -# The following parameters are used when installing a new Postfix version. -# -# sendmail_path: The full pathname of the Postfix sendmail command. -# This is the Sendmail-compatible mail posting interface. -# -sendmail_path = /usr/sbin/sendmail - -# newaliases_path: The full pathname of the Postfix newaliases command. -# This is the Sendmail-compatible command to build alias databases. -# -newaliases_path = /usr/bin/newaliases - -# mailq_path: The full pathname of the Postfix mailq command. This -# is the Sendmail-compatible mail queue listing command. -# -mailq_path = /usr/bin/mailq - -# setgid_group: The group for mail submission and queue management -# commands. This must be a group name with a numerical group ID that -# is not shared with other accounts, not even with the Postfix account. -# -setgid_group = postdrop - -# html_directory: The location of the Postfix HTML documentation. -# -html_directory = no - -# manpage_directory: The location of the Postfix on-line manual pages. -# -manpage_directory = /usr/share/man - -# sample_directory: The location of the Postfix sample configuration files. -# This parameter is obsolete as of Postfix 2.1. -# -sample_directory = /usr/share/doc/postfix - -# readme_directory: The location of the Postfix README files. -# -readme_directory = /usr/share/doc/postfix -inet_protocols = ipv4 - -append_dot_mydomain = no -biff = no -smtpd_helo_required = yes -smtpd_recipient_restrictions = permit_mynetworks, - permit_sasl_authenticated, - reject_unauth_destination, - reject_unauth_pipelining, - reject_non_fqdn_recipient -smtpd_sender_restrictions = permit_mynetworks, - reject_sender_login_mismatch, - permit_sasl_authenticated, - reject_unknown_helo_hostname, - reject_unknown_recipient_domain, - reject_unknown_sender_domain -smtpd_client_restrictions = permit_mynetworks, - permit_sasl_authenticated, - reject_unknown_client_hostname - -# Postfix 2.10 requires this option. Postfix < 2.10 ignores this. -# The option is intentionally left empty. -smtpd_relay_restrictions = - -# Maximum size of Message in bytes (50MB) -message_size_limit = 52428800 - -## SASL Auth Settings -smtpd_sasl_auth_enable = yes -smtpd_sasl_local_domain = $myhostname -broken_sasl_auth_clients = yes - -# Virtual delivery settings -virtual_mailbox_base = / -virtual_mailbox_maps = mysql:/etc/postfix/mysql-virtual_mailbox_maps.cf -virtual_mailbox_domains = mysql:/etc/postfix/mysql-virtual_mailbox_domains.cf -virtual_alias_maps = mysql:/etc/postfix/mysql-virtual_alias_maps.cf -smtpd_sender_login_maps = mysql:/etc/postfix/mysql-virtual_sender_permissions.cf -virtual_uid_maps = static: -virtual_gid_maps = static: - -# Local delivery settings -local_transport = local -alias_maps = $alias_database - -# Default Mailbox size, is set to 0 which means unlimited! -mailbox_size_limit = 0 -virtual_mailbox_limit = 0 - -### TLS settings -### -## TLS for outgoing mails from the server to another server -#smtp_tls_security_level = may -#smtp_tls_note_starttls_offer = yes -## TLS for email client -#smtpd_tls_security_level = may -#smtpd_tls_cert_file = /etc/ssl/server/.pem -#smtpd_tls_key_file = $smtpd_tls_cert_file -#smtpd_tls_CAfile = /etc/ssl/certs/ca-certificates.crt -#smtpd_tls_loglevel = 1 -#smtpd_tls_received_header = yes -]]> - - - //service[@type='smtp']/general/files[@index=0] - - -sql_user: -sql_passwd: -sql_database: -sql_select: SELECT password FROM mail_users WHERE username='%u@%r' OR email='%u@%r' -]]> - - - //service[@type='smtp']/general/commands[@index=3] - - - - - - - - - - - - - to select which instance is used (an alternative -# to -c ). The instance name is also added to Dovecot processes -# in ps output. -#instance_name = dovecot - -# Greeting message for clients. -#login_greeting = Dovecot ready. - -# Space separated list of trusted network ranges. Connections from these -# IPs are allowed to override their IP addresses and ports (for logging and -# for authentication checks). disable_plaintext_auth is also ignored for -# these networks. Typically you'd specify your IMAP proxy servers here. -#login_trusted_networks = - -# Sepace separated list of login access check sockets (e.g. tcpwrap) -#login_access_sockets = - -# With proxy_maybe=yes if proxy destination matches any of these IPs, don't do -# proxying. This isn't necessary normally, but may be useful if the destination -# IP is e.g. a load balancer's IP. -#auth_proxy_self = - -# Show more verbose process titles (in ps). Currently shows user name and -# IP address. Useful for seeing who are actually using the IMAP processes -# (eg. shared mailboxes or if same uid is used for multiple accounts). -#verbose_proctitle = no - -# Should all processes be killed when Dovecot master process shuts down. -# Setting this to "no" means that Dovecot can be upgraded without -# forcing existing client connections to close (although that could also be -# a problem if the upgrade is e.g. because of a security fix). -#shutdown_clients = yes - -# If non-zero, run mail commands via this many connections to doveadm server, -# instead of running them directly in the same process. -#doveadm_worker_count = 0 -# UNIX socket or host:port used for connecting to doveadm server -#doveadm_socket_path = doveadm-server - -# Space separated list of environment variables that are preserved on Dovecot -# startup and passed down to all of its child processes. You can also give -# key=value pairs to always set specific settings. -#import_environment = TZ - -## -## Dictionary server settings -## - -# Dictionary can be used to store key=value lists. This is used by several -# plugins. The dictionary can be accessed either directly or though a -# dictionary server. The following dict block maps dictionary names to URIs -# when the server is used. These can then be referenced using URIs in format -# "proxy::". - -dict { - #quota = mysql:/etc/dovecot/dovecot-dict-sql.conf.ext - #expire = sqlite:/etc/dovecot/dovecot-dict-sql.conf.ext -} - -# Most of the actual configuration gets included below. The filenames are -# first sorted by their ASCII value and parsed in that order. The 00-prefixes -# in filenames are intended to make it easier to understand the ordering. -!include conf.d/*.conf - -# A config file can also tried to be included without giving an error if -# it's not found: -!include_try local.conf -]]> - - - - dbname= user= password= - -# Default password scheme. -# -# List of supported schemes is in -# http://wiki2.dovecot.org/Authentication/PasswordSchemes -# -default_pass_scheme = CRYPT - -# passdb query to retrieve the password. It can return fields: -# password - The user's password. This field must be returned. -# user - user@domain from the database. Needed with case-insensitive lookups. -# username and domain - An alternative way to represent the "user" field. -# -# The "user" field is often necessary with case-insensitive lookups to avoid -# e.g. "name" and "nAme" logins creating two different mail directories. If -# your user and domain names are in separate fields, you can return "username" -# and "domain" fields instead of "user". -# -# The query can also return other fields which have a special meaning, see -# http://wiki2.dovecot.org/PasswordDatabase/ExtraFields -# -# Commonly used available substitutions (see http://wiki2.dovecot.org/Variables -# for full list): -# %u = entire user@domain -# %n = user part of user@domain -# %d = domain part of user@domain -# -# Note that these can be used only as input to SQL query. If the query outputs -# any of these substitutions, they're not touched. Otherwise it would be -# difficult to have eg. usernames containing '%' characters. -# -# Example: -# password_query = SELECT userid AS user, pw AS password \ -# FROM users WHERE userid = '%u' AND active = 'Y' -# -#password_query = \ -# SELECT username, domain, password \ -# FROM users WHERE username = '%n' AND domain = '%d' - -# userdb query to retrieve the user information. It can return fields: -# uid - System UID (overrides mail_uid setting) -# gid - System GID (overrides mail_gid setting) -# home - Home directory -# mail - Mail location (overrides mail_location setting) -# -# None of these are strictly required. If you use a single UID and GID, and -# home or mail directory fits to a template string, you could use userdb static -# instead. For a list of all fields that can be returned, see -# http://wiki2.dovecot.org/UserDatabase/ExtraFields -# -# Examples: -# user_query = SELECT home, uid, gid FROM users WHERE userid = '%u' -# user_query = SELECT dir AS home, user AS uid, group AS gid FROM users where userid = '%u' -# user_query = SELECT home, 501 AS uid, 501 AS gid FROM users WHERE userid = '%u' -# -#user_query = \ -# SELECT home, uid, gid \ -# FROM users WHERE username = '%n' AND domain = '%d' -user_query = SELECT CONCAT(homedir, maildir) AS home, CONCAT('maildir:', homedir, maildir) AS mail, uid, gid, CONCAT('*:storage=', quota, 'M') as quota_rule FROM mail_users WHERE (username = '%u' OR email = '%u') - -# If you wish to avoid two SQL lookups (passdb + userdb), you can use -# userdb prefetch instead of userdb sql in dovecot.conf. In that case you'll -# also have to return userdb fields in password_query prefixed with "userdb_" -# string. For example: -#password_query = \ -# SELECT userid AS user, password, \ -# home AS userdb_home, uid AS userdb_uid, gid AS userdb_gid \ -# FROM users WHERE userid = '%u' -password_query = SELECT username AS user, password_enc AS password, CONCAT(homedir, maildir) AS userdb_home, uid AS userdb_uid, gid AS userdb_gid, CONCAT('maildir:', homedir, maildir) AS userdb_mail, CONCAT('*:storage=', quota, 'M') as userdb_quota_rule FROM mail_users WHERE (username = '%u' OR email = '%u') AND ((imap = 1 AND '%Ls' = 'imap') OR (pop3 = 1 AND '%Ls' = 'pop3') OR '%Ls' = 'smtp' OR '%Ls' = 'sieve') - -# Query to get a list of all usernames. -#iterate_query = SELECT username AS user FROM users -]]> - - - - to characters. For example "#@/@" means -# that '#' and '/' characters are translated to '@'. -#auth_username_translation = - -# Username formatting before it's looked up from databases. You can use -# the standard variables here, eg. %Lu would lowercase the username, %n would -# drop away the domain if it was given, or "%n-AT-%d" would change the '@' into -# "-AT-". This translation is done after auth_username_translation changes. -#auth_username_format = %Lu - -# If you want to allow master users to log in by specifying the master -# username within the normal username string (ie. not using SASL mechanism's -# support for it), you can specify the separator character here. The format -# is then . UW-IMAP uses "*" as the -# separator, so that could be a good choice. -#auth_master_user_separator = - -# Username to use for users logging in with ANONYMOUS SASL mechanism -#auth_anonymous_username = anonymous - -# Maximum number of dovecot-auth worker processes. They're used to execute -# blocking passdb and userdb queries (eg. MySQL and PAM). They're -# automatically created and destroyed as needed. -#auth_worker_max_count = 30 - -# Host name to use in GSSAPI principal names. The default is to use the -# name returned by gethostname(). Use "$ALL" (with quotes) to allow all keytab -# entries. -#auth_gssapi_hostname = - -# Kerberos keytab to use for the GSSAPI mechanism. Will use the system -# default (usually /etc/krb5.keytab) if not specified. You may need to change -# the auth service to run as root to be able to read this file. -#auth_krb5_keytab = - -# Do NTLM and GSS-SPNEGO authentication using Samba's winbind daemon and -# ntlm_auth helper. -#auth_use_winbind = no - -# Path for Samba's ntlm_auth helper binary. -#auth_winbind_helper_path = /usr/bin/ntlm_auth - -# Time to delay before replying to failed authentications. -#auth_failure_delay = 2 secs - -# Require a valid SSL client certificate or the authentication fails. -#auth_ssl_require_client_cert = no - -# Take the username from client's SSL certificate, using -# X509_NAME_get_text_by_NID() which returns the subject's DN's -# CommonName. -#auth_ssl_username_from_cert = no - -# Space separated list of wanted authentication mechanisms: -# plain login digest-md5 cram-md5 ntlm rpa apop anonymous gssapi otp skey -# gss-spnego -# NOTE: See also disable_plaintext_auth setting. -auth_mechanisms = plain login - -## -## Password and user databases -## - -# -# Password database is used to verify user's password (and nothing more). -# You can have multiple passdbs and userdbs. This is useful if you want to -# allow both system users (/etc/passwd) and virtual users to login without -# duplicating the system users into virtual database. -# -# -# -# User database specifies where mails are located and what user/group IDs -# own them. For single-UID configuration use "static" userdb. -# -# - -#!include auth-deny.conf.ext -#!include auth-master.conf.ext - -#!include auth-system.conf.ext -!include auth-sql.conf.ext -#!include auth-ldap.conf.ext -#!include auth-passwdfile.conf.ext -#!include auth-checkpassword.conf.ext -#!include auth-vpopmail.conf.ext -#!include auth-static.conf.ext -]]> - - - - -# -mail_location = mbox:~/mail:INBOX=/var/mail/%u - -# If you need to set multiple mailbox locations or want to change default -# namespace settings, you can do it by defining namespace sections. -# -# You can have private, shared and public namespaces. Private namespaces -# are for user's personal mails. Shared namespaces are for accessing other -# users' mailboxes that have been shared. Public namespaces are for shared -# mailboxes that are managed by sysadmin. If you create any shared or public -# namespaces you'll typically want to enable ACL plugin also, otherwise all -# users can access all the shared mailboxes, assuming they have permissions -# on filesystem level to do so. -namespace inbox { - # Namespace type: private, shared or public - #type = private - - # Hierarchy separator to use. You should use the same separator for all - # namespaces or some clients get confused. '/' is usually a good one. - # The default however depends on the underlying mail storage format. - #separator = - - # Prefix required to access this namespace. This needs to be different for - # all namespaces. For example "Public/". - #prefix = - - # Physical location of the mailbox. This is in same format as - # mail_location, which is also the default for it. - #location = - - # There can be only one INBOX, and this setting defines which namespace - # has it. - inbox = yes - - # If namespace is hidden, it's not advertised to clients via NAMESPACE - # extension. You'll most likely also want to set list=no. This is mostly - # useful when converting from another server with different namespaces which - # you want to deprecate but still keep working. For example you can create - # hidden namespaces with prefixes "~/mail/", "~%u/mail/" and "mail/". - #hidden = no - - # Show the mailboxes under this namespace with LIST command. This makes the - # namespace visible for clients that don't support NAMESPACE extension. - # "children" value lists child mailboxes, but hides the namespace prefix. - #list = yes - - # Namespace handles its own subscriptions. If set to "no", the parent - # namespace handles them (empty prefix should always have this as "yes") - #subscriptions = yes -} - -# Example shared namespace configuration -#namespace { - #type = shared - #separator = / - - # Mailboxes are visible under "shared/user@domain/" - # %%n, %%d and %%u are expanded to the destination user. - #prefix = shared/%%u/ - - # Mail location for other users' mailboxes. Note that %variables and ~/ - # expands to the logged in user's data. %%n, %%d, %%u and %%h expand to the - # destination user's data. - #location = maildir:%%h/Maildir:INDEX=~/Maildir/shared/%%u - - # Use the default namespace for saving subscriptions. - #subscriptions = no - - # List the shared/ namespace only if there are visible shared mailboxes. - #list = children -#} -# Should shared INBOX be visible as "shared/user" or "shared/user/INBOX"? -#mail_shared_explicit_inbox = yes - -# System user and group used to access mails. If you use multiple, userdb -# can override these by returning uid or gid fields. You can use either numbers -# or names. -#mail_uid = -#mail_gid = - -# Group to enable temporarily for privileged operations. Currently this is -# used only with INBOX when either its initial creation or dotlocking fails. -# Typically this is set to "mail" to give access to /var/mail. -#mail_privileged_group = - -# Grant access to these supplementary groups for mail processes. Typically -# these are used to set up access to shared mailboxes. Note that it may be -# dangerous to set these if users can create symlinks (e.g. if "mail" group is -# set here, ln -s /var/mail ~/mail/var could allow a user to delete others' -# mailboxes, or ln -s /secret/shared/box ~/mail/mybox would allow reading it). -mail_access_groups = vmail - -# Allow full filesystem access to clients. There's no access checks other than -# what the operating system does for the active UID/GID. It works with both -# maildir and mboxes, allowing you to prefix mailboxes names with eg. /path/ -# or ~user/. -#mail_full_filesystem_access = no - -## -## Mail processes -## - -# Don't use mmap() at all. This is required if you store indexes to shared -# filesystems (NFS or clustered filesystem). -#mmap_disable = no - -# Rely on O_EXCL to work when creating dotlock files. NFS supports O_EXCL -# since version 3, so this should be safe to use nowadays by default. -#dotlock_use_excl = yes - -# When to use fsync() or fdatasync() calls: -# optimized (default): Whenever necessary to avoid losing important data -# always: Useful with e.g. NFS when write()s are delayed -# never: Never use it (best performance, but crashes can lose data) -#mail_fsync = optimized - -# Mail storage exists in NFS. Set this to yes to make Dovecot flush NFS caches -# whenever needed. If you're using only a single mail server this isn't needed. -#mail_nfs_storage = no -# Mail index files also exist in NFS. Setting this to yes requires -# mmap_disable=yes and fsync_disable=no. -#mail_nfs_index = no - -# Locking method for index files. Alternatives are fcntl, flock and dotlock. -# Dotlocking uses some tricks which may create more disk I/O than other locking -# methods. NFS users: flock doesn't work, remember to change mmap_disable. -#lock_method = fcntl - -# Directory in which LDA/LMTP temporarily stores incoming mails >128 kB. -#mail_temp_dir = /tmp - -# Valid UID range for users, defaults to 500 and above. This is mostly -# to make sure that users can't log in as daemons or other system users. -# Note that denying root logins is hardcoded to dovecot binary and can't -# be done even if first_valid_uid is set to 0. -#first_valid_uid = 500 -#last_valid_uid = 0 - -# Valid GID range for users, defaults to non-root/wheel. Users having -# non-valid GID as primary group ID aren't allowed to log in. If user -# belongs to supplementary groups with non-valid GIDs, those groups are -# not set. -#first_valid_gid = 1 -#last_valid_gid = 0 - -# Maximum allowed length for mail keyword name. It's only forced when trying -# to create new keywords. -#mail_max_keyword_length = 50 - -# ':' separated list of directories under which chrooting is allowed for mail -# processes (ie. /var/mail will allow chrooting to /var/mail/foo/bar too). -# This setting doesn't affect login_chroot, mail_chroot or auth chroot -# settings. If this setting is empty, "/./" in home dirs are ignored. -# WARNING: Never add directories here which local users can modify, that -# may lead to root exploit. Usually this should be done only if you don't -# allow shell access for users. -#valid_chroot_dirs = - -# Default chroot directory for mail processes. This can be overridden for -# specific users in user database by giving /./ in user's home directory -# (eg. /home/./user chroots into /home). Note that usually there is no real -# need to do chrooting, Dovecot doesn't allow users to access files outside -# their mail directory anyway. If your home directories are prefixed with -# the chroot directory, append "/." to mail_chroot. -#mail_chroot = - -# UNIX socket path to master authentication server to find users. -# This is used by imap (for shared users) and lda. -#auth_socket_path = /var/run/dovecot/auth-userdb - -# Directory where to look up mail plugins. -#mail_plugin_dir = /usr/lib/dovecot/modules - -# Space separated list of plugins to load for all services. Plugins specific to -# IMAP, LDA, etc. are added to this list in their own .conf files. -#mail_plugins = - -## -## Mailbox handling optimizations -## - -# The minimum number of mails in a mailbox before updates are done to cache -# file. This allows optimizing Dovecot's behavior to do less disk writes at -# the cost of more disk reads. -#mail_cache_min_mail_count = 0 - -# When IDLE command is running, mailbox is checked once in a while to see if -# there are any new mails or other changes. This setting defines the minimum -# time to wait between those checks. Dovecot can also use dnotify, inotify and -# kqueue to find out immediately when changes occur. -#mailbox_idle_check_interval = 30 secs - -# Save mails with CR+LF instead of plain LF. This makes sending those mails -# take less CPU, especially with sendfile() syscall with Linux and FreeBSD. -# But it also creates a bit more disk I/O which may just make it slower. -# Also note that if other software reads the mboxes/maildirs, they may handle -# the extra CRs wrong and cause problems. -#mail_save_crlf = no - -# Max number of mails to keep open and prefetch to memory. This only works with -# some mailbox formats and/or operating systems. -#mail_prefetch_count = 0 - -# How often to scan for stale temporary files and delete them (0 = never). -# These should exist only after Dovecot dies in the middle of saving mails. -#mail_temp_scan_interval = 1w - -## -## Maildir-specific settings -## - -# By default LIST command returns all entries in maildir beginning with a dot. -# Enabling this option makes Dovecot return only entries which are directories. -# This is done by stat()ing each entry, so it causes more disk I/O. -# (For systems setting struct dirent->d_type, this check is free and it's -# done always regardless of this setting) -#maildir_stat_dirs = no - -# When copying a message, do it with hard links whenever possible. This makes -# the performance much better, and it's unlikely to have any side effects. -#maildir_copy_with_hardlinks = yes - -# Assume Dovecot is the only MUA accessing Maildir: Scan cur/ directory only -# when its mtime changes unexpectedly or when we can't find the mail otherwise. -#maildir_very_dirty_syncs = no - -# If enabled, Dovecot doesn't use the S= in the Maildir filenames for -# getting the mail's physical size, except when recalculating Maildir++ quota. -# This can be useful in systems where a lot of the Maildir filenames have a -# broken size. The performance hit for enabling this is very small. -#maildir_broken_filename_sizes = no - -## -## mbox-specific settings -## - -# Which locking methods to use for locking mbox. There are four available: -# dotlock: Create .lock file. This is the oldest and most NFS-safe -# solution. If you want to use /var/mail/ like directory, the users -# will need write access to that directory. -# dotlock_try: Same as dotlock, but if it fails because of permissions or -# because there isn't enough disk space, just skip it. -# fcntl : Use this if possible. Works with NFS too if lockd is used. -# flock : May not exist in all systems. Doesn't work with NFS. -# lockf : May not exist in all systems. Doesn't work with NFS. -# -# You can use multiple locking methods; if you do the order they're declared -# in is important to avoid deadlocks if other MTAs/MUAs are using multiple -# locking methods as well. Some operating systems don't allow using some of -# them simultaneously. -#mbox_read_locks = fcntl -#mbox_write_locks = dotlock fcntl - -# Maximum time to wait for lock (all of them) before aborting. -#mbox_lock_timeout = 5 mins - -# If dotlock exists but the mailbox isn't modified in any way, override the -# lock file after this much time. -#mbox_dotlock_change_timeout = 2 mins - -# When mbox changes unexpectedly we have to fully read it to find out what -# changed. If the mbox is large this can take a long time. Since the change -# is usually just a newly appended mail, it'd be faster to simply read the -# new mails. If this setting is enabled, Dovecot does this but still safely -# fallbacks to re-reading the whole mbox file whenever something in mbox isn't -# how it's expected to be. The only real downside to this setting is that if -# some other MUA changes message flags, Dovecot doesn't notice it immediately. -# Note that a full sync is done with SELECT, EXAMINE, EXPUNGE and CHECK -# commands. -#mbox_dirty_syncs = yes - -# Like mbox_dirty_syncs, but don't do full syncs even with SELECT, EXAMINE, -# EXPUNGE or CHECK commands. If this is set, mbox_dirty_syncs is ignored. -#mbox_very_dirty_syncs = no - -# Delay writing mbox headers until doing a full write sync (EXPUNGE and CHECK -# commands and when closing the mailbox). This is especially useful for POP3 -# where clients often delete all mails. The downside is that our changes -# aren't immediately visible to other MUAs. -#mbox_lazy_writes = yes - -# If mbox size is smaller than this (e.g. 100k), don't write index files. -# If an index file already exists it's still read, just not updated. -#mbox_min_index_size = 0 - -# Mail header selection algorithm to use for MD5 POP3 UIDLs when -# pop3_uidl_format=%m. For backwards compatibility we use apop3d inspired -# algorithm, but it fails if the first Received: header isn't unique in all -# mails. An alternative algorithm is "all" that selects all headers. -#mbox_md5 = apop3d - -## -## mdbox-specific settings -## - -# Maximum dbox file size until it's rotated. -#mdbox_rotate_size = 2M - -# Maximum dbox file age until it's rotated. Typically in days. Day begins -# from midnight, so 1d = today, 2d = yesterday, etc. 0 = check disabled. -#mdbox_rotate_interval = 0 - -# When creating new mdbox files, immediately preallocate their size to -# mdbox_rotate_size. This setting currently works only in Linux with some -# filesystems (ext4, xfs). -#mdbox_preallocate_space = no - -## -## Mail attachments -## - -# sdbox and mdbox support saving mail attachments to external files, which -# also allows single instance storage for them. Other backends don't support -# this for now. - -# WARNING: This feature hasn't been tested much yet. Use at your own risk. - -# Directory root where to store mail attachments. Disabled, if empty. -#mail_attachment_dir = - -# Attachments smaller than this aren't saved externally. It's also possible to -# write a plugin to disable saving specific attachments externally. -#mail_attachment_min_size = 128k - -# Filesystem backend to use for saving attachments: -# posix : No SiS done by Dovecot (but this might help FS's own deduplication) -# sis posix : SiS with immediate byte-by-byte comparison during saving -# sis-queue posix : SiS with delayed comparison and deduplication -#mail_attachment_fs = sis posix - -# Hash format to use in attachment filenames. You can add any text and -# variables: %{md4}, %{md5}, %{sha1}, %{sha256}, %{sha512}, %{size}. -# Variables can be truncated, e.g. %{sha256:80} returns only first 80 bits -#mail_attachment_hash = %{sha1} -]]> - - - - . -postmaster_address = postmaster@ - -# Hostname to use in various parts of sent mails, eg. in Message-Id. -# Default is the system's real hostname. -#hostname = - -# If user is over quota, return with temporary failure instead of -# bouncing the mail. -#quota_full_tempfail = no - -# Binary to use for sending mails. -#sendmail_path = /usr/sbin/sendmail - -# If non-empty, send mails via this SMTP host[:port] instead of sendmail. -#submission_host = - -# Subject: header to use for rejection mails. You can use the same variables -# as for rejection_reason below. -#rejection_subject = Rejected: %s - -# Human readable error message for rejection mails. You can use variables: -# %n = CRLF, %r = reason, %s = original subject, %t = recipient -#rejection_reason = Your message to <%t> was automatically rejected:%n%r - -# Delimiter character between local-part and detail in email address. -#recipient_delimiter = + - -# Header where the original recipient address (SMTP's RCPT TO: address) is taken -# from if not available elsewhere. With dovecot-lda -a parameter overrides this. -# A commonly used header for this is X-Original-To. -#lda_original_recipient_header = - -# Should saving a mail to a nonexistent mailbox automatically create it? -#lda_mailbox_autocreate = no - -# Should automatically created mailboxes be also automatically subscribed? -#lda_mailbox_autosubscribe = no - -protocol lda { - # Space separated list of plugins to load (default is global mail_plugins). - mail_plugins = $mail_plugins quota sieve -} -]]> - - - - - - - - - #service_count = 1 - - # Number of processes to always keep waiting for more connections. - #process_min_avail = 0 - - # If you set service_count=0, you probably need to grow this. - #vsz_limit = 64M -} - -service managesieve { - # Max. number of ManageSieve processes (connections) - #process_limit = 1024 -} - -# Service configuration - -protocol sieve { - # Maximum ManageSieve command line length in bytes. ManageSieve usually does - # not involve overly long command lines, so this setting will not normally - # need adjustment - #managesieve_max_line_length = 65536 - - # Maximum number of ManageSieve connections allowed for a user from each IP - # address. - # NOTE: The username is compared case-sensitively. - #mail_max_userip_connections = 10 - - # Space separated list of plugins to load (none known to be useful so far). - # Do NOT try to load IMAP plugins here. - #mail_plugins = - - # MANAGESIEVE logout format string: - # %i - total number of bytes read from client - # %o - total number of bytes sent to client - #managesieve_logout_format = bytes=%i/%o - - # To fool ManageSieve clients that are focused on CMU's timesieved you can - # specify the IMPLEMENTATION capability that Dovecot reports to clients. - # For example: 'Cyrus timsieved v2.2.13' - #managesieve_implementation_string = Dovecot Pigeonhole - - # Explicitly specify the SIEVE and NOTIFY capability reported by the server - # before login. If left unassigned these will be reported dynamically - # according to what the Sieve interpreter supports by default (after login - # this may differ depending on the user). - #managesieve_sieve_capability = - #managesieve_notify_capability = - - # The maximum number of compile errors that are returned to the client upon - # script upload or script verification. - #managesieve_max_compile_errors = 5 - - # Refer to 90-sieve.conf for script quota configuration and configuration of - # Sieve execution limits. -} -]]> - - - - = 2.1.4) : %v.%u - # Dovecot v0.99.x : %v.%u - # tpop3d : %Mf - # - # Note that Outlook 2003 seems to have problems with %v.%u format which was - # Dovecot's default, so if you're building a new server it would be a good - # idea to change this. %08Xu%08Xv should be pretty fail-safe. - # - #pop3_uidl_format = %08Xu%08Xv - - # Permanently save UIDLs sent to POP3 clients, so pop3_uidl_format changes - # won't change those UIDLs. Currently this works only with Maildir. - #pop3_save_uidl = no - - # What to do about duplicate UIDLs if they exist? - # allow: Show duplicates to clients. - # rename: Append a temporary -2, -3, etc. counter after the UIDL. - #pop3_uidl_duplicates = allow - - # POP3 logout format string: - # %i - total number of bytes read from client - # %o - total number of bytes sent to client - # %t - number of TOP commands - # %p - number of bytes sent to client as a result of TOP command - # %r - number of RETR commands - # %b - number of bytes sent to client as a result of RETR command - # %d - number of deleted messages - # %m - number of messages (before deletion) - # %s - mailbox size in bytes (before deletion) - # %u - old/new UIDL hash. may help finding out if UIDLs changed unexpectedly - pop3_logout_format = in=%i out=%o top=%t/%p retr=%r/%b del=%d/%m size=%s - - # Maximum number of POP3 connections allowed for a user from each IP address. - # NOTE: The username is compared case-sensitively. - #mail_max_userip_connections = 10 - - # Space separated list of plugins to load (default is global mail_plugins). - mail_plugins = $mail_plugins quota - - # Workarounds for various client bugs: - # outlook-no-nuls: - # Outlook and Outlook Express hang if mails contain NUL characters. - # This setting replaces them with 0x80 character. - # oe-ns-eoh: - # Outlook Express and Netscape Mail breaks if end of headers-line is - # missing. This option simply sends it if it's missing. - # The list is space-separated. - #pop3_client_workarounds = -} -]]> - - - - See sieve_before fore executing scripts before the user's personal - # script. - #sieve_default = /var/lib/dovecot/sieve/default.sieve - - # Directory for :personal include scripts for the include extension. This - # is also where the ManageSieve service stores the user's scripts. - sieve_dir = ~/sieve - - # Directory for :global include scripts for the include extension. - #sieve_global_dir = - - # Path to a script file or a directory containing script files that need to be - # executed before the user's script. If the path points to a directory, all - # the Sieve scripts contained therein (with the proper .sieve extension) are - # executed. The order of execution within a directory is determined by the - # file names, using a normal 8bit per-character comparison. Multiple script - # file or directory paths can be specified by appending an increasing number. - #sieve_before = - #sieve_before2 = - #sieve_before3 = (etc...) - - # Identical to sieve_before, only the specified scripts are executed after the - # user's script (only when keep is still in effect!). Multiple script file or - # directory paths can be specified by appending an increasing number. - #sieve_after = - #sieve_after2 = - #sieve_after2 = (etc...) - - # Which Sieve language extensions are available to users. By default, all - # supported extensions are available, except for deprecated extensions or - # those that are still under development. Some system administrators may want - # to disable certain Sieve extensions or enable those that are not available - # by default. This setting can use '+' and '-' to specify differences relative - # to the default. For example `sieve_extensions = +imapflags' will enable the - # deprecated imapflags extension in addition to all extensions were already - # enabled by default. - #sieve_extensions = +notify +imapflags - - # Which Sieve language extensions are ONLY available in global scripts. This - # can be used to restrict the use of certain Sieve extensions to administrator - # control, for instance when these extensions can cause security concerns. - # This setting has higher precedence than the `sieve_extensions' setting - # (above), meaning that the extensions enabled with this setting are never - # available to the user's personal script no matter what is specified for the - # `sieve_extensions' setting. The syntax of this setting is similar to the - # `sieve_extensions' setting, with the difference that extensions are - # enabled or disabled for exclusive use in global scripts. Currently, no - # extensions are marked as such by default. - #sieve_global_extensions = - - # The Pigeonhole Sieve interpreter can have plugins of its own. Using this - # setting, the used plugins can be specified. Check the Dovecot wiki - # (wiki2.dovecot.org) or the pigeonhole website - # (http://pigeonhole.dovecot.org) for available plugins. - #sieve_plugins = - - # The separator that is expected between the :user and :detail - # address parts introduced by the subaddress extension. This may - # also be a sequence of characters (e.g. '--'). The current - # implementation looks for the separator from the left of the - # localpart and uses the first one encountered. The :user part is - # left of the separator and the :detail part is right. This setting - # is also used by Dovecot's LMTP service. - #recipient_delimiter = + - - # The maximum size of a Sieve script. The compiler will refuse to compile any - # script larger than this limit. If set to 0, no limit on the script size is - # enforced. - #sieve_max_script_size = 1M - - # The maximum number of actions that can be performed during a single script - # execution. If set to 0, no limit on the total number of actions is enforced. - #sieve_max_actions = 32 - - # The maximum number of redirect actions that can be performed during a single - # script execution. If set to 0, no redirect actions are allowed. - #sieve_max_redirects = 4 - - # The maximum number of personal Sieve scripts a single user can have. If set - # to 0, no limit on the number of scripts is enforced. - # (Currently only relevant for ManageSieve) - #sieve_quota_max_scripts = 0 - - # The maximum amount of disk storage a single user's scripts may occupy. If - # set to 0, no limit on the used amount of disk storage is enforced. - # (Currently only relevant for ManageSieve) - #sieve_quota_max_storage = 0 -} -]]> - - - - - - - - - - //service[@type='mail']/general/installs[@index=1] - - //service[@type='mail']/general/files[@index=1] - - - - #service_count = 1 - - # Number of processes to always keep waiting for more connections. - #process_min_avail = 0 - - # If you set service_count=0, you probably need to grow this. - #vsz_limit = $default_vsz_limit -} - -service pop3-login { - inet_listener pop3 { - #port = 110 - } - inet_listener pop3s { - #port = 995 - #ssl = yes - } -} - -service lmtp { - unix_listener lmtp { - #mode = 0666 - } - - # Create inet listener only if you can't use the above UNIX socket - #inet_listener lmtp { - # Avoid making LMTP visible for the entire internet - #address = - #port = - #} -} - -service imap { - # Most of the memory goes to mmap()ing files. You may need to increase this - # limit if you have huge mailboxes. - #vsz_limit = $default_vsz_limit - - # Max. number of IMAP processes (connections) - #process_limit = 1024 -} - -service pop3 { - # Max. number of POP3 processes (connections) - #process_limit = 1024 -} - -service auth { - # auth_socket_path points to this userdb socket by default. It's typically - # used by dovecot-lda, doveadm, possibly imap process, etc. Users that have - # full permissions to this socket are able to get a list of all usernames and - # get the results of everyone's userdb lookups. - # - # The default 0666 mode allows anyone to connect to the socket, but the - # userdb lookups will succeed only if the userdb returns an "uid" field that - # matches the caller process's UID. Also if caller's uid or gid matches the - # socket's uid or gid the lookup succeeds. Anything else causes a failure. - # - # To give the caller full permissions to lookup all users, set the mode to - # something else than 0666 and Dovecot lets the kernel enforce the - # permissions (e.g. 0777 allows everyone full permissions). - unix_listener auth-userdb { - #mode = 0666 - #user = - #group = - } - - # Postfix smtp-auth - unix_listener /var/spool/postfix/private/auth { - mode = 0660 - user = postfix - group = postfix - } - # Exim4 smtp-auth - unix_listener auth-client { - mode = 0660 - user = mail - } - - # Auth process is run as this user. - #user = $default_internal_user -} - -service auth-worker { - # Auth worker process is run as root by default, so that it can access - # /etc/shadow. If this isn't necessary, the user should be changed to - # $default_internal_user. - #user = root -} - -service dict { - # If dict proxy is used, mail processes should have access to its socket. - # For example: mode=0660, group=vmail and global mail_access_groups=vmail - unix_listener dict { - #mode = 0600 - #user = - #group = - } -} -]]> - - - //service[@type='mail']/general/commands[@index=1] - - - - - //service[@type='mail']/general/installs[@index=1] - - //service[@type='mail']/general/files[@index=1] - - - - #service_count = 1 - - # Number of processes to always keep waiting for more connections. - #process_min_avail = 0 - - # If you set service_count=0, you probably need to grow this. - #vsz_limit = $default_vsz_limit -} - -service pop3-login { - inet_listener pop3 { - #port = 110 - } - inet_listener pop3s { - #port = 995 - #ssl = yes - } -} - -service lmtp { - unix_listener lmtp { - #mode = 0666 - } - - # Create inet listener only if you can't use the above UNIX socket - #inet_listener lmtp { - # Avoid making LMTP visible for the entire internet - #address = - #port = - #} -} - -service imap { - # Most of the memory goes to mmap()ing files. You may need to increase this - # limit if you have huge mailboxes. - #vsz_limit = $default_vsz_limit - - # Max. number of IMAP processes (connections) - #process_limit = 1024 -} - -service pop3 { - # Max. number of POP3 processes (connections) - #process_limit = 1024 -} - -service auth { - # auth_socket_path points to this userdb socket by default. It's typically - # used by dovecot-lda, doveadm, possibly imap process, etc. Users that have - # full permissions to this socket are able to get a list of all usernames and - # get the results of everyone's userdb lookups. - # - # The default 0666 mode allows anyone to connect to the socket, but the - # userdb lookups will succeed only if the userdb returns an "uid" field that - # matches the caller process's UID. Also if caller's uid or gid matches the - # socket's uid or gid the lookup succeeds. Anything else causes a failure. - # - # To give the caller full permissions to lookup all users, set the mode to - # something else than 0666 and Dovecot lets the kernel enforce the - # permissions (e.g. 0777 allows everyone full permissions). - unix_listener auth-userdb { - #mode = 0666 - #user = - #group = - } - - # Postfix smtp-auth - unix_listener /var/spool/postfix/private/auth { - mode = 0660 - user = postfix - group = postfix - } - # Exim4 smtp-auth - unix_listener auth-client { - mode = 0660 - user = mail - # group = Debian-exim - } - - # Auth process is run as this user. - #user = $default_internal_user -} - -service auth-worker { - # Auth worker process is run as root by default, so that it can access - # /etc/shadow. If this isn't necessary, the user should be changed to - # $default_internal_user. - #user = root -} - -service dict { - # If dict proxy is used, mail processes should have access to its socket. - # For example: mode=0660, group=vmail and global mail_access_groups=vmail - unix_listener dict { - #mode = 0600 - #user = - #group = - } -} -]]> - - - //service[@type='mail']/general/commands[@index=1] - - - - - - - - - - - -MYSQL_USERNAME -MYSQL_PASSWORD - -##NAME: SSLINFO:0 -# -# The SSL information. -# -# To use SSL-encrypted connections, define the following variables (available -# in MySQL 4.0, or higher): -# -# -# MYSQL_SSL_KEY /path/to/file -# MYSQL_SSL_CERT /path/to/file -# MYSQL_SSL_CACERT /path/to/file -# MYSQL_SSL_CAPATH /path/to/file -# MYSQL_SSL_CIPHERS ALL:!DES - -##NAME: MYSQL_SOCKET:0 -# -# MYSQL_SOCKET can be used with MySQL version 3.22 or later, it specifies the -# filesystem pipe used for the connection -# -# MYSQL_SOCKET /var/run/mysqld/mysqld.sock - -##NAME: MYSQL_PORT:0 -# -# MYSQL_PORT can be used with MySQL version 3.22 or later to specify a port to -# connect to. - -MYSQL_PORT 0 - -##NAME: MYSQL_OPT:0 -# -# Leave MYSQL_OPT as 0, unless you know what you're doing. - -MYSQL_OPT 0 - -##NAME: MYSQL_DATABASE:0 -# -# The name of the MySQL database we will open: - -MYSQL_DATABASE - -#NAME: MYSQL_CHARACTER_SET:0 -# -# This is optional. MYSQL_CHARACTER_SET installs a character set. This option -# can be used with MySQL version 4.1 or later. MySQL supports 70+ collations -# for 30+ character sets. See MySQL documentations for more detalis. -# -# MYSQL_CHARACTER_SET latin1 - -##NAME: MYSQL_USER_TABLE:0 -# -# The name of the table containing your user data. See README.authmysqlrc -# for the required fields in this table. - -MYSQL_USER_TABLE mail_users - -##NAME: MYSQL_CRYPT_PWFIELD:0 -# -# Either MYSQL_CRYPT_PWFIELD or MYSQL_CLEAR_PWFIELD must be defined. Both -# are OK too. crypted passwords go into MYSQL_CRYPT_PWFIELD, cleartext -# passwords go into MYSQL_CLEAR_PWFIELD. Cleartext passwords allow -# CRAM-MD5 authentication to be implemented. - -MYSQL_CRYPT_PWFIELD password_enc - -##NAME: MYSQL_CLEAR_PWFIELD:0 -# -# -# MYSQL_CLEAR_PWFIELD clear - -##NAME: MYSQL_DEFAULT_DOMAIN:0 -# -# If DEFAULT_DOMAIN is defined, and someone tries to log in as 'user', -# we will look up 'user@DEFAULT_DOMAIN' instead. -# -# -# DEFAULT_DOMAIN example.com - -##NAME: MYSQL_UID_FIELD:0 -# -# Other fields in the mysql table: -# -# MYSQL_UID_FIELD - contains the numerical userid of the account -# -MYSQL_UID_FIELD uid - -##NAME: MYSQL_GID_FIELD:0 -# -# Numerical groupid of the account - -MYSQL_GID_FIELD gid - -##NAME: MYSQL_LOGIN_FIELD:0 -# -# The login id, default is id. Basically the query is: -# -# SELECT MYSQL_UID_FIELD, MYSQL_GID_FIELD, ... WHERE id='loginid' -# - -MYSQL_LOGIN_FIELD username - -##NAME: MYSQL_HOME_FIELD:0 -# - -MYSQL_HOME_FIELD homedir - -##NAME: MYSQL_NAME_FIELD:0 -# -# The user's name (optional) - -#MYSQL_NAME_FIELD name - -##NAME: MYSQL_MAILDIR_FIELD:0 -# -# This is an optional field, and can be used to specify an arbitrary -# location of the maildir for the account, which normally defaults to -# $HOME/Maildir (where $HOME is read from MYSQL_HOME_FIELD). -# -# You still need to provide a MYSQL_HOME_FIELD, even if you uncomment this -# out. -# -MYSQL_MAILDIR_FIELD maildir - -##NAME: MYSQL_DEFAULTDELIVERY:0 -# -# Courier mail server only: optional field specifies custom mail delivery -# instructions for this account (if defined) -- essentially overrides -# DEFAULTDELIVERY from ${sysconfdir}/courierd -# -# MYSQL_DEFAULTDELIVERY defaultdelivery - -##NAME: MYSQL_QUOTA_FIELD:0 -# -# Define MYSQL_QUOTA_FIELD to be the name of the field that can optionally -# specify a maildir quota. See README.maildirquota for more information -# -MYSQL_QUOTA_FIELD (quota*1024*1024) - -##NAME: MYSQL_AUXOPTIONS:0 -# -# Auxiliary options. The MYSQL_AUXOPTIONS field should be a char field that -# contains a single string consisting of comma-separated "ATTRIBUTE=NAME" -# pairs. These names are additional attributes that define various per-account -# "options", as given in INSTALL's description of the "Account OPTIONS" -# setting. -# -# MYSQL_AUXOPTIONS_FIELD auxoptions -# -# You might want to try something like this, if you'd like to use a bunch -# of individual fields, instead of a single text blob: -# -MYSQL_AUXOPTIONS_FIELD CONCAT("allowimap=",imap,",allowpop3=",pop3) -# -# This will let you define fields called "disableimap", etc, with the end result -# being something that the OPTIONS parser understands. - - -##NAME: MYSQL_WHERE_CLAUSE:0 -# -# This is optional, MYSQL_WHERE_CLAUSE can be basically set to an arbitrary -# fixed string that is appended to the WHERE clause of our query -# -# MYSQL_WHERE_CLAUSE server='mailhost.example.com' - -##NAME: MYSQL_SELECT_CLAUSE:0 -# -# (EXPERIMENTAL) -# This is optional, MYSQL_SELECT_CLAUSE can be set when you have a database, -# which is structuraly different from proposed. The fixed string will -# be used to do a SELECT operation on database, which should return fields -# in order specified bellow: -# -# username, cryptpw, clearpw, uid, gid, home, maildir, quota, fullname, options -# -# The username field should include the domain (see example below). -# -# Enabling this option causes ignorance of any other field-related -# options, excluding default domain. -# -# There are two variables, which you can use. Substitution will be made -# for them, so you can put entered username (local part) and domain name -# in the right place of your query. These variables are: -# $(local_part), $(domain), $(service) -# -# If a $(domain) is empty (not given by the remote user) the default domain -# name is used in its place. -# -# $(service) will expand out to the service being authenticated: imap, imaps, -# pop3 or pop3s. Courier mail server only: service will also expand out to -# "courier", when searching for local mail account's location. In this case, -# if the "maildir" field is not empty it will be used in place of -# DEFAULTDELIVERY. Courier mail server will also use esmtp when doing -# authenticated ESMTP. -# -# This example is a little bit modified adaptation of vmail-sql -# database scheme: -# -# MYSQL_SELECT_CLAUSE SELECT CONCAT(popbox.local_part, '@', popbox.domain_name), \ -# CONCAT('{MD5}', popbox.password_hash), \ -# popbox.clearpw, \ -# domain.uid, \ -# domain.gid, \ -# CONCAT(domain.path, '/', popbox.mbox_name), \ -# '', \ -# domain.quota, \ -# '', \ -# CONCAT("disableimap=",disableimap,",disablepop3=", \ -# disablepop3,",disablewebmail=",disablewebmail, \ -# ",sharedgroup=",sharedgroup) \ -# FROM popbox, domain \ -# WHERE popbox.local_part = '$(local_part)' \ -# AND popbox.domain_name = '$(domain)' \ -# AND popbox.domain_name = domain.domain_name - - -##NAME: MYSQL_ENUMERATE_CLAUSE:1 -# -# {EXPERIMENTAL} -# Optional custom SQL query used to enumerate accounts for authenumerate, -# in order to compile a list of accounts for shared folders. The query -# should return the following fields: name, uid, gid, homedir, maildir, options -# -# Example: -# MYSQL_ENUMERATE_CLAUSE SELECT CONCAT(popbox.local_part, '@', popbox.domain_name), \ -# domain.uid, \ -# domain.gid, \ -# CONCAT(domain.path, '/', popbox.mbox_name), \ -# '', \ -# CONCAT('sharedgroup=', sharedgroup) \ -# FROM popbox, domain \ -# WHERE popbox.local_part = '$(local_part)' \ -# AND popbox.domain_name = '$(domain)' \ -# AND popbox.domain_name = domain.domain_name - - - -##NAME: MYSQL_CHPASS_CLAUSE:0 -# -# (EXPERIMENTAL) -# This is optional, MYSQL_CHPASS_CLAUSE can be set when you have a database, -# which is structuraly different from proposed. The fixed string will -# be used to do an UPDATE operation on database. In other words, it is -# used, when changing password. -# -# There are four variables, which you can use. Substitution will be made -# for them, so you can put entered username (local part) and domain name -# in the right place of your query. There variables are: -# $(local_part) , $(domain) , $(newpass) , $(newpass_crypt) -# -# If a $(domain) is empty (not given by the remote user) the default domain -# name is used in its place. -# $(newpass) contains plain password -# $(newpass_crypt) contains its crypted form -# -# MYSQL_CHPASS_CLAUSE UPDATE popbox \ -# SET clearpw='$(newpass)', \ -# password_hash='$(newpass_crypt)' \ -# WHERE local_part='$(local_part)' \ -# AND domain_name='$(domain)' -# -]]> - - - - - - - - - - - - - " -[ -f /etc/ssl/certs/proftpd_ec.crt ] || openssl req -new -x509 -nodes -newkey ec:<(openssl ecparam -name secp521r1) -keyout /etc/ssl/private/proftpd_ec.key -out /etc/ssl/certs/proftpd_ec.crt -days 3650 -subj "/C=US/ST=Some-State/O=Internet Widgits Pty Ltd/CN=" -chmod 0600 /etc/ssl/private/proftpd.key /etc/ssl/private/proftpd_ec.key -]]> - - - - - - - - FTP Server" -ServerType standalone -DeferWelcome off - -MultilineRFC2228 on -DefaultServer on -ShowSymlinks on - -TimeoutNoTransfer 600 -TimeoutStalled 600 -TimeoutIdle 1200 - -DisplayLogin welcome.msg -DisplayChdir .message true -ListOptions "-l" - -DenyFilter \*.*/ - -# Use this to jail all users in their homes -# DefaultRoot ~ - -# Users require a valid shell listed in /etc/shells to login. -# Use this directive to release that constrain. -# RequireValidShell off - -# Port 21 is the standard FTP port. -Port 21 - -# In some cases you have to specify passive ports range to by-pass -# firewall limitations. Ephemeral ports can be used for that, but -# feel free to use a more narrow range. -# PassivePorts 49152 65534 - -# If your host was NATted, this option is useful in order to -# allow passive tranfers to work. You have to use your public -# address and opening the passive ports used on your firewall as well. -# MasqueradeAddress 1.2.3.4 - -# This is useful for masquerading address with dynamic IPs: -# refresh any configured MasqueradeAddress directives every 8 hours - -# DynMasqRefresh 28800 - - -# To prevent DoS attacks, set the maximum number of child processes -# to 30. If you need to allow more than 30 concurrent connections -# at once, simply increase this value. Note that this ONLY works -# in standalone mode, in inetd mode you should use an inetd server -# that allows you to limit maximum number of processes per service -# (such as xinetd) -MaxInstances 30 - -# Set the user and group that the server normally runs at. -User proftpd -Group nogroup - -# Umask 022 is a good standard umask to prevent new files and dirs -# (second parm) from being group and world writable. -Umask 022 022 -# Normally, we want files to be overwriteable. -AllowOverwrite on - -# Uncomment this if you are using NIS or LDAP via NSS to retrieve passwords: -# PersistentPasswd off - -# This is required to use both PAM-based authentication and local passwords -# AuthOrder mod_auth_pam.c* mod_auth_unix.c - -# Be warned: use of this directive impacts CPU average load! -# Uncomment this if you like to see progress and transfer rate with ftpwho -# in downloads. That is not needed for uploads rates. -# -# UseSendFile off - -TransferLog /var/log/proftpd/xferlog -SystemLog /var/log/proftpd/proftpd.log - -# Logging onto /var/log/lastlog is enabled but set to off by default -#UseLastlog on - -# In order to keep log file dates consistent after chroot, use timezone info -# from /etc/localtime. If this is not set, and proftpd is configured to -# chroot (e.g. DefaultRoot or ), it will use the non-daylight -# savings timezone regardless of whether DST is in effect. -#SetEnv TZ :/etc/localtime - - -QuotaEngine on - - - -Ratios off - - - -# Delay engine reduces impact of the so-called Timing Attack described in -# http://www.securityfocus.com/bid/11430/discuss -# It is on by default. - -DelayEngine on - - - -ControlsEngine off -ControlsMaxClients 2 -ControlsLog /var/log/proftpd/controls.log -ControlsInterval 5 -ControlsSocket /var/run/proftpd/proftpd.sock - - - -AdminControlsEngine off - - -# -# Alternative authentication frameworks -# -#Include /etc/proftpd/ldap.conf -Include /etc/proftpd/sql.conf - -# -# This is used for FTPS connections -# -Include /etc/proftpd/tls.conf - -# -# Useful to keep VirtualHost/VirtualRoot directives separated -# -#Include /etc/proftpd/virtuals.conf - -# A basic anonymous configuration, no upload directories. - -# -# User ftp -# Group nogroup -# # We want clients to be able to login with "anonymous" as well as "ftp" -# UserAlias anonymous ftp -# # Cosmetic changes, all files belongs to ftp user -# DirFakeUser on ftp -# DirFakeGroup on ftp -# -# RequireValidShell off -# -# # Limit the maximum number of anonymous logins -# MaxClients 10 -# -# # We want 'welcome.msg' displayed at login, and '.message' displayed -# # in each newly chdired directory. -# DisplayLogin welcome.msg -# DisplayChdir .message -# -# # Limit WRITE everywhere in the anonymous chroot -# -# -# DenyAll -# -# -# -# # Uncomment this if you're brave. -# # -# # # Umask 022 is a good standard umask to prevent new files and dirs -# # # (second parm) from being group and world writable. -# # Umask 022 022 -# # -# # DenyAll -# # -# # -# # AllowAll -# # -# # -# -# - -# Include other custom configuration files -Include /etc/proftpd/conf.d/ -]]> - - - - - - - - -DefaultRoot ~ -RequireValidShell off -AuthOrder mod_sql.c - -SQLBackend mysql -SQLEngine on -SQLAuthenticate on - -SQLAuthTypes Crypt -SQLAuthenticate users* groups* -SQLConnectInfo @ -SQLUserInfo ftp_users username password uid gid homedir shell -SQLGroupInfo ftp_groups groupname gid members -SQLUserWhereClause "login_enabled = 'y'" - -SQLLog PASS login -SQLNamedQuery login UPDATE "last_login=now(), login_count=login_count+1 WHERE username='%u'" ftp_users - -SQLLog RETR download -SQLNamedQuery download UPDATE "down_count=down_count+1, down_bytes=down_bytes+%b WHERE username='%u'" ftp_users - -SQLLog STOR upload -SQLNamedQuery upload UPDATE "up_count=up_count+1, up_bytes=up_bytes+%b WHERE username='%u'" ftp_users - -QuotaEngine on -QuotaShowQuotas on -QuotaDisplayUnits Mb -QuotaLock /var/lock/ftpd.quotatab.lock -QuotaLimitTable sql:/get-quota-limit -QuotaTallyTable sql:/get-quota-tally/update-quota-tally/insert-quota-tally -SQLNamedQuery get-quota-limit SELECT "ftp_users.username AS name, ftp_quotalimits.quota_type, ftp_quotalimits.per_session, ftp_quotalimits.limit_type, panel_customers.diskspace*1024 AS bytes_in_avail, ftp_quotalimits.bytes_out_avail, ftp_quotalimits.bytes_xfer_avail, ftp_quotalimits.files_in_avail, ftp_quotalimits.files_out_avail, ftp_quotalimits.files_xfer_avail FROM ftp_users, ftp_quotalimits, panel_customers WHERE ftp_users.username = '%{0}' AND panel_customers.loginname = SUBSTRING_INDEX('%{0}', 'ftp', 1) AND quota_type ='%{1}'" -SQLNamedQuery get-quota-tally SELECT "name, quota_type, bytes_in_used,bytes_out_used, bytes_xfer_used, files_in_used, files_out_used,files_xfer_used FROM ftp_quotatallies WHERE name = '%{0}' AND quota_type = '%{1}'" -SQLNamedQuery update-quota-tally UPDATE "bytes_in_used = bytes_in_used + %{0}, bytes_out_used = bytes_out_used + %{1}, bytes_xfer_used = bytes_xfer_used + %{2}, files_in_used = files_in_used + %{3}, files_out_used= files_out_used + %{4}, files_xfer_used = files_xfer_used + %{5} WHERE name= '%{6}' AND quota_type = '%{7}'" ftp_quotatallies -SQLNamedQuery insert-quota-tally INSERT "%{0}, %{1}, %{2}, %{3}, %{4},%{5}, %{6}, %{7}" ftp_quotatallies - - -]]> - - - - -TLSEngine on -TLSLog /var/log/proftpd/tls.log -TLSProtocol TLSv1 TLSv1.1 TLSv1.2 -TLSRSACertificateFile /etc/ssl/certs/proftpd.crt -TLSRSACertificateKeyFile /etc/ssl/private/proftpd.key -TLSECCertificateFile /etc/ssl/certs/proftpd_ec.crt -TLSECCertificateKeyFile /etc/ssl/private/proftpd_ec.key -TLSOptions NoCertRequest NoSessionReuseRequired -TLSVerifyClient off - -# Are clients required to use FTP over TLS when talking to this server? -#TLSRequired on - -# Allow SSL/TLS renegotiations when the client requests them, but -# do not force the renegotations. Some clients do not support -# SSL/TLS renegotiations; when mod_tls forces a renegotiation, these -# clients will close the data connection, or there will be a timeout -# on an idle data connection. -# -#TLSRenegotiate required off - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# Optional : MySQL port. Don't define this if a local unix socket is used. - -# MYSQLPort 3306 - - -# Optional : define the location of mysql.sock if the server runs on this host. - -MYSQLSocket /var/run/mysqld/mysqld.sock - - -# Mandatory : user to bind the server as. - -MYSQLUser - - -# Mandatory : user password. You must have a password. - -MYSQLPassword - - -# Mandatory : database to open. - -MYSQLDatabase - - -# Mandatory : how passwords are stored -# Valid values are : "cleartext", "crypt", "sha1", "md5" and "password" -# ("password" = MySQL password() function) -# You can also use "any" to try "crypt", "sha1", "md5" *and* "password" - -MYSQLCrypt any - - -# In the following directives, parts of the strings are replaced at -# run-time before performing queries : -# -# \L is replaced by the login of the user trying to authenticate. -# \I is replaced by the IP address the user connected to. -# \P is replaced by the port number the user connected to. -# \R is replaced by the IP address the user connected from. -# \D is replaced by the remote IP address, as a long decimal number. -# -# Very complex queries can be performed using these substitution strings, -# especially for virtual hosting. - - -# Query to execute in order to fetch the password - -MYSQLGetPW SELECT password FROM ftp_users WHERE username="\L" AND login_enabled="y" - - -# Query to execute in order to fetch the system user name or uid - -MYSQLGetUID SELECT uid FROM ftp_users WHERE username="\L" AND login_enabled="y" - - -# Optional : default UID - if set this overrides MYSQLGetUID - -#MYSQLDefaultUID 1000 - - -# Query to execute in order to fetch the system user group or gid - -MYSQLGetGID SELECT gid FROM ftp_users WHERE username="\L" AND login_enabled="y" - - -# Optional : default GID - if set this overrides MYSQLGetGID - -#MYSQLDefaultGID 1000 - - -# Query to execute in order to fetch the home directory - -MYSQLGetDir SELECT homedir FROM ftp_users WHERE username="\L" AND login_enabled="y" - - -# Optional : query to get the maximal number of files -# Pure-FTPd must have been compiled with virtual quotas support. - -# MySQLGetQTAFS SELECT QuotaFiles FROM users WHERE User='\L' - - -# Optional : query to get the maximal disk usage (virtual quotas) -# The number should be in Megabytes. -# Pure-FTPd must have been compiled with virtual quotas support. - -MySQLGetQTASZ SELECT panel_customers.diskspace/1024 AS QuotaSize FROM panel_customers, ftp_users WHERE username = "\L" AND panel_customers.loginname = SUBSTRING_INDEX('\L', 'ftp', 1) - - -# Optional : ratios. The server has to be compiled with ratio support. - -# MySQLGetRatioUL SELECT ULRatio FROM users WHERE User='\L' -# MySQLGetRatioDL SELECT DLRatio FROM users WHERE User='\L' - - -# Optional : bandwidth throttling. -# The server has to be compiled with throttling support. -# Values are in KB/s . - -# MySQLGetBandwidthUL SELECT ULBandwidth FROM users WHERE User='\L' -# MySQLGetBandwidthDL SELECT DLBandwidth FROM users WHERE User='\L' - -# Enable ~ expansion. NEVER ENABLE THIS BLINDLY UNLESS : -# 1) You know what you are doing. -# 2) Real and virtual users match. - -# MySQLForceTildeExpansion 1 - - -# If you're using a transactionnal storage engine, you can enable SQL -# transactions to avoid races. Leave this commented if you are using the -# traditional MyIsam engine. - -# MySQLTransactions On -]]> - - - - - - - - - - - - - - - - - - - - - - - scripts/froxlor_master_cronjob.php -]]> - - - - - - - - - - - - - - - - - - -database -username -password -port 3306 -#socket /var/run/mysqld/mysqld.sock -]]> - - - - - {{sql.socket}} - - - - - - -password -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - *.log { - missingok - weekly - rotate 4 - compress - delaycompress - notifempty - create - sharedscripts - postrotate - > /dev/null 2>&1 || true - endscript -} -]]> - - - - - - - - - {{settings.system.mod_fcgid_ownvhost}} - - - - - - - - - - - - - - {{settings.system.webserver}} - - - - - - {{settings.system.webserver}} - - - - - - {{settings.system.webserver}} - - - - - {{settings.phpfpm.enabled_ownvhost}} - - {{settings.phpfpm.vhost_httpuser}} - - - - - - {{settings.system.webserver}} - - {{settings.phpfpm.enabled_ownvhost}} - - - - - {{settings.system.webserver}} - - - - - - - - - - From 5a070d6d9196005c6424f0476c6d63fb1a917008 Mon Sep 17 00:00:00 2001 From: Michael Kaufmann Date: Thu, 24 May 2018 11:36:13 +0200 Subject: [PATCH 0605/1335] add settings to customize webserver logs Signed-off-by: Michael Kaufmann --- actions/admin/settings/130.webserver.php | 44 ++++++++++++++++++- install/froxlor.sql | 3 ++ .../updates/froxlor/0.9/update_0.9.inc.php | 11 +++++ lib/version.inc.php | 2 +- lng/english.lng.php | 8 ++++ .../jobs/cron_tasks.inc.http.10.apache.php | 18 +++++++- scripts/jobs/cron_tasks.inc.http.30.nginx.php | 8 +++- 7 files changed, 88 insertions(+), 6 deletions(-) diff --git a/actions/admin/settings/130.webserver.php b/actions/admin/settings/130.webserver.php index 4374b148..35ac4857 100644 --- a/actions/admin/settings/130.webserver.php +++ b/actions/admin/settings/130.webserver.php @@ -104,14 +104,54 @@ return array( 'save_method' => 'storeSettingField' ), 'system_logfiles_directory' => array( - 'label' => $lng['serversettings']['logfiles_directory'], + 'label' => (Settings::Get('system.webserver') != 'apache2') ? $lng['serversettings']['logfiles_directory'] : $lng['serversettings']['logfiles_directory2'], 'settinggroup' => 'system', 'varname' => 'logfiles_directory', 'type' => 'string', - 'string_type' => 'dir', + 'string_type' => (Settings::Get('system.webserver') != 'apache2') ? 'dir' : '', 'default' => '/var/customers/logs/', 'save_method' => 'storeSettingField' ), + 'system_logfiles_format' => array( + 'label' => $lng['serversettings']['logfiles_format'], + 'settinggroup' => 'system', + 'varname' => 'logfiles_format', + 'type' => 'string', + 'default' => '', + 'string_emptyallowed' => true, + 'save_method' => 'storeSettingField', + 'websrv_avail' => array( + 'apache2', + 'nginx' + ) + ), + 'system_logfiles_type' => array( + 'label' => $lng['serversettings']['logfiles_type'], + 'settinggroup' => 'system', + 'varname' => 'logfiles_type', + 'type' => 'option', + 'default' => '1', + 'option_mode' => 'one', + 'option_options' => array( + '1' => 'combined', + '2' => 'vhost_combined' + ), + 'save_method' => 'storeSettingField', + 'websrv_avail' => array( + 'apache2' + ) + ), + 'system_logfiles_piped' => array( + 'label' => $lng['serversettings']['logfiles_piped'], + 'settinggroup' => 'system', + 'varname' => 'logfiles_piped', + 'type' => 'bool', + 'default' => false, + 'save_method' => 'storeSettingField', + 'websrv_avail' => array( + 'apache2' + ) + ), 'system_customersslpath' => array( 'label' => $lng['serversettings']['customerssl_directory'], 'settinggroup' => 'system', diff --git a/install/froxlor.sql b/install/froxlor.sql index bdad8357..a3b63af7 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -656,6 +656,9 @@ opcache.interned_strings_buffer'), ('system', 'nssextrausers', '0'), ('system', 'disable_le_selfcheck', '0'), ('system', 'ssl_protocols', 'TLSv1,TLSv1.2'), + ('system', 'logfiles_format', ''), + ('system', 'logfiles_type', '1'), + ('system', 'logfiles_piped', '0'), ('panel', 'decimal_places', '4'), ('panel', 'adminmail', 'admin@SERVERNAME'), ('panel', 'phpmyadmin_url', ''), diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index 0e9c09e6..c1a9010e 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -3948,3 +3948,14 @@ if (isDatabaseVersion('201802130')) { updateToDbVersion('201802250'); } + +if (isDatabaseVersion('201802250')) { + + showUpdateStep("Adding webserver logfile settings"); + Settings::AddNew("system.logfiles_format", ''); + Settings::AddNew("system.logfiles_type", '1'); + Settings::AddNew("system.logfiles_piped", '0'); + lastStepStatus(0); + + updateToDbVersion('201805240'); +} diff --git a/lib/version.inc.php b/lib/version.inc.php index 9ad32b3a..65278766 100644 --- a/lib/version.inc.php +++ b/lib/version.inc.php @@ -19,7 +19,7 @@ $version = '0.9.39.5'; // Database version (YYYYMMDDC where C is a daily counter) -$dbversion = '201802250'; +$dbversion = '201805240'; // Distribution branding-tag (used for Debian etc.) $branding = ''; diff --git a/lng/english.lng.php b/lng/english.lng.php index fe156bf9..717948a2 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -337,6 +337,14 @@ $lng['serversettings']['documentroot_prefix']['title'] = 'Home directory'; $lng['serversettings']['documentroot_prefix']['description'] = 'Where should all home directories be stored?'; $lng['serversettings']['logfiles_directory']['title'] = 'Logfiles directory'; $lng['serversettings']['logfiles_directory']['description'] = 'Where should all log files be stored?'; +$lng['serversettings']['logfiles_directory2']['title'] = 'Logfiles directory or custom script'; +$lng['serversettings']['logfiles_directory2']['description'] = 'Where should all log files be stored? Optionally, you can specify a script here and use the placeholder {LOGFILE} if needed. In case of a custom script you will need to activate the logfiles piped option'; +$lng['serversettings']['logfiles_format']['title'] = 'Access-log format'; +$lng['serversettings']['logfiles_format']['description'] = 'Enter a custom log-format here according to your webservers specifications, leave empty for default'; +$lng['serversettings']['logfiles_type']['title'] = 'Access-log type'; +$lng['serversettings']['logfiles_type']['description'] = 'Chose between combined or vhost_combined here.'; +$lng['serversettings']['logfiles_piped']['title'] = 'Pipe webserver logfiles to specified script (see above)'; +$lng['serversettings']['logfiles_piped']['description'] = 'When using a custom script for the logfiles you need to activate this in order for it to be executed'; $lng['serversettings']['ipaddress']['title'] = 'IP-address'; $lng['serversettings']['ipaddress']['description'] = 'What\'s the main IP-address of this server?'; $lng['serversettings']['hostname']['title'] = 'Hostname'; diff --git a/scripts/jobs/cron_tasks.inc.http.10.apache.php b/scripts/jobs/cron_tasks.inc.http.10.apache.php index 5f966f38..2c5db125 100644 --- a/scripts/jobs/cron_tasks.inc.http.10.apache.php +++ b/scripts/jobs/cron_tasks.inc.http.10.apache.php @@ -733,8 +733,22 @@ class apache extends HttpConfigBase chown($access_log, Settings::Get('system.httpuser')); chgrp($access_log, Settings::Get('system.httpgroup')); - $logfiles_text .= ' ErrorLog "' . $error_log . "\"\n"; - $logfiles_text .= ' CustomLog "' . $access_log . '" combined' . "\n"; + $logtype = 'combined'; + if (Settings::Get('system.logfiles_format') != '') { + $logtype = 'frx_custom'; + $logfiles_text .= ' LogFormat "' . Settings::Get('system.logfiles_format') . '" ' . $logtype . "\n"; + } + if (Settings::Get('system.logfiles_type') == '2' && Settings::Get('system.logfiles_format') == '') { + $logtype = 'vhost_combined'; + } + + if (Settings::Get('system.logfiles_piped') == '1') { + $logfiles_text .= ' ErrorLog "|' . str_replace('{LOGFILE}', $error_log, Settings::Get('system.logfiles_directory')) . "\"\n"; + $logfiles_text .= ' CustomLog "|' . str_replace('{LOGFILE}', $access_log, Settings::Get('system.logfiles_directory')) . '" ' . $logtype . "\n"; + } else { + $logfiles_text .= ' ErrorLog "' . $error_log . '"' . "\n"; + $logfiles_text .= ' CustomLog "' . $access_log . '" ' . $logtype . "\n"; + } if (Settings::Get('system.awstats_enabled') == '1') { if ((int) $domain['parentdomainid'] == 0) { diff --git a/scripts/jobs/cron_tasks.inc.http.30.nginx.php b/scripts/jobs/cron_tasks.inc.http.30.nginx.php index f5bb1efe..c11dafeb 100644 --- a/scripts/jobs/cron_tasks.inc.http.30.nginx.php +++ b/scripts/jobs/cron_tasks.inc.http.30.nginx.php @@ -1005,7 +1005,13 @@ class nginx extends HttpConfigBase chown($access_log, Settings::Get('system.httpuser')); chgrp($access_log, Settings::Get('system.httpgroup')); - $logfiles_text .= "\t" . 'access_log ' . $access_log . ' combined;' . "\n"; + $logtype = 'combined'; + if (Settings::Get('system.logfiles_format') != '') { + $logtype = 'frx_custom'; + $logfiles_text .= "\t" . 'log_format ' . $logtype . ' "' . Settings::Get('system.logfiles_format') . '";' . "\n"; + } + + $logfiles_text .= "\t" . 'access_log ' . $access_log . ' ' . $logtype . ';' . "\n"; $logfiles_text .= "\t" . 'error_log ' . $error_log . ' error;' . "\n"; if (Settings::Get('system.awstats_enabled') == '1') { From bad680cfdbe0a1f7cd0e36fa0950b148ad31898a Mon Sep 17 00:00:00 2001 From: Michael Kaufmann Date: Thu, 24 May 2018 13:23:37 +0200 Subject: [PATCH 0606/1335] enhancements for webserver-log settings Signed-off-by: Michael Kaufmann --- install/froxlor.sql | 2 +- lng/english.lng.php | 2 +- lng/german.lng.php | 8 +++++ .../jobs/cron_tasks.inc.http.10.apache.php | 36 +++++++++++++------ 4 files changed, 35 insertions(+), 13 deletions(-) diff --git a/install/froxlor.sql b/install/froxlor.sql index a3b63af7..bda0e4d9 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -691,7 +691,7 @@ opcache.interned_strings_buffer'), ('panel', 'password_special_char', '!?<>§$%+#=@'), ('panel', 'customer_hide_options', ''), ('panel', 'version', '0.9.39.5'), - ('panel', 'db_version', '201802250'); + ('panel', 'db_version', '201805240'); DROP TABLE IF EXISTS `panel_tasks`; diff --git a/lng/english.lng.php b/lng/english.lng.php index 717948a2..2c068780 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -338,7 +338,7 @@ $lng['serversettings']['documentroot_prefix']['description'] = 'Where should all $lng['serversettings']['logfiles_directory']['title'] = 'Logfiles directory'; $lng['serversettings']['logfiles_directory']['description'] = 'Where should all log files be stored?'; $lng['serversettings']['logfiles_directory2']['title'] = 'Logfiles directory or custom script'; -$lng['serversettings']['logfiles_directory2']['description'] = 'Where should all log files be stored? Optionally, you can specify a script here and use the placeholder {LOGFILE} if needed. In case of a custom script you will need to activate the logfiles piped option'; +$lng['serversettings']['logfiles_directory2']['description'] = 'Where should all log files be stored? Optionally, you can specify a script here and use the placeholders {LOGFILE}, {DOMAIN} and {CUSTOMER} if needed. In case of a custom script you will need to activate the Pipe webserver logfiles option'; $lng['serversettings']['logfiles_format']['title'] = 'Access-log format'; $lng['serversettings']['logfiles_format']['description'] = 'Enter a custom log-format here according to your webservers specifications, leave empty for default'; $lng['serversettings']['logfiles_type']['title'] = 'Access-log type'; diff --git a/lng/german.lng.php b/lng/german.lng.php index 7f44f981..87fd914e 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -333,6 +333,14 @@ $lng['serversettings']['documentroot_prefix']['title'] = 'Heimatverzeichnis'; $lng['serversettings']['documentroot_prefix']['description'] = 'Wo sollen die Heimatverzeichnisse der Kunden liegen?'; $lng['serversettings']['logfiles_directory']['title'] = 'Webserver-Logdateien-Verzeichnis'; $lng['serversettings']['logfiles_directory']['description'] = 'Wo sollen die Logdateien des Webservers liegen?'; +$lng['serversettings']['logfiles_directory2']['title'] = 'Webserver-Logdateien-Verzeichnis oder eigenes Script'; +$lng['serversettings']['logfiles_directory2']['description'] = 'Wo sollen die Logdateien des Webservers liegen? Optional kann hier ein Script hinterlegt und die Platzhalter {LOGFILE}, {DOMAIN} und {CUSTOMER} genutzt werden, sofern nötig. Falls ein Script angegeben wird, muss die Option Webserver Logdateien umleiten gesetzt werden'; +$lng['serversettings']['logfiles_format']['title'] = 'Access-Log Format'; +$lng['serversettings']['logfiles_format']['description'] = 'Hier kann ein angepasstes Log-format entsprechend der Webserver-Dokumentation angegeben werden, leer lassen für Standard'; +$lng['serversettings']['logfiles_type']['title'] = 'Access-Log Typ'; +$lng['serversettings']['logfiles_type']['description'] = 'Wähle zwischen combined oder vhost_combined.'; +$lng['serversettings']['logfiles_piped']['title'] = 'Webserver Logdateien zu eigenem Script umleiten (siehe oben)'; +$lng['serversettings']['logfiles_piped']['description'] = 'Wenn ein Script für die Logdateien verwendet wird, muss diese Option aktiviert werden, damit der Webserver die Ausgabe an das Script weitergibt.'; $lng['serversettings']['ipaddress']['title'] = 'IP-Adresse'; $lng['serversettings']['ipaddress']['description'] = 'Welche Haupt-IP-Adresse hat der Server?'; $lng['serversettings']['hostname']['title'] = 'Hostname'; diff --git a/scripts/jobs/cron_tasks.inc.http.10.apache.php b/scripts/jobs/cron_tasks.inc.http.10.apache.php index 2c5db125..9dab54a5 100644 --- a/scripts/jobs/cron_tasks.inc.http.10.apache.php +++ b/scripts/jobs/cron_tasks.inc.http.10.apache.php @@ -722,16 +722,7 @@ class apache extends HttpConfigBase // The normal access/error - logging is enabled $error_log = makeCorrectFile(Settings::Get('system.logfiles_directory') . $domain['loginname'] . $speciallogfile . '-error.log'); - // Create the logfile if it does not exist (fixes #46) - touch($error_log); - chown($error_log, Settings::Get('system.httpuser')); - chgrp($error_log, Settings::Get('system.httpgroup')); - $access_log = makeCorrectFile(Settings::Get('system.logfiles_directory') . $domain['loginname'] . $speciallogfile . '-access.log'); - // Create the logfile if it does not exist (fixes #46) - touch($access_log); - chown($access_log, Settings::Get('system.httpuser')); - chgrp($access_log, Settings::Get('system.httpgroup')); $logtype = 'combined'; if (Settings::Get('system.logfiles_format') != '') { @@ -743,9 +734,32 @@ class apache extends HttpConfigBase } if (Settings::Get('system.logfiles_piped') == '1') { - $logfiles_text .= ' ErrorLog "|' . str_replace('{LOGFILE}', $error_log, Settings::Get('system.logfiles_directory')) . "\"\n"; - $logfiles_text .= ' CustomLog "|' . str_replace('{LOGFILE}', $access_log, Settings::Get('system.logfiles_directory')) . '" ' . $logtype . "\n"; + // don't use custom-script as path for logfile-names + $error_log = makeCorrectFile($domain['loginname'] . $speciallogfile . '-error.log'); + $access_log = makeCorrectFile($domain['loginname'] . $speciallogfile . '-access.log'); + // replace for error_log + $command = replace_variables(Settings::Get('system.logfiles_directory'), array( + 'LOGFILE' => $error_log, + 'DOMAIN' => $domain['domain'], + 'CUSTOMER' => $domain['loginname'] + )); + $logfiles_text .= ' ErrorLog "| ' . $command . "\"\n"; + // replace for access_log + $command = replace_variables(Settings::Get('system.logfiles_directory'), array( + 'LOGFILE' => $access_log, + 'DOMAIN' => $domain['domain'], + 'CUSTOMER' => $domain['loginname'] + )); + $logfiles_text .= ' CustomLog "| ' . $command . '" ' . $logtype . "\n"; } else { + // Create the logfile if it does not exist (fixes #46) + touch($error_log); + chown($error_log, Settings::Get('system.httpuser')); + chgrp($error_log, Settings::Get('system.httpgroup')); + touch($access_log); + chown($access_log, Settings::Get('system.httpuser')); + chgrp($access_log, Settings::Get('system.httpgroup')); + $logfiles_text .= ' ErrorLog "' . $error_log . '"' . "\n"; $logfiles_text .= ' CustomLog "' . $access_log . '" ' . $logtype . "\n"; } From f8236dff7be5b8ece933943442ce5a4adc284387 Mon Sep 17 00:00:00 2001 From: Michael Kaufmann Date: Thu, 24 May 2018 14:44:59 +0200 Subject: [PATCH 0607/1335] fix not quite correctly webserver-log-piping Signed-off-by: Michael Kaufmann --- actions/admin/settings/130.webserver.php | 16 ++++++++++++++-- install/froxlor.sql | 3 ++- install/updates/froxlor/0.9/update_0.9.inc.php | 9 +++++++++ lib/version.inc.php | 2 +- lng/english.lng.php | 4 ++-- lng/german.lng.php | 4 ++-- scripts/jobs/cron_tasks.inc.http.10.apache.php | 9 +++------ 7 files changed, 33 insertions(+), 14 deletions(-) diff --git a/actions/admin/settings/130.webserver.php b/actions/admin/settings/130.webserver.php index 35ac4857..ad56665b 100644 --- a/actions/admin/settings/130.webserver.php +++ b/actions/admin/settings/130.webserver.php @@ -104,14 +104,26 @@ return array( 'save_method' => 'storeSettingField' ), 'system_logfiles_directory' => array( - 'label' => (Settings::Get('system.webserver') != 'apache2') ? $lng['serversettings']['logfiles_directory'] : $lng['serversettings']['logfiles_directory2'], + 'label' => $lng['serversettings']['logfiles_directory'], 'settinggroup' => 'system', 'varname' => 'logfiles_directory', 'type' => 'string', - 'string_type' => (Settings::Get('system.webserver') != 'apache2') ? 'dir' : '', + 'string_type' => 'dir', 'default' => '/var/customers/logs/', 'save_method' => 'storeSettingField' ), + 'system_logfiles_script' => array( + 'label' => $lng['serversettings']['logfiles_script'], + 'settinggroup' => 'system', + 'varname' => 'logfiles_script', + 'type' => 'string', + 'string_type' => '', + 'default' => '', + 'save_method' => 'storeSettingField', + 'websrv_avail' => array( + 'apache2' + ) + ), 'system_logfiles_format' => array( 'label' => $lng['serversettings']['logfiles_format'], 'settinggroup' => 'system', diff --git a/install/froxlor.sql b/install/froxlor.sql index bda0e4d9..f39913fb 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -659,6 +659,7 @@ opcache.interned_strings_buffer'), ('system', 'logfiles_format', ''), ('system', 'logfiles_type', '1'), ('system', 'logfiles_piped', '0'), + ('system', 'logfiles_script', ''), ('panel', 'decimal_places', '4'), ('panel', 'adminmail', 'admin@SERVERNAME'), ('panel', 'phpmyadmin_url', ''), @@ -691,7 +692,7 @@ opcache.interned_strings_buffer'), ('panel', 'password_special_char', '!?<>§$%+#=@'), ('panel', 'customer_hide_options', ''), ('panel', 'version', '0.9.39.5'), - ('panel', 'db_version', '201805240'); + ('panel', 'db_version', '201805241'); DROP TABLE IF EXISTS `panel_tasks`; diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index c1a9010e..e5e3c60e 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -3959,3 +3959,12 @@ if (isDatabaseVersion('201802250')) { updateToDbVersion('201805240'); } + +if (isDatabaseVersion('201805240')) { + + showUpdateStep("Adding webserver logfile-script settings"); + Settings::AddNew("system.logfiles_script", ''); + lastStepStatus(0); + + updateToDbVersion('201805241'); +} diff --git a/lib/version.inc.php b/lib/version.inc.php index 65278766..3f870d38 100644 --- a/lib/version.inc.php +++ b/lib/version.inc.php @@ -19,7 +19,7 @@ $version = '0.9.39.5'; // Database version (YYYYMMDDC where C is a daily counter) -$dbversion = '201805240'; +$dbversion = '201805241'; // Distribution branding-tag (used for Debian etc.) $branding = ''; diff --git a/lng/english.lng.php b/lng/english.lng.php index 2c068780..6a6ffe92 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -337,8 +337,8 @@ $lng['serversettings']['documentroot_prefix']['title'] = 'Home directory'; $lng['serversettings']['documentroot_prefix']['description'] = 'Where should all home directories be stored?'; $lng['serversettings']['logfiles_directory']['title'] = 'Logfiles directory'; $lng['serversettings']['logfiles_directory']['description'] = 'Where should all log files be stored?'; -$lng['serversettings']['logfiles_directory2']['title'] = 'Logfiles directory or custom script'; -$lng['serversettings']['logfiles_directory2']['description'] = 'Where should all log files be stored? Optionally, you can specify a script here and use the placeholders {LOGFILE}, {DOMAIN} and {CUSTOMER} if needed. In case of a custom script you will need to activate the Pipe webserver logfiles option'; +$lng['serversettings']['logfiles_script']['title'] = 'Custom script to pipe log-files to'; +$lng['serversettings']['logfiles_script']['description'] = 'You can specify a script here and use the placeholders {LOGFILE}, {DOMAIN} and {CUSTOMER} if needed. In case you want to use it you will need to activate the Pipe webserver logfiles option too. No prefixed pipe-character is needed.'; $lng['serversettings']['logfiles_format']['title'] = 'Access-log format'; $lng['serversettings']['logfiles_format']['description'] = 'Enter a custom log-format here according to your webservers specifications, leave empty for default'; $lng['serversettings']['logfiles_type']['title'] = 'Access-log type'; diff --git a/lng/german.lng.php b/lng/german.lng.php index 87fd914e..1c2936af 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -333,8 +333,8 @@ $lng['serversettings']['documentroot_prefix']['title'] = 'Heimatverzeichnis'; $lng['serversettings']['documentroot_prefix']['description'] = 'Wo sollen die Heimatverzeichnisse der Kunden liegen?'; $lng['serversettings']['logfiles_directory']['title'] = 'Webserver-Logdateien-Verzeichnis'; $lng['serversettings']['logfiles_directory']['description'] = 'Wo sollen die Logdateien des Webservers liegen?'; -$lng['serversettings']['logfiles_directory2']['title'] = 'Webserver-Logdateien-Verzeichnis oder eigenes Script'; -$lng['serversettings']['logfiles_directory2']['description'] = 'Wo sollen die Logdateien des Webservers liegen? Optional kann hier ein Script hinterlegt und die Platzhalter {LOGFILE}, {DOMAIN} und {CUSTOMER} genutzt werden, sofern nötig. Falls ein Script angegeben wird, muss die Option Webserver Logdateien umleiten gesetzt werden'; +$lng['serversettings']['logfiles_script']['title'] = 'Eigenes Script zu dem Log-Files übergeben werden'; +$lng['serversettings']['logfiles_script']['description'] = 'Hier kann ein Script an das die Loginhalte übergeben werden hinterlegt und die Platzhalter {LOGFILE}, {DOMAIN} und {CUSTOMER} genutzt werden, sofern nötig. Falls ein Script angegeben wird, muss die Option Webserver Logdateien umleiten gesetzt werden'; $lng['serversettings']['logfiles_format']['title'] = 'Access-Log Format'; $lng['serversettings']['logfiles_format']['description'] = 'Hier kann ein angepasstes Log-format entsprechend der Webserver-Dokumentation angegeben werden, leer lassen für Standard'; $lng['serversettings']['logfiles_type']['title'] = 'Access-Log Typ'; diff --git a/scripts/jobs/cron_tasks.inc.http.10.apache.php b/scripts/jobs/cron_tasks.inc.http.10.apache.php index 9dab54a5..4294ecf5 100644 --- a/scripts/jobs/cron_tasks.inc.http.10.apache.php +++ b/scripts/jobs/cron_tasks.inc.http.10.apache.php @@ -733,19 +733,16 @@ class apache extends HttpConfigBase $logtype = 'vhost_combined'; } - if (Settings::Get('system.logfiles_piped') == '1') { - // don't use custom-script as path for logfile-names - $error_log = makeCorrectFile($domain['loginname'] . $speciallogfile . '-error.log'); - $access_log = makeCorrectFile($domain['loginname'] . $speciallogfile . '-access.log'); + if (Settings::Get('system.logfiles_piped') == '1' && Settings::Get('system.logfiles_script') != '') { // replace for error_log - $command = replace_variables(Settings::Get('system.logfiles_directory'), array( + $command = replace_variables(Settings::Get('system.logfiles_script'), array( 'LOGFILE' => $error_log, 'DOMAIN' => $domain['domain'], 'CUSTOMER' => $domain['loginname'] )); $logfiles_text .= ' ErrorLog "| ' . $command . "\"\n"; // replace for access_log - $command = replace_variables(Settings::Get('system.logfiles_directory'), array( + $command = replace_variables(Settings::Get('system.logfiles_script'), array( 'LOGFILE' => $access_log, 'DOMAIN' => $domain['domain'], 'CUSTOMER' => $domain['loginname'] From 18fb422a699254e350bea40ca44a0fee93ca5641 Mon Sep 17 00:00:00 2001 From: Michael Kaufmann Date: Thu, 24 May 2018 14:50:52 +0200 Subject: [PATCH 0608/1335] correct order of settings Signed-off-by: Michael Kaufmann --- actions/admin/settings/130.webserver.php | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/actions/admin/settings/130.webserver.php b/actions/admin/settings/130.webserver.php index ad56665b..4caa875f 100644 --- a/actions/admin/settings/130.webserver.php +++ b/actions/admin/settings/130.webserver.php @@ -124,6 +124,17 @@ return array( 'apache2' ) ), + 'system_logfiles_piped' => array( + 'label' => $lng['serversettings']['logfiles_piped'], + 'settinggroup' => 'system', + 'varname' => 'logfiles_piped', + 'type' => 'bool', + 'default' => false, + 'save_method' => 'storeSettingField', + 'websrv_avail' => array( + 'apache2' + ) + ), 'system_logfiles_format' => array( 'label' => $lng['serversettings']['logfiles_format'], 'settinggroup' => 'system', @@ -153,17 +164,6 @@ return array( 'apache2' ) ), - 'system_logfiles_piped' => array( - 'label' => $lng['serversettings']['logfiles_piped'], - 'settinggroup' => 'system', - 'varname' => 'logfiles_piped', - 'type' => 'bool', - 'default' => false, - 'save_method' => 'storeSettingField', - 'websrv_avail' => array( - 'apache2' - ) - ), 'system_customersslpath' => array( 'label' => $lng['serversettings']['customerssl_directory'], 'settinggroup' => 'system', From 10330f8a7ab99fe56d022eb5d14964199fe7658e Mon Sep 17 00:00:00 2001 From: Michael Kaufmann Date: Thu, 24 May 2018 17:44:36 +0200 Subject: [PATCH 0609/1335] fix fallback redirect code when customredirect is enabled and default is selected Signed-off-by: Michael Kaufmann --- lib/functions/output/function.RedirectCode.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/functions/output/function.RedirectCode.php b/lib/functions/output/function.RedirectCode.php index 5a8cd57d..79c7528c 100644 --- a/lib/functions/output/function.RedirectCode.php +++ b/lib/functions/output/function.RedirectCode.php @@ -72,7 +72,8 @@ function getDomainRedirectCode($domainid = 0) { $default = '301'; if (Settings::Get('customredirect.enabled') == '1') { $all_codes = getRedirectCodes(false); - $default = $all_codes[Settings::Get('customredirect.default')]; + $_default = $all_codes[Settings::Get('customredirect.default')]; + $default = ($_default == '---') ? $default : $_default; } $code = $default; if ($domainid > 0) { From c1e62e6be719affc003774a639de5c952ffd8ffc Mon Sep 17 00:00:00 2001 From: Michael Kaufmann Date: Tue, 29 May 2018 15:47:41 +0200 Subject: [PATCH 0610/1335] get rid of serialization completely to avoid possible code execution, fixes #555 Signed-off-by: Michael Kaufmann --- admin_domains.php | 28 ++++++++--------- customer_extras.php | 4 +-- dns_editor.php | 8 ++--- install/froxlor.sql | 2 +- install/lib/class.FroxlorInstall.php | 19 ++++++------ install/lng/english.lng.php | 1 - install/lng/german.lng.php | 1 - .../updates/froxlor/0.9/update_0.9.inc.php | 30 +++++++++++++++++++ lib/classes/output/class.paging.php | 4 +-- .../froxlor/function.CronjobFunctions.php | 2 +- lib/functions/froxlor/function.inserttask.php | 10 +++---- scripts/jobs/cron_backup.php | 2 +- scripts/jobs/cron_tasks.php | 2 +- 13 files changed, 71 insertions(+), 42 deletions(-) diff --git a/admin_domains.php b/admin_domains.php index f731d59c..c2804a16 100644 --- a/admin_domains.php +++ b/admin_domains.php @@ -528,7 +528,7 @@ if ($page == 'domains' || $page == 'overview') { $ipandports = array(); if (isset($_POST['ipandport']) && ! is_array($_POST['ipandport'])) { - $_POST['ipandport'] = unserialize($_POST['ipandport']); + $_POST['ipandport'] = json_decode($_POST['ipandport'], true); } if (isset($_POST['ipandport']) && is_array($_POST['ipandport'])) { @@ -564,7 +564,7 @@ if ($page == 'domains' || $page == 'overview') { $ssl_ipandports = array(); if (isset($_POST['ssl_ipandport']) && ! is_array($_POST['ssl_ipandport'])) { - $_POST['ssl_ipandport'] = unserialize($_POST['ssl_ipandport']); + $_POST['ssl_ipandport'] = json_decode($_POST['ssl_ipandport'], true); } // Verify SSL-Ports @@ -606,7 +606,7 @@ if ($page == 'domains' || $page == 'overview') { $ssl_redirect = 0; $letsencrypt = 0; $http2 = 0; - // we need this for the serialize + // we need this for the json-encode // if ssl is disabled or no ssl-ip/port exists $ssl_ipandports[] = - 1; @@ -622,7 +622,7 @@ if ($page == 'domains' || $page == 'overview') { $ssl_redirect = 0; $letsencrypt = 0; $http2 = 0; - // we need this for the serialize + // we need this for the json-encode // if ssl is disabled or no ssl-ip/port exists $ssl_ipandports[] = - 1; @@ -692,7 +692,7 @@ if ($page == 'domains' || $page == 'overview') { } if (count($ssl_ipandports) == 0) { - // we need this for the serialize + // we need this for the json-encode // if ssl is disabled or no ssl-ip/port exists $ssl_ipandports[] = - 1; } @@ -794,9 +794,9 @@ if ($page == 'domains' || $page == 'overview') { 'dkim' => $dkim, 'speciallogfile' => $speciallogfile, 'selectserveralias' => $serveraliasoption, - 'ipandport' => serialize($ipandports), + 'ipandport' => json_encode($ipandports), 'ssl_redirect' => $ssl_redirect, - 'ssl_ipandport' => serialize($ssl_ipandports), + 'ssl_ipandport' => json_encode($ssl_ipandports), 'phpenabled' => $phpenabled, 'openbasedir' => $openbasedir, 'phpsettingid' => $phpsettingid, @@ -1420,7 +1420,7 @@ if ($page == 'domains' || $page == 'overview') { $ipandports = array(); if (isset($_POST['ipandport']) && ! is_array($_POST['ipandport'])) { - $_POST['ipandport'] = unserialize($_POST['ipandport']); + $_POST['ipandport'] = json_decode($_POST['ipandport'], true); } if (isset($_POST['ipandport']) && is_array($_POST['ipandport'])) { @@ -1466,7 +1466,7 @@ if ($page == 'domains' || $page == 'overview') { $ssl_ipandports = array(); if (isset($_POST['ssl_ipandport']) && ! is_array($_POST['ssl_ipandport'])) { - $_POST['ssl_ipandport'] = unserialize($_POST['ssl_ipandport']); + $_POST['ssl_ipandport'] = json_decode($_POST['ssl_ipandport'], true); } if (isset($_POST['ssl_ipandport']) && is_array($_POST['ssl_ipandport'])) { @@ -1494,7 +1494,7 @@ if ($page == 'domains' || $page == 'overview') { $ssl_redirect = 0; $letsencrypt = 0; $http2 = 0; - // we need this for the serialize + // we need this for the json-encode // if ssl is disabled or no ssl-ip/port exists $ssl_ipandports[] = - 1; @@ -1510,7 +1510,7 @@ if ($page == 'domains' || $page == 'overview') { $ssl_redirect = 0; $letsencrypt = 0; $http2 = 0; - // we need this for the serialize + // we need this for the json-encode // if ssl is disabled or no ssl-ip/port exists $ssl_ipandports[] = - 1; @@ -1603,7 +1603,7 @@ if ($page == 'domains' || $page == 'overview') { } if (count($ssl_ipandports) == 0) { - // we need this for the serialize + // we need this for the json-encode // if ssl is disabled or no ssl-ip/port exists $ssl_ipandports[] = - 1; } @@ -1668,8 +1668,8 @@ if ($page == 'domains' || $page == 'overview') { 'issubof' => $issubof, 'speciallogfile' => $speciallogfile, 'speciallogverified' => $speciallogverified, - 'ipandport' => serialize($ipandports), - 'ssl_ipandport' => serialize($ssl_ipandports), + 'ipandport' => json_encode($ipandports), + 'ssl_ipandport' => json_encode($ssl_ipandports), 'letsencrypt' => $letsencrypt, 'http2' => $http2, 'hsts_maxage' => $hsts_maxage, diff --git a/customer_extras.php b/customer_extras.php index 5800805d..5ef3cb61 100644 --- a/customer_extras.php +++ b/customer_extras.php @@ -563,7 +563,7 @@ if ($page == 'overview') { $existing_backupJob = null; while ($entry = $sel_stmt->fetch()) { - $data = unserialize($entry['data']); + $data = json_decode($entry['data'], true); if ($data['customerid'] == $userinfo['customerid']) { $existing_backupJob = $entry; break; @@ -613,7 +613,7 @@ if ($page == 'overview') { if (!empty($existing_backupJob)) { $action = "abort"; - $row = unserialize($entry['data']); + $row = json_decode($entry['data'], true); $row['path'] = makeCorrectDir(str_replace($userinfo['documentroot'], "/", $row['destdir'])); $row['backup_web'] = ($row['backup_web'] == '1') ? $lng['panel']['yes'] : $lng['panel']['no']; $row['backup_mail'] = ($row['backup_mail'] == '1') ? $lng['panel']['yes'] : $lng['panel']['no']; diff --git a/dns_editor.php b/dns_editor.php index 98a4d683..75b5978e 100644 --- a/dns_editor.php +++ b/dns_editor.php @@ -208,7 +208,7 @@ if ($action == 'add_record' && ! empty($_POST)) { // check for duplicate foreach ($dom_entries as $existing_entry) { - // compare serialized string of array + // compare json-encoded string of array $check_entry = $existing_entry; // new entry has no ID yet unset($check_entry['id']); @@ -218,9 +218,9 @@ if ($action == 'add_record' && ! empty($_POST)) { $check_entry['prio'] = (int) $check_entry['prio']; $check_entry['ttl'] = (int) $check_entry['ttl']; $check_entry['domain_id'] = (int) $check_entry['domain_id']; - // serialize both - $check_entry = serialize($check_entry); - $new = serialize($new_entry); + // encode both + $check_entry = json_encode($check_entry); + $new = json_encode($new_entry); // compare if ($check_entry === $new) { $errors[] = $lng['error']['dns_duplicate_entry']; diff --git a/install/froxlor.sql b/install/froxlor.sql index f39913fb..f445a0e9 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -692,7 +692,7 @@ opcache.interned_strings_buffer'), ('panel', 'password_special_char', '!?<>§$%+#=@'), ('panel', 'customer_hide_options', ''), ('panel', 'version', '0.9.39.5'), - ('panel', 'db_version', '201805241'); + ('panel', 'db_version', '201805290'); DROP TABLE IF EXISTS `panel_tasks`; diff --git a/install/lib/class.FroxlorInstall.php b/install/lib/class.FroxlorInstall.php index 1cbbd7ca..598bf66a 100644 --- a/install/lib/class.FroxlorInstall.php +++ b/install/lib/class.FroxlorInstall.php @@ -1015,6 +1015,16 @@ class FroxlorInstall $content .= $this->_status_message('green', $this->_lng['requirements']['installed']); } + // check for json extension + $content .= $this->_status_message('begin', $this->_lng['requirements']['phpjson']); + + if (! extension_loaded('json')) { + $content .= $this->_status_message('red', $this->_lng['requirements']['notinstalled']); + $_die = true; + } else { + $content .= $this->_status_message('green', $this->_lng['requirements']['installed']); + } + // check for bcmath extension $content .= $this->_status_message('begin', $this->_lng['requirements']['phpbcmath']); @@ -1033,15 +1043,6 @@ class FroxlorInstall $content .= $this->_status_message('green', $this->_lng['requirements']['installed']); } - // check for json extension - $content .= $this->_status_message('begin', $this->_lng['requirements']['phpjson']); - - if (! extension_loaded('json')) { - $content .= $this->_status_message('orange', $this->_lng['requirements']['notinstalled'] . "
    " . $this->_lng['requirements']['jsondescription']); - } else { - $content .= $this->_status_message('green', $this->_lng['requirements']['installed']); - } - // check for open_basedir $content .= $this->_status_message('begin', $this->_lng['requirements']['openbasedir']); $php_ob = @ini_get("open_basedir"); diff --git a/install/lng/english.lng.php b/install/lng/english.lng.php index 8651bb83..a4cea2dc 100644 --- a/install/lng/english.lng.php +++ b/install/lng/english.lng.php @@ -38,7 +38,6 @@ $lng['requirements']['phpzip'] = 'PHP zip-extension...'; $lng['requirements']['phpjson'] = 'PHP json-extension...'; $lng['requirements']['bcmathdescription'] = 'Traffic-calculation related functions will not work correctly!'; $lng['requirements']['zipdescription'] = 'The auto-update feature requires the zip extension.'; -$lng['requirements']['jsondescription'] = 'The settings import/export feature requires the json extension.'; $lng['requirements']['openbasedir'] = 'open_basedir...'; $lng['requirements']['openbasedirenabled'] = 'Froxlor will not work properly with open_basedir enabled. Please disable open_basedir for Froxlor in the coresponding php.ini'; $lng['requirements']['diedbecauseofrequirements'] = 'Cannot install Froxlor without these requirements! Try to fix them and retry.'; diff --git a/install/lng/german.lng.php b/install/lng/german.lng.php index d18e94c0..4e4638db 100644 --- a/install/lng/german.lng.php +++ b/install/lng/german.lng.php @@ -38,7 +38,6 @@ $lng['requirements']['phpzip'] = 'PHP zip-Erweiterung...'; $lng['requirements']['phpjson'] = 'PHP json-Erweiterung...'; $lng['requirements']['bcmathdescription'] = 'Traffic-Berechnungs bezogene Funktionen stehen nicht vollständig zur Verfügung!'; $lng['requirements']['zipdescription'] = 'Die Auto-Update Funktion benötigt die zip Erweiterung.'; -$lng['requirements']['jsondescription'] = 'Die Einstellungen Import/Export Funktion benötigt die json Erweiterung.'; $lng['requirements']['openbasedir'] = 'open_basedir genutzt wird...'; $lng['requirements']['openbasedirenabled'] = 'Froxlor wird mit aktiviertem open_basedir nicht vollständig funktionieren. Bitte deaktivieren Sie open_basedir für Froxlor in der entsprechenden php.ini'; $lng['requirements']['diedbecauseofrequirements'] = 'Kann Froxlor ohne diese Voraussetzungen nicht installieren! Beheben Sie die angezeigten Probleme und versuchen Sie es erneut.'; diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index e5e3c60e..17432b94 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -3968,3 +3968,33 @@ if (isDatabaseVersion('201805240')) { updateToDbVersion('201805241'); } + +if (isDatabaseVersion('201805241')) { + + $do_update = true; + showUpdateStep("Checking for required PHP json-extension"); + if (! extension_loaded('json')) { + $do_update = false; + lastStepStatus(2, 'not installed'); + } else { + lastStepStatus(0); + + showUpdateStep("Checking for current cronjobs that need converting"); + $result_tasks_stmt = Database::query(" + SELECT * FROM `" . TABLE_PANEL_TASKS . "` ORDER BY `id` ASC + "); + $upd_stmt = Database::prepare("UPDATE `" . TABLE_PANEL_TASKS . "` SET `data` = :data WHERE `id` = :taskid"); + while ($row = $result_tasks_stmt->fetch(PDO::FETCH_ASSOC)) { + if (! empty($row['data'])) { + $data = unserialize($row['data']); + Database::pexecute($upd_stmt, array( + 'data' => json_encode($data), + 'taskid' => $row['id'] + )); + } + } + lastStepStatus(0); + + updateToDbVersion('201805290'); + } +} diff --git a/lib/classes/output/class.paging.php b/lib/classes/output/class.paging.php index 8ee2bc25..64643b7d 100644 --- a/lib/classes/output/class.paging.php +++ b/lib/classes/output/class.paging.php @@ -114,7 +114,7 @@ class paging { $this->userinfo = $userinfo; if (!is_array($this->userinfo['lastpaging'])) { - $this->userinfo['lastpaging'] = unserialize($this->userinfo['lastpaging']); + $this->userinfo['lastpaging'] = json_decode($this->userinfo['lastpaging'], true); } $this->table = $table; @@ -224,7 +224,7 @@ class paging { AND `adminsession` = :adminsession "); $upd_data = array( - 'lastpaging' => serialize($this->userinfo['lastpaging']), + 'lastpaging' => json_encode($this->userinfo['lastpaging']), 'hash' => $userinfo['hash'], 'userid' => $userinfo['userid'], 'ipaddr' => $userinfo['ipaddress'], diff --git a/lib/functions/froxlor/function.CronjobFunctions.php b/lib/functions/froxlor/function.CronjobFunctions.php index a2c09785..0b5f80ff 100644 --- a/lib/functions/froxlor/function.CronjobFunctions.php +++ b/lib/functions/froxlor/function.CronjobFunctions.php @@ -63,7 +63,7 @@ function getOutstandingTasks() { while ($row = $result->fetch(PDO::FETCH_ASSOC)) { if ($row['data'] != '') { - $row['data'] = unserialize($row['data']); + $row['data'] = json_decode($row['data'], true); } // rebuilding webserver-configuration diff --git a/lib/functions/froxlor/function.inserttask.php b/lib/functions/froxlor/function.inserttask.php index 3f2cdbde..705bf77e 100644 --- a/lib/functions/froxlor/function.inserttask.php +++ b/lib/functions/froxlor/function.inserttask.php @@ -70,7 +70,7 @@ function inserttask($type, $param1 = '', $param2 = '', $param3 = '', $param4 = ' $data['uid'] = $param2; $data['gid'] = $param3; $data['store_defaultindex'] = $param4; - $data = serialize($data); + $data = json_encode($data); Database::pexecute($ins_stmt, array('type' => '2', 'data' => $data)); } elseif ($type == '6' @@ -78,7 +78,7 @@ function inserttask($type, $param1 = '', $param2 = '', $param3 = '', $param4 = ' ) { $data = array(); $data['loginname'] = $param1; - $data = serialize($data); + $data = json_encode($data); Database::pexecute($ins_stmt, array('type' => '6', 'data' => $data)); } elseif ($type == '7' @@ -88,7 +88,7 @@ function inserttask($type, $param1 = '', $param2 = '', $param3 = '', $param4 = ' $data = array(); $data['loginname'] = $param1; $data['email'] = $param2; - $data = serialize($data); + $data = json_encode($data); Database::pexecute($ins_stmt, array('type' => '7', 'data' => $data)); } elseif ($type == '8' @@ -98,13 +98,13 @@ function inserttask($type, $param1 = '', $param2 = '', $param3 = '', $param4 = ' $data = array(); $data['loginname'] = $param1; $data['homedir'] = $param2; - $data = serialize($data); + $data = json_encode($data); Database::pexecute($ins_stmt, array('type' => '8', 'data' => $data)); } elseif ($type == '20' && is_array($param1) ) { - $data = serialize($param1); + $data = json_encode($param1); Database::pexecute($ins_stmt, array('type' => '20', 'data' => $data)); } } diff --git a/scripts/jobs/cron_backup.php b/scripts/jobs/cron_backup.php index b01fe1ed..131a2004 100644 --- a/scripts/jobs/cron_backup.php +++ b/scripts/jobs/cron_backup.php @@ -79,7 +79,7 @@ $all_jobs = $result_tasks_stmt->fetchAll(); foreach ($all_jobs as $row) { if ($row['data'] != '') { - $row['data'] = unserialize($row['data']); + $row['data'] = json_decode($row['data'], true); } if (is_array($row['data'])) { diff --git a/scripts/jobs/cron_tasks.php b/scripts/jobs/cron_tasks.php index b7176a53..48cbbb66 100644 --- a/scripts/jobs/cron_tasks.php +++ b/scripts/jobs/cron_tasks.php @@ -43,7 +43,7 @@ while ($row = $result_tasks_stmt->fetch(PDO::FETCH_ASSOC)) { $resultIDs[] = $row['id']; if ($row['data'] != '') { - $row['data'] = unserialize($row['data']); + $row['data'] = json_decode($row['data'], true); } /** From b80bdcbc4fe6149cb696944e25433a8479ca13d4 Mon Sep 17 00:00:00 2001 From: Michael Kaufmann Date: Tue, 29 May 2018 15:54:44 +0200 Subject: [PATCH 0611/1335] forgot to add version file Signed-off-by: Michael Kaufmann --- lib/version.inc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/version.inc.php b/lib/version.inc.php index 3f870d38..eb1a527a 100644 --- a/lib/version.inc.php +++ b/lib/version.inc.php @@ -19,7 +19,7 @@ $version = '0.9.39.5'; // Database version (YYYYMMDDC where C is a daily counter) -$dbversion = '201805241'; +$dbversion = '201805290'; // Distribution branding-tag (used for Debian etc.) $branding = ''; From 72835c56ada3f86403c740631d9644c756782242 Mon Sep 17 00:00:00 2001 From: Daniel Reichelt Date: Tue, 5 Jun 2018 06:51:10 +0200 Subject: [PATCH 0612/1335] trim trailing whitespace --- admin_domains.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/admin_domains.php b/admin_domains.php index c2804a16..b12ca648 100644 --- a/admin_domains.php +++ b/admin_domains.php @@ -1124,7 +1124,6 @@ if ($page == 'domains' || $page == 'overview') { } } } elseif ($action == 'edit' && $id != 0) { - $result_stmt = Database::prepare(" SELECT `d`.*, `c`.`customerid` FROM `" . TABLE_PANEL_DOMAINS . "` `d` @@ -2250,12 +2249,12 @@ if ($page == 'domains' || $page == 'overview') { } } } elseif ($action == 'jqGetCustomerPHPConfigs') { - + $customerid = intval($_POST['customerid']); $allowed_phpconfigs = getCustomerDetail($customerid, 'allowed_phpconfigs'); echo !empty($allowed_phpconfigs) ? $allowed_phpconfigs : json_encode(array()); exit; - + } elseif ($action == 'import') { if (isset($_POST['send']) && $_POST['send'] == 'send') { From 2ba4137e7da5917db676b674d412fe2f39ce0269 Mon Sep 17 00:00:00 2001 From: Daniel Reichelt Date: Tue, 5 Jun 2018 06:51:20 +0200 Subject: [PATCH 0613/1335] fix triggering an LE CSR when changing www on a main domain Prior to this, LE CSRs were triggered only when the wwwserveralias was changed on alias domains, but not on main domains. Fixes #526 --- admin_domains.php | 8 ++++++++ customer_domains.php | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/admin_domains.php b/admin_domains.php index b12ca648..00ab4143 100644 --- a/admin_domains.php +++ b/admin_domains.php @@ -2027,7 +2027,15 @@ if ($page == 'domains' || $page == 'overview') { } else if ($result['wwwserveralias'] != $wwwserveralias || $result['letsencrypt'] != $letsencrypt) { // or when wwwserveralias or letsencrypt was changed + triggerLetsEncryptCSRForAliasDestinationDomain($aliasdomain, $log); + + if ($aliasdomain === 0) { + // in case the wwwserveralias is set on a main domain, $aliasdomain is 0 + // --> the call just above to triggerLetsEncryptCSRForAliasDestinationDomain + // is a noop...let's repeat it with the domain id of the main domain + triggerLetsEncryptCSRForAliasDestinationDomain($id, $log); + } } $log->logAction(ADM_ACTION, LOG_INFO, "edited domain #" . $id); diff --git a/customer_domains.php b/customer_domains.php index 23bbb07f..a84b2366 100644 --- a/customer_domains.php +++ b/customer_domains.php @@ -773,7 +773,15 @@ if ($page == 'overview') { triggerLetsEncryptCSRForAliasDestinationDomain($aliasdomain, $log); } elseif ($result['wwwserveralias'] != $wwwserveralias || $result['letsencrypt'] != $letsencrypt) { // or when wwwserveralias or letsencrypt was changed + triggerLetsEncryptCSRForAliasDestinationDomain($aliasdomain, $log); + + if ($aliasdomain === 0) { + // in case the wwwserveralias is set on a main domain, $aliasdomain is 0 + // --> the call just above to triggerLetsEncryptCSRForAliasDestinationDomain + // is a noop...let's repeat it with the domain id of the main domain + triggerLetsEncryptCSRForAliasDestinationDomain($id, $log); + } } // check whether LE has been disabled, so we remove the certificate From 06ef81cc5bd3dd34898abbc05cef04f6e4547ab4 Mon Sep 17 00:00:00 2001 From: Michael Kaufmann Date: Sun, 17 Jun 2018 09:41:31 +0200 Subject: [PATCH 0614/1335] adjust year in copyright in std-customer-index file, thx to demlak Signed-off-by: Michael Kaufmann --- templates/misc/standardcustomer/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/misc/standardcustomer/index.html b/templates/misc/standardcustomer/index.html index de82cf3b..109e2d88 100644 --- a/templates/misc/standardcustomer/index.html +++ b/templates/misc/standardcustomer/index.html @@ -57,7 +57,7 @@ From aa881560cc996c38cbf8c20ee62854e27f72c73c Mon Sep 17 00:00:00 2001 From: Michael Kaufmann Date: Tue, 19 Jun 2018 21:46:11 +0200 Subject: [PATCH 0615/1335] deny access to tickets not owned by current user, thx to chbi Signed-off-by: Michael Kaufmann --- customer_tickets.php | 30 +- lib/classes/ticket/class.ticket.php | 489 ++++++++++++++-------------- 2 files changed, 273 insertions(+), 246 deletions(-) diff --git a/customer_tickets.php b/customer_tickets.php index 1a7508b9..d237ac06 100644 --- a/customer_tickets.php +++ b/customer_tickets.php @@ -238,7 +238,11 @@ if ($page == 'overview') { } } elseif ($action == 'answer' && $id != 0) { if (isset($_POST['send']) && $_POST['send'] == 'send') { - $replyticket = ticket::getInstanceOf($userinfo, -1); + try { + $replyticket = ticket::getInstanceOf($userinfo, -1); + } catch(Exception $e) { + standard_error($e->getMessage()); + } $replyticket->Set('subject', validate($_POST['subject'], 'subject'), true, false); $replyticket->Set('priority', validate($_POST['priority'], 'priority'), true, false); $replyticket->Set('message', validate(str_replace("\r\n", "\n", $_POST['message']), 'message', '/^[^\0]*$/'), true, false); @@ -272,7 +276,11 @@ if ($page == 'overview') { } } else { $ticket_replies = ''; - $mainticket = ticket::getInstanceOf($userinfo, (int)$id); + try { + $mainticket = ticket::getInstanceOf($userinfo, (int)$id); + } catch(Exception $e) { + standard_error($e->getMessage()); + } $dt = date("d.m.Y H:i\h", $mainticket->Get('dt')); $status = ticket::getStatusText($lng, $mainticket->Get('status')); @@ -351,7 +359,11 @@ if ($page == 'overview') { } elseif ($action == 'close' && $id != 0) { if (isset($_POST['send']) && $_POST['send'] == 'send') { $now = time(); - $mainticket = ticket::getInstanceOf($userinfo, (int)$id); + try { + $mainticket = ticket::getInstanceOf($userinfo, (int)$id); + } catch(Exception $e) { + standard_error($e->getMessage()); + } $mainticket->Set('lastchange', $now, true, true); $mainticket->Set('lastreplier', '0', true, true); $mainticket->Set('status', '3', true, true); @@ -359,7 +371,11 @@ if ($page == 'overview') { $log->logAction(USR_ACTION, LOG_NOTICE, "closed support-ticket '" . $mainticket->Get('subject') . "'"); redirectTo($filename, array('page' => $page, 's' => $s)); } else { - $mainticket = ticket::getInstanceOf($userinfo, (int)$id); + try { + $mainticket = ticket::getInstanceOf($userinfo, (int)$id); + } catch(Exception $e) { + standard_error($e->getMessage()); + } ask_yesno('ticket_reallyclose', $filename, array('id' => $id, 'page' => $page, 'action' => $action), $mainticket->Get('subject')); } } elseif ($action == 'reopen' && $id != 0) { @@ -377,7 +393,11 @@ if ($page == 'overview') { } $now = time(); - $mainticket = ticket::getInstanceOf($userinfo, (int)$id); + try { + $mainticket = ticket::getInstanceOf($userinfo, (int)$id); + } catch(Exception $e) { + standard_error($e->getMessage()); + } $mainticket->Set('lastchange', $now, true, true); $mainticket->Set('lastreplier', '0', true, true); $mainticket->Set('status', '0', true, true); diff --git a/lib/classes/ticket/class.ticket.php b/lib/classes/ticket/class.ticket.php index a9d69ff8..4fb06eb4 100644 --- a/lib/classes/ticket/class.ticket.php +++ b/lib/classes/ticket/class.ticket.php @@ -19,46 +19,53 @@ * * Support Tickets - Tickets-Class */ - -class ticket { +class ticket +{ /** * Userinfo + * * @var array */ private $userinfo = array(); /** * Ticket ID - * @var tid + * + * @var int */ private $tid = - 1; /** * Ticket Data Array - * @var t_data + * + * @var array */ private $t_data = array(); /** * Ticket-Object-Array - * @var tickets + * + * @var ticket */ - static private $tickets = array(); + private static $tickets = array(); /** * Class constructor. * - * @param array userinfo - * @param int ticket id + * @param + * array userinfo + * @param + * int ticket id */ - private function __construct($userinfo, $tid = - 1) { + private function __construct($userinfo, $tid = - 1) + { $this->userinfo = $userinfo; $this->tid = $tid; - + // initialize data array $this->initData(); - + // read data from database $this->readData(); } @@ -66,21 +73,24 @@ class ticket { /** * Singleton ftw ;-) * - * @param array userinfo - * @param int ticket id + * @param + * array userinfo + * @param + * int ticket id */ - static public function getInstanceOf($_usernfo, $_tid) { - if (!isset(self::$tickets[$_tid])) { - self::$tickets[$_tid] = new ticket($_usernfo, $_tid); + static public function getInstanceOf($_usernfo, $_tid) + { + if (! isset(self::$tickets[$_tid . '-' . $_usernfo['userid']])) { + self::$tickets[$_tid . '-' . $_usernfo['userid']] = new ticket($_usernfo, $_tid); } - return self::$tickets[$_tid]; + return self::$tickets[$_tid . '-' . $_usernfo['userid']]; } /** * Initialize data-array */ - private function initData() { - + private function initData() + { $this->Set('customer', 0, true, true); $this->Set('admin', 1, true, true); $this->Set('subject', '', true, true); @@ -100,16 +110,33 @@ class ticket { /** * Read ticket data from database. */ - private function readData() { - - if (isset($this->tid) - && $this->tid != - 1 - ) { - $_ticket_stmt = Database::prepare(' - SELECT * FROM `' . TABLE_PANEL_TICKETS . '` WHERE `id` = :tid' - ); - $_ticket = Database::pexecute_first($_ticket_stmt, array('tid' => $this->tid)); - + private function readData() + { + if (isset($this->tid) && $this->tid != - 1) { + + if ($this->userinfo['customerid'] > 0) { + $_ticket_stmt = Database::prepare(' + SELECT * FROM `' . TABLE_PANEL_TICKETS . '` WHERE `id` = :tid AND `customerid` = :cid'); + $tdata = array( + 'tid' => $this->tid, + 'cid' => $this->userinfo['customerid'] + ); + } else { + $_ticket_stmt = Database::prepare(' + SELECT * FROM `' . TABLE_PANEL_TICKETS . '` WHERE `id` = :tid' . ($this->userinfo['customers_see_all'] ? '' : ' AND `adminid` = :adminid')); + $tdata = array( + 'tid' => $this->tid + ); + if ($this->userinfo['customers_see_all'] != '1') { + $tdata['adminid'] = $this->userinfo['adminid']; + } + } + $_ticket = Database::pexecute_first($_ticket_stmt, $tdata); + + if ($_ticket == false) { + throw new Exception("Invalid ticket id"); + } + $this->Set('customer', $_ticket['customerid'], true, false); $this->Set('admin', $_ticket['adminid'], true, false); $this->Set('subject', $_ticket['subject'], true, false); @@ -130,8 +157,8 @@ class ticket { /** * Insert data to database */ - public function Insert() { - + public function Insert() + { $ins_stmt = Database::prepare(" INSERT INTO `" . TABLE_PANEL_TICKETS . "` SET `customerid` = :customerid, @@ -146,8 +173,7 @@ class ticket { `status` = :status, `lastreplier` = :lastreplier, `by` = :by, - `answerto` = :answerto" - ); + `answerto` = :answerto"); $ins_data = array( 'customerid' => $this->Get('customer'), 'adminid' => $this->Get('admin'), @@ -171,8 +197,9 @@ class ticket { /** * Update data in database */ - public function Update() { - + public function Update() + { + // Update "main" ticket $upd_stmt = Database::prepare(' UPDATE `' . TABLE_PANEL_TICKETS . '` SET @@ -180,8 +207,7 @@ class ticket { `lastchange` = :lastchange, `status` = :status, `lastreplier` = :lastreplier - WHERE `id` = :tid' - ); + WHERE `id` = :tid'); $upd_data = array( 'priority' => $this->Get('priority'), 'lastchange' => $this->Get('lastchange'), @@ -196,38 +222,44 @@ class ticket { /** * Moves a ticket to the archive */ - public function Archive() { - + public function Archive() + { + // Update "main" ticket $upd_stmt = Database::prepare(' - UPDATE `' . TABLE_PANEL_TICKETS . '` SET `archived` = "1" WHERE `id` = :tid' - ); - Database::pexecute($upd_stmt, array('tid' => $this->tid)); - + UPDATE `' . TABLE_PANEL_TICKETS . '` SET `archived` = "1" WHERE `id` = :tid'); + Database::pexecute($upd_stmt, array( + 'tid' => $this->tid + )); + // Update "answers" to ticket $upd_stmt = Database::prepare(' - UPDATE `' . TABLE_PANEL_TICKETS . '` SET `archived` = "1" WHERE `answerto` = :tid' - ); - Database::pexecute($upd_stmt, array('tid' => $this->tid)); + UPDATE `' . TABLE_PANEL_TICKETS . '` SET `archived` = "1" WHERE `answerto` = :tid'); + Database::pexecute($upd_stmt, array( + 'tid' => $this->tid + )); return true; } /** * Remove ticket from database */ - public function Delete() { - + public function Delete() + { + // Delete "main" ticket $del_stmt = Database::prepare(' - DELETE FROM `' . TABLE_PANEL_TICKETS . '` WHERE `id` = :tid' - ); - Database::pexecute($del_stmt, array('tid' => $this->tid)); - + DELETE FROM `' . TABLE_PANEL_TICKETS . '` WHERE `id` = :tid'); + Database::pexecute($del_stmt, array( + 'tid' => $this->tid + )); + // Delete "answers" to ticket" $del_stmt = Database::prepare(' - DELETE FROM `' . TABLE_PANEL_TICKETS . '` WHERE `answerto` = :tid' - ); - Database::pexecute($del_stmt, array('tid' => $this->tid)); + DELETE FROM `' . TABLE_PANEL_TICKETS . '` WHERE `answerto` = :tid'); + Database::pexecute($del_stmt, array( + 'tid' => $this->tid + )); return true; } @@ -237,16 +269,17 @@ class ticket { public function sendMail($customerid = - 1, $template_subject = null, $default_subject = null, $template_body = null, $default_body = null) { global $mail, $theme; - + // Some checks are to be made here in the future if ($customerid != - 1) { // Get e-mail message for customer $usr_stmt = Database::prepare(' SELECT `name`, `firstname`, `company`, `email` - FROM `' . TABLE_PANEL_CUSTOMERS . '` WHERE `customerid` = :customerid' - ); - $usr = Database::pexecute_first($usr_stmt, array('customerid' => $customerid)); - + FROM `' . TABLE_PANEL_CUSTOMERS . '` WHERE `customerid` = :customerid'); + $usr = Database::pexecute_first($usr_stmt, array( + 'customerid' => $customerid + )); + $replace_arr = array( 'FIRSTNAME' => $usr['firstname'], 'NAME' => $usr['name'], @@ -268,23 +301,21 @@ class ticket { SELECT `value` FROM `" . TABLE_PANEL_TEMPLATES . "` WHERE `adminid`= :adminid AND `language`= :lang - AND `templategroup`= 'mails' AND `varname`= :tplsubject" - ); + AND `templategroup`= 'mails' AND `varname`= :tplsubject"); $result = Database::pexecute_first($result_stmt, $tpl_seldata); $mail_subject = html_entity_decode(replace_variables((($result['value'] != '') ? $result['value'] : $default_subject), $replace_arr)); - + unset($tpl_seldata['tplsubject']); $tpl_seldata['tplmailbody'] = $template_body; - + $result_stmt = Database::prepare(" SELECT `value` FROM `" . TABLE_PANEL_TEMPLATES . "` WHERE `adminid`= :adminid AND `language`= :lang - AND `templategroup`= 'mails' AND `varname`= :tplmailbody" - ); + AND `templategroup`= 'mails' AND `varname`= :tplmailbody"); $result = Database::pexecute_first($result_stmt, $tpl_seldata); $mail_body = html_entity_decode(replace_variables((($result['value'] != '') ? $result['value'] : $default_body), $replace_arr)); - + if ($customerid != - 1) { $_mailerror = false; try { @@ -294,28 +325,30 @@ class ticket { $mail->MsgHTML(str_replace("\n", "
    ", $mail_body)); $mail->AddAddress($usr['email'], $usr['firstname'] . ' ' . $usr['name']); $mail->Send(); - } catch(phpmailerException $e) { + } catch (phpmailerException $e) { $mailerr_msg = $e->errorMessage(); $_mailerror = true; } catch (Exception $e) { $mailerr_msg = $e->getMessage(); $_mailerror = true; } - + if ($_mailerror) { - $rstlog = FroxlorLogger::getInstanceOf(array('loginname' => 'ticket_class')); + $rstlog = FroxlorLogger::getInstanceOf(array( + 'loginname' => 'ticket_class' + )); $rstlog->logAction(ADM_ACTION, LOG_ERR, "Error sending mail: " . $mailerr_msg); standard_error('errorsendingmail', $usr['email']); } $mail->ClearAddresses(); - } else { - + $admin_stmt = Database::prepare(" SELECT `name`, `email` FROM `" . TABLE_PANEL_ADMINS . "` - WHERE `adminid` = :adminid" - ); - $admin = Database::pexecute_first($admin_stmt, array('adminid' => $this->userinfo['adminid'])); + WHERE `adminid` = :adminid"); + $admin = Database::pexecute_first($admin_stmt, array( + 'adminid' => $this->userinfo['adminid'] + )); $_mailerror = false; try { $mail->SetFrom(Settings::Get('ticket.noreply_email'), Settings::Get('ticket.noreply_name')); @@ -324,20 +357,22 @@ class ticket { $mail->MsgHTML(str_replace("\n", "
    ", $mail_body)); $mail->AddAddress($admin['email'], $admin['name']); $mail->Send(); - } catch(phpmailerException $e) { + } catch (phpmailerException $e) { $mailerr_msg = $e->errorMessage(); $_mailerror = true; } catch (Exception $e) { $mailerr_msg = $e->getMessage(); $_mailerror = true; } - + if ($_mailerror) { - $rstlog = FroxlorLogger::getInstanceOf(array('loginname' => 'ticket_class')); + $rstlog = FroxlorLogger::getInstanceOf(array( + 'loginname' => 'ticket_class' + )); $rstlog->logAction(ADM_ACTION, LOG_ERR, "Error sending mail: " . $mailerr_msg); standard_error('errorsendingmail', $admin['email']); } - + $mail->ClearAddresses(); } } @@ -345,21 +380,18 @@ class ticket { /** * Add a support-categories */ - static public function addCategory($_category = null, $_admin = 1, $_order = 1) { - - if ($_category != null - && $_category != '' - ) { + static public function addCategory($_category = null, $_admin = 1, $_order = 1) + { + if ($_category != null && $_category != '') { if ($_order < 1) { $_order = 1; } - + $ins_stmt = Database::prepare(" INSERT INTO `" . TABLE_PANEL_TICKET_CATS . "` SET `name` = :name, `adminid` = :adminid, - `logicalorder` = :lo" - ); + `logicalorder` = :lo"); $ins_data = array( 'name' => $_category, 'adminid' => $_admin, @@ -374,23 +406,24 @@ class ticket { /** * Edit a support-categories */ - static public function editCategory($_category = null, $_id = 0, $_order = 1) { - - if ($_category != null - && $_category != '' - && $_id != 0 - ) { + static public function editCategory($_category = null, $_id = 0, $_order = 1) + { + if ($_category != null && $_category != '' && $_id != 0) { if ($_order < 1) { $_order = 1; } - + $upd_stmt = Database::prepare(" UPDATE `" . TABLE_PANEL_TICKET_CATS . "` SET `name` = :name, `logicalorder` = :lo WHERE `id` = :id "); - Database::pexecute($upd_stmt, array('name' => $_category, 'lo' => $_order, 'id' => $_id)); + Database::pexecute($upd_stmt, array( + 'name' => $_category, + 'lo' => $_order, + 'id' => $_id + )); return true; } return false; @@ -399,40 +432,43 @@ class ticket { /** * Delete a support-categories */ - static public function deleteCategory($_id = 0) { - + static public function deleteCategory($_id = 0) + { if ($_id != 0) { - + $result_stmt = Database::prepare(" SELECT COUNT(`id`) as `numtickets` FROM `" . TABLE_PANEL_TICKETS . "` - WHERE `category` = :cat" - ); - $result = Database::pexecute_first($result_stmt, array('cat' => $_id)); - + WHERE `category` = :cat"); + $result = Database::pexecute_first($result_stmt, array( + 'cat' => $_id + )); + if ($result['numtickets'] == "0") { $del_stmt = Database::prepare(" - DELETE FROM `" . TABLE_PANEL_TICKET_CATS . "` WHERE `id` = :id" - ); - Database::pexecute($del_stmt, array('id' => $_id)); + DELETE FROM `" . TABLE_PANEL_TICKET_CATS . "` WHERE `id` = :id"); + Database::pexecute($del_stmt, array( + 'id' => $_id + )); return true; } else { return false; } } - + return false; } /** * Return a support-category-name */ - static public function getCategoryName($_id = 0) { - + static public function getCategoryName($_id = 0) + { if ($_id != 0) { $stmt = Database::prepare(" - SELECT `name` FROM `" . TABLE_PANEL_TICKET_CATS . "` WHERE `id` = :id" - ); - $category = Database::pexecute_first($stmt, array('id' => $_id)); + SELECT `name` FROM `" . TABLE_PANEL_TICKET_CATS . "` WHERE `id` = :id"); + $category = Database::pexecute_first($stmt, array( + 'id' => $_id + )); return $category['name']; } return null; @@ -440,32 +476,33 @@ class ticket { /** * get the highest order number - * - * @param object $_uid admin-id (optional) - * + * + * @param object $_uid + * admin-id (optional) + * * @return int highest order number */ - static public function getHighestOrderNumber($_uid = 0) { - + static public function getHighestOrderNumber($_uid = 0) + { $where = ''; $sel_data = array(); if ($_uid > 0) { $where = " WHERE `adminid` = :adminid"; $sel_data['adminid'] = $_uid; } - $sql = "SELECT MAX(`logicalorder`) as `highestorder` FROM `" . TABLE_PANEL_TICKET_CATS . "`".$where.";"; + $sql = "SELECT MAX(`logicalorder`) as `highestorder` FROM `" . TABLE_PANEL_TICKET_CATS . "`" . $where . ";"; $result_stmt = Database::prepare($sql); $result = Database::pexecute_first($result_stmt, $sel_data); - return (isset($result['highestorder']) ? (int)$result['highestorder'] : 0); + return (isset($result['highestorder']) ? (int) $result['highestorder'] : 0); } /** * returns the last x archived tickets */ - static public function getLastArchived($_num = 10, $_admin = 1) { - + static public function getLastArchived($_num = 10, $_admin = 1) + { if ($_num > 0) { - + $archived = array(); $counter = 0; $result_stmt = Database::prepare(" @@ -477,12 +514,13 @@ class ticket { FROM `" . TABLE_PANEL_TICKETS . "` `main` WHERE `main`.`answerto` = '0' AND `main`.`archived` = '1' AND `main`.`adminid` = :adminid - ORDER BY `main`.`lastchange` DESC LIMIT 0, ".(int)$_num - ); - Database::pexecute($result_stmt, array('adminid' => $_admin)); - + ORDER BY `main`.`lastchange` DESC LIMIT 0, " . (int) $_num); + Database::pexecute($result_stmt, array( + 'adminid' => $_admin + )); + while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) { - + $archived[$counter]['id'] = $row['id']; $archived[$counter]['customerid'] = $row['customerid']; $archived[$counter]['adminid'] = $row['adminid']; @@ -496,9 +534,9 @@ class ticket { $archived[$counter]['lastchange'] = $row['lastchange']; $archived[$counter]['status'] = $row['status']; $archived[$counter]['by'] = $row['by']; - $counter++; + $counter ++; } - + if (isset($archived[0]['id'])) { return $archived; } else { @@ -516,129 +554,102 @@ class ticket { static public function getArchiveSearchStatement($subject = null, $priority = null, $fromdate = null, $todate = null, $message = null, $customer = - 1, $admin = 1, $categories = null) { $search_params = array(); - + $query = " SELECT `main`.*, ( SELECT COUNT(`sub`.`id`) FROM `" . TABLE_PANEL_TICKETS . "` `sub` WHERE `sub`.`answerto` = `main`.`id` ) as `ticket_answers` FROM `" . TABLE_PANEL_TICKETS . "` `main` - WHERE `main`.`archived` = '1' AND `main`.`adminid` = :admin" - ; - + WHERE `main`.`archived` = '1' AND `main`.`adminid` = :admin"; + $search_params['admin'] = $admin; - - if ($subject != NULL - && $subject != '' - ) { + + if ($subject != NULL && $subject != '') { $query .= " AND `main`.`subject` LIKE :subject"; - $search_params['subject'] = "%".$subject."%"; + $search_params['subject'] = "%" . $subject . "%"; } - - if ($priority != null - && isset($priority[0]) - && $priority[0] != '' - ) { - - if (isset($priority[1]) - && $priority[1] != '' - ) { - - if (isset($priority[2]) - && $priority[2] != '' - ) { - + + if ($priority != null && isset($priority[0]) && $priority[0] != '') { + + if (isset($priority[1]) && $priority[1] != '') { + + if (isset($priority[2]) && $priority[2] != '') { + $query .= " AND (`main`.`priority` = '1' OR `main`.`priority` = '2' OR `main`.`priority` = '3')"; - } else { - + $query .= " AND (`main`.`priority` = '1' OR `main`.`priority` = '1')"; } - - } elseif (isset($priority[2]) - && $priority[2] != '' - ) { - + } elseif (isset($priority[2]) && $priority[2] != '') { + $query .= " AND (`main`.`priority` = '1' OR `main`.`priority` = '3')"; - } else { $query .= " AND `main`.`priority` = '1'"; } - - } elseif($priority != null - && isset($priority[1]) - && $priority[1] != '' - ) { - if (isset($priority[2]) - && $priority[2] != '' - ) { + } elseif ($priority != null && isset($priority[1]) && $priority[1] != '') { + if (isset($priority[2]) && $priority[2] != '') { $query .= " AND (`main`.`priority` = '2' OR `main`.`priority` = '3')"; } else { $query .= " AND `main`.`priority` = '2'"; } - - } elseif($priority != null) { - - if (isset($priority[3]) - && $priority[3] != '' - ) { + } elseif ($priority != null) { + + if (isset($priority[3]) && $priority[3] != '') { $query .= " AND `main`.`priority` = '3'"; } } - - if ($fromdate != null - && $fromdate > 0 - ) { + + if ($fromdate != null && $fromdate > 0) { $query .= " AND `main`.`lastchange` > :fromdate"; $search_params['fromdate'] = strtotime($fromdate); } - - if ($todate != null - && $todate > 0 - ) { + + if ($todate != null && $todate > 0) { $query .= " AND `main`.`lastchange` < :todate"; $search_params['todate'] = strtotime($todate); } - - if ($message != null - && $message != '' - ) { + + if ($message != null && $message != '') { $query .= " AND `main`.`message` LIKE :message"; - $search_params['message'] = "%".$message."%"; + $search_params['message'] = "%" . $message . "%"; } - + if ($customer != - 1) { $query .= " AND `main`.`customerid` = :customer"; $search_params['customer'] = $customer; } - + if ($categories != null) { - + $cats = array(); foreach ($categories as $index => $catid) { if ($catid != "") { $cats[] = $catid; } } - + if (count($cats) > 0) { $query .= " AND ("; } - + foreach ($cats as $catid) { if (isset($catid) && $catid > 0) { - $query .= "`main`.`category` = :catid_".$catid." OR "; - $search_params['catid_'.$catid] = $catid; + $query .= "`main`.`category` = :catid_" . $catid . " OR "; + $search_params['catid_' . $catid] = $catid; } } - + if (count($cats) > 0) { $query = substr($query, 0, strlen($query) - 3); $query .= ") "; } } - - return array('0' => $query, '1' => $search_params); + + return array( + '0' => $query, + '1' => $search_params + ); } /** @@ -646,8 +657,7 @@ class ticket { */ static public function getStatusText($_lng, $_status = 0) { - switch($_status) - { + switch ($_status) { case 0: return $_lng['ticket']['open']; break; @@ -668,8 +678,7 @@ class ticket { */ static public function getPriorityText($_lng, $_priority = 0) { - switch($_priority) - { + switch ($_priority) { case 1: return $_lng['ticket']['high']; break; @@ -684,19 +693,19 @@ class ticket { private function convertLatin1ToHtml($str) { - $html_entities = array ( - "Ä" => "Ä", - "ä" => "ä", - "Ö" => "Ö", - "ö" => "ö", - "Ü" => "Ü", - "ü" => "ü", - "ß" => "ß" - /* - * @TODO continue this table for all the special-characters - */ + $html_entities = array( + "Ä" => "Ä", + "ä" => "ä", + "Ö" => "Ö", + "ö" => "ö", + "Ü" => "Ü", + "ü" => "ü", + "ß" => "ß" + /* + * @TODO continue this table for all the special-characters + */ ); - + foreach ($html_entities as $key => $value) { $str = str_replace($key, $value, $str); } @@ -706,45 +715,47 @@ class ticket { /** * function customerHasTickets * - * @param int customer-id - * - * @return array/bool array of ticket-ids if customer has any, else false + * @param + * int customer-id + * + * @return array/bool array of ticket-ids if customer has any, else false */ - static public function customerHasTickets($_cid = 0) { - + static public function customerHasTickets($_cid = 0) + { if ($_cid != 0) { $result_stmt = Database::prepare(" - SELECT `id` FROM `" . TABLE_PANEL_TICKETS . "` WHERE `customerid` = :cid" - ); - Database::pexecute($result_stmt, array('cid' => $_cid)); - + SELECT `id` FROM `" . TABLE_PANEL_TICKETS . "` WHERE `customerid` = :cid"); + Database::pexecute($result_stmt, array( + 'cid' => $_cid + )); + $tickets = array(); while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) { $tickets[] = $row['id']; } - + return $tickets; } - + return false; } /** * Get a data-var */ - public function Get($_var = '', $_vartrusted = false) { - + public function Get($_var = '', $_vartrusted = false) + { if ($_var != '') { - if (!$_vartrusted) { + if (! $_vartrusted) { $_var = htmlspecialchars($_var); } - + if (isset($this->t_data[$_var])) { if (strtolower($_var) == 'message') { // avoid double line-breaks, #1413 $this->t_data[$_var] = str_replace("
    \n", "\n", $this->t_data[$_var]); return nl2br($this->t_data[$_var]); - } elseif(strtolower($_var) == 'subject') { + } elseif (strtolower($_var) == 'subject') { return nl2br($this->t_data[$_var]); } else { return $this->t_data[$_var]; @@ -758,25 +769,21 @@ class ticket { /** * Set a data-var */ - public function Set($_var = '', $_value = '', $_vartrusted = false, $_valuetrusted = false) { - - if ($_var != '' - && $_value != '' - ) { - if (!$_vartrusted) { + public function Set($_var = '', $_value = '', $_vartrusted = false, $_valuetrusted = false) + { + if ($_var != '' && $_value != '') { + if (! $_vartrusted) { $_var = strip_tags($_var); } - - if (!$_valuetrusted) { + + if (! $_valuetrusted) { $_value = strip_tags($_value, '
    '); } - - if (strtolower($_var) == 'message' - || strtolower($_var) == 'subject' - ) { + + if (strtolower($_var) == 'message' || strtolower($_var) == 'subject') { $_value = $this->convertLatin1ToHtml($_value); } - + $this->t_data[$_var] = $_value; } } From 436d141bd1c6f06a66b6d6e593d359afc3c2a80e Mon Sep 17 00:00:00 2001 From: Michael Kaufmann Date: Thu, 21 Jun 2018 07:52:11 +0200 Subject: [PATCH 0616/1335] fix ticket access when posting answer Signed-off-by: Michael Kaufmann --- customer_tickets.php | 7 +++++-- lib/classes/ticket/class.ticket.php | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/customer_tickets.php b/customer_tickets.php index d237ac06..5262adb5 100644 --- a/customer_tickets.php +++ b/customer_tickets.php @@ -250,6 +250,11 @@ if ($page == 'overview') { if ($replyticket->Get('message') == null) { standard_error(array('stringisempty', 'mymessage')); } else { + try { + $mainticket = ticket::getInstanceOf($userinfo, (int)$id); + } catch(Exception $e) { + standard_error($e->getMessage()); + } $now = time(); $replyticket->Set('customer', (int)$userinfo['customerid'], true, true); $replyticket->Set('lastchange', $now, true, true); @@ -260,8 +265,6 @@ if ($page == 'overview') { $replyticket->Insert(); // Update priority if changed - $mainticket = ticket::getInstanceOf($userinfo, (int)$id); - if ($replyticket->Get('priority') != $mainticket->Get('priority')) { $mainticket->Set('priority', $replyticket->Get('priority'), true); } diff --git a/lib/classes/ticket/class.ticket.php b/lib/classes/ticket/class.ticket.php index 4fb06eb4..b3b14384 100644 --- a/lib/classes/ticket/class.ticket.php +++ b/lib/classes/ticket/class.ticket.php @@ -46,7 +46,7 @@ class ticket /** * Ticket-Object-Array * - * @var ticket + * @var ticket[] */ private static $tickets = array(); From da993985618cb3efeb61f42cd6c4f39a42e6045a Mon Sep 17 00:00:00 2001 From: Michael Kaufmann Date: Thu, 21 Jun 2018 07:55:35 +0200 Subject: [PATCH 0617/1335] update jquery-ui Signed-off-by: Michael Kaufmann --- css/images/ui-bg_glass_55_fbf9ee_1x400.png | Bin 0 -> 335 bytes css/images/ui-bg_glass_65_ffffff_1x400.png | Bin 280 -> 207 bytes css/images/ui-bg_glass_75_dadada_1x400.png | Bin 0 -> 262 bytes css/images/ui-bg_glass_75_e6e6e6_1x400.png | Bin 0 -> 262 bytes css/images/ui-bg_glass_95_fef1ec_1x400.png | Bin 0 -> 332 bytes .../ui-bg_highlight-soft_75_cccccc_1x100.png | Bin 0 -> 280 bytes css/images/ui-icons_222222_256x240.png | Bin 4862 -> 6922 bytes css/images/ui-icons_2e83ff_256x240.png | Bin 0 -> 4549 bytes css/images/ui-icons_454545_256x240.png | Bin 0 -> 6992 bytes css/images/ui-icons_888888_256x240.png | Bin 0 -> 6999 bytes css/images/ui-icons_cd0a0a_256x240.png | Bin 0 -> 4549 bytes css/jquery-ui.min.css | 10 +++++----- 12 files changed, 5 insertions(+), 5 deletions(-) create mode 100644 css/images/ui-bg_glass_55_fbf9ee_1x400.png create mode 100644 css/images/ui-bg_glass_75_dadada_1x400.png create mode 100644 css/images/ui-bg_glass_75_e6e6e6_1x400.png create mode 100644 css/images/ui-bg_glass_95_fef1ec_1x400.png create mode 100644 css/images/ui-bg_highlight-soft_75_cccccc_1x100.png create mode 100644 css/images/ui-icons_2e83ff_256x240.png create mode 100644 css/images/ui-icons_454545_256x240.png create mode 100644 css/images/ui-icons_888888_256x240.png create mode 100644 css/images/ui-icons_cd0a0a_256x240.png diff --git a/css/images/ui-bg_glass_55_fbf9ee_1x400.png b/css/images/ui-bg_glass_55_fbf9ee_1x400.png new file mode 100644 index 0000000000000000000000000000000000000000..118209b4b6adcabef21f26f93b63b04263cd38d3 GIT binary patch literal 335 zcmeAS@N?(olHy`uVBq!ia0vp^j6gI&fCnc6a#?2AmP!?*K(O3p^r= zfwTu0yPeFo12TF&T^vI^j=w#x$i?I+((tf;UXnmgbH|3oY>pC!)f}(GR!16S-u+#{ ze6YEqRkW=8vGl=5qArKM<9}TC-}iEvB{zdaTcX5$wyRTK&ALYGq)eZD3$! kV9wV{HQiD+2?Uy7F}l3=FCzt`Q|Ei6yC4 gx%nxXX_X8{28P%*m@1za0%~CJboFyt=akR{01NjSYXATM delta 168 zcmX@lID=_|iW^J1qpu?a!^VE@KZ&di3=E9LLGDfr>(0r%1acITJ%W507^>757#dm_ z7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD?*A#qe+wT>~I83NbXZGBma_w9qv$wlXkC wd9_Ces7RXUCGx5b?-VBQkUm|IuXOmYJrBRJgj{Vx zMbNnqUkncy+qa2-mWYc>swkcIuvGK#>(0d)B7)5f`@$Ei28nH~0h*~=;u=wsl30>z zm0Xkxq!^403@vmG%ybQmLk!HVj7_Z!OtcLQtPBhqZ+a@AXvob^$xN%nt>Ht<$2mX^ N44$rjF6*2UngCW^PcZ-h literal 0 HcmV?d00001 diff --git a/css/images/ui-bg_glass_75_e6e6e6_1x400.png b/css/images/ui-bg_glass_75_e6e6e6_1x400.png new file mode 100644 index 0000000000000000000000000000000000000000..5c738d545a82220d1cbf6bc45be9d2194806f696 GIT binary patch literal 262 zcmeAS@N?(olHy`uVBq!ia0vp^j6gI&0LWmFTHNUZq?nSt-Ch3w7g=q17Rci)@Q5r1 z(jH*!b~4)z#PD=+46!(!TrvH)L6@80)r*_cdCvDr%)6ghVL16=s@mbz7H!uRdGeDa z?kzLg)16i!f8fKx84s0>4afpGrm9eRnfr++(ft7(l<4sQm6b-rgDVb@NxHWue`8Wrt Ofx*+&&t;ucLK6U?tWQ4x literal 0 HcmV?d00001 diff --git a/css/images/ui-bg_glass_95_fef1ec_1x400.png b/css/images/ui-bg_glass_95_fef1ec_1x400.png new file mode 100644 index 0000000000000000000000000000000000000000..1d828735a5b5a2d0e88aac81b90f0dbb5dff162c GIT binary patch literal 332 zcmeAS@N?(olHy`uVBq!ia0vp^j6gI&fCnc6a#?2AmP!?*K(O3p^r= zfwTu0yPeFo12VciT^vI^j=w#>k(V)1qW$CZ|6)SVV-&*#dav<$DMuV&n0Dbpw@a>=^7Y^7?@ibn_3x|Xd4(<85lI) h^i)96kei>9nO2Eg!-tlSbATEcJYD@<);T3K0RXc|bS3}* literal 0 HcmV?d00001 diff --git a/css/images/ui-bg_highlight-soft_75_cccccc_1x100.png b/css/images/ui-bg_highlight-soft_75_cccccc_1x100.png new file mode 100644 index 0000000000000000000000000000000000000000..4d4a7e9280a7021f5d3a6c5004637a588f481f24 GIT binary patch literal 280 zcmeAS@N?(olHy`uVBq!ia0vp^j6j?s03;ZUuHXC*q?nSt-Ch3w7g=q17Rci)@Q5r1 z(jH*!b~4)z$cXZEaSV~ToLo`U+vu0Ue0cG9p8hWqa?gxxGLm=1A1u)Cewe3oSeCaf zI$k30UHXoTXA5lSJe(zTcE%W-S*bfB&J`pw9sa4-R?IGW?p~6`>jMSP&M+u3 zY@9al)zrvpHlQu4C9V-ADTyViR>?)FK#IZ0z|cb1z)aV`IK;r*%GlJ(z(m`?z{|9pEC2TIxHV+jYr=H%}!7RG)%@|?KBm>pyGpu&ojtW_wyym4g`c9 zco$2c8`p{vTVxgdal_?TR2q}t~bOfL?S*Jz>-hj zej%U-ol64Z=H=<4vts*B%Lz$=z-A~15GXb3wI}HLd?o-|{AomRiY`p@EGlXSgLObn zHiI+BXw_n(p}o|pYMok=31u8OMO?~bm-n`CS;fRowPbbSwTgu0=*7l(0v$^yp&hkM zDNj1Y0bXyV)+53{VIwdGW4Fo5{;9<>33wL?Z3XX8uv#Z=U!_NVfTGUbDr4v}Tu zR`?wX3hn^0$(t{SX1-GeGS~e&#-4Kh2MVt#b5SU8CjmH-w$O;aCD6t?ZrfdZEbk@Y zJqZfG&3O7WH7q-{({FD9v;hC5@$-r}DY+5dbrzbDrH+tT&pfKJUur|7-n>I<<=u4E zqp>W4q^FJ|mzJ;N(7*VMG zU2BTU!JR%!p+}0CBhHUm_PbWLt8EWDyGtR zVU)%zz+8FYJEe|QI~>NS9o>Rn3~1g%$0eQ`!lR@ZF2O92__?mx1rtva8{14&Kj1L0 zDaZ(xRC21rv8=fJO|)hhkn=UJGcknv`SnzJbkjtkmm+>?J%~|f+$2Sbx8vOAIgY2t zqQT*KpE$b>z=kxqpl_!>-2K;g2dm}3JV23iDpVU^-or__aU{~34xQEW(|mEprL&b- zAQSAsLrN#rJ|@c*=_coAKi$0zq;K9IiTC{?&;K{{PJd1H8|KNh0y%+oVJU9m3t9~>S^EF8ZniI_zTt3H+C~_J}%``4jjf5yho+NMLCrcAJN*P z^^L0M2Nk|oV{v_&cv{>_czfr@d)!?(MX2RGno!%Jx$h65%CqQ#A0d5;| zSSLyd*lk78{Jmt_-J$L6#h)e1B{JA5+7@mTA2cKQHMTmEgVqts${HgsFO~C=B_Q*( zHl%u4XCA+FdC1CvH?&IhdHSiEW#v4eIhhZEQ+hV>M>Dkvof`t}fb!@dj0LrbRqUg5 z8nXf_+F;+Ir4&w^l=4m~sy#TR$Q8xfPB%72n=*iaN9EKWeXj`CkLbf69eW>JCVDPZ z7L|9kyXOYG(A0Zx(eZiSb@JFFdT6i-rdyVPce<|Y|A~7p%E!+Vm5pq)E_mjl3TB&{ z7xkGp$iSk&UEtWes|0wb9iDlJf)%nsDc$a1kP0__Pvgop<&Te#x#)w#k-3S#)*eW* znUIBK40-siM_X970Y(XHD$9MH=f zxxGx(wa4Z;q^W3V0>lRag@tvPcuViWb!iG;t2 z5-KFMfg<4J#MU$lBUwKHsdM*=Xc&102XwKxclrG1%i=4}FJahwrX;H zNwp>$UiNsQ3MaH`j_r!Cf!2nGp?oQYWGDyw*oRPD)Jji};0ay%lW)Y}e_~utKE<_L zAW#===qs>H+NFwJaOrgI)RmNr*QaH#W&q37EwXhR=bE;rxBx~wP_0#}8%G*VMyw0t zOXoQ8i8jP(H)=AY#xJECZlA{!*{lM`Wp((OZTS&YORwOY5MEdRRM@+^ANnn3WENTU zA3OQOy5)T|{ zY;G!nWz5+Wt(#<-Sr@5cgAv1gCuY^U)Ic+>!QAozYINV2tObk6EFYy2AmNW0n)r&g zKC+K(98CT!SCiRNi%Qcy2?^*e&zkhXK-*gD+1)epREUD@_8y6#qo9GdvS=?m7pBQ` zkL)xGZw}JHFBf3X*09PMs%@LyD-6@bK$YKo{x5a39#&fGyXO3w6XU>6jJg zQxb0!eyd)r8P}79d{lXm@XwyGF;M~{Y|HRvIVJl6MGu643K(3krt#!mUF&&tM z-{qM2sSo2s1@j!}h>Z+rA6Pc(peiB|1BSb{g@P{1VOv-kb2MJ5|L!vR}v>GSI@0<7h7Ey`Iq`4cv)8v39ONQF*(#v*Db zNB_~;W^ZHpEvX>TpNcjEwqgc~S;?zigBbTgVqHJ$%9~EWjd99l7Ya*mEn>GbEF!ic zTiJw8JQJ^ti0A(>+*#ha6svdftjY|(XtMW!CU}#pC^AEGg0!z^e{u6KNctmD_L=|1h_F85eW|d& z+iZ@#_$CIQXazAg$AB$+7?7Q|`Sca-nC$sw5+|$(DDuQc{)a!^hpV0Kx)WHV*$GjO z|LEJ?QV~#Qf$SxPaJFBkim9;NXMLaE_V9$4CY5uDt)dIV@{AVCq{`geO@^4SpUoRc zPVkO7ol5!RI8EDt^|cnZYF*_zKo{IflvWA_^?@N?-+@@l++5RRzEjGcx9-;pT`$Bw zYqm3fyCB@{Bh2RL_l6IB3$|M?yE=cV3>a6u#0604l9gb^&P;UI&K6>^A3rei z_AsGQ4Ph7qJMYk+^0a=4`12DAaYopombf^7U%29p-_H&Hu0FqO+@n`^e)LD62&Z84ykM?K4rpE-FOj2zS#H`pWk3AGV5%vZY0F1 z&VL&c0mopw@;XD4Qf8}=H2Iu{Vc9X=d9#KP5u$xsT}<}l7uBv2d>vWdXQF`>_6zd>Qkqko0@L$$)M-(3;=I!1JeN+rI>;xQSPDrF{CFaxPI^sok@u+J#M za|fi2jTJ73_5xC67LB)FzWAh)!m=%c+%oP!)FAN02CIm?G2@F7h~Gn>I;UiEEu;C< z);%{mKeh{tOdolre6l`Ubu6F4{op|tGN=Ad<=3cywRTiuWg{O(T@R}k`(MIXtG*44-UH&+4)(f(HhC1 zXsTq@i1&P?`-bqFAKa=oz$VRWlUTm*f{!U8!!N~$vB$ElIJZ0-MPoSq2y8Z>$Os?- zGitN8Lb*rCC$GeItn-ey9Ac6UJ+857Sgm5CLje5}kRry*T%TnPML@s<9Yg&9Jc>75 z9_UG@JJPA!V$7TfAcPr|@Es-aqeJt6yVLvzFUY(eR}|GGERaSJoA{bPCayxFF9gc6 z^N|sr=T+@yO}@iNU&Th$wC-VJBDztJZk!3#RCDb&k;!k4YXrXpK0LoRkco;%sq4p9 zqSP9OCLHKHxQVL(9&zrHL3fl~$FAr4$$+J%-=#(cT0I+BR?h`OnYD2FF^**(Xzz2s z#U!p1!yAQ8q zF_PD>c=N0{AM_3a7t9mUp^?$L6f#bY6OSkSHJZ6=c};=~Evb&aDUPk24+w1dPxB>; zHCbF&kbo=5)dNhQ1*Cr@omSHHWAO^|I5C1T@)P0Udp&wAPqepw9%h%6p1ZG#IOnP# z{uXSe?kNT%HP|H3^^N89$WH*`M;j3TK;=ta4?i2?JNb2U@R(s7rOV3j1>qa@j@KcH zJlr;{VG$cF0|Rt*46bb~aV|-q z73E4Wdq=&cryrwo%JgSG+kUsHH$wBn&on#@&z-V!vo&}R{c2AiEHqO?WQ-+-$LQ;| zSzsy5l3p8|_9NdxMK)PB!T>0P7k z&0CLgF@ySTJ=Bov9-*8aasg4`HBXV7GNBwSFWbfYpSUZwDVpY=r`DKG;QAq+)te2G z3jx&_i{~qs7fS+B*mZzdUd0prA#xjI_$&supo1tyP>sGKT!YLB=Cpj>xaiZ2&;OCd z7@~3TTVdi#^s^JD;Do?PT*OL`OohekdMverH^CeOJu2iQJPN++A9PQ@_?i+-l31q7 zsGebe^Uz2N$&&Im!nq>i#lZi3quVjmk&ofh?(5xyFQC{RbF-nue6Q`*?X*_#){G{@ zK6VgEs(n5yGLa=RVXuM-b3K?mqz0qy^q&O@{4h|_%MO1OeV8r4a$+ILh@P7F>Sjy* z`NPx!G`aDXuVsN(6|i63h#SzB>Hv^01!&-CYDW(j*2dAsem{P{o%?R3<|we8Z3{o$ zD#M;c1M7HJ1P6B8TCQRtdbsTW{Digg;=Tbh>ZLgI{R((4_UG(Sikptwn^Z`kcz&IY z0+NRJ^MlZY_n%$*l{qZ6#K6QWkMt{#=`}~c-spzOmr&=#tG8Vx`41nE*dJ5BnDXwx zM?U;=@oq53vM7qIZp5zz{&;QGv8O^S^(hKv?&=`g5=s3wuV{6Ql*moEnrz!8Ni>)P z>1uAq3&nbGE6lGHHl(l1UYr>`$O|3n3Y$mc{};v-(f4;jf;K*GAnTFZ17Hz43@FgV ziYF@6)04!;wB=B(dn0QU9tZzoF)5My#Ad3M^JND`IGi!5bS+h+B2$1^BL@Pt`IAbo zD)j5t9t+h&e7A5eJdOfMt4c+KJ8IXSSBX?Fe}VfE;+FW({Smz{KLO_Vg}SY0Xr^yF z1yh>@=f`l(!w&jrxu=|7G0z1wFqE5uJoW2WH~)%B^K257mHJg-(EIY$j=GMdObJV1 zRn|;yWG0Gk2CwO6bswvia-uxcDePIVR%08pf&{wJ-AYdv*)72*>^j5x#3UaM-E{il zk~&2g5nB`XZu_S3SN{e)AjE5wOs_bC_`Ncj;;$v=fvJIad(>fnQ|6AI{V&`SZ2_}CjpF}SlN#1n&An*VSa%egX%sCk_;{f35__sJ zOE^+UR`2JCbE!`(`mV4sOQK%d_;w9jI>M$c5^Y3wN@(kv{fh7uACHiAg-*_{UV3Ox zxp}8j@X_c)@`B5g4*{rYr=jQ7nnbQeic%Bgn-hWX^W$9FWS`dwVo76vy%ts0>Zbh2UvGm^{MpxM@{%>Y143I($OJfe zEv*E>MJ(+Tv`bAP4K~lx5gs3mEVZ?7tZYrBH;-V9J5Bx-)`^MLk$l_PRpp6?^F_wo zlV%W&v?Ydr?*)k05&BBKTxMXpTaA6|Qmo%`o%)m6vov2j7OvRi9Z-kplQ`yu@^3=f zFd6Ivjkw9pp-AQo&E};^*;RXBXsMp`DT#SIUOwl_*1H*_!HuuEZ?> z5!DSZS)x?A%vwFV!O1iQO%o;fK9I(% z$+BLxXIs^+|8ROTUlMBHcFokLkLgvLEWN5zy-40Pk>a#Pdr7^9HIuYjas)=?c8yXs zJKg~ZJkTHb(x7#`Et7pBoDc1#3##xyM z3}}lJ#$VdHnRhOTxUsU!Ziv*)`uE55lnXMS3zQr&NjQNO(;olsf)1uNe71-LxfUTn z>)M(OJ_&>rg74}GVOS@^kx`pSzJb@RY4ShyR zgpl6Y%~N}Ed?s`=A9k)Ga-YfSiz8Z%{}{sCRT*RI%FPz!@xcRbf2MJM9o#p_NqdNim~b;E?;dRhDlGrPjCy_vl2 z_70gh4;SVyo?cOaCAh=6SOzQ}en{jzFI_0%XEfx8j zNWFIdL-nht-6wHKB=T~D9QGsl%>mhNv>zIk6IJ=yW)5w2`To@u?IeJ~`~XHE8+$%1 z6-iC^E)-)noXcFG{Fo*BHxaic9kEceQ2597`9X1NMmjE_^I4Ed+t8GRH6Y85>O-3x z>hnmv8;jPzLWJ)VXN+#P0)|6EGy1ZcLFp{NzV|oH)>N!1@Uy-{QrJ#+3_B zTly^uFUV;yC^X#J^A>qTXUr%*tpBT)vbR?ZQiy~%TFl%B;!F_^kLIfY`61qpq>rN( zGq1E~?$>2+Qq4f;m`^g zi@XO$y03_pVr*+*)_~0g%)f9}BCi~w)cNz^d^6ql=&8w|k8@#HcvG$iCMHlhwd2Rn z%KB1{`F!7>?v5BeW%NC*Ej(?6t=w&%41kxL2g1S4$HBv+>9qA|%Yi%f`(u%+1YR zVPExc0nRSg_V0ZE{{V^eY0{?v^1qFso#H| Q^5g_ikWrPcku(qaFX2Wbe*gdg literal 4862 zcmYjV2{hE*`~S|@jAl%|)}$;mq9kIHeH}~oB_eCq3R!02wGY0Ot@PTd2w6%LB1@Jr zB3TlNtiwc8#-6n>{QchZJO5wzocnq1bD!tjbC=J3&V6peB~!g4Y$w2SURAkfXORcDiU{gfW_V*rva&_aE+EwpzBb>ig)um4s1K zsjet%Xq>EYv}(4t66`(c+>Pgi000SC>fcBg#8U1InSCyx^dDbpS@Am+TcUB^BELJt z5`ODEWGPaWn}*{NKJDYG8fQa4{l$O5%(-Q6v!$?#Q;PMH7If#Fkbh`7>nfa9E)@c* z|F~w%LlloVcIsDg)+HQUrN%d^>T7>f&`;S0Tw%4bh<9LS*l_HhkbDI+r3ReB!O%2| zW4m+tEwIcVlr0!ZC5n5N#W z%@F43h;_Dy?3BJkXP3fXmpj|SlJn!ar4gr&AWk{18|no8;D7mIDZHo^Oa68B?uc}a zx1r&5$i^9B2SyHwpAi8F5M5XP=WEQsZ@>Uh({|^_3V1Kx5ZopK1}rk8>VPgR4TAty zSpJ;FzmuY*wIxGV!8sqny#&`o^a!E3>RKzDlXa* z*FMDbdnI1^SRsTXKJ<+uH`Ffrvn-~amjf|8*}1d5S{66bFPtkpb{~Nxv>A-_APq2x zP2tY6z&EnDgw605D#~+TutVmQfEMz59M?>QVl=?>4%GL@7WAiu5f7}R^j?1M{v<7ngy^FK&&79 zc6{YN{P*~LMK7yi-pN&8kn4MQlnG>{k{kRjCBBxJa)wgDnKCLJhW1@Tbd{OwizP2B ziwTDVesj;azZ4`73X5>r@~gJvvp}(b{!&c5Bk`?YRd@vs{=u(5tM(M-)Dq`qt;8E>}Zz z+yfb{Nu^|bYs#rHh8c(QhZ}EsO#CBR6y(68wM5g|zYmXtH_5@_g$U|=>e>){Er;Kf}t!Vw@uy0;hyoD-)p^eCN`t)*T zO2|4MriboN@?dmwOVCrI@ZX7-9iGV}t{Gf&6a56780M$MJz~eA>WGb>-e0Gll26uV z&3Km?hMgBo9eOLOriARr0ts2x{wWPMd|e`~JcqaRpD>zqKHqtQ8i_*CP_E;eg$7s! zG{q+m-+Uo|uXP6?2h{OPB-LcznP=&&jf-MTjh?|zw$$AhJmZ{)vu}XkZ8@uf|AkbW zG$5mpL_yW4Mhgwd=8w36(*=uaFv3)Wz3S1Xc5iREO$H(;C48-SZYwHR{!z1sQkn%} zBirJc=6Yua`4Zz8U3y$_w&IqZq1lmPT+f^IPYVWzHYNVvWP3VVJu1`IP3s%qi#$c7 z`!+dBH!qbCeas0h@k$}GGrTt*2Mz??3oRea8{W?W{X4iBGr5Rr(E5+kTs zYeMV7n!2WxBz%NVh~RkJ23`3@u^`E4{pzQ|YM>llpB?n9vF(lW4D;~Tpn@7q>Zq2Z zC+Be#2ZD=csYiLa{}Rf5g3#m=(R_sPnAtLq-5PvWhQMA_XI9KbjfVp=Jo#_plTKJ` z3~jOEb5y;hdvC^q#m=thOuZdwITtCj^ZF^o z4deQrIGrQ#e(v%soU{!T8Sj@a#0l`DDwzx1SRYyTqUOfAvAUtH+Fji;?%=k< z`&pb&q_}Q72kpWPDZ%7Qsaf~3ku|tdq4PT*#$=f^$G$|-&*4kYRGw{NJc3?M!~sEe z`KFS$o+!uOJG{k%;zGkLv=vse?za_9Dj7ja+CY^JWMa=0hR;q-=p7j`nQ+hsK7vI7 z$5w2fKM2Dw;|?Nr?LF#oc)dB*uu*j`K=BAEmG8igw+1j4eeYC!)0&o_Q9L;&3p zG25_ML00*D?rx7aQ*Y{xi&_wE#Fp1$*Zmi%qaSI=%3{hYPA$qaHh_iUxb$|(`}oPN za8pB-#Zt^GpdGvf|6f2E7IM0Q#Ns7w9`wGeDuBf*J(tf3YdN3TFqKxWJhR|S3;moz zco00w`0Ja}9woi6O68J2#r$|@Ri4%1YB_kk)vITAYz~8ebyj~kR4W7bm%sjr-2dxq zrva0>wj8ATb*tu|WVX!=BYErIf=$F<(u6BXHM$#?)~%Dl&FS7Z&(4=JwhUL*3irA{ z1rD6XJA`#zUhHma?_z(N)bV*if&TM@4c^l>`?B_4%Dbc8&19y==+DzCdp?Ipqzt>> zo=keNx>Z%Wnycw}bl;8{AfY)Iyw-JdYMrHT*zW>Dmf_ZrjsLx!kN^avz3rxTj%`n`o;pcVU$$`zA5I0(GX;mN zgFdPYox%v`Wliqa&rp?-9F;O;&#d1zq!c?qv+PE5Fz%LTPBKi23r|!r^z5WeXJ( zQ4kQoEa_mcA@}=Rb>36XD|S6$#w7-f=55@(-Ym&}qKewJAqmP@(Uu-SCf zmoKaaUP7|n_R1ON8L?<}*bjjJYF)T$RXEj4X7E%!Iyzr8Y?t@w4)FXseIQUM-M4N2 zZGoJ-dApr^pq*LVCvMu6=3rB8$jUbH$JlWV0oYsloGi&^JDV%t<;0yWB0nfL*6A`W z{WmJ+@1O5?e1vZz73~ywq;5 zjwEm0d$NzrzA%hKPdvUbXSRVBJxad(GYU+9Zogb4=cJK$@0b?EPmd%F=1~XT@4tH~ z#)@`Ms-3jprCrgDCf#&O0toX3jf(mpk(V|eyIW($^zwN<(-g%$Kh#$P@2u-kw7_vw zCOY!YOi1nLdLZ#HRfx+h-)dT9_G3+H_Cu&9#eUQLX7&}IXY4BtY4Ydhn?pXXH8%vC zy|&h{_E~ut1r7;PaeuE5Os4Jx&bgY0uYLB_nAiczj6oq@dr57h=Zrn?2h zJxnl0K^{~s&Dbfi-g#a}On7~TCbP3&{L^p$sPeS}L6iG`wOzlxm*pl(DO3JFSOb%; zfd?}dkg2+CS$3%_J!SP56hq2KUSFx(XF3^ysyD@daq^c6&&R$GOcH!Gw>+LubN%|u zg+zxv+}bNO+3&P{tvF5&n$>aFcsV98UxNdm)5+3%4%%Phalf>nY+;^U@6 zb_|vz(|p^8YjN>A7{mh+=5P*>Wrt9>NAon>9&`c3vdw5-YUR9zq7Xc9aLsj1JT1@o zOOU)R2*k7g`NE|rzPc8;hWUxZB@LXjHBNlf0$I zsa#m<62@r&Z+)AWKHcqFvF-j3T(LZ*q_B*lBWMuLx-nq={*|&>9$E2Oo)Cu}1QHVE zn&u>O>}$2ZaMRj&w@;Jm{2&I4gDflL)plCy;+faMPVc%?fLXYZB8->*{hWL0=DbQs zZx@GATUfcSyvt3)2rcYKy2^ zUgTPxSLjv8+TC?VFP*zL;w6V|kJx@6^$G9ui;u?7Q?X(dW7KEHFg;#ybS(+a#@01u z3azXrgho=o1=-JO;K3L39tG^MqL!fI{FNVBwj54qV%=^)P^Vx|oGd$`44E8x n2P`ngkV_;MjgJN*eRHt0aIr{$q)gr!$ diff --git a/css/images/ui-icons_2e83ff_256x240.png b/css/images/ui-icons_2e83ff_256x240.png new file mode 100644 index 0000000000000000000000000000000000000000..f2bf8388370920783b94285cb75827ce4b4cc1c5 GIT binary patch literal 4549 zcmeHK_fr#0w@yL`gaFc{D^V0dT0jt_h7t^f4$^rEQj}g5g#baS(xnI}2(MH@h)9zn zp$O6h3{7bfL3)uU1VTRa&U`cXhx;GgXLk0S-Pvc(?z1yz&UtKXc$0;Rp9uf}u;`+- zO#uMlsSDU*q&uA_boCBTH&~7J%~4X3wErLfRY0J5#phHY>p)XOGXNjkH87)b>!WPx zi?{rcu2_Cf*?yJ_D%_fA4E(%}@D&*|iD3aE9Ub8Q5I;s1Fr`|n8{`2cJ2=F%MrZyms{v7&r0rf>&2b|hJ zJ{q9YZDkcFg(n&&yxSR~su6zj=B! z7A|uH*>BQlsX*L!-`H5IUM{a6X_BjqY{XC%d=Nks#?H9fVRw$pQY7V-8n`;H-J#Dp7{n{!; zoScGT%oF%zObcvCO77SmyV$UtWUT!copFB4dE_K&eiX_Bnfp5`Pve~cFs%LR)&K(m zdcm5KpKirZWW@5_y?@P4--zd%eDXBF!_~L|3IGp2!7H2QyzkN0hXh z@EnU9yPkCh-Twur>ORhmMa{YS(GCGKTKAP8QGU~#pi?RSqDf1=TYV`9(zki;!L9`0 z!2fFCr|G6poHD?}IA3l{==(~lGugVI)Ry5|MVtg+UblwXLe(BwDTybz5zFbGGA4D# zm{^>@0knH8Ff!QT!pKg%f+tpK90VG1o3$w^WWSSrM*_7RTL(Na_%s<2HK57(Ba=iG z;jb)+4{lTQ{;CB&*+o8{_eX9$0$&9!RQW{z`gY|d$}e)8cI+gK7=ahxdPuk2dsWmawrJ;2QWsD=7HV^ zhDaN|xWU^oGXPXc{z#0n#$&~IfuK)twCPs@S58~Oo^55S`qvpIhKT3jO(2Y%-K{D_ zSY33w_!l~feJ$|;H=|XqdfaeoN2ih*F6V8WDL4$!{r5eGT!LzDhCP95*1)?ruWy1qxxFz}wN z6uJtQ`(7N6@8n?JDGr$v%k#F)g*;_C=_jC=I505l2H1pl+hnX{(J+*KGw-Z8umX@Z^>(P1?+*XcvNAyxeGfWJFry_MPXlwX~=_J&H8Z+HF2pQ4!FvZ@F4Sv)hxIRg?Bm3B_+zC_jek=&*sYe5s5<dK8(qtsM2W;kk!+)V^>Eqo@;d=~*CQpi~Ig;n+J5?dX0%zn(GE_9&2Gpy;#_sEo z8%hfcAeZukljD?@z=nVjZ7nvCTKf*?&PT7qe;OF`&$dT$l(v&dWV1oCxH}!1yXoYxeVd%wR*6#Yp9oBh7e#A$`D5%4U@ zac@v|fT;g4_0Ay|y>fNWPRm&8JJZzVAIa{P&5p>t3ZzAj{-B6uCArK0o~jKC(?WmI zdW-6QtmCZGiD{Xl3%=D8kf$>7HH zO_EM_%_ehwH-;!gPTts`Mtn9Wkkr}YqTU;dXDp#rD{>2o&-7FiYWFLAI%=}>AW$EsTU5j za#ZtF%}c-vvZHt(AP>1qZ3nBorw!8`RD}by zD|$w?oQsIWi!@>qA?!!&ygxcyCp&$Q zmImn#xK^d}!o*@*51$#?f}Ckas*Bf9i=J%qSFFEYQvXG_RHKmUH4FHW#$z5HEWkBd zER}0l$v7qTZ6_iF3+J(9L&XASw<7*#^!s?Ryex}K!57skgL~bW85=7p)Pv2ip%Qwr ziiIQo*_=VZ`y8QmONUUZJT4+9Ab(f-f_lY9TiO+0pV7iJD)&I3*0tH8-d&_Xvq`I1 z?AJg_JC;aWxP3vw+4{zl>l(6z;+ccT`^_h1Thi-KDod*I^f2#lfz{?1wUM^+&%*ZI z`Vi${KZ4#+TZVta_*Q$h?LH4jzMW)X%$Js9c@`w+)%kZN_??H3qTcWsfI>4!q zF+I)XZim*@*JV%4kG^H+Bx`_G%slQjwAbFAMgdr)-#1=rv6&`u*Gjp(33vY)`(^C+ zB*~$-kk8-JQS-WU*5b3(d{MemqFGk}$d-sp`dz+Qp1~w74>S#rC?<;jkh3DhcXm$h zD_qC#R#jrJL2!^Gj;-neW3H}4-?b}bbeM{%xh#^F5Kj2*tN3_k8$+>Z5pV5QuRAJw}DP+|O# zWq8L{5ZuGNWv>buVHTyrgaz%(_VboV;|#tryvFqB3LLAlo85Mg4t`K#dE)|@vs}wZ znJlt^!2(nJ_T;xYS)=O(1(U&Q9}ftbXNFye;a?#d`T z2M@!O^4*7hyk3aO8+@m^Wf*4GtW4Sa?H+e{vR5e}51#=vyG0A-nEZ~lSMWeg-CCJ> z6Fok9?q-XJfK@xk%Ppb!Yr=K)JD%3tt!oZL#SdPAO1r=_?x2~v8*!7^9bqN|sBLgQ zLn$-no{>XvU#s@i8?F!WS4%1xJdE_taD5X62`FS7dvAZX%XJxL(Zve_%^9g(rBj|A zg3<}MD60z}+ec>W?ggbv5yCd-CzXXe{A0mf1A9_aV6MVGx)&viEUP}kaC!aWsk>@2 z?QW5H=FGkY-@HnT2?KyS!?N@}1<1!4Zp0egiXXW*q+iw{Za@@IFWch5lR}b#9XcL? zLm{s}mMC)Gc#Obqk&dL`op>#>skmJ&`CoQvLQ)_z(ey2KdQq8WEZm$_y|vwKU+1X* z?(E+Jmi<`*2sh_K=h~V2bqV@dZDR5jCGuu74um(xvKbc_DQd}JMxUo@ME57ERLQ>Y zn0$2bnI044AMJ?*=`-nrUf0wOEG(q13^PN3in*M~NR%2M*1PLm6rh zaI*NRQuzyw>0mjaCde#U?2f`xi2rCn;g(rM-kneJ&oUWA`DdfTXd0Fe1Fbq}mw=#k zLyL-jUb@{L(-w!;=PK6=eWf~$q7}Q1Y^#i%wLR1v%FkvC8qFIug$g7MP}lSzW_hLK z&V?IZ7Msu2NtU7x_-|g}WO)9ud(AxIFiaH<*Byz_Q8SmOQbVrVpVwu*sfTm}ax5M^ zDfe^qS~JuE<4g+Ve~~hu~c^@&DcW10?qfv z^?9H9y@vspU*;soonncFfzVeAPMu(y2g5gNKc-izg~Qb=9a z>8U!`al!HjbD%2L*zKLbOb6-I$uGZFvZ>O%>+I^Z<9CQjGqu!aF<=)%d`0jk zy&J-ttxq~j^W-=Rj}6h@SxvFbIGGg2Mtq3;tchRu;fa2k+V=)wg_vJC3 z{V@8mQ_CK#2@Y;bhaZ+n@LAO!M&=JPSV3xC*+wFH&lTc(H#A0-&|fIaaF1ez;|AwZ z2Admk1wKxpKCkQ-5Ha_iDN3e)9halVFZK(6l!=RDJmcQ|{mcAtc9n< zxaeu+Offpsowy5vl>acGXMT1z@!T-T`(BKn| zf&Ur@``uLtcxW{=V|O-&-Z-=>{iK*aeR8tzStS1c`oPFI**G`Us~cYtQKtT3F5|?q z8$z)`X3$nvX2(I5%_pIoAXaMCIxic3jJ7g?90D~Wm#2?)v=anr3^l|jIgdP>cuyPe z%Yi7XKxfB57bPcums10fMaalYBCbiw$e7E>DaqeZl93fhAe0aYM44Op{~~z#IJ>z% z{J#kryTd%E1in8J%zRt}gB<-`02bo? F{sUG;a@YU> literal 0 HcmV?d00001 diff --git a/css/images/ui-icons_454545_256x240.png b/css/images/ui-icons_454545_256x240.png new file mode 100644 index 0000000000000000000000000000000000000000..d6169e8bf9389ab9b5b7d2c6f0c5fe3e4d363105 GIT binary patch literal 6992 zcmZ{Jbx<76vhMDpfgr(y1`QHmaR~%lf_s1jw?%>l3$g?j_W;3NgS%T`aR}}Z9Kzy3 zmf&*v-FxcQJNKRU$4vKBbyv@PGu>13O_;i>93BoO4gdhaQ;>&f0sz2A6>yA={@BZA zI!itZkeQMk1Rz`XI+62n^yHKL2bV`F|KIKHpzRv~fYM$8BK6MW*I@?C4l4JhU&5+Z zX?B6>M{wc*nf5t->YI0m%k!c6iV6D-HiA+N=Q{Bvn#`}m4j85ZvFzIFY|%J>xxbN+ z)KhhOMWAack{rDyA-k^9P{-{Y^-4&T)}WQ|)zSX7U*Jm-kJzQ#7H}DD8x^^@Hd^Id zO}JQr`JnFakU}mtj z1A#B^`J~>9Aj!aCs_5V1v5fm^rO5!bpr=#5Ec4gVZ+s}W-{jz#54|=utEYqV2QDVqeWLoK&SDCg)CL8cuq(~CMMZgT%6AWTd@*7 z6&71eU>T)Xe>ATJzEE*l7!d_4EfiRtlm6_$KqoujO=FRN&<+w0vNs>&-_Ed+(5C5$ zmkYkr=0{kY`cMqEqv<( z_g<1p@{QK8lz=aO1*Gv*be8bxc48H*~?C5sKsk6Y+G94lgYv>T~L|y)zUs?t_{Tkesms}ypzUDGt zRKP9jp1&y^@&RZ)xNxKyc}wp*1n`O+;#zYQ0#N>+ zwL0RCF;cJTN#50JeIN3zGCFJL?|Pv`sHdd9Okxo&0#6;1k;Hzh-x1*puL6C4Rf06- z@a?%qB!?Rcm-Kxu^;0S$aw3oi+ReC&%oRq{3G>G&kxB}sam-E4#4k>x`2Edch79Z`l~i|Vub!Rci8uT%U5Qm- zqzrUOWT)C)V7C0Ot{iK@**yiK6J*$P=vs^p<%g&3l?e^uQP(SX{G^~DEz@uDlSwWC z6HFn2>d!B-S;p7EJ46TDw;VlxIsE?PQTPYF)AjM54|{7S7g#H5O?B`H**ZP3#Rcm* z14Vjb`0wAD5v>;=@y1`jQyS87ZX^RYr;e045q~!y3PGK@B^ND$Oe^By*Pn{6o@PbJ z!lX~i+!?Z!hDHPe+ix8>vxm$X+}l3qzB?$x6vTUXWbL&MukaQe@6BjA#BLZ(saaH| zggtTHoi}V+RQiLAF|y}jtq~lUu*VwWLl%v`4A%T<8BjfHLPOI12-{xii)iECHzfHd zr62uYRj@yB%Cen7i_&0M+83`9EAR~5s_^5&9d+V~;i7mZj`xA~1xnBw*ODz`UG%eN zwZ3T!bX~sAc;6U3Vl=#&c<0aF%RcBv$yV`;1STH=o*?s*_U2Co?5UH4rIxGb-_86C z0V)FXqarc$Tp9jU$_LA;l|P&~n77aoKeiVuBf+GjpUp$fXB6kUWgiU(@RP6-ZEZ^! z6&A#>(=ZWB&pdpM;h|6VqZAmie8G@Fcf$1v{(lni-_NX(c?|LZXW$5{s?(yKDNjFt#Y{0GhcICF7V&#Q=1gJ<&oth1v|juBqBGovPk-dcNT(hWqc zWJ6H4Mr-_$uiQGVcZ-Kh6@v_>YV?tg4|uPt-H=98>l5Q)dqz{@eqQ+5i>@XOx=Lw0Xjqh0_w6?9r}i zHz^HH<3>U^Z|^f;3Ltd0j-A)8wr?S-+)b`ua{R?dJbK>}>9YdzQs=FH5)}5q~n^@mpnbkDf>;vFH7gTfo1GuAqIgTs<^LXm9fV(}{jL}_UeSnA>mB(5 z*^HS7)v)lv+8RF85S{DN^}DAJ3$UL@K!=hf7KRfi<8`F6N!2;flN(lV=4S+jIOp$ z&wiGBqp2e~U)Y*0XNoN*D6?F9C?0~OqzKTZ|CIO*v}MVErdyBA9oC0?Y9)*eri{4v zt17yAMm?p@V*cb#Hvw)krZA;q@IpgTXX8_PoUdYZBVTm!*+lbrHvpo=gZ{7Ug@PHF ztf)&_aQ*FG3cvp$*nZCdEfz4`hu8SiZY!R){{6K*wg|@|8S79F+w9cQ{?B=>`ku`f zj?Tob2hPNf?6M|5^{zL0{x-1?h%b{7uO!`AjnZ#vx{8VYmf}L=)Coi^a4gtedw<1E z&98NFQ%awC#S$?PlDy6A7gUAtEOwf6Sub2*l}b`7VqbPLO7zKEBi+3)buf$LF-a0! z-IDu7roL^8V&MvTlFnupTDE2HrkpfNtkC`xk!nfmAWQ$kb~wBHTLHj)w{aO{pHet( zo-t4j3FMTX{Z2ibk_a&om22FD=D2D+L7fOa-^2!!;`&#lgUhpn)<0_`P zPAvs$6X-Wo(Xn{=i6A|sYBOBI?JZNFts7##k;ah^QKUbGI$vXZW(eBj6Q1I4CQjPm zp`vkoMXCER^+U(lGG(QG6C00(0K-*e&q_Kb$V!DRqfj(5@@bgKCR7{k9p%>dY+)5d zepzE*kZG)Y%=Wp@W6NHyjIv-KVWidJy$;`Fyyp-ZL@k|n6d z-ud<^O;$1tvrmX&CF`^y6LQen#4l1DbFF=xV*Yko(y@L=4Q9N#dHKuS8Wr8@783TREimPeayuWEgh(Glh^D46x(av1+! z=ktQ1Q76vNEP&P`)WfsgTSyflWj4;eDSyt^%R~t!*1NQk333xEZ3xBQBShRxrz252+a?C9G|#oO^9xDr1KUz&nUjD8&f#!VBPS`fm^ zw>P4kEeImD>VXgHnOPp0i||U+e(-EYGW5Y+WvPV0E7E7u3QmP97?d z?zh65fjF@N^Hj`Kvw#meID+CQJB936N&AZ(oty?pl82s38*l2mW@&lEV3(rtTdb#N zx*h>C)Vh;#NMk^vQiF-Kq2kCP!@}(Q%!oys4KG^=Y|om ze(PAL;ga^D@4{2ToH*SqLxjs(rN!;|qAg9SuDPfDHc#+fUE+%1a5gSHvD!d&2l8zOK;zr_$Z+%6C|;Y@#iL$H0Y z^@`<)>wP)#c`2-;>1#!*76jk2r7JjI><+gry3kWX4ds)$71awl@jgwo~@Ilplz0QPW( zX;U;YX`3iK@B7RgQG8K};*vsN(XM*PT7$M&OpOuawA{WXNSjI)J;UI*F=e@+eBbl+ z#fE2F=g|oUATxJ}Dx5GHh${qqC~3}@K6K;x%gA^W$d!B^xH zNejV0tgIQ+@vt}JZ7~Xq2H=W z0$=)iMqAv`u~Ja7#=bYxJ@bky^*YHB{cjxy=HKG!>^(xQl>}3JV2B9yOS!*jkZBg-Z^~jGb zq+rflL_Oy^Hqtv_Rqc98JA`0C+2!&UvDh9yxzT2s%R4FjMRLdJGtAX`n=F)Yw%r7X zEEK$}pA-<568b@uSA_NLi@GRHOSiP0t@fgRw{Qa6_{Vj3@E}v%t?ucIDFD5T!5TGg zb$;4K?M@B3##@3N2UbESO+uxFV9}S!?~|GvCC3)0PQ{t_pZ0q+$J}gsQ@{m8v!^9D z!uk!rV~%gr@sb@iQEC+s+Q7MbSeoZ>lJ=P>O>l%uU6C}CNWihy3-Mnp`e=;jtEFco zB*=DczVn^bRSez9S|{9}z&#^*SV!vJMrO#Z-p$l4e?39lFmcc&-8#eIlGKk9U-@Rx zQ8?r7Hi2K3cHku#z%g9i#S?^&J^1xI+3RVN<5h&32x9DnAsP}bi&rJX;n1QjO4{IU z`gFZveK0;i1m*<#?j>t#+r)}_(Hi+!+L>AyUSO;yV4LgWBE;FP} zXB?1wA8H5NLIQ-)9-bfwYGy~Kjnc?oi}k-MbtTcyt$AMgYrFCC9Vx@W9iNuAh+W_C zP@Jl$^SqK8o~%R!cDy5xdyma=sL{7yGB!8*p{pRS+G1;X=$-Ml z(aNAbWRU>^IWXqS_f3Je18@EP?*LiQsQ&kxPg-8VrEw+mEw$$f8>*?f(Y2Bjw97)DTN2-B>D7QwR#`K8n?=j6Q6}{j2P>-q6?B=Q=7lc zRHYqg4)nXZ6kN5^UsR-*@-liMF(NixSw-tOn1(cV(O0q}g|R}DzFZN?hWIExV60HT z+OQV`iRJJ1q9SXlBeWwPwyba%SfrT&xkBAO2e1mjBRe67vIEi*{p&gOwGA-*@h)g} z&gz%j5(cDCVB-J*MPMxpo{Kga48P!aylFLNP{jHQ<&!B{)6ha^m0axgnrIV zz9w!NG363dJEd*R*Ls6LrSSDOWDHIi{^t zEE3l>G=eji-}6FFEho;T-3N+*lWM%swipv`flWB_LN+e6?0S#2 zYv<{a>K~iF&*C?S70EItez?Omk$QevNDjc#AhSIV6`Ko+zNfTE@GX?=F)dZETbYxe z*A}X=Z~pxMmCc8OCcyi4YuY z4X9)<>d1HDCn!(%kK7oGN5RgUXWyzvWMO;64A20#9;hV zjA!3^9W|tk<25zIOgbNAbI>DRQSNPz`Xm{u+<-idJD<&ZK$@9KFIcD*Poz(6uQfI? zBtt?r`uM(A4J?4fTR>9e#ua`GAyOvU3!17$j5Q2JnP{%>>sPkQBa*;+2YETi(`BaL zNL-6ba&k3EI%ZUCR%JK=w!;*a&a%|L8arxN+ocAjxOxHevxqvcl zyWdPk5!#m6&TM7Rz}@VeGS?0wx|~@D0g@^iJbk3ara@H1!9=Ijl0xTJ^k+giPbG%) zyaVkjG&5Vc*USApUEd2-%_}d5{KJG@CLsPFn(q{3A`08*@gG3XCT&&x5lbb|e$M!M zv754_za7E=;A|;&Nj7&)PdhHaTsN(1uq9+Y-VKxfyQ3mnx| z?5!Q$`)8Wq1TzHZzD}lM+pU6}x}tQq()Mmwu5S5KZL4}!#&6)m*^@N}$phJY276T! zFZDF7SG5QpJw0)3pph-X(h;_ zUU*UTZEidO=VXj^vvkj@wB!B#>4{K&fKAIKOUrv40bNCgBX*s`r@xSjf-ZzGy5r}& z3P+RfVUB9XTlk9tKJaV!hxZ%qtofSx5A*=v@Ae(PciD{cUxrLWfH8;ry`cG>!KSpR z{yofDff(^S$&^$Mw1eF=jBKA9^j*})vouc)6JIs-HIq9g^jpYfhnRHfTd<7SS0m;i z$;&b#3D;0tzPK+4$<~x1q8pgM5djc|@GKz!{?%@4_wFSrDgLB&?LgU6kmGu_It&fF z6CVdmFpYnvgHzqTF-h6FOp_-!9+2*x8pD1^MZ-wLY{=#N-s3tSQaw@Hpr+YT#d{|k zcDHAs#6Sd(&;$}0y!!_rUr~F6+V;*E&TgK^bs4)So_HDILoRQ7iBH3I#1=P|#8F$x zE1H=2j#Lh2rxq}U#J5j4 zG@Dw^$S=tpC%^jM3D6PG*OLEh`;MukDe)cg?EV*d_upAZPZ_YptwyKXB8M?Bi>N+Z ztl)JuZ0R4*5k{XjU#<`c@)8#+%J2^fYy-?wG!0e6W;R)kclJqUa37(Y%IbH7V0r>6 z&=}GBDflKC323|mlZBL%xx9fl!8p<1Sn+}8q?)njX<9{dzV@Ks8bqf| zutHPq3mfmH#uxxqkv`y&HLM&itjaWfgvW$+8H)r4`F?~P%tV-W`AA^xJh3nzCj;uI zw$?}ZZh;R;*i9euW7`k60YCg-0a)2KUU)Dlcpuyb<3>FAZ zkmbGIJ!jwU+aLE<-@aAd=d0V*UG?2~ElnjNd>VWJ06?UoEdLq+KznQgj&U&`?+SU| z(vJe&T1`nF08=?)XMB9Z^HetSd6dfkyc7prBmn>#Uln;7-N1#z+z?m0-mrcthj}6w zs$IVc?0MB*NqLmqw0W&IykMhE<715Sw-rtey|Ppy$7Oa-7Uhic!q-qv=X<ow+JFz5Rtu#ncw$ia8BfaFp!Yi96&?N2M}Pz&3@|7>i@(XAg^AF=PIm% zdBX$f@sJ{q&aiPuPurDeR0+DWJ9uu32I4}3?gYQhgrA-CT%pJpK+O(p>XrI$V0R{U zxLL1P<*w&<4n&}vYVOvojf#PYDL4(ItoaT&oAo$3+?MM4==HjngxIQx}zMlWZ ztE?V0R?Bd4DcZR=d`R8wyk(WBBHVPK@fsNZ>@O^w0O_toe=?tOEEq`?M-7ebA&~A_ z$AYgPqmO$AqB*|o00_~AOGrx|)t(}H*c*UIHwEa(Q$Z_J63g&6xe|Ue7;_`*+AEjo z$>}B}YxZb@2A}g}(mwvKc3PbCPzw_bm@esZ(8r*d#mGwhy3LL5ob@cd$X%d0i*q&O zZg0x1xL9N4(u^ob0@@kq&I~8E@yO@hM^k5B%M>x&YbG95kUJE!MJH8`2%b}rHt6N8 zvgb0-wCgJt_Mh%h5V|ghbguuhZ0H?s1j2bALH81fm46qZjcQ2!%@Ok1f@Hj=rJW+2 zx%6M~{O7m-Pq=&pW6$X}Or`HSb&5_A2=lW@-ENQ%sj&|!f0-)qkUvh#yOjXKf{j47){seq)+IN+TT6w#5e; z-*Rbnh(^uun-FEGIa0yLpU}F#GyH*=!)l?j_-fp5t|G%g(Gib!Vcw`o+%4n}FHC?K zNW5?|zY&kdZWBGM^!;Ve_)7pOQZ z#y=CNiOf#$!7D1x)`Oe98F8A^t;|ViZem=VG{a8Ov!eETW%fhn8PQs?*#0|j3?&}H zQgLiWto=;)HPT|v;pN6w4Bfuv8ayF!!dE!$*VmLAH`&O4yBhxCy&xaQ4Q{`{+P@eK zET+!LUS?ySJU5TVeI3=$EuvGs%OOqpyA0(;c0;X?z)eT&XIkPX>b6IMZUoeIt0O@l z)TBE_r#j-sdJ7~zx6DeBjTdeIqLIyy_)0S_VZ;2j)4xFXpb}G!L1awC|Nrz122ncmG|%UxLJPaPgtw`DmX2w=n6CF)bm{BeRb(ca>s$T~6+-_K81 zszu{Z^Wnbz13nfBz8xmAb9*uAEy!-v-x_A~Q}siS0zl)%fbfT$EG-YZ^g`F8XbpNx zcY@n+1yY~sR@-{#(9`?d;JQN&Hzx>z7hg#3LfPMRRj<1~QnD49Xr;-u{}qttfSY$# z9~-zB5kMq%TIY|VU2^c@a+KoT5?1Em5gAwe52HpPYe>dWSI5@Xv`QRNaq!f#soe?? zX!sSe@T@Y?Lp4l!pauu`9hH+oVk<{D)V;gcZMAwZ&#`7X}FO?s#hB}pS}_&1FNXwWA`#RH*JE>`3onp2%ses#ccCT{QNf5 ztUwTqY1`c4Ok3Ij@-x%Y4XHMPiAhRgL)8KDF2uGY^LwAkvXyGZFHG%d>i5BACL(hS=02&!5CF~lIP3>L$4Kn4Afe%0k&~ec-L>NT>Vr& z-+V-;CM(TpCyUC*ORpm^G9>w*8^V9>e@`XQ9N+z5aS}|X28ft6589H8Y5Or5`vz?3 zxz2@?Dq?Rh^|ohQ_qlA9s4=tf*tC@M?h77#CP(f|Qvpu7n}V8OIZgAlsgmJN#=kya z=~O;XDq(8-cs2OdS#x^iRTeEw9bg;qTfz3iGk53=%4i<}x#n(gPSTA?^6FWc-$L!} z3~}NADQjs>a-@4=BCT&{pEN71rr&+PcNaJ|X!^w(k@Yq7KCRdFSr`dDngvcgfJKk% z-hipFTFS%Zchw}rpUKmpWD;5SU!HBj&*<=tdDs|`0wF*cNBBTe`jGjD+uC<$#39vSjr268557Ekkk`o)3ZcuxDj z&yz=YzwcxL=%CblrlcoU$G%;8OvR3ZN%4@bre0jb`kE-F#G=*Mq~h>*k4m&ETb(L}gn!Hjoxm!x;q`X)b~mKK{0;ApRz~}m4)#0AKDCM8 zQKnWCeJ3zPUTbi&}+-HV@&jM2RH?HYfA@_ zhBt2Q(k2^TXNJ$cxyh{3lEElE7j-peQ0|w-lY-=S_PFaS+P7Y~SqWG&A0S!!T1%ID zM9!y`>>Ez1@c`HF242r%v$hkF1g^9tP9%M^lMAFcgmu(`?{%8y!exJrPwHnx_$Np%9@p0rNk~za0J+JoR>ov}a|I7zJ9=r~u zKhxV!G0K2GqB7J1(VP%KJS{U8$ar`e-1n*Z8t*H{YH`(D1aTQ| znGcBGr5q(22XB?mmYRh7S0PvGow4sL2i8;qdII_U4S@2 z;Cuh7H^DHq#(BVk=>~lazw}C~@iYe1?B}BFEt&=IQsNw9fN3oUpn`LBPh)5s(FHZ| z#4-T&Q5wGp68+#K3U{_rqq^w990T>!jX7reXx8;|H@NE2?IfKHova!%MUZzSCeJ!f z+A}cy7U1Q`odG@69{*ft6rPg}Rq^6`ax>ZSt_B^{<1Al#uV})(kp&(lA!2f)v3$b= zTfEOKxG7<2Q^gGD4;V4fo>EaNi4>!BI`()1uHjIW_Mo8%-f#@18wawG#rBu<^ z_$dxvm_U`V{zqs$rllbS`5QXh%me7--g8-1aYKVS?pd#mr%JF){7-lyGPQ1UMt(xJ zcgt1Sej11S0(_x}+9^n!=5}yJ@9?rgjb9n1M`_yAl%PT>(XWQfRN(!sRwM z;Uh0((ce?s-w23V!V%wd=+;V8^K>83Yjx|f$@i?C-KkL}_=;Bw+rxX@L)@P4-RsvB zg>cv=%UskAyr-C}DYZ<9J*?6gPCPn(_|W96QMe3yX}U~QMIm;UP2nS0?&}Oqp9@;g znp1ekjD7^_)akR!Ox$FqPkEKi_;T@0Rq#~%wP1KMfPeu>Ji+r%*BXU#y80TejN*tT zf_+gFlkP`14&fhu3yg^vadYoNqwj{)iEL@EBtmn^LlenEjaof7gOQC=0-td(O^b^d z*5LN*Lml6*ZcW44>?7(!>P?)vkMSg*-`x}Dvqy^#TZtiixa*MfFtv@RjM0IF@Yib> zyo`+d>#{HI?1fu1exJH`jkOc#??>P5&G3zWspz9fiOR07Wqbj7pkdhJ3We1iGj)Aa zc}Ea04BzV!ys^8TQa0!xyIH}7Y|k+I8>*;LWgjo!vhFR@A6lM8SiHXL3`(8&$$ z3-pT;_m`U>fLd`VSq3%&X!f1AjGG#B@Q?P0Zl$)Zc zFY>3ig1|@Usj1t~meSf_%zsS);zUcTl`B4DNjLX;qQ>SJu&S39D0_J(W3TfM79`}9 zrepV8JdBZhJeJ~q+B)1(LCINX2M^Z6X7s_*j+O|{I8G(w6x`Akz{(Jo zHdj8^A^tC_K0PVN3E-t-O^MB`ie;!9TSq@=?R@95!~#QVVEujIH!oBZz7;Qq4$5=zew@8Ib>j46f0WWke5 z!38}clJCN>j5oNqR9v67SZ2NGrM3+4`M)e&8{Vpkqh59sb>t_Tw8SJ|`p7YL&4($3 zgXO{{IAai-BWq6|C0R>_{MI*G+{DjXYcUW@%OG-J(|=JpPJ9zVsj%g zxDzn~F8BsrOq;TAr#|I6L5cLU*XbDG3XnQ-`zu3q;VcJ?3>k2?3uLZFpcVMO##!fV zzvG+mV#hZV)f@6p7=1c9_s*y%bYF6S@!C@ugJSHt#D{gHi^kIzGayYn{6^y|Gj)MM zmWPa`0inl3W#?s&EH~=cId@xsBKsE{Up5NZOI}!!s$4FadZpTu9(?D}>6$7P9Q(Y( zF!1JiCqX6R!<6JPa}w1? z#M@IA8=UjXd$euB4ld%N*?fRh4ep6HAT!%0`#C;6X{wtVk8ml+8X_{kF3Doe2>8}L zkp`~BRozA@MwuCEMY!I)Hh6}agMGyukrh5xlbAMNW4-2eL>?~joh!Da0+ZYMixk$H zMiusW5E;HG^D;?L5gbkaj%@zo$2&B4%zJVugF{nJS*I{hu=qD_E?)|@f_k!=g`KZ~ z29(SLciCm-Y_8vjhq@A#=NM2hlOWXs)`NVjPtx<@d?%t*8wlpVR#7jGaDXf{P82`C z^go;k^XeR}EM5x_x;x@Yx}V2|e9X&#ikm>= zF!=!gtlXdT0UC@W)&o3+n^V`r$y>Do3J=EYcO@&P&|RXjMdJDTX$oydK1h5Ywb7_8{@qx!8Yx2oRj~Zzm9QwL z$+=m6q#5~5dw>0^&3aHzsm>DP*XED>*}jID+MjakrSZ&(CE(Qna6RuD<|^wVjsGj^ z!i1mSd6(+OFqS+?UBg9g=p87YXHz=ASgeoI^G!_vTaCth}Yv84AHI8%Ej zCv>L;tR072%L;N!?039-xqP{2<<^J}q+BOL(zuu(*lwZq6LL&%zVqtScp>0z-|f;U z6B$%<=^?--XWF&b{AzVGiP+3ij(REP3vgZ)+Yu;6}f}G%WjtRbFN#Z|}yiY^jAfnnA zZ?Tm%i6*r+_d~9$-`u=vmDn{>y=g8KVOumoEDyfniGByHAGH?8H=KNXR*{D(PyoY} z4jWx*)4~cky{0<{cEMqjR=uT2jeR^n9zt-QphV)A$0I>I5=Kp;8NXMPROtW;R#I@5 z$_?NPo)MzjC0g5(S^HSQX>zg{e@~|@Yj)AkpTc_*&bd9zIw%X~!|UF~cAE*Frc(Hp zj@}l;MeWU1(ZkW5(G2bS0@E9aD~3UGx-x>@pMn^dz&Rn0YPG$q&kmSeq_qM1T>9Qm zYI^YnW9*nJFo$bphmyM%(<**JE@a@vqfa*zrfR$6@t)&dcR=-kLi$xG`N*Gf*YfXh za~m5tA`E&hxpb0Zce$yJ}~q3%Kjz`Kv_? zn)3c?d1e4l8!E5uxo?KLyI{|k(A4Ul$R|NQgJnxzSS9{hN1tT`$b4h5B!|MH#gS3b zssRIJ#@v&2Vre{WoTCq!^e>1%KI*VMj^gTxajcSOn~~TEF45sTA+4#ERq}y4QD|;g z1ateBghNF{wT?^Zy}>nJO>3TJ8W9@%5UB#Ueuj+xa1tzLhoAk>Q$QW{^=YH1zN3tr zyJbAqdyvuxEFi~u;n$MYsgekbXAx6zV?BB&JBOu*CkE)7{9#LitCT@Rq%u`#smT?? zp0QX}uWXyJuk=utr&yGdhHY`!DcHMps&UtWlV{>?`e3%#n8S6$R(YU9y0;=Ud2nOhSNm{1I}mTso8#HO&u-C(#%}Js3&$!SNK(&=~0>4yW71nE-1| z^d|N5LLYfY&p?xZB~k^@YIAY4K7`XI6`myqf^3fSz*oUi&vtClWO`KAZte6+!ak88WHwv zGfoaz^Lx${xwsZ#!TC6%M%q^PIt1Tt05yo@Q&BLrmPPOWse$_EkN6@*zl&vDX;d$7 z|EWSk!$^aN2E|xQhNTQPTp+lgnJhb^w;x!+w z)bEaRZu;bO#G`w>=0gDH8&X%&@4ZPZAc)zb2iU{<1>K!_SEJ3D%mdm_6Q_8l=xeDcUR=6MQ(!Dh+(WM1t;i z7h%@cKOjQ^$Z@9kYg59yiiiJOTDRQ*zC4 zD5_utUXY+w)4(MUa9*a{JVdj8QsQWFhUP+5+Dqqt_XEPD5nv=-|BzKxD&o8p&j`@G zF}BMzZYtqyeYt=xe)Hx5Cw<2;!K0P%x)K!QB{ z{JQ)?Vqj4*egQ5JNDKr5l{r=Xi-DVmt&@Gw|8F33_LKV2;MqTB(DAVM4Y2aI1IXBV hSlcnGxLG;ay|%Nm4e}ba6Ms|!s3>U4SIb(y`47Y|2vz_9 literal 0 HcmV?d00001 diff --git a/css/images/ui-icons_cd0a0a_256x240.png b/css/images/ui-icons_cd0a0a_256x240.png new file mode 100644 index 0000000000000000000000000000000000000000..49370189231d006600b0f0c2967cad1583eba634 GIT binary patch literal 4549 zcmeHK_fr#0w@yL`gaFc{D^V0dT0jt_h7t_DO6Mg=QF;{=0tBf_mm;7byix@LktRh# z5u`-G(3BPtq!;M~LO%4)d^7il`ybqAcJ`dz*=Nq~vomMTd17R6n~8y!0RR9nX`?kw z007{b3)rHkIh!Z6b&k$9n2q$zPy_~s{~!NVK!y;wdM1!nfQf-AfQRJVvlOKeK;) zG(fA@T?GI*=cKKvX7=#=x*g1MvYr*vi+;15^~~;rE_=zlF{dxTu6`?DZK_QC?%~-~ zxXc!0w@Ia@0%SGD=VuM=t|036I5|bK3akyK@NV;ce80zy5LQw^%%g1EddqL}P7 zAP;7}p`4_>$jI<)(!NRDe~wUl2Lbz~U&xhZ6L-Mi58IN#JcL!Jb>u7w;&Pb=G?j|j z+4;j5CveHSt*{{pnG-wAV&ig>k>+1?#rZDhk&-C+Q7Ct0?m<+Z+Iv1=SjY7(0$nxq zk`*~W-I5nikKwr=c*9oDk~SS+7*M7oAkBSuy`fgUbnRO05@D;12PXct`w1wF~RUTU^h$gt=%W0m`Cw0Xb zn_s*Iw0*)iGT7-%&x*f>!&j*t1{iXhwkybGzn6Yb1ht-6`9IYEJQ)#1P^bTyNu&tz zRu;qux2t%4(*U3DBA?9rA-5ibuLB(35_6kYoAFm)34GMo`X6zo%ywrpzY_^)vF71v z)c2DilF6NXvKP}tDJWcNaLins3AqfCH1H&imJMu>z!m++EvXggAWG(8=5>-NUj^6s z--27)SG?akzYm$a3a^&4wImGm;GVa2Nl&w9g&{h=3GsU|SJ(O9RTJZ{*ZjGb%Jfc+ zX06OTMtgY|8_(2ecn@67x%Df1xi&r;S}liv1eMcq0lt3$SpBYGUl8;UV2l>c1q}p- zNE*Jp#oakW04gPa!be%*FrvFa&}TT>pnd6MHd5HfX1#5!D$Y}-)3(ZGeUFxo@PVWd zx(b&0Q5=u!Vq@MZ4w)0l^Rmf>JYzT=s75ic>B6iUVH29|ldKT3rw2 z7Gh*hjvi(>`v}{1HJ85LD;er=0;Y$u&ItTUV2(HFWfzQClC6ab%TP(LRGVTNH^`_z zA7|@bXmO(D;;V>nutl~0u>Y5ul?kfo7b`xX7D1_PWN0lf~n1=@8PLsIw}H5Zv*Rg+HW z!(_+Lw~tJl)iFnf2vU%9AO=SqUcWpXl7Ft39=a4SLB9Rx2~%QDeTJWJ4u-p5_H{2E z(|7*qVrrjs0LtTAyHoj0_fvw#4vfiwGN~ATw_yz2$U_hDuMN}wb1e65` z+Ghf(l_fEw)!FpWqsX)^N80DKc#a`~JHE;gfLs3X+|TV@M}7P--Eo^VeWU*(^W??N z%wC`l?kiP859ewE-=i}yep*Z+B|RpBm@`%o$|E}fD_5#G@C{bxlll1)Sh^i)!)}T; zQ~e*%(M(=SMwLDKjJ)4^ZW?@L9;fu|N-UJYfiyqgr3}FrI3ZV(p~B(TpguJgRv-J^ zP->VzsgxI-9H+1ZHUNZZYOsJ*I(FE1K6xJf(@39xz9W*Yw1Y@A9TMsjgDbu?Sa`7k zDq0gF)vAZWdw)bK*{dbJVpX?8Up#zp)Il~72C}OIxH%Ey(rJ{FJau zhKB8mRZN&kGCF11=fL>Ui{O#C2i4Qv6Fb&<4U6WLy{FwZENz$8MOqGIc|0$qX>8Xg z?hQ&4@OqC@?;UZ_D%SMww2q~|H%VRone1lK;(*MnK$_?14GLLQlDhpKC|ffzEDRK_ zw<_P6`tn;glyNI-sfxdd{cYPeVRA|Co#I{Lm$9>b<3KMXp(_8FZ9ex%nR=Es_Qkma z`HzI66uuh7!p~l*XLpXdU=}n5f&13L1=l#xn4@i;KuZA zqE>e8CSya7E?$6?yuEj=BHxelBq8z~51*mm%5lBl`asD(`QpvXymT%;)hYaD9sk^L ze7|@({~~P)EkFL%j{>ih5dr;g(c&b@IriTc%0&S>Vx)9SyvRFTG;7G`i$+W)o><+L zBdN>KTyZo_2 z61ldO^ivYwcOo({a4riLR4ib2E8=fP55$S&WtmszJ=_c%Dxnpr zSUBdL%^3uI$PwtUun(okVk2_=^LHgLsa9;Xr(N^$9xY6xa1sJEZp;q#?IQVFjN3$F zzXgyxFnH?1-AiIlR=1wsRFkeQo;iH7-*Q^ECAt2zvZMw_3-kIOP-CX6GSXiDMbNHC z526_CTdgzHp5d1;zSU7hZj{$2RQcA zr>7a;@6@>drtGQN@%QYUWHqposr!S*j=Hho&p7*3(4JItk~u;cmZTzm6SD z67Bm6dHgIK)NeXvEk0k(7p5u2n|AwyZ1C8mgYw1l3_hJXlea1Ks)$6?K zl|iMnSc^VFh3(rmbACHjw9 z2KQ|E!M)sDcFK?uMqvs}kl(IsKW~XRPUjQDZA5#az@aL;#dY`i@FzKzJIjP4 zDWmQKL;K+VHqEKG93SJamsHZZ8|s|n_%00Mlgl{q+Wumj>paS&jpGNJ(Nnrhr#w3O zB@^zDR~J5YjLgI44VIg&8m=OY$&t*qOqEvV=Ufma>04+ROY(@s*BZ*?v z0iPGEj9IvBR7rJrOe29RP|XUYvBqnG89jiVNk9%o0UF3h*w#_fg;`oH+%y^Z_iA!{ zr;DE}6~9s$4wwCFgG_@(?#V5M_>KA(Zka~p-TNH>Jd;kCcQz`Fs%G&hz_OEi1qfO< zFs~TkrrGT^X|->Ap>(s*N21FxTE557rpnMs(_O{B{CqaQ;k;pUC|?qRvZezu%_|*u zD%|ii-+ZA;v=Fw(efJC}!ShetYUitu!j#c)?U4vA6*EZ+CFHu@MQ!HWI!IR_+v4HV za$g6}H3Ka$*0@0SchdF}LUc0%R=bFTyf&53R-kVTjKnbegaszWQrr~OV+*Sns6MxD z&U?@AJ@UW$DknMa3`7*bg;$!3&b=n^FG7`ysp%B-%p*E_3{z_hkyBEuyG&+sa zS>K8)@OBLKer>mahzWEeE13LsoR6En+AaK1CN7TtoLkS2uk*u^CscejmI(?_1unou zh0iKyvf;U&C!yCD1H{73%1^Ru&w7luJzC)_^7|2AfmT7D0dB1jVG&rRL|`k$$E zyf@fb@2^6@L#v?~yR$L0Mxj;dr^U4Clau|=BXNQ2gpqNQQEsSb53VAjO!ebj#;HXQ zglvt>psuXUj)N*&PD3|A%#^BiZWi1ab!7rM1ZqMqPoHRMCh$|~YVl8V9(y!%pEcfB z15lO$P7VRi3XXoxX9ggRkdhTg+z^+NGLw=~kiDfKB`u0TC?F7sGS~9|Mey`?a&>w1 ze-qSphq=xOJbxmXdbWU+9qu`sI6F8!^6hn2Iy(f=Mj2>Us$(Ai E2b3c!L;wH) literal 0 HcmV?d00001 diff --git a/css/jquery-ui.min.css b/css/jquery-ui.min.css index 3c6d34c0..b0a15796 100644 --- a/css/jquery-ui.min.css +++ b/css/jquery-ui.min.css @@ -1,7 +1,7 @@ -/*! jQuery UI - v1.10.4 - 2014-04-02 +/*! jQuery UI - v1.10.4 - 2018-06-04 * http://jqueryui.com -* Includes: jquery.ui.core.css, jquery.ui.accordion.css, jquery.ui.autocomplete.css, jquery.ui.button.css, jquery.ui.datepicker.css, jquery.ui.dialog.css, jquery.ui.menu.css, jquery.ui.progressbar.css, jquery.ui.resizable.css, jquery.ui.selectable.css, jquery.ui.slider.css, jquery.ui.spinner.css, jquery.ui.tabs.css, jquery.ui.tooltip.css, jquery.ui.theme.css -* To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Trebuchet%20MS%2CTahoma%2CVerdana%2CArial%2Csans-serif&fwDefault=bold&fsDefault=1.1em&cornerRadius=4px&bgColorHeader=f6a828&bgTextureHeader=gloss_wave&bgImgOpacityHeader=35&borderColorHeader=e78f08&fcHeader=ffffff&iconColorHeader=ffffff&bgColorContent=eeeeee&bgTextureContent=highlight_soft&bgImgOpacityContent=100&borderColorContent=dddddd&fcContent=333333&iconColorContent=222222&bgColorDefault=f6f6f6&bgTextureDefault=glass&bgImgOpacityDefault=100&borderColorDefault=cccccc&fcDefault=1c94c4&iconColorDefault=ef8c08&bgColorHover=fdf5ce&bgTextureHover=glass&bgImgOpacityHover=100&borderColorHover=fbcb09&fcHover=c77405&iconColorHover=ef8c08&bgColorActive=ffffff&bgTextureActive=glass&bgImgOpacityActive=65&borderColorActive=fbd850&fcActive=eb8f00&iconColorActive=ef8c08&bgColorHighlight=ffe45c&bgTextureHighlight=highlight_soft&bgImgOpacityHighlight=75&borderColorHighlight=fed22f&fcHighlight=363636&iconColorHighlight=228ef1&bgColorError=b81900&bgTextureError=diagonals_thick&bgImgOpacityError=18&borderColorError=cd0a0a&fcError=ffffff&iconColorError=ffd27a&bgColorOverlay=666666&bgTextureOverlay=diagonals_thick&bgImgOpacityOverlay=20&opacityOverlay=50&bgColorShadow=000000&bgTextureShadow=flat&bgImgOpacityShadow=10&opacityShadow=20&thicknessShadow=5px&offsetTopShadow=-5px&offsetLeftShadow=-5px&cornerRadiusShadow=5px -* Copyright 2014 jQuery Foundation and other contributors; Licensed MIT */ +* Includes: jquery.ui.core.css, jquery.ui.resizable.css, jquery.ui.selectable.css, jquery.ui.accordion.css, jquery.ui.autocomplete.css, jquery.ui.button.css, jquery.ui.datepicker.css, jquery.ui.dialog.css, jquery.ui.menu.css, jquery.ui.progressbar.css, jquery.ui.slider.css, jquery.ui.spinner.css, jquery.ui.tabs.css, jquery.ui.tooltip.css, jquery.ui.theme.css +* To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Verdana%2CArial%2Csans-serif&fwDefault=normal&fsDefault=1.1em&cornerRadius=4px&bgColorHeader=cccccc&bgTextureHeader=highlight_soft&bgImgOpacityHeader=75&borderColorHeader=aaaaaa&fcHeader=222222&iconColorHeader=222222&bgColorContent=ffffff&bgTextureContent=flat&bgImgOpacityContent=75&borderColorContent=aaaaaa&fcContent=222222&iconColorContent=222222&bgColorDefault=e6e6e6&bgTextureDefault=glass&bgImgOpacityDefault=75&borderColorDefault=d3d3d3&fcDefault=555555&iconColorDefault=888888&bgColorHover=dadada&bgTextureHover=glass&bgImgOpacityHover=75&borderColorHover=999999&fcHover=212121&iconColorHover=454545&bgColorActive=ffffff&bgTextureActive=glass&bgImgOpacityActive=65&borderColorActive=aaaaaa&fcActive=212121&iconColorActive=454545&bgColorHighlight=fbf9ee&bgTextureHighlight=glass&bgImgOpacityHighlight=55&borderColorHighlight=fcefa1&fcHighlight=363636&iconColorHighlight=2e83ff&bgColorError=fef1ec&bgTextureError=glass&bgImgOpacityError=95&borderColorError=cd0a0a&fcError=cd0a0a&iconColorError=cd0a0a&bgColorOverlay=aaaaaa&bgTextureOverlay=flat&bgImgOpacityOverlay=0&opacityOverlay=30&bgColorShadow=aaaaaa&bgTextureShadow=flat&bgImgOpacityShadow=0&opacityShadow=30&thicknessShadow=8px&offsetTopShadow=-8px&offsetLeftShadow=-8px&cornerRadiusShadow=8px +* Copyright jQuery Foundation and other contributors; Licensed MIT */ -.ui-helper-hidden{display:none}.ui-helper-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.ui-helper-reset{margin:0;padding:0;border:0;outline:0;line-height:1.3;text-decoration:none;font-size:100%;list-style:none}.ui-helper-clearfix:before,.ui-helper-clearfix:after{content:"";display:table;border-collapse:collapse}.ui-helper-clearfix:after{clear:both}.ui-helper-clearfix{min-height:0}.ui-helper-zfix{width:100%;height:100%;top:0;left:0;position:absolute;opacity:0;filter:Alpha(Opacity=0)}.ui-front{z-index:100}.ui-state-disabled{cursor:default!important}.ui-icon{display:block;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat}.ui-widget-overlay{position:fixed;top:0;left:0;width:100%;height:100%}.ui-accordion .ui-accordion-header{display:block;cursor:pointer;position:relative;margin-top:2px;padding:.5em .5em .5em .7em;min-height:0}.ui-accordion .ui-accordion-icons{padding-left:2.2em}.ui-accordion .ui-accordion-noicons{padding-left:.7em}.ui-accordion .ui-accordion-icons .ui-accordion-icons{padding-left:2.2em}.ui-accordion .ui-accordion-header .ui-accordion-header-icon{position:absolute;left:.5em;top:50%;margin-top:-8px}.ui-accordion .ui-accordion-content{padding:1em 2.2em;border-top:0;overflow:auto}.ui-autocomplete{position:absolute;top:0;left:0;cursor:default}.ui-button{display:inline-block;position:relative;padding:0;line-height:normal;margin-right:.1em;cursor:pointer;vertical-align:middle;text-align:center;overflow:visible}.ui-button,.ui-button:link,.ui-button:visited,.ui-button:hover,.ui-button:active{text-decoration:none}.ui-button-icon-only{width:2.2em}button.ui-button-icon-only{width:2.4em}.ui-button-icons-only{width:3.4em}button.ui-button-icons-only{width:3.7em}.ui-button .ui-button-text{display:block;line-height:normal}.ui-button-text-only .ui-button-text{padding:.4em 1em}.ui-button-icon-only .ui-button-text,.ui-button-icons-only .ui-button-text{padding:.4em;text-indent:-9999999px}.ui-button-text-icon-primary .ui-button-text,.ui-button-text-icons .ui-button-text{padding:.4em 1em .4em 2.1em}.ui-button-text-icon-secondary .ui-button-text,.ui-button-text-icons .ui-button-text{padding:.4em 2.1em .4em 1em}.ui-button-text-icons .ui-button-text{padding-left:2.1em;padding-right:2.1em}input.ui-button{padding:.4em 1em}.ui-button-icon-only .ui-icon,.ui-button-text-icon-primary .ui-icon,.ui-button-text-icon-secondary .ui-icon,.ui-button-text-icons .ui-icon,.ui-button-icons-only .ui-icon{position:absolute;top:50%;margin-top:-8px}.ui-button-icon-only .ui-icon{left:50%;margin-left:-8px}.ui-button-text-icon-primary .ui-button-icon-primary,.ui-button-text-icons .ui-button-icon-primary,.ui-button-icons-only .ui-button-icon-primary{left:.5em}.ui-button-text-icon-secondary .ui-button-icon-secondary,.ui-button-text-icons .ui-button-icon-secondary,.ui-button-icons-only .ui-button-icon-secondary{right:.5em}.ui-buttonset{margin-right:7px}.ui-buttonset .ui-button{margin-left:0;margin-right:-.3em}input.ui-button::-moz-focus-inner,button.ui-button::-moz-focus-inner{border:0;padding:0}.ui-datepicker{width:17em;padding:.2em .2em 0;display:none}.ui-datepicker .ui-datepicker-header{position:relative;padding:.2em 0}.ui-datepicker .ui-datepicker-prev,.ui-datepicker .ui-datepicker-next{position:absolute;top:2px;width:1.8em;height:1.8em}.ui-datepicker .ui-datepicker-prev-hover,.ui-datepicker .ui-datepicker-next-hover{top:1px}.ui-datepicker .ui-datepicker-prev{left:2px}.ui-datepicker .ui-datepicker-next{right:2px}.ui-datepicker .ui-datepicker-prev-hover{left:1px}.ui-datepicker .ui-datepicker-next-hover{right:1px}.ui-datepicker .ui-datepicker-prev span,.ui-datepicker .ui-datepicker-next span{display:block;position:absolute;left:50%;margin-left:-8px;top:50%;margin-top:-8px}.ui-datepicker .ui-datepicker-title{margin:0 2.3em;line-height:1.8em;text-align:center}.ui-datepicker .ui-datepicker-title select{font-size:1em;margin:1px 0}.ui-datepicker select.ui-datepicker-month,.ui-datepicker select.ui-datepicker-year{width:49%}.ui-datepicker table{width:100%;font-size:.9em;border-collapse:collapse;margin:0 0 .4em}.ui-datepicker th{padding:.7em .3em;text-align:center;font-weight:bold;border:0}.ui-datepicker td{border:0;padding:1px}.ui-datepicker td span,.ui-datepicker td a{display:block;padding:.2em;text-align:right;text-decoration:none}.ui-datepicker .ui-datepicker-buttonpane{background-image:none;margin:.7em 0 0 0;padding:0 .2em;border-left:0;border-right:0;border-bottom:0}.ui-datepicker .ui-datepicker-buttonpane button{float:right;margin:.5em .2em .4em;cursor:pointer;padding:.2em .6em .3em .6em;width:auto;overflow:visible}.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current{float:left}.ui-datepicker.ui-datepicker-multi{width:auto}.ui-datepicker-multi .ui-datepicker-group{float:left}.ui-datepicker-multi .ui-datepicker-group table{width:95%;margin:0 auto .4em}.ui-datepicker-multi-2 .ui-datepicker-group{width:50%}.ui-datepicker-multi-3 .ui-datepicker-group{width:33.3%}.ui-datepicker-multi-4 .ui-datepicker-group{width:25%}.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header,.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header{border-left-width:0}.ui-datepicker-multi .ui-datepicker-buttonpane{clear:left}.ui-datepicker-row-break{clear:both;width:100%;font-size:0}.ui-datepicker-rtl{direction:rtl}.ui-datepicker-rtl .ui-datepicker-prev{right:2px;left:auto}.ui-datepicker-rtl .ui-datepicker-next{left:2px;right:auto}.ui-datepicker-rtl .ui-datepicker-prev:hover{right:1px;left:auto}.ui-datepicker-rtl .ui-datepicker-next:hover{left:1px;right:auto}.ui-datepicker-rtl .ui-datepicker-buttonpane{clear:right}.ui-datepicker-rtl .ui-datepicker-buttonpane button{float:left}.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current,.ui-datepicker-rtl .ui-datepicker-group{float:right}.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header,.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header{border-right-width:0;border-left-width:1px}.ui-dialog{overflow:hidden;position:absolute;top:0;left:0;padding:.2em;outline:0}.ui-dialog .ui-dialog-titlebar{padding:.4em 1em;position:relative}.ui-dialog .ui-dialog-title{float:left;margin:.1em 0;white-space:nowrap;width:90%;overflow:hidden;text-overflow:ellipsis}.ui-dialog .ui-dialog-titlebar-close{position:absolute;right:.3em;top:50%;width:20px;margin:-10px 0 0 0;padding:1px;height:20px}.ui-dialog .ui-dialog-content{position:relative;border:0;padding:.5em 1em;background:none;overflow:auto}.ui-dialog .ui-dialog-buttonpane{text-align:left;border-width:1px 0 0 0;background-image:none;margin-top:.5em;padding:.3em 1em .5em .4em}.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset{float:right}.ui-dialog .ui-dialog-buttonpane button{margin:.5em .4em .5em 0;cursor:pointer}.ui-dialog .ui-resizable-se{width:12px;height:12px;right:-5px;bottom:-5px;background-position:16px 16px}.ui-draggable .ui-dialog-titlebar{cursor:move}.ui-menu{list-style:none;padding:2px;margin:0;display:block;outline:none}.ui-menu .ui-menu{margin-top:-3px;position:absolute}.ui-menu .ui-menu-item{margin:0;padding:0;width:100%;list-style-image:url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)}.ui-menu .ui-menu-divider{margin:5px -2px 5px -2px;height:0;font-size:0;line-height:0;border-width:1px 0 0 0}.ui-menu .ui-menu-item a{text-decoration:none;display:block;padding:2px .4em;line-height:1.5;min-height:0;font-weight:normal}.ui-menu .ui-menu-item a.ui-state-focus,.ui-menu .ui-menu-item a.ui-state-active{font-weight:normal;margin:-1px}.ui-menu .ui-state-disabled{font-weight:normal;margin:.4em 0 .2em;line-height:1.5}.ui-menu .ui-state-disabled a{cursor:default}.ui-menu-icons{position:relative}.ui-menu-icons .ui-menu-item a{position:relative;padding-left:2em}.ui-menu .ui-icon{position:absolute;top:.2em;left:.2em}.ui-menu .ui-menu-icon{position:static;float:right}.ui-progressbar{height:2em;text-align:left;overflow:hidden}.ui-progressbar .ui-progressbar-value{margin:-1px;height:100%}.ui-progressbar .ui-progressbar-overlay{background:url("images/animated-overlay.gif");height:100%;filter:alpha(opacity=25);opacity:0.25}.ui-progressbar-indeterminate .ui-progressbar-value{background-image:none}.ui-resizable{position:relative}.ui-resizable-handle{position:absolute;font-size:0.1px;display:block}.ui-resizable-disabled .ui-resizable-handle,.ui-resizable-autohide .ui-resizable-handle{display:none}.ui-resizable-n{cursor:n-resize;height:7px;width:100%;top:-5px;left:0}.ui-resizable-s{cursor:s-resize;height:7px;width:100%;bottom:-5px;left:0}.ui-resizable-e{cursor:e-resize;width:7px;right:-5px;top:0;height:100%}.ui-resizable-w{cursor:w-resize;width:7px;left:-5px;top:0;height:100%}.ui-resizable-se{cursor:se-resize;width:12px;height:12px;right:1px;bottom:1px}.ui-resizable-sw{cursor:sw-resize;width:9px;height:9px;left:-5px;bottom:-5px}.ui-resizable-nw{cursor:nw-resize;width:9px;height:9px;left:-5px;top:-5px}.ui-resizable-ne{cursor:ne-resize;width:9px;height:9px;right:-5px;top:-5px}.ui-selectable-helper{position:absolute;z-index:100;border:1px dotted black}.ui-slider{position:relative;text-align:left}.ui-slider .ui-slider-handle{position:absolute;z-index:2;width:1.2em;height:1.2em;cursor:default}.ui-slider .ui-slider-range{position:absolute;z-index:1;font-size:.7em;display:block;border:0;background-position:0 0}.ui-slider.ui-state-disabled .ui-slider-handle,.ui-slider.ui-state-disabled .ui-slider-range{filter:inherit}.ui-slider-horizontal{height:.8em}.ui-slider-horizontal .ui-slider-handle{top:-.3em;margin-left:-.6em}.ui-slider-horizontal .ui-slider-range{top:0;height:100%}.ui-slider-horizontal .ui-slider-range-min{left:0}.ui-slider-horizontal .ui-slider-range-max{right:0}.ui-slider-vertical{width:.8em;height:100px}.ui-slider-vertical .ui-slider-handle{left:-.3em;margin-left:0;margin-bottom:-.6em}.ui-slider-vertical .ui-slider-range{left:0;width:100%}.ui-slider-vertical .ui-slider-range-min{bottom:0}.ui-slider-vertical .ui-slider-range-max{top:0}.ui-spinner{position:relative;display:inline-block;overflow:hidden;padding:0;vertical-align:middle}.ui-spinner-input{border:none;background:none;color:inherit;padding:0;margin:.2em 0;vertical-align:middle;margin-left:.4em;margin-right:22px}.ui-spinner-button{width:16px;height:50%;font-size:.5em;padding:0;margin:0;text-align:center;position:absolute;cursor:default;display:block;overflow:hidden;right:0}.ui-spinner a.ui-spinner-button{border-top:none;border-bottom:none;border-right:none}.ui-spinner .ui-icon{position:absolute;margin-top:-8px;top:50%;left:0}.ui-spinner-up{top:0}.ui-spinner-down{bottom:0}.ui-spinner .ui-icon-triangle-1-s{background-position:-65px -16px}.ui-tabs{position:relative;padding:.2em}.ui-tabs .ui-tabs-nav{margin:0;padding:.2em .2em 0}.ui-tabs .ui-tabs-nav li{list-style:none;float:left;position:relative;top:0;margin:1px .2em 0 0;border-bottom-width:0;padding:0;white-space:nowrap}.ui-tabs .ui-tabs-nav .ui-tabs-anchor{float:left;padding:.5em 1em;text-decoration:none}.ui-tabs .ui-tabs-nav li.ui-tabs-active{margin-bottom:-1px;padding-bottom:1px}.ui-tabs .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor,.ui-tabs .ui-tabs-nav li.ui-state-disabled .ui-tabs-anchor,.ui-tabs .ui-tabs-nav li.ui-tabs-loading .ui-tabs-anchor{cursor:text}.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor{cursor:pointer}.ui-tabs .ui-tabs-panel{display:block;border-width:0;padding:1em 1.4em;background:none}.ui-tooltip{padding:8px;position:absolute;z-index:9999;max-width:300px;-webkit-box-shadow:0 0 5px #aaa;box-shadow:0 0 5px #aaa}body .ui-tooltip{border-width:2px}.ui-widget{font-family:Trebuchet MS,Tahoma,Verdana,Arial,sans-serif;font-size:1.1em}.ui-widget .ui-widget{font-size:1em}.ui-widget input,.ui-widget select,.ui-widget textarea,.ui-widget button{font-family:Trebuchet MS,Tahoma,Verdana,Arial,sans-serif;font-size:1em}.ui-widget-content{border:1px solid #ddd;background:#eee url("images/ui-bg_highlight-soft_100_eeeeee_1x100.png") 50% top repeat-x;color:#333}.ui-widget-content a{color:#333}.ui-widget-header{border:1px solid #e78f08;background:#f6a828 url("images/ui-bg_gloss-wave_35_f6a828_500x100.png") 50% 50% repeat-x;color:#fff;font-weight:bold}.ui-widget-header a{color:#fff}.ui-state-default,.ui-widget-content .ui-state-default,.ui-widget-header .ui-state-default{border:1px solid #ccc;background:#f6f6f6 url("images/ui-bg_glass_100_f6f6f6_1x400.png") 50% 50% repeat-x;font-weight:bold;color:#1c94c4}.ui-state-default a,.ui-state-default a:link,.ui-state-default a:visited{color:#1c94c4;text-decoration:none}.ui-state-hover,.ui-widget-content .ui-state-hover,.ui-widget-header .ui-state-hover,.ui-state-focus,.ui-widget-content .ui-state-focus,.ui-widget-header .ui-state-focus{border:1px solid #fbcb09;background:#fdf5ce url("images/ui-bg_glass_100_fdf5ce_1x400.png") 50% 50% repeat-x;font-weight:bold;color:#c77405}.ui-state-hover a,.ui-state-hover a:hover,.ui-state-hover a:link,.ui-state-hover a:visited,.ui-state-focus a,.ui-state-focus a:hover,.ui-state-focus a:link,.ui-state-focus a:visited{color:#c77405;text-decoration:none}.ui-state-active,.ui-widget-content .ui-state-active,.ui-widget-header .ui-state-active{border:1px solid #fbd850;background:#fff url("images/ui-bg_glass_65_ffffff_1x400.png") 50% 50% repeat-x;font-weight:bold;color:#eb8f00}.ui-state-active a,.ui-state-active a:link,.ui-state-active a:visited{color:#eb8f00;text-decoration:none}.ui-state-highlight,.ui-widget-content .ui-state-highlight,.ui-widget-header .ui-state-highlight{border:1px solid #fed22f;background:#ffe45c url("images/ui-bg_highlight-soft_75_ffe45c_1x100.png") 50% top repeat-x;color:#363636}.ui-state-highlight a,.ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a{color:#363636}.ui-state-error,.ui-widget-content .ui-state-error,.ui-widget-header .ui-state-error{border:1px solid #cd0a0a;background:#b81900 url("images/ui-bg_diagonals-thick_18_b81900_40x40.png") 50% 50% repeat;color:#fff}.ui-state-error a,.ui-widget-content .ui-state-error a,.ui-widget-header .ui-state-error a{color:#fff}.ui-state-error-text,.ui-widget-content .ui-state-error-text,.ui-widget-header .ui-state-error-text{color:#fff}.ui-priority-primary,.ui-widget-content .ui-priority-primary,.ui-widget-header .ui-priority-primary{font-weight:bold}.ui-priority-secondary,.ui-widget-content .ui-priority-secondary,.ui-widget-header .ui-priority-secondary{opacity:.7;filter:Alpha(Opacity=70);font-weight:normal}.ui-state-disabled,.ui-widget-content .ui-state-disabled,.ui-widget-header .ui-state-disabled{opacity:.35;filter:Alpha(Opacity=35);background-image:none}.ui-state-disabled .ui-icon{filter:Alpha(Opacity=35)}.ui-icon{width:16px;height:16px}.ui-icon,.ui-widget-content .ui-icon{background-image:url("images/ui-icons_222222_256x240.png")}.ui-widget-header .ui-icon{background-image:url("images/ui-icons_ffffff_256x240.png")}.ui-state-default .ui-icon{background-image:url("images/ui-icons_ef8c08_256x240.png")}.ui-state-hover .ui-icon,.ui-state-focus .ui-icon{background-image:url("images/ui-icons_ef8c08_256x240.png")}.ui-state-active .ui-icon{background-image:url("images/ui-icons_ef8c08_256x240.png")}.ui-state-highlight .ui-icon{background-image:url("images/ui-icons_228ef1_256x240.png")}.ui-state-error .ui-icon,.ui-state-error-text .ui-icon{background-image:url("images/ui-icons_ffd27a_256x240.png")}.ui-icon-blank{background-position:16px 16px}.ui-icon-carat-1-n{background-position:0 0}.ui-icon-carat-1-ne{background-position:-16px 0}.ui-icon-carat-1-e{background-position:-32px 0}.ui-icon-carat-1-se{background-position:-48px 0}.ui-icon-carat-1-s{background-position:-64px 0}.ui-icon-carat-1-sw{background-position:-80px 0}.ui-icon-carat-1-w{background-position:-96px 0}.ui-icon-carat-1-nw{background-position:-112px 0}.ui-icon-carat-2-n-s{background-position:-128px 0}.ui-icon-carat-2-e-w{background-position:-144px 0}.ui-icon-triangle-1-n{background-position:0 -16px}.ui-icon-triangle-1-ne{background-position:-16px -16px}.ui-icon-triangle-1-e{background-position:-32px -16px}.ui-icon-triangle-1-se{background-position:-48px -16px}.ui-icon-triangle-1-s{background-position:-64px -16px}.ui-icon-triangle-1-sw{background-position:-80px -16px}.ui-icon-triangle-1-w{background-position:-96px -16px}.ui-icon-triangle-1-nw{background-position:-112px -16px}.ui-icon-triangle-2-n-s{background-position:-128px -16px}.ui-icon-triangle-2-e-w{background-position:-144px -16px}.ui-icon-arrow-1-n{background-position:0 -32px}.ui-icon-arrow-1-ne{background-position:-16px -32px}.ui-icon-arrow-1-e{background-position:-32px -32px}.ui-icon-arrow-1-se{background-position:-48px -32px}.ui-icon-arrow-1-s{background-position:-64px -32px}.ui-icon-arrow-1-sw{background-position:-80px -32px}.ui-icon-arrow-1-w{background-position:-96px -32px}.ui-icon-arrow-1-nw{background-position:-112px -32px}.ui-icon-arrow-2-n-s{background-position:-128px -32px}.ui-icon-arrow-2-ne-sw{background-position:-144px -32px}.ui-icon-arrow-2-e-w{background-position:-160px -32px}.ui-icon-arrow-2-se-nw{background-position:-176px -32px}.ui-icon-arrowstop-1-n{background-position:-192px -32px}.ui-icon-arrowstop-1-e{background-position:-208px -32px}.ui-icon-arrowstop-1-s{background-position:-224px -32px}.ui-icon-arrowstop-1-w{background-position:-240px -32px}.ui-icon-arrowthick-1-n{background-position:0 -48px}.ui-icon-arrowthick-1-ne{background-position:-16px -48px}.ui-icon-arrowthick-1-e{background-position:-32px -48px}.ui-icon-arrowthick-1-se{background-position:-48px -48px}.ui-icon-arrowthick-1-s{background-position:-64px -48px}.ui-icon-arrowthick-1-sw{background-position:-80px -48px}.ui-icon-arrowthick-1-w{background-position:-96px -48px}.ui-icon-arrowthick-1-nw{background-position:-112px -48px}.ui-icon-arrowthick-2-n-s{background-position:-128px -48px}.ui-icon-arrowthick-2-ne-sw{background-position:-144px -48px}.ui-icon-arrowthick-2-e-w{background-position:-160px -48px}.ui-icon-arrowthick-2-se-nw{background-position:-176px -48px}.ui-icon-arrowthickstop-1-n{background-position:-192px -48px}.ui-icon-arrowthickstop-1-e{background-position:-208px -48px}.ui-icon-arrowthickstop-1-s{background-position:-224px -48px}.ui-icon-arrowthickstop-1-w{background-position:-240px -48px}.ui-icon-arrowreturnthick-1-w{background-position:0 -64px}.ui-icon-arrowreturnthick-1-n{background-position:-16px -64px}.ui-icon-arrowreturnthick-1-e{background-position:-32px -64px}.ui-icon-arrowreturnthick-1-s{background-position:-48px -64px}.ui-icon-arrowreturn-1-w{background-position:-64px -64px}.ui-icon-arrowreturn-1-n{background-position:-80px -64px}.ui-icon-arrowreturn-1-e{background-position:-96px -64px}.ui-icon-arrowreturn-1-s{background-position:-112px -64px}.ui-icon-arrowrefresh-1-w{background-position:-128px -64px}.ui-icon-arrowrefresh-1-n{background-position:-144px -64px}.ui-icon-arrowrefresh-1-e{background-position:-160px -64px}.ui-icon-arrowrefresh-1-s{background-position:-176px -64px}.ui-icon-arrow-4{background-position:0 -80px}.ui-icon-arrow-4-diag{background-position:-16px -80px}.ui-icon-extlink{background-position:-32px -80px}.ui-icon-newwin{background-position:-48px -80px}.ui-icon-refresh{background-position:-64px -80px}.ui-icon-shuffle{background-position:-80px -80px}.ui-icon-transfer-e-w{background-position:-96px -80px}.ui-icon-transferthick-e-w{background-position:-112px -80px}.ui-icon-folder-collapsed{background-position:0 -96px}.ui-icon-folder-open{background-position:-16px -96px}.ui-icon-document{background-position:-32px -96px}.ui-icon-document-b{background-position:-48px -96px}.ui-icon-note{background-position:-64px -96px}.ui-icon-mail-closed{background-position:-80px -96px}.ui-icon-mail-open{background-position:-96px -96px}.ui-icon-suitcase{background-position:-112px -96px}.ui-icon-comment{background-position:-128px -96px}.ui-icon-person{background-position:-144px -96px}.ui-icon-print{background-position:-160px -96px}.ui-icon-trash{background-position:-176px -96px}.ui-icon-locked{background-position:-192px -96px}.ui-icon-unlocked{background-position:-208px -96px}.ui-icon-bookmark{background-position:-224px -96px}.ui-icon-tag{background-position:-240px -96px}.ui-icon-home{background-position:0 -112px}.ui-icon-flag{background-position:-16px -112px}.ui-icon-calendar{background-position:-32px -112px}.ui-icon-cart{background-position:-48px -112px}.ui-icon-pencil{background-position:-64px -112px}.ui-icon-clock{background-position:-80px -112px}.ui-icon-disk{background-position:-96px -112px}.ui-icon-calculator{background-position:-112px -112px}.ui-icon-zoomin{background-position:-128px -112px}.ui-icon-zoomout{background-position:-144px -112px}.ui-icon-search{background-position:-160px -112px}.ui-icon-wrench{background-position:-176px -112px}.ui-icon-gear{background-position:-192px -112px}.ui-icon-heart{background-position:-208px -112px}.ui-icon-star{background-position:-224px -112px}.ui-icon-link{background-position:-240px -112px}.ui-icon-cancel{background-position:0 -128px}.ui-icon-plus{background-position:-16px -128px}.ui-icon-plusthick{background-position:-32px -128px}.ui-icon-minus{background-position:-48px -128px}.ui-icon-minusthick{background-position:-64px -128px}.ui-icon-close{background-position:-80px -128px}.ui-icon-closethick{background-position:-96px -128px}.ui-icon-key{background-position:-112px -128px}.ui-icon-lightbulb{background-position:-128px -128px}.ui-icon-scissors{background-position:-144px -128px}.ui-icon-clipboard{background-position:-160px -128px}.ui-icon-copy{background-position:-176px -128px}.ui-icon-contact{background-position:-192px -128px}.ui-icon-image{background-position:-208px -128px}.ui-icon-video{background-position:-224px -128px}.ui-icon-script{background-position:-240px -128px}.ui-icon-alert{background-position:0 -144px}.ui-icon-info{background-position:-16px -144px}.ui-icon-notice{background-position:-32px -144px}.ui-icon-help{background-position:-48px -144px}.ui-icon-check{background-position:-64px -144px}.ui-icon-bullet{background-position:-80px -144px}.ui-icon-radio-on{background-position:-96px -144px}.ui-icon-radio-off{background-position:-112px -144px}.ui-icon-pin-w{background-position:-128px -144px}.ui-icon-pin-s{background-position:-144px -144px}.ui-icon-play{background-position:0 -160px}.ui-icon-pause{background-position:-16px -160px}.ui-icon-seek-next{background-position:-32px -160px}.ui-icon-seek-prev{background-position:-48px -160px}.ui-icon-seek-end{background-position:-64px -160px}.ui-icon-seek-start{background-position:-80px -160px}.ui-icon-seek-first{background-position:-80px -160px}.ui-icon-stop{background-position:-96px -160px}.ui-icon-eject{background-position:-112px -160px}.ui-icon-volume-off{background-position:-128px -160px}.ui-icon-volume-on{background-position:-144px -160px}.ui-icon-power{background-position:0 -176px}.ui-icon-signal-diag{background-position:-16px -176px}.ui-icon-signal{background-position:-32px -176px}.ui-icon-battery-0{background-position:-48px -176px}.ui-icon-battery-1{background-position:-64px -176px}.ui-icon-battery-2{background-position:-80px -176px}.ui-icon-battery-3{background-position:-96px -176px}.ui-icon-circle-plus{background-position:0 -192px}.ui-icon-circle-minus{background-position:-16px -192px}.ui-icon-circle-close{background-position:-32px -192px}.ui-icon-circle-triangle-e{background-position:-48px -192px}.ui-icon-circle-triangle-s{background-position:-64px -192px}.ui-icon-circle-triangle-w{background-position:-80px -192px}.ui-icon-circle-triangle-n{background-position:-96px -192px}.ui-icon-circle-arrow-e{background-position:-112px -192px}.ui-icon-circle-arrow-s{background-position:-128px -192px}.ui-icon-circle-arrow-w{background-position:-144px -192px}.ui-icon-circle-arrow-n{background-position:-160px -192px}.ui-icon-circle-zoomin{background-position:-176px -192px}.ui-icon-circle-zoomout{background-position:-192px -192px}.ui-icon-circle-check{background-position:-208px -192px}.ui-icon-circlesmall-plus{background-position:0 -208px}.ui-icon-circlesmall-minus{background-position:-16px -208px}.ui-icon-circlesmall-close{background-position:-32px -208px}.ui-icon-squaresmall-plus{background-position:-48px -208px}.ui-icon-squaresmall-minus{background-position:-64px -208px}.ui-icon-squaresmall-close{background-position:-80px -208px}.ui-icon-grip-dotted-vertical{background-position:0 -224px}.ui-icon-grip-dotted-horizontal{background-position:-16px -224px}.ui-icon-grip-solid-vertical{background-position:-32px -224px}.ui-icon-grip-solid-horizontal{background-position:-48px -224px}.ui-icon-gripsmall-diagonal-se{background-position:-64px -224px}.ui-icon-grip-diagonal-se{background-position:-80px -224px}.ui-corner-all,.ui-corner-top,.ui-corner-left,.ui-corner-tl{border-top-left-radius:4px}.ui-corner-all,.ui-corner-top,.ui-corner-right,.ui-corner-tr{border-top-right-radius:4px}.ui-corner-all,.ui-corner-bottom,.ui-corner-left,.ui-corner-bl{border-bottom-left-radius:4px}.ui-corner-all,.ui-corner-bottom,.ui-corner-right,.ui-corner-br{border-bottom-right-radius:4px}.ui-widget-overlay{background:#666 url("images/ui-bg_diagonals-thick_20_666666_40x40.png") 50% 50% repeat;opacity:.5;filter:Alpha(Opacity=50)}.ui-widget-shadow{margin:-5px 0 0 -5px;padding:5px;background:#000 url("images/ui-bg_flat_10_000000_40x100.png") 50% 50% repeat-x;opacity:.2;filter:Alpha(Opacity=20);border-radius:5px} \ No newline at end of file +.ui-helper-hidden{display:none}.ui-helper-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.ui-helper-reset{margin:0;padding:0;border:0;outline:0;line-height:1.3;text-decoration:none;font-size:100%;list-style:none}.ui-helper-clearfix:before,.ui-helper-clearfix:after{content:"";display:table;border-collapse:collapse}.ui-helper-clearfix:after{clear:both}.ui-helper-clearfix{min-height:0}.ui-helper-zfix{width:100%;height:100%;top:0;left:0;position:absolute;opacity:0;filter:Alpha(Opacity=0)}.ui-front{z-index:100}.ui-state-disabled{cursor:default!important}.ui-icon{display:block;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat}.ui-widget-overlay{position:fixed;top:0;left:0;width:100%;height:100%}.ui-resizable{position:relative}.ui-resizable-handle{position:absolute;font-size:0.1px;display:block}.ui-resizable-disabled .ui-resizable-handle,.ui-resizable-autohide .ui-resizable-handle{display:none}.ui-resizable-n{cursor:n-resize;height:7px;width:100%;top:-5px;left:0}.ui-resizable-s{cursor:s-resize;height:7px;width:100%;bottom:-5px;left:0}.ui-resizable-e{cursor:e-resize;width:7px;right:-5px;top:0;height:100%}.ui-resizable-w{cursor:w-resize;width:7px;left:-5px;top:0;height:100%}.ui-resizable-se{cursor:se-resize;width:12px;height:12px;right:1px;bottom:1px}.ui-resizable-sw{cursor:sw-resize;width:9px;height:9px;left:-5px;bottom:-5px}.ui-resizable-nw{cursor:nw-resize;width:9px;height:9px;left:-5px;top:-5px}.ui-resizable-ne{cursor:ne-resize;width:9px;height:9px;right:-5px;top:-5px}.ui-selectable-helper{position:absolute;z-index:100;border:1px dotted black}.ui-accordion .ui-accordion-header{display:block;cursor:pointer;position:relative;margin-top:2px;padding:.5em .5em .5em .7em;min-height:0}.ui-accordion .ui-accordion-icons{padding-left:2.2em}.ui-accordion .ui-accordion-noicons{padding-left:.7em}.ui-accordion .ui-accordion-icons .ui-accordion-icons{padding-left:2.2em}.ui-accordion .ui-accordion-header .ui-accordion-header-icon{position:absolute;left:.5em;top:50%;margin-top:-8px}.ui-accordion .ui-accordion-content{padding:1em 2.2em;border-top:0;overflow:auto}.ui-autocomplete{position:absolute;top:0;left:0;cursor:default}.ui-button{display:inline-block;position:relative;padding:0;line-height:normal;margin-right:.1em;cursor:pointer;vertical-align:middle;text-align:center;overflow:visible}.ui-button,.ui-button:link,.ui-button:visited,.ui-button:hover,.ui-button:active{text-decoration:none}.ui-button-icon-only{width:2.2em}button.ui-button-icon-only{width:2.4em}.ui-button-icons-only{width:3.4em}button.ui-button-icons-only{width:3.7em}.ui-button .ui-button-text{display:block;line-height:normal}.ui-button-text-only .ui-button-text{padding:.4em 1em}.ui-button-icon-only .ui-button-text,.ui-button-icons-only .ui-button-text{padding:.4em;text-indent:-9999999px}.ui-button-text-icon-primary .ui-button-text,.ui-button-text-icons .ui-button-text{padding:.4em 1em .4em 2.1em}.ui-button-text-icon-secondary .ui-button-text,.ui-button-text-icons .ui-button-text{padding:.4em 2.1em .4em 1em}.ui-button-text-icons .ui-button-text{padding-left:2.1em;padding-right:2.1em}input.ui-button{padding:.4em 1em}.ui-button-icon-only .ui-icon,.ui-button-text-icon-primary .ui-icon,.ui-button-text-icon-secondary .ui-icon,.ui-button-text-icons .ui-icon,.ui-button-icons-only .ui-icon{position:absolute;top:50%;margin-top:-8px}.ui-button-icon-only .ui-icon{left:50%;margin-left:-8px}.ui-button-text-icon-primary .ui-button-icon-primary,.ui-button-text-icons .ui-button-icon-primary,.ui-button-icons-only .ui-button-icon-primary{left:.5em}.ui-button-text-icon-secondary .ui-button-icon-secondary,.ui-button-text-icons .ui-button-icon-secondary,.ui-button-icons-only .ui-button-icon-secondary{right:.5em}.ui-buttonset{margin-right:7px}.ui-buttonset .ui-button{margin-left:0;margin-right:-.3em}input.ui-button::-moz-focus-inner,button.ui-button::-moz-focus-inner{border:0;padding:0}.ui-datepicker{width:17em;padding:.2em .2em 0;display:none}.ui-datepicker .ui-datepicker-header{position:relative;padding:.2em 0}.ui-datepicker .ui-datepicker-prev,.ui-datepicker .ui-datepicker-next{position:absolute;top:2px;width:1.8em;height:1.8em}.ui-datepicker .ui-datepicker-prev-hover,.ui-datepicker .ui-datepicker-next-hover{top:1px}.ui-datepicker .ui-datepicker-prev{left:2px}.ui-datepicker .ui-datepicker-next{right:2px}.ui-datepicker .ui-datepicker-prev-hover{left:1px}.ui-datepicker .ui-datepicker-next-hover{right:1px}.ui-datepicker .ui-datepicker-prev span,.ui-datepicker .ui-datepicker-next span{display:block;position:absolute;left:50%;margin-left:-8px;top:50%;margin-top:-8px}.ui-datepicker .ui-datepicker-title{margin:0 2.3em;line-height:1.8em;text-align:center}.ui-datepicker .ui-datepicker-title select{font-size:1em;margin:1px 0}.ui-datepicker select.ui-datepicker-month,.ui-datepicker select.ui-datepicker-year{width:49%}.ui-datepicker table{width:100%;font-size:.9em;border-collapse:collapse;margin:0 0 .4em}.ui-datepicker th{padding:.7em .3em;text-align:center;font-weight:bold;border:0}.ui-datepicker td{border:0;padding:1px}.ui-datepicker td span,.ui-datepicker td a{display:block;padding:.2em;text-align:right;text-decoration:none}.ui-datepicker .ui-datepicker-buttonpane{background-image:none;margin:.7em 0 0 0;padding:0 .2em;border-left:0;border-right:0;border-bottom:0}.ui-datepicker .ui-datepicker-buttonpane button{float:right;margin:.5em .2em .4em;cursor:pointer;padding:.2em .6em .3em .6em;width:auto;overflow:visible}.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current{float:left}.ui-datepicker.ui-datepicker-multi{width:auto}.ui-datepicker-multi .ui-datepicker-group{float:left}.ui-datepicker-multi .ui-datepicker-group table{width:95%;margin:0 auto .4em}.ui-datepicker-multi-2 .ui-datepicker-group{width:50%}.ui-datepicker-multi-3 .ui-datepicker-group{width:33.3%}.ui-datepicker-multi-4 .ui-datepicker-group{width:25%}.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header,.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header{border-left-width:0}.ui-datepicker-multi .ui-datepicker-buttonpane{clear:left}.ui-datepicker-row-break{clear:both;width:100%;font-size:0}.ui-datepicker-rtl{direction:rtl}.ui-datepicker-rtl .ui-datepicker-prev{right:2px;left:auto}.ui-datepicker-rtl .ui-datepicker-next{left:2px;right:auto}.ui-datepicker-rtl .ui-datepicker-prev:hover{right:1px;left:auto}.ui-datepicker-rtl .ui-datepicker-next:hover{left:1px;right:auto}.ui-datepicker-rtl .ui-datepicker-buttonpane{clear:right}.ui-datepicker-rtl .ui-datepicker-buttonpane button{float:left}.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current,.ui-datepicker-rtl .ui-datepicker-group{float:right}.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header,.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header{border-right-width:0;border-left-width:1px}.ui-dialog{overflow:hidden;position:absolute;top:0;left:0;padding:.2em;outline:0}.ui-dialog .ui-dialog-titlebar{padding:.4em 1em;position:relative}.ui-dialog .ui-dialog-title{float:left;margin:.1em 0;white-space:nowrap;width:90%;overflow:hidden;text-overflow:ellipsis}.ui-dialog .ui-dialog-titlebar-close{position:absolute;right:.3em;top:50%;width:20px;margin:-10px 0 0 0;padding:1px;height:20px}.ui-dialog .ui-dialog-content{position:relative;border:0;padding:.5em 1em;background:none;overflow:auto}.ui-dialog .ui-dialog-buttonpane{text-align:left;border-width:1px 0 0 0;background-image:none;margin-top:.5em;padding:.3em 1em .5em .4em}.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset{float:right}.ui-dialog .ui-dialog-buttonpane button{margin:.5em .4em .5em 0;cursor:pointer}.ui-dialog .ui-resizable-se{width:12px;height:12px;right:-5px;bottom:-5px;background-position:16px 16px}.ui-draggable .ui-dialog-titlebar{cursor:move}.ui-menu{list-style:none;padding:2px;margin:0;display:block;outline:none}.ui-menu .ui-menu{margin-top:-3px;position:absolute}.ui-menu .ui-menu-item{margin:0;padding:0;width:100%;list-style-image:url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)}.ui-menu .ui-menu-divider{margin:5px -2px 5px -2px;height:0;font-size:0;line-height:0;border-width:1px 0 0 0}.ui-menu .ui-menu-item a{text-decoration:none;display:block;padding:2px .4em;line-height:1.5;min-height:0;font-weight:normal}.ui-menu .ui-menu-item a.ui-state-focus,.ui-menu .ui-menu-item a.ui-state-active{font-weight:normal;margin:-1px}.ui-menu .ui-state-disabled{font-weight:normal;margin:.4em 0 .2em;line-height:1.5}.ui-menu .ui-state-disabled a{cursor:default}.ui-menu-icons{position:relative}.ui-menu-icons .ui-menu-item a{position:relative;padding-left:2em}.ui-menu .ui-icon{position:absolute;top:.2em;left:.2em}.ui-menu .ui-menu-icon{position:static;float:right}.ui-progressbar{height:2em;text-align:left;overflow:hidden}.ui-progressbar .ui-progressbar-value{margin:-1px;height:100%}.ui-progressbar .ui-progressbar-overlay{background:url("images/animated-overlay.gif");height:100%;filter:alpha(opacity=25);opacity:0.25}.ui-progressbar-indeterminate .ui-progressbar-value{background-image:none}.ui-slider{position:relative;text-align:left}.ui-slider .ui-slider-handle{position:absolute;z-index:2;width:1.2em;height:1.2em;cursor:default}.ui-slider .ui-slider-range{position:absolute;z-index:1;font-size:.7em;display:block;border:0;background-position:0 0}.ui-slider.ui-state-disabled .ui-slider-handle,.ui-slider.ui-state-disabled .ui-slider-range{filter:inherit}.ui-slider-horizontal{height:.8em}.ui-slider-horizontal .ui-slider-handle{top:-.3em;margin-left:-.6em}.ui-slider-horizontal .ui-slider-range{top:0;height:100%}.ui-slider-horizontal .ui-slider-range-min{left:0}.ui-slider-horizontal .ui-slider-range-max{right:0}.ui-slider-vertical{width:.8em;height:100px}.ui-slider-vertical .ui-slider-handle{left:-.3em;margin-left:0;margin-bottom:-.6em}.ui-slider-vertical .ui-slider-range{left:0;width:100%}.ui-slider-vertical .ui-slider-range-min{bottom:0}.ui-slider-vertical .ui-slider-range-max{top:0}.ui-spinner{position:relative;display:inline-block;overflow:hidden;padding:0;vertical-align:middle}.ui-spinner-input{border:none;background:none;color:inherit;padding:0;margin:.2em 0;vertical-align:middle;margin-left:.4em;margin-right:22px}.ui-spinner-button{width:16px;height:50%;font-size:.5em;padding:0;margin:0;text-align:center;position:absolute;cursor:default;display:block;overflow:hidden;right:0}.ui-spinner a.ui-spinner-button{border-top:none;border-bottom:none;border-right:none}.ui-spinner .ui-icon{position:absolute;margin-top:-8px;top:50%;left:0}.ui-spinner-up{top:0}.ui-spinner-down{bottom:0}.ui-spinner .ui-icon-triangle-1-s{background-position:-65px -16px}.ui-tabs{position:relative;padding:.2em}.ui-tabs .ui-tabs-nav{margin:0;padding:.2em .2em 0}.ui-tabs .ui-tabs-nav li{list-style:none;float:left;position:relative;top:0;margin:1px .2em 0 0;border-bottom-width:0;padding:0;white-space:nowrap}.ui-tabs .ui-tabs-nav .ui-tabs-anchor{float:left;padding:.5em 1em;text-decoration:none}.ui-tabs .ui-tabs-nav li.ui-tabs-active{margin-bottom:-1px;padding-bottom:1px}.ui-tabs .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor,.ui-tabs .ui-tabs-nav li.ui-state-disabled .ui-tabs-anchor,.ui-tabs .ui-tabs-nav li.ui-tabs-loading .ui-tabs-anchor{cursor:text}.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor{cursor:pointer}.ui-tabs .ui-tabs-panel{display:block;border-width:0;padding:1em 1.4em;background:none}.ui-tooltip{padding:8px;position:absolute;z-index:9999;max-width:300px;-webkit-box-shadow:0 0 5px #aaa;box-shadow:0 0 5px #aaa}body .ui-tooltip{border-width:2px}.ui-widget{font-family:Verdana,Arial,sans-serif;font-size:1.1em}.ui-widget .ui-widget{font-size:1em}.ui-widget input,.ui-widget select,.ui-widget textarea,.ui-widget button{font-family:Verdana,Arial,sans-serif;font-size:1em}.ui-widget-content{border:1px solid #aaa;background:#fff;color:#222}.ui-widget-content a{color:#222}.ui-widget-header{border:1px solid #aaa;background:#ccc url("images/ui-bg_highlight-soft_75_cccccc_1x100.png") 50% 50% repeat-x;color:#222;font-weight:bold}.ui-widget-header a{color:#222}.ui-state-default,.ui-widget-content .ui-state-default,.ui-widget-header .ui-state-default{border:1px solid #d3d3d3;background:#e6e6e6 url("images/ui-bg_glass_75_e6e6e6_1x400.png") 50% 50% repeat-x;font-weight:normal;color:#555}.ui-state-default a,.ui-state-default a:link,.ui-state-default a:visited{color:#555;text-decoration:none}.ui-state-hover,.ui-widget-content .ui-state-hover,.ui-widget-header .ui-state-hover,.ui-state-focus,.ui-widget-content .ui-state-focus,.ui-widget-header .ui-state-focus{border:1px solid #999;background:#dadada url("images/ui-bg_glass_75_dadada_1x400.png") 50% 50% repeat-x;font-weight:normal;color:#212121}.ui-state-hover a,.ui-state-hover a:hover,.ui-state-hover a:link,.ui-state-hover a:visited,.ui-state-focus a,.ui-state-focus a:hover,.ui-state-focus a:link,.ui-state-focus a:visited{color:#212121;text-decoration:none}.ui-state-active,.ui-widget-content .ui-state-active,.ui-widget-header .ui-state-active{border:1px solid #aaa;background:#fff url("images/ui-bg_glass_65_ffffff_1x400.png") 50% 50% repeat-x;font-weight:normal;color:#212121}.ui-state-active a,.ui-state-active a:link,.ui-state-active a:visited{color:#212121;text-decoration:none}.ui-state-highlight,.ui-widget-content .ui-state-highlight,.ui-widget-header .ui-state-highlight{border:1px solid #fcefa1;background:#fbf9ee url("images/ui-bg_glass_55_fbf9ee_1x400.png") 50% 50% repeat-x;color:#363636}.ui-state-highlight a,.ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a{color:#363636}.ui-state-error,.ui-widget-content .ui-state-error,.ui-widget-header .ui-state-error{border:1px solid #cd0a0a;background:#fef1ec url("images/ui-bg_glass_95_fef1ec_1x400.png") 50% 50% repeat-x;color:#cd0a0a}.ui-state-error a,.ui-widget-content .ui-state-error a,.ui-widget-header .ui-state-error a{color:#cd0a0a}.ui-state-error-text,.ui-widget-content .ui-state-error-text,.ui-widget-header .ui-state-error-text{color:#cd0a0a}.ui-priority-primary,.ui-widget-content .ui-priority-primary,.ui-widget-header .ui-priority-primary{font-weight:bold}.ui-priority-secondary,.ui-widget-content .ui-priority-secondary,.ui-widget-header .ui-priority-secondary{opacity:.7;filter:Alpha(Opacity=70);font-weight:normal}.ui-state-disabled,.ui-widget-content .ui-state-disabled,.ui-widget-header .ui-state-disabled{opacity:.35;filter:Alpha(Opacity=35);background-image:none}.ui-state-disabled .ui-icon{filter:Alpha(Opacity=35)}.ui-icon{width:16px;height:16px}.ui-icon,.ui-widget-content .ui-icon{background-image:url("images/ui-icons_222222_256x240.png")}.ui-widget-header .ui-icon{background-image:url("images/ui-icons_222222_256x240.png")}.ui-state-default .ui-icon{background-image:url("images/ui-icons_888888_256x240.png")}.ui-state-hover .ui-icon,.ui-state-focus .ui-icon{background-image:url("images/ui-icons_454545_256x240.png")}.ui-state-active .ui-icon{background-image:url("images/ui-icons_454545_256x240.png")}.ui-state-highlight .ui-icon{background-image:url("images/ui-icons_2e83ff_256x240.png")}.ui-state-error .ui-icon,.ui-state-error-text .ui-icon{background-image:url("images/ui-icons_cd0a0a_256x240.png")}.ui-icon-blank{background-position:16px 16px}.ui-icon-carat-1-n{background-position:0 0}.ui-icon-carat-1-ne{background-position:-16px 0}.ui-icon-carat-1-e{background-position:-32px 0}.ui-icon-carat-1-se{background-position:-48px 0}.ui-icon-carat-1-s{background-position:-64px 0}.ui-icon-carat-1-sw{background-position:-80px 0}.ui-icon-carat-1-w{background-position:-96px 0}.ui-icon-carat-1-nw{background-position:-112px 0}.ui-icon-carat-2-n-s{background-position:-128px 0}.ui-icon-carat-2-e-w{background-position:-144px 0}.ui-icon-triangle-1-n{background-position:0 -16px}.ui-icon-triangle-1-ne{background-position:-16px -16px}.ui-icon-triangle-1-e{background-position:-32px -16px}.ui-icon-triangle-1-se{background-position:-48px -16px}.ui-icon-triangle-1-s{background-position:-64px -16px}.ui-icon-triangle-1-sw{background-position:-80px -16px}.ui-icon-triangle-1-w{background-position:-96px -16px}.ui-icon-triangle-1-nw{background-position:-112px -16px}.ui-icon-triangle-2-n-s{background-position:-128px -16px}.ui-icon-triangle-2-e-w{background-position:-144px -16px}.ui-icon-arrow-1-n{background-position:0 -32px}.ui-icon-arrow-1-ne{background-position:-16px -32px}.ui-icon-arrow-1-e{background-position:-32px -32px}.ui-icon-arrow-1-se{background-position:-48px -32px}.ui-icon-arrow-1-s{background-position:-64px -32px}.ui-icon-arrow-1-sw{background-position:-80px -32px}.ui-icon-arrow-1-w{background-position:-96px -32px}.ui-icon-arrow-1-nw{background-position:-112px -32px}.ui-icon-arrow-2-n-s{background-position:-128px -32px}.ui-icon-arrow-2-ne-sw{background-position:-144px -32px}.ui-icon-arrow-2-e-w{background-position:-160px -32px}.ui-icon-arrow-2-se-nw{background-position:-176px -32px}.ui-icon-arrowstop-1-n{background-position:-192px -32px}.ui-icon-arrowstop-1-e{background-position:-208px -32px}.ui-icon-arrowstop-1-s{background-position:-224px -32px}.ui-icon-arrowstop-1-w{background-position:-240px -32px}.ui-icon-arrowthick-1-n{background-position:0 -48px}.ui-icon-arrowthick-1-ne{background-position:-16px -48px}.ui-icon-arrowthick-1-e{background-position:-32px -48px}.ui-icon-arrowthick-1-se{background-position:-48px -48px}.ui-icon-arrowthick-1-s{background-position:-64px -48px}.ui-icon-arrowthick-1-sw{background-position:-80px -48px}.ui-icon-arrowthick-1-w{background-position:-96px -48px}.ui-icon-arrowthick-1-nw{background-position:-112px -48px}.ui-icon-arrowthick-2-n-s{background-position:-128px -48px}.ui-icon-arrowthick-2-ne-sw{background-position:-144px -48px}.ui-icon-arrowthick-2-e-w{background-position:-160px -48px}.ui-icon-arrowthick-2-se-nw{background-position:-176px -48px}.ui-icon-arrowthickstop-1-n{background-position:-192px -48px}.ui-icon-arrowthickstop-1-e{background-position:-208px -48px}.ui-icon-arrowthickstop-1-s{background-position:-224px -48px}.ui-icon-arrowthickstop-1-w{background-position:-240px -48px}.ui-icon-arrowreturnthick-1-w{background-position:0 -64px}.ui-icon-arrowreturnthick-1-n{background-position:-16px -64px}.ui-icon-arrowreturnthick-1-e{background-position:-32px -64px}.ui-icon-arrowreturnthick-1-s{background-position:-48px -64px}.ui-icon-arrowreturn-1-w{background-position:-64px -64px}.ui-icon-arrowreturn-1-n{background-position:-80px -64px}.ui-icon-arrowreturn-1-e{background-position:-96px -64px}.ui-icon-arrowreturn-1-s{background-position:-112px -64px}.ui-icon-arrowrefresh-1-w{background-position:-128px -64px}.ui-icon-arrowrefresh-1-n{background-position:-144px -64px}.ui-icon-arrowrefresh-1-e{background-position:-160px -64px}.ui-icon-arrowrefresh-1-s{background-position:-176px -64px}.ui-icon-arrow-4{background-position:0 -80px}.ui-icon-arrow-4-diag{background-position:-16px -80px}.ui-icon-extlink{background-position:-32px -80px}.ui-icon-newwin{background-position:-48px -80px}.ui-icon-refresh{background-position:-64px -80px}.ui-icon-shuffle{background-position:-80px -80px}.ui-icon-transfer-e-w{background-position:-96px -80px}.ui-icon-transferthick-e-w{background-position:-112px -80px}.ui-icon-folder-collapsed{background-position:0 -96px}.ui-icon-folder-open{background-position:-16px -96px}.ui-icon-document{background-position:-32px -96px}.ui-icon-document-b{background-position:-48px -96px}.ui-icon-note{background-position:-64px -96px}.ui-icon-mail-closed{background-position:-80px -96px}.ui-icon-mail-open{background-position:-96px -96px}.ui-icon-suitcase{background-position:-112px -96px}.ui-icon-comment{background-position:-128px -96px}.ui-icon-person{background-position:-144px -96px}.ui-icon-print{background-position:-160px -96px}.ui-icon-trash{background-position:-176px -96px}.ui-icon-locked{background-position:-192px -96px}.ui-icon-unlocked{background-position:-208px -96px}.ui-icon-bookmark{background-position:-224px -96px}.ui-icon-tag{background-position:-240px -96px}.ui-icon-home{background-position:0 -112px}.ui-icon-flag{background-position:-16px -112px}.ui-icon-calendar{background-position:-32px -112px}.ui-icon-cart{background-position:-48px -112px}.ui-icon-pencil{background-position:-64px -112px}.ui-icon-clock{background-position:-80px -112px}.ui-icon-disk{background-position:-96px -112px}.ui-icon-calculator{background-position:-112px -112px}.ui-icon-zoomin{background-position:-128px -112px}.ui-icon-zoomout{background-position:-144px -112px}.ui-icon-search{background-position:-160px -112px}.ui-icon-wrench{background-position:-176px -112px}.ui-icon-gear{background-position:-192px -112px}.ui-icon-heart{background-position:-208px -112px}.ui-icon-star{background-position:-224px -112px}.ui-icon-link{background-position:-240px -112px}.ui-icon-cancel{background-position:0 -128px}.ui-icon-plus{background-position:-16px -128px}.ui-icon-plusthick{background-position:-32px -128px}.ui-icon-minus{background-position:-48px -128px}.ui-icon-minusthick{background-position:-64px -128px}.ui-icon-close{background-position:-80px -128px}.ui-icon-closethick{background-position:-96px -128px}.ui-icon-key{background-position:-112px -128px}.ui-icon-lightbulb{background-position:-128px -128px}.ui-icon-scissors{background-position:-144px -128px}.ui-icon-clipboard{background-position:-160px -128px}.ui-icon-copy{background-position:-176px -128px}.ui-icon-contact{background-position:-192px -128px}.ui-icon-image{background-position:-208px -128px}.ui-icon-video{background-position:-224px -128px}.ui-icon-script{background-position:-240px -128px}.ui-icon-alert{background-position:0 -144px}.ui-icon-info{background-position:-16px -144px}.ui-icon-notice{background-position:-32px -144px}.ui-icon-help{background-position:-48px -144px}.ui-icon-check{background-position:-64px -144px}.ui-icon-bullet{background-position:-80px -144px}.ui-icon-radio-on{background-position:-96px -144px}.ui-icon-radio-off{background-position:-112px -144px}.ui-icon-pin-w{background-position:-128px -144px}.ui-icon-pin-s{background-position:-144px -144px}.ui-icon-play{background-position:0 -160px}.ui-icon-pause{background-position:-16px -160px}.ui-icon-seek-next{background-position:-32px -160px}.ui-icon-seek-prev{background-position:-48px -160px}.ui-icon-seek-end{background-position:-64px -160px}.ui-icon-seek-start{background-position:-80px -160px}.ui-icon-seek-first{background-position:-80px -160px}.ui-icon-stop{background-position:-96px -160px}.ui-icon-eject{background-position:-112px -160px}.ui-icon-volume-off{background-position:-128px -160px}.ui-icon-volume-on{background-position:-144px -160px}.ui-icon-power{background-position:0 -176px}.ui-icon-signal-diag{background-position:-16px -176px}.ui-icon-signal{background-position:-32px -176px}.ui-icon-battery-0{background-position:-48px -176px}.ui-icon-battery-1{background-position:-64px -176px}.ui-icon-battery-2{background-position:-80px -176px}.ui-icon-battery-3{background-position:-96px -176px}.ui-icon-circle-plus{background-position:0 -192px}.ui-icon-circle-minus{background-position:-16px -192px}.ui-icon-circle-close{background-position:-32px -192px}.ui-icon-circle-triangle-e{background-position:-48px -192px}.ui-icon-circle-triangle-s{background-position:-64px -192px}.ui-icon-circle-triangle-w{background-position:-80px -192px}.ui-icon-circle-triangle-n{background-position:-96px -192px}.ui-icon-circle-arrow-e{background-position:-112px -192px}.ui-icon-circle-arrow-s{background-position:-128px -192px}.ui-icon-circle-arrow-w{background-position:-144px -192px}.ui-icon-circle-arrow-n{background-position:-160px -192px}.ui-icon-circle-zoomin{background-position:-176px -192px}.ui-icon-circle-zoomout{background-position:-192px -192px}.ui-icon-circle-check{background-position:-208px -192px}.ui-icon-circlesmall-plus{background-position:0 -208px}.ui-icon-circlesmall-minus{background-position:-16px -208px}.ui-icon-circlesmall-close{background-position:-32px -208px}.ui-icon-squaresmall-plus{background-position:-48px -208px}.ui-icon-squaresmall-minus{background-position:-64px -208px}.ui-icon-squaresmall-close{background-position:-80px -208px}.ui-icon-grip-dotted-vertical{background-position:0 -224px}.ui-icon-grip-dotted-horizontal{background-position:-16px -224px}.ui-icon-grip-solid-vertical{background-position:-32px -224px}.ui-icon-grip-solid-horizontal{background-position:-48px -224px}.ui-icon-gripsmall-diagonal-se{background-position:-64px -224px}.ui-icon-grip-diagonal-se{background-position:-80px -224px}.ui-corner-all,.ui-corner-top,.ui-corner-left,.ui-corner-tl{border-top-left-radius:4px}.ui-corner-all,.ui-corner-top,.ui-corner-right,.ui-corner-tr{border-top-right-radius:4px}.ui-corner-all,.ui-corner-bottom,.ui-corner-left,.ui-corner-bl{border-bottom-left-radius:4px}.ui-corner-all,.ui-corner-bottom,.ui-corner-right,.ui-corner-br{border-bottom-right-radius:4px}.ui-widget-overlay{background:#aaa;opacity:.3;filter:Alpha(Opacity=30)}.ui-widget-shadow{margin:-8px 0 0 -8px;padding:8px;background:#aaa;opacity:.3;filter:Alpha(Opacity=30);border-radius:8px} \ No newline at end of file From f1d91dfef88166df46298d1b4c48ccc89ae1b148 Mon Sep 17 00:00:00 2001 From: Michael Kaufmann Date: Thu, 21 Jun 2018 08:21:27 +0200 Subject: [PATCH 0618/1335] interface adjustments for apikey interaction Signed-off-by: Michael Kaufmann --- api_keys.php | 33 ++++++++--------- lng/english.lng.php | 6 ++++ lng/german.lng.php | 4 +++ templates/Sparkle/api_keys/keys_key.tpl | 25 +++++++++++-- templates/Sparkle/api_keys/keys_list.tpl | 8 ++--- templates/Sparkle/assets/js/apikey.js | 45 ++++++++++++++++++++++++ templates/Sparkle/assets/js/domains.js | 15 -------- templates/Sparkle/assets/js/main.js | 16 +++++++++ templates/Sparkle/config.json | 2 +- 9 files changed, 115 insertions(+), 39 deletions(-) create mode 100644 templates/Sparkle/assets/js/apikey.js diff --git a/api_keys.php b/api_keys.php index c60480b1..0af7142c 100644 --- a/api_keys.php +++ b/api_keys.php @@ -27,7 +27,7 @@ $del_stmt = Database::prepare("DELETE FROM `" . TABLE_API_KEYS . "` WHERE id = : $success_message = ""; $id = isset($_GET['id']) ? (int) $_GET['id'] : 0; -// do the delete and then just show a success-message and the certificates list again +// do the delete and then just show a success-message and the apikeys list again if ($action == 'delete') { if ($id > 0) { $chk = (AREA == 'admin' && $userinfo['customers_see_all'] == '1') ? true : false; @@ -65,20 +65,21 @@ if ($action == 'delete') { `apikey` = :key, `secret` = :secret, `adminid` = :aid, `customerid` = :cid, `valid_until` = '-1', `allowed_from` = '' "); // customer generates for himself, admins will see a customer-select-box - if (AREA == 'customer') { - $key = hash('sha256', openssl_random_pseudo_bytes(64 * 64)); - $secret = hash('sha512', openssl_random_pseudo_bytes(64 * 64 * 4)); - Database::pexecute($ins_stmt, array( - 'key' => $key, - 'secret' => $secret, - 'aid' => $userinfo['adminid'], - 'cid' => $userinfo['customerid'] - )); - redirectTo($filename, array( - 'page' => $page, - 's' => $s - )); + if (AREA == 'admin') { + $cid = 0; } + elseif (AREA == 'customer') { + $cid = $userinfo['customerid']; + } + $key = hash('sha256', openssl_random_pseudo_bytes(64 * 64)); + $secret = hash('sha512', openssl_random_pseudo_bytes(64 * 64 * 4)); + Database::pexecute($ins_stmt, array( + 'key' => $key, + 'secret' => $secret, + 'aid' => $userinfo['adminid'], + 'cid' => $cid + )); + $success_message = $lng['apikeys']['apikey_added']; } $log->logAction(USR_ACTION, LOG_NOTICE, "viewed api::api_keys"); @@ -167,8 +168,8 @@ if (count($all_keys) == 0) { $row = htmlentities_array($key); // shorten keys - $row['apikey'] = substr($row['apikey'], 0, 20) . '...'; - $row['secret'] = substr($row['secret'], 0, 20) . '...'; + $row['_apikey'] = substr($row['apikey'], 0, 20) . '...'; + $row['_secret'] = substr($row['secret'], 0, 20) . '...'; // check whether the api key is not valid anymore $isValid = true; diff --git a/lng/english.lng.php b/lng/english.lng.php index eccc3693..a409f5da 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -2135,3 +2135,9 @@ $lng['menue']['main']['apikeys'] = 'API keys'; $lng['apikeys']['no_api_keys'] = 'No API keys found'; $lng['apikeys']['key_add'] = 'Add new key'; $lng['apikeys']['apikey_removed'] = 'The api key with the id #%s has been removed successfully'; +$lng['apikeys']['apikey_added'] = 'A new api key has been generated successfully'; +$lng['apikeys']['clicktoview'] = 'Click to view'; +$lng['apikeys']['allowed_from'] = 'Allowed from'; +$lng['apikeys']['allowed_from_help'] = 'Comma separated list of ip addresses. Default empty.'; +$lng['apikeys']['valid_until'] = 'Valid until'; +$lng['apikeys']['valid_until_help'] = 'Date until valid, format YYYY-MM-DD'; diff --git a/lng/german.lng.php b/lng/german.lng.php index 3e98de24..e45d5c0b 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -1785,3 +1785,7 @@ $lng['menue']['main']['apikeys'] = 'API Keys'; $lng['apikeys']['no_api_keys'] = 'Keine API Keys gefunden'; $lng['apikeys']['key_add'] = 'API Key hinzufügen'; $lng['apikeys']['apikey_removed'] = 'Der API Key mit der ID #%s wurde erfolgreich gelöscht.'; +$lng['apikeys']['allowed_from'] = 'Erlaube Zugriff von'; +$lng['apikeys']['allowed_from_help'] = 'Komma getrennte Liste von IPs. Standard ist leer.'; +$lng['apikeys']['valid_until'] = 'Gültig bis'; +$lng['apikeys']['valid_until_help'] = 'Datum Gültigkeitsende, Format JJJJ-MM-TT'; diff --git a/templates/Sparkle/api_keys/keys_key.tpl b/templates/Sparkle/api_keys/keys_key.tpl index 12eb43a2..263d0807 100644 --- a/templates/Sparkle/api_keys/keys_key.tpl +++ b/templates/Sparkle/api_keys/keys_key.tpl @@ -3,10 +3,10 @@ {$adminCustomerLink} - {$row['apikey']} + {$row['_apikey']} - {$row['secret']} + {$row['_secret']} {$row['allowed_from']} @@ -23,5 +23,24 @@ {$lng['panel']['delete']} + - + \ No newline at end of file diff --git a/templates/Sparkle/api_keys/keys_list.tpl b/templates/Sparkle/api_keys/keys_list.tpl index 11671ad7..43cdf15d 100644 --- a/templates/Sparkle/api_keys/keys_list.tpl +++ b/templates/Sparkle/api_keys/keys_list.tpl @@ -34,11 +34,11 @@ - - + + - - + + diff --git a/templates/Sparkle/assets/js/apikey.js b/templates/Sparkle/assets/js/apikey.js new file mode 100644 index 00000000..b6efb43a --- /dev/null +++ b/templates/Sparkle/assets/js/apikey.js @@ -0,0 +1,45 @@ +/** + * + */ +$(document).ready(function() { + + function editApikey(id) { + var sid = getUrlParameter('s'); + var page = getUrlParameter('page'); + + var apikey_id = $('#dialog-' + id + ' input[name="id"]').val(); + var allowed_from = $('#dialog-' + id + ' input[name="allowed_from"]').val(); + var valid_until = $('#dialog-' + id + ' input[name="valid_until"]').val(); + + $.ajax({ + url: "admin_index.php?s="+sid+"&page="+page+"&action=jqEditApiKey", + type: "POST", + data: { + id: apikey_id, allowed_from: allowed_from, valid_until: valid_until + }, + dataType: "json", + success: function(json) { + $('#dialog-' + id).dialog("close"); + location.reload(); + }, + error: function(a, b) { + console.log(a, b); + } + }); + } + + $("span[id|='apikey'], span[id|='secret']").click(function() { + var id = $(this).attr('data-id'); + $('#dialog-' + id).dialog({ + modal : true, + buttons : { + Ok : function() { + editApikey(id); + $(this).dialog("close"); + } + }, + width : 800 + }); + }); + +}); diff --git a/templates/Sparkle/assets/js/domains.js b/templates/Sparkle/assets/js/domains.js index b95e04de..4b43c0fb 100644 --- a/templates/Sparkle/assets/js/domains.js +++ b/templates/Sparkle/assets/js/domains.js @@ -1,20 +1,5 @@ $(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]; - } - } - }; - /** * disable unusable php-configuration by customer settings */ diff --git a/templates/Sparkle/assets/js/main.js b/templates/Sparkle/assets/js/main.js index 3ea7cc4f..71a0a035 100644 --- a/templates/Sparkle/assets/js/main.js +++ b/templates/Sparkle/assets/js/main.js @@ -5,6 +5,22 @@ function twoDigits(value) { return value; } $(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]; + } + } + }; + // Scroll to top $(window).scroll(function() { if ($(this).scrollTop() > 100) { diff --git a/templates/Sparkle/config.json b/templates/Sparkle/config.json index 53bd1088..b6d2f779 100644 --- a/templates/Sparkle/config.json +++ b/templates/Sparkle/config.json @@ -1 +1 @@ -{"variants":{"default":{"css":["main.css"],"js":["main.js"],"description":"Default"},"froxlor":{"css":["main.css","froxlor.css"],"js":["main.js"],"description":"Froxlor"}},"author":"Roman Schmerold"} +{"variants":{"default":{"css":["main.css"],"js":["main.js", "apikey.js"],"description":"Default"},"froxlor":{"css":["main.css","froxlor.css"],"js":["main.js", "apikey.js"],"description":"Froxlor"}},"author":"Roman Schmerold"} From d4312fc481dfe580fd0f09aa86052c2f04f6530a Mon Sep 17 00:00:00 2001 From: Michael Kaufmann Date: Fri, 22 Jun 2018 10:05:04 +0200 Subject: [PATCH 0619/1335] update jquery/jquery-ui; fininshed api_key editing (needs a bit more validating); added PhpSettings-Unit-test Signed-off-by: Michael Kaufmann --- api_keys.php | 36 +++++++++++- .../ui-bg_diagonals-thick_18_b81900_40x40.png | Bin 369 -> 0 bytes .../ui-bg_diagonals-thick_20_666666_40x40.png | Bin 387 -> 0 bytes css/images/ui-bg_flat_10_000000_40x100.png | Bin 278 -> 0 bytes css/images/ui-bg_glass_100_f6f6f6_1x400.png | Bin 232 -> 0 bytes css/images/ui-bg_glass_100_fdf5ce_1x400.png | Bin 321 -> 0 bytes css/images/ui-bg_glass_55_fbf9ee_1x400.png | Bin 335 -> 0 bytes css/images/ui-bg_glass_65_ffffff_1x400.png | Bin 207 -> 0 bytes css/images/ui-bg_glass_75_dadada_1x400.png | Bin 262 -> 0 bytes css/images/ui-bg_glass_75_e6e6e6_1x400.png | Bin 262 -> 0 bytes css/images/ui-bg_glass_95_fef1ec_1x400.png | Bin 332 -> 0 bytes .../ui-bg_gloss-wave_35_f6a828_500x100.png | Bin 5227 -> 0 bytes .../ui-bg_highlight-soft_100_eeeeee_1x100.png | Bin 246 -> 0 bytes .../ui-bg_highlight-soft_75_cccccc_1x100.png | Bin 280 -> 0 bytes .../ui-bg_highlight-soft_75_ffe45c_1x100.png | Bin 287 -> 0 bytes css/images/ui-icons_222222_256x240.png | Bin 6922 -> 0 bytes css/images/ui-icons_228ef1_256x240.png | Bin 4406 -> 0 bytes css/images/ui-icons_444444_256x240.png | Bin 0 -> 6992 bytes css/images/ui-icons_454545_256x240.png | Bin 6992 -> 0 bytes css/images/ui-icons_555555_256x240.png | Bin 0 -> 6988 bytes ...56x240.png => ui-icons_777620_256x240.png} | Bin 4549 -> 4549 bytes css/images/ui-icons_777777_256x240.png | Bin 0 -> 6999 bytes css/images/ui-icons_888888_256x240.png | Bin 6999 -> 0 bytes ...56x240.png => ui-icons_cc0000_256x240.png} | Bin 4549 -> 4549 bytes css/images/ui-icons_ef8c08_256x240.png | Bin 4406 -> 0 bytes css/images/ui-icons_ffd27a_256x240.png | Bin 4406 -> 0 bytes css/images/ui-icons_ffffff_256x240.png | Bin 4848 -> 6299 bytes css/jquery-ui.min.css | 8 +-- js/jquery-ui.min.js | 15 +++-- js/jquery.min.js | 6 +- lib/init.php | 2 +- templates/Sparkle/api_keys/keys_key.tpl | 9 +-- templates/Sparkle/assets/js/apikey.js | 52 ++++++++++++++---- templates/Sparkle/assets/js/domains.js | 15 +++++ templates/Sparkle/assets/js/main.js | 15 ----- tests/PhpAndFpm/PhpSettingsTest.php | 29 ++++++++++ 36 files changed, 139 insertions(+), 48 deletions(-) delete mode 100644 css/images/ui-bg_diagonals-thick_18_b81900_40x40.png delete mode 100644 css/images/ui-bg_diagonals-thick_20_666666_40x40.png delete mode 100644 css/images/ui-bg_flat_10_000000_40x100.png delete mode 100644 css/images/ui-bg_glass_100_f6f6f6_1x400.png delete mode 100644 css/images/ui-bg_glass_100_fdf5ce_1x400.png delete mode 100644 css/images/ui-bg_glass_55_fbf9ee_1x400.png delete mode 100644 css/images/ui-bg_glass_65_ffffff_1x400.png delete mode 100644 css/images/ui-bg_glass_75_dadada_1x400.png delete mode 100644 css/images/ui-bg_glass_75_e6e6e6_1x400.png delete mode 100644 css/images/ui-bg_glass_95_fef1ec_1x400.png delete mode 100644 css/images/ui-bg_gloss-wave_35_f6a828_500x100.png delete mode 100644 css/images/ui-bg_highlight-soft_100_eeeeee_1x100.png delete mode 100644 css/images/ui-bg_highlight-soft_75_cccccc_1x100.png delete mode 100644 css/images/ui-bg_highlight-soft_75_ffe45c_1x100.png delete mode 100644 css/images/ui-icons_222222_256x240.png delete mode 100644 css/images/ui-icons_228ef1_256x240.png create mode 100644 css/images/ui-icons_444444_256x240.png delete mode 100644 css/images/ui-icons_454545_256x240.png create mode 100644 css/images/ui-icons_555555_256x240.png rename css/images/{ui-icons_2e83ff_256x240.png => ui-icons_777620_256x240.png} (90%) create mode 100644 css/images/ui-icons_777777_256x240.png delete mode 100644 css/images/ui-icons_888888_256x240.png rename css/images/{ui-icons_cd0a0a_256x240.png => ui-icons_cc0000_256x240.png} (88%) delete mode 100644 css/images/ui-icons_ef8c08_256x240.png delete mode 100644 css/images/ui-icons_ffd27a_256x240.png create mode 100644 tests/PhpAndFpm/PhpSettingsTest.php diff --git a/api_keys.php b/api_keys.php index 0af7142c..ec536571 100644 --- a/api_keys.php +++ b/api_keys.php @@ -80,6 +80,37 @@ if ($action == 'delete') { 'cid' => $cid )); $success_message = $lng['apikeys']['apikey_added']; +} elseif ($action == 'jqEditApiKey') { + $keyid = isset($_POST['id']) ? (int)$_POST['id'] : 0; + $allowed_from = isset($_POST['allowed_from']) ? $_POST['allowed_from'] : ""; + $valid_until = isset($_POST['valid_until']) ? (int)$_POST['valid_until'] : -1; + + // @todo validate allowed_from + + if ($valid_until <= 0 || !is_numeric($valid_until)) { + $valid_until = -1; + } + + $upd_stmt = Database::prepare(" + UPDATE `" . TABLE_API_KEYS . "` SET + `valid_until` = :vu, `allowed_from` = :af + WHERE `id` = :keyid AND `adminid` = :aid AND `customerid` = :cid + "); + if (AREA == 'admin') { + $cid = 0; + } + elseif (AREA == 'customer') { + $cid = $userinfo['customerid']; + } + Database::pexecute($upd_stmt, array( + 'keyid' => $keyid, + 'af' => $allowed_from, + 'vu' => $valid_until, + 'aid' => $userinfo['adminid'], + 'cid' => $cid + )); + echo json_encode(true); + exit; } $log->logAction(USR_ACTION, LOG_NOTICE, "viewed api::api_keys"); @@ -178,9 +209,10 @@ if (count($all_keys) == 0) { $isValid = false; } // format - $row['valid_until'] = date('d.m.Y H:i', $row['valid_until']); + $row['valid_until'] = date('Y-m-d', $row['valid_until']); } else { - $row['valid_until'] = "∞"; + // infinity + $row['valid_until'] = ""; } eval("\$apikeys.=\"" . getTemplate("api_keys/keys_key", true) . "\";"); } else { diff --git a/css/images/ui-bg_diagonals-thick_18_b81900_40x40.png b/css/images/ui-bg_diagonals-thick_18_b81900_40x40.png deleted file mode 100644 index cc1e486a5a8fa064acbc9d761c4f8bca99f9e532..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 369 zcmeAS@N?(olHy`uVBq!ia0vp^8Xzpd1SErbK34)MmUKs7M+SzC{oH>NS%G}U;vjb? zhIQv;UIIA^$sR$z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8ZKG z?e6j)3^*gc-vNqn7I;J!18EO1b~~AE2V|V^ba4#HxcBykAy>15fLr4BQ>-lsl52Eq zV*Dn(zVTG$VAKY&H#$Db7CV$Z4fp5&D*d_PX64JfTQ2_%lrY|Ztm*~#^E>mN9{-ni z?$7&sDUDxOs6|ZO#e8j9-huE1bq8a^L;ocUu2er*xbpR0zJiifkNL9$HXrc1!n{H5 zD$DAi@&mW7@c!Vs()`r1{}*$?5%!m!1$VY@mzJqLThL%?wn}HirB5?5qD*vWiTQRf mbG^JM^Wqjv?%IU)TUhseSI&*pJl_TM6N9I#pUXO@geCx&l9MC= diff --git a/css/images/ui-bg_diagonals-thick_20_666666_40x40.png b/css/images/ui-bg_diagonals-thick_20_666666_40x40.png deleted file mode 100644 index 7bc8a92833b6f725e11f2f259b505f0a9f4f2a59..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 387 zcmeAS@N?(olHy`uVBq!ia0vp^8XznHBp80OT7LpkEa{HEjtmSN`?>!lvI6;x#X;^) z4C~IxyaaL-l0AZa85pY67#JE_7#My5g&JNkFq9fFFuY1&V6d9Oz#v{QXIG#NP=YDR z+uh~=f05OOXMsG<0*}aIAngIhZYQ(tfQ%YX7sn8f<8QBS++H8mU)r~CX z4LsK!yRsFIhfa9Hb)rc6!cMu3cbH1@qjk(1T|Rd_O$zRK*6I6kwUXIUAiL(>^w~v> zvvgjZ`w{;28qgi8C9V-ADTyViR>?)FK#IZ0z|ch30Emo249%9nO2EggXnga2|x`Dp00i_>zopr0J+|PcmMzZ diff --git a/css/images/ui-bg_flat_10_000000_40x100.png b/css/images/ui-bg_flat_10_000000_40x100.png deleted file mode 100644 index c5d10e6570cbfc4ade4bcdc227937aa3e7ac452e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 278 zcmeAS@N?(olHy`uVBq!ia0vp^8bF-F2qYNp$opRhQY`6?zK#qG8~eHcB(ehejKx9j zP7LeL$-D$|6p}rHd>I(3)EF2VS{N990fib~Fff!FFfhDIU|_JC!N4G1FlSew4N!t9 z$=lt9f$?sa@Dd=8v%n*=7)X17vD?XPJ0OGK)5S4F<9u?01nc4kpkYiA42-2_ZRJ2I z)e_f;l9a@fRIB8oR3OD*WMF8bYXC$>A%BP diff --git a/css/images/ui-bg_glass_100_f6f6f6_1x400.png b/css/images/ui-bg_glass_100_f6f6f6_1x400.png deleted file mode 100644 index cc405328ce69191a6bfbfd580d49cf4629c14206..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 232 zcmeAS@N?(olHy`uVBq!ia0vp^j6gI&0LWmFTHNUZq*&4&eH|GXHuiJ>Nn`~nC=POW zVpw-h<|UA$kn9oU%fL{j#=y|f!octgDAe$RfuYoZf#FpG1B2BJ1_tqhIlBUFfD%ke z-tI2{|BI|PJPYJ;7I;J!18EO1b~~AE2V^*Vx;TbdoKF5x)7#mYDAKE>wOBrhCx4B^ zy*-IZHph-sG98;=xMv5p~S|>aIb}5OaIwM QE1+=zopr01W~|uK)l5 diff --git a/css/images/ui-bg_glass_100_fdf5ce_1x400.png b/css/images/ui-bg_glass_100_fdf5ce_1x400.png deleted file mode 100644 index c1662ceca30f394ac08947f50a4b799878bcad63..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 321 zcmeAS@N?(olHy`uVBq!ia0vp^j6gI&fCw!Bv|Lw@jjIrX5XnoFeIKG^1(3g3KNAWnVRY?v?y%eD~QVQhkC$0GU?JN{g~pt#URncV#Y_$k|XK-R<-i}h8L%X=5pRm`FK+?Cepa!-KTJ)wz#gJ zUm0|}kDpz-nc6a#?2AmP!?*K(O3p^r= zfwTu0yPeFo12TF&T^vI^j=w#x$i?I+((tf;UXnmgbH|3oY>pC!)f}(GR!16S-u+#{ ze6YEqRkW=8vGl=5qArKM<9}TC-}iEvB{zdaTcX5$wyRTK&ALYGq)eZD3$! kV9>=^7Y^7?@ibn_3wdYa19?85p?Km9In5kei>9nO2EggQ@a)A)p2ZPgg&e IbxsLQ0P3kSL;wH) diff --git a/css/images/ui-bg_glass_75_dadada_1x400.png b/css/images/ui-bg_glass_75_dadada_1x400.png deleted file mode 100644 index a2c6083f55700e107ec990d7155a4e85c6fc93c4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 262 zcmeAS@N?(olHy`uVBq!ia0vp^j6gI&0LWmFTHNUZq?nSt-Ch3w7g=q17Rci)@Q5r1 z(jH*!b~4)z#PD=+46!(!T=8puqDZgOs>RXUCGx5b?-VBQkUm|IuXOmYJrBRJgj{Vx zMbNnqUkncy+qa2-mWYc>swkcIuvGK#>(0d)B7)5f`@$Ei28nH~0h*~=;u=wsl30>z zm0Xkxq!^403@vmG%ybQmLk!HVj7_Z!OtcLQtPBhqZ+a@AXvob^$xN%nt>Ht<$2mX^ N44$rjF6*2UngCW^PcZ-h diff --git a/css/images/ui-bg_glass_75_e6e6e6_1x400.png b/css/images/ui-bg_glass_75_e6e6e6_1x400.png deleted file mode 100644 index 5c738d545a82220d1cbf6bc45be9d2194806f696..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 262 zcmeAS@N?(olHy`uVBq!ia0vp^j6gI&0LWmFTHNUZq?nSt-Ch3w7g=q17Rci)@Q5r1 z(jH*!b~4)z#PD=+46!(!TrvH)L6@80)r*_cdCvDr%)6ghVL16=s@mbz7H!uRdGeDa z?kzLg)16i!f8fKx84s0>4afpGrm9eRnfr++(ft7(l<4sQm6b-rgDVb@NxHWue`8Wrt Ofx*+&&t;ucLK6U?tWQ4x diff --git a/css/images/ui-bg_glass_95_fef1ec_1x400.png b/css/images/ui-bg_glass_95_fef1ec_1x400.png deleted file mode 100644 index 1d828735a5b5a2d0e88aac81b90f0dbb5dff162c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 332 zcmeAS@N?(olHy`uVBq!ia0vp^j6gI&fCnc6a#?2AmP!?*K(O3p^r= zfwTu0yPeFo12VciT^vI^j=w#>k(V)1qW$CZ|6)SVV-&*#dav<$DMuV&n0Dbpw@a>=^7Y^7?@ibn_3x|Xd4(<85lI) h^i)96kei>9nO2Eg!-tlSbATEcJYD@<);T3K0RXc|bS3}* diff --git a/css/images/ui-bg_gloss-wave_35_f6a828_500x100.png b/css/images/ui-bg_gloss-wave_35_f6a828_500x100.png deleted file mode 100644 index 50e4fd032f3f1c4e18d1433c058bdb5b4ec93492..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5227 zcmb_gdsLEXw|APauWu@?rq6Dso@yFRGfPJ;6`4`f#FD(0to_#oSL)NS?ABQ*MgVl`MvvHd%w@#zu#V5 zKN;k2x^~A}6B85DfMdQPCMI7j8h@YOxXSoVb-8}lc=-qHhZ8@Tn9#86mQSrVuD{3# z@%J{V>)RldV79YYO%> z0{=`1^!GKa*jG{N*T$W%emWMNVZ0t&`S^7qyi80sKMnBxA?$)s_8M6L=?w3DA8_+> zSSV!m?r@NW-Qa^~0n(vci8kp{;n}Y{vz`)MbBkPmzt;bcU1utHpS1k`KbIrCze!yU z?_YKQ_p|Tn*dm5Nw6P|bcarF6o0=bn3p-|h{kM$|9$r32SXi}dDeVC)GrNQ144alq zHQMPae|2NFytm95mds|homLH}az1?P`_WQ{zyH_9O-4KNc+c*SwkCUJ-%Nh(qdk9O z`4Rk#p8LuF|3Hf_d91j8LMPwcTJhcadd2lK`su;{3w2XH(ajUsi*jn*n;Ut6EU-p~ zzcV06bZGYjwAk={h`N?DlWc%v0{`%X*mu3HnR$Rik@$W=zKfcigLq*I=*!vE6AK0H zBqQpXIkkTZ8yI$Wh=WM;!9T|F3@UXUm`r}ZNM6kr&N7;$1tsx&@F)nJH=EgI{RP-l z;qpLJoTKL2Bw59S|1g1u&x zc7Uey-+89Q07n6 z_WlYxkNQW+^9M6e%boEw+oHkFS}Xu?Kr(l7I#Ar0DT|6MwmL*y4WM>2$un5kAPDfN z?<}Nm@@Q;VR<1*d@DfP22gOhehFZeQ+~A1Oy5M5m@t4{}N+Eu2jTFJh-6ufE9%K?| zhIg?Ag^Miplngd)@x(bhmR=T`J7I6H6zt*qrxx1#ZRA4i%F?0duj2n)z;}*3Hca)i z{oPkuAZ7UAlC&Sc$9d)6p@vV@E_n7t_nyeIYy>x8j&m@YeN)3Nsvc(O!wre8NllFN zJua=5-$vA&nWYf-9L$64%e@v)Q)L}iHPBDmMJSyH1~!8;AC@Zc$7_QFKre2Il4Xo& zY9F?702hBLk$fLl-FY=!XO)KIH9hZdt?)G;~Sy zHHQyE1c1R6I1tB;DlZHz!)=PLrQyx_EWvC5 zyFf|y2j^4S(5O8Gfp-l(4#HTxHwF(o2QWLA()I^&%}O`XKehkk17>n8UMn0=-1-jO zryCu2=$Re`=I-T}i}dgE*jL4@&X{A^W0*vDGros%Ry=V6`hL;D5k*tmE&Z5kalRdCO2$F2(@6aH{w%|=9MqX-k!31`n#hzyH==DaGmB6$B(R#r|a3fJ3Z7E zSG`zI7Zck&a9a|<(ZJ5u7wAB8ew1Ng{sdOI;}qDsL|u)(@7wP#(7cYWda&pZ-6p;^ zmGs!_l17RaM)I$Ba6a4yaiSroN228u#bf){2oAd8Fk_-)fXFk+`?rWcYzBq*i( zP(WQ7+5b-zNuLFdh?+PnBeiZJ3niV(!aXpVo6IBd%;X%5T;+4s zbe&W3*scEZJXoPshp<2iYP>DLfOlTu#`u@YahY36kT{kW#xMSoUl5-B(%qk!knm!z zA>H1^9mD5AdKi1eH8gxoNhEj?7vgHbv1WLt2!iq$DtU}k4w88g;pZS}w$Av-=Kk$+ z>envQPm3)W>Pp6_ZUFF}b#j92loYjh-%2gf4t?Umx|^4@uQDp+arc(xjoDUBr)=^ADf~I>>xyYhv6~ZvhW;BRnL(Lz$Fp$`5LG@aT zc!bkW9_@6kAuPySOviZEpoSV1X_}kwv zcGFz}n}V>lepCCx4|Pa=WN_&)&NETX5)VU{wZ|Zqea-jh1v$L$36@;<2K?}{NAR%X z0m(8KC$74+?csih73SIxzq`yI#xyC;wahNR8}8iT<#eRw!=x;-j^@E_d4~_QnRn0V zyjWVHXc!=fFiVp!NZBoD^)cp_id!9Re7{NZ}Rc=UEKEVhI^GBA7rKo zOx)}S>l?9so+t^e3kMTrzWIu-dm=eO!eourMl>7l8PtGcIdB>}~E4_z6s?k_0Iq-8Rz@aSxIk4osrLmemS)Sh9rvEAEYvW8uyh9yzW+6Xqd!42rAUT0hnYJ)F7NVdR_&IyIQKRs~$gdcDwZ zhpVR+w`m$>y`^cn>A1D84MV@xO%p6QLp#SL_?Av};D?Cgtqs_UK+h!z+`ZPy@ zLcsu>haO=hsj-V57**-~1@rW8w44ao z3!Z*VPgG!Lmw><^DtQW1@$c9QgOefDjqPn|v7^a3V{AkTA0#$*$wybqqhob|oy>!| zdsV4YHgR`^>q27RghrOrqkA!I;{QZ(=}?ySrumMEg7JEiTt{60cgz<8urz2kU5C~{?!IY+XUx@ z{^chX*s>WKu(z;`t66KuSUX3l&P0cpjxN8>XvTYgtmF1OrlVbrj?$*TlqJ^nymQ8e zdEw>1wA-S|IQlk^=X}!Ym$_%t=1u+o;*BI9wIBoedP%qL11kfV-G-};D{J*?@jvU2 zX;u!mWJmA$&ub{t22Ybz)!$-4CKaN$Vh%6@q(bqZ3Ofb}U*UP*wdOE_u*Ylo9dX(C zlvn7Hm>1hsn8W=Ijjy~t@P`XH2zwX*=vVmR#Yp<%2w|5!;6|qz9JYA zkKEPm#wtXJ$pPQ}1p?%;}B#YbA0=3FSvDE4Iwr;{PKctg)XMy-?2(?t!+CMS~K zHTG_ELs5v%_&A~ZXE`?0Ej|I)jD|MGB;9VbsNa(pD*U{Cf?7u5?zH*e6x%ngZ zF~YpFDp@T4?-GcoBVR9U{0y4CXla%1Y{UKj>u$&j6%Melp7F^&r9)1+(|xO$1Z ziwLIzVf|IANB>bjln!$<@V&JLe%_8wtB5$1x|dW3sDGQHUaDE_h?+W#BCSSzSF$-D$O1UOt+|Nj zgJh%1tc`|Sbf(9-21Y#yE^<=5aVb6%efC8w3q&Bz7mOk;(ITBDw9SL!Fw3sgM{ov& z<;rMD=2SdrJwP`&6THM6oO?NzP)QBpqGFy`NJ92diikPLec?c50na&_n%qVznWLyV zVU-1iPc;V&cJ|?t5)Vqocyv1IO397Xn)Nl2Uv-kZ^uZ-@e6Jjx@KZB;-{ zG6@sZ5~bb`-eY?ut+N*lRX7K0W0jJKtFq(ShjOtDo9<9?_S7@@;R6VRqC--)jvd)X zN|4#Ue}B5%Dcd;t-!D%CR*M>}4jFE|g9SAK#fK-SuH(_&vE(C!z9?{tBwP>@e)M`O z#I%M*irRDXA!rbS=zhG6R7WnfbV~{TT%lvLf?td7mI|V<`hil|SfmQdwS^PTS;C$R zHpm5*hH>6l)D&MV(antG##x81I!M%=nvIfFse||xaSYcAtp1vI-kOZWGVY}Tks{n- z`|P>k5Mfwy=yodJ9Pi0RyCUmwMuLn{Ey(7`IWEAkLoR7Sf_Gpe5gheFA-8a zX9A8Ge6nTlk@32NpBv288u}PjJKTD`1qPUAv%-)9_#nc9t+XhMqE?swuRsMTeeVFwXl9_+6kGgMy zFuL5`pc4!b1|qMRad?Cq>|l(YiVTaRZHnd;vY!oXe%8V-X}oq6a-HtAWp5haOFNeI z;(MJei5gWE|IlobaxMKWIns(~iNKW5q6Fm(phnpOqlCXIFVmM-bG;!Y{jylKKSHI+ zJ|O8uC4Vy?%hlzW`*iK(!WXDTA+`G*J3XtO_nc0Gb$JjcpEVBV*$v)*$)5P&Q$biE zm5+2fg5@9;MN@$~SUfkB22%+4kNhf{Ud+_7Y5!VJ)s75ynoGciQM|jAEsu%lKW5-H zZ3a9^vnYngIA-gM~b=)|{G-#gaQaiKL@-B_9=4%0|D#6vUjyih>Ft9@Ks|aie z9T+p4#}@XvhoX*?(*s#4mDx#gw0}L=Q`1h*s>XG@8`dhZO%Tl76s!;zVu$WgQNmn$ zEE3td55kyQ*b}HsIUI~aR2H?YJqb^}Gn&)WH-FcKsj^c^FLl$Es#Q~`XQ_{JwBDi= xyWGQuBfi*(zjU}@oFTyHOh2ssB#{Q|fqZzJ%3hmd{9Df?;AoINn{1`8HkKv5aIvl{>{H(Np_kp}l}CqhT%0UhzQs}1a|PYn hXM30ZUH`V7z3{5)w)U+I6M=>^c)I$ztaD0e0sv+>P3Qms diff --git a/css/images/ui-bg_highlight-soft_75_cccccc_1x100.png b/css/images/ui-bg_highlight-soft_75_cccccc_1x100.png deleted file mode 100644 index 4d4a7e9280a7021f5d3a6c5004637a588f481f24..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 280 zcmeAS@N?(olHy`uVBq!ia0vp^j6j?s03;ZUuHXC*q?nSt-Ch3w7g=q17Rci)@Q5r1 z(jH*!b~4)z$cXZEaSV~ToLo`U+vu0Ue0cG9p8hWqa?gxxGLm=1A1u)Cewe3oSeCaf zI$k30UHXoTXA5lSJe(zTcE%W-S*bfB&J`pw9sa4-R?IGW?p~6`>jMSP&M+u3 zY@9al)zrvpHlQu4C9V-ADTyViR>?)FK#IZ0z|cb1z)aV`IK;r*%GlJ(z(m`?z{NsW*1m6Iq|G@bT8aQwD$j|9|&`M^9k=hx1MoVjkXiRfu~dZz`bwtlz4m=|e)( z#`>#@MG21Q@2?j8m7vA_zlk^duQvC?#-|F~n%A%fzW&$Qz{3Cky>#V9Cm~jbXHV@r UrBB+00bRi0>FVdQ&MBb@0M;*QUjP6A diff --git a/css/images/ui-icons_222222_256x240.png b/css/images/ui-icons_222222_256x240.png deleted file mode 100644 index e9c8e16ac5e7f61c843fbac290ce30c5de7e40b6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6922 zcmZ`;WmH_vmTkHN1c$~6PLSY|#sb0J-5r7l4-l+z2m}f4?tFL%PU9Z51HqaEx5g#t z@MPA!_h#lt-E(W*b5`Bjwd&M9yQ0-q|9pEC2TIxHV+jYr=H%}!7RG)%@|?KBm>pyGpu&ojtW_wyym4g`c9 zco$2c8`p{vTVxgdal_?TR2q}t~bOfL?S*Jz>-hj zej%U-ol64Z=H=<4vts*B%Lz$=z-A~15GXb3wI}HLd?o-|{AomRiY`p@EGlXSgLObn zHiI+BXw_n(p}o|pYMok=31u8OMO?~bm-n`CS;fRowPbbSwTgu0=*7l(0v$^yp&hkM zDNj1Y0bXyV)+53{VIwdGW4Fo5{;9<>33wL?Z3XX8uv#Z=U!_NVfTGUbDr4v}Tu zR`?wX3hn^0$(t{SX1-GeGS~e&#-4Kh2MVt#b5SU8CjmH-w$O;aCD6t?ZrfdZEbk@Y zJqZfG&3O7WH7q-{({FD9v;hC5@$-r}DY+5dbrzbDrH+tT&pfKJUur|7-n>I<<=u4E zqp>W4q^FJ|mzJ;N(7*VMG zU2BTU!JR%!p+}0CBhHUm_PbWLt8EWDyGtR zVU)%zz+8FYJEe|QI~>NS9o>Rn3~1g%$0eQ`!lR@ZF2O92__?mx1rtva8{14&Kj1L0 zDaZ(xRC21rv8=fJO|)hhkn=UJGcknv`SnzJbkjtkmm+>?J%~|f+$2Sbx8vOAIgY2t zqQT*KpE$b>z=kxqpl_!>-2K;g2dm}3JV23iDpVU^-or__aU{~34xQEW(|mEprL&b- zAQSAsLrN#rJ|@c*=_coAKi$0zq;K9IiTC{?&;K{{PJd1H8|KNh0y%+oVJU9m3t9~>S^EF8ZniI_zTt3H+C~_J}%``4jjf5yho+NMLCrcAJN*P z^^L0M2Nk|oV{v_&cv{>_czfr@d)!?(MX2RGno!%Jx$h65%CqQ#A0d5;| zSSLyd*lk78{Jmt_-J$L6#h)e1B{JA5+7@mTA2cKQHMTmEgVqts${HgsFO~C=B_Q*( zHl%u4XCA+FdC1CvH?&IhdHSiEW#v4eIhhZEQ+hV>M>Dkvof`t}fb!@dj0LrbRqUg5 z8nXf_+F;+Ir4&w^l=4m~sy#TR$Q8xfPB%72n=*iaN9EKWeXj`CkLbf69eW>JCVDPZ z7L|9kyXOYG(A0Zx(eZiSb@JFFdT6i-rdyVPce<|Y|A~7p%E!+Vm5pq)E_mjl3TB&{ z7xkGp$iSk&UEtWes|0wb9iDlJf)%nsDc$a1kP0__Pvgop<&Te#x#)w#k-3S#)*eW* znUIBK40-siM_X970Y(XHD$9MH=f zxxGx(wa4Z;q^W3V0>lRag@tvPcuViWb!iG;t2 z5-KFMfg<4J#MU$lBUwKHsdM*=Xc&102XwKxclrG1%i=4}FJahwrX;H zNwp>$UiNsQ3MaH`j_r!Cf!2nGp?oQYWGDyw*oRPD)Jji};0ay%lW)Y}e_~utKE<_L zAW#===qs>H+NFwJaOrgI)RmNr*QaH#W&q37EwXhR=bE;rxBx~wP_0#}8%G*VMyw0t zOXoQ8i8jP(H)=AY#xJECZlA{!*{lM`Wp((OZTS&YORwOY5MEdRRM@+^ANnn3WENTU zA3OQOy5)T|{ zY;G!nWz5+Wt(#<-Sr@5cgAv1gCuY^U)Ic+>!QAozYINV2tObk6EFYy2AmNW0n)r&g zKC+K(98CT!SCiRNi%Qcy2?^*e&zkhXK-*gD+1)epREUD@_8y6#qo9GdvS=?m7pBQ` zkL)xGZw}JHFBf3X*09PMs%@LyD-6@bK$YKo{x5a39#&fGyXO3w6XU>6jJg zQxb0!eyd)r8P}79d{lXm@XwyGF;M~{Y|HRvIVJl6MGu643K(3krt#!mUF&&tM z-{qM2sSo2s1@j!}h>Z+rA6Pc(peiB|1BSb{g@P{1VOv-kb2MJ5|L!vR}v>GSI@0<7h7Ey`Iq`4cv)8v39ONQF*(#v*Db zNB_~;W^ZHpEvX>TpNcjEwqgc~S;?zigBbTgVqHJ$%9~EWjd99l7Ya*mEn>GbEF!ic zTiJw8JQJ^ti0A(>+*#ha6svdftjY|(XtMW!CU}#pC^AEGg0!z^e{u6KNctmD_L=|1h_F85eW|d& z+iZ@#_$CIQXazAg$AB$+7?7Q|`Sca-nC$sw5+|$(DDuQc{)a!^hpV0Kx)WHV*$GjO z|LEJ?QV~#Qf$SxPaJFBkim9;NXMLaE_V9$4CY5uDt)dIV@{AVCq{`geO@^4SpUoRc zPVkO7ol5!RI8EDt^|cnZYF*_zKo{IflvWA_^?@N?-+@@l++5RRzEjGcx9-;pT`$Bw zYqm3fyCB@{Bh2RL_l6IB3$|M?yE=cV3>a6u#0604l9gb^&P;UI&K6>^A3rei z_AsGQ4Ph7qJMYk+^0a=4`12DAaYopombf^7U%29p-_H&Hu0FqO+@n`^e)LD62&Z84ykM?K4rpE-FOj2zS#H`pWk3AGV5%vZY0F1 z&VL&c0mopw@;XD4Qf8}=H2Iu{Vc9X=d9#KP5u$xsT}<}l7uBv2d>vWdXQF`>_6zd>Qkqko0@L$$)M-(3;=I!1JeN+rI>;xQSPDrF{CFaxPI^sok@u+J#M za|fi2jTJ73_5xC67LB)FzWAh)!m=%c+%oP!)FAN02CIm?G2@F7h~Gn>I;UiEEu;C< z);%{mKeh{tOdolre6l`Ubu6F4{op|tGN=Ad<=3cywRTiuWg{O(T@R}k`(MIXtG*44-UH&+4)(f(HhC1 zXsTq@i1&P?`-bqFAKa=oz$VRWlUTm*f{!U8!!N~$vB$ElIJZ0-MPoSq2y8Z>$Os?- zGitN8Lb*rCC$GeItn-ey9Ac6UJ+857Sgm5CLje5}kRry*T%TnPML@s<9Yg&9Jc>75 z9_UG@JJPA!V$7TfAcPr|@Es-aqeJt6yVLvzFUY(eR}|GGERaSJoA{bPCayxFF9gc6 z^N|sr=T+@yO}@iNU&Th$wC-VJBDztJZk!3#RCDb&k;!k4YXrXpK0LoRkco;%sq4p9 zqSP9OCLHKHxQVL(9&zrHL3fl~$FAr4$$+J%-=#(cT0I+BR?h`OnYD2FF^**(Xzz2s z#U!p1!yAQ8q zF_PD>c=N0{AM_3a7t9mUp^?$L6f#bY6OSkSHJZ6=c};=~Evb&aDUPk24+w1dPxB>; zHCbF&kbo=5)dNhQ1*Cr@omSHHWAO^|I5C1T@)P0Udp&wAPqepw9%h%6p1ZG#IOnP# z{uXSe?kNT%HP|H3^^N89$WH*`M;j3TK;=ta4?i2?JNb2U@R(s7rOV3j1>qa@j@KcH zJlr;{VG$cF0|Rt*46bb~aV|-q z73E4Wdq=&cryrwo%JgSG+kUsHH$wBn&on#@&z-V!vo&}R{c2AiEHqO?WQ-+-$LQ;| zSzsy5l3p8|_9NdxMK)PB!T>0P7k z&0CLgF@ySTJ=Bov9-*8aasg4`HBXV7GNBwSFWbfYpSUZwDVpY=r`DKG;QAq+)te2G z3jx&_i{~qs7fS+B*mZzdUd0prA#xjI_$&supo1tyP>sGKT!YLB=Cpj>xaiZ2&;OCd z7@~3TTVdi#^s^JD;Do?PT*OL`OohekdMverH^CeOJu2iQJPN++A9PQ@_?i+-l31q7 zsGebe^Uz2N$&&Im!nq>i#lZi3quVjmk&ofh?(5xyFQC{RbF-nue6Q`*?X*_#){G{@ zK6VgEs(n5yGLa=RVXuM-b3K?mqz0qy^q&O@{4h|_%MO1OeV8r4a$+ILh@P7F>Sjy* z`NPx!G`aDXuVsN(6|i63h#SzB>Hv^01!&-CYDW(j*2dAsem{P{o%?R3<|we8Z3{o$ zD#M;c1M7HJ1P6B8TCQRtdbsTW{Digg;=Tbh>ZLgI{R((4_UG(Sikptwn^Z`kcz&IY z0+NRJ^MlZY_n%$*l{qZ6#K6QWkMt{#=`}~c-spzOmr&=#tG8Vx`41nE*dJ5BnDXwx zM?U;=@oq53vM7qIZp5zz{&;QGv8O^S^(hKv?&=`g5=s3wuV{6Ql*moEnrz!8Ni>)P z>1uAq3&nbGE6lGHHl(l1UYr>`$O|3n3Y$mc{};v-(f4;jf;K*GAnTFZ17Hz43@FgV ziYF@6)04!;wB=B(dn0QU9tZzoF)5My#Ad3M^JND`IGi!5bS+h+B2$1^BL@Pt`IAbo zD)j5t9t+h&e7A5eJdOfMt4c+KJ8IXSSBX?Fe}VfE;+FW({Smz{KLO_Vg}SY0Xr^yF z1yh>@=f`l(!w&jrxu=|7G0z1wFqE5uJoW2WH~)%B^K257mHJg-(EIY$j=GMdObJV1 zRn|;yWG0Gk2CwO6bswvia-uxcDePIVR%08pf&{wJ-AYdv*)72*>^j5x#3UaM-E{il zk~&2g5nB`XZu_S3SN{e)AjE5wOs_bC_`Ncj;;$v=fvJIad(>fnQ|6AI{V&`SZ2_}CjpF}SlN#1n&An*VSa%egX%sCk_;{f35__sJ zOE^+UR`2JCbE!`(`mV4sOQK%d_;w9jI>M$c5^Y3wN@(kv{fh7uACHiAg-*_{UV3Ox zxp}8j@X_c)@`B5g4*{rYr=jQ7nnbQeic%Bgn-hWX^W$9FWS`dwVo76vy%ts0>Zbh2UvGm^{MpxM@{%>Y143I($OJfe zEv*E>MJ(+Tv`bAP4K~lx5gs3mEVZ?7tZYrBH;-V9J5Bx-)`^MLk$l_PRpp6?^F_wo zlV%W&v?Ydr?*)k05&BBKTxMXpTaA6|Qmo%`o%)m6vov2j7OvRi9Z-kplQ`yu@^3=f zFd6Ivjkw9pp-AQo&E};^*;RXBXsMp`DT#SIUOwl_*1H*_!HuuEZ?> z5!DSZS)x?A%vwFV!O1iQO%o;fK9I(% z$+BLxXIs^+|8ROTUlMBHcFokLkLgvLEWN5zy-40Pk>a#Pdr7^9HIuYjas)=?c8yXs zJKg~ZJkTHb(x7#`Et7pBoDc1#3##xyM z3}}lJ#$VdHnRhOTxUsU!Ziv*)`uE55lnXMS3zQr&NjQNO(;olsf)1uNe71-LxfUTn z>)M(OJ_&>rg74}GVOS@^kx`pSzJb@RY4ShyR zgpl6Y%~N}Ed?s`=A9k)Ga-YfSiz8Z%{}{sCRT*RI%FPz!@xcRbf2MJM9o#p_NqdNim~b;E?;dRhDlGrPjCy_vl2 z_70gh4;SVyo?cOaCAh=6SOzQ}en{jzFI_0%XEfx8j zNWFIdL-nht-6wHKB=T~D9QGsl%>mhNv>zIk6IJ=yW)5w2`To@u?IeJ~`~XHE8+$%1 z6-iC^E)-)noXcFG{Fo*BHxaic9kEceQ2597`9X1NMmjE_^I4Ed+t8GRH6Y85>O-3x z>hnmv8;jPzLWJ)VXN+#P0)|6EGy1ZcLFp{NzV|oH)>N!1@Uy-{QrJ#+3_B zTly^uFUV;yC^X#J^A>qTXUr%*tpBT)vbR?ZQiy~%TFl%B;!F_^kLIfY`61qpq>rN( zGq1E~?$>2+Qq4f;m`^g zi@XO$y03_pVr*+*)_~0g%)f9}BCi~w)cNz^d^6ql=&8w|k8@#HcvG$iCMHlhwd2Rn z%KB1{`F!7>?v5BeW%NC*Ej(?6t=w&%41kxL2g1S4$HBv+>9qA|%Yi%f`(u%+1YR zVPExc0nRSg_V0ZE{{V^eY0{?v^1qFso#H| Q^5g_ikWrPcku(qaFX2Wbe*gdg diff --git a/css/images/ui-icons_228ef1_256x240.png b/css/images/ui-icons_228ef1_256x240.png deleted file mode 100644 index fe17e2f0701489344ab4c340e1ceb1c816995fe2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4406 zcmeHK`8(8K^ncIBM3zZJ!f0&Sw$%T8=iKKx_dd@#=bk0i%EFk7<0JH0JGWpP3tK>l zR}Bic>WoxGP|D4gO&bF7) zRn4XMDxTFeo?(K>))H$RY+y)+N>QJ?;3Lz5df(}MV-K|dgB#Lz!p4X1fZWZD4S^Jx zh{>UFB*4Tu=#c)yn*bLEz4ZWqhy9|VzFqjYUza&OCRz||gC<@JPqMr#%$_>=b)Lmm+Rie*MssDZ?#en4n!a(58AXl4b93tImrwyu*5Af9*T%)6E6aRa3w;s zM8?Eg^5e48a)`JCs9J$)m>K*z#;O3izL6Q6{gj3R%I=bc>o)kAx_ye#kG{N{UHtj3_0EPYqB-Ypy{jW&9oPaD zyiyV_S~wP8BXm!LLWrMg1?8>6!ugk8o=CZGW%wWZ`gMA^_VrT{HJ~smUj`P?p;)@4 zvkOA78%uxLxSt{F6CcD8%4gi&Cy9KS;R$an62yxc+s7ia6i~9MKZ6m1;KVFop*B7t z1U_(i)-(4vv2TcQV;8?U699HRFPfQHNo%>&tJ!rT<$p0=P~P0G*8Ta<)#n^f3NsuF z&7h{kt1PKP-nNsw8BdlPJe8jhkMv|;?~ZE)9@`x-eI3^ko+ZL35_scEFqA;zeEjv> zlnUhd>d%gDAw`1}V7-3=31)n=*zqjnPz+3Xh~({ubuqrCj}x#|4$o%Z{Rhl63t_KJPs4q7lQKwoA=lUOWH5h_cDz+B{ zDSRIjUb9V>3qu7ZmD7wzZu4{U;gyJ+2*vE9v~)7lJhA@$aHK+t9mv5b-kx%0h>vi; z&uzn9mR5lhfOy!u394;@z*&@V%&b3zLap5+JwSh*&T(1_Xq$#PwdW<&i1C;pYcv0I z1h2yB;pWg@)_tLB#FopS1P8ud@0XG86Tj{kqnCw_?CQSJK*{*ja>#QeC`E_t`R@OW zTbP{?;Vv}x%Hz`9g+wTZ)1BoYcva-?K3C#TCeZTxs1{D)@sk*S$_LokqPKJbx8`N2 z1TUOiGcraEo>);or|{~j(BCHd?q>>LprDhMI_lFt+$kwm!gyXvRo}N>?Wt!KJy4WZ z@&3NT;;O8l%8@mJBFU4ls}=+oQ!HkMdEmx%g+`dd-|Ppw)>}dU`KpCu@$A}wydV2+ zSyh{6ln}G^rYuG4J+`Y({hTno;>O}H$q6rzd1vY3MRB7Z-t;w_XYzn@97}<{-u}5g z9``bRq?9G01vyD^3?U{(#+xTYA}&vcA^={Q`!-U|eG&M3p65IaqU3K@It50{QwA&@ zee9s;<%xBU&lahbbgNilT4ZNPFv&}9#uso>@au@s_gBLEZro%CQLnjEb8OxC(8%z( zV0z~u*FDz6g4kgmKs2}xJR)Z*kPjRzXB`|%mxo?)?Y%rD2G!AQyZpj5*D^LgpH6R+ z3?#Zux}U%hRxaMMJm*Q0S(T?)JhP8y7A>F%b&L-GwswOZzok#uUc{%?Gb%ahxiwuz z5to;)aCeD**ZolzryZeIu2|ur*)-K~hwcsNjnm&9lA!c9YhF~_p-*h;X?E9r+BQhYfujM%VJ z$efWd@Qhf}_i$q?Rf^Wz)rI2)uTs0b3JGNF}y`DL~ z-cKU*=K%=3kM(UQ@YMSE@6e{ylLDC{Tl*WywcyefDO$;^Sxi>%6_Oa1#0ysa!og}} z;G-k+iK3{(nsBt4B?^8z#H@s>y}0u68tAKj>d{lG&oo+iN-Tcd9^w4{hZ$53djd_Y z1T5;lQLutg&#jnuJVYLB3?*v{Z%+~zNf-_ECfSl9RQ@K7Ji=rzY?Kczecj9bV76F`(!JiWMNIb}xvR@?8f>zXMx5f?aCT zIo*(44YEg#_O-6WD9J7w*-})WBqM6Agzj7PLUnZaz85ma|KP%xo)C|}wJvvI$weum zV-4h_OhlCC4Wx><8d;_~!Ms++VP|Sie~Hnl(b?K_H=Q-Qfaw>!zNaa7S_Wi7iU|!1 z*jdBLe}@z#6p7z9B$J$B0;&RJollL8%O2Z@o)MlE0!d_kR~rGE_kQ5fELP zM3r_&K2G3LtQygB@M$vv_x6YX?)>v4!1=)TBR#3epcrw@HEI`$-h}MdYd-Y#C;lEp z)uPwfjGCXBN$046!#HLdkx2}#_6VaifV_+OCs!bZF=9NIiqZ~J7BtTuU;JX?_(xjyQMi{-)<%&y2W%?8liVw92cby{Y4Q7)~Q>rc1=Y7-Lu zV;9L}XR_p8VXDTMc_KBP4)=@9GmpM&)6^R!jiHaTDlUI=#$CaCLNz6{nyU?9$2ymE zK8L*XtS^>kuY!uWop}?^R)_b6$~bcVRT!+)T>-iQeo5b+AM)@1G`~1>X6Zrb*d_2l z7D9%-csM@U&dz|pV!K)GtY`u}y!OY^Iv3$?$O%x~pRFHu4%|%^?@e#_C*+i2N^926 zmUla(sbzL>=(ZpeNVpFPMMn$3mO>wo_<(xFuhfw~{*>+f0ySY8P3wX^g>E(6+5ghl z&~e^lkKh@x5j6ggI5tox`LnmrlG4D~bc!AwET8|reB2pGyFx~z!}XF!e#F<=EwU}6 zh^~~dQby8J9sfImWZ71*xnPNyr4%0)sMs7?v{Q7-EI##!oco;X$wzw_C7o6k6Inb;P!%sFgLCskAFjM^e7QgR)yYKf^-Vo30U`hV#DA-e0cH^r(&@J5;N}` zPkl9_M4`Irv15hT8Qk9$2N8SzdoCxeTTXITFfMw@?I;z`XuNxLV0Z0E`vznn@d=Oo_%G{$H=#D_YvGptW zOEccI4EsgPee9P!m{a7!XgzH{(m4X{3_6e1Ko<=^613%E06ABy_qG~#_8h%|CDP7q zpSj4Xu(v$NzsX#nNa5&lh|aaH^5@SQwcrM94@ZoODtx0oACHrg4=p{^6{N9v~T0l-&|?`{wM83aMWyeSbG9i zFy=#cdr_;u@M@Fs$}8crE8Hv89Za~d}9lq!Oyyi$c zBjWDl2BrY8$St9=uiSq%>Uuf zY}PUgpGegwh*P#Z(y=}JuJU~ZP>}T^&vF%b>6feTB^^5>q+vm;>5)968OEmR!91L{ z50OuInSztFh0o9$JFJ^EvKXw+2c?T=(TdKap!VsW#KZ`DqN8Vu_)PBbvwnXf^_2#fsF(cn1gT3)tqV zrrZEEaJ5-LS0a@DnI{j?;3qNmh7VpFpIFVrjwLc<3u&OIZ4!AZT jE)BU0YbhxTZw^3m9Aj?j1)HvivoUbd$ilD|i;w>w0<$sw diff --git a/css/images/ui-icons_444444_256x240.png b/css/images/ui-icons_444444_256x240.png new file mode 100644 index 0000000000000000000000000000000000000000..19f664d970194372c3228494e34ac01d611a4d45 GIT binary patch literal 6992 zcmZvhcTiK`*7uVTieMljO?vMobcpmK9qA}Nbd@ehhkzixNhgR%Z_+_J2uKSZY0^I{Q28>^X z-Kd=7A$Mm$)*32G0Hp<~qSm|BJvY_oukWntzn>?AuVerKYG*Yic>|vx`yc$B>{J5# zWgY4?X69Mj-=z&u7@jg^$r$V}%@ODo4tWucu&Y|RDwlE_)+P_|D?p&7ddl2vpA@cR zSaFs2pV+o5gSen;OOf60WSq#pD^%B66c*~F;Oo^Grk=KJGvKkUog^G*Njg|;X8Q+L zVKcEq{#fZw-`k*Ll;kn$T zR?3|P!T#O|%RiIEP{5+J!S7S>E&J&eC;)Y!`%}T}a~BCRK~$SEpCC5q#|Y~NYNfrE zc;&8?BZ6%LU4Y*w0LQC>#9plngzxt#A|P;~OAHvov;p^`g@d=@a(dqi7`C7apL~^+ zw!x|E9POIvoqX78`+0Ny#%Xs3rYV3u?iQgwLp=$D?r(OAEdA11aV^Ef3=e5(VOs+A zDsYy6QYndjoJj+AP9Jur^?H5;4%3(~ayTU)>cj<8oNlAoRc{T$rNfrGL4cWo%rz(($6n3OuelM00Y6V7L0-3jrdy;k7{ZTnCG5fU)T?X*izfb?#hq?b4+*FlYCds|v zw^>jlD(RWKEJgi$8XyB(6M~Zdu4Vp zjz`5Ze;(9Qp8lYvC2n(PpDU}#p>eqXyIs=ffW?OHt;^5YuR%;DhX2UlC1bcl2_YCugibe70$6C{yYXhk`KC#Ib-!N_V*A#WK5 z!4uqj(UlQxE>YI^raVNWgw&lx73ehWEje49z#t)#s6npqwt2QNAS8r6I1SD6Y0k9E zOtB6So;GK-+pBl}jj5~QqGbtw`h?IgcU3XJVMQ!@hD$!9k2u9Z(A$t-?QAlgNo0KF zg+qg|H%?6oFy#aI9dUa<#gH<*{Exdh29LNDMfK~w%QOYdOFq4R-&rR&-4`-!kgdTf zI#L1JCw0>9DY9NNsQ-*V@8O-PWE5`RyYE>Fwi7|3b}Gct5ZVUK&LJu)@(RI{GRX;VEiM!DWZYGK}S z)=Zu0A@bL+oS4?Lmyq$t1{!E1k1rIk=B$wl_lJcx=!oMJuZ)sK5TY^#dEr)ae*a^P zvcJM%g*S7q26{v^tmDdsFBfgY?A`Vz&tR_tPYhy!b@W?9RtAWTfBVp~PtZ7;S-YT3 z?SId6d(OORLE|?j(ZX4fvsP?i!Wkbej42uAc&9gH|F&k-ijJ)3PPUzL#stf6GGqmZ zKjihG)EKl@K$pYU1Q*SEH1NNI1mG^(M@@NW-qyvuwlC7<|(F zwC}qu*jV^$N^qjdLmbF-ipQIPr690HhC|8%iPcMhFUY2>quH&9J8KeJZohI`XdPk> z&=Q>+l}w!D{}4)}xwoWU)$Y#2wgK*H-(0AQg^`cGv5B;qR-f%rzH=NPPRCDs^{R|T zZC;81h1a$Cz$e%e8F_CvPK^aW7^W2F4PBcM`PT>d&ziNcNyO|C4q)-CzuH!LlwK%l z`FnTjc6x@`)%XwWSS*rQg$`Q(_TU$&nbU&P!lv_ouW=tA9(4I#O^;fc2k7tMC^V9C zQlRa)zBm$(#0wa;UN7t~Rt_>Fo-)O{-a>e5yf7A1YZK#9I~G&YA%4WU3!YYu#&(K| zsaiz3V>Pa*$x9f4d&3`Gww$sf!wpy2d8w2Eu$&lK+80$7N`c)ooH=OX{81TQ?r8US zFL_-)%P&x`fPfQVCLnUAo?FPNu5Ui>vzJQ0+&JS)2vcyG!buTjxkq9{|M=EDcY*;; zCT?7MuD_10>C_8-*8u)LqgHu}migjxv6wnBO^V(U2b`-Q&60 ztv=%V!#;6Go@!wQcK3~^?E>FDXaTkn+X7)#dMM-8WQ(P|aEz#} za>B2fHgg2t20T!x2{V0BQjih{2bHPVL?SgYQk-SFK8Qn(*02!Wtaq6(e1INvxr z8TuT6ql4jNd}Y0qBejsK$$sIZz7LX@Cjl$`E(;lG`>6VcVJ#_d*c9ojpE@#_IpW=~ zt?uO;cc1nno0~Vo1gy!D3PHo{hjUDuOHA!^x`Nw-VeA%uksuh*2uP3)|F7$XiVc{d zZcJTt@%~1hxc~N@^Ntx#5@5Iw@@2?rBSi*LbFu*SF#5r#lNP04QM@vnT2uTos$bq> zll{&72D^oEbXQ8lNx#jGyB57)3m z!{$z!MEP?~iX;-jF(MEVBIH=_XXR%1(>mQy{&XF~mY;2$*D_xz3et!{LUR? zPwV+|M|&_@X2cIA^n0Pt(F;2n{2w^p2RuTGj&LtVE6B}c$Vaeg0sAIhTXBB~CFJ+R zlWd|0Ov5TPBrAmZqPjfeUHW4bw)03aKV60T9$0ZxfXyWlCi3pG#?w_jb$OA+Tx_7~ zN5RnxuwG_MypFL-_smnbD_V#zzn1kFDTj- zx0(VRm~n%cUP~1dBt^<4T|Y2s9<$zdfu+@tBsAmvrFOQvf~YF1nul&b)7PTJs|aF2 z++qdUW01Q6M&E9%aYCQ#DhF=bv5gb~Ngv4d1g$SlXdh|+k~x+bCi&?q&RwF?q=3@O z9U))V%fR`?S86B?m?LlJ>t#3y@2-oy)}RG;fD-So{;mp9KHcQ;fy}x=Y|Qcv-y6Rc zHO48UZ*)S)Y97$O9zM*rf4oVyFf4?A^Ue2$s+0DXh9|`}^vi=IZs#AgjK7@w_zI_} zF%0B4o)WQ;M(9UlshF@EUf1;)z;HFigR;&EigF42xMOKWRTN%^70r4_=V!+ouBb}V z5X<2}42pAy%{lK;QU%BI5IX@D7gY~=9KFN&cPWeMyFc6_dY5u!Oz#v0mdhTbNYzfd z4jv_%tYRMKo{+-NFlxi3e!^)JJa}07%{L{^2C8kF4@mKMSMm2gVwkz_QuG)ik z69Q=qb!9WFTbMbOAnsMm!6oj9QduW~{MQ?cB6> ziWQ4JTFv|W&n0G`D~t$_1so{`K%6G*sGk|{jaY8P`{3>{8A_d^?3wgHTlMY$MZ9U`Nv@l z7(tm~o5lW~EW9l1x6h3T#iS3nin;OAcNaRpwKffN-9)X7|CMlj9iE6x4o~ffC8pXE zIHZn{s4U+hmJADmnNy%DJzOepE&{<&i{DWOMF8{ce*kUAR)mGB$!?t?7NpCyo zC3=m|=@928j^QY@k6q#${I%J5mgnv&)|VsQ@pY8xUlr#PN4|FF0`#jiAq)LIa`nWM z4m&n4vzMYvqeOpPVrb}c00CgCgou8M$MdwS74=yiqxA^W2;#?kQ@2^!JxfHH0J;l{;nF@f)IP@D=!-)Erp^b#=%H?@X+e=aS#bi#q(6^eoqx!< z9^4*uDQ)h$4p6_i=K3uVCOM=F@9N>L>pG9rSf^zS^C8aFTRmlBWTaVBV7oSVa_3pW zd)4r9?H(9%x_Yj-iEO(q9KcD(W^%etUp*~qdT4ZK63b}kIe;J=wlju2vlFDIUK}` zD3Q&tfL2jmpL=(t(}P7$s|F{IsO9#)oj(z0A)JUtbzTK!l&t%ma<~HT8Mp#}md0dR z(+wy0+S=Yy+HCKP<0#-<8nPKkPDU%q{lPn+=Rahk$EP^w(LVMj^~$mPosagAaVY}! z@jU>M;pwDg%Ck%X5ifKcjxu z8_&4z+t!CYW&_X{qhCcsNpOhHn<_D@$%nfMDxDJ{ZCStXVSv%hTW2^Tw96F#Xbs{q z_vCEFesgku_@3gQtO$pjuE-p#@)C9p)&sSTw5wWzku8o7Jos;dv|G#9zRuy*m_S2T ztW$-v*Y?{!U=Ml1Gjw+YcyHL2J!#j-ux!65T$p~fXLI+8w^j{c?FX<9oRiK>_C&q| zCk4#guU6H>p8pgGu`?;1ugi#x;TgQy^ZV$KDP}A&SY)lsHsqVxDnVKGWp}nG;ZW#X zYB$UF+3b0#HJ@i@@$*k3sSo3o93#D5@Tvd5|2eV-oP#eVFRCse}zYX9D)A^U9pVet>L>xV%@{H<3hv#BS= z&4AcqvFnCOQ3-kRcGCP3{P%%6aJrTr1*cbr3#L61sa)eP*S-i2vL;^{A2Ch=m|mN$ z(h}7apw8;HYAJP}kZiedLf!SC8maH#92w8jo6F_K7N(A*S$FUE`_v>}vILMJMd7*M z<<_J6%?t6yHyMN|uuatZMbH;W{$BQGW5V=ZR%$CkiE>X2-6RHZAj~NJgWVK|<#eU+ zgp3T+VJLjMm9>IvJXz;XGz8q?SM~4Ax?azTxH7p+zT#~lX&cB7|I6@`d2ms_y)0O@ z8H9~y+1^YD`Dh&WmlMDUBcAYN9pbMx%m;GVJI0V~6UpukY$`_1KG-kp3`z(JM~&Q=>iIBjxH zM4R3?Anzv9$$tX_5XZT_*OgE^GxFU6Me$f_gty$2%rvj|QPq#lFMkclnFp?g^$jJR z`i9XRF{*P6+mWWeMtSUe9}(Gd^TAaIk0{oIl}~%v(Tn&}N+MI7))90!3F7XlN8sD~ zZ(sWw%+P2vwAbOSeaR@^!d*5GBu!HY4HA_MorgltJ0mUL|4@8!X&Si#;;$=pL`Gh( z{Ni68{j9XWtfaJODO?boY1e^!^*;0(@X;nN;`vYA7G9(Rkz9ej;T*KFhE^b3HYbK; z*5P(+L!n-eF6nAiagjZLSt$P?R%eDu*AB(E0ft{* zhp!Bte}C_}yhS=~!yumnu7nYpakjnfW;otnO!hw?&LzU?GUVY&(4E~p*WOfE%B+<& z{d8HcM^y2UdXcq*_ElnhgV`z6Kf&v3R>y2d&?TNOAPwo>$3?#@ksn*`2gk)z`K
    `A6pHt6IR6lNF6^&(ovlbP|R$VC3 z#Kj`X&Lyxos$n5C#2uMfZZsvbj)i z+$1(n=6q63)^IOf+^1x0R%Jn?&*qGX`{G5;8i!QR3oXJfw~DcSI#S?P@6TZI#^)nO zwBfhXH&dU8oVeLDqb6r-R>&#mCM;g!tPBL&yUn8|aGk2Z;+<@plmQtZfs}+*Cs>)w zJK)hUcu#Axe^!n8SWqhu2KsCjRL7fdv|8+ohg@hb&_b}-xyXe1kWcrO41<^<<7&ZQ zz`{1CCxUin(^K(w@eEMT(^}Rul1#tbP)zHUXQs;T&4gaG6=ckgoPc(7n z*nP2UyM#N~U!%DWT-%~=m$RLWZ{BbaHA2Pw2Jkcvd9BQ+`pcKWLIsiFyNfBO1oInH zC%Gdn^(k@+F)a~?>K~};Kc#WB z^{nk2D~t~niv&TrKOd+v`FXMR=DN+SgIjGD%g1Ye+s=6>ipoV8mP1RD?mg8&DMU=q zO^zcP7!_*&@^Np)=&5r*;QialnPQcsta!aRkF70#VabT)k?5u-;YzptvmB<#WuY3R zCHt}suyr7}=u<>SA!(YcV$8U;juwU8^~d)`Oo#4yDH(yMUn7ajXIk654oRQH1d5R5 zWQl6N4<$AfH86-?|I)&>?_Yvz75C_;;V8U9u;+dh-hiJfqm(Gh|2>&!^|uw8Sf!N& z9WrD?bX<1ttD2$v^8DeVAp2*9cB(6Un`IlxfzXmt;l3tj(F!-4ZXJajoUOha-mse| zA!`x7DG7-qhC}*iY;B^j1mbc$3RLMO^k4L=lXULiXz#zsf{Fz|)4^5x2{aQ>Z*5#E z?vdX&8)WO?ucP>9E?pIzt1RwC{5rPs{h+hGPYV7P+>A3xJe?O)$@6T{rX>PzP+?q> z;;#q;(=T~?W`h>R_;Ks-_~}fi2}AdrFMFIdE1o0}W}xGQU9x@`PQY7H{;8Qo?e<{~ zpKwEX7LjJY8@J`OP0iy!ifw(LLS^?8cYRZLaU*tLUX+^zUp#OYztQ*IJJVtGpa=vt z5kUQuI#p2*!|A_c$k4oQv63yk2dbsn%h^HtD>s48SH&Fx^p!EU%dtP`gZW_eQ~lD% z=1H@F2j8g~?=|<`-+VUSqGzqu`pWji0Wn_*0&TTAnpzFDp}&mJKU@JgOf_ zb;2;7x~$&YR>MwuN4Y+=+#p^M4MJWZpFdmo<}A=FxP1T!{_NcOYnv;lm?Pr55-@Rp z_Zw(#Yp@9w*S~`|Bbq3EEti?agR{4d!p#l31aBX|JVE(dSOq@?uUcKRg4>v`Ea^WP zvScvPt5m&LU<@#dVu1HHWDd&M<}MA}MO{nFS4ALwSEZ&SkQdw*mL1lF(*{jkf#MMB zDV>%pS4?g14ZS1Mk<_u3q$@5+yn5KrVLW`$+I=o&gd}Nxq`=Yx|=X%|V^yMSA#&uGDocw=YDx0(zS#)KM1H zcoDN9KqHH7E90e0OzF(xVRN5$N2dQJSx(Pi?9tyoYdK*!kX=m5UqNr4#V%tnAXyf7 zh@#7GW^T>=a7yJ|->AUNYkTaF&Z3y^_JZa&v|Chg2WGnNI7g+Br+G{N_$LYQcblb3(QrR$@sbar5n*kBIjSafWl~O) z!}!-exeo$ZFhbe1GXl>SNQJ|K(@!Nl$wETs7nUKenZX|bynIKP@Sc+xXhW`-WP{Q# znGJRZ{n8~pVjUN|nJx6cPp1T|E}cOX`x_uF310qRZ&nkGYt=14@CQ^2_-^Syl*w#c zhTX`qiW%(KoCFMjV2^Gg)Q!t@HNpPl-)M0MK-J{85Q@g-{rMHerkBXXsBTNiu<+oQ zN_*2Wi0&X+{H=SoHscgP)2!BpnBWcAJ{dRSwjilve-qFi$_rqJz%pyu&v}Z&W{I9g zD~K=^RFMj1UI#(9fbZ0Bd`?LKmDIU1dz2C>Hn(By*(zLjIogqGttE5VO@Opqm8e|j zn|*e5+1qX1hqVBw{@|Vnx#PR}5cHP1IXLDTH7(IIU^kG^z0C}zV3r|Qg==OHDzt3N(HOX{{aG= BEO!6^ literal 0 HcmV?d00001 diff --git a/css/images/ui-icons_454545_256x240.png b/css/images/ui-icons_454545_256x240.png deleted file mode 100644 index d6169e8bf9389ab9b5b7d2c6f0c5fe3e4d363105..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6992 zcmZ{Jbx<76vhMDpfgr(y1`QHmaR~%lf_s1jw?%>l3$g?j_W;3NgS%T`aR}}Z9Kzy3 zmf&*v-FxcQJNKRU$4vKBbyv@PGu>13O_;i>93BoO4gdhaQ;>&f0sz2A6>yA={@BZA zI!itZkeQMk1Rz`XI+62n^yHKL2bV`F|KIKHpzRv~fYM$8BK6MW*I@?C4l4JhU&5+Z zX?B6>M{wc*nf5t->YI0m%k!c6iV6D-HiA+N=Q{Bvn#`}m4j85ZvFzIFY|%J>xxbN+ z)KhhOMWAack{rDyA-k^9P{-{Y^-4&T)}WQ|)zSX7U*Jm-kJzQ#7H}DD8x^^@Hd^Id zO}JQr`JnFakU}mtj z1A#B^`J~>9Aj!aCs_5V1v5fm^rO5!bpr=#5Ec4gVZ+s}W-{jz#54|=utEYqV2QDVqeWLoK&SDCg)CL8cuq(~CMMZgT%6AWTd@*7 z6&71eU>T)Xe>ATJzEE*l7!d_4EfiRtlm6_$KqoujO=FRN&<+w0vNs>&-_Ed+(5C5$ zmkYkr=0{kY`cMqEqv<( z_g<1p@{QK8lz=aO1*Gv*be8bxc48H*~?C5sKsk6Y+G94lgYv>T~L|y)zUs?t_{Tkesms}ypzUDGt zRKP9jp1&y^@&RZ)xNxKyc}wp*1n`O+;#zYQ0#N>+ zwL0RCF;cJTN#50JeIN3zGCFJL?|Pv`sHdd9Okxo&0#6;1k;Hzh-x1*puL6C4Rf06- z@a?%qB!?Rcm-Kxu^;0S$aw3oi+ReC&%oRq{3G>G&kxB}sam-E4#4k>x`2Edch79Z`l~i|Vub!Rci8uT%U5Qm- zqzrUOWT)C)V7C0Ot{iK@**yiK6J*$P=vs^p<%g&3l?e^uQP(SX{G^~DEz@uDlSwWC z6HFn2>d!B-S;p7EJ46TDw;VlxIsE?PQTPYF)AjM54|{7S7g#H5O?B`H**ZP3#Rcm* z14Vjb`0wAD5v>;=@y1`jQyS87ZX^RYr;e045q~!y3PGK@B^ND$Oe^By*Pn{6o@PbJ z!lX~i+!?Z!hDHPe+ix8>vxm$X+}l3qzB?$x6vTUXWbL&MukaQe@6BjA#BLZ(saaH| zggtTHoi}V+RQiLAF|y}jtq~lUu*VwWLl%v`4A%T<8BjfHLPOI12-{xii)iECHzfHd zr62uYRj@yB%Cen7i_&0M+83`9EAR~5s_^5&9d+V~;i7mZj`xA~1xnBw*ODz`UG%eN zwZ3T!bX~sAc;6U3Vl=#&c<0aF%RcBv$yV`;1STH=o*?s*_U2Co?5UH4rIxGb-_86C z0V)FXqarc$Tp9jU$_LA;l|P&~n77aoKeiVuBf+GjpUp$fXB6kUWgiU(@RP6-ZEZ^! z6&A#>(=ZWB&pdpM;h|6VqZAmie8G@Fcf$1v{(lni-_NX(c?|LZXW$5{s?(yKDNjFt#Y{0GhcICF7V&#Q=1gJ<&oth1v|juBqBGovPk-dcNT(hWqc zWJ6H4Mr-_$uiQGVcZ-Kh6@v_>YV?tg4|uPt-H=98>l5Q)dqz{@eqQ+5i>@XOx=Lw0Xjqh0_w6?9r}i zHz^HH<3>U^Z|^f;3Ltd0j-A)8wr?S-+)b`ua{R?dJbK>}>9YdzQs=FH5)}5q~n^@mpnbkDf>;vFH7gTfo1GuAqIgTs<^LXm9fV(}{jL}_UeSnA>mB(5 z*^HS7)v)lv+8RF85S{DN^}DAJ3$UL@K!=hf7KRfi<8`F6N!2;flN(lV=4S+jIOp$ z&wiGBqp2e~U)Y*0XNoN*D6?F9C?0~OqzKTZ|CIO*v}MVErdyBA9oC0?Y9)*eri{4v zt17yAMm?p@V*cb#Hvw)krZA;q@IpgTXX8_PoUdYZBVTm!*+lbrHvpo=gZ{7Ug@PHF ztf)&_aQ*FG3cvp$*nZCdEfz4`hu8SiZY!R){{6K*wg|@|8S79F+w9cQ{?B=>`ku`f zj?Tob2hPNf?6M|5^{zL0{x-1?h%b{7uO!`AjnZ#vx{8VYmf}L=)Coi^a4gtedw<1E z&98NFQ%awC#S$?PlDy6A7gUAtEOwf6Sub2*l}b`7VqbPLO7zKEBi+3)buf$LF-a0! z-IDu7roL^8V&MvTlFnupTDE2HrkpfNtkC`xk!nfmAWQ$kb~wBHTLHj)w{aO{pHet( zo-t4j3FMTX{Z2ibk_a&om22FD=D2D+L7fOa-^2!!;`&#lgUhpn)<0_`P zPAvs$6X-Wo(Xn{=i6A|sYBOBI?JZNFts7##k;ah^QKUbGI$vXZW(eBj6Q1I4CQjPm zp`vkoMXCER^+U(lGG(QG6C00(0K-*e&q_Kb$V!DRqfj(5@@bgKCR7{k9p%>dY+)5d zepzE*kZG)Y%=Wp@W6NHyjIv-KVWidJy$;`Fyyp-ZL@k|n6d z-ud<^O;$1tvrmX&CF`^y6LQen#4l1DbFF=xV*Yko(y@L=4Q9N#dHKuS8Wr8@783TREimPeayuWEgh(Glh^D46x(av1+! z=ktQ1Q76vNEP&P`)WfsgTSyflWj4;eDSyt^%R~t!*1NQk333xEZ3xBQBShRxrz252+a?C9G|#oO^9xDr1KUz&nUjD8&f#!VBPS`fm^ zw>P4kEeImD>VXgHnOPp0i||U+e(-EYGW5Y+WvPV0E7E7u3QmP97?d z?zh65fjF@N^Hj`Kvw#meID+CQJB936N&AZ(oty?pl82s38*l2mW@&lEV3(rtTdb#N zx*h>C)Vh;#NMk^vQiF-Kq2kCP!@}(Q%!oys4KG^=Y|om ze(PAL;ga^D@4{2ToH*SqLxjs(rN!;|qAg9SuDPfDHc#+fUE+%1a5gSHvD!d&2l8zOK;zr_$Z+%6C|;Y@#iL$H0Y z^@`<)>wP)#c`2-;>1#!*76jk2r7JjI><+gry3kWX4ds)$71awl@jgwo~@Ilplz0QPW( zX;U;YX`3iK@B7RgQG8K};*vsN(XM*PT7$M&OpOuawA{WXNSjI)J;UI*F=e@+eBbl+ z#fE2F=g|oUATxJ}Dx5GHh${qqC~3}@K6K;x%gA^W$d!B^xH zNejV0tgIQ+@vt}JZ7~Xq2H=W z0$=)iMqAv`u~Ja7#=bYxJ@bky^*YHB{cjxy=HKG!>^(xQl>}3JV2B9yOS!*jkZBg-Z^~jGb zq+rflL_Oy^Hqtv_Rqc98JA`0C+2!&UvDh9yxzT2s%R4FjMRLdJGtAX`n=F)Yw%r7X zEEK$}pA-<568b@uSA_NLi@GRHOSiP0t@fgRw{Qa6_{Vj3@E}v%t?ucIDFD5T!5TGg zb$;4K?M@B3##@3N2UbESO+uxFV9}S!?~|GvCC3)0PQ{t_pZ0q+$J}gsQ@{m8v!^9D z!uk!rV~%gr@sb@iQEC+s+Q7MbSeoZ>lJ=P>O>l%uU6C}CNWihy3-Mnp`e=;jtEFco zB*=DczVn^bRSez9S|{9}z&#^*SV!vJMrO#Z-p$l4e?39lFmcc&-8#eIlGKk9U-@Rx zQ8?r7Hi2K3cHku#z%g9i#S?^&J^1xI+3RVN<5h&32x9DnAsP}bi&rJX;n1QjO4{IU z`gFZveK0;i1m*<#?j>t#+r)}_(Hi+!+L>AyUSO;yV4LgWBE;FP} zXB?1wA8H5NLIQ-)9-bfwYGy~Kjnc?oi}k-MbtTcyt$AMgYrFCC9Vx@W9iNuAh+W_C zP@Jl$^SqK8o~%R!cDy5xdyma=sL{7yGB!8*p{pRS+G1;X=$-Ml z(aNAbWRU>^IWXqS_f3Je18@EP?*LiQsQ&kxPg-8VrEw+mEw$$f8>*?f(Y2Bjw97)DTN2-B>D7QwR#`K8n?=j6Q6}{j2P>-q6?B=Q=7lc zRHYqg4)nXZ6kN5^UsR-*@-liMF(NixSw-tOn1(cV(O0q}g|R}DzFZN?hWIExV60HT z+OQV`iRJJ1q9SXlBeWwPwyba%SfrT&xkBAO2e1mjBRe67vIEi*{p&gOwGA-*@h)g} z&gz%j5(cDCVB-J*MPMxpo{Kga48P!aylFLNP{jHQ<&!B{)6ha^m0axgnrIV zz9w!NG363dJEd*R*Ls6LrSSDOWDHIi{^t zEE3l>G=eji-}6FFEho;T-3N+*lWM%swipv`flWB_LN+e6?0S#2 zYv<{a>K~iF&*C?S70EItez?Omk$QevNDjc#AhSIV6`Ko+zNfTE@GX?=F)dZETbYxe z*A}X=Z~pxMmCc8OCcyi4YuY z4X9)<>d1HDCn!(%kK7oGN5RgUXWyzvWMO;64A20#9;hV zjA!3^9W|tk<25zIOgbNAbI>DRQSNPz`Xm{u+<-idJD<&ZK$@9KFIcD*Poz(6uQfI? zBtt?r`uM(A4J?4fTR>9e#ua`GAyOvU3!17$j5Q2JnP{%>>sPkQBa*;+2YETi(`BaL zNL-6ba&k3EI%ZUCR%JK=w!;*a&a%|L8arxN+ocAjxOxHevxqvcl zyWdPk5!#m6&TM7Rz}@VeGS?0wx|~@D0g@^iJbk3ara@H1!9=Ijl0xTJ^k+giPbG%) zyaVkjG&5Vc*USApUEd2-%_}d5{KJG@CLsPFn(q{3A`08*@gG3XCT&&x5lbb|e$M!M zv754_za7E=;A|;&Nj7&)PdhHaTsN(1uq9+Y-VKxfyQ3mnx| z?5!Q$`)8Wq1TzHZzD}lM+pU6}x}tQq()Mmwu5S5KZL4}!#&6)m*^@N}$phJY276T! zFZDF7SG5QpJw0)3pph-X(h;_ zUU*UTZEidO=VXj^vvkj@wB!B#>4{K&fKAIKOUrv40bNCgBX*s`r@xSjf-ZzGy5r}& z3P+RfVUB9XTlk9tKJaV!hxZ%qtofSx5A*=v@Ae(PciD{cUxrLWfH8;ry`cG>!KSpR z{yofDff(^S$&^$Mw1eF=jBKA9^j*})vouc)6JIs-HIq9g^jpYfhnRHfTd<7SS0m;i z$;&b#3D;0tzPK+4$<~x1q8pgM5djc|@GKz!{?%@4_wFSrDgLB&?LgU6kmGu_It&fF z6CVdmFpYnvgHzqTF-h6FOp_-!9+2*x8pD1^MZ-wLY{=#N-s3tSQaw@Hpr+YT#d{|k zcDHAs#6Sd(&;$}0y!!_rUr~F6+V;*E&TgK^bs4)So_HDILoRQ7iBH3I#1=P|#8F$x zE1H=2j#Lh2rxq}U#J5j4 zG@Dw^$S=tpC%^jM3D6PG*OLEh`;MukDe)cg?EV*d_upAZPZ_YptwyKXB8M?Bi>N+Z ztl)JuZ0R4*5k{XjU#<`c@)8#+%J2^fYy-?wG!0e6W;R)kclJqUa37(Y%IbH7V0r>6 z&=}GBDflKC323|mlZBL%xx9fl!8p<1Sn+}8q?)njX<9{dzV@Ks8bqf| zutHPq3mfmH#uxxqkv`y&HLM&itjaWfgvW$+8H)r4`F?~P%tV-W`AA^xJh3nzCj;uI zw$?}ZZh;R;*i9euW7`k60YCg-0a)2KUU)DbeAX+ z(lK~>?>YDPJLlZ{$9~p+KA*Mrex9}0`tH5HJ6cCenV0}d00027s)~Xx0D$hgz#$&? z-BU69h0L7*TdON800z}@5vq4@_#P@IUUyREznfCOvlIY89aR-%^?m1my$^P=Rrc)}nev#FJE^KgXEw7+V@d`y^D~bgP-i_Wna_cf}GegPJ!E=3$vK1!@mU6ht2U zuzbkL;UpS*N{L?pd})D%hu?mBszca0;{P^PZhSjZ-^Ji#klR({E0N8!glp&R}8On1Zu%Vlfmq>R|!%`>K&;<;^%`;tgP#y z3VSP2iXADxc_$$Gz=<5_^^6L0bk<4N@8bJZV6X^^7z~yl@c!`_fj8leDu_{)9%BxZ zJC2H)z~}1L_RruAHeI$Fu4yTBDcc~CD+CP@j6Eona$uCoU)1ClD$=cT&x~N=-}3jj z>my6Cz!y4a(PJEf);q0K7s2+!Hq7!~gET%0 z?zknmD^VA{58Nl{y9Vj6*t$G+1bI^j{6Wl-!hZz2*-pl4J1n{CuouyXXaL~d9EO*W zG2!Z1|K+Gg+l=bUc=LJ}i0187QG4uA=ay(#I)tRvv&XoqJiC{uXVshl&6sQC>BDg& zr_=S1v!^oP3)Z=+5__#d=*=*Ryu#7fg)Ti;It&z2s%sMKbZ_HbZI;|jy|s#3oMV`<)U zrzxj1(AUp|W8#jcsCPaQibx_xqrZ==txmeG_hT+NK|H`kn_X0v;mu4{Xujk-ghs?gpz)|O1n_bKTP%PxX@D0t-XuS7`#^4C`Th>#}x1zyGK9B zYfZyI=p(b?aQiRyZg2~b>GUbdi>dmlIF4xgY)g_wz=omP06r>Uw9%5ritF949jySn9 z)CFQK;NRCE7uZ~xS*euaCy~WKf=oN=h%i8(=9kcA?q)jpX=pU0zb>oUU7O?lva8Z1P(U zgUClISn&?tu=XLtHovoEHu)X;9ibq~Yx`Yze><1aEM-IA6JtO$1P>%Hgt= zSVxu8I$eS5g*}#ZWT~E)aA_cfThs%ye280;g`0IsHmtt3mW?7a;;v)A& zn=GhJ&2@|S^S>x!|44P~TQvgP?#WPod-Xq9_b+iCp$$Cis!^FI+QjJ?PIVQS3)9j! zkD^?1^?N{D#~79;E9P_LR+v5x-P%pYQyrwPEFHWeYWa?PD4vMZ(W>TZ@k`6!JGN36 z`wJpZKjutn(YWkGDXdyONIu^=UJ!G{l#m0 zF3Bz8!G6lJQV&GWA@TCogngMqdq?wj{c5w8L3Aq!ftli2zEmE|TN^=Br4>b^ju}}N zKy@kC*vveIH92ocwvhMSV?};NqbUXSQRrG zf#X81tT%#L2d{@ye*A{gJeE>l-IvO@@<7ABh$x)kZi~s#3mxvE2_uGO!|W$So?p0t z7#0ZrWlyQj!iKY$JEnhEzY3UR{6pJN;zg_ghS7^Il25+X^B=O_ZDigouQSc%zW&= zuzi)yz6VQo4$3!`-^6%OSWx}A?b=KbM0Huc!%ym_SvuzHO*fpeR{CRj^wL+M;oM8c-VS0)JTtK;Tqg3Z}aoFXBbDRfRdK8YVkyg6C&f=)S z>CeM*j&(=1XrR11XI+G-x-Tt+vBuV{aEUQEdpcs)K1#AT_MOO>J$4>JMiSydM=2Ay zbU8;l$JC}z%0eBzQo!9M>&CK*%1b$?cCaIM5b3=aqd$P}`{j0Jo$+h$obk8%hllbF z1Xi0YcTROu(VTJbCSN2<`C33&AR#Q*taUGPx+j)gxE-@{JVXz@=Lasz5@QlS zW51~`%;Y9A#wgv5_FRDyG;>)VgXzY_uFa3R8W~i})Wmwof{MU=ZT|3nlYTg8yE?~c z!@ZHK_+GPImT>OygJr{XtWBO014&@^K^xCk#>4P0wNaR3I_!3WM55LGsu0DqokzYX zEIN1?)-yIg$r&%n+#gnilxJPkqk>&Tqs|RpTAw>N@u{I>U}Xx&ba36HOrr>r!?Q*E_UQb}ZVN+#D%9LVlfdMh!Z~)o zL)*2gC^5>ggK4?%N4i|=;OofUFH!ibGhr)yPVBlEMLdGDbAeO5SNs8tYo$ZCR^&iJ z2wZdyEWy?B_aW>50ne=Y1$GVxl`JJHwX4%b(#&lc(G@n4FU2`q6YG9C88(65rNbaz zS3X0KS8OR?$=tgjRKG@i*LQ2Ay`*N`P*N{8vCGgzMpcFIeAWOzifOx4U!<>+d2f}} z#m&Y2O}lo$1fGqrrGE0l=a#)8NbKg}N^Fi&$x@Q(RrKB({s`rI%lyWIOx@vRfIm4q zVD}M20??;YeMZUsz?YcS&@2NmM%K;u~hzRl_ImCm>5-cr>?Tp0s8S((UA2c*iHa z5;4NVe{5*HJNO+Ha!Wol+Y4cnbi5UV=h+alPhCze@=j<|#T_0Gyq$KW%-8@g*>uG_ zkZ7gq8KswR7Ar13%59)q?iZI{U-5lQB`ng3ZDz`D?8*c-!Zay*BXe<(CK&m05J3f@Nw*a2B6ea+PN?ABU*hB5wMKGW!V%^|S6I zXm7?PFyk$Cj^*IN*F9}5?U(AqI1FoVCJwZJt1u5SlZ=BH>_fyxl+DO@0Oqe=z*}%5 zDF`%&N5!>!VE6uhV4Ba7`Le;MXMgP(og+Qz&4r7i=!OvC#GHOtX&1ogp=|crtH5wQ zC>6P@jBteg$4ZyHh9rH!cZDMig{l5JIMfto+D)d=tJpu7-FDS0WKqasVLKb9|P+69?5R?+1n zt=~T6-l;eYg@bZZZiS$)JkbW!kNFJX7BoFC*SHM04ZHfgb&7*k`Wuub=hwJrJ0FhtjtV#Mdi@xgeO~;rYIug6Pg?7FlG_~`1K-kZiQmc!n>cIFRtYpS z#7IvUexJGIG<7-BTh^J#*#g5d=TFkqXI43uPSoP^8C1?>cM;#&y{an=T-}K`^x0*~ z!riUjdG=gZZORscVWj7%JhB2qv#jw8 z%gC>W?9T_YhZ#?bjZV)wjaJP)VVE2I!{Zn-Mi4xShElA248%NFJ!Ku43Ea~9zLC5E z^LeoQb336lpLtI5jF)`cB7Bb5NUU8*=K@{zy0N;FMDuCcp<_aNeN`?_sjH}87~vaF zDF@?r2v^{a`9TQ8xFt#bKIVcHFp#;kfnku_z1#aJM*E7ak~R?@5x-Q&)bAV(3buk4 z^`q`bC0q)L*Y<^_FN|X|(t@axK1okN{_@qzXWz@QPLY`lt~zwwUJq70>w*$W5WQp0 zbfl>6XaCb{+N9+d8a%NM-@tWSwRKzVxd;~2w}0j8g)*J%8eSY4?)aq|<$@P=W2wNW z`YW#vGu-VCN;A3Whv;Ln#{S~QAr1-FlzLo}-@;>YluPRb?1tM#sIeWh1h4VKt zk!eqOn7I1SEZxL`Kl{md!!S~mT5znCR?P4O@ApHGB*#^`HF}EMbnUHdiN2zH=DrD6 zWWpW&P3PKP0#rY;Q!sM`OGPQq;NfGd>_=P1uMp*1HIcK(a=dRRzs^nSJ7)|9th(5f-@HB6ahY`Tq5e(8^X3AwrK&Lh*`ic@Vig$o8u0 z7ye%ZmE%Bvg%#B4=8~bkB==TppU6Koxt(u!xc_X~-KPdlX!RkoT!3*DY$zm%DHRPN z;$#8otkgDg33Xxu*_DG(qg?98jW7VMpmlDX5 zyT9Q~B_N(+wu$BxA>M7-GBa1X|NKp6Kl9yI=I8Xu6cOT2GG^89pVr8`Og`>}o>i7# zE6MOvB@oxQM~-*NdhbnvbU^h0x1eq7fJvlnVLFRbE~nM71X*_Gu;iWE?7BPY%Ov4k z4q)&BtyuJob#%#>hD2L%p>e*g$_md;>DHSdvf>iGo+cLIa*yXK6{mnFW-e=>Wld_ZAsBSQ;W22f{hRMPmO ztY-#kV;`U|e>$6_f}N`_P(WZ^+aS+Qb>g|bcDaR z$TuocSxmkd5lNTV%|c%$IG10xe&as()Ed3Z0962whW7JLP9eRQkmw|B0?^nx;oq6v>I zAPKFK^&y;LYWp$akY_s0=byeGT}E5QS78oewH_MMp?c^2BUMoT&tgtF--tj+>=%!QbLmIS+>LR6d*zF7g6@a zf9)Bh?_4*plr5;;DP>?Qom-WACIvWtXy<2IAco-()E3WP`^?eIFA*NiHz{vWbrMR5 zr%);Ro`8_jcc*w)(Eq1R@35*5yW=IJjQN$hMAAgg9ct`}wym7f3bnB$Hv$VmfcLJ5 zs28U2yaoBH&`9YaaDLcGFn@jV?52WuY1^S@gtfPug_DcC#@;=4I$7|Og$HY6!`(3tv2Qdk&=PWG zN-%ke*A{HyG8g6K(ugQ$!8bvDGA-{1$*%)KEl5GN9e$;xpi_g^_P}n6(h?87D>vxZ z!aT&~%7|?=Msf@oe>nD%5k-qZ!b8F@vQ(%n`{cSOM+x+4=~?O7%z1;2eXmj!s>jP3 zv~|&+1TZ+!m>*{9Eaa$IT@bmMe&GG2qF{E{j?vBUmXHbMqmxIao5ClbhGlRUD>$Ix)$+j=ai#?gQeLHNts>;#*8hJX zO5u6p_112e>Ul9R2YsB3T+ofER6nN{r-(%PmoL;5>QwFE*6ST6CQ<>kSdFs+6Zg-F z#Am4;T6!eQ9e&~W`-BYalLTyA~?OO z(nLw9$z@~D_x$;cNXUD|0fy_+aF~$0c2yaP-VEqq}MnkTeH%I_&ZMtDxvTv@Cjb7vuiOK(lKY^u`2VH z=!wx_|EVx;fc9wRka;Z7f9mTpJa9s6$vuI@IDc7FeA0231z5D^y^`iH^s za4y-M0nOht=(*b=e5_vB0fwjI&1l{+cdhVT@tr__Qyv=kC%ykWoLktb9j0~+zEwv2{ ztPBjK89Tcf7#LJbTq8Rc?MtW?ChKk%1w04US(ficg**Xb1q>v9tmJ diff --git a/css/images/ui-icons_777777_256x240.png b/css/images/ui-icons_777777_256x240.png new file mode 100644 index 0000000000000000000000000000000000000000..323c4564a74caa26eca81548d184610bcaedfb1d GIT binary patch literal 6999 zcmZu$WmFv7vTg?V;Q%wiB?L=w4T0dU0R|tO!GjIKVQ`lq!7ag^;1VD}2pR|kG9gHS z0KwhuaqfNZt$W^jKf0@Tch%}$U+=12^>wtCh9WTmH30ws#L7x?+5muYe+lg4VcnnQ z^W3HG1;|oWQ4a93-u_&DKf-raGW58YO8+_(h8&**05wusPFmM{b|=@*$wo2Yv!rbU z;g(afsjRAXEaw$iouT5CRaiyaIc=*sRXwR^Ax0ZkVa@in9ZyKAC?-aC_?^s6EDN^$ zjzZl6b8u?N{!2NFLQ)#+Ch&HZzP*liqjFMAbw%Tu@J*kbPtL8IslfW^R*pyQUemaL z^_Iup#n@_iEs9YqflJtv8bLT_gwx2T7HZ=j-Ij*kg9L{=}O3x zT^8dsZP(~6Z32&B&tXybL9CuC0B+$Bdb<12w>>kwM0c}ET?fiiVxl=7_aJ0aAeiZa zkycb*uwn5Vm*5%>xlw$ggOhQM2bD7q4Tnqkcg0eJ2_vK$>B$Kk@z+LDcKDKHJ>21-0*nG7DPkB0}w|-Q4xKF zQhiI<1xx#&30H3nJJD-jl>eN?dQjvGgaqnm43N!-=xwF%xyE|tw^@Z-m)-ZSbB}|G zqlLa_41}nWkqBiKT7*wE*Dt=gMC?{*28>tXu-D-7LS04-vA*#Nu&d5nJMqD~9$!tD z|5Q`Y&f4psKJ*3r4(8;f=mr*^!)GuvQ*nf4M=@O!6Yph(?l7hxRRM~gbAGTrnAW^P^_*K%6_ z@aNy(p88+znnCguMf=-Omwu%x>p?*u{$Q4eS*$TOt}$9_r-{#6kre)Rhk`@C&g?BS zFjh%%#8!;gtxCp~`l^?s!{0eLwfxl@#!2RtPtl|2pB@l!c^)i_-A2JA=yZ!5GBYn2 z`tClCH&t!bOwF4<@kFZdkuaLNJ~gPG(3Pv}WmCe33@Vqc(sIZg8ROR(-hp>n{$@bMbA#vYV_pZAHdSIBMz>kDUGJ zDE`=(VhT{M|D~cXP$|`cl|5BgZXiPG>?qw_+Cjuz*~`8Bsy#K7pSnlgbB1OWC&37z zFA069dl$l^! zaHpeuJ<5DY^JC@xWZY_lm9-?SjyA-1qApvQIqt=Q`*@Uj za~Wf-so{KabDpTKbUPt7H`@Ul#7lEtBGfdIYJ4KiV!V!wI>Ux|2Ng2U(~EI~FA0*a zcytabW27P92LL$bH!|6ajaA$E27`7n*XfU9YMfn$FEoHnUDSumoxe;1B=U0hHkk{8 zF0Iwnk0Z12OV+c6lDJl#uG#|#J9)pb->+_6j6+~R*WY7JOcabo#XXLd3VvTS-P)}(XBk+ysp&&1prikDdO@&5#5CvZ&D zCuplL6)1rsX7x)`{mSMr+kqQ@*+%#zW&WYwtF#6?3AIzs^Z=isgUS>V8nu(Mj<(@W z3Gup@K}`Y%(8bitq$D#^Fg4d?4a7s|8ki4@{n-Uik@#xbRQUSi58an#1&A52KxlpH z%OW``2^?NJMjq26`JB3b5vB_BtM?oqNJGK;#%5Z}HJfb?81*|&^vc#CMjC<<CUl`W4u8ppJ`z&TZ_&1g0fSS7O{0! zuQwSqWsyP$DK67F71>_ABH&D5aCaegaC=ECeFH;!^fvF%5HU?(eoMa+@u6X8YI;0f zpW?TUn+L;JQI=*@)$k~Nt@E3zu$$?tgS?bBg-?NeE*IBqu|N1P>BT@@B!6KjRp?#U z$|%8u7Md4~OC^7ot{*WnglUZ~A*eZrR{y2M?^s}9(=O!vqnAD13aaaRfRNNktm92k zBKqi1d+i>0xvJNN!F~iWH=ePROdgVB&@6{Dvi}K|@@}tq$Y0BrJg?|o^!Ia9T^kQ~ z#L@g$)pG9I*TE|vuy%JJQsbOqK6HZ11)0-hev`&LyX`RlMl0vdy2Gn54*6C8?tk`^ zzv1>%21+I~Bg%t|y7NO17R!ip9U1>Z{b*g*%c#zo?1QLw6EoQsO@nMQML!u`2B=f{ zRlM=uUiW-yo$dVcw`Vb)nxRLKUDed*0CVHp!K3*_(*s@;sX}^OChJ1VjJ_#^MTmOz0kQq z9IGB-XVYJ$v(#FW@6b=waOH5nQ1Hp=9X0cp=&VPxn1Re;0)#D9;3ODg!s10raQ*Sm zSHaEwR^w&IH!qo!^?=lmQL5q(9@bbgAqF% zRk1JIt3(4%{rYZH5gDthJ%mm)rS_x)-vwsDxJ30(?}RCw%N#~sCXeJAkO5RqYWni% z$|IKOY0u-0v#nGg(SA=X6X(IzT%0UJ%&f74VCFP;P+r}g$OZ1ftC@69F8t1(S4?p^ z;Yz>&arwq9Fh^zh3r_i@8AR0WUh4M6=<_6Dc;8wWjmckMLz_km_c@E&1+<1Xs1=1BR%fLi|ZX6 z>=SUheFH{g+vrb1D^%yBrV3o3<84d$c|ezLE3`i7wQSFKAem(DMyPJ^)*qvG&7lSn zSJ|X;SlyY{0;+&CTU$t~?Gt&79>gJ@u2UF~@{j0!a=U%d{opR1@=)aCGkl+LVdqn_ zLyeW>NqgcWQbGng63bAoekkP`bD?CP@i`5EFo?&SUbKsqr)?Ox2xcl$$5gAscKf-g zt$UX$ryt!jzFu>|8DpfJZ70XF7421r(d+jS$GI=&tr$UI2LkUTD*?K4D8JH>dc%qV zR(vmCtYdwbe~@!riPxYan21qAS?go-2iO~gAHgW%c}^Sjys<}dhL&@$dB=nYLu>K! znIQ&$b?FlpevU1VBHY$o!%)D`>8T~?&YvPG!ifb_Z134_l0{gZJ|Cvcym`k0(93=_ zeUI&}i~2{|RoAl5@f^k-@&w?DjQ`==n$x)v!BH$9U{q%VT|BhKFYf+9dxK1<$wK~B zy{d>Sg?sLydV~C}Q(dWB9+GUAGs$T3;C|!q)RKq(DX8ycViv2U%?Y zftdv~xAoddE-8BFB<_oz9YmFclo;5)Sq)LwoVzUd4T=BAgt54)$L&Ub*I#SzPutk0 zrqpxLt7fKvOLPqNiN~acO{Q#`0dG`h%w^zeV>`?@$d7p4WjThqHksI8rqm&GQER`f z#XRFTR1%E(n?MdDOU_P*vbXUJ2RQ?*qMxWea=vFFw|?!lg~v9w4LV=H6V??>Ul5*L zeX2@T%P~XnNXck1Ia!Q*I_u?XxNp-ZViGUlIYHd#d8=4cVI0wiV zhJ71TOaDObZt@1n9}+*fgdnZ?nyTHBV>jWvkx$idy~7?sM4@iVw3uPwL=0Vmxdm)2 zYYCIGqQzlWwsZQHeTkPG2G!)JmB!toi8a(ZawLfM-jG!Bn-TvcwhOT4Ayd~dM?7J) zaKUDuz$L8HqNsAkHozsU)WZ?{tg5IuW|%DzWc0M!h!!&nuJj^I;6NaUKA~{cGjl~e z(rUWejI^u|S(uoD-7^~4R?z&2Ok#Cjat(auE^+2A6g12cEj*OC;zDz9VqmsGbv15( z*h%e$p*(a*w5tr4dws(NbYbQ&>g~0yD+b(E&~nvc^y}r+A_d-pt6MKeY9pfwYHWiJ zAxTmv5Z=7@GLAPN#g){2PlNc@YDgp)1?$YxWYZp2D`Dxn0DDW!3~Ttm1cQc@Z#CY2 zY3L6?8V-LR6GXML)DvJri$PB~`_Kv2d`-7P3tUk>9fYTMRwjk2!q?&@1tZ-@bx-5V zWzFi|Ga%e+gXNF0$i`QM0uy;K_p#unI$_>e27{kHg>V@X&4R>n;z@o_Bh7e8wJ4wxgPWrCMp4Ne4c+9|AD6E(6)j%?38j>-o$kKVH<;h#W0M*V&KhQ6 z#H0hlQE4G~*Eo-JmMQj@;nyfL&i*)u^_2sjgQ68Yk%Q+lFIR+R7Cx(opCisFFQ$nu zPKe~&x%!=!sQ4u~lrE{gbBOgW2+gDQ98RCGE-$ON^Hwgb8@?U0NoXj{(2dUP?1$&X zum3zbx(}o1WwoWkUWcV0;ArnGPi^;*MKBkG)G#Js`zM^RF*ap z5BUJS4U{%!^fUUFG-|24>!f<`PtzIv;YyfDCgGtECZZ;6UjdySs$zk?B;hNRNHEed z+le?19h5$)qm^-`H->&!83;AeI2+(%e^%DP=zQaF8tX`Z=&_@Dh;U$N?X?O&XIKXf zp`|4%gQT8f?#0M%oUrwbbISM=p={O+)}mx^!>oU5#}YdboV)x|KRxgeACdj)`~vgX z?h~vi!HE$&`-cYTESUzx zcx0Fqi>{b>pn|9}N#>uXZG1grJwc%Tm8w?)P2Wr86H7B}!V@f&b%mEn9 z*B6%EXXV8u?uAeIZ*9n!nNMq}C%gOqTEq^N{uqejsT+P+b@%`dkt)Py)NbIcA{;SD zfSfXnf6Cr!xbtWOSL-UWbRU7!hsx4N*~K5GHsO#Q2~EU7=5-Gw$D(tl#f^3mW{zRP zcVEE`pUHd=T;0vSJR+JKJGzeIa^?!h+6WB3<~?HU?ltOUamflAKpxz#JQ7uQ{eN+kMxWE_fCwg4B>G>Q{8F*~mwTXpj3J5}98IF=8%b@YjA*FFhBP`|Ca*Y0XV-;=-bP0O`VY528i&O1t1Y}-UsOC$-|nTm zQZrYrchgaLudbcfnw#Efou|{^5T0Qho#LPUv#m_|<5*!wbyx>MCQg)5z~ekC({Eud zxt$$$Q8dMi@*j?hQ%qso=}_;ov+s%{f&*4(gq)YkT|=4|tZYIYz&k$477rz?0`jxF zc{D=ExE^|YcK%Kliq<0HCoitG>VEuXYKqN z%QZKnSy``QZZofD!6(~)nl`%~X=oG?TKjVR-XMoFFk%obhD|wV8^_L!@u#TUI3$y= z3ZyI-JTk z;&?vK1g=DGm6A2YR2Y2Y<4~uM4E80Ou=wQrriFWf;Hy(NT0wTHCeufmKup&*@N1v6 z=k^SV7Df|L#cw4(Y#0ecWH40}P+5Et2k&N$N!+WUm-mQHlY|P`flA89PouwEhG+J^ zPlACE28A|Ci#tQGBep9>Egjo9H+ctxrT(zjVg9B)oTOBhGsw3+fBO92Duijw8m5)* zs@%gvPp3!pL|Dg*>F?MHWUc#OKJV|bDL|dj31plu_-;#EgC=6&#S1;e)Mi(&b9bbP zr>*pv$xx7aq~<(Xy9#Sd<`N%$!Io=*+=7X_n3u`N4Z0FPyCgNNT$|wW{TDLyznNr; zpZ&(;db44WI|PAKt_O`I{DrH2HgVt5LEyrz_}(a&hT%uESyScd-WlnJh3}sB2ojVY z!XzLaI+8t(wCv|oBy9=$ts1VaH6`6|Pf8iSHm@WOTXjc*UN`))>cK{#W3RDiX&*Nh z2#eJBgA_B~jerv0_)h+ublIHhu8o%5>|0-&JwFouc6#oJI>>mwo4FU$i4#>Y11Jx| z5TYX?Gj#8bFt6VqUtNR{Fg%%;4OPNCHYo1%@3i;L6Ryl6=K$Sr3cQ+Buh5lsgGX(P zThCPP=TC*Dux{bx8caH&5MU|QOl^)sfp}4WvR5zB=m#ANCQG+JVr*y(J`yjZUBR6c z(wh8~_?a7JMtV=28vm0-y{CV3akbI|pCy(YD^k`_NlZFV=0Ok>U5`URQ^lKJTIrs#I-xnnf!ZFtJJ_`~l}JH{;PY&&zDF;&WDJ&2F|_ z=5{v4J5dVzd*t1GptG*id}btE6up(a&s0p_H2{%8tD zMMl@ZKMD5uy6!s{`!-{NWWBOjkkztDRDSYP?_ zVG8?B?jdgK+e17ltu-aJO4fz}ze@y0nVF=tZ#VS~B99Wi2*p5~N#bL;;N6WpY;?d9 z=->GR(*`@!!ImRQ+?)~bNonjvJd`bi%qI4@&FMuIPq+)ZV!p4}X2=LpzEoLM4@C>6 z?q2*$g+WkC5U(*8_m@I9je#Y{E$ieb=zA@2s zRcb+cmfe@-kM6O<=D@_~X77p~oRUn}`?c_*D$5n*0M8*qr=((AWNp#n)@0D&ovUI(AdlG`-(|swh?6g|90*7^ zDE?f1F#q|S{7tc|=f?PcfN3e+-KFC$6x~36ES>IxJ|5mDl%XbZA$Ah|??C%cUY&_L zbdFS$LA_lOdvK=bo;zsfN5b1b4;G@azgf*U3x)e~eRp}bC2$BbxB4agQ^9vRPa2m7 z)A@62B&G8WJ@s#FbD}sRTB}i|L~-OFGD}ll3$cCdZ9-0QmA7c@IGB=rg0n`U=~WYc zlA`ipsFd-YS^6%YI~cpC#7HrB_XS2EbJ_n*1b@%(^DZR{Uy|t?=^#ieD38lJCaFAl z-7iF)756q3M+$-(Iu~$48+rBLR$JJ5@Z8@RI~OoP8@1R5F)USJ$gmRRyWw6>aH#mLTTXxh32<-HkyaBfY{PSw%}0;;+9TsG&AzGoR$=lcpuyb<3>FAZ zkmbGIJ!jwU+aLE<-@aAd=d0V*UG?2~ElnjNd>VWJ06?UoEdLq+KznQgj&U&`?+SU| z(vJe&T1`nF08=?)XMB9Z^HetSd6dfkyc7prBmn>#Uln;7-N1#z+z?m0-mrcthj}6w zs$IVc?0MB*NqLmqw0W&IykMhE<715Sw-rtey|Ppy$7Oa-7Uhic!q-qv=X<ow+JFz5Rtu#ncw$ia8BfaFp!Yi96&?N2M}Pz&3@|7>i@(XAg^AF=PIm% zdBX$f@sJ{q&aiPuPurDeR0+DWJ9uu32I4}3?gYQhgrA-CT%pJpK+O(p>XrI$V0R{U zxLL1P<*w&<4n&}vYVOvojf#PYDL4(ItoaT&oAo$3+?MM4==HjngxIQx}zMlWZ ztE?V0R?Bd4DcZR=d`R8wyk(WBBHVPK@fsNZ>@O^w0O_toe=?tOEEq`?M-7ebA&~A_ z$AYgPqmO$AqB*|o00_~AOGrx|)t(}H*c*UIHwEa(Q$Z_J63g&6xe|Ue7;_`*+AEjo z$>}B}YxZb@2A}g}(mwvKc3PbCPzw_bm@esZ(8r*d#mGwhy3LL5ob@cd$X%d0i*q&O zZg0x1xL9N4(u^ob0@@kq&I~8E@yO@hM^k5B%M>x&YbG95kUJE!MJH8`2%b}rHt6N8 zvgb0-wCgJt_Mh%h5V|ghbguuhZ0H?s1j2bALH81fm46qZjcQ2!%@Ok1f@Hj=rJW+2 zx%6M~{O7m-Pq=&pW6$X}Or`HSb&5_A2=lW@-ENQ%sj&|!f0-)qkUvh#yOjXKf{j47){seq)+IN+TT6w#5e; z-*Rbnh(^uun-FEGIa0yLpU}F#GyH*=!)l?j_-fp5t|G%g(Gib!Vcw`o+%4n}FHC?K zNW5?|zY&kdZWBGM^!;Ve_)7pOQZ z#y=CNiOf#$!7D1x)`Oe98F8A^t;|ViZem=VG{a8Ov!eETW%fhn8PQs?*#0|j3?&}H zQgLiWto=;)HPT|v;pN6w4Bfuv8ayF!!dE!$*VmLAH`&O4yBhxCy&xaQ4Q{`{+P@eK zET+!LUS?ySJU5TVeI3=$EuvGs%OOqpyA0(;c0;X?z)eT&XIkPX>b6IMZUoeIt0O@l z)TBE_r#j-sdJ7~zx6DeBjTdeIqLIyy_)0S_VZ;2j)4xFXpb}G!L1awC|Nrz122ncmG|%UxLJPaPgtw`DmX2w=n6CF)bm{BeRb(ca>s$T~6+-_K81 zszu{Z^Wnbz13nfBz8xmAb9*uAEy!-v-x_A~Q}siS0zl)%fbfT$EG-YZ^g`F8XbpNx zcY@n+1yY~sR@-{#(9`?d;JQN&Hzx>z7hg#3LfPMRRj<1~QnD49Xr;-u{}qttfSY$# z9~-zB5kMq%TIY|VU2^c@a+KoT5?1Em5gAwe52HpPYe>dWSI5@Xv`QRNaq!f#soe?? zX!sSe@T@Y?Lp4l!pauu`9hH+oVk<{D)V;gcZMAwZ&#`7X}FO?s#hB}pS}_&1FNXwWA`#RH*JE>`3onp2%ses#ccCT{QNf5 ztUwTqY1`c4Ok3Ij@-x%Y4XHMPiAhRgL)8KDF2uGY^LwAkvXyGZFHG%d>i5BACL(hS=02&!5CF~lIP3>L$4Kn4Afe%0k&~ec-L>NT>Vr& z-+V-;CM(TpCyUC*ORpm^G9>w*8^V9>e@`XQ9N+z5aS}|X28ft6589H8Y5Or5`vz?3 zxz2@?Dq?Rh^|ohQ_qlA9s4=tf*tC@M?h77#CP(f|Qvpu7n}V8OIZgAlsgmJN#=kya z=~O;XDq(8-cs2OdS#x^iRTeEw9bg;qTfz3iGk53=%4i<}x#n(gPSTA?^6FWc-$L!} z3~}NADQjs>a-@4=BCT&{pEN71rr&+PcNaJ|X!^w(k@Yq7KCRdFSr`dDngvcgfJKk% z-hipFTFS%Zchw}rpUKmpWD;5SU!HBj&*<=tdDs|`0wF*cNBBTe`jGjD+uC<$#39vSjr268557Ekkk`o)3ZcuxDj z&yz=YzwcxL=%CblrlcoU$G%;8OvR3ZN%4@bre0jb`kE-F#G=*Mq~h>*k4m&ETb(L}gn!Hjoxm!x;q`X)b~mKK{0;ApRz~}m4)#0AKDCM8 zQKnWCeJ3zPUTbi&}+-HV@&jM2RH?HYfA@_ zhBt2Q(k2^TXNJ$cxyh{3lEElE7j-peQ0|w-lY-=S_PFaS+P7Y~SqWG&A0S!!T1%ID zM9!y`>>Ez1@c`HF242r%v$hkF1g^9tP9%M^lMAFcgmu(`?{%8y!exJrPwHnx_$Np%9@p0rNk~za0J+JoR>ov}a|I7zJ9=r~u zKhxV!G0K2GqB7J1(VP%KJS{U8$ar`e-1n*Z8t*H{YH`(D1aTQ| znGcBGr5q(22XB?mmYRh7S0PvGow4sL2i8;qdII_U4S@2 z;Cuh7H^DHq#(BVk=>~lazw}C~@iYe1?B}BFEt&=IQsNw9fN3oUpn`LBPh)5s(FHZ| z#4-T&Q5wGp68+#K3U{_rqq^w990T>!jX7reXx8;|H@NE2?IfKHova!%MUZzSCeJ!f z+A}cy7U1Q`odG@69{*ft6rPg}Rq^6`ax>ZSt_B^{<1Al#uV})(kp&(lA!2f)v3$b= zTfEOKxG7<2Q^gGD4;V4fo>EaNi4>!BI`()1uHjIW_Mo8%-f#@18wawG#rBu<^ z_$dxvm_U`V{zqs$rllbS`5QXh%me7--g8-1aYKVS?pd#mr%JF){7-lyGPQ1UMt(xJ zcgt1Sej11S0(_x}+9^n!=5}yJ@9?rgjb9n1M`_yAl%PT>(XWQfRN(!sRwM z;Uh0((ce?s-w23V!V%wd=+;V8^K>83Yjx|f$@i?C-KkL}_=;Bw+rxX@L)@P4-RsvB zg>cv=%UskAyr-C}DYZ<9J*?6gPCPn(_|W96QMe3yX}U~QMIm;UP2nS0?&}Oqp9@;g znp1ekjD7^_)akR!Ox$FqPkEKi_;T@0Rq#~%wP1KMfPeu>Ji+r%*BXU#y80TejN*tT zf_+gFlkP`14&fhu3yg^vadYoNqwj{)iEL@EBtmn^LlenEjaof7gOQC=0-td(O^b^d z*5LN*Lml6*ZcW44>?7(!>P?)vkMSg*-`x}Dvqy^#TZtiixa*MfFtv@RjM0IF@Yib> zyo`+d>#{HI?1fu1exJH`jkOc#??>P5&G3zWspz9fiOR07Wqbj7pkdhJ3We1iGj)Aa zc}Ea04BzV!ys^8TQa0!xyIH}7Y|k+I8>*;LWgjo!vhFR@A6lM8SiHXL3`(8&$$ z3-pT;_m`U>fLd`VSq3%&X!f1AjGG#B@Q?P0Zl$)Zc zFY>3ig1|@Usj1t~meSf_%zsS);zUcTl`B4DNjLX;qQ>SJu&S39D0_J(W3TfM79`}9 zrepV8JdBZhJeJ~q+B)1(LCINX2M^Z6X7s_*j+O|{I8G(w6x`Akz{(Jo zHdj8^A^tC_K0PVN3E-t-O^MB`ie;!9TSq@=?R@95!~#QVVEujIH!oBZz7;Qq4$5=zew@8Ib>j46f0WWke5 z!38}clJCN>j5oNqR9v67SZ2NGrM3+4`M)e&8{Vpkqh59sb>t_Tw8SJ|`p7YL&4($3 zgXO{{IAai-BWq6|C0R>_{MI*G+{DjXYcUW@%OG-J(|=JpPJ9zVsj%g zxDzn~F8BsrOq;TAr#|I6L5cLU*XbDG3XnQ-`zu3q;VcJ?3>k2?3uLZFpcVMO##!fV zzvG+mV#hZV)f@6p7=1c9_s*y%bYF6S@!C@ugJSHt#D{gHi^kIzGayYn{6^y|Gj)MM zmWPa`0inl3W#?s&EH~=cId@xsBKsE{Up5NZOI}!!s$4FadZpTu9(?D}>6$7P9Q(Y( zF!1JiCqX6R!<6JPa}w1? z#M@IA8=UjXd$euB4ld%N*?fRh4ep6HAT!%0`#C;6X{wtVk8ml+8X_{kF3Doe2>8}L zkp`~BRozA@MwuCEMY!I)Hh6}agMGyukrh5xlbAMNW4-2eL>?~joh!Da0+ZYMixk$H zMiusW5E;HG^D;?L5gbkaj%@zo$2&B4%zJVugF{nJS*I{hu=qD_E?)|@f_k!=g`KZ~ z29(SLciCm-Y_8vjhq@A#=NM2hlOWXs)`NVjPtx<@d?%t*8wlpVR#7jGaDXf{P82`C z^go;k^XeR}EM5x_x;x@Yx}V2|e9X&#ikm>= zF!=!gtlXdT0UC@W)&o3+n^V`r$y>Do3J=EYcO@&P&|RXjMdJDTX$oydK1h5Ywb7_8{@qx!8Yx2oRj~Zzm9QwL z$+=m6q#5~5dw>0^&3aHzsm>DP*XED>*}jID+MjakrSZ&(CE(Qna6RuD<|^wVjsGj^ z!i1mSd6(+OFqS+?UBg9g=p87YXHz=ASgeoI^G!_vTaCth}Yv84AHI8%Ej zCv>L;tR072%L;N!?039-xqP{2<<^J}q+BOL(zuu(*lwZq6LL&%zVqtScp>0z-|f;U z6B$%<=^?--XWF&b{AzVGiP+3ij(REP3vgZ)+Yu;6}f}G%WjtRbFN#Z|}yiY^jAfnnA zZ?Tm%i6*r+_d~9$-`u=vmDn{>y=g8KVOumoEDyfniGByHAGH?8H=KNXR*{D(PyoY} z4jWx*)4~cky{0<{cEMqjR=uT2jeR^n9zt-QphV)A$0I>I5=Kp;8NXMPROtW;R#I@5 z$_?NPo)MzjC0g5(S^HSQX>zg{e@~|@Yj)AkpTc_*&bd9zIw%X~!|UF~cAE*Frc(Hp zj@}l;MeWU1(ZkW5(G2bS0@E9aD~3UGx-x>@pMn^dz&Rn0YPG$q&kmSeq_qM1T>9Qm zYI^YnW9*nJFo$bphmyM%(<**JE@a@vqfa*zrfR$6@t)&dcR=-kLi$xG`N*Gf*YfXh za~m5tA`E&hxpb0Zce$yJ}~q3%Kjz`Kv_? zn)3c?d1e4l8!E5uxo?KLyI{|k(A4Ul$R|NQgJnxzSS9{hN1tT`$b4h5B!|MH#gS3b zssRIJ#@v&2Vre{WoTCq!^e>1%KI*VMj^gTxajcSOn~~TEF45sTA+4#ERq}y4QD|;g z1ateBghNF{wT?^Zy}>nJO>3TJ8W9@%5UB#Ueuj+xa1tzLhoAk>Q$QW{^=YH1zN3tr zyJbAqdyvuxEFi~u;n$MYsgekbXAx6zV?BB&JBOu*CkE)7{9#LitCT@Rq%u`#smT?? zp0QX}uWXyJuk=utr&yGdhHY`!DcHMps&UtWlV{>?`e3%#n8S6$R(YU9y0;=Ud2nOhSNm{1I}mTso8#HO&u-C(#%}Js3&$!SNK(&=~0>4yW71nE-1| z^d|N5LLYfY&p?xZB~k^@YIAY4K7`XI6`myqf^3fSz*oUi&vtClWO`KAZte6+!ak88WHwv zGfoaz^Lx${xwsZ#!TC6%M%q^PIt1Tt05yo@Q&BLrmPPOWse$_EkN6@*zl&vDX;d$7 z|EWSk!$^aN2E|xQhNTQPTp+lgnJhb^w;x!+w z)bEaRZu;bO#G`w>=0gDH8&X%&@4ZPZAc)zb2iU{<1>K!_SEJ3D%mdm_6Q_8l=xeDcUR=6MQ(!Dh+(WM1t;i z7h%@cKOjQ^$Z@9kYg59yiiiJOTDRQ*zC4 zD5_utUXY+w)4(MUa9*a{JVdj8QsQWFhUP+5+Dqqt_XEPD5nv=-|BzKxD&o8p&j`@G zF}BMzZYtqyeYt=xe)Hx5Cw<2;!K0P%x)K!QB{ z{JQ)?Vqj4*egQ5JNDKr5l{r=Xi-DVmt&@Gw|8F33_LKV2;MqTB(DAVM4Y2aI1IXBV hSlcnGxLG;ay|%Nm4e}ba6Ms|!s3>U4SIb(y`47Y|2vz_9 diff --git a/css/images/ui-icons_cd0a0a_256x240.png b/css/images/ui-icons_cc0000_256x240.png similarity index 88% rename from css/images/ui-icons_cd0a0a_256x240.png rename to css/images/ui-icons_cc0000_256x240.png index 49370189231d006600b0f0c2967cad1583eba634..45ac7787cd2bb4d6c3eea7e6e4a893034ddf75d8 100644 GIT binary patch delta 368 zcmX@Ad{lXYX8jokhJi%&18Y5l2)ggjUoASn*_x40z}rmMz+BhRIKd*#!cVrwAGX E0LPeQGXMYp delta 368 zcmX@Ad{lXYX8l<%u7O1L18Y5(2)gf0&X&F2Y|Y3g;BBgFV61Cs7-DE*Wol_Rc?MtW?ChKk%1w04US(ficg**Xb1p54Qp-y diff --git a/css/images/ui-icons_ef8c08_256x240.png b/css/images/ui-icons_ef8c08_256x240.png deleted file mode 100644 index 60eb071e0259da5c0599989e72f8adcf50baf81f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4406 zcmeHK`8(8K^ncIBM8ixX5=LXozD3z)46KF>Mlo+ZWHR3E{1nhgK|grR}1B>;eq zSYRCvIoihzjrNWR)I-}u8vyFl+3D9{M=~hTQXdc0d^odsB%s|4EKL9)Tnqq+2>`Hv zbVOVLfM9t5SRw#`+EV~H<)7DXp>d>l%KWmm-r9Zm|F8dR0g7s4-;wQ{gDg$006wG= z6mH%Xmm_!9<~HsI;q(c`cL)v*9z3VPWg#?|BSzdx+U2ft60yg#|6|_;y)%@ymr<3? zCHKppS2vtvfXEgiYiulFNV-B{uPg6kqx?Fb={$Wmtlz_%;@157NAG}KjrDbbWSD@# zk#Nl4z#;I6{=*vpmj=AF0DzO#P*>YJ?Axy^>~7;ND3$>O&xNO%UggHm?0qTca~kg$ zZZ9?X`h2gV z(a$8Y4=ucTo>0v(Lt3qu#{)xBc+Vy%F;X3~7K@oPD@7S*98x@R%cMY2~TfAIU@V?K%&)9|8 zaRH73ea~Ek>MkT)Hf+y93PMms?;ao$elmcT-^bNlWS%^Y=B9ptjV^kL=X0oDfr=pE zVgD`lWqYcS|eV zR3rHqt+ynpYVYx#waORx;pI0Me~FHJf=nnSi-y8_-N>{x%jeR7d@NIjwa%u|hT!rt zZMcLfq6RrlwF@RDM#PyULBg+0grESV!~;vQ=H76^efJA)I+4=1D(nNIq^bR8cHY*| zi_*keyXT8j6?^6}{Im#4a1hB;YQ_h!m+|cg*Y=Ym_-x!_1x;OdoyxXy=E9=GVuSW5 z1Bm;~@dcryJbVHhmkRc!1n$6nUl`IdwLhQdhBLvk@ZM*WqD90=&FK=(JO*DY$ zJmGo@=dogV+w6inNn%x+YWmzJj#0RP@u;PD__ej`?D#Hy!uOy)wVqSRPRpt8)C<3| zbd{r1@Vn-Z(pdFyr83!aH`S)e`eUE(6&gs%<5?1bUOWI=BLe;}&B$xo{?I#mp~#v^ndjA;g;G1sNKy zoPz<;!+XH3dgRR9hVh-J)W+};S%TqQ1^1tM2;LTV?7@@k-@ikfQcm+^2y7i}B-MaRR>WwXMiGgA=!Y>-g_ReEvt<8{zi?UdtZ6rZcKa2A{XxHHWD{SPCs4E7Y7Pyv|M zexu@fq3&DJ?>LE^_-Jb8B*BIxED%2sG>RyF6?Tg&c^3rCll0VH`w)||@7zSJ}&C7P=Pllbb+ZW?`M4t#y% zqK5=FhhMp+>(DkYWqM{;B^z(*u05X~E+p)b6{8%$jpX^DWgf4?BhIdFbS;5yu5tC+ z3G*}#G?*l)8NsAIPbbH?xVt)lqd8+T$XHic*78U9Gm-scZAqW?n~3ZlTFujI7BYn) z1mFNOpe;I4qwnq80k>}g_k*`*%+}#`%$xnP-k=yBa!S{Na0%B9koY^W>0{XCCXKW8 zNmU>l%t&wRO0=BhqMj91@o5sO#+>hgX%AFGbN_n*ee91**wRzt$+s3|j!daYS!|4s zl$e2l+`Nuh(N+V~NFxYot{-}?`s|lzjcSdpeOIGdy-T=0-W&U>QfDPV2BheaQ2(7Z z7wPYi{P;rQJGx|&1B^$Jhph3bp@DIOGHgWr#QxT+m?ZiuGUMQPAvIkA)E5rXq={6j zcjUS7T#iv9nhiW__HcRo!*6%KF%fV$wEDQ0Sg2Eky6zOYi^gt3cI#9h`S=lk4`6Dr zYb<)r&yB^imB68FGY#lOx>|d3Zh~L#ZO;$-b$yy&96Y!5Fl6*HxIYsm z!CEvFmt<|N!(G1JtaM&59v)WnV`&}XaWD83DE80R4{KYFCe!yuclzS9OK~ODYv;?l zY*UpoI@mN@(D5Xf5AlV^^TB3(ACGy1dW5gml0N>Fq+c?X>8HPv5kU8W|{?|Gs?E0Z6?{#$v;?l7@f8)mksIEMtgH z)X)-o;!-X5I}g#)tsoQLVj(jzE<8}aIlO2sYoAec<}o>^5%Kh+4UC#btBj7R>${ZE z44RhtMpO-l)}Ut=3MAL8$y(z{R=vdbAqCIvrS4ou?8QKrCXo?_F_?#wa&7}MiK(~R}&za5N+&hIw;@E<(9>SxE{c+MtRFl3Y1`~ zrX%*|Gza9T&>%eqgcN=~;U7E~PP(x#MTv;tM)bfv?}+5+T; z77zaPInGo-Pc?@LYxoe0Se?h+z81S|Jv#m5?kLu$;rMSv>c9Uc_v@q`&ftzw{&${I=<&hXhOcKIn1R4;aK|p=$Vje+A)X;58AQi#mT_*D#RC zhg#n<$r*fu8JXjQgDy;pP+NvaPZl_V*IfywxQq093HLoO!n<@%I0&)^Pd9cuiywss z-2@qH5uF-w|rcYUNPcC5zzlM+WX?#dZ^aEAFiXY;5yUb0)gYla(mr zj z1}x(J`1bdM?Ju#8TINCwre>CGx(I9CYaaCNL~@(wsT;TvKlpAT-fdQ9zPcxW1)_iR zed_T`^NFcP7x0h5-%f%($~@Y>)+S=u@|UJ3;ODa|i)lWx|2AnMsK^+c!H?AE-`@JH zwN{(~PYWS4_Y~V(0>%%jC0WT`SH*W3ii$1 z?@c^;;PG*#EB3KIoxb@o?umZ#UgoA(m|3lkZSzhXw>?q3OaA1>PY)<`Fy2MT@6pdJ z<}!wmFs1DwOx^BC!*?GzN%wky0xcFfmn*?bznpw7Ygp@{b@N+|j^*mj&^L_^=i#&i zh;*9cB%Gwqe~#ABVbQFTNoP_XmMosf$~ugI+NZmdYC7s|IzF`o@Lo|@tW>k~nU`-5 zxo#I+GXD18a73XB`~lkA@4v)}FRNvwr*UAa8E#J))kFqBv*9RnW`s)mJ2+UI$0}Db z`6j3y(Pj!=377k4o;*UmugK^dE_h8`LKOo)n!t$3ua34o>yQI5DYcK7%XL7_TJvYJ zBkl#IlYRkS$LIoOk(XoK#)(xdBUptkvyeV;1%1@+&_;&#ZMn-+jy4oy#{jAi5gQQ4 i`kaNe&M0C4 diff --git a/css/images/ui-icons_ffd27a_256x240.png b/css/images/ui-icons_ffd27a_256x240.png deleted file mode 100644 index 4cece816e7da0e6dcce52535c81380ba18bfe2d1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4406 zcmeHK`8(8K^ncIBM3_lL!f0&Sws!@tLv9zGTai5s9=Y z`A`^xlr4lX%#5$k=kxvT`!9T7&wcJW=RVK5_j%4a_bjOvW`!#TVj=+S z9Uc)E03bvO0G0>`WF2Po2)_@>F z6$-cLj?Yy*ZF>uMopAD)$~!J@3_qU7=#nUk*9k3UE$4biEt%NsHSn?joWZHiwwKXW z&87D$p4Bv-VS>n(;%gjiU`U2?QJ)yrJgYpz^wNeibgds)`+O#_LP0Bnt$qY@f_$^T$im*r%0*ORA6QNnc zW1=njaoK4(MArkTYJp0a3H&+Eq5!(Skr|x*l!gY%?vjM+H;1rVXOH;BNrH|j@?89B zK8zxg{iu|I{NMqiWlRSLDfpr8%vsdBZHmE z*WGuYo%lPXu7JVby}z#iaHkBpf)Of;6GX&%fIxuSasvu|szraU|1npCK4_(4dqIH0 z`!V4)+hn;AR6s&0&2Z#4A15zCk+_Le$UaI-Co@eG>)#JY%BR?X>ixQ5T^@mWXwR@xo*ss$$4od-T(=dnjyo4H29wSt3=6{Yr z$e$i=4((;#7pz8ZIsHknwYnIS@6iN_8WDyv`;OE97lp;bjY6X{?E9D z*$H9pLPM`SE{$DCghDvoQ5Hf_LGA8yCH`arEx(Uyy2?L(62nLN02^EMmMY-ZxC|9X zz{xcuW7Ob@6}5BnubvA2ZKUgVrtk$CI%$qkoA%*ONwE+jAShLR-+HyCo>lZf(VE5k z`|^vcGJeWO*7%DgPQI>M5Liqxn-$`L8`c#X;0k}UAM9Fg1^wr%7LLWUYXfqA?7L-E zZ5mO6%+{MS6wUYet~#}ILhy+GK`QY+~eu|l-S&X8b|m+Xu$;2`hU5uxj^Nbudb$qu4kbED>1yYOOB;c>zA z&OxqwtceBD!#sd&a2|L>&Xgw~*qP7T*_SR4z2e$?c}f(D(P+E;!Z_DFHb0+EZ<7cl zx=gyAz+N0KH+bQwfk zUb@2FCGuVSM_HU!glf4$g}X-6RKtsnk*^;mLyJ?qlERPpEhR`JZqSB7F1z4wNj<@%d zF#UM|lHg-`+W|ba{{1_&DfJ|OrtsGOMsh8+TTW$&&_}G-?|a)H(9fJ(`V^dyG32+TEOLvYyd5CM;~3u3 z+9cOk=XjgLzgx1n=7^Sfhi`T4H2WetBwL(9UFml~Lw)1Xa^7 z&j+(*+nQXoGcF|H&j;NcX)V7yAr@0+4v>_X*>mcnd$QA=sWH4A=%iP-cheawbKq+m z=RKvdxk73!-3NC0snavN>N$8b58e5U2vIS|>{zuxJ_P><9g74#eko2Zfz=Hj^T;TBxBuR*~=e2PDKrjwNHQUS;`lM z5`ca5psvJZt)Y)!2i&0vJOJLFF<*z*vu+G1_<&;h$*J87Vx_#-K~nF)W{+T(nlPsu zlB+?s=+VB`l^8{tMFVS!%9CVdt%cxyvtB4hd+&Q8WBiY6_|g;N@wb-cPAu6d1#GOI ztfY~!;=G<@@m3?t7!!=JFbq3WbNWjRrUtXM=Vm->Z~@mZaD7ih_Ovv}h!hhV7O=DC zD)${ykWeIcTc1pFgz>BJlQEwf8=2QTM~sP|INy4gkR*OZW$ynjqGTw8`XeCPH1R5} zj(k`COR=g%^TDUhp000y`0vg?PXZhdtUuC|iu8(+*PNqvQP@q$ZoS4sUw`87L3Ay4 zjm@C>nTb@6DmaW|rV*9I&}@$|NCU{bxPNj5QV1u?b4k{o+w2}jv7$Wi&cJWA#G$bl zjCt!PgJjjov*g)|)X4Qwd)HVl*MivY0Q*F$eYqJQk7 znCwiJ>?>T=7&A|}rqk|zk!j}9cWoLvqogtHaaP6UPmZov2p&)kan0sxJ=n3%CCulL zcOLb{QtVYwVV5&+!rAHwzEEj<&c6zS6}u}yH^49H+w(*I-Jj+cht4cL2pziw9>_vU zvlkD?C)?QQ@l|X$tDY4}fQQ%qSX$@uyc==?6!&NAhm9R~li7RY+x-bSWw_FswX@~j zc4?}a9UR&%s05PhhlHY|1z>Z*k4Jn!y<%7DNFRU7bbf&vF%71*!5)IQ8t&|W>1*gX z@4n~h5wQ_8{*X8}P$%)Tx6ho?z}R$%9vv*7|Gs?O5lFj2#$v;Dl1F~T*V!zxEu)Ff zl(14p(o!AYJ5PzStzc7u5>azWUOZ5-IkIS@;E-8->Jd5jIoFeqwlGRMttuwAzW+jI zGiX}=8&M+yT8o-lD3n>VA?r*eTlW#$hn2mymwNJ?u;+tZqm?1IT0l%69dhp}%oMFX zGEQTb>;A#*1$7~A*MdC04cXD7L}XbNc4rCFDP+TM+4G4F=L+H_kdHVNix!ZWdEa>I ztC1!0)lHA>E4S?B6x38Ct~$(WR@*J-VLu2=p6Uzf!*x z<4w!3U$pGUeu;xQ1umTC)8-?cBhb#E^LTY^(eNVyYaRxWbG3SJt6^u)-WymV?cDa6 zi<}C3%X9pj^aYBfD?JX`xz<(w{5gpW@mCWdlNe*-XEvnN$LpTWg1GI&u0;DNObM5w ztEVIBbJ~MSR20a7*~zH^f>mt*o@dNt2YebY;*2kqx!jbLmu7d7U8jczwC>^0q+5 z;l=$w{Z2EL&=bw!Vi;dyF}urz`_~fJt%oNc-xIU-P4GC6U{_PF%;0`onjN@b0tn^EJH%D-gpQ z@6(Q6m`_SOyg+CS{&ouNS?<~PwJr(GQLr>U2|t@tRYLPs__s+5MMlNqjDDm&`}Wp% zt+nzLcv=*ZMOSHW37pukkzpryUy<5ns;IaceYNw8?6#dp?Xrkrd*J4%!l>S~J5kh5 z*%~L~$l6V`=5kLfrhoprG4>ip66T)Q7)fVD z+?{-U-}B>2cibaG24nMM{A0rude)|QxOttPUGq*np94{O5297h*JHjx3)Yy{GR6{()_4i47kx6V^Z zxdCe6YBPhbL@53opldc%k@C*TJvYJ z6YfQ47vmhfp4kn`CNIajPY|owMzP9U=Ar%IO2(M|fvr64+j6&;B5gRQFF_{V1MIvK^OF~bls-Q9bK`Xv`yGy%b5otPGTUS6i;Q`USo z-*o35Lc(ij%hYFmE%@FKtCBJ~F14s=~XJ)gRXxQuvPKgU; z1CaJU_z=bq>K+AE`i>ZV5t^~_@{Yf9L(2nL1faCaUYX)w*-}4<(W?v2gi{iamuadO z^FC#0ceJZNuzG6R(|YFQ`vRG4bO?Ata97?^Hj8jPTu+7C3H_KU_J*2B`fWJaQ)n;C z^E5GNyH@Lzxy~Y;(X+*`{FyoBs-SNy~aMj+l(?IS|fG%juy8CQjB0>-hUiFd(sAyB|8}D9+L0t`RoCGDf#X z)XUd6G-jt1Hhb+gHsp=gHy5Qa3`DO|r6v5n&_ak$%izwpHuvC}I*~z_u+!i$ilfop z>**`*H3L@fLsp5jn$k@DdNP%pwwJuP@`fRcZ5uMX>m(vpsdFxlJD5Lb^iR zPkdd|rDGzlj!>QnEG*61Z!`+0?%u zmJ^;mK5?Zk4RC<0MI785WE-JC7SB8VrKdp<_D$giPTzwIkEG(V+N-8O&lh$Yqn zqm^y{-Nwc10q>!xK6DWQ(vECI$)d|gcteh7P(i1a)574!4Y*WZ4j>*bNbGn-Z*&jN znsP56w^)~U#8|?DCJSg@;4-!mM8*XnuHm z`11r22Lt~6LRF#cpcy6yur#QMmW`QJi|zL0BBZYsf^~K$c*YlJ0(TISvJG+QstBvc zXZJ)9RPD|l$9%4-ua89PkPAfBar>aV2d~`q%w_)iV`S*rE>Yt@d++arThC?H^zeHr zgMB^dc_{MheONoB zT3-(R=iWroo1wz!3T$JE{`X^1&Xd#AR8of~@1}zv0yz$}t7qUcewR~Hw|&~w+i@0J ze=^`LOzsT}Dk|ay<*~a{f?F_;!O}or!g!j;xl$V1nETGEDmngJRq~}VDF_Bc8 zg-X(ab|`N+L$;R%>wXX?fBwEM?fiZ#sd!lb1^`iZ=x$Vs+qS+YF(%2u^j8|$$f^Dd zkHmtAwHyX4>T#mb#kGM9OR1Hcbk5Yh;AnO+n=!dOVw^ECe34Y2Ftvi9hJ~EqdW*l$3c- zB0hrKu9Pupl-{#nu>SIA^2)-L&h!ckNvSV~u}lQs3V%pAX_dMT1{&^!E%YBYBj!J< zCfyivW5?1o*MHft1RYbUgm+#URh_m%BeBo37K45w5 z3oYt(sQiEy$CsArju3#=pNabC1x1tWmA6~srhb|`~qCDqSNCk=js6v7~YhtOv6X9Aqs{U@#PPoV+tuS!&+UuYmgJNub?7Wns; z0|;sd_z8(rbvPa8D_BXo+rwv}2KX8qsBoi7aH4GH^K(+Am>mERUNqowyil_cS^6OH zl{y+-$(|QW#OZzp(K|erl+_Gh5LFuD?>Fxw{UCq+BgUWk7(^%}gmAESKFb%rMlkN7 zAw+%0tY8PWhjsP~f+_T_r5JB(W@Sp^2PS(M&Ylb=L6t}uskJ9cgqT8BVd$VZFGZYW z2nA_UMbRR4)p~O ze0uUc7z7id`J3zg8?_$@wnms`tB*j@5sT(ou88;Df3QPv`K+v0YXs$ij=vwvSI9mye(oZ*@*j@D1RuQPb#f3=2R_xk`(6ql#fHB zQDft_ZLG4}3K4A9`!jmrwaY%P=4S)Fp%iSCQJ%(x?eJtf{FUIK+`F;Us1@}r7o(nZ zic)5@?$FQdIXSi2s8(;jf-_+l><5E$1GC=MQCYCEVIC&s&BO%V;nk!_q$kZN1{-Sj zj$ypS1+24>&yhiINy-EMBOksE!!5*N#FcAb>rRr$s^OoS=Z`T482{nEDp~mnJX;am z!WfjJAF8FstyBJ7ZQ`1x4Bv6}gi`^dPbIJ;8w@@>5krMxmK75Xt5Xy$J6Z2%x-k*x zP)rM-N!wz1cSR|s07SFp z&&?A(tDSq985W+pK@q8eJuBlm<2Q|5!&xf&NW`NIq5=<7A_2^sg~H)cJfSKcfA|n7 z1uj%eH_1kb=;Qt3aI^S@g^3k)yvx+C&UvHi*Pg1%kKd7PTp&F^EBOb=Vr#`W0G1SR3) z2rwUaP=eB+JE8uUYG49a$Gxi)%cnd$Ql%)4{)I@Lv<$ob9Qx@pZL6_nJ6V|Vu+1ys z-_OSgl|3B^;I}uAys*b>PVe6-qO0WnukD_>+S~c&Qvv9&swAZH5)Z5=D z%<_!Qs-G1gFJFVw+Ctc>c1;R1Y((*T4DxljzV54MP0|oO{5bPQm!BkDN~cv-E%rXQ zK^oBYfDxl%bF^Ml`7>K)G9W@p1$y%UCim&8trlm#@~^QqPhL9sVQiK_rntLSd&ROG zC~qjgl#X+(*($lulUPQz9RG>NI?HUCN`ARV9q_y#KHKO?@KNtRG?GtvB@xQM&a2b^ zBuV>EAiHxWhe?skF?wkcYHf(=XEAFfZ9Fu-rEiwK@)tX>pZyYZJ#L!C~~$dw2q)_B@iqps6;e(W1Yo`qB22)OVxO zd-?<<{?FNoqJ9Ry#OpADLosB=AH!CVMWH^?<(Mg2H4fLk7VggkQ(j}{?x@i z6VN$K(WHdH@wB-N&h*#s4)Y9bi_PNc*debeSA zO{|MWix^;SMA)AwjdKjAcZYP7`5FfW3VJ*@Zq!r@np{Dx%W@K>(Da0E@*OPX1iJCS z!)EWEyc$$ml*|CC#K^JXc{8F>69hKxxq)&^E=$k+kDiT=DKE~jp~=bX8xq;!he{nW z#l%3kRpUeH(2p3Ssmb4@-qFmk(Xm=JLkr217jDW>wAICxs+xHSjwG|pCdXx<^X~qR zc&akK55Z&Et?YJkn-|O*DtB>In`dLF^&}iB%1KuSB0xL*=9`Oyo@rLbIY_jNfJ{Cd zi``AGp89jhut|)g8)e@bWyhZ|tgeU(bP(p#fyx3=lOKea-(6QR(d^dY?%?tx3_ohE zp90|k-SyFUQx2)V=0yFa2Xn3%mJ{LS@1b7K^qcSgDSg4nJz3wk>wg z&NjY0p~JZYiHQEUl>d)3k;NQluDeFV4~qLPr3|sLtcuEFbR*l5f`oy5Ep*rDx^G<@ ztrWdl5=f+{4HKzzVjgkZCc8RPnFopr4oDMTn7mzwoT6fj93o1YR;Qe<8=UxLh8c>Y z1&#Y3IPguq{soYQuy^XSwP2G?{hE z4iDUvZnlU(O5qi-D;@6V0a|cGDmF!4X$*78og=XuR51Sfh{mr|==SttLa4%n8}9HV zY+^9=q8zvtVw9^KCGW58f=I7vKY1lmYA9Sk=WvYQD)frSQQj8osI-3kP%ic_;*Pu_ z`dfGEmP<&-8W6S* z%o78x#DG5?s|;ec-FLwk6_wf>4C13gA_7K76D2+;4rA4@bat%*dq%-7sr}C9#uVv2L8*@R z=LOl69w6(ve5VaWwPbB#V*j<)WM_QjB5NZff{}xKDfm+M3IY971v7?q94duX{^mt~ zOh@#j^$26U@x?;!iIycBA%GRO(sh+b=wGiwY&n?XRsZVjIi-=7mSNZ&JeJ`n^Fg-* z`$1JJ9o9N9D9unzh*ofmV4}d+B%nkcFxYX6745{6%5#&yYLz^%p_(6DA8*Kxv~v4!hru>w`L+G|d+WK(Fj$y{ zV<;3i-!;2$HZ@XtCG+6ykh7A#KjVD4vE*eag(BRS?u1_;=mnmq4+HU_liqhPBlz58 z6mvkB6$;C$$v$X$9a@$faxblM#dV|P7Dru1d%;VLVI5zDKSj>b6I>xSw+F-+%4t7C zWSo}a#{=NBn;-GF8yHST_`X0CS`-l&CE~=M;tL0)T>}QV4alHuqpZyTBs~8{i_))o z&11$r6$9Mn@MBWp%!TCX5i2ZABKGuVUe2%NS67?s<_c?K$S8S0mZS=u&u<~RpMRqs z|0$j)cQb*g?%+`KfiS&5$uEk{ZiO&2d4shSJDi?oFI7F{Z8eiOgTcBhjlbz0&cP?o+=rjt=PBoI#_w~hZ zg9XAR(Ujr7MCz35iMz ziAmfQ6O|JelarJc5EYdZ6=em*C;lG?S9jZ|_5uHN5S5h^my(mb{V#|8p*)&P2bRA^ tFm<>0_OtP{1C(vuAKP(iyV^L|nb_Ib1|a+F6fQdf+Uf>sl`7T|{{zsr!pr~w literal 4848 zcmY*d2{_bW_rLQs!wkugsL0r|B+O5XHO7*iL4``j(xPlr_Wer<$r6RKWGnl=uQMeQ zQDk4DGTE18XL$Yo@B4q3`#kq^&bjw_?sM-w_k8ZTH_XUTi%cQJfkT9hFMNTGr<6>F?#_uIJ|Z+^*cNQt|esUKo=cd+-z(3xtEXvMtV;Wsgp_iVUSew zVmgwt!ojSGSPQ973~}$&As900fp0*M`Dq@7gXIV5!3i$dth5k$%L-vJ6GS?`B1>K7 z-B5;|MahhQa7`3cEn>%*0YD@_D6|4dK-hQ7e;^@36#wEhf6JugLEzdJ2 z{nlcIzO10BJXSSN4V30XT$KqNgwJ0F8PQDF>aK(+;|SgGr%E>1m^!D006PGn0sKFJ z7yV*cWbpPWBeM|bxQd7&L{WkYMM60>z{i<@M);jmL{O)IF>Bdt5o4*!yjT~7c+n+k zgyWC^j^xcUaUF1K{}sk8Ow`iXy{|ugD{w<1T8S1NMNB`co`#J6w4TO)_`p3kiiHES z!=wQpGTlPuFBYw?^ZI5w)9h2+`%|hQ^9vD zuit-A<1u|F%=ObZ(Gy9QGQ%NJhnIDiF7p%Nhoe9!0Z5_7Etx{}3{ntd+R>DT-64n6 z6>-5u5q|4J-T`EF`Oh#dzA!r$?l5TQ82bx)Y1Q~I%jo)J!LHC|p$kVh6Mt4puCpYw z&T$#RrxkD254ehxe@wwm+SO_YI}Sla{{2wk|2rQ33DgScItkL|703Lvk9(cPU`pB; z?aHo+(ga}=y}u-4^Tzbg)&?nP7P4dqcUMYxO4BVdgrL!Pj-8quwLu{XGHT!CqX}=H zo*0@YVii8!ddF$t62KsP7CKx>Hk@)jFbv&o^z_R>jn2mW>19g+qvSUd2o7sGbCW55 z0dbCscQVbfA;Hm;`XTDjMbR&!HYs?6hYo*`0gy&IM!=YigGE8V2CtEs5ubU1h~QF} z3|+okJU;E(rvb-@dpyC9riJ;)hp?CL|q%zTOEYtg2r1A2!Q>Y z`Th;Hp_8(lio#H967NLA!^bXY((|p`>p|K_5fk>N4tzzP@M^#_+KGM=V533BRPlN>N?lRg|&~ed1Fl8L!l{ENjf=ITLw8_`MoD} zNx3WS%Zc*`SNi27UuQkCPoP<}h4Oh=QPgf1S*2%{w-uk;_~X7uVzH>XR@ATOtZ;I~ zyk}V2#*mM_3O`#O+jy;q<@Ij;?j)x`LYrjuxUWf8Ie{j8gv5vWK zEkNyC@(e%r{wf^N!tQ60zWDe}3W|K4I$EzddR(gK=;F=lEahnoIS0F}8{Vo7i$#Mh z1!!T%8MSgi5*>2{(|wA-Ly3~9&-1Ul3QQ)x2ITQok23MABVLAYWZzpAl0p6w8`fZhz+y!`Z0JHu_zYZ_i&ku|1zg}{3}-js^Nkb zV*7cz*@f~`vtB-&#dGKPx|<+vG%3bv1uK8?ikbDjG1%jQZKXiB=o)7da~M00der$70gyDYh3zO_ofe$Ex?6{&)! zmCYRN;U!;UES3ho$rZawPP{gCMS|Hu_p`>HWQKt%x7FOr?K1TKr{&u#dx?jKVSqA(0&wL{gBWhwt2qFKI1vtC1R<>zf$E$O+R1ZTwZ%RNOmpBtAb{+gp$u5szX-|KT5vF?W5$6KHg>r>R^A&B=5jYls( z!wzIug!S866oA#ZyX8EUVX8M`O1 z*eDT2IaV2sullaId0w-SNU`@}sbAEFovNiO&k3BHwdF+(c>nf7dlZ;w*2!ZKCXe2_ zudY|Y#q0!Wm5+D#IzFMlWm3ABj&I&uTrgyM?tEa?@*A?8CNjN#Qw!ghO|jdS_Qg=y zpzz_);K|v;4v~m&i=0b-LI}afNr|DkH><|3hbr0hNMmX~#2xS9`?A*-Id*P)g`f_n z&qo;`$FCVFRt70&(<78dp7_<_;KV-1co!eW;5OO(ts3j`NT@_$)k%{2)ADWxDyHxA ziFyaKhghxX0Xl;j{$Dp4yq~JzCGuG#^OL2eNP_Ps|HPf`^N_?8pONw=yG_phG+t(3 z_S)PWk-Mt=RWj13)$9{pu!(ru=$H5$+e01sizinXjdmt2xqhU78l)SqZGW9h96a>$ z*qu8mpNsqCO8X4|2ppK@7-I8Gi!*H~F@ne5_$fgSZQEDxCZBBn?3LqRr?R6lJF{gR zfxGLMHklh8@1`vAfm|(b(nfjmQbD3@d(-G>mqEL^)SW`2q~p3$Ah+ARm(U1)?JmY> zvvi+>i^PjFf@*eW(Mt^8*LtU_%7_ZV`8npI4P9G3!bojNiFo%vsjfVh+*}3n;eve% zWnaA&o}_@PnP!(`P=33OQh(>hcQm&0d;N{+-tJPFA{|8RmTk*m!|9o6y`jRCYnyaITT13C>}VTi7f|_b^uXnM6Lt7c=~SQPY5yCiuV!}n zmKxsf{yLRp9FR-m&sj=-%*L6jKk6-7u(`EqQICa2#P)nLc1I2T`pieXZHmE~-K^wI zgwsvo)~vNv^wJxt+VBU$`wZ3S*h;$6OX<>7?BtOvv7}q*!M^rSgixmupB#6>-pwyB zhCKp3wG8XZ1j}uSKh9IgM&o2^IKw=0Xd8^VZ-F9(J6}t`B~B`h<%O?n_^i70GQK)0 zW;EKxs;(tHFInT9CB@Y%8!$W{;)AnF)>%9mtx*3cFhrTTwRgAe$l~M=hsoTHMj55S z+@0h0;)I|j`QEhYg`*?*=)3J?n{xIwWsuW$ok{@lHG3h`Oxbtoc_}zTrAlhS_EqyZ z*^^^Ikn?Ky?#VyU&U+Kx%x9$4(?g{fR#lV?D>MJaR-ZiAur8kghX z>RpvpE~%$|%oexyC}%7eC~6`4XS#c)@D}H$0^~11_4&4j*LjxI^kDAJNd7ySp+TjasDOa5>d=^_o?iz4uy zDpDk<`^YyRG~!o>J%yzdW5)%pS|alvcbL3XK)ktwcqBy4l%zh}2HjUMW%CUBKAik7 zI{%tA26z`00RB50$7?}L(y|O9x^RMj9-91SE$ytLN#`jaN2osz^y*!3K@}Y+x@LYW zrYKnY*;K7wrGEAu2YD)%e~jr*)Cq)OlY6W2{OqJSVQzgzmj*uvC0EOn0~9WH!Tl{& z7rD3XNJmH?IgSRs&a}*J6br!JS!+@$b*i@f`0*sE=Y!2%so{H==!LZ?={u=;s_pmh z-7GPqU(|wl`?Y(zE?cEXW-34Mp3eJeo}5J_C4D8bp(P1{{Vm5VR}Xhe4hzg;mQ-2% z4k;J%T5SRD2*h|xahM)Ev*b6k5_e&s5XfW*->mU!f00YyGPHD&dEd5_k{4!>@G=%> zb;aq>b7yRDd~?&`1Ao)_KcAhS0>Q{OP@|2AT(oWf;NR%i&VZpdu<>8&bUZCv={q+$ zT%{BZUfHcNzFA}!e`+AOD+zOT4Yu#vMBtqmwRhMfY7aGrt8x)EAY466Y`0jQr z7EZf)4K9Dpl8q;7P)tMfX|a2g3r68Rf*tb-PSLSu^9zUGz(Op6bDLTaDtN+Kl;F9k z(Lixpu9IBF@gvT;o#_ez-HW#3Vmq5Rx!(6fBa)QS_?(c@6eT*IM!+NV!^=Wq z9)NxraB9G#)+Sc_BD)jO#uE&hqYU1<*CZ2qwkJ+jv7=!W_`(G6%wU}9KR*-y)I>C4 zWF!0?gKcaU%JB>NDq$P|t+E=xnM!L0pwL~N9$N{Lywi5io5Q2-b9GG-;8#VR!YW6e z{|ch;1xnkeI7fv&8)s^DpDXo+N{@;n3t0W^%Q^mLzyB7E=}QjIbR0-h&9i$(=`8$Q e`)31|-DTSp_6-(eIx-LTi_Qf@jY2i6pnm{5M6z`N diff --git a/css/jquery-ui.min.css b/css/jquery-ui.min.css index b0a15796..8a59b9db 100644 --- a/css/jquery-ui.min.css +++ b/css/jquery-ui.min.css @@ -1,7 +1,7 @@ -/*! jQuery UI - v1.10.4 - 2018-06-04 +/*! jQuery UI - v1.12.1 - 2018-06-17 * http://jqueryui.com -* Includes: jquery.ui.core.css, jquery.ui.resizable.css, jquery.ui.selectable.css, jquery.ui.accordion.css, jquery.ui.autocomplete.css, jquery.ui.button.css, jquery.ui.datepicker.css, jquery.ui.dialog.css, jquery.ui.menu.css, jquery.ui.progressbar.css, jquery.ui.slider.css, jquery.ui.spinner.css, jquery.ui.tabs.css, jquery.ui.tooltip.css, jquery.ui.theme.css -* To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Verdana%2CArial%2Csans-serif&fwDefault=normal&fsDefault=1.1em&cornerRadius=4px&bgColorHeader=cccccc&bgTextureHeader=highlight_soft&bgImgOpacityHeader=75&borderColorHeader=aaaaaa&fcHeader=222222&iconColorHeader=222222&bgColorContent=ffffff&bgTextureContent=flat&bgImgOpacityContent=75&borderColorContent=aaaaaa&fcContent=222222&iconColorContent=222222&bgColorDefault=e6e6e6&bgTextureDefault=glass&bgImgOpacityDefault=75&borderColorDefault=d3d3d3&fcDefault=555555&iconColorDefault=888888&bgColorHover=dadada&bgTextureHover=glass&bgImgOpacityHover=75&borderColorHover=999999&fcHover=212121&iconColorHover=454545&bgColorActive=ffffff&bgTextureActive=glass&bgImgOpacityActive=65&borderColorActive=aaaaaa&fcActive=212121&iconColorActive=454545&bgColorHighlight=fbf9ee&bgTextureHighlight=glass&bgImgOpacityHighlight=55&borderColorHighlight=fcefa1&fcHighlight=363636&iconColorHighlight=2e83ff&bgColorError=fef1ec&bgTextureError=glass&bgImgOpacityError=95&borderColorError=cd0a0a&fcError=cd0a0a&iconColorError=cd0a0a&bgColorOverlay=aaaaaa&bgTextureOverlay=flat&bgImgOpacityOverlay=0&opacityOverlay=30&bgColorShadow=aaaaaa&bgTextureShadow=flat&bgImgOpacityShadow=0&opacityShadow=30&thicknessShadow=8px&offsetTopShadow=-8px&offsetLeftShadow=-8px&cornerRadiusShadow=8px +* Includes: draggable.css, core.css, resizable.css, selectable.css, sortable.css, accordion.css, autocomplete.css, menu.css, button.css, controlgroup.css, checkboxradio.css, datepicker.css, dialog.css, progressbar.css, selectmenu.css, slider.css, spinner.css, tabs.css, tooltip.css, theme.css +* To view and modify this theme, visit http://jqueryui.com/themeroller/?scope=&folderName=base&cornerRadiusShadow=8px&offsetLeftShadow=0px&offsetTopShadow=0px&thicknessShadow=5px&opacityShadow=30&bgImgOpacityShadow=0&bgTextureShadow=flat&bgColorShadow=666666&opacityOverlay=30&bgImgOpacityOverlay=0&bgTextureOverlay=flat&bgColorOverlay=aaaaaa&iconColorError=cc0000&fcError=5f3f3f&borderColorError=f1a899&bgTextureError=flat&bgColorError=fddfdf&iconColorHighlight=777620&fcHighlight=777620&borderColorHighlight=dad55e&bgTextureHighlight=flat&bgColorHighlight=fffa90&iconColorActive=ffffff&fcActive=ffffff&borderColorActive=003eff&bgTextureActive=flat&bgColorActive=007fff&iconColorHover=555555&fcHover=2b2b2b&borderColorHover=cccccc&bgTextureHover=flat&bgColorHover=ededed&iconColorDefault=777777&fcDefault=454545&borderColorDefault=c5c5c5&bgTextureDefault=flat&bgColorDefault=f6f6f6&iconColorContent=444444&fcContent=333333&borderColorContent=dddddd&bgTextureContent=flat&bgColorContent=ffffff&iconColorHeader=444444&fcHeader=333333&borderColorHeader=dddddd&bgTextureHeader=flat&bgColorHeader=e9e9e9&cornerRadius=3px&fwDefault=normal&fsDefault=1em&ffDefault=Arial%2CHelvetica%2Csans-serif * Copyright jQuery Foundation and other contributors; Licensed MIT */ -.ui-helper-hidden{display:none}.ui-helper-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.ui-helper-reset{margin:0;padding:0;border:0;outline:0;line-height:1.3;text-decoration:none;font-size:100%;list-style:none}.ui-helper-clearfix:before,.ui-helper-clearfix:after{content:"";display:table;border-collapse:collapse}.ui-helper-clearfix:after{clear:both}.ui-helper-clearfix{min-height:0}.ui-helper-zfix{width:100%;height:100%;top:0;left:0;position:absolute;opacity:0;filter:Alpha(Opacity=0)}.ui-front{z-index:100}.ui-state-disabled{cursor:default!important}.ui-icon{display:block;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat}.ui-widget-overlay{position:fixed;top:0;left:0;width:100%;height:100%}.ui-resizable{position:relative}.ui-resizable-handle{position:absolute;font-size:0.1px;display:block}.ui-resizable-disabled .ui-resizable-handle,.ui-resizable-autohide .ui-resizable-handle{display:none}.ui-resizable-n{cursor:n-resize;height:7px;width:100%;top:-5px;left:0}.ui-resizable-s{cursor:s-resize;height:7px;width:100%;bottom:-5px;left:0}.ui-resizable-e{cursor:e-resize;width:7px;right:-5px;top:0;height:100%}.ui-resizable-w{cursor:w-resize;width:7px;left:-5px;top:0;height:100%}.ui-resizable-se{cursor:se-resize;width:12px;height:12px;right:1px;bottom:1px}.ui-resizable-sw{cursor:sw-resize;width:9px;height:9px;left:-5px;bottom:-5px}.ui-resizable-nw{cursor:nw-resize;width:9px;height:9px;left:-5px;top:-5px}.ui-resizable-ne{cursor:ne-resize;width:9px;height:9px;right:-5px;top:-5px}.ui-selectable-helper{position:absolute;z-index:100;border:1px dotted black}.ui-accordion .ui-accordion-header{display:block;cursor:pointer;position:relative;margin-top:2px;padding:.5em .5em .5em .7em;min-height:0}.ui-accordion .ui-accordion-icons{padding-left:2.2em}.ui-accordion .ui-accordion-noicons{padding-left:.7em}.ui-accordion .ui-accordion-icons .ui-accordion-icons{padding-left:2.2em}.ui-accordion .ui-accordion-header .ui-accordion-header-icon{position:absolute;left:.5em;top:50%;margin-top:-8px}.ui-accordion .ui-accordion-content{padding:1em 2.2em;border-top:0;overflow:auto}.ui-autocomplete{position:absolute;top:0;left:0;cursor:default}.ui-button{display:inline-block;position:relative;padding:0;line-height:normal;margin-right:.1em;cursor:pointer;vertical-align:middle;text-align:center;overflow:visible}.ui-button,.ui-button:link,.ui-button:visited,.ui-button:hover,.ui-button:active{text-decoration:none}.ui-button-icon-only{width:2.2em}button.ui-button-icon-only{width:2.4em}.ui-button-icons-only{width:3.4em}button.ui-button-icons-only{width:3.7em}.ui-button .ui-button-text{display:block;line-height:normal}.ui-button-text-only .ui-button-text{padding:.4em 1em}.ui-button-icon-only .ui-button-text,.ui-button-icons-only .ui-button-text{padding:.4em;text-indent:-9999999px}.ui-button-text-icon-primary .ui-button-text,.ui-button-text-icons .ui-button-text{padding:.4em 1em .4em 2.1em}.ui-button-text-icon-secondary .ui-button-text,.ui-button-text-icons .ui-button-text{padding:.4em 2.1em .4em 1em}.ui-button-text-icons .ui-button-text{padding-left:2.1em;padding-right:2.1em}input.ui-button{padding:.4em 1em}.ui-button-icon-only .ui-icon,.ui-button-text-icon-primary .ui-icon,.ui-button-text-icon-secondary .ui-icon,.ui-button-text-icons .ui-icon,.ui-button-icons-only .ui-icon{position:absolute;top:50%;margin-top:-8px}.ui-button-icon-only .ui-icon{left:50%;margin-left:-8px}.ui-button-text-icon-primary .ui-button-icon-primary,.ui-button-text-icons .ui-button-icon-primary,.ui-button-icons-only .ui-button-icon-primary{left:.5em}.ui-button-text-icon-secondary .ui-button-icon-secondary,.ui-button-text-icons .ui-button-icon-secondary,.ui-button-icons-only .ui-button-icon-secondary{right:.5em}.ui-buttonset{margin-right:7px}.ui-buttonset .ui-button{margin-left:0;margin-right:-.3em}input.ui-button::-moz-focus-inner,button.ui-button::-moz-focus-inner{border:0;padding:0}.ui-datepicker{width:17em;padding:.2em .2em 0;display:none}.ui-datepicker .ui-datepicker-header{position:relative;padding:.2em 0}.ui-datepicker .ui-datepicker-prev,.ui-datepicker .ui-datepicker-next{position:absolute;top:2px;width:1.8em;height:1.8em}.ui-datepicker .ui-datepicker-prev-hover,.ui-datepicker .ui-datepicker-next-hover{top:1px}.ui-datepicker .ui-datepicker-prev{left:2px}.ui-datepicker .ui-datepicker-next{right:2px}.ui-datepicker .ui-datepicker-prev-hover{left:1px}.ui-datepicker .ui-datepicker-next-hover{right:1px}.ui-datepicker .ui-datepicker-prev span,.ui-datepicker .ui-datepicker-next span{display:block;position:absolute;left:50%;margin-left:-8px;top:50%;margin-top:-8px}.ui-datepicker .ui-datepicker-title{margin:0 2.3em;line-height:1.8em;text-align:center}.ui-datepicker .ui-datepicker-title select{font-size:1em;margin:1px 0}.ui-datepicker select.ui-datepicker-month,.ui-datepicker select.ui-datepicker-year{width:49%}.ui-datepicker table{width:100%;font-size:.9em;border-collapse:collapse;margin:0 0 .4em}.ui-datepicker th{padding:.7em .3em;text-align:center;font-weight:bold;border:0}.ui-datepicker td{border:0;padding:1px}.ui-datepicker td span,.ui-datepicker td a{display:block;padding:.2em;text-align:right;text-decoration:none}.ui-datepicker .ui-datepicker-buttonpane{background-image:none;margin:.7em 0 0 0;padding:0 .2em;border-left:0;border-right:0;border-bottom:0}.ui-datepicker .ui-datepicker-buttonpane button{float:right;margin:.5em .2em .4em;cursor:pointer;padding:.2em .6em .3em .6em;width:auto;overflow:visible}.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current{float:left}.ui-datepicker.ui-datepicker-multi{width:auto}.ui-datepicker-multi .ui-datepicker-group{float:left}.ui-datepicker-multi .ui-datepicker-group table{width:95%;margin:0 auto .4em}.ui-datepicker-multi-2 .ui-datepicker-group{width:50%}.ui-datepicker-multi-3 .ui-datepicker-group{width:33.3%}.ui-datepicker-multi-4 .ui-datepicker-group{width:25%}.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header,.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header{border-left-width:0}.ui-datepicker-multi .ui-datepicker-buttonpane{clear:left}.ui-datepicker-row-break{clear:both;width:100%;font-size:0}.ui-datepicker-rtl{direction:rtl}.ui-datepicker-rtl .ui-datepicker-prev{right:2px;left:auto}.ui-datepicker-rtl .ui-datepicker-next{left:2px;right:auto}.ui-datepicker-rtl .ui-datepicker-prev:hover{right:1px;left:auto}.ui-datepicker-rtl .ui-datepicker-next:hover{left:1px;right:auto}.ui-datepicker-rtl .ui-datepicker-buttonpane{clear:right}.ui-datepicker-rtl .ui-datepicker-buttonpane button{float:left}.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current,.ui-datepicker-rtl .ui-datepicker-group{float:right}.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header,.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header{border-right-width:0;border-left-width:1px}.ui-dialog{overflow:hidden;position:absolute;top:0;left:0;padding:.2em;outline:0}.ui-dialog .ui-dialog-titlebar{padding:.4em 1em;position:relative}.ui-dialog .ui-dialog-title{float:left;margin:.1em 0;white-space:nowrap;width:90%;overflow:hidden;text-overflow:ellipsis}.ui-dialog .ui-dialog-titlebar-close{position:absolute;right:.3em;top:50%;width:20px;margin:-10px 0 0 0;padding:1px;height:20px}.ui-dialog .ui-dialog-content{position:relative;border:0;padding:.5em 1em;background:none;overflow:auto}.ui-dialog .ui-dialog-buttonpane{text-align:left;border-width:1px 0 0 0;background-image:none;margin-top:.5em;padding:.3em 1em .5em .4em}.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset{float:right}.ui-dialog .ui-dialog-buttonpane button{margin:.5em .4em .5em 0;cursor:pointer}.ui-dialog .ui-resizable-se{width:12px;height:12px;right:-5px;bottom:-5px;background-position:16px 16px}.ui-draggable .ui-dialog-titlebar{cursor:move}.ui-menu{list-style:none;padding:2px;margin:0;display:block;outline:none}.ui-menu .ui-menu{margin-top:-3px;position:absolute}.ui-menu .ui-menu-item{margin:0;padding:0;width:100%;list-style-image:url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)}.ui-menu .ui-menu-divider{margin:5px -2px 5px -2px;height:0;font-size:0;line-height:0;border-width:1px 0 0 0}.ui-menu .ui-menu-item a{text-decoration:none;display:block;padding:2px .4em;line-height:1.5;min-height:0;font-weight:normal}.ui-menu .ui-menu-item a.ui-state-focus,.ui-menu .ui-menu-item a.ui-state-active{font-weight:normal;margin:-1px}.ui-menu .ui-state-disabled{font-weight:normal;margin:.4em 0 .2em;line-height:1.5}.ui-menu .ui-state-disabled a{cursor:default}.ui-menu-icons{position:relative}.ui-menu-icons .ui-menu-item a{position:relative;padding-left:2em}.ui-menu .ui-icon{position:absolute;top:.2em;left:.2em}.ui-menu .ui-menu-icon{position:static;float:right}.ui-progressbar{height:2em;text-align:left;overflow:hidden}.ui-progressbar .ui-progressbar-value{margin:-1px;height:100%}.ui-progressbar .ui-progressbar-overlay{background:url("images/animated-overlay.gif");height:100%;filter:alpha(opacity=25);opacity:0.25}.ui-progressbar-indeterminate .ui-progressbar-value{background-image:none}.ui-slider{position:relative;text-align:left}.ui-slider .ui-slider-handle{position:absolute;z-index:2;width:1.2em;height:1.2em;cursor:default}.ui-slider .ui-slider-range{position:absolute;z-index:1;font-size:.7em;display:block;border:0;background-position:0 0}.ui-slider.ui-state-disabled .ui-slider-handle,.ui-slider.ui-state-disabled .ui-slider-range{filter:inherit}.ui-slider-horizontal{height:.8em}.ui-slider-horizontal .ui-slider-handle{top:-.3em;margin-left:-.6em}.ui-slider-horizontal .ui-slider-range{top:0;height:100%}.ui-slider-horizontal .ui-slider-range-min{left:0}.ui-slider-horizontal .ui-slider-range-max{right:0}.ui-slider-vertical{width:.8em;height:100px}.ui-slider-vertical .ui-slider-handle{left:-.3em;margin-left:0;margin-bottom:-.6em}.ui-slider-vertical .ui-slider-range{left:0;width:100%}.ui-slider-vertical .ui-slider-range-min{bottom:0}.ui-slider-vertical .ui-slider-range-max{top:0}.ui-spinner{position:relative;display:inline-block;overflow:hidden;padding:0;vertical-align:middle}.ui-spinner-input{border:none;background:none;color:inherit;padding:0;margin:.2em 0;vertical-align:middle;margin-left:.4em;margin-right:22px}.ui-spinner-button{width:16px;height:50%;font-size:.5em;padding:0;margin:0;text-align:center;position:absolute;cursor:default;display:block;overflow:hidden;right:0}.ui-spinner a.ui-spinner-button{border-top:none;border-bottom:none;border-right:none}.ui-spinner .ui-icon{position:absolute;margin-top:-8px;top:50%;left:0}.ui-spinner-up{top:0}.ui-spinner-down{bottom:0}.ui-spinner .ui-icon-triangle-1-s{background-position:-65px -16px}.ui-tabs{position:relative;padding:.2em}.ui-tabs .ui-tabs-nav{margin:0;padding:.2em .2em 0}.ui-tabs .ui-tabs-nav li{list-style:none;float:left;position:relative;top:0;margin:1px .2em 0 0;border-bottom-width:0;padding:0;white-space:nowrap}.ui-tabs .ui-tabs-nav .ui-tabs-anchor{float:left;padding:.5em 1em;text-decoration:none}.ui-tabs .ui-tabs-nav li.ui-tabs-active{margin-bottom:-1px;padding-bottom:1px}.ui-tabs .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor,.ui-tabs .ui-tabs-nav li.ui-state-disabled .ui-tabs-anchor,.ui-tabs .ui-tabs-nav li.ui-tabs-loading .ui-tabs-anchor{cursor:text}.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor{cursor:pointer}.ui-tabs .ui-tabs-panel{display:block;border-width:0;padding:1em 1.4em;background:none}.ui-tooltip{padding:8px;position:absolute;z-index:9999;max-width:300px;-webkit-box-shadow:0 0 5px #aaa;box-shadow:0 0 5px #aaa}body .ui-tooltip{border-width:2px}.ui-widget{font-family:Verdana,Arial,sans-serif;font-size:1.1em}.ui-widget .ui-widget{font-size:1em}.ui-widget input,.ui-widget select,.ui-widget textarea,.ui-widget button{font-family:Verdana,Arial,sans-serif;font-size:1em}.ui-widget-content{border:1px solid #aaa;background:#fff;color:#222}.ui-widget-content a{color:#222}.ui-widget-header{border:1px solid #aaa;background:#ccc url("images/ui-bg_highlight-soft_75_cccccc_1x100.png") 50% 50% repeat-x;color:#222;font-weight:bold}.ui-widget-header a{color:#222}.ui-state-default,.ui-widget-content .ui-state-default,.ui-widget-header .ui-state-default{border:1px solid #d3d3d3;background:#e6e6e6 url("images/ui-bg_glass_75_e6e6e6_1x400.png") 50% 50% repeat-x;font-weight:normal;color:#555}.ui-state-default a,.ui-state-default a:link,.ui-state-default a:visited{color:#555;text-decoration:none}.ui-state-hover,.ui-widget-content .ui-state-hover,.ui-widget-header .ui-state-hover,.ui-state-focus,.ui-widget-content .ui-state-focus,.ui-widget-header .ui-state-focus{border:1px solid #999;background:#dadada url("images/ui-bg_glass_75_dadada_1x400.png") 50% 50% repeat-x;font-weight:normal;color:#212121}.ui-state-hover a,.ui-state-hover a:hover,.ui-state-hover a:link,.ui-state-hover a:visited,.ui-state-focus a,.ui-state-focus a:hover,.ui-state-focus a:link,.ui-state-focus a:visited{color:#212121;text-decoration:none}.ui-state-active,.ui-widget-content .ui-state-active,.ui-widget-header .ui-state-active{border:1px solid #aaa;background:#fff url("images/ui-bg_glass_65_ffffff_1x400.png") 50% 50% repeat-x;font-weight:normal;color:#212121}.ui-state-active a,.ui-state-active a:link,.ui-state-active a:visited{color:#212121;text-decoration:none}.ui-state-highlight,.ui-widget-content .ui-state-highlight,.ui-widget-header .ui-state-highlight{border:1px solid #fcefa1;background:#fbf9ee url("images/ui-bg_glass_55_fbf9ee_1x400.png") 50% 50% repeat-x;color:#363636}.ui-state-highlight a,.ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a{color:#363636}.ui-state-error,.ui-widget-content .ui-state-error,.ui-widget-header .ui-state-error{border:1px solid #cd0a0a;background:#fef1ec url("images/ui-bg_glass_95_fef1ec_1x400.png") 50% 50% repeat-x;color:#cd0a0a}.ui-state-error a,.ui-widget-content .ui-state-error a,.ui-widget-header .ui-state-error a{color:#cd0a0a}.ui-state-error-text,.ui-widget-content .ui-state-error-text,.ui-widget-header .ui-state-error-text{color:#cd0a0a}.ui-priority-primary,.ui-widget-content .ui-priority-primary,.ui-widget-header .ui-priority-primary{font-weight:bold}.ui-priority-secondary,.ui-widget-content .ui-priority-secondary,.ui-widget-header .ui-priority-secondary{opacity:.7;filter:Alpha(Opacity=70);font-weight:normal}.ui-state-disabled,.ui-widget-content .ui-state-disabled,.ui-widget-header .ui-state-disabled{opacity:.35;filter:Alpha(Opacity=35);background-image:none}.ui-state-disabled .ui-icon{filter:Alpha(Opacity=35)}.ui-icon{width:16px;height:16px}.ui-icon,.ui-widget-content .ui-icon{background-image:url("images/ui-icons_222222_256x240.png")}.ui-widget-header .ui-icon{background-image:url("images/ui-icons_222222_256x240.png")}.ui-state-default .ui-icon{background-image:url("images/ui-icons_888888_256x240.png")}.ui-state-hover .ui-icon,.ui-state-focus .ui-icon{background-image:url("images/ui-icons_454545_256x240.png")}.ui-state-active .ui-icon{background-image:url("images/ui-icons_454545_256x240.png")}.ui-state-highlight .ui-icon{background-image:url("images/ui-icons_2e83ff_256x240.png")}.ui-state-error .ui-icon,.ui-state-error-text .ui-icon{background-image:url("images/ui-icons_cd0a0a_256x240.png")}.ui-icon-blank{background-position:16px 16px}.ui-icon-carat-1-n{background-position:0 0}.ui-icon-carat-1-ne{background-position:-16px 0}.ui-icon-carat-1-e{background-position:-32px 0}.ui-icon-carat-1-se{background-position:-48px 0}.ui-icon-carat-1-s{background-position:-64px 0}.ui-icon-carat-1-sw{background-position:-80px 0}.ui-icon-carat-1-w{background-position:-96px 0}.ui-icon-carat-1-nw{background-position:-112px 0}.ui-icon-carat-2-n-s{background-position:-128px 0}.ui-icon-carat-2-e-w{background-position:-144px 0}.ui-icon-triangle-1-n{background-position:0 -16px}.ui-icon-triangle-1-ne{background-position:-16px -16px}.ui-icon-triangle-1-e{background-position:-32px -16px}.ui-icon-triangle-1-se{background-position:-48px -16px}.ui-icon-triangle-1-s{background-position:-64px -16px}.ui-icon-triangle-1-sw{background-position:-80px -16px}.ui-icon-triangle-1-w{background-position:-96px -16px}.ui-icon-triangle-1-nw{background-position:-112px -16px}.ui-icon-triangle-2-n-s{background-position:-128px -16px}.ui-icon-triangle-2-e-w{background-position:-144px -16px}.ui-icon-arrow-1-n{background-position:0 -32px}.ui-icon-arrow-1-ne{background-position:-16px -32px}.ui-icon-arrow-1-e{background-position:-32px -32px}.ui-icon-arrow-1-se{background-position:-48px -32px}.ui-icon-arrow-1-s{background-position:-64px -32px}.ui-icon-arrow-1-sw{background-position:-80px -32px}.ui-icon-arrow-1-w{background-position:-96px -32px}.ui-icon-arrow-1-nw{background-position:-112px -32px}.ui-icon-arrow-2-n-s{background-position:-128px -32px}.ui-icon-arrow-2-ne-sw{background-position:-144px -32px}.ui-icon-arrow-2-e-w{background-position:-160px -32px}.ui-icon-arrow-2-se-nw{background-position:-176px -32px}.ui-icon-arrowstop-1-n{background-position:-192px -32px}.ui-icon-arrowstop-1-e{background-position:-208px -32px}.ui-icon-arrowstop-1-s{background-position:-224px -32px}.ui-icon-arrowstop-1-w{background-position:-240px -32px}.ui-icon-arrowthick-1-n{background-position:0 -48px}.ui-icon-arrowthick-1-ne{background-position:-16px -48px}.ui-icon-arrowthick-1-e{background-position:-32px -48px}.ui-icon-arrowthick-1-se{background-position:-48px -48px}.ui-icon-arrowthick-1-s{background-position:-64px -48px}.ui-icon-arrowthick-1-sw{background-position:-80px -48px}.ui-icon-arrowthick-1-w{background-position:-96px -48px}.ui-icon-arrowthick-1-nw{background-position:-112px -48px}.ui-icon-arrowthick-2-n-s{background-position:-128px -48px}.ui-icon-arrowthick-2-ne-sw{background-position:-144px -48px}.ui-icon-arrowthick-2-e-w{background-position:-160px -48px}.ui-icon-arrowthick-2-se-nw{background-position:-176px -48px}.ui-icon-arrowthickstop-1-n{background-position:-192px -48px}.ui-icon-arrowthickstop-1-e{background-position:-208px -48px}.ui-icon-arrowthickstop-1-s{background-position:-224px -48px}.ui-icon-arrowthickstop-1-w{background-position:-240px -48px}.ui-icon-arrowreturnthick-1-w{background-position:0 -64px}.ui-icon-arrowreturnthick-1-n{background-position:-16px -64px}.ui-icon-arrowreturnthick-1-e{background-position:-32px -64px}.ui-icon-arrowreturnthick-1-s{background-position:-48px -64px}.ui-icon-arrowreturn-1-w{background-position:-64px -64px}.ui-icon-arrowreturn-1-n{background-position:-80px -64px}.ui-icon-arrowreturn-1-e{background-position:-96px -64px}.ui-icon-arrowreturn-1-s{background-position:-112px -64px}.ui-icon-arrowrefresh-1-w{background-position:-128px -64px}.ui-icon-arrowrefresh-1-n{background-position:-144px -64px}.ui-icon-arrowrefresh-1-e{background-position:-160px -64px}.ui-icon-arrowrefresh-1-s{background-position:-176px -64px}.ui-icon-arrow-4{background-position:0 -80px}.ui-icon-arrow-4-diag{background-position:-16px -80px}.ui-icon-extlink{background-position:-32px -80px}.ui-icon-newwin{background-position:-48px -80px}.ui-icon-refresh{background-position:-64px -80px}.ui-icon-shuffle{background-position:-80px -80px}.ui-icon-transfer-e-w{background-position:-96px -80px}.ui-icon-transferthick-e-w{background-position:-112px -80px}.ui-icon-folder-collapsed{background-position:0 -96px}.ui-icon-folder-open{background-position:-16px -96px}.ui-icon-document{background-position:-32px -96px}.ui-icon-document-b{background-position:-48px -96px}.ui-icon-note{background-position:-64px -96px}.ui-icon-mail-closed{background-position:-80px -96px}.ui-icon-mail-open{background-position:-96px -96px}.ui-icon-suitcase{background-position:-112px -96px}.ui-icon-comment{background-position:-128px -96px}.ui-icon-person{background-position:-144px -96px}.ui-icon-print{background-position:-160px -96px}.ui-icon-trash{background-position:-176px -96px}.ui-icon-locked{background-position:-192px -96px}.ui-icon-unlocked{background-position:-208px -96px}.ui-icon-bookmark{background-position:-224px -96px}.ui-icon-tag{background-position:-240px -96px}.ui-icon-home{background-position:0 -112px}.ui-icon-flag{background-position:-16px -112px}.ui-icon-calendar{background-position:-32px -112px}.ui-icon-cart{background-position:-48px -112px}.ui-icon-pencil{background-position:-64px -112px}.ui-icon-clock{background-position:-80px -112px}.ui-icon-disk{background-position:-96px -112px}.ui-icon-calculator{background-position:-112px -112px}.ui-icon-zoomin{background-position:-128px -112px}.ui-icon-zoomout{background-position:-144px -112px}.ui-icon-search{background-position:-160px -112px}.ui-icon-wrench{background-position:-176px -112px}.ui-icon-gear{background-position:-192px -112px}.ui-icon-heart{background-position:-208px -112px}.ui-icon-star{background-position:-224px -112px}.ui-icon-link{background-position:-240px -112px}.ui-icon-cancel{background-position:0 -128px}.ui-icon-plus{background-position:-16px -128px}.ui-icon-plusthick{background-position:-32px -128px}.ui-icon-minus{background-position:-48px -128px}.ui-icon-minusthick{background-position:-64px -128px}.ui-icon-close{background-position:-80px -128px}.ui-icon-closethick{background-position:-96px -128px}.ui-icon-key{background-position:-112px -128px}.ui-icon-lightbulb{background-position:-128px -128px}.ui-icon-scissors{background-position:-144px -128px}.ui-icon-clipboard{background-position:-160px -128px}.ui-icon-copy{background-position:-176px -128px}.ui-icon-contact{background-position:-192px -128px}.ui-icon-image{background-position:-208px -128px}.ui-icon-video{background-position:-224px -128px}.ui-icon-script{background-position:-240px -128px}.ui-icon-alert{background-position:0 -144px}.ui-icon-info{background-position:-16px -144px}.ui-icon-notice{background-position:-32px -144px}.ui-icon-help{background-position:-48px -144px}.ui-icon-check{background-position:-64px -144px}.ui-icon-bullet{background-position:-80px -144px}.ui-icon-radio-on{background-position:-96px -144px}.ui-icon-radio-off{background-position:-112px -144px}.ui-icon-pin-w{background-position:-128px -144px}.ui-icon-pin-s{background-position:-144px -144px}.ui-icon-play{background-position:0 -160px}.ui-icon-pause{background-position:-16px -160px}.ui-icon-seek-next{background-position:-32px -160px}.ui-icon-seek-prev{background-position:-48px -160px}.ui-icon-seek-end{background-position:-64px -160px}.ui-icon-seek-start{background-position:-80px -160px}.ui-icon-seek-first{background-position:-80px -160px}.ui-icon-stop{background-position:-96px -160px}.ui-icon-eject{background-position:-112px -160px}.ui-icon-volume-off{background-position:-128px -160px}.ui-icon-volume-on{background-position:-144px -160px}.ui-icon-power{background-position:0 -176px}.ui-icon-signal-diag{background-position:-16px -176px}.ui-icon-signal{background-position:-32px -176px}.ui-icon-battery-0{background-position:-48px -176px}.ui-icon-battery-1{background-position:-64px -176px}.ui-icon-battery-2{background-position:-80px -176px}.ui-icon-battery-3{background-position:-96px -176px}.ui-icon-circle-plus{background-position:0 -192px}.ui-icon-circle-minus{background-position:-16px -192px}.ui-icon-circle-close{background-position:-32px -192px}.ui-icon-circle-triangle-e{background-position:-48px -192px}.ui-icon-circle-triangle-s{background-position:-64px -192px}.ui-icon-circle-triangle-w{background-position:-80px -192px}.ui-icon-circle-triangle-n{background-position:-96px -192px}.ui-icon-circle-arrow-e{background-position:-112px -192px}.ui-icon-circle-arrow-s{background-position:-128px -192px}.ui-icon-circle-arrow-w{background-position:-144px -192px}.ui-icon-circle-arrow-n{background-position:-160px -192px}.ui-icon-circle-zoomin{background-position:-176px -192px}.ui-icon-circle-zoomout{background-position:-192px -192px}.ui-icon-circle-check{background-position:-208px -192px}.ui-icon-circlesmall-plus{background-position:0 -208px}.ui-icon-circlesmall-minus{background-position:-16px -208px}.ui-icon-circlesmall-close{background-position:-32px -208px}.ui-icon-squaresmall-plus{background-position:-48px -208px}.ui-icon-squaresmall-minus{background-position:-64px -208px}.ui-icon-squaresmall-close{background-position:-80px -208px}.ui-icon-grip-dotted-vertical{background-position:0 -224px}.ui-icon-grip-dotted-horizontal{background-position:-16px -224px}.ui-icon-grip-solid-vertical{background-position:-32px -224px}.ui-icon-grip-solid-horizontal{background-position:-48px -224px}.ui-icon-gripsmall-diagonal-se{background-position:-64px -224px}.ui-icon-grip-diagonal-se{background-position:-80px -224px}.ui-corner-all,.ui-corner-top,.ui-corner-left,.ui-corner-tl{border-top-left-radius:4px}.ui-corner-all,.ui-corner-top,.ui-corner-right,.ui-corner-tr{border-top-right-radius:4px}.ui-corner-all,.ui-corner-bottom,.ui-corner-left,.ui-corner-bl{border-bottom-left-radius:4px}.ui-corner-all,.ui-corner-bottom,.ui-corner-right,.ui-corner-br{border-bottom-right-radius:4px}.ui-widget-overlay{background:#aaa;opacity:.3;filter:Alpha(Opacity=30)}.ui-widget-shadow{margin:-8px 0 0 -8px;padding:8px;background:#aaa;opacity:.3;filter:Alpha(Opacity=30);border-radius:8px} \ No newline at end of file +.ui-draggable-handle{-ms-touch-action:none;touch-action:none}.ui-helper-hidden{display:none}.ui-helper-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.ui-helper-reset{margin:0;padding:0;border:0;outline:0;line-height:1.3;text-decoration:none;font-size:100%;list-style:none}.ui-helper-clearfix:before,.ui-helper-clearfix:after{content:"";display:table;border-collapse:collapse}.ui-helper-clearfix:after{clear:both}.ui-helper-zfix{width:100%;height:100%;top:0;left:0;position:absolute;opacity:0;filter:Alpha(Opacity=0)}.ui-front{z-index:100}.ui-state-disabled{cursor:default!important;pointer-events:none}.ui-icon{display:inline-block;vertical-align:middle;margin-top:-.25em;position:relative;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat}.ui-widget-icon-block{left:50%;margin-left:-8px;display:block}.ui-widget-overlay{position:fixed;top:0;left:0;width:100%;height:100%}.ui-resizable{position:relative}.ui-resizable-handle{position:absolute;font-size:0.1px;display:block;-ms-touch-action:none;touch-action:none}.ui-resizable-disabled .ui-resizable-handle,.ui-resizable-autohide .ui-resizable-handle{display:none}.ui-resizable-n{cursor:n-resize;height:7px;width:100%;top:-5px;left:0}.ui-resizable-s{cursor:s-resize;height:7px;width:100%;bottom:-5px;left:0}.ui-resizable-e{cursor:e-resize;width:7px;right:-5px;top:0;height:100%}.ui-resizable-w{cursor:w-resize;width:7px;left:-5px;top:0;height:100%}.ui-resizable-se{cursor:se-resize;width:12px;height:12px;right:1px;bottom:1px}.ui-resizable-sw{cursor:sw-resize;width:9px;height:9px;left:-5px;bottom:-5px}.ui-resizable-nw{cursor:nw-resize;width:9px;height:9px;left:-5px;top:-5px}.ui-resizable-ne{cursor:ne-resize;width:9px;height:9px;right:-5px;top:-5px}.ui-selectable{-ms-touch-action:none;touch-action:none}.ui-selectable-helper{position:absolute;z-index:100;border:1px dotted black}.ui-sortable-handle{-ms-touch-action:none;touch-action:none}.ui-accordion .ui-accordion-header{display:block;cursor:pointer;position:relative;margin:2px 0 0 0;padding:.5em .5em .5em .7em;font-size:100%}.ui-accordion .ui-accordion-content{padding:1em 2.2em;border-top:0;overflow:auto}.ui-autocomplete{position:absolute;top:0;left:0;cursor:default}.ui-menu{list-style:none;padding:0;margin:0;display:block;outline:0}.ui-menu .ui-menu{position:absolute}.ui-menu .ui-menu-item{margin:0;cursor:pointer;list-style-image:url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7")}.ui-menu .ui-menu-item-wrapper{position:relative;padding:3px 1em 3px .4em}.ui-menu .ui-menu-divider{margin:5px 0;height:0;font-size:0;line-height:0;border-width:1px 0 0 0}.ui-menu .ui-state-focus,.ui-menu .ui-state-active{margin:-1px}.ui-menu-icons{position:relative}.ui-menu-icons .ui-menu-item-wrapper{padding-left:2em}.ui-menu .ui-icon{position:absolute;top:0;bottom:0;left:.2em;margin:auto 0}.ui-menu .ui-menu-icon{left:auto;right:0}.ui-button{padding:.4em 1em;display:inline-block;position:relative;line-height:normal;margin-right:.1em;cursor:pointer;vertical-align:middle;text-align:center;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;overflow:visible}.ui-button,.ui-button:link,.ui-button:visited,.ui-button:hover,.ui-button:active{text-decoration:none}.ui-button-icon-only{width:2em;box-sizing:border-box;text-indent:-9999px;white-space:nowrap}input.ui-button.ui-button-icon-only{text-indent:0}.ui-button-icon-only .ui-icon{position:absolute;top:50%;left:50%;margin-top:-8px;margin-left:-8px}.ui-button.ui-icon-notext .ui-icon{padding:0;width:2.1em;height:2.1em;text-indent:-9999px;white-space:nowrap}input.ui-button.ui-icon-notext .ui-icon{width:auto;height:auto;text-indent:0;white-space:normal;padding:.4em 1em}input.ui-button::-moz-focus-inner,button.ui-button::-moz-focus-inner{border:0;padding:0}.ui-controlgroup{vertical-align:middle;display:inline-block}.ui-controlgroup > .ui-controlgroup-item{float:left;margin-left:0;margin-right:0}.ui-controlgroup > .ui-controlgroup-item:focus,.ui-controlgroup > .ui-controlgroup-item.ui-visual-focus{z-index:9999}.ui-controlgroup-vertical > .ui-controlgroup-item{display:block;float:none;width:100%;margin-top:0;margin-bottom:0;text-align:left}.ui-controlgroup-vertical .ui-controlgroup-item{box-sizing:border-box}.ui-controlgroup .ui-controlgroup-label{padding:.4em 1em}.ui-controlgroup .ui-controlgroup-label span{font-size:80%}.ui-controlgroup-horizontal .ui-controlgroup-label + .ui-controlgroup-item{border-left:none}.ui-controlgroup-vertical .ui-controlgroup-label + .ui-controlgroup-item{border-top:none}.ui-controlgroup-horizontal .ui-controlgroup-label.ui-widget-content{border-right:none}.ui-controlgroup-vertical .ui-controlgroup-label.ui-widget-content{border-bottom:none}.ui-controlgroup-vertical .ui-spinner-input{width:75%;width:calc( 100% - 2.4em )}.ui-controlgroup-vertical .ui-spinner .ui-spinner-up{border-top-style:solid}.ui-checkboxradio-label .ui-icon-background{box-shadow:inset 1px 1px 1px #ccc;border-radius:.12em;border:none}.ui-checkboxradio-radio-label .ui-icon-background{width:16px;height:16px;border-radius:1em;overflow:visible;border:none}.ui-checkboxradio-radio-label.ui-checkboxradio-checked .ui-icon,.ui-checkboxradio-radio-label.ui-checkboxradio-checked:hover .ui-icon{background-image:none;width:8px;height:8px;border-width:4px;border-style:solid}.ui-checkboxradio-disabled{pointer-events:none}.ui-datepicker{width:17em;padding:.2em .2em 0;display:none}.ui-datepicker .ui-datepicker-header{position:relative;padding:.2em 0}.ui-datepicker .ui-datepicker-prev,.ui-datepicker .ui-datepicker-next{position:absolute;top:2px;width:1.8em;height:1.8em}.ui-datepicker .ui-datepicker-prev-hover,.ui-datepicker .ui-datepicker-next-hover{top:1px}.ui-datepicker .ui-datepicker-prev{left:2px}.ui-datepicker .ui-datepicker-next{right:2px}.ui-datepicker .ui-datepicker-prev-hover{left:1px}.ui-datepicker .ui-datepicker-next-hover{right:1px}.ui-datepicker .ui-datepicker-prev span,.ui-datepicker .ui-datepicker-next span{display:block;position:absolute;left:50%;margin-left:-8px;top:50%;margin-top:-8px}.ui-datepicker .ui-datepicker-title{margin:0 2.3em;line-height:1.8em;text-align:center}.ui-datepicker .ui-datepicker-title select{font-size:1em;margin:1px 0}.ui-datepicker select.ui-datepicker-month,.ui-datepicker select.ui-datepicker-year{width:45%}.ui-datepicker table{width:100%;font-size:.9em;border-collapse:collapse;margin:0 0 .4em}.ui-datepicker th{padding:.7em .3em;text-align:center;font-weight:bold;border:0}.ui-datepicker td{border:0;padding:1px}.ui-datepicker td span,.ui-datepicker td a{display:block;padding:.2em;text-align:right;text-decoration:none}.ui-datepicker .ui-datepicker-buttonpane{background-image:none;margin:.7em 0 0 0;padding:0 .2em;border-left:0;border-right:0;border-bottom:0}.ui-datepicker .ui-datepicker-buttonpane button{float:right;margin:.5em .2em .4em;cursor:pointer;padding:.2em .6em .3em .6em;width:auto;overflow:visible}.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current{float:left}.ui-datepicker.ui-datepicker-multi{width:auto}.ui-datepicker-multi .ui-datepicker-group{float:left}.ui-datepicker-multi .ui-datepicker-group table{width:95%;margin:0 auto .4em}.ui-datepicker-multi-2 .ui-datepicker-group{width:50%}.ui-datepicker-multi-3 .ui-datepicker-group{width:33.3%}.ui-datepicker-multi-4 .ui-datepicker-group{width:25%}.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header,.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header{border-left-width:0}.ui-datepicker-multi .ui-datepicker-buttonpane{clear:left}.ui-datepicker-row-break{clear:both;width:100%;font-size:0}.ui-datepicker-rtl{direction:rtl}.ui-datepicker-rtl .ui-datepicker-prev{right:2px;left:auto}.ui-datepicker-rtl .ui-datepicker-next{left:2px;right:auto}.ui-datepicker-rtl .ui-datepicker-prev:hover{right:1px;left:auto}.ui-datepicker-rtl .ui-datepicker-next:hover{left:1px;right:auto}.ui-datepicker-rtl .ui-datepicker-buttonpane{clear:right}.ui-datepicker-rtl .ui-datepicker-buttonpane button{float:left}.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current,.ui-datepicker-rtl .ui-datepicker-group{float:right}.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header,.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header{border-right-width:0;border-left-width:1px}.ui-datepicker .ui-icon{display:block;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat;left:.5em;top:.3em}.ui-dialog{position:absolute;top:0;left:0;padding:.2em;outline:0}.ui-dialog .ui-dialog-titlebar{padding:.4em 1em;position:relative}.ui-dialog .ui-dialog-title{float:left;margin:.1em 0;white-space:nowrap;width:90%;overflow:hidden;text-overflow:ellipsis}.ui-dialog .ui-dialog-titlebar-close{position:absolute;right:.3em;top:50%;width:20px;margin:-10px 0 0 0;padding:1px;height:20px}.ui-dialog .ui-dialog-content{position:relative;border:0;padding:.5em 1em;background:none;overflow:auto}.ui-dialog .ui-dialog-buttonpane{text-align:left;border-width:1px 0 0 0;background-image:none;margin-top:.5em;padding:.3em 1em .5em .4em}.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset{float:right}.ui-dialog .ui-dialog-buttonpane button{margin:.5em .4em .5em 0;cursor:pointer}.ui-dialog .ui-resizable-n{height:2px;top:0}.ui-dialog .ui-resizable-e{width:2px;right:0}.ui-dialog .ui-resizable-s{height:2px;bottom:0}.ui-dialog .ui-resizable-w{width:2px;left:0}.ui-dialog .ui-resizable-se,.ui-dialog .ui-resizable-sw,.ui-dialog .ui-resizable-ne,.ui-dialog .ui-resizable-nw{width:7px;height:7px}.ui-dialog .ui-resizable-se{right:0;bottom:0}.ui-dialog .ui-resizable-sw{left:0;bottom:0}.ui-dialog .ui-resizable-ne{right:0;top:0}.ui-dialog .ui-resizable-nw{left:0;top:0}.ui-draggable .ui-dialog-titlebar{cursor:move}.ui-progressbar{height:2em;text-align:left;overflow:hidden}.ui-progressbar .ui-progressbar-value{margin:-1px;height:100%}.ui-progressbar .ui-progressbar-overlay{background:url("data:image/gif;base64,R0lGODlhKAAoAIABAAAAAP///yH/C05FVFNDQVBFMi4wAwEAAAAh+QQJAQABACwAAAAAKAAoAAACkYwNqXrdC52DS06a7MFZI+4FHBCKoDeWKXqymPqGqxvJrXZbMx7Ttc+w9XgU2FB3lOyQRWET2IFGiU9m1frDVpxZZc6bfHwv4c1YXP6k1Vdy292Fb6UkuvFtXpvWSzA+HycXJHUXiGYIiMg2R6W459gnWGfHNdjIqDWVqemH2ekpObkpOlppWUqZiqr6edqqWQAAIfkECQEAAQAsAAAAACgAKAAAApSMgZnGfaqcg1E2uuzDmmHUBR8Qil95hiPKqWn3aqtLsS18y7G1SzNeowWBENtQd+T1JktP05nzPTdJZlR6vUxNWWjV+vUWhWNkWFwxl9VpZRedYcflIOLafaa28XdsH/ynlcc1uPVDZxQIR0K25+cICCmoqCe5mGhZOfeYSUh5yJcJyrkZWWpaR8doJ2o4NYq62lAAACH5BAkBAAEALAAAAAAoACgAAAKVDI4Yy22ZnINRNqosw0Bv7i1gyHUkFj7oSaWlu3ovC8GxNso5fluz3qLVhBVeT/Lz7ZTHyxL5dDalQWPVOsQWtRnuwXaFTj9jVVh8pma9JjZ4zYSj5ZOyma7uuolffh+IR5aW97cHuBUXKGKXlKjn+DiHWMcYJah4N0lYCMlJOXipGRr5qdgoSTrqWSq6WFl2ypoaUAAAIfkECQEAAQAsAAAAACgAKAAAApaEb6HLgd/iO7FNWtcFWe+ufODGjRfoiJ2akShbueb0wtI50zm02pbvwfWEMWBQ1zKGlLIhskiEPm9R6vRXxV4ZzWT2yHOGpWMyorblKlNp8HmHEb/lCXjcW7bmtXP8Xt229OVWR1fod2eWqNfHuMjXCPkIGNileOiImVmCOEmoSfn3yXlJWmoHGhqp6ilYuWYpmTqKUgAAIfkECQEAAQAsAAAAACgAKAAAApiEH6kb58biQ3FNWtMFWW3eNVcojuFGfqnZqSebuS06w5V80/X02pKe8zFwP6EFWOT1lDFk8rGERh1TTNOocQ61Hm4Xm2VexUHpzjymViHrFbiELsefVrn6XKfnt2Q9G/+Xdie499XHd2g4h7ioOGhXGJboGAnXSBnoBwKYyfioubZJ2Hn0RuRZaflZOil56Zp6iioKSXpUAAAh+QQJAQABACwAAAAAKAAoAAACkoQRqRvnxuI7kU1a1UU5bd5tnSeOZXhmn5lWK3qNTWvRdQxP8qvaC+/yaYQzXO7BMvaUEmJRd3TsiMAgswmNYrSgZdYrTX6tSHGZO73ezuAw2uxuQ+BbeZfMxsexY35+/Qe4J1inV0g4x3WHuMhIl2jXOKT2Q+VU5fgoSUI52VfZyfkJGkha6jmY+aaYdirq+lQAACH5BAkBAAEALAAAAAAoACgAAAKWBIKpYe0L3YNKToqswUlvznigd4wiR4KhZrKt9Upqip61i9E3vMvxRdHlbEFiEXfk9YARYxOZZD6VQ2pUunBmtRXo1Lf8hMVVcNl8JafV38aM2/Fu5V16Bn63r6xt97j09+MXSFi4BniGFae3hzbH9+hYBzkpuUh5aZmHuanZOZgIuvbGiNeomCnaxxap2upaCZsq+1kAACH5BAkBAAEALAAAAAAoACgAAAKXjI8By5zf4kOxTVrXNVlv1X0d8IGZGKLnNpYtm8Lr9cqVeuOSvfOW79D9aDHizNhDJidFZhNydEahOaDH6nomtJjp1tutKoNWkvA6JqfRVLHU/QUfau9l2x7G54d1fl995xcIGAdXqMfBNadoYrhH+Mg2KBlpVpbluCiXmMnZ2Sh4GBqJ+ckIOqqJ6LmKSllZmsoq6wpQAAAh+QQJAQABACwAAAAAKAAoAAAClYx/oLvoxuJDkU1a1YUZbJ59nSd2ZXhWqbRa2/gF8Gu2DY3iqs7yrq+xBYEkYvFSM8aSSObE+ZgRl1BHFZNr7pRCavZ5BW2142hY3AN/zWtsmf12p9XxxFl2lpLn1rseztfXZjdIWIf2s5dItwjYKBgo9yg5pHgzJXTEeGlZuenpyPmpGQoKOWkYmSpaSnqKileI2FAAACH5BAkBAAEALAAAAAAoACgAAAKVjB+gu+jG4kORTVrVhRlsnn2dJ3ZleFaptFrb+CXmO9OozeL5VfP99HvAWhpiUdcwkpBH3825AwYdU8xTqlLGhtCosArKMpvfa1mMRae9VvWZfeB2XfPkeLmm18lUcBj+p5dnN8jXZ3YIGEhYuOUn45aoCDkp16hl5IjYJvjWKcnoGQpqyPlpOhr3aElaqrq56Bq7VAAAOw==");height:100%;filter:alpha(opacity=25);opacity:0.25}.ui-progressbar-indeterminate .ui-progressbar-value{background-image:none}.ui-selectmenu-menu{padding:0;margin:0;position:absolute;top:0;left:0;display:none}.ui-selectmenu-menu .ui-menu{overflow:auto;overflow-x:hidden;padding-bottom:1px}.ui-selectmenu-menu .ui-menu .ui-selectmenu-optgroup{font-size:1em;font-weight:bold;line-height:1.5;padding:2px 0.4em;margin:0.5em 0 0 0;height:auto;border:0}.ui-selectmenu-open{display:block}.ui-selectmenu-text{display:block;margin-right:20px;overflow:hidden;text-overflow:ellipsis}.ui-selectmenu-button.ui-button{text-align:left;white-space:nowrap;width:14em}.ui-selectmenu-icon.ui-icon{float:right;margin-top:0}.ui-slider{position:relative;text-align:left}.ui-slider .ui-slider-handle{position:absolute;z-index:2;width:1.2em;height:1.2em;cursor:default;-ms-touch-action:none;touch-action:none}.ui-slider .ui-slider-range{position:absolute;z-index:1;font-size:.7em;display:block;border:0;background-position:0 0}.ui-slider.ui-state-disabled .ui-slider-handle,.ui-slider.ui-state-disabled .ui-slider-range{filter:inherit}.ui-slider-horizontal{height:.8em}.ui-slider-horizontal .ui-slider-handle{top:-.3em;margin-left:-.6em}.ui-slider-horizontal .ui-slider-range{top:0;height:100%}.ui-slider-horizontal .ui-slider-range-min{left:0}.ui-slider-horizontal .ui-slider-range-max{right:0}.ui-slider-vertical{width:.8em;height:100px}.ui-slider-vertical .ui-slider-handle{left:-.3em;margin-left:0;margin-bottom:-.6em}.ui-slider-vertical .ui-slider-range{left:0;width:100%}.ui-slider-vertical .ui-slider-range-min{bottom:0}.ui-slider-vertical .ui-slider-range-max{top:0}.ui-spinner{position:relative;display:inline-block;overflow:hidden;padding:0;vertical-align:middle}.ui-spinner-input{border:none;background:none;color:inherit;padding:.222em 0;margin:.2em 0;vertical-align:middle;margin-left:.4em;margin-right:2em}.ui-spinner-button{width:1.6em;height:50%;font-size:.5em;padding:0;margin:0;text-align:center;position:absolute;cursor:default;display:block;overflow:hidden;right:0}.ui-spinner a.ui-spinner-button{border-top-style:none;border-bottom-style:none;border-right-style:none}.ui-spinner-up{top:0}.ui-spinner-down{bottom:0}.ui-tabs{position:relative;padding:.2em}.ui-tabs .ui-tabs-nav{margin:0;padding:.2em .2em 0}.ui-tabs .ui-tabs-nav li{list-style:none;float:left;position:relative;top:0;margin:1px .2em 0 0;border-bottom-width:0;padding:0;white-space:nowrap}.ui-tabs .ui-tabs-nav .ui-tabs-anchor{float:left;padding:.5em 1em;text-decoration:none}.ui-tabs .ui-tabs-nav li.ui-tabs-active{margin-bottom:-1px;padding-bottom:1px}.ui-tabs .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor,.ui-tabs .ui-tabs-nav li.ui-state-disabled .ui-tabs-anchor,.ui-tabs .ui-tabs-nav li.ui-tabs-loading .ui-tabs-anchor{cursor:text}.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor{cursor:pointer}.ui-tabs .ui-tabs-panel{display:block;border-width:0;padding:1em 1.4em;background:none}.ui-tooltip{padding:8px;position:absolute;z-index:9999;max-width:300px}body .ui-tooltip{border-width:2px}.ui-widget{font-family:Arial,Helvetica,sans-serif;font-size:1em}.ui-widget .ui-widget{font-size:1em}.ui-widget input,.ui-widget select,.ui-widget textarea,.ui-widget button{font-family:Arial,Helvetica,sans-serif;font-size:1em}.ui-widget.ui-widget-content{border:1px solid #c5c5c5}.ui-widget-content{border:1px solid #ddd;background:#fff;color:#333}.ui-widget-content a{color:#333}.ui-widget-header{border:1px solid #ddd;background:#e9e9e9;color:#333;font-weight:bold}.ui-widget-header a{color:#333}.ui-state-default,.ui-widget-content .ui-state-default,.ui-widget-header .ui-state-default,.ui-button,html .ui-button.ui-state-disabled:hover,html .ui-button.ui-state-disabled:active{border:1px solid #c5c5c5;background:#f6f6f6;font-weight:normal;color:#454545}.ui-state-default a,.ui-state-default a:link,.ui-state-default a:visited,a.ui-button,a:link.ui-button,a:visited.ui-button,.ui-button{color:#454545;text-decoration:none}.ui-state-hover,.ui-widget-content .ui-state-hover,.ui-widget-header .ui-state-hover,.ui-state-focus,.ui-widget-content .ui-state-focus,.ui-widget-header .ui-state-focus,.ui-button:hover,.ui-button:focus{border:1px solid #ccc;background:#ededed;font-weight:normal;color:#2b2b2b}.ui-state-hover a,.ui-state-hover a:hover,.ui-state-hover a:link,.ui-state-hover a:visited,.ui-state-focus a,.ui-state-focus a:hover,.ui-state-focus a:link,.ui-state-focus a:visited,a.ui-button:hover,a.ui-button:focus{color:#2b2b2b;text-decoration:none}.ui-visual-focus{box-shadow:0 0 3px 1px rgb(94,158,214)}.ui-state-active,.ui-widget-content .ui-state-active,.ui-widget-header .ui-state-active,a.ui-button:active,.ui-button:active,.ui-button.ui-state-active:hover{border:1px solid #003eff;background:#007fff;font-weight:normal;color:#fff}.ui-icon-background,.ui-state-active .ui-icon-background{border:#003eff;background-color:#fff}.ui-state-active a,.ui-state-active a:link,.ui-state-active a:visited{color:#fff;text-decoration:none}.ui-state-highlight,.ui-widget-content .ui-state-highlight,.ui-widget-header .ui-state-highlight{border:1px solid #dad55e;background:#fffa90;color:#777620}.ui-state-checked{border:1px solid #dad55e;background:#fffa90}.ui-state-highlight a,.ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a{color:#777620}.ui-state-error,.ui-widget-content .ui-state-error,.ui-widget-header .ui-state-error{border:1px solid #f1a899;background:#fddfdf;color:#5f3f3f}.ui-state-error a,.ui-widget-content .ui-state-error a,.ui-widget-header .ui-state-error a{color:#5f3f3f}.ui-state-error-text,.ui-widget-content .ui-state-error-text,.ui-widget-header .ui-state-error-text{color:#5f3f3f}.ui-priority-primary,.ui-widget-content .ui-priority-primary,.ui-widget-header .ui-priority-primary{font-weight:bold}.ui-priority-secondary,.ui-widget-content .ui-priority-secondary,.ui-widget-header .ui-priority-secondary{opacity:.7;filter:Alpha(Opacity=70);font-weight:normal}.ui-state-disabled,.ui-widget-content .ui-state-disabled,.ui-widget-header .ui-state-disabled{opacity:.35;filter:Alpha(Opacity=35);background-image:none}.ui-state-disabled .ui-icon{filter:Alpha(Opacity=35)}.ui-icon{width:16px;height:16px}.ui-icon,.ui-widget-content .ui-icon{background-image:url("images/ui-icons_444444_256x240.png")}.ui-widget-header .ui-icon{background-image:url("images/ui-icons_444444_256x240.png")}.ui-state-hover .ui-icon,.ui-state-focus .ui-icon,.ui-button:hover .ui-icon,.ui-button:focus .ui-icon{background-image:url("images/ui-icons_555555_256x240.png")}.ui-state-active .ui-icon,.ui-button:active .ui-icon{background-image:url("images/ui-icons_ffffff_256x240.png")}.ui-state-highlight .ui-icon,.ui-button .ui-state-highlight.ui-icon{background-image:url("images/ui-icons_777620_256x240.png")}.ui-state-error .ui-icon,.ui-state-error-text .ui-icon{background-image:url("images/ui-icons_cc0000_256x240.png")}.ui-button .ui-icon{background-image:url("images/ui-icons_777777_256x240.png")}.ui-icon-blank{background-position:16px 16px}.ui-icon-caret-1-n{background-position:0 0}.ui-icon-caret-1-ne{background-position:-16px 0}.ui-icon-caret-1-e{background-position:-32px 0}.ui-icon-caret-1-se{background-position:-48px 0}.ui-icon-caret-1-s{background-position:-65px 0}.ui-icon-caret-1-sw{background-position:-80px 0}.ui-icon-caret-1-w{background-position:-96px 0}.ui-icon-caret-1-nw{background-position:-112px 0}.ui-icon-caret-2-n-s{background-position:-128px 0}.ui-icon-caret-2-e-w{background-position:-144px 0}.ui-icon-triangle-1-n{background-position:0 -16px}.ui-icon-triangle-1-ne{background-position:-16px -16px}.ui-icon-triangle-1-e{background-position:-32px -16px}.ui-icon-triangle-1-se{background-position:-48px -16px}.ui-icon-triangle-1-s{background-position:-65px -16px}.ui-icon-triangle-1-sw{background-position:-80px -16px}.ui-icon-triangle-1-w{background-position:-96px -16px}.ui-icon-triangle-1-nw{background-position:-112px -16px}.ui-icon-triangle-2-n-s{background-position:-128px -16px}.ui-icon-triangle-2-e-w{background-position:-144px -16px}.ui-icon-arrow-1-n{background-position:0 -32px}.ui-icon-arrow-1-ne{background-position:-16px -32px}.ui-icon-arrow-1-e{background-position:-32px -32px}.ui-icon-arrow-1-se{background-position:-48px -32px}.ui-icon-arrow-1-s{background-position:-65px -32px}.ui-icon-arrow-1-sw{background-position:-80px -32px}.ui-icon-arrow-1-w{background-position:-96px -32px}.ui-icon-arrow-1-nw{background-position:-112px -32px}.ui-icon-arrow-2-n-s{background-position:-128px -32px}.ui-icon-arrow-2-ne-sw{background-position:-144px -32px}.ui-icon-arrow-2-e-w{background-position:-160px -32px}.ui-icon-arrow-2-se-nw{background-position:-176px -32px}.ui-icon-arrowstop-1-n{background-position:-192px -32px}.ui-icon-arrowstop-1-e{background-position:-208px -32px}.ui-icon-arrowstop-1-s{background-position:-224px -32px}.ui-icon-arrowstop-1-w{background-position:-240px -32px}.ui-icon-arrowthick-1-n{background-position:1px -48px}.ui-icon-arrowthick-1-ne{background-position:-16px -48px}.ui-icon-arrowthick-1-e{background-position:-32px -48px}.ui-icon-arrowthick-1-se{background-position:-48px -48px}.ui-icon-arrowthick-1-s{background-position:-64px -48px}.ui-icon-arrowthick-1-sw{background-position:-80px -48px}.ui-icon-arrowthick-1-w{background-position:-96px -48px}.ui-icon-arrowthick-1-nw{background-position:-112px -48px}.ui-icon-arrowthick-2-n-s{background-position:-128px -48px}.ui-icon-arrowthick-2-ne-sw{background-position:-144px -48px}.ui-icon-arrowthick-2-e-w{background-position:-160px -48px}.ui-icon-arrowthick-2-se-nw{background-position:-176px -48px}.ui-icon-arrowthickstop-1-n{background-position:-192px -48px}.ui-icon-arrowthickstop-1-e{background-position:-208px -48px}.ui-icon-arrowthickstop-1-s{background-position:-224px -48px}.ui-icon-arrowthickstop-1-w{background-position:-240px -48px}.ui-icon-arrowreturnthick-1-w{background-position:0 -64px}.ui-icon-arrowreturnthick-1-n{background-position:-16px -64px}.ui-icon-arrowreturnthick-1-e{background-position:-32px -64px}.ui-icon-arrowreturnthick-1-s{background-position:-48px -64px}.ui-icon-arrowreturn-1-w{background-position:-64px -64px}.ui-icon-arrowreturn-1-n{background-position:-80px -64px}.ui-icon-arrowreturn-1-e{background-position:-96px -64px}.ui-icon-arrowreturn-1-s{background-position:-112px -64px}.ui-icon-arrowrefresh-1-w{background-position:-128px -64px}.ui-icon-arrowrefresh-1-n{background-position:-144px -64px}.ui-icon-arrowrefresh-1-e{background-position:-160px -64px}.ui-icon-arrowrefresh-1-s{background-position:-176px -64px}.ui-icon-arrow-4{background-position:0 -80px}.ui-icon-arrow-4-diag{background-position:-16px -80px}.ui-icon-extlink{background-position:-32px -80px}.ui-icon-newwin{background-position:-48px -80px}.ui-icon-refresh{background-position:-64px -80px}.ui-icon-shuffle{background-position:-80px -80px}.ui-icon-transfer-e-w{background-position:-96px -80px}.ui-icon-transferthick-e-w{background-position:-112px -80px}.ui-icon-folder-collapsed{background-position:0 -96px}.ui-icon-folder-open{background-position:-16px -96px}.ui-icon-document{background-position:-32px -96px}.ui-icon-document-b{background-position:-48px -96px}.ui-icon-note{background-position:-64px -96px}.ui-icon-mail-closed{background-position:-80px -96px}.ui-icon-mail-open{background-position:-96px -96px}.ui-icon-suitcase{background-position:-112px -96px}.ui-icon-comment{background-position:-128px -96px}.ui-icon-person{background-position:-144px -96px}.ui-icon-print{background-position:-160px -96px}.ui-icon-trash{background-position:-176px -96px}.ui-icon-locked{background-position:-192px -96px}.ui-icon-unlocked{background-position:-208px -96px}.ui-icon-bookmark{background-position:-224px -96px}.ui-icon-tag{background-position:-240px -96px}.ui-icon-home{background-position:0 -112px}.ui-icon-flag{background-position:-16px -112px}.ui-icon-calendar{background-position:-32px -112px}.ui-icon-cart{background-position:-48px -112px}.ui-icon-pencil{background-position:-64px -112px}.ui-icon-clock{background-position:-80px -112px}.ui-icon-disk{background-position:-96px -112px}.ui-icon-calculator{background-position:-112px -112px}.ui-icon-zoomin{background-position:-128px -112px}.ui-icon-zoomout{background-position:-144px -112px}.ui-icon-search{background-position:-160px -112px}.ui-icon-wrench{background-position:-176px -112px}.ui-icon-gear{background-position:-192px -112px}.ui-icon-heart{background-position:-208px -112px}.ui-icon-star{background-position:-224px -112px}.ui-icon-link{background-position:-240px -112px}.ui-icon-cancel{background-position:0 -128px}.ui-icon-plus{background-position:-16px -128px}.ui-icon-plusthick{background-position:-32px -128px}.ui-icon-minus{background-position:-48px -128px}.ui-icon-minusthick{background-position:-64px -128px}.ui-icon-close{background-position:-80px -128px}.ui-icon-closethick{background-position:-96px -128px}.ui-icon-key{background-position:-112px -128px}.ui-icon-lightbulb{background-position:-128px -128px}.ui-icon-scissors{background-position:-144px -128px}.ui-icon-clipboard{background-position:-160px -128px}.ui-icon-copy{background-position:-176px -128px}.ui-icon-contact{background-position:-192px -128px}.ui-icon-image{background-position:-208px -128px}.ui-icon-video{background-position:-224px -128px}.ui-icon-script{background-position:-240px -128px}.ui-icon-alert{background-position:0 -144px}.ui-icon-info{background-position:-16px -144px}.ui-icon-notice{background-position:-32px -144px}.ui-icon-help{background-position:-48px -144px}.ui-icon-check{background-position:-64px -144px}.ui-icon-bullet{background-position:-80px -144px}.ui-icon-radio-on{background-position:-96px -144px}.ui-icon-radio-off{background-position:-112px -144px}.ui-icon-pin-w{background-position:-128px -144px}.ui-icon-pin-s{background-position:-144px -144px}.ui-icon-play{background-position:0 -160px}.ui-icon-pause{background-position:-16px -160px}.ui-icon-seek-next{background-position:-32px -160px}.ui-icon-seek-prev{background-position:-48px -160px}.ui-icon-seek-end{background-position:-64px -160px}.ui-icon-seek-start{background-position:-80px -160px}.ui-icon-seek-first{background-position:-80px -160px}.ui-icon-stop{background-position:-96px -160px}.ui-icon-eject{background-position:-112px -160px}.ui-icon-volume-off{background-position:-128px -160px}.ui-icon-volume-on{background-position:-144px -160px}.ui-icon-power{background-position:0 -176px}.ui-icon-signal-diag{background-position:-16px -176px}.ui-icon-signal{background-position:-32px -176px}.ui-icon-battery-0{background-position:-48px -176px}.ui-icon-battery-1{background-position:-64px -176px}.ui-icon-battery-2{background-position:-80px -176px}.ui-icon-battery-3{background-position:-96px -176px}.ui-icon-circle-plus{background-position:0 -192px}.ui-icon-circle-minus{background-position:-16px -192px}.ui-icon-circle-close{background-position:-32px -192px}.ui-icon-circle-triangle-e{background-position:-48px -192px}.ui-icon-circle-triangle-s{background-position:-64px -192px}.ui-icon-circle-triangle-w{background-position:-80px -192px}.ui-icon-circle-triangle-n{background-position:-96px -192px}.ui-icon-circle-arrow-e{background-position:-112px -192px}.ui-icon-circle-arrow-s{background-position:-128px -192px}.ui-icon-circle-arrow-w{background-position:-144px -192px}.ui-icon-circle-arrow-n{background-position:-160px -192px}.ui-icon-circle-zoomin{background-position:-176px -192px}.ui-icon-circle-zoomout{background-position:-192px -192px}.ui-icon-circle-check{background-position:-208px -192px}.ui-icon-circlesmall-plus{background-position:0 -208px}.ui-icon-circlesmall-minus{background-position:-16px -208px}.ui-icon-circlesmall-close{background-position:-32px -208px}.ui-icon-squaresmall-plus{background-position:-48px -208px}.ui-icon-squaresmall-minus{background-position:-64px -208px}.ui-icon-squaresmall-close{background-position:-80px -208px}.ui-icon-grip-dotted-vertical{background-position:0 -224px}.ui-icon-grip-dotted-horizontal{background-position:-16px -224px}.ui-icon-grip-solid-vertical{background-position:-32px -224px}.ui-icon-grip-solid-horizontal{background-position:-48px -224px}.ui-icon-gripsmall-diagonal-se{background-position:-64px -224px}.ui-icon-grip-diagonal-se{background-position:-80px -224px}.ui-corner-all,.ui-corner-top,.ui-corner-left,.ui-corner-tl{border-top-left-radius:3px}.ui-corner-all,.ui-corner-top,.ui-corner-right,.ui-corner-tr{border-top-right-radius:3px}.ui-corner-all,.ui-corner-bottom,.ui-corner-left,.ui-corner-bl{border-bottom-left-radius:3px}.ui-corner-all,.ui-corner-bottom,.ui-corner-right,.ui-corner-br{border-bottom-right-radius:3px}.ui-widget-overlay{background:#aaa;opacity:.3;filter:Alpha(Opacity=30)}.ui-widget-shadow{-webkit-box-shadow:0 0 5px #666;box-shadow:0 0 5px #666} \ No newline at end of file diff --git a/js/jquery-ui.min.js b/js/jquery-ui.min.js index 16ae3381..678a0ef3 100644 --- a/js/jquery-ui.min.js +++ b/js/jquery-ui.min.js @@ -1,6 +1,13 @@ -/*! jQuery UI - v1.10.4 - 2014-02-19 +/*! jQuery UI - v1.12.1 - 2018-06-17 * http://jqueryui.com -* Includes: jquery.ui.core.js, jquery.ui.widget.js, jquery.ui.mouse.js, jquery.ui.position.js, jquery.ui.draggable.js, jquery.ui.resizable.js, jquery.ui.sortable.js, jquery.ui.button.js, jquery.ui.dialog.js, jquery.ui.tooltip.js, jquery.ui.effect.js, jquery.ui.effect-scale.js, jquery.ui.effect-slide.js -* Copyright 2014 jQuery Foundation and other contributors; Licensed MIT */ +* Includes: widget.js, position.js, data.js, disable-selection.js, focusable.js, form-reset-mixin.js, jquery-1-7.js, keycode.js, labels.js, scroll-parent.js, tabbable.js, unique-id.js, widgets/draggable.js, widgets/droppable.js, widgets/resizable.js, widgets/selectable.js, widgets/sortable.js, widgets/accordion.js, widgets/autocomplete.js, widgets/button.js, widgets/checkboxradio.js, widgets/controlgroup.js, widgets/datepicker.js, widgets/dialog.js, widgets/menu.js, widgets/mouse.js, widgets/progressbar.js, widgets/selectmenu.js, widgets/slider.js, widgets/spinner.js, widgets/tabs.js, widgets/tooltip.js, effect.js, effects/effect-blind.js, effects/effect-bounce.js, effects/effect-clip.js, effects/effect-drop.js, effects/effect-explode.js, effects/effect-fade.js, effects/effect-fold.js, effects/effect-highlight.js, effects/effect-puff.js, effects/effect-pulsate.js, effects/effect-scale.js, effects/effect-shake.js, effects/effect-size.js, effects/effect-slide.js, effects/effect-transfer.js +* Copyright jQuery Foundation and other contributors; Licensed MIT */ -(function(e,t){function i(t,i){var s,a,o,r=t.nodeName.toLowerCase();return"area"===r?(s=t.parentNode,a=s.name,t.href&&a&&"map"===s.nodeName.toLowerCase()?(o=e("img[usemap=#"+a+"]")[0],!!o&&n(o)):!1):(/input|select|textarea|button|object/.test(r)?!t.disabled:"a"===r?t.href||i:i)&&n(t)}function n(t){return e.expr.filters.visible(t)&&!e(t).parents().addBack().filter(function(){return"hidden"===e.css(this,"visibility")}).length}var s=0,a=/^ui-id-\d+$/;e.ui=e.ui||{},e.extend(e.ui,{version:"1.10.4",keyCode:{BACKSPACE:8,COMMA:188,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,LEFT:37,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SPACE:32,TAB:9,UP:38}}),e.fn.extend({focus:function(t){return function(i,n){return"number"==typeof i?this.each(function(){var t=this;setTimeout(function(){e(t).focus(),n&&n.call(t)},i)}):t.apply(this,arguments)}}(e.fn.focus),scrollParent:function(){var t;return t=e.ui.ie&&/(static|relative)/.test(this.css("position"))||/absolute/.test(this.css("position"))?this.parents().filter(function(){return/(relative|absolute|fixed)/.test(e.css(this,"position"))&&/(auto|scroll)/.test(e.css(this,"overflow")+e.css(this,"overflow-y")+e.css(this,"overflow-x"))}).eq(0):this.parents().filter(function(){return/(auto|scroll)/.test(e.css(this,"overflow")+e.css(this,"overflow-y")+e.css(this,"overflow-x"))}).eq(0),/fixed/.test(this.css("position"))||!t.length?e(document):t},zIndex:function(i){if(i!==t)return this.css("zIndex",i);if(this.length)for(var n,s,a=e(this[0]);a.length&&a[0]!==document;){if(n=a.css("position"),("absolute"===n||"relative"===n||"fixed"===n)&&(s=parseInt(a.css("zIndex"),10),!isNaN(s)&&0!==s))return s;a=a.parent()}return 0},uniqueId:function(){return this.each(function(){this.id||(this.id="ui-id-"+ ++s)})},removeUniqueId:function(){return this.each(function(){a.test(this.id)&&e(this).removeAttr("id")})}}),e.extend(e.expr[":"],{data:e.expr.createPseudo?e.expr.createPseudo(function(t){return function(i){return!!e.data(i,t)}}):function(t,i,n){return!!e.data(t,n[3])},focusable:function(t){return i(t,!isNaN(e.attr(t,"tabindex")))},tabbable:function(t){var n=e.attr(t,"tabindex"),s=isNaN(n);return(s||n>=0)&&i(t,!s)}}),e("").outerWidth(1).jquery||e.each(["Width","Height"],function(i,n){function s(t,i,n,s){return e.each(a,function(){i-=parseFloat(e.css(t,"padding"+this))||0,n&&(i-=parseFloat(e.css(t,"border"+this+"Width"))||0),s&&(i-=parseFloat(e.css(t,"margin"+this))||0)}),i}var a="Width"===n?["Left","Right"]:["Top","Bottom"],o=n.toLowerCase(),r={innerWidth:e.fn.innerWidth,innerHeight:e.fn.innerHeight,outerWidth:e.fn.outerWidth,outerHeight:e.fn.outerHeight};e.fn["inner"+n]=function(i){return i===t?r["inner"+n].call(this):this.each(function(){e(this).css(o,s(this,i)+"px")})},e.fn["outer"+n]=function(t,i){return"number"!=typeof t?r["outer"+n].call(this,t):this.each(function(){e(this).css(o,s(this,t,!0,i)+"px")})}}),e.fn.addBack||(e.fn.addBack=function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}),e("").data("a-b","a").removeData("a-b").data("a-b")&&(e.fn.removeData=function(t){return function(i){return arguments.length?t.call(this,e.camelCase(i)):t.call(this)}}(e.fn.removeData)),e.ui.ie=!!/msie [\w.]+/.exec(navigator.userAgent.toLowerCase()),e.support.selectstart="onselectstart"in document.createElement("div"),e.fn.extend({disableSelection:function(){return this.bind((e.support.selectstart?"selectstart":"mousedown")+".ui-disableSelection",function(e){e.preventDefault()})},enableSelection:function(){return this.unbind(".ui-disableSelection")}}),e.extend(e.ui,{plugin:{add:function(t,i,n){var s,a=e.ui[t].prototype;for(s in n)a.plugins[s]=a.plugins[s]||[],a.plugins[s].push([i,n[s]])},call:function(e,t,i){var n,s=e.plugins[t];if(s&&e.element[0].parentNode&&11!==e.element[0].parentNode.nodeType)for(n=0;s.length>n;n++)e.options[s[n][0]]&&s[n][1].apply(e.element,i)}},hasScroll:function(t,i){if("hidden"===e(t).css("overflow"))return!1;var n=i&&"left"===i?"scrollLeft":"scrollTop",s=!1;return t[n]>0?!0:(t[n]=1,s=t[n]>0,t[n]=0,s)}})})(jQuery);(function(t,e){var i=0,s=Array.prototype.slice,n=t.cleanData;t.cleanData=function(e){for(var i,s=0;null!=(i=e[s]);s++)try{t(i).triggerHandler("remove")}catch(o){}n(e)},t.widget=function(i,s,n){var o,a,r,h,l={},c=i.split(".")[0];i=i.split(".")[1],o=c+"-"+i,n||(n=s,s=t.Widget),t.expr[":"][o.toLowerCase()]=function(e){return!!t.data(e,o)},t[c]=t[c]||{},a=t[c][i],r=t[c][i]=function(t,i){return this._createWidget?(arguments.length&&this._createWidget(t,i),e):new r(t,i)},t.extend(r,a,{version:n.version,_proto:t.extend({},n),_childConstructors:[]}),h=new s,h.options=t.widget.extend({},h.options),t.each(n,function(i,n){return t.isFunction(n)?(l[i]=function(){var t=function(){return s.prototype[i].apply(this,arguments)},e=function(t){return s.prototype[i].apply(this,t)};return function(){var i,s=this._super,o=this._superApply;return this._super=t,this._superApply=e,i=n.apply(this,arguments),this._super=s,this._superApply=o,i}}(),e):(l[i]=n,e)}),r.prototype=t.widget.extend(h,{widgetEventPrefix:a?h.widgetEventPrefix||i:i},l,{constructor:r,namespace:c,widgetName:i,widgetFullName:o}),a?(t.each(a._childConstructors,function(e,i){var s=i.prototype;t.widget(s.namespace+"."+s.widgetName,r,i._proto)}),delete a._childConstructors):s._childConstructors.push(r),t.widget.bridge(i,r)},t.widget.extend=function(i){for(var n,o,a=s.call(arguments,1),r=0,h=a.length;h>r;r++)for(n in a[r])o=a[r][n],a[r].hasOwnProperty(n)&&o!==e&&(i[n]=t.isPlainObject(o)?t.isPlainObject(i[n])?t.widget.extend({},i[n],o):t.widget.extend({},o):o);return i},t.widget.bridge=function(i,n){var o=n.prototype.widgetFullName||i;t.fn[i]=function(a){var r="string"==typeof a,h=s.call(arguments,1),l=this;return a=!r&&h.length?t.widget.extend.apply(null,[a].concat(h)):a,r?this.each(function(){var s,n=t.data(this,o);return n?t.isFunction(n[a])&&"_"!==a.charAt(0)?(s=n[a].apply(n,h),s!==n&&s!==e?(l=s&&s.jquery?l.pushStack(s.get()):s,!1):e):t.error("no such method '"+a+"' for "+i+" widget instance"):t.error("cannot call methods on "+i+" prior to initialization; "+"attempted to call method '"+a+"'")}):this.each(function(){var e=t.data(this,o);e?e.option(a||{})._init():t.data(this,o,new n(a,this))}),l}},t.Widget=function(){},t.Widget._childConstructors=[],t.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",defaultElement:"
    ",options:{disabled:!1,create:null},_createWidget:function(e,s){s=t(s||this.defaultElement||this)[0],this.element=t(s),this.uuid=i++,this.eventNamespace="."+this.widgetName+this.uuid,this.options=t.widget.extend({},this.options,this._getCreateOptions(),e),this.bindings=t(),this.hoverable=t(),this.focusable=t(),s!==this&&(t.data(s,this.widgetFullName,this),this._on(!0,this.element,{remove:function(t){t.target===s&&this.destroy()}}),this.document=t(s.style?s.ownerDocument:s.document||s),this.window=t(this.document[0].defaultView||this.document[0].parentWindow)),this._create(),this._trigger("create",null,this._getCreateEventData()),this._init()},_getCreateOptions:t.noop,_getCreateEventData:t.noop,_create:t.noop,_init:t.noop,destroy:function(){this._destroy(),this.element.unbind(this.eventNamespace).removeData(this.widgetName).removeData(this.widgetFullName).removeData(t.camelCase(this.widgetFullName)),this.widget().unbind(this.eventNamespace).removeAttr("aria-disabled").removeClass(this.widgetFullName+"-disabled "+"ui-state-disabled"),this.bindings.unbind(this.eventNamespace),this.hoverable.removeClass("ui-state-hover"),this.focusable.removeClass("ui-state-focus")},_destroy:t.noop,widget:function(){return this.element},option:function(i,s){var n,o,a,r=i;if(0===arguments.length)return t.widget.extend({},this.options);if("string"==typeof i)if(r={},n=i.split("."),i=n.shift(),n.length){for(o=r[i]=t.widget.extend({},this.options[i]),a=0;n.length-1>a;a++)o[n[a]]=o[n[a]]||{},o=o[n[a]];if(i=n.pop(),1===arguments.length)return o[i]===e?null:o[i];o[i]=s}else{if(1===arguments.length)return this.options[i]===e?null:this.options[i];r[i]=s}return this._setOptions(r),this},_setOptions:function(t){var e;for(e in t)this._setOption(e,t[e]);return this},_setOption:function(t,e){return this.options[t]=e,"disabled"===t&&(this.widget().toggleClass(this.widgetFullName+"-disabled ui-state-disabled",!!e).attr("aria-disabled",e),this.hoverable.removeClass("ui-state-hover"),this.focusable.removeClass("ui-state-focus")),this},enable:function(){return this._setOption("disabled",!1)},disable:function(){return this._setOption("disabled",!0)},_on:function(i,s,n){var o,a=this;"boolean"!=typeof i&&(n=s,s=i,i=!1),n?(s=o=t(s),this.bindings=this.bindings.add(s)):(n=s,s=this.element,o=this.widget()),t.each(n,function(n,r){function h(){return i||a.options.disabled!==!0&&!t(this).hasClass("ui-state-disabled")?("string"==typeof r?a[r]:r).apply(a,arguments):e}"string"!=typeof r&&(h.guid=r.guid=r.guid||h.guid||t.guid++);var l=n.match(/^(\w+)\s*(.*)$/),c=l[1]+a.eventNamespace,u=l[2];u?o.delegate(u,c,h):s.bind(c,h)})},_off:function(t,e){e=(e||"").split(" ").join(this.eventNamespace+" ")+this.eventNamespace,t.unbind(e).undelegate(e)},_delay:function(t,e){function i(){return("string"==typeof t?s[t]:t).apply(s,arguments)}var s=this;return setTimeout(i,e||0)},_hoverable:function(e){this.hoverable=this.hoverable.add(e),this._on(e,{mouseenter:function(e){t(e.currentTarget).addClass("ui-state-hover")},mouseleave:function(e){t(e.currentTarget).removeClass("ui-state-hover")}})},_focusable:function(e){this.focusable=this.focusable.add(e),this._on(e,{focusin:function(e){t(e.currentTarget).addClass("ui-state-focus")},focusout:function(e){t(e.currentTarget).removeClass("ui-state-focus")}})},_trigger:function(e,i,s){var n,o,a=this.options[e];if(s=s||{},i=t.Event(i),i.type=(e===this.widgetEventPrefix?e:this.widgetEventPrefix+e).toLowerCase(),i.target=this.element[0],o=i.originalEvent)for(n in o)n in i||(i[n]=o[n]);return this.element.trigger(i,s),!(t.isFunction(a)&&a.apply(this.element[0],[i].concat(s))===!1||i.isDefaultPrevented())}},t.each({show:"fadeIn",hide:"fadeOut"},function(e,i){t.Widget.prototype["_"+e]=function(s,n,o){"string"==typeof n&&(n={effect:n});var a,r=n?n===!0||"number"==typeof n?i:n.effect||i:e;n=n||{},"number"==typeof n&&(n={duration:n}),a=!t.isEmptyObject(n),n.complete=o,n.delay&&s.delay(n.delay),a&&t.effects&&t.effects.effect[r]?s[e](n):r!==e&&s[r]?s[r](n.duration,n.easing,o):s.queue(function(i){t(this)[e](),o&&o.call(s[0]),i()})}})})(jQuery);(function(t){var e=!1;t(document).mouseup(function(){e=!1}),t.widget("ui.mouse",{version:"1.10.4",options:{cancel:"input,textarea,button,select,option",distance:1,delay:0},_mouseInit:function(){var e=this;this.element.bind("mousedown."+this.widgetName,function(t){return e._mouseDown(t)}).bind("click."+this.widgetName,function(i){return!0===t.data(i.target,e.widgetName+".preventClickEvent")?(t.removeData(i.target,e.widgetName+".preventClickEvent"),i.stopImmediatePropagation(),!1):undefined}),this.started=!1},_mouseDestroy:function(){this.element.unbind("."+this.widgetName),this._mouseMoveDelegate&&t(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate)},_mouseDown:function(i){if(!e){this._mouseStarted&&this._mouseUp(i),this._mouseDownEvent=i;var s=this,n=1===i.which,a="string"==typeof this.options.cancel&&i.target.nodeName?t(i.target).closest(this.options.cancel).length:!1;return n&&!a&&this._mouseCapture(i)?(this.mouseDelayMet=!this.options.delay,this.mouseDelayMet||(this._mouseDelayTimer=setTimeout(function(){s.mouseDelayMet=!0},this.options.delay)),this._mouseDistanceMet(i)&&this._mouseDelayMet(i)&&(this._mouseStarted=this._mouseStart(i)!==!1,!this._mouseStarted)?(i.preventDefault(),!0):(!0===t.data(i.target,this.widgetName+".preventClickEvent")&&t.removeData(i.target,this.widgetName+".preventClickEvent"),this._mouseMoveDelegate=function(t){return s._mouseMove(t)},this._mouseUpDelegate=function(t){return s._mouseUp(t)},t(document).bind("mousemove."+this.widgetName,this._mouseMoveDelegate).bind("mouseup."+this.widgetName,this._mouseUpDelegate),i.preventDefault(),e=!0,!0)):!0}},_mouseMove:function(e){return t.ui.ie&&(!document.documentMode||9>document.documentMode)&&!e.button?this._mouseUp(e):this._mouseStarted?(this._mouseDrag(e),e.preventDefault()):(this._mouseDistanceMet(e)&&this._mouseDelayMet(e)&&(this._mouseStarted=this._mouseStart(this._mouseDownEvent,e)!==!1,this._mouseStarted?this._mouseDrag(e):this._mouseUp(e)),!this._mouseStarted)},_mouseUp:function(e){return t(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate),this._mouseStarted&&(this._mouseStarted=!1,e.target===this._mouseDownEvent.target&&t.data(e.target,this.widgetName+".preventClickEvent",!0),this._mouseStop(e)),!1},_mouseDistanceMet:function(t){return Math.max(Math.abs(this._mouseDownEvent.pageX-t.pageX),Math.abs(this._mouseDownEvent.pageY-t.pageY))>=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return!0}})})(jQuery);(function(t,e){function i(t,e,i){return[parseFloat(t[0])*(p.test(t[0])?e/100:1),parseFloat(t[1])*(p.test(t[1])?i/100:1)]}function s(e,i){return parseInt(t.css(e,i),10)||0}function n(e){var i=e[0];return 9===i.nodeType?{width:e.width(),height:e.height(),offset:{top:0,left:0}}:t.isWindow(i)?{width:e.width(),height:e.height(),offset:{top:e.scrollTop(),left:e.scrollLeft()}}:i.preventDefault?{width:0,height:0,offset:{top:i.pageY,left:i.pageX}}:{width:e.outerWidth(),height:e.outerHeight(),offset:e.offset()}}t.ui=t.ui||{};var a,o=Math.max,r=Math.abs,l=Math.round,h=/left|center|right/,c=/top|center|bottom/,u=/[\+\-]\d+(\.[\d]+)?%?/,d=/^\w+/,p=/%$/,f=t.fn.position;t.position={scrollbarWidth:function(){if(a!==e)return a;var i,s,n=t("
    "),o=n.children()[0];return t("body").append(n),i=o.offsetWidth,n.css("overflow","scroll"),s=o.offsetWidth,i===s&&(s=n[0].clientWidth),n.remove(),a=i-s},getScrollInfo:function(e){var i=e.isWindow||e.isDocument?"":e.element.css("overflow-x"),s=e.isWindow||e.isDocument?"":e.element.css("overflow-y"),n="scroll"===i||"auto"===i&&e.widths?"left":i>0?"right":"center",vertical:0>a?"top":n>0?"bottom":"middle"};u>p&&p>r(i+s)&&(l.horizontal="center"),d>g&&g>r(n+a)&&(l.vertical="middle"),l.important=o(r(i),r(s))>o(r(n),r(a))?"horizontal":"vertical",e.using.call(this,t,l)}),c.offset(t.extend(M,{using:h}))})},t.ui.position={fit:{left:function(t,e){var i,s=e.within,n=s.isWindow?s.scrollLeft:s.offset.left,a=s.width,r=t.left-e.collisionPosition.marginLeft,l=n-r,h=r+e.collisionWidth-a-n;e.collisionWidth>a?l>0&&0>=h?(i=t.left+l+e.collisionWidth-a-n,t.left+=l-i):t.left=h>0&&0>=l?n:l>h?n+a-e.collisionWidth:n:l>0?t.left+=l:h>0?t.left-=h:t.left=o(t.left-r,t.left)},top:function(t,e){var i,s=e.within,n=s.isWindow?s.scrollTop:s.offset.top,a=e.within.height,r=t.top-e.collisionPosition.marginTop,l=n-r,h=r+e.collisionHeight-a-n;e.collisionHeight>a?l>0&&0>=h?(i=t.top+l+e.collisionHeight-a-n,t.top+=l-i):t.top=h>0&&0>=l?n:l>h?n+a-e.collisionHeight:n:l>0?t.top+=l:h>0?t.top-=h:t.top=o(t.top-r,t.top)}},flip:{left:function(t,e){var i,s,n=e.within,a=n.offset.left+n.scrollLeft,o=n.width,l=n.isWindow?n.scrollLeft:n.offset.left,h=t.left-e.collisionPosition.marginLeft,c=h-l,u=h+e.collisionWidth-o-l,d="left"===e.my[0]?-e.elemWidth:"right"===e.my[0]?e.elemWidth:0,p="left"===e.at[0]?e.targetWidth:"right"===e.at[0]?-e.targetWidth:0,f=-2*e.offset[0];0>c?(i=t.left+d+p+f+e.collisionWidth-o-a,(0>i||r(c)>i)&&(t.left+=d+p+f)):u>0&&(s=t.left-e.collisionPosition.marginLeft+d+p+f-l,(s>0||u>r(s))&&(t.left+=d+p+f))},top:function(t,e){var i,s,n=e.within,a=n.offset.top+n.scrollTop,o=n.height,l=n.isWindow?n.scrollTop:n.offset.top,h=t.top-e.collisionPosition.marginTop,c=h-l,u=h+e.collisionHeight-o-l,d="top"===e.my[1],p=d?-e.elemHeight:"bottom"===e.my[1]?e.elemHeight:0,f="top"===e.at[1]?e.targetHeight:"bottom"===e.at[1]?-e.targetHeight:0,g=-2*e.offset[1];0>c?(s=t.top+p+f+g+e.collisionHeight-o-a,t.top+p+f+g>c&&(0>s||r(c)>s)&&(t.top+=p+f+g)):u>0&&(i=t.top-e.collisionPosition.marginTop+p+f+g-l,t.top+p+f+g>u&&(i>0||u>r(i))&&(t.top+=p+f+g))}},flipfit:{left:function(){t.ui.position.flip.left.apply(this,arguments),t.ui.position.fit.left.apply(this,arguments)},top:function(){t.ui.position.flip.top.apply(this,arguments),t.ui.position.fit.top.apply(this,arguments)}}},function(){var e,i,s,n,a,o=document.getElementsByTagName("body")[0],r=document.createElement("div");e=document.createElement(o?"div":"body"),s={visibility:"hidden",width:0,height:0,border:0,margin:0,background:"none"},o&&t.extend(s,{position:"absolute",left:"-1000px",top:"-1000px"});for(a in s)e.style[a]=s[a];e.appendChild(r),i=o||document.documentElement,i.insertBefore(e,i.firstChild),r.style.cssText="position: absolute; left: 10.7432222px;",n=t(r).offset().left,t.support.offsetFractions=n>10&&11>n,e.innerHTML="",i.removeChild(e)}()})(jQuery);(function(t){t.widget("ui.draggable",t.ui.mouse,{version:"1.10.4",widgetEventPrefix:"drag",options:{addClasses:!0,appendTo:"parent",axis:!1,connectToSortable:!1,containment:!1,cursor:"auto",cursorAt:!1,grid:!1,handle:!1,helper:"original",iframeFix:!1,opacity:!1,refreshPositions:!1,revert:!1,revertDuration:500,scope:"default",scroll:!0,scrollSensitivity:20,scrollSpeed:20,snap:!1,snapMode:"both",snapTolerance:20,stack:!1,zIndex:!1,drag:null,start:null,stop:null},_create:function(){"original"!==this.options.helper||/^(?:r|a|f)/.test(this.element.css("position"))||(this.element[0].style.position="relative"),this.options.addClasses&&this.element.addClass("ui-draggable"),this.options.disabled&&this.element.addClass("ui-draggable-disabled"),this._mouseInit()},_destroy:function(){this.element.removeClass("ui-draggable ui-draggable-dragging ui-draggable-disabled"),this._mouseDestroy()},_mouseCapture:function(e){var i=this.options;return this.helper||i.disabled||t(e.target).closest(".ui-resizable-handle").length>0?!1:(this.handle=this._getHandle(e),this.handle?(t(i.iframeFix===!0?"iframe":i.iframeFix).each(function(){t("
    ").css({width:this.offsetWidth+"px",height:this.offsetHeight+"px",position:"absolute",opacity:"0.001",zIndex:1e3}).css(t(this).offset()).appendTo("body")}),!0):!1)},_mouseStart:function(e){var i=this.options;return this.helper=this._createHelper(e),this.helper.addClass("ui-draggable-dragging"),this._cacheHelperProportions(),t.ui.ddmanager&&(t.ui.ddmanager.current=this),this._cacheMargins(),this.cssPosition=this.helper.css("position"),this.scrollParent=this.helper.scrollParent(),this.offsetParent=this.helper.offsetParent(),this.offsetParentCssPosition=this.offsetParent.css("position"),this.offset=this.positionAbs=this.element.offset(),this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left},this.offset.scroll=!1,t.extend(this.offset,{click:{left:e.pageX-this.offset.left,top:e.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()}),this.originalPosition=this.position=this._generatePosition(e),this.originalPageX=e.pageX,this.originalPageY=e.pageY,i.cursorAt&&this._adjustOffsetFromHelper(i.cursorAt),this._setContainment(),this._trigger("start",e)===!1?(this._clear(),!1):(this._cacheHelperProportions(),t.ui.ddmanager&&!i.dropBehaviour&&t.ui.ddmanager.prepareOffsets(this,e),this._mouseDrag(e,!0),t.ui.ddmanager&&t.ui.ddmanager.dragStart(this,e),!0)},_mouseDrag:function(e,i){if("fixed"===this.offsetParentCssPosition&&(this.offset.parent=this._getParentOffset()),this.position=this._generatePosition(e),this.positionAbs=this._convertPositionTo("absolute"),!i){var s=this._uiHash();if(this._trigger("drag",e,s)===!1)return this._mouseUp({}),!1;this.position=s.position}return this.options.axis&&"y"===this.options.axis||(this.helper[0].style.left=this.position.left+"px"),this.options.axis&&"x"===this.options.axis||(this.helper[0].style.top=this.position.top+"px"),t.ui.ddmanager&&t.ui.ddmanager.drag(this,e),!1},_mouseStop:function(e){var i=this,s=!1;return t.ui.ddmanager&&!this.options.dropBehaviour&&(s=t.ui.ddmanager.drop(this,e)),this.dropped&&(s=this.dropped,this.dropped=!1),"original"!==this.options.helper||t.contains(this.element[0].ownerDocument,this.element[0])?("invalid"===this.options.revert&&!s||"valid"===this.options.revert&&s||this.options.revert===!0||t.isFunction(this.options.revert)&&this.options.revert.call(this.element,s)?t(this.helper).animate(this.originalPosition,parseInt(this.options.revertDuration,10),function(){i._trigger("stop",e)!==!1&&i._clear()}):this._trigger("stop",e)!==!1&&this._clear(),!1):!1},_mouseUp:function(e){return t("div.ui-draggable-iframeFix").each(function(){this.parentNode.removeChild(this)}),t.ui.ddmanager&&t.ui.ddmanager.dragStop(this,e),t.ui.mouse.prototype._mouseUp.call(this,e)},cancel:function(){return this.helper.is(".ui-draggable-dragging")?this._mouseUp({}):this._clear(),this},_getHandle:function(e){return this.options.handle?!!t(e.target).closest(this.element.find(this.options.handle)).length:!0},_createHelper:function(e){var i=this.options,s=t.isFunction(i.helper)?t(i.helper.apply(this.element[0],[e])):"clone"===i.helper?this.element.clone().removeAttr("id"):this.element;return s.parents("body").length||s.appendTo("parent"===i.appendTo?this.element[0].parentNode:i.appendTo),s[0]===this.element[0]||/(fixed|absolute)/.test(s.css("position"))||s.css("position","absolute"),s},_adjustOffsetFromHelper:function(e){"string"==typeof e&&(e=e.split(" ")),t.isArray(e)&&(e={left:+e[0],top:+e[1]||0}),"left"in e&&(this.offset.click.left=e.left+this.margins.left),"right"in e&&(this.offset.click.left=this.helperProportions.width-e.right+this.margins.left),"top"in e&&(this.offset.click.top=e.top+this.margins.top),"bottom"in e&&(this.offset.click.top=this.helperProportions.height-e.bottom+this.margins.top)},_getParentOffset:function(){var e=this.offsetParent.offset();return"absolute"===this.cssPosition&&this.scrollParent[0]!==document&&t.contains(this.scrollParent[0],this.offsetParent[0])&&(e.left+=this.scrollParent.scrollLeft(),e.top+=this.scrollParent.scrollTop()),(this.offsetParent[0]===document.body||this.offsetParent[0].tagName&&"html"===this.offsetParent[0].tagName.toLowerCase()&&t.ui.ie)&&(e={top:0,left:0}),{top:e.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:e.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if("relative"===this.cssPosition){var t=this.element.position();return{top:t.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:t.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.element.css("marginLeft"),10)||0,top:parseInt(this.element.css("marginTop"),10)||0,right:parseInt(this.element.css("marginRight"),10)||0,bottom:parseInt(this.element.css("marginBottom"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var e,i,s,n=this.options;return n.containment?"window"===n.containment?(this.containment=[t(window).scrollLeft()-this.offset.relative.left-this.offset.parent.left,t(window).scrollTop()-this.offset.relative.top-this.offset.parent.top,t(window).scrollLeft()+t(window).width()-this.helperProportions.width-this.margins.left,t(window).scrollTop()+(t(window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top],undefined):"document"===n.containment?(this.containment=[0,0,t(document).width()-this.helperProportions.width-this.margins.left,(t(document).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top],undefined):n.containment.constructor===Array?(this.containment=n.containment,undefined):("parent"===n.containment&&(n.containment=this.helper[0].parentNode),i=t(n.containment),s=i[0],s&&(e="hidden"!==i.css("overflow"),this.containment=[(parseInt(i.css("borderLeftWidth"),10)||0)+(parseInt(i.css("paddingLeft"),10)||0),(parseInt(i.css("borderTopWidth"),10)||0)+(parseInt(i.css("paddingTop"),10)||0),(e?Math.max(s.scrollWidth,s.offsetWidth):s.offsetWidth)-(parseInt(i.css("borderRightWidth"),10)||0)-(parseInt(i.css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left-this.margins.right,(e?Math.max(s.scrollHeight,s.offsetHeight):s.offsetHeight)-(parseInt(i.css("borderBottomWidth"),10)||0)-(parseInt(i.css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top-this.margins.bottom],this.relative_container=i),undefined):(this.containment=null,undefined)},_convertPositionTo:function(e,i){i||(i=this.position);var s="absolute"===e?1:-1,n="absolute"!==this.cssPosition||this.scrollParent[0]!==document&&t.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent;return this.offset.scroll||(this.offset.scroll={top:n.scrollTop(),left:n.scrollLeft()}),{top:i.top+this.offset.relative.top*s+this.offset.parent.top*s-("fixed"===this.cssPosition?-this.scrollParent.scrollTop():this.offset.scroll.top)*s,left:i.left+this.offset.relative.left*s+this.offset.parent.left*s-("fixed"===this.cssPosition?-this.scrollParent.scrollLeft():this.offset.scroll.left)*s}},_generatePosition:function(e){var i,s,n,a,o=this.options,r="absolute"!==this.cssPosition||this.scrollParent[0]!==document&&t.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent,l=e.pageX,h=e.pageY;return this.offset.scroll||(this.offset.scroll={top:r.scrollTop(),left:r.scrollLeft()}),this.originalPosition&&(this.containment&&(this.relative_container?(s=this.relative_container.offset(),i=[this.containment[0]+s.left,this.containment[1]+s.top,this.containment[2]+s.left,this.containment[3]+s.top]):i=this.containment,e.pageX-this.offset.click.lefti[2]&&(l=i[2]+this.offset.click.left),e.pageY-this.offset.click.top>i[3]&&(h=i[3]+this.offset.click.top)),o.grid&&(n=o.grid[1]?this.originalPageY+Math.round((h-this.originalPageY)/o.grid[1])*o.grid[1]:this.originalPageY,h=i?n-this.offset.click.top>=i[1]||n-this.offset.click.top>i[3]?n:n-this.offset.click.top>=i[1]?n-o.grid[1]:n+o.grid[1]:n,a=o.grid[0]?this.originalPageX+Math.round((l-this.originalPageX)/o.grid[0])*o.grid[0]:this.originalPageX,l=i?a-this.offset.click.left>=i[0]||a-this.offset.click.left>i[2]?a:a-this.offset.click.left>=i[0]?a-o.grid[0]:a+o.grid[0]:a)),{top:h-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+("fixed"===this.cssPosition?-this.scrollParent.scrollTop():this.offset.scroll.top),left:l-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+("fixed"===this.cssPosition?-this.scrollParent.scrollLeft():this.offset.scroll.left)}},_clear:function(){this.helper.removeClass("ui-draggable-dragging"),this.helper[0]===this.element[0]||this.cancelHelperRemoval||this.helper.remove(),this.helper=null,this.cancelHelperRemoval=!1},_trigger:function(e,i,s){return s=s||this._uiHash(),t.ui.plugin.call(this,e,[i,s]),"drag"===e&&(this.positionAbs=this._convertPositionTo("absolute")),t.Widget.prototype._trigger.call(this,e,i,s)},plugins:{},_uiHash:function(){return{helper:this.helper,position:this.position,originalPosition:this.originalPosition,offset:this.positionAbs}}}),t.ui.plugin.add("draggable","connectToSortable",{start:function(e,i){var s=t(this).data("ui-draggable"),n=s.options,a=t.extend({},i,{item:s.element});s.sortables=[],t(n.connectToSortable).each(function(){var i=t.data(this,"ui-sortable");i&&!i.options.disabled&&(s.sortables.push({instance:i,shouldRevert:i.options.revert}),i.refreshPositions(),i._trigger("activate",e,a))})},stop:function(e,i){var s=t(this).data("ui-draggable"),n=t.extend({},i,{item:s.element});t.each(s.sortables,function(){this.instance.isOver?(this.instance.isOver=0,s.cancelHelperRemoval=!0,this.instance.cancelHelperRemoval=!1,this.shouldRevert&&(this.instance.options.revert=this.shouldRevert),this.instance._mouseStop(e),this.instance.options.helper=this.instance.options._helper,"original"===s.options.helper&&this.instance.currentItem.css({top:"auto",left:"auto"})):(this.instance.cancelHelperRemoval=!1,this.instance._trigger("deactivate",e,n))})},drag:function(e,i){var s=t(this).data("ui-draggable"),n=this;t.each(s.sortables,function(){var a=!1,o=this;this.instance.positionAbs=s.positionAbs,this.instance.helperProportions=s.helperProportions,this.instance.offset.click=s.offset.click,this.instance._intersectsWith(this.instance.containerCache)&&(a=!0,t.each(s.sortables,function(){return this.instance.positionAbs=s.positionAbs,this.instance.helperProportions=s.helperProportions,this.instance.offset.click=s.offset.click,this!==o&&this.instance._intersectsWith(this.instance.containerCache)&&t.contains(o.instance.element[0],this.instance.element[0])&&(a=!1),a})),a?(this.instance.isOver||(this.instance.isOver=1,this.instance.currentItem=t(n).clone().removeAttr("id").appendTo(this.instance.element).data("ui-sortable-item",!0),this.instance.options._helper=this.instance.options.helper,this.instance.options.helper=function(){return i.helper[0]},e.target=this.instance.currentItem[0],this.instance._mouseCapture(e,!0),this.instance._mouseStart(e,!0,!0),this.instance.offset.click.top=s.offset.click.top,this.instance.offset.click.left=s.offset.click.left,this.instance.offset.parent.left-=s.offset.parent.left-this.instance.offset.parent.left,this.instance.offset.parent.top-=s.offset.parent.top-this.instance.offset.parent.top,s._trigger("toSortable",e),s.dropped=this.instance.element,s.currentItem=s.element,this.instance.fromOutside=s),this.instance.currentItem&&this.instance._mouseDrag(e)):this.instance.isOver&&(this.instance.isOver=0,this.instance.cancelHelperRemoval=!0,this.instance.options.revert=!1,this.instance._trigger("out",e,this.instance._uiHash(this.instance)),this.instance._mouseStop(e,!0),this.instance.options.helper=this.instance.options._helper,this.instance.currentItem.remove(),this.instance.placeholder&&this.instance.placeholder.remove(),s._trigger("fromSortable",e),s.dropped=!1)})}}),t.ui.plugin.add("draggable","cursor",{start:function(){var e=t("body"),i=t(this).data("ui-draggable").options;e.css("cursor")&&(i._cursor=e.css("cursor")),e.css("cursor",i.cursor)},stop:function(){var e=t(this).data("ui-draggable").options;e._cursor&&t("body").css("cursor",e._cursor)}}),t.ui.plugin.add("draggable","opacity",{start:function(e,i){var s=t(i.helper),n=t(this).data("ui-draggable").options;s.css("opacity")&&(n._opacity=s.css("opacity")),s.css("opacity",n.opacity)},stop:function(e,i){var s=t(this).data("ui-draggable").options;s._opacity&&t(i.helper).css("opacity",s._opacity)}}),t.ui.plugin.add("draggable","scroll",{start:function(){var e=t(this).data("ui-draggable");e.scrollParent[0]!==document&&"HTML"!==e.scrollParent[0].tagName&&(e.overflowOffset=e.scrollParent.offset())},drag:function(e){var i=t(this).data("ui-draggable"),s=i.options,n=!1;i.scrollParent[0]!==document&&"HTML"!==i.scrollParent[0].tagName?(s.axis&&"x"===s.axis||(i.overflowOffset.top+i.scrollParent[0].offsetHeight-e.pageY=0;u--)r=p.snapElements[u].left,l=r+p.snapElements[u].width,h=p.snapElements[u].top,c=h+p.snapElements[u].height,r-f>_||m>l+f||h-f>b||v>c+f||!t.contains(p.snapElements[u].item.ownerDocument,p.snapElements[u].item)?(p.snapElements[u].snapping&&p.options.snap.release&&p.options.snap.release.call(p.element,e,t.extend(p._uiHash(),{snapItem:p.snapElements[u].item})),p.snapElements[u].snapping=!1):("inner"!==g.snapMode&&(s=f>=Math.abs(h-b),n=f>=Math.abs(c-v),a=f>=Math.abs(r-_),o=f>=Math.abs(l-m),s&&(i.position.top=p._convertPositionTo("relative",{top:h-p.helperProportions.height,left:0}).top-p.margins.top),n&&(i.position.top=p._convertPositionTo("relative",{top:c,left:0}).top-p.margins.top),a&&(i.position.left=p._convertPositionTo("relative",{top:0,left:r-p.helperProportions.width}).left-p.margins.left),o&&(i.position.left=p._convertPositionTo("relative",{top:0,left:l}).left-p.margins.left)),d=s||n||a||o,"outer"!==g.snapMode&&(s=f>=Math.abs(h-v),n=f>=Math.abs(c-b),a=f>=Math.abs(r-m),o=f>=Math.abs(l-_),s&&(i.position.top=p._convertPositionTo("relative",{top:h,left:0}).top-p.margins.top),n&&(i.position.top=p._convertPositionTo("relative",{top:c-p.helperProportions.height,left:0}).top-p.margins.top),a&&(i.position.left=p._convertPositionTo("relative",{top:0,left:r}).left-p.margins.left),o&&(i.position.left=p._convertPositionTo("relative",{top:0,left:l-p.helperProportions.width}).left-p.margins.left)),!p.snapElements[u].snapping&&(s||n||a||o||d)&&p.options.snap.snap&&p.options.snap.snap.call(p.element,e,t.extend(p._uiHash(),{snapItem:p.snapElements[u].item})),p.snapElements[u].snapping=s||n||a||o||d)}}),t.ui.plugin.add("draggable","stack",{start:function(){var e,i=this.data("ui-draggable").options,s=t.makeArray(t(i.stack)).sort(function(e,i){return(parseInt(t(e).css("zIndex"),10)||0)-(parseInt(t(i).css("zIndex"),10)||0)});s.length&&(e=parseInt(t(s[0]).css("zIndex"),10)||0,t(s).each(function(i){t(this).css("zIndex",e+i)}),this.css("zIndex",e+s.length))}}),t.ui.plugin.add("draggable","zIndex",{start:function(e,i){var s=t(i.helper),n=t(this).data("ui-draggable").options;s.css("zIndex")&&(n._zIndex=s.css("zIndex")),s.css("zIndex",n.zIndex)},stop:function(e,i){var s=t(this).data("ui-draggable").options;s._zIndex&&t(i.helper).css("zIndex",s._zIndex)}})})(jQuery);(function(t){function e(t){return parseInt(t,10)||0}function i(t){return!isNaN(parseInt(t,10))}t.widget("ui.resizable",t.ui.mouse,{version:"1.10.4",widgetEventPrefix:"resize",options:{alsoResize:!1,animate:!1,animateDuration:"slow",animateEasing:"swing",aspectRatio:!1,autoHide:!1,containment:!1,ghost:!1,grid:!1,handles:"e,s,se",helper:!1,maxHeight:null,maxWidth:null,minHeight:10,minWidth:10,zIndex:90,resize:null,start:null,stop:null},_create:function(){var e,i,s,n,a,o=this,r=this.options;if(this.element.addClass("ui-resizable"),t.extend(this,{_aspectRatio:!!r.aspectRatio,aspectRatio:r.aspectRatio,originalElement:this.element,_proportionallyResizeElements:[],_helper:r.helper||r.ghost||r.animate?r.helper||"ui-resizable-helper":null}),this.element[0].nodeName.match(/canvas|textarea|input|select|button|img/i)&&(this.element.wrap(t("
    ").css({position:this.element.css("position"),width:this.element.outerWidth(),height:this.element.outerHeight(),top:this.element.css("top"),left:this.element.css("left")})),this.element=this.element.parent().data("ui-resizable",this.element.data("ui-resizable")),this.elementIsWrapper=!0,this.element.css({marginLeft:this.originalElement.css("marginLeft"),marginTop:this.originalElement.css("marginTop"),marginRight:this.originalElement.css("marginRight"),marginBottom:this.originalElement.css("marginBottom")}),this.originalElement.css({marginLeft:0,marginTop:0,marginRight:0,marginBottom:0}),this.originalResizeStyle=this.originalElement.css("resize"),this.originalElement.css("resize","none"),this._proportionallyResizeElements.push(this.originalElement.css({position:"static",zoom:1,display:"block"})),this.originalElement.css({margin:this.originalElement.css("margin")}),this._proportionallyResize()),this.handles=r.handles||(t(".ui-resizable-handle",this.element).length?{n:".ui-resizable-n",e:".ui-resizable-e",s:".ui-resizable-s",w:".ui-resizable-w",se:".ui-resizable-se",sw:".ui-resizable-sw",ne:".ui-resizable-ne",nw:".ui-resizable-nw"}:"e,s,se"),this.handles.constructor===String)for("all"===this.handles&&(this.handles="n,e,s,w,se,sw,ne,nw"),e=this.handles.split(","),this.handles={},i=0;e.length>i;i++)s=t.trim(e[i]),a="ui-resizable-"+s,n=t("
    "),n.css({zIndex:r.zIndex}),"se"===s&&n.addClass("ui-icon ui-icon-gripsmall-diagonal-se"),this.handles[s]=".ui-resizable-"+s,this.element.append(n);this._renderAxis=function(e){var i,s,n,a;e=e||this.element;for(i in this.handles)this.handles[i].constructor===String&&(this.handles[i]=t(this.handles[i],this.element).show()),this.elementIsWrapper&&this.originalElement[0].nodeName.match(/textarea|input|select|button/i)&&(s=t(this.handles[i],this.element),a=/sw|ne|nw|se|n|s/.test(i)?s.outerHeight():s.outerWidth(),n=["padding",/ne|nw|n/.test(i)?"Top":/se|sw|s/.test(i)?"Bottom":/^e$/.test(i)?"Right":"Left"].join(""),e.css(n,a),this._proportionallyResize()),t(this.handles[i]).length},this._renderAxis(this.element),this._handles=t(".ui-resizable-handle",this.element).disableSelection(),this._handles.mouseover(function(){o.resizing||(this.className&&(n=this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i)),o.axis=n&&n[1]?n[1]:"se")}),r.autoHide&&(this._handles.hide(),t(this.element).addClass("ui-resizable-autohide").mouseenter(function(){r.disabled||(t(this).removeClass("ui-resizable-autohide"),o._handles.show())}).mouseleave(function(){r.disabled||o.resizing||(t(this).addClass("ui-resizable-autohide"),o._handles.hide())})),this._mouseInit()},_destroy:function(){this._mouseDestroy();var e,i=function(e){t(e).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing").removeData("resizable").removeData("ui-resizable").unbind(".resizable").find(".ui-resizable-handle").remove()};return this.elementIsWrapper&&(i(this.element),e=this.element,this.originalElement.css({position:e.css("position"),width:e.outerWidth(),height:e.outerHeight(),top:e.css("top"),left:e.css("left")}).insertAfter(e),e.remove()),this.originalElement.css("resize",this.originalResizeStyle),i(this.originalElement),this},_mouseCapture:function(e){var i,s,n=!1;for(i in this.handles)s=t(this.handles[i])[0],(s===e.target||t.contains(s,e.target))&&(n=!0);return!this.options.disabled&&n},_mouseStart:function(i){var s,n,a,o=this.options,r=this.element.position(),h=this.element;return this.resizing=!0,/absolute/.test(h.css("position"))?h.css({position:"absolute",top:h.css("top"),left:h.css("left")}):h.is(".ui-draggable")&&h.css({position:"absolute",top:r.top,left:r.left}),this._renderProxy(),s=e(this.helper.css("left")),n=e(this.helper.css("top")),o.containment&&(s+=t(o.containment).scrollLeft()||0,n+=t(o.containment).scrollTop()||0),this.offset=this.helper.offset(),this.position={left:s,top:n},this.size=this._helper?{width:this.helper.width(),height:this.helper.height()}:{width:h.width(),height:h.height()},this.originalSize=this._helper?{width:h.outerWidth(),height:h.outerHeight()}:{width:h.width(),height:h.height()},this.originalPosition={left:s,top:n},this.sizeDiff={width:h.outerWidth()-h.width(),height:h.outerHeight()-h.height()},this.originalMousePosition={left:i.pageX,top:i.pageY},this.aspectRatio="number"==typeof o.aspectRatio?o.aspectRatio:this.originalSize.width/this.originalSize.height||1,a=t(".ui-resizable-"+this.axis).css("cursor"),t("body").css("cursor","auto"===a?this.axis+"-resize":a),h.addClass("ui-resizable-resizing"),this._propagate("start",i),!0},_mouseDrag:function(e){var i,s=this.helper,n={},a=this.originalMousePosition,o=this.axis,r=this.position.top,h=this.position.left,l=this.size.width,c=this.size.height,u=e.pageX-a.left||0,d=e.pageY-a.top||0,p=this._change[o];return p?(i=p.apply(this,[e,u,d]),this._updateVirtualBoundaries(e.shiftKey),(this._aspectRatio||e.shiftKey)&&(i=this._updateRatio(i,e)),i=this._respectSize(i,e),this._updateCache(i),this._propagate("resize",e),this.position.top!==r&&(n.top=this.position.top+"px"),this.position.left!==h&&(n.left=this.position.left+"px"),this.size.width!==l&&(n.width=this.size.width+"px"),this.size.height!==c&&(n.height=this.size.height+"px"),s.css(n),!this._helper&&this._proportionallyResizeElements.length&&this._proportionallyResize(),t.isEmptyObject(n)||this._trigger("resize",e,this.ui()),!1):!1},_mouseStop:function(e){this.resizing=!1;var i,s,n,a,o,r,h,l=this.options,c=this;return this._helper&&(i=this._proportionallyResizeElements,s=i.length&&/textarea/i.test(i[0].nodeName),n=s&&t.ui.hasScroll(i[0],"left")?0:c.sizeDiff.height,a=s?0:c.sizeDiff.width,o={width:c.helper.width()-a,height:c.helper.height()-n},r=parseInt(c.element.css("left"),10)+(c.position.left-c.originalPosition.left)||null,h=parseInt(c.element.css("top"),10)+(c.position.top-c.originalPosition.top)||null,l.animate||this.element.css(t.extend(o,{top:h,left:r})),c.helper.height(c.size.height),c.helper.width(c.size.width),this._helper&&!l.animate&&this._proportionallyResize()),t("body").css("cursor","auto"),this.element.removeClass("ui-resizable-resizing"),this._propagate("stop",e),this._helper&&this.helper.remove(),!1},_updateVirtualBoundaries:function(t){var e,s,n,a,o,r=this.options;o={minWidth:i(r.minWidth)?r.minWidth:0,maxWidth:i(r.maxWidth)?r.maxWidth:1/0,minHeight:i(r.minHeight)?r.minHeight:0,maxHeight:i(r.maxHeight)?r.maxHeight:1/0},(this._aspectRatio||t)&&(e=o.minHeight*this.aspectRatio,n=o.minWidth/this.aspectRatio,s=o.maxHeight*this.aspectRatio,a=o.maxWidth/this.aspectRatio,e>o.minWidth&&(o.minWidth=e),n>o.minHeight&&(o.minHeight=n),o.maxWidth>s&&(o.maxWidth=s),o.maxHeight>a&&(o.maxHeight=a)),this._vBoundaries=o},_updateCache:function(t){this.offset=this.helper.offset(),i(t.left)&&(this.position.left=t.left),i(t.top)&&(this.position.top=t.top),i(t.height)&&(this.size.height=t.height),i(t.width)&&(this.size.width=t.width)},_updateRatio:function(t){var e=this.position,s=this.size,n=this.axis;return i(t.height)?t.width=t.height*this.aspectRatio:i(t.width)&&(t.height=t.width/this.aspectRatio),"sw"===n&&(t.left=e.left+(s.width-t.width),t.top=null),"nw"===n&&(t.top=e.top+(s.height-t.height),t.left=e.left+(s.width-t.width)),t},_respectSize:function(t){var e=this._vBoundaries,s=this.axis,n=i(t.width)&&e.maxWidth&&e.maxWidtht.width,r=i(t.height)&&e.minHeight&&e.minHeight>t.height,h=this.originalPosition.left+this.originalSize.width,l=this.position.top+this.size.height,c=/sw|nw|w/.test(s),u=/nw|ne|n/.test(s);return o&&(t.width=e.minWidth),r&&(t.height=e.minHeight),n&&(t.width=e.maxWidth),a&&(t.height=e.maxHeight),o&&c&&(t.left=h-e.minWidth),n&&c&&(t.left=h-e.maxWidth),r&&u&&(t.top=l-e.minHeight),a&&u&&(t.top=l-e.maxHeight),t.width||t.height||t.left||!t.top?t.width||t.height||t.top||!t.left||(t.left=null):t.top=null,t},_proportionallyResize:function(){if(this._proportionallyResizeElements.length){var t,e,i,s,n,a=this.helper||this.element;for(t=0;this._proportionallyResizeElements.length>t;t++){if(n=this._proportionallyResizeElements[t],!this.borderDif)for(this.borderDif=[],i=[n.css("borderTopWidth"),n.css("borderRightWidth"),n.css("borderBottomWidth"),n.css("borderLeftWidth")],s=[n.css("paddingTop"),n.css("paddingRight"),n.css("paddingBottom"),n.css("paddingLeft")],e=0;i.length>e;e++)this.borderDif[e]=(parseInt(i[e],10)||0)+(parseInt(s[e],10)||0);n.css({height:a.height()-this.borderDif[0]-this.borderDif[2]||0,width:a.width()-this.borderDif[1]-this.borderDif[3]||0})}}},_renderProxy:function(){var e=this.element,i=this.options;this.elementOffset=e.offset(),this._helper?(this.helper=this.helper||t("
    "),this.helper.addClass(this._helper).css({width:this.element.outerWidth()-1,height:this.element.outerHeight()-1,position:"absolute",left:this.elementOffset.left+"px",top:this.elementOffset.top+"px",zIndex:++i.zIndex}),this.helper.appendTo("body").disableSelection()):this.helper=this.element},_change:{e:function(t,e){return{width:this.originalSize.width+e}},w:function(t,e){var i=this.originalSize,s=this.originalPosition;return{left:s.left+e,width:i.width-e}},n:function(t,e,i){var s=this.originalSize,n=this.originalPosition;return{top:n.top+i,height:s.height-i}},s:function(t,e,i){return{height:this.originalSize.height+i}},se:function(e,i,s){return t.extend(this._change.s.apply(this,arguments),this._change.e.apply(this,[e,i,s]))},sw:function(e,i,s){return t.extend(this._change.s.apply(this,arguments),this._change.w.apply(this,[e,i,s]))},ne:function(e,i,s){return t.extend(this._change.n.apply(this,arguments),this._change.e.apply(this,[e,i,s]))},nw:function(e,i,s){return t.extend(this._change.n.apply(this,arguments),this._change.w.apply(this,[e,i,s]))}},_propagate:function(e,i){t.ui.plugin.call(this,e,[i,this.ui()]),"resize"!==e&&this._trigger(e,i,this.ui())},plugins:{},ui:function(){return{originalElement:this.originalElement,element:this.element,helper:this.helper,position:this.position,size:this.size,originalSize:this.originalSize,originalPosition:this.originalPosition}}}),t.ui.plugin.add("resizable","animate",{stop:function(e){var i=t(this).data("ui-resizable"),s=i.options,n=i._proportionallyResizeElements,a=n.length&&/textarea/i.test(n[0].nodeName),o=a&&t.ui.hasScroll(n[0],"left")?0:i.sizeDiff.height,r=a?0:i.sizeDiff.width,h={width:i.size.width-r,height:i.size.height-o},l=parseInt(i.element.css("left"),10)+(i.position.left-i.originalPosition.left)||null,c=parseInt(i.element.css("top"),10)+(i.position.top-i.originalPosition.top)||null;i.element.animate(t.extend(h,c&&l?{top:c,left:l}:{}),{duration:s.animateDuration,easing:s.animateEasing,step:function(){var s={width:parseInt(i.element.css("width"),10),height:parseInt(i.element.css("height"),10),top:parseInt(i.element.css("top"),10),left:parseInt(i.element.css("left"),10)};n&&n.length&&t(n[0]).css({width:s.width,height:s.height}),i._updateCache(s),i._propagate("resize",e)}})}}),t.ui.plugin.add("resizable","containment",{start:function(){var i,s,n,a,o,r,h,l=t(this).data("ui-resizable"),c=l.options,u=l.element,d=c.containment,p=d instanceof t?d.get(0):/parent/.test(d)?u.parent().get(0):d;p&&(l.containerElement=t(p),/document/.test(d)||d===document?(l.containerOffset={left:0,top:0},l.containerPosition={left:0,top:0},l.parentData={element:t(document),left:0,top:0,width:t(document).width(),height:t(document).height()||document.body.parentNode.scrollHeight}):(i=t(p),s=[],t(["Top","Right","Left","Bottom"]).each(function(t,n){s[t]=e(i.css("padding"+n))}),l.containerOffset=i.offset(),l.containerPosition=i.position(),l.containerSize={height:i.innerHeight()-s[3],width:i.innerWidth()-s[1]},n=l.containerOffset,a=l.containerSize.height,o=l.containerSize.width,r=t.ui.hasScroll(p,"left")?p.scrollWidth:o,h=t.ui.hasScroll(p)?p.scrollHeight:a,l.parentData={element:p,left:n.left,top:n.top,width:r,height:h}))},resize:function(e){var i,s,n,a,o=t(this).data("ui-resizable"),r=o.options,h=o.containerOffset,l=o.position,c=o._aspectRatio||e.shiftKey,u={top:0,left:0},d=o.containerElement;d[0]!==document&&/static/.test(d.css("position"))&&(u=h),l.left<(o._helper?h.left:0)&&(o.size.width=o.size.width+(o._helper?o.position.left-h.left:o.position.left-u.left),c&&(o.size.height=o.size.width/o.aspectRatio),o.position.left=r.helper?h.left:0),l.top<(o._helper?h.top:0)&&(o.size.height=o.size.height+(o._helper?o.position.top-h.top:o.position.top),c&&(o.size.width=o.size.height*o.aspectRatio),o.position.top=o._helper?h.top:0),o.offset.left=o.parentData.left+o.position.left,o.offset.top=o.parentData.top+o.position.top,i=Math.abs((o._helper?o.offset.left-u.left:o.offset.left-u.left)+o.sizeDiff.width),s=Math.abs((o._helper?o.offset.top-u.top:o.offset.top-h.top)+o.sizeDiff.height),n=o.containerElement.get(0)===o.element.parent().get(0),a=/relative|absolute/.test(o.containerElement.css("position")),n&&a&&(i-=Math.abs(o.parentData.left)),i+o.size.width>=o.parentData.width&&(o.size.width=o.parentData.width-i,c&&(o.size.height=o.size.width/o.aspectRatio)),s+o.size.height>=o.parentData.height&&(o.size.height=o.parentData.height-s,c&&(o.size.width=o.size.height*o.aspectRatio))},stop:function(){var e=t(this).data("ui-resizable"),i=e.options,s=e.containerOffset,n=e.containerPosition,a=e.containerElement,o=t(e.helper),r=o.offset(),h=o.outerWidth()-e.sizeDiff.width,l=o.outerHeight()-e.sizeDiff.height;e._helper&&!i.animate&&/relative/.test(a.css("position"))&&t(this).css({left:r.left-n.left-s.left,width:h,height:l}),e._helper&&!i.animate&&/static/.test(a.css("position"))&&t(this).css({left:r.left-n.left-s.left,width:h,height:l})}}),t.ui.plugin.add("resizable","alsoResize",{start:function(){var e=t(this).data("ui-resizable"),i=e.options,s=function(e){t(e).each(function(){var e=t(this);e.data("ui-resizable-alsoresize",{width:parseInt(e.width(),10),height:parseInt(e.height(),10),left:parseInt(e.css("left"),10),top:parseInt(e.css("top"),10)})})};"object"!=typeof i.alsoResize||i.alsoResize.parentNode?s(i.alsoResize):i.alsoResize.length?(i.alsoResize=i.alsoResize[0],s(i.alsoResize)):t.each(i.alsoResize,function(t){s(t)})},resize:function(e,i){var s=t(this).data("ui-resizable"),n=s.options,a=s.originalSize,o=s.originalPosition,r={height:s.size.height-a.height||0,width:s.size.width-a.width||0,top:s.position.top-o.top||0,left:s.position.left-o.left||0},h=function(e,s){t(e).each(function(){var e=t(this),n=t(this).data("ui-resizable-alsoresize"),a={},o=s&&s.length?s:e.parents(i.originalElement[0]).length?["width","height"]:["width","height","top","left"];t.each(o,function(t,e){var i=(n[e]||0)+(r[e]||0);i&&i>=0&&(a[e]=i||null)}),e.css(a)})};"object"!=typeof n.alsoResize||n.alsoResize.nodeType?h(n.alsoResize):t.each(n.alsoResize,function(t,e){h(t,e)})},stop:function(){t(this).removeData("resizable-alsoresize")}}),t.ui.plugin.add("resizable","ghost",{start:function(){var e=t(this).data("ui-resizable"),i=e.options,s=e.size;e.ghost=e.originalElement.clone(),e.ghost.css({opacity:.25,display:"block",position:"relative",height:s.height,width:s.width,margin:0,left:0,top:0}).addClass("ui-resizable-ghost").addClass("string"==typeof i.ghost?i.ghost:""),e.ghost.appendTo(e.helper)},resize:function(){var e=t(this).data("ui-resizable");e.ghost&&e.ghost.css({position:"relative",height:e.size.height,width:e.size.width})},stop:function(){var e=t(this).data("ui-resizable");e.ghost&&e.helper&&e.helper.get(0).removeChild(e.ghost.get(0))}}),t.ui.plugin.add("resizable","grid",{resize:function(){var e=t(this).data("ui-resizable"),i=e.options,s=e.size,n=e.originalSize,a=e.originalPosition,o=e.axis,r="number"==typeof i.grid?[i.grid,i.grid]:i.grid,h=r[0]||1,l=r[1]||1,c=Math.round((s.width-n.width)/h)*h,u=Math.round((s.height-n.height)/l)*l,d=n.width+c,p=n.height+u,f=i.maxWidth&&d>i.maxWidth,g=i.maxHeight&&p>i.maxHeight,m=i.minWidth&&i.minWidth>d,v=i.minHeight&&i.minHeight>p;i.grid=r,m&&(d+=h),v&&(p+=l),f&&(d-=h),g&&(p-=l),/^(se|s|e)$/.test(o)?(e.size.width=d,e.size.height=p):/^(ne)$/.test(o)?(e.size.width=d,e.size.height=p,e.position.top=a.top-u):/^(sw)$/.test(o)?(e.size.width=d,e.size.height=p,e.position.left=a.left-c):(p-l>0?(e.size.height=p,e.position.top=a.top-u):(e.size.height=l,e.position.top=a.top+n.height-l),d-h>0?(e.size.width=d,e.position.left=a.left-c):(e.size.width=h,e.position.left=a.left+n.width-h))}})})(jQuery);(function(t){function e(t,e,i){return t>e&&e+i>t}function i(t){return/left|right/.test(t.css("float"))||/inline|table-cell/.test(t.css("display"))}t.widget("ui.sortable",t.ui.mouse,{version:"1.10.4",widgetEventPrefix:"sort",ready:!1,options:{appendTo:"parent",axis:!1,connectWith:!1,containment:!1,cursor:"auto",cursorAt:!1,dropOnEmpty:!0,forcePlaceholderSize:!1,forceHelperSize:!1,grid:!1,handle:!1,helper:"original",items:"> *",opacity:!1,placeholder:!1,revert:!1,scroll:!0,scrollSensitivity:20,scrollSpeed:20,scope:"default",tolerance:"intersect",zIndex:1e3,activate:null,beforeStop:null,change:null,deactivate:null,out:null,over:null,receive:null,remove:null,sort:null,start:null,stop:null,update:null},_create:function(){var t=this.options;this.containerCache={},this.element.addClass("ui-sortable"),this.refresh(),this.floating=this.items.length?"x"===t.axis||i(this.items[0].item):!1,this.offset=this.element.offset(),this._mouseInit(),this.ready=!0},_destroy:function(){this.element.removeClass("ui-sortable ui-sortable-disabled"),this._mouseDestroy();for(var t=this.items.length-1;t>=0;t--)this.items[t].item.removeData(this.widgetName+"-item");return this},_setOption:function(e,i){"disabled"===e?(this.options[e]=i,this.widget().toggleClass("ui-sortable-disabled",!!i)):t.Widget.prototype._setOption.apply(this,arguments)},_mouseCapture:function(e,i){var s=null,n=!1,o=this;return this.reverting?!1:this.options.disabled||"static"===this.options.type?!1:(this._refreshItems(e),t(e.target).parents().each(function(){return t.data(this,o.widgetName+"-item")===o?(s=t(this),!1):undefined}),t.data(e.target,o.widgetName+"-item")===o&&(s=t(e.target)),s?!this.options.handle||i||(t(this.options.handle,s).find("*").addBack().each(function(){this===e.target&&(n=!0)}),n)?(this.currentItem=s,this._removeCurrentsFromItems(),!0):!1:!1)},_mouseStart:function(e,i,s){var n,o,a=this.options;if(this.currentContainer=this,this.refreshPositions(),this.helper=this._createHelper(e),this._cacheHelperProportions(),this._cacheMargins(),this.scrollParent=this.helper.scrollParent(),this.offset=this.currentItem.offset(),this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left},t.extend(this.offset,{click:{left:e.pageX-this.offset.left,top:e.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()}),this.helper.css("position","absolute"),this.cssPosition=this.helper.css("position"),this.originalPosition=this._generatePosition(e),this.originalPageX=e.pageX,this.originalPageY=e.pageY,a.cursorAt&&this._adjustOffsetFromHelper(a.cursorAt),this.domPosition={prev:this.currentItem.prev()[0],parent:this.currentItem.parent()[0]},this.helper[0]!==this.currentItem[0]&&this.currentItem.hide(),this._createPlaceholder(),a.containment&&this._setContainment(),a.cursor&&"auto"!==a.cursor&&(o=this.document.find("body"),this.storedCursor=o.css("cursor"),o.css("cursor",a.cursor),this.storedStylesheet=t("").appendTo(o)),a.opacity&&(this.helper.css("opacity")&&(this._storedOpacity=this.helper.css("opacity")),this.helper.css("opacity",a.opacity)),a.zIndex&&(this.helper.css("zIndex")&&(this._storedZIndex=this.helper.css("zIndex")),this.helper.css("zIndex",a.zIndex)),this.scrollParent[0]!==document&&"HTML"!==this.scrollParent[0].tagName&&(this.overflowOffset=this.scrollParent.offset()),this._trigger("start",e,this._uiHash()),this._preserveHelperProportions||this._cacheHelperProportions(),!s)for(n=this.containers.length-1;n>=0;n--)this.containers[n]._trigger("activate",e,this._uiHash(this));return t.ui.ddmanager&&(t.ui.ddmanager.current=this),t.ui.ddmanager&&!a.dropBehaviour&&t.ui.ddmanager.prepareOffsets(this,e),this.dragging=!0,this.helper.addClass("ui-sortable-helper"),this._mouseDrag(e),!0},_mouseDrag:function(e){var i,s,n,o,a=this.options,r=!1;for(this.position=this._generatePosition(e),this.positionAbs=this._convertPositionTo("absolute"),this.lastPositionAbs||(this.lastPositionAbs=this.positionAbs),this.options.scroll&&(this.scrollParent[0]!==document&&"HTML"!==this.scrollParent[0].tagName?(this.overflowOffset.top+this.scrollParent[0].offsetHeight-e.pageY=0;i--)if(s=this.items[i],n=s.item[0],o=this._intersectsWithPointer(s),o&&s.instance===this.currentContainer&&n!==this.currentItem[0]&&this.placeholder[1===o?"next":"prev"]()[0]!==n&&!t.contains(this.placeholder[0],n)&&("semi-dynamic"===this.options.type?!t.contains(this.element[0],n):!0)){if(this.direction=1===o?"down":"up","pointer"!==this.options.tolerance&&!this._intersectsWithSides(s))break;this._rearrange(e,s),this._trigger("change",e,this._uiHash());break}return this._contactContainers(e),t.ui.ddmanager&&t.ui.ddmanager.drag(this,e),this._trigger("sort",e,this._uiHash()),this.lastPositionAbs=this.positionAbs,!1},_mouseStop:function(e,i){if(e){if(t.ui.ddmanager&&!this.options.dropBehaviour&&t.ui.ddmanager.drop(this,e),this.options.revert){var s=this,n=this.placeholder.offset(),o=this.options.axis,a={};o&&"x"!==o||(a.left=n.left-this.offset.parent.left-this.margins.left+(this.offsetParent[0]===document.body?0:this.offsetParent[0].scrollLeft)),o&&"y"!==o||(a.top=n.top-this.offset.parent.top-this.margins.top+(this.offsetParent[0]===document.body?0:this.offsetParent[0].scrollTop)),this.reverting=!0,t(this.helper).animate(a,parseInt(this.options.revert,10)||500,function(){s._clear(e)})}else this._clear(e,i);return!1}},cancel:function(){if(this.dragging){this._mouseUp({target:null}),"original"===this.options.helper?this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper"):this.currentItem.show();for(var e=this.containers.length-1;e>=0;e--)this.containers[e]._trigger("deactivate",null,this._uiHash(this)),this.containers[e].containerCache.over&&(this.containers[e]._trigger("out",null,this._uiHash(this)),this.containers[e].containerCache.over=0)}return this.placeholder&&(this.placeholder[0].parentNode&&this.placeholder[0].parentNode.removeChild(this.placeholder[0]),"original"!==this.options.helper&&this.helper&&this.helper[0].parentNode&&this.helper.remove(),t.extend(this,{helper:null,dragging:!1,reverting:!1,_noFinalSort:null}),this.domPosition.prev?t(this.domPosition.prev).after(this.currentItem):t(this.domPosition.parent).prepend(this.currentItem)),this},serialize:function(e){var i=this._getItemsAsjQuery(e&&e.connected),s=[];return e=e||{},t(i).each(function(){var i=(t(e.item||this).attr(e.attribute||"id")||"").match(e.expression||/(.+)[\-=_](.+)/);i&&s.push((e.key||i[1]+"[]")+"="+(e.key&&e.expression?i[1]:i[2]))}),!s.length&&e.key&&s.push(e.key+"="),s.join("&")},toArray:function(e){var i=this._getItemsAsjQuery(e&&e.connected),s=[];return e=e||{},i.each(function(){s.push(t(e.item||this).attr(e.attribute||"id")||"")}),s},_intersectsWith:function(t){var e=this.positionAbs.left,i=e+this.helperProportions.width,s=this.positionAbs.top,n=s+this.helperProportions.height,o=t.left,a=o+t.width,r=t.top,h=r+t.height,l=this.offset.click.top,c=this.offset.click.left,u="x"===this.options.axis||s+l>r&&h>s+l,d="y"===this.options.axis||e+c>o&&a>e+c,p=u&&d;return"pointer"===this.options.tolerance||this.options.forcePointerForContainers||"pointer"!==this.options.tolerance&&this.helperProportions[this.floating?"width":"height"]>t[this.floating?"width":"height"]?p:e+this.helperProportions.width/2>o&&a>i-this.helperProportions.width/2&&s+this.helperProportions.height/2>r&&h>n-this.helperProportions.height/2},_intersectsWithPointer:function(t){var i="x"===this.options.axis||e(this.positionAbs.top+this.offset.click.top,t.top,t.height),s="y"===this.options.axis||e(this.positionAbs.left+this.offset.click.left,t.left,t.width),n=i&&s,o=this._getDragVerticalDirection(),a=this._getDragHorizontalDirection();return n?this.floating?a&&"right"===a||"down"===o?2:1:o&&("down"===o?2:1):!1},_intersectsWithSides:function(t){var i=e(this.positionAbs.top+this.offset.click.top,t.top+t.height/2,t.height),s=e(this.positionAbs.left+this.offset.click.left,t.left+t.width/2,t.width),n=this._getDragVerticalDirection(),o=this._getDragHorizontalDirection();return this.floating&&o?"right"===o&&s||"left"===o&&!s:n&&("down"===n&&i||"up"===n&&!i)},_getDragVerticalDirection:function(){var t=this.positionAbs.top-this.lastPositionAbs.top;return 0!==t&&(t>0?"down":"up")},_getDragHorizontalDirection:function(){var t=this.positionAbs.left-this.lastPositionAbs.left;return 0!==t&&(t>0?"right":"left")},refresh:function(t){return this._refreshItems(t),this.refreshPositions(),this},_connectWith:function(){var t=this.options;return t.connectWith.constructor===String?[t.connectWith]:t.connectWith},_getItemsAsjQuery:function(e){function i(){r.push(this)}var s,n,o,a,r=[],h=[],l=this._connectWith();if(l&&e)for(s=l.length-1;s>=0;s--)for(o=t(l[s]),n=o.length-1;n>=0;n--)a=t.data(o[n],this.widgetFullName),a&&a!==this&&!a.options.disabled&&h.push([t.isFunction(a.options.items)?a.options.items.call(a.element):t(a.options.items,a.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),a]);for(h.push([t.isFunction(this.options.items)?this.options.items.call(this.element,null,{options:this.options,item:this.currentItem}):t(this.options.items,this.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),this]),s=h.length-1;s>=0;s--)h[s][0].each(i);return t(r)},_removeCurrentsFromItems:function(){var e=this.currentItem.find(":data("+this.widgetName+"-item)");this.items=t.grep(this.items,function(t){for(var i=0;e.length>i;i++)if(e[i]===t.item[0])return!1;return!0})},_refreshItems:function(e){this.items=[],this.containers=[this];var i,s,n,o,a,r,h,l,c=this.items,u=[[t.isFunction(this.options.items)?this.options.items.call(this.element[0],e,{item:this.currentItem}):t(this.options.items,this.element),this]],d=this._connectWith();if(d&&this.ready)for(i=d.length-1;i>=0;i--)for(n=t(d[i]),s=n.length-1;s>=0;s--)o=t.data(n[s],this.widgetFullName),o&&o!==this&&!o.options.disabled&&(u.push([t.isFunction(o.options.items)?o.options.items.call(o.element[0],e,{item:this.currentItem}):t(o.options.items,o.element),o]),this.containers.push(o));for(i=u.length-1;i>=0;i--)for(a=u[i][1],r=u[i][0],s=0,l=r.length;l>s;s++)h=t(r[s]),h.data(this.widgetName+"-item",a),c.push({item:h,instance:a,width:0,height:0,left:0,top:0})},refreshPositions:function(e){this.offsetParent&&this.helper&&(this.offset.parent=this._getParentOffset());var i,s,n,o;for(i=this.items.length-1;i>=0;i--)s=this.items[i],s.instance!==this.currentContainer&&this.currentContainer&&s.item[0]!==this.currentItem[0]||(n=this.options.toleranceElement?t(this.options.toleranceElement,s.item):s.item,e||(s.width=n.outerWidth(),s.height=n.outerHeight()),o=n.offset(),s.left=o.left,s.top=o.top);if(this.options.custom&&this.options.custom.refreshContainers)this.options.custom.refreshContainers.call(this);else for(i=this.containers.length-1;i>=0;i--)o=this.containers[i].element.offset(),this.containers[i].containerCache.left=o.left,this.containers[i].containerCache.top=o.top,this.containers[i].containerCache.width=this.containers[i].element.outerWidth(),this.containers[i].containerCache.height=this.containers[i].element.outerHeight();return this},_createPlaceholder:function(e){e=e||this;var i,s=e.options;s.placeholder&&s.placeholder.constructor!==String||(i=s.placeholder,s.placeholder={element:function(){var s=e.currentItem[0].nodeName.toLowerCase(),n=t("<"+s+">",e.document[0]).addClass(i||e.currentItem[0].className+" ui-sortable-placeholder").removeClass("ui-sortable-helper");return"tr"===s?e.currentItem.children().each(function(){t("
    ",e.document[0]).attr("colspan",t(this).attr("colspan")||1).appendTo(n)}):"img"===s&&n.attr("src",e.currentItem.attr("src")),i||n.css("visibility","hidden"),n},update:function(t,n){(!i||s.forcePlaceholderSize)&&(n.height()||n.height(e.currentItem.innerHeight()-parseInt(e.currentItem.css("paddingTop")||0,10)-parseInt(e.currentItem.css("paddingBottom")||0,10)),n.width()||n.width(e.currentItem.innerWidth()-parseInt(e.currentItem.css("paddingLeft")||0,10)-parseInt(e.currentItem.css("paddingRight")||0,10)))}}),e.placeholder=t(s.placeholder.element.call(e.element,e.currentItem)),e.currentItem.after(e.placeholder),s.placeholder.update(e,e.placeholder)},_contactContainers:function(s){var n,o,a,r,h,l,c,u,d,p,f=null,g=null;for(n=this.containers.length-1;n>=0;n--)if(!t.contains(this.currentItem[0],this.containers[n].element[0]))if(this._intersectsWith(this.containers[n].containerCache)){if(f&&t.contains(this.containers[n].element[0],f.element[0]))continue;f=this.containers[n],g=n}else this.containers[n].containerCache.over&&(this.containers[n]._trigger("out",s,this._uiHash(this)),this.containers[n].containerCache.over=0);if(f)if(1===this.containers.length)this.containers[g].containerCache.over||(this.containers[g]._trigger("over",s,this._uiHash(this)),this.containers[g].containerCache.over=1);else{for(a=1e4,r=null,p=f.floating||i(this.currentItem),h=p?"left":"top",l=p?"width":"height",c=this.positionAbs[h]+this.offset.click[h],o=this.items.length-1;o>=0;o--)t.contains(this.containers[g].element[0],this.items[o].item[0])&&this.items[o].item[0]!==this.currentItem[0]&&(!p||e(this.positionAbs.top+this.offset.click.top,this.items[o].top,this.items[o].height))&&(u=this.items[o].item.offset()[h],d=!1,Math.abs(u-c)>Math.abs(u+this.items[o][l]-c)&&(d=!0,u+=this.items[o][l]),a>Math.abs(u-c)&&(a=Math.abs(u-c),r=this.items[o],this.direction=d?"up":"down"));if(!r&&!this.options.dropOnEmpty)return;if(this.currentContainer===this.containers[g])return;r?this._rearrange(s,r,null,!0):this._rearrange(s,null,this.containers[g].element,!0),this._trigger("change",s,this._uiHash()),this.containers[g]._trigger("change",s,this._uiHash(this)),this.currentContainer=this.containers[g],this.options.placeholder.update(this.currentContainer,this.placeholder),this.containers[g]._trigger("over",s,this._uiHash(this)),this.containers[g].containerCache.over=1}},_createHelper:function(e){var i=this.options,s=t.isFunction(i.helper)?t(i.helper.apply(this.element[0],[e,this.currentItem])):"clone"===i.helper?this.currentItem.clone():this.currentItem;return s.parents("body").length||t("parent"!==i.appendTo?i.appendTo:this.currentItem[0].parentNode)[0].appendChild(s[0]),s[0]===this.currentItem[0]&&(this._storedCSS={width:this.currentItem[0].style.width,height:this.currentItem[0].style.height,position:this.currentItem.css("position"),top:this.currentItem.css("top"),left:this.currentItem.css("left")}),(!s[0].style.width||i.forceHelperSize)&&s.width(this.currentItem.width()),(!s[0].style.height||i.forceHelperSize)&&s.height(this.currentItem.height()),s},_adjustOffsetFromHelper:function(e){"string"==typeof e&&(e=e.split(" ")),t.isArray(e)&&(e={left:+e[0],top:+e[1]||0}),"left"in e&&(this.offset.click.left=e.left+this.margins.left),"right"in e&&(this.offset.click.left=this.helperProportions.width-e.right+this.margins.left),"top"in e&&(this.offset.click.top=e.top+this.margins.top),"bottom"in e&&(this.offset.click.top=this.helperProportions.height-e.bottom+this.margins.top)},_getParentOffset:function(){this.offsetParent=this.helper.offsetParent();var e=this.offsetParent.offset();return"absolute"===this.cssPosition&&this.scrollParent[0]!==document&&t.contains(this.scrollParent[0],this.offsetParent[0])&&(e.left+=this.scrollParent.scrollLeft(),e.top+=this.scrollParent.scrollTop()),(this.offsetParent[0]===document.body||this.offsetParent[0].tagName&&"html"===this.offsetParent[0].tagName.toLowerCase()&&t.ui.ie)&&(e={top:0,left:0}),{top:e.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:e.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if("relative"===this.cssPosition){var t=this.currentItem.position();return{top:t.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:t.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.currentItem.css("marginLeft"),10)||0,top:parseInt(this.currentItem.css("marginTop"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var e,i,s,n=this.options;"parent"===n.containment&&(n.containment=this.helper[0].parentNode),("document"===n.containment||"window"===n.containment)&&(this.containment=[0-this.offset.relative.left-this.offset.parent.left,0-this.offset.relative.top-this.offset.parent.top,t("document"===n.containment?document:window).width()-this.helperProportions.width-this.margins.left,(t("document"===n.containment?document:window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top]),/^(document|window|parent)$/.test(n.containment)||(e=t(n.containment)[0],i=t(n.containment).offset(),s="hidden"!==t(e).css("overflow"),this.containment=[i.left+(parseInt(t(e).css("borderLeftWidth"),10)||0)+(parseInt(t(e).css("paddingLeft"),10)||0)-this.margins.left,i.top+(parseInt(t(e).css("borderTopWidth"),10)||0)+(parseInt(t(e).css("paddingTop"),10)||0)-this.margins.top,i.left+(s?Math.max(e.scrollWidth,e.offsetWidth):e.offsetWidth)-(parseInt(t(e).css("borderLeftWidth"),10)||0)-(parseInt(t(e).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left,i.top+(s?Math.max(e.scrollHeight,e.offsetHeight):e.offsetHeight)-(parseInt(t(e).css("borderTopWidth"),10)||0)-(parseInt(t(e).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top])},_convertPositionTo:function(e,i){i||(i=this.position);var s="absolute"===e?1:-1,n="absolute"!==this.cssPosition||this.scrollParent[0]!==document&&t.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent,o=/(html|body)/i.test(n[0].tagName);return{top:i.top+this.offset.relative.top*s+this.offset.parent.top*s-("fixed"===this.cssPosition?-this.scrollParent.scrollTop():o?0:n.scrollTop())*s,left:i.left+this.offset.relative.left*s+this.offset.parent.left*s-("fixed"===this.cssPosition?-this.scrollParent.scrollLeft():o?0:n.scrollLeft())*s}},_generatePosition:function(e){var i,s,n=this.options,o=e.pageX,a=e.pageY,r="absolute"!==this.cssPosition||this.scrollParent[0]!==document&&t.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent,h=/(html|body)/i.test(r[0].tagName);return"relative"!==this.cssPosition||this.scrollParent[0]!==document&&this.scrollParent[0]!==this.offsetParent[0]||(this.offset.relative=this._getRelativeOffset()),this.originalPosition&&(this.containment&&(e.pageX-this.offset.click.leftthis.containment[2]&&(o=this.containment[2]+this.offset.click.left),e.pageY-this.offset.click.top>this.containment[3]&&(a=this.containment[3]+this.offset.click.top)),n.grid&&(i=this.originalPageY+Math.round((a-this.originalPageY)/n.grid[1])*n.grid[1],a=this.containment?i-this.offset.click.top>=this.containment[1]&&i-this.offset.click.top<=this.containment[3]?i:i-this.offset.click.top>=this.containment[1]?i-n.grid[1]:i+n.grid[1]:i,s=this.originalPageX+Math.round((o-this.originalPageX)/n.grid[0])*n.grid[0],o=this.containment?s-this.offset.click.left>=this.containment[0]&&s-this.offset.click.left<=this.containment[2]?s:s-this.offset.click.left>=this.containment[0]?s-n.grid[0]:s+n.grid[0]:s)),{top:a-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+("fixed"===this.cssPosition?-this.scrollParent.scrollTop():h?0:r.scrollTop()),left:o-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+("fixed"===this.cssPosition?-this.scrollParent.scrollLeft():h?0:r.scrollLeft())}},_rearrange:function(t,e,i,s){i?i[0].appendChild(this.placeholder[0]):e.item[0].parentNode.insertBefore(this.placeholder[0],"down"===this.direction?e.item[0]:e.item[0].nextSibling),this.counter=this.counter?++this.counter:1;var n=this.counter;this._delay(function(){n===this.counter&&this.refreshPositions(!s)})},_clear:function(t,e){function i(t,e,i){return function(s){i._trigger(t,s,e._uiHash(e))}}this.reverting=!1;var s,n=[];if(!this._noFinalSort&&this.currentItem.parent().length&&this.placeholder.before(this.currentItem),this._noFinalSort=null,this.helper[0]===this.currentItem[0]){for(s in this._storedCSS)("auto"===this._storedCSS[s]||"static"===this._storedCSS[s])&&(this._storedCSS[s]="");this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper")}else this.currentItem.show();for(this.fromOutside&&!e&&n.push(function(t){this._trigger("receive",t,this._uiHash(this.fromOutside))}),!this.fromOutside&&this.domPosition.prev===this.currentItem.prev().not(".ui-sortable-helper")[0]&&this.domPosition.parent===this.currentItem.parent()[0]||e||n.push(function(t){this._trigger("update",t,this._uiHash())}),this!==this.currentContainer&&(e||(n.push(function(t){this._trigger("remove",t,this._uiHash())}),n.push(function(t){return function(e){t._trigger("receive",e,this._uiHash(this))}}.call(this,this.currentContainer)),n.push(function(t){return function(e){t._trigger("update",e,this._uiHash(this))}}.call(this,this.currentContainer)))),s=this.containers.length-1;s>=0;s--)e||n.push(i("deactivate",this,this.containers[s])),this.containers[s].containerCache.over&&(n.push(i("out",this,this.containers[s])),this.containers[s].containerCache.over=0);if(this.storedCursor&&(this.document.find("body").css("cursor",this.storedCursor),this.storedStylesheet.remove()),this._storedOpacity&&this.helper.css("opacity",this._storedOpacity),this._storedZIndex&&this.helper.css("zIndex","auto"===this._storedZIndex?"":this._storedZIndex),this.dragging=!1,this.cancelHelperRemoval){if(!e){for(this._trigger("beforeStop",t,this._uiHash()),s=0;n.length>s;s++)n[s].call(this,t);this._trigger("stop",t,this._uiHash())}return this.fromOutside=!1,!1}if(e||this._trigger("beforeStop",t,this._uiHash()),this.placeholder[0].parentNode.removeChild(this.placeholder[0]),this.helper[0]!==this.currentItem[0]&&this.helper.remove(),this.helper=null,!e){for(s=0;n.length>s;s++)n[s].call(this,t);this._trigger("stop",t,this._uiHash())}return this.fromOutside=!1,!0},_trigger:function(){t.Widget.prototype._trigger.apply(this,arguments)===!1&&this.cancel()},_uiHash:function(e){var i=e||this;return{helper:i.helper,placeholder:i.placeholder||t([]),position:i.position,originalPosition:i.originalPosition,offset:i.positionAbs,item:i.currentItem,sender:e?e.element:null}}})})(jQuery);(function(e){var t,i="ui-button ui-widget ui-state-default ui-corner-all",n="ui-button-icons-only ui-button-icon-only ui-button-text-icons ui-button-text-icon-primary ui-button-text-icon-secondary ui-button-text-only",s=function(){var t=e(this);setTimeout(function(){t.find(":ui-button").button("refresh")},1)},a=function(t){var i=t.name,n=t.form,s=e([]);return i&&(i=i.replace(/'/g,"\\'"),s=n?e(n).find("[name='"+i+"']"):e("[name='"+i+"']",t.ownerDocument).filter(function(){return!this.form})),s};e.widget("ui.button",{version:"1.10.4",defaultElement:"").button({label:this.options.closeText,icons:{primary:"ui-icon-closethick"},text:!1}).addClass("ui-dialog-titlebar-close").appendTo(this.uiDialogTitlebar),this._on(this.uiDialogTitlebarClose,{click:function(e){e.preventDefault(),this.close(e)}}),t=e("").uniqueId().addClass("ui-dialog-title").prependTo(this.uiDialogTitlebar),this._title(t),this.uiDialog.attr({"aria-labelledby":t.attr("id")})},_title:function(e){this.options.title||e.html(" "),e.text(this.options.title)},_createButtonPane:function(){this.uiDialogButtonPane=e("
    ").addClass("ui-dialog-buttonpane ui-widget-content ui-helper-clearfix"),this.uiButtonSet=e("
    ").addClass("ui-dialog-buttonset").appendTo(this.uiDialogButtonPane),this._createButtons()},_createButtons:function(){var t=this,i=this.options.buttons;return this.uiDialogButtonPane.remove(),this.uiButtonSet.empty(),e.isEmptyObject(i)||e.isArray(i)&&!i.length?(this.uiDialog.removeClass("ui-dialog-buttons"),undefined):(e.each(i,function(i,a){var s,n;a=e.isFunction(a)?{click:a,text:i}:a,a=e.extend({type:"button"},a),s=a.click,a.click=function(){s.apply(t.element[0],arguments)},n={icons:a.icons,text:a.showText},delete a.icons,delete a.showText,e("",a).button(n).appendTo(t.uiButtonSet)}),this.uiDialog.addClass("ui-dialog-buttons"),this.uiDialogButtonPane.appendTo(this.uiDialog),undefined)},_makeDraggable:function(){function t(e){return{position:e.position,offset:e.offset}}var i=this,a=this.options;this.uiDialog.draggable({cancel:".ui-dialog-content, .ui-dialog-titlebar-close",handle:".ui-dialog-titlebar",containment:"document",start:function(a,s){e(this).addClass("ui-dialog-dragging"),i._blockFrames(),i._trigger("dragStart",a,t(s))},drag:function(e,a){i._trigger("drag",e,t(a))},stop:function(s,n){a.position=[n.position.left-i.document.scrollLeft(),n.position.top-i.document.scrollTop()],e(this).removeClass("ui-dialog-dragging"),i._unblockFrames(),i._trigger("dragStop",s,t(n))}})},_makeResizable:function(){function t(e){return{originalPosition:e.originalPosition,originalSize:e.originalSize,position:e.position,size:e.size}}var i=this,a=this.options,s=a.resizable,n=this.uiDialog.css("position"),r="string"==typeof s?s:"n,e,s,w,se,sw,ne,nw";this.uiDialog.resizable({cancel:".ui-dialog-content",containment:"document",alsoResize:this.element,maxWidth:a.maxWidth,maxHeight:a.maxHeight,minWidth:a.minWidth,minHeight:this._minHeight(),handles:r,start:function(a,s){e(this).addClass("ui-dialog-resizing"),i._blockFrames(),i._trigger("resizeStart",a,t(s))},resize:function(e,a){i._trigger("resize",e,t(a))},stop:function(s,n){a.height=e(this).height(),a.width=e(this).width(),e(this).removeClass("ui-dialog-resizing"),i._unblockFrames(),i._trigger("resizeStop",s,t(n))}}).css("position",n)},_minHeight:function(){var e=this.options;return"auto"===e.height?e.minHeight:Math.min(e.minHeight,e.height)},_position:function(){var e=this.uiDialog.is(":visible");e||this.uiDialog.show(),this.uiDialog.position(this.options.position),e||this.uiDialog.hide()},_setOptions:function(a){var s=this,n=!1,r={};e.each(a,function(e,a){s._setOption(e,a),e in t&&(n=!0),e in i&&(r[e]=a)}),n&&(this._size(),this._position()),this.uiDialog.is(":data(ui-resizable)")&&this.uiDialog.resizable("option",r)},_setOption:function(e,t){var i,a,s=this.uiDialog;"dialogClass"===e&&s.removeClass(this.options.dialogClass).addClass(t),"disabled"!==e&&(this._super(e,t),"appendTo"===e&&this.uiDialog.appendTo(this._appendTo()),"buttons"===e&&this._createButtons(),"closeText"===e&&this.uiDialogTitlebarClose.button({label:""+t}),"draggable"===e&&(i=s.is(":data(ui-draggable)"),i&&!t&&s.draggable("destroy"),!i&&t&&this._makeDraggable()),"position"===e&&this._position(),"resizable"===e&&(a=s.is(":data(ui-resizable)"),a&&!t&&s.resizable("destroy"),a&&"string"==typeof t&&s.resizable("option","handles",t),a||t===!1||this._makeResizable()),"title"===e&&this._title(this.uiDialogTitlebar.find(".ui-dialog-title")))},_size:function(){var e,t,i,a=this.options;this.element.show().css({width:"auto",minHeight:0,maxHeight:"none",height:0}),a.minWidth>a.width&&(a.width=a.minWidth),e=this.uiDialog.css({height:"auto",width:a.width}).outerHeight(),t=Math.max(0,a.minHeight-e),i="number"==typeof a.maxHeight?Math.max(0,a.maxHeight-e):"none","auto"===a.height?this.element.css({minHeight:t,maxHeight:i,height:"auto"}):this.element.height(Math.max(0,a.height-e)),this.uiDialog.is(":data(ui-resizable)")&&this.uiDialog.resizable("option","minHeight",this._minHeight())},_blockFrames:function(){this.iframeBlocks=this.document.find("iframe").map(function(){var t=e(this);return e("
    ").css({position:"absolute",width:t.outerWidth(),height:t.outerHeight()}).appendTo(t.parent()).offset(t.offset())[0]})},_unblockFrames:function(){this.iframeBlocks&&(this.iframeBlocks.remove(),delete this.iframeBlocks)},_allowInteraction:function(t){return e(t.target).closest(".ui-dialog").length?!0:!!e(t.target).closest(".ui-datepicker").length},_createOverlay:function(){if(this.options.modal){var t=this,i=this.widgetFullName;e.ui.dialog.overlayInstances||this._delay(function(){e.ui.dialog.overlayInstances&&this.document.bind("focusin.dialog",function(a){t._allowInteraction(a)||(a.preventDefault(),e(".ui-dialog:visible:last .ui-dialog-content").data(i)._focusTabbable())})}),this.overlay=e("
    ").addClass("ui-widget-overlay ui-front").appendTo(this._appendTo()),this._on(this.overlay,{mousedown:"_keepFocus"}),e.ui.dialog.overlayInstances++}},_destroyOverlay:function(){this.options.modal&&this.overlay&&(e.ui.dialog.overlayInstances--,e.ui.dialog.overlayInstances||this.document.unbind("focusin.dialog"),this.overlay.remove(),this.overlay=null)}}),e.ui.dialog.overlayInstances=0,e.uiBackCompat!==!1&&e.widget("ui.dialog",e.ui.dialog,{_position:function(){var t,i=this.options.position,a=[],s=[0,0];i?(("string"==typeof i||"object"==typeof i&&"0"in i)&&(a=i.split?i.split(" "):[i[0],i[1]],1===a.length&&(a[1]=a[0]),e.each(["left","top"],function(e,t){+a[e]===a[e]&&(s[e]=a[e],a[e]=t)}),i={my:a[0]+(0>s[0]?s[0]:"+"+s[0])+" "+a[1]+(0>s[1]?s[1]:"+"+s[1]),at:a.join(" ")}),i=e.extend({},e.ui.dialog.prototype.options.position,i)):i=e.ui.dialog.prototype.options.position,t=this.uiDialog.is(":visible"),t||this.uiDialog.show(),this.uiDialog.position(i),t||this.uiDialog.hide()}})})(jQuery);(function(t){function e(e,i){var s=(e.attr("aria-describedby")||"").split(/\s+/);s.push(i),e.data("ui-tooltip-id",i).attr("aria-describedby",t.trim(s.join(" ")))}function i(e){var i=e.data("ui-tooltip-id"),s=(e.attr("aria-describedby")||"").split(/\s+/),n=t.inArray(i,s);-1!==n&&s.splice(n,1),e.removeData("ui-tooltip-id"),s=t.trim(s.join(" ")),s?e.attr("aria-describedby",s):e.removeAttr("aria-describedby")}var s=0;t.widget("ui.tooltip",{version:"1.10.4",options:{content:function(){var e=t(this).attr("title")||"";return t("").text(e).html()},hide:!0,items:"[title]:not([disabled])",position:{my:"left top+15",at:"left bottom",collision:"flipfit flip"},show:!0,tooltipClass:null,track:!1,close:null,open:null},_create:function(){this._on({mouseover:"open",focusin:"open"}),this.tooltips={},this.parents={},this.options.disabled&&this._disable()},_setOption:function(e,i){var s=this;return"disabled"===e?(this[i?"_disable":"_enable"](),this.options[e]=i,void 0):(this._super(e,i),"content"===e&&t.each(this.tooltips,function(t,e){s._updateContent(e)}),void 0)},_disable:function(){var e=this;t.each(this.tooltips,function(i,s){var n=t.Event("blur");n.target=n.currentTarget=s[0],e.close(n,!0)}),this.element.find(this.options.items).addBack().each(function(){var e=t(this);e.is("[title]")&&e.data("ui-tooltip-title",e.attr("title")).attr("title","")})},_enable:function(){this.element.find(this.options.items).addBack().each(function(){var e=t(this);e.data("ui-tooltip-title")&&e.attr("title",e.data("ui-tooltip-title"))})},open:function(e){var i=this,s=t(e?e.target:this.element).closest(this.options.items);s.length&&!s.data("ui-tooltip-id")&&(s.attr("title")&&s.data("ui-tooltip-title",s.attr("title")),s.data("ui-tooltip-open",!0),e&&"mouseover"===e.type&&s.parents().each(function(){var e,s=t(this);s.data("ui-tooltip-open")&&(e=t.Event("blur"),e.target=e.currentTarget=this,i.close(e,!0)),s.attr("title")&&(s.uniqueId(),i.parents[this.id]={element:this,title:s.attr("title")},s.attr("title",""))}),this._updateContent(s,e))},_updateContent:function(t,e){var i,s=this.options.content,n=this,o=e?e.type:null;return"string"==typeof s?this._open(e,t,s):(i=s.call(t[0],function(i){t.data("ui-tooltip-open")&&n._delay(function(){e&&(e.type=o),this._open(e,t,i)})}),i&&this._open(e,t,i),void 0)},_open:function(i,s,n){function o(t){l.of=t,a.is(":hidden")||a.position(l)}var a,r,h,l=t.extend({},this.options.position);if(n){if(a=this._find(s),a.length)return a.find(".ui-tooltip-content").html(n),void 0;s.is("[title]")&&(i&&"mouseover"===i.type?s.attr("title",""):s.removeAttr("title")),a=this._tooltip(s),e(s,a.attr("id")),a.find(".ui-tooltip-content").html(n),this.options.track&&i&&/^mouse/.test(i.type)?(this._on(this.document,{mousemove:o}),o(i)):a.position(t.extend({of:s},this.options.position)),a.hide(),this._show(a,this.options.show),this.options.show&&this.options.show.delay&&(h=this.delayedShow=setInterval(function(){a.is(":visible")&&(o(l.of),clearInterval(h))},t.fx.interval)),this._trigger("open",i,{tooltip:a}),r={keyup:function(e){if(e.keyCode===t.ui.keyCode.ESCAPE){var i=t.Event(e);i.currentTarget=s[0],this.close(i,!0)}},remove:function(){this._removeTooltip(a)}},i&&"mouseover"!==i.type||(r.mouseleave="close"),i&&"focusin"!==i.type||(r.focusout="close"),this._on(!0,s,r)}},close:function(e){var s=this,n=t(e?e.currentTarget:this.element),o=this._find(n);this.closing||(clearInterval(this.delayedShow),n.data("ui-tooltip-title")&&n.attr("title",n.data("ui-tooltip-title")),i(n),o.stop(!0),this._hide(o,this.options.hide,function(){s._removeTooltip(t(this))}),n.removeData("ui-tooltip-open"),this._off(n,"mouseleave focusout keyup"),n[0]!==this.element[0]&&this._off(n,"remove"),this._off(this.document,"mousemove"),e&&"mouseleave"===e.type&&t.each(this.parents,function(e,i){t(i.element).attr("title",i.title),delete s.parents[e]}),this.closing=!0,this._trigger("close",e,{tooltip:o}),this.closing=!1)},_tooltip:function(e){var i="ui-tooltip-"+s++,n=t("
    ").attr({id:i,role:"tooltip"}).addClass("ui-tooltip ui-widget ui-corner-all ui-widget-content "+(this.options.tooltipClass||""));return t("
    ").addClass("ui-tooltip-content").appendTo(n),n.appendTo(this.document[0].body),this.tooltips[i]=e,n},_find:function(e){var i=e.data("ui-tooltip-id");return i?t("#"+i):t()},_removeTooltip:function(t){t.remove(),delete this.tooltips[t.attr("id")]},_destroy:function(){var e=this;t.each(this.tooltips,function(i,s){var n=t.Event("blur");n.target=n.currentTarget=s[0],e.close(n,!0),t("#"+i).remove(),s.data("ui-tooltip-title")&&(s.attr("title",s.data("ui-tooltip-title")),s.removeData("ui-tooltip-title"))})}})})(jQuery);(function(t,e){var i="ui-effects-";t.effects={effect:{}},function(t,e){function i(t,e,i){var s=u[e.type]||{};return null==t?i||!e.def?null:e.def:(t=s.floor?~~t:parseFloat(t),isNaN(t)?e.def:s.mod?(t+s.mod)%s.mod:0>t?0:t>s.max?s.max:t)}function s(i){var s=h(),n=s._rgba=[];return i=i.toLowerCase(),f(l,function(t,a){var o,r=a.re.exec(i),l=r&&a.parse(r),h=a.space||"rgba";return l?(o=s[h](l),s[c[h].cache]=o[c[h].cache],n=s._rgba=o._rgba,!1):e}),n.length?("0,0,0,0"===n.join()&&t.extend(n,a.transparent),s):a[i]}function n(t,e,i){return i=(i+1)%1,1>6*i?t+6*(e-t)*i:1>2*i?e:2>3*i?t+6*(e-t)*(2/3-i):t}var a,o="backgroundColor borderBottomColor borderLeftColor borderRightColor borderTopColor color columnRuleColor outlineColor textDecorationColor textEmphasisColor",r=/^([\-+])=\s*(\d+\.?\d*)/,l=[{re:/rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,parse:function(t){return[t[1],t[2],t[3],t[4]]}},{re:/rgba?\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,parse:function(t){return[2.55*t[1],2.55*t[2],2.55*t[3],t[4]]}},{re:/#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})/,parse:function(t){return[parseInt(t[1],16),parseInt(t[2],16),parseInt(t[3],16)]}},{re:/#([a-f0-9])([a-f0-9])([a-f0-9])/,parse:function(t){return[parseInt(t[1]+t[1],16),parseInt(t[2]+t[2],16),parseInt(t[3]+t[3],16)]}},{re:/hsla?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,space:"hsla",parse:function(t){return[t[1],t[2]/100,t[3]/100,t[4]]}}],h=t.Color=function(e,i,s,n){return new t.Color.fn.parse(e,i,s,n)},c={rgba:{props:{red:{idx:0,type:"byte"},green:{idx:1,type:"byte"},blue:{idx:2,type:"byte"}}},hsla:{props:{hue:{idx:0,type:"degrees"},saturation:{idx:1,type:"percent"},lightness:{idx:2,type:"percent"}}}},u={"byte":{floor:!0,max:255},percent:{max:1},degrees:{mod:360,floor:!0}},d=h.support={},p=t("

    ")[0],f=t.each;p.style.cssText="background-color:rgba(1,1,1,.5)",d.rgba=p.style.backgroundColor.indexOf("rgba")>-1,f(c,function(t,e){e.cache="_"+t,e.props.alpha={idx:3,type:"percent",def:1}}),h.fn=t.extend(h.prototype,{parse:function(n,o,r,l){if(n===e)return this._rgba=[null,null,null,null],this;(n.jquery||n.nodeType)&&(n=t(n).css(o),o=e);var u=this,d=t.type(n),p=this._rgba=[];return o!==e&&(n=[n,o,r,l],d="array"),"string"===d?this.parse(s(n)||a._default):"array"===d?(f(c.rgba.props,function(t,e){p[e.idx]=i(n[e.idx],e)}),this):"object"===d?(n instanceof h?f(c,function(t,e){n[e.cache]&&(u[e.cache]=n[e.cache].slice())}):f(c,function(e,s){var a=s.cache;f(s.props,function(t,e){if(!u[a]&&s.to){if("alpha"===t||null==n[t])return;u[a]=s.to(u._rgba)}u[a][e.idx]=i(n[t],e,!0)}),u[a]&&0>t.inArray(null,u[a].slice(0,3))&&(u[a][3]=1,s.from&&(u._rgba=s.from(u[a])))}),this):e},is:function(t){var i=h(t),s=!0,n=this;return f(c,function(t,a){var o,r=i[a.cache];return r&&(o=n[a.cache]||a.to&&a.to(n._rgba)||[],f(a.props,function(t,i){return null!=r[i.idx]?s=r[i.idx]===o[i.idx]:e})),s}),s},_space:function(){var t=[],e=this;return f(c,function(i,s){e[s.cache]&&t.push(i)}),t.pop()},transition:function(t,e){var s=h(t),n=s._space(),a=c[n],o=0===this.alpha()?h("transparent"):this,r=o[a.cache]||a.to(o._rgba),l=r.slice();return s=s[a.cache],f(a.props,function(t,n){var a=n.idx,o=r[a],h=s[a],c=u[n.type]||{};null!==h&&(null===o?l[a]=h:(c.mod&&(h-o>c.mod/2?o+=c.mod:o-h>c.mod/2&&(o-=c.mod)),l[a]=i((h-o)*e+o,n)))}),this[n](l)},blend:function(e){if(1===this._rgba[3])return this;var i=this._rgba.slice(),s=i.pop(),n=h(e)._rgba;return h(t.map(i,function(t,e){return(1-s)*n[e]+s*t}))},toRgbaString:function(){var e="rgba(",i=t.map(this._rgba,function(t,e){return null==t?e>2?1:0:t});return 1===i[3]&&(i.pop(),e="rgb("),e+i.join()+")"},toHslaString:function(){var e="hsla(",i=t.map(this.hsla(),function(t,e){return null==t&&(t=e>2?1:0),e&&3>e&&(t=Math.round(100*t)+"%"),t});return 1===i[3]&&(i.pop(),e="hsl("),e+i.join()+")"},toHexString:function(e){var i=this._rgba.slice(),s=i.pop();return e&&i.push(~~(255*s)),"#"+t.map(i,function(t){return t=(t||0).toString(16),1===t.length?"0"+t:t}).join("")},toString:function(){return 0===this._rgba[3]?"transparent":this.toRgbaString()}}),h.fn.parse.prototype=h.fn,c.hsla.to=function(t){if(null==t[0]||null==t[1]||null==t[2])return[null,null,null,t[3]];var e,i,s=t[0]/255,n=t[1]/255,a=t[2]/255,o=t[3],r=Math.max(s,n,a),l=Math.min(s,n,a),h=r-l,c=r+l,u=.5*c;return e=l===r?0:s===r?60*(n-a)/h+360:n===r?60*(a-s)/h+120:60*(s-n)/h+240,i=0===h?0:.5>=u?h/c:h/(2-c),[Math.round(e)%360,i,u,null==o?1:o]},c.hsla.from=function(t){if(null==t[0]||null==t[1]||null==t[2])return[null,null,null,t[3]];var e=t[0]/360,i=t[1],s=t[2],a=t[3],o=.5>=s?s*(1+i):s+i-s*i,r=2*s-o;return[Math.round(255*n(r,o,e+1/3)),Math.round(255*n(r,o,e)),Math.round(255*n(r,o,e-1/3)),a]},f(c,function(s,n){var a=n.props,o=n.cache,l=n.to,c=n.from;h.fn[s]=function(s){if(l&&!this[o]&&(this[o]=l(this._rgba)),s===e)return this[o].slice();var n,r=t.type(s),u="array"===r||"object"===r?s:arguments,d=this[o].slice();return f(a,function(t,e){var s=u["object"===r?t:e.idx];null==s&&(s=d[e.idx]),d[e.idx]=i(s,e)}),c?(n=h(c(d)),n[o]=d,n):h(d)},f(a,function(e,i){h.fn[e]||(h.fn[e]=function(n){var a,o=t.type(n),l="alpha"===e?this._hsla?"hsla":"rgba":s,h=this[l](),c=h[i.idx];return"undefined"===o?c:("function"===o&&(n=n.call(this,c),o=t.type(n)),null==n&&i.empty?this:("string"===o&&(a=r.exec(n),a&&(n=c+parseFloat(a[2])*("+"===a[1]?1:-1))),h[i.idx]=n,this[l](h)))})})}),h.hook=function(e){var i=e.split(" ");f(i,function(e,i){t.cssHooks[i]={set:function(e,n){var a,o,r="";if("transparent"!==n&&("string"!==t.type(n)||(a=s(n)))){if(n=h(a||n),!d.rgba&&1!==n._rgba[3]){for(o="backgroundColor"===i?e.parentNode:e;(""===r||"transparent"===r)&&o&&o.style;)try{r=t.css(o,"backgroundColor"),o=o.parentNode}catch(l){}n=n.blend(r&&"transparent"!==r?r:"_default")}n=n.toRgbaString()}try{e.style[i]=n}catch(l){}}},t.fx.step[i]=function(e){e.colorInit||(e.start=h(e.elem,i),e.end=h(e.end),e.colorInit=!0),t.cssHooks[i].set(e.elem,e.start.transition(e.end,e.pos))}})},h.hook(o),t.cssHooks.borderColor={expand:function(t){var e={};return f(["Top","Right","Bottom","Left"],function(i,s){e["border"+s+"Color"]=t}),e}},a=t.Color.names={aqua:"#00ffff",black:"#000000",blue:"#0000ff",fuchsia:"#ff00ff",gray:"#808080",green:"#008000",lime:"#00ff00",maroon:"#800000",navy:"#000080",olive:"#808000",purple:"#800080",red:"#ff0000",silver:"#c0c0c0",teal:"#008080",white:"#ffffff",yellow:"#ffff00",transparent:[null,null,null,0],_default:"#ffffff"}}(jQuery),function(){function i(e){var i,s,n=e.ownerDocument.defaultView?e.ownerDocument.defaultView.getComputedStyle(e,null):e.currentStyle,a={};if(n&&n.length&&n[0]&&n[n[0]])for(s=n.length;s--;)i=n[s],"string"==typeof n[i]&&(a[t.camelCase(i)]=n[i]);else for(i in n)"string"==typeof n[i]&&(a[i]=n[i]);return a}function s(e,i){var s,n,o={};for(s in i)n=i[s],e[s]!==n&&(a[s]||(t.fx.step[s]||!isNaN(parseFloat(n)))&&(o[s]=n));return o}var n=["add","remove","toggle"],a={border:1,borderBottom:1,borderColor:1,borderLeft:1,borderRight:1,borderTop:1,borderWidth:1,margin:1,padding:1};t.each(["borderLeftStyle","borderRightStyle","borderBottomStyle","borderTopStyle"],function(e,i){t.fx.step[i]=function(t){("none"!==t.end&&!t.setAttr||1===t.pos&&!t.setAttr)&&(jQuery.style(t.elem,i,t.end),t.setAttr=!0)}}),t.fn.addBack||(t.fn.addBack=function(t){return this.add(null==t?this.prevObject:this.prevObject.filter(t))}),t.effects.animateClass=function(e,a,o,r){var l=t.speed(a,o,r);return this.queue(function(){var a,o=t(this),r=o.attr("class")||"",h=l.children?o.find("*").addBack():o;h=h.map(function(){var e=t(this);return{el:e,start:i(this)}}),a=function(){t.each(n,function(t,i){e[i]&&o[i+"Class"](e[i])})},a(),h=h.map(function(){return this.end=i(this.el[0]),this.diff=s(this.start,this.end),this}),o.attr("class",r),h=h.map(function(){var e=this,i=t.Deferred(),s=t.extend({},l,{queue:!1,complete:function(){i.resolve(e)}});return this.el.animate(this.diff,s),i.promise()}),t.when.apply(t,h.get()).done(function(){a(),t.each(arguments,function(){var e=this.el;t.each(this.diff,function(t){e.css(t,"")})}),l.complete.call(o[0])})})},t.fn.extend({addClass:function(e){return function(i,s,n,a){return s?t.effects.animateClass.call(this,{add:i},s,n,a):e.apply(this,arguments)}}(t.fn.addClass),removeClass:function(e){return function(i,s,n,a){return arguments.length>1?t.effects.animateClass.call(this,{remove:i},s,n,a):e.apply(this,arguments)}}(t.fn.removeClass),toggleClass:function(i){return function(s,n,a,o,r){return"boolean"==typeof n||n===e?a?t.effects.animateClass.call(this,n?{add:s}:{remove:s},a,o,r):i.apply(this,arguments):t.effects.animateClass.call(this,{toggle:s},n,a,o)}}(t.fn.toggleClass),switchClass:function(e,i,s,n,a){return t.effects.animateClass.call(this,{add:i,remove:e},s,n,a)}})}(),function(){function s(e,i,s,n){return t.isPlainObject(e)&&(i=e,e=e.effect),e={effect:e},null==i&&(i={}),t.isFunction(i)&&(n=i,s=null,i={}),("number"==typeof i||t.fx.speeds[i])&&(n=s,s=i,i={}),t.isFunction(s)&&(n=s,s=null),i&&t.extend(e,i),s=s||i.duration,e.duration=t.fx.off?0:"number"==typeof s?s:s in t.fx.speeds?t.fx.speeds[s]:t.fx.speeds._default,e.complete=n||i.complete,e}function n(e){return!e||"number"==typeof e||t.fx.speeds[e]?!0:"string"!=typeof e||t.effects.effect[e]?t.isFunction(e)?!0:"object"!=typeof e||e.effect?!1:!0:!0}t.extend(t.effects,{version:"1.10.4",save:function(t,e){for(var s=0;e.length>s;s++)null!==e[s]&&t.data(i+e[s],t[0].style[e[s]])},restore:function(t,s){var n,a;for(a=0;s.length>a;a++)null!==s[a]&&(n=t.data(i+s[a]),n===e&&(n=""),t.css(s[a],n))},setMode:function(t,e){return"toggle"===e&&(e=t.is(":hidden")?"show":"hide"),e},getBaseline:function(t,e){var i,s;switch(t[0]){case"top":i=0;break;case"middle":i=.5;break;case"bottom":i=1;break;default:i=t[0]/e.height}switch(t[1]){case"left":s=0;break;case"center":s=.5;break;case"right":s=1;break;default:s=t[1]/e.width}return{x:s,y:i}},createWrapper:function(e){if(e.parent().is(".ui-effects-wrapper"))return e.parent();var i={width:e.outerWidth(!0),height:e.outerHeight(!0),"float":e.css("float")},s=t("

    ").addClass("ui-effects-wrapper").css({fontSize:"100%",background:"transparent",border:"none",margin:0,padding:0}),n={width:e.width(),height:e.height()},a=document.activeElement;try{a.id}catch(o){a=document.body}return e.wrap(s),(e[0]===a||t.contains(e[0],a))&&t(a).focus(),s=e.parent(),"static"===e.css("position")?(s.css({position:"relative"}),e.css({position:"relative"})):(t.extend(i,{position:e.css("position"),zIndex:e.css("z-index")}),t.each(["top","left","bottom","right"],function(t,s){i[s]=e.css(s),isNaN(parseInt(i[s],10))&&(i[s]="auto")}),e.css({position:"relative",top:0,left:0,right:"auto",bottom:"auto"})),e.css(n),s.css(i).show()},removeWrapper:function(e){var i=document.activeElement;return e.parent().is(".ui-effects-wrapper")&&(e.parent().replaceWith(e),(e[0]===i||t.contains(e[0],i))&&t(i).focus()),e},setTransition:function(e,i,s,n){return n=n||{},t.each(i,function(t,i){var a=e.cssUnit(i);a[0]>0&&(n[i]=a[0]*s+a[1])}),n}}),t.fn.extend({effect:function(){function e(e){function s(){t.isFunction(a)&&a.call(n[0]),t.isFunction(e)&&e()}var n=t(this),a=i.complete,r=i.mode;(n.is(":hidden")?"hide"===r:"show"===r)?(n[r](),s()):o.call(n[0],i,s)}var i=s.apply(this,arguments),n=i.mode,a=i.queue,o=t.effects.effect[i.effect];return t.fx.off||!o?n?this[n](i.duration,i.complete):this.each(function(){i.complete&&i.complete.call(this)}):a===!1?this.each(e):this.queue(a||"fx",e)},show:function(t){return function(e){if(n(e))return t.apply(this,arguments);var i=s.apply(this,arguments);return i.mode="show",this.effect.call(this,i)}}(t.fn.show),hide:function(t){return function(e){if(n(e))return t.apply(this,arguments);var i=s.apply(this,arguments);return i.mode="hide",this.effect.call(this,i)}}(t.fn.hide),toggle:function(t){return function(e){if(n(e)||"boolean"==typeof e)return t.apply(this,arguments);var i=s.apply(this,arguments);return i.mode="toggle",this.effect.call(this,i)}}(t.fn.toggle),cssUnit:function(e){var i=this.css(e),s=[];return t.each(["em","px","%","pt"],function(t,e){i.indexOf(e)>0&&(s=[parseFloat(i),e])}),s}})}(),function(){var e={};t.each(["Quad","Cubic","Quart","Quint","Expo"],function(t,i){e[i]=function(e){return Math.pow(e,t+2)}}),t.extend(e,{Sine:function(t){return 1-Math.cos(t*Math.PI/2)},Circ:function(t){return 1-Math.sqrt(1-t*t)},Elastic:function(t){return 0===t||1===t?t:-Math.pow(2,8*(t-1))*Math.sin((80*(t-1)-7.5)*Math.PI/15)},Back:function(t){return t*t*(3*t-2)},Bounce:function(t){for(var e,i=4;((e=Math.pow(2,--i))-1)/11>t;);return 1/Math.pow(4,3-i)-7.5625*Math.pow((3*e-2)/22-t,2)}}),t.each(e,function(e,i){t.easing["easeIn"+e]=i,t.easing["easeOut"+e]=function(t){return 1-i(1-t)},t.easing["easeInOut"+e]=function(t){return.5>t?i(2*t)/2:1-i(-2*t+2)/2}})}()})(jQuery);(function(t){t.effects.effect.puff=function(e,i){var s=t(this),n=t.effects.setMode(s,e.mode||"hide"),a="hide"===n,o=parseInt(e.percent,10)||150,r=o/100,l={height:s.height(),width:s.width(),outerHeight:s.outerHeight(),outerWidth:s.outerWidth()};t.extend(e,{effect:"scale",queue:!1,fade:!0,mode:n,complete:i,percent:a?o:100,from:a?l:{height:l.height*r,width:l.width*r,outerHeight:l.outerHeight*r,outerWidth:l.outerWidth*r}}),s.effect(e)},t.effects.effect.scale=function(e,i){var s=t(this),n=t.extend(!0,{},e),a=t.effects.setMode(s,e.mode||"effect"),o=parseInt(e.percent,10)||(0===parseInt(e.percent,10)?0:"hide"===a?0:100),r=e.direction||"both",l=e.origin,h={height:s.height(),width:s.width(),outerHeight:s.outerHeight(),outerWidth:s.outerWidth()},c={y:"horizontal"!==r?o/100:1,x:"vertical"!==r?o/100:1};n.effect="size",n.queue=!1,n.complete=i,"effect"!==a&&(n.origin=l||["middle","center"],n.restore=!0),n.from=e.from||("show"===a?{height:0,width:0,outerHeight:0,outerWidth:0}:h),n.to={height:h.height*c.y,width:h.width*c.x,outerHeight:h.outerHeight*c.y,outerWidth:h.outerWidth*c.x},n.fade&&("show"===a&&(n.from.opacity=0,n.to.opacity=1),"hide"===a&&(n.from.opacity=1,n.to.opacity=0)),s.effect(n)},t.effects.effect.size=function(e,i){var s,n,a,o=t(this),r=["position","top","bottom","left","right","width","height","overflow","opacity"],l=["position","top","bottom","left","right","overflow","opacity"],h=["width","height","overflow"],c=["fontSize"],u=["borderTopWidth","borderBottomWidth","paddingTop","paddingBottom"],d=["borderLeftWidth","borderRightWidth","paddingLeft","paddingRight"],p=t.effects.setMode(o,e.mode||"effect"),f=e.restore||"effect"!==p,g=e.scale||"both",m=e.origin||["middle","center"],v=o.css("position"),_=f?r:l,b={height:0,width:0,outerHeight:0,outerWidth:0};"show"===p&&o.show(),s={height:o.height(),width:o.width(),outerHeight:o.outerHeight(),outerWidth:o.outerWidth()},"toggle"===e.mode&&"show"===p?(o.from=e.to||b,o.to=e.from||s):(o.from=e.from||("show"===p?b:s),o.to=e.to||("hide"===p?b:s)),a={from:{y:o.from.height/s.height,x:o.from.width/s.width},to:{y:o.to.height/s.height,x:o.to.width/s.width}},("box"===g||"both"===g)&&(a.from.y!==a.to.y&&(_=_.concat(u),o.from=t.effects.setTransition(o,u,a.from.y,o.from),o.to=t.effects.setTransition(o,u,a.to.y,o.to)),a.from.x!==a.to.x&&(_=_.concat(d),o.from=t.effects.setTransition(o,d,a.from.x,o.from),o.to=t.effects.setTransition(o,d,a.to.x,o.to))),("content"===g||"both"===g)&&a.from.y!==a.to.y&&(_=_.concat(c).concat(h),o.from=t.effects.setTransition(o,c,a.from.y,o.from),o.to=t.effects.setTransition(o,c,a.to.y,o.to)),t.effects.save(o,_),o.show(),t.effects.createWrapper(o),o.css("overflow","hidden").css(o.from),m&&(n=t.effects.getBaseline(m,s),o.from.top=(s.outerHeight-o.outerHeight())*n.y,o.from.left=(s.outerWidth-o.outerWidth())*n.x,o.to.top=(s.outerHeight-o.to.outerHeight)*n.y,o.to.left=(s.outerWidth-o.to.outerWidth)*n.x),o.css(o.from),("content"===g||"both"===g)&&(u=u.concat(["marginTop","marginBottom"]).concat(c),d=d.concat(["marginLeft","marginRight"]),h=r.concat(u).concat(d),o.find("*[width]").each(function(){var i=t(this),s={height:i.height(),width:i.width(),outerHeight:i.outerHeight(),outerWidth:i.outerWidth()};f&&t.effects.save(i,h),i.from={height:s.height*a.from.y,width:s.width*a.from.x,outerHeight:s.outerHeight*a.from.y,outerWidth:s.outerWidth*a.from.x},i.to={height:s.height*a.to.y,width:s.width*a.to.x,outerHeight:s.height*a.to.y,outerWidth:s.width*a.to.x},a.from.y!==a.to.y&&(i.from=t.effects.setTransition(i,u,a.from.y,i.from),i.to=t.effects.setTransition(i,u,a.to.y,i.to)),a.from.x!==a.to.x&&(i.from=t.effects.setTransition(i,d,a.from.x,i.from),i.to=t.effects.setTransition(i,d,a.to.x,i.to)),i.css(i.from),i.animate(i.to,e.duration,e.easing,function(){f&&t.effects.restore(i,h)})})),o.animate(o.to,{queue:!1,duration:e.duration,easing:e.easing,complete:function(){0===o.to.opacity&&o.css("opacity",o.from.opacity),"hide"===p&&o.hide(),t.effects.restore(o,_),f||("static"===v?o.css({position:"relative",top:o.to.top,left:o.to.left}):t.each(["top","left"],function(t,e){o.css(e,function(e,i){var s=parseInt(i,10),n=t?o.to.left:o.to.top;return"auto"===i?n+"px":s+n+"px"})})),t.effects.removeWrapper(o),i()}})}})(jQuery);(function(t){t.effects.effect.slide=function(e,i){var s,n=t(this),a=["position","top","bottom","left","right","width","height"],o=t.effects.setMode(n,e.mode||"show"),r="show"===o,l=e.direction||"left",h="up"===l||"down"===l?"top":"left",c="up"===l||"left"===l,u={};t.effects.save(n,a),n.show(),s=e.distance||n["top"===h?"outerHeight":"outerWidth"](!0),t.effects.createWrapper(n).css({overflow:"hidden"}),r&&n.css(h,c?isNaN(s)?"-"+s:-s:s),u[h]=(r?c?"+=":"-=":c?"-=":"+=")+s,n.animate(u,{queue:!1,duration:e.duration,easing:e.easing,complete:function(){"hide"===o&&n.hide(),t.effects.restore(n,a),t.effects.removeWrapper(n),i()}})}})(jQuery); \ No newline at end of file +(function(t){"function"==typeof define&&define.amd?define(["jquery"],t):t(jQuery)})(function(t){function e(t){for(var e=t.css("visibility");"inherit"===e;)t=t.parent(),e=t.css("visibility");return"hidden"!==e}function i(t){for(var e,i;t.length&&t[0]!==document;){if(e=t.css("position"),("absolute"===e||"relative"===e||"fixed"===e)&&(i=parseInt(t.css("zIndex"),10),!isNaN(i)&&0!==i))return i;t=t.parent()}return 0}function s(){this._curInst=null,this._keyEvent=!1,this._disabledInputs=[],this._datepickerShowing=!1,this._inDialog=!1,this._mainDivId="ui-datepicker-div",this._inlineClass="ui-datepicker-inline",this._appendClass="ui-datepicker-append",this._triggerClass="ui-datepicker-trigger",this._dialogClass="ui-datepicker-dialog",this._disableClass="ui-datepicker-disabled",this._unselectableClass="ui-datepicker-unselectable",this._currentClass="ui-datepicker-current-day",this._dayOverClass="ui-datepicker-days-cell-over",this.regional=[],this.regional[""]={closeText:"Done",prevText:"Prev",nextText:"Next",currentText:"Today",monthNames:["January","February","March","April","May","June","July","August","September","October","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],dayNames:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],dayNamesShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],dayNamesMin:["Su","Mo","Tu","We","Th","Fr","Sa"],weekHeader:"Wk",dateFormat:"mm/dd/yy",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},this._defaults={showOn:"focus",showAnim:"fadeIn",showOptions:{},defaultDate:null,appendText:"",buttonText:"...",buttonImage:"",buttonImageOnly:!1,hideIfNoPrevNext:!1,navigationAsDateFormat:!1,gotoCurrent:!1,changeMonth:!1,changeYear:!1,yearRange:"c-10:c+10",showOtherMonths:!1,selectOtherMonths:!1,showWeek:!1,calculateWeek:this.iso8601Week,shortYearCutoff:"+10",minDate:null,maxDate:null,duration:"fast",beforeShowDay:null,beforeShow:null,onSelect:null,onChangeMonthYear:null,onClose:null,numberOfMonths:1,showCurrentAtPos:0,stepMonths:1,stepBigMonths:12,altField:"",altFormat:"",constrainInput:!0,showButtonPanel:!1,autoSize:!1,disabled:!1},t.extend(this._defaults,this.regional[""]),this.regional.en=t.extend(!0,{},this.regional[""]),this.regional["en-US"]=t.extend(!0,{},this.regional.en),this.dpDiv=n(t("
    "))}function n(e){var i="button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a";return e.on("mouseout",i,function(){t(this).removeClass("ui-state-hover"),-1!==this.className.indexOf("ui-datepicker-prev")&&t(this).removeClass("ui-datepicker-prev-hover"),-1!==this.className.indexOf("ui-datepicker-next")&&t(this).removeClass("ui-datepicker-next-hover")}).on("mouseover",i,o)}function o(){t.datepicker._isDisabledDatepicker(p.inline?p.dpDiv.parent()[0]:p.input[0])||(t(this).parents(".ui-datepicker-calendar").find("a").removeClass("ui-state-hover"),t(this).addClass("ui-state-hover"),-1!==this.className.indexOf("ui-datepicker-prev")&&t(this).addClass("ui-datepicker-prev-hover"),-1!==this.className.indexOf("ui-datepicker-next")&&t(this).addClass("ui-datepicker-next-hover"))}function a(e,i){t.extend(e,i);for(var s in i)null==i[s]&&(e[s]=i[s]);return e}function r(t){return function(){var e=this.element.val();t.apply(this,arguments),this._refresh(),e!==this.element.val()&&this._trigger("change")}}t.ui=t.ui||{},t.ui.version="1.12.1";var h=0,l=Array.prototype.slice;t.cleanData=function(e){return function(i){var s,n,o;for(o=0;null!=(n=i[o]);o++)try{s=t._data(n,"events"),s&&s.remove&&t(n).triggerHandler("remove")}catch(a){}e(i)}}(t.cleanData),t.widget=function(e,i,s){var n,o,a,r={},h=e.split(".")[0];e=e.split(".")[1];var l=h+"-"+e;return s||(s=i,i=t.Widget),t.isArray(s)&&(s=t.extend.apply(null,[{}].concat(s))),t.expr[":"][l.toLowerCase()]=function(e){return!!t.data(e,l)},t[h]=t[h]||{},n=t[h][e],o=t[h][e]=function(t,e){return this._createWidget?(arguments.length&&this._createWidget(t,e),void 0):new o(t,e)},t.extend(o,n,{version:s.version,_proto:t.extend({},s),_childConstructors:[]}),a=new i,a.options=t.widget.extend({},a.options),t.each(s,function(e,s){return t.isFunction(s)?(r[e]=function(){function t(){return i.prototype[e].apply(this,arguments)}function n(t){return i.prototype[e].apply(this,t)}return function(){var e,i=this._super,o=this._superApply;return this._super=t,this._superApply=n,e=s.apply(this,arguments),this._super=i,this._superApply=o,e}}(),void 0):(r[e]=s,void 0)}),o.prototype=t.widget.extend(a,{widgetEventPrefix:n?a.widgetEventPrefix||e:e},r,{constructor:o,namespace:h,widgetName:e,widgetFullName:l}),n?(t.each(n._childConstructors,function(e,i){var s=i.prototype;t.widget(s.namespace+"."+s.widgetName,o,i._proto)}),delete n._childConstructors):i._childConstructors.push(o),t.widget.bridge(e,o),o},t.widget.extend=function(e){for(var i,s,n=l.call(arguments,1),o=0,a=n.length;a>o;o++)for(i in n[o])s=n[o][i],n[o].hasOwnProperty(i)&&void 0!==s&&(e[i]=t.isPlainObject(s)?t.isPlainObject(e[i])?t.widget.extend({},e[i],s):t.widget.extend({},s):s);return e},t.widget.bridge=function(e,i){var s=i.prototype.widgetFullName||e;t.fn[e]=function(n){var o="string"==typeof n,a=l.call(arguments,1),r=this;return o?this.length||"instance"!==n?this.each(function(){var i,o=t.data(this,s);return"instance"===n?(r=o,!1):o?t.isFunction(o[n])&&"_"!==n.charAt(0)?(i=o[n].apply(o,a),i!==o&&void 0!==i?(r=i&&i.jquery?r.pushStack(i.get()):i,!1):void 0):t.error("no such method '"+n+"' for "+e+" widget instance"):t.error("cannot call methods on "+e+" prior to initialization; "+"attempted to call method '"+n+"'")}):r=void 0:(a.length&&(n=t.widget.extend.apply(null,[n].concat(a))),this.each(function(){var e=t.data(this,s);e?(e.option(n||{}),e._init&&e._init()):t.data(this,s,new i(n,this))})),r}},t.Widget=function(){},t.Widget._childConstructors=[],t.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",defaultElement:"
    ",options:{classes:{},disabled:!1,create:null},_createWidget:function(e,i){i=t(i||this.defaultElement||this)[0],this.element=t(i),this.uuid=h++,this.eventNamespace="."+this.widgetName+this.uuid,this.bindings=t(),this.hoverable=t(),this.focusable=t(),this.classesElementLookup={},i!==this&&(t.data(i,this.widgetFullName,this),this._on(!0,this.element,{remove:function(t){t.target===i&&this.destroy()}}),this.document=t(i.style?i.ownerDocument:i.document||i),this.window=t(this.document[0].defaultView||this.document[0].parentWindow)),this.options=t.widget.extend({},this.options,this._getCreateOptions(),e),this._create(),this.options.disabled&&this._setOptionDisabled(this.options.disabled),this._trigger("create",null,this._getCreateEventData()),this._init()},_getCreateOptions:function(){return{}},_getCreateEventData:t.noop,_create:t.noop,_init:t.noop,destroy:function(){var e=this;this._destroy(),t.each(this.classesElementLookup,function(t,i){e._removeClass(i,t)}),this.element.off(this.eventNamespace).removeData(this.widgetFullName),this.widget().off(this.eventNamespace).removeAttr("aria-disabled"),this.bindings.off(this.eventNamespace)},_destroy:t.noop,widget:function(){return this.element},option:function(e,i){var s,n,o,a=e;if(0===arguments.length)return t.widget.extend({},this.options);if("string"==typeof e)if(a={},s=e.split("."),e=s.shift(),s.length){for(n=a[e]=t.widget.extend({},this.options[e]),o=0;s.length-1>o;o++)n[s[o]]=n[s[o]]||{},n=n[s[o]];if(e=s.pop(),1===arguments.length)return void 0===n[e]?null:n[e];n[e]=i}else{if(1===arguments.length)return void 0===this.options[e]?null:this.options[e];a[e]=i}return this._setOptions(a),this},_setOptions:function(t){var e;for(e in t)this._setOption(e,t[e]);return this},_setOption:function(t,e){return"classes"===t&&this._setOptionClasses(e),this.options[t]=e,"disabled"===t&&this._setOptionDisabled(e),this},_setOptionClasses:function(e){var i,s,n;for(i in e)n=this.classesElementLookup[i],e[i]!==this.options.classes[i]&&n&&n.length&&(s=t(n.get()),this._removeClass(n,i),s.addClass(this._classes({element:s,keys:i,classes:e,add:!0})))},_setOptionDisabled:function(t){this._toggleClass(this.widget(),this.widgetFullName+"-disabled",null,!!t),t&&(this._removeClass(this.hoverable,null,"ui-state-hover"),this._removeClass(this.focusable,null,"ui-state-focus"))},enable:function(){return this._setOptions({disabled:!1})},disable:function(){return this._setOptions({disabled:!0})},_classes:function(e){function i(i,o){var a,r;for(r=0;i.length>r;r++)a=n.classesElementLookup[i[r]]||t(),a=e.add?t(t.unique(a.get().concat(e.element.get()))):t(a.not(e.element).get()),n.classesElementLookup[i[r]]=a,s.push(i[r]),o&&e.classes[i[r]]&&s.push(e.classes[i[r]])}var s=[],n=this;return e=t.extend({element:this.element,classes:this.options.classes||{}},e),this._on(e.element,{remove:"_untrackClassesElement"}),e.keys&&i(e.keys.match(/\S+/g)||[],!0),e.extra&&i(e.extra.match(/\S+/g)||[]),s.join(" ")},_untrackClassesElement:function(e){var i=this;t.each(i.classesElementLookup,function(s,n){-1!==t.inArray(e.target,n)&&(i.classesElementLookup[s]=t(n.not(e.target).get()))})},_removeClass:function(t,e,i){return this._toggleClass(t,e,i,!1)},_addClass:function(t,e,i){return this._toggleClass(t,e,i,!0)},_toggleClass:function(t,e,i,s){s="boolean"==typeof s?s:i;var n="string"==typeof t||null===t,o={extra:n?e:i,keys:n?t:e,element:n?this.element:t,add:s};return o.element.toggleClass(this._classes(o),s),this},_on:function(e,i,s){var n,o=this;"boolean"!=typeof e&&(s=i,i=e,e=!1),s?(i=n=t(i),this.bindings=this.bindings.add(i)):(s=i,i=this.element,n=this.widget()),t.each(s,function(s,a){function r(){return e||o.options.disabled!==!0&&!t(this).hasClass("ui-state-disabled")?("string"==typeof a?o[a]:a).apply(o,arguments):void 0}"string"!=typeof a&&(r.guid=a.guid=a.guid||r.guid||t.guid++);var h=s.match(/^([\w:-]*)\s*(.*)$/),l=h[1]+o.eventNamespace,c=h[2];c?n.on(l,c,r):i.on(l,r)})},_off:function(e,i){i=(i||"").split(" ").join(this.eventNamespace+" ")+this.eventNamespace,e.off(i).off(i),this.bindings=t(this.bindings.not(e).get()),this.focusable=t(this.focusable.not(e).get()),this.hoverable=t(this.hoverable.not(e).get())},_delay:function(t,e){function i(){return("string"==typeof t?s[t]:t).apply(s,arguments)}var s=this;return setTimeout(i,e||0)},_hoverable:function(e){this.hoverable=this.hoverable.add(e),this._on(e,{mouseenter:function(e){this._addClass(t(e.currentTarget),null,"ui-state-hover")},mouseleave:function(e){this._removeClass(t(e.currentTarget),null,"ui-state-hover")}})},_focusable:function(e){this.focusable=this.focusable.add(e),this._on(e,{focusin:function(e){this._addClass(t(e.currentTarget),null,"ui-state-focus")},focusout:function(e){this._removeClass(t(e.currentTarget),null,"ui-state-focus")}})},_trigger:function(e,i,s){var n,o,a=this.options[e];if(s=s||{},i=t.Event(i),i.type=(e===this.widgetEventPrefix?e:this.widgetEventPrefix+e).toLowerCase(),i.target=this.element[0],o=i.originalEvent)for(n in o)n in i||(i[n]=o[n]);return this.element.trigger(i,s),!(t.isFunction(a)&&a.apply(this.element[0],[i].concat(s))===!1||i.isDefaultPrevented())}},t.each({show:"fadeIn",hide:"fadeOut"},function(e,i){t.Widget.prototype["_"+e]=function(s,n,o){"string"==typeof n&&(n={effect:n});var a,r=n?n===!0||"number"==typeof n?i:n.effect||i:e;n=n||{},"number"==typeof n&&(n={duration:n}),a=!t.isEmptyObject(n),n.complete=o,n.delay&&s.delay(n.delay),a&&t.effects&&t.effects.effect[r]?s[e](n):r!==e&&s[r]?s[r](n.duration,n.easing,o):s.queue(function(i){t(this)[e](),o&&o.call(s[0]),i()})}}),t.widget,function(){function e(t,e,i){return[parseFloat(t[0])*(u.test(t[0])?e/100:1),parseFloat(t[1])*(u.test(t[1])?i/100:1)]}function i(e,i){return parseInt(t.css(e,i),10)||0}function s(e){var i=e[0];return 9===i.nodeType?{width:e.width(),height:e.height(),offset:{top:0,left:0}}:t.isWindow(i)?{width:e.width(),height:e.height(),offset:{top:e.scrollTop(),left:e.scrollLeft()}}:i.preventDefault?{width:0,height:0,offset:{top:i.pageY,left:i.pageX}}:{width:e.outerWidth(),height:e.outerHeight(),offset:e.offset()}}var n,o=Math.max,a=Math.abs,r=/left|center|right/,h=/top|center|bottom/,l=/[\+\-]\d+(\.[\d]+)?%?/,c=/^\w+/,u=/%$/,d=t.fn.position;t.position={scrollbarWidth:function(){if(void 0!==n)return n;var e,i,s=t("
    "),o=s.children()[0];return t("body").append(s),e=o.offsetWidth,s.css("overflow","scroll"),i=o.offsetWidth,e===i&&(i=s[0].clientWidth),s.remove(),n=e-i},getScrollInfo:function(e){var i=e.isWindow||e.isDocument?"":e.element.css("overflow-x"),s=e.isWindow||e.isDocument?"":e.element.css("overflow-y"),n="scroll"===i||"auto"===i&&e.widthi?"left":e>0?"right":"center",vertical:0>r?"top":s>0?"bottom":"middle"};l>p&&p>a(e+i)&&(u.horizontal="center"),c>f&&f>a(s+r)&&(u.vertical="middle"),u.important=o(a(e),a(i))>o(a(s),a(r))?"horizontal":"vertical",n.using.call(this,t,u)}),h.offset(t.extend(D,{using:r}))})},t.ui.position={fit:{left:function(t,e){var i,s=e.within,n=s.isWindow?s.scrollLeft:s.offset.left,a=s.width,r=t.left-e.collisionPosition.marginLeft,h=n-r,l=r+e.collisionWidth-a-n;e.collisionWidth>a?h>0&&0>=l?(i=t.left+h+e.collisionWidth-a-n,t.left+=h-i):t.left=l>0&&0>=h?n:h>l?n+a-e.collisionWidth:n:h>0?t.left+=h:l>0?t.left-=l:t.left=o(t.left-r,t.left)},top:function(t,e){var i,s=e.within,n=s.isWindow?s.scrollTop:s.offset.top,a=e.within.height,r=t.top-e.collisionPosition.marginTop,h=n-r,l=r+e.collisionHeight-a-n;e.collisionHeight>a?h>0&&0>=l?(i=t.top+h+e.collisionHeight-a-n,t.top+=h-i):t.top=l>0&&0>=h?n:h>l?n+a-e.collisionHeight:n:h>0?t.top+=h:l>0?t.top-=l:t.top=o(t.top-r,t.top)}},flip:{left:function(t,e){var i,s,n=e.within,o=n.offset.left+n.scrollLeft,r=n.width,h=n.isWindow?n.scrollLeft:n.offset.left,l=t.left-e.collisionPosition.marginLeft,c=l-h,u=l+e.collisionWidth-r-h,d="left"===e.my[0]?-e.elemWidth:"right"===e.my[0]?e.elemWidth:0,p="left"===e.at[0]?e.targetWidth:"right"===e.at[0]?-e.targetWidth:0,f=-2*e.offset[0];0>c?(i=t.left+d+p+f+e.collisionWidth-r-o,(0>i||a(c)>i)&&(t.left+=d+p+f)):u>0&&(s=t.left-e.collisionPosition.marginLeft+d+p+f-h,(s>0||u>a(s))&&(t.left+=d+p+f))},top:function(t,e){var i,s,n=e.within,o=n.offset.top+n.scrollTop,r=n.height,h=n.isWindow?n.scrollTop:n.offset.top,l=t.top-e.collisionPosition.marginTop,c=l-h,u=l+e.collisionHeight-r-h,d="top"===e.my[1],p=d?-e.elemHeight:"bottom"===e.my[1]?e.elemHeight:0,f="top"===e.at[1]?e.targetHeight:"bottom"===e.at[1]?-e.targetHeight:0,g=-2*e.offset[1];0>c?(s=t.top+p+f+g+e.collisionHeight-r-o,(0>s||a(c)>s)&&(t.top+=p+f+g)):u>0&&(i=t.top-e.collisionPosition.marginTop+p+f+g-h,(i>0||u>a(i))&&(t.top+=p+f+g))}},flipfit:{left:function(){t.ui.position.flip.left.apply(this,arguments),t.ui.position.fit.left.apply(this,arguments)},top:function(){t.ui.position.flip.top.apply(this,arguments),t.ui.position.fit.top.apply(this,arguments)}}}}(),t.ui.position,t.extend(t.expr[":"],{data:t.expr.createPseudo?t.expr.createPseudo(function(e){return function(i){return!!t.data(i,e)}}):function(e,i,s){return!!t.data(e,s[3])}}),t.fn.extend({disableSelection:function(){var t="onselectstart"in document.createElement("div")?"selectstart":"mousedown";return function(){return this.on(t+".ui-disableSelection",function(t){t.preventDefault()})}}(),enableSelection:function(){return this.off(".ui-disableSelection")}}),t.ui.focusable=function(i,s){var n,o,a,r,h,l=i.nodeName.toLowerCase();return"area"===l?(n=i.parentNode,o=n.name,i.href&&o&&"map"===n.nodeName.toLowerCase()?(a=t("img[usemap='#"+o+"']"),a.length>0&&a.is(":visible")):!1):(/^(input|select|textarea|button|object)$/.test(l)?(r=!i.disabled,r&&(h=t(i).closest("fieldset")[0],h&&(r=!h.disabled))):r="a"===l?i.href||s:s,r&&t(i).is(":visible")&&e(t(i)))},t.extend(t.expr[":"],{focusable:function(e){return t.ui.focusable(e,null!=t.attr(e,"tabindex"))}}),t.ui.focusable,t.fn.form=function(){return"string"==typeof this[0].form?this.closest("form"):t(this[0].form)},t.ui.formResetMixin={_formResetHandler:function(){var e=t(this);setTimeout(function(){var i=e.data("ui-form-reset-instances");t.each(i,function(){this.refresh()})})},_bindFormResetHandler:function(){if(this.form=this.element.form(),this.form.length){var t=this.form.data("ui-form-reset-instances")||[];t.length||this.form.on("reset.ui-form-reset",this._formResetHandler),t.push(this),this.form.data("ui-form-reset-instances",t)}},_unbindFormResetHandler:function(){if(this.form.length){var e=this.form.data("ui-form-reset-instances");e.splice(t.inArray(this,e),1),e.length?this.form.data("ui-form-reset-instances",e):this.form.removeData("ui-form-reset-instances").off("reset.ui-form-reset")}}},"1.7"===t.fn.jquery.substring(0,3)&&(t.each(["Width","Height"],function(e,i){function s(e,i,s,o){return t.each(n,function(){i-=parseFloat(t.css(e,"padding"+this))||0,s&&(i-=parseFloat(t.css(e,"border"+this+"Width"))||0),o&&(i-=parseFloat(t.css(e,"margin"+this))||0)}),i}var n="Width"===i?["Left","Right"]:["Top","Bottom"],o=i.toLowerCase(),a={innerWidth:t.fn.innerWidth,innerHeight:t.fn.innerHeight,outerWidth:t.fn.outerWidth,outerHeight:t.fn.outerHeight};t.fn["inner"+i]=function(e){return void 0===e?a["inner"+i].call(this):this.each(function(){t(this).css(o,s(this,e)+"px")})},t.fn["outer"+i]=function(e,n){return"number"!=typeof e?a["outer"+i].call(this,e):this.each(function(){t(this).css(o,s(this,e,!0,n)+"px")})}}),t.fn.addBack=function(t){return this.add(null==t?this.prevObject:this.prevObject.filter(t))}),t.ui.keyCode={BACKSPACE:8,COMMA:188,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,LEFT:37,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SPACE:32,TAB:9,UP:38},t.ui.escapeSelector=function(){var t=/([!"#$%&'()*+,./:;<=>?@[\]^`{|}~])/g;return function(e){return e.replace(t,"\\$1")}}(),t.fn.labels=function(){var e,i,s,n,o;return this[0].labels&&this[0].labels.length?this.pushStack(this[0].labels):(n=this.eq(0).parents("label"),s=this.attr("id"),s&&(e=this.eq(0).parents().last(),o=e.add(e.length?e.siblings():this.siblings()),i="label[for='"+t.ui.escapeSelector(s)+"']",n=n.add(o.find(i).addBack(i))),this.pushStack(n))},t.fn.scrollParent=function(e){var i=this.css("position"),s="absolute"===i,n=e?/(auto|scroll|hidden)/:/(auto|scroll)/,o=this.parents().filter(function(){var e=t(this);return s&&"static"===e.css("position")?!1:n.test(e.css("overflow")+e.css("overflow-y")+e.css("overflow-x"))}).eq(0);return"fixed"!==i&&o.length?o:t(this[0].ownerDocument||document)},t.extend(t.expr[":"],{tabbable:function(e){var i=t.attr(e,"tabindex"),s=null!=i;return(!s||i>=0)&&t.ui.focusable(e,s)}}),t.fn.extend({uniqueId:function(){var t=0;return function(){return this.each(function(){this.id||(this.id="ui-id-"+ ++t)})}}(),removeUniqueId:function(){return this.each(function(){/^ui-id-\d+$/.test(this.id)&&t(this).removeAttr("id")})}}),t.ui.ie=!!/msie [\w.]+/.exec(navigator.userAgent.toLowerCase());var c=!1;t(document).on("mouseup",function(){c=!1}),t.widget("ui.mouse",{version:"1.12.1",options:{cancel:"input, textarea, button, select, option",distance:1,delay:0},_mouseInit:function(){var e=this;this.element.on("mousedown."+this.widgetName,function(t){return e._mouseDown(t)}).on("click."+this.widgetName,function(i){return!0===t.data(i.target,e.widgetName+".preventClickEvent")?(t.removeData(i.target,e.widgetName+".preventClickEvent"),i.stopImmediatePropagation(),!1):void 0}),this.started=!1},_mouseDestroy:function(){this.element.off("."+this.widgetName),this._mouseMoveDelegate&&this.document.off("mousemove."+this.widgetName,this._mouseMoveDelegate).off("mouseup."+this.widgetName,this._mouseUpDelegate)},_mouseDown:function(e){if(!c){this._mouseMoved=!1,this._mouseStarted&&this._mouseUp(e),this._mouseDownEvent=e;var i=this,s=1===e.which,n="string"==typeof this.options.cancel&&e.target.nodeName?t(e.target).closest(this.options.cancel).length:!1;return s&&!n&&this._mouseCapture(e)?(this.mouseDelayMet=!this.options.delay,this.mouseDelayMet||(this._mouseDelayTimer=setTimeout(function(){i.mouseDelayMet=!0},this.options.delay)),this._mouseDistanceMet(e)&&this._mouseDelayMet(e)&&(this._mouseStarted=this._mouseStart(e)!==!1,!this._mouseStarted)?(e.preventDefault(),!0):(!0===t.data(e.target,this.widgetName+".preventClickEvent")&&t.removeData(e.target,this.widgetName+".preventClickEvent"),this._mouseMoveDelegate=function(t){return i._mouseMove(t)},this._mouseUpDelegate=function(t){return i._mouseUp(t)},this.document.on("mousemove."+this.widgetName,this._mouseMoveDelegate).on("mouseup."+this.widgetName,this._mouseUpDelegate),e.preventDefault(),c=!0,!0)):!0}},_mouseMove:function(e){if(this._mouseMoved){if(t.ui.ie&&(!document.documentMode||9>document.documentMode)&&!e.button)return this._mouseUp(e);if(!e.which)if(e.originalEvent.altKey||e.originalEvent.ctrlKey||e.originalEvent.metaKey||e.originalEvent.shiftKey)this.ignoreMissingWhich=!0;else if(!this.ignoreMissingWhich)return this._mouseUp(e)}return(e.which||e.button)&&(this._mouseMoved=!0),this._mouseStarted?(this._mouseDrag(e),e.preventDefault()):(this._mouseDistanceMet(e)&&this._mouseDelayMet(e)&&(this._mouseStarted=this._mouseStart(this._mouseDownEvent,e)!==!1,this._mouseStarted?this._mouseDrag(e):this._mouseUp(e)),!this._mouseStarted)},_mouseUp:function(e){this.document.off("mousemove."+this.widgetName,this._mouseMoveDelegate).off("mouseup."+this.widgetName,this._mouseUpDelegate),this._mouseStarted&&(this._mouseStarted=!1,e.target===this._mouseDownEvent.target&&t.data(e.target,this.widgetName+".preventClickEvent",!0),this._mouseStop(e)),this._mouseDelayTimer&&(clearTimeout(this._mouseDelayTimer),delete this._mouseDelayTimer),this.ignoreMissingWhich=!1,c=!1,e.preventDefault()},_mouseDistanceMet:function(t){return Math.max(Math.abs(this._mouseDownEvent.pageX-t.pageX),Math.abs(this._mouseDownEvent.pageY-t.pageY))>=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return!0}}),t.ui.plugin={add:function(e,i,s){var n,o=t.ui[e].prototype;for(n in s)o.plugins[n]=o.plugins[n]||[],o.plugins[n].push([i,s[n]])},call:function(t,e,i,s){var n,o=t.plugins[e];if(o&&(s||t.element[0].parentNode&&11!==t.element[0].parentNode.nodeType))for(n=0;o.length>n;n++)t.options[o[n][0]]&&o[n][1].apply(t.element,i)}},t.ui.safeActiveElement=function(t){var e;try{e=t.activeElement}catch(i){e=t.body}return e||(e=t.body),e.nodeName||(e=t.body),e},t.ui.safeBlur=function(e){e&&"body"!==e.nodeName.toLowerCase()&&t(e).trigger("blur")},t.widget("ui.draggable",t.ui.mouse,{version:"1.12.1",widgetEventPrefix:"drag",options:{addClasses:!0,appendTo:"parent",axis:!1,connectToSortable:!1,containment:!1,cursor:"auto",cursorAt:!1,grid:!1,handle:!1,helper:"original",iframeFix:!1,opacity:!1,refreshPositions:!1,revert:!1,revertDuration:500,scope:"default",scroll:!0,scrollSensitivity:20,scrollSpeed:20,snap:!1,snapMode:"both",snapTolerance:20,stack:!1,zIndex:!1,drag:null,start:null,stop:null},_create:function(){"original"===this.options.helper&&this._setPositionRelative(),this.options.addClasses&&this._addClass("ui-draggable"),this._setHandleClassName(),this._mouseInit()},_setOption:function(t,e){this._super(t,e),"handle"===t&&(this._removeHandleClassName(),this._setHandleClassName())},_destroy:function(){return(this.helper||this.element).is(".ui-draggable-dragging")?(this.destroyOnClear=!0,void 0):(this._removeHandleClassName(),this._mouseDestroy(),void 0)},_mouseCapture:function(e){var i=this.options;return this.helper||i.disabled||t(e.target).closest(".ui-resizable-handle").length>0?!1:(this.handle=this._getHandle(e),this.handle?(this._blurActiveElement(e),this._blockFrames(i.iframeFix===!0?"iframe":i.iframeFix),!0):!1)},_blockFrames:function(e){this.iframeBlocks=this.document.find(e).map(function(){var e=t(this);return t("
    ").css("position","absolute").appendTo(e.parent()).outerWidth(e.outerWidth()).outerHeight(e.outerHeight()).offset(e.offset())[0]})},_unblockFrames:function(){this.iframeBlocks&&(this.iframeBlocks.remove(),delete this.iframeBlocks)},_blurActiveElement:function(e){var i=t.ui.safeActiveElement(this.document[0]),s=t(e.target);s.closest(i).length||t.ui.safeBlur(i)},_mouseStart:function(e){var i=this.options;return this.helper=this._createHelper(e),this._addClass(this.helper,"ui-draggable-dragging"),this._cacheHelperProportions(),t.ui.ddmanager&&(t.ui.ddmanager.current=this),this._cacheMargins(),this.cssPosition=this.helper.css("position"),this.scrollParent=this.helper.scrollParent(!0),this.offsetParent=this.helper.offsetParent(),this.hasFixedAncestor=this.helper.parents().filter(function(){return"fixed"===t(this).css("position")}).length>0,this.positionAbs=this.element.offset(),this._refreshOffsets(e),this.originalPosition=this.position=this._generatePosition(e,!1),this.originalPageX=e.pageX,this.originalPageY=e.pageY,i.cursorAt&&this._adjustOffsetFromHelper(i.cursorAt),this._setContainment(),this._trigger("start",e)===!1?(this._clear(),!1):(this._cacheHelperProportions(),t.ui.ddmanager&&!i.dropBehaviour&&t.ui.ddmanager.prepareOffsets(this,e),this._mouseDrag(e,!0),t.ui.ddmanager&&t.ui.ddmanager.dragStart(this,e),!0)},_refreshOffsets:function(t){this.offset={top:this.positionAbs.top-this.margins.top,left:this.positionAbs.left-this.margins.left,scroll:!1,parent:this._getParentOffset(),relative:this._getRelativeOffset()},this.offset.click={left:t.pageX-this.offset.left,top:t.pageY-this.offset.top}},_mouseDrag:function(e,i){if(this.hasFixedAncestor&&(this.offset.parent=this._getParentOffset()),this.position=this._generatePosition(e,!0),this.positionAbs=this._convertPositionTo("absolute"),!i){var s=this._uiHash();if(this._trigger("drag",e,s)===!1)return this._mouseUp(new t.Event("mouseup",e)),!1;this.position=s.position}return this.helper[0].style.left=this.position.left+"px",this.helper[0].style.top=this.position.top+"px",t.ui.ddmanager&&t.ui.ddmanager.drag(this,e),!1},_mouseStop:function(e){var i=this,s=!1;return t.ui.ddmanager&&!this.options.dropBehaviour&&(s=t.ui.ddmanager.drop(this,e)),this.dropped&&(s=this.dropped,this.dropped=!1),"invalid"===this.options.revert&&!s||"valid"===this.options.revert&&s||this.options.revert===!0||t.isFunction(this.options.revert)&&this.options.revert.call(this.element,s)?t(this.helper).animate(this.originalPosition,parseInt(this.options.revertDuration,10),function(){i._trigger("stop",e)!==!1&&i._clear()}):this._trigger("stop",e)!==!1&&this._clear(),!1},_mouseUp:function(e){return this._unblockFrames(),t.ui.ddmanager&&t.ui.ddmanager.dragStop(this,e),this.handleElement.is(e.target)&&this.element.trigger("focus"),t.ui.mouse.prototype._mouseUp.call(this,e)},cancel:function(){return this.helper.is(".ui-draggable-dragging")?this._mouseUp(new t.Event("mouseup",{target:this.element[0]})):this._clear(),this},_getHandle:function(e){return this.options.handle?!!t(e.target).closest(this.element.find(this.options.handle)).length:!0},_setHandleClassName:function(){this.handleElement=this.options.handle?this.element.find(this.options.handle):this.element,this._addClass(this.handleElement,"ui-draggable-handle")},_removeHandleClassName:function(){this._removeClass(this.handleElement,"ui-draggable-handle")},_createHelper:function(e){var i=this.options,s=t.isFunction(i.helper),n=s?t(i.helper.apply(this.element[0],[e])):"clone"===i.helper?this.element.clone().removeAttr("id"):this.element;return n.parents("body").length||n.appendTo("parent"===i.appendTo?this.element[0].parentNode:i.appendTo),s&&n[0]===this.element[0]&&this._setPositionRelative(),n[0]===this.element[0]||/(fixed|absolute)/.test(n.css("position"))||n.css("position","absolute"),n},_setPositionRelative:function(){/^(?:r|a|f)/.test(this.element.css("position"))||(this.element[0].style.position="relative")},_adjustOffsetFromHelper:function(e){"string"==typeof e&&(e=e.split(" ")),t.isArray(e)&&(e={left:+e[0],top:+e[1]||0}),"left"in e&&(this.offset.click.left=e.left+this.margins.left),"right"in e&&(this.offset.click.left=this.helperProportions.width-e.right+this.margins.left),"top"in e&&(this.offset.click.top=e.top+this.margins.top),"bottom"in e&&(this.offset.click.top=this.helperProportions.height-e.bottom+this.margins.top)},_isRootNode:function(t){return/(html|body)/i.test(t.tagName)||t===this.document[0]},_getParentOffset:function(){var e=this.offsetParent.offset(),i=this.document[0];return"absolute"===this.cssPosition&&this.scrollParent[0]!==i&&t.contains(this.scrollParent[0],this.offsetParent[0])&&(e.left+=this.scrollParent.scrollLeft(),e.top+=this.scrollParent.scrollTop()),this._isRootNode(this.offsetParent[0])&&(e={top:0,left:0}),{top:e.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:e.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if("relative"!==this.cssPosition)return{top:0,left:0};var t=this.element.position(),e=this._isRootNode(this.scrollParent[0]);return{top:t.top-(parseInt(this.helper.css("top"),10)||0)+(e?0:this.scrollParent.scrollTop()),left:t.left-(parseInt(this.helper.css("left"),10)||0)+(e?0:this.scrollParent.scrollLeft())}},_cacheMargins:function(){this.margins={left:parseInt(this.element.css("marginLeft"),10)||0,top:parseInt(this.element.css("marginTop"),10)||0,right:parseInt(this.element.css("marginRight"),10)||0,bottom:parseInt(this.element.css("marginBottom"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var e,i,s,n=this.options,o=this.document[0];return this.relativeContainer=null,n.containment?"window"===n.containment?(this.containment=[t(window).scrollLeft()-this.offset.relative.left-this.offset.parent.left,t(window).scrollTop()-this.offset.relative.top-this.offset.parent.top,t(window).scrollLeft()+t(window).width()-this.helperProportions.width-this.margins.left,t(window).scrollTop()+(t(window).height()||o.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top],void 0):"document"===n.containment?(this.containment=[0,0,t(o).width()-this.helperProportions.width-this.margins.left,(t(o).height()||o.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top],void 0):n.containment.constructor===Array?(this.containment=n.containment,void 0):("parent"===n.containment&&(n.containment=this.helper[0].parentNode),i=t(n.containment),s=i[0],s&&(e=/(scroll|auto)/.test(i.css("overflow")),this.containment=[(parseInt(i.css("borderLeftWidth"),10)||0)+(parseInt(i.css("paddingLeft"),10)||0),(parseInt(i.css("borderTopWidth"),10)||0)+(parseInt(i.css("paddingTop"),10)||0),(e?Math.max(s.scrollWidth,s.offsetWidth):s.offsetWidth)-(parseInt(i.css("borderRightWidth"),10)||0)-(parseInt(i.css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left-this.margins.right,(e?Math.max(s.scrollHeight,s.offsetHeight):s.offsetHeight)-(parseInt(i.css("borderBottomWidth"),10)||0)-(parseInt(i.css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top-this.margins.bottom],this.relativeContainer=i),void 0):(this.containment=null,void 0) +},_convertPositionTo:function(t,e){e||(e=this.position);var i="absolute"===t?1:-1,s=this._isRootNode(this.scrollParent[0]);return{top:e.top+this.offset.relative.top*i+this.offset.parent.top*i-("fixed"===this.cssPosition?-this.offset.scroll.top:s?0:this.offset.scroll.top)*i,left:e.left+this.offset.relative.left*i+this.offset.parent.left*i-("fixed"===this.cssPosition?-this.offset.scroll.left:s?0:this.offset.scroll.left)*i}},_generatePosition:function(t,e){var i,s,n,o,a=this.options,r=this._isRootNode(this.scrollParent[0]),h=t.pageX,l=t.pageY;return r&&this.offset.scroll||(this.offset.scroll={top:this.scrollParent.scrollTop(),left:this.scrollParent.scrollLeft()}),e&&(this.containment&&(this.relativeContainer?(s=this.relativeContainer.offset(),i=[this.containment[0]+s.left,this.containment[1]+s.top,this.containment[2]+s.left,this.containment[3]+s.top]):i=this.containment,t.pageX-this.offset.click.lefti[2]&&(h=i[2]+this.offset.click.left),t.pageY-this.offset.click.top>i[3]&&(l=i[3]+this.offset.click.top)),a.grid&&(n=a.grid[1]?this.originalPageY+Math.round((l-this.originalPageY)/a.grid[1])*a.grid[1]:this.originalPageY,l=i?n-this.offset.click.top>=i[1]||n-this.offset.click.top>i[3]?n:n-this.offset.click.top>=i[1]?n-a.grid[1]:n+a.grid[1]:n,o=a.grid[0]?this.originalPageX+Math.round((h-this.originalPageX)/a.grid[0])*a.grid[0]:this.originalPageX,h=i?o-this.offset.click.left>=i[0]||o-this.offset.click.left>i[2]?o:o-this.offset.click.left>=i[0]?o-a.grid[0]:o+a.grid[0]:o),"y"===a.axis&&(h=this.originalPageX),"x"===a.axis&&(l=this.originalPageY)),{top:l-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+("fixed"===this.cssPosition?-this.offset.scroll.top:r?0:this.offset.scroll.top),left:h-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+("fixed"===this.cssPosition?-this.offset.scroll.left:r?0:this.offset.scroll.left)}},_clear:function(){this._removeClass(this.helper,"ui-draggable-dragging"),this.helper[0]===this.element[0]||this.cancelHelperRemoval||this.helper.remove(),this.helper=null,this.cancelHelperRemoval=!1,this.destroyOnClear&&this.destroy()},_trigger:function(e,i,s){return s=s||this._uiHash(),t.ui.plugin.call(this,e,[i,s,this],!0),/^(drag|start|stop)/.test(e)&&(this.positionAbs=this._convertPositionTo("absolute"),s.offset=this.positionAbs),t.Widget.prototype._trigger.call(this,e,i,s)},plugins:{},_uiHash:function(){return{helper:this.helper,position:this.position,originalPosition:this.originalPosition,offset:this.positionAbs}}}),t.ui.plugin.add("draggable","connectToSortable",{start:function(e,i,s){var n=t.extend({},i,{item:s.element});s.sortables=[],t(s.options.connectToSortable).each(function(){var i=t(this).sortable("instance");i&&!i.options.disabled&&(s.sortables.push(i),i.refreshPositions(),i._trigger("activate",e,n))})},stop:function(e,i,s){var n=t.extend({},i,{item:s.element});s.cancelHelperRemoval=!1,t.each(s.sortables,function(){var t=this;t.isOver?(t.isOver=0,s.cancelHelperRemoval=!0,t.cancelHelperRemoval=!1,t._storedCSS={position:t.placeholder.css("position"),top:t.placeholder.css("top"),left:t.placeholder.css("left")},t._mouseStop(e),t.options.helper=t.options._helper):(t.cancelHelperRemoval=!0,t._trigger("deactivate",e,n))})},drag:function(e,i,s){t.each(s.sortables,function(){var n=!1,o=this;o.positionAbs=s.positionAbs,o.helperProportions=s.helperProportions,o.offset.click=s.offset.click,o._intersectsWith(o.containerCache)&&(n=!0,t.each(s.sortables,function(){return this.positionAbs=s.positionAbs,this.helperProportions=s.helperProportions,this.offset.click=s.offset.click,this!==o&&this._intersectsWith(this.containerCache)&&t.contains(o.element[0],this.element[0])&&(n=!1),n})),n?(o.isOver||(o.isOver=1,s._parent=i.helper.parent(),o.currentItem=i.helper.appendTo(o.element).data("ui-sortable-item",!0),o.options._helper=o.options.helper,o.options.helper=function(){return i.helper[0]},e.target=o.currentItem[0],o._mouseCapture(e,!0),o._mouseStart(e,!0,!0),o.offset.click.top=s.offset.click.top,o.offset.click.left=s.offset.click.left,o.offset.parent.left-=s.offset.parent.left-o.offset.parent.left,o.offset.parent.top-=s.offset.parent.top-o.offset.parent.top,s._trigger("toSortable",e),s.dropped=o.element,t.each(s.sortables,function(){this.refreshPositions()}),s.currentItem=s.element,o.fromOutside=s),o.currentItem&&(o._mouseDrag(e),i.position=o.position)):o.isOver&&(o.isOver=0,o.cancelHelperRemoval=!0,o.options._revert=o.options.revert,o.options.revert=!1,o._trigger("out",e,o._uiHash(o)),o._mouseStop(e,!0),o.options.revert=o.options._revert,o.options.helper=o.options._helper,o.placeholder&&o.placeholder.remove(),i.helper.appendTo(s._parent),s._refreshOffsets(e),i.position=s._generatePosition(e,!0),s._trigger("fromSortable",e),s.dropped=!1,t.each(s.sortables,function(){this.refreshPositions()}))})}}),t.ui.plugin.add("draggable","cursor",{start:function(e,i,s){var n=t("body"),o=s.options;n.css("cursor")&&(o._cursor=n.css("cursor")),n.css("cursor",o.cursor)},stop:function(e,i,s){var n=s.options;n._cursor&&t("body").css("cursor",n._cursor)}}),t.ui.plugin.add("draggable","opacity",{start:function(e,i,s){var n=t(i.helper),o=s.options;n.css("opacity")&&(o._opacity=n.css("opacity")),n.css("opacity",o.opacity)},stop:function(e,i,s){var n=s.options;n._opacity&&t(i.helper).css("opacity",n._opacity)}}),t.ui.plugin.add("draggable","scroll",{start:function(t,e,i){i.scrollParentNotHidden||(i.scrollParentNotHidden=i.helper.scrollParent(!1)),i.scrollParentNotHidden[0]!==i.document[0]&&"HTML"!==i.scrollParentNotHidden[0].tagName&&(i.overflowOffset=i.scrollParentNotHidden.offset())},drag:function(e,i,s){var n=s.options,o=!1,a=s.scrollParentNotHidden[0],r=s.document[0];a!==r&&"HTML"!==a.tagName?(n.axis&&"x"===n.axis||(s.overflowOffset.top+a.offsetHeight-e.pageY=0;d--)h=s.snapElements[d].left-s.margins.left,l=h+s.snapElements[d].width,c=s.snapElements[d].top-s.margins.top,u=c+s.snapElements[d].height,h-g>_||m>l+g||c-g>b||v>u+g||!t.contains(s.snapElements[d].item.ownerDocument,s.snapElements[d].item)?(s.snapElements[d].snapping&&s.options.snap.release&&s.options.snap.release.call(s.element,e,t.extend(s._uiHash(),{snapItem:s.snapElements[d].item})),s.snapElements[d].snapping=!1):("inner"!==f.snapMode&&(n=g>=Math.abs(c-b),o=g>=Math.abs(u-v),a=g>=Math.abs(h-_),r=g>=Math.abs(l-m),n&&(i.position.top=s._convertPositionTo("relative",{top:c-s.helperProportions.height,left:0}).top),o&&(i.position.top=s._convertPositionTo("relative",{top:u,left:0}).top),a&&(i.position.left=s._convertPositionTo("relative",{top:0,left:h-s.helperProportions.width}).left),r&&(i.position.left=s._convertPositionTo("relative",{top:0,left:l}).left)),p=n||o||a||r,"outer"!==f.snapMode&&(n=g>=Math.abs(c-v),o=g>=Math.abs(u-b),a=g>=Math.abs(h-m),r=g>=Math.abs(l-_),n&&(i.position.top=s._convertPositionTo("relative",{top:c,left:0}).top),o&&(i.position.top=s._convertPositionTo("relative",{top:u-s.helperProportions.height,left:0}).top),a&&(i.position.left=s._convertPositionTo("relative",{top:0,left:h}).left),r&&(i.position.left=s._convertPositionTo("relative",{top:0,left:l-s.helperProportions.width}).left)),!s.snapElements[d].snapping&&(n||o||a||r||p)&&s.options.snap.snap&&s.options.snap.snap.call(s.element,e,t.extend(s._uiHash(),{snapItem:s.snapElements[d].item})),s.snapElements[d].snapping=n||o||a||r||p)}}),t.ui.plugin.add("draggable","stack",{start:function(e,i,s){var n,o=s.options,a=t.makeArray(t(o.stack)).sort(function(e,i){return(parseInt(t(e).css("zIndex"),10)||0)-(parseInt(t(i).css("zIndex"),10)||0)});a.length&&(n=parseInt(t(a[0]).css("zIndex"),10)||0,t(a).each(function(e){t(this).css("zIndex",n+e)}),this.css("zIndex",n+a.length))}}),t.ui.plugin.add("draggable","zIndex",{start:function(e,i,s){var n=t(i.helper),o=s.options;n.css("zIndex")&&(o._zIndex=n.css("zIndex")),n.css("zIndex",o.zIndex)},stop:function(e,i,s){var n=s.options;n._zIndex&&t(i.helper).css("zIndex",n._zIndex)}}),t.ui.draggable,t.widget("ui.droppable",{version:"1.12.1",widgetEventPrefix:"drop",options:{accept:"*",addClasses:!0,greedy:!1,scope:"default",tolerance:"intersect",activate:null,deactivate:null,drop:null,out:null,over:null},_create:function(){var e,i=this.options,s=i.accept;this.isover=!1,this.isout=!0,this.accept=t.isFunction(s)?s:function(t){return t.is(s)},this.proportions=function(){return arguments.length?(e=arguments[0],void 0):e?e:e={width:this.element[0].offsetWidth,height:this.element[0].offsetHeight}},this._addToManager(i.scope),i.addClasses&&this._addClass("ui-droppable")},_addToManager:function(e){t.ui.ddmanager.droppables[e]=t.ui.ddmanager.droppables[e]||[],t.ui.ddmanager.droppables[e].push(this)},_splice:function(t){for(var e=0;t.length>e;e++)t[e]===this&&t.splice(e,1)},_destroy:function(){var e=t.ui.ddmanager.droppables[this.options.scope];this._splice(e)},_setOption:function(e,i){if("accept"===e)this.accept=t.isFunction(i)?i:function(t){return t.is(i)};else if("scope"===e){var s=t.ui.ddmanager.droppables[this.options.scope];this._splice(s),this._addToManager(i)}this._super(e,i)},_activate:function(e){var i=t.ui.ddmanager.current;this._addActiveClass(),i&&this._trigger("activate",e,this.ui(i))},_deactivate:function(e){var i=t.ui.ddmanager.current;this._removeActiveClass(),i&&this._trigger("deactivate",e,this.ui(i))},_over:function(e){var i=t.ui.ddmanager.current;i&&(i.currentItem||i.element)[0]!==this.element[0]&&this.accept.call(this.element[0],i.currentItem||i.element)&&(this._addHoverClass(),this._trigger("over",e,this.ui(i)))},_out:function(e){var i=t.ui.ddmanager.current;i&&(i.currentItem||i.element)[0]!==this.element[0]&&this.accept.call(this.element[0],i.currentItem||i.element)&&(this._removeHoverClass(),this._trigger("out",e,this.ui(i)))},_drop:function(e,i){var s=i||t.ui.ddmanager.current,n=!1;return s&&(s.currentItem||s.element)[0]!==this.element[0]?(this.element.find(":data(ui-droppable)").not(".ui-draggable-dragging").each(function(){var i=t(this).droppable("instance");return i.options.greedy&&!i.options.disabled&&i.options.scope===s.options.scope&&i.accept.call(i.element[0],s.currentItem||s.element)&&u(s,t.extend(i,{offset:i.element.offset()}),i.options.tolerance,e)?(n=!0,!1):void 0}),n?!1:this.accept.call(this.element[0],s.currentItem||s.element)?(this._removeActiveClass(),this._removeHoverClass(),this._trigger("drop",e,this.ui(s)),this.element):!1):!1},ui:function(t){return{draggable:t.currentItem||t.element,helper:t.helper,position:t.position,offset:t.positionAbs}},_addHoverClass:function(){this._addClass("ui-droppable-hover")},_removeHoverClass:function(){this._removeClass("ui-droppable-hover")},_addActiveClass:function(){this._addClass("ui-droppable-active")},_removeActiveClass:function(){this._removeClass("ui-droppable-active")}});var u=t.ui.intersect=function(){function t(t,e,i){return t>=e&&e+i>t}return function(e,i,s,n){if(!i.offset)return!1;var o=(e.positionAbs||e.position.absolute).left+e.margins.left,a=(e.positionAbs||e.position.absolute).top+e.margins.top,r=o+e.helperProportions.width,h=a+e.helperProportions.height,l=i.offset.left,c=i.offset.top,u=l+i.proportions().width,d=c+i.proportions().height;switch(s){case"fit":return o>=l&&u>=r&&a>=c&&d>=h;case"intersect":return o+e.helperProportions.width/2>l&&u>r-e.helperProportions.width/2&&a+e.helperProportions.height/2>c&&d>h-e.helperProportions.height/2;case"pointer":return t(n.pageY,c,i.proportions().height)&&t(n.pageX,l,i.proportions().width);case"touch":return(a>=c&&d>=a||h>=c&&d>=h||c>a&&h>d)&&(o>=l&&u>=o||r>=l&&u>=r||l>o&&r>u);default:return!1}}}();t.ui.ddmanager={current:null,droppables:{"default":[]},prepareOffsets:function(e,i){var s,n,o=t.ui.ddmanager.droppables[e.options.scope]||[],a=i?i.type:null,r=(e.currentItem||e.element).find(":data(ui-droppable)").addBack();t:for(s=0;o.length>s;s++)if(!(o[s].options.disabled||e&&!o[s].accept.call(o[s].element[0],e.currentItem||e.element))){for(n=0;r.length>n;n++)if(r[n]===o[s].element[0]){o[s].proportions().height=0;continue t}o[s].visible="none"!==o[s].element.css("display"),o[s].visible&&("mousedown"===a&&o[s]._activate.call(o[s],i),o[s].offset=o[s].element.offset(),o[s].proportions({width:o[s].element[0].offsetWidth,height:o[s].element[0].offsetHeight}))}},drop:function(e,i){var s=!1;return t.each((t.ui.ddmanager.droppables[e.options.scope]||[]).slice(),function(){this.options&&(!this.options.disabled&&this.visible&&u(e,this,this.options.tolerance,i)&&(s=this._drop.call(this,i)||s),!this.options.disabled&&this.visible&&this.accept.call(this.element[0],e.currentItem||e.element)&&(this.isout=!0,this.isover=!1,this._deactivate.call(this,i)))}),s},dragStart:function(e,i){e.element.parentsUntil("body").on("scroll.droppable",function(){e.options.refreshPositions||t.ui.ddmanager.prepareOffsets(e,i)})},drag:function(e,i){e.options.refreshPositions&&t.ui.ddmanager.prepareOffsets(e,i),t.each(t.ui.ddmanager.droppables[e.options.scope]||[],function(){if(!this.options.disabled&&!this.greedyChild&&this.visible){var s,n,o,a=u(e,this,this.options.tolerance,i),r=!a&&this.isover?"isout":a&&!this.isover?"isover":null;r&&(this.options.greedy&&(n=this.options.scope,o=this.element.parents(":data(ui-droppable)").filter(function(){return t(this).droppable("instance").options.scope===n}),o.length&&(s=t(o[0]).droppable("instance"),s.greedyChild="isover"===r)),s&&"isover"===r&&(s.isover=!1,s.isout=!0,s._out.call(s,i)),this[r]=!0,this["isout"===r?"isover":"isout"]=!1,this["isover"===r?"_over":"_out"].call(this,i),s&&"isout"===r&&(s.isout=!1,s.isover=!0,s._over.call(s,i)))}})},dragStop:function(e,i){e.element.parentsUntil("body").off("scroll.droppable"),e.options.refreshPositions||t.ui.ddmanager.prepareOffsets(e,i)}},t.uiBackCompat!==!1&&t.widget("ui.droppable",t.ui.droppable,{options:{hoverClass:!1,activeClass:!1},_addActiveClass:function(){this._super(),this.options.activeClass&&this.element.addClass(this.options.activeClass)},_removeActiveClass:function(){this._super(),this.options.activeClass&&this.element.removeClass(this.options.activeClass)},_addHoverClass:function(){this._super(),this.options.hoverClass&&this.element.addClass(this.options.hoverClass)},_removeHoverClass:function(){this._super(),this.options.hoverClass&&this.element.removeClass(this.options.hoverClass)}}),t.ui.droppable,t.widget("ui.resizable",t.ui.mouse,{version:"1.12.1",widgetEventPrefix:"resize",options:{alsoResize:!1,animate:!1,animateDuration:"slow",animateEasing:"swing",aspectRatio:!1,autoHide:!1,classes:{"ui-resizable-se":"ui-icon ui-icon-gripsmall-diagonal-se"},containment:!1,ghost:!1,grid:!1,handles:"e,s,se",helper:!1,maxHeight:null,maxWidth:null,minHeight:10,minWidth:10,zIndex:90,resize:null,start:null,stop:null},_num:function(t){return parseFloat(t)||0},_isNumber:function(t){return!isNaN(parseFloat(t))},_hasScroll:function(e,i){if("hidden"===t(e).css("overflow"))return!1;var s=i&&"left"===i?"scrollLeft":"scrollTop",n=!1;return e[s]>0?!0:(e[s]=1,n=e[s]>0,e[s]=0,n)},_create:function(){var e,i=this.options,s=this;this._addClass("ui-resizable"),t.extend(this,{_aspectRatio:!!i.aspectRatio,aspectRatio:i.aspectRatio,originalElement:this.element,_proportionallyResizeElements:[],_helper:i.helper||i.ghost||i.animate?i.helper||"ui-resizable-helper":null}),this.element[0].nodeName.match(/^(canvas|textarea|input|select|button|img)$/i)&&(this.element.wrap(t("
    ").css({position:this.element.css("position"),width:this.element.outerWidth(),height:this.element.outerHeight(),top:this.element.css("top"),left:this.element.css("left")})),this.element=this.element.parent().data("ui-resizable",this.element.resizable("instance")),this.elementIsWrapper=!0,e={marginTop:this.originalElement.css("marginTop"),marginRight:this.originalElement.css("marginRight"),marginBottom:this.originalElement.css("marginBottom"),marginLeft:this.originalElement.css("marginLeft")},this.element.css(e),this.originalElement.css("margin",0),this.originalResizeStyle=this.originalElement.css("resize"),this.originalElement.css("resize","none"),this._proportionallyResizeElements.push(this.originalElement.css({position:"static",zoom:1,display:"block"})),this.originalElement.css(e),this._proportionallyResize()),this._setupHandles(),i.autoHide&&t(this.element).on("mouseenter",function(){i.disabled||(s._removeClass("ui-resizable-autohide"),s._handles.show())}).on("mouseleave",function(){i.disabled||s.resizing||(s._addClass("ui-resizable-autohide"),s._handles.hide())}),this._mouseInit()},_destroy:function(){this._mouseDestroy();var e,i=function(e){t(e).removeData("resizable").removeData("ui-resizable").off(".resizable").find(".ui-resizable-handle").remove()};return this.elementIsWrapper&&(i(this.element),e=this.element,this.originalElement.css({position:e.css("position"),width:e.outerWidth(),height:e.outerHeight(),top:e.css("top"),left:e.css("left")}).insertAfter(e),e.remove()),this.originalElement.css("resize",this.originalResizeStyle),i(this.originalElement),this},_setOption:function(t,e){switch(this._super(t,e),t){case"handles":this._removeHandles(),this._setupHandles();break;default:}},_setupHandles:function(){var e,i,s,n,o,a=this.options,r=this;if(this.handles=a.handles||(t(".ui-resizable-handle",this.element).length?{n:".ui-resizable-n",e:".ui-resizable-e",s:".ui-resizable-s",w:".ui-resizable-w",se:".ui-resizable-se",sw:".ui-resizable-sw",ne:".ui-resizable-ne",nw:".ui-resizable-nw"}:"e,s,se"),this._handles=t(),this.handles.constructor===String)for("all"===this.handles&&(this.handles="n,e,s,w,se,sw,ne,nw"),s=this.handles.split(","),this.handles={},i=0;s.length>i;i++)e=t.trim(s[i]),n="ui-resizable-"+e,o=t("
    "),this._addClass(o,"ui-resizable-handle "+n),o.css({zIndex:a.zIndex}),this.handles[e]=".ui-resizable-"+e,this.element.append(o);this._renderAxis=function(e){var i,s,n,o;e=e||this.element;for(i in this.handles)this.handles[i].constructor===String?this.handles[i]=this.element.children(this.handles[i]).first().show():(this.handles[i].jquery||this.handles[i].nodeType)&&(this.handles[i]=t(this.handles[i]),this._on(this.handles[i],{mousedown:r._mouseDown})),this.elementIsWrapper&&this.originalElement[0].nodeName.match(/^(textarea|input|select|button)$/i)&&(s=t(this.handles[i],this.element),o=/sw|ne|nw|se|n|s/.test(i)?s.outerHeight():s.outerWidth(),n=["padding",/ne|nw|n/.test(i)?"Top":/se|sw|s/.test(i)?"Bottom":/^e$/.test(i)?"Right":"Left"].join(""),e.css(n,o),this._proportionallyResize()),this._handles=this._handles.add(this.handles[i])},this._renderAxis(this.element),this._handles=this._handles.add(this.element.find(".ui-resizable-handle")),this._handles.disableSelection(),this._handles.on("mouseover",function(){r.resizing||(this.className&&(o=this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i)),r.axis=o&&o[1]?o[1]:"se")}),a.autoHide&&(this._handles.hide(),this._addClass("ui-resizable-autohide"))},_removeHandles:function(){this._handles.remove()},_mouseCapture:function(e){var i,s,n=!1;for(i in this.handles)s=t(this.handles[i])[0],(s===e.target||t.contains(s,e.target))&&(n=!0);return!this.options.disabled&&n},_mouseStart:function(e){var i,s,n,o=this.options,a=this.element;return this.resizing=!0,this._renderProxy(),i=this._num(this.helper.css("left")),s=this._num(this.helper.css("top")),o.containment&&(i+=t(o.containment).scrollLeft()||0,s+=t(o.containment).scrollTop()||0),this.offset=this.helper.offset(),this.position={left:i,top:s},this.size=this._helper?{width:this.helper.width(),height:this.helper.height()}:{width:a.width(),height:a.height()},this.originalSize=this._helper?{width:a.outerWidth(),height:a.outerHeight()}:{width:a.width(),height:a.height()},this.sizeDiff={width:a.outerWidth()-a.width(),height:a.outerHeight()-a.height()},this.originalPosition={left:i,top:s},this.originalMousePosition={left:e.pageX,top:e.pageY},this.aspectRatio="number"==typeof o.aspectRatio?o.aspectRatio:this.originalSize.width/this.originalSize.height||1,n=t(".ui-resizable-"+this.axis).css("cursor"),t("body").css("cursor","auto"===n?this.axis+"-resize":n),this._addClass("ui-resizable-resizing"),this._propagate("start",e),!0},_mouseDrag:function(e){var i,s,n=this.originalMousePosition,o=this.axis,a=e.pageX-n.left||0,r=e.pageY-n.top||0,h=this._change[o];return this._updatePrevProperties(),h?(i=h.apply(this,[e,a,r]),this._updateVirtualBoundaries(e.shiftKey),(this._aspectRatio||e.shiftKey)&&(i=this._updateRatio(i,e)),i=this._respectSize(i,e),this._updateCache(i),this._propagate("resize",e),s=this._applyChanges(),!this._helper&&this._proportionallyResizeElements.length&&this._proportionallyResize(),t.isEmptyObject(s)||(this._updatePrevProperties(),this._trigger("resize",e,this.ui()),this._applyChanges()),!1):!1},_mouseStop:function(e){this.resizing=!1;var i,s,n,o,a,r,h,l=this.options,c=this;return this._helper&&(i=this._proportionallyResizeElements,s=i.length&&/textarea/i.test(i[0].nodeName),n=s&&this._hasScroll(i[0],"left")?0:c.sizeDiff.height,o=s?0:c.sizeDiff.width,a={width:c.helper.width()-o,height:c.helper.height()-n},r=parseFloat(c.element.css("left"))+(c.position.left-c.originalPosition.left)||null,h=parseFloat(c.element.css("top"))+(c.position.top-c.originalPosition.top)||null,l.animate||this.element.css(t.extend(a,{top:h,left:r})),c.helper.height(c.size.height),c.helper.width(c.size.width),this._helper&&!l.animate&&this._proportionallyResize()),t("body").css("cursor","auto"),this._removeClass("ui-resizable-resizing"),this._propagate("stop",e),this._helper&&this.helper.remove(),!1},_updatePrevProperties:function(){this.prevPosition={top:this.position.top,left:this.position.left},this.prevSize={width:this.size.width,height:this.size.height}},_applyChanges:function(){var t={};return this.position.top!==this.prevPosition.top&&(t.top=this.position.top+"px"),this.position.left!==this.prevPosition.left&&(t.left=this.position.left+"px"),this.size.width!==this.prevSize.width&&(t.width=this.size.width+"px"),this.size.height!==this.prevSize.height&&(t.height=this.size.height+"px"),this.helper.css(t),t},_updateVirtualBoundaries:function(t){var e,i,s,n,o,a=this.options;o={minWidth:this._isNumber(a.minWidth)?a.minWidth:0,maxWidth:this._isNumber(a.maxWidth)?a.maxWidth:1/0,minHeight:this._isNumber(a.minHeight)?a.minHeight:0,maxHeight:this._isNumber(a.maxHeight)?a.maxHeight:1/0},(this._aspectRatio||t)&&(e=o.minHeight*this.aspectRatio,s=o.minWidth/this.aspectRatio,i=o.maxHeight*this.aspectRatio,n=o.maxWidth/this.aspectRatio,e>o.minWidth&&(o.minWidth=e),s>o.minHeight&&(o.minHeight=s),o.maxWidth>i&&(o.maxWidth=i),o.maxHeight>n&&(o.maxHeight=n)),this._vBoundaries=o},_updateCache:function(t){this.offset=this.helper.offset(),this._isNumber(t.left)&&(this.position.left=t.left),this._isNumber(t.top)&&(this.position.top=t.top),this._isNumber(t.height)&&(this.size.height=t.height),this._isNumber(t.width)&&(this.size.width=t.width)},_updateRatio:function(t){var e=this.position,i=this.size,s=this.axis;return this._isNumber(t.height)?t.width=t.height*this.aspectRatio:this._isNumber(t.width)&&(t.height=t.width/this.aspectRatio),"sw"===s&&(t.left=e.left+(i.width-t.width),t.top=null),"nw"===s&&(t.top=e.top+(i.height-t.height),t.left=e.left+(i.width-t.width)),t},_respectSize:function(t){var e=this._vBoundaries,i=this.axis,s=this._isNumber(t.width)&&e.maxWidth&&e.maxWidtht.width,a=this._isNumber(t.height)&&e.minHeight&&e.minHeight>t.height,r=this.originalPosition.left+this.originalSize.width,h=this.originalPosition.top+this.originalSize.height,l=/sw|nw|w/.test(i),c=/nw|ne|n/.test(i);return o&&(t.width=e.minWidth),a&&(t.height=e.minHeight),s&&(t.width=e.maxWidth),n&&(t.height=e.maxHeight),o&&l&&(t.left=r-e.minWidth),s&&l&&(t.left=r-e.maxWidth),a&&c&&(t.top=h-e.minHeight),n&&c&&(t.top=h-e.maxHeight),t.width||t.height||t.left||!t.top?t.width||t.height||t.top||!t.left||(t.left=null):t.top=null,t},_getPaddingPlusBorderDimensions:function(t){for(var e=0,i=[],s=[t.css("borderTopWidth"),t.css("borderRightWidth"),t.css("borderBottomWidth"),t.css("borderLeftWidth")],n=[t.css("paddingTop"),t.css("paddingRight"),t.css("paddingBottom"),t.css("paddingLeft")];4>e;e++)i[e]=parseFloat(s[e])||0,i[e]+=parseFloat(n[e])||0;return{height:i[0]+i[2],width:i[1]+i[3]}},_proportionallyResize:function(){if(this._proportionallyResizeElements.length)for(var t,e=0,i=this.helper||this.element;this._proportionallyResizeElements.length>e;e++)t=this._proportionallyResizeElements[e],this.outerDimensions||(this.outerDimensions=this._getPaddingPlusBorderDimensions(t)),t.css({height:i.height()-this.outerDimensions.height||0,width:i.width()-this.outerDimensions.width||0})},_renderProxy:function(){var e=this.element,i=this.options;this.elementOffset=e.offset(),this._helper?(this.helper=this.helper||t("
    "),this._addClass(this.helper,this._helper),this.helper.css({width:this.element.outerWidth(),height:this.element.outerHeight(),position:"absolute",left:this.elementOffset.left+"px",top:this.elementOffset.top+"px",zIndex:++i.zIndex}),this.helper.appendTo("body").disableSelection()):this.helper=this.element},_change:{e:function(t,e){return{width:this.originalSize.width+e}},w:function(t,e){var i=this.originalSize,s=this.originalPosition;return{left:s.left+e,width:i.width-e}},n:function(t,e,i){var s=this.originalSize,n=this.originalPosition;return{top:n.top+i,height:s.height-i}},s:function(t,e,i){return{height:this.originalSize.height+i}},se:function(e,i,s){return t.extend(this._change.s.apply(this,arguments),this._change.e.apply(this,[e,i,s]))},sw:function(e,i,s){return t.extend(this._change.s.apply(this,arguments),this._change.w.apply(this,[e,i,s]))},ne:function(e,i,s){return t.extend(this._change.n.apply(this,arguments),this._change.e.apply(this,[e,i,s]))},nw:function(e,i,s){return t.extend(this._change.n.apply(this,arguments),this._change.w.apply(this,[e,i,s]))}},_propagate:function(e,i){t.ui.plugin.call(this,e,[i,this.ui()]),"resize"!==e&&this._trigger(e,i,this.ui())},plugins:{},ui:function(){return{originalElement:this.originalElement,element:this.element,helper:this.helper,position:this.position,size:this.size,originalSize:this.originalSize,originalPosition:this.originalPosition}}}),t.ui.plugin.add("resizable","animate",{stop:function(e){var i=t(this).resizable("instance"),s=i.options,n=i._proportionallyResizeElements,o=n.length&&/textarea/i.test(n[0].nodeName),a=o&&i._hasScroll(n[0],"left")?0:i.sizeDiff.height,r=o?0:i.sizeDiff.width,h={width:i.size.width-r,height:i.size.height-a},l=parseFloat(i.element.css("left"))+(i.position.left-i.originalPosition.left)||null,c=parseFloat(i.element.css("top"))+(i.position.top-i.originalPosition.top)||null;i.element.animate(t.extend(h,c&&l?{top:c,left:l}:{}),{duration:s.animateDuration,easing:s.animateEasing,step:function(){var s={width:parseFloat(i.element.css("width")),height:parseFloat(i.element.css("height")),top:parseFloat(i.element.css("top")),left:parseFloat(i.element.css("left"))};n&&n.length&&t(n[0]).css({width:s.width,height:s.height}),i._updateCache(s),i._propagate("resize",e)}})}}),t.ui.plugin.add("resizable","containment",{start:function(){var e,i,s,n,o,a,r,h=t(this).resizable("instance"),l=h.options,c=h.element,u=l.containment,d=u instanceof t?u.get(0):/parent/.test(u)?c.parent().get(0):u;d&&(h.containerElement=t(d),/document/.test(u)||u===document?(h.containerOffset={left:0,top:0},h.containerPosition={left:0,top:0},h.parentData={element:t(document),left:0,top:0,width:t(document).width(),height:t(document).height()||document.body.parentNode.scrollHeight}):(e=t(d),i=[],t(["Top","Right","Left","Bottom"]).each(function(t,s){i[t]=h._num(e.css("padding"+s))}),h.containerOffset=e.offset(),h.containerPosition=e.position(),h.containerSize={height:e.innerHeight()-i[3],width:e.innerWidth()-i[1]},s=h.containerOffset,n=h.containerSize.height,o=h.containerSize.width,a=h._hasScroll(d,"left")?d.scrollWidth:o,r=h._hasScroll(d)?d.scrollHeight:n,h.parentData={element:d,left:s.left,top:s.top,width:a,height:r}))},resize:function(e){var i,s,n,o,a=t(this).resizable("instance"),r=a.options,h=a.containerOffset,l=a.position,c=a._aspectRatio||e.shiftKey,u={top:0,left:0},d=a.containerElement,p=!0;d[0]!==document&&/static/.test(d.css("position"))&&(u=h),l.left<(a._helper?h.left:0)&&(a.size.width=a.size.width+(a._helper?a.position.left-h.left:a.position.left-u.left),c&&(a.size.height=a.size.width/a.aspectRatio,p=!1),a.position.left=r.helper?h.left:0),l.top<(a._helper?h.top:0)&&(a.size.height=a.size.height+(a._helper?a.position.top-h.top:a.position.top),c&&(a.size.width=a.size.height*a.aspectRatio,p=!1),a.position.top=a._helper?h.top:0),n=a.containerElement.get(0)===a.element.parent().get(0),o=/relative|absolute/.test(a.containerElement.css("position")),n&&o?(a.offset.left=a.parentData.left+a.position.left,a.offset.top=a.parentData.top+a.position.top):(a.offset.left=a.element.offset().left,a.offset.top=a.element.offset().top),i=Math.abs(a.sizeDiff.width+(a._helper?a.offset.left-u.left:a.offset.left-h.left)),s=Math.abs(a.sizeDiff.height+(a._helper?a.offset.top-u.top:a.offset.top-h.top)),i+a.size.width>=a.parentData.width&&(a.size.width=a.parentData.width-i,c&&(a.size.height=a.size.width/a.aspectRatio,p=!1)),s+a.size.height>=a.parentData.height&&(a.size.height=a.parentData.height-s,c&&(a.size.width=a.size.height*a.aspectRatio,p=!1)),p||(a.position.left=a.prevPosition.left,a.position.top=a.prevPosition.top,a.size.width=a.prevSize.width,a.size.height=a.prevSize.height)},stop:function(){var e=t(this).resizable("instance"),i=e.options,s=e.containerOffset,n=e.containerPosition,o=e.containerElement,a=t(e.helper),r=a.offset(),h=a.outerWidth()-e.sizeDiff.width,l=a.outerHeight()-e.sizeDiff.height;e._helper&&!i.animate&&/relative/.test(o.css("position"))&&t(this).css({left:r.left-n.left-s.left,width:h,height:l}),e._helper&&!i.animate&&/static/.test(o.css("position"))&&t(this).css({left:r.left-n.left-s.left,width:h,height:l})}}),t.ui.plugin.add("resizable","alsoResize",{start:function(){var e=t(this).resizable("instance"),i=e.options;t(i.alsoResize).each(function(){var e=t(this);e.data("ui-resizable-alsoresize",{width:parseFloat(e.width()),height:parseFloat(e.height()),left:parseFloat(e.css("left")),top:parseFloat(e.css("top"))})})},resize:function(e,i){var s=t(this).resizable("instance"),n=s.options,o=s.originalSize,a=s.originalPosition,r={height:s.size.height-o.height||0,width:s.size.width-o.width||0,top:s.position.top-a.top||0,left:s.position.left-a.left||0}; +t(n.alsoResize).each(function(){var e=t(this),s=t(this).data("ui-resizable-alsoresize"),n={},o=e.parents(i.originalElement[0]).length?["width","height"]:["width","height","top","left"];t.each(o,function(t,e){var i=(s[e]||0)+(r[e]||0);i&&i>=0&&(n[e]=i||null)}),e.css(n)})},stop:function(){t(this).removeData("ui-resizable-alsoresize")}}),t.ui.plugin.add("resizable","ghost",{start:function(){var e=t(this).resizable("instance"),i=e.size;e.ghost=e.originalElement.clone(),e.ghost.css({opacity:.25,display:"block",position:"relative",height:i.height,width:i.width,margin:0,left:0,top:0}),e._addClass(e.ghost,"ui-resizable-ghost"),t.uiBackCompat!==!1&&"string"==typeof e.options.ghost&&e.ghost.addClass(this.options.ghost),e.ghost.appendTo(e.helper)},resize:function(){var e=t(this).resizable("instance");e.ghost&&e.ghost.css({position:"relative",height:e.size.height,width:e.size.width})},stop:function(){var e=t(this).resizable("instance");e.ghost&&e.helper&&e.helper.get(0).removeChild(e.ghost.get(0))}}),t.ui.plugin.add("resizable","grid",{resize:function(){var e,i=t(this).resizable("instance"),s=i.options,n=i.size,o=i.originalSize,a=i.originalPosition,r=i.axis,h="number"==typeof s.grid?[s.grid,s.grid]:s.grid,l=h[0]||1,c=h[1]||1,u=Math.round((n.width-o.width)/l)*l,d=Math.round((n.height-o.height)/c)*c,p=o.width+u,f=o.height+d,g=s.maxWidth&&p>s.maxWidth,m=s.maxHeight&&f>s.maxHeight,_=s.minWidth&&s.minWidth>p,v=s.minHeight&&s.minHeight>f;s.grid=h,_&&(p+=l),v&&(f+=c),g&&(p-=l),m&&(f-=c),/^(se|s|e)$/.test(r)?(i.size.width=p,i.size.height=f):/^(ne)$/.test(r)?(i.size.width=p,i.size.height=f,i.position.top=a.top-d):/^(sw)$/.test(r)?(i.size.width=p,i.size.height=f,i.position.left=a.left-u):((0>=f-c||0>=p-l)&&(e=i._getPaddingPlusBorderDimensions(this)),f-c>0?(i.size.height=f,i.position.top=a.top-d):(f=c-e.height,i.size.height=f,i.position.top=a.top+o.height-f),p-l>0?(i.size.width=p,i.position.left=a.left-u):(p=l-e.width,i.size.width=p,i.position.left=a.left+o.width-p))}}),t.ui.resizable,t.widget("ui.selectable",t.ui.mouse,{version:"1.12.1",options:{appendTo:"body",autoRefresh:!0,distance:0,filter:"*",tolerance:"touch",selected:null,selecting:null,start:null,stop:null,unselected:null,unselecting:null},_create:function(){var e=this;this._addClass("ui-selectable"),this.dragged=!1,this.refresh=function(){e.elementPos=t(e.element[0]).offset(),e.selectees=t(e.options.filter,e.element[0]),e._addClass(e.selectees,"ui-selectee"),e.selectees.each(function(){var i=t(this),s=i.offset(),n={left:s.left-e.elementPos.left,top:s.top-e.elementPos.top};t.data(this,"selectable-item",{element:this,$element:i,left:n.left,top:n.top,right:n.left+i.outerWidth(),bottom:n.top+i.outerHeight(),startselected:!1,selected:i.hasClass("ui-selected"),selecting:i.hasClass("ui-selecting"),unselecting:i.hasClass("ui-unselecting")})})},this.refresh(),this._mouseInit(),this.helper=t("
    "),this._addClass(this.helper,"ui-selectable-helper")},_destroy:function(){this.selectees.removeData("selectable-item"),this._mouseDestroy()},_mouseStart:function(e){var i=this,s=this.options;this.opos=[e.pageX,e.pageY],this.elementPos=t(this.element[0]).offset(),this.options.disabled||(this.selectees=t(s.filter,this.element[0]),this._trigger("start",e),t(s.appendTo).append(this.helper),this.helper.css({left:e.pageX,top:e.pageY,width:0,height:0}),s.autoRefresh&&this.refresh(),this.selectees.filter(".ui-selected").each(function(){var s=t.data(this,"selectable-item");s.startselected=!0,e.metaKey||e.ctrlKey||(i._removeClass(s.$element,"ui-selected"),s.selected=!1,i._addClass(s.$element,"ui-unselecting"),s.unselecting=!0,i._trigger("unselecting",e,{unselecting:s.element}))}),t(e.target).parents().addBack().each(function(){var s,n=t.data(this,"selectable-item");return n?(s=!e.metaKey&&!e.ctrlKey||!n.$element.hasClass("ui-selected"),i._removeClass(n.$element,s?"ui-unselecting":"ui-selected")._addClass(n.$element,s?"ui-selecting":"ui-unselecting"),n.unselecting=!s,n.selecting=s,n.selected=s,s?i._trigger("selecting",e,{selecting:n.element}):i._trigger("unselecting",e,{unselecting:n.element}),!1):void 0}))},_mouseDrag:function(e){if(this.dragged=!0,!this.options.disabled){var i,s=this,n=this.options,o=this.opos[0],a=this.opos[1],r=e.pageX,h=e.pageY;return o>r&&(i=r,r=o,o=i),a>h&&(i=h,h=a,a=i),this.helper.css({left:o,top:a,width:r-o,height:h-a}),this.selectees.each(function(){var i=t.data(this,"selectable-item"),l=!1,c={};i&&i.element!==s.element[0]&&(c.left=i.left+s.elementPos.left,c.right=i.right+s.elementPos.left,c.top=i.top+s.elementPos.top,c.bottom=i.bottom+s.elementPos.top,"touch"===n.tolerance?l=!(c.left>r||o>c.right||c.top>h||a>c.bottom):"fit"===n.tolerance&&(l=c.left>o&&r>c.right&&c.top>a&&h>c.bottom),l?(i.selected&&(s._removeClass(i.$element,"ui-selected"),i.selected=!1),i.unselecting&&(s._removeClass(i.$element,"ui-unselecting"),i.unselecting=!1),i.selecting||(s._addClass(i.$element,"ui-selecting"),i.selecting=!0,s._trigger("selecting",e,{selecting:i.element}))):(i.selecting&&((e.metaKey||e.ctrlKey)&&i.startselected?(s._removeClass(i.$element,"ui-selecting"),i.selecting=!1,s._addClass(i.$element,"ui-selected"),i.selected=!0):(s._removeClass(i.$element,"ui-selecting"),i.selecting=!1,i.startselected&&(s._addClass(i.$element,"ui-unselecting"),i.unselecting=!0),s._trigger("unselecting",e,{unselecting:i.element}))),i.selected&&(e.metaKey||e.ctrlKey||i.startselected||(s._removeClass(i.$element,"ui-selected"),i.selected=!1,s._addClass(i.$element,"ui-unselecting"),i.unselecting=!0,s._trigger("unselecting",e,{unselecting:i.element})))))}),!1}},_mouseStop:function(e){var i=this;return this.dragged=!1,t(".ui-unselecting",this.element[0]).each(function(){var s=t.data(this,"selectable-item");i._removeClass(s.$element,"ui-unselecting"),s.unselecting=!1,s.startselected=!1,i._trigger("unselected",e,{unselected:s.element})}),t(".ui-selecting",this.element[0]).each(function(){var s=t.data(this,"selectable-item");i._removeClass(s.$element,"ui-selecting")._addClass(s.$element,"ui-selected"),s.selecting=!1,s.selected=!0,s.startselected=!0,i._trigger("selected",e,{selected:s.element})}),this._trigger("stop",e),this.helper.remove(),!1}}),t.widget("ui.sortable",t.ui.mouse,{version:"1.12.1",widgetEventPrefix:"sort",ready:!1,options:{appendTo:"parent",axis:!1,connectWith:!1,containment:!1,cursor:"auto",cursorAt:!1,dropOnEmpty:!0,forcePlaceholderSize:!1,forceHelperSize:!1,grid:!1,handle:!1,helper:"original",items:"> *",opacity:!1,placeholder:!1,revert:!1,scroll:!0,scrollSensitivity:20,scrollSpeed:20,scope:"default",tolerance:"intersect",zIndex:1e3,activate:null,beforeStop:null,change:null,deactivate:null,out:null,over:null,receive:null,remove:null,sort:null,start:null,stop:null,update:null},_isOverAxis:function(t,e,i){return t>=e&&e+i>t},_isFloating:function(t){return/left|right/.test(t.css("float"))||/inline|table-cell/.test(t.css("display"))},_create:function(){this.containerCache={},this._addClass("ui-sortable"),this.refresh(),this.offset=this.element.offset(),this._mouseInit(),this._setHandleClassName(),this.ready=!0},_setOption:function(t,e){this._super(t,e),"handle"===t&&this._setHandleClassName()},_setHandleClassName:function(){var e=this;this._removeClass(this.element.find(".ui-sortable-handle"),"ui-sortable-handle"),t.each(this.items,function(){e._addClass(this.instance.options.handle?this.item.find(this.instance.options.handle):this.item,"ui-sortable-handle")})},_destroy:function(){this._mouseDestroy();for(var t=this.items.length-1;t>=0;t--)this.items[t].item.removeData(this.widgetName+"-item");return this},_mouseCapture:function(e,i){var s=null,n=!1,o=this;return this.reverting?!1:this.options.disabled||"static"===this.options.type?!1:(this._refreshItems(e),t(e.target).parents().each(function(){return t.data(this,o.widgetName+"-item")===o?(s=t(this),!1):void 0}),t.data(e.target,o.widgetName+"-item")===o&&(s=t(e.target)),s?!this.options.handle||i||(t(this.options.handle,s).find("*").addBack().each(function(){this===e.target&&(n=!0)}),n)?(this.currentItem=s,this._removeCurrentsFromItems(),!0):!1:!1)},_mouseStart:function(e,i,s){var n,o,a=this.options;if(this.currentContainer=this,this.refreshPositions(),this.helper=this._createHelper(e),this._cacheHelperProportions(),this._cacheMargins(),this.scrollParent=this.helper.scrollParent(),this.offset=this.currentItem.offset(),this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left},t.extend(this.offset,{click:{left:e.pageX-this.offset.left,top:e.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()}),this.helper.css("position","absolute"),this.cssPosition=this.helper.css("position"),this.originalPosition=this._generatePosition(e),this.originalPageX=e.pageX,this.originalPageY=e.pageY,a.cursorAt&&this._adjustOffsetFromHelper(a.cursorAt),this.domPosition={prev:this.currentItem.prev()[0],parent:this.currentItem.parent()[0]},this.helper[0]!==this.currentItem[0]&&this.currentItem.hide(),this._createPlaceholder(),a.containment&&this._setContainment(),a.cursor&&"auto"!==a.cursor&&(o=this.document.find("body"),this.storedCursor=o.css("cursor"),o.css("cursor",a.cursor),this.storedStylesheet=t("").appendTo(o)),a.opacity&&(this.helper.css("opacity")&&(this._storedOpacity=this.helper.css("opacity")),this.helper.css("opacity",a.opacity)),a.zIndex&&(this.helper.css("zIndex")&&(this._storedZIndex=this.helper.css("zIndex")),this.helper.css("zIndex",a.zIndex)),this.scrollParent[0]!==this.document[0]&&"HTML"!==this.scrollParent[0].tagName&&(this.overflowOffset=this.scrollParent.offset()),this._trigger("start",e,this._uiHash()),this._preserveHelperProportions||this._cacheHelperProportions(),!s)for(n=this.containers.length-1;n>=0;n--)this.containers[n]._trigger("activate",e,this._uiHash(this));return t.ui.ddmanager&&(t.ui.ddmanager.current=this),t.ui.ddmanager&&!a.dropBehaviour&&t.ui.ddmanager.prepareOffsets(this,e),this.dragging=!0,this._addClass(this.helper,"ui-sortable-helper"),this._mouseDrag(e),!0},_mouseDrag:function(e){var i,s,n,o,a=this.options,r=!1;for(this.position=this._generatePosition(e),this.positionAbs=this._convertPositionTo("absolute"),this.lastPositionAbs||(this.lastPositionAbs=this.positionAbs),this.options.scroll&&(this.scrollParent[0]!==this.document[0]&&"HTML"!==this.scrollParent[0].tagName?(this.overflowOffset.top+this.scrollParent[0].offsetHeight-e.pageY=0;i--)if(s=this.items[i],n=s.item[0],o=this._intersectsWithPointer(s),o&&s.instance===this.currentContainer&&n!==this.currentItem[0]&&this.placeholder[1===o?"next":"prev"]()[0]!==n&&!t.contains(this.placeholder[0],n)&&("semi-dynamic"===this.options.type?!t.contains(this.element[0],n):!0)){if(this.direction=1===o?"down":"up","pointer"!==this.options.tolerance&&!this._intersectsWithSides(s))break;this._rearrange(e,s),this._trigger("change",e,this._uiHash());break}return this._contactContainers(e),t.ui.ddmanager&&t.ui.ddmanager.drag(this,e),this._trigger("sort",e,this._uiHash()),this.lastPositionAbs=this.positionAbs,!1},_mouseStop:function(e,i){if(e){if(t.ui.ddmanager&&!this.options.dropBehaviour&&t.ui.ddmanager.drop(this,e),this.options.revert){var s=this,n=this.placeholder.offset(),o=this.options.axis,a={};o&&"x"!==o||(a.left=n.left-this.offset.parent.left-this.margins.left+(this.offsetParent[0]===this.document[0].body?0:this.offsetParent[0].scrollLeft)),o&&"y"!==o||(a.top=n.top-this.offset.parent.top-this.margins.top+(this.offsetParent[0]===this.document[0].body?0:this.offsetParent[0].scrollTop)),this.reverting=!0,t(this.helper).animate(a,parseInt(this.options.revert,10)||500,function(){s._clear(e)})}else this._clear(e,i);return!1}},cancel:function(){if(this.dragging){this._mouseUp(new t.Event("mouseup",{target:null})),"original"===this.options.helper?(this.currentItem.css(this._storedCSS),this._removeClass(this.currentItem,"ui-sortable-helper")):this.currentItem.show();for(var e=this.containers.length-1;e>=0;e--)this.containers[e]._trigger("deactivate",null,this._uiHash(this)),this.containers[e].containerCache.over&&(this.containers[e]._trigger("out",null,this._uiHash(this)),this.containers[e].containerCache.over=0)}return this.placeholder&&(this.placeholder[0].parentNode&&this.placeholder[0].parentNode.removeChild(this.placeholder[0]),"original"!==this.options.helper&&this.helper&&this.helper[0].parentNode&&this.helper.remove(),t.extend(this,{helper:null,dragging:!1,reverting:!1,_noFinalSort:null}),this.domPosition.prev?t(this.domPosition.prev).after(this.currentItem):t(this.domPosition.parent).prepend(this.currentItem)),this},serialize:function(e){var i=this._getItemsAsjQuery(e&&e.connected),s=[];return e=e||{},t(i).each(function(){var i=(t(e.item||this).attr(e.attribute||"id")||"").match(e.expression||/(.+)[\-=_](.+)/);i&&s.push((e.key||i[1]+"[]")+"="+(e.key&&e.expression?i[1]:i[2]))}),!s.length&&e.key&&s.push(e.key+"="),s.join("&")},toArray:function(e){var i=this._getItemsAsjQuery(e&&e.connected),s=[];return e=e||{},i.each(function(){s.push(t(e.item||this).attr(e.attribute||"id")||"")}),s},_intersectsWith:function(t){var e=this.positionAbs.left,i=e+this.helperProportions.width,s=this.positionAbs.top,n=s+this.helperProportions.height,o=t.left,a=o+t.width,r=t.top,h=r+t.height,l=this.offset.click.top,c=this.offset.click.left,u="x"===this.options.axis||s+l>r&&h>s+l,d="y"===this.options.axis||e+c>o&&a>e+c,p=u&&d;return"pointer"===this.options.tolerance||this.options.forcePointerForContainers||"pointer"!==this.options.tolerance&&this.helperProportions[this.floating?"width":"height"]>t[this.floating?"width":"height"]?p:e+this.helperProportions.width/2>o&&a>i-this.helperProportions.width/2&&s+this.helperProportions.height/2>r&&h>n-this.helperProportions.height/2},_intersectsWithPointer:function(t){var e,i,s="x"===this.options.axis||this._isOverAxis(this.positionAbs.top+this.offset.click.top,t.top,t.height),n="y"===this.options.axis||this._isOverAxis(this.positionAbs.left+this.offset.click.left,t.left,t.width),o=s&&n;return o?(e=this._getDragVerticalDirection(),i=this._getDragHorizontalDirection(),this.floating?"right"===i||"down"===e?2:1:e&&("down"===e?2:1)):!1},_intersectsWithSides:function(t){var e=this._isOverAxis(this.positionAbs.top+this.offset.click.top,t.top+t.height/2,t.height),i=this._isOverAxis(this.positionAbs.left+this.offset.click.left,t.left+t.width/2,t.width),s=this._getDragVerticalDirection(),n=this._getDragHorizontalDirection();return this.floating&&n?"right"===n&&i||"left"===n&&!i:s&&("down"===s&&e||"up"===s&&!e)},_getDragVerticalDirection:function(){var t=this.positionAbs.top-this.lastPositionAbs.top;return 0!==t&&(t>0?"down":"up")},_getDragHorizontalDirection:function(){var t=this.positionAbs.left-this.lastPositionAbs.left;return 0!==t&&(t>0?"right":"left")},refresh:function(t){return this._refreshItems(t),this._setHandleClassName(),this.refreshPositions(),this},_connectWith:function(){var t=this.options;return t.connectWith.constructor===String?[t.connectWith]:t.connectWith},_getItemsAsjQuery:function(e){function i(){r.push(this)}var s,n,o,a,r=[],h=[],l=this._connectWith();if(l&&e)for(s=l.length-1;s>=0;s--)for(o=t(l[s],this.document[0]),n=o.length-1;n>=0;n--)a=t.data(o[n],this.widgetFullName),a&&a!==this&&!a.options.disabled&&h.push([t.isFunction(a.options.items)?a.options.items.call(a.element):t(a.options.items,a.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),a]);for(h.push([t.isFunction(this.options.items)?this.options.items.call(this.element,null,{options:this.options,item:this.currentItem}):t(this.options.items,this.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),this]),s=h.length-1;s>=0;s--)h[s][0].each(i);return t(r)},_removeCurrentsFromItems:function(){var e=this.currentItem.find(":data("+this.widgetName+"-item)");this.items=t.grep(this.items,function(t){for(var i=0;e.length>i;i++)if(e[i]===t.item[0])return!1;return!0})},_refreshItems:function(e){this.items=[],this.containers=[this];var i,s,n,o,a,r,h,l,c=this.items,u=[[t.isFunction(this.options.items)?this.options.items.call(this.element[0],e,{item:this.currentItem}):t(this.options.items,this.element),this]],d=this._connectWith();if(d&&this.ready)for(i=d.length-1;i>=0;i--)for(n=t(d[i],this.document[0]),s=n.length-1;s>=0;s--)o=t.data(n[s],this.widgetFullName),o&&o!==this&&!o.options.disabled&&(u.push([t.isFunction(o.options.items)?o.options.items.call(o.element[0],e,{item:this.currentItem}):t(o.options.items,o.element),o]),this.containers.push(o));for(i=u.length-1;i>=0;i--)for(a=u[i][1],r=u[i][0],s=0,l=r.length;l>s;s++)h=t(r[s]),h.data(this.widgetName+"-item",a),c.push({item:h,instance:a,width:0,height:0,left:0,top:0})},refreshPositions:function(e){this.floating=this.items.length?"x"===this.options.axis||this._isFloating(this.items[0].item):!1,this.offsetParent&&this.helper&&(this.offset.parent=this._getParentOffset());var i,s,n,o;for(i=this.items.length-1;i>=0;i--)s=this.items[i],s.instance!==this.currentContainer&&this.currentContainer&&s.item[0]!==this.currentItem[0]||(n=this.options.toleranceElement?t(this.options.toleranceElement,s.item):s.item,e||(s.width=n.outerWidth(),s.height=n.outerHeight()),o=n.offset(),s.left=o.left,s.top=o.top);if(this.options.custom&&this.options.custom.refreshContainers)this.options.custom.refreshContainers.call(this);else for(i=this.containers.length-1;i>=0;i--)o=this.containers[i].element.offset(),this.containers[i].containerCache.left=o.left,this.containers[i].containerCache.top=o.top,this.containers[i].containerCache.width=this.containers[i].element.outerWidth(),this.containers[i].containerCache.height=this.containers[i].element.outerHeight();return this},_createPlaceholder:function(e){e=e||this;var i,s=e.options;s.placeholder&&s.placeholder.constructor!==String||(i=s.placeholder,s.placeholder={element:function(){var s=e.currentItem[0].nodeName.toLowerCase(),n=t("<"+s+">",e.document[0]);return e._addClass(n,"ui-sortable-placeholder",i||e.currentItem[0].className)._removeClass(n,"ui-sortable-helper"),"tbody"===s?e._createTrPlaceholder(e.currentItem.find("tr").eq(0),t("
    ",e.document[0]).appendTo(n)):"tr"===s?e._createTrPlaceholder(e.currentItem,n):"img"===s&&n.attr("src",e.currentItem.attr("src")),i||n.css("visibility","hidden"),n},update:function(t,n){(!i||s.forcePlaceholderSize)&&(n.height()||n.height(e.currentItem.innerHeight()-parseInt(e.currentItem.css("paddingTop")||0,10)-parseInt(e.currentItem.css("paddingBottom")||0,10)),n.width()||n.width(e.currentItem.innerWidth()-parseInt(e.currentItem.css("paddingLeft")||0,10)-parseInt(e.currentItem.css("paddingRight")||0,10)))}}),e.placeholder=t(s.placeholder.element.call(e.element,e.currentItem)),e.currentItem.after(e.placeholder),s.placeholder.update(e,e.placeholder)},_createTrPlaceholder:function(e,i){var s=this;e.children().each(function(){t("",s.document[0]).attr("colspan",t(this).attr("colspan")||1).appendTo(i)})},_contactContainers:function(e){var i,s,n,o,a,r,h,l,c,u,d=null,p=null;for(i=this.containers.length-1;i>=0;i--)if(!t.contains(this.currentItem[0],this.containers[i].element[0]))if(this._intersectsWith(this.containers[i].containerCache)){if(d&&t.contains(this.containers[i].element[0],d.element[0]))continue;d=this.containers[i],p=i}else this.containers[i].containerCache.over&&(this.containers[i]._trigger("out",e,this._uiHash(this)),this.containers[i].containerCache.over=0);if(d)if(1===this.containers.length)this.containers[p].containerCache.over||(this.containers[p]._trigger("over",e,this._uiHash(this)),this.containers[p].containerCache.over=1);else{for(n=1e4,o=null,c=d.floating||this._isFloating(this.currentItem),a=c?"left":"top",r=c?"width":"height",u=c?"pageX":"pageY",s=this.items.length-1;s>=0;s--)t.contains(this.containers[p].element[0],this.items[s].item[0])&&this.items[s].item[0]!==this.currentItem[0]&&(h=this.items[s].item.offset()[a],l=!1,e[u]-h>this.items[s][r]/2&&(l=!0),n>Math.abs(e[u]-h)&&(n=Math.abs(e[u]-h),o=this.items[s],this.direction=l?"up":"down"));if(!o&&!this.options.dropOnEmpty)return;if(this.currentContainer===this.containers[p])return this.currentContainer.containerCache.over||(this.containers[p]._trigger("over",e,this._uiHash()),this.currentContainer.containerCache.over=1),void 0;o?this._rearrange(e,o,null,!0):this._rearrange(e,null,this.containers[p].element,!0),this._trigger("change",e,this._uiHash()),this.containers[p]._trigger("change",e,this._uiHash(this)),this.currentContainer=this.containers[p],this.options.placeholder.update(this.currentContainer,this.placeholder),this.containers[p]._trigger("over",e,this._uiHash(this)),this.containers[p].containerCache.over=1}},_createHelper:function(e){var i=this.options,s=t.isFunction(i.helper)?t(i.helper.apply(this.element[0],[e,this.currentItem])):"clone"===i.helper?this.currentItem.clone():this.currentItem;return s.parents("body").length||t("parent"!==i.appendTo?i.appendTo:this.currentItem[0].parentNode)[0].appendChild(s[0]),s[0]===this.currentItem[0]&&(this._storedCSS={width:this.currentItem[0].style.width,height:this.currentItem[0].style.height,position:this.currentItem.css("position"),top:this.currentItem.css("top"),left:this.currentItem.css("left")}),(!s[0].style.width||i.forceHelperSize)&&s.width(this.currentItem.width()),(!s[0].style.height||i.forceHelperSize)&&s.height(this.currentItem.height()),s},_adjustOffsetFromHelper:function(e){"string"==typeof e&&(e=e.split(" ")),t.isArray(e)&&(e={left:+e[0],top:+e[1]||0}),"left"in e&&(this.offset.click.left=e.left+this.margins.left),"right"in e&&(this.offset.click.left=this.helperProportions.width-e.right+this.margins.left),"top"in e&&(this.offset.click.top=e.top+this.margins.top),"bottom"in e&&(this.offset.click.top=this.helperProportions.height-e.bottom+this.margins.top)},_getParentOffset:function(){this.offsetParent=this.helper.offsetParent();var e=this.offsetParent.offset();return"absolute"===this.cssPosition&&this.scrollParent[0]!==this.document[0]&&t.contains(this.scrollParent[0],this.offsetParent[0])&&(e.left+=this.scrollParent.scrollLeft(),e.top+=this.scrollParent.scrollTop()),(this.offsetParent[0]===this.document[0].body||this.offsetParent[0].tagName&&"html"===this.offsetParent[0].tagName.toLowerCase()&&t.ui.ie)&&(e={top:0,left:0}),{top:e.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:e.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if("relative"===this.cssPosition){var t=this.currentItem.position();return{top:t.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:t.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.currentItem.css("marginLeft"),10)||0,top:parseInt(this.currentItem.css("marginTop"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var e,i,s,n=this.options;"parent"===n.containment&&(n.containment=this.helper[0].parentNode),("document"===n.containment||"window"===n.containment)&&(this.containment=[0-this.offset.relative.left-this.offset.parent.left,0-this.offset.relative.top-this.offset.parent.top,"document"===n.containment?this.document.width():this.window.width()-this.helperProportions.width-this.margins.left,("document"===n.containment?this.document.height()||document.body.parentNode.scrollHeight:this.window.height()||this.document[0].body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top]),/^(document|window|parent)$/.test(n.containment)||(e=t(n.containment)[0],i=t(n.containment).offset(),s="hidden"!==t(e).css("overflow"),this.containment=[i.left+(parseInt(t(e).css("borderLeftWidth"),10)||0)+(parseInt(t(e).css("paddingLeft"),10)||0)-this.margins.left,i.top+(parseInt(t(e).css("borderTopWidth"),10)||0)+(parseInt(t(e).css("paddingTop"),10)||0)-this.margins.top,i.left+(s?Math.max(e.scrollWidth,e.offsetWidth):e.offsetWidth)-(parseInt(t(e).css("borderLeftWidth"),10)||0)-(parseInt(t(e).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left,i.top+(s?Math.max(e.scrollHeight,e.offsetHeight):e.offsetHeight)-(parseInt(t(e).css("borderTopWidth"),10)||0)-(parseInt(t(e).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top])},_convertPositionTo:function(e,i){i||(i=this.position);var s="absolute"===e?1:-1,n="absolute"!==this.cssPosition||this.scrollParent[0]!==this.document[0]&&t.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent,o=/(html|body)/i.test(n[0].tagName);return{top:i.top+this.offset.relative.top*s+this.offset.parent.top*s-("fixed"===this.cssPosition?-this.scrollParent.scrollTop():o?0:n.scrollTop())*s,left:i.left+this.offset.relative.left*s+this.offset.parent.left*s-("fixed"===this.cssPosition?-this.scrollParent.scrollLeft():o?0:n.scrollLeft())*s}},_generatePosition:function(e){var i,s,n=this.options,o=e.pageX,a=e.pageY,r="absolute"!==this.cssPosition||this.scrollParent[0]!==this.document[0]&&t.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent,h=/(html|body)/i.test(r[0].tagName);return"relative"!==this.cssPosition||this.scrollParent[0]!==this.document[0]&&this.scrollParent[0]!==this.offsetParent[0]||(this.offset.relative=this._getRelativeOffset()),this.originalPosition&&(this.containment&&(e.pageX-this.offset.click.leftthis.containment[2]&&(o=this.containment[2]+this.offset.click.left),e.pageY-this.offset.click.top>this.containment[3]&&(a=this.containment[3]+this.offset.click.top)),n.grid&&(i=this.originalPageY+Math.round((a-this.originalPageY)/n.grid[1])*n.grid[1],a=this.containment?i-this.offset.click.top>=this.containment[1]&&i-this.offset.click.top<=this.containment[3]?i:i-this.offset.click.top>=this.containment[1]?i-n.grid[1]:i+n.grid[1]:i,s=this.originalPageX+Math.round((o-this.originalPageX)/n.grid[0])*n.grid[0],o=this.containment?s-this.offset.click.left>=this.containment[0]&&s-this.offset.click.left<=this.containment[2]?s:s-this.offset.click.left>=this.containment[0]?s-n.grid[0]:s+n.grid[0]:s)),{top:a-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+("fixed"===this.cssPosition?-this.scrollParent.scrollTop():h?0:r.scrollTop()),left:o-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+("fixed"===this.cssPosition?-this.scrollParent.scrollLeft():h?0:r.scrollLeft())}},_rearrange:function(t,e,i,s){i?i[0].appendChild(this.placeholder[0]):e.item[0].parentNode.insertBefore(this.placeholder[0],"down"===this.direction?e.item[0]:e.item[0].nextSibling),this.counter=this.counter?++this.counter:1;var n=this.counter;this._delay(function(){n===this.counter&&this.refreshPositions(!s)})},_clear:function(t,e){function i(t,e,i){return function(s){i._trigger(t,s,e._uiHash(e))}}this.reverting=!1;var s,n=[];if(!this._noFinalSort&&this.currentItem.parent().length&&this.placeholder.before(this.currentItem),this._noFinalSort=null,this.helper[0]===this.currentItem[0]){for(s in this._storedCSS)("auto"===this._storedCSS[s]||"static"===this._storedCSS[s])&&(this._storedCSS[s]="");this.currentItem.css(this._storedCSS),this._removeClass(this.currentItem,"ui-sortable-helper")}else this.currentItem.show();for(this.fromOutside&&!e&&n.push(function(t){this._trigger("receive",t,this._uiHash(this.fromOutside))}),!this.fromOutside&&this.domPosition.prev===this.currentItem.prev().not(".ui-sortable-helper")[0]&&this.domPosition.parent===this.currentItem.parent()[0]||e||n.push(function(t){this._trigger("update",t,this._uiHash())}),this!==this.currentContainer&&(e||(n.push(function(t){this._trigger("remove",t,this._uiHash())}),n.push(function(t){return function(e){t._trigger("receive",e,this._uiHash(this))}}.call(this,this.currentContainer)),n.push(function(t){return function(e){t._trigger("update",e,this._uiHash(this))}}.call(this,this.currentContainer)))),s=this.containers.length-1;s>=0;s--)e||n.push(i("deactivate",this,this.containers[s])),this.containers[s].containerCache.over&&(n.push(i("out",this,this.containers[s])),this.containers[s].containerCache.over=0);if(this.storedCursor&&(this.document.find("body").css("cursor",this.storedCursor),this.storedStylesheet.remove()),this._storedOpacity&&this.helper.css("opacity",this._storedOpacity),this._storedZIndex&&this.helper.css("zIndex","auto"===this._storedZIndex?"":this._storedZIndex),this.dragging=!1,e||this._trigger("beforeStop",t,this._uiHash()),this.placeholder[0].parentNode.removeChild(this.placeholder[0]),this.cancelHelperRemoval||(this.helper[0]!==this.currentItem[0]&&this.helper.remove(),this.helper=null),!e){for(s=0;n.length>s;s++)n[s].call(this,t);this._trigger("stop",t,this._uiHash())}return this.fromOutside=!1,!this.cancelHelperRemoval},_trigger:function(){t.Widget.prototype._trigger.apply(this,arguments)===!1&&this.cancel()},_uiHash:function(e){var i=e||this;return{helper:i.helper,placeholder:i.placeholder||t([]),position:i.position,originalPosition:i.originalPosition,offset:i.positionAbs,item:i.currentItem,sender:e?e.element:null}}}),t.widget("ui.accordion",{version:"1.12.1",options:{active:0,animate:{},classes:{"ui-accordion-header":"ui-corner-top","ui-accordion-header-collapsed":"ui-corner-all","ui-accordion-content":"ui-corner-bottom"},collapsible:!1,event:"click",header:"> li > :first-child, > :not(li):even",heightStyle:"auto",icons:{activeHeader:"ui-icon-triangle-1-s",header:"ui-icon-triangle-1-e"},activate:null,beforeActivate:null},hideProps:{borderTopWidth:"hide",borderBottomWidth:"hide",paddingTop:"hide",paddingBottom:"hide",height:"hide"},showProps:{borderTopWidth:"show",borderBottomWidth:"show",paddingTop:"show",paddingBottom:"show",height:"show"},_create:function(){var e=this.options;this.prevShow=this.prevHide=t(),this._addClass("ui-accordion","ui-widget ui-helper-reset"),this.element.attr("role","tablist"),e.collapsible||e.active!==!1&&null!=e.active||(e.active=0),this._processPanels(),0>e.active&&(e.active+=this.headers.length),this._refresh()},_getCreateEventData:function(){return{header:this.active,panel:this.active.length?this.active.next():t()}},_createIcons:function(){var e,i,s=this.options.icons;s&&(e=t(""),this._addClass(e,"ui-accordion-header-icon","ui-icon "+s.header),e.prependTo(this.headers),i=this.active.children(".ui-accordion-header-icon"),this._removeClass(i,s.header)._addClass(i,null,s.activeHeader)._addClass(this.headers,"ui-accordion-icons")) +},_destroyIcons:function(){this._removeClass(this.headers,"ui-accordion-icons"),this.headers.children(".ui-accordion-header-icon").remove()},_destroy:function(){var t;this.element.removeAttr("role"),this.headers.removeAttr("role aria-expanded aria-selected aria-controls tabIndex").removeUniqueId(),this._destroyIcons(),t=this.headers.next().css("display","").removeAttr("role aria-hidden aria-labelledby").removeUniqueId(),"content"!==this.options.heightStyle&&t.css("height","")},_setOption:function(t,e){return"active"===t?(this._activate(e),void 0):("event"===t&&(this.options.event&&this._off(this.headers,this.options.event),this._setupEvents(e)),this._super(t,e),"collapsible"!==t||e||this.options.active!==!1||this._activate(0),"icons"===t&&(this._destroyIcons(),e&&this._createIcons()),void 0)},_setOptionDisabled:function(t){this._super(t),this.element.attr("aria-disabled",t),this._toggleClass(null,"ui-state-disabled",!!t),this._toggleClass(this.headers.add(this.headers.next()),null,"ui-state-disabled",!!t)},_keydown:function(e){if(!e.altKey&&!e.ctrlKey){var i=t.ui.keyCode,s=this.headers.length,n=this.headers.index(e.target),o=!1;switch(e.keyCode){case i.RIGHT:case i.DOWN:o=this.headers[(n+1)%s];break;case i.LEFT:case i.UP:o=this.headers[(n-1+s)%s];break;case i.SPACE:case i.ENTER:this._eventHandler(e);break;case i.HOME:o=this.headers[0];break;case i.END:o=this.headers[s-1]}o&&(t(e.target).attr("tabIndex",-1),t(o).attr("tabIndex",0),t(o).trigger("focus"),e.preventDefault())}},_panelKeyDown:function(e){e.keyCode===t.ui.keyCode.UP&&e.ctrlKey&&t(e.currentTarget).prev().trigger("focus")},refresh:function(){var e=this.options;this._processPanels(),e.active===!1&&e.collapsible===!0||!this.headers.length?(e.active=!1,this.active=t()):e.active===!1?this._activate(0):this.active.length&&!t.contains(this.element[0],this.active[0])?this.headers.length===this.headers.find(".ui-state-disabled").length?(e.active=!1,this.active=t()):this._activate(Math.max(0,e.active-1)):e.active=this.headers.index(this.active),this._destroyIcons(),this._refresh()},_processPanels:function(){var t=this.headers,e=this.panels;this.headers=this.element.find(this.options.header),this._addClass(this.headers,"ui-accordion-header ui-accordion-header-collapsed","ui-state-default"),this.panels=this.headers.next().filter(":not(.ui-accordion-content-active)").hide(),this._addClass(this.panels,"ui-accordion-content","ui-helper-reset ui-widget-content"),e&&(this._off(t.not(this.headers)),this._off(e.not(this.panels)))},_refresh:function(){var e,i=this.options,s=i.heightStyle,n=this.element.parent();this.active=this._findActive(i.active),this._addClass(this.active,"ui-accordion-header-active","ui-state-active")._removeClass(this.active,"ui-accordion-header-collapsed"),this._addClass(this.active.next(),"ui-accordion-content-active"),this.active.next().show(),this.headers.attr("role","tab").each(function(){var e=t(this),i=e.uniqueId().attr("id"),s=e.next(),n=s.uniqueId().attr("id");e.attr("aria-controls",n),s.attr("aria-labelledby",i)}).next().attr("role","tabpanel"),this.headers.not(this.active).attr({"aria-selected":"false","aria-expanded":"false",tabIndex:-1}).next().attr({"aria-hidden":"true"}).hide(),this.active.length?this.active.attr({"aria-selected":"true","aria-expanded":"true",tabIndex:0}).next().attr({"aria-hidden":"false"}):this.headers.eq(0).attr("tabIndex",0),this._createIcons(),this._setupEvents(i.event),"fill"===s?(e=n.height(),this.element.siblings(":visible").each(function(){var i=t(this),s=i.css("position");"absolute"!==s&&"fixed"!==s&&(e-=i.outerHeight(!0))}),this.headers.each(function(){e-=t(this).outerHeight(!0)}),this.headers.next().each(function(){t(this).height(Math.max(0,e-t(this).innerHeight()+t(this).height()))}).css("overflow","auto")):"auto"===s&&(e=0,this.headers.next().each(function(){var i=t(this).is(":visible");i||t(this).show(),e=Math.max(e,t(this).css("height","").height()),i||t(this).hide()}).height(e))},_activate:function(e){var i=this._findActive(e)[0];i!==this.active[0]&&(i=i||this.active[0],this._eventHandler({target:i,currentTarget:i,preventDefault:t.noop}))},_findActive:function(e){return"number"==typeof e?this.headers.eq(e):t()},_setupEvents:function(e){var i={keydown:"_keydown"};e&&t.each(e.split(" "),function(t,e){i[e]="_eventHandler"}),this._off(this.headers.add(this.headers.next())),this._on(this.headers,i),this._on(this.headers.next(),{keydown:"_panelKeyDown"}),this._hoverable(this.headers),this._focusable(this.headers)},_eventHandler:function(e){var i,s,n=this.options,o=this.active,a=t(e.currentTarget),r=a[0]===o[0],h=r&&n.collapsible,l=h?t():a.next(),c=o.next(),u={oldHeader:o,oldPanel:c,newHeader:h?t():a,newPanel:l};e.preventDefault(),r&&!n.collapsible||this._trigger("beforeActivate",e,u)===!1||(n.active=h?!1:this.headers.index(a),this.active=r?t():a,this._toggle(u),this._removeClass(o,"ui-accordion-header-active","ui-state-active"),n.icons&&(i=o.children(".ui-accordion-header-icon"),this._removeClass(i,null,n.icons.activeHeader)._addClass(i,null,n.icons.header)),r||(this._removeClass(a,"ui-accordion-header-collapsed")._addClass(a,"ui-accordion-header-active","ui-state-active"),n.icons&&(s=a.children(".ui-accordion-header-icon"),this._removeClass(s,null,n.icons.header)._addClass(s,null,n.icons.activeHeader)),this._addClass(a.next(),"ui-accordion-content-active")))},_toggle:function(e){var i=e.newPanel,s=this.prevShow.length?this.prevShow:e.oldPanel;this.prevShow.add(this.prevHide).stop(!0,!0),this.prevShow=i,this.prevHide=s,this.options.animate?this._animate(i,s,e):(s.hide(),i.show(),this._toggleComplete(e)),s.attr({"aria-hidden":"true"}),s.prev().attr({"aria-selected":"false","aria-expanded":"false"}),i.length&&s.length?s.prev().attr({tabIndex:-1,"aria-expanded":"false"}):i.length&&this.headers.filter(function(){return 0===parseInt(t(this).attr("tabIndex"),10)}).attr("tabIndex",-1),i.attr("aria-hidden","false").prev().attr({"aria-selected":"true","aria-expanded":"true",tabIndex:0})},_animate:function(t,e,i){var s,n,o,a=this,r=0,h=t.css("box-sizing"),l=t.length&&(!e.length||t.index()",delay:300,options:{icons:{submenu:"ui-icon-caret-1-e"},items:"> *",menus:"ul",position:{my:"left top",at:"right top"},role:"menu",blur:null,focus:null,select:null},_create:function(){this.activeMenu=this.element,this.mouseHandled=!1,this.element.uniqueId().attr({role:this.options.role,tabIndex:0}),this._addClass("ui-menu","ui-widget ui-widget-content"),this._on({"mousedown .ui-menu-item":function(t){t.preventDefault()},"click .ui-menu-item":function(e){var i=t(e.target),s=t(t.ui.safeActiveElement(this.document[0]));!this.mouseHandled&&i.not(".ui-state-disabled").length&&(this.select(e),e.isPropagationStopped()||(this.mouseHandled=!0),i.has(".ui-menu").length?this.expand(e):!this.element.is(":focus")&&s.closest(".ui-menu").length&&(this.element.trigger("focus",[!0]),this.active&&1===this.active.parents(".ui-menu").length&&clearTimeout(this.timer)))},"mouseenter .ui-menu-item":function(e){if(!this.previousFilter){var i=t(e.target).closest(".ui-menu-item"),s=t(e.currentTarget);i[0]===s[0]&&(this._removeClass(s.siblings().children(".ui-state-active"),null,"ui-state-active"),this.focus(e,s))}},mouseleave:"collapseAll","mouseleave .ui-menu":"collapseAll",focus:function(t,e){var i=this.active||this.element.find(this.options.items).eq(0);e||this.focus(t,i)},blur:function(e){this._delay(function(){var i=!t.contains(this.element[0],t.ui.safeActiveElement(this.document[0]));i&&this.collapseAll(e)})},keydown:"_keydown"}),this.refresh(),this._on(this.document,{click:function(t){this._closeOnDocumentClick(t)&&this.collapseAll(t),this.mouseHandled=!1}})},_destroy:function(){var e=this.element.find(".ui-menu-item").removeAttr("role aria-disabled"),i=e.children(".ui-menu-item-wrapper").removeUniqueId().removeAttr("tabIndex role aria-haspopup");this.element.removeAttr("aria-activedescendant").find(".ui-menu").addBack().removeAttr("role aria-labelledby aria-expanded aria-hidden aria-disabled tabIndex").removeUniqueId().show(),i.children().each(function(){var e=t(this);e.data("ui-menu-submenu-caret")&&e.remove()})},_keydown:function(e){var i,s,n,o,a=!0;switch(e.keyCode){case t.ui.keyCode.PAGE_UP:this.previousPage(e);break;case t.ui.keyCode.PAGE_DOWN:this.nextPage(e);break;case t.ui.keyCode.HOME:this._move("first","first",e);break;case t.ui.keyCode.END:this._move("last","last",e);break;case t.ui.keyCode.UP:this.previous(e);break;case t.ui.keyCode.DOWN:this.next(e);break;case t.ui.keyCode.LEFT:this.collapse(e);break;case t.ui.keyCode.RIGHT:this.active&&!this.active.is(".ui-state-disabled")&&this.expand(e);break;case t.ui.keyCode.ENTER:case t.ui.keyCode.SPACE:this._activate(e);break;case t.ui.keyCode.ESCAPE:this.collapse(e);break;default:a=!1,s=this.previousFilter||"",o=!1,n=e.keyCode>=96&&105>=e.keyCode?""+(e.keyCode-96):String.fromCharCode(e.keyCode),clearTimeout(this.filterTimer),n===s?o=!0:n=s+n,i=this._filterMenuItems(n),i=o&&-1!==i.index(this.active.next())?this.active.nextAll(".ui-menu-item"):i,i.length||(n=String.fromCharCode(e.keyCode),i=this._filterMenuItems(n)),i.length?(this.focus(e,i),this.previousFilter=n,this.filterTimer=this._delay(function(){delete this.previousFilter},1e3)):delete this.previousFilter}a&&e.preventDefault()},_activate:function(t){this.active&&!this.active.is(".ui-state-disabled")&&(this.active.children("[aria-haspopup='true']").length?this.expand(t):this.select(t))},refresh:function(){var e,i,s,n,o,a=this,r=this.options.icons.submenu,h=this.element.find(this.options.menus);this._toggleClass("ui-menu-icons",null,!!this.element.find(".ui-icon").length),s=h.filter(":not(.ui-menu)").hide().attr({role:this.options.role,"aria-hidden":"true","aria-expanded":"false"}).each(function(){var e=t(this),i=e.prev(),s=t("").data("ui-menu-submenu-caret",!0);a._addClass(s,"ui-menu-icon","ui-icon "+r),i.attr("aria-haspopup","true").prepend(s),e.attr("aria-labelledby",i.attr("id"))}),this._addClass(s,"ui-menu","ui-widget ui-widget-content ui-front"),e=h.add(this.element),i=e.find(this.options.items),i.not(".ui-menu-item").each(function(){var e=t(this);a._isDivider(e)&&a._addClass(e,"ui-menu-divider","ui-widget-content")}),n=i.not(".ui-menu-item, .ui-menu-divider"),o=n.children().not(".ui-menu").uniqueId().attr({tabIndex:-1,role:this._itemRole()}),this._addClass(n,"ui-menu-item")._addClass(o,"ui-menu-item-wrapper"),i.filter(".ui-state-disabled").attr("aria-disabled","true"),this.active&&!t.contains(this.element[0],this.active[0])&&this.blur()},_itemRole:function(){return{menu:"menuitem",listbox:"option"}[this.options.role]},_setOption:function(t,e){if("icons"===t){var i=this.element.find(".ui-menu-icon");this._removeClass(i,null,this.options.icons.submenu)._addClass(i,null,e.submenu)}this._super(t,e)},_setOptionDisabled:function(t){this._super(t),this.element.attr("aria-disabled",t+""),this._toggleClass(null,"ui-state-disabled",!!t)},focus:function(t,e){var i,s,n;this.blur(t,t&&"focus"===t.type),this._scrollIntoView(e),this.active=e.first(),s=this.active.children(".ui-menu-item-wrapper"),this._addClass(s,null,"ui-state-active"),this.options.role&&this.element.attr("aria-activedescendant",s.attr("id")),n=this.active.parent().closest(".ui-menu-item").children(".ui-menu-item-wrapper"),this._addClass(n,null,"ui-state-active"),t&&"keydown"===t.type?this._close():this.timer=this._delay(function(){this._close()},this.delay),i=e.children(".ui-menu"),i.length&&t&&/^mouse/.test(t.type)&&this._startOpening(i),this.activeMenu=e.parent(),this._trigger("focus",t,{item:e})},_scrollIntoView:function(e){var i,s,n,o,a,r;this._hasScroll()&&(i=parseFloat(t.css(this.activeMenu[0],"borderTopWidth"))||0,s=parseFloat(t.css(this.activeMenu[0],"paddingTop"))||0,n=e.offset().top-this.activeMenu.offset().top-i-s,o=this.activeMenu.scrollTop(),a=this.activeMenu.height(),r=e.outerHeight(),0>n?this.activeMenu.scrollTop(o+n):n+r>a&&this.activeMenu.scrollTop(o+n-a+r))},blur:function(t,e){e||clearTimeout(this.timer),this.active&&(this._removeClass(this.active.children(".ui-menu-item-wrapper"),null,"ui-state-active"),this._trigger("blur",t,{item:this.active}),this.active=null)},_startOpening:function(t){clearTimeout(this.timer),"true"===t.attr("aria-hidden")&&(this.timer=this._delay(function(){this._close(),this._open(t)},this.delay))},_open:function(e){var i=t.extend({of:this.active},this.options.position);clearTimeout(this.timer),this.element.find(".ui-menu").not(e.parents(".ui-menu")).hide().attr("aria-hidden","true"),e.show().removeAttr("aria-hidden").attr("aria-expanded","true").position(i)},collapseAll:function(e,i){clearTimeout(this.timer),this.timer=this._delay(function(){var s=i?this.element:t(e&&e.target).closest(this.element.find(".ui-menu"));s.length||(s=this.element),this._close(s),this.blur(e),this._removeClass(s.find(".ui-state-active"),null,"ui-state-active"),this.activeMenu=s},this.delay)},_close:function(t){t||(t=this.active?this.active.parent():this.element),t.find(".ui-menu").hide().attr("aria-hidden","true").attr("aria-expanded","false")},_closeOnDocumentClick:function(e){return!t(e.target).closest(".ui-menu").length},_isDivider:function(t){return!/[^\-\u2014\u2013\s]/.test(t.text())},collapse:function(t){var e=this.active&&this.active.parent().closest(".ui-menu-item",this.element);e&&e.length&&(this._close(),this.focus(t,e))},expand:function(t){var e=this.active&&this.active.children(".ui-menu ").find(this.options.items).first();e&&e.length&&(this._open(e.parent()),this._delay(function(){this.focus(t,e)}))},next:function(t){this._move("next","first",t)},previous:function(t){this._move("prev","last",t)},isFirstItem:function(){return this.active&&!this.active.prevAll(".ui-menu-item").length},isLastItem:function(){return this.active&&!this.active.nextAll(".ui-menu-item").length},_move:function(t,e,i){var s;this.active&&(s="first"===t||"last"===t?this.active["first"===t?"prevAll":"nextAll"](".ui-menu-item").eq(-1):this.active[t+"All"](".ui-menu-item").eq(0)),s&&s.length&&this.active||(s=this.activeMenu.find(this.options.items)[e]()),this.focus(i,s)},nextPage:function(e){var i,s,n;return this.active?(this.isLastItem()||(this._hasScroll()?(s=this.active.offset().top,n=this.element.height(),this.active.nextAll(".ui-menu-item").each(function(){return i=t(this),0>i.offset().top-s-n}),this.focus(e,i)):this.focus(e,this.activeMenu.find(this.options.items)[this.active?"last":"first"]())),void 0):(this.next(e),void 0)},previousPage:function(e){var i,s,n;return this.active?(this.isFirstItem()||(this._hasScroll()?(s=this.active.offset().top,n=this.element.height(),this.active.prevAll(".ui-menu-item").each(function(){return i=t(this),i.offset().top-s+n>0}),this.focus(e,i)):this.focus(e,this.activeMenu.find(this.options.items).first())),void 0):(this.next(e),void 0)},_hasScroll:function(){return this.element.outerHeight()",options:{appendTo:null,autoFocus:!1,delay:300,minLength:1,position:{my:"left top",at:"left bottom",collision:"none"},source:null,change:null,close:null,focus:null,open:null,response:null,search:null,select:null},requestIndex:0,pending:0,_create:function(){var e,i,s,n=this.element[0].nodeName.toLowerCase(),o="textarea"===n,a="input"===n;this.isMultiLine=o||!a&&this._isContentEditable(this.element),this.valueMethod=this.element[o||a?"val":"text"],this.isNewMenu=!0,this._addClass("ui-autocomplete-input"),this.element.attr("autocomplete","off"),this._on(this.element,{keydown:function(n){if(this.element.prop("readOnly"))return e=!0,s=!0,i=!0,void 0;e=!1,s=!1,i=!1;var o=t.ui.keyCode;switch(n.keyCode){case o.PAGE_UP:e=!0,this._move("previousPage",n);break;case o.PAGE_DOWN:e=!0,this._move("nextPage",n);break;case o.UP:e=!0,this._keyEvent("previous",n);break;case o.DOWN:e=!0,this._keyEvent("next",n);break;case o.ENTER:this.menu.active&&(e=!0,n.preventDefault(),this.menu.select(n));break;case o.TAB:this.menu.active&&this.menu.select(n);break;case o.ESCAPE:this.menu.element.is(":visible")&&(this.isMultiLine||this._value(this.term),this.close(n),n.preventDefault());break;default:i=!0,this._searchTimeout(n)}},keypress:function(s){if(e)return e=!1,(!this.isMultiLine||this.menu.element.is(":visible"))&&s.preventDefault(),void 0;if(!i){var n=t.ui.keyCode;switch(s.keyCode){case n.PAGE_UP:this._move("previousPage",s);break;case n.PAGE_DOWN:this._move("nextPage",s);break;case n.UP:this._keyEvent("previous",s);break;case n.DOWN:this._keyEvent("next",s)}}},input:function(t){return s?(s=!1,t.preventDefault(),void 0):(this._searchTimeout(t),void 0)},focus:function(){this.selectedItem=null,this.previous=this._value()},blur:function(t){return this.cancelBlur?(delete this.cancelBlur,void 0):(clearTimeout(this.searching),this.close(t),this._change(t),void 0)}}),this._initSource(),this.menu=t("
    UserAPI-keys{$lng['login']['username']}API-key SecretAllowed fromValid until{$lng['apikeys']['allowed_from']}{$lng['apikeys']['valid_until']} {$lng['panel']['options']}
     
     
    "+"",P=u?"":"",w=0;7>w;w++)M=(w+c)%7,P+="";for(T+=P+"",S=this._getDaysInMonth(te,Z),te===t.selectedYear&&Z===t.selectedMonth&&(t.selectedDay=Math.min(t.selectedDay,S)),H=(this._getFirstDayOfMonth(te,Z)-c+7)%7,z=Math.ceil((H+S)/7),O=X?this.maxRows>z?this.maxRows:z:z,this.maxRows=O,A=this._daylightSavingAdjust(new Date(te,Z,1-H)),N=0;O>N;N++){for(T+="",W=u?"":"",w=0;7>w;w++)E=m?m.apply(t.input?t.input[0]:null,[A]):[!0,""],F=A.getMonth()!==Z,L=F&&!v||!E[0]||Q&&Q>A||J&&A>J,W+="",A.setDate(A.getDate()+1),A=this._daylightSavingAdjust(A);T+=W+""}Z++,Z>11&&(Z=0,te++),T+="
    "+this._get(t,"weekHeader")+"=5?" class='ui-datepicker-week-end'":"")+">"+""+p[M]+"
    "+this._get(t,"calculateWeek")(A)+""+(F&&!_?" ":L?""+A.getDate()+"":""+A.getDate()+"")+"
    "+(X?""+(U[0]>0&&C===U[1]-1?"
    ":""):""),x+=T}y+=x}return y+=l,t._keyEvent=!1,y},_generateMonthYearHeader:function(t,e,i,s,n,o,a,r){var h,l,c,u,d,p,f,g,m=this._get(t,"changeMonth"),_=this._get(t,"changeYear"),v=this._get(t,"showMonthAfterYear"),b="
    ",y="";if(o||!m)y+=""+a[e]+"";else{for(h=s&&s.getFullYear()===i,l=n&&n.getFullYear()===i,y+=""}if(v||(b+=y+(!o&&m&&_?"":" ")),!t.yearshtml)if(t.yearshtml="",o||!_)b+=""+i+"";else{for(u=this._get(t,"yearRange").split(":"),d=(new Date).getFullYear(),p=function(t){var e=t.match(/c[+\-].*/)?i+parseInt(t.substring(1),10):t.match(/[+\-].*/)?d+parseInt(t,10):parseInt(t,10);return isNaN(e)?d:e},f=p(u[0]),g=Math.max(f,p(u[1]||"")),f=s?Math.max(f,s.getFullYear()):f,g=n?Math.min(g,n.getFullYear()):g,t.yearshtml+="",b+=t.yearshtml,t.yearshtml=null}return b+=this._get(t,"yearSuffix"),v&&(b+=(!o&&m&&_?"":" ")+y),b+="
    "},_adjustInstDate:function(t,e,i){var s=t.selectedYear+("Y"===i?e:0),n=t.selectedMonth+("M"===i?e:0),o=Math.min(t.selectedDay,this._getDaysInMonth(s,n))+("D"===i?e:0),a=this._restrictMinMax(t,this._daylightSavingAdjust(new Date(s,n,o)));t.selectedDay=a.getDate(),t.drawMonth=t.selectedMonth=a.getMonth(),t.drawYear=t.selectedYear=a.getFullYear(),("M"===i||"Y"===i)&&this._notifyChange(t)},_restrictMinMax:function(t,e){var i=this._getMinMaxDate(t,"min"),s=this._getMinMaxDate(t,"max"),n=i&&i>e?i:e;return s&&n>s?s:n},_notifyChange:function(t){var e=this._get(t,"onChangeMonthYear");e&&e.apply(t.input?t.input[0]:null,[t.selectedYear,t.selectedMonth+1,t])},_getNumberOfMonths:function(t){var e=this._get(t,"numberOfMonths");return null==e?[1,1]:"number"==typeof e?[1,e]:e},_getMinMaxDate:function(t,e){return this._determineDate(t,this._get(t,e+"Date"),null)},_getDaysInMonth:function(t,e){return 32-this._daylightSavingAdjust(new Date(t,e,32)).getDate()},_getFirstDayOfMonth:function(t,e){return new Date(t,e,1).getDay()},_canAdjustMonth:function(t,e,i,s){var n=this._getNumberOfMonths(t),o=this._daylightSavingAdjust(new Date(i,s+(0>e?e:n[0]*n[1]),1));return 0>e&&o.setDate(this._getDaysInMonth(o.getFullYear(),o.getMonth())),this._isInRange(t,o)},_isInRange:function(t,e){var i,s,n=this._getMinMaxDate(t,"min"),o=this._getMinMaxDate(t,"max"),a=null,r=null,h=this._get(t,"yearRange");return h&&(i=h.split(":"),s=(new Date).getFullYear(),a=parseInt(i[0],10),r=parseInt(i[1],10),i[0].match(/[+\-].*/)&&(a+=s),i[1].match(/[+\-].*/)&&(r+=s)),(!n||e.getTime()>=n.getTime())&&(!o||e.getTime()<=o.getTime())&&(!a||e.getFullYear()>=a)&&(!r||r>=e.getFullYear())},_getFormatConfig:function(t){var e=this._get(t,"shortYearCutoff");return e="string"!=typeof e?e:(new Date).getFullYear()%100+parseInt(e,10),{shortYearCutoff:e,dayNamesShort:this._get(t,"dayNamesShort"),dayNames:this._get(t,"dayNames"),monthNamesShort:this._get(t,"monthNamesShort"),monthNames:this._get(t,"monthNames")}},_formatDate:function(t,e,i,s){e||(t.currentDay=t.selectedDay,t.currentMonth=t.selectedMonth,t.currentYear=t.selectedYear);var n=e?"object"==typeof e?e:this._daylightSavingAdjust(new Date(s,i,e)):this._daylightSavingAdjust(new Date(t.currentYear,t.currentMonth,t.currentDay));return this.formatDate(this._get(t,"dateFormat"),n,this._getFormatConfig(t))}}),t.fn.datepicker=function(e){if(!this.length)return this;t.datepicker.initialized||(t(document).on("mousedown",t.datepicker._checkExternalClick),t.datepicker.initialized=!0),0===t("#"+t.datepicker._mainDivId).length&&t("body").append(t.datepicker.dpDiv);var i=Array.prototype.slice.call(arguments,1);return"string"!=typeof e||"isDisabled"!==e&&"getDate"!==e&&"widget"!==e?"option"===e&&2===arguments.length&&"string"==typeof arguments[1]?t.datepicker["_"+e+"Datepicker"].apply(t.datepicker,[this[0]].concat(i)):this.each(function(){"string"==typeof e?t.datepicker["_"+e+"Datepicker"].apply(t.datepicker,[this].concat(i)):t.datepicker._attachDatepicker(this,e)}):t.datepicker["_"+e+"Datepicker"].apply(t.datepicker,[this[0]].concat(i))},t.datepicker=new s,t.datepicker.initialized=!1,t.datepicker.uuid=(new Date).getTime(),t.datepicker.version="1.12.1",t.datepicker,t.widget("ui.dialog",{version:"1.12.1",options:{appendTo:"body",autoOpen:!0,buttons:[],classes:{"ui-dialog":"ui-corner-all","ui-dialog-titlebar":"ui-corner-all"},closeOnEscape:!0,closeText:"Close",draggable:!0,hide:null,height:"auto",maxHeight:null,maxWidth:null,minHeight:150,minWidth:150,modal:!1,position:{my:"center",at:"center",of:window,collision:"fit",using:function(e){var i=t(this).css(e).offset().top;0>i&&t(this).css("top",e.top-i)}},resizable:!0,show:null,title:null,width:300,beforeClose:null,close:null,drag:null,dragStart:null,dragStop:null,focus:null,open:null,resize:null,resizeStart:null,resizeStop:null},sizeRelatedOptions:{buttons:!0,height:!0,maxHeight:!0,maxWidth:!0,minHeight:!0,minWidth:!0,width:!0},resizableRelatedOptions:{maxHeight:!0,maxWidth:!0,minHeight:!0,minWidth:!0},_create:function(){this.originalCss={display:this.element[0].style.display,width:this.element[0].style.width,minHeight:this.element[0].style.minHeight,maxHeight:this.element[0].style.maxHeight,height:this.element[0].style.height},this.originalPosition={parent:this.element.parent(),index:this.element.parent().children().index(this.element)},this.originalTitle=this.element.attr("title"),null==this.options.title&&null!=this.originalTitle&&(this.options.title=this.originalTitle),this.options.disabled&&(this.options.disabled=!1),this._createWrapper(),this.element.show().removeAttr("title").appendTo(this.uiDialog),this._addClass("ui-dialog-content","ui-widget-content"),this._createTitlebar(),this._createButtonPane(),this.options.draggable&&t.fn.draggable&&this._makeDraggable(),this.options.resizable&&t.fn.resizable&&this._makeResizable(),this._isOpen=!1,this._trackFocus()},_init:function(){this.options.autoOpen&&this.open()},_appendTo:function(){var e=this.options.appendTo;return e&&(e.jquery||e.nodeType)?t(e):this.document.find(e||"body").eq(0)},_destroy:function(){var t,e=this.originalPosition;this._untrackInstance(),this._destroyOverlay(),this.element.removeUniqueId().css(this.originalCss).detach(),this.uiDialog.remove(),this.originalTitle&&this.element.attr("title",this.originalTitle),t=e.parent.children().eq(e.index),t.length&&t[0]!==this.element[0]?t.before(this.element):e.parent.append(this.element)},widget:function(){return this.uiDialog},disable:t.noop,enable:t.noop,close:function(e){var i=this;this._isOpen&&this._trigger("beforeClose",e)!==!1&&(this._isOpen=!1,this._focusedElement=null,this._destroyOverlay(),this._untrackInstance(),this.opener.filter(":focusable").trigger("focus").length||t.ui.safeBlur(t.ui.safeActiveElement(this.document[0])),this._hide(this.uiDialog,this.options.hide,function(){i._trigger("close",e)}))},isOpen:function(){return this._isOpen},moveToTop:function(){this._moveToTop()},_moveToTop:function(e,i){var s=!1,n=this.uiDialog.siblings(".ui-front:visible").map(function(){return+t(this).css("z-index")}).get(),o=Math.max.apply(null,n);return o>=+this.uiDialog.css("z-index")&&(this.uiDialog.css("z-index",o+1),s=!0),s&&!i&&this._trigger("focus",e),s},open:function(){var e=this;return this._isOpen?(this._moveToTop()&&this._focusTabbable(),void 0):(this._isOpen=!0,this.opener=t(t.ui.safeActiveElement(this.document[0])),this._size(),this._position(),this._createOverlay(),this._moveToTop(null,!0),this.overlay&&this.overlay.css("z-index",this.uiDialog.css("z-index")-1),this._show(this.uiDialog,this.options.show,function(){e._focusTabbable(),e._trigger("focus")}),this._makeFocusTarget(),this._trigger("open"),void 0)},_focusTabbable:function(){var t=this._focusedElement;t||(t=this.element.find("[autofocus]")),t.length||(t=this.element.find(":tabbable")),t.length||(t=this.uiDialogButtonPane.find(":tabbable")),t.length||(t=this.uiDialogTitlebarClose.filter(":tabbable")),t.length||(t=this.uiDialog),t.eq(0).trigger("focus")},_keepFocus:function(e){function i(){var e=t.ui.safeActiveElement(this.document[0]),i=this.uiDialog[0]===e||t.contains(this.uiDialog[0],e);i||this._focusTabbable()}e.preventDefault(),i.call(this),this._delay(i)},_createWrapper:function(){this.uiDialog=t("
    ").hide().attr({tabIndex:-1,role:"dialog"}).appendTo(this._appendTo()),this._addClass(this.uiDialog,"ui-dialog","ui-widget ui-widget-content ui-front"),this._on(this.uiDialog,{keydown:function(e){if(this.options.closeOnEscape&&!e.isDefaultPrevented()&&e.keyCode&&e.keyCode===t.ui.keyCode.ESCAPE)return e.preventDefault(),this.close(e),void 0;if(e.keyCode===t.ui.keyCode.TAB&&!e.isDefaultPrevented()){var i=this.uiDialog.find(":tabbable"),s=i.filter(":first"),n=i.filter(":last");e.target!==n[0]&&e.target!==this.uiDialog[0]||e.shiftKey?e.target!==s[0]&&e.target!==this.uiDialog[0]||!e.shiftKey||(this._delay(function(){n.trigger("focus")}),e.preventDefault()):(this._delay(function(){s.trigger("focus")}),e.preventDefault())}},mousedown:function(t){this._moveToTop(t)&&this._focusTabbable()}}),this.element.find("[aria-describedby]").length||this.uiDialog.attr({"aria-describedby":this.element.uniqueId().attr("id")})},_createTitlebar:function(){var e;this.uiDialogTitlebar=t("
    "),this._addClass(this.uiDialogTitlebar,"ui-dialog-titlebar","ui-widget-header ui-helper-clearfix"),this._on(this.uiDialogTitlebar,{mousedown:function(e){t(e.target).closest(".ui-dialog-titlebar-close")||this.uiDialog.trigger("focus")}}),this.uiDialogTitlebarClose=t("").button({label:t("").text(this.options.closeText).html(),icon:"ui-icon-closethick",showLabel:!1}).appendTo(this.uiDialogTitlebar),this._addClass(this.uiDialogTitlebarClose,"ui-dialog-titlebar-close"),this._on(this.uiDialogTitlebarClose,{click:function(t){t.preventDefault(),this.close(t)}}),e=t("").uniqueId().prependTo(this.uiDialogTitlebar),this._addClass(e,"ui-dialog-title"),this._title(e),this.uiDialogTitlebar.prependTo(this.uiDialog),this.uiDialog.attr({"aria-labelledby":e.attr("id")})},_title:function(t){this.options.title?t.text(this.options.title):t.html(" ")},_createButtonPane:function(){this.uiDialogButtonPane=t("
    "),this._addClass(this.uiDialogButtonPane,"ui-dialog-buttonpane","ui-widget-content ui-helper-clearfix"),this.uiButtonSet=t("
    ").appendTo(this.uiDialogButtonPane),this._addClass(this.uiButtonSet,"ui-dialog-buttonset"),this._createButtons()},_createButtons:function(){var e=this,i=this.options.buttons;return this.uiDialogButtonPane.remove(),this.uiButtonSet.empty(),t.isEmptyObject(i)||t.isArray(i)&&!i.length?(this._removeClass(this.uiDialog,"ui-dialog-buttons"),void 0):(t.each(i,function(i,s){var n,o;s=t.isFunction(s)?{click:s,text:i}:s,s=t.extend({type:"button"},s),n=s.click,o={icon:s.icon,iconPosition:s.iconPosition,showLabel:s.showLabel,icons:s.icons,text:s.text},delete s.click,delete s.icon,delete s.iconPosition,delete s.showLabel,delete s.icons,"boolean"==typeof s.text&&delete s.text,t("",s).button(o).appendTo(e.uiButtonSet).on("click",function(){n.apply(e.element[0],arguments)})}),this._addClass(this.uiDialog,"ui-dialog-buttons"),this.uiDialogButtonPane.appendTo(this.uiDialog),void 0)},_makeDraggable:function(){function e(t){return{position:t.position,offset:t.offset}}var i=this,s=this.options;this.uiDialog.draggable({cancel:".ui-dialog-content, .ui-dialog-titlebar-close",handle:".ui-dialog-titlebar",containment:"document",start:function(s,n){i._addClass(t(this),"ui-dialog-dragging"),i._blockFrames(),i._trigger("dragStart",s,e(n))},drag:function(t,s){i._trigger("drag",t,e(s))},stop:function(n,o){var a=o.offset.left-i.document.scrollLeft(),r=o.offset.top-i.document.scrollTop();s.position={my:"left top",at:"left"+(a>=0?"+":"")+a+" "+"top"+(r>=0?"+":"")+r,of:i.window},i._removeClass(t(this),"ui-dialog-dragging"),i._unblockFrames(),i._trigger("dragStop",n,e(o))}})},_makeResizable:function(){function e(t){return{originalPosition:t.originalPosition,originalSize:t.originalSize,position:t.position,size:t.size}}var i=this,s=this.options,n=s.resizable,o=this.uiDialog.css("position"),a="string"==typeof n?n:"n,e,s,w,se,sw,ne,nw";this.uiDialog.resizable({cancel:".ui-dialog-content",containment:"document",alsoResize:this.element,maxWidth:s.maxWidth,maxHeight:s.maxHeight,minWidth:s.minWidth,minHeight:this._minHeight(),handles:a,start:function(s,n){i._addClass(t(this),"ui-dialog-resizing"),i._blockFrames(),i._trigger("resizeStart",s,e(n))},resize:function(t,s){i._trigger("resize",t,e(s))},stop:function(n,o){var a=i.uiDialog.offset(),r=a.left-i.document.scrollLeft(),h=a.top-i.document.scrollTop();s.height=i.uiDialog.height(),s.width=i.uiDialog.width(),s.position={my:"left top",at:"left"+(r>=0?"+":"")+r+" "+"top"+(h>=0?"+":"")+h,of:i.window},i._removeClass(t(this),"ui-dialog-resizing"),i._unblockFrames(),i._trigger("resizeStop",n,e(o))}}).css("position",o)},_trackFocus:function(){this._on(this.widget(),{focusin:function(e){this._makeFocusTarget(),this._focusedElement=t(e.target)}})},_makeFocusTarget:function(){this._untrackInstance(),this._trackingInstances().unshift(this)},_untrackInstance:function(){var e=this._trackingInstances(),i=t.inArray(this,e);-1!==i&&e.splice(i,1)},_trackingInstances:function(){var t=this.document.data("ui-dialog-instances");return t||(t=[],this.document.data("ui-dialog-instances",t)),t},_minHeight:function(){var t=this.options;return"auto"===t.height?t.minHeight:Math.min(t.minHeight,t.height)},_position:function(){var t=this.uiDialog.is(":visible");t||this.uiDialog.show(),this.uiDialog.position(this.options.position),t||this.uiDialog.hide()},_setOptions:function(e){var i=this,s=!1,n={};t.each(e,function(t,e){i._setOption(t,e),t in i.sizeRelatedOptions&&(s=!0),t in i.resizableRelatedOptions&&(n[t]=e)}),s&&(this._size(),this._position()),this.uiDialog.is(":data(ui-resizable)")&&this.uiDialog.resizable("option",n)},_setOption:function(e,i){var s,n,o=this.uiDialog;"disabled"!==e&&(this._super(e,i),"appendTo"===e&&this.uiDialog.appendTo(this._appendTo()),"buttons"===e&&this._createButtons(),"closeText"===e&&this.uiDialogTitlebarClose.button({label:t("").text(""+this.options.closeText).html()}),"draggable"===e&&(s=o.is(":data(ui-draggable)"),s&&!i&&o.draggable("destroy"),!s&&i&&this._makeDraggable()),"position"===e&&this._position(),"resizable"===e&&(n=o.is(":data(ui-resizable)"),n&&!i&&o.resizable("destroy"),n&&"string"==typeof i&&o.resizable("option","handles",i),n||i===!1||this._makeResizable()),"title"===e&&this._title(this.uiDialogTitlebar.find(".ui-dialog-title")))},_size:function(){var t,e,i,s=this.options;this.element.show().css({width:"auto",minHeight:0,maxHeight:"none",height:0}),s.minWidth>s.width&&(s.width=s.minWidth),t=this.uiDialog.css({height:"auto",width:s.width}).outerHeight(),e=Math.max(0,s.minHeight-t),i="number"==typeof s.maxHeight?Math.max(0,s.maxHeight-t):"none","auto"===s.height?this.element.css({minHeight:e,maxHeight:i,height:"auto"}):this.element.height(Math.max(0,s.height-t)),this.uiDialog.is(":data(ui-resizable)")&&this.uiDialog.resizable("option","minHeight",this._minHeight())},_blockFrames:function(){this.iframeBlocks=this.document.find("iframe").map(function(){var e=t(this);return t("
    ").css({position:"absolute",width:e.outerWidth(),height:e.outerHeight()}).appendTo(e.parent()).offset(e.offset())[0]})},_unblockFrames:function(){this.iframeBlocks&&(this.iframeBlocks.remove(),delete this.iframeBlocks)},_allowInteraction:function(e){return t(e.target).closest(".ui-dialog").length?!0:!!t(e.target).closest(".ui-datepicker").length},_createOverlay:function(){if(this.options.modal){var e=!0;this._delay(function(){e=!1}),this.document.data("ui-dialog-overlays")||this._on(this.document,{focusin:function(t){e||this._allowInteraction(t)||(t.preventDefault(),this._trackingInstances()[0]._focusTabbable())}}),this.overlay=t("
    ").appendTo(this._appendTo()),this._addClass(this.overlay,null,"ui-widget-overlay ui-front"),this._on(this.overlay,{mousedown:"_keepFocus"}),this.document.data("ui-dialog-overlays",(this.document.data("ui-dialog-overlays")||0)+1)}},_destroyOverlay:function(){if(this.options.modal&&this.overlay){var t=this.document.data("ui-dialog-overlays")-1;t?this.document.data("ui-dialog-overlays",t):(this._off(this.document,"focusin"),this.document.removeData("ui-dialog-overlays")),this.overlay.remove(),this.overlay=null}}}),t.uiBackCompat!==!1&&t.widget("ui.dialog",t.ui.dialog,{options:{dialogClass:""},_createWrapper:function(){this._super(),this.uiDialog.addClass(this.options.dialogClass)},_setOption:function(t,e){"dialogClass"===t&&this.uiDialog.removeClass(this.options.dialogClass).addClass(e),this._superApply(arguments)}}),t.ui.dialog,t.widget("ui.progressbar",{version:"1.12.1",options:{classes:{"ui-progressbar":"ui-corner-all","ui-progressbar-value":"ui-corner-left","ui-progressbar-complete":"ui-corner-right"},max:100,value:0,change:null,complete:null},min:0,_create:function(){this.oldValue=this.options.value=this._constrainedValue(),this.element.attr({role:"progressbar","aria-valuemin":this.min}),this._addClass("ui-progressbar","ui-widget ui-widget-content"),this.valueDiv=t("
    ").appendTo(this.element),this._addClass(this.valueDiv,"ui-progressbar-value","ui-widget-header"),this._refreshValue()},_destroy:function(){this.element.removeAttr("role aria-valuemin aria-valuemax aria-valuenow"),this.valueDiv.remove()},value:function(t){return void 0===t?this.options.value:(this.options.value=this._constrainedValue(t),this._refreshValue(),void 0)},_constrainedValue:function(t){return void 0===t&&(t=this.options.value),this.indeterminate=t===!1,"number"!=typeof t&&(t=0),this.indeterminate?!1:Math.min(this.options.max,Math.max(this.min,t))},_setOptions:function(t){var e=t.value;delete t.value,this._super(t),this.options.value=this._constrainedValue(e),this._refreshValue()},_setOption:function(t,e){"max"===t&&(e=Math.max(this.min,e)),this._super(t,e)},_setOptionDisabled:function(t){this._super(t),this.element.attr("aria-disabled",t),this._toggleClass(null,"ui-state-disabled",!!t)},_percentage:function(){return this.indeterminate?100:100*(this.options.value-this.min)/(this.options.max-this.min)},_refreshValue:function(){var e=this.options.value,i=this._percentage();this.valueDiv.toggle(this.indeterminate||e>this.min).width(i.toFixed(0)+"%"),this._toggleClass(this.valueDiv,"ui-progressbar-complete",null,e===this.options.max)._toggleClass("ui-progressbar-indeterminate",null,this.indeterminate),this.indeterminate?(this.element.removeAttr("aria-valuenow"),this.overlayDiv||(this.overlayDiv=t("
    ").appendTo(this.valueDiv),this._addClass(this.overlayDiv,"ui-progressbar-overlay"))):(this.element.attr({"aria-valuemax":this.options.max,"aria-valuenow":e}),this.overlayDiv&&(this.overlayDiv.remove(),this.overlayDiv=null)),this.oldValue!==e&&(this.oldValue=e,this._trigger("change")),e===this.options.max&&this._trigger("complete")}}),t.widget("ui.selectmenu",[t.ui.formResetMixin,{version:"1.12.1",defaultElement:"",widgetEventPrefix:"spin",options:{classes:{"ui-spinner":"ui-corner-all","ui-spinner-down":"ui-corner-br","ui-spinner-up":"ui-corner-tr"},culture:null,icons:{down:"ui-icon-triangle-1-s",up:"ui-icon-triangle-1-n"},incremental:!0,max:null,min:null,numberFormat:null,page:10,step:1,change:null,spin:null,start:null,stop:null},_create:function(){this._setOption("max",this.options.max),this._setOption("min",this.options.min),this._setOption("step",this.options.step),""!==this.value()&&this._value(this.element.val(),!0),this._draw(),this._on(this._events),this._refresh(),this._on(this.window,{beforeunload:function(){this.element.removeAttr("autocomplete")}})},_getCreateOptions:function(){var e=this._super(),i=this.element;return t.each(["min","max","step"],function(t,s){var n=i.attr(s);null!=n&&n.length&&(e[s]=n)}),e},_events:{keydown:function(t){this._start(t)&&this._keydown(t)&&t.preventDefault()},keyup:"_stop",focus:function(){this.previous=this.element.val()},blur:function(t){return this.cancelBlur?(delete this.cancelBlur,void 0):(this._stop(),this._refresh(),this.previous!==this.element.val()&&this._trigger("change",t),void 0)},mousewheel:function(t,e){if(e){if(!this.spinning&&!this._start(t))return!1;this._spin((e>0?1:-1)*this.options.step,t),clearTimeout(this.mousewheelTimer),this.mousewheelTimer=this._delay(function(){this.spinning&&this._stop(t)},100),t.preventDefault()}},"mousedown .ui-spinner-button":function(e){function i(){var e=this.element[0]===t.ui.safeActiveElement(this.document[0]);e||(this.element.trigger("focus"),this.previous=s,this._delay(function(){this.previous=s}))}var s;s=this.element[0]===t.ui.safeActiveElement(this.document[0])?this.previous:this.element.val(),e.preventDefault(),i.call(this),this.cancelBlur=!0,this._delay(function(){delete this.cancelBlur,i.call(this)}),this._start(e)!==!1&&this._repeat(null,t(e.currentTarget).hasClass("ui-spinner-up")?1:-1,e)},"mouseup .ui-spinner-button":"_stop","mouseenter .ui-spinner-button":function(e){return t(e.currentTarget).hasClass("ui-state-active")?this._start(e)===!1?!1:(this._repeat(null,t(e.currentTarget).hasClass("ui-spinner-up")?1:-1,e),void 0):void 0},"mouseleave .ui-spinner-button":"_stop"},_enhance:function(){this.uiSpinner=this.element.attr("autocomplete","off").wrap("").parent().append("")},_draw:function(){this._enhance(),this._addClass(this.uiSpinner,"ui-spinner","ui-widget ui-widget-content"),this._addClass("ui-spinner-input"),this.element.attr("role","spinbutton"),this.buttons=this.uiSpinner.children("a").attr("tabIndex",-1).attr("aria-hidden",!0).button({classes:{"ui-button":""}}),this._removeClass(this.buttons,"ui-corner-all"),this._addClass(this.buttons.first(),"ui-spinner-button ui-spinner-up"),this._addClass(this.buttons.last(),"ui-spinner-button ui-spinner-down"),this.buttons.first().button({icon:this.options.icons.up,showLabel:!1}),this.buttons.last().button({icon:this.options.icons.down,showLabel:!1}),this.buttons.height()>Math.ceil(.5*this.uiSpinner.height())&&this.uiSpinner.height()>0&&this.uiSpinner.height(this.uiSpinner.height())},_keydown:function(e){var i=this.options,s=t.ui.keyCode;switch(e.keyCode){case s.UP:return this._repeat(null,1,e),!0;case s.DOWN:return this._repeat(null,-1,e),!0;case s.PAGE_UP:return this._repeat(null,i.page,e),!0;case s.PAGE_DOWN:return this._repeat(null,-i.page,e),!0}return!1},_start:function(t){return this.spinning||this._trigger("start",t)!==!1?(this.counter||(this.counter=1),this.spinning=!0,!0):!1},_repeat:function(t,e,i){t=t||500,clearTimeout(this.timer),this.timer=this._delay(function(){this._repeat(40,e,i)},t),this._spin(e*this.options.step,i)},_spin:function(t,e){var i=this.value()||0;this.counter||(this.counter=1),i=this._adjustValue(i+t*this._increment(this.counter)),this.spinning&&this._trigger("spin",e,{value:i})===!1||(this._value(i),this.counter++)},_increment:function(e){var i=this.options.incremental;return i?t.isFunction(i)?i(e):Math.floor(e*e*e/5e4-e*e/500+17*e/200+1):1},_precision:function(){var t=this._precisionOf(this.options.step);return null!==this.options.min&&(t=Math.max(t,this._precisionOf(this.options.min))),t},_precisionOf:function(t){var e=""+t,i=e.indexOf(".");return-1===i?0:e.length-i-1},_adjustValue:function(t){var e,i,s=this.options;return e=null!==s.min?s.min:0,i=t-e,i=Math.round(i/s.step)*s.step,t=e+i,t=parseFloat(t.toFixed(this._precision())),null!==s.max&&t>s.max?s.max:null!==s.min&&s.min>t?s.min:t},_stop:function(t){this.spinning&&(clearTimeout(this.timer),clearTimeout(this.mousewheelTimer),this.counter=0,this.spinning=!1,this._trigger("stop",t))},_setOption:function(t,e){var i,s,n;return"culture"===t||"numberFormat"===t?(i=this._parse(this.element.val()),this.options[t]=e,this.element.val(this._format(i)),void 0):(("max"===t||"min"===t||"step"===t)&&"string"==typeof e&&(e=this._parse(e)),"icons"===t&&(s=this.buttons.first().find(".ui-icon"),this._removeClass(s,null,this.options.icons.up),this._addClass(s,null,e.up),n=this.buttons.last().find(".ui-icon"),this._removeClass(n,null,this.options.icons.down),this._addClass(n,null,e.down)),this._super(t,e),void 0)},_setOptionDisabled:function(t){this._super(t),this._toggleClass(this.uiSpinner,null,"ui-state-disabled",!!t),this.element.prop("disabled",!!t),this.buttons.button(t?"disable":"enable")},_setOptions:r(function(t){this._super(t)}),_parse:function(t){return"string"==typeof t&&""!==t&&(t=window.Globalize&&this.options.numberFormat?Globalize.parseFloat(t,10,this.options.culture):+t),""===t||isNaN(t)?null:t},_format:function(t){return""===t?"":window.Globalize&&this.options.numberFormat?Globalize.format(t,this.options.numberFormat,this.options.culture):t},_refresh:function(){this.element.attr({"aria-valuemin":this.options.min,"aria-valuemax":this.options.max,"aria-valuenow":this._parse(this.element.val())})},isValid:function(){var t=this.value();return null===t?!1:t===this._adjustValue(t)},_value:function(t,e){var i;""!==t&&(i=this._parse(t),null!==i&&(e||(i=this._adjustValue(i)),t=this._format(i))),this.element.val(t),this._refresh()},_destroy:function(){this.element.prop("disabled",!1).removeAttr("autocomplete role aria-valuemin aria-valuemax aria-valuenow"),this.uiSpinner.replaceWith(this.element)},stepUp:r(function(t){this._stepUp(t)}),_stepUp:function(t){this._start()&&(this._spin((t||1)*this.options.step),this._stop())},stepDown:r(function(t){this._stepDown(t)}),_stepDown:function(t){this._start()&&(this._spin((t||1)*-this.options.step),this._stop())},pageUp:r(function(t){this._stepUp((t||1)*this.options.page)}),pageDown:r(function(t){this._stepDown((t||1)*this.options.page)}),value:function(t){return arguments.length?(r(this._value).call(this,t),void 0):this._parse(this.element.val())},widget:function(){return this.uiSpinner}}),t.uiBackCompat!==!1&&t.widget("ui.spinner",t.ui.spinner,{_enhance:function(){this.uiSpinner=this.element.attr("autocomplete","off").wrap(this._uiSpinnerHtml()).parent().append(this._buttonHtml())},_uiSpinnerHtml:function(){return""},_buttonHtml:function(){return""}}),t.ui.spinner,t.widget("ui.tabs",{version:"1.12.1",delay:300,options:{active:null,classes:{"ui-tabs":"ui-corner-all","ui-tabs-nav":"ui-corner-all","ui-tabs-panel":"ui-corner-bottom","ui-tabs-tab":"ui-corner-top"},collapsible:!1,event:"click",heightStyle:"content",hide:null,show:null,activate:null,beforeActivate:null,beforeLoad:null,load:null},_isLocal:function(){var t=/#.*$/;return function(e){var i,s;i=e.href.replace(t,""),s=location.href.replace(t,"");try{i=decodeURIComponent(i)}catch(n){}try{s=decodeURIComponent(s)}catch(n){}return e.hash.length>1&&i===s}}(),_create:function(){var e=this,i=this.options;this.running=!1,this._addClass("ui-tabs","ui-widget ui-widget-content"),this._toggleClass("ui-tabs-collapsible",null,i.collapsible),this._processTabs(),i.active=this._initialActive(),t.isArray(i.disabled)&&(i.disabled=t.unique(i.disabled.concat(t.map(this.tabs.filter(".ui-state-disabled"),function(t){return e.tabs.index(t)}))).sort()),this.active=this.options.active!==!1&&this.anchors.length?this._findActive(i.active):t(),this._refresh(),this.active.length&&this.load(i.active)},_initialActive:function(){var e=this.options.active,i=this.options.collapsible,s=location.hash.substring(1);return null===e&&(s&&this.tabs.each(function(i,n){return t(n).attr("aria-controls")===s?(e=i,!1):void 0}),null===e&&(e=this.tabs.index(this.tabs.filter(".ui-tabs-active"))),(null===e||-1===e)&&(e=this.tabs.length?0:!1)),e!==!1&&(e=this.tabs.index(this.tabs.eq(e)),-1===e&&(e=i?!1:0)),!i&&e===!1&&this.anchors.length&&(e=0),e},_getCreateEventData:function(){return{tab:this.active,panel:this.active.length?this._getPanelForTab(this.active):t()}},_tabKeydown:function(e){var i=t(t.ui.safeActiveElement(this.document[0])).closest("li"),s=this.tabs.index(i),n=!0;if(!this._handlePageNav(e)){switch(e.keyCode){case t.ui.keyCode.RIGHT:case t.ui.keyCode.DOWN:s++;break;case t.ui.keyCode.UP:case t.ui.keyCode.LEFT:n=!1,s--;break;case t.ui.keyCode.END:s=this.anchors.length-1;break;case t.ui.keyCode.HOME:s=0;break;case t.ui.keyCode.SPACE:return e.preventDefault(),clearTimeout(this.activating),this._activate(s),void 0;case t.ui.keyCode.ENTER:return e.preventDefault(),clearTimeout(this.activating),this._activate(s===this.options.active?!1:s),void 0;default:return}e.preventDefault(),clearTimeout(this.activating),s=this._focusNextTab(s,n),e.ctrlKey||e.metaKey||(i.attr("aria-selected","false"),this.tabs.eq(s).attr("aria-selected","true"),this.activating=this._delay(function(){this.option("active",s)},this.delay))}},_panelKeydown:function(e){this._handlePageNav(e)||e.ctrlKey&&e.keyCode===t.ui.keyCode.UP&&(e.preventDefault(),this.active.trigger("focus"))},_handlePageNav:function(e){return e.altKey&&e.keyCode===t.ui.keyCode.PAGE_UP?(this._activate(this._focusNextTab(this.options.active-1,!1)),!0):e.altKey&&e.keyCode===t.ui.keyCode.PAGE_DOWN?(this._activate(this._focusNextTab(this.options.active+1,!0)),!0):void 0},_findNextTab:function(e,i){function s(){return e>n&&(e=0),0>e&&(e=n),e}for(var n=this.tabs.length-1;-1!==t.inArray(s(),this.options.disabled);)e=i?e+1:e-1;return e},_focusNextTab:function(t,e){return t=this._findNextTab(t,e),this.tabs.eq(t).trigger("focus"),t},_setOption:function(t,e){return"active"===t?(this._activate(e),void 0):(this._super(t,e),"collapsible"===t&&(this._toggleClass("ui-tabs-collapsible",null,e),e||this.options.active!==!1||this._activate(0)),"event"===t&&this._setupEvents(e),"heightStyle"===t&&this._setupHeightStyle(e),void 0)},_sanitizeSelector:function(t){return t?t.replace(/[!"$%&'()*+,.\/:;<=>?@\[\]\^`{|}~]/g,"\\$&"):""},refresh:function(){var e=this.options,i=this.tablist.children(":has(a[href])");e.disabled=t.map(i.filter(".ui-state-disabled"),function(t){return i.index(t)}),this._processTabs(),e.active!==!1&&this.anchors.length?this.active.length&&!t.contains(this.tablist[0],this.active[0])?this.tabs.length===e.disabled.length?(e.active=!1,this.active=t()):this._activate(this._findNextTab(Math.max(0,e.active-1),!1)):e.active=this.tabs.index(this.active):(e.active=!1,this.active=t()),this._refresh()},_refresh:function(){this._setOptionDisabled(this.options.disabled),this._setupEvents(this.options.event),this._setupHeightStyle(this.options.heightStyle),this.tabs.not(this.active).attr({"aria-selected":"false","aria-expanded":"false",tabIndex:-1}),this.panels.not(this._getPanelForTab(this.active)).hide().attr({"aria-hidden":"true"}),this.active.length?(this.active.attr({"aria-selected":"true","aria-expanded":"true",tabIndex:0}),this._addClass(this.active,"ui-tabs-active","ui-state-active"),this._getPanelForTab(this.active).show().attr({"aria-hidden":"false"})):this.tabs.eq(0).attr("tabIndex",0)},_processTabs:function(){var e=this,i=this.tabs,s=this.anchors,n=this.panels;this.tablist=this._getList().attr("role","tablist"),this._addClass(this.tablist,"ui-tabs-nav","ui-helper-reset ui-helper-clearfix ui-widget-header"),this.tablist.on("mousedown"+this.eventNamespace,"> li",function(e){t(this).is(".ui-state-disabled")&&e.preventDefault()}).on("focus"+this.eventNamespace,".ui-tabs-anchor",function(){t(this).closest("li").is(".ui-state-disabled")&&this.blur()}),this.tabs=this.tablist.find("> li:has(a[href])").attr({role:"tab",tabIndex:-1}),this._addClass(this.tabs,"ui-tabs-tab","ui-state-default"),this.anchors=this.tabs.map(function(){return t("a",this)[0]}).attr({role:"presentation",tabIndex:-1}),this._addClass(this.anchors,"ui-tabs-anchor"),this.panels=t(),this.anchors.each(function(i,s){var n,o,a,r=t(s).uniqueId().attr("id"),h=t(s).closest("li"),l=h.attr("aria-controls");e._isLocal(s)?(n=s.hash,a=n.substring(1),o=e.element.find(e._sanitizeSelector(n))):(a=h.attr("aria-controls")||t({}).uniqueId()[0].id,n="#"+a,o=e.element.find(n),o.length||(o=e._createPanel(a),o.insertAfter(e.panels[i-1]||e.tablist)),o.attr("aria-live","polite")),o.length&&(e.panels=e.panels.add(o)),l&&h.data("ui-tabs-aria-controls",l),h.attr({"aria-controls":a,"aria-labelledby":r}),o.attr("aria-labelledby",r)}),this.panels.attr("role","tabpanel"),this._addClass(this.panels,"ui-tabs-panel","ui-widget-content"),i&&(this._off(i.not(this.tabs)),this._off(s.not(this.anchors)),this._off(n.not(this.panels)))},_getList:function(){return this.tablist||this.element.find("ol, ul").eq(0)},_createPanel:function(e){return t("
    ").attr("id",e).data("ui-tabs-destroy",!0)},_setOptionDisabled:function(e){var i,s,n;for(t.isArray(e)&&(e.length?e.length===this.anchors.length&&(e=!0):e=!1),n=0;s=this.tabs[n];n++)i=t(s),e===!0||-1!==t.inArray(n,e)?(i.attr("aria-disabled","true"),this._addClass(i,null,"ui-state-disabled")):(i.removeAttr("aria-disabled"),this._removeClass(i,null,"ui-state-disabled"));this.options.disabled=e,this._toggleClass(this.widget(),this.widgetFullName+"-disabled",null,e===!0)},_setupEvents:function(e){var i={};e&&t.each(e.split(" "),function(t,e){i[e]="_eventHandler"}),this._off(this.anchors.add(this.tabs).add(this.panels)),this._on(!0,this.anchors,{click:function(t){t.preventDefault()}}),this._on(this.anchors,i),this._on(this.tabs,{keydown:"_tabKeydown"}),this._on(this.panels,{keydown:"_panelKeydown"}),this._focusable(this.tabs),this._hoverable(this.tabs)},_setupHeightStyle:function(e){var i,s=this.element.parent();"fill"===e?(i=s.height(),i-=this.element.outerHeight()-this.element.height(),this.element.siblings(":visible").each(function(){var e=t(this),s=e.css("position");"absolute"!==s&&"fixed"!==s&&(i-=e.outerHeight(!0))}),this.element.children().not(this.panels).each(function(){i-=t(this).outerHeight(!0)}),this.panels.each(function(){t(this).height(Math.max(0,i-t(this).innerHeight()+t(this).height()))}).css("overflow","auto")):"auto"===e&&(i=0,this.panels.each(function(){i=Math.max(i,t(this).height("").height())}).height(i))},_eventHandler:function(e){var i=this.options,s=this.active,n=t(e.currentTarget),o=n.closest("li"),a=o[0]===s[0],r=a&&i.collapsible,h=r?t():this._getPanelForTab(o),l=s.length?this._getPanelForTab(s):t(),c={oldTab:s,oldPanel:l,newTab:r?t():o,newPanel:h};e.preventDefault(),o.hasClass("ui-state-disabled")||o.hasClass("ui-tabs-loading")||this.running||a&&!i.collapsible||this._trigger("beforeActivate",e,c)===!1||(i.active=r?!1:this.tabs.index(o),this.active=a?t():o,this.xhr&&this.xhr.abort(),l.length||h.length||t.error("jQuery UI Tabs: Mismatching fragment identifier."),h.length&&this.load(this.tabs.index(o),e),this._toggle(e,c))},_toggle:function(e,i){function s(){o.running=!1,o._trigger("activate",e,i)}function n(){o._addClass(i.newTab.closest("li"),"ui-tabs-active","ui-state-active"),a.length&&o.options.show?o._show(a,o.options.show,s):(a.show(),s())}var o=this,a=i.newPanel,r=i.oldPanel;this.running=!0,r.length&&this.options.hide?this._hide(r,this.options.hide,function(){o._removeClass(i.oldTab.closest("li"),"ui-tabs-active","ui-state-active"),n()}):(this._removeClass(i.oldTab.closest("li"),"ui-tabs-active","ui-state-active"),r.hide(),n()),r.attr("aria-hidden","true"),i.oldTab.attr({"aria-selected":"false","aria-expanded":"false"}),a.length&&r.length?i.oldTab.attr("tabIndex",-1):a.length&&this.tabs.filter(function(){return 0===t(this).attr("tabIndex")}).attr("tabIndex",-1),a.attr("aria-hidden","false"),i.newTab.attr({"aria-selected":"true","aria-expanded":"true",tabIndex:0})},_activate:function(e){var i,s=this._findActive(e);s[0]!==this.active[0]&&(s.length||(s=this.active),i=s.find(".ui-tabs-anchor")[0],this._eventHandler({target:i,currentTarget:i,preventDefault:t.noop}))},_findActive:function(e){return e===!1?t():this.tabs.eq(e)},_getIndex:function(e){return"string"==typeof e&&(e=this.anchors.index(this.anchors.filter("[href$='"+t.ui.escapeSelector(e)+"']"))),e},_destroy:function(){this.xhr&&this.xhr.abort(),this.tablist.removeAttr("role").off(this.eventNamespace),this.anchors.removeAttr("role tabIndex").removeUniqueId(),this.tabs.add(this.panels).each(function(){t.data(this,"ui-tabs-destroy")?t(this).remove():t(this).removeAttr("role tabIndex aria-live aria-busy aria-selected aria-labelledby aria-hidden aria-expanded")}),this.tabs.each(function(){var e=t(this),i=e.data("ui-tabs-aria-controls");i?e.attr("aria-controls",i).removeData("ui-tabs-aria-controls"):e.removeAttr("aria-controls")}),this.panels.show(),"content"!==this.options.heightStyle&&this.panels.css("height","")},enable:function(e){var i=this.options.disabled;i!==!1&&(void 0===e?i=!1:(e=this._getIndex(e),i=t.isArray(i)?t.map(i,function(t){return t!==e?t:null}):t.map(this.tabs,function(t,i){return i!==e?i:null})),this._setOptionDisabled(i))},disable:function(e){var i=this.options.disabled;if(i!==!0){if(void 0===e)i=!0;else{if(e=this._getIndex(e),-1!==t.inArray(e,i))return;i=t.isArray(i)?t.merge([e],i).sort():[e]}this._setOptionDisabled(i)}},load:function(e,i){e=this._getIndex(e);var s=this,n=this.tabs.eq(e),o=n.find(".ui-tabs-anchor"),a=this._getPanelForTab(n),r={tab:n,panel:a},h=function(t,e){"abort"===e&&s.panels.stop(!1,!0),s._removeClass(n,"ui-tabs-loading"),a.removeAttr("aria-busy"),t===s.xhr&&delete s.xhr};this._isLocal(o[0])||(this.xhr=t.ajax(this._ajaxSettings(o,i,r)),this.xhr&&"canceled"!==this.xhr.statusText&&(this._addClass(n,"ui-tabs-loading"),a.attr("aria-busy","true"),this.xhr.done(function(t,e,n){setTimeout(function(){a.html(t),s._trigger("load",i,r),h(n,e)},1)}).fail(function(t,e){setTimeout(function(){h(t,e)},1)})))},_ajaxSettings:function(e,i,s){var n=this;return{url:e.attr("href").replace(/#.*$/,""),beforeSend:function(e,o){return n._trigger("beforeLoad",i,t.extend({jqXHR:e,ajaxSettings:o},s))}}},_getPanelForTab:function(e){var i=t(e).attr("aria-controls");return this.element.find(this._sanitizeSelector("#"+i))}}),t.uiBackCompat!==!1&&t.widget("ui.tabs",t.ui.tabs,{_processTabs:function(){this._superApply(arguments),this._addClass(this.tabs,"ui-tab")}}),t.ui.tabs,t.widget("ui.tooltip",{version:"1.12.1",options:{classes:{"ui-tooltip":"ui-corner-all ui-widget-shadow"},content:function(){var e=t(this).attr("title")||"";return t("").text(e).html()},hide:!0,items:"[title]:not([disabled])",position:{my:"left top+15",at:"left bottom",collision:"flipfit flip"},show:!0,track:!1,close:null,open:null},_addDescribedBy:function(e,i){var s=(e.attr("aria-describedby")||"").split(/\s+/);s.push(i),e.data("ui-tooltip-id",i).attr("aria-describedby",t.trim(s.join(" ")))},_removeDescribedBy:function(e){var i=e.data("ui-tooltip-id"),s=(e.attr("aria-describedby")||"").split(/\s+/),n=t.inArray(i,s);-1!==n&&s.splice(n,1),e.removeData("ui-tooltip-id"),s=t.trim(s.join(" ")),s?e.attr("aria-describedby",s):e.removeAttr("aria-describedby")},_create:function(){this._on({mouseover:"open",focusin:"open"}),this.tooltips={},this.parents={},this.liveRegion=t("
    ").attr({role:"log","aria-live":"assertive","aria-relevant":"additions"}).appendTo(this.document[0].body),this._addClass(this.liveRegion,null,"ui-helper-hidden-accessible"),this.disabledTitles=t([])},_setOption:function(e,i){var s=this;this._super(e,i),"content"===e&&t.each(this.tooltips,function(t,e){s._updateContent(e.element)})},_setOptionDisabled:function(t){this[t?"_disable":"_enable"]()},_disable:function(){var e=this;t.each(this.tooltips,function(i,s){var n=t.Event("blur");n.target=n.currentTarget=s.element[0],e.close(n,!0)}),this.disabledTitles=this.disabledTitles.add(this.element.find(this.options.items).addBack().filter(function(){var e=t(this);return e.is("[title]")?e.data("ui-tooltip-title",e.attr("title")).removeAttr("title"):void 0}))},_enable:function(){this.disabledTitles.each(function(){var e=t(this);e.data("ui-tooltip-title")&&e.attr("title",e.data("ui-tooltip-title"))}),this.disabledTitles=t([])},open:function(e){var i=this,s=t(e?e.target:this.element).closest(this.options.items);s.length&&!s.data("ui-tooltip-id")&&(s.attr("title")&&s.data("ui-tooltip-title",s.attr("title")),s.data("ui-tooltip-open",!0),e&&"mouseover"===e.type&&s.parents().each(function(){var e,s=t(this);s.data("ui-tooltip-open")&&(e=t.Event("blur"),e.target=e.currentTarget=this,i.close(e,!0)),s.attr("title")&&(s.uniqueId(),i.parents[this.id]={element:this,title:s.attr("title")},s.attr("title",""))}),this._registerCloseHandlers(e,s),this._updateContent(s,e))},_updateContent:function(t,e){var i,s=this.options.content,n=this,o=e?e.type:null;return"string"==typeof s||s.nodeType||s.jquery?this._open(e,t,s):(i=s.call(t[0],function(i){n._delay(function(){t.data("ui-tooltip-open")&&(e&&(e.type=o),this._open(e,t,i))})}),i&&this._open(e,t,i),void 0)},_open:function(e,i,s){function n(t){l.of=t,a.is(":hidden")||a.position(l)}var o,a,r,h,l=t.extend({},this.options.position);if(s){if(o=this._find(i))return o.tooltip.find(".ui-tooltip-content").html(s),void 0;i.is("[title]")&&(e&&"mouseover"===e.type?i.attr("title",""):i.removeAttr("title")),o=this._tooltip(i),a=o.tooltip,this._addDescribedBy(i,a.attr("id")),a.find(".ui-tooltip-content").html(s),this.liveRegion.children().hide(),h=t("
    ").html(a.find(".ui-tooltip-content").html()),h.removeAttr("name").find("[name]").removeAttr("name"),h.removeAttr("id").find("[id]").removeAttr("id"),h.appendTo(this.liveRegion),this.options.track&&e&&/^mouse/.test(e.type)?(this._on(this.document,{mousemove:n}),n(e)):a.position(t.extend({of:i},this.options.position)),a.hide(),this._show(a,this.options.show),this.options.track&&this.options.show&&this.options.show.delay&&(r=this.delayedShow=setInterval(function(){a.is(":visible")&&(n(l.of),clearInterval(r))},t.fx.interval)),this._trigger("open",e,{tooltip:a})}},_registerCloseHandlers:function(e,i){var s={keyup:function(e){if(e.keyCode===t.ui.keyCode.ESCAPE){var s=t.Event(e);s.currentTarget=i[0],this.close(s,!0)}}};i[0]!==this.element[0]&&(s.remove=function(){this._removeTooltip(this._find(i).tooltip)}),e&&"mouseover"!==e.type||(s.mouseleave="close"),e&&"focusin"!==e.type||(s.focusout="close"),this._on(!0,i,s)},close:function(e){var i,s=this,n=t(e?e.currentTarget:this.element),o=this._find(n);return o?(i=o.tooltip,o.closing||(clearInterval(this.delayedShow),n.data("ui-tooltip-title")&&!n.attr("title")&&n.attr("title",n.data("ui-tooltip-title")),this._removeDescribedBy(n),o.hiding=!0,i.stop(!0),this._hide(i,this.options.hide,function(){s._removeTooltip(t(this))}),n.removeData("ui-tooltip-open"),this._off(n,"mouseleave focusout keyup"),n[0]!==this.element[0]&&this._off(n,"remove"),this._off(this.document,"mousemove"),e&&"mouseleave"===e.type&&t.each(this.parents,function(e,i){t(i.element).attr("title",i.title),delete s.parents[e] +}),o.closing=!0,this._trigger("close",e,{tooltip:i}),o.hiding||(o.closing=!1)),void 0):(n.removeData("ui-tooltip-open"),void 0)},_tooltip:function(e){var i=t("
    ").attr("role","tooltip"),s=t("
    ").appendTo(i),n=i.uniqueId().attr("id");return this._addClass(s,"ui-tooltip-content"),this._addClass(i,"ui-tooltip","ui-widget ui-widget-content"),i.appendTo(this._appendTo(e)),this.tooltips[n]={element:e,tooltip:i}},_find:function(t){var e=t.data("ui-tooltip-id");return e?this.tooltips[e]:null},_removeTooltip:function(t){t.remove(),delete this.tooltips[t.attr("id")]},_appendTo:function(t){var e=t.closest(".ui-front, dialog");return e.length||(e=this.document[0].body),e},_destroy:function(){var e=this;t.each(this.tooltips,function(i,s){var n=t.Event("blur"),o=s.element;n.target=n.currentTarget=o[0],e.close(n,!0),t("#"+i).remove(),o.data("ui-tooltip-title")&&(o.attr("title")||o.attr("title",o.data("ui-tooltip-title")),o.removeData("ui-tooltip-title"))}),this.liveRegion.remove()}}),t.uiBackCompat!==!1&&t.widget("ui.tooltip",t.ui.tooltip,{options:{tooltipClass:null},_tooltip:function(){var t=this._superApply(arguments);return this.options.tooltipClass&&t.tooltip.addClass(this.options.tooltipClass),t}}),t.ui.tooltip;var f="ui-effects-",g="ui-effects-style",m="ui-effects-animated",_=t;t.effects={effect:{}},function(t,e){function i(t,e,i){var s=u[e.type]||{};return null==t?i||!e.def?null:e.def:(t=s.floor?~~t:parseFloat(t),isNaN(t)?e.def:s.mod?(t+s.mod)%s.mod:0>t?0:t>s.max?s.max:t)}function s(i){var s=l(),n=s._rgba=[];return i=i.toLowerCase(),f(h,function(t,o){var a,r=o.re.exec(i),h=r&&o.parse(r),l=o.space||"rgba";return h?(a=s[l](h),s[c[l].cache]=a[c[l].cache],n=s._rgba=a._rgba,!1):e}),n.length?("0,0,0,0"===n.join()&&t.extend(n,o.transparent),s):o[i]}function n(t,e,i){return i=(i+1)%1,1>6*i?t+6*(e-t)*i:1>2*i?e:2>3*i?t+6*(e-t)*(2/3-i):t}var o,a="backgroundColor borderBottomColor borderLeftColor borderRightColor borderTopColor color columnRuleColor outlineColor textDecorationColor textEmphasisColor",r=/^([\-+])=\s*(\d+\.?\d*)/,h=[{re:/rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,parse:function(t){return[t[1],t[2],t[3],t[4]]}},{re:/rgba?\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,parse:function(t){return[2.55*t[1],2.55*t[2],2.55*t[3],t[4]]}},{re:/#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})/,parse:function(t){return[parseInt(t[1],16),parseInt(t[2],16),parseInt(t[3],16)]}},{re:/#([a-f0-9])([a-f0-9])([a-f0-9])/,parse:function(t){return[parseInt(t[1]+t[1],16),parseInt(t[2]+t[2],16),parseInt(t[3]+t[3],16)]}},{re:/hsla?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,space:"hsla",parse:function(t){return[t[1],t[2]/100,t[3]/100,t[4]]}}],l=t.Color=function(e,i,s,n){return new t.Color.fn.parse(e,i,s,n)},c={rgba:{props:{red:{idx:0,type:"byte"},green:{idx:1,type:"byte"},blue:{idx:2,type:"byte"}}},hsla:{props:{hue:{idx:0,type:"degrees"},saturation:{idx:1,type:"percent"},lightness:{idx:2,type:"percent"}}}},u={"byte":{floor:!0,max:255},percent:{max:1},degrees:{mod:360,floor:!0}},d=l.support={},p=t("

    ")[0],f=t.each;p.style.cssText="background-color:rgba(1,1,1,.5)",d.rgba=p.style.backgroundColor.indexOf("rgba")>-1,f(c,function(t,e){e.cache="_"+t,e.props.alpha={idx:3,type:"percent",def:1}}),l.fn=t.extend(l.prototype,{parse:function(n,a,r,h){if(n===e)return this._rgba=[null,null,null,null],this;(n.jquery||n.nodeType)&&(n=t(n).css(a),a=e);var u=this,d=t.type(n),p=this._rgba=[];return a!==e&&(n=[n,a,r,h],d="array"),"string"===d?this.parse(s(n)||o._default):"array"===d?(f(c.rgba.props,function(t,e){p[e.idx]=i(n[e.idx],e)}),this):"object"===d?(n instanceof l?f(c,function(t,e){n[e.cache]&&(u[e.cache]=n[e.cache].slice())}):f(c,function(e,s){var o=s.cache;f(s.props,function(t,e){if(!u[o]&&s.to){if("alpha"===t||null==n[t])return;u[o]=s.to(u._rgba)}u[o][e.idx]=i(n[t],e,!0)}),u[o]&&0>t.inArray(null,u[o].slice(0,3))&&(u[o][3]=1,s.from&&(u._rgba=s.from(u[o])))}),this):e},is:function(t){var i=l(t),s=!0,n=this;return f(c,function(t,o){var a,r=i[o.cache];return r&&(a=n[o.cache]||o.to&&o.to(n._rgba)||[],f(o.props,function(t,i){return null!=r[i.idx]?s=r[i.idx]===a[i.idx]:e})),s}),s},_space:function(){var t=[],e=this;return f(c,function(i,s){e[s.cache]&&t.push(i)}),t.pop()},transition:function(t,e){var s=l(t),n=s._space(),o=c[n],a=0===this.alpha()?l("transparent"):this,r=a[o.cache]||o.to(a._rgba),h=r.slice();return s=s[o.cache],f(o.props,function(t,n){var o=n.idx,a=r[o],l=s[o],c=u[n.type]||{};null!==l&&(null===a?h[o]=l:(c.mod&&(l-a>c.mod/2?a+=c.mod:a-l>c.mod/2&&(a-=c.mod)),h[o]=i((l-a)*e+a,n)))}),this[n](h)},blend:function(e){if(1===this._rgba[3])return this;var i=this._rgba.slice(),s=i.pop(),n=l(e)._rgba;return l(t.map(i,function(t,e){return(1-s)*n[e]+s*t}))},toRgbaString:function(){var e="rgba(",i=t.map(this._rgba,function(t,e){return null==t?e>2?1:0:t});return 1===i[3]&&(i.pop(),e="rgb("),e+i.join()+")"},toHslaString:function(){var e="hsla(",i=t.map(this.hsla(),function(t,e){return null==t&&(t=e>2?1:0),e&&3>e&&(t=Math.round(100*t)+"%"),t});return 1===i[3]&&(i.pop(),e="hsl("),e+i.join()+")"},toHexString:function(e){var i=this._rgba.slice(),s=i.pop();return e&&i.push(~~(255*s)),"#"+t.map(i,function(t){return t=(t||0).toString(16),1===t.length?"0"+t:t}).join("")},toString:function(){return 0===this._rgba[3]?"transparent":this.toRgbaString()}}),l.fn.parse.prototype=l.fn,c.hsla.to=function(t){if(null==t[0]||null==t[1]||null==t[2])return[null,null,null,t[3]];var e,i,s=t[0]/255,n=t[1]/255,o=t[2]/255,a=t[3],r=Math.max(s,n,o),h=Math.min(s,n,o),l=r-h,c=r+h,u=.5*c;return e=h===r?0:s===r?60*(n-o)/l+360:n===r?60*(o-s)/l+120:60*(s-n)/l+240,i=0===l?0:.5>=u?l/c:l/(2-c),[Math.round(e)%360,i,u,null==a?1:a]},c.hsla.from=function(t){if(null==t[0]||null==t[1]||null==t[2])return[null,null,null,t[3]];var e=t[0]/360,i=t[1],s=t[2],o=t[3],a=.5>=s?s*(1+i):s+i-s*i,r=2*s-a;return[Math.round(255*n(r,a,e+1/3)),Math.round(255*n(r,a,e)),Math.round(255*n(r,a,e-1/3)),o]},f(c,function(s,n){var o=n.props,a=n.cache,h=n.to,c=n.from;l.fn[s]=function(s){if(h&&!this[a]&&(this[a]=h(this._rgba)),s===e)return this[a].slice();var n,r=t.type(s),u="array"===r||"object"===r?s:arguments,d=this[a].slice();return f(o,function(t,e){var s=u["object"===r?t:e.idx];null==s&&(s=d[e.idx]),d[e.idx]=i(s,e)}),c?(n=l(c(d)),n[a]=d,n):l(d)},f(o,function(e,i){l.fn[e]||(l.fn[e]=function(n){var o,a=t.type(n),h="alpha"===e?this._hsla?"hsla":"rgba":s,l=this[h](),c=l[i.idx];return"undefined"===a?c:("function"===a&&(n=n.call(this,c),a=t.type(n)),null==n&&i.empty?this:("string"===a&&(o=r.exec(n),o&&(n=c+parseFloat(o[2])*("+"===o[1]?1:-1))),l[i.idx]=n,this[h](l)))})})}),l.hook=function(e){var i=e.split(" ");f(i,function(e,i){t.cssHooks[i]={set:function(e,n){var o,a,r="";if("transparent"!==n&&("string"!==t.type(n)||(o=s(n)))){if(n=l(o||n),!d.rgba&&1!==n._rgba[3]){for(a="backgroundColor"===i?e.parentNode:e;(""===r||"transparent"===r)&&a&&a.style;)try{r=t.css(a,"backgroundColor"),a=a.parentNode}catch(h){}n=n.blend(r&&"transparent"!==r?r:"_default")}n=n.toRgbaString()}try{e.style[i]=n}catch(h){}}},t.fx.step[i]=function(e){e.colorInit||(e.start=l(e.elem,i),e.end=l(e.end),e.colorInit=!0),t.cssHooks[i].set(e.elem,e.start.transition(e.end,e.pos))}})},l.hook(a),t.cssHooks.borderColor={expand:function(t){var e={};return f(["Top","Right","Bottom","Left"],function(i,s){e["border"+s+"Color"]=t}),e}},o=t.Color.names={aqua:"#00ffff",black:"#000000",blue:"#0000ff",fuchsia:"#ff00ff",gray:"#808080",green:"#008000",lime:"#00ff00",maroon:"#800000",navy:"#000080",olive:"#808000",purple:"#800080",red:"#ff0000",silver:"#c0c0c0",teal:"#008080",white:"#ffffff",yellow:"#ffff00",transparent:[null,null,null,0],_default:"#ffffff"}}(_),function(){function e(e){var i,s,n=e.ownerDocument.defaultView?e.ownerDocument.defaultView.getComputedStyle(e,null):e.currentStyle,o={};if(n&&n.length&&n[0]&&n[n[0]])for(s=n.length;s--;)i=n[s],"string"==typeof n[i]&&(o[t.camelCase(i)]=n[i]);else for(i in n)"string"==typeof n[i]&&(o[i]=n[i]);return o}function i(e,i){var s,o,a={};for(s in i)o=i[s],e[s]!==o&&(n[s]||(t.fx.step[s]||!isNaN(parseFloat(o)))&&(a[s]=o));return a}var s=["add","remove","toggle"],n={border:1,borderBottom:1,borderColor:1,borderLeft:1,borderRight:1,borderTop:1,borderWidth:1,margin:1,padding:1};t.each(["borderLeftStyle","borderRightStyle","borderBottomStyle","borderTopStyle"],function(e,i){t.fx.step[i]=function(t){("none"!==t.end&&!t.setAttr||1===t.pos&&!t.setAttr)&&(_.style(t.elem,i,t.end),t.setAttr=!0)}}),t.fn.addBack||(t.fn.addBack=function(t){return this.add(null==t?this.prevObject:this.prevObject.filter(t))}),t.effects.animateClass=function(n,o,a,r){var h=t.speed(o,a,r);return this.queue(function(){var o,a=t(this),r=a.attr("class")||"",l=h.children?a.find("*").addBack():a;l=l.map(function(){var i=t(this);return{el:i,start:e(this)}}),o=function(){t.each(s,function(t,e){n[e]&&a[e+"Class"](n[e])})},o(),l=l.map(function(){return this.end=e(this.el[0]),this.diff=i(this.start,this.end),this}),a.attr("class",r),l=l.map(function(){var e=this,i=t.Deferred(),s=t.extend({},h,{queue:!1,complete:function(){i.resolve(e)}});return this.el.animate(this.diff,s),i.promise()}),t.when.apply(t,l.get()).done(function(){o(),t.each(arguments,function(){var e=this.el;t.each(this.diff,function(t){e.css(t,"")})}),h.complete.call(a[0])})})},t.fn.extend({addClass:function(e){return function(i,s,n,o){return s?t.effects.animateClass.call(this,{add:i},s,n,o):e.apply(this,arguments)}}(t.fn.addClass),removeClass:function(e){return function(i,s,n,o){return arguments.length>1?t.effects.animateClass.call(this,{remove:i},s,n,o):e.apply(this,arguments)}}(t.fn.removeClass),toggleClass:function(e){return function(i,s,n,o,a){return"boolean"==typeof s||void 0===s?n?t.effects.animateClass.call(this,s?{add:i}:{remove:i},n,o,a):e.apply(this,arguments):t.effects.animateClass.call(this,{toggle:i},s,n,o)}}(t.fn.toggleClass),switchClass:function(e,i,s,n,o){return t.effects.animateClass.call(this,{add:i,remove:e},s,n,o)}})}(),function(){function e(e,i,s,n){return t.isPlainObject(e)&&(i=e,e=e.effect),e={effect:e},null==i&&(i={}),t.isFunction(i)&&(n=i,s=null,i={}),("number"==typeof i||t.fx.speeds[i])&&(n=s,s=i,i={}),t.isFunction(s)&&(n=s,s=null),i&&t.extend(e,i),s=s||i.duration,e.duration=t.fx.off?0:"number"==typeof s?s:s in t.fx.speeds?t.fx.speeds[s]:t.fx.speeds._default,e.complete=n||i.complete,e}function i(e){return!e||"number"==typeof e||t.fx.speeds[e]?!0:"string"!=typeof e||t.effects.effect[e]?t.isFunction(e)?!0:"object"!=typeof e||e.effect?!1:!0:!0}function s(t,e){var i=e.outerWidth(),s=e.outerHeight(),n=/^rect\((-?\d*\.?\d*px|-?\d+%|auto),?\s*(-?\d*\.?\d*px|-?\d+%|auto),?\s*(-?\d*\.?\d*px|-?\d+%|auto),?\s*(-?\d*\.?\d*px|-?\d+%|auto)\)$/,o=n.exec(t)||["",0,i,s,0];return{top:parseFloat(o[1])||0,right:"auto"===o[2]?i:parseFloat(o[2]),bottom:"auto"===o[3]?s:parseFloat(o[3]),left:parseFloat(o[4])||0}}t.expr&&t.expr.filters&&t.expr.filters.animated&&(t.expr.filters.animated=function(e){return function(i){return!!t(i).data(m)||e(i)}}(t.expr.filters.animated)),t.uiBackCompat!==!1&&t.extend(t.effects,{save:function(t,e){for(var i=0,s=e.length;s>i;i++)null!==e[i]&&t.data(f+e[i],t[0].style[e[i]])},restore:function(t,e){for(var i,s=0,n=e.length;n>s;s++)null!==e[s]&&(i=t.data(f+e[s]),t.css(e[s],i))},setMode:function(t,e){return"toggle"===e&&(e=t.is(":hidden")?"show":"hide"),e},createWrapper:function(e){if(e.parent().is(".ui-effects-wrapper"))return e.parent();var i={width:e.outerWidth(!0),height:e.outerHeight(!0),"float":e.css("float")},s=t("

    ").addClass("ui-effects-wrapper").css({fontSize:"100%",background:"transparent",border:"none",margin:0,padding:0}),n={width:e.width(),height:e.height()},o=document.activeElement;try{o.id}catch(a){o=document.body}return e.wrap(s),(e[0]===o||t.contains(e[0],o))&&t(o).trigger("focus"),s=e.parent(),"static"===e.css("position")?(s.css({position:"relative"}),e.css({position:"relative"})):(t.extend(i,{position:e.css("position"),zIndex:e.css("z-index")}),t.each(["top","left","bottom","right"],function(t,s){i[s]=e.css(s),isNaN(parseInt(i[s],10))&&(i[s]="auto")}),e.css({position:"relative",top:0,left:0,right:"auto",bottom:"auto"})),e.css(n),s.css(i).show()},removeWrapper:function(e){var i=document.activeElement;return e.parent().is(".ui-effects-wrapper")&&(e.parent().replaceWith(e),(e[0]===i||t.contains(e[0],i))&&t(i).trigger("focus")),e}}),t.extend(t.effects,{version:"1.12.1",define:function(e,i,s){return s||(s=i,i="effect"),t.effects.effect[e]=s,t.effects.effect[e].mode=i,s},scaledDimensions:function(t,e,i){if(0===e)return{height:0,width:0,outerHeight:0,outerWidth:0};var s="horizontal"!==i?(e||100)/100:1,n="vertical"!==i?(e||100)/100:1;return{height:t.height()*n,width:t.width()*s,outerHeight:t.outerHeight()*n,outerWidth:t.outerWidth()*s}},clipToBox:function(t){return{width:t.clip.right-t.clip.left,height:t.clip.bottom-t.clip.top,left:t.clip.left,top:t.clip.top}},unshift:function(t,e,i){var s=t.queue();e>1&&s.splice.apply(s,[1,0].concat(s.splice(e,i))),t.dequeue()},saveStyle:function(t){t.data(g,t[0].style.cssText)},restoreStyle:function(t){t[0].style.cssText=t.data(g)||"",t.removeData(g)},mode:function(t,e){var i=t.is(":hidden");return"toggle"===e&&(e=i?"show":"hide"),(i?"hide"===e:"show"===e)&&(e="none"),e},getBaseline:function(t,e){var i,s;switch(t[0]){case"top":i=0;break;case"middle":i=.5;break;case"bottom":i=1;break;default:i=t[0]/e.height}switch(t[1]){case"left":s=0;break;case"center":s=.5;break;case"right":s=1;break;default:s=t[1]/e.width}return{x:s,y:i}},createPlaceholder:function(e){var i,s=e.css("position"),n=e.position();return e.css({marginTop:e.css("marginTop"),marginBottom:e.css("marginBottom"),marginLeft:e.css("marginLeft"),marginRight:e.css("marginRight")}).outerWidth(e.outerWidth()).outerHeight(e.outerHeight()),/^(static|relative)/.test(s)&&(s="absolute",i=t("<"+e[0].nodeName+">").insertAfter(e).css({display:/^(inline|ruby)/.test(e.css("display"))?"inline-block":"block",visibility:"hidden",marginTop:e.css("marginTop"),marginBottom:e.css("marginBottom"),marginLeft:e.css("marginLeft"),marginRight:e.css("marginRight"),"float":e.css("float")}).outerWidth(e.outerWidth()).outerHeight(e.outerHeight()).addClass("ui-effects-placeholder"),e.data(f+"placeholder",i)),e.css({position:s,left:n.left,top:n.top}),i},removePlaceholder:function(t){var e=f+"placeholder",i=t.data(e);i&&(i.remove(),t.removeData(e))},cleanUp:function(e){t.effects.restoreStyle(e),t.effects.removePlaceholder(e)},setTransition:function(e,i,s,n){return n=n||{},t.each(i,function(t,i){var o=e.cssUnit(i);o[0]>0&&(n[i]=o[0]*s+o[1])}),n}}),t.fn.extend({effect:function(){function i(e){function i(){r.removeData(m),t.effects.cleanUp(r),"hide"===s.mode&&r.hide(),a()}function a(){t.isFunction(h)&&h.call(r[0]),t.isFunction(e)&&e()}var r=t(this);s.mode=c.shift(),t.uiBackCompat===!1||o?"none"===s.mode?(r[l](),a()):n.call(r[0],s,i):(r.is(":hidden")?"hide"===l:"show"===l)?(r[l](),a()):n.call(r[0],s,a)}var s=e.apply(this,arguments),n=t.effects.effect[s.effect],o=n.mode,a=s.queue,r=a||"fx",h=s.complete,l=s.mode,c=[],u=function(e){var i=t(this),s=t.effects.mode(i,l)||o;i.data(m,!0),c.push(s),o&&("show"===s||s===o&&"hide"===s)&&i.show(),o&&"none"===s||t.effects.saveStyle(i),t.isFunction(e)&&e()};return t.fx.off||!n?l?this[l](s.duration,h):this.each(function(){h&&h.call(this)}):a===!1?this.each(u).each(i):this.queue(r,u).queue(r,i)},show:function(t){return function(s){if(i(s))return t.apply(this,arguments);var n=e.apply(this,arguments);return n.mode="show",this.effect.call(this,n)}}(t.fn.show),hide:function(t){return function(s){if(i(s))return t.apply(this,arguments);var n=e.apply(this,arguments);return n.mode="hide",this.effect.call(this,n)}}(t.fn.hide),toggle:function(t){return function(s){if(i(s)||"boolean"==typeof s)return t.apply(this,arguments);var n=e.apply(this,arguments);return n.mode="toggle",this.effect.call(this,n)}}(t.fn.toggle),cssUnit:function(e){var i=this.css(e),s=[];return t.each(["em","px","%","pt"],function(t,e){i.indexOf(e)>0&&(s=[parseFloat(i),e])}),s},cssClip:function(t){return t?this.css("clip","rect("+t.top+"px "+t.right+"px "+t.bottom+"px "+t.left+"px)"):s(this.css("clip"),this)},transfer:function(e,i){var s=t(this),n=t(e.to),o="fixed"===n.css("position"),a=t("body"),r=o?a.scrollTop():0,h=o?a.scrollLeft():0,l=n.offset(),c={top:l.top-r,left:l.left-h,height:n.innerHeight(),width:n.innerWidth()},u=s.offset(),d=t("
    ").appendTo("body").addClass(e.className).css({top:u.top-r,left:u.left-h,height:s.innerHeight(),width:s.innerWidth(),position:o?"fixed":"absolute"}).animate(c,e.duration,e.easing,function(){d.remove(),t.isFunction(i)&&i()})}}),t.fx.step.clip=function(e){e.clipInit||(e.start=t(e.elem).cssClip(),"string"==typeof e.end&&(e.end=s(e.end,e.elem)),e.clipInit=!0),t(e.elem).cssClip({top:e.pos*(e.end.top-e.start.top)+e.start.top,right:e.pos*(e.end.right-e.start.right)+e.start.right,bottom:e.pos*(e.end.bottom-e.start.bottom)+e.start.bottom,left:e.pos*(e.end.left-e.start.left)+e.start.left})}}(),function(){var e={};t.each(["Quad","Cubic","Quart","Quint","Expo"],function(t,i){e[i]=function(e){return Math.pow(e,t+2)}}),t.extend(e,{Sine:function(t){return 1-Math.cos(t*Math.PI/2)},Circ:function(t){return 1-Math.sqrt(1-t*t)},Elastic:function(t){return 0===t||1===t?t:-Math.pow(2,8*(t-1))*Math.sin((80*(t-1)-7.5)*Math.PI/15)},Back:function(t){return t*t*(3*t-2)},Bounce:function(t){for(var e,i=4;((e=Math.pow(2,--i))-1)/11>t;);return 1/Math.pow(4,3-i)-7.5625*Math.pow((3*e-2)/22-t,2)}}),t.each(e,function(e,i){t.easing["easeIn"+e]=i,t.easing["easeOut"+e]=function(t){return 1-i(1-t)},t.easing["easeInOut"+e]=function(t){return.5>t?i(2*t)/2:1-i(-2*t+2)/2}})}();var v=t.effects;t.effects.define("blind","hide",function(e,i){var s={up:["bottom","top"],vertical:["bottom","top"],down:["top","bottom"],left:["right","left"],horizontal:["right","left"],right:["left","right"]},n=t(this),o=e.direction||"up",a=n.cssClip(),r={clip:t.extend({},a)},h=t.effects.createPlaceholder(n);r.clip[s[o][0]]=r.clip[s[o][1]],"show"===e.mode&&(n.cssClip(r.clip),h&&h.css(t.effects.clipToBox(r)),r.clip=a),h&&h.animate(t.effects.clipToBox(r),e.duration,e.easing),n.animate(r,{queue:!1,duration:e.duration,easing:e.easing,complete:i})}),t.effects.define("bounce",function(e,i){var s,n,o,a=t(this),r=e.mode,h="hide"===r,l="show"===r,c=e.direction||"up",u=e.distance,d=e.times||5,p=2*d+(l||h?1:0),f=e.duration/p,g=e.easing,m="up"===c||"down"===c?"top":"left",_="up"===c||"left"===c,v=0,b=a.queue().length;for(t.effects.createPlaceholder(a),o=a.css(m),u||(u=a["top"===m?"outerHeight":"outerWidth"]()/3),l&&(n={opacity:1},n[m]=o,a.css("opacity",0).css(m,_?2*-u:2*u).animate(n,f,g)),h&&(u/=Math.pow(2,d-1)),n={},n[m]=o;d>v;v++)s={},s[m]=(_?"-=":"+=")+u,a.animate(s,f,g).animate(n,f,g),u=h?2*u:u/2;h&&(s={opacity:0},s[m]=(_?"-=":"+=")+u,a.animate(s,f,g)),a.queue(i),t.effects.unshift(a,b,p+1)}),t.effects.define("clip","hide",function(e,i){var s,n={},o=t(this),a=e.direction||"vertical",r="both"===a,h=r||"horizontal"===a,l=r||"vertical"===a;s=o.cssClip(),n.clip={top:l?(s.bottom-s.top)/2:s.top,right:h?(s.right-s.left)/2:s.right,bottom:l?(s.bottom-s.top)/2:s.bottom,left:h?(s.right-s.left)/2:s.left},t.effects.createPlaceholder(o),"show"===e.mode&&(o.cssClip(n.clip),n.clip=s),o.animate(n,{queue:!1,duration:e.duration,easing:e.easing,complete:i})}),t.effects.define("drop","hide",function(e,i){var s,n=t(this),o=e.mode,a="show"===o,r=e.direction||"left",h="up"===r||"down"===r?"top":"left",l="up"===r||"left"===r?"-=":"+=",c="+="===l?"-=":"+=",u={opacity:0};t.effects.createPlaceholder(n),s=e.distance||n["top"===h?"outerHeight":"outerWidth"](!0)/2,u[h]=l+s,a&&(n.css(u),u[h]=c+s,u.opacity=1),n.animate(u,{queue:!1,duration:e.duration,easing:e.easing,complete:i})}),t.effects.define("explode","hide",function(e,i){function s(){b.push(this),b.length===u*d&&n()}function n(){p.css({visibility:"visible"}),t(b).remove(),i()}var o,a,r,h,l,c,u=e.pieces?Math.round(Math.sqrt(e.pieces)):3,d=u,p=t(this),f=e.mode,g="show"===f,m=p.show().css("visibility","hidden").offset(),_=Math.ceil(p.outerWidth()/d),v=Math.ceil(p.outerHeight()/u),b=[];for(o=0;u>o;o++)for(h=m.top+o*v,c=o-(u-1)/2,a=0;d>a;a++)r=m.left+a*_,l=a-(d-1)/2,p.clone().appendTo("body").wrap("
    ").css({position:"absolute",visibility:"visible",left:-a*_,top:-o*v}).parent().addClass("ui-effects-explode").css({position:"absolute",overflow:"hidden",width:_,height:v,left:r+(g?l*_:0),top:h+(g?c*v:0),opacity:g?0:1}).animate({left:r+(g?0:l*_),top:h+(g?0:c*v),opacity:g?1:0},e.duration||500,e.easing,s)}),t.effects.define("fade","toggle",function(e,i){var s="show"===e.mode;t(this).css("opacity",s?0:1).animate({opacity:s?1:0},{queue:!1,duration:e.duration,easing:e.easing,complete:i})}),t.effects.define("fold","hide",function(e,i){var s=t(this),n=e.mode,o="show"===n,a="hide"===n,r=e.size||15,h=/([0-9]+)%/.exec(r),l=!!e.horizFirst,c=l?["right","bottom"]:["bottom","right"],u=e.duration/2,d=t.effects.createPlaceholder(s),p=s.cssClip(),f={clip:t.extend({},p)},g={clip:t.extend({},p)},m=[p[c[0]],p[c[1]]],_=s.queue().length;h&&(r=parseInt(h[1],10)/100*m[a?0:1]),f.clip[c[0]]=r,g.clip[c[0]]=r,g.clip[c[1]]=0,o&&(s.cssClip(g.clip),d&&d.css(t.effects.clipToBox(g)),g.clip=p),s.queue(function(i){d&&d.animate(t.effects.clipToBox(f),u,e.easing).animate(t.effects.clipToBox(g),u,e.easing),i()}).animate(f,u,e.easing).animate(g,u,e.easing).queue(i),t.effects.unshift(s,_,4)}),t.effects.define("highlight","show",function(e,i){var s=t(this),n={backgroundColor:s.css("backgroundColor")};"hide"===e.mode&&(n.opacity=0),t.effects.saveStyle(s),s.css({backgroundImage:"none",backgroundColor:e.color||"#ffff99"}).animate(n,{queue:!1,duration:e.duration,easing:e.easing,complete:i})}),t.effects.define("size",function(e,i){var s,n,o,a=t(this),r=["fontSize"],h=["borderTopWidth","borderBottomWidth","paddingTop","paddingBottom"],l=["borderLeftWidth","borderRightWidth","paddingLeft","paddingRight"],c=e.mode,u="effect"!==c,d=e.scale||"both",p=e.origin||["middle","center"],f=a.css("position"),g=a.position(),m=t.effects.scaledDimensions(a),_=e.from||m,v=e.to||t.effects.scaledDimensions(a,0);t.effects.createPlaceholder(a),"show"===c&&(o=_,_=v,v=o),n={from:{y:_.height/m.height,x:_.width/m.width},to:{y:v.height/m.height,x:v.width/m.width}},("box"===d||"both"===d)&&(n.from.y!==n.to.y&&(_=t.effects.setTransition(a,h,n.from.y,_),v=t.effects.setTransition(a,h,n.to.y,v)),n.from.x!==n.to.x&&(_=t.effects.setTransition(a,l,n.from.x,_),v=t.effects.setTransition(a,l,n.to.x,v))),("content"===d||"both"===d)&&n.from.y!==n.to.y&&(_=t.effects.setTransition(a,r,n.from.y,_),v=t.effects.setTransition(a,r,n.to.y,v)),p&&(s=t.effects.getBaseline(p,m),_.top=(m.outerHeight-_.outerHeight)*s.y+g.top,_.left=(m.outerWidth-_.outerWidth)*s.x+g.left,v.top=(m.outerHeight-v.outerHeight)*s.y+g.top,v.left=(m.outerWidth-v.outerWidth)*s.x+g.left),a.css(_),("content"===d||"both"===d)&&(h=h.concat(["marginTop","marginBottom"]).concat(r),l=l.concat(["marginLeft","marginRight"]),a.find("*[width]").each(function(){var i=t(this),s=t.effects.scaledDimensions(i),o={height:s.height*n.from.y,width:s.width*n.from.x,outerHeight:s.outerHeight*n.from.y,outerWidth:s.outerWidth*n.from.x},a={height:s.height*n.to.y,width:s.width*n.to.x,outerHeight:s.height*n.to.y,outerWidth:s.width*n.to.x};n.from.y!==n.to.y&&(o=t.effects.setTransition(i,h,n.from.y,o),a=t.effects.setTransition(i,h,n.to.y,a)),n.from.x!==n.to.x&&(o=t.effects.setTransition(i,l,n.from.x,o),a=t.effects.setTransition(i,l,n.to.x,a)),u&&t.effects.saveStyle(i),i.css(o),i.animate(a,e.duration,e.easing,function(){u&&t.effects.restoreStyle(i)})})),a.animate(v,{queue:!1,duration:e.duration,easing:e.easing,complete:function(){var e=a.offset();0===v.opacity&&a.css("opacity",_.opacity),u||(a.css("position","static"===f?"relative":f).offset(e),t.effects.saveStyle(a)),i()}})}),t.effects.define("scale",function(e,i){var s=t(this),n=e.mode,o=parseInt(e.percent,10)||(0===parseInt(e.percent,10)?0:"effect"!==n?0:100),a=t.extend(!0,{from:t.effects.scaledDimensions(s),to:t.effects.scaledDimensions(s,o,e.direction||"both"),origin:e.origin||["middle","center"]},e);e.fade&&(a.from.opacity=1,a.to.opacity=0),t.effects.effect.size.call(this,a,i)}),t.effects.define("puff","hide",function(e,i){var s=t.extend(!0,{},e,{fade:!0,percent:parseInt(e.percent,10)||150});t.effects.effect.scale.call(this,s,i)}),t.effects.define("pulsate","show",function(e,i){var s=t(this),n=e.mode,o="show"===n,a="hide"===n,r=o||a,h=2*(e.times||5)+(r?1:0),l=e.duration/h,c=0,u=1,d=s.queue().length;for((o||!s.is(":visible"))&&(s.css("opacity",0).show(),c=1);h>u;u++)s.animate({opacity:c},l,e.easing),c=1-c;s.animate({opacity:c},l,e.easing),s.queue(i),t.effects.unshift(s,d,h+1)}),t.effects.define("shake",function(e,i){var s=1,n=t(this),o=e.direction||"left",a=e.distance||20,r=e.times||3,h=2*r+1,l=Math.round(e.duration/h),c="up"===o||"down"===o?"top":"left",u="up"===o||"left"===o,d={},p={},f={},g=n.queue().length;for(t.effects.createPlaceholder(n),d[c]=(u?"-=":"+=")+a,p[c]=(u?"+=":"-=")+2*a,f[c]=(u?"-=":"+=")+2*a,n.animate(d,l,e.easing);r>s;s++)n.animate(p,l,e.easing).animate(f,l,e.easing);n.animate(p,l,e.easing).animate(d,l/2,e.easing).queue(i),t.effects.unshift(n,g,h+1)}),t.effects.define("slide","show",function(e,i){var s,n,o=t(this),a={up:["bottom","top"],down:["top","bottom"],left:["right","left"],right:["left","right"]},r=e.mode,h=e.direction||"left",l="up"===h||"down"===h?"top":"left",c="up"===h||"left"===h,u=e.distance||o["top"===l?"outerHeight":"outerWidth"](!0),d={};t.effects.createPlaceholder(o),s=o.cssClip(),n=o.position()[l],d[l]=(c?-1:1)*u+n,d.clip=o.cssClip(),d.clip[a[h][1]]=d.clip[a[h][0]],"show"===r&&(o.cssClip(d.clip),o.css(l,d[l]),d.clip=s,d[l]=n),o.animate(d,{queue:!1,duration:e.duration,easing:e.easing,complete:i})});var v;t.uiBackCompat!==!1&&(v=t.effects.define("transfer",function(e,i){t(this).transfer(e,i)}))}); \ No newline at end of file diff --git a/js/jquery.min.js b/js/jquery.min.js index ab28a247..4d9b3a25 100644 --- a/js/jquery.min.js +++ b/js/jquery.min.js @@ -1,4 +1,2 @@ -/*! jQuery v1.11.1 | (c) 2005, 2014 jQuery Foundation, Inc. | jquery.org/license */ -!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=c.slice,e=c.concat,f=c.push,g=c.indexOf,h={},i=h.toString,j=h.hasOwnProperty,k={},l="1.11.1",m=function(a,b){return new m.fn.init(a,b)},n=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,o=/^-ms-/,p=/-([\da-z])/gi,q=function(a,b){return b.toUpperCase()};m.fn=m.prototype={jquery:l,constructor:m,selector:"",length:0,toArray:function(){return d.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=m.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return m.each(this,a,b)},map:function(a){return this.pushStack(m.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},m.extend=m.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||m.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(e=arguments[h]))for(d in e)a=g[d],c=e[d],g!==c&&(j&&c&&(m.isPlainObject(c)||(b=m.isArray(c)))?(b?(b=!1,f=a&&m.isArray(a)?a:[]):f=a&&m.isPlainObject(a)?a:{},g[d]=m.extend(j,f,c)):void 0!==c&&(g[d]=c));return g},m.extend({expando:"jQuery"+(l+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===m.type(a)},isArray:Array.isArray||function(a){return"array"===m.type(a)},isWindow:function(a){return null!=a&&a==a.window},isNumeric:function(a){return!m.isArray(a)&&a-parseFloat(a)>=0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},isPlainObject:function(a){var b;if(!a||"object"!==m.type(a)||a.nodeType||m.isWindow(a))return!1;try{if(a.constructor&&!j.call(a,"constructor")&&!j.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}if(k.ownLast)for(b in a)return j.call(a,b);for(b in a);return void 0===b||j.call(a,b)},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(b){b&&m.trim(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(o,"ms-").replace(p,q)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=r(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(n,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(r(Object(a))?m.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){var d;if(b){if(g)return g.call(b,a,c);for(d=b.length,c=c?0>c?Math.max(0,d+c):c:0;d>c;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,b){var c=+b.length,d=0,e=a.length;while(c>d)a[e++]=b[d++];if(c!==c)while(void 0!==b[d])a[e++]=b[d++];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=r(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(f=a[b],b=a,a=f),m.isFunction(a)?(c=d.call(arguments,2),e=function(){return a.apply(b||this,c.concat(d.call(arguments)))},e.guid=a.guid=a.guid||m.guid++,e):void 0},now:function(){return+new Date},support:k}),m.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function r(a){var b=a.length,c=m.type(a);return"function"===c||m.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var s=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+-new Date,v=a.document,w=0,x=0,y=gb(),z=gb(),A=gb(),B=function(a,b){return a===b&&(l=!0),0},C="undefined",D=1<<31,E={}.hasOwnProperty,F=[],G=F.pop,H=F.push,I=F.push,J=F.slice,K=F.indexOf||function(a){for(var b=0,c=this.length;c>b;b++)if(this[b]===a)return b;return-1},L="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",M="[\\x20\\t\\r\\n\\f]",N="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",O=N.replace("w","w#"),P="\\["+M+"*("+N+")(?:"+M+"*([*^$|!~]?=)"+M+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+O+"))|)"+M+"*\\]",Q=":("+N+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+P+")*)|.*)\\)|)",R=new RegExp("^"+M+"+|((?:^|[^\\\\])(?:\\\\.)*)"+M+"+$","g"),S=new RegExp("^"+M+"*,"+M+"*"),T=new RegExp("^"+M+"*([>+~]|"+M+")"+M+"*"),U=new RegExp("="+M+"*([^\\]'\"]*?)"+M+"*\\]","g"),V=new RegExp(Q),W=new RegExp("^"+O+"$"),X={ID:new RegExp("^#("+N+")"),CLASS:new RegExp("^\\.("+N+")"),TAG:new RegExp("^("+N.replace("w","w*")+")"),ATTR:new RegExp("^"+P),PSEUDO:new RegExp("^"+Q),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+L+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/^(?:input|select|textarea|button)$/i,Z=/^h\d$/i,$=/^[^{]+\{\s*\[native \w/,_=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ab=/[+~]/,bb=/'|\\/g,cb=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),db=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)};try{I.apply(F=J.call(v.childNodes),v.childNodes),F[v.childNodes.length].nodeType}catch(eb){I={apply:F.length?function(a,b){H.apply(a,J.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function fb(a,b,d,e){var f,h,j,k,l,o,r,s,w,x;if((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,d=d||[],!a||"string"!=typeof a)return d;if(1!==(k=b.nodeType)&&9!==k)return[];if(p&&!e){if(f=_.exec(a))if(j=f[1]){if(9===k){if(h=b.getElementById(j),!h||!h.parentNode)return d;if(h.id===j)return d.push(h),d}else if(b.ownerDocument&&(h=b.ownerDocument.getElementById(j))&&t(b,h)&&h.id===j)return d.push(h),d}else{if(f[2])return I.apply(d,b.getElementsByTagName(a)),d;if((j=f[3])&&c.getElementsByClassName&&b.getElementsByClassName)return I.apply(d,b.getElementsByClassName(j)),d}if(c.qsa&&(!q||!q.test(a))){if(s=r=u,w=b,x=9===k&&a,1===k&&"object"!==b.nodeName.toLowerCase()){o=g(a),(r=b.getAttribute("id"))?s=r.replace(bb,"\\$&"):b.setAttribute("id",s),s="[id='"+s+"'] ",l=o.length;while(l--)o[l]=s+qb(o[l]);w=ab.test(a)&&ob(b.parentNode)||b,x=o.join(",")}if(x)try{return I.apply(d,w.querySelectorAll(x)),d}catch(y){}finally{r||b.removeAttribute("id")}}}return i(a.replace(R,"$1"),b,d,e)}function gb(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function hb(a){return a[u]=!0,a}function ib(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function jb(a,b){var c=a.split("|"),e=a.length;while(e--)d.attrHandle[c[e]]=b}function kb(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||D)-(~a.sourceIndex||D);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function lb(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function mb(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function nb(a){return hb(function(b){return b=+b,hb(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function ob(a){return a&&typeof a.getElementsByTagName!==C&&a}c=fb.support={},f=fb.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=fb.setDocument=function(a){var b,e=a?a.ownerDocument||a:v,g=e.defaultView;return e!==n&&9===e.nodeType&&e.documentElement?(n=e,o=e.documentElement,p=!f(e),g&&g!==g.top&&(g.addEventListener?g.addEventListener("unload",function(){m()},!1):g.attachEvent&&g.attachEvent("onunload",function(){m()})),c.attributes=ib(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ib(function(a){return a.appendChild(e.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=$.test(e.getElementsByClassName)&&ib(function(a){return a.innerHTML="
    ",a.firstChild.className="i",2===a.getElementsByClassName("i").length}),c.getById=ib(function(a){return o.appendChild(a).id=u,!e.getElementsByName||!e.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if(typeof b.getElementById!==C&&p){var c=b.getElementById(a);return c&&c.parentNode?[c]:[]}},d.filter.ID=function(a){var b=a.replace(cb,db);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(cb,db);return function(a){var c=typeof a.getAttributeNode!==C&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return typeof b.getElementsByTagName!==C?b.getElementsByTagName(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return typeof b.getElementsByClassName!==C&&p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=$.test(e.querySelectorAll))&&(ib(function(a){a.innerHTML="",a.querySelectorAll("[msallowclip^='']").length&&q.push("[*^$]="+M+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+M+"*(?:value|"+L+")"),a.querySelectorAll(":checked").length||q.push(":checked")}),ib(function(a){var b=e.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+M+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=$.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ib(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",Q)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=$.test(o.compareDocumentPosition),t=b||$.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===e||a.ownerDocument===v&&t(v,a)?-1:b===e||b.ownerDocument===v&&t(v,b)?1:k?K.call(k,a)-K.call(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,f=a.parentNode,g=b.parentNode,h=[a],i=[b];if(!f||!g)return a===e?-1:b===e?1:f?-1:g?1:k?K.call(k,a)-K.call(k,b):0;if(f===g)return kb(a,b);c=a;while(c=c.parentNode)h.unshift(c);c=b;while(c=c.parentNode)i.unshift(c);while(h[d]===i[d])d++;return d?kb(h[d],i[d]):h[d]===v?-1:i[d]===v?1:0},e):n},fb.matches=function(a,b){return fb(a,null,null,b)},fb.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(U,"='$1']"),!(!c.matchesSelector||!p||r&&r.test(b)||q&&q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return fb(b,n,null,[a]).length>0},fb.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},fb.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&E.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},fb.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},fb.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=fb.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=fb.selectors={cacheLength:50,createPseudo:hb,match:X,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(cb,db),a[3]=(a[3]||a[4]||a[5]||"").replace(cb,db),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||fb.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&fb.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return X.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&V.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(cb,db).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+M+")"+a+"("+M+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||typeof a.getAttribute!==C&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=fb.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h;if(q){if(f){while(p){l=b;while(l=l[p])if(h?l.nodeName.toLowerCase()===r:1===l.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){k=q[u]||(q[u]={}),j=k[a]||[],n=j[0]===w&&j[1],m=j[0]===w&&j[2],l=n&&q.childNodes[n];while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if(1===l.nodeType&&++m&&l===b){k[a]=[w,n,m];break}}else if(s&&(j=(b[u]||(b[u]={}))[a])&&j[0]===w)m=j[1];else while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if((h?l.nodeName.toLowerCase()===r:1===l.nodeType)&&++m&&(s&&((l[u]||(l[u]={}))[a]=[w,m]),l===b))break;return m-=e,m===d||m%d===0&&m/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||fb.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?hb(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=K.call(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:hb(function(a){var b=[],c=[],d=h(a.replace(R,"$1"));return d[u]?hb(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),!c.pop()}}),has:hb(function(a){return function(b){return fb(a,b).length>0}}),contains:hb(function(a){return function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:hb(function(a){return W.test(a||"")||fb.error("unsupported lang: "+a),a=a.replace(cb,db).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Z.test(a.nodeName)},input:function(a){return Y.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:nb(function(){return[0]}),last:nb(function(a,b){return[b-1]}),eq:nb(function(a,b,c){return[0>c?c+b:c]}),even:nb(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:nb(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:nb(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:nb(function(a,b,c){for(var d=0>c?c+b:c;++db;b++)d+=a[b].value;return d}function rb(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(i=b[u]||(b[u]={}),(h=i[d])&&h[0]===w&&h[1]===f)return j[2]=h[2];if(i[d]=j,j[2]=a(b,c,g))return!0}}}function sb(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function tb(a,b,c){for(var d=0,e=b.length;e>d;d++)fb(a,b[d],c);return c}function ub(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(!c||c(f,d,e))&&(g.push(f),j&&b.push(h));return g}function vb(a,b,c,d,e,f){return d&&!d[u]&&(d=vb(d)),e&&!e[u]&&(e=vb(e,f)),hb(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||tb(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:ub(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=ub(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?K.call(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=ub(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):I.apply(g,r)})}function wb(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=rb(function(a){return a===b},h,!0),l=rb(function(a){return K.call(b,a)>-1},h,!0),m=[function(a,c,d){return!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d))}];f>i;i++)if(c=d.relative[a[i].type])m=[rb(sb(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return vb(i>1&&sb(m),i>1&&qb(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(R,"$1"),c,e>i&&wb(a.slice(i,e)),f>e&&wb(a=a.slice(e)),f>e&&qb(a))}m.push(c)}return sb(m)}function xb(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,m,o,p=0,q="0",r=f&&[],s=[],t=j,u=f||e&&d.find.TAG("*",k),v=w+=null==t?1:Math.random()||.1,x=u.length;for(k&&(j=g!==n&&g);q!==x&&null!=(l=u[q]);q++){if(e&&l){m=0;while(o=a[m++])if(o(l,g,h)){i.push(l);break}k&&(w=v)}c&&((l=!o&&l)&&p--,f&&r.push(l))}if(p+=q,c&&q!==p){m=0;while(o=b[m++])o(r,s,g,h);if(f){if(p>0)while(q--)r[q]||s[q]||(s[q]=G.call(i));s=ub(s)}I.apply(i,s),k&&!f&&s.length>0&&p+b.length>1&&fb.uniqueSort(i)}return k&&(w=v,j=t),r};return c?hb(f):f}return h=fb.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=wb(b[c]),f[u]?d.push(f):e.push(f);f=A(a,xb(e,d)),f.selector=a}return f},i=fb.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(cb,db),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=X.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(cb,db),ab.test(j[0].type)&&ob(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&qb(j),!a)return I.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,ab.test(a)&&ob(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ib(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ib(function(a){return a.innerHTML="
    ","#"===a.firstChild.getAttribute("href")})||jb("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ib(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||jb("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ib(function(a){return null==a.getAttribute("disabled")})||jb(L,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),fb}(a);m.find=s,m.expr=s.selectors,m.expr[":"]=m.expr.pseudos,m.unique=s.uniqueSort,m.text=s.getText,m.isXMLDoc=s.isXML,m.contains=s.contains;var t=m.expr.match.needsContext,u=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,v=/^.[^:#\[\.,]*$/;function w(a,b,c){if(m.isFunction(b))return m.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return m.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(v.test(b))return m.filter(b,a,c);b=m.filter(b,a)}return m.grep(a,function(a){return m.inArray(a,b)>=0!==c})}m.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?m.find.matchesSelector(d,a)?[d]:[]:m.find.matches(a,m.grep(b,function(a){return 1===a.nodeType}))},m.fn.extend({find:function(a){var b,c=[],d=this,e=d.length;if("string"!=typeof a)return this.pushStack(m(a).filter(function(){for(b=0;e>b;b++)if(m.contains(d[b],this))return!0}));for(b=0;e>b;b++)m.find(a,d[b],c);return c=this.pushStack(e>1?m.unique(c):c),c.selector=this.selector?this.selector+" "+a:a,c},filter:function(a){return this.pushStack(w(this,a||[],!1))},not:function(a){return this.pushStack(w(this,a||[],!0))},is:function(a){return!!w(this,"string"==typeof a&&t.test(a)?m(a):a||[],!1).length}});var x,y=a.document,z=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,A=m.fn.init=function(a,b){var c,d;if(!a)return this;if("string"==typeof a){if(c="<"===a.charAt(0)&&">"===a.charAt(a.length-1)&&a.length>=3?[null,a,null]:z.exec(a),!c||!c[1]&&b)return!b||b.jquery?(b||x).find(a):this.constructor(b).find(a);if(c[1]){if(b=b instanceof m?b[0]:b,m.merge(this,m.parseHTML(c[1],b&&b.nodeType?b.ownerDocument||b:y,!0)),u.test(c[1])&&m.isPlainObject(b))for(c in b)m.isFunction(this[c])?this[c](b[c]):this.attr(c,b[c]);return this}if(d=y.getElementById(c[2]),d&&d.parentNode){if(d.id!==c[2])return x.find(a);this.length=1,this[0]=d}return this.context=y,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):m.isFunction(a)?"undefined"!=typeof x.ready?x.ready(a):a(m):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),m.makeArray(a,this))};A.prototype=m.fn,x=m(y);var B=/^(?:parents|prev(?:Until|All))/,C={children:!0,contents:!0,next:!0,prev:!0};m.extend({dir:function(a,b,c){var d=[],e=a[b];while(e&&9!==e.nodeType&&(void 0===c||1!==e.nodeType||!m(e).is(c)))1===e.nodeType&&d.push(e),e=e[b];return d},sibling:function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c}}),m.fn.extend({has:function(a){var b,c=m(a,this),d=c.length;return this.filter(function(){for(b=0;d>b;b++)if(m.contains(this,c[b]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=t.test(a)||"string"!=typeof a?m(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&m.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?m.unique(f):f)},index:function(a){return a?"string"==typeof a?m.inArray(this[0],m(a)):m.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(m.unique(m.merge(this.get(),m(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function D(a,b){do a=a[b];while(a&&1!==a.nodeType);return a}m.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return m.dir(a,"parentNode")},parentsUntil:function(a,b,c){return m.dir(a,"parentNode",c)},next:function(a){return D(a,"nextSibling")},prev:function(a){return D(a,"previousSibling")},nextAll:function(a){return m.dir(a,"nextSibling")},prevAll:function(a){return m.dir(a,"previousSibling")},nextUntil:function(a,b,c){return m.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return m.dir(a,"previousSibling",c)},siblings:function(a){return m.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return m.sibling(a.firstChild)},contents:function(a){return m.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:m.merge([],a.childNodes)}},function(a,b){m.fn[a]=function(c,d){var e=m.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=m.filter(d,e)),this.length>1&&(C[a]||(e=m.unique(e)),B.test(a)&&(e=e.reverse())),this.pushStack(e)}});var E=/\S+/g,F={};function G(a){var b=F[a]={};return m.each(a.match(E)||[],function(a,c){b[c]=!0}),b}m.Callbacks=function(a){a="string"==typeof a?F[a]||G(a):m.extend({},a);var b,c,d,e,f,g,h=[],i=!a.once&&[],j=function(l){for(c=a.memory&&l,d=!0,f=g||0,g=0,e=h.length,b=!0;h&&e>f;f++)if(h[f].apply(l[0],l[1])===!1&&a.stopOnFalse){c=!1;break}b=!1,h&&(i?i.length&&j(i.shift()):c?h=[]:k.disable())},k={add:function(){if(h){var d=h.length;!function f(b){m.each(b,function(b,c){var d=m.type(c);"function"===d?a.unique&&k.has(c)||h.push(c):c&&c.length&&"string"!==d&&f(c)})}(arguments),b?e=h.length:c&&(g=d,j(c))}return this},remove:function(){return h&&m.each(arguments,function(a,c){var d;while((d=m.inArray(c,h,d))>-1)h.splice(d,1),b&&(e>=d&&e--,f>=d&&f--)}),this},has:function(a){return a?m.inArray(a,h)>-1:!(!h||!h.length)},empty:function(){return h=[],e=0,this},disable:function(){return h=i=c=void 0,this},disabled:function(){return!h},lock:function(){return i=void 0,c||k.disable(),this},locked:function(){return!i},fireWith:function(a,c){return!h||d&&!i||(c=c||[],c=[a,c.slice?c.slice():c],b?i.push(c):j(c)),this},fire:function(){return k.fireWith(this,arguments),this},fired:function(){return!!d}};return k},m.extend({Deferred:function(a){var b=[["resolve","done",m.Callbacks("once memory"),"resolved"],["reject","fail",m.Callbacks("once memory"),"rejected"],["notify","progress",m.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return m.Deferred(function(c){m.each(b,function(b,f){var g=m.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&m.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?m.extend(a,d):d}},e={};return d.pipe=d.then,m.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=d.call(arguments),e=c.length,f=1!==e||a&&m.isFunction(a.promise)?e:0,g=1===f?a:m.Deferred(),h=function(a,b,c){return function(e){b[a]=this,c[a]=arguments.length>1?d.call(arguments):e,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(e>1)for(i=new Array(e),j=new Array(e),k=new Array(e);e>b;b++)c[b]&&m.isFunction(c[b].promise)?c[b].promise().done(h(b,k,c)).fail(g.reject).progress(h(b,j,i)):--f;return f||g.resolveWith(k,c),g.promise()}});var H;m.fn.ready=function(a){return m.ready.promise().done(a),this},m.extend({isReady:!1,readyWait:1,holdReady:function(a){a?m.readyWait++:m.ready(!0)},ready:function(a){if(a===!0?!--m.readyWait:!m.isReady){if(!y.body)return setTimeout(m.ready);m.isReady=!0,a!==!0&&--m.readyWait>0||(H.resolveWith(y,[m]),m.fn.triggerHandler&&(m(y).triggerHandler("ready"),m(y).off("ready")))}}});function I(){y.addEventListener?(y.removeEventListener("DOMContentLoaded",J,!1),a.removeEventListener("load",J,!1)):(y.detachEvent("onreadystatechange",J),a.detachEvent("onload",J))}function J(){(y.addEventListener||"load"===event.type||"complete"===y.readyState)&&(I(),m.ready())}m.ready.promise=function(b){if(!H)if(H=m.Deferred(),"complete"===y.readyState)setTimeout(m.ready);else if(y.addEventListener)y.addEventListener("DOMContentLoaded",J,!1),a.addEventListener("load",J,!1);else{y.attachEvent("onreadystatechange",J),a.attachEvent("onload",J);var c=!1;try{c=null==a.frameElement&&y.documentElement}catch(d){}c&&c.doScroll&&!function e(){if(!m.isReady){try{c.doScroll("left")}catch(a){return setTimeout(e,50)}I(),m.ready()}}()}return H.promise(b)};var K="undefined",L;for(L in m(k))break;k.ownLast="0"!==L,k.inlineBlockNeedsLayout=!1,m(function(){var a,b,c,d;c=y.getElementsByTagName("body")[0],c&&c.style&&(b=y.createElement("div"),d=y.createElement("div"),d.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(d).appendChild(b),typeof b.style.zoom!==K&&(b.style.cssText="display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1",k.inlineBlockNeedsLayout=a=3===b.offsetWidth,a&&(c.style.zoom=1)),c.removeChild(d))}),function(){var a=y.createElement("div");if(null==k.deleteExpando){k.deleteExpando=!0;try{delete a.test}catch(b){k.deleteExpando=!1}}a=null}(),m.acceptData=function(a){var b=m.noData[(a.nodeName+" ").toLowerCase()],c=+a.nodeType||1;return 1!==c&&9!==c?!1:!b||b!==!0&&a.getAttribute("classid")===b};var M=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,N=/([A-Z])/g;function O(a,b,c){if(void 0===c&&1===a.nodeType){var d="data-"+b.replace(N,"-$1").toLowerCase();if(c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:M.test(c)?m.parseJSON(c):c}catch(e){}m.data(a,b,c)}else c=void 0}return c}function P(a){var b;for(b in a)if(("data"!==b||!m.isEmptyObject(a[b]))&&"toJSON"!==b)return!1;return!0}function Q(a,b,d,e){if(m.acceptData(a)){var f,g,h=m.expando,i=a.nodeType,j=i?m.cache:a,k=i?a[h]:a[h]&&h; -if(k&&j[k]&&(e||j[k].data)||void 0!==d||"string"!=typeof b)return k||(k=i?a[h]=c.pop()||m.guid++:h),j[k]||(j[k]=i?{}:{toJSON:m.noop}),("object"==typeof b||"function"==typeof b)&&(e?j[k]=m.extend(j[k],b):j[k].data=m.extend(j[k].data,b)),g=j[k],e||(g.data||(g.data={}),g=g.data),void 0!==d&&(g[m.camelCase(b)]=d),"string"==typeof b?(f=g[b],null==f&&(f=g[m.camelCase(b)])):f=g,f}}function R(a,b,c){if(m.acceptData(a)){var d,e,f=a.nodeType,g=f?m.cache:a,h=f?a[m.expando]:m.expando;if(g[h]){if(b&&(d=c?g[h]:g[h].data)){m.isArray(b)?b=b.concat(m.map(b,m.camelCase)):b in d?b=[b]:(b=m.camelCase(b),b=b in d?[b]:b.split(" ")),e=b.length;while(e--)delete d[b[e]];if(c?!P(d):!m.isEmptyObject(d))return}(c||(delete g[h].data,P(g[h])))&&(f?m.cleanData([a],!0):k.deleteExpando||g!=g.window?delete g[h]:g[h]=null)}}}m.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(a){return a=a.nodeType?m.cache[a[m.expando]]:a[m.expando],!!a&&!P(a)},data:function(a,b,c){return Q(a,b,c)},removeData:function(a,b){return R(a,b)},_data:function(a,b,c){return Q(a,b,c,!0)},_removeData:function(a,b){return R(a,b,!0)}}),m.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=m.data(f),1===f.nodeType&&!m._data(f,"parsedAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=m.camelCase(d.slice(5)),O(f,d,e[d])));m._data(f,"parsedAttrs",!0)}return e}return"object"==typeof a?this.each(function(){m.data(this,a)}):arguments.length>1?this.each(function(){m.data(this,a,b)}):f?O(f,a,m.data(f,a)):void 0},removeData:function(a){return this.each(function(){m.removeData(this,a)})}}),m.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=m._data(a,b),c&&(!d||m.isArray(c)?d=m._data(a,b,m.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=m.queue(a,b),d=c.length,e=c.shift(),f=m._queueHooks(a,b),g=function(){m.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return m._data(a,c)||m._data(a,c,{empty:m.Callbacks("once memory").add(function(){m._removeData(a,b+"queue"),m._removeData(a,c)})})}}),m.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.lengthh;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},W=/^(?:checkbox|radio)$/i;!function(){var a=y.createElement("input"),b=y.createElement("div"),c=y.createDocumentFragment();if(b.innerHTML="
    a",k.leadingWhitespace=3===b.firstChild.nodeType,k.tbody=!b.getElementsByTagName("tbody").length,k.htmlSerialize=!!b.getElementsByTagName("link").length,k.html5Clone="<:nav>"!==y.createElement("nav").cloneNode(!0).outerHTML,a.type="checkbox",a.checked=!0,c.appendChild(a),k.appendChecked=a.checked,b.innerHTML="",k.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue,c.appendChild(b),b.innerHTML="",k.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,k.noCloneEvent=!0,b.attachEvent&&(b.attachEvent("onclick",function(){k.noCloneEvent=!1}),b.cloneNode(!0).click()),null==k.deleteExpando){k.deleteExpando=!0;try{delete b.test}catch(d){k.deleteExpando=!1}}}(),function(){var b,c,d=y.createElement("div");for(b in{submit:!0,change:!0,focusin:!0})c="on"+b,(k[b+"Bubbles"]=c in a)||(d.setAttribute(c,"t"),k[b+"Bubbles"]=d.attributes[c].expando===!1);d=null}();var X=/^(?:input|select|textarea)$/i,Y=/^key/,Z=/^(?:mouse|pointer|contextmenu)|click/,$=/^(?:focusinfocus|focusoutblur)$/,_=/^([^.]*)(?:\.(.+)|)$/;function ab(){return!0}function bb(){return!1}function cb(){try{return y.activeElement}catch(a){}}m.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,n,o,p,q,r=m._data(a);if(r){c.handler&&(i=c,c=i.handler,e=i.selector),c.guid||(c.guid=m.guid++),(g=r.events)||(g=r.events={}),(k=r.handle)||(k=r.handle=function(a){return typeof m===K||a&&m.event.triggered===a.type?void 0:m.event.dispatch.apply(k.elem,arguments)},k.elem=a),b=(b||"").match(E)||[""],h=b.length;while(h--)f=_.exec(b[h])||[],o=q=f[1],p=(f[2]||"").split(".").sort(),o&&(j=m.event.special[o]||{},o=(e?j.delegateType:j.bindType)||o,j=m.event.special[o]||{},l=m.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&m.expr.match.needsContext.test(e),namespace:p.join(".")},i),(n=g[o])||(n=g[o]=[],n.delegateCount=0,j.setup&&j.setup.call(a,d,p,k)!==!1||(a.addEventListener?a.addEventListener(o,k,!1):a.attachEvent&&a.attachEvent("on"+o,k))),j.add&&(j.add.call(a,l),l.handler.guid||(l.handler.guid=c.guid)),e?n.splice(n.delegateCount++,0,l):n.push(l),m.event.global[o]=!0);a=null}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,n,o,p,q,r=m.hasData(a)&&m._data(a);if(r&&(k=r.events)){b=(b||"").match(E)||[""],j=b.length;while(j--)if(h=_.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=m.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,n=k[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),i=f=n.length;while(f--)g=n[f],!e&&q!==g.origType||c&&c.guid!==g.guid||h&&!h.test(g.namespace)||d&&d!==g.selector&&("**"!==d||!g.selector)||(n.splice(f,1),g.selector&&n.delegateCount--,l.remove&&l.remove.call(a,g));i&&!n.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||m.removeEvent(a,o,r.handle),delete k[o])}else for(o in k)m.event.remove(a,o+b[j],c,d,!0);m.isEmptyObject(k)&&(delete r.handle,m._removeData(a,"events"))}},trigger:function(b,c,d,e){var f,g,h,i,k,l,n,o=[d||y],p=j.call(b,"type")?b.type:b,q=j.call(b,"namespace")?b.namespace.split("."):[];if(h=l=d=d||y,3!==d.nodeType&&8!==d.nodeType&&!$.test(p+m.event.triggered)&&(p.indexOf(".")>=0&&(q=p.split("."),p=q.shift(),q.sort()),g=p.indexOf(":")<0&&"on"+p,b=b[m.expando]?b:new m.Event(p,"object"==typeof b&&b),b.isTrigger=e?2:3,b.namespace=q.join("."),b.namespace_re=b.namespace?new RegExp("(^|\\.)"+q.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=d),c=null==c?[b]:m.makeArray(c,[b]),k=m.event.special[p]||{},e||!k.trigger||k.trigger.apply(d,c)!==!1)){if(!e&&!k.noBubble&&!m.isWindow(d)){for(i=k.delegateType||p,$.test(i+p)||(h=h.parentNode);h;h=h.parentNode)o.push(h),l=h;l===(d.ownerDocument||y)&&o.push(l.defaultView||l.parentWindow||a)}n=0;while((h=o[n++])&&!b.isPropagationStopped())b.type=n>1?i:k.bindType||p,f=(m._data(h,"events")||{})[b.type]&&m._data(h,"handle"),f&&f.apply(h,c),f=g&&h[g],f&&f.apply&&m.acceptData(h)&&(b.result=f.apply(h,c),b.result===!1&&b.preventDefault());if(b.type=p,!e&&!b.isDefaultPrevented()&&(!k._default||k._default.apply(o.pop(),c)===!1)&&m.acceptData(d)&&g&&d[p]&&!m.isWindow(d)){l=d[g],l&&(d[g]=null),m.event.triggered=p;try{d[p]()}catch(r){}m.event.triggered=void 0,l&&(d[g]=l)}return b.result}},dispatch:function(a){a=m.event.fix(a);var b,c,e,f,g,h=[],i=d.call(arguments),j=(m._data(this,"events")||{})[a.type]||[],k=m.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=m.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,g=0;while((e=f.handlers[g++])&&!a.isImmediatePropagationStopped())(!a.namespace_re||a.namespace_re.test(e.namespace))&&(a.handleObj=e,a.data=e.data,c=((m.event.special[e.origType]||{}).handle||e.handler).apply(f.elem,i),void 0!==c&&(a.result=c)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&(!a.button||"click"!==a.type))for(;i!=this;i=i.parentNode||this)if(1===i.nodeType&&(i.disabled!==!0||"click"!==a.type)){for(e=[],f=0;h>f;f++)d=b[f],c=d.selector+" ",void 0===e[c]&&(e[c]=d.needsContext?m(c,this).index(i)>=0:m.find(c,this,null,[i]).length),e[c]&&e.push(d);e.length&&g.push({elem:i,handlers:e})}return h]","i"),hb=/^\s+/,ib=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,jb=/<([\w:]+)/,kb=/\s*$/g,rb={option:[1,""],legend:[1,"
    ","
    "],area:[1,"",""],param:[1,"",""],thead:[1,"","
    "],tr:[2,"","
    "],col:[2,"","
    "],td:[3,"","
    "],_default:k.htmlSerialize?[0,"",""]:[1,"X
    ","
    "]},sb=db(y),tb=sb.appendChild(y.createElement("div"));rb.optgroup=rb.option,rb.tbody=rb.tfoot=rb.colgroup=rb.caption=rb.thead,rb.th=rb.td;function ub(a,b){var c,d,e=0,f=typeof a.getElementsByTagName!==K?a.getElementsByTagName(b||"*"):typeof a.querySelectorAll!==K?a.querySelectorAll(b||"*"):void 0;if(!f)for(f=[],c=a.childNodes||a;null!=(d=c[e]);e++)!b||m.nodeName(d,b)?f.push(d):m.merge(f,ub(d,b));return void 0===b||b&&m.nodeName(a,b)?m.merge([a],f):f}function vb(a){W.test(a.type)&&(a.defaultChecked=a.checked)}function wb(a,b){return m.nodeName(a,"table")&&m.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function xb(a){return a.type=(null!==m.find.attr(a,"type"))+"/"+a.type,a}function yb(a){var b=pb.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function zb(a,b){for(var c,d=0;null!=(c=a[d]);d++)m._data(c,"globalEval",!b||m._data(b[d],"globalEval"))}function Ab(a,b){if(1===b.nodeType&&m.hasData(a)){var c,d,e,f=m._data(a),g=m._data(b,f),h=f.events;if(h){delete g.handle,g.events={};for(c in h)for(d=0,e=h[c].length;e>d;d++)m.event.add(b,c,h[c][d])}g.data&&(g.data=m.extend({},g.data))}}function Bb(a,b){var c,d,e;if(1===b.nodeType){if(c=b.nodeName.toLowerCase(),!k.noCloneEvent&&b[m.expando]){e=m._data(b);for(d in e.events)m.removeEvent(b,d,e.handle);b.removeAttribute(m.expando)}"script"===c&&b.text!==a.text?(xb(b).text=a.text,yb(b)):"object"===c?(b.parentNode&&(b.outerHTML=a.outerHTML),k.html5Clone&&a.innerHTML&&!m.trim(b.innerHTML)&&(b.innerHTML=a.innerHTML)):"input"===c&&W.test(a.type)?(b.defaultChecked=b.checked=a.checked,b.value!==a.value&&(b.value=a.value)):"option"===c?b.defaultSelected=b.selected=a.defaultSelected:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}}m.extend({clone:function(a,b,c){var d,e,f,g,h,i=m.contains(a.ownerDocument,a);if(k.html5Clone||m.isXMLDoc(a)||!gb.test("<"+a.nodeName+">")?f=a.cloneNode(!0):(tb.innerHTML=a.outerHTML,tb.removeChild(f=tb.firstChild)),!(k.noCloneEvent&&k.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||m.isXMLDoc(a)))for(d=ub(f),h=ub(a),g=0;null!=(e=h[g]);++g)d[g]&&Bb(e,d[g]);if(b)if(c)for(h=h||ub(a),d=d||ub(f),g=0;null!=(e=h[g]);g++)Ab(e,d[g]);else Ab(a,f);return d=ub(f,"script"),d.length>0&&zb(d,!i&&ub(a,"script")),d=h=e=null,f},buildFragment:function(a,b,c,d){for(var e,f,g,h,i,j,l,n=a.length,o=db(b),p=[],q=0;n>q;q++)if(f=a[q],f||0===f)if("object"===m.type(f))m.merge(p,f.nodeType?[f]:f);else if(lb.test(f)){h=h||o.appendChild(b.createElement("div")),i=(jb.exec(f)||["",""])[1].toLowerCase(),l=rb[i]||rb._default,h.innerHTML=l[1]+f.replace(ib,"<$1>")+l[2],e=l[0];while(e--)h=h.lastChild;if(!k.leadingWhitespace&&hb.test(f)&&p.push(b.createTextNode(hb.exec(f)[0])),!k.tbody){f="table"!==i||kb.test(f)?""!==l[1]||kb.test(f)?0:h:h.firstChild,e=f&&f.childNodes.length;while(e--)m.nodeName(j=f.childNodes[e],"tbody")&&!j.childNodes.length&&f.removeChild(j)}m.merge(p,h.childNodes),h.textContent="";while(h.firstChild)h.removeChild(h.firstChild);h=o.lastChild}else p.push(b.createTextNode(f));h&&o.removeChild(h),k.appendChecked||m.grep(ub(p,"input"),vb),q=0;while(f=p[q++])if((!d||-1===m.inArray(f,d))&&(g=m.contains(f.ownerDocument,f),h=ub(o.appendChild(f),"script"),g&&zb(h),c)){e=0;while(f=h[e++])ob.test(f.type||"")&&c.push(f)}return h=null,o},cleanData:function(a,b){for(var d,e,f,g,h=0,i=m.expando,j=m.cache,l=k.deleteExpando,n=m.event.special;null!=(d=a[h]);h++)if((b||m.acceptData(d))&&(f=d[i],g=f&&j[f])){if(g.events)for(e in g.events)n[e]?m.event.remove(d,e):m.removeEvent(d,e,g.handle);j[f]&&(delete j[f],l?delete d[i]:typeof d.removeAttribute!==K?d.removeAttribute(i):d[i]=null,c.push(f))}}}),m.fn.extend({text:function(a){return V(this,function(a){return void 0===a?m.text(this):this.empty().append((this[0]&&this[0].ownerDocument||y).createTextNode(a))},null,a,arguments.length)},append:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=wb(this,a);b.appendChild(a)}})},prepend:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=wb(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},remove:function(a,b){for(var c,d=a?m.filter(a,this):this,e=0;null!=(c=d[e]);e++)b||1!==c.nodeType||m.cleanData(ub(c)),c.parentNode&&(b&&m.contains(c.ownerDocument,c)&&zb(ub(c,"script")),c.parentNode.removeChild(c));return this},empty:function(){for(var a,b=0;null!=(a=this[b]);b++){1===a.nodeType&&m.cleanData(ub(a,!1));while(a.firstChild)a.removeChild(a.firstChild);a.options&&m.nodeName(a,"select")&&(a.options.length=0)}return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return m.clone(this,a,b)})},html:function(a){return V(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a)return 1===b.nodeType?b.innerHTML.replace(fb,""):void 0;if(!("string"!=typeof a||mb.test(a)||!k.htmlSerialize&&gb.test(a)||!k.leadingWhitespace&&hb.test(a)||rb[(jb.exec(a)||["",""])[1].toLowerCase()])){a=a.replace(ib,"<$1>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(m.cleanData(ub(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=arguments[0];return this.domManip(arguments,function(b){a=this.parentNode,m.cleanData(ub(this)),a&&a.replaceChild(b,this)}),a&&(a.length||a.nodeType)?this:this.remove()},detach:function(a){return this.remove(a,!0)},domManip:function(a,b){a=e.apply([],a);var c,d,f,g,h,i,j=0,l=this.length,n=this,o=l-1,p=a[0],q=m.isFunction(p);if(q||l>1&&"string"==typeof p&&!k.checkClone&&nb.test(p))return this.each(function(c){var d=n.eq(c);q&&(a[0]=p.call(this,c,d.html())),d.domManip(a,b)});if(l&&(i=m.buildFragment(a,this[0].ownerDocument,!1,this),c=i.firstChild,1===i.childNodes.length&&(i=c),c)){for(g=m.map(ub(i,"script"),xb),f=g.length;l>j;j++)d=i,j!==o&&(d=m.clone(d,!0,!0),f&&m.merge(g,ub(d,"script"))),b.call(this[j],d,j);if(f)for(h=g[g.length-1].ownerDocument,m.map(g,yb),j=0;f>j;j++)d=g[j],ob.test(d.type||"")&&!m._data(d,"globalEval")&&m.contains(h,d)&&(d.src?m._evalUrl&&m._evalUrl(d.src):m.globalEval((d.text||d.textContent||d.innerHTML||"").replace(qb,"")));i=c=null}return this}}),m.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){m.fn[a]=function(a){for(var c,d=0,e=[],g=m(a),h=g.length-1;h>=d;d++)c=d===h?this:this.clone(!0),m(g[d])[b](c),f.apply(e,c.get());return this.pushStack(e)}});var Cb,Db={};function Eb(b,c){var d,e=m(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:m.css(e[0],"display");return e.detach(),f}function Fb(a){var b=y,c=Db[a];return c||(c=Eb(a,b),"none"!==c&&c||(Cb=(Cb||m("