#!/usr/bin/perl  -w
#==============================================================================
# lat-hosts
# =========
# 0.9.0 (2004-09-08)
# (c)2003-2004 Altiplano bvba
#==============================================================================
package esmith;
use strict;
use esmith::config;
use esmith::db;
use esmith::util;
use Getopt::Long;
use Pod::Usage;
my %conf;
tie %conf, 'esmith::config';
my %accounts;
tie %accounts, 'esmith::config', '/home/e-smith/db/accounts';
my %hosts;
tie %hosts, 'esmith::config', '/home/e-smith/db/hosts';
my %domains;
tie %domains, 'esmith::config', '/home/e-smith/db/domains';
my ($Hlp, $Cml, $Frc, $Inp);
my $Add =0;
my $Del =0;

#==============================================================================
#  Main
#==============================================================================
sub performCreateLocalHostEntry ($$$$$$$);

# Analyze commandline options
GetOptions  ("help"           => \$Hlp,
             "add"            => \$Add,
             "delete"         => \$Del,
             "force"          => \$Frc,
             "command-line=s" => \$Cml,
             "input-file=s"   => \$Inp);

if ( $Hlp ) { &PrintPod(9); exit; }

# We need one argument or the other, but not both
if ((($Cml && $Inp) || (! $Cml && ! $Inp)) ||
    ($Add + $Del != 1))
	{ &PrintPod(1); exit; }

