add first part of new dns-editor

Signed-off-by: Michael Kaufmann (d00p) <d00p@froxlor.org>
This commit is contained in:
Michael Kaufmann (d00p)
2016-05-09 15:09:09 +02:00
parent 6369e160b8
commit 5acd51fdd3
11 changed files with 361 additions and 2 deletions

View File

@@ -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)

233
dns_editor.php Normal file
View File

@@ -0,0 +1,233 @@
<?php
if (! defined('AREA'))
die('You cannot access this file directly!');
/**
* This file is part of the Froxlor project.
* Copyright (c) 2016 the Froxlor Team (see authors).
*
* For the full copyright and license information, please view the COPYING
* file that was distributed with this source code. You can also view the
* COPYING file online at http://files.froxlor.org/misc/COPYING.txt
*
* @copyright (c) the authors
* @author Froxlor team <team@froxlor.org> (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("<br>", $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) . "\";");

View File

@@ -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;

View File

@@ -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');
}

View File

@@ -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';

View File

@@ -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 = '';

View File

@@ -4,6 +4,7 @@ $header
<h2>
<img src="templates/{$theme}/assets/img/icons/domain_edit_big.png" alt="{$title}" />&nbsp;
{$title}
&nbsp;(<small><a href="{$linker->getLink(array('section' => 'domains', 'page' => 'domaindnseditor', 'domain_id' => $id))}">edit DNS</a></small>)
</h2>
</header>

View File

@@ -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%;
}

View File

@@ -0,0 +1,10 @@
<tr>
<td>{$entry['record']}</td>
<td>{$entry['type']}</td>
<td><if $entry['prio'] <= 0>&nbsp;<else>{$entry['prio']}</if></td>
<td>{$entry['content']}</td>
<td>{$entry['ttl']}</td>
<td>
<a href="{$linker->getLink(array('section' => 'domains', 'page' => $page, 'action' => 'delete', 'domain_id' => $domain_id, 'id' => $entry['id']))}"><img src="templates/{$theme}/assets/img/icons/delete.png" alt="{$lng['panel']['delete']}" title="{$lng['panel']['delete']}" /></a>
</td>
</tr>

28
templates/Sparkle/dns_editor/index.tpl vendored Normal file
View File

@@ -0,0 +1,28 @@
$header
<article>
<header>
<h2>
<img src="templates/{$theme}/assets/img/icons/domain_edit_big.png" alt="" />&nbsp;
DNS Editor&nbsp;(<a href="{$linker->getLink(array('section' => 'domains', 'page' => 'domains', 'action' => 'edit', 'id' => $domain_id))}">{$domain}</a>, {$entriescount} records)
</h2>
</header>
<if !empty($errors)>
<div class="errorcontainer bradius">
<div class="errortitle">{$lng['error']['error']}</div>
<div class="error">{$errors}</div>
</div>
</if>
<if !empty($success_message)>
<div class="successcontainer bradius">
<div class="successtitle">{$lng['success']['success']}</div>
<div class="success">{$success_message}</div>
</div>
</if>
<section>
{$record_list}
</section>
</article>
$footer

25
templates/Sparkle/dns_editor/list.tpl vendored Normal file
View File

@@ -0,0 +1,25 @@
<form method="post" action="{$linker->getLink(array('section' => 'domains', 'page' => $page, 'action' => 'add_record', 'domain_id' => $domain_id))}">
<table class="full hl">
<thead>
<tr>
<th class="size-20">Record</th>
<th class="size-5">Type</th>
<th class="size-5">Priority</th>
<th class="size-50">Content</th>
<th class="size-10">TTL</th>
<th class="size-10">&nbsp;</th>
</tr>
</thead>
<tbody>
<tr>
<td><input id="dns_record" type="text" name="record[record]" class="small" placeholder="Record" value="{$record}" /></td>
<td><select id="dns_type" name="record[type]" class="small">{$type_select}</select></td>
<td><input id="dns_mxp" type="text" name="record[prio]" class="small" placeholder="MX priority" value="{$prio}" /></td>
<td><input id="dns_content" type="text" name="record[content]" class="small" placeholder="Content" value="{$content}" /></td>
<td><input id="dns_ttl" type="text" name="record[ttl]" class="small" placeholder="18000" value="{$ttl}" /></td>
<td><input type="submit" class="submitsearch" value="add" name="add" /></td>
</tr>
{$existing_entries}
</tbody>
</table>
</form>