# debian-linux-lib.pl
# NIS functions for debian linux NIS client and server

$yp_makefile = "/var/yp/Makefile";
$ypserv_conf = "/etc/ypserv.conf";
$pid_file = "/var/run/ypserv.pid";

# get_nis_support()
# Returns 0 for no support, 1 for client only, or 2 for client & server
sub get_nis_support
{
if (!&has_command("ypbind")) {
	return 0;
	}
elsif (!&has_command("ypserv")) {
	return 1;
	}
else {
	return 2;
	}
}

# get_client_config()
# Returns a hash ref containg details of the client's NIS settings
sub get_client_config
{
local $nis = { 'broadcast' => 1 };
open(CONF, $config{'client_conf'});
while(<CONF>) {
	s/\r|\n//g;
	s/#.*$//g;
	if (/^\s*domain\s*(\S+)\s*server\s*(\S+)/i) {
		push(@{$nis->{'servers'}}, $2);
		$nis->{'broadcast'} = 0;
		}
	elsif (/^\s*ypserver\s*(\S+)/) {
		push(@{$nis->{'servers'}}, $1);
		$nis->{'broadcast'} = 0;
		}
	}
close(CONF);
open(DOMAIN, "/etc/defaultdomain");
chop($nis->{'domain'} = <DOMAIN>);
close(DOMAIN);
return $nis;
}

# save_client_config(&config)
# Saves and applies the NIS client configuration in the give hash.
# Returns an error message if any, or undef on success.
sub save_client_config
{
# Save the config file
open(CONF, ">$config{'client_conf'}");
open(DOMAIN, ">/etc/defaultdomain");
if ($_[0]->{'domain'}) {
	if (!$_[0]->{'broadcast'}) {
		local @s = @{$_[0]->{'servers'}};
		print CONF "domain $_[0]->{'domain'} server ",shift(@s),"\n";
		foreach $s (@s) {
			print CONF "ypserver $s\n";
			}
		}
	print DOMAIN $_[0]->{'domain'},"\n";
	}
close(CONF);
close(DOMAIN);
if ($_[0]->{'domain'}) {
	&init::enable_at_boot("nis");
	}

# Apply by running the init script
local $init = &init_script("nis");
&system_logged("$init stop >/dev/null 2>&1");
&system_logged("killall ypbind >/dev/null 2>&1");
&system_logged("$init start >/dev/null 2>&1");
if ($_[0]->{'domain'}) {
	$out = `ypwhich 2>&1`;
	if ($?) {
		system("killall ypbind >/dev/null 2>&1");
		return $text{'client_eypwhich'};
		}
	}
return undef;
}

@nis_files = ( "passwd", "shadow", "group", "gshadow", "adjunct",
	       "aliases", "ethers", "bootparams", "hosts", "networks",
	       "printcap", "protocols", "publickeys", "rpc", "services",
	       "netgroup", "netid", "auto_master", "auto_home",
	       "auto_local" );

@nis_tables = ( "passwd", "group", "hosts", "rpc", "services", "netid",
		"protocols", "netgrp", "mail", "shadow", "publickey", "networks",
		"ethers", "bootparams", "printcap", "amd.home", "auto.master",
		"auto.home", "auto.local", "passwd.adjunct" );

# show_server_config()
# Display a form for editing NIS server options
sub show_server_config
{
local ($var, $rule) = &parse_yp_makefile();
local $init = &init_script("nis");
open(INIT, $init);
while(<INIT>) {
	$mode = $1 if (/^\s*NISSERVER\s*=["\s]*([^"\s]+)/);
	}
close(INIT);

print "<tr> <td><b>$text{'server_boot'}</b></td>\n";
printf "<td><input type=radio name=boot value=1 %s> %s\n",
	$mode eq 'false' ? '' : 'checked', $text{'yes'};
printf "<input type=radio name=boot value=0 %s> %s</td>\n",
	$mode eq 'false' ? 'checked' : '', $text{'no'};

local $dom = $var->{'LOCALDOMAIN'}->{'value'};
print "<td><b>$text{'server_domain'}</b></td>\n";
printf "<td><input type=radio name=domain_auto value=1 %s> %s\n",
	$dom =~ /`.*domainname`/ ? 'checked' : '', $text{'server_domain_auto'};
printf "<input type=radio name=domain_auto value=0 %s>\n",
	$dom =~ /`.*domainname`/ ? '' : 'checked';
printf "<input name=domain size=20 value='%s'></td> </tr>\n",
	$dom =~ /`.*domainname`/ ? '' : $dom;

