#!/usr/bin/perl

package main;

use strict;
use FindBin;
use IO::Socket::INET;
use POSIX;

$ENV{PERl5LIB}=''; #it may have been set by Oracle to its own ancient Perl

use vars qw($orig_bin_dir $binary_httpd $interp_name $ostype %config);

$ostype = 'unix';
{
    $ostype = 'win' if ($^O =~ /(win32)|(cygwin)/i);
}

$orig_bin_dir = $FindBin::Bin;
$binary_httpd = 1;
$interp_name = "$^X";

my $no_perl = 0;

if ($ENV{PAR_INITIALIZED} && $ENV{PAR_TEMP})
{
    my $d = __FILE__;
    $d = substr($d,0,rindex($d,'/'));
    $FindBin::Bin = $d;	#"$ENV{PAR_TEMP}/inc/script";
    $no_perl = 1;
}
chdir $FindBin::Bin;
use lib ("$FindBin::Bin/../../perl/common-inc","$FindBin::Bin/../../perl/fallback-inc",$FindBin::Bin);


if ($no_perl)
{    
    my $exepath = $0;
    if ($^O =~ /(cyg)?win\d+/i)
    {
	$exepath =~ s,\\,/,g;
    }
    $exepath = "$orig_bin_dir/$0" if (index($exepath,$orig_bin_dir)!=0);
    $interp_name = "$exepath --runother";
};
$interp_name = "\"$interp_name\"";
$ENV{PERL_INTERP_NAME} = $interp_name;

if ($no_perl)
{
    if ($ARGV[0] eq '--runother')
    {
	shift @ARGV;
	my $file = shift @ARGV;
	my $pos = rindex($file,'/');
	if ($pos >= 0 )
	{
	    my $dir = substr($file,0,$pos);
	    chdir($dir);
	};
	do $file;
	print STDERR "there were problems running file $file: $@\n" if $@;
	exit;
    };
}



#These are configuration settings. They can be modified by the 
# '../config/siteinfo.pm' file that is evaluated (if it's present). 
#They also can be overriden by passing space-separated list of values on 
#commandline. 
#Ones with names starting from '_' are private and shouldn't be overriden from
#commandline in most cases
%config = ( 
#it's GREATLY recommended  to alter these options. 
#Override them in ../config/siteinfo.pm, NOT HERE!!
	'sitename', 	'default',
#there is can be a point in changing these:
#Override them in ../config/siteinfo.pm, NOT HERE!!
	'urlpath',	'', #document path to open on the site by default.
			    #this will go after http://127.0.0.1:9000:/
	'show_win32_firewall_alert',1,
	'win32_firewall_alert_message','',#if empty, default message is used

	'nobrowser',	0,  #if 1, browser won't be started. 
	'macosx_prefer_ie', 0,
	'win32_prefer_ie', 1,
	'win32_prefer_ie_kiosk_mode', 0,
	'port_to_start_scanning_from', 9000,
	'do_not_eject_on_shutdown',0,

#description of these properties can be found here:
#http://msdn.microsoft.com/workshop/browser/webbrowser/reference/objects/internetexplorer.asp
        'win32_msie_prop__addressbar','',
        'win32_msie_prop__fullscreen','',
        'win32_msie_prop__height','',
        'win32_msie_prop__left','',
        'win32_msie_prop__menubar','',
        'win32_msie_prop__resizable','',
        'win32_msie_prop__silent','',
        'win32_msie_prop__statusbar','',
        'win32_msie_prop__theatermode','',
        'win32_msie_prop__toolbar','',
        'win32_msie_prop__top','',
        'win32_msie_prop__width','',

#this block of settings specifies which programs to run before and 
#after browser; after program is run, this wrapper waits for specified
#count of seconds and then tries to run next program
#and so on. If program name is empty, nothing is run.
#  Specify path to program relative to the top of cdrom; use '/' as directory
#separators, e.g. "somedir/myprog.exe".
	'program_to_run_before_server_1', '',
	'program_to_run_before_server_1_sleep_seconds', '',
	'program_to_run_before_server_2', '',
	'program_to_run_before_server_2_sleep_seconds', '',
	'program_to_run_before_server_3', '',
	'program_to_run_before_server_3_sleep_seconds', '',

	'program_to_run_before_browser_1', '',
	'program_to_run_before_browser_1_sleep_seconds', '',
	'program_to_run_before_browser_2', '',
	'program_to_run_before_browser_2_sleep_seconds', '',
	'program_to_run_before_browser_3', '',
	'program_to_run_before_browser_3_sleep_seconds', '',

	'program_to_run_after_browser_1', '',
	'program_to_run_after_browser_1_sleep_seconds', '',
	'program_to_run_after_browser_2', '',
	'program_to_run_after_browser_2_sleep_seconds', '',
	'program_to_run_after_browser_3', '',
	'program_to_run_after_browser_3_sleep_seconds', '',




#when debugging the product, one can find tweaking these options helpful
	'_debug',	0,  #if 1, generated config file won't be deleted,
			    #and a trace of startup will be shown on console
	'_debugtofile',	0,


#these are private settings that nobody may wish to alter
	'_extroot',	'', #pipe-separated list directory names to look 
			    #for extensions
	'_httpd_mods_root', '',  #pipe-separated list directory names to look 
			    #for httpd modules; default is /httpd if empty
	'_home_subdir',	'.stunnixws',
	'_docroot_default', '../www/root',#where document root is located
			    #physically. No slash at the end!!
	'statedir', 	'',#autocomputed if empty
	'_serverroot',	'',#don't change
	'_docroot',	'',#actual docroot, can 
	'_other_progs_pidfile','other-progs-pids.txt',
	'_splashflagfilename','',
	'_showlogo_pid','0',


#these entries predeclare private structures. Don't try to put any values
#here - they will be ignored most probably.
	'_extroots',	[],	
	'_httpd_mods_roots', [],

	'_binary_httpd', $binary_httpd,
	'_httpd_defines',{},
	'_onserver_started_callbacks',[],#list of subs to call
);

