#!/usr/bin/perl -w

use utf8;
use strict;
use warnings;

BEGIN {
    unshift @INC, ( $ENV{POPHOME} || '@POPHOME@' ) . '/lib'
      unless $ENV{KUNDE_NO_PERLPATH};
}

use Cf qw($NS_EXT $NSZ $UPDATE_ZONEN_COMMAND);
use Dbase::Globals qw(
  def_or_minus
  explain_child_error
  find_descr
  flag_names
  get_ipkunde
  get_gruppen
  name_kunde
);
use Dbase::Help qw(Do DoFn DoTime DoTrans qquote);
use Loader qw(log_update);
use noris::REST::Backend qw(get_param error success);

my $mask_domainflags_dnszone = get_gruppen( domainflags => dnszone => 1 );
my %noris_dns = map +( lc() => undef ), split ' ', $NS_EXT;

my $dns_master = get_param( hostname  => 'dns_master' );
my $domain     = get_param( hostname  => 'domain' );
my $reseller   = get_param( printable => 'reseller' );

my $flags_soll = 0;
my $dns_master_id;
my $kunde_id;
DoTrans {

    if ( exists $noris_dns{ lc $dns_master } ) {    # Wir sind selbst Master.
        $flags_soll |= $mask_domainflags_dnszone;
    }
    else {    # Datenbank-ID des DNS-Masters ermitteln:
        defined( my $dns_master_ip = gethostbyname($dns_master) )
          or
          error('Kann den angegebenen DNS-Master $dns_master nicht aufloesen.');

        require Socket and Socket->import('inet_ntoa');
        $dns_master_ip = inet_ntoa($dns_master_ip);
        $dns_master_id = get_ipkunde( $dns_master_ip, 2 )
          or error(
            "DNS-Server $dns_master [$dns_master_ip] ist nicht registriert.");

        require Net::DNS;

        my $resolver = Net::DNS::Resolver->new(
            nameservers => [$dns_master_ip],
            retrans     => 1,
          )
          or error( 'Es ist ein interner Fehler bei der '
              . 'Erzeugung eines Resolver-Objekts aufgetreten.' );

        my $answer = $resolver->send( $domain, 'NS' )
          or error( 'Es ist ein interner Fehler bei der '
              . 'DNS-Abfrage des Masters aufgetreten: '
              . $resolver->errorstring );

        error(  "Auf dem Server $dns_master [$dns_master_ip]"
              . ' ist keine entsprechende DNS-Zone aufgesetzt.' )
          unless $answer->header->aa;

        my @nserver = $answer->answer
          or error('Der Master liefert keine NS-Records fuer den Domainnamen.');

        for (@nserver) {    # Brauchen wir eine Slave-Zone?
            next unless exists $noris_dns{ lc $_->nsdname };
            $flags_soll |= $mask_domainflags_dnszone;
            last;
        }
    }

    $reseller =~ /([^.]*)\z/;    # Sub-Reseller, vgl. RT#406317-10
    ( $kunde_id, my $mail_ip ) =
      DoFn( 'SELECT id, mail_ip FROM kunde WHERE name = ' . qquote($1) );
    $kunde_id = DoFn( "SELECT kunde FROM uucpkunde WHERE name = " . qquote($1) )
      or error(qq(Es gibt keinen Reseller "$reseller".))
      unless defined $kunde_id;

    my $qdomain = qquote($domain);
    my $now     = DoTime();

    # Falls die Domain gerade zur Löschung vorgemerkt ist,
    # diese jetzt vorziehen:
    {
        my $domainstatus_geloescht =
          find_descr( domainstatus => geloescht => 1 );

        Do(<<_);    # s. RT#459102-4
	UPDATE domainkunde SET ende = $now
	WHERE  domain = $qdomain
	   AND ende   > $now
	   AND status = $domainstatus_geloescht
_
    }

    unless ( my ( $id, $domain_kunde, $nserver, $flags, $ende ) = DoFn(<<_) ) {
	SELECT id, kunde, nserver, flags, ende
	FROM   domainkunde
	WHERE  domain = $qdomain AND ( ende IS NULL OR ende > $now )
	FOR UPDATE
_

        my $domainstatus_beantragt =
          find_descr( domainstatus => beantragt => 1 );
        my $nic_partnergate = find_descr( nic => partnergate => 1 );

        my $domain_id = Do(<<_);
	INSERT INTO domainkunde SET
		beginn   = $now,
		domain   = $qdomain,
		kunde    = $kunde_id,
		mail_ip  = ${\ qquote($mail_ip) },
		nic      = $nic_partnergate,
		nserver  = ${\ qquote($dns_master_id) },
		flags    = $flags_soll,
		status   = $domainstatus_beantragt
_
        log_update(
            domainkunde =>
              id => $domain_id,
            undef,
            domain => '*',
            undef, $domain
        );
    }

    elsif ( $domain_kunde != $kunde_id ) {
        error(qq(Die Domain "$domain" gehört einem anderen Kunden.));
    }

    elsif ($ende) {

        # Bislang ist kein einschlägiger Fall bekannt.
	# Sollte er doch mal auftreten, behandeln wir das erstmal als Fehler,
	# da unklar ist, was ggf. sinnvoll getan werden sollte,
	# insbesondere hinsichtlich des zugehörigen Kundentarifs.
        error(
            qq(Die Domain "$domain" ist bekannt, wird aber demnächst beendet.)
        );
    }

    elsif (
        ( my $flags_ok = ( $flags & $flags_soll ) == $flags_soll )
        && ( my $has_correct_nserver =
              defined $dns_master_id
            ? defined $nserver && $nserver == $dns_master_id
            : !defined $nserver )
      )
    {
        success(
            $flags_soll & $mask_domainflags_dnszone
            ? qq(Für die Domain "$domain" ist bereits eine DNS-Zone mit "$dns_master" als Master-DNS-Server registriert.)
            : qq(Die Domain "$domain" ist bereits mit "$dns_master" als Master-DNS-Server registriert; eine Slave-Zone ist nicht erforderlich.)
        );
    }

    else {
        my @update;
        unless ($flags_ok) {
            my $new_flags = $flags | $flags_soll;
            log_update(
                domainkunde =>
                  id => $id,
                undef,
                flags => undef,
                scalar flag_names( $new_flags, domainflags => $flags )
            );
            push @update, "flags=$new_flags";
        }
        unless ($has_correct_nserver) {
            log_update(
                domainkunde =>
                  id => $id,
                undef,
                nserver => undef,
                def_or_minus($nserver)
            );
            push @update, 'nserver=' . qquote($dns_master_id);
        }

        Do(     'UPDATE domainkunde SET '
              . join( ', ', @update )
              . " WHERE id = $id" );
    }
};

