#!/usr/bin/perl 
#
# $Id: htpasswd.pl,v 1.1 2004-03-28 23:17:30 ueli Exp $
# 
# (c) 2003 by ueli@heuer.org
#
# 
use lib "/home/default/includes";

use CGI qw/:standard/;
use CGI::Pretty qw( :html4 );
use Crypt::Cracklib;
use DBI;


require "htpasswd.inc";

# DB-username/passwort dieser user MUSS schreibrechte haben
# htpasswd.inc defines $dbsource, $dbuser and $dbpassword
# $dbsource should be a DBI-Resource 
# --- htpasswd.inc ---
# as an Example you may use following:
#$dbuser='htadmin';
#$dbhost='localhost';
#$dbpassword='geheim';
#$dbname='htpasswd';
#$dbsource = "DBI:mysql:database=$dbname;host=$dbhost:";
#1;
# --- eof htpasswd.inc ---


# wo liegt die server config (ohne $includes !!!)
$SERVER_CONF='/etc/httpd2/httpd.conf';
$CRACKLIB = '/var/cache/cracklib/cracklib_dict';

$server_ip=$ENV{'SERVER_ADDR'};
if ($server_ip =~ /:/) {
	$server_ip = "[$server_ip]";
}

$db = DBI->connect( $dbsource, $dbuser, $dbpassword) || do { print header(-Location=>"http://$server_ip/htpasswd/nodb.php"); exit(0); };

# fetch some global values
($server,$domain)=get_servername(server_name());
$master_user = remote_user();
$script = script_name();

$user=param('user');
$password=param('password');
$host_group=get_host_group();

if ( $master_user eq '' ) { 
	print header,h1("zu dumm, das geht einfach nicht ohne username und passwort");
	exit(0); 
} 

# prepare some queries ...
#

$check_member_h = $db->prepare("select user_name from user_group where user_name = ? and user_group = ? and host_group = ?");
$user_h = $db->prepare("select user_name,isadmin from user_info where host_group = ? order by isadmin desc,user_name");
$is_master_h = $db->prepare("select isadmin from user_info,host_info where user_info.host_group=host_info.host_group and host=? AND user_name = ?");
$get_password_h = $db->prepare("select user_passwd from user_info,host_info where user_info.host_group=host_info.host_group and host=? AND user_name = ?");
$check_user_h = $db->prepare ("select user_name from user_info where user_name = ? and host_group = ?");

$|=1;

# do ddatabase jobs bevor any header is sent ...
if (param) {
	$doit = param('doit');
	$salt = sprintf("%c%c",int(rand(95))+33,int(rand(95))+33);
	if ($doit eq 'saveuser') {
		$old_passwd = get_password($user);
		if ( is_admin($master_user) ) {
			if ( param('delete') == 1 ) {
				# delete user from user and group table
				if ( $user ne $master_user ) {
					# delete only "other" users, NOT myself!
					$delete_user_h = $db->prepare ("delete from user_info where user_name = ? and host_group = ?");
					$delete_group_member_h = $db->prepare ("delete from user_group where user_name = ? and host_group = ?");
					$delete_user_h->execute($user,$host_group);
					$delete_group_member_h->execute($user,$host_group);
					print header(-Location=>"$script?action=deleted&user=$user");
					exit (0);
				} else {
					$error{'user'} = "I do not like suicid :(<br>I do not delete myself!";
				}
			} else {
				$passwd_ok = 1==0;
				$admin = param('admin')+0; 
				if ( $password eq "" ) { 
					$passwd = $old_passwd;
					$passwd_ok = 1==1;
				} else { 
					if (check($password,$CRACKLIB)) {
						$passwd=crypt($password,$salt); 
						$passwd_ok = 1==1;
					}
				}
				if ( $passwd_ok ) {
					$update_adminuser_h = $db->prepare("update user_info set user_passwd = ?,isadmin=? where user_name = ? and host_group = ?"); 
					$update_adminuser_h->execute($passwd ,$admin ,$user,$host_group);
					print header(-Location=>"$script?action=saved&user=$user");
					exit (0);
				} else {
					$error{'user'} = "Bad Password (e.g. password was too easy to guess)";
				}
			}
		} else {
	 			
			if (param('password') eq param('password1')) {
				if ( check($password,$CRACKLIB)) {
					$passwd=crypt($password,$salt);
					$update_user_h = $db->prepare("update user_info set user_passwd = ? where user_name = ? and host_group = ?"); 
					$update_user_h->execute($passwd,$user,$host_group);
					print header(-Location=>"$script?action=saved&user=$user");
					exit (0);
				} else {
					$error{'user'} = "Bad Password (e.g. password was too easy to guess)";
				}
			} else {
				$error{'user'} = "Password missmatch";
			}
		}
	} elsif ($doit eq 'adduser') {

		if (!user_exists($user)) {
			# be sure, the username is something usefull
			if ( $user =~  /[a-z0-9\-_]+/i ) {
				if ( check($password,$CRACKLIB)) {
					$passwd = crypt($password,$salt); 
					$isadmin = (param('isadmin')==1)+0;
					$add_user_h = $db->prepare ("insert into user_info(user_name,user_passwd,isadmin,host_group,changed,created) values (?,?,?,?,now(),now())");
					$add_user_h->execute($user,$passwd,$isadmin,$host_group);
				
					print header(-Location=>"$script?action=added&user=$user");
					exit(0);
				} else {
					$error{'user'} = "Bad Password (e.g. password was too easy to guess)";
				}
			} else {
				$error{'user'} = "Bad username";
			}
		} else {
			$error{'adduser'} = "User \"$user\" exists";
		}
			
	} elsif ( $doit eq 'savegroup') {
		$delete_group_h = $db->prepare("delete from user_group where user_group = ? and host_group = ?");
		$insert_group_h = $db->prepare("insert into user_group(user_name,user_group,host_group,changed,created) values (?,?,?,now(),now())");
		$group = param('group');
		@member = param('member');

		$db->do('begin');
		$db->do('lock tables user_group write');
		$delete_group_h->execute($group,$host_group);
		
		for ($i=0;$i<=$#member;$i++) {
			
			$insert_group_h->execute($member[$i],$group,$host_group);
		}
		$db->do('unlock tables');
		$db->do('commit');

		print header(-Location=>"$script?action=grouplist");
		exit(0);
	} elsif ( $doit eq 'addgroup') {
		$insert_group_h = $db->prepare("insert into user_group(user_name,user_group,host_group,changed,created) values (?,?,?,now(),now())");
		$group = param('group');
		@member = param('member');

		if ( $group =~ /[a-z0-9\-_]+/i ) {
			if ( ! group_exist($group)) {
				for ($i=0;$i<=$#member;$i++) {
				
					$insert_group_h->execute($member[$i],$group,$host_group);
				}
				print header(-Location=>"$script?action=grouplist");
				exit(0);
			} else {
				$error{'addgroup'} = "Group \"$group\" exists";
			}
		} else {
			$error{'addgroup'} = "Group \"$group\" is not allowed (wrong name)";
		}

	}
	
}

print header; 
print start_html(-title=>"Manage htpasswd entries for the site http://$server/", -style=>{-src=>"http://$server_ip/htpasswd/styles/htpasswd.css"});
print h3("Manage htpasswd entries for the site http://$server/");
print p,"You're logged in as user \"$master_user\"."; # [ ",a({-href=>"http://$server$script?logout=true&bye=$master_user"},"logout"),"]";

