# STTA Rev. 1.3 Copyright (c) 2001-2004 Jesús Pérez Lorenzo  --- license GNU GPL 
# stta: (@#) 1.31051043346- [2004_01_29_102036]
# Lib Common 
# Just in case NO  WEBMIN (web-lib.pl, etc.) are loaded ...
# Some variables are needed and set here, will be reassigned later
# Globals ...
# Some functions are hooks 
# others are empty to avoid full Webmin code for line command execution

# Vital libraries
use Socket;
#use SelfLoader;

$stta_sid="";

# Read Config File 
sub initialize () {
   $no_out=$config{output_cgis};
   $scriptname=$tool_name;
   &read_file("$config_directory/config", \%gconfig);
   if ( $gconfig{lang} && $gconfig{lang} ne "" ) { $current_lang=$gconfig{lang} }
   &read_file("$config_directory/miniserv.conf", \%gconfig);
   $module_name="stta";
   $root_directory=$gconfig{root};
}
sub start_debug () {
   if ( $debug gt 0 ) {
         if ( !$DebugFile ) { $DebugFile=$config{stta_debugname}.$tool_name.".log";}
         open(DebugLog, ">>$DebugFile");
         if ( !DebugLog) { $debug=0 };
   }
}

# Read Module Text 
# load_language([module])
# Returns a hashtable mapping text codes to strings in the appropriate language
sub load_language
{
local %text;
if ($module_name) {
        local $mod = $_[0] ? "../$_[0]" : ".";
        &read_file("../lang/$default_lang", \%text);
        &read_file("../lang/$current_lang", \%text);
        &read_file("$mod/lang/$default_lang", \%text);
        &read_file("$mod/lang/$current_lang", \%text);
        }
else {
        &read_file("lang/$default_lang", \%text);
        &read_file("lang/$current_lang", \%text);
        if ($_[0]) {
                &read_file("$_[0]/lang/$default_lang", \%text);
                &read_file("$_[0]/lang/$current_lang", \%text);
                }
        }
foreach $k (keys %text) {
        $text{$k} =~ s/\$([A-Za-z0-9\.\-\_]+)/text_subs($1,\%text)/ge;
        }
return %text;
}

sub text_subs
{
local $t = $_[1]->{$_[0]};
return defined($t) ? $t : '$'.$_[0];
}
 
# text(message, [substitute]+)
sub text
{
local $rv = $text{$_[0]};
local $i;
for($i=1; $i<@_; $i++) {
        $rv =~ s/\$$i/$_[$i]/g;
        }
return $rv;
}

# read_file(file, &assoc, [&order], [lowercase])
# Fill an associative array with name=value pairs from a file
sub read_file
{
    open(ARFILE, $_[0]) || return 0;
    while(<ARFILE>) {
	    s/\r|\n//g;
            if (!/^#/ && /^([^=]+)=(.*)$/) {
		    $_[1]->{$_[3] ? lc($1) : $1} = $2;
		    push(@{$_[2]}, $1) if ($_[2]);
        	    }
            }
    close(ARFILE);
    return 1;
}

# write_file(file, array)
# Write out the contents of an associative array as name=value lines
sub write_file
{
local(%old, @order);
&read_file($_[0], \%old, \@order);
open(ARFILE, ">$_[0]");
foreach $k (@order) {
        print ARFILE $k,"=",$_[1]->{$k},"\n" if (exists($_[1]->{$k}));
        }
foreach $k (keys %{$_[1]}) {
        print ARFILE $k,"=",$_[1]->{$k},"\n" if (!exists($old{$k}));
        }
close(ARFILE);
}

sub lock_file
{
    return 1;
}
sub unlock_file
{
    return 1;
}


# get_module_acl([user], [module])
# Returns an array containing access control options for the given user
sub get_module_acl
{
local %rv;
local $u = defined($_[0]) ? $_[0] : $base_remote_user;
local $m = defined($_[1]) ? $_[1] : $module_name;
&read_file($module_name ? "../$m/defaultacl" : "./$m/defaultacl", \%rv);
if ($gconfig{"risk_$u"} && $m) {
        local $rf = $gconfig{"risk_$u"}.'.risk';
        &read_file($module_name ? "../$m/$rf" : "./$m/$rf", \%rv);
 
        local $sf = $gconfig{"skill_$u"}.'.skill';
        &read_file($module_name ? "../$m/$sf" : "./$m/$sf", \%rv);
        }
else {
        &read_file("$config_directory/$m/$u.acl", \%rv);
        }
return %rv;
}

