#! /usr/bin/perl
#line 3
# Copyright: (C) 2014-2014 Heiko Schlittermann <hs@schlittermann>
# This program is released unter the Terms of the GPL.
use 5.010;
use strict;
use warnings;
use if $ENV{DEBUG} // '' eq 'dnsvi' => 'Smart::Comments';
use Getopt::Long;
use Pod::Usage;
use File::Copy;

#use blib;
use DNS::Vi;

sub slurp {
    local $/ = undef;
    local @ARGV = @_;
    <>;
}

sub main {
    my %o = (
        local  => undef,
        key    => undef,
        server => undef,
        debug  => undef,
        editor => $ENV{EDITOR} // 'vi',
        skip => [qw/RRSIG NSEC3 NSEC3PARAM NSEC DNSKEY TSIG/],
    );

    GetOptions(
        'k|key=s'    => \$o{key},
        's|server=s' => \$o{server},
        'd|debug!'   => \$o{debug},
        'l|local!'   => \$o{local},
        'editor=s'   => \$o{editor},
      )
      && @ARGV >= 1
      or pod2usage();

    my $zone = shift @ARGV;

    $o{server} =
      $o{local} ? 'localhost' : (split ' ', `dig +short soa $zone`)[0]
      if not defined $o{server};

    my @dig = (
        dig => 'AXFR',
        defined $o{key} ? (-k => $o{key}) : (),
        defined $o{server} ? ("\@$o{server}") : (),
        $zone
    );

    my @zone1 = parse($_ = `@dig`, { -skip => $o{skip} } )
      or die "Empty zone\n";
    my $fh2;
    my @zone2 = do {
	if (my $file = shift @ARGV) {
	    parse(slurp($file), { -skip => $o{skip} });
	} 
	else {
	    edit(@zone1, { -skip => $o{skip}, -editor => $o{editor}, -backup => \$fh2 });
	}
    };
    ### @zone2

    my %delta = delta(\@zone1, \@zone2);
    if (!%delta) {
	    say 'nothing changed';
	    return 0;
    }

    say 'The following changes need your confirmation.';
    say join "\n", show(@delta{qw/add del/});
    print 'confirm (yes|NO): ';
    return 1 if <STDIN> !~ /^y/i;

    update(\@zone1, @delta{qw/add del/}, {
	-server => $o{server},
	-local => $o{local},
	-debug => $o{debug},
	-key => $o{key}})
    or do { 
	copy($fh2->filename, ",dnsvi-$$")
	and say "Saved as ',dnsvi-$$'"
	    if $fh2;
    };

    return 0;
}

exit main(@ARGV) if not caller;

__END__

=head1 NAME

 dnsvi -- editor for dynamically maintained zones

=head1 SYNOPSIS

 dnsvi [[-l] | [[-k key] [-s server]]] [-d] <zone> [<file>]

=head1 DESCRIPTION

This tools supports you in maintaining a dynamic zone. Normally you'll
use it with the name of zone. For batch mode you may use it with an
additional parameter, die edited zone file.

=head2 OPTIONS

=over

=item B<-l>|B<--local> 

Local mode, when running on the server where the updates need to go to.
But still zone transfers need to be enabled! (default: off)

=item B<-s>|B<--server> B<server-name>

The name of the server to contact for the AXFR and the update.
(default: main nameserver from the SOA record)

=item B<-k>|B<--key> B<key-file>

The name of the key file we need for TSIG (the AXFR will use it,
as well as the update).

To create such a key you may use 

    dnssec-keygen -a HMAC-MD5 -b 512 -n USER heiko

Then copy the resulting files somewhere (you'll need both files).
On the server side include the key into to configuration:

    key "<name>" {
	algorithm   HMAC-MD5;
	secret	    "<the secret from the created key file>"
    };

Per zone you should use 

    zone "<zone>" {
	...
	update-policy {
	    grant local-ddns zonesub any;   // support for -l
	    grant <key-name> zonesub;	    // support for -k
	};
	...
    };

=item B<-d>

This option enables debugging of C<nsupdate>. (default: off)

=back

=head1 PREREQUISITES

We need some tools to be installed:

=over

=item B<dig>

The domain information groper is used for the zone transfer currently.

=item B<nsupdate>

The nsupdate tool is used to send the updates back to the server.

=back

=cut