print "<tr> <td><b>$text{'server_type'}</b></td>\n";
printf "<td colspan=3><input type=radio name=type value=1 %s> %s\n",
	$mode eq 'slave' ? '' : 'checked', $text{'server_master'};
printf "<input type=radio name=type value=0 %s> %s\n",
	$mode eq 'slave' ? 'checked' : '', $text{'server_slave'};
printf "<input name=slave size=30 value='%s'></td> </tr>\n",
	$mode eq 'slave' ? $config{'slave'} : '';

print "</table></td></tr></table><p>\n";
print "<table border width=100%>\n";
print "<tr $tb> <td><b>$text{'server_mheader'}</b></td> </tr>\n";
print "<tr $cb> <td><table width=100%>\n";

print "<tr> <td><b>$text{'server_dns'}</b></td>\n";
printf "<td><input type=radio name=b value='-b' %s> %s\n",
	$var->{'B'}->{'value'} eq '-b' ? 'checked' : '', $text{'yes'};
printf "<input type=radio name=b value='' %s> %s</td>\n",
	$var->{'B'}->{'value'} eq '-b' ? '' : 'checked', $text{'no'};

print "<td><b>$text{'server_push'}</b></td>\n";
printf "<td><input type=radio name=nopush value=false %s> %s\n",
	$var->{'NOPUSH'}->{'value'} eq 'true' ? '' : 'checked', $text{'yes'};
printf "<input type=radio name=nopush value=true %s> %s</td> </tr>\n",
	$var->{'NOPUSH'}->{'value'} eq 'true' ? 'checked' : '', $text{'no'};

local %inall;
map { $inall{$_}++ } split(/\s+/, $rule->{'all'}->{'value'});
print "<tr> <td rowspan=2 valign=top><b>$text{'server_tables'}</b></td>\n";
print "<td rowspan=2><select multiple size=5 name=tables>\n";
foreach $t (grep { $rule->{$_} } @nis_tables) {
	printf "<option value=%s %s>%s\n",
		$t, $inall{$t} ? 'selected' : '', $t;
	}
print "</select></td>\n";

print "<td><b>$text{'server_minuid'}</b></td>\n";
printf "<td><input name=minuid size=10 value='%s'></td> </tr>\n",
	$var->{'MINUID'}->{'value'};

print "<td><b>$text{'server_mingid'}</b></td>\n";
printf "<td><input name=mingid size=10 value='%s'></td> </tr>\n",
	$var->{'MINGID'}->{'value'};

print "<tr> <td><b>$text{'server_slaves'}</b></td>\n";
open(SLAVES, "/var/yp/ypservers");
while(<SLAVES>) {
	s/\s//g;
	push(@slaves, $_) if ($_);
	}
close(SLAVES);
printf "<td colspan=3><input name=slaves size=60 value='%s'></td> </tr>\n",
	join(" ", @slaves);

print "</table></td></tr></table><p>\n";
print "<table border width=100%>\n";
print "<tr $tb> <td><b>$text{'server_fheader'}</b></td> </tr>\n";
print "<tr $cb> <td><table width=100%>\n";

local $i = 0;
foreach $t (@nis_files) {
	local $f = &expand_vars($var->{uc($t)}->{'value'}, $var);
	next if (!$f);
	print "<tr>\n" if ($i%2 == 0);
	print "<td><b>",&text('server_file', $text{"desc_$t"} ? $text{"desc_$t"}
							      : $t),"</b></td>\n";
	print "<td><input name=$t size=30 value='$f'></td>\n";
	print "</tr>\n" if ($i++%2 == 1);
	}
}

