#!/usr/bin/perl
 
#
# Set these here!  These point to your original bind files, not the new stuff.
#

$named_boot = '/opt/etc/nsmaint/tmp/ns/named.boot';
$db_files = '/opt/etc/nsmaint/tmp/ns';

############################

open (STDERR, ">&STDOUT");
select ((select(STDOUT), $| = 1)[0]); # Synchronize STDOUT
select ((select(STDERR), $| = 1)[0]); # with STDERR
 
($dns_lib = $0) =~ s:(^|/)[^/]*$::;
($script=$0) =~ s:^.*/([^/]*)$:$1:;     # Get the simple name of this script.
 
push (@INC, $dns_lib);
 
require 5.0;
require 'dns_lib.perl';

umask 0007;

$SIG{'INT'} = 'bail';


die "$tree_root doesn't exist or is not writable.\n" unless (-e $tree_root && -d _ && -w _);

$tree{'/'} = 1;

open (NAMED_BOOT, $named_boot) || die "Couldn't find $named_boot\n";
open (ZONEINFO, ">$tree_root/.zoneinfo") || die "Couldn't create .zoneinfo\n";

FILE_READ_LOOP: while (<NAMED_BOOT>) {

    chomp;
    s/[\;\#].*$//o;		# Kill comments
    s/^\s*//o;			# Kill leading whitespace
    s/\s*$//o;			# Kill trailng whitespace

    next FILE_READ_LOOP unless $_; # Bail if $_ is NIL

    tr/A-Z/a-z/;
    @entry = split;

    $zone = $entry[1];
    $zone .= '.' unless $zone =~ m/\.$/o;

    if ($entry[0] eq 'primary') {

	print "PRIMARY: $zone\n";

	$path = getpath ($zone);
	$previous_host = $zone;

	open (DBFILE, "$db_files/$entry[2]") || (warn "Can't open DB file: $entry[2]\n" && next);

	while (<DBFILE>) {

	    chomp;	      
	    s/[\;\#].*$//o;	# Kill comments
	    s/\s*$//o;		# Kill trailng whitespace

	    if (m/^\$ORIGIN\s+(\S+)$/io) {

		$previous_host = $1;
		next;

	    }
	    
	    last unless m/^\s*$/o; # Bail if $_ is not NIL (including whitespace)

	} # End while (leaving line in $_)
	    
	die "No SOA record: $entry[2]\n" unless s/^(\S+)(\s+IN\s+SOA\s+)/\@$2/oi;

	if ($1 !~ m/\.$/o) {

	    $previous_host = "$1.$previous_host" unless $1 eq '@';

	} else {

	    $previous_host = $1 unless $1 eq '@';

	}

	die "Current zone ($zone) and SOA origin ($previous_host) don't match.\n" unless $previous_host =~ m/^$zone$/i;

	if (-e "$path.soa") {

	    warn "SOA for $entry[2] exists already.\n";
	    open (SOA, ">/dev/null") || die "Can't open /dev/null $!\n";

	} else {

	    open (SOA, ">$path.soa") || die "Can't open new SOA file: $zone\n";

	}
	    
	print SOA "$_\n";

	if (m/[^(;]*\([^);]*(;.*)?$/o) {

	    while (<DBFILE>) { 

		print SOA $_;
		last if m/^[^\)\;]*\)\s*(\;.*)?$/o;

		} # End while

	} # End if
	
	close SOA;

	open (PRIMARY, ">>$path.primary") || die "Can't open PRIMARY file: $zone\n";
	$sortme{"$path.primary"} = 1;
	$previous_host = '@';

	PRIMARY_DB_READ:  while (<DBFILE>) {

	    chomp;
	    s/[\;\#].*$//o;	# Kill comments
	    s/\s*$//o;		# Kill trailng whitespace

	    next PRIMARY_DB_READ if m/^\s*$/o;

	    if (m/^\$ORIGIN\s+(\S+)$/io) {

		$new_origin = lc($1);
		$new_origin .= ".$zone" if $new_origin !~ m/\.$/o;
		
		print "NEW ORIGIN: $new_origin\n";

		warn "NEW ORIGIN ($new_origin) outside of parent zone ($zone).\n" if $new_origin !~ m/$zone$/i;
		close (PRIMARY);
		$path = getpath ($new_origin);
		open (PRIMARY, ">>$path.primary") || die "Can't open PRIMARY file: $new_origin\n";
		$sortme{"$path.primary"} = 1;

		next PRIMARY_DB_READ;

	    }

	    s/^(\s+)/$previous_host$1/;
	    m/^(\S+)\s+/;
	    $previous_host = $1;

	    if ($previous_host =~ m/^([^.]+)\.(.*)$/o) {

		$new_host = lc($1);
		do { warn "ERROR: No new host from $previous_host!\n"; next PRIMARY_DB_READ } if $new_host eq "";
		$new_origin = lc($2);
		do { warn "ERROR: No new origin from $previous_host!\n"; next PRIMARY_DB_READ } if $new_origin eq "";
		$new_origin .= ".$zone" if $new_origin !~ m/\.$/o;

		warn "DOTTED HOST ORIGIN ($new_origin) outside of parent zone ($zone).\n" if $new_origin !~ m/$zone$/i;

		$path = getpath ($new_origin);
		open (TEMP_PRIMARY, ">>$path.primary") || die "Can't open PRIMARY file: $new_origin\n";
		$sortme{"$path.primary"} = 1;
		$numtabs = 3 - int(length($new_host)/8);
		$ws = $numtabs > 0 ? "\t" x $numtabs : ' ';

		s/^(\S+)\s+(\S+)\s+(\S+)\s+(.+)$/$new_host$ws$2\t$3\t$4/;
		$in_rec = $2;
		$rec_type = $3;
		die "Bad Record: $_, Line: $., Domain: $zone\n" if $in_rec !~ m/^IN$/io;
		die "Multiple SOA records: $zone\n" if $rec_type =~ m/^SOA$/io;
		
		if ($rec_type !~ m/^HINFO$/io && $rec_type !~ m/^TXT$/io) {

		    print TEMP_PRIMARY "$_\n";
		    print "DOTTED HOST NAME: $new_host.$new_origin from $zone\n";

		}
		
		close (TEMP_PRIMARY);
		next PRIMARY_DB_READ;

	    }

	    $numtabs = 3 - int(length($previous_host)/8);
	    $ws = $numtabs > 0 ? "\t" x $numtabs : ' ';

	    s/^(\S+)\s+(\S+)\s+(\S+)\s+(.+)$/$1$ws$2\t$3\t$4/;
	    $in_rec = $2;
	    $rec_type = $3;
	    die "Bad Record: $_, Line: $., Domain: $zone\n" if $in_rec !~ m/^IN$/io;
	    die "Multiple SOA records: $zone\n" if $rec_type =~ m/^SOA$/io;
	    next PRIMARY_DB_READ if ($rec_type =~ m/^HINFO$/io || $rec_type =~ m/^TXT$/io);

	    print PRIMARY "$_\n";
	    
	} # End while PRIMARY_DB_READ

	close (PRIMARY);
	close (DBFILE);

    } elsif ($entry[0] eq 'secondary') {
    
	print "SECONDARY: $zone\n";

	$path = getpath ($zone);

	warn "SECONDARY for $zone exists already.\n" if -e "$path.secondary";
	open (SECONDARY, ">$path.secondary") || die "Can't open new SECONDARY file: $zone\n";

	$whine = 1;

	foreach $ns (@entry[2 .. $#entry]) {

	    if ($ns =~ m/^(\d{1,3}\.){3}\d{1,3}$/o) {

		print SECONDARY "$ns\n";
		$whine = 0;

	    }

	}

	print "No primary NS for secondary $entry[1].\n" if $whine;
	close (SECONDARY);
	
    }

}

close (NAMED_BOOT);
close (ZONEINFO);

foreach $file (keys (%sortme)) {

    open (SORT, $file) || warn "Can't open $file to sort: $!\n";
    @file = <SORT>;
    close (SORT);
    open (SORT, ">$file.tmp") || warn "Can't open $file.tmp to sort: $!\n";
    print "SORTING: $file\n";

    if ($file =~ m:^$tree_root/arpa/in-addr:o) {

	@sort = sort (numb @file);

    } else {

	@sort = sort (lex @file);

    }

    for ($i=$#sort; $i > 0; $i--) {

	if ($sort[$i-1] eq $sort[$i]) {

	    print "DUPLICATES in $file [$i-1] & [$i]:\n1 - $sort[$i-1]2 - $sort[$i]\n";
	    splice (@sort, $i-1, 1);

	}

    }

    print (SORT @sort);
    close (SORT);
    rename ("$file.tmp", $file) || warn "Can't rename $file.tmp to $file: $!\n";

}

exit;


sub lex { $a cmp $b }


sub numb { $a <=> $b }
 

sub getpath {

    local ($domain) = @_;
    local (@dirs) = reverse (split ('\.', lc($domain)));
    local ($path) = '/';

    foreach $dir (@dirs) {

	$path .= "$dir/";
	next if $tree{$path};	# Bail if we have the hash
	
	if (-e $tree_root.$path) { # Does it exist?

	    if (! -d _) {	# Is it a directory?

		die "$tree_root$path is not a directory.\n";

	    }


	} elsif (mkdir ($tree_root.$path, 0770)) {

	    mkdir ($tree_root.$path.'RCS', 0770) || warn "Couldn't create RCS directory in $dir.  $!\n";
	    print ZONEINFO join ('.', reverse (split ('/', $path))),"\n";

	} else {

	    die "$tree_root$path is not a directory and could not be created.\n";
	}

	$tree{$path} = 1;

    }

    return $tree_root.$path;

}
    

sub bail {

    close (NAMED_BOOT);
    close (ZONEINFO);
    close (PRIMARY);
    close (SECONDARY);
    close (SOA);
    exit;

}
