#!/usr/local/bin/perl

	$VERSION		= "2.3";
	$MOD_DATE		= "9.Aug.94";
	$AUTHOR			= "Paul Hulford";

# 09-Aug-94 PDH Add line to print version for -v option 

#
# Get options and print verions if required
#

require "getopts.pl";
&Getopts("vharmwqul:H:f:g:") || help && exit;
die "\nhgaccount v$VERSION - $AUTHOR $MOD_DATE\n\n" if $opt_v;
&help && exit if $opt_h;


#

#
# Help options
#

sub help
{
	print "\n";
	print "Description:\n";
	print "         Adds or removes a list of account names from\n";
	print "         Hyper-G. Also -u option, updates passwords\n";
	print "         so that local unix account can auto-identify.\n";
	print "         No arguments lists current hg user accounts\n";
	print "\n";
	print "Files:\n";
	print "         .passwd holds encrypted passds of hg users\n";
	print "         so when they change the old encryptions can be\n";
	print "         removed from hg.\n";
	print "\n";
	print "Usage:\n";
	print "         hgaccount -h\n";
	print "         hgaccount -v\n";
	print "         hgaccount [-l expr]\n";
	print "         hgaccount -u\n";
	print "         hgaccount -a -f file [-g group] [-H host_list]\n";
	print "         hgaccount -r -f file\n";
	print "         hgaccount -m\n";
	print "\n";
	print "Options:\n";
	print "        -h          -- help\n";
	print "        -v          -- version\n";	
	print "        -l expr     -- list accounts matching expr\n";
	print "        -u          -- update passwds\n";
	print "        -a          -- add accoumts\n";
	print "        -r          -- remove accounts\n";
	print "        -m          -- make new ./passwd file\n";
	print "        -f file     -- names to add/remove\n";
	print "        -g          -- group for -a option\n";
	print "        -H          -- host_list for -a option\n";
	print "\n";
	print "Output:\n";
	print "        *           -- update add or remove failed\n";
	print "        ^           -- user has no local password\n";
	print "\n";
}

# arguments

$nopts  =  $opt_u ? 1:0;
$nopts +=  $opt_a ? 1:0;
$nopts +=  $opt_r ? 1:0;
$nopts +=  $opt_l ? 1:0;
$nopts +=  $opt_m ? 1:0;
$nopts +=  $opt_h ? 1:0;
$nopts +=  $opt_v ? 1:0;

die( "\nERROR! too many options\n\n" ) && help && exit if $nopts > 1;

die( "\nERROR! -f arg required\n\n" ) if ! $opt_f && ( $opt_a || $opt_r );

# miscelaneous

	$TMP   = "/tmp/hgaccount.$$";

	$PF = "$ENV{'HOME'}/.passwd";
	if ( ! -e $PF)
	{
		&make_pwd;
		undef( $opt_m ) if $opt_m;
	}

	die( "ERROR could not open \"$PF\"\n" )
		if ! (-r $PF);

# body

	&update_hg  if $opt_u;
	&add_hg     if $opt_a;
	&remove_hg  if $opt_r;
	&make_pwd   if $opt_m;
	&list_users if $opt_l || $nopts == 0 ;   # default

exit;

#/////////////////////////////////////////////////////////////////////////*

sub list_users
{
	local ( $name, $oldpwd, $newpwd, $real_name );

	print "List:\n";

	&get_hgusers;

	open( UL, "<$TMP.u") 
		|| &mydie("ERROR! could not open file \"$TMP.u\" for read\n");
	
	while(<UL>)
	{
		( $name, $oldpwd, $newpwd, $real_name ) = &parse_line( $_ );
	
		print "   $name\n" if $newpwd;
		print "^  $name\n" if ! $newpwd;
	}

	system "/bin/rm $TMP.[a-z]";
	
	print "\n";
	print "   Listed.\n";
	print "\n";
}	

#/////////////////////////////////////////////////////////////////////////*

sub update_hg
{
	local ( $name, $oldpwd, $newpwd, $real_name, $upd );

	print "Update:\n";
	
	&get_hgusers;

	open(CMDF, ">$TMP.c" ) 
		|| die( "ERROR! could not open file \"$TMP.c\"\n");

	open(SF, "<$PF" ) 
		|| &mydie( "ERROR! could not open file \"$PF\"\n" );

	open(NPWDF,">$TMP.p") 
		|| &mydie("ERROR! could not open file \"$TMP.p\"\n");

	open( UL, "<$TMP.u") 
		|| &mydie("ERROR! could not open file \"$TMP.u\" for read\n");
		

	$upd = 0;

	while(<SF>)
	{
		( $name, $oldpwd, $newpwd, $real_name ) = &parse_line( $_ );

		next if ! $name;

		$known = &check_hgname( $name );

# if name has no hyper-g account then notify and continue
# dont add to new passwd file

		if ( ! $known )
		{
			print "*  $name\n";
			next;
		}

# if no local passwd, name not a local name
# but known to hyperg so add to passwd file

		if( ! $newpwd )
		{
			print "^  $name\n";
			print NPWDF "$name:\n";
			next;
		}

# create command to update password

		if ( "$newpwd" ne "$oldpwd" )
		{
			print "   $name\n"; 
			$upd = 1; 
			print CMDF "3\n$name\n10\n$oldpwd\n4\n$newpwd\nq\n";
		}
	
		print NPWDF "$name:$newpwd\n";
	}
	
	print CMDF "q\n";     # final quit
	
	close CMDF;
	close NPWDF;
	close SF;
	close UL;

	if ( $upd )
	{
		&execute_cmd;

		`/bin/mv $PF $PF.old`;	
		! system("/bin/mv $TMP.p $PF")
			|| &mydie("ERROR updating \"$PF\" file failed\n");
	}
	
	system "/bin/rm $TMP.[a-z]";
	
	print $upd ? "   Updated.\n" : "   No Change.\n";
	print "\n";
}	

#/////////////////////////////////////////////////////////////////////////*

sub add_hg
{
	local ( $name, $oldpwd, $newpwd, $real_name, $upd );

	print "Add:\n";

	&get_hgusers;

	&check_uniq( $opt_f ) 
		|| die( "ERROR! duplicates in file \"$opt_f\"\n" );

	! system( "/bin/cp -p $PF $TMP.p" )
		|| die( "ERROR! copy \"$PF\" \"$TMP.p\" failed.\n") ;

	open( CMDF, ">$TMP.c" ) 
		|| &mydie( "ERROR! could not open \"$TMP.c\"\n");

	open( NPWDF,">>$TMP.p") 
		|| &mydie("ERROR! could not open file \"$TMP.p\"\n");

	open( UL, "<$TMP.u") 
		|| &mydie("ERROR! could not open file \"$TMP.u\" for read\n");

	open( SF, "<$opt_f" ) 
		|| &mydie( "ERROR! could not open file \"$opt_f\"\n" );
	
	$upd = 0;


	while(<SF>)
	{
		( $name, $oldpwd, $newpwd, $real_name ) = &parse_line( $_ );

		next if ! $name;

		$known = &check_hgname( $name );

# if no existing hg account then can add. 
 
		print "\*  $name\n" if $known;
	
		if( ! $known )
		{
			if( $newpwd )
			{
				$host_list = join(':',split(/ /,$opt_H));
				$host_list = ":$host_list" if $host_list;
				while( $host_list =~ /^(.*):([^:]*)$/ )
					{ $host_list = $1."\n1\n$name\@".$2; }

				print "   $name\n" if $newpwd;
				print CMDF "1\n$name\n4\n$newpwd\n5\n$real_name\n";
				print CMDF "2\n$opt_g\n";
				print CMDF "$host_list\n13\n";
				print NPWDF "$name:$newpwd\n";
			}
			else
			{	
				print "^  $name\n";
				print CMDF "1\n$name\n5\n$real_name\n2\n$opt_g\n";
				print CMDF "13\n";
				print NPWDF "$name:\n";
			}
		
			$upd = 1; 
		}
	}
	
	print CMDF "q\n";  # final quit
	
	close CMDF;
	close SF;
	close UL;	
	close NPWDF;

	if ( $upd  )
	{
		&execute_cmd;

		`/bin/mv $PF $PF.old`;	
		! system("/bin/mv $TMP.p $PF")
			|| &mydie("ERROR updating \"$PF\" file failed\n");
	}
	
	system "/bin/rm $TMP.[a-z]";
	
	print $upd ? "   Added.\n" : "   No Change.\n";
	print "\n";
}	

#/////////////////////////////////////////////////////////////////////////*

sub remove_hg
{
	local ( $name, $oldpwd, $newpwd, $real_name, $upd, $remove );

	print "Remove:\n";

	&get_hgusers;

	&check_uniq( $opt_f ) 
		|| die( "ERROR! duplicates in file \"$opt_f\"\n" );

	open( CMDF, ">$TMP.c" ) 
		|| die( "ERROR! could not open \"$TMP.c\"\n");

	open( RL,">$TMP.r") 
		|| &mydie("ERROR! could not open file \"$TMP.r\"\n");

	open( UL, "<$TMP.u") 
		|| die("ERROR! could not open file \"$TMP.u\" for read\n");

	open( SF, "<$opt_f" ) 
		|| &mydie( "ERROR! could not open file \"$opt_f\"\n" );
	
	$upd = 0;

	while(<SF>)
	{
		( $name, $oldpwd, $newpwd, $real_name ) = &parse_line( $_ );

		next if ! $name;

		$known = &check_hgname( $name );

# if no existing hg account then cant remove.
 
		print "\*  $name\n" if ! $known;
	
		if( $known )
		{
			print "   $name\n" if $newpwd;
			print "^  $name\n" if ! $newpwd;
			print CMDF "3\n$name\n13\ny\n";
			print RL "$name\n";
		
			$upd = 1; 
		}
	}
	
	print CMDF "q\n";  # final quit
	
	close CMDF;
	close SF;
	close UL;	
	close RL;

	if ( $upd  )
	{
		&execute_cmd;

		open( NPWDF, ">$TMP.p" )
			|| &mydie( "ERROR! could not open file \"$TMP.p\"\n" );
	
		open( PF, "<$PF" )
			|| &mydie( "ERROR! could not open file \"$PF\"\n" );
	
		open( RL, "<$TMP.r" )
			|| &mydie( "ERROR! could not open file \"$TMP.r\"\n" );
	
# remove names from list
	
		while(<PF>)
		{
			$line = $_;
			$name = $_;
			$name =~ s/:.*//;
		
			$remove = 0;
			while(<RL>)
			{
				next unless $name eq $_;
				$remove = 1;
				last;
			}
			seek( RL, 0, 0 );
	
			print NPWDF $line unless $remove;
		}		
	
		close RL;
		close PF;
		close NPWDF;
	
		`/bin/mv $PF $PF.old`;	
		! system("/bin/mv $TMP.p $PF")
			|| &mydie("ERROR updating \"$PF\" file failed\n");
	}

	system "/bin/rm $TMP.[a-z]";
	
	print $upd ? "   Removed.\n" : "   No Change.\n";
	print "\n";
}	

#/////////////////////////////////////////////////////////////////////////*