# parse_server_config()
# Parse and save the NIS server options
sub parse_server_config
{
local ($var, $rule) = &parse_yp_makefile();
$in{'minuid'} =~ /^\d+$/ || &error($text{'server_eminuid'});
$in{'mingid'} =~ /^\d+$/ || &error($text{'server_emingid'});
$in{'domain_auto'} || $in{'domain'} =~ /^[A-Za-z0-9\.\-]+$/ ||
	&error($text{'server_edomain'});
$in{'type'} || gethostbyname($in{'slave'}) || &error($text{'server_eslave'});
&update_makefile($var->{'MINUID'}, $in{'minuid'});
&update_makefile($var->{'MINGID'}, $in{'mingid'});
&update_makefile($var->{'NOPUSH'}, $in{'nopush'});
&update_makefile($var->{'B'}, $in{'b'});
&update_makefile($var->{'LOCALDOMAIN'}, $in{'domain_auto'} ? "`domainname`"
							   : $in{'domain'});
&update_makefile($rule->{'all'}, join(" ", split(/\0/, $in{'tables'})), "");

foreach $t (@nis_files) {
	local $old = &expand_vars($var->{uc($t)}->{'value'}, $var);
	next if (!$old);
	if ($old ne $in{$t}) {
		$in{$t} =~ /\S/ || &error(&text('server_efile', $text{"desc_$t"}));
		&update_makefile($var->{uc($t)}, $in{$t});
		}
	}

open(SLAVES, ">/var/yp/ypservers");
foreach $s (split(/\s+/, $in{'slaves'})) {
	print SLAVES "$s\n";
	}
close(SLAVES);

if ($in{'boot'}) {
	&init::enable_at_boot("nis");
	}
local $init = &init_script("nis");
local $lref = &read_file_lines($init);
foreach $l (@$lref) {
	if ($l =~ /^\s*NISSERVER=/) {
		$l = "NISSERVER=".(!$in{'boot'} ? 'false' :
				   $in{'type'} ? 'master' : 'slave');
		}
	}
&flush_file_lines();
&system_logged("$init stop >/dev/null 2>&1");
&system_logged("$init start >/dev/null 2>&1");
if ($in{'type'}) {
	# Master server
	delete($config{'slave'});
	&apply_table_changes() if ($in{'boot'});
	}
else {
	$out = &backquote_logged("/usr/lib/yp/ypinit -s $in{'slave'} 2>&1");
	if ($?) { &error("<tt>$out</tt>"); }
	$config{'slave'} = $in{'slave'};
	}
&write_file("$module_config_directory/config", \%config);
}

# get_server_mode()
# Returns 0 if the NIS server is inactive, 1 if active as a master, or 2 if
# active as a slave.
sub get_server_mode
{
if (&init::action_status("nis") != 2) {
	return 0;
	}
elsif ($config{'slave'}) {
	return 2;
	}
else {
	return 1;
	}
}

# list_nis_tables()
# Returns a list of structures of all NIS tables
sub list_nis_tables
{
local ($var, $rule) = &parse_yp_makefile();
local @rv;
local $dom = $var->{'LOCALDOMAIN'}->{'value'};
chop($dom = `domainname`) if ($dom =~ /`.*domainname`/);
local %file;
map { $file{uc($_)} = &expand_vars($var->{uc($_)}->{'value'}, $var) } @nis_files;
local @all = split(/\s+/, $rule->{'all'}->{'value'});
foreach $t (@all) {
	local $table = { 'table' => $t,
		         'index' => scalar(@rv),
		         'domain' => $dom };
	if ($t eq "passwd") {
		if ($var->{'MERGE_PASSWD'}->{'value'} eq 'true') {
			$table->{'type'} = 'passwd_shadow';
			$table->{'files'} = [ $file{'PASSWD'}, $file{'SHADOW'} ];
			}
		elsif (&indexof('shadow', @all) >= 0) {
			# Show separate shadow and passwd tables as one table
			$table->{'type'} = 'passwd_shadow_full';
			$table->{'files'} = [ $file{'PASSWD'}, $file{'SHADOW'} ];
			@all = grep { $_ ne 'shadow' } @all;
			}
		else {
			$table->{'type'} = 'passwd';
			$table->{'files'} = [ $file{'PASSWD'} ];
			}
		}
	elsif ($t eq "group") {
		if ($var->{'MERGE_GROUP'}->{'value'} eq 'true') {
			$table->{'type'} = 'group_shadow';
			$table->{'files'} = [ $file{'GROUP'}, $file{'GSHADOW'} ];
			}
		else {
			$table->{'type'} = 'group';
			$table->{'files'} = [ $file{'GROUP'} ];
			}
		}
	elsif ($t eq "netgrp") {
		$table->{'type'} = "netgroup";
		$table->{'files'} = [ $file{'NETGROUP'} ];
		}
	elsif ($t eq "mail") {
		$table->{'type'} = "aliases";
		$table->{'files'} = [ $file{'ALIASES'} ];
		}
	else {
		$table->{'type'} = $t;
		$table->{'files'} = [ $file{uc($t)} ];
		}
	push(@rv, $table);
	}
return @rv;
}

# apply_table_changes()
# Do whatever is necessary for the table text files to be loaded into
# the NIS server
sub apply_table_changes
{
&system_logged("(cd /var/yp ; make) >/dev/null 2>&1 </dev/null");
}

# show_server_security()
# Show NIS server security-related options
sub show_server_security
{
local ($opts, $hosts) = &parse_ypserv_conf();

local $port = $opts->{'xfr_check_port'} ? $opts->{'xfr_check_port'}->{'value'}
					: 1;
print "<tr> <td><b>$text{'security_port'}</b></td>\n";
printf "<td><input type=radio name=port value=1 %s> %s\n",
	$port ? 'checked' : '', $text{'yes'};
printf "<input type=radio name=port value=0 %s> %s</td> </tr>\n",
	$port ? '' : 'checked', $text{'no'};

print "<tr> <td valign=top><b>$text{'security_maps'}</b></td>\n";
print "<td><table border>\n";
print "<tr $tb> <td><b>$text{'security_hosts'}</b></td> ",
      "<td><b>$text{'security_map'}</b></td> ",
      "<td><b>$text{'security_sec'}</b></td> ",
      "<td><b>$text{'security_mangle'}</b></td> </tr>\n";
local $i = 0;
foreach $h (@$hosts, { }) {
	print "<tr $cb>\n";
	print "<td><input name=host_$i size=20 value='$h->{'host'}'></td>\n";
	printf "<td><input type=radio name=map_def_$i value=1 %s> %s\n",
		$h->{'map'} eq '*' ? 'checked' : '', $text{'security_tall'};
	printf "<input type=radio name=map_def_$i value=0 %s>\n",
		$h->{'map'} eq '*' ? '' : 'checked';
	printf "<input name=map_$i size=20 value='%s'></td>\n",
		$h->{'map'} eq '*' ? '' : $h->{'map'};
	print "<td><select name=sec_$i>\n";
	foreach $s ('none', 'port', 'deny', 'des') {
		printf "<option value=%s %s>%s\n",
			$s, $h->{'sec'} eq $s ? 'selected' : '',
			$text{"security_sec_$s"};
		}
	print "</select></td>\n";
	printf "<td><input name=mangle_$i type=radio value=0 %s> %s\n",
		$h->{'mangle'} ? '' : 'checked', $text{'security_none'};
	printf "<input name=mangle_$i type=radio value=1 %s> %s\n",
		$h->{'mangle'} ? 'checked' : '';
	print "<input name=field_$i size=4 value='$h->{'field'}'></td>\n";
	print "</tr>\n";
	$i++;
	}
print "</table></td> </tr>\n";
}

# parse_server_security()
# Save and apply server security options
sub parse_server_security
{
# Save security settings
local ($opts, $hosts) = &parse_ypserv_conf();
local $lref = &read_file_lines($ypserv_conf);
local $xfr = $opts->{'xfr_check_port'};
local $line = $in{'port'} ? 'xfr_check_port: yes' : 'xfr_check_port: no';
if ($xfr) {
	$lref->[$xfr->{'line'}] = $line;
	}
else {
	push(@$lref, $line);
	}

# Save host restrictions
local ($i, $j, $offset);
for($i=0; defined($in{"host_$i"}); $i++) {
	next if (!$in{"host_$i"});
	$in{"host_$i"} =~ /^[^:\s]+$/ ||
		&error(&text('security_ehost', $in{"host_$i"}));
	$in{"map_def_$i"} || $in{"map_$i"} =~ /^[^:\s]+$/ ||
		&error(&text('security_emap', $in{"map_$i"}));
	$in{"field_$i"} =~ /^\d*$/ ||
		&error(&text('security_efield', $in{"field_$i"}));
	local @line = ( $in{"host_$i"},
		        $in{"map_def_$i"} ? "*" : $in{"map_$i"},
		        $in{"sec_$i"},
		        $in{"mangle_$i"} ? "yes" : "no" );
	push(@line, $in{"field_$i"})
		if ($in{"field_$i"} && $in{"field_$i"} != 2);
	local $old = $hosts->[$j++];
	if ($old) {
		$lref->[$old->{'line'}] = join(":", @line);
		}
	else {
		push(@$lref, join(":", @line));
		}
	}
while($hosts->[$j]) {
	splice(@$lref, $hosts->[$j]->{'line'}-$offset, 1);
	$j++; $offset++;
	}
&flush_file_lines();

# Apply the changes
if (open(PID, $pid_file) && <PID> =~ /(\d+)/ && kill(0, $1)) {
	&kill_logged('HUP', $1);
	close(PID);
	}
}

1;

