#!/usr/local/bin/perl
# index.cgi
# List all subnets and shared networks

require './dhcpd-lib.pl';
&ReadParse();
$horder = $in{'horder'};
$norder = $in{'norder'};
if ($horder eq "" && open(INDEX, "$module_config_directory/hindex.".$remote_user)) {
	chop($horder = <INDEX>);
	close(INDEX);
	}
if (!$horder) {
	$horder = 0;
	}
if ($norder eq "" && open(INDEX, "$module_config_directory/nindex.".$remote_user)) {
	chop($norder = <INDEX>);
	close(INDEX);
	}
if (!$norder) {
	$norder = 0;
	}
$nocols = $config{'dhcpd_nocols'} ? $config{'dhcpd_nocols'} : 5;
$conf = &get_config();
%access = &get_module_acl();

# Check if dhcpd is installed
if (!(-x $config{'dhcpd_path'})) {
	&header($text{'index_title'}, "", undef, 1, 1, 0,
		&help_search_link("dhcpd", "man", "doc", "howto", "google"));
	print "<hr>\n";
	print "<p>";
	print &text('index_dhcpdnotfound', $config{'dhcpd_path'},
		    "$gconfig{'webprefix'}/config.cgi?$module_name"),"<p>\n";
	print "<hr>\n";
	&footer("/", $text{'index_return'});
	exit;
	}

# Check if it is the right version
$out = `$config{'dhcpd_path'} -v 2>&1`;
if ($out !~ /DHCP/ || $out =~ /V1/) {
	&header($text{'index_title'}, "", undef, 1, 1, 0,
		&help_search_link("dhcpd", "man", "doc", "howto", "google"));
	print "<hr>\n";
        print "<p>";
        print (&text('index_dhcpdver',$config{'dhcpd_path'})),"<p>\n";
	print "<pre>$out</pre>\n";
        print "<hr>\n";
        &footer("/", $text{'index_return'});
	exit;
	}
$ver = $out =~ /\sV([0-9\.]+)/ ? $1 :
       $out =~ /-T/ ? 3 : 2;
if ($ver ne $config{'dhcpd_version'}) {
	$config{'dhcpd_version'} = $ver;
	&write_file("$module_config_directory/config", \%config);
	}

&header($text{'index_title'}, "", undef, 1, 1, 0,
	&help_search_link("dhcpd", "man", "doc", "howto", "google"),
	undef, undef, &text('index_version', $ver));
print "<hr>\n";

# get top-level hosts
foreach $h (&find("host", $conf)) {
	push(@host, $h);
	}
foreach $g (&find("group", $conf)) {
	push(@group, $g);
	foreach $h (&find("host", $g->{'members'})) {
		push(@host, $h);
		$group{$h} = $g->{'index'};
		$par{$h} = $g;
		push(@{$g->{'hosts'}}, $h->{'values'}->[0]);
		}
	}

# get subnets and shared nets, and the hosts and groups within them
@subn = &find("subnet", $conf);
foreach $u (@subn) {
	$maxsubn = $maxsubn > $u->{'index'} ? $maxsubn : $u->{'index'};
	foreach $h (&find("host", $u->{'members'})) {
		$maxhost = $maxhost > $h->{'index'} ? $maxhost : $h->{'index'};
		$subnet{$h} = $u->{'index'};
		$par{$h} = $u;
		push(@host, $h);
		}
	foreach $g (&find("group", $u->{'members'})) {
		$maxgroup = $maxgroup > $g->{'index'} ? $maxgroup : $g->{'index'};
		$subnet{$g} = $u->{'index'};
		$par{$g} = $u;
		push(@group, $g);
		foreach $h (&find("host", $g->{'members'})) {
			$maxhost = $maxhost > $h->{'index'} ? $maxhost : $h->{'index'};
			$subnet{$h} = $u->{'index'};
			$group{$h} = $g->{'index'};
			$par{$h} = $g;
			push(@{$g->{'hosts'}}, $h->{'values'}->[0]);
			push(@host, $h);
			}
		}
	}
@shan = &find("shared-network", $conf);
foreach $s (@shan) {
	$maxshar = $maxshar > $s->{'index'} ? $maxshar : $s->{'index'};
	foreach $h (&find("host", $s->{'members'})) {
		$maxhost = $maxhost > $h->{'index'} ? $maxhost : $h->{'index'};
		$shared{$h} = $s->{'index'};
		$par{$h} = $s;
		push(@host, $h);
		}
	foreach $g (&find("group", $s->{'members'})) {
		$maxgroup = $maxgroup > $g->{'index'} ? $maxgroup : $g->{'index'};
		$shared{$g} = $s->{'index'};
		$par{$g} = $s;
		push(@group, $g);
		foreach $h (&find("host", $g->{'members'})) {
			$maxhost = $maxhost > $h->{'index'} ? $maxhost : $h->{'index'};
			$group{$h} = $g->{'index'};
			$shared{$h} = $s->{'index'};
			$par{$h} = $g;
			push(@{$g->{'hosts'}}, $h->{'values'}->[0]);
			push(@host, $h);
			}
		}
	foreach $u (&find("subnet", $s->{'members'})) {
		$maxsubn = $maxsubn > $u->{'index'} ? $maxsubn : $u->{'index'};
		$par{$u} = $s;
		push(@subn, $u);
		$shared{$u} = $s->{'index'};
		foreach $h (&find("host", $u->{'members'})) {
			$maxhost = $maxhost > $h->{'index'} ? $maxhost : $h->{'index'};
			$subnet{$h} = $u->{'index'};
			$shared{$h} = $s->{'index'};
			$par{$h} = $u;
			push(@host, $h);
			}
		foreach $g (&find("group", $u->{'members'})) {
			$maxgroup = $maxgroup > $g->{'index'} ? $maxgroup : $g->{'index'};
			$subnet{$g} = $u->{'index'};
			$shared{$g} = $s->{'index'};
			$par{$g} = $u;
			push(@group, $g);
			foreach $h (&find("host", $g->{'members'})) {
				$maxhost = $maxhost > $h->{'index'} ? $maxhost : $h->{'index'};
				$subnet{$h} = $u->{'index'};
				$group{$h} = $g->{'index'};
				$shared{$h} = $s->{'index'};
				$par{$h} = $g;
				push(@{$g->{'hosts'}}, $h->{'values'}->[0]);
				push(@host, $h);
				}
			}
		}
	}
foreach $s (@shan) {
	$s->{'order'} = (1 + $s->{'index'}) * (2 + $maxsubn);
	}
foreach $s (@subn) {
	$s->{'order'} = (defined($shared{$s}) ? (1 + $shared{$s}) * (2 + $maxsubn) : 0)
			+ 1 + $s->{'index'};
	}
if ($norder == 0) {
	@subn = (@subn, @shan);
	}
elsif ($norder == 1) {
	@subn = (@subn, @shan);
	@subn = sort { $a->{'order'} <=> $b->{'order'} } @subn;
	}
elsif ($norder == 2) {
	@subn = sort { $a->{'values'}->[0] <=> $b->{'values'}->[0] } @subn;
	@shan = sort { $a->{'values'}->[0] cmp $b->{'values'}->[0] } @shan;
	@subn = (@subn, @shan);
	}

# display subnets and shared nets
foreach $u (@subn) {
	local $can_view = &can('r', \%access, $u);
	next if !$can_view && $access{'hide'};
	if ($u->{'name'} eq "subnet") {
		push(@links, $can_view ? 
			"edit_subnet.cgi?idx=$u->{'index'}".
			($shared{$u} ne "" ? "&sidx=$shared{$u}" : "") :
			undef);
		push(@titles, &html_escape($u->{'values'}->[0]));
		push(@icons, "images/subnet.gif");
		}
	else {
		push(@links, $can_view ?
			"edit_shared.cgi?idx=$u->{'index'}" : undef);
		push(@titles, &html_escape($u->{'values'}->[0]));
		push(@icons, "images/shared.gif");
		}
	}
