#!/usr/bin/perl

use strict;
use warnings;

use Getopt::Std;
use Socket;
use Net::LDAP; # From libnet-ldap-perl debian package
use SiteSummary;
use Debian::Edu qw(prompt4password find_ldap_server find_ldap_base);

my $debug = 0;

my $server = $ARGV[0] || find_ldap_server() || "ldap";
my $base   = $ARGV[1] || find_ldap_base($server)
    || "dc=skole,dc=skolelinux,dc=no";

my $binddn = "cn=admin,ou=ldap-access,$base";

sub usage {
    my $retval = shift;
    print <<EOF;
sitesummary2ldapdhcp
Add machine objects to LDAP based on sitesummary information.

 -d             Enable debug output.
 -i ID          Handle host with the given sitesummary ID only.

EOF
    exit $retval;
}

my %opts;
my $retval = 0;

getopts("di:", \%opts) || usage(1);
$debug = 1 if $opts{d};

my $ldap = Net::LDAP->new( $server ) or die "$@";
$ldap->start_tls();
print "Connecting to LDAP as $binddn\n";
my $bindpw = prompt4password('enter password: ', -echo => '*');

my $mesg = $ldap->bind($binddn, password => $bindpw);
die "Unable to bind to LDAP server: " . $mesg->error if $mesg->code;

if ($opts{i}) {
    host_handler($opts{i});
} else {
    for_all_hosts(\&host_handler);
}
$mesg = $ldap->unbind;
exit $retval;


sub host_handler {
    my $hostid = shift;
    my $ipaddr = SiteSummary::get_primary_ip_address($hostid);
    my $fqdn = scalar gethostbyaddr(inet_aton($ipaddr), AF_INET);
    my $macref = [(get_ether_hwaddr($hostid))];
    if ($fqdn) {
        print "info: Updating $fqdn [$ipaddr] id $hostid.\n";
        add_or_update_machine($ldap, $fqdn, $ipaddr, $macref);
    } else {
        print "warning: Not registring entry $hostid, missing in DNS.\n";
    }
}

sub get_ether_hwaddr {
    my $hostid = shift;
    my $path = get_filepath_current($hostid, "/system/ifconfig-a");
    if (open(FILE, "<", $path)) {
        my $sysinfo = 0;
        my @hwaddr = ();
        while (<FILE>) {
            chomp;
            if (m/Link encap:Ethernet  HWaddr (.+\S)\s*$/) {
                push(@hwaddr, $1);
            }
        }
        close(FILE);
        return @hwaddr;
    } else {
        return undef;
    }
}

sub add_or_update_dhcp {
    my ($ldap, $fqdn, $ipaddr, $macref) = @_;

    my $mac = (@{$macref})[0], # FIXME How to handle several MAC addresses?
    my ($hostname, $dnsdomain) = split(/\./, $fqdn, 2);

    my $cn = "cn=$hostname,cn=group1,cn=INTERNAL,cn=config,ou=dhcp,$base";

    my $mesg = $ldap->search(
        base   => $cn,
        filter => '(objectClass=dhcpHost)'
        );

    if (0 == $mesg->count) {
        # Create
        my $attr = [
            'objectClass'    => 'dhcpHost',
            'cn'             => $hostname,
            'dhcpHWAddress'  => "ethernet $mac",
            'dhcpStatements' => "fixed-address $fqdn"
            ];

        my $result = $ldap->add($cn, attr => $attr);
        $result->code && die $result->error;
    } elsif (1 == $mesg->count) {
        # Update

        foreach my $entry ($mesg->all_entries) {
            $entry->dump if $debug;
            $entry->replace(
                'dhcpHWAddress'  => "ethernet $mac",
                'dhcpStatements' => "fixed-address $fqdn"
                );
            $entry->update($ldap);
        }
    } else {
        print STDERR "error: Not sure how to handle this\n";
    }
}

sub add_or_update_machine {
    my ($ldap, $fqdn, $ipaddr, $macref) = @_;
    print "Updating host entry for $fqdn\n" if $debug;
    add_or_update_dhcp($ldap, $fqdn, $ipaddr, $macref);
}
