From 5acd51fdd34c49811d045286c390779508e01755 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 9 May 2016 15:09:09 +0200 Subject: [PATCH] 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']} + + 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 @@ +
+ + + + + + + + + + + + + + + + + + + + + {$existing_entries} + +
RecordTypePriorityContentTTL 
+