#!/usr/bin/perl
# $Id: cvsdadm,v 1.35.2.6 2006/06/27 17:06:11 serge Exp $
#------------------------------------------------------------------------------
#  Copyright (c) 2006 Serge Gagnon <serge.gagnon@b2b2c.ca>
#  All rights reserved.
#  Copyright (c) 2001 Raymond M. Schneider <ray@thought.net>
#  All rights reserved.
#  Redistribution and use in source and binary forms, with or without
#  modification, are permitted provided that the following conditions
#  are met:
#
#  1. Redistributions of source code must retain the above copyright
#     notice, this list of conditions and the following disclaimer.
#  2. Redistributions in binary form must reproduce the above copyright
#     notice, this list of conditions and the following disclaimer in the
#     documentation and/or other materials provided with the distribution.
#  3. The name of author may not be used to endorse or promote products
#     derived from this software without specific prior written permission.
#
#  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
#  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
#  AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
#  THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
#  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
#  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
#  OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
#  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
#  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
#  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#------------------------------------------------------------------------------

#-----------------------------------------------------------------------------#
#		         Start of POD Documentation                           #
#-----------------------------------------------------------------------------#

=pod

=head1 NAME


cvsdadm - CVSd pserver administration program

=head1 SYNOPSIS


cvsdadm [-a] [-C] [-q] [-e] [-k] [-d] [-h] [-l]  [-r] [-w] [-v][-u userid] [-p password] [-R repository] [-s system_user_id]

=head1 DESCRIPTION


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


=head1 OPTIONS

=over

=item B<-i> interactive use, user provided with a menu

=item B<-a> add a cvs user

=item B<-C> Create mode need -R if not in interactive mode

=item B<-k> kill a cvs user

=item B<-d> disable a cvs user account

=item B<-e> enable a cvs user account

=item B<-h> print help and exit

=item B<-l> print a listing of all user

=item B<-q> query the repository about a user

=item B<-u> I<cvs_userid>

=item B<-p> I<cvs_user_passwd>

=item B<-s> I<system_user_cvs_user_maps_to>

=item B<-R> I<full_or_relative_path_to_repository>

=item B<-w> specify for cvs writers access

=item B<-r> specify for cvs readers access

=item B<-v> print version and exit

=back

=head2 EXAMPLES


In the following examples % is the command prompt. A short description
following the example describes whats going on in each.

% cvsdadm -i

This will start cvsdadm's interactive user menu. This will allow the
user to perform cvsdadm functions interactively.

% cvsdadm -q -u username

Allows the user to query the cvs repositories

% cvsdadm -C -R ./Ports

The above will create all the repository files in Ports directory but not
the Ports directory itself.

% cvsdadm -C -[adkeq] -u you -p password -R Ports

The above have two behavior. First, it will create the repository if it not already
created. Second if the repository is created but some files are missing, it will create
these files before any -adkeq operations. No file will be overwritten, so -C is not
dangerous for your existing repository!

% cvsdadm -i -C

The above is the same as the previous one but will work in interactive mode

% cvsdadm -a -u username -p userpasswd -R /repos -w

The above will add username with userpasswd to the cvs repository 
located at /repos with writers priviledges.

% cvsdadm -a -u username -p userpasswd -s systemusername -R /repos -w

The above will add username with userpasswd mapped to systemusername
in cvs repository /repos with writers priviledges.

NOTE: 
The above two commands can be executed with -r as well, instead 
of -w. If executed with -r, the cvs users priviledges will be
that of a cvs repository reader rather than writer.

% cvsdadm -k -u username -R /repos

This kills the cvs user username in the cvs repository /repos. Their
cvs account will no longer exist after this command is issued.

% cvsdadm -e -u username -R /repos

This command re-enables a disabled cvs user in cvs repository /repos.

% cvsdadm -d -u username -R /repos
	
This command disables a cvs user in cvs repository /repos.
	
% cvsdadm -l
	
This command will print a listing of all CVS user. If you want a full listing of
CVS user you could  make this command in a for loop like the example below (rc shell)
	
% for ( i in `{cvsdadm -l |grep -v '>')} {cvsdadm -q -u $i}

% cvsdadm -h

This command will display some cvsdadm help.

=head1 SEE ALSO


cvs(1) cvsd(8) cvsd.conf(5) cvsd-buildroot(8) cvsd-passwd(8)

=head1 AUTHOR


Serge Gagnon <serge.gagnon@b2b2c.ca>

Original author of cvspadm:
Raymond M. Schneider <ray@thought.net>
=head1 BUGS


Some. They can be viewed in the TODO file.


=cut

#-----------------------------------------------------------------------------#
#                          End of POD Documentation                           #
#-----------------------------------------------------------------------------#


#includes
use Getopt::Std;
use File::Copy;
#datadumper used for debugging
#use Data::Dumper;

$CVSDADM_VERSION = <<VER;

This is Cvsdadm, version 0.3

This project is a fork of cvsdadm
and it is maintained by Serge Gagnon

VER

#check to make sure that at least one flag was passed, else die and spit usage
if(@ARGV < 1) {
	die "Usage:\n cvsdadm [-i] [-q] [-C] [-a] [-e] [-l] [-h] [-k] [-d] [-r] [-w] [-u userid] [-p password] [-R repository] [-s system_user_id]\n";
}

#gather options
getopts('aiqChkeldrvwu:p:R:s:', \%args);

if ($args{v}) {die ($CVSDADM_VERSION);}

#see if we want a listing of all users in the repository
if ($args{l}) {
	$repos = &Init_repository();
		&List_users($repos);

}

#see if we want interactive use
if ($args{i}) {
	#ahh, we want interactive use, we shall disregard all other options
	#and go straight to interactive use
	&InteractiveCVSDADM();
}

#see if we are adding a user
if ($args{a}) {
	#so grasshopper, we want to add a cvs user. otay lets go for it.
	print "\nAttempting to add a user to CVS...\n";
	if (!$args{u} || !$args{p}) {
		print "Please supply a userid and password ...";
		exit(1);
	 }
	else {
		$repos= &Init_repository();
		$encpass = CVS_Crypt($args{p});
		&Addto_CVS_passwd($args{u},$encpass,$repos,$args{s});
		if($args{w}) {
			print "\nAttempting add to writers...\n";
			&Addto_CVS_writer($args{u},$repos);
		}
		if($args{r}) {
			print "\nAttempting to add to readers...\n";
			&Addto_CVS_reader($args{u}, $repos);
		}
	}
}

#see if we are querying the repository
if ($args{q}) {
	#oh yes, we want to see if so and so is in their or just see if
	#we can get a report on users and permissions.

	#if no other values, then we will prompt for them by calling
	#QueryRepos(), we need -u and -R to perform a query
	if (!$args{u}) {
		die "Must have specified a user.\n";
	}
	$repos = &Init_repository();
	print "==> Querying for $args{u} in repository $repos";
	&QueryRepos($args{u},$repos);
}

#if disabling a user
if ($args{d}) {
	#so you wish to strip some cvs users account. alrighty then...
	#check to see if -u and -R defined, 
	print "\nAttempting to disable a user in CVS...\n";
	if (!$args{u}) {
		print "Please supply a userid.\n";
		exit(1);
	}
	else {
		$repos= &Init_repository();
		&Disable_cvsuser($args{u}, $repos);
	}
}

#if enabling a user
if ($args{e}) {
	#enabling a user well lets start calling the appropriate routines
	#check to see if -u and -R defined
	print "\nAttempting to enable a user in CVS...\n";
	if (!$args{u}) {
		print "Please supply a userid.\n";
		exit(1);
	}
	else {
		$repos= &Init_repository();
		&Enable_cvsuser($args{u}, $repos);
	}
}

#if killing a user
if ($args{k}) {
	#check to see if we have -u -R defined
	if (!$args{u}) {
		die "Need -u defined when using -k";
	}
	$repos= &Init_repository();
	&Kill_cvsaccount($args{u}, $repos);
}

#looking for help
if ($args{h}) {
	#disregard any other options and call to help...
	&Help();

}

#see if we are creating a new repository
if ($args{C}) {
	if(!$args{R} && !$args{a} && !$args{d} && !$args{e} && !$args{k} && !$args{q}) {
		print "Please supply a repository ...\n";
		exit (1);
	} else {
	$repos= &Init_repository();
		if (!$args{a} && !$args{d} && !$args{e} && !$args{k} && !$args{q}) {
#		print "Repository $repos successfully created.\n";
		}
	}
}

#catch another goof on the part of a user...forgeting to -a
if (($args{r} || $args{w}) && !$args{a}) {
	die "Must specify -a with use of -r or -w.\n";
}

#-----------------------------------------------------------------------------#
# 				Subroutines                                   #
#-----------------------------------------------------------------------------#

sub InteractiveCVSDADM {
	#ok, so you're not the commandline type, you gotta have interaction eh?
	#here ya go... =)
	MENU:
 	&Print_Interactive_Menu();
	print "[a,d,e,k,l,q,h,Q] ";
	chomp($choice = <STDIN>);
	if ($choice eq "") {
		goto MENU;
	}
	if ($choice eq "Q") {
		print "Thanks for using cvsdadm.\n";
		exit(0);
	}
	if ($choice eq "h") {
		goto MENU;
	}
	if ($choice eq "d") {
		#disable a user
		DISABLE:
		print "Disable CVS User\n";
		print "----------------\n";
		print "Enter userid: ";
		chomp(my $user = <STDIN>);
		if ($user eq "") { goto DISABLE;}
		print "Enter Repository: ";
		chomp(my $repos = <STDIN>);
		if ($repos eq "") { goto DISABLE;}
		print "\nYou entered the following:\n";
		print "Userid: $user\n";
		print "Repos : $repos\n";
		print "Is this correct? (y)es (n)o ";
		chomp($ans = <STDIN>);
		if ($ans eq 'n') {
			print "Would you like to (a)bort or (r)etry? ";
			chomp($abortretry = <STDIN>);
				if ($abortretry eq 'a') {
					goto MENU;
				}
				if ($abortretry eq 'r') {
					goto DISABLE;
				}
			goto MENU;
		}
		if ($ans eq 'y') {
			&Init_repository($repos);
			&Disable_cvsuser($user,$repos);
		}
	goto MENU;
	}
	
	if ($choice eq "e") {
                #enable a user
                ENABLE:
                print "Enable CVS User\n";
                print "---------------\n";
                print "Enter userid: ";
                chomp(my $user = <STDIN>);
                if ($user eq "") { goto ENABLE;}
                print "Enter Repository: ";
                chomp(my $repos = <STDIN>);
                if ($repos eq "") { goto ENABLE;}
                print "\nYou entered the following:\n";
                print "Userid: $user\n";
                print "Repos : $repos\n";
		print "Is this correct? (y)es (n)o ";
		chomp($ans = <STDIN>);
		if ($ans eq 'n') {
			print "Would you like to (a)bort or (r)etry? ";
			chomp($abortretry = <STDIN>);
				if ($abortretry eq 'a') {
					goto MENU;
				}
				if ($abortretry eq 'r') {
					goto ENABLE;
				}
			goto MENU;
		}
		if ($ans eq 'y') {
			&Init_repository($repos);
			&Enable_cvsuser($user,$repos);
                }
        goto MENU;
        }

	if ($choice eq "k") {
		#kill a cvs user, remove altogether
                KILLER:
                print "Kill a CVS User\n";
                print "---------------\n";
                print "Enter userid: ";
                chomp(my $user = <STDIN>);
                if ($user eq "") { goto KILLER;}
                print "Enter Repository: ";
                chomp(my $repos = <STDIN>);
                if ($repos eq "") { goto KILLER;}
                print "\nYou entered the following:\n";
                print "Userid: $user\n";
                print "Repos : $repos\n";
		print "Is this correct? (y)es (n)o ";
		chomp($ans = <STDIN>);
		if ($ans eq 'n') {
			print "Would you like to (a)bort or (r)etry? ";
			chomp($abortretry = <STDIN>);
				if ($abortretry eq 'a') {
					goto MENU;
				}
				if ($abortretry eq 'r') {
					goto KILLER;
				}
			goto MENU;
		}
                if ($ans eq 'y') {
			&Init_repository($repos);
			&Kill_cvsaccount($user,$repos);
                }
        goto MENU;
        }

	if ($choice eq "q") {
		#Query a cvs user in the repository
                QUERY:
                print "Query a CVS User\n";
                print "---------------\n";
                print "Enter userid: ";
                chomp(my $user = <STDIN>);
                if ($user eq "") { goto QUERY;}
                print "Enter Repository: ";
                chomp(my $repos = <STDIN>);
                if ($repos eq "") { goto QUERY;}
                print "\nYou entered the following:\n";
                print "Userid: $user\n";
                print "Repos : $repos\n";
		print "Is this correct? (y)es (n)o ";
		chomp($ans = <STDIN>);
		if ($ans eq 'n') {
			print "Would you like to (a)bort or (r)etry? ";
			chomp($abortretry = <STDIN>);
				if ($abortretry eq 'a') {
					goto MENU;
				}
				if ($abortretry eq 'r') {
					goto QUERY;
				}
			goto MENU;
		}
                if ($ans eq 'y') {
			&Init_repository($repos);
			&QueryRepos($user,$repos);
                }
        goto MENU;
        }

	if ($choice eq 'l') {
		#List all CVS user
		LIST:
		print "List all CVS user\n";
		print "---------------\n";
		print "Enter Repository: ";
		chomp(my $repos = <STDIN>);
		print "\nYou entered the following:\n";
		print "Repos: $repos\n";
		print "Is this correct? (y)es (n)o ";
		chomp($ans = <STDIN>);
		if ($ans eq 'n') {
			print "Would you like to (a)bort or (r)etry? ";
			chomp($abortretry = <STDIN>);
			if ($abortretry eq 'a') {
				goto MENU;
			}
			if ($abortretry eq 'r') {
				goto LIST;
			}
			goto MENU;
		}
		if ($ans eq 'y') {
			&Init_repository($repos);
			&List_users($repos);
		}
	goto MENU;
	}
	
	if ($choice eq 'a') {
		#then we need -u -p -R -w or -r and possibly -s defined
		AGAIN:
		print "Enter a Userid: ";
		chomp(my $user = <STDIN>);
		if ($user eq "") { 
			print "Not a legal User. Try again.\n";
			goto AGAIN;
		}
		PASS:
		print "Enter a Passwd: ";
		chomp(my $pass = <STDIN>);
		if ($pass eq "") {
			print "Not legal passwd. Try again.";
			goto PASS;
		}
		print "Press enter if No System Userid is needed.\n";
		print "Enter a system UserID: ";
		chomp (my $sysuser = <STDIN>);
		print "Enter a Repos: (full or relative path): ";
		chomp(my $repos = <STDIN>);
		PERMS:
		print "Enter permissions(w\|r): ";
		chomp(my $perms = <STDIN>);
		if (!$perms eq "w" | !$perms eq "r") { 
			print "Not legal perms. Try again.";
			goto PERMS;
		}
		print "\nYou entered the following:\n";
		print "Userid: $user\n";
		print "Passwd: $pass\n";
		if ($sysuser eq ""){ 
			print "No system userid specified.\n";
		}
		else {
			print "SysUserID: $sysuser\n";
		}
		print "Repos : $repos\n";
		print "Perms : $perms\n";
		print "Is this correct? (y)es (n)o ";
		chomp($ans = <STDIN>);
		if ($ans eq 'n') {
			print "Would you like to (a)bort or (r)etry? ";
			chomp($abortretry = <STDIN>);
				if ($abortretry eq 'a') {
					goto MENU;
				}
				if ($abortretry eq 'r') {
					goto AGAIN;
				}
			goto MENU;
		}
		if ($ans eq 'y') {
			my $encpass = CVS_Crypt($pass);
			&Init_repository($repos);
			&Addto_CVS_passwd($user,$encpass,$repos,$sysuser);
			if($perms eq 'w') {
                	        print "\nAttempting add to writers...\n";
                                &Addto_CVS_writer($user,$repos);
			}
                	if($perms eq 'r') {
                        	print "\nAttempting to add to readers...\n";
                                &Addto_CVS_reader($user, $repos);
                        }
                }
		goto MENU;
        }
	goto MENU;
}

sub Print_Interactive_Menu {
	print <<ENDOFMENU;

cvsdadm Menu
------------
Option	\| Description
-------------------------------------
a	\| Add a CVS user
d	\| Disable a CVS user
e	\| Enable a disabled CVS user
k	\| Kill(Remove) a CVS user
l	\| List all CVS user
q	\| Query a CVS user
h	\| Prints this Menu
Q	\| Quit

ENDOFMENU
}

sub List_users {
	my $repos = shift(@_);
	open (CVSPASSWD, "$repos/CVSROOT/passwd") ||
		die "Cannot open $repos/CVSROOT/passwd:$!\n";
		print "==> Print a listing of all users in $repos repository\n";
	while (<CVSPASSWD>) {
		@listing = split(/:/, $_);
		print "$listing[0]\n";
		next;
	}
	close(CVSPASSWD);

}

sub QueryRepos {
	#In querying we are going to take a user and repos as args...
	#then we are going to see if they have an account, then report
	#on the users perms and if they are mapped to a system user.
	my ($user, $repos) = @_;
 	my $pfile = "passwd";
	my $wfile = "writers";
	my $rfile = "readers";
	
	#first lets check for a user match in passwd
	print "\n";
	&Check_CVS($user,$repos,$pfile);
	&Check_CVS($user,$repos,$wfile);
	&Check_CVS($user,$repos,$rfile);
}

sub Check_CVS {
	my($user, $repos, $cvsfile, $silent) =  @_;
	print "Querying $cvsfile...\n";
        open (CVSFILE, "$repos/CVSROOT/$cvsfile") ||
		die "Cannot open $repos/CVSROOT/$cvsfile:$!\n";
        while (<CVSFILE>){
                if ($_ =~/$user/){
			if ($cvsfile eq "passwd" && $_ =~/^$user:/) {
				if ($silent ne "") {
					return "exist";
				} else {
                        		print "$user exists in $cvsfile\n";
				@vals = split(/:/, $_);
				$vals[2] =~ s/\n//;
				if ($vals[2]){
				 print "$user maps to system user $vals[2]\n";
				}
				if ($_ =~/.*:\*.*/) {
					print "$user is DISABLE\n";
				} else {
					print "$user is ENABLE\n";
				}
			}
		}
			if ($cvsfile eq "writers" && $_ =~/^$user$/) {
				if ($silent ne "") {
					return "exist";
				} else {
					print "$user has read/write permissions\n";
				}
			}
			if ($cvsfile eq "readers" && $_ =~/^$user$/) {
				if ($silent ne "") {
					return "exist";
				} else {
					print "$user has read only permissions\n";
				}
			}
                }
                else {
                        next;
                }
        }
	print "\n";
        close(CVSFILE);
}

sub Addto_CVS_passwd {
	my($user,$pass,$repos,$sysuser) = @_;
	if ($sysuser eq "root") {
		print "Sorry, cvsdadm will _not_ allow mapping of cvs";
		print "\nusers to the root superuser.\n";
		exit(1);
	}
	if (&Check_CVS($user, $repos, passwd, if_exist) eq "exist")
		{print "\n==>User $user ALREADY exist in passwd file\n";
	}else {
		open(CVSPWD,">>$repos/CVSROOT/passwd") ||
			die "Cannot open $repos/CVSROOT/passwd:$!\n";
		if($sysuser) {
			print CVSPWD "$user:$pass:$sysuser\n";
		}
		else {
			print CVSPWD "$user:$pass\n";
		}
		print "\n==> Successfully added $user to $repos/CVSROOT/passwd\n";
		close(CVSPWD);
	}
}

sub Removefrom_CVS_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.new:$!\n";
	while(<PWD>) {
		next if $_ =~ /^\s*($|#)/;
		@vals = split(/:/,$_);
		push @users, $_;
		next if $vals[0] ne $user;
		pop @users;
	}
	while(@users) {
		$entry = pop @users;
		print NPWD $entry;
	}
	close(PWD);
	close(NPWD);
	copy("$repos/CVSROOT/passwd", "$repos/CVSROOT/passwd.bak");
        move("$repos/CVSROOT/passwd.new", "$repos/CVSROOT/passwd");
        print "\n==> Successfully removed user.\n";
}

sub Remove_CVS_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 "\n==> $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 Remove_CVS_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 "\n==> $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 Addto_CVS_writer {
        my ($user, $repos) = @_;
	if (&Check_CVS($user, $repos, writers, if_exist) eq "exist") {
		print "\n==>User $user ALREADY exist in writers file\n";
	} else {
		open(WF,">>$repos/CVSROOT/writers") ||
			die "Cannot open $repos/CVSROOT/writers:$!\n";
		print WF "$user\n";
		print "\n==> Successfully added $user to $repos/CVSROOT/writers\n";
		close (WF);
	}
}

sub Addto_CVS_reader {
        my ($user, $repos) = @_;
	if (&Check_CVS($user, $repos, readers, if_exist) eq "exist") {
		print "\n==>User $user ALREADY exist in reader file\n";
	} else {
		open(RF,">>$repos/CVSROOT/readers") ||
			die "Cannot open $repos/CVSROOT/readers:$!\n";
		print RF "$user\n";
		print "\n==> Successfully added $user to $repos/CVSROOT/readers\n";
		close (RF);
	}
}

sub Disable_cvsuser {
        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.new:$!\n";
        while (<PWD>) {
                next if $_ =~ /^\s*($|#)/;
                @vals = split(/:/,$_);
                push @users, $_;
                next if $vals[0] ne $user;
                @vals2 = split(/:/, pop @users);
		if ($vals2[1] =~ /\*.*/) {
			print "\n==> User $user ALREADY disable\n";
		} else {
			$vals2[1] =~ s/$vals[1]/\*$vals2[1]/;
        			print "==> Successfully disabled $user\n";
		}
			if($vals2[2]) {
			$userentry = "$vals2[0]:$vals2[1]:$vals2[2]";
			}
			else { # there is no systemuser being mapped so its just 
		       # user:passwd
			$userentry = "$vals2[0]:$vals2[1]";
		}
                push @users, $userentry;
        }
        while(@users) {
                $entry = pop @users;
                print NPWD $entry;
        }
        close(PWD);
        close(NPWD);
        copy("$repos/CVSROOT/passwd", "$repos/CVSROOT/passwd.bak");
        move("$repos/CVSROOT/passwd.new", "$repos/CVSROOT/passwd");
}

sub Enable_cvsuser {
        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*($|#)/;
                @vals = split(/:/,$_);
                push @users, $_;
                next if $vals[0] ne $user;
                @vals2 = split(/:/, pop @users);
                $vals2[1] =~ s/\*(.*)/$1/;
		if($vals2[2]) {
			$userentry = "$vals2[0]:$vals2[1]:$vals2[2]";
		}
		else { #this is no system user being mapped
			$userentry = "$vals2[0]:$vals2[1]";
		}
                push @users, $userentry;
        }
        while(@users) {
                $entry = pop @users;
                print NPWD $entry;
        }
        close(PWD);
        close(NPWD);
        copy("$repos/CVSROOT/passwd", "$repos/CVSROOT/passwd.bak");
        move("$repos/CVSROOT/passwd.new", "$repos/CVSROOT/passwd");
        print "\n==> Successfully enabled $user\n";

}

sub Kill_cvsaccount {
	#total anihilation of a cvs user...there is no e s c a p e.... =)
	my($user,$repos) = @_;
	&Removefrom_CVS_passwd($user,$repos);
	&Remove_CVS_writer($user,$repos);
	&Remove_CVS_reader($user,$repos);
	print "\n==> $user completely removed from $repos\n";
}

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 Init_repository {
	my $repos = shift(@_);
	if ($repos eq "") {
		if (!$args{R}) {
			$repos= &Is_cwd();
		} else {
		$repos= $args{R};
		}
	}
	if ( &Is_sane($repos) eq "OK") {
		return $repos;
	}
}

# see if the repository is in the cwd
sub Is_cwd {
	if (! opendir(CVSROOT, "./CVSROOT")) {
		die "Must have specified a repository or cd into a repository.\n";
	} else {
		closedir(CVSROOT);
		return (".");
	}
}

# see if repository is sane
sub Is_sane {
	my $repos = shift(@_);
	if (! opendir(CVSROOT, "$repos/CVSROOT")) {
		if($args{C}) {
			mkdir("$repos/CVSROOT") ||
			die "Cannot create $repos/CVSROOT:$!\n";
		} else {
			die "Repository not sane: $repos/CVSROOT not found.\nTry using cvsdadm with -C\n";
		}
	} else {
		closedir(CVSROOT);
	} 
	if (! open(WR, "$repos/CVSROOT/writers")) {
		if($args{C}) {
			open(WR, ">>$repos/CVSROOT/writers") || 
			die "Cannot create $repos/CVSROOT/writers:$!\n";
			close(WR);
		} else {
			die "Repository not sane: $repos/CVSROOT/writers not found.\nTry using cvsdadm with -C\n";
		}
	} else {
		close(WR);
	} 
	if (! open(PW, "$repos/CVSROOT/passwd")) {
		if($args{C}) {
			open(PW, ">>$repos/CVSROOT/passwd") || 
			die "Cannot create $repos/CVSROOT/passwd:$!\n";
			close(PW);
		} else {
			die "Repository not sane: $repos/CVSROOT/passwd not found.\nTry using cvsdadm with -C\n";
		}
	} else {
		close(PW);
	} 
	if (! open(RE, "$repos/CVSROOT/readers")) {
		if($args{C}) {
			open(RE, ">>$repos/CVSROOT/readers") || 
			die "Cannot create $repos/CVSROOT/readers:$!\n";
			close(RE);
		} else {
			die "Repository not sane: $repos/CVSROOT/readers not found.\nTry using cvsdadm with -C\n";
		}
	} else {
		close(RE);
	}
	return ("OK");
}
	
sub Help {
	#print out some standard help information
	print <<ENDOFHELP;
cvsdadm help
------------

cvsdadm
Copyright 2006 Serge Gagnon <serge.gagnon\@b2b2c.ca>
All rights reserved.

NAME
        cvsdadm - CVSd pserver administration program
SYNOPSIS
        cvsdadm [-a] [-C] [-q] [-e] [-k] [-d] [-h] [-l]  [-r] [-w] [-v][-u userid] [-p password]
                [-R repository] [-s system_user_id]
DESCRIPTION
        cvsdadm is a tool to assist CVSd administrators in the user admin
        of the CVSROOT/passwd CVSROOT/readers CVSROOT/writers files when
        pserver authentication is being used for the repository.
OPTIONS
        -i -- interactive use, user provided with a menu
        -a -- add a cvs user
        -C -- Create mode need -R if not in interactive mode
        -k -- kill a cvs user
        -d -- disable a cvs user account
        -e -- enable a cvs user account
        -h -- print help and exit
        -l -- print a listing of all user
        -q -- query the repository about a user
        -u <cvs_userid>
        -p <cvs_user_passwd>
        -s <system_user_cvs_user_maps_to>
        -R <full_or_relative_path_to_repository>
        -w -- specify for cvs writers access
        -r -- specify for cvs readers access
        -v -- print version and exit
EXAMPLES
        In the following examples $ is the command prompt. A short description
        following the example describes whats going on in each.
            % cvsdadm -i

            This will start cvsdadm's interactive user menu. This will allow the
            user to perform cvsdadm functions interactively.

            % cvsdadm -q -u username

            Allows the user to query the cvs repositories

            % cvsdadm -C -R ./Ports

            The above will create all the repository files in Ports directory but not
            the Ports directory itself.

            % cvsdadm -C -[adkeq] -u you -p password -R Ports

            The above have two behavior. First, it will create the repository if it not already
            created. Second if the repository is created but some files are missing, it will create
            these files before any -adkeq operations. No file will be overwritten, so -C is not
            dangerous for your existing repository!

            % cvsdadm -i -C

            The above is the same as the previous one but will work in interactive mode

            % cvsdadm -a -u username -p userpasswd -R /repos -w

            The above will add username with userpasswd to the cvs repository 
            located at /repos with writers priviledges.

            % cvsdadm -a -u username -p userpasswd -s systemusername -R /repos -w

            The above will add username with userpasswd mapped to systemusername
            in cvs repository /repos with writers priviledges.

            NOTE: 
            The above two commands can be executed with -r as well, instead 
            of -w. If executed with -r, the cvs users priviledges will be
            that of a cvs repository reader rather than writer.

            % cvsdadm -k -u username -R /repos

            This kills the cvs user username in the cvs repository /repos. Their
            cvs account will no longer exist after this command is issued.

            % cvsdadm -e -u username -R /repos

            This command re-enables a disabled cvs user in cvs repository /repos.

            % cvsdadm -d -u username -R /repos
        
            This command disables a cvs user in cvs repository /repos.
        
            % cvsdadm -l
        
            This command will print a listing of all CVS user. If you want a full listing of
            CVS user you could  make this command in a for loop like the example below (rc shell)
        
            % for ( i in `{cvsdadm -l |grep -v '>')} {cvsdadm -q -u $i}

            % cvsdadm -h

            This command will display some cvsdadm help.

SEE ALSO
        cvs(1) cvsd(8) cvsd.conf(5) cvsd-buildroot(8) cvsd-passwd(8)
AUTHOR
        Serge Gagnon <serge.gagnon\@b2b2c.ca>
        
        Original author of cvspadm:
	Raymond M. Schneider <ray\@hackfoo.net>
BUGS
        Some. They can be viewed in the TODO file.

ENDOFHELP
}