success('Die Domain wurde erfolgreich in unserer Datenbank angelegt.')
  unless $flags_soll & $mask_domainflags_dnszone;

unless ( defined $dns_master_id ) {    # Bei Bedarf Master-DNS-Zone anlegen.

    open my $save_stdout, '>&', \*STDOUT or die "Error dup()ping STDOUT: $!\n";
    open STDOUT, '>&', \*STDERR or die "Error redirecting STDOUT: $!\n";

    # Noch verbesserungswürdig:
    # * Dateiname für Identity in Konfiguration auslagern.
    # * Möglichst ROLLBACK, wenn hier ein Fehler auftritt, weil's in der
    #   Datenbank sonst fälschlicherweise so aussieht, als säße die Zone
    #   schon, und damit ein Retry für den Reseller unmöglich ist.
    #   Aber: Dazu müssten wir die Konfigurationsneuenerierung auf dns0 vom
    #   Anlegen der Zone trennen, da diese selbst auf die Datenbank zugreift
    #   und daher erst nach Abschluss der Transaktion funktionieren kann.
    my $error = system qw(ssh -a), $NSZ,
      -i  => '/etc/id_dsa.partnergate',
      add => $domain, name_kunde($kunde_id);

    open STDOUT, '>&', $save_stdout or die "Error restoring STDOUT: $!\n";

    error(
        explain_child_error(
            'Externes Programm zur Generierung der Master-DNS-Zone')
    ) if $error;
}

system $UPDATE_ZONEN_COMMAND
  and error(
    explain_child_error('Externes Program zur Aktualisierung der Zonenliste') );

success('Die DNS-Zone wurde erfolgreich eingerichtet.');