if (param) {
        @action = param('action');
	if (defined (@action)) {
		for ($i=0;$i<=$#action;$i++) {
			if ($action[$i] eq 'list' ) {
				print p,b("List user");
				if ( is_admin($master_user) ) {
					$user_h->execute($host_group);
					#print p,$user_h->rows," found";
					while (($user,$isadmin) = $user_h->fetchrow) {
						print br;
						if ($isadmin > 0 ) {
							print img{src=>"http://$server_ip/htpasswd/star.gif",alt=>"admin"};
						} else {
							print img{src=>"http://$server_ip/htpasswd/empty.gif",alt=>""};
						}
						print " ",link_user($user);
					}
				} else {
					print br,b("Sorry, but only administrators can see all users");
				}
			}
			if ($action[$i] eq 'add' ) {
				print p,b("Add user");
				if ( is_admin($master_user) ) { 
					print  start_form(-method=>"post",-action=>"$script");
					if ($error{'adduser'}) { print span({-class=>'error'},$error{'adduser'}),p; }
			 		print  input({-type=>'hidden',-name=>'action',-value=>'add'}),
			 		 input({-type=>'hidden',-name=>'doit',-value=>'adduser'}),
					 "new username",br,input({-name=>'user',-type=>'text',-size=>30,-maxlength=>30,-value=>$user}),
					 br,"password",br,input({-name=>'password',-type=>'text',-size=>12,-maxlength=>12}),
					 br,checkbox(-name=>'admin',-checked=>0,-value=>1,-label=>"user is an administrator"),
					 p,submit({-class=>'center',-value=>'add user now'}),end_form;
					
					 if ( $error{'adduser'} ) { $user = undef; }
				} else {
					print br,b("Sorry, but only administrators can add users");
				}
			}
			if ($action[$i] eq 'grouplist' ) {
				print p,b("List groups");
				if ( is_admin($master_user) ) {
					$group_h = $db->prepare("select user_group,user_name from user_group,host_info where user_group.host_group=host_info.host_group and host=? order by user_group,user_name");
					$group_h->execute($server);
					$old_group="";
					while (($group,$user) = $group_h->fetchrow) {
						if ($old_group ne $group) { 
							print br,a({href=>"$script?group=$group"},$group),b("&raquo; "), link_user($user);
							$old_group=$group;
						} else {
							print ", ",link_user($user);
						}
					}
				} else {
					print br,b("Sorry, but only administrators can see all groups");
				}
			}
			if ($action[$i] eq 'groupadd' ) {
				if ( is_admin($master_user) ) {
					print p,b("Add a new group");
					$user_h->execute($host_group);
					$k=0;
					@user_list=undef;
					while ( ($user,$isadmin) = $user_h->fetchrow) {
						if ( $user ne "" ) { 
							$user_list[$k++] = $user; 
						}
					}
					print  start_form(-method=>"post",-action=>"$script"),
					 input({-type=>'hidden',-name=>'doit',-value=>'savegroup'}),
					 b("Groupname:"),br,
					 input({-type=>'text',-name=>'group',-value=>"$group"}),
					 p,scrolling_list({-name=>'member',-values=>\@user_list, -default=>'',-multiple=>true}),
					 p,submit({-class=>'center',-value=>'change now'}),end_form;
				} else {
					print p,b("Sorry, but only administrators can add groups");
				}
			}
			if ($action[$i] eq 'saved' ) {
				print p,b,"Modify user \"$user\"",
				p,"Settings of user ",link_user($user)," are changed";
				$user=undef;
			}
			if ($action[$i] eq 'added' ) {
				print p,b,"Add user \"$user\"",
				p,"User ",link_user($user)," added";
				$user=undef;
			}
			if ($action[$i] eq 'deleted' ) {
				print p,b,"Delete user \"$user\"",
				p,"User $user deleted.";
				$user=undef;
			}
		}
	}
	if ( $user ne "" ) {
		print p,b("Modify user \"$user\"");
		if ( $error{'user'} ) { print p,span({-class=>'error'},$error{'user'}),p; }
		if ( $user eq $master_user || is_admin($master_user) ) {
			print  start_form(-method=>"post",-action=>"$script"),
			 input({-type=>'hidden',-name=>'user',-value=>"$user"}),
			 input({-type=>'hidden',-name=>'doit',-value=>"saveuser"});
			if (is_admin($master_user)) {
				print b($user),"\'s new password:",br,input({-name=>'password',-type=>'text',-size=>12,-maxlength=>12}),
				 br,checkbox(-name=>'admin',-checked=>is_admin($user),-value=>1,-label=>"user is an administrator");
				if ( $user ne $domain && $user ne $master_user ) { # don't delet master user! 
					print br,checkbox(-name=>'delete',-checked=>0,-value=>1,-label=>"Delete user");
				} else { 
					print br, sprintf ("I'll not delete %s", $user eq $domain ? "the master user" : "myself");
				}
			} else {
				print "Please type your new password:",br,input({-name=>'password',-type=>'password',-size=>12,-maxlength=>12});
				print br,"Please type your new password again:",br,input({-name=>'password1',-type=>'password',-size=>12,-maxlength=>12});
			}
			print  p,submit({-class=>'center',-value=>'change now'}),end_form;
		} else { 
			print br,b("Sorry, but only administrators can change other users");
		}
	}
	$group=param('group');
	if ( $group ne "" ) {
		print p,b("Modify group \"$group\"");
		if ( $user eq $master_user || is_admin($master_user) ) {
			print p,"Please select the members of the group",br,"if there are no members, the group will be deleted.";
			$user_h->execute($host_group);
			$j=0;
			while ( ($user,$isadmin) = $user_h->fetchrow) {
				$user_list[$k++] = $user; 
				if (user_member($user,$group)) {
					$user_sel[$j++] = $user;
				}
			}
			print  start_form(-method=>"post",-action=>"$script"),
			 input({-type=>'hidden',-name=>'group',-value=>"$group"}),
			 input({-type=>'hidden',-name=>'doit',-value=>'savegroup'}),
			 p,scrolling_list({-name=>'member',-values=>\@user_list, -default=>\@user_sel,-multiple=>true}),
			 p,submit({-class=>'center',-value=>'change now'}),end_form;

		} else { 
			print br,b("Sorry, but only administrators can change group members");
		}
	}
		
} else {
	$label{'list'} = 'list all user';
	$label{'add'} = 'add a user';
	$label{'grouplist'} = 'list all groups';
	$label{'groupadd'} = 'add a group';

	print p,b("Do following:"), br;

	# print list, group, add only  if you are an administrator
	if (is_admin($master_user) ) {
		print  start_form,
		checkbox_group(-name=>'action',
			  -values=>['list','add','grouplist','groupadd'],
			  -defaults=>['list'],
			  -linebreak=>'true',
			  -labels=>\%label),
  		p,
		submit('Do it now'),
		end_form;
	} else {
		print a({href=>"$script?user=$master_user"},"change password");

	}
}
print hr({-width=>400,-align=>'left',-size=>1,-noshade}),
      "[ ",a({-href=>'/'},$server),"] &nbsp; ",
      "[ ",a({href=>$script},"Manage htpasswd"),"]",
      hr({-width=>400,-align=>'left',-size=>1,-noshade}),
      span({-class=>'copy'},"&copy; 2003 by ueli(at)<a href=\"http://www.heuer.org/mod_auth_mysql/\">heuer.org</a>");
