#!/usr/local/bin/perl
# create_master.cgi
# Create a new master zone

require './bind8-lib.pl';
&ReadParse();
&error_setup($text{'mcreate_err'});
%access = &get_module_acl();
$access{'master'} || &error($text{'mcreate_ecannot'});
$access{'ro'} && &error($text{'master_ero'});
$conf = &get_config();
$vconf = $in{'view'} ne '' ? $conf->[$in{'view'}]->{'members'} : $conf;

# validate inputs
if ($in{'rev'}) {
	local($ipv4);
	($ipv4 = &check_net_ip($in{'zone'})) ||
	$config{'support_aaaa'} &&
	($in{'zone'} =~ /^([\w:]+)(\/\d+)$/ || &check_ip6address($1)) ||
		&error(&text('create_enet', $in{'zone'}));
	if ($ipv4) {
		$in{'zone'} = &ip_to_arpa($in{'zone'});
		}
	else {
		$in{'zone'} = &net_to_ip6int($1, ($2 ? substr($2, 1) : "" ));
		}
	}
else {
	($in{'zone'} =~ /^[\d\.]+$/ || $in{'zone'} =~ /^[\d\:]+(\/[\d]+)?$/) &&
		&error(&text('create_edom2', $in{'zone'}));
	&valdnsname($in{'zone'}, 0, ".") ||
		&error(&text('create_edom', $in{'zone'}));
	}
$in{'zone'} =~ s/\.$//;
&valdnsname($in{'master'}, 0, ".") ||
	&error(&text('master_emaster', $in{'master'}));
if ($in{'master'} !~ /\.$/) { $in{'master'} .= "."; }
&valemail($in{'email'}) || &valemail(&dotted_to_email($in{'email'})) ||
	&error(&text('master_eemail', $in{'email'}));
$in{'email'} = &email_to_dotted($in{'email'});
$in{'refresh'} =~ /^\d+$/ ||
        &error(&text('master_erefresh', $in{'refresh'}));
$in{'retry'} =~ /^\d+$/ ||
        &error(&text('master_eretry', $in{'retry'}));
$in{'expiry'} =~ /^\d+$/ ||
        &error(&text('master_eexpiry', $in{'expiry'}));
$in{'minimum'} =~ /^\d+$/ ||
        &error(&text('master_eminimum', $in{'minimum'}));
$base = $config{'master_dir'} ? $config{'master_dir'} :
	$access{'dir'} eq '/' ? &base_directory($conf) :
				$access{'dir'};
$base =~ s/\/+$// if ($base ne '/');
if ($in{'tmpl'}) {
	for($i=0; $config{"tmpl_$i"}; $i++) {
		@c = split(/\s+/, $config{"tmpl_$i"}, 3);
		if ($c[1] eq 'A' && !$c[2] && !&check_ipaddress($in{'ip'})) {
			&error($text{'master_eip'});
			}
		}
	}
foreach $z (&find("zone", $vconf)) {
	if ($z->{'value'} eq $in{'zone'}) {
		&error($text{'master_etaken'});
		}
	}
if (!$in{'file_def'}) {
	$in{'file'} =~ /^\S+$/ ||
		&error(&text('create_efile', $in{'file'}));
	if ($in{'file'} !~ /^\//) {
		$in{'file'} = $base."/".$in{'file'};
		}
	&allowed_zone_file(\%access, $in{'file'}) ||
		&error(&text('create_efile2', $in{'file'}));
	}
elsif ($in{'rev'}) {
	# create filename for reverse zone
	$in{'file'} = &ip6int_to_net(&arpa_to_ip($in{'zone'}));
	$in{'file'} =~ s/\//_/;
	local $format = $config{'reversezonefilename_format'};
	$format =~ s/ZONE/$in{'file'}/g;
	$in{'file'} = $base."/".$format;
	}
else {
	# create filename for forward zone
	local $format = $config{'forwardzonefilename_format'};
	$format =~ s/ZONE/$in{'zone'}/g;
	$in{'file'} = $base."/".$format;
	}
-r $config{'chroot'}.$in{'file'} && &error(&text('create_efile4', $in{'file'}));
if ($in{'slave'}) {
	@mips = split(/\s+/, $in{'mip'});
	@mips || &error($text{'master_emips'});
	foreach $m (@mips) {
		&check_ipaddress($m) || &error(&text('master_emip', $m));
		}
	}

# Create the zone file
&lock_file("$config{'chroot'}$in{'file'}");
open(ZONE, ">$config{'chroot'}$in{'file'}") ||
	&error(&text('create_efile3', $in{'file'}, $!));
print ZONE "\$ttl $in{'minimum'}$in{'minunit'}\n" if ($config{'master_ttl'});
close(ZONE);
$in{'file'} =~ s/^$config{'chroot'}//;

# create the SOA and NS records
if ($config{'soa_style'} == 1) {
        $serial = &date_serial()."00";
        }
else {
        $serial = time();
        }
$vals = "$in{'master'} $in{'email'} (\n".
        "\t\t\t$serial\n".
        "\t\t\t$in{'refresh'}$in{'refunit'}\n".
        "\t\t\t$in{'retry'}$in{'retunit'}\n".
        "\t\t\t$in{'expiry'}$in{'expunit'}\n".
        "\t\t\t$in{'minimum'}$in{'minunit'} )";
&create_record($in{'file'}, "$in{'zone'}.", undef, "IN", "SOA", $vals);
&create_record($in{'file'}, "$in{'zone'}.", undef, "IN", "NS", $in{'master'})
	if ($in{'master_ns'});
