#!/usr/bin/perl

use strict;
use File::Path;
use File::Basename;
use Socket;
use Cwd;

use lib 'd:/Programs/Perl/lib';
use lib "$ENV{TkPerlDir}/lib";

use Deps;
use Utils;
use MyApplication;

#===============================================
# デバッグ関係変数
#===============================================
my $Debug = 0;
#$PrintLevelが大きいほど、情報が詳しくなる
my $PrintLevel = 0;

#===============================================
# スクリプト大域変数
#===============================================

my $LF = "<br>\n";
my $DirectorySeparator = "\\";

my $Perl = '/usr/bin/perl';
my $ProgramsDir = "c:\\Programs";
$ProgramsDir = "d:\\Programs" unless(-e $ProgramsDir);
$ProgramsDir = "e:\\Programs" unless(-e $ProgramsDir);
$ProgramsDir = "f:\\Programs" unless(-e $ProgramsDir);

my $App = new MyApplication;
exit if($App->Initialize() < 0);

my $BaseDir = "";
if($App->OS() eq 'linux') {
	$BaseDir = ""; #"$ENV{HOME}/bin";
}
else {
	$BaseDir = ""; #"C:\\Programs";
}

my $ProgramPath = $App->SpeculateProgramPath($0, $BaseDir);
print "Program Path=$ProgramPath\n";
my ($drive, $directory, $filename, $ext1, $lastdir, $filebody) = Deps::SplitFilePath($ProgramPath);
$BaseDir = "$drive$directory";
print "Base Dir=$BaseDir\n";

#アクセス制限の有無
my $IsLimitAccess = 1;
#許可するIPアドレス
my @AllowedIPAddresses =
	(
	 "^127\.0\.0\.1\$",
	 "^192\.168\.1\.\\d{1,3}\$",
	 "^192\.168\.11\.\\d{1,4}\$",
	 "^192\.168\.10\.\\d{1,4}\$",
	 "^131\.112\.130\.56\$"
	);

my $OS = $^O;
my %CmdLines;

if($OS eq 'MSWin32') {
	$DirectorySeparator = "\\";
	$Perl = "c:\\perl\\bin\\perl.exe";
	$ProgramPath = 'd:\\Programs\\Perl\\develop\\rsysd.pl';
	$ProgramPath = 'e:\\Programs\\Perl\\develop\\rsysd.pl' unless -e $ProgramPath;
	my $ComSpec = $ENV{'ComSpec'};
	my $WinDir = $ENV{'windir'};
#	$CmdLines{'ls'}  = "$ComSpec /C dir";
	$CmdLines{'ps'}  = "c:\\cygwin\\bin\\ps.exe auc";
	$CmdLines{'psx'} = "c:\\cygwin\\bin\\ps.exe auxc";
	$CmdLines{'net'} = "$WinDir\\system32\\netstat.exe -a";
	my $OSVer = `$ComSpec /C ver`;
	if($OSVer =~ /XP/) {
		$CmdLines{'ps'}  = "$ProgramsDir\\bin\\processlist.exe";
		$CmdLines{'psx'} = "$ProgramsDir\\bin\\processlist.exe";
	}
	my $SFUPath = "$ENV{'windir'}\\system32\\posix.exe";
	if(-e $SFUPath) {
		$CmdLines{'ps'}  = "$SFUPath /u /c /bin/ps -l";
		$CmdLines{'psx'} = "$SFUPath /u /c /bin/ps -A -l";
	}
}
elsif($OS eq 'linux') {
	$DirectorySeparator = "/";
#	$CmdLines{'ls'}  = "/bin/ls";
	$CmdLines{'ps'}  = "/bin/ps auc";
	$CmdLines{'psx'} = "/bin/ps auxc";
	$CmdLines{'net'} = "/bin/netstat -p";
}

my $ServerPort = 7797;

$ENV{PATH} = "";

my ($iaddr, $paddr);
my $proto = getprotobyname('tcp');

my $NowTime  = time();
my $Today     = &BuildDateString($NowTime);
my $IPAddress = $ENV{'REMOTE_ADDR'};

#===============================================
# パス（読み込みDB）
# Web関係変数
# CGI の仮想パス
# プログラム名
#===============================================
my $Program     = basename($0);

#==========================================
# メイン関数スタート
#==========================================

