#!/usr/bin/perl -Tw
#
# LMS version 1.4.5 Persus
#
#  (C) 2001-2005 LMS Developers
#
#  Please, see the doc/AUTHORS for more information about authors!
#
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License Version 2 as
#  published by the Free Software Foundation.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software
#  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
#  USA.
#
#  $Id: lms-makedhcpconf,v 1.33.2.2 2005/01/01 19:19:06 alec Exp $

use strict;
use DBI;
use Config::IniFiles;
use Getopt::Long;
use vars qw($configfile $quiet $help $version);

sub mask2prefix($)
{
	my $mask = shift @_;
	my @tmp = split('\.',$mask,4);
	my $q = sprintf("%b%b%b%b",$tmp[0],$tmp[1],$tmp[2],$tmp[3]);
	$q =~ s/0*$//;
	if ($q =~ /0/) {
		print " You idiot. error in mask\n";
	}
	my $len = length($q) ;
	return $len;
}

sub matchip($$$)
{
	my ($ip,$net,$mask) = @_;
	my $prefix = mask2prefix($mask);
	my $bmask = 2**32 <<(32-$prefix);
	my @net = split('\.',$net,4);
	my $bnet = dotquad2u32($net);
	if(($bnet & $bmask)!= $bnet) {
		print "EEediot net &mask != net\n"; return 1==0
	}
	my $bip = dotquad2u32($ip);
	return (($bip&$bmask) == $bnet);
}

sub dotquad2u32($)
{
	my $dq = shift||'0.0.0.0';
	my @dq = split('\.',$dq,4);
	return ((($dq[0] << 8) + $dq[1] << 8) + $dq[2] << 8) + $dq[3];
}

sub u32todotquad($)
{
	my $p = shift @_;
	return sprintf "%d.%d.%d.%d", ($p>>24)&0xff,($p>>16)&0xff, ($p>>8)&0xff,$p&0xff;
}


my $_version = '1.4.5 Persus';

my %options = (
	"--config-file|C=s"	=>	\$configfile,
	"--quiet|q"		=>	\$quiet,
	"--help|h"		=>	\$help,
	"--version|v"		=>	\$version
);

Getopt::Long::config("no_ignore_case");
GetOptions(%options);

if($help)
{
	print STDERR <<EOF;
lms-makedhcpconf, version $_version
(C) 2001-2005 LMS Developers

-C, --config-file=/etc/lms/lms.ini	alternate config file (default: /etc/lms/lms.ini);
-h, --help			print this help and exit;
-v, --version			print version info and exit;
-q, --quiet			suppress any output, except errors;

EOF
	exit 0;
}

if($version)
{
	print STDERR <<EOF;
lms-makedhcpconf, version $_version
(C) 2001-2005 LMS Developers

EOF
	exit 0;
}

if(!$configfile)
{
	$configfile = "/etc/lms/lms.ini";
}

if(!$quiet)
{
	print STDOUT "lms-makedhcpconf, version $_version\n";
	print STDOUT "(C) Copyright 2001-2005 LMS Developers\n";
	print STDOUT "Using file $configfile as config.\n";
}

if(! -r $configfile)
{
	print STDERR "Fatal error: Unable to read configuration file $configfile, exiting.\n";
	exit 1;
}

my $ini = new Config::IniFiles -file => $configfile;
print @Config::IniFiles::errors;

my $networks_list = $ini->val('dhcp', 'networks') || '';
my $usergroups_list = $ini->val('dhcp', 'usergroups') || '';
my $deflease = $ini->val('dhcp', 'default_lease_time') || '86400';
my $maxlease = $ini->val('dhcp', 'max_lease_time') || '86400';
my $cfile = $ini->val('dhcp', 'config_file') || '/etc/dhcpd.conf';
my $cuid = $ini->val('dhcp','config_owneruid') || 0;
my $cgid = $ini->val('dhcp','config_ownergid') || 0;
my $cperm = $ini->val('dhcp','config_permission') || '0600';
my $olddhcpd = $ini->val('dhcp','ignore_ddns') || '';
my $logfacility = $ini->val('dhcp','log_facility') || '';

my $dbhost = $ini->val('database', 'host') || 'localhost';
my $dbuser = $ini->val('database', 'user') || 'root';
my $dbpasswd = $ini->val('database', 'password') || '';
my $dbname = $ini->val('database', 'database') || 'lms';
my $dbtype = $ini->val('database', 'type') || 'mysql';

my $dbase;

if($dbtype eq "mysql")
{
	$dbase = DBI->connect("DBI:mysql:database=$dbname;host=$dbhost","$dbuser","$dbpasswd", { RaiseError => 1 });
}
elsif($dbtype eq "postgres")
{
	$dbase = DBI->connect("DBI:Pg:dbname=$dbname;host=$dbhost","$dbuser","$dbpasswd", { RaiseError => 1 });
}
elsif($dbtype eq "sqlite")
{
	$dbase = DBI->connect("DBI:SQLite:dbname=$dbname;host=$dbhost","$dbuser","$dbpasswd", { RaiseError => 1 });
	$dbase->func('inet_ntoa',1,'u32todotquad','create_function');
	$dbase->func('inet_aton',1,'dotquad2u32','create_function');	
}
else
{
	print STDERR "Fatal error: unsupported database type: $dbtype, exiting.\n";
	exit 1;
}

open(DHCPDCONF, ">$cfile") or die("Fatal error: Unable to write $cfile, exiting.\n");

if(!$olddhcpd)
{
	print DHCPDCONF "ddns-update-style none;\n";
}

if($logfacility)
{
	print DHCPDCONF "log-facility ".$logfacility.";\n";
}

print DHCPDCONF "shared-network LMS {\n";