if (-f 'siteinfo.pm')
{
    do "siteinfo.pm";    
}
if (-f '../config/siteinfo.pm')
{
    do "../config/siteinfo.pm";
}



my $tracefilename;

sub t { 
    print STDERR join('',@_) . "\n" if $config{_debug}; 
    if ($config{_debugtofile}) {
	#we do this so that windows to flush file buffers
	open(LOGDBG,">>${tracefilename}");
	print LOGDBG join('',@_) . "\n";
	close LOGDBG;
    };
}

{
    if ($config{_debugtofile}) {
	$tracefilename = $ostype eq 'win' ? 'c:/spwstrace.txt' : 
	    '/tmp/spwstrace.txt';
	unlink $tracefilename;
	my $stderrto = $ostype eq 'win' ? 'c:/spws-stderr.txt' : 
	    '/tmp/spws-stderr.txt';
	open(STDERR,">$stderrto");
	t("#NOTE: PLEASE ALSO SEE $stderrto for error messages produced by other parts of Stunnix Web Server!");
    };
}


{#override config from commandline
    my %newconfig = @ARGV;
    map { $config{$_} = $newconfig{$_} if exists $newconfig{$_} ;} 
	 keys %config;
}

{#compute dirs
    if (!$config{statedir})
    {
	if ($ENV{HOME}) {
	    $config{statedir} = "$ENV{HOME}/$config{_home_subdir}/";
	} elsif ($ENV{TEMP}) {
	    $config{statedir} = "$ENV{TEMP}/$config{_home_subdir}/";
	} elsif ($ENV{TMP}) {
	    $config{statedir} = "$ENV{TMP}/$config{_home_subdir}/";
	} else {
	    $config{statedir} = "$FindBin::Bin/state";
	};
    }
    if (!$config{_serverroot})
    {
	$config{_stunnixws_serverroot} = $config{_serverroot} = 
	    "$FindBin::Bin/";
    }
    {
	my $d = getcwd();
	chdir $config{_serverroot}; chdir ".."; chdir "..";
	$config{cdroot} = getcwd();
	chdir $d;
    }
    if (!$config{_extroot})
    {
	$config{_extroot} = "$config{_serverroot}/../../extensions/";
    };
    if (!$config{_httpd_mods_root})
    {
	$config{_httpd_mods_root} = "$config{_serverroot}/../../httpd/";
    };
    {
	$config{_extroots} = [ split('\|',$config{_extroot}) ];
	$config{_httpd_mods_roots} = [ split('\|',$config{_httpd_mods_root}) ];
    }
}