print end_html;


# check if $user is a domainadmin or not
# return true if $user is an admin
sub is_admin {
	local ($user,$dummy) = @_;

	$is_master_h->execute($server,$user);
	($is_admin) = $is_master_h->fetchrow;

	return ($is_admin == 1);
}

sub get_host_group  {

	$get_host_group_h = $db->prepare("select host_group from host_info where host = ?");
	$get_host_group_h->execute($server);
	($host_group) = $get_host_group_h->fetchrow;

	return $host_group;
}

sub get_password {
	local ($user,$dummy) = @_;

	$get_password_h->execute($server,$user);
	($crypt_pw) = $get_password_h->fetchrow;

	return $crypt_pw;
}

sub link_user {
	local ($user,$dummy) = @_;
	
	if ( user_exists($user)) { 
		return a({href=>"$script?user=$user"},$user); 
	} else {
		return span({-class=>'error',-title=>"User does not exists"},$user);
	}
}

sub user_member {
	local ($user,$group,@dummy) = @_;

	$check_member_h->execute ($user,$group,$host_group);

	return ($check_member_h->rows > 0);
}

sub user_exists  {
	local ($user,$dummy) = @_;

	$check_user_h->execute($user,$host_group);

	return ($check_user_h->rows > 0);
}

sub get_servername {
	# da apache2 server_name falsch (?) zurückliefert, bleibt nur der weg via configdatei 
	# eventuell sieht das dann mit mod_perl etwas anderst aus ... 
	local ($ser,$dummy) = @_;

	open CONF, "< $SERVER_CONF";
	while (<CONF>) {
		next if /\s*#/;
		next if (! /<VirtualHost/i );
		$hostname="";
		$found=1==0;
		while ( ! /<\/VirtualHost/i ) {
			$_=<CONF>;
			next if /\s*#/;
			if (/ServerName\s+([\w\.\-]*)\s*/i ) {
				$hostname = $1;
				$hostname =~ /^([a-z0-9\-]*)\.([a-z0-9\-]*\.[a-z]{2,4})$/;
				$host = $1;
				$domain = $2;
			}
			if (/ServerAlias/i ) {
				if (/\s+$ser\s*/i ) {
					$found = 1==1;
				}
			}
		}
		if (( $hostname =~ /$ser/i ) || $found ) {
			close CONF;
			return ($hostname,$domain);
		}

	}
	close CONF;
	return (undef,undef);
}