socket(SOCK, PF_INET, SOCK_STREAM, $proto) or die "socket: $!";
setsockopt(SOCK, SOL_SOCKET, SO_REUSEADDR, pack("l", 1)) or die "setsockopt: $!";

my $port = $ServerPort;
bind(SOCK, sockaddr_in($port, INADDR_ANY)) or die "bind: $!";
listen(SOCK, SOMAXCONN) or die "listen: $!";

for( ; $paddr = accept(CLIENT, SOCK) ; close CLIENT) {
	($port, $iaddr) = sockaddr_in($paddr);
	my $name = gethostbyaddr($iaddr, AF_INET);
#print "name: $name\n";
	my $ClientCommand = <CLIENT>;
	chomp($ClientCommand);
	$ClientCommand =~ s/[\r\n]//g;
print CLIENT "ClientCommand: $ClientCommand\n";

	my $IPAddress = inet_ntoa($iaddr);
#print CLIENT "Client IPAddr: $IPAddress\n";
	if($IsLimitAccess and
	   !&IsAllowedIPAddress($IPAddress, @AllowedIPAddresses)) {
		print CLIENT "Your access is not allowed ($IPAddress).\n";
		next;
	}

#For relay: ex. ps@vasp
	if($ClientCommand =~ /^(.*)@(.*?)$/) {
		my $nextcmd = $1;
		my $nexthost = $2;
print CLIENT "Forward $nextcmd to $nexthost\n";
		my ($accesshost, $accessport) = ($nexthost =~ /^(.*):(.*?)$/);
#print "accesshost: $accesshost\n";
		my $proto = getprotobyname('tcp');
		my $iaddr = inet_aton($accesshost);
		next if($iaddr eq '');
#print "iad: ", $iaddr, "\n";

		my $paddr = sockaddr_in($accessport, $iaddr);
print CLIENT "Response for $nextcmd from $accesshost:$accessport\n";
print CLIENT "\n";
		socket(OUTSOCK, PF_INET, SOCK_STREAM, $proto) 
			or next; #die "socket: $!$LF";
		connect(OUTSOCK, $paddr) 
			or next; #die "connect: $!$LF";

		my @result;
#バッファに入れず、そのまま出力にまわす
		select OUTSOCK; $| = 1; select STDOUT; 
		print OUTSOCK "$nextcmd\n";	
		@result = <OUTSOCK>;
		print CLIENT @result;
		close OUTSOCK;
	}
	else {
		if($ClientCommand eq '-reboot') {
			print CLIENT "Launch [$Perl $ProgramPath] and shutdown this server.\n";
#			close SOCK;
#			close CLIENT;
			exec("$Perl $ProgramPath");
#			exit;
		}
#		$ClientCommand = lc $ClientCommand;
#print CLIENT "lc ClientCommand: $ClientCommand\n";
#print CLIENT "OS: $OS\n";
#foreach my $s (keys %CmdLines) {
#	print CLIENT "[$s]: [$CmdLines{$s}]\n";
#}
		my $cmd = $CmdLines{$ClientCommand};
#print CLIENT "lc ClientCommand: [$ClientCommand] : cmd: [$cmd]\n";
		my $now = localtime(time);
		print CLIENT "Local time: $now\n";
		if($cmd ne '') {
			print CLIENT "CMD: $ClientCommand [$cmd]\n";
			my $result = `$cmd`;
			print CLIENT $result;
		}
		else {
			my $localcmd = Deps::MakePath($BaseDir, ["remotecmd", $ClientCommand], 0);
#print CLIENT "CMDLINE: $localcmd [$cmd]\n";
			if(-f $localcmd) {
				print CLIENT "CMD: $ClientCommand [$localcmd]\n";
				my $result = `$localcmd`;
				print CLIENT $result;
			}
			else {
				print CLIENT "Invalid command: [$localcmd] [$ClientCommand]\n";
			}
		}
	}
}
exit;


sub IsAllowedIPAddress
{
	my ($IPAddress, @allowed) = @_;
	
	foreach my $exp (@allowed) {
		return 1 if($IPAddress =~ /$exp/i);
	}
	return 0;
}

sub BuildDateString
{
	my ($time) = (@_);
	
	my ($sec, $min, $hour, $mday, $mon, $year,
	        $wday, $yday, $isdst) = localtime($time);
	$year += 1900;
	$mon++;
	my $str = sprintf("%02d/%02d/%02d %02d:%02d:%02d", 
			$year, $mon, $mday, $hour, $min, $sec);
	return $str;
}

