mirror of
https://github.com/webmin/webmin.git
synced 2026-07-25 02:40:37 +01:00
ⓘ Require a live Usermin miniserv PID before allowing one-time login URL creation, so the feature is only offered when the session backend can actually be used.
1132 lines
28 KiB
Perl
Executable File
1132 lines
28 KiB
Perl
Executable File
=head1 usermin-lib.pl
|
|
|
|
Functions for configuring Usermin running on this system. Example usage :
|
|
|
|
foreign_require("usermin", "usermin-lib.pl");
|
|
@usermods = usermin::list_usermin_usermods();
|
|
push(@usermods, [ 'joe', '', 'mailbox changepass' ]);
|
|
usermin::save_usermin_usermods(\@usermods);
|
|
|
|
=cut
|
|
|
|
BEGIN { push(@INC, ".."); };
|
|
use WebminCore;
|
|
&init_config();
|
|
%access = &get_module_acl();
|
|
$access{'upgrade'} = 0 if (&is_readonly_mode()); # too hard to fake
|
|
&foreign_require("webmin");
|
|
&foreign_require("acl");
|
|
%text = ( %webmin::text, %text );
|
|
|
|
if (!defined($gconfig{'noselfwebminup'})) {
|
|
$access{'upgrade'} = 0
|
|
if (&webmin::has_repos());
|
|
}
|
|
else {
|
|
$access{'upgrade'} = !$gconfig{'noselfwebminup'};
|
|
}
|
|
my $can_http_ssl = &can_use_http_ssl();
|
|
my $http_proto = $can_http_ssl ? "https" : "http";
|
|
|
|
$usermin_miniserv_config = "$config{'usermin_dir'}/miniserv.conf";
|
|
$usermin_config = "$config{'usermin_dir'}/config";
|
|
|
|
$update_host = "webmin.com";
|
|
$update_ssl = $can_http_ssl;
|
|
$update_port = $update_ssl ? 443 : 80;
|
|
$update_page = "/uupdates/uupdates.txt";
|
|
|
|
$standard_usermin_dir = "/etc/usermin";
|
|
$latest_page_url = "$http_proto://$update_host/index6.html";
|
|
$latest_rpm = "$http_proto://$update_host/download/usermin-latest.noarch.rpm";
|
|
$latest_tgz = "$http_proto://$update_host/download/usermin-latest.tar.gz";
|
|
|
|
$default_key_size = 4096;
|
|
|
|
$cron_cmd = "$module_config_directory/update.pl";
|
|
|
|
=head2 get_usermin_miniserv_config(&hash)
|
|
|
|
Similar to the standard get_miniserv_config function, but this one fills in
|
|
the given hash ref with the contents of the /etc/usermin/miniserv.conf file.
|
|
|
|
=cut
|
|
sub get_usermin_miniserv_config
|
|
{
|
|
&read_file($usermin_miniserv_config, \%usermin_miniserv_config_cache)
|
|
if (!%usermin_miniserv_config_cache);
|
|
%{$_[0]} = %usermin_miniserv_config_cache;
|
|
}
|
|
|
|
=head2 put_usermin_miniserv_config(&hash)
|
|
|
|
Writes out the Usermin miniserv configuration, based on the given hash ref.
|
|
|
|
=cut
|
|
sub put_usermin_miniserv_config
|
|
{
|
|
%usermin_miniserv_config_cache = %{$_[0]};
|
|
&write_file($usermin_miniserv_config, \%usermin_miniserv_config_cache);
|
|
}
|
|
|
|
=head2 get_usermin_version([format_dev_version], [include_release])
|
|
|
|
Returns the version number of Usermin on this system.
|
|
|
|
=cut
|
|
sub get_usermin_version
|
|
{
|
|
my ($ui_format_dev, $inc_release_version) = @_;
|
|
local %miniserv;
|
|
&get_usermin_miniserv_config(\%miniserv);
|
|
open(VERSION, "<$miniserv{'root'}/version");
|
|
local $version = <VERSION>;
|
|
close(VERSION);
|
|
$version =~ s/\r|\n//g;
|
|
my $release_version = "";
|
|
# Usermin minor version
|
|
if ($inc_release_version) {
|
|
my $usermin_version_release = read_file_contents("$miniserv{'root'}/release") || "";
|
|
$usermin_version_release =~ s/\r|\n//g;
|
|
$release_version = "-".$usermin_version_release
|
|
if ($usermin_version_release > 1);
|
|
}
|
|
|
|
# Format dev version nicely
|
|
if ($ui_format_dev && length($version) == 13) {
|
|
return substr($version, 0, 5) . "." . substr($version, 5, 5 - 1) . "." . substr($version, 5 * 2 - 1) . $release_version;
|
|
}
|
|
else {
|
|
return $version . $release_version;
|
|
}
|
|
}
|
|
|
|
=head2 restart_usermin_miniserv
|
|
|
|
Send a HUP signal to Usermin's miniserv, telling it to restart and re-read
|
|
all configuration files.
|
|
|
|
=cut
|
|
sub restart_usermin_miniserv
|
|
{
|
|
return undef if (&is_readonly_mode());
|
|
local($pid, %miniserv, $addr, $i);
|
|
&get_usermin_miniserv_config(\%miniserv) || return;
|
|
$miniserv{'inetd'} && return;
|
|
open(PID, "<".$miniserv{'pidfile'}) || &error("Failed to open PID file");
|
|
chop($pid = <PID>);
|
|
close(PID);
|
|
if (!$pid) { &error("Invalid PID file"); }
|
|
return &kill_logged('HUP', $pid);
|
|
}
|
|
|
|
=head2 reload_usermin_miniserv
|
|
|
|
Sends a USR1 signal to the miniserv process, telling it to re-read most
|
|
configuration files.
|
|
|
|
=cut
|
|
sub reload_usermin_miniserv
|
|
{
|
|
return undef if (&is_readonly_mode());
|
|
local %miniserv;
|
|
&get_usermin_miniserv_config(\%miniserv) || return;
|
|
$miniserv{'inetd'} && return;
|
|
|
|
local($pid, $addr, $i);
|
|
open(PID, "<".$miniserv{'pidfile'}) || &error("Failed to open PID file");
|
|
chop($pid = <PID>);
|
|
close(PID);
|
|
if (!$pid) { &error("Invalid PID file"); }
|
|
return &kill_logged('USR1', $pid);
|
|
}
|
|
|
|
=head2 get_usermin_config(&hash)
|
|
|
|
Fills in the given hash ref with the contents of the global Usermin
|
|
configuration file, typically at /etc/usermin/config.
|
|
|
|
=cut
|
|
sub get_usermin_config
|
|
{
|
|
&read_file($usermin_config, \%usermin_config_cache)
|
|
if (!%usermin_config_cache);
|
|
%{$_[0]} = %usermin_config_cache;
|
|
}
|
|
|
|
=head2 put_usermin_config(&hash)
|
|
|
|
Writes the given hash ref to the global Usermin configuration file.
|
|
|
|
=cut
|
|
sub put_usermin_config
|
|
{
|
|
%usermin_config_cache = %{$_[0]};
|
|
&write_file($usermin_config, \%usermin_config_cache);
|
|
}
|
|
|
|
=head2 get_usermin_root_directory
|
|
|
|
Returns the Usermin install root directory.
|
|
|
|
=cut
|
|
sub get_usermin_root_directory
|
|
{
|
|
my %miniserv;
|
|
&get_usermin_miniserv_config(\%miniserv);
|
|
return $miniserv{'root'};
|
|
}
|
|
|
|
=head2 list_themes
|
|
|
|
Returns an array of all usermin themes. The format is the same as the
|
|
webmin::list_themes function.
|
|
|
|
=cut
|
|
sub list_themes
|
|
{
|
|
local @rv;
|
|
local %miniserv;
|
|
&get_usermin_miniserv_config(\%miniserv);
|
|
opendir(DIR, $miniserv{'root'});
|
|
foreach $m (readdir(DIR)) {
|
|
next if ($m =~ /^\./);
|
|
local %tinfo = &get_usermin_theme_info($m);
|
|
next if (!%tinfo);
|
|
next if (!&check_usermin_os_support(\%tinfo));
|
|
push(@rv, \%tinfo);
|
|
}
|
|
closedir(DIR);
|
|
return @rv;
|
|
}
|
|
|
|
=head2 list_visible_themes([current-theme])
|
|
|
|
Lists all themes the user should be able to use, possibly including their
|
|
current theme if one is set.
|
|
|
|
=cut
|
|
sub list_visible_themes
|
|
{
|
|
my ($curr) = @_;
|
|
my @rv;
|
|
my %done;
|
|
foreach my $theme (&list_themes()) {
|
|
my $iscurr = $curr && $theme->{'dir'} eq $curr;
|
|
next if (-l $root_directory."/".$theme->{'dir'} &&
|
|
!$iscurr);
|
|
next if ($done{$theme->{'desc'}}++ && !$iscurr);
|
|
push(@rv, $theme);
|
|
}
|
|
return @rv;
|
|
}
|
|
|
|
=head2 list_modules
|
|
|
|
Returns a list of all usermin modules installed and supported on this system.
|
|
Each is a hash ref in the same format as returned by Webmin's get_module_info
|
|
function.
|
|
|
|
=cut
|
|
sub list_modules
|
|
{
|
|
local (@mlist, $m, %miniserv);
|
|
&get_usermin_miniserv_config(\%miniserv);
|
|
local %cats;
|
|
&read_file_cached("$config{'usermin_dir'}/webmin.cats", \%cats);
|
|
opendir(DIR, $miniserv{'root'});
|
|
foreach $m (readdir(DIR)) {
|
|
local %minfo;
|
|
if ((%minfo = &get_usermin_module_info($m)) &&
|
|
&check_usermin_os_support(\%minfo)) {
|
|
$minfo{'realcategory'} = $minfo{'category'};
|
|
$minfo{'category'} = $cats{$m} if (defined($cats{$m}));
|
|
push(@mlist, \%minfo);
|
|
}
|
|
}
|
|
closedir(DIR);
|
|
@mlist = sort { $a->{'desc'} cmp $b->{'desc'} } @mlist;
|
|
return @mlist;
|
|
}
|
|
|
|
=head2 get_usermin_module_info(module, [noclone])
|
|
|
|
Returns a hash contain details of a module, in the same format as
|
|
Webmin's get_module_info function. Useful keys include :
|
|
|
|
=item dir - The module's relative directory.
|
|
|
|
=item desc - The human-readable title.
|
|
|
|
=item category - Category the module is in, like login or apps.
|
|
|
|
=item depends - Space-separated list of dependent modules.
|
|
|
|
=item os_support - List of supported operating systems and versions.
|
|
|
|
=cut
|
|
sub get_usermin_module_info
|
|
{
|
|
return () if ($_[0] =~ /^\./);
|
|
local (%rv, $clone, %miniserv, $o);
|
|
&get_usermin_miniserv_config(\%miniserv);
|
|
&read_file("$miniserv{'root'}/$_[0]/module.info", \%rv) || return ();
|
|
$clone = -l "$miniserv{'root'}/$_[0]";
|
|
foreach $o (@lang_order_list) {
|
|
$rv{"desc"} = $rv{"desc_$o"} if ($rv{"desc_$o"});
|
|
}
|
|
if ($clone && !$_[1] && $config_directory) {
|
|
$rv{'clone'} = $rv{'desc'};
|
|
&read_file("$config{'usermin_dir'}/$_[0]/clone", \%rv);
|
|
}
|
|
$rv{'dir'} = $_[0];
|
|
$rv{'realcategory'} = $rv{'category'};
|
|
|
|
# Apply description overrides
|
|
$rv{'realdesc'} = $rv{'desc'};
|
|
local %descs;
|
|
&read_file_cached("$config{'usermin_dir'}/webmin.descs", \%descs);
|
|
if ($descs{$_[0]." ".$current_lang}) {
|
|
$rv{'desc'} = $descs{$_[0]." ".$current_lang};
|
|
}
|
|
elsif ($descs{$_[0]}) {
|
|
$rv{'desc'} = $descs{$_[0]};
|
|
}
|
|
|
|
return %rv;
|
|
}
|
|
|
|
=head2 get_usermin_theme_info(theme)
|
|
|
|
Like get_usermin_module_info, but returns the details of a theme instead.
|
|
This is basically the contents of its theme.info file.
|
|
|
|
=cut
|
|
sub get_usermin_theme_info
|
|
{
|
|
local (%tinfo, $o);
|
|
local %miniserv;
|
|
&get_usermin_miniserv_config(\%miniserv);
|
|
&read_file("$miniserv{'root'}/$_[0]/theme.info", \%tinfo) || return ();
|
|
foreach $o (@lang_order_list) {
|
|
$tinfo{"desc"} = $rv{"desc_$o"} if ($tinfo{"desc_$o"});
|
|
}
|
|
$tinfo{'dir'} = $_[0];
|
|
return %tinfo;
|
|
}
|
|
|
|
=head2 check_usermin_os_support(&minfo)
|
|
|
|
Given a Usermin module information hash ref (as returned by
|
|
get_usermin_module_info), checks if it is supported on this OS. Returns 1 if
|
|
yes, 0 if no.
|
|
|
|
=cut
|
|
sub check_usermin_os_support
|
|
{
|
|
local $oss = $_[0]->{'os_support'};
|
|
return 1 if (!$oss || $oss eq '*');
|
|
local %uconfig;
|
|
&get_usermin_config(\%uconfig);
|
|
while(1) {
|
|
local ($os, $ver, $codes);
|
|
if ($oss =~ /^([^\/\s]+)\/([^\{\s]+)\{([^\}]*)\}\s*(.*)$/) {
|
|
$os = $1; $ver = $2; $codes = $3; $oss = $4;
|
|
}
|
|
elsif ($oss =~ /^([^\/\s]+)\/([^\/\s]+)\s*(.*)$/) {
|
|
$os = $1; $ver = $2; $oss = $3;
|
|
}
|
|
elsif ($oss =~ /^([^\{\s]+)\{([^\}]*)\}\s*(.*)$/) {
|
|
$os = $1; $codes = $2; $oss = $3;
|
|
}
|
|
elsif ($oss =~ /^\{([^\}]*)\}\s*(.*)$/) {
|
|
$codes = $1; $oss = $2;
|
|
}
|
|
elsif ($oss =~ /^(\S+)\s*(.*)$/) {
|
|
$os = $1; $oss = $2;
|
|
}
|
|
else { last; }
|
|
next if ($os && !($os eq $uconfig{'os_type'} ||
|
|
$uconfig{'os_type'} =~ /^(\S+)-(\S+)$/ && $os eq "*-$2"));
|
|
next if ($ver && $ver ne $uconfig{'os_version'});
|
|
next if ($codes && !eval $codes);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
=head2 read_usermin_acl(&array, &array)
|
|
|
|
Reads the acl file into the given hashes. The first maps user,module to
|
|
1 where granted, which the second maps a user to an array ref of module dirs.
|
|
|
|
=cut
|
|
sub read_usermin_acl
|
|
{
|
|
local($user, $_, @mods);
|
|
if (!%usermin_acl_hash_cache) {
|
|
open(ACL, "<".&usermin_acl_filename());
|
|
while(<ACL>) {
|
|
if (/^(\S+):\s*(.*)/) {
|
|
local(@mods);
|
|
$user = $1;
|
|
@mods = split(/\s+/, $2);
|
|
foreach $m (@mods) {
|
|
$usermin_acl_hash_cache{$user,$m}++;
|
|
}
|
|
$usermin_acl_array_cache{$user} = \@mods;
|
|
}
|
|
}
|
|
close(ACL);
|
|
}
|
|
if ($_[0]) { %{$_[0]} = %usermin_acl_hash_cache; }
|
|
if ($_[1]) { %{$_[1]} = %usermin_acl_array_cache; }
|
|
}
|
|
|
|
=head2 usermin_acl_filename
|
|
|
|
Returns the file containing the webmin ACL.
|
|
|
|
=cut
|
|
sub usermin_acl_filename
|
|
{
|
|
return "$config{'usermin_dir'}/webmin.acl";
|
|
}
|
|
|
|
=head2 save_usermin_acl(user, &modules)
|
|
|
|
Updates the list of available modules in Usermin.
|
|
|
|
=cut
|
|
sub save_usermin_acl
|
|
{
|
|
&open_tempfile(ACL, ">".&usermin_acl_filename());
|
|
&print_tempfile(ACL, $_[0],": ",join(" ", @{$_[1]}),"\n");
|
|
&close_tempfile(ACL);
|
|
}
|
|
|
|
=head2 install_usermin_module(file, unlink, nodeps)
|
|
|
|
Installs a usermin module or theme, and returns either an error message
|
|
or references to three arrays for descriptions, directories and sizes.
|
|
On success or failure, the file is deleted if the unlink parameter is set.
|
|
|
|
=cut
|
|
sub install_usermin_module
|
|
{
|
|
local ($file, $need_unlink, $nodeps) = @_;
|
|
local (@mdescs, @mdirs, @msizes);
|
|
if (&is_readonly_mode()) {
|
|
return "Module installs are not allowed in readonly mode";
|
|
}
|
|
|
|
# Uncompress the module file if needed
|
|
open(MFILE, "<".$file);
|
|
read(MFILE, $two, 2);
|
|
close(MFILE);
|
|
local $qfile = quotemeta($file);
|
|
if ($two eq "\037\235") {
|
|
if (!&has_command("uncompress")) {
|
|
unlink($file) if ($need_unlink);
|
|
return &text('install_ecomp', "<tt>uncompress</tt>");
|
|
}
|
|
local $temp = $file =~ /\/([^\/]+)\.Z/i ? &transname("$1")
|
|
: &transname();
|
|
local $qtemp = quotemeta($temp);
|
|
local $out = `uncompress -c $qfile 2>&1 >$qtemp`;
|
|
unlink($file) if ($need_unlink);
|
|
if ($?) {
|
|
unlink($temp);
|
|
return &text('install_ecomp2', $out);
|
|
}
|
|
$file = $temp;
|
|
$qfile = quotemeta($file);
|
|
$need_unlink = 1;
|
|
}
|
|
elsif ($two eq "\037\213") {
|
|
if (!&has_command("gunzip")) {
|
|
unlink($file) if ($need_unlink);
|
|
return &text('install_egzip', "<tt>gunzip</tt>");
|
|
}
|
|
local $temp = $file =~ /\/([^\/]+)\.gz/i ? &transname("$1")
|
|
: &transname();
|
|
local $qtemp = quotemeta($temp);
|
|
local $out = `gunzip -c $qfile 2>&1 >$qtemp`;
|
|
unlink($file) if ($need_unlink);
|
|
if ($?) {
|
|
unlink($temp);
|
|
return &text('install_egzip2', $out);
|
|
}
|
|
$file = $temp;
|
|
$qfile = quotemeta($file);
|
|
$need_unlink = 1;
|
|
}
|
|
|
|
local %miniserv;
|
|
&get_usermin_miniserv_config(\%miniserv);
|
|
|
|
# Check if this is an RPM usermin module or theme
|
|
local ($type, $redirect_to);
|
|
open(TYPE, "<../install-type");
|
|
chop($type = <TYPE>);
|
|
close(TYPE);
|
|
if ($type eq 'rpm' && $file =~ /\.rpm$/i &&
|
|
($out = `rpm -qp $qfile 2>/dev/null`)) {
|
|
# Looks like an RPM of some kind, hopefully an RPM usermin module
|
|
# or theme
|
|
local ($out, %minfo, %tinfo);
|
|
if ($out !~ /^(wbm|wbt)-([^\s\-]+)/) {
|
|
unlink($file) if ($need_unlink);
|
|
return $text{'install_erpm'};
|
|
}
|
|
$redirect_to = $name = $2;
|
|
$out = &backquote_logged("rpm -Uv $qfile 2>&1");
|
|
if ($?) {
|
|
unlink($file) if ($need_unlink);
|
|
return &text('install_eirpm', "<tt>$out</tt>");
|
|
}
|
|
|
|
$mdirs[0] = "$miniserv{'root'}/$name";
|
|
if (%minfo = &get_usermin_module_info($name)) {
|
|
# Get the new module info
|
|
$mdescs[0] = $minfo{'desc'};
|
|
$msizes[0] = &disk_usage_kb($mdirs[0]);
|
|
|
|
# Update the ACL for the usermin user
|
|
local %acl;
|
|
&read_usermin_acl(undef, \%acl);
|
|
&open_tempfile(ACL, "> ".&usermin_acl_filename());
|
|
foreach $u (keys %acl) {
|
|
local @mods = @{$acl{$u}};
|
|
if ($u eq 'user') {
|
|
push(@mods, $name);
|
|
@mods = &unique(@mods);
|
|
}
|
|
&print_tempfile(ACL, "$u: ",join(' ', @mods),"\n");
|
|
}
|
|
&close_tempfile(ACL);
|
|
&webmin_log("install", undef, $name,
|
|
{ 'desc' => $mdescs[0] });
|
|
}
|
|
elsif (%tinfo = &get_usermin_theme_info($name)) {
|
|
# Get the theme info
|
|
$mdescs[0] = $tinfo{'desc'};
|
|
$msizes[0] = &disk_usage_kb($mdirs[0]);
|
|
&webmin_log("tinstall", undef, $name,
|
|
{ 'desc' => $mdescs[0] });
|
|
}
|
|
else {
|
|
unlink($file) if ($need_unlink);
|
|
return $text{'install_eneither'};
|
|
}
|
|
}
|
|
else {
|
|
# Check if this is a valid module (a tar file of multiple module or
|
|
# theme directories)
|
|
local (%mods, %hasfile);
|
|
local $tar = `tar tf $qfile 2>&1`;
|
|
if ($?) {
|
|
unlink($file) if ($need_unlink);
|
|
return &text('install_etar', $tar);
|
|
}
|
|
foreach $f (split(/\n/, $tar)) {
|
|
if ($f =~ /^\.\/([^\/]+)\/(.*)$/ || $f =~ /^([^\/]+)\/(.*)$/) {
|
|
$redirect_to = $1 if (!$redirect_to);
|
|
$mods{$1}++;
|
|
$hasfile{$1,$2}++;
|
|
}
|
|
}
|
|
foreach $m (keys %mods) {
|
|
if (!$hasfile{$m,"module.info"} && !$hasfile{$m,"theme.info"}) {
|
|
unlink($file) if ($need_unlink);
|
|
return &text('install_einfo', "<tt>$m</tt>");
|
|
}
|
|
}
|
|
if (!%mods) {
|
|
unlink($file) if ($need_unlink);
|
|
return $text{'install_enone'};
|
|
}
|
|
|
|
# Get the module.info files to check dependencies
|
|
local $ver = &get_usermin_version();
|
|
local $tmpdir = &transname();
|
|
mkdir($tmpdir, 0700);
|
|
local $err;
|
|
local @realmods;
|
|
foreach $m (keys %mods) {
|
|
next if (!$hasfile{$m,"module.info"});
|
|
push(@realmods, $m);
|
|
local %minfo;
|
|
system("cd $tmpdir ; tar xf $qfile $m/module.info ./$m/module.info >/dev/null 2>&1");
|
|
if (!&read_file("$tmpdir/$m/module.info", \%minfo)) {
|
|
$err = &text('install_einfo', "<tt>$m</tt>");
|
|
}
|
|
elsif (!&check_usermin_os_support(\%minfo)) {
|
|
$err = &text('install_eos', "<tt>$m</tt>",
|
|
$gconfig{'real_os_type'},
|
|
$gconfig{'real_os_version'});
|
|
}
|
|
elsif (!$minfo{'usermin'}) {
|
|
$err = &text('install_eusermin', "<tt>$m</tt>");
|
|
}
|
|
elsif (!$nodeps) {
|
|
local $deps = $minfo{'usermin_depends'} ||
|
|
$minfo{'depends'};
|
|
foreach $dep (split(/\s+/, $minfo{'depends'})) {
|
|
if ($dep =~ /^[0-9\.]+$/) {
|
|
if ($dep > $ver) {
|
|
$err = &text('install_ever',
|
|
"<tt>$m</tt>",
|
|
"<tt>$dep</tt>");
|
|
}
|
|
}
|
|
elsif (!-r "$miniserv{'root'}/$dep/module.info"
|
|
&& !$mods{$dep}) {
|
|
$err = &text('install_edep',
|
|
"<tt>$m</tt>", "<tt>$dep</tt>");
|
|
}
|
|
}
|
|
foreach $dep (split(/\s+/, $minfo{'perldepends'})) {
|
|
eval "use $dep";
|
|
if ($@) {
|
|
$err = &text('install_eperldep',
|
|
"<tt>$m</tt>", "<tt>$dep</tt>",
|
|
"/cpan/download.cgi?source=3&cpan=$dep");
|
|
}
|
|
}
|
|
}
|
|
last if ($err);
|
|
}
|
|
system("rm -rf $tmpdir >/dev/null 2>&1");
|
|
if ($err) {
|
|
unlink($file) if ($need_unlink);
|
|
return $err;
|
|
}
|
|
|
|
# Delete modules or themes being replaced
|
|
foreach $m (@realmods) {
|
|
system("rm -rf '$miniserv{'root'}/$m' 2>&1 >/dev/null") if ($m ne 'webmin');
|
|
}
|
|
|
|
# Extract all the modules and update perl path and ownership
|
|
local $out = `cd $miniserv{'root'} ; tar xf $qfile 2>&1 >/dev/null`;
|
|
if ($?) {
|
|
unlink($file) if ($need_unlink);
|
|
return &text('install_eextract', $out);
|
|
}
|
|
if ($need_unlink) { unlink($file); }
|
|
local $perl;
|
|
open(PERL, "<$miniserv{'root'}/miniserv.pl");
|
|
<PERL> =~ /^#!(\S+)/; $perl = $1;
|
|
close(PERL);
|
|
local @st = stat($0);
|
|
foreach $moddir (keys %mods) {
|
|
local $pwd = "$miniserv{'root'}/$moddir";
|
|
if ($hasfile{$moddir,"module.info"}) {
|
|
local %minfo = &get_usermin_module_info($moddir);
|
|
push(@mdescs, $minfo{'desc'});
|
|
push(@mdirs, $pwd);
|
|
push(@msizes, &disk_usage_kb($pwd));
|
|
&webmin_log("install", undef, $moddir,
|
|
{ 'desc' => $minfo{'desc'} });
|
|
}
|
|
else {
|
|
local %tinfo = &get_usermin_theme_info($moddir);
|
|
&read_file("theme.info", \%tinfo);
|
|
push(@mdescs, $tinfo{'desc'});
|
|
push(@mdirs, $pwd);
|
|
push(@msizes, &disk_usage_kb($pwd));
|
|
&webmin_log("tinstall", undef, $moddir,
|
|
{ 'desc' => $tinfo{'desc'} });
|
|
}
|
|
system("(find $pwd -name '*.cgi' ; find $pwd -name '*.pl') 2>/dev/null | $perl $miniserv{'root'}/perlpath.pl $perl -");
|
|
system("chown -R $st[4]:$st[5] $pwd");
|
|
}
|
|
|
|
# Copy appropriate config file from modules to /etc/webmin
|
|
local %ugconfig;
|
|
&get_usermin_config(\%ugconfig);
|
|
system("$perl $miniserv{'root'}/copyconfig.pl '$ugconfig{'os_type'}/$ugconfig{'real_os_type'}' '$ugconfig{'os_version'}/$ugconfig{'real_os_version'}' $miniserv{'root'} $config{'usermin_dir'} ".join(' ', @realmods));
|
|
|
|
# Update ACL for this user so they can access the new modules
|
|
local %acl;
|
|
&read_usermin_acl(undef, \%acl);
|
|
&open_tempfile(ACL, "> ".&usermin_acl_filename());
|
|
foreach $u (keys %acl) {
|
|
local @mods = @{$acl{$u}};
|
|
if ($u eq 'user') {
|
|
push(@mods, @realmods);
|
|
@mods = &unique(@mods);
|
|
}
|
|
&print_tempfile(ACL, "$u: ",join(' ', @mods),"\n");
|
|
}
|
|
&close_tempfile(ACL);
|
|
}
|
|
&flush_modules_cache();
|
|
|
|
return [ \@mdescs, \@mdirs, \@msizes ];
|
|
}
|
|
|
|
=head2 list_usermin_usermods
|
|
|
|
Returns the list of additional module restrictions for usermin.
|
|
This is a list of array refs, each element of which contains a username,
|
|
a flag and an array ref of module names. The flag can be one of :
|
|
|
|
=item + - Add the modules to the list available to this user.
|
|
|
|
=item - - Take the modules away from this user.
|
|
|
|
=item blank - Assign the modules to the list for this user.
|
|
|
|
=cut
|
|
sub list_usermin_usermods
|
|
{
|
|
local @rv;
|
|
open(USERMODS, "<$config{'usermin_dir'}/usermin.mods");
|
|
while(<USERMODS>) {
|
|
if (/^([^:]+):(\+|-|):(.*)/) {
|
|
push(@rv, [ $1, $2, [ split(/\s+/, $3) ] ]);
|
|
}
|
|
}
|
|
close(USERMODS);
|
|
return @rv;
|
|
}
|
|
|
|
=head2 save_usermin_usermods(&usermods)
|
|
|
|
Saves the list of additional module restrictions. This must be an array ref
|
|
in the same format as returned by list_usermin_usermods.
|
|
|
|
=cut
|
|
sub save_usermin_usermods
|
|
{
|
|
&open_tempfile(USERMODS, ">$config{'usermin_dir'}/usermin.mods");
|
|
foreach $u (@{$_[0]}) {
|
|
&print_tempfile(USERMODS,
|
|
join(":", $u->[0], $u->[1], join(" ", @{$u->[2]})),"\n");
|
|
}
|
|
&close_tempfile(USERMODS);
|
|
}
|
|
|
|
=head2 get_usermin_miniserv_users
|
|
|
|
Returns a list of Usermin users from miniserv.users. In normal use, there
|
|
is only one, as all authentication is done using Unix users.
|
|
|
|
=cut
|
|
sub get_usermin_miniserv_users
|
|
{
|
|
local %miniserv;
|
|
&get_usermin_miniserv_config(\%miniserv);
|
|
local @rv;
|
|
open(USERS, "<".$miniserv{'userfile'});
|
|
while(<USERS>) {
|
|
s/\r|\n//g;
|
|
local @u = split(/:/, $_);
|
|
push(@rv, { 'name' => $u[0],
|
|
'pass' => $u[1],
|
|
'sync' => $u[2],
|
|
'cert' => $u[3],
|
|
'allow' => $u[4] });
|
|
}
|
|
close(USERS);
|
|
return @rv;
|
|
}
|
|
|
|
=head2 save_usermin_miniserv_users(&user, ...)
|
|
|
|
Updats the list of Usermin miniserv users, each of which is a hash ref
|
|
in the format returned by get_usermin_miniserv_users.
|
|
|
|
=cut
|
|
sub save_usermin_miniserv_users
|
|
{
|
|
local %miniserv;
|
|
&get_usermin_miniserv_config(\%miniserv);
|
|
&open_tempfile(USERS, ">$miniserv{'userfile'}");
|
|
local $u;
|
|
foreach $u (@_) {
|
|
&print_tempfile(USERS,
|
|
join(":", $u->{'name'}, $u->{'pass'}, $u->{'sync'},
|
|
$u->{'cert'}, $u->{'allow'}),"\n");
|
|
}
|
|
&close_tempfile(USERS);
|
|
}
|
|
|
|
=head2 can_use_module(module)
|
|
|
|
Returns 1 if the current Webmin user can use some function of this module.
|
|
|
|
=cut
|
|
sub can_use_module
|
|
{
|
|
return 1 if ($access{'mods'} eq '*');
|
|
local @mods = split(/\s+/, $access{'mods'});
|
|
return &indexof($_[0], @mods) >= 0;
|
|
}
|
|
|
|
=head2 get_usermin_base_version
|
|
|
|
Gets the usermin version, rounded to the nearest .01
|
|
|
|
=cut
|
|
sub get_usermin_base_version
|
|
{
|
|
return &base_version(&get_usermin_version());
|
|
}
|
|
|
|
=head2 base_version
|
|
|
|
Rounds a version number to the nearest .01
|
|
|
|
=cut
|
|
sub base_version
|
|
{
|
|
return sprintf("%.2f0", $_[0]);
|
|
}
|
|
|
|
=head2 find_cron_job(\@jobs)
|
|
|
|
Finds the cron job for Usermin updates, given an array ref of cron jobs
|
|
as returned by cron::list_cron_jobs.
|
|
|
|
=cut
|
|
sub find_cron_job
|
|
{
|
|
local ($job) = grep { $_->{'user'} eq 'root' &&
|
|
$_->{'command'} eq $cron_cmd } @{$_[0]};
|
|
return $job;
|
|
}
|
|
|
|
=head2 delete_usermin_module(module, [delete-acls])
|
|
|
|
Deletes some usermin module, clone or theme, and return a description of
|
|
the thing deleted.
|
|
|
|
=cut
|
|
sub delete_usermin_module
|
|
{
|
|
local $m = $_[0];
|
|
return undef if (!$m);
|
|
local $qm = quotemeta($m);
|
|
local %minfo = &get_usermin_module_info($m);
|
|
%minfo = &get_usermin_theme_info($m) if (!%minfo);
|
|
return undef if (!%minfo);
|
|
local ($mdesc, @aclrm);
|
|
@aclrm = ( $m ) if ($_[1]);
|
|
local %miniserv;
|
|
&get_usermin_miniserv_config(\%miniserv);
|
|
local %ugconfig;
|
|
&get_usermin_config(\%uconfig);
|
|
local $mdir = "$miniserv{'root'}/$m";
|
|
local $cdir = "$config{'usermin_dir'}/$m";
|
|
if ($minfo{'clone'}) {
|
|
# Deleting a clone
|
|
local %cinfo;
|
|
&read_file("$config{'usermin_dir'}/$m/clone", \%cinfo);
|
|
&unlink_logged($mdir);
|
|
&system_logged("rm -rf ".quotemeta($cdir));
|
|
if ($ugconfig{'theme'}) {
|
|
&unlink_logged("$miniserv{'root'}/$ugconfig{'theme'}/$m");
|
|
}
|
|
$mdesc = &text('delete_desc1', $minfo{'desc'}, $minfo{'clone'});
|
|
}
|
|
else {
|
|
# Delete any clones of this module
|
|
local @clones;
|
|
local @mst = stat($mdir);
|
|
opendir(DIR, $miniserv{'root'});
|
|
local $l;
|
|
foreach $l (readdir(DIR)) {
|
|
@lst = stat("$miniserv{'root'}/$l");
|
|
if (-l "$miniserv{'root'}/$l" && $lst[1] == $mst[1]) {
|
|
&unlink_logged("$miniserv{'root'}/$l");
|
|
&system_logged("rm -rf ".quotemeta("$config{'usermin_dir'}/$l"));
|
|
push(@clones, $l);
|
|
}
|
|
}
|
|
closedir(DIR);
|
|
|
|
open(TYPE, "<$mdir/install-type");
|
|
chop($type = <TYPE>);
|
|
close(TYPE);
|
|
|
|
# Deleting the real module
|
|
local $size = &disk_usage_kb($mdir);
|
|
$mdesc = &text('delete_desc2', "<b>$minfo{'desc'}</b>",
|
|
"<tt>$mdir</tt>", $size);
|
|
if ($type eq 'rpm') {
|
|
# This module was installed from an RPM .. rpm -e it
|
|
&system_logged("rpm -e ubm-$qm");
|
|
}
|
|
else {
|
|
# Module was installed from a .wbm file .. just rm it
|
|
&system_logged("rm -rf ".quotemeta($mdir));
|
|
}
|
|
}
|
|
|
|
&webmin_log("delete", undef, $m, { 'desc' => $minfo{'desc'} });
|
|
return $mdesc;
|
|
}
|
|
|
|
=head2 flush_modules_cache
|
|
|
|
Forces a rebuild of the Usermin module cache.
|
|
|
|
=cut
|
|
sub flush_modules_cache
|
|
{
|
|
&unlink_file("$config{'usermin_dir'}/module.infos.cache");
|
|
}
|
|
|
|
=head2 stop_usermin
|
|
|
|
Kills the running Usermin server process. Return value is always undef.
|
|
|
|
=cut
|
|
sub stop_usermin
|
|
{
|
|
&system_logged("$config{'usermin_dir'}/stop >/dev/null 2>&1 </dev/null");
|
|
return undef;
|
|
}
|
|
|
|
=head2 start_usermin
|
|
|
|
Starts the Usermin server process. Return value is always undef.
|
|
|
|
=cut
|
|
sub start_usermin
|
|
{
|
|
&system_logged("$config{'usermin_dir'}/start >/dev/null 2>&1 </dev/null");
|
|
return undef;
|
|
}
|
|
|
|
=head2 get_install_type
|
|
|
|
Returns the package type Usermin was installed form (rpm, deb, solaris-pkg
|
|
or undef for tar.gz).
|
|
|
|
=cut
|
|
sub get_install_type
|
|
{
|
|
local (%miniserv, $mode);
|
|
&get_usermin_miniserv_config(\%miniserv);
|
|
if (open(MODE, "<$miniserv{'root'}/install-type")) {
|
|
chop($mode = <MODE>);
|
|
close(MODE);
|
|
}
|
|
else {
|
|
if ($miniserv{'root'} eq "/usr/libexec/usermin") {
|
|
$mode = "rpm";
|
|
}
|
|
elsif ($miniserv{'root'} eq "/usr/share/usermin") {
|
|
$mode = "deb";
|
|
}
|
|
else {
|
|
$mode = undef;
|
|
}
|
|
}
|
|
return $mode;
|
|
}
|
|
|
|
=head2 can_create_usermin_login_url(&miniserv)
|
|
|
|
Returns 1 if Usermin can accept a one-time login URL.
|
|
|
|
=cut
|
|
sub can_create_usermin_login_url
|
|
{
|
|
my ($miniserv) = @_;
|
|
return $miniserv->{'session'} &&
|
|
&check_pid_file($miniserv->{'pidfile'}) &&
|
|
defined(&acl::open_session_db) &&
|
|
defined(&acl::generate_random_session_id) &&
|
|
defined(&reload_usermin_miniserv);
|
|
}
|
|
|
|
=head2 create_usermin_login_url(username, [lifetime])
|
|
|
|
Creates a one-time URL that logs into Usermin as some user.
|
|
|
|
=cut
|
|
sub create_usermin_login_url
|
|
{
|
|
my ($user, $lifetime) = @_;
|
|
my %miniserv;
|
|
&get_usermin_miniserv_config(\%miniserv);
|
|
&can_create_usermin_login_url(\%miniserv) ||
|
|
&error($text{'switch_esession'});
|
|
&check_pid_file($miniserv{'pidfile'}) ||
|
|
&error($text{'switch_esession'});
|
|
$lifetime ||= 5*60;
|
|
my $sid = &create_usermin_login_session(\%miniserv, "-".$user,
|
|
$lifetime);
|
|
$sid || &error($text{'switch_esession'});
|
|
|
|
# Make sure the running Usermin process re-opens the session DBM, without
|
|
# stopping the service or racing its PID file re-creation.
|
|
&reload_usermin_miniserv();
|
|
my $url = &get_usermin_login_base_url(\%miniserv);
|
|
$url =~ s/\/+$//;
|
|
return $url."/session_login.cgi?session=".&urlize($sid);
|
|
}
|
|
|
|
=head2 create_usermin_login_session(&miniserv, username, lifetime)
|
|
|
|
Creates a one-time Usermin session token for the current browser.
|
|
|
|
=cut
|
|
sub create_usermin_login_session
|
|
{
|
|
my ($miniserv, $user, $lifetime) = @_;
|
|
return if (&is_readonly_mode());
|
|
my $sid = &acl::generate_random_session_id();
|
|
return if (!$sid);
|
|
my $ip = $ENV{'REMOTE_ADDR'} || "127.0.0.1";
|
|
my $now = time();
|
|
&acl::open_session_db($miniserv);
|
|
$acl::sessiondb{$sid} = "$user $now $ip".($lifetime ? " ".$lifetime : "");
|
|
dbmclose(%acl::sessiondb);
|
|
return $sid;
|
|
}
|
|
|
|
=head2 get_usermin_login_base_url(&miniserv)
|
|
|
|
Returns the base URL to use for browser handoff to Usermin.
|
|
|
|
=cut
|
|
sub get_usermin_login_base_url
|
|
{
|
|
my ($miniserv) = @_;
|
|
return $miniserv->{'redirect_url'} if ($miniserv->{'redirect_url'});
|
|
|
|
eval "use Net::SSLeay";
|
|
if ($@) {
|
|
$miniserv->{'ssl'} = 0;
|
|
}
|
|
my $ssl = $miniserv->{'ssl'} || $miniserv->{'inetd_ssl'};
|
|
|
|
# Work out redirect host
|
|
my @sockets = &webmin::get_miniserv_sockets($miniserv);
|
|
my ($host, $port, $url);
|
|
my %uconfig;
|
|
&get_usermin_config(\%uconfig);
|
|
if ($uconfig{'host'}) {
|
|
# Specific hostname set
|
|
$host = $uconfig{'host'};
|
|
}
|
|
else {
|
|
if ($sockets[0]->[0] ne "*") {
|
|
# Listening on special IP
|
|
$host = $sockets[0]->[0];
|
|
$port = $sockets[0]->[1] if ($sockets[0]->[1] ne '*');
|
|
}
|
|
else {
|
|
# Use same hostname as this server
|
|
$host = $ENV{'HTTP_HOST'};
|
|
$host =~ s/:.*//;
|
|
}
|
|
}
|
|
$port ||= $uconfig{'port'} || $miniserv->{'port'};
|
|
$url = ($ssl ? "https://" : "http://").$host.":".$port."/";
|
|
return $url;
|
|
}
|
|
|
|
=head2 switch_to_usermin_user(username)
|
|
|
|
Returns a redirect URL for auto-logging into Usermin as some user.
|
|
|
|
=cut
|
|
sub switch_to_usermin_user
|
|
{
|
|
my ($user) = @_;
|
|
my $url = &create_usermin_login_url($user);
|
|
return wantarray ? ("", $url) : $url;
|
|
}
|
|
|
|
=head2 get_usermin_email_url([module], [cgi], [force-default], [force-host])
|
|
|
|
Returns the base URL for accessing Usermin on this system, for use in emails.
|
|
|
|
=cut
|
|
sub get_usermin_email_url
|
|
{
|
|
my ($mod, $cgi, $def, $forcehost) = @_;
|
|
|
|
# Work out the base URL
|
|
my $url;
|
|
if (!$def && $config{'usermin_email_url'}) {
|
|
$url = $config{'usermin_email_url'};
|
|
}
|
|
else {
|
|
my %miniserv;
|
|
&get_usermin_miniserv_config(\%miniserv);
|
|
my $proto = $miniserv{'ssl'} ? 'https' : 'http';
|
|
my $port = $miniserv{'port'};
|
|
my $host = $forcehost || &get_system_hostname();
|
|
my $defport = $proto eq 'https' ? 443 : 80;
|
|
$url = $proto."://".$host.($port == $defport ? "" : ":".$port);
|
|
}
|
|
|
|
# Append module if needed
|
|
$url =~ s/\/$//;
|
|
if ($mod && $cgi) {
|
|
$url .= "/".$mod."/".$cgi;
|
|
}
|
|
elsif ($mod) {
|
|
$url .= "/".$mod."/";
|
|
}
|
|
elsif ($cgi) {
|
|
$url .= "/".$cgi;
|
|
}
|
|
return $url;
|
|
}
|
|
|
|
=head2 create_cron_wrapper(wrapper-path, module, script)
|
|
|
|
Creates a wrapper script which calls a script in some module's directory
|
|
with the proper usermin environment variables set.
|
|
|
|
The parameters are :
|
|
|
|
=item wrapper-path - Full path to the wrapper to create, like /etc/usermin/yourmodule/foo.pl
|
|
|
|
=item module - Module containing the real script to call.
|
|
|
|
=item script - Program within that module for the wrapper to run.
|
|
|
|
=cut
|
|
sub create_cron_wrapper
|
|
{
|
|
my ($wrapper, $mod, $script) = @_;
|
|
my $perl_path = &get_perl_path();
|
|
&open_tempfile(CMD, ">$wrapper");
|
|
&print_tempfile(CMD, <<EOF
|
|
#!$perl_path
|
|
open(CONF, "<$usermin_miniserv_config") || die "Failed to open $usermin_miniserv_config : \$!";
|
|
while(<CONF>) {
|
|
\$root = \$1 if (/^root=(.*)/);
|
|
}
|
|
close(CONF);
|
|
\$root || die "No root= line found in $usermin_miniserv_config";
|
|
\$ENV{'PERLLIB'} = "\$root";
|
|
\$ENV{'WEBMIN_CONFIG'} = "$config{'usermin_dir'}";
|
|
\$ENV{'WEBMIN_VAR'} = "$ENV{'WEBMIN_VAR'}";
|
|
delete(\$ENV{'MINISERV_CONFIG'});
|
|
EOF
|
|
);
|
|
if ($mod) {
|
|
&print_tempfile(CMD, "chdir(\"\$root/$mod\");\n");
|
|
&print_tempfile(CMD, "exec(\"\$root/$mod/$script\", \@ARGV) || die \"Failed to run \$root/$mod/$script : \$!\";\n");
|
|
}
|
|
else {
|
|
&print_tempfile(CMD, "chdir(\"\$root\");\n");
|
|
&print_tempfile(CMD, "exec(\"\$root/$script\", \@ARGV) || die \"Failed to run \$root/$script : \$!\";\n");
|
|
}
|
|
&close_tempfile(CMD);
|
|
chmod(0755, $wrapper);
|
|
}
|
|
|
|
1;
|