{#create statedir
    my $statedir = $config{statedir};
    mkdir $statedir;
    
    $config{statedir} = $statedir .= "/$config{sitename}/";
    if (! -d $statedir && $config{show_win32_firewall_alert} && 
	$ostype eq 'win')
    {
	my $msg = "The server will now load. " . 
	    "If prompted, please allow access through any " .
	    "firewalls (such as Zone Alarm or Windows firewall)";
	$msg = $config{win32_firewall_alert_message} 
		    if length($config{win32_firewall_alert_message});
	system("showmessage.exe $msg");
    };
    $ENV{STUNNIXWS_TMPSTATEDIR} = $statedir;
    mkdir $statedir;
    mkdir "$statedir/logs";

    _die("failed to create $statedir:  $!") if ! -d $statedir;    

    #compute computable values
    $config{_portfnm} = "$statedir/port.file";
}
if ($^O =~ /Win32/)
{
    eval("use Win32::Registry;");
    if (!$@)
    {
	my $prefix = "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders\\";
	my $item = "Local AppData";
	my $k;
	$::HKEY_CURRENT_USER->Open($prefix,$k);
	my ($type,$value);
        $k->QueryValueEx($item,$type,$value);
	$ENV{STUNNIXWS_STATEDIR} = $value;
    } else {
	#try to find some solution
	$ENV{STUNNIXWS_STATEDIR} = "c:/";
    }
} else {
    $ENV{STUNNIXWS_STATEDIR} = $ENV{HOME}; #support only unix for now
}

{
    my $p = "$ENV{STUNNIXWS_STATEDIR}/$config{_home_subdir}";    
    mkdir $p;
    $p .= "/$config{sitename}/";
    mkdir $p;
    $ENV{STUNNIXWS_PERMSTATEDIR} = $p;
}

if ($ostype ne 'win' && -r "$config{_serverroot}/dummyscript.sh" &&
     system("\"$config{_serverroot}/dummyscript.sh\" >/dev/null 2>&1")/256 )
{
    my $msg = "Your CDROM is mounted with 'noexec' flag or similar. Some functionality\n"
."may be lost, so either remount CDROM with 'noexec' flag or copy entire\n"
."CDROM to harddisk and run it from there";

    if ($ENV{DISPLAY})
    {
	system("xmessage \"$msg\" >/dev/null 2>&1 &");
    };
    print STDERR $msg . "\n";
}

if ($binary_httpd && -r "httpd_modules/httpd.pm")
{
    eval('use httpd_modules::httpd;');
    my $o = new httpd_modules::httpd('httpd','booting'=>1);
    $o->configure({'config',\%config});
    $binary_httpd = $config{_binary_httpd};
} else {
    $binary_httpd = $config{_binary_httpd} = 0;
};

use startutils;

{
    my $port;
    t("starting network operations");
    other_setup_funcs();
    $ENV{STARTSITE_PID} = $$; 
    $ENV{STUNNIX_WEBSERVER_WAS_ALREADY_RUNNING} = 1;
    if (!($port = is_server_working()))
    {
	$ENV{STUNNIX_WEBSERVER_WAS_ALREADY_RUNNING} = 0;
	$port = findfreeport();
	$config{'_httpd_port'} = $ENV{SERVER_PORT} = $port;
	foreach (1..3)	{ run_exta_program('before_server',$_) };
	start_server($port);
	foreach (@{$config{_onserver_started_callbacks}})
	{
	    &$_();
	};
	unlink "$config{statedir}/$config{_other_progs_pidfile}";
    } else {
	$ENV{SERVER_PORT} = $port;
	foreach (1..3){ run_exta_program('before_server',$_) };
    };
    $ENV{SERVER_PORT} = $port;
    foreach (1..3){ run_exta_program('before_browser',$_) };
    open_page($port);
    unlink $config{_splashflagfilename} if length($config{_splashflagfilename});
    kill 9, $config{_showlogo_pid} if $config{_showlogo_pid};
    foreach  (1..3) { run_exta_program('after_browser',$_) }
    t("done everything");
}

sub is_server_working
{
    #check existance of port.file
    my $port;
    return 0 if ! -f $config{_portfnm}; 
    {
	t("file '$config{_portfnm}' probably contains port number");
	if (open(PORTNR,"<$config{_portfnm}"))
	{
	    $port = <PORTNR>;
	    chomp $port;
	    t("[$port] is stored in file");
	    $port = 0 + $port;
	    return 0 if !$port;	    
	} else {
	    return 0;
	};
    }
    return is_server_working_on_port($port);
}



#returns free port
my @ports_already_found;
sub findfreeport
{
    my @ports_except_list = @_;    
    push @ports_except_list, @ports_already_found;
    my ($portfrom,$portto) = ($config{port_to_start_scanning_from},65536);
    my %ports_except;
    @ports_except{@ports_except_list} = @ports_except_list;
    for(my $port=$portfrom; $port < $portto; ++$port)
    {
	next if $ports_except{$port};
        my $remote = opensk($port);
	t("checking port $port");
	if ($remote) {
	    t("port $port is busy");
	    $remote->close;
	} else {
	    t("port $port is free, will use it");
	    push @ports_already_found, $port;
	    return $port;
	};
    };
    _die("no free ports found!");
}