if ($access{'r_sub'} || $access{'c_sub'} || $access{'r_sha'} || $access{'c_sha'}) {
	print "<h3>$text{'index_subtitle'}</h3>\n";
	$sp = "";
	if (@links) {
		&index_links($norder, "n", 3, $text{'index_ndisplay'},
			"horder=$horder");
		print "<br><a href='edit_subnet.cgi?new=1'>",
		      "$text{'index_addsub'}</a>&nbsp;&nbsp;\n"
			if $access{'c_sub'};
		print "<a href='edit_shared.cgi?new=1'>",
		      "$text{'index_addnet'}</a><br>\n"
			if $access{'c_sha'};
		if ($config{'hostnet_list'} == 0) {
			&icons_table(\@links, \@titles, \@icons, $nocols);
			}
		else {
			&net_table(\@subn, 0, scalar(@subn), \@links, \@titles);
			}
		}
	else {
		print "$text{'index_nosubdef'} <p>\n";
		}
	}
print "<a href='edit_subnet.cgi?new=1'>$text{'index_addsub'}</a>&nbsp;&nbsp;\n"
	if $access{'c_sub'};
print "<a href='edit_shared.cgi?new=1'>$text{'index_addnet'}</a><p>\n"
	if $access{'c_sha'};
print "<hr>\n";

foreach $g (@group) {
	$parent = (defined($subnet{$g}) ? 1 + $subnet{$g} : 0) +
		  (defined($shared{$g}) ? (1 + $shared{$g}) * (2 + $maxsubn) : 0);
	$g->{'order'} = $parent + (1 + $g->{'index'}) / (2 + $maxgroup);
	}
foreach $h (@host) {
	$parent = (defined($group{$h}) ? (1 + $group{$h}) / (2 + $maxgroup) : 0) +
		  (defined($subnet{$h}) ? 1 + $subnet{$h} : 0) +
		  (defined($shared{$h}) ? (1 + $shared{$h}) * (2 + $maxsubn) : 0);
	$h->{'order'} = $parent + (1 + $h->{'index'}) /
			((1 + @group) * (2 + $maxhost));
	}
if ($horder == 0) {
	@host = (@host, @group);
	}
elsif ($horder == 1) {
	@host = (@host, @group);
	@host = sort { $a->{'order'} <=> $b->{'order'} } @host;
	}
elsif ($horder == 2) {
	@host = sort { $a->{'values'}->[0] cmp $b->{'values'}->[0] } @host;
	@host = (@host, @group);
	}
elsif ($horder == 3) {
	@host = sort { &hardware($a) cmp &hardware($b) } @host;
	@host = (@host, @group);
	}
elsif ($horder == 4) {
	@host = sort { &ipaddress($a) cmp &ipaddress($b) } @host;
	@host = (@host, @group);
	}

