package HaCi::Plugins::GenZonefile;

use strict;
use warnings;

use base qw/HaCi::Plugin/;
use HaCi::Mathematics qw/dec2net netv6Dec2net/;
use HaCi::Utils qw/netID2Stuff getNetworkChilds getMaintInfosFromNet/;
use HaCi::GUI::gettext qw/_gettext/;
use Net::Ping;

our $conf; *conf  = \$HaCi::Conf::conf;

our $INFO	= {
	name				=> 'GenZonefile',
	version			=> '0.1',
	recurrent		=> 1,
	onDemand		=> 1,
	description	=> _gettext('This Plugin generates a zonefile which contains all ip addresses of this network.'),
	api					=> [
		{
			name	=> 'STATUS', 
			descr	=> _gettext('Generated zonefile'),
		},
	],
	menuOnDemand	=> [
		{
			NAME			=> 'origin',
			DESCR			=> 'Origin',
			TYPE			=> 'textbox',
			SIZE			=> 25,
			MAXLENGTH	=> 128,
			VALUE			=> 'example.com'
		},
		{
			NAME			=> 'primaryns',
			DESCR			=> 'Primary Nameserver',
			TYPE			=> 'textbox',
			SIZE			=> 25,
			MAXLENGTH	=> 128,
			VALUE			=> 'ns1.example.com'
		},
		{
			NAME			=> 'secondaryns',
			DESCR			=> 'Secondary Nameserver',
			TYPE			=> 'textbox',
			SIZE			=> 25,
			MAXLENGTH	=> 128,
			VALUE			=> 'ns2.example.com'
		},
		{
			NAME			=> 'primarymx',
			DESCR			=> 'Primary Mailexchanger with priority',
			TYPE			=> 'textbox',
			SIZE			=> 25,
			MAXLENGTH	=> 128,
			VALUE			=> '10 mx1.example.com'
		},
		{
			NAME			=> 'secondarymx',
			DESCR			=> 'Secondary Mailexchanger with priority',
			TYPE			=> 'textbox',
			SIZE			=> 25,
			MAXLENGTH	=> 128,
			VALUE			=> '20 mx2.example.com'
		},
		{
			NAME			=> 'mailaddress',
			DESCR			=> 'Email address',
			TYPE			=> 'textbox',
			SIZE			=> 25,
			MAXLENGTH	=> 128,
			VALUE			=> 'hostmaster.example.com'
		},
		{
			NAME			=> 'ttl',
			DESCR			=> 'Default TTL',
			TYPE			=> 'textbox',
			SIZE			=> 4,
			MAXLENGTH	=> 10,
			VALUE			=> '3600'
		},
		{
			NAME			=> 'refresh',
			DESCR			=> 'Refresh',
			TYPE			=> 'textbox',
			SIZE			=> 5,
			MAXLENGTH	=> 10,
			VALUE			=> '86400'
		},
		{
			NAME			=> 'retry',
			DESCR			=> 'Retry',
			TYPE			=> 'textbox',
			SIZE			=> 4,
			MAXLENGTH	=> 10,
			VALUE			=> '7200'
		},
		{
			NAME			=> 'expire',
			DESCR			=> 'Expire',
			TYPE			=> 'textbox',
			SIZE			=> 7,
			MAXLENGTH	=> 10,
			VALUE			=> '3600000'
		},
		{
			NAME			=> 'minimum',
			DESCR			=> 'Minimum',
			TYPE			=> 'textbox',
			SIZE			=> 6,
			MAXLENGTH	=> 10,
			VALUE			=> '172800'
		},
		{
			NAME			=> 'arr',
			DESCR			=> 'A-RR',
			TYPE			=> 'checkbox',
			CHECKED		=> '1'
		},
		{
			NAME			=> 'ptrrr',
			DESCR			=> 'PTR-RR',
			TYPE			=> 'checkbox',
			CHECKED		=> '0'
		},
	],
	globMenuRecurrent	=> [
		{
			NAME			=> 'targetDir',
			DESCR			=> 'Target directory',
			TYPE			=> 'textbox',
			SIZE			=> 25,
			MAXLENGTH	=> 255,
			VALUE			=> $conf->{static}->{path}->{workdir} . '/spool/GenZonefile'
		},
	]
};

sub run_onDemand {
	my $self				= shift;
	my $networkRef	= shift;
	my $confValues	= shift;

	my $networkDec	= $networkRef->{network};
	my $ipv6				= ($networkRef->{ipv6ID}) ? 1 : 0;
	my $network			= ($ipv6) ? &netv6Dec2net($networkDec) : &dec2net($networkDec);
	my $zonefiles		= $self->genZonefile($networkRef, $confValues);

	my $zonefile	= '';
	my $origins		= '';
	foreach (@{$zonefiles}) {
		my $origin		= $_->{ORIGIN};
		my $content		= $_->{ZONEFILE};

		$zonefile	.= "\n" . ' ' x 80 . "\n" if $zonefile ne '';
		$origins	.= ' and ' if $origins ne '';
		$zonefile	.= $content;
		$origins	.= $origin;
	}

	$self->{zonefile}	= $zonefile;
	$self->{origin}		= $origins;
	$self->{network}	= $network;

	return 1;
}

sub run_recurrent {
	my $self			= shift;
	my $networks	= shift;
	my $config		= shift;

	my $destDir	= $config->{-1}->{targetDir};

	unless (-d $destDir || mkdir $destDir) {
		$self->warnl("Cannot create Directory '$destDir': $!", 1);
	};

	foreach (@$networks) {
		my $netID			= $_->{ID};
		my $zonefiles	= $self->genZonefile($_, $config->{$netID}, 1);
	
		foreach (@{$zonefiles}) {
			my $origin		= $_->{ORIGIN};
			my $content		= $_->{ZONEFILE};
			my $zoneFile	= $destDir . '/' . $origin;

			unless (open ZONE, '>' . $zoneFile) {
				$self->warnl("Cannot open Zonefile '$zoneFile' for writing: $!", 1);
			}
			print ZONE $content;
			close ZONE;
		}
	}
}

sub genZonefile {
	my $self				= shift;
	my $networkRef	= shift;
	my $confValues	= shift;
	my $recurrent		= shift || 0;

	my $networkDec	= $networkRef->{network};
	my $netID				= $networkRef->{ID};
	my $ipv6ID			= $networkRef->{ipv6ID};

	my $origin		= $confValues->{origin} || 'example.com';
	my $primNS		= $confValues->{primaryns} || 'ns1.example.com';
	my $secNS			= $confValues->{secondaryns} || 'ns2.example.com';
	my $primMX		= $confValues->{primarymx} || '10 mx1.example.com';
	my $secMX			= $confValues->{secondarymx} || '20 mx2.example.com';
	my $mail			= $confValues->{mailaddress} || 'hostmaster.example.com';
	my $ttl				= $confValues->{ttl} || '3600';
	my $refresh		= $confValues->{refresh} || '86400';
	my $retry			= $confValues->{retry} || '7200';
	my $expire		= $confValues->{expire} || '3600000';
	my $minimum		= $confValues->{minimum} || '172800';
	my $arr				= $confValues->{arr};
	my $ptrrr			= $confValues->{ptrrr};
	my $ipv6			= ($ipv6ID) ? 1 : 0;
	my $network		= ($ipv6) ? &netv6Dec2net($networkDec) : &dec2net($networkDec);
	my @networks	= &getNetworkChilds($netID);
	my @time			= localtime();
	my $serial		= sprintf("%04i%02i%02i%02i", $time[5] + 1900, $time[4] + 1, $time[3], 1);
	my ($ip, $cidr)	= split(/\//, $network, 2);
	my $zonefiles	= [];

	my $zonefileA	= qq{
\$TTL 3600
$origin.\tIN SOA    $primNS. $mail. (
\t$serial ; serial
\t$refresh      ; refresh
\t$retry       ; retry
\t$expire    ; expire
\t$minimum     ; minimum
\t)
;                       
\tIN NS    $primNS.
\tIN NS    $secNS.
;                       
\tIN MX    $primMX.
\tIN MX    $secMX.
;
};

	my $nets	= ();
	foreach (@networks) {
		$nets->{$_->{network}}	= $_;
	}
	
	my @as		= ();
	my @ptrs	= ();
	foreach (sort {$a<=>$b} keys %{$nets}) {
		my $net					= $nets->{$_};
		my $netID				= $net->{ID};
		my $networkDec	= $net->{network};
		my $network			= ($ipv6) ? &netv6Dec2net($networkDec) : &dec2net($networkDec);
		my ($ip, $cidr)	= split(/\//, $network, 2);
		next if ($ipv6 && $cidr != 128) || (!$ipv6 && $cidr != 32);

		my $netInfos		= &getMaintInfosFromNet($netID);
		my $descr				= $netInfos->{description};
		$descr					=~ s/\s/_/g;
		$descr					=~ s/\W//g;
		$descr					=~ s/_/-/g;
		$descr					=~ s/$origin$//;
	
		push @as, "$descr\tIN " . (($ipv6) ? 'AAAA' : 'A') . "    $ip\n" if $arr;
		$ip	=~ s/.*[\.:](\w+)/$1/;
		$ip	= join('.', reverse split//, $ip) if $ipv6;
		push @ptrs, "$ip\tIN PTR  $descr.$origin.\n" 
	}

	$zonefileA	.= join('', @as);
	push @{$zonefiles}, {
		ORIGIN		=> $origin,
		ZONEFILE	=> $zonefileA
	};

	my $originPTR	= '';
	if ($ptrrr) {
		if ($cidr == (($ipv6) ? 112 : 24)) {
			$ip					=~ s/[\.:]\w+$//;
			$ip					=~ s/://g;
			my $ipRef		= join('.', reverse (($ipv6) ? split//, $ip : split/\./, $ip));
			$originPTR	= $ipRef . '.' . (($ipv6) ? 'ip6' : 'in-addr') . '.arpa';

			my $zonefilePTR		.= qq{
\$TTL 3600
$originPTR.\tIN SOA    $primNS. $mail. (
\t$serial ; serial
\t$refresh      ; refresh
\t$retry       ; retry
\t$expire    ; expire
\t$minimum     ; minimum
\t)
;                       
\tIN NS    $primNS.
\tIN NS    $secNS.
;                       
\tIN MX    $primMX.
\tIN MX    $secMX.
;
};
			$zonefilePTR	.= join('', @ptrs);
			push @{$zonefiles}, {
				ORIGIN		=> $originPTR,
				ZONEFILE	=> $zonefilePTR
			};
		} else {
			if ($recurrent) {
				$self->warnl("Reverse zonefile can only be generated for /24 (IPv4) and /112 (IPv6) networks! ($network)\n", 2);
			} else {
				push @{$zonefiles}, {
					ORIGIN		=> $originPTR,
					ZONEFILE	=> "Reverse zonefile can only be generated for /24 (IPv4) and /112 (IPv6) networks!"
				};
			}
		}
	}

	my @origins				= ();
	push @origins, $origin if $arr;
	push @origins, $originPTR if $ptrrr && $cidr == (($ipv6) ? 112 : 24);

	return $zonefiles;
}

sub show {
	my $self	= shift;
	my $show	= {
		HEADER	=> sprintf("Generated zonefile for '%s'", $self->{origin}),
		BODY		=> [
			{
				elements	=> [
					{
						target	=> 'single',
						type		=> 'label',
						value		=> '<pre>' . $self->{zonefile} . '</pre>'
					},
				]
			},
		]
	};

	return $show;
}

1;