my @records;
if ($Inp) {
    open(LIST,"< $Inp")  ||  die "Can't find $Inp.\n";
    @records = grep(!/(^\s*#)|(^\s*$)/,<LIST>);
    close(LIST); }
elsif ($Cml) { @records=($Cml); }
else { &PrintPod(1); exit; }

if ($Add) {
    # Process each user
    foreach my $record (@records)
    {
        my @fields=split(/\|/,$record);
        for (my $cnt=0; $cnt <= $#fields; ++$cnt) { for ($fields[$cnt]) { s/^\s+//; s/\s+$//; }}

        performCreateLocalHostEntry ($fields[0],    # HostName
                                     $fields[1],    # DomainName
                                     $fields[2],    # Location
                                     $fields[3],    # Visibility
                                     $fields[4],    # Local IP
                                     $fields[5],    # Global IP
                                     $fields[6]);   # Mac Address
    }
}

if ($Del) {
    &ExpandWildCard;  # Check for wildcards and expand if necessary

    # Process each user
    foreach my $record (@records)
    {
        my @fields=split(/\|/,$record);
        for (my $cnt=0; $cnt <= $#fields; ++$cnt) { for ($fields[$cnt]) { s/^\s+//; s/\s+$//; }}

        if (! $fields[1] ) { $fields[1] = db_get(\%conf, 'DomainName') }
        my $FullHostName = lc($fields[0]).".".lc($fields[1]);

        if ((db_get(\%hosts, $FullHostName)) &&
            (db_get_type(\%hosts, $FullHostName)) eq "host") {
            my $yn = 'yes';
            if (! $Frc) {
                print "Do you want to delete host '$FullHostName' [yes/NO/all]? ";
                $yn = <STDIN>;
                if ($yn =~ /^a/i) { $Frc = -1; $yn="yes"; }
            }
            if ($yn =~ /^y/i) {
            print "Deleting '$FullHostName'...\n";
                db_delete(\%hosts, $FullHostName);
                system ("/sbin/e-smith/signal-event", "host-delete", "$FullHostName") == 0
                    or die ("Error occurred while deleting '$FullHostName'.\n");
            }
        }
        else { print "Can't find host '$FullHostName'.\n\a";}
    }
}

#==============================================================================
# Subroutines
#==============================================================================
# Create local host
sub performCreateLocalHostEntry ($$$$$$$)
{
    my $REGEXPMACAddress = '([0-9a-f][0-9a-f](:[0-9a-f][0-9a-f]){5})';
    my $REGEXPIPAddress = '(self|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})';
    my $REGEXPHostname = '([a-z0-9][a-z0-9-]*)';

    my ($q) = @_;
    my ($HostName, $DomainName, $Location, $Visibility, $InternalIP, $ExternalIP, $MACAddress) = @_;
    my %hostProperties;

    #------------------------------------------------------------
    # Validate parameters and untaint them
    #------------------------------------------------------------
    # Hostname
    $HostName = lc($HostName);
    if ($HostName =~ /^$REGEXPHostname$/) {
        $HostName = $1;
    }
    else {
        print "Bad hostname ($HostName).\a\n";
        return;
    }
    if (length $HostName > 32) {
        print "Hostname too long.\a\n";
        return;
    }

    # Domainname
    if (! $DomainName) { $DomainName = db_get(\%conf, 'DomainName') }
    $DomainName = lc($DomainName);
    if ((! db_get(\%domains, $DomainName)) &&
        ( db_get(\%conf, 'DomainName') ne $DomainName)) {
        print "\nThe domainname '$DomainName' is not hosted on this server.\a\n";
        return;
    }
    if ($DomainName =~ /^([a-z0-9\-\.]+)$/) {
        $DomainName = $1;
    }
    else {
        print "Unexpected characters in '$DomainName'.\a\n";
        return;
    }

    print "\nCreating $HostName.$DomainName...\n";

    # Location
    if (! $Location) { $Location = "Self" }
    if ( $Location =~ /(local)|(remote)|(self)/i ) {
        $Location = ucfirst lc $Location;
    }
    else { $Location = "Self" }

    # Visibility
    if ( ! $Visibility ) {
        $Visibility = "Local";
        $ExternalIP = "";
    }
    if ( $Visibility =~ /(local)|(global)/i ) {
        $Visibility = ucfirst lc $Visibility;
    }
    else { $Visibility = "Local" }
    if ($Visibility eq "Local") { $ExternalIP = "" }

    # Internal IP address
    if (! $InternalIP) { $InternalIP = ''; }
    if ($Location eq 'Self') { $InternalIP = '' }
    elsif ($InternalIP =~ /^$REGEXPIPAddress$/ ) { $InternalIP = $1; }
    else {
        print "Error: IP Address '$InternalIP' is invalid. Did not create hostname.\a\n";
        return;
    }

    # MAC address
    if (( ! $MACAddress ) || ($Location ne "Local")) { $MACAddress = "" }
    if ( length($MACAddress) == 0 )
    {
        # They don't want one
    }
    elsif ($MACAddress =~ /^$REGEXPMACAddress$/i )
    {
        $MACAddress = $1;
    }
    else
    {
        print "Invalid Mac Address \n";
    return;
    }
    # External IP
    if ( ! $ExternalIP ) { $ExternalIP = "" }
    if ($Visibility eq 'Global')    # Wants externally visible - needs external IP
    {
    if ( length ($ExternalIP) == 0 )
    {
        print "Error: You did not enter a Global IP value for '$HostName'.\a\n";
        return;
    }
    elsif ($ExternalIP =~ /^$REGEXPIPAddress$/ )
    {
        $ExternalIP = $1;
    }
    else
    {
        print "Error: IP Address '$ExternalIP' is invalid. Did not create hostname.\a\n";
        return;
    }
    }
    else   # Wants internally visible - optional external IP
    {
    if ( length ($ExternalIP) == 0 )
    {
        # They didn't specify one
    }
    elsif ($ExternalIP =~ /^$REGEXPIPAddress$/ )
    {
        $ExternalIP = $1;
    }
    else
    {
        print "Error: IP Address '$ExternalIP' is invalid. Did not create hostname.\a\n";
        return;
    }
    }

    #------------------------------------------------------------
    # Looks good. Find out if this entry has been taken
    #------------------------------------------------------------

    my $fullHostName = join (".", $HostName, $DomainName);

    if (defined (db_get(\%hosts, $fullHostName)) )
    {
    print "HostName '$fullHostName' already exists. \n";
    return;
    }

    #------------------------------------------------------------
    # Host entry is available! Update hosts database.
    #------------------------------------------------------------
    print "HostName    : $HostName\n";
    print "DomainName  : $DomainName\n";
    print "Location    : $Location\n";
    print "Visibility  : $Visibility\n";
    print "Local IP    : $InternalIP\n";
    print "Global IP   : $ExternalIP\n";
    print "Mac Address : $MACAddress\n";

    $hostProperties{'Visibility'} = $Visibility;
    $hostProperties{'HostType'}   = $Location;
    $hostProperties{'InternalIP'} = $InternalIP;
    $hostProperties{'ExternalIP'} = $ExternalIP;
    $hostProperties{'MACAddress'} = $MACAddress;
    db_set(\%hosts, $fullHostName, 'host', \%hostProperties);

    #------------------------------------------------------------
    # Signal the create-host event.
    #------------------------------------------------------------

    system ("/sbin/e-smith/signal-event", "host-create", "$HostName") == 0
        or die ("Error occurred while creating hostname.\n");
}
#==============================================================================
# Test for wildcards in the hostname. If any wildecards are found, the array
# @records is expanded with the hostnames that meet the conditions.
sub ExpandWildCard {
    my $ctrec = 0;
    foreach my $record (@records)
    {
        my @fld=split(/\|/,$record);
        for (my $cnt=0; $cnt <= $#fld; ++$cnt) { for ($fld[$cnt]) { s/^\s+//; s/\s+$//; }}

        if ($fld[0] =~ /\*|\?/) {   # Does it contain the wildcards?
            $fld[0] =~ s/\*/\.\*/g; # Replace * with .* to allow for grep.
            $fld[0] =~ s/\?/\./g;   # Replace ? with . to allow for grep.

            if (! $fld[1]) { $fld[1] = db_get(\%conf, 'DomainName'); }

            open USRS, "</home/e-smith/db/hosts" or die "Can't open /home/e-smith/db/hosts: $!";
            my @match = grep /^$fld[0]\.$fld[1]=host\|/i, <USRS>;
            close(USRS);

            my $cu = 0;
            foreach my $tst (@match) {
                $tst =~ /\.$fld[1]\=/; $tst = $`;
                for (my $cnt=1; $cnt <= $#fld; ++$cnt) { $tst = $tst." | ".$fld[$cnt]; };
                if ($cu == 0 ) {
                    $records[$ctrec] = $tst;
                    $cu =1;
                }
                else {
                    push(@records, $tst);
                }
            }
        }
        ++$ctrec;
    }
}
#==============================================================================
# Print the pod text as a help screen
sub PrintPod {
    my ($verbose, $message) = @_;
    pod2usage(-verbose => $verbose, -message => $message, -exitval => 64);
}

#==============================================================================

=pod

=head1 NAME

B<lat-hosts> - The lazy administrator's tool to manage hostnames

=head1 DESCRIPTION

Creates or deletes hosnames on Mitel's SME servers.
This tool is functionally equivalent to the 'Hostnames and addresses' option in the server-manager, but can be run from the command line or called from an other script.
It allows you, for example, to create a large number of hostnames in a batch process, or delete hostnames on a remote machine via an ssh console.

See F</usr/doc/lazy-admin-tools/example.hosts> for the format of the input file.

=head1 SYNOPSIS

B<lat-hosts> -a -c "host| domain| loc. | visib. | loc.ip | glob.ip | mac"

B<lat-hosts> -a -i /path/to/hosts.list

B<lat-hosts> -d [-f] -c "Host | Domain"

B<lat-hosts> -d [-f] -i /path/to/hosts.list

=head1 OPTIONS

The following options are supported:

=over 4

=item B<-a>, B<--add>

Add a host name to the server

=item B<-c "Arguments">, B<--command-line="Arguments">

Take arguments from the command line. See below for the various arguments that are accepted.

=item B<-d>, B<--delete>

Delete a hostname from the server.  Wildcards (* and ?) are accepted.

=item B<-f>, B<--force>

Don't prompt before deleting.

=item B<-h>, B<--help>

Extended help for this tool

=item B<-i FILE>, B<--input-file=FILE>

Use the information from F<FILE> to create or delete the hostname(s).

=back

=head2 Arguments:

   host*      : Must contain only letters, numbers, and hyphens,
                and must start with a letter or number. Wildcards
                (* and ?) can only be used to delete hostnames.
   domain     : Must be an existing (virtual) domain name. If
                omitted, the primary site is assumed.
   loc.       : Location of the server:
                'Local' (on the LAN)
                'Remote' (on the Internet)
                'Self' (alias for the SME server)
   visib.     : 'Local' (only visible on the LAN)
                'Global' (visible on the entire Internet).
   loc.ip     : Internal IP number
   glob.ip    : Public IP number
   mac        : The ethernet address is optional and causes the
                DHCP server to statically bind the local IP address
                to the computer with this ethernet address.

   * mandatory field

=head1 EXAMPLES

B<lat-hosts -a -c "ntp | hogwarts.net | self | local">

Creates host name 'ntp.hogwarts.net' on the SME server. The host name will be visible only on the LAN.

B<lat-hosts -a -i /root/hosts.list>

Creates the host names defined in F</root/hosts.list>. Refer to F</usr/doc/lazy-admin-tools/example.hosts> for an example of an input file.

B<lat-hosts -d -f -c "ftp*">

Deletes all host names that start with 'ftp'. The host names will be deleted without prompting (-f).

=head1 SEE ALSO

lat-group(8), lat-pseudonyms(8), lat-ibays(8), lat-quota(8), lat-domains(8), lat-users(8), lat-procmail(8), lat-pptp(8), lat-dump(8)

=head1 VERSION

Version 0.9.0 (2004-09-08). The latest version is hosted at B<http://www.contribs.org/contribs/mblotwijk/>

=head1 COPYRIGHT

(c)2003-2004, Altiplano bvba (B<http://www.altiplano.be>). Released under the terms of the GNU license.


=head1 BUGS

Please report bugs to <Bugs@Altiplano.Be>

=cut

#==============================================================================
