<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;"># htpasswd-file-lib.pl
# Functions for reading and writing a .htpasswd format file
# XXX md5 and old password

BEGIN { push(@INC, ".."); };
use WebminCore;
if (!$module_name) {
	&amp;init_config();
	%access = &amp;get_module_acl();
	}
do 'md5-lib.pl';
$htdigest_command = &amp;has_command("htdigest") || &amp;has_command("htdigest2");

# list_users([file])
# Returns an array of user and password details from the given file
sub list_users
{
local $file = $_[0] || $config{'file'};
if (!defined($list_authusers_cache{$file})) {
	$list_authusers_cache{$file} = [ ];
	local $_;
	local $lnum = 0;
	local $count = 0;
	open(HTPASSWD, $file);
	while(&lt;HTPASSWD&gt;) {
		if (/^(#?)\s*([^:]+):(\S*)/) {
			push(@{$list_authusers_cache{$file}},
				  { 'user' =&gt; $2,
				    'pass' =&gt; $3,
				    'enabled' =&gt; !$1,
				    'file' =&gt; $file,
				    'line' =&gt; $lnum,
				    'index' =&gt; $count++ });
			}
		$lnum++;
		}
	close(HTPASSWD);
	}
return $list_authusers_cache{$file};
}

# list_digest_users([file])
# Returns an array of user, domain and password details from the given file
sub list_digest_users
{
local $file = $_[0] || $config{'file'};
if (!defined($list_authusers_cache{$file})) {
	$list_authusers_cache{$file} = [ ];
	local $_;
	local $lnum = 0;
	local $count = 0;
	open(HTPASSWD, $file);
	while(&lt;HTPASSWD&gt;) {
		if (/^(#?)\s*(\S+):(\S+):(\S*)/) {
			push(@{$list_authusers_cache{$file}},
				  { 'user' =&gt; $2,
				    'dom' =&gt; $3,
				    'pass' =&gt; $4,
				    'enabled' =&gt; !$1,
				    'digest' =&gt; 1,
				    'file' =&gt; $file,
				    'line' =&gt; $lnum,
				    'index' =&gt; $count++ });
			}
		$lnum++;
		}
	close(HTPASSWD);
	}
return $list_authusers_cache{$file};
}

# modify_user(&amp;user)
sub modify_user
{
local $lref = &amp;read_file_lines($_[0]-&gt;{'file'});
if ($_[0]-&gt;{'digest'}) {
	$lref-&gt;[$_[0]-&gt;{'line'}] = ($_[0]-&gt;{'enabled'} ? "" : "#").
			   "$_[0]-&gt;{'user'}:$_[0]-&gt;{'dom'}:$_[0]-&gt;{'pass'}";
	}
else {
	$lref-&gt;[$_[0]-&gt;{'line'}] = ($_[0]-&gt;{'enabled'} ? "" : "#").
				   "$_[0]-&gt;{'user'}:$_[0]-&gt;{'pass'}";
	}
&amp;flush_file_lines($_[0]-&gt;{'file'});
}

# create_user(&amp;user, [file])
sub create_user
{
$_[0]-&gt;{'file'} = $_[1] || $config{'file'};
local $lref = &amp;read_file_lines($_[0]-&gt;{'file'});
$_[0]-&gt;{'line'} = @$lref;
if ($_[0]-&gt;{'digest'}) {
	push(@$lref, ($_[0]-&gt;{'enabled'} ? "" : "#").
		     "$_[0]-&gt;{'user'}:$_[0]-&gt;{'dom'}:$_[0]-&gt;{'pass'}");
	}
else {
	push(@$lref, ($_[0]-&gt;{'enabled'} ? "" : "#").
		     "$_[0]-&gt;{'user'}:$_[0]-&gt;{'pass'}");
	}
&amp;flush_file_lines($_[0]-&gt;{'file'});
$_[0]-&gt;{'index'} = @{$list_authusers_cache{$_[0]-&gt;{'file'}}};
push(@{$list_authusers_cache{$_[0]-&gt;{'file'}}}, $_[0]);
}

# delete_user(&amp;user)
sub delete_user
{
local $lref = &amp;read_file_lines($_[0]-&gt;{'file'});
splice(@$lref, $_[0]-&gt;{'line'}, 1);
&amp;flush_file_lines($_[0]-&gt;{'file'});
splice(@{$list_authusers_cache{$_[0]-&gt;{'file'}}}, $_[0]-&gt;{'index'}, 1);
map { $_-&gt;{'line'}-- if ($_-&gt;{'line'} &gt; $_[0]-&gt;{'line'}) }
    @{$list_authusers_cache{$_[0]-&gt;{'file'}}};
}

# encrypt_password(string, [old], md5mode)
sub encrypt_password
{
&amp;seed_random();
if ($_[2] == 1) {
	# MD5
	return &amp;encrypt_md5($_[0], $_[1]);
	}
elsif ($_[2] == 2) {
	# SHA1
	return &amp;encrypt_sha1($_[0]);
	}
elsif ($_[2] == 3) {
	# Digest
	return &amp;digest_password(undef, undef, $_[0]);
	}
else {
	# Crypt
	if ($gconfig{'os_type'} eq 'windows' &amp;&amp; &amp;has_command("htpasswd")) {
		# Call htpasswd program
		local $qp = quotemeta($_[0]);
		local $out = `htpasswd -n -b foo $qp 2&gt;&amp;1 &lt;$null_file`;
		if ($out =~ /^foo:(\S+)/) {
			return $1;
			}
		else {
			&amp;error("htpasswd failed : $out");
			}
		}
	else {
		# Use built-in encryption code
		local $salt = $_[1] ||
			      chr(int(rand(26))+65).chr(int(rand(26))+65);
		return &amp;unix_crypt($_[0], $salt);
		}
	}
}

# digest_password(user, realm, pass)
# Encrypts a password in the format used by htdigest
sub digest_password
{
local ($user, $dom, $pass) = @_;
local $temp = &amp;tempname();
eval "use Digest::MD5";
if (!$@) {
	# Use the digest::MD5 module to do the encryption directly
	return Digest::MD5::md5_hex("$user:$dom:$pass");
	}
else {
	# Shell out to htdigest command
	&amp;foreign_require("proc", "proc-lib.pl");
	local ($fh, $fpid) = &amp;proc::pty_process_exec("$htdigest_command -c $temp ".quotemeta($dom)." ".quotemeta($user));
	&amp;wait_for($fh, "password:");
	&amp;sysprint($fh, "$pass\n");
	&amp;wait_for($fh, "password:");
	&amp;sysprint($fh, "$pass\n");
	&amp;wait_for($fh);
	close($fh);
	local $tempusers = &amp;list_digest_users($temp);
	unlink($temp);
	return $tempusers-&gt;[0]-&gt;{'pass'};
	}
}

# list_groups(file)
# Returns an array of group details from the given file
sub list_groups
{
local $file = $_[0];
if (!defined($list_authgroups_cache{$file})) {
	$list_authgroups_cache{$file} = [ ];
	local $_;
	local $lnum = 0;
	local $count = 0;
	open(HTPASSWD, $file);
	while(&lt;HTPASSWD&gt;) {
		if (/^(#?)\s*(\S+):\s*(.*)/) {
			push(@{$list_authgroups_cache{$file}},
				  { 'group' =&gt; $2,
				    'enabled' =&gt; !$1,
				    'members' =&gt; [ split(/\s+/, $3) ],
				    'file' =&gt; $file,
				    'line' =&gt; $lnum,
				    'index' =&gt; $count++ });
			}
		$lnum++;
		}
	close(HTPASSWD);
	}
return $list_authgroups_cache{$file};
}

# modify_group(&amp;group)
sub modify_group
{
local $lref = &amp;read_file_lines($_[0]-&gt;{'file'});
$lref-&gt;[$_[0]-&gt;{'line'}] = ($_[0]-&gt;{'enabled'} ? "" : "#").
			   "$_[0]-&gt;{'group'}: ".
			   join(" ", @{$_[0]-&gt;{'members'}});
&amp;flush_file_lines();
}

# create_group(&amp;group, [file])
sub create_group
{
$_[0]-&gt;{'file'} = $_[1] || $config{'file'};
local $lref = &amp;read_file_lines($_[0]-&gt;{'file'});
$_[0]-&gt;{'line'} = @$lref;
push(@$lref, ($_[0]-&gt;{'enabled'} ? "" : "#").
	      "$_[0]-&gt;{'group'}: ".
	      join(" ", @{$_[0]-&gt;{'members'}}));
&amp;flush_file_lines();
$_[0]-&gt;{'index'} = @{$list_authgroups_cache{$_[0]-&gt;{'file'}}};
push(@{$list_authgroups_cache{$_[0]-&gt;{'file'}}}, $_[0]);
}

# delete_group(&amp;group)
sub delete_group
{
local $lref = &amp;read_file_lines($_[0]-&gt;{'file'});
splice(@$lref, $_[0]-&gt;{'line'}, 1);
&amp;flush_file_lines();
splice(@{$list_authgroups_cache{$_[0]-&gt;{'file'}}}, $_[0]-&gt;{'index'}, 1);
map { $_-&gt;{'line'}-- if ($_-&gt;{'line'} &gt; $_[0]-&gt;{'line'}) }
    @{$list_authgroups_cache{$_[0]-&gt;{'file'}}};
}


1;

</pre></body></html>