=head2 init_config_full

This function must be called by all Webmin CGI scripts, either directly or
indirectly via a per-module lib.pl file. It performs a number of initialization
and housekeeping tasks, such as working out the module name, checking that the
current user has access to the module, and populating global variables. Some
of the variables set include :

=item $config_directory - Base Webmin config directory, typically /etc/webmin

=item $var_directory - Base logs directory, typically /var/webmin

=item %config - Per-module configuration.

=item %gconfig - Global configuration.

=item $scriptname - Base name of the current perl script.

=item $module_name - The name of the current module.

=item $module_config_directory - The config directory for this module.

=item $module_config_file - The config file for this module.

=item $module_var_directory - The data directory for this module.

=item $module_root_directory - This module's code directory.

=item $webmin_logfile - The detailed logfile for webmin.

=item $remote_user - The actual username used to login to webmin.

=item $base_remote_user - The username whose permissions are in effect.

=item $current_theme - The theme currently in use.

=item $root_directory - The first root directory of this webmin install.

=item @root_directories - All root directories for this webmin install.

=cut
sub init_config_full
{
# Record first process ID that called this, so we know when it exited to clean
# up temp files
$main::initial_process_id ||= $$;

# Configuration and spool directories
if (!defined($ENV{'WEBMIN_CONFIG'})) {
	die "WEBMIN_CONFIG not set";
	}
$config_directory = $ENV{'WEBMIN_CONFIG'};
if (!defined($ENV{'WEBMIN_VAR'})) {
	open(VARPATH, "$config_directory/var-path");
	chop($var_directory = <VARPATH>);
	close(VARPATH);
	}
else {
	$var_directory = $ENV{'WEBMIN_VAR'};
	}
$main::http_cache_directory = $ENV{'WEBMIN_VAR'}."/cache";
$main::default_debug_log_file = $ENV{'WEBMIN_VAR'}."/webmin.debug";

if ($ENV{'SESSION_ID'}) {
	# Hide this variable from called programs, but keep it for internal use
	$main::session_id = $ENV{'SESSION_ID'};
	delete($ENV{'SESSION_ID'});
	}
if ($ENV{'REMOTE_PASS'}) {
	# Hide the password too
	$main::remote_pass = $ENV{'REMOTE_PASS'};
	delete($ENV{'REMOTE_PASS'});
	}

if ($> == 0 && $< != 0 && !$ENV{'FOREIGN_MODULE_NAME'}) {
	# Looks like we are running setuid, but the real UID hasn't been set.
	# Do so now, so that executed programs don't get confused
	$( = $);
	$< = $>;
	}

# Read the webmin global config file. This contains the OS type and version,
# OS specific configuration and global options such as proxy servers
$config_file = "$config_directory/config";
%gconfig = ( );
&read_file_cached($config_file, \%gconfig);
$gconfig{'webprefix'} = '' if (!exists($gconfig{'webprefix'}));
$null_file = $gconfig{'os_type'} eq 'windows' ? "NUL" : "/dev/null";
$path_separator = $gconfig{'os_type'} eq 'windows' ? ';' : ':';

# Work out of this is a web, command line or cron job
if (!$main::webmin_script_type) {
	if ($ENV{'SCRIPT_NAME'}) {
		# Run via a CGI
		$main::webmin_script_type = 'web';
		}
	else {
		# Cron jobs have no TTY
		if ($gconfig{'os_type'} eq 'windows' ||
		    open(DEVTTY, ">/dev/tty")) {
			$main::webmin_script_type = 'cmd';
			close(DEVTTY);
			}
		else {
			$main::webmin_script_type = 'cron';
			}
		}
	}

# If this is a cron job, suppress STDERR warnings
if ($main::webmin_script_type eq 'cron') {
	$SIG{__WARN__} = sub { };
	}

# If debugging is enabled, open the debug log
if (($ENV{'WEBMIN_DEBUG'} || $gconfig{'debug_enabled'}) &&
    !$main::opened_debug_log++) {
	my $dlog = $gconfig{'debug_file'} || $main::default_debug_log_file;
	my $dsize = $gconfig{'debug_size'} || $main::default_debug_log_size;
	my @st = stat($dlog);
	if ($dsize && $st[7] > $dsize) {
		rename($dlog, $dlog.".0");
		}

	open(main::DEBUGLOG, ">>$dlog");
	$main::opened_debug_log = 1;

	if ($gconfig{'debug_what_start'}) {
		my $script_name = $0 =~ /([^\/]+)$/ ? $1 : '-';
		$main::debug_log_start_time = time();
		&webmin_debug_log("START", "script=$script_name");
		}
	}

# Set PATH and LD_LIBRARY_PATH
if ($gconfig{'path'}) {
	if ($gconfig{'syspath'}) {
		# Webmin only
		$ENV{'PATH'} = $gconfig{'path'};
		}
	else {
		# Include OS too
		$ENV{'PATH'} = $gconfig{'path'}.$path_separator.$ENV{'PATH'};
		}
	}
$ENV{$gconfig{'ld_env'}} = $gconfig{'ld_path'} if ($gconfig{'ld_env'});

# Set http_proxy and ftp_proxy environment variables, based on Webmin settings
if ($gconfig{'http_proxy'}) {
	$ENV{'http_proxy'} = $gconfig{'http_proxy'};
	}
if ($gconfig{'ftp_proxy'}) {
	$ENV{'ftp_proxy'} = $gconfig{'ftp_proxy'};
	}
if ($gconfig{'noproxy'}) {
	$ENV{'no_proxy'} = $gconfig{'noproxy'};
	}

# Find all root directories
my %miniserv;
if (&get_miniserv_config(\%miniserv)) {
	@root_directories = ( $miniserv{'root'} );
	for($i=0; defined($miniserv{"extraroot_$i"}); $i++) {
		push(@root_directories, $miniserv{"extraroot_$i"});
		}
	}

# Work out which module we are in, and read the per-module config file
$0 =~ s/\\/\//g;	# Force consistent path on Windows
if (defined($ENV{'FOREIGN_MODULE_NAME'}) && $ENV{'FOREIGN_ROOT_DIRECTORY'}) {
	# In a foreign call - use the module name given
	$root_directory = $ENV{'FOREIGN_ROOT_DIRECTORY'};
	$module_name = $ENV{'FOREIGN_MODULE_NAME'};
	@root_directories = ( $root_directory ) if (!@root_directories);
	}
elsif ($ENV{'SCRIPT_NAME'}) {
	my $sn = $ENV{'SCRIPT_NAME'};
	$sn =~ s/^$gconfig{'webprefix'}//
		if (!$gconfig{'webprefixnoredir'});
	if ($sn =~ /^\/([^\/]+)\//) {
		# Get module name from CGI path
		$module_name = $1;
		}
	if ($ENV{'SERVER_ROOT'}) {
		$root_directory = $ENV{'SERVER_ROOT'};
		}
	elsif ($ENV{'SCRIPT_FILENAME'}) {
		$root_directory = $ENV{'SCRIPT_FILENAME'};
		$root_directory =~ s/$sn$//;
		}
	@root_directories = ( $root_directory ) if (!@root_directories);
	}
else {
	# Get root directory from miniserv.conf, and deduce module name from $0
	$root_directory = $root_directories[0];
	my $rok = 0;
	foreach my $r (@root_directories) {
		if ($0 =~ /^$r\/([^\/]+)\/[^\/]+$/i) {
			# Under a module directory
			$module_name = $1;
			$rok = 1;
			last;
			}
		elsif ($0 =~ /^$root_directory\/[^\/]+$/i) {
			# At the top level
			$rok = 1;
			last;
			}
		}
	&error("Script was not run with full path (failed to find $0 under $root_directory)") if (!$rok);
	}

# Set the umask based on config
if ($gconfig{'umask'} ne '' && !$main::umask_already++) {
	umask(oct($gconfig{'umask'}));
	}

# If this is a cron job or other background task, set the nice level
if (!$main::nice_already && $main::webmin_script_type eq 'cron') {
	# Set nice level
	if ($gconfig{'nice'}) {
		eval 'POSIX::nice($gconfig{\'nice\'});';
		}

	# Set IO scheduling class and priority
	if ($gconfig{'sclass'} ne '' || $gconfig{'sprio'} ne '') {
		my $cmd = "ionice";
		$cmd .= " -c ".quotemeta($gconfig{'sclass'})
			if ($gconfig{'sclass'} ne '');
		$cmd .= " -n ".quotemeta($gconfig{'sprio'})
			if ($gconfig{'sprio'} ne '');
		$cmd .= " -p $$";
		&execute_command("$cmd >/dev/null 2>&1");
		}
	}
$main::nice_already++;

# Get the username
my $u = $ENV{'BASE_REMOTE_USER'} || $ENV{'REMOTE_USER'};
$base_remote_user = $u;
$remote_user = $ENV{'REMOTE_USER'};

# Work out if user is definitely in the DB, and if so get his attrs
$remote_user_proto = $ENV{"REMOTE_USER_PROTO"};
%remote_user_attrs = ( );
if ($remote_user_proto) {
	my $userdb = &get_userdb_string();
	my ($dbh, $proto, $prefix, $args) =
		$userdb ? &connect_userdb($userdb) : ( );
	if (ref($dbh)) {
		if ($proto eq "mysql" || $proto eq "postgresql") {
			# Read attrs from SQL
			my $cmd = $dbh->prepare("select webmin_user_attr.attr,webmin_user_attr.value from webmin_user_attr,webmin_user where webmin_user_attr.id = webmin_user.id and webmin_user.name = ?");
			if ($cmd && $cmd->execute($base_remote_user)) {
				while(my ($attr, $value) = $cmd->fetchrow()) {
					$remote_user_attrs{$attr} = $value;
					}
				$cmd->finish();
				}
			}
		elsif ($proto eq "ldap") {
			# Read attrs from LDAP
			my $rv = $dbh->search(
				base => $prefix,
				filter => '(&(cn='.$base_remote_user.')'.
					  '(objectClass='.
					  $args->{'userclass'}.'))',
				scope => 'sub');
			my ($u) = $rv && !$rv->code ? $rv->all_entries : ( );
			if ($u) {
				foreach $la ($u->get_value('webminAttr')) {
					my ($attr, $value) = split(/=/, $la, 2);
					$remote_user_attrs{$attr} = $value;
					}
				}
			}
		&disconnect_userdb($userdb, $dbh);
		}
	}

if ($module_name) {
	# Find and load the configuration file for this module
	my (@ruinfo, $rgroup);
	$module_config_directory = "$config_directory/$module_name";
	if (&get_product_name() eq "usermin" &&
	    -r "$module_config_directory/config.$remote_user") {
		# Based on username
		$module_config_file = "$module_config_directory/config.$remote_user";
		}
	elsif (&get_product_name() eq "usermin" &&
	    (@ruinfo = getpwnam($remote_user)) &&
	    ($rgroup = getgrgid($ruinfo[3])) &&
	    -r "$module_config_directory/config.\@$rgroup") {
		# Based on group name
		$module_config_file = "$module_config_directory/config.\@$rgroup";
		}
	else {
		# Global config
		$module_config_file = "$module_config_directory/config";
		}
	%config = ( );
	&read_file_cached($module_config_file, \%config);

	# Create a module-specific var directory
	my $var_base = "$var_directory/modules";
	if (!-d $var_base) {
		&make_dir($var_base, 0700);
		}
	$module_var_directory = "$var_base/$module_name";
	if (!-d $module_var_directory) {
		&make_dir($module_var_directory, 0700);
		}

	# Fix up windows-specific substitutions in values
	foreach my $k (keys %config) {
		if ($config{$k} =~ /\$\{systemroot\}/) {
			my $root = &get_windows_root();
			$config{$k} =~ s/\$\{systemroot\}/$root/g;
			}
		}
	}

# Record the initial module
$main::initial_module_name ||= $module_name;

# Set some useful variables
my $current_themes;
$current_themes = $ENV{'MOBILE_DEVICE'} && defined($gconfig{'mobile_theme'}) ?
		    $gconfig{'mobile_theme'} :
		  defined($remote_user_attrs{'theme'}) ?
		    $remote_user_attrs{'theme'} :
		  defined($gconfig{'theme_'.$remote_user}) ?
		    $gconfig{'theme_'.$remote_user} :
		  defined($gconfig{'theme_'.$base_remote_user}) ?
		    $gconfig{'theme_'.$base_remote_user} :
		    $gconfig{'theme'};
@current_themes = split(/\s+/, $current_themes);
$current_theme = $current_themes[0];
@theme_root_directories = map { "$root_directory/$_" } @current_themes;
$theme_root_directory = $theme_root_directories[0];
@theme_configs = ( );
foreach my $troot (@theme_root_directories) {
	my %onetconfig;
	&read_file_cached("$troot/config", \%onetconfig);
	&read_file_cached("$troot/config", \%tconfig);
	push(@theme_configs, \%onetconfig);
	}
$tb = defined($tconfig{'cs_header'}) ? "bgcolor=#$tconfig{'cs_header'}" :
      defined($gconfig{'cs_header'}) ? "bgcolor=#$gconfig{'cs_header'}" :
				       "bgcolor=#9999ff";
$cb = defined($tconfig{'cs_table'}) ? "bgcolor=#$tconfig{'cs_table'}" :
      defined($gconfig{'cs_table'}) ? "bgcolor=#$gconfig{'cs_table'}" :
				      "bgcolor=#cccccc";
$tb .= ' '.$tconfig{'tb'} if ($tconfig{'tb'});
$cb .= ' '.$tconfig{'cb'} if ($tconfig{'cb'});
if ($tconfig{'preload_functions'}) {
	# Force load of theme functions right now, if requested
	&load_theme_library();
	}
if ($tconfig{'oofunctions'} && !$main::loaded_theme_oo_library++) {
	# Load the theme's Webmin:: package classes
	do "$theme_root_directory/$tconfig{'oofunctions'}";
	}

$0 =~ /([^\/]+)$/;
$scriptname = $1;
$webmin_logfile = $gconfig{'webmin_log'} ? $gconfig{'webmin_log'}
					 : "$var_directory/webmin.log";

# Load language strings into %text
my @langs = &list_languages();
my $accepted_lang;
if ($gconfig{'acceptlang'}) {
	foreach my $a (split(/,/, $ENV{'HTTP_ACCEPT_LANGUAGE'})) {
		$a =~ s/;.*//;	# Remove ;q=0.5 or similar
		my ($al) = grep { $_->{'lang'} eq $a } @langs;
		if ($al) {
			$accepted_lang = $al->{'lang'};
			last;
			}
		}
	}
$current_lang = $force_lang ? $force_lang :
    $accepted_lang ? $accepted_lang :
    $remote_user_attrs{'lang'} ? $remote_user_attrs{'lang'} :
    $gconfig{"lang_$remote_user"} ? $gconfig{"lang_$remote_user"} :
    $gconfig{"lang_$base_remote_user"} ? $gconfig{"lang_$base_remote_user"} :
    $gconfig{"lang"} ? $gconfig{"lang"} : $default_lang;
foreach my $l (@langs) {
	$current_lang_info = $l if ($l->{'lang'} eq $current_lang);
	}
@lang_order_list = &unique($default_lang,
		     	   split(/:/, $current_lang_info->{'fallback'}),
			   $current_lang);
%text = &load_language($module_name);
%text || &error("Failed to determine Webmin root from SERVER_ROOT, SCRIPT_FILENAME or the full command line");

# Get the %module_info for this module
if ($module_name) {
	my ($mi) = grep { $_->{'dir'} eq $module_name }
			 &get_all_module_infos(2);
	%module_info = %$mi;
	$module_root_directory = &module_root_directory($module_name);
	}

if ($module_name && !$main::no_acl_check &&
    !defined($ENV{'FOREIGN_MODULE_NAME'}) &&
    $main::webmin_script_type eq 'web') {
	# Check if the HTTP user can access this module
	if (!&foreign_available($module_name)) {
		if (!&foreign_check($module_name)) {
			&error(&text('emodulecheck',
				     "<i>$module_info{'desc'}</i>"));
			}
		else {
			&error(&text('emodule', "<i>$u</i>",
				     "<i>$module_info{'desc'}</i>"));
			}
		}
	$main::no_acl_check++;
	}

# Check the Referer: header for nasty redirects
my @referers = split(/\s+/, $gconfig{'referers'});
my $referer_site;
my $r = $ENV{'HTTP_REFERER'};
my $referer_port = $r =~ /^https:/ ? 443 : 80;
if ($r =~ /^(http|https|ftp):\/\/([^:\/]+:[^@\/]+@)?\[([^\]]+)\](:(\d+))?/ ||
    $r =~ /^(http|https|ftp):\/\/([^:\/]+:[^@\/]+@)?([^\/:@]+)(:(\d+))?/) {
	$referer_site = $3;
	$referer_port = $5 if ($5);
	}
my $http_host = $ENV{'HTTP_HOST'};
my $http_port = $ENV{'SERVER_PORT'} || 80;
if ($http_host =~ s/:(\d+)$//) {
	$http_port = $1;
	}
$http_host =~ s/^\[(\S+)\]$/$1/;
my $unsafe_index = $unsafe_index_cgi ||
		   &get_module_variable('$unsafe_index_cgi');
my $trustvar = $trust_unknown_referers ||
	       &get_module_variable('$trust_unknown_referers');
my $trust = 0;
if (!$0) {
	# Script name not known
	$trust = 1;
	}
elsif ($trustvar == 1) {
	# Module doesn't want referer checking at all
	$trust = 1;
	}
elsif ($ENV{'DISABLE_REFERERS_CHECK'}) {
	# Check disabled by environment, perhaps due to cross-module call
	$trust = 1;
	}
elsif (($ENV{'SCRIPT_NAME'} =~ /^\/(index.cgi)?$/ ||
	$ENV{'SCRIPT_NAME'} =~ /^\/([a-z0-9\_\-]+)\/(index.cgi)?$/i) &&
       !$unsafe_index) {
	# Script is a module's index.cgi, which is normally safe
	$trust = 1;
	}
elsif ($0 =~ /(session_login|pam_login)\.cgi$/) {
	# Webmin login page, which doesn't get a referer
	$trust = 1;
	}
elsif ($gconfig{'referer'}) {
	# Referer checking disabled completely
	$trust = 1;
	}
elsif (!$ENV{'MINISERV_CONFIG'}) {
	# Not a CGI script
	$trust = 1;
	}
elsif ($main::no_referers_check) {
	# Caller requested disabling of checks completely
	$trust = 1;
	}
elsif ($ENV{'HTTP_USER_AGENT'} =~ /^Webmin/i) {
	# Remote call from Webmin itself
	$trust = 1;
	}
elsif (!$referer_site) {
	# No referer set in URL
	if (!$gconfig{'referers_none'}) {
		# Known referers are allowed
		$trust = 1;
		}
	elsif ($trustvar == 2) {
		# Module wants to trust unknown referers
		$trust = 1;
		}
	else {
		$trust = 0;
		}
	}
elsif (&indexof($referer_site, @referers) >= 0) {
	# Site is on the trusted list
	$trust = 1;
	}
elsif ($referer_site eq $http_host &&
       (!$referer_port || !$http_port || $referer_port == $http_port)) {
	# Link came from this website
	$trust = 1;
	}
else {
	# Unknown link source
	$trust = 0;
	}
if (!$trust) {
	# Looks like a link from elsewhere .. show an error
	&header($text{'referer_title'}, "", undef, 0, 1, 1);

	$prot = lc($ENV{'HTTPS'}) eq 'on' ? "https" : "http";
	my $url = "<tt>".&html_escape("$prot://$ENV{'HTTP_HOST'}$ENV{'REQUEST_URI'}")."</tt>";
	if ($referer_site) {
		# From a known host
		print &text('referer_warn',
			    "<tt>".&html_escape($r)."</tt>", $url);
		print "<p>\n";
		print &text('referer_fix1', &html_escape($http_host)),"<p>\n";
		print &text('referer_fix2', &html_escape($http_host)),"<p>\n";
		}
	else {
		# No referer info given
		print &text('referer_warn_unknown', $url),"<p>\n";
		print &text('referer_fix3u'),"<p>\n";
		print &text('referer_fix2u'),"<p>\n";
		}
	print "<p>\n";

	&footer("/", $text{'index'});
	exit;
	}
$main::no_referers_check++;
$main::completed_referers_check++;

# Call theme post-init
if (defined(&theme_post_init_config)) {
	&theme_post_init_config(@_);
	}

# Record that we have done the calling library in this package
my ($callpkg, $lib) = caller();
$lib =~ s/^.*\///;
$main::done_foreign_require{$callpkg,$lib} = 1;

# If a licence checking is enabled, do it now
if ($gconfig{'licence_module'} && !$main::done_licence_module_check &&
    &foreign_check($gconfig{'licence_module'}) &&
    -r "$root_directory/$gconfig{'licence_module'}/licence_check.pl") {
	my $oldpwd = &get_current_dir();
	$main::done_licence_module_check++;
	$main::licence_module = $gconfig{'licence_module'};
	&foreign_require($main::licence_module, "licence_check.pl");
	($main::licence_status, $main::licence_message) =
		&foreign_call($main::licence_module, "check_licence");
	chdir($oldpwd);
	}

# Export global variables to caller
if ($main::export_to_caller) {
	foreach my $v ('$config_file', '%gconfig', '$null_file',
		       '$path_separator', '@root_directories',
		       '$root_directory', '$module_name',
		       '$base_remote_user', '$remote_user',
		       '$remote_user_proto', '%remote_user_attrs',
		       '$module_config_directory', '$module_config_file',
		       '%config', '@current_themes', '$current_theme',
		       '@theme_root_directories', '$theme_root_directory',
		       '%tconfig','@theme_configs', '$tb', '$cb', '$scriptname',
		       '$webmin_logfile', '$current_lang',
		       '$current_lang_info', '@lang_order_list', '%text',
		       '%module_info', '$module_root_directory',
		       '$module_var_directory') {
		my ($vt, $vn) = split('', $v, 2);
		eval "${vt}${callpkg}::${vn} = ${vt}${vn}";
		}
	}

return 1;
}

sub init_config_preload {
    $main::initial_process_id ||= $$;

    if ( !defined( $ENV{'WEBMIN_CONFIG'} ) ) {
        die "WEBMIN_CONFIG not set";
    }
    $config_directory = $ENV{'WEBMIN_CONFIG'};
    if ( !defined( $ENV{'WEBMIN_VAR'} ) ) {
        open( VARPATH, "$config_directory/var-path" );
        chop( $var_directory = <VARPATH> );
        close(VARPATH);
    }
    else {
        $var_directory = $ENV{'WEBMIN_VAR'};
    }
    $main::http_cache_directory   = $ENV{'WEBMIN_VAR'} . "/cache";
    $main::default_debug_log_file = $ENV{'WEBMIN_VAR'} . "/webmin.debug";

    if ( $> == 0 && $< != 0 && !$ENV{'FOREIGN_MODULE_NAME'} ) {
        $( = $);
        $< = $>;
    }

    $config_file = "$config_directory/config";
    %gconfig     = ();
    &read_file_cached( $config_file, \%gconfig );
    $gconfig{'webprefix'} = '' if ( !exists( $gconfig{'webprefix'} ) );
    $null_file      = $gconfig{'os_type'} eq 'windows' ? "NUL" : "/dev/null";
    $path_separator = $gconfig{'os_type'} eq 'windows' ? ';'   : ':';

    if ( ( $ENV{'WEBMIN_DEBUG'} || $gconfig{'debug_enabled'} )
        && !$main::opened_debug_log++ )
    {
        my $dlog  = $gconfig{'debug_file'} || $main::default_debug_log_file;
        my $dsize = $gconfig{'debug_size'} || $main::default_debug_log_size;
        my @st    = stat($dlog);
        if ( $dsize && $st[7] > $dsize ) {
            rename( $dlog, $dlog . ".0" );
        }

        open( main::DEBUGLOG, ">>$dlog" );
        $main::opened_debug_log = 1;

        if ( $gconfig{'debug_what_start'} ) {
            my $script_name = $0 =~ /([^\/]+)$/ ? $1 : '-';
            $main::debug_log_start_time = time();
            &webmin_debug_log( "START", "script=$script_name" );
        }
    }

    if ( $gconfig{'path'} ) {
        if ( $gconfig{'syspath'} ) {

            # Webmin only
            $ENV{'PATH'} = $gconfig{'path'};
        }
        else {
            # Include OS too
            $ENV{'PATH'} = $gconfig{'path'} . $path_separator . $ENV{'PATH'};
        }
    }
    $ENV{ $gconfig{'ld_env'} } = $gconfig{'ld_path'} if ( $gconfig{'ld_env'} );

    if ( $gconfig{'http_proxy'} ) {
        $ENV{'http_proxy'} = $gconfig{'http_proxy'};
    }
    if ( $gconfig{'ftp_proxy'} ) {
        $ENV{'ftp_proxy'} = $gconfig{'ftp_proxy'};
    }
    if ( $gconfig{'noproxy'} ) {
        $ENV{'no_proxy'} = $gconfig{'noproxy'};
    }

    my %miniserv;
    if ( &get_miniserv_config( \%miniserv ) ) {
        @root_directories = ( $miniserv{'root'} );
        for ( $i = 0 ; defined( $miniserv{"extraroot_$i"} ) ; $i++ ) {
            push( @root_directories, $miniserv{"extraroot_$i"} );
        }
    }

    if ($module_name) {

        # Find and load the configuration file for this module
        my ( @ruinfo, $rgroup );
        $module_config_directory = "$config_directory/$module_name";
        if ( &get_product_name() eq "usermin"
            && -r "$module_config_directory/config.$remote_user" )
        {
            # Based on username
            $module_config_file =
              "$module_config_directory/config.$remote_user";
        }
        elsif (&get_product_name() eq "usermin"
            && ( @ruinfo = getpwnam($remote_user) )
            && ( $rgroup = getgrgid( $ruinfo[3] ) )
            && -r "$module_config_directory/config.\@$rgroup" )
        {
            # Based on group name
            $module_config_file = "$module_config_directory/config.\@$rgroup";
        }
        else {
            # Global config
            $module_config_file = "$module_config_directory/config";
        }
        %config = ();
        &read_file_cached( $module_config_file, \%config );

        my $var_base = "$var_directory/modules";
        if ( !-d $var_base ) {
            &make_dir( $var_base, 0700 );
        }
        $module_var_directory = "$var_base/$module_name";
        if ( !-d $module_var_directory ) {
            &make_dir( $module_var_directory, 0700 );
        }

        foreach my $k ( keys %config ) {
            if ( $config{$k} =~ /\$\{systemroot\}/ ) {
                my $root = &get_windows_root();
                $config{$k} =~ s/\$\{systemroot\}/$root/g;
            }
        }
    }

    $main::initial_module_name ||= $module_name;

    my $current_themes;
    $current_themes =
      $ENV{'MOBILE_DEVICE'}
      && defined( $gconfig{'mobile_theme'} )   ? $gconfig{'mobile_theme'}
      : defined( $remote_user_attrs{'theme'} ) ? $remote_user_attrs{'theme'}
      : defined( $gconfig{ 'theme_' . $remote_user } )
      ? $gconfig{ 'theme_' . $remote_user }
      : defined( $gconfig{ 'theme_' . $base_remote_user } )
      ? $gconfig{ 'theme_' . $base_remote_user }
      : $gconfig{'theme'};
    @current_themes         = split( /\s+/, $current_themes );
    $current_theme          = $current_themes[0];
    @theme_root_directories = map { "$root_directory/$_" } @current_themes;
    $theme_root_directory   = $theme_root_directories[0];
    @theme_configs          = ();

    foreach my $troot (@theme_root_directories) {
        my %onetconfig;
        &read_file_cached( "$troot/config", \%onetconfig );
        &read_file_cached( "$troot/config", \%tconfig );
        push( @theme_configs, \%onetconfig );
    }
    $tb =
        defined( $tconfig{'cs_header'} ) ? "bgcolor=#$tconfig{'cs_header'}"
      : defined( $gconfig{'cs_header'} ) ? "bgcolor=#$gconfig{'cs_header'}"
      :                                    "bgcolor=#9999ff";
    $cb =
        defined( $tconfig{'cs_table'} ) ? "bgcolor=#$tconfig{'cs_table'}"
      : defined( $gconfig{'cs_table'} ) ? "bgcolor=#$gconfig{'cs_table'}"
      :                                   "bgcolor=#cccccc";
    $tb .= ' ' . $tconfig{'tb'} if ( $tconfig{'tb'} );
    $cb .= ' ' . $tconfig{'cb'} if ( $tconfig{'cb'} );
    if ( $tconfig{'preload_functions'} ) {
        &load_theme_library();
    }
    if ( $tconfig{'oofunctions'} && !$main::loaded_theme_oo_library++ ) {
        do "$theme_root_directory/$tconfig{'oofunctions'}";
    }

    $webmin_logfile =
        $gconfig{'webmin_log'}
      ? $gconfig{'webmin_log'}
      : "$var_directory/webmin.log";
    if ($module_name) {
        my ($mi) =
          grep { $_->{'dir'} eq $module_name } &get_all_module_infos(2);
        %module_info           = %$mi;
        $module_root_directory = &module_root_directory($module_name);
    }

    if (   $module_name
        && !$main::no_acl_check
        && !defined( $ENV{'FOREIGN_MODULE_NAME'} )
        && $main::webmin_script_type eq 'web' )
    {
        if ( !&foreign_available($module_name) ) {
            if ( !&foreign_check($module_name) ) {
                &error(
                    &text( 'emodulecheck', "<i>$module_info{'desc'}</i>" ) );
            }
            else {
                &error(
                    &text(
                        'emodule', "<i>$u</i>",
                        "<i>$module_info{'desc'}</i>"
                    )
                );
            }
        }
        $main::no_acl_check++;
    }

    if ( defined(&theme_post_init_config) ) {
        &theme_post_init_config(@_);
    }

    my ( $callpkg, $lib ) = caller();
    $lib =~ s/^.*\///;
    $main::done_foreign_require{ $callpkg, $lib } = 1;

    if (   $gconfig{'licence_module'}
        && !$main::done_licence_module_check
        && &foreign_check( $gconfig{'licence_module'} )
        && -r "$root_directory/$gconfig{'licence_module'}/licence_check.pl" )
    {
        my $oldpwd = &get_current_dir();
        $main::done_licence_module_check++;
        $main::licence_module = $gconfig{'licence_module'};
        &foreign_require( $main::licence_module, "licence_check.pl" );
        ( $main::licence_status, $main::licence_message ) =
          &foreign_call( $main::licence_module, "check_licence" );
        chdir($oldpwd);
    }

    if ($main::export_to_caller) {
        foreach my $v (
            '$config_file',            '%gconfig',
            '$null_file',              '$path_separator',
            '@root_directories',       '$root_directory',
            '$module_name',            '$module_config_directory',
            '$module_config_file',     '%config',
            '@current_themes',         '$current_theme',
            '@theme_root_directories', '$theme_root_directory',
            '%tconfig',                '@theme_configs',
            '$tb',                     '$cb',
            '$webmin_logfile',         '$current_lang',
            '$current_lang_info',      '@lang_order_list',
            '%module_info',            '$module_root_directory',
            '$module_var_directory'
          )
        {
            my ( $vt, $vn ) = split( '', $v, 2 );
            eval "${vt}${callpkg}::${vn} = ${vt}${vn}";
        }
    }

    return 1;
}
#-------------------------------------------------------------------------------
sub init_config_runtime {
    if ( $ENV{'SESSION_ID'} ) {

        # Hide this variable from called programs, but keep it for internal use
        $main::session_id = $ENV{'SESSION_ID'};
        delete( $ENV{'SESSION_ID'} );
    }
    if ( $ENV{'REMOTE_PASS'} ) {

        # Hide the password too
        $main::remote_pass = $ENV{'REMOTE_PASS'};
        delete( $ENV{'REMOTE_PASS'} );
    }

    if ( $> == 0 && $< != 0 && !$ENV{'FOREIGN_MODULE_NAME'} ) {
        $( = $);
        $< = $>;
    }

    # Run via a CGI
    $main::webmin_script_type = 'web';

    if ( ( $ENV{'WEBMIN_DEBUG'} || $gconfig{'debug_enabled'} )
        && !$main::opened_debug_log++ )
    {
        my $dlog  = $gconfig{'debug_file'} || $main::default_debug_log_file;
        my $dsize = $gconfig{'debug_size'} || $main::default_debug_log_size;
        my @st    = stat($dlog);
        if ( $dsize && $st[7] > $dsize ) {
            rename( $dlog, $dlog . ".0" );
        }

        open( main::DEBUGLOG, ">>$dlog" );
        $main::opened_debug_log = 1;

        if ( $gconfig{'debug_what_start'} ) {
            my $script_name = $0 =~ /([^\/]+)$/ ? $1 : '-';
            $main::debug_log_start_time = time();
            &webmin_debug_log( "START", "script=$script_name" );
        }
    }

    $0 =~ s/\\/\//g;    # Force consistent path on Windows
    if ( defined( $ENV{'FOREIGN_MODULE_NAME'} )
        && $ENV{'FOREIGN_ROOT_DIRECTORY'} )
    {
        # In a foreign call - use the module name given
        $root_directory   = $ENV{'FOREIGN_ROOT_DIRECTORY'};
        $module_name      = $ENV{'FOREIGN_MODULE_NAME'};
        @root_directories = ($root_directory) if ( !@root_directories );
    }
    elsif ( $ENV{'SCRIPT_NAME'} ) {
        my $sn = $ENV{'SCRIPT_NAME'};
        $sn =~ s/^$gconfig{'webprefix'}//
          if ( !$gconfig{'webprefixnoredir'} );
        if ( $sn =~ /^\/([^\/]+)\// ) {

            # Get module name from CGI path
            $module_name = $1;
        }
        if ( $ENV{'SERVER_ROOT'} ) {
            $root_directory = $ENV{'SERVER_ROOT'};
        }
        elsif ( $ENV{'SCRIPT_FILENAME'} ) {
            $root_directory = $ENV{'SCRIPT_FILENAME'};
            $root_directory =~ s/$sn$//;
        }
        @root_directories = ($root_directory) if ( !@root_directories );
    }
    else {
        $root_directory = $root_directories[0];
        my $rok = 0;
        foreach my $r (@root_directories) {
            if ( $0 =~ /^$r\/([^\/]+)\/[^\/]+$/i ) {

                # Under a module directory
                $module_name = $1;
                $rok         = 1;
                last;
            }
            elsif ( $0 =~ /^$root_directory\/[^\/]+$/i ) {

                # At the top level
                $rok = 1;
                last;
            }
        }
        &error(
"Script was not run with full path (failed to find $0 under $root_directory)"
        ) if ( !$rok );
    }

    my $u = $ENV{'BASE_REMOTE_USER'} || $ENV{'REMOTE_USER'};
    $base_remote_user = $u;
    $remote_user      = $ENV{'REMOTE_USER'};

    $remote_user_proto = $ENV{"REMOTE_USER_PROTO"};
    %remote_user_attrs = ();
    if ($remote_user_proto) {
        my $userdb = &get_userdb_string();
        my ( $dbh, $proto, $prefix, $args ) =
          $userdb ? &connect_userdb($userdb) : ();
        if ( ref($dbh) ) {
            if ( $proto eq "mysql" || $proto eq "postgresql" ) {

                # Read attrs from SQL
                my $cmd = $dbh->prepare(
"select webmin_user_attr.attr,webmin_user_attr.value from webmin_user_attr,webmin_user where webmin_user_attr.id = webmin_user.id and webmin_user.name = ?"
                );
                if ( $cmd && $cmd->execute($base_remote_user) ) {
                    while ( my ( $attr, $value ) = $cmd->fetchrow() ) {
                        $remote_user_attrs{$attr} = $value;
                    }
                    $cmd->finish();
                }
            }
            &disconnect_userdb( $userdb, $dbh );
        }
    }

    $0 =~ /([^\/]+)$/;
    $scriptname = $1;
    $webmin_logfile =
        $gconfig{'webmin_log'}
      ? $gconfig{'webmin_log'}
      : "$var_directory/webmin.log";

    my @langs = &list_languages();
    my $accepted_lang;
    if ( $gconfig{'acceptlang'} ) {
        foreach my $a ( split( /,/, $ENV{'HTTP_ACCEPT_LANGUAGE'} ) ) {
            $a =~ s/;.*//;    # Remove ;q=0.5 or similar
            my ($al) = grep { $_->{'lang'} eq $a } @langs;
            if ($al) {
                $accepted_lang = $al->{'lang'};
                last;
            }
        }
    }
    $current_lang =
        $force_lang                        ? $force_lang
      : $accepted_lang                     ? $accepted_lang
      : $remote_user_attrs{'lang'}         ? $remote_user_attrs{'lang'}
      : $gconfig{"lang_$remote_user"}      ? $gconfig{"lang_$remote_user"}
      : $gconfig{"lang_$base_remote_user"} ? $gconfig{"lang_$base_remote_user"}
      : $gconfig{"lang"}                   ? $gconfig{"lang"}
      :                                      $default_lang;
    foreach my $l (@langs) {
        $current_lang_info = $l if ( $l->{'lang'} eq $current_lang );
    }
    @lang_order_list =
      &unique( $default_lang, split( /:/, $current_lang_info->{'fallback'} ),
        $current_lang );
    %text = &load_language($module_name);
    %text
      || &error(
"Failed to determine Webmin root from SERVER_ROOT, SCRIPT_FILENAME or the full command line"
      );

    $trust = 1;

    $main::no_referers_check++;
    $main::completed_referers_check++;

    my ( $callpkg, $lib ) = caller();
    $lib =~ s/^.*\///;
    $main::done_foreign_require{ $callpkg, $lib } = 1;

#    if ($main::export_to_caller) {
        foreach my $v (
            '$base_remote_user',  '$remote_user',
            '$remote_user_proto', '%remote_user_attrs',
            '$tb',                '$cb',
            '$scriptname',        '$current_lang',
            '$current_lang_info', '@lang_order_list',
            '%text',
          )
        {
            my ( $vt, $vn ) = split( '', $v, 2 );
            eval "${vt}${callpkg}::${vn} = ${vt}${vn}";
        }
#    }

    return 1;
}

sub init_config {
    goto &init_config_full if not grep { $_ eq $0 } qw(JSONService.cgi apiDispatcher_session.cgi apiDispatcher.cgi);

    use feature 'state';

    state $init_config_flag;

    unless ($init_config_flag) {
       $init_config_flag = 1;

       goto &init_config_full;
    }

    goto &init_config_runtime;

    return 1;
}