sub make_pwd
{
	local ( $name, $remove );

	print "Make:\n";

	`touch $PF` if ! (-e $PF);

	&get_hgusers;

	`cat $PF    | sed 's/:.*//' | /bin/sort > $TMP.v`;

	`/bin/diff $TMP.u $TMP.v > $TMP.d`;

	`cat $TMP.d | egrep '^<' | sed 's/^< //' > $TMP.a`;  # add list
	`cat $TMP.d | egrep '^>' | sed 's/^> //' > $TMP.r`;  # remove list

	open( NPWDF, ">$TMP.p" )
		|| &mydie( "ERROR! could not open file \"$TMP.p\"\n" );

	open( PF, "<$PF" )
		|| &mydie( "ERROR! could not open file \"$PF\"\n" );

	open( RL, "<$TMP.r" )
		|| &mydie( "ERROR! could not open file \"$TMP.r\"\n" );

# remove names from list
	
	while(<PF>)
	{
		$line = $_;
		$name = $_;
		$name =~ s/:.*/:/;
	
		$remove = 0;
		while(<RL>)
		{
			next unless $name eq $_;
			$remove = 1;
			last;
		}
		seek( RL, 0, 0 );

		print NPWDF $line unless $remove;
	}		

	close RL;
	close PF;
	close NPWDF;

# add names to list

	`cat $TMP.a >> $TMP.p`;
	
# replace old passwd file

	`/bin/mv $PF $PF.old`;
	! system("/bin/mv $TMP.p $PF")
		|| &mydie("ERROR updating \"$PF\" file failed\n");

	system "/bin/rm $TMP.[a-z]";
	
	print "   Made.\n";
	print "\n";
}	

#/////////////////////////////////////////////////////////////////////////*

sub execute_cmd
{
	print "\n";

	! system( "hgadmin > $TMP.o < $TMP.c" )
		|| &mydie("ERROR! Cannot connect to database\n");
}			 

#/////////////////////////////////////////////////////////////////////////*

sub mydie
{
	system "/bin/rm $TMP.[a-z]";
	die @_;
}

#/////////////////////////////////////////////////////////////////////////*

sub get_hgusers
{
	local( $hdr, $body, $accounts );

# create command to list user accounts

	open( CMDF, ">$TMP.c" ) 
		|| die( "ERROR! could not open \"$TMP.c\"\n");
	print CMDF "3\n\nq\nq\n";
	close CMDF;

# issue command to hgadmin
	
	&execute_cmd;

	open( HF, "<$TMP.o" )
		|| &mydie("ERROR! could not open file \"$TMP.o\" for read\n");

	open( UL, ">$TMP.u") 
		|| &mydie("ERROR! could not open file \"$TMP.u\" for write\n");

# process output extracting account list

	$accounts = 0;
	$hdr = 0;
	$body = 0;

	while(<HF>)
	{
		$hdr || /Select User/ || next;
		$hdr = 1;

		$body || /^[0-9]+/ || next;
		$body = 1;
		
		/^$/ && last;

		s/^[0-9]+\s//;
		s/\s.*//;

		/^$/ && next;

		if ( $_ =~ /$opt_l/ )
		{	
			print UL;	
			$accounts++;
		}
	}

	close UL;
	close HF;

	system( "/bin/rm $TMP.[co]" );

	return $accounts++;
}			 

#/////////////////////////////////////////////////////////////////////////*

sub parse_line
{
	local ( $name, $oldpwd, $newpwd, $real_name );

	$line2p = $_[0];
	chop $line2p;
	( $name, $oldpwd ) = split(/:/,$line2p);

	$ypm = `ypmatch $name passwd 2> /dev/null`;		
	( $a, $newpwd, $a, $a, $real_name ) = split(/:/,$ypm);
	undef $a;	

	$real_name =~ s/,.*//g;

	return( $name, $oldpwd, $newpwd, $real_name );
}

#/////////////////////////////////////////////////////////////////////////*


sub check_hgname
{
		local( $ch_name, $hg_name );

		$ch_name = $_[0];

		$hg_name = 0;
		while(<UL>)
		{
			chop;
			next unless ( $_ eq $ch_name );
			$hg_name = 1;
			last;
		}
		seek( UL, 0, 0);

		return $hg_name;
}

#/////////////////////////////////////////////////////////////////////////*

sub check_uniq
{
	local( $len1, $len2 );
	local( $ch_file ) = $_[0];

	$len1 = `cat $ch_file | sed 's/:.*//' | sort | wc -l`;
	$len2 = `cat $ch_file | sed 's/:.*//' | sort | uniq | wc -l`;

	return 1 if $len1 == $len2;
	return 0;
}