# display hosts
@links = @titles = @icons = ();
foreach $h (@host) {
	local $can_view = &can('r', \%access, $h);
	next if !$can_view && $access{'hide'};
	if ($h->{'name'} eq 'host') {
		push(@links, $can_view ?
			"edit_host.cgi?idx=$h->{'index'}".
			(defined($group{$h}) ? "&gidx=$group{$h}" : "").
			(defined($subnet{$h}) ? "&uidx=$subnet{$h}" : "").
			(defined($shared{$h}) ? "&sidx=$shared{$h}" : "") :
			undef);
		push(@titles, &html_escape($h->{'values'}->[0]));
		if ($config{'show_ip'}) {
			local $fixed = &find("fixed-address", $h->{'members'});
			$titles[$#titles] .= "<br>$fixed->{'value'}"
				if ($fixed);
			}
		push(@icons, "images/host.gif");
		}
	else {
		push(@links, $can_view ?
			"edit_group.cgi?idx=$h->{'index'}".
			(defined($subnet{$h}) ? "&uidx=$subnet{$h}" : "").
			(defined($shared{$h}) ? "&sidx=$shared{$h}" : "") :
			undef);
		$gm = @{$h->{'hosts'}};
		push(@titles, &html_escape(&group_name($gm, $h)));
		push(@icons, "images/group.gif");
		}
	}
if ($access{'r_hst'} || $access{'c_hst'} || $access{'r_grp'} || $access{'c_grp'}) {
	print "<h3>$text{'index_hst'}</h3>\n";
	$sp = "";
	if (@links) {
		&index_links($horder, "h", 5, $text{'index_hdisplay'},
			"norder=$norder");
		print "<br><a href='edit_host.cgi?new=1'>",
		      "$text{'index_addhst'}</a>&nbsp;&nbsp;\n"
			if $access{'c_hst'};
		print "<a href='edit_group.cgi?new=1'>",
		      "$text{'index_addhstg'}</a><br>\n"
			if $access{'c_grp'};
		if ($config{'hostnet_list'} == 0) {
			&icons_table(\@links, \@titles, \@icons, $nocols);
			}
		else {
			&host_table(\@host, 0, scalar(@host), \@links, \@titles);
			}
		}
	else {
		print "$text{'index_nohst'} <p>\n";
		}
	}
print "<a href='edit_host.cgi?new=1'>$text{'index_addhst'}</a>&nbsp;&nbsp;\n"
	if $access{'c_hst'};
print "<a href='edit_group.cgi?new=1'>$text{'index_addhstg'}</a><p>\n"
	if $access{'c_grp'};
print "<hr>\n";

print "<table>\n";
if ($access{'global'}) {
	print "<form action=edit_options.cgi>\n";
	print "<input type=hidden name=global value=1>\n";
	print "<tr> <td><input type=submit value=\"$text{'index_buttego'}\"></td>\n";
	print "<td>$text{'index_ego'} \n";
	print "</td> </tr>\n";
	print "</form>\n";
	}
if (!$access{'noconfig'}) {
	print "<form action=edit_iface.cgi>\n";
	print "<tr> <td><input type=submit value=\"$text{'index_buttiface'}\"></td>\n";
	print "<td>$text{'index_iface'} \n";
	print "</td> </tr>\n";
	print "</form>\n";
	}
if ($access{'r_leases'}) {
	print "<form action=list_leases.cgi>\n";
	print "<tr> <td><input type=submit value=\"$text{'index_buttlal'}\"></td>\n";
	print "<td>$text{'index_lal'} \n";
	print "</td> </tr>\n";
	print "</form>\n";
	}
if ($access{'apply'}) {
	if (open(PID, $config{'pid_file'})) {
		chop($pid = <PID>);
		close(PID);
		}
	if ($pid && kill(0, $pid)) {
		print "<form action=restart.cgi>\n";
		print "<input type=hidden name=pid value=$pid>\n";
		print "<tr> <td><input type=submit value=\"$text{'index_buttapply'}\"></td>\n";
		print "<td>$text{'index_apply'} \n";
		print "</td></tr>\n";
		print "</form>\n";
		}
	else {
		print "<form action=start.cgi>\n";
		print "<tr> <td><input type=submit value=\"$text{'index_buttstart'}\"></td>\n";
		print "<td>$text{'index_start'} \n";
		print "</td> </tr>\n";
		print "</form>\n";
		}
	}
print "</table>\n";

print "<hr>\n";
&footer("/", $text{'index_return'});

# Returns canonized hardware address.
sub hardware {
	local ($hconf, $addr);
	$hconf = $_[0]->{'members'} ? &find("hardware", $_[0]->{'members'}) : undef;
	if ($hconf) {
		$addr = uc($hconf->{'values'}->[1]);
		$addr =~ s/(^|\:)([0-9A-F])(?=$|\:)/$1\x30$2/g;
	}
	return $hconf ? $addr : undef;
}

# Returns ip address for sorting on
sub ipaddress
{
return undef if (!$_[0]->{'members'});
local $iconf = &find("fixed-address", $_[0]->{'members'});
return undef if (!$iconf);
return sprintf "%3.3d.%3.3d.%3.3d.%3.3d",
		split(/\./, $iconf->{'values'}->[0]);
}

sub fixedaddr {
	local ($fixed, $addr);
	$fixed = &find("fixed-address", $_[0]->{'members'});
	$addr = join(" ", grep { $_ ne "," } @{$fixed->{'values'}});
	$addr =~ s/, / /g;
	return $addr;
}

sub netmask {
	return $_[0]->{'values'}->[2];
}

# index_links(current, name, max, txt, ref)
sub index_links
{
local (%linkname, $l);
print "<table><tr><td valign=top><b>$_[3] </b>&nbsp;&nbsp;</td>\n";
print "<td>";
for ($l = 0; $l < $_[2]; $l++) {
	if ($l ne $_[0]) {
		print "<a href=?$_[1]order=$l\&$_[4]>";
		}
	else {
		print "<b>";
		}
	print $text{"index_$_[1]order$l"};
	if ($l ne $_[0]) {
		print "</a>";
		}
	else {
		print "</b>";
		}
	print "&nbsp;\n";
	}
print "</td></table>\n";
open(INDEX, "> $module_config_directory/$_[1]index.".$remote_user);
print INDEX "$_[0]\n";
close(INDEX);
}

sub host_table
{
local ($i, $h, $parent);
print "<table border width=95%>\n";
print "<tr $tb> <td><b>", $text{'index_hostgroup'}, "</b></td> ",
      "<td><b>", $text{'index_parent'}, "</b></td> ",
      "<td><b>", $text{'index_hardware'}, "</b></td> ",
      "<td><b>", $text{'index_nameip'}, "</b></td> </tr>\n";
for ($i = $_[1]; $i < $_[2]; $i++) {
	print "<tr $cb> <td>\n";
	$h = $_[0]->[$i];
	if ($h->{'name'} eq 'host') {
		print $sp;
		}
	else {
		print $text{'index_group'}, " ";
		$sp = "\&nbsp;\&nbsp;";
		}
	if ($_[3]->[$i]) {
		print "<a href=$_[3]->[$i]>", $_[4]->[$i], "</a> </td>\n";
		}
	else {
		print $_[4]->[$i], "</td>\n";
		}
	if ($par{$h}->{'name'} eq "group") {
		$parent = "$text{'index_togroup'} ".&group_name(scalar @{$par{$h}->{'hosts'}});
		}
	elsif ($par{$h}->{'name'} eq "subnet") {
		$parent = "$text{'index_tosubnet'} $par{$h}->{'values'}->[0]";
		}
	elsif ($par{$h}->{'name'} eq "shared-network") {
		$parent = "$text{'index_toshared'} $par{$h}->{'values'}->[0]";
		}
	print "<td>", $parent, "\&nbsp;</td>\n";
	print "<td>", $_[3]->[$i] ? &hardware($h) : "", "\&nbsp;</td>\n";
	print "<td>", $_[3]->[$i] ? &fixedaddr($h) : "", "\&nbsp;</td>\n";
	print "</tr>\n";
	}
print "</table>\n"
}

sub net_table
{
local ($i, $n);
print "<table border width=95%>\n";
print "<tr $tb> <td><b>", $text{'index_net'}, "</b></td> ",
      "<td><b>", $text{'index_netmask'}, "</b></td> ",
      "<td><b>", $text{'index_parent'}, "</b></td> </tr>\n";
for ($i = $_[1]; $i < $_[2]; $i++) {
	print "<tr $cb> <td>\n";
	$n = $_[0]->[$i];
	if ($n->{'name'} eq 'subnet') {
		print $sp;
		}
	else {
		$sp = "\&nbsp;\&nbsp;";
		}
	if ($_[3]->[$i]) {
		print "<a href=$_[3]->[$i]>", $_[4]->[$i], "</a> </td>\n";
		}
	else {
		print $_[4]->[$i], "</td>\n";
		}
	print "<td>", $_[3]->[$i] ? &netmask($n) : "", "\&nbsp;</td>\n";
	print "<td>", $par{$n} ? 
		"$text{'index_toshared'} $par{$n}->{'values'}->[0]" : "",
		"\&nbsp;</td>\n";
	print "</tr>\n";
	}
print "</table>\n"
}