if(!$networks_list)
{
	my $dbq = $dbase->prepare("SELECT name FROM networks");
	$dbq->execute();
	while (my $row = $dbq->fetchrow_hashref()) {
		$networks_list = "$networks_list $row->{'name'}";
	}
}

my @networks = split ' ',$networks_list;

my $iniusergroups = $usergroups_list;

if(!$usergroups_list)
{
	my $dbq = $dbase->prepare("SELECT name FROM usergroups");
	$dbq->execute();
	while (my $row = $dbq->fetchrow_hashref()) {
		$usergroups_list = "$usergroups_list $row->{'name'}";
	}
	
}

my @usergroups = split ' ',$usergroups_list;

my $dbq = $dbase->prepare("SELECT id, name FROM usergroups");
$dbq->execute();

my %usergrouplist;

while (my $row = $dbq->fetchrow_hashref())
{
	$usergrouplist{$row->{'name'}} = $row->{'id'};
}

foreach my $key (@networks)
{
	my $dbq = $dbase->prepare("SELECT id, inet_ntoa(address) AS address, mask, gateway, dns, dns2, domain, wins, dhcpstart, dhcpend FROM networks WHERE name = UPPER('$key')");
	$dbq->execute();
	while (my $row = $dbq->fetchrow_hashref()) {
		$key = lc($key);
		my $ndeflease = $ini->val("dhcp:$key",'default_lease_time') || $deflease;
		my $nmaxlease = $ini->val("dhcp:$key",'max_lease_time') || $maxlease;
		print DHCPDCONF "\tsubnet $row->{'address'} netmask $row->{'mask'} { # Network: $key (ID:$row->{'id'})\n";
		print DHCPDCONF "\t\tdefault-lease-time $ndeflease;\n";
		print DHCPDCONF "\t\tmax-lease-time $nmaxlease;\n";
		if($row->{'dhcpstart'} && $row->{'dhcpend'})
		{
			print DHCPDCONF "\t\trange $row->{'dhcpstart'} $row->{'dhcpend'};\n";
		}
		if($row->{'mask'})
		{
			print DHCPDCONF "\t\toption subnet-mask $row->{'mask'};\n";
		}
		if($row->{'gateway'})
		{
			print DHCPDCONF "\t\toption routers $row->{'gateway'};\n";
		}
		if($row->{'dns'} && $row->{'dns2'})
		{
			print DHCPDCONF "\t\toption domain-name-servers $row->{'dns'}, $row->{'dns2'};\n";
		}elsif($row->{'dns'})
		{
			print DHCPDCONF "\t\toption domain-name-servers $row->{'dns'};\n";
		}elsif($row->{'dns2'})
		{
			print DHCPDCONF "\t\toption domain-name-servers $row->{'dns2'};\n";
		}
		if($row->{'domain'})
		{
			print DHCPDCONF "\t\toption domain-name \"$row->{'domain'}\";\n";
		}
		if($row->{'wins'})
		{
			print DHCPDCONF "\t\toption netbios-name-servers $row->{'wins'};\n";
		}
		print DHCPDCONF "\n";
		my $dbq2 = $dbase->prepare("SELECT id, name, ipaddr, mac, ownerid FROM nodes ORDER BY ipaddr");
		$dbq2->execute();
		while (my $row2 = $dbq2->fetchrow_hashref())
		{
			$row2->{'ipaddr'} = u32todotquad($row2->{'ipaddr'});
			if(matchip($row2->{'ipaddr'},$row->{'address'},$row->{'mask'}))
			{
				my $usergroupscount;
				foreach my $key2 (@usergroups)
				{
					my $dbq3 = $dbase->prepare("SELECT count(userid) as total FROM userassignments WHERE userid=$row2->{'ownerid'} AND usergroupid=".$usergrouplist{$key2});
					$dbq3->execute();
					my $srow = $dbq3->fetchrow_hashref();
					if ($srow->{'total'} eq 1)
					{
						$usergroupscount++;
					}
				}
				if ($usergroupscount || !$iniusergroups)
				{
					my $ipaddr = $row2->{'ipaddr'};
					my $ndomain = $ini->val("dhcp:$ipaddr",'domain') || '';
					my $ngateway = $ini->val("dhcp:$ipaddr",'gateway') || '';
					my $ndns = $ini->val("dhcp:$ipaddr",'dns') || '';
					my $nwins = $ini->val("dhcp:$ipaddr",'wins') || '';
					print DHCPDCONF "\t\thost $row2->{'name'} { # ID: $row2->{'id'}\n";
					print DHCPDCONF "\t\t\thardware ethernet $row2->{'mac'};\n";
					print DHCPDCONF "\t\t\tfixed-address $row2->{'ipaddr'};\n";
					if($ndomain)
					{
						print DHCPDCONF "\t\t\toption domain-name \"$ndomain\";\n";
					}
					if($ngateway)
					{
						print DHCPDCONF "\t\t\toption routers $ngateway;\n";
					}
					if($ndns)
					{
						print DHCPDCONF "\t\t\toption domain-name-servers $ndns;\n";
					}
					if($nwins)
					{
						print DHCPDCONF "\t\t\toption netbios-name-servers $nwins;\n";
					}
					print DHCPDCONF "\t\t}\n\n";
					}
				}
		}
		print DHCPDCONF "\t}\n\n";
	}
}
$dbase->disconnect();
print DHCPDCONF "}\n";
close(DHCPDCONF);
chown $cuid, $cgid, $cfile or print "Warning! Unable to set owner of $cfile to $cuid.$cgid.\n";
chmod oct($cperm), $cfile or print "Warning! Unable to set permission $cperm to $cfile.\n";
