#!/usr/perl5/bin/perl
#
# Copyright (c) 1999 by Sun Microsystems, Inc.
# All rights reserved.
#
#ident	"@(#)projmod.pl	1.1	00/02/14 SMI"
#

require 5.005;
use strict;
use locale;
use English;
use FileHandle;
use File::Basename;
use Getopt::Std;
use POSIX qw(locale_h isalpha isdigit);
use Sun::Solaris::Utils qw(textdomain gettext);
use Sun::Solaris::Project qw(getprojbyname getprojbyid);

sub usage(@)
{
	my (@msg) = @_;
	print STDERR basename($0), ": @msg\n" if (@msg);
	print STDERR gettext(
	    "Usage: projmod [-c comment] [-U user[,user ...]] " .
	    "[-G group[,group ...]]\n\t[-p projid [-o]] " .
	    "[-l new_projectname] project\n");
	exit(2);
}

setlocale(LC_ALL, "");
textdomain("SUNW_OST_OSCMD");

my %opts = ();
getopts("c:G:l:p:oU:", \%opts) || usage();

my $project = $ARGV[0];
my $projid = $opts{'p'};
my $comment = $opts{'c'};
my $users = $opts{'U'};
my $groups = $opts{'G'};
my $newprojname = $opts{'l'};
my $projmin = 100;		# minimum value for project
my $intmax = (2 ** 31) - 1;     # max value for project
my $exitstatus = 0;

my $project_file = "/etc/project";

usage(gettext("ERROR: you must be root to administer projects"))
    if ($EUID != 0);

usage(gettext("ERROR: you must specify a project name"))
    unless defined $ARGV[0];

# if -o is specified, then -p must be specified as well
usage(gettext("-o requires -p projid to be specified"))
    if (exists($opts{'o'}) && ! exists($opts{'p'}));

# if a comment is specified, be sure it doesn't contain the field delimiter
usage(gettext("ERROR: comment may not contain : or \\n"))
    if ($comment =~ /[:\n]/);

# validate group names/ids
# produces a comma-delimited list of groups
if (exists($opts{'G'})) {
	if ($opts{'G'} =~ /:/) {
		printf STDERR gettext("projmod: ERROR: malformed group " .
		    "name.\n");
		exit(3);
	}
	my $numgrps = split(/\,/, $opts{'G'});
	$groups = "";
	my $sep = "";
	my $i = 0;
	my @pw;
	for ($i = 0; $i < $numgrps; $i++) {
		my $curgroup = shift @_;
		$curgroup =~ s/^\s+//;
		$curgroup =~ s/\s+$//;
		if ($curgroup =~ /^[0-9]+/) {
			@pw = getgrgid($curgroup);
		} else {
			@pw = getgrnam($curgroup);
		}
		if ($pw[0] eq "") {
			printf STDERR gettext("projmod: group %s does not " .
			    "exist. Choose another.\n"), $curgroup;
			exit(6);
		}
		$groups = $groups . $sep . $pw[0];
		$sep = ",";
	}  
}

# validate user names/ids
# produces a comma-delimited list of user names
if (exists($opts{'U'})) {
	if ($opts{'U'} =~ /:/) {
		printf STDERR gettext("projmod: ERROR: malformed user " .
		    "name.\n");
		exit(3);
	}
	my $numusers = split(/\,/, $opts{'U'});
	$users = "";
	my $sep = "";
	my $i = 0;
	my @pw;
	for ($i = 0; $i < $numusers; $i++) {
		my $curuser = shift @_;
		$curuser =~ s/^\s+//;
		$curuser =~ s/\s+$//;
		if ($curuser =~ /^[0-9]+/) {
			@pw = getpwuid($curuser);
		} else {
			@pw = getpwnam($curuser);
		}
		if ($pw[0] eq "") {
			printf STDERR gettext("projmod: user %s does not " .
			    "exist. Choose another.\n"), $curuser;
			exit(6);
		}
		$users = $users . $sep . $pw[0];
		$sep = ",";
	}  
}

# check for projid out of range