if ($in{'slave'} && $access{'remote'}) {
	local @bn = gethostbyname($in{'slave'});
	local $full = "$bn[0].";
	&create_record($in{'file'}, "$in{'zone'}.", undef, "IN", "NS", $full);
	}

if ($in{'tmpl'}) {
	# Create template records
	for($i=0; $config{"tmpl_$i"}; $i++) {
		@c = split(/\s+/, $config{"tmpl_$i"}, 3);
		&create_record($in{'file'},
			       $c[0] eq '.' ? "$in{'zone'}." : $c[0], undef,
			       "IN", $c[1], $c[2] ? $c[2] : $in{'ip'});
		}
	if ($config{'tmpl_include'}) {
		# Add whatever is in the template file
		open(TMPL, $config{'tmpl_include'});
		open(FILE, ">>$config{'chroot'}$in{'file'}");
		while(<TMPL>) {
			print FILE $_;
			}
		close(TMPL);
		close(FILE);
		}
	}
&unlock_file("$config{'chroot'}$in{'file'}");
&set_ownership("$config{'chroot'}$in{'file'}");

# create the zone directive
if ($in{'view'} ne '') {
	$view = $conf->[$in{'view'}];
	&lock_file($config{'chroot'}.$view->{'file'});
	$dir = { 'name' => 'zone',
		 'values' => [ $in{'zone'} ],
		 'type' => 1,
		 'members' => [ { 'name' => 'type',
				  'values' => [ 'master' ] },
				{ 'name' => 'file',
				  'values' => [ $in{'file'} ] } ]
		};
	&save_directive($view, undef, [ $dir ], 1);
	&flush_file_lines();
	&unlock_file($config{'chroot'}.$view->{'file'});
	}
else {
	$named_conf = $config{'zones_file'} ? $config{'zones_file'}
					    : $config{'named_conf'};
	&lock_file($config{'chroot'}.$named_conf);
	open(CONF, ">>$config{'chroot'}$named_conf");
	print CONF "zone \"$in{'zone'}\" {\n";
	print CONF "\ttype master;\n";
	print CONF "\tfile \"$in{'file'}\";\n";
	print CONF "\t};\n";
	print CONF "\n";
	close(CONF);
	&unlock_file($config{'chroot'}.$named_conf);
	&set_ownership("$config{'chroot'}.$named_conf");
	}
&webmin_log("create", "master", $in{'zone'}, \%in);

# Add the new zone to the access list
if ($access{'zones'} ne '*') {
	$access{'zones'} .= " ".$in{'zone'};
	&save_module_acl(\%access);
	}
undef(@get_config_cache);
$conf = &get_config();
foreach $c (@$conf) {
	if ($c->{'name'} eq 'zone' && $c->{'value'} eq $in{'zone'}) {
		$idx = $c->{'index'};
		}
	}

# Create on slave server
if ($in{'slave'} && $access{'remote'}) {
	&error_setup($text{'master_err3'});
	&remote_error_setup(\&slave_error_handler);
	&remote_foreign_require($in{'slave'}, "bind8", "bind8-lib.pl");
	$slave_error && &error($slave_error);

	$sparent = &remote_foreign_call($in{'slave'}, "bind8",
					"get_config_parent");
	$sconfig = &remote_foreign_config($in{'slave'}, "bind8");
	$sconf = $sparent->{'members'};
	$opts = &find("options", $sconf);
	$opts || &error(&text('master_eslave', $in{'slave'}));
	foreach $z (&find("zone", $sconf)) {
		if ($z->{'value'} eq $in{'zone'}) {
			&error($text{'master_etaken'});
			}
		}
	@mdirs = map { { 'name' => $_ } } @mips;
	$masters = { 'name' => 'masters',
		     'type' => 1,
		     'members' => \@mdirs };
	$dir = { 'name' => 'zone',
		 'values' => [ $in{'zone'} ],
		 'type' => 1,
		 'members' => [ { 'name' => 'type',
				  'values' => [ 'slave' ] },
				$masters
			      ]
		};
	local $sbase;
	if ($in{'sfile_def'} != 1) {
		$sbase = $sconfig{'slave_dir'} ? $sconfig{'slave_dir'} :
				&remote_foreign_call($in{'slave'}, "bind8",
						     "base_directory", $sconf);
		}
	local $file;
	if ($in{'sfile_def'} == 0) {
		# Use entered slave filename
		$file = $in{'sfile'} =~ /^\// ? $in{'sfile'}
					     : $sbase."/".$in{'sfile'};
		}
	elsif ($in{'sfile_def'} == 2) {
		# Work out a filename
		if ($in{'rev'}) {
			# create filename for reverse zone
			$file = &ip6int_to_net(&arpa_to_ip($in{'zone'}));
			$file = s/\//_/;
			$file = $sbase."/".$in{'zone'}.".rev";
			}
		else {
			# create filename for forward zone
			$file = $sbase."/".$in{'zone'}.".hosts";
			}
		}
	push(@{$dir->{'members'}}, { 'name' => 'file',
				     'values' => [ $file ] } ) if ($file);
	&remote_foreign_call($in{'slave'}, "bind8", "save_directive", $sparent,
			     undef, [ $dir ], 0);
	&remote_foreign_call($in{'slave'}, "bind8", "flush_file_lines");
	}

&redirect(defined($idx) ? "edit_master.cgi?index=$idx" : "index.cgi");

sub slave_error_handler
{
$slave_error = $_[0];
}