# webmin_log(action, type, object, &params, [module])
# Log some action taken by a user
sub webmin_log
{
return if (!$config{'stta_logname'});
local $m = $_[4] ? $_[4] : $module_name;
#local $m_logfile=$config{'stta_logname'}.$tool_name.".log";
local $m_logfile="/var/webmin/webmin.log";

#$m="$tool_name ($m)";
$m="tarantella";
# log the action
local $now = time();
local @tm = localtime($now);
local $script_name = $0 =~ /([^\/]+)$/ ? $1 : '-';
local $id = sprintf "%d.%d.%d",
		$now, $$, $main::action_id_count;
$stta_sid=$id;
$main::action_id_count++;
local $line = sprintf "%s [%2.2d/%s/%4.4d %2.2d:%2.2d:%2.2d] %s %s %s %s %s \"%s\" \"%s\" \"%s\"",
	$id, $tm[3], $text{"smonth_".($tm[4]+1)}, $tm[5]+1900,
	$tm[2], $tm[1], $tm[0],
	$remote_user, $main::session_id ? $main::session_id : '-',
	$ENV{'REMOTE_HOST'},
	$m, $script_name,
	$_[0], $_[1] ne '' ? $_[1] : '-', $_[2] ne '' ? $_[2] : '-';
foreach $k (sort { $a cmp $b } keys %{$_[3]}) {
	local $v = $_[3]->{$k};
	if ($v eq '') {
		$line .= " $k=''";
		}
	elsif (ref($v) eq 'ARRAY') {
		foreach $vv (@$v) {
			next if (ref($vv));
			$vv =~ s/(['"\\\r\n\t\%])/sprintf("%%%2.2X",ord($1))/ge;
			$line .= " $k='$vv'";
			}
		}
	elsif (!ref($v)) {
		foreach $vv (split(/\0/, $v)) {
			$vv =~ s/(['"\\\r\n\t\%])/sprintf("%%%2.2X",ord($1))/ge;
			$line .= " $k='$vv'";
			}
		}
	}
  if ( $debug gt 0 ) {
    print DebugLog $line,"\n";
  } else {
    open(WEBMINLOG, ">>$m_logfile");
    print WEBMINLOG $line,"\n";
    close(WEBMINLOG);
  }

}

# additional_log(type, object, data)
# Records additional log data for an upcoming call to webmin_log, such
# as command that was run or SQL that was executed.
sub additional_log
{
   return if (!$config{'stta_logname'});
   webmin_log($_[0],$_[1],$_[2],$_[3],$_[4]);
}

sub endclose_debug
{
   print DebugLog "----------------------------------------------\n";
   close (DebugLog);
}

# error([message]+)
# Display an error message and exit. The variable $whatfailed must be set
# to the name of the operation that failed.
sub error
{
   if ( $debug gt 0 ) {
        print DebugLog "$text{'error'}: ";
        print DebugLog ($whatfailed ? "$whatfailed : " : ""),@_,"\n";
   }
   if ( $debug gt 0 ) { &endclose_debug(); }   
   exit;
}

sub SrvError
{
      print DebugLog @_,"\n";
      if ( $debug gt 0 ) { &endclose_debug(); }   
      exit;
}
 
# error_setup(message)
# Register a message to be prepended to all error strings
sub error_setup
{
$whatfailed = $_[0];
}

# unique
# Returns the unique elements of some array
sub unique
{
local(%found, @rv, $e);
foreach $e (@_) {
        if (!$found{$e}++) { push(@rv, $e); }
        }
return @rv;
}

# seed_random()
# Seeds the random number generator, if needed
sub seed_random
{
if (!$main::done_seed_random) {
        if (open(RANDOM, "/dev/urandom")) {
                local $buf;
                read(RANDOM, $buf, 4);
                close(RANDOM);
                srand(time() ^ $$ ^ $buf);
                }
        else {
                srand(time() ^ $$);
                }
        $main::done_seed_random = 1;
        }
}

# tempname([filename])
# Returns a mostly random temporary file name
sub tempname
{
local $tmp_dir = "/tmp/.webmin";
while(1) {
        local @st = lstat($tmp_dir);
        last if (!$st[4] && !$st[5] && $st[2] & 0x4000 &&
                 ($st[2] & 0777) == 0755);
        if (@st) {
                unlink($tmp_dir) || rmdir($tmp_dir) ||
                        system("/bin/rm -rf \"$tmp_dir\"");
                }
        mkdir($tmp_dir, 0755) || next;
        chown(0, 0, $tmp_dir);
        chmod(0755, $tmp_dir);
        }
if (defined($_[0]) && $_[0] !~ /\.\./) {
        return "$tmp_dir/$_[0]";
        }
else {
        $main::tempfilecount++;
        &seed_random();
        return $tmp_dir."/".int(rand(1000000))."_".
               $main::tempfilecount."_".$scriptname;
        }
}

sub html_errshow
{
   local $where_err = $_[0];

   if ( !$DebugFile ) {
        print DebugLog  "$wher_err ($err) $stta_sid:".eval("\$text{'msgerr_$err'}")."\n";
   }

}

# foreign_call(module, function, [arg]*)
# Call a function in another module
sub foreign_call
{
local $pkg = $_[0] ? $_[0] : "global";
$pkg =~ s/[^A-Za-z0-9]/_/g;
local @args = @_[2 .. @_-1];
$main::foreign_args = \@args;
local @rv = eval <<EOF;
#package $pkg;
&$_[1](\@{\$main::foreign_args});
EOF
if ($@) { &error("$_[0]::$_[1] failed : $@"); }
return wantarray ? @rv : $rv[0];
}

# safe_process_exec(command, uid, gid, handle, input, fixtags, bsmode)
# Executes the given command as the given user/group and writes all output
# to the given file handle. Finishes when there is no more output or the
# process stops running. Returns the number of bytes read.
sub safe_process_exec
{
# setup pipes and fork the process
local $chld = $SIG{'CHLD'};
pipe(OUTr, OUTw);
pipe(INr, INw);
local $pid = fork();
if (!$pid) {
	#setsid();
	untie(*STDIN);
	untie(*STDOUT);
	untie(*STDERR);
	open(STDIN, "<&INr");
	open(STDOUT, ">&OUTw");
	open(STDERR, ">&OUTw");
	$| = 1;
	close(OUTr); close(INw);

	if ($_[1]) {
		if (defined($_[2])) {
			# switch to given UID and GID
			$( = $_[2]; $) = "$_[2] $_[2]";
			($>, $<) = ($_[1], $_[1]);
			}
		}

	# run the command
	exec("/bin/sh", "-c", $_[0]);
#	print "Exec failed : $!\n";
	exit 1;
	}
close(OUTw); close(INr);
#	print "Exec $_[0]\n";

# Feed input (if any)
print INw $_[4];
close(INw);

# Read and show output
local $fn = fileno(OUTr);
local $got = 0;
local $out = $_[3];
local $line;
while(1) {
	local ($rmask, $buf);
	vec($rmask, $fn, 1) = 1;
	local $sel = select($rmask, undef, undef, 1);
	if ($sel > 0 && vec($rmask, $fn, 1)) {
		# got something to read.. print it
		sysread(OUTr, $buf, 1024) || last;
		$got += length($buf);
		if ($_[5]) {
			$buf = &html_escape($buf);
			}
		if ($_[6]) {
			# Convert backspaces and returns and escapes
			$line .= $buf;
			while($line =~ s/^([^\n]*\n)//) {
				local $one = $1;
				while($one =~ s/.\010//) { }
				$out =~ s/\033[^m]+m//g;
				print $out $one;
				}
			}
		else {
			print $out $buf;
			}
		}
	elsif ($sel == 0) {
		# nothing to read. maybe the process is done, and a subprocess
		# is hanging things up
		last if (!kill(0, $pid));
		}
	}
close(OUTr);
print $out $line;
return $got;
}

1;  # Return true    