if (exists($opts{'p'})) {
	if ($projid < 0 || ! isdigit($projid)) {
		printf STDERR gettext("projmod: ERROR: projectid %s is " .
		    "invalid.\n"), $projid;
		exit(3);
	}
	if ($projid < $projmin) {
		printf STDERR gettext("projmod: ERROR: projectid %s is " .
		    "reserved.\n"), $projid;
		exit(4);
	}

	if ($projid > $intmax) {
		printf STDERR gettext("projmod: ERROR: projectid %s is " .
		    "too big. Choose another.\n"), $projid;
		exit(4);
	}

	if (! exists($opts{'o'}) && defined(getprojbyid($projid))) {
		printf STDERR gettext("projmod: ERROR: projectid %s is " .
		    "already in use. Choose another.\n"), $projid;
		exit(4);
	}
}

# check format of new_project

if (exists($opts{'l'})) {
	if ($newprojname eq $project) {
		printf STDERR gettext("projmod: ERROR: new project name " .
		    "cannot be the same as project name.\n");
		exit(4);
	}
	if (isdigit($newprojname)) {
		printf STDERR gettext("projmod: ERROR: new project name " .
	            "cannot be numeric.\n");
		exit(3);
	}

	if ($newprojname =~ /[:\n]/) {
		printf STDERR gettext("projmod: ERROR: new project name %s " .
		    "is invalid.\n"), $newprojname;
		exit(3);
	}

	if (exists($opts{'l'}) && defined(getprojbyname($newprojname))) {
		printf STDERR gettext("projmod: ERROR: new project name is ".
		    "already in use.\n");
		exit(4);
	}

	if ($newprojname !~ /^[A-Za-z][A-Za-z0-9\._-]+/) {
		printf STDERR gettext("projmod: WARNING: new project name " .
		    "%s may be invalid.\n"), $newprojname;
	}
}

#
# Open the existing local database and the modified version.
#
my $original_fh;
my $new_fh;

$new_fh = new FileHandle "$project_file.tmp", O_WRONLY | O_EXCL | O_CREAT;
if (!defined($new_fh)) {
	printf STDERR "projmod: %s\n", $ERRNO;
	unlink "$project_file.tmp";
	exit(1);
}

$original_fh = new FileHandle "$project_file", O_RDONLY;
if (!defined($original_fh)) {
	unlink "$project_file.tmp";
	printf STDERR gettext("projmod: ERROR: Cannot update system " .
	    "files - project cannot be created.\n");
	exit(10);
}

# In order to verify uniqueness of project id's we need to suck in
# the whole existing project file and create a hash keyed on projid...

my $pos = $original_fh->getpos;
my %checkproj = ();
while (<$original_fh>) {
	my ($t_project, $t_projid, $t_comment, $t_users,
	    $t_groups, $t_attrs) = split(/:/);
	$checkproj{$t_projid} = $t_projid;
}

$original_fh->setpos($pos);

if (exists($opts{'p'}) && ! exists($opts{'o'}) && 
    $checkproj{$projid} == $projid) {
	printf STDERR gettext("projmod: ERROR: projectid %d is " .
	    "already in use. Choose another.\n"), $projid;
	unlink "$project_file.tmp";
	exit(9);
}

my $found = 0;	
while (<$original_fh>) {
	my ($this_project, $this_projid, $this_comment, $these_users,
	    $these_groups, $these_attributes) = split(/:/);
	if ($this_project eq $project) {
		$project = exists($opts{'l'}) ? $opts{'l'} : $this_project;
		$projid = exists($opts{'p'}) ? $opts{'p'} : $this_projid;
		$comment = exists($opts{'c'}) ? $comment : $this_comment;
		$users = exists($opts{'U'}) ? $users : $these_users;
		$groups = exists($opts{'G'}) ? $groups : $these_groups;
		my $line = "$project:$projid:$comment:$users:$groups:\n";
		if (length $line > 512) {
			printf STDERR gettext("projmod: ERROR: project " .
			    "entry exceeds 512 bytes.\n");
			$exitstatus = 10;
			$new_fh->print($_);
		} else {
			$new_fh->print($line);
			$exitstatus = 0;
		}
		$found++;
	} else {
		$new_fh->print($_);
	}
}

rename("$project_file.tmp", $project_file);

if ($found == 0) {
        printf STDERR gettext("projmod: ERROR: project %s not found.\n"),
	    $project;
	exit(6);
}

exit($exitstatus);




