<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;"># webalizer-lib.pl
# Common functions for editing the webalizer config file

use strict;
use warnings;
no warnings 'redefine';
BEGIN { push(@INC, ".."); };
use WebminCore;
&amp;init_config();
our ($module_root_directory, %text, %config, $module_config_directory);

our $cron_cmd = "$module_config_directory/webalizer.pl";
our $custom_logs_file = "$module_config_directory/custom-logs";
our %access = &amp;get_module_acl();

# Use sample config if needed
if (!-r $config{'webalizer_conf'} &amp;&amp; $config{'alt_conf'} &amp;&amp;
    -r $config{'alt_conf'}) {
	&amp;copy_source_dest($config{'alt_conf'}, $config{'webalizer_conf'});
	}

# get_config([logfile])
# Parse the webalizer config file for a single logfile or global
sub get_config
{
my ($logfile) = @_;
my $file;
if ($logfile) {
	$file = &amp;config_file_name($logfile);
	}
$file = $config{'webalizer_conf'} if (!$file || !-r $file);
-r $file || &amp;error("Webalizer config file $file does not exist!");
my @rv;
my $lnum = 0;
open(FILE, $file);
while(&lt;FILE&gt;) {
	s/\r|\n//g;
	s/#.*$//;
	if (/^\s*(\S+)\s+(.*)/) {
		push(@rv, { 'name' =&gt; $1,
			    'value' =&gt; $2,
			    'line' =&gt; $lnum,
			    'file' =&gt; $file,
			    'index' =&gt; scalar(@rv) });
		}
	$lnum++;
	}
close(FILE);
return \@rv;
}

# save_directive(&amp;config, name, [value]*)
sub save_directive
{
my ($conf, $name, @values) = @_;
my @old = &amp;find($name, $conf);
my $lref = &amp;read_file_lines($conf-&gt;[0]-&gt;{'file'});
for(my $i=0; $i&lt;@old || $i&lt;@values; $i++) {
	if ($i &lt; @old &amp;&amp; $i &lt; @values) {
		# Just replacing a line
		$lref-&gt;[$old[$i]-&gt;{'line'}] = "$name $values[$i]";
		}
	elsif ($i &lt; @old) {
		# Deleting a line
		splice(@$lref, $old[$i]-&gt;{'line'}, 1);
		&amp;renumber($conf, $old[$i]-&gt;{'line'}, -1);
		}
	elsif ($i &lt; @values) {
		# Adding a line
		if (@old) {
			# after the last one of the same type
			splice(@$lref, $old[$#old]-&gt;{'line'}+1, 0,
			       "$name $values[$i]");
			&amp;renumber($conf, $old[$#old]-&gt;{'line'}+1, 1);
			}
		else {
			# at end of file
			push(@$lref, "$name $values[$i]");
			}
		}
	}
}

# renumber(&amp;config, line, offset)
sub renumber
{
my ($conf, $line, $offset) = @_;
foreach my $c (@$conf) {
	$c-&gt;{'line'} += $offset if ($c-&gt;{'line'} &gt;= $line);
	}
}

# config_file_name(logfile)
sub config_file_name
{
my ($p) = @_;
$p =~ s/^\///;
$p =~ s/\//_/g;
return "$module_config_directory/$p.conf";
}

# find(name, &amp;config)
sub find
{
my ($name, $conf) = @_;
my @rv;
foreach my $c (@$conf) {
	push(@rv, $c) if (lc($c-&gt;{'name'}) eq lc($name));
	}
return wantarray ? @rv : $rv[0];
}

# find_value(name, &amp;config)
sub find_value
{
my @rv = map { $_-&gt;{'value'} } &amp;find(@_);
return wantarray ? @rv : $rv[0];
}

# all_log_files(file)
# Given a base log file name, returns a list of all log files in the same
# directory that start with the same name
sub all_log_files
{
my ($file) = @_;
$file =~ /^(.*)\/([^\/]+)$/;
my $dir = $1;
my $base = $2;
my @rv;
opendir(DIR, $dir);
foreach my $f (readdir(DIR)) {
	if ($f =~ /^\Q$base\E/ &amp;&amp; -f "$dir/$f") {
		push(@rv, "$dir/$f");
		}
	}
closedir(DIR);
return @rv;
}

# get_log_config(path)
# Get the configuration for some log file
sub get_log_config
{
my ($path) = @_;
my %rv;
&amp;read_file(&amp;log_config_name($path), \%rv) || return undef;
return \%rv;
}

# save_log_config(path, &amp;config)
sub save_log_config
{
my ($path, $conf) = @_;
return &amp;write_file(&amp;log_config_name($path), $conf);
}

# log_config_name(path)
sub log_config_name
{
my ($p) = @_;
$p =~ s/^\///;
$p =~ s/\//_/g;
return "$module_config_directory/$p.log";
}

# generate_report(file, handle, escape)
# Generates a new webalizer report to the configured directory, and sends
# any output to the given file handle. Returns 1 if any of the log files
# worked OK.
sub generate_report
{
my $h = $_[1];
my $lconf = &amp;get_log_config($_[0]);
my @all = $config{'skip_old'} ? ( $_[0] ) : &amp;all_log_files($_[0]);
if (!@all) {
	print $h "Log file $_[0] does not exist\n";
	return;
	}
my %mtime;
foreach my $a (@all) {
	my @st = stat($a);
	$mtime{$a} = $st[9];
	}
my $prog = &amp;get_webalizer_prog();
my $type = $lconf-&gt;{'type'} == 1 ? "" :
	      $lconf-&gt;{'type'} == 2 ? "-F squid" :
	      $lconf-&gt;{'type'} == 3 ? "-F ftp" : "";
my $cfile = &amp;config_file_name($_[0]);
my $conf = -r $cfile ? "-c $cfile" : "";
if ($lconf-&gt;{'over'} &amp;&amp; !&amp;is_readonly_mode()) {
	unlink("$lconf-&gt;{'dir'}/webalizer.current");
	unlink("$lconf-&gt;{'dir'}/webalizer.hist");
	}
unlink("$lconf-&gt;{'dir'}/__db.dns_cache.db");
my $user = $lconf-&gt;{'user'} || "root";
if ($user ne "root" &amp;&amp; -r $cfile) {
	chmod(0644, $cfile);
	}
if (!-d $lconf-&gt;{'dir'}) {
	mkdir($lconf-&gt;{'dir'}, 0755);
	if ($user ne "root") {
		my @uinfo = getpwnam($user);
		chown($uinfo[2], $uinfo[3], $lconf-&gt;{'dir'});
		}
	}
my $anyok = 0;
foreach my $f (sort { $mtime{$a} &lt;=&gt; $mtime{$b} } @all) {
	my $cmd = "$config{'webalizer'} $conf -o ".
		     quotemeta($lconf-&gt;{'dir'})." $type -p ".quotemeta($f);
	if ($user ne "root") {
		$cmd = &amp;command_as_user($user, 0, $cmd);
		}
	my $fh = "OUT";
	&amp;open_execute_command($fh, "$cmd 2&gt;&amp;1", 1);
	while(&lt;$fh&gt;) {
		print $h $_[2] ? &amp;html_escape($_) : $_;
		}
	close($fh);
	$anyok = 1 if (!$?);
	&amp;additional_log("exec", undef, $cmd);
	}
return $anyok;
}

# spaced_buttons(button, ...)
sub spaced_buttons
{
my $pc = int(100 / scalar(@_));
print "&lt;table width=100%&gt;&lt;tr&gt;\n";
foreach $b (@_) {
	my $al = $b eq $_[0] ? "align=left" :
		    $b eq $_[@_-1] ? "align=right" : "align=center";
	print "&lt;td width=$pc% $al&gt;$b&lt;/td&gt;\n";
	}
print "&lt;/table&gt;\n";
}

# read_custom_logs()
sub read_custom_logs
{
open(LOGS, $custom_logs_file) || return ();
my @rv = map { /^(.*\S)\s+(\S+)/; { 'file' =&gt; $1, 'type' =&gt; $2 } } &lt;LOGS&gt;;
close(LOGS);
return @rv;
}

# write_custom_logs(log, ...)
sub write_custom_logs
{
my $fh = "LOGS";
&amp;open_tempfile($fh, "&gt;$custom_logs_file");
&amp;print_tempfile($fh, map { "$_-&gt;{'file'} $_-&gt;{'type'}\n" } @_);
&amp;close_tempfile($fh);
}

# can_edit_log(file)
sub can_edit_log
{
foreach my $d (split(/\s+/, $access{'dir'})) {
	my $ok = &amp;is_under_directory($d, $_[0]);
	return 1 if ($ok);
	}
return 0;
}

# get_webalizer_version(&amp;out)
# Returns the Webalizer version number, and puts output into the given scalar
# reference.
sub get_webalizer_version
{
my $out = &amp;backquote_command("$config{'webalizer'} -V 2&gt;&amp;1 &lt;/dev/null");
${$_[0]} = $out;
return $out =~ /\sV(\S+)/ ? $1 : undef;
}

# get_webalizer_prog()
# Returns either 'webalizer' or 'awffull'
sub get_webalizer_prog
{
return $config{'webalizer'} =~ /awffull/i ? "awffull" : "webalizer";
}

# get_all_logs()
# Returns a list of all log files the module can report on
sub get_all_logs
{
# Query apache and squid for their logfiles
my %auto = map { $_, 1 } split(/,/, $config{'auto'});
my @logs;
if (&amp;foreign_installed("apache") &amp;&amp; $auto{'apache'}) {
	&amp;foreign_require("apache", "apache-lib.pl");
	my $conf = &amp;apache::get_config();
	my @dirs = ( &amp;apache::find_all_directives($conf, "CustomLog"),
		  	&amp;apache::find_all_directives($conf, "TransferLog") );
	my $root = &amp;apache::find_directive_struct("ServerRoot", $conf);
	my $d;
	foreach $d (@dirs) {
		my $lf = $d-&gt;{'words'}-&gt;[0];
		if ($lf =~ /^\|\S+writelogs.pl\s+\S+\s+(\S+)/) {
			# Virtualmin log writer .. use real file
			$lf = $1;
			}
		next if ($lf =~ /^\|/);
		if ($lf !~ /^\//) {
			$lf = "$root-&gt;{'words'}-&gt;[0]/$lf";
			}
		open(FILE, $lf);
		my $line = &lt;FILE&gt;;
		close(FILE);
		if (!$line || $line =~ /^([a-zA-Z0-9\.\-\:]+)\s+\S+\s+\S+\s+\[\d+\/[a-zA-z]+\/\d+:\d+:\d+:\d+\s+[0-9\+\-]+\]/) {
			push(@logs, { 'file' =&gt; $lf,
				      'type' =&gt; 1 });
			}
		}
	}

# Add log file from Squid
if (&amp;foreign_installed("squid") &amp;&amp; $auto{'squid'}) {
	&amp;foreign_require("squid", "squid-lib.pl");
	my $conf = &amp;squid::get_config();
	my $log = &amp;squid::find_value("cache_access_log", $conf);
	$log = "$squid::config{'log_dir'}/access.log"
		if (!$log &amp;&amp; -d $squid::config{'log_dir'});
	push(@logs, { 'file' =&gt; $log,
		      'type' =&gt; 2 }) if ($log);
	}

# Add log file from proftpd
if (&amp;foreign_installed("proftpd") &amp;&amp; $auto{'proftpd'}) {
	&amp;foreign_require("proftpd", "proftpd-lib.pl");
	my $conf = &amp;proftpd::get_config();
	my $global = &amp;proftpd::find_directive_struct("Global", $conf);
	my $log = &amp;proftpd::find_directive("TransferLog", $global-&gt;{'members'}) || "/var/log/xferlog";
	push(@logs, { 'file' =&gt; $log, 'type' =&gt; 3 });
	}

# Add log file from wu-ftpd
if (&amp;foreign_installed("wuftpd") &amp;&amp; $auto{'wuftpd'}) {
	my %wconfig = &amp;foreign_config("wuftpd");
	push(@logs, { 'file' =&gt; $wconfig{'log_file'}, 'type' =&gt; 3 });
	}

# Add custom logfiles
push(@logs, map { $_-&gt;{'custom'} = 1; $_ } &amp;read_custom_logs());
foreach my $l (@logs) {
	$l-&gt;{'custom'} ||= 0;
	}

return @logs;
}

# lconf_to_cron(&amp;lconf, &amp;job)
# Copy fields from a webalizer config to a cron job
sub lconf_to_cron
{
my ($lconf, $job) = @_;
$job-&gt;{'special'} = $lconf-&gt;{'special'};
$job-&gt;{'mins'} = $lconf-&gt;{'mins'};
$job-&gt;{'hours'} = $lconf-&gt;{'hours'};
$job-&gt;{'days'} = $lconf-&gt;{'days'};
$job-&gt;{'months'} = $lconf-&gt;{'months'};
$job-&gt;{'weekdays'} = $lconf-&gt;{'weekdays'};
}

1;

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