#!/usr/bin/perl -w
#
# REQUIRED:
# -p: part number for domains
#
# -n: named.conf file (or an include file with zones you want to import),
#     for example root@ns.isp.com:/var/named/named.conf
#
# OPTIONAL:
# -d: dry-run, debug: don't insert any records, just dump debugging output
# -s: import slave zones as master.  useful if you need to recreate your
#     primary nameserver from a secondary
# -c dir: override patch for downloading zone files (for example, when
#         downloading zone files from chrooted bind)
#
# need to manually put header in
#  /usr/local/etc/freeside/export.<datasrc./bind/<machine>/named.conf.HEADER
# (or, nowadays, better just to include the file freeside exports)

use strict;

use vars qw($domain_svcpart);

use Getopt::Std;
use Data::Dumper;
#use BIND::Conf_Parser;
#use DNS::ZoneParse 0.81;

use Net::SCP qw(scp iscp);

use FS::UID qw(adminsuidsetup datasrc);
use FS::Record qw(qsearch); #qsearchs);
#use FS::svc_acct_sm;
use FS::svc_domain;
use FS::domain_record;
#use FS::svc_acct;
#use FS::part_svc;

use vars qw($opt_p $opt_n $opt_s $opt_c $opt_d);
getopts("p:n:sc:d");

my $user = shift or die &usage;
adminsuidsetup $user;

$FS::svc_Common::noexport_hack = 1;
$FS::domain_record::noserial_hack = 1;

use vars qw($spooldir);
$spooldir = "/usr/local/etc/freeside/export.". datasrc. "/bind";
mkdir $spooldir unless -d $spooldir;

$domain_svcpart = $opt_p;

my $named_conf = $opt_n;

use vars qw($named_machine $prefix);
$named_machine = (split(/:/, $named_conf))[0];
my $pnamed_machine = $named_machine;
$pnamed_machine =~ s/^[\w\-]+\@//;
$prefix = "$spooldir/$pnamed_machine";
mkdir $prefix unless -d $prefix;

#iscp("$named_conf","$prefix/named.conf.import");
scp("$named_conf","$prefix/named.conf.import");

##

$FS::svc_domain::whois_hack=1;

my $p = Parser->new;
$p->parse_file("$prefix/named.conf.import");

print "\nBIND import completed.\n";

##

sub usage {
  die "Usage:\n\n  bind.import -p partnum -n \"user\@machine:/path/to/named.conf\" [ -s ] [ -c chroot_dir ] [ -f ] user\n";
}

########
BEGIN {
  
  package Parser;
  use BIND::Conf_Parser;
  use vars qw(@ISA $named_dir);
  @ISA = qw(BIND::Conf_Parser);

  $named_dir = 'COULD_NOT_FIND_NAMED_DIRECTORY_TRY_SETTING_-C_OPTION';
  sub handle_option {
    my($self, $option, $argument) = @_;
    return unless $option eq "directory";
    $named_dir = $argument;
    #warn "found named dir: $named_dir\n";
  }
  
  sub handle_zone {
    my($self, $name, $class, $type, $options) = @_;
    return unless $class eq 'in';
    return if grep { $name eq $_ } (qw(
      . localhost 127.in-addr.arpa 0.in-addr.arpa 255.in-addr.arpa
      0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa
      0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.int
    ));

    use FS::Record qw(qsearchs);
    use FS::svc_domain;

    my $domain =
      qsearchs('svc_domain', { 'domain' => $name } )
      || new FS::svc_domain( {
                               svcpart => $main::domain_svcpart,
                               domain  => $name,
                               action  => 'N',
                           } );
    unless ( $domain->svcnum ) {
      my $error = $domain->insert;
      die $error if $error;
    }

    if ( $type eq 'slave' && !$main::opt_s ) {

      if ( $main::opt_d ) {

        use Data::Dumper;
        print "$name: ". Dumper($options);

      } else {

        foreach my $master ( @{ $options->{masters} } ) {
          my $domain_record = new FS::domain_record( {
            'svcnum'  => $domain->svcnum,
            'reczone' => '@',
            'recaf'   => 'IN',
            'rectype' => '_mstr',
            'recdata' => $master,
          } );
          my $error = $domain_record->insert;
          die $error if $error;
        }

      }

    } elsif ( $type eq 'master' || ( $type eq 'slave' && $main::opt_s ) ) {

      my $file = $options->{file};
  
      use File::Basename;
      my $basefile = basename($file);
      my $sourcefile = $file;
      if ( $main::opt_c ) {
        $sourcefile = "$main::opt_c/$sourcefile" if $main::opt_c;
      } else {
        $sourcefile = "$named_dir/$sourcefile" unless $file =~ /^\//;
      }

      use Net::SCP qw(iscp scp);
      #iscp("$main::named_machine:$sourcefile",
      #     "$main::prefix/$basefile.import");
      scp("$main::named_machine:$sourcefile",
          "$main::prefix/$basefile.import");
    
      use DNS::ZoneParse 0.84;
      my $zone = DNS::ZoneParse->new("$main::prefix/$basefile.import");
    
      my $dump = $zone->dump;

      if ( $main::opt_d ) {

        use Data::Dumper;
        print "$name: ". Dumper($dump);

      } else {
    
        foreach my $rectype ( keys %$dump ) {
          if ( $rectype =~ /^SOA$/i ) {
            my $rec = $dump->{$rectype};
            $rec->{email} =~ s/\@/\./;
            my $domain_record = new FS::domain_record( {
              'svcnum'  => $domain->svcnum,
              'reczone' => $rec->{origin},
              'recaf'   => 'IN',
              'rectype' => $rectype,
              'recdata' =>
                $rec->{primary}. ' '. $rec->{email}. ' ( '.
               join(' ', map $rec->{$_},
                             qw( serial refresh retry expire minimumTTL ) ).
               ' )',
            } );
            my $error = $domain_record->insert;
            die $error if $error;
         } else {
            #die $dump->{$rectype};

            my $datasub;
            if ( $rectype =~ /^MX$/i ) {
              $datasub = sub { $_[0]->{priority}. ' '. $_[0]->{host}; };
            } elsif ( $rectype =~ /^TXT$/i ) {
              $datasub = sub { $_[0]->{text}; };
            } else {
              $datasub = sub { $_[0]->{host}; };
            }

            foreach my $rec ( @{ $dump->{$rectype} } ) {
              my $domain_record = new FS::domain_record( {
                'svcnum'  => $domain->svcnum,
                'reczone' => $rec->{name},
                'recaf'   => $rec->{class} || 'IN',
                'rectype' => $rectype,
                'recdata' => &{$datasub}($rec),
              } );
              my $error = $domain_record->insert;
              if ( $error ) {
                warn "$error inserting ".
                     $rec->{name}. ' . '. $domain->domain. "\n";
                warn Dumper($rec);
                #system('cat',"$main::prefix/$basefile.import");
                die;
              }
            }
          }
        }

      }

    #} else {
    #  die "unrecognized type $type\n";
    }
    
  }

}
#########