#!/usr/local/bin/perl

#
# Copyright (c) 2001 Raymond M Schneider <ray@hackfoo.net>
# All Rights Reserved. Please see LICENSE file.
#

=pod

=head1 cvspadm

=begin text

Copyright 2001 Raymond M Schneider <ray@hackfoo.net>
All rights reserved.

NAME
	cvspadm - CVS pserver administration program

SYNOPSIS
	cvspadm [-a] [-e] [-k] [-d] [-r] [-w] [-u userid] [-p password]
	       [-R repository]

DESCRIPTION
	cvspadm is a tool to assist CVS administrators in the user admin
        of the CVSROOT/passwd CVSROOT/readers CVSROOT/writers files when
        pserver authentication is being used for the repository.

	It currently does _not_ handling mapping cvs accounts to real
        system userid's. This could be added trivially, but I ran out
        time.

OPTIONS
	-u <userid>
	-p <password>
	-R <path_to_repository>
	-w -- for writers access
	-r -- for readers access
	-d -- disable user acct
	-k -- kill user acct
	-e -- enable user acct
	-a -- add a cvs user
	
SEE ALSO
	cvs(1)

AUTHOR
	Raymond M Schneider <ray@hackfoo.net>

BUGS
	cvspadm doesnt handle mappings to system userids currently. This
        is something that does come in handy, it should get added.

=end text

=cut

#includes
use Getopt::Std;
use File::Copy;
use Data::Dumper;

if (@ARGV < 1){
	die "Usage: cvspadm -u <userid> -p <passwd> -R <repository> -w|-r|-d|-k|-e|-a\n";
}

#
# Parsing arguments ;)
#
getopts('u:p:R:wrdkea', \%args);

#
# if this is being used to add a user to cvs
#
if ($args{a}) {
	print "\nAttempting to add a user to CVS...";
	if (!$args{u} || !$args{p} || !$args{R}) {
		print "Please supply a userid, password and repository...";
		exit(1);
	}
	else {
		$encpass = CVS_Crypt($args{p});
		&Add_to_passwd($args{u},$encpass,$args{R});
		if($args{w}) {
	        	print "\nAttempting add to writers...";
       	 		if(!$args{u} || !$args{R}) {
                		print "Please supply a userid and repository.\n";
                		exit(1);
        		}
        		else {
                		&Add_writer($args{u},$args{R});
        		}
		}
		if($args{r}) {
			print "\nAttempting to add to readers...";
			if(!$args{u} || !$args{R}) {
				print "Please supply a userid and repository.\n";
				exit(1);
			}
			else {
				&Add_reader($args{u}, $args{R});
			}
		}
	}
}

#
# if this is being used to kill a cvs user
#
if ($args{k}) {
	print "\nAttempting to kill a user in CVS...";
	if(!$args{u} || !$args{R}) {
		print "Please supply a userid and repository.\n";
		exit(1);
	}
	else {
		&Remove_cvsuser($args{u}, $args{R});
	}
}


#
# if this is being used to disable a cvs user
#
if ($args{d}) {
	print "\nAttempting to disable a user in CVS...";
	if (!$args{u} || !$args{R}) {
		print "Please supply a userid and repository.\n";
		exit(1);
	}
	else {
		&Disable_cvsuser($args{u}, $args{R});
	}
}

#
# if this is being used to enable a cvs user
#
if ($args{e}) {
	print "\nAttempting to enable a user in CVS...";
	if (!$args{u} || !$args{R}) {
		print "Please supply a userid and repository.\n";
		exit(1);
	}
	else {
		&Enable_cvsuser($args{u}, $args{R});
	}
}

#
# Subroutines
#

sub CVS_Crypt{
	my $pw = shift(@_);
	my @char = ('a'..'z','A'..'Z','0'..'9');
	srand (localtime());
	my $salty = $char[rand(32)]. $char[rand(32)];
	print "Password encrypted...\n";
	return (crypt($pw,$salty));
}

sub Check_file{
	my($user, $repos, $cvsfile) =  @_;
	open (CVSFILE, "$repos/CVSROOT/$cvsfile") || die "Cannot open $repos/CVSROOT/passwd:$!\n";
	while (<CVSFILE>){
		if ($_ =~/$user/){
			print $_ if $_ =~/$user/;
			print "\nUser already exists\n";
			exit(1);
		}
		else {
			next;
		}
	}
	close(CVSFILE);
		
}

sub Add_to_passwd{
	my ($user, $pass, $repos) = @_;
	open(CVSPWD,">>$repos/CVSROOT/passwd") || die "Cannot open $repos/CVSROOT/passwd:$!\n";
 	print CVSPWD "$user:$pass\n";
	print "Successfully added $user to $repos/CVSROOT/passwd\n";
	close (CVSPWD);	
}

sub Remove_from_passwd{
	my($user, $repos) = @_;
	@users;
	open(PWD,"$repos/CVSROOT/passwd") || die "Cannot open $repos/CVSROOT/passwd:$!\n";
	open(NPWD, ">>$repos/CVSROOT/passwd.new") || die "Cannot open $repos/CVSROOT/passwd:$!\n";
	while (<PWD>) {
		next if $_ =~ /^\s*($|#)/;
		($u, $p) = split(/:/,$_);
		push @users, $_;
		next if $u ne $user;
		pop @users;
	}
	while(@users) {
		$e = pop @users;
		print NPWD $e;
	}
	close(PWD);
	close(NPWD);
	copy("$repos/CVSROOT/passwd", "$repos/CVSROOT/passwd.bak");
	move("$repos/CVSROOT/passwd.new", "$repos/CVSROOT/passwd");
	print "Successfully removed user.\n";
}

sub Add_writer{
	my ($user, $repos) = @_;
	open(WF,">>$repos/CVSROOT/writers") || die "Cannot open $repos/CVSROOT/writers:$!\n";
	print WF "$user\n";
	print "Successfully added $user to $repos/CVSROOT/writers\n";
	close (WF);

}

sub Remove_writer{
	my($user, $repos) = @_;
	@users;
	open(WF,"$repos/CVSROOT/writers") || die "Cannot open $repos/CVSROOT/writers:$!\n";
	open(NWF,">>$repos/CVSROOT/writers.new") || die "Cannot open $repos/CVSROOT/writers.new:$!\n";
	while(<WF>) {
		next if $_ =~ /^\s*($|#)/;
		push @users, $_;
		chomp($_);
		next if $_ ne $user;
		pop @users;
		print "$user found in writers, removed.\n";
	}
	while(@users){
		$e = pop @users;
		print NWF $e;
	}
	close(WF);
	close(NWF);
	copy("$repos/CVSROOT/writers","$repos/CVSROOT/writers.bak");
	move("$repos/CVSROOT/writers.new","$repos/CVSROOT/writers");

}

sub Add_reader{
        my ($user, $repos) = @_;
        open(RF,">>$repos/CVSROOT/readers") || die "Cannot open $repos/CVSROOT/readers:$!\n";
        print RF "$user\n";
        print "Successfully added $user to $repos/CVSROOT/readers\n";
        close (RF);


}

sub Remove_reader{
        my($user, $repos) = @_;
        @users;
        open(RF,"<$repos/CVSROOT/readers") || die "Cannot open $repos/CVSROOT/readers:$!\n";
        open(NRF,">>$repos/CVSROOT/readers.new") || die "Cannot open $repos/CVSROOT/readers.new:$!\n";
        while(<RF>) {
                next if $_ =~ /^\s*($|#)/;
                push @users, $_;
                chomp($_);
                next if $_ ne $user;
                pop @users;
                print "$user found in readers, removed.\n";
        }
        while(@users){
                $e = pop @users;
                print NRF $e;
        }
        close(RF);
        close(NRF);
        copy("$repos/CVSROOT/readers","$repos/CVSROOT/readers.bak");
        move("$repos/CVSROOT/readers.new","$repos/CVSROOT/readers");


}

sub Disable_cvsuser{
        my($user, $repos) = @_;
	@users;
        open(PWD,"$repos/CVSROOT/passwd") || die "Cannot open $repos/CVSROOT/pas
swd:$!\n";
        open(NPWD, ">>$repos/CVSROOT/passwd.new") || die "Cannot open $repos/CVSROOT
/passwd:$!\n";
        while (<PWD>) {
                next if $_ =~ /^\s*($|#)/;
                ($u, $p) = split(/:/,$_);
                push @users, $_;
                next if $u ne $user;
		($u2,$p2) = split(/:/, pop @users);
		$p2 =~ s/$p2/\*$p2/;
		$userentry = "$u2:$p2";
		push @users, $userentry;
        }
        while(@users) {
                $e = pop @users;
                print NPWD $e;
        }
        close(PWD);
        close(NPWD);
        copy("$repos/CVSROOT/passwd", "$repos/CVSROOT/passwd.bak");
        move("$repos/CVSROOT/passwd.new", "$repos/CVSROOT/passwd");
        print "Successfully disabled $user\n";

}

sub Enable_cvsuser{
        my($user, $repos) = @_;
        @users;
        open(PWD,"$repos/CVSROOT/passwd") || die "Cannot open $repos/CVSROOT/pas
swd:$!\n";
        open(NPWD, ">>$repos/CVSROOT/passwd.new") || die "Cannot open $repos/CVSROOT
/passwd:$!\n";
        while (<PWD>) {
                next if $_ =~ /^\s*($|#)/;
                ($u, $p) = split(/:/,$_);
                push @users, $_;
                next if $u ne $user;
                ($u2,$p2) = split(/:/, pop @users);
                $p2 =~ s/\*(.*)/$1/;
                $userentry = "$u2:$p2";
                push @users, $userentry;
        }
        while(@users) {
                $e = pop @users;
                print NPWD $e;
        }
        close(PWD);
        close(NPWD);
        copy("$repos/CVSROOT/passwd", "$repos/CVSROOT/passwd.bak");
        move("$repos/CVSROOT/passwd.new", "$repos/CVSROOT/passwd");
        print "Successfully enabled $user\n";


}

sub Remove_cvsuser{
	#total removal of user requires the following subroutines to be run.
	my($user,$repos) = @_;
	&Remove_from_passwd($user,$repos);
	&Remove_writer($user,$repos);
	&Remove_reader($user,$repos);
	print "$user completely removed from $repos\n";
	
}
