1#!/usr/local/bin/perl
2# $Id: index.cgi,v 1.6 2005/04/16 14:30:21 jfranken Exp $
3# * List all subnets and shared networks
4#
5# File modified 2005-04-15 by Johannes Franken <jfranken@jfranken.de>:
6# * Added support for DNS Zones (list, edit, create zone-directives)
7# * Added a button to edit key-directives
8# * Added a button to edit dhcp.conf in a text editor
9
10require './dhcpd-lib.pl';
11$display_max = $config{'display_max'} || 1000000000;
12&ReadParse();
13$horder = $in{'horder'};
14$norder = $in{'norder'};
15if ($horder eq "" && open(INDEX, "<$module_config_directory/hindex.".$remote_user)) {
16	chop($horder = <INDEX>);
17	close(INDEX);
18	}
19if (!$horder) {
20	$horder = 0;
21	}
22if ($norder eq "" && open(INDEX, "<$module_config_directory/nindex.".$remote_user)) {
23	chop($norder = <INDEX>);
24	close(INDEX);
25	}
26if (!$norder) {
27	$norder = 0;
28	}
29$nocols = $config{'dhcpd_nocols'} ? $config{'dhcpd_nocols'} : 5;
30$conf = &get_config();
31%access = &get_module_acl();
32
33# Check if dhcpd is installed
34if (!-x $config{'dhcpd_path'}) {
35	&ui_print_header(undef, $text{'index_title'}, "", undef, 1, 1, 0,
36		&help_search_link("dhcpd", "man", "doc", "howto", "google"));
37	print &text('index_dhcpdnotfound', $config{'dhcpd_path'},
38		    "$gconfig{'webprefix'}/config.cgi?$module_name"),"<p>\n";
39
40	&foreign_require("software", "software-lib.pl");
41	$lnk = &software::missing_install_link("dhcpd", $text{'index_dhcpd'},
42			"../$module_name/", $text{'index_title'});
43	print $lnk,"<p>\n" if ($lnk);
44
45	&ui_print_footer("/", $text{'index_return'});
46	exit;
47	}
48
49# Check if it is the right version
50@st = stat($config{'dhcpd_path'});
51if ($st[7] != $config{'dhcpd_size'} || $st[9] != $config{'dhcpd_mtime'}) {
52	# File has changed .. get the version
53	local $ver = &get_dhcpd_version(\$out);
54	if (!$ver) {
55		&ui_print_header(undef, $text{'index_title'}, "", undef, 1, 1, 0,
56			&help_search_link("dhcpd", "man", "doc", "howto", "google"));
57		print (&text('index_dhcpdver2',$config{'dhcpd_path'},
58			     2, 3)),"<p>\n";
59		print "<pre>$out</pre>\n";
60		&ui_print_footer("/", $text{'index_return'});
61		exit;
62		}
63	$config{'dhcpd_version'} = $ver;
64	$config{'dhcpd_size'} = $st[7];
65	$config{'dhcpd_mtime'} = $st[9];
66	&write_file("$module_config_directory/config", \%config);
67	}
68
69&ui_print_header(undef, $text{'index_title'}, "", undef, 1, 1, 0,
70	&help_search_link("dhcpd", "man", "doc", "howto", "google"),
71	undef, undef, &text('index_version', $config{'dhcpd_version'}));
72
73# Create lookup type HTML
74# XXX change text, add to lookup_*
75$matches = ui_select("match", $config{'match_default'} || 0,
76		     [ [0, $text{'index_match0'} ],
77                       [1, $text{'index_match1'} ],
78                       [2, $text{'index_match2'} ] ]);
79
80# get top-level hosts
81foreach $h (&find("host", $conf)) {
82	push(@host, $h);
83	}
84foreach $g (&find("group", $conf)) {
85	push(@group, $g);
86	foreach $h (&find("host", $g->{'members'})) {
87		push(@host, $h);
88		$group{$h} = $g->{'index'};
89		$par{$h} = $g;
90		push(@{$g->{'hosts'}}, $h->{'values'}->[0]);
91		}
92	}
93
94# get subnets and shared nets, and the hosts and groups within them
95@subn = &find("subnet", $conf);
96foreach $u (@subn) {
97	$maxsubn = $maxsubn > $u->{'index'} ? $maxsubn : $u->{'index'};
98	foreach $h (&find("host", $u->{'members'})) {
99		$maxhost = $maxhost > $h->{'index'} ? $maxhost : $h->{'index'};
100		$subnet{$h} = $u->{'index'};
101		$par{$h} = $u;
102		push(@host, $h);
103		}
104	foreach $g (&find("group", $u->{'members'})) {
105		$maxgroup = $maxgroup > $g->{'index'} ? $maxgroup : $g->{'index'};
106		$subnet{$g} = $u->{'index'};
107		$par{$g} = $u;
108		push(@group, $g);
109		foreach $h (&find("host", $g->{'members'})) {
110			$maxhost = $maxhost > $h->{'index'} ? $maxhost : $h->{'index'};
111			$subnet{$h} = $u->{'index'};
112			$group{$h} = $g->{'index'};
113			$par{$h} = $g;
114			push(@{$g->{'hosts'}}, $h->{'values'}->[0]);
115			push(@host, $h);
116			}
117		}
118	}
119@shan = &find("shared-network", $conf);
120foreach $s (@shan) {
121	$maxshar = $maxshar > $s->{'index'} ? $maxshar : $s->{'index'};
122	foreach $h (&find("host", $s->{'members'})) {
123		$maxhost = $maxhost > $h->{'index'} ? $maxhost : $h->{'index'};
124		$shared{$h} = $s->{'index'};
125		$par{$h} = $s;
126		push(@host, $h);
127		}
128	foreach $g (&find("group", $s->{'members'})) {
129		$maxgroup = $maxgroup > $g->{'index'} ? $maxgroup : $g->{'index'};
130		$shared{$g} = $s->{'index'};
131		$par{$g} = $s;
132		push(@group, $g);
133		foreach $h (&find("host", $g->{'members'})) {
134			$maxhost = $maxhost > $h->{'index'} ? $maxhost : $h->{'index'};
135			$group{$h} = $g->{'index'};
136			$shared{$h} = $s->{'index'};
137			$par{$h} = $g;
138			push(@{$g->{'hosts'}}, $h->{'values'}->[0]);
139			push(@host, $h);
140			}
141		}
142	foreach $u (&find("subnet", $s->{'members'})) {
143		$maxsubn = $maxsubn > $u->{'index'} ? $maxsubn : $u->{'index'};
144		$par{$u} = $s;
145		push(@subn, $u);
146		$shared{$u} = $s->{'index'};
147		foreach $h (&find("host", $u->{'members'})) {
148			$maxhost = $maxhost > $h->{'index'} ? $maxhost : $h->{'index'};
149			$subnet{$h} = $u->{'index'};
150			$shared{$h} = $s->{'index'};
151			$par{$h} = $u;
152			push(@host, $h);
153			}
154		foreach $g (&find("group", $u->{'members'})) {
155			$maxgroup = $maxgroup > $g->{'index'} ? $maxgroup : $g->{'index'};
156			$subnet{$g} = $u->{'index'};
157			$shared{$g} = $s->{'index'};
158			$par{$g} = $u;
159			push(@group, $g);
160			foreach $h (&find("host", $g->{'members'})) {
161				$maxhost = $maxhost > $h->{'index'} ? $maxhost : $h->{'index'};
162				$subnet{$h} = $u->{'index'};
163				$group{$h} = $g->{'index'};
164				$shared{$h} = $s->{'index'};
165				$par{$h} = $g;
166				push(@{$g->{'hosts'}}, $h->{'values'}->[0]);
167				push(@host, $h);
168				}
169			}
170		}
171	}
172foreach $s (@shan) {
173	$s->{'order'} = (1 + $s->{'index'}) * (2 + $maxsubn);
174	}
175foreach $s (@subn) {
176	$s->{'order'} = (defined($shared{$s}) ? (1 + $shared{$s}) * (2 + $maxsubn) : 0)
177			+ 1 + $s->{'index'};
178	}
179if ($norder == 0) {
180	@subn = (@subn, @shan);
181	}
182elsif ($norder == 1) {
183	@subn = (@subn, @shan);
184	@subn = sort { $a->{'order'} <=> $b->{'order'} } @subn;
185	}
186elsif ($norder == 2) {
187	@subn = sort { $a->{'values'}->[0] <=> $b->{'values'}->[0] } @subn;
188	@shan = sort { $a->{'values'}->[0] cmp $b->{'values'}->[0] } @shan;
189	@subn = (@subn, @shan);
190	}
191
192# display subnets and shared nets
193my @cansubn;
194foreach $u (@subn) {
195	local $can_view = &can('r', \%access, $u);
196	next if !$can_view && $access{'hide'};
197	local ($l, $t, $i);
198	push(@cansubn, $u);
199	if ($u->{'name'} eq "subnet") {
200		push(@ulinks, $l = $can_view ?
201			"edit_subnet.cgi?idx=$u->{'index'}".
202			($shared{$u} ne "" ? "&sidx=$shared{$u}" : "") :
203			undef);
204		push(@uicons, $i = "images/subnet.gif");
205		push(@checkboxids, $u->{'index'}.
206				   ($shared{$u} ne "" ? "/$shared{$u}" : ""));
207		}
208	else {
209		push(@slinks, $l = $can_view ?
210			"edit_shared.cgi?idx=$u->{'index'}" : undef);
211		push(@sicons, $i = "images/shared.gif");
212		push(@checkboxids, $u->{'index'});
213		}
214	if ($config{'desc_name'} && $u->{'comment'}) {
215		push(@utitles, $t = &html_escape($u->{'comment'}));
216		}
217	else {
218		push(@utitles, $t = &html_escape($u->{'values'}->[0]));
219		}
220	push(@uslinks, $l);	# so that ordering is preserved
221	push(@ustitles, $t);
222	push(@usicons, $i);
223	}
224@checkboxes = map { &ui_checkbox("d", $_) } @checkboxids;
225if ($access{'r_sub'} || $access{'c_sub'} ||
226    $access{'r_sha'} || $access{'c_sha'}) {
227	print &ui_subheading($text{'index_subtitle'});
228	$sp = "";
229	if (@ulinks < $display_max && @slinks < $display_max) {
230		@links = @uslinks;
231		@titles = @ustitles;
232		@icons = @usicons;
233		}
234	elsif (@ulinks < $display_max) {
235		@links = @ulinks;
236		@titles = @utitles;
237		@icons = @uicons;
238		}
239	elsif (@slinks < $display_max) {
240		@links = @slinks;
241		@titles = @stitles;
242		@icons = @sicons;
243		}
244	if (@links) {
245		# Show table of subnets and shared nets
246		$show_subnet_delete = 1;
247		print &ui_form_start("delete_subnets.cgi");
248		&subnet_add_links();
249		if ($config{'hostnet_list'} == 0) {
250			&icons_table(\@links, \@titles, \@icons, $nocols,
251				     undef, undef, undef, \@checkboxes);
252			}
253		else {
254			&net_table(\@cansubn, 0, scalar(@cansubn), \@links,
255				   \@titles, \@checkboxids);
256			}
257		}
258	elsif (!@ulinks && !@slinks) {
259		# No subnets or shared nets
260		print "$text{'index_nosubdef'} <p>\n";
261		}
262	$show_subnet_shared = 1;
263	}
264&subnet_add_links();
265if ($show_subnet_delete) {
266	print &ui_form_end([ [ "delete", $text{'index_delete'} ] ]);
267	}
268
269# Show too-many forms
270if ($show_subnet_shared) {
271	if (@ulinks >= $display_max) {
272		# Could not show all subnets, so show lookup form
273        print &ui_form_start("lookup_subnet.cgi", "get");
274        print &ui_table_start(undef, undef, 2);
275        print &ui_table_row($text{'index_subtoomany'}, &ui_submit($text{'index_sublook2'}));
276        print &ui_table_row($matches, &ui_textbox("subnet", "", 30));
277	    print &ui_table_end();
278        print &ui_form_end(undef,undef,1);
279		}
280	if (@slinks >= $display_max) {
281		# Could not show all shared nets, so show lookup form
282        print &ui_form_start("lookup_shared.cgi", "get");
283        print &ui_table_start(undef, undef, 2);
284        print &ui_table_row($text{'index_shatoomany'}, &ui_submit($text{'index_shalook2'}));
285        print &ui_table_row($matches, &ui_textbox("shared", "", 30));
286	    print &ui_table_end();
287        print &ui_form_end(undef,undef,1);
288		}
289	}
290
291print &ui_hr();
292
293foreach $g (@group) {
294	$parent = (defined($subnet{$g}) ? 1 + $subnet{$g} : 0) +
295		  (defined($shared{$g}) ? (1 + $shared{$g}) * (2 + $maxsubn) : 0);
296	$g->{'order'} = $parent + (1 + $g->{'index'}) / (2 + $maxgroup);
297	}
298foreach $h (@host) {
299	$parent = (defined($group{$h}) ? (1 + $group{$h}) / (2 + $maxgroup) : 0) +
300		  (defined($subnet{$h}) ? 1 + $subnet{$h} : 0) +
301		  (defined($shared{$h}) ? (1 + $shared{$h}) * (2 + $maxsubn) : 0);
302	$h->{'order'} = $parent + (1 + $h->{'index'}) /
303			((1 + @group) * (2 + $maxhost));
304	}
305if ($horder == 0) {
306	@host = (@host, @group);
307	}
308elsif ($horder == 1) {
309	@host = (@host, @group);
310	@host = sort { $a->{'line'} <=> $b->{'line'} } @host;
311	}
312elsif ($horder == 2) {
313	@host = sort { $a->{'values'}->[0] cmp $b->{'values'}->[0] } @host;
314	@host = (@host, @group);
315	}
316elsif ($horder == 3) {
317	@host = sort { &hardware($a) cmp &hardware($b) } @host;
318	@host = (@host, @group);
319	}
320elsif ($horder == 4) {
321	@host = sort { &ipaddress($a) cmp &ipaddress($b) } @host;
322	@host = (@host, @group);
323	}
324
325# display hosts
326my @canhost;
327foreach $h (@host) {
328	local $can_view = &can('r', \%access, $h);
329	next if !$can_view && $access{'hide'};
330	push(@canhost, $h);
331	if ($h->{'name'} eq 'host') {
332		# Add icon for a host
333		push(@hlinks, $l = $can_view ?
334			"edit_host.cgi?idx=$h->{'index'}".
335			(defined($group{$h}) ? "&gidx=$group{$h}" : "").
336			(defined($subnet{$h}) ? "&uidx=$subnet{$h}" : "").
337			(defined($shared{$h}) ? "&sidx=$shared{$h}" : "") :
338			undef);
339		if ($config{'desc_name'} && $h->{'comment'}) {
340			push(@htitles, &html_escape($h->{'comment'}));
341			}
342		else {
343			push(@htitles, &html_escape($h->{'values'}->[0]));
344			}
345		if ($config{'show_ip'}) {
346			$fv = &fixedaddr($h);
347			$htitles[$#htitles] .= "<br>".$fv if ($fv);
348			}
349		if ($config{'show_mac'}) {
350			local $hard = &find("hardware", $h->{'members'});
351			$htitles[$#htitles] .= "<br>$hard->{'values'}->[1]"
352				if ($hard);
353			}
354		$t = $htitles[$#htitles];
355		push(@hicons, $i = "images/host.gif");
356		push(@hgcheckboxids, join("/", $h->{'index'}, $group{$h},
357					       $subnet{$h}, $shared{$h}));
358		}
359	else {
360		# Add icon for a group
361		push(@glinks, $l = $can_view ?
362			"edit_group.cgi?idx=$h->{'index'}".
363			(defined($subnet{$h}) ? "&uidx=$subnet{$h}" : "").
364			(defined($shared{$h}) ? "&sidx=$shared{$h}" : "") :
365			undef);
366		$gm = @{$h->{'hosts'}};
367		push(@gtitles, $t = &html_escape(&group_name($gm, $h)));
368		push(@gicons, $i = "images/group.gif");
369		push(@hgcheckboxids, join("/", $h->{'index'}, $subnet{$h},
370					       $shared{$h}));
371		}
372	push(@hglinks, $l);
373	push(@hgtitles, $t);
374	push(@hgicons, $i);
375	}
376@hgcheckboxes = map { &ui_checkbox("d", $_) } @hgcheckboxids;
377if ($access{'r_hst'} || $access{'c_hst'} ||
378    $access{'r_grp'} || $access{'c_grp'}) {
379	print &ui_subheading($text{'index_hst'});
380	$sp = "";
381	if (@hlinks < $display_max && @glinks < $display_max) {
382		@links = @hglinks;
383		@titles = @hgtitles;
384		@icons = @hgicons;
385		}
386	elsif (@hlinks < $display_max) {
387		@links = @hlinks;
388		@titles = @htitles;
389		@icons = @hicons;
390		}
391	elsif (@glinks < $display_max) {
392		@links = @glinks;
393		@titles = @gtitles;
394		@icons = @gicons;
395		}
396	if (@links) {
397		# Some hosts or groups to show
398		&index_links($horder, "h", 5, $text{'index_hdisplay'},
399			"norder=$norder");
400		$show_host_delete = 1;
401		print &ui_form_start("delete_hosts.cgi");
402		&host_add_links();
403		if ($config{'hostnet_list'} == 0) {
404			&icons_table(\@links, \@titles, \@icons, $nocols,
405				     undef, undef, undef, \@hgcheckboxes);
406			}
407		else {
408			&host_table(\@canhost, 0, scalar(@canhost), \@links,
409				    \@titles, \@hgcheckboxids);
410			}
411		}
412	elsif (!@hlinks && !@glinks) {
413		# None to show at all
414		print "$text{'index_nohst'} <p>\n";
415		}
416	$show_host_group = 1;
417	}
418&host_add_links();
419if ($show_host_delete) {
420	print &ui_form_end([ [ "delete", $text{'index_delete'} ] ]);
421	}
422
423# Show too-many forms
424if ($show_host_group) {
425	if (@hlinks >= $display_max) {
426		# Could not show all hosts, so show lookup form
427        print &ui_form_start("lookup_host.cgi", "get");
428	    print &ui_table_start(undef, undef, 2);
429        print &ui_table_row($text{'index_hsttoomany'}, &ui_submit($text{'index_hstlook2'}));
430        print &ui_table_row($matches, &ui_textbox("host", "", 30));
431	    print &ui_table_end();
432        print &ui_form_end(undef,undef,1);
433		}
434	if (@glinks >= $display_max) {
435		# Could not show all groups, so show lookup form
436        print &ui_form_start("lookup_group.cgi", "get");
437	    print &ui_table_start(undef, undef, 2);
438        print &ui_table_row($text{'index_grptoomany'}, &ui_submit($text{'index_grplook2'}));
439        print &ui_table_row($matches, &ui_textbox("group", "", 30));
440	    print &ui_table_end();
441        print &ui_form_end(undef,undef,1);
442		}
443	}
444
445print &ui_hr();
446
447############ START ZONES #####
448
449if ($config{'dhcpd_version'} >= 3 && $access{'zones'}) {
450	print &ui_subheading($text{'zone_key'});
451
452	# get zones
453	@zones = &find("zone", $conf);
454	@zones = sort { $a->{'values'}->[0] <=> $b->{'values'}->[0] } @zones;
455	if (@zones) {
456		# display zones
457        print &ui_link("edit_zones.cgi?new=1",$text{'index_addzone'})."&nbsp;&nbsp;\n" if $access{'c_sub'};
458		foreach $z (@zones) {
459			# print "ZONE: $z->{'value'} <br>";
460			push(@zlinks, "edit_zones.cgi?idx=$z->{'index'}");
461			push(@zicons, "images/files.gif");
462			push(@ztitles, &html_escape($z->{'values'}->[0]));
463			}
464		if ($config{'hostnet_list'} == 0) {
465			# user wants icons
466			&icons_table(\@zlinks, \@ztitles, \@zicons, $nocols);
467			}
468		else {
469			# user wants text
470			&zone_table(\@zones, 0, scalar(@zones), \@zlinks, \@ztitles);
471			}
472		}
473	else {
474		print "<b>$text{'index_nozones'}</b><p>\n";
475		}
476    print &ui_link("edit_zones.cgi?new=1",$text{'index_addzone'})."&nbsp;&nbsp;\n" if $access{'c_sub'};
477	print &ui_hr();
478
479}
480
481# Buttons for global actions
482print &ui_buttons_start();
483
484if ($access{'global'}) {
485	# Edit global client options
486	print &ui_buttons_row("edit_options.cgi",
487		$text{'index_buttego'}, $text{'index_ego'},
488		&ui_hidden("global", 1));
489	}
490
491if ($config{'dhcpd_version'} >= 3 && $access{'global'}) {
492	# TSIG keys
493	print &ui_buttons_row("edit_keys.cgi",
494		$text{'index_buttekey'}, $text{'index_key'});
495	}
496if (!$access{'noconfig'}) {
497	# Manually edit config file
498	print &ui_buttons_row("edit_text.cgi",
499		$text{'index_buttetext'}, $text{'index_text'});
500	}
501if (!$access{'noconfig'}) {
502	# Select network interfaces
503	print &ui_buttons_row("edit_iface.cgi",
504		$text{'index_buttiface'}, $text{'index_iface'});
505	}
506if ($access{'r_leases'}) {
507	# Show active leases
508	print &ui_buttons_row("list_leases.cgi",
509		$text{'index_buttlal'}, $text{'index_lal'});
510	}
511if ($access{'apply'}) {
512	$pid = &is_dhcpd_running();
513	if ($pid) {
514		# Apply and stop buttons
515		print &ui_buttons_row("restart.cgi",
516			$text{'index_buttapply'}, $text{'index_apply'},
517			&ui_hidden("pid", $pid));
518		print &ui_buttons_row("stop.cgi",
519			$text{'index_stop'}, $text{'index_stopdesc'},
520			&ui_hidden("pid", $pid));
521		}
522	else {
523		# Start button
524		print &ui_buttons_row("start.cgi",
525			$text{'index_buttstart'}, $text{'index_start'});
526		}
527	}
528print &ui_buttons_end();
529
530&ui_print_footer("/", $text{'index_return'});
531
532# Returns canonized hardware address.
533sub hardware {
534	local ($hconf, $addr);
535	$hconf = $_[0]->{'members'} ? &find("hardware", $_[0]->{'members'}) : undef;
536	if ($hconf) {
537		$addr = uc($hconf->{'values'}->[1]);
538		$addr =~ s/(^|\:)([0-9A-F])(?=$|\:)/$1\x30$2/g;
539	}
540	return $hconf ? $addr : undef;
541}
542
543# Returns ip address for sorting on
544sub ipaddress
545{
546return undef if (!$_[0]->{'members'});
547local $iconf = &find("fixed-address", $_[0]->{'members'});
548return undef if (!$iconf);
549return sprintf "%3.3d.%3.3d.%3.3d.%3.3d",
550		split(/\./, $iconf->{'values'}->[0]);
551}
552
553sub fixedaddr {
554	local ($fixed, $addr);
555	$fixed = &find("fixed-address", $_[0]->{'members'});
556	if ($fixed) {
557		$addr = join(" ", grep { $_ ne "," } @{$fixed->{'values'}});
558		return $addr;
559	} else {
560		return undef;
561	}
562}
563
564sub netmask {
565	return $_[0]->{'values'}->[2];
566}
567
568# index_links(current, name, max, txt, ref)
569sub index_links
570{
571local (%linkname, $l);
572local @links;
573for (my $l = 0; $l < $_[2]; $l++) {
574	my $msg = $text{"index_$_[1]order$l"};
575	if ($l eq $_[0]) {
576		push(@links, $msg);
577		}
578	else {
579		push(@links, &ui_link("?$_[1]order=$l\&$_[4]",$msg) );
580		}
581	}
582print "<b>$_[3]</b> ",&ui_links_row(\@links),"\n";
583open(INDEX, "> $module_config_directory/$_[1]index.".$remote_user);
584print INDEX "$_[0]\n";
585close(INDEX);
586}
587
588sub host_table
589{
590local ($i, $h, $parent);
591local @tds = ( "width=5" );
592print &ui_columns_start([ "", $text{'index_hostgroup'},
593			  $text{'index_parent'}, $text{'index_hardware'},
594			  $text{'index_nameip'} ], 100, 0, \@tds);
595for ($i = $_[1]; $i < $_[2]; $i++) {
596	local @cols;
597	local $firstcol;
598	$parent = "";
599	$par_type = "";
600	$h = $_[0]->[$i];
601	if ($h->{'name'} eq 'host') {
602		$firstcol .= $sp;
603		}
604	else {
605		$firstcol .= $text{'index_group'}." ";
606		$sp = "\&nbsp;\&nbsp;";
607		}
608	if ($_[3]->[$i]) {
609		$firstcol .= &ui_link($_[3]->[$i], $_[4]->[$i]);
610		}
611	else {
612		$firstcol .= $_[4]->[$i];
613		}
614	push(@cols, $firstcol);
615
616	if ($par{$h}->{'name'} eq "group") {
617	    $par_type = $text{'index_togroup'};
618	    $parent = &group_name(scalar @{$par{$h}->{'hosts'}});
619	} elsif ($par{$h}->{'name'} eq "subnet") {
620	    $par_type = $text{'index_tosubnet'};
621	    $parent = $par{$h}->{'values'}->[0];
622	} elsif ($par{$h}->{'name'} eq "shared-network") {
623	    $par_type = $text{'index_toshared'};
624	    $parent = $par{$h}->{'values'}->[0];
625	}
626
627	if ($config{'desc_name'} && $par{$h}->{'comment'}) {
628	    $parent = $par{$h}->{'comment'};
629	}
630	push(@cols, "$par_type  $parent");
631	push(@cols, $_[3]->[$i] ? &hardware($h) : "");
632	push(@cols, $_[3]->[$i] ? &fixedaddr($h) : "");
633	print &ui_checked_columns_row(\@cols, \@tds, "d", $_[5]->[$i]);
634	}
635print &ui_columns_end();
636}
637
638#&net_table(\@subn, 0, scalar(@subn), \@links, \@titles, \@checkboxids);
639sub net_table
640{
641local ($i, $n);
642local @tds = ( "width=5" );
643print &ui_columns_start([ "", $text{'index_net'}, $text{'index_netmask'},
644			  $text{'index_desc'}, $text{'index_parent'} ], 100,
645			0, \@tds);
646for ($i = $_[1]; $i < $_[2]; $i++) {
647	local @cols;
648	$n = $_[0]->[$i];
649	local $first;
650	if ($n->{'name'} eq 'subnet') {
651		$first = $sp;
652		}
653	else {
654		$sp = "\&nbsp;\&nbsp;";
655		}
656	if ($_[3]->[$i]) {
657		$first .= &ui_link($_[3]->[$i],$_[4]->[$i]);
658		}
659	else {
660		$first .= $_[4]->[$i];
661		}
662	push(@cols, $first);
663	push(@cols, $_[3]->[$i] ? &netmask($n) : "");
664	push(@cols, $n->{'comment'});
665	push(@cols, $par{$n} ?
666		"$text{'index_toshared'} $par{$n}->{'values'}->[0]" : "");
667	print &ui_checked_columns_row(\@cols, \@tds, "d", $_[5]->[$i]);
668	}
669print &ui_columns_end();
670}
671
672
673#&zone_table(\@zones, 0, scalar(@zones), \@zlinks, \@ztitles);
674sub zone_table
675{
676my $i;
677print &ui_table_start($text{'index_zone'}, "width=95%", 2);
678for ($i = $_[1]; $i < $_[2]; $i++) {
679    print &ui_table_row(undef, &ui_link($_[3]->[$i],$_[4]->[$i]) );
680	}
681print &ui_table_end();
682}
683
684sub subnet_add_links
685{
686local @links;
687if ($show_subnet_delete) {
688	push(@links, &select_all_link("d"),
689		     &select_invert_link("d"));
690	}
691push(@links, &ui_link("edit_subnet.cgi?new=1",$text{'index_addsub'}) )
692	if $access{'c_sub'};
693push(@links, &ui_link("edit_shared.cgi?new=1",$text{'index_addnet'}) )
694	if $access{'c_sha'};
695print &ui_links_row(\@links);
696}
697
698sub host_add_links
699{
700local @links;
701if ($show_host_delete) {
702	push(@links, &select_all_link("d", 1),
703		     &select_invert_link("d", 1));
704	}
705push(@links, &ui_link("edit_host.cgi?new=1",$text{'index_addhst'}) )
706	if $access{'c_hst'};
707push(@links, &ui_link("edit_group.cgi?new=1",$text{'index_addhstg'}) )
708	if $access{'c_grp'};
709print &ui_links_row(\@links);
710}
711
712