#! /usr/bin/perl -w

#########################################################################################
#
#	pwadmin.pl: A simple CGI based .htpasswd editor
#
#	(c) copyright 2002 Rob Harvey / UKServers Ltd - rob@ukservers.net
#
#	Script supplied for use on http://www.virtualnames.co.uk/ hosting accounts
#
#	Do with this as you wish, but you must keep this copyright notice intact
#
#	This is a quick and nasty hack to let people edit their .htpasswd files over the web
#	No guarantee of any sort is given or should be implied - use this at your own risk!
#
#	Important: If you forget your admin password, you will need to either delete the
#	.htpasswd file -or- edit the file and delete the password entry for the admin user
#	This will force the program to re-prompt for a new password.
#
#
#	Version: 1.0	17/July/2002 RGH: First public release
#
#########################################################################################

use strict;
use CGI qw(:standard);
use CGI::Carp 'fatalsToBrowser';


#########################################################################################
# User configurable stuff
#########################################################################################

	# This is the full path to the .htpasswd file
	my $passwdfile = "/data/sites/web/b65pntb-hostingcom/web/.htpasswd";

	# The title displayed when you run this script
	my $title = "Password editor for winster.org";

	# The name of the admin user
	my $admin = 'pwadmin';


	# Simple style stuff here
	my $mystyle=<<END;
body {font-family:arial,helvetica;font-size:10pt}
END




#########################################################################################
# No user serviceable parts beyond this point...
#########################################################################################

    my $cgi = new CGI;

# Send http headers and start the page

    print header();
    print start_html(-title=>$title, -style=>{-code=>$mystyle});


# Read in the current password file - if there is one...

	my %passwords;
	&ReadPasswordFile;

# Log the admin user in if required

	if (-e $passwdfile and $passwords{$admin} and (!param('apw') 
					or $passwords{$admin} ne crypt(param('apw'), substr($passwords{$admin}, 0, 2)))) {

		print startform(-method=>"POST"),
				table({-border=>0},
	            	Tr([
    	            	th({-colspan=>2, bgcolor=>'#ccccff'}, ['Administrator Log-in']),

                		td([b('Password'), password_field(-name=>'apw', -size=>32)]),
                		td(['', submit(-name=>'action', -value=>'Login')])
		            ])
				),
        	    endform;
	
		print hr, <<EOF;
If you have forgotten your admin password, please consult the help information
EOF
	
		undef $cgi;
		exit;
	}


# Handle actions

	my $err;

	param('username') =~ /^\s*(\S+)\s*$/;
	my $user = $1;

	my $action = param('action');

	if (!$err and (($action eq 'Add/Edit user') or ($action eq 'Set admin password')) and $user) {

		$err = "Invalid username" if $user !~ /^[\w\-]+$/;
		$err = "Password too short" if length(param('v1')) < 4;
		$err = "Passwords do not match" if param('v1') ne param('v2');

		if (!$err) {
			my $exists = $passwords{$user};

			my @alphabet = ('a' .. 'z', 'A' .. 'Z', '0' .. '9', '.', '/');
  			my $salt = join ('', @alphabet[rand (64), rand (64)]);

			$passwords{$user} = crypt(param('v1'), $salt);
			&WritePasswordFile;

			print ("Edited password for: $user", br) if $exists and $user ne $admin;
			print (b("Warning").": Edited administrator password for: $user", br) if $exists and $user eq $admin;
			print ("Added new user: $user", br) if !$exists;

			param('apw', param('v1')) if $user eq $admin;
		}
	}

	if (!$err and $action eq 'Delete existing user' and $user) {
		delete $passwords{$user};
		&WritePasswordFile;
		print ("Deleted user: $user", br);
	}

	if (!$err) {
	    param(-name=>'username', -value=>"");
    	param(-name=>'v1', -value=>"");
	    param(-name=>'v2', -value=>"");
	}



# Display the editor form

    print h1($title);

	my @spacer = (Tr([td(['&nbsp;', '&nbsp;'])]));

	my @error;
	push @error, td(['', b(font({-color=>'red'}, "Error: ".$err))]) if $err;

	my @adduser;
	if (!$passwords{$admin}) {
		param('username', $admin);

		@adduser = (
			startform(-method=>"POST"), 
			hidden(-name=>'username', -default=>$admin),
			Tr([
				th({-colspan=>2, bgcolor=>'#ccccff'}, ["Please set a password for '$admin'"]),

				td([b('Password'), password_field(-name=>'v1', -size=>32)]),
				td([b('Confirm Password'), password_field(-name=>'v2', -size=>32)]),
				@error,

				td(['', submit(-name=>'action', -value=>'Set admin password')])
			]),
			endform

			);

	} else {
		@adduser = (
			startform(-method=>"POST"),
			hidden('apw'), 
			Tr([
				th({-colspan=>2, bgcolor=>'#ccccff'}, ['Add/Edit user']),

				td([b('Username'), textfield(-name=>'username', -size=>32)]),
				td([b('Password'), password_field(-name=>'v1', -size=>32)]),
				td([b('Confirm Password'), password_field(-name=>'v2', -size=>32)]),
				@error,

				td(['', submit(-name=>'action', -value=>'Add/Edit user')])
			]),
			endform

			);
	}

	my @deluser;

	my %dusers = %passwords;
	delete $dusers{$admin};

	push @deluser, (
			startform(-method=>"POST"),
			hidden('apw'),
            Tr([
				th({-colspan=>2, bgcolor=>'#ccccff'}, ['Delete existing user']),
                td([b('Username'), popup_menu(-name=>'username', -values=>['', sort(keys(%dusers))]) ]),
                td(['', submit(-name=>'action', -value=>'Delete existing user')])
            ]),
			endform) if keys(%passwords) > 1;
	

	print table({-border=>0},
			@adduser,
			@spacer,
			@spacer,
			@deluser
		);

	if (keys(%passwords) > 0) {

		print (br, scalar(keys(%passwords))-1, " user(s) in password file");

		if ($action eq 'Show sample .htaccess file') {

			print p, hr, h2("Sample .htaccess file");
			print table({-border=>0, -bgcolor=>'#ffffc0'}, 
				Tr(
					td(
						code(
'AuthName "My secure area"'.br.'
AuthType Basic'.br.'
AuthUserFile '.$passwdfile.br.'
require valid-user'.br
						)
					)
				)
			);

			print <<'EOF';
<p>
You will need to put the code above into a file called <i>.htaccess</i> in the directory you wish to protect.<br>
Ensure you upload the file in text/ascii mode and that it really is called .htaccess (ie: not .htaccess.txt for instance).
<p>
<b>Warning</b>: If you put the .htaccess file in your main domain directory, all files (including this script!) will be protected.<br>
- This may cause a problem if you haven't set up any passwords.
<hr>
EOF

		} else {
			print p, startform, submit(-name=>'action', -value=>'Show sample .htaccess file'), hidden('apw'), endform;
		}

		param('apw', '');
		print p, startform, submit(-name=>'action', -value=>'Logout'), hidden('apw'), endform;

	}

	undef $cgi;



# Subroutines

sub ReadPasswordFile {

	return unless -e $passwdfile;

	open FH, "< $passwdfile" or die "Unable to open $passwdfile: $!\n";
	while (<FH>) {
		chop;
		next if /^#/;
		next if /^\s*$/;
		my ($name, $pass) = split(/:/, $_, 2);
		$passwords{$name} = $pass;
	}
	close FH;
}

sub WritePasswordFile {

	open FH, "> $passwdfile" or die "Unable to write to $passwdfile: $!\n";

	print FH "# Updated using ".script_name()." on ".localtime." by $admin\n\n";

	foreach my $key (keys(%passwords)) {
		print FH $key, ":", $passwords{$key}, "\n";
	}

	close FH;
}


