#!/bin/sh
eval 'if [ -x /usr/local/cpanel/3rdparty/bin/perl ]; then exec /usr/local/cpanel/3rdparty/bin/perl -x -- $0 ${1+"$@"}; else exec /usr/bin/perl -x $0 ${1+"$@"}; fi;'
if 0;

#!/usr/bin/perl
# Game Server Module, Daemon      Copyright(c) cPanel, Inc.
#                                 All rights Reserved.
# darren@cpanel.net               http://cpgs.cpanel.net
BEGIN {
    unshift( @INC, '/var/cpanel/cpgs', '/var/cpanel/cpgs/games' );
}

$| = 1;
use strict;
use Storable qw(nfreeze thaw);
use cPGSLib;
use cPGSLogger;
use IPC::Open2;

my $cpgsdlog = '/tmp/cpgsclient.log';
my $debug    = 0;
my $logger;

if ($debug) {
    $logger ||= cPGS::cPGSLogger->new();
    cPGS::cPGSLogger->openlog($cpgsdlog);
}

# metadata to send with each request, can hold whatever it needs depending on requirements of command
my %meta;
my %data;
my $remuser;
my $verbose = 1;
$0 = 'cPGSComm';

my $has_ssl = eval { require IO::Socket::SSL; } ? 0 : 1;    # ? <- fix syntax parsing

if ( !@ARGV ) {
    print "This is not meant to be called directly\n";
    exit;
}
if ( -e '/var/cpanel/cpgs/verbose' ) {
    $verbose = 1;
}

my $cnt = 0;

my ( $meta_input, $data_input ) = @ARGV;

# msglog("[META] $meta_input\n[DATA] $data_input");

if ($has_ssl) {
    print "You must have the perl module IO::Socket::SSL installed on this server.\n";
    print "Try `perl -MCPAN -e'force install IO::Socket::SSL'` as root to install the module.\n";
    exit(0);
}
else {
    use IO::Socket::SSL;
}

my ( $sock, $port, $sslpem );

my $meta_inf = cPGSLib::decode_base64($meta_input);
%meta = %{ thaw($meta_inf) };

my $data_inf = cPGSLib::decode_base64($data_input);
%data = %{ thaw($data_inf) };

foreach my $var ( keys %meta ) {
    msglog("meta: $var = $meta{$var}");
}
foreach my $var ( keys %data ) {
    msglog("data: $var = $data{$var}");
}

# $data_ser will be passed through, $meta_ser will be decoded, used and re-encoded

my $calling_user = getpwuid($<);

my $user = $calling_user || $ENV{'CPGSUSER'} || $ENV{'USER'} || $meta{'user'};
my $game = $meta{'game'};
my $srvn = $meta{'srvn'};

if ( $game and $srvn ) {
    if ( !$meta{'remuser'} ) {
        msglog("no remuser meta set");
        $remuser = cPGSLib::getremuser( $user, $game, $srvn );
        $meta{'remuser'} = $remuser;
    }
    else {
        msglog("remuser is being assigned to the meta ($meta{'remuser'}) or to ($user)");
        $remuser = $meta{'remuser'} || $user;
    }
}
else {
    if ( $user eq 'root' and $meta{'remuser'} ) {
        msglog("user is root and $meta{'remuser'} is set, allowing root to override");
        $remuser = $meta{'remuser'};
    }
    else {
        # now that passwords are enabled this is counter productive
        #msglog("user is not root and/or there is no $meta{'remuser'} set, defaulting to $user");
        #$remuser = $user;
    }
}

my $ipval   = $meta{'remip'};
my $command = $meta{'command'};

if ( $verbose == 1 ) {
    msglog("user is $user ($remuser)");

}

if ( !$ipval || !$command ) {
    print "Could not find an IP or Command in input\n";

    #require Data::Dumper;
    #print Dumper( %meta, %data );

    #   print "Usage:\n\n\t$0 <ip> <command> <args>\n\n";
    #   print "\tip\t = IP address of the cPGSD server to communicate with\n";
    #   print "\tcommand\t = Command to have the remote server execute\n";
    #   print "\targs\t = OPTIONAL arguments for the command, some commands require arguments, some do not\n\n";
    #   print "\tExample: $0 127.0.0.1 getversion\n\n";
    exit;
}

my @cfg_data;
if(open( my $config_fh, '<', '/var/cpanel/cpgs/cpgsd.cfg' )) {
    @cfg_data = (<$config_fh>);
    close($config_fh);
} else {
     print "Could not open config file /var/cpanel/cpgs/cpgsd.cfg : $!\n";
     exit;
}

foreach (@cfg_data) {
    s/(\r|\n)//g;
    my ( $var, $val ) = split( /\=/, $_, 2 );
    if ( $var eq 'port' )   { $port   = $val; }
    elsif ( $var eq 'sslpem' ) { $sslpem = $val; }
}

if ( !-e $sslpem ) {
    msglog("Could not find cert/key bundle $sslpem , generating..");
    gensslpem($sslpem);
    if ( !-e $sslpem ) {
        msglog("Unable to generate pem, exiting.");
        exit;
    }
}

if( $user eq 'root' ) {
    $user = $user || $remuser;
    msglog("user is root, setting to $user or $remuser");
}

my ( $status, $secretkey ) = cPGSLib::getauthkey( $user, $meta{'command'} );
if( $status == 1 ) {
    msglog("Bad status from getauthkey : $secretkey");
}

if ( $verbose == 1 ) {
    msglog("==>>\t$ipval $command");
}

$meta{'secretkey'} = $secretkey;
$meta{'localuser'} = $user if $user;
$meta{'remuser'}   = $remuser if $remuser;

if ( $ARGV[0] eq 'DEBUG' ) { $IO::Socket::SSL::DEBUG = 1; }

if (
    !(
        $sock = IO::Socket::SSL->new(
            PeerAddr        => $ipval,
            PeerPort        => $port,
            Proto           => 'tcp',
            SSL_use_cert    => 1,
            SSL_verify_mode => 0x00,
            SSL_key_file    => $sslpem,
            SSL_cert_file   => $sslpem,
            Timeout         => 5,
        )
    )
  ) {

    # create a reply with error message

    my $sslerr = &IO::Socket::SSL::errstr;
    my %reply;
    $reply{'retcode'} = 1;
    $reply{'errmsg'}  = <<EOE;
<table width="530px">
<tr><th><b>Can not connect to remote socket.</b></th></tr>
<tr><td bgcolor="#c0c0c0" align=left> $sslerr </td></tr>
<tr><td>This can caused by a few things:</td></tr>
<tr><td align=left>
 <ul>
  <li>The cPGSD server is not running on $ipval:$port
  <li>There is a firewall blocking access to $ipval:$port
  <li>$ipval is not the right IP and/or $port is not the right port
  <li>Solar Flares (Please contact support to investigate)
 </ul>
</td></tr>
</table>
EOE

    print cPGSLib::encode_base64( nfreeze \%reply ) . "\n\n";
    if ($sock) { close($sock); }
    exit(0);

}
else {
    print "Connect ($sock).\n" if ($IO::Socket::SSL::DEBUG);
}

my ( $subject_name, $issuer_name, $cipher );
if ( ref($sock) eq "IO::Socket::SSL" ) {
    $subject_name = $sock->peer_certificate("subject");
    $issuer_name  = $sock->peer_certificate("issuer");
    $cipher       = $sock->get_cipher();
}

$SIG{PIPE} = sub {
    if ( $verbose == 1 ) {
        msglog("Caught a sig pipe in $0 : $!");
        exit(0);
    }
    exit(0);
};

my $meta_ser = cPGSLib::encode_base64( nfreeze \%meta );
$meta_ser =~ s/\n/###/g;
my $data_ser = cPGSLib::encode_base64( nfreeze \%data );
$data_ser =~ s/\n/###/g;
msglog("Writing meta/data to socket");
print $sock "$meta_ser $data_ser\n\n";
$| = 1;
select(STDERR);
$| = 1;
select(STDOUT);
$| = 1;

my $buf;
msglog("Reading from socket until term command received..");
my $terminate = 0;
until ( $terminate == 1 ) {
    my $sock_fd = fileno($sock);
    msglog("\$sock fd = $sock_fd");
    $_ = <$sock>;
    msglog("<<==\t\"$_\"");    # if $debug;
    if (m/^CPGS_TERMINATE_CONNECTION/) {
        msglog("Received TERMINATE CONNECTION command");
        $terminate = 1;
    }
    else {
        if ($debug) { msglog("  <data> $_ "); }
        $buf .= $_;
    }
    if ( !$_ ) {
        msglog("Recieved null input, terminating..");    # for testing, this will probably break something
        $terminate = 1;
    }

    #   select(undef,undef,undef,0.01);
}

msglog(" Buffer:\n$buf\n\n") if $debug;

if ( my $ret_ser = cPGSLib::decode_base64($buf) ) {
    if ( my %ret = %{ thaw($ret_ser) } ) {
        my $retcode = $ret{'retcode'};
        my $retmsg  = $ret{'retmsg'};

        if ( $retcode != 0 ) {
            msglog( "Command failed: " . $ret{'errmsg'} );
            print cPGSLib::encode_base64($ret_ser) . "\n\n";
        }
        else {
            msglog("Returning data successfully");
            print cPGSLib::encode_base64($ret_ser) . "\n\n";
        }
    }
    else {
        msglog("Could not thaw serialized data: $!");
    }
}
else {
    msglog("Could not decode base64 data: $!");
}

msglog( "\n===[ Complete ]=" . '=' x 94 . "\n" );

$sock->close();

exit 0;

sub msglog {
    my ($msg) = @_;
    if ($debug) {
        if ($verbose) {
            my ( $from, $file, $line ) = caller;
            my $time         = time;
            my $localtime    = localtime($time);
            my $calling_user = getpwuid($<);
            if ( $logger->msg( "\[$localtime\] \[$$\] \[$file\:$line\] \[$calling_user\] " . $msg ) ) {
                return 1;
            }
            else {
                return 0;
            }
        }
    }
    return 1;
}

sub gensslpem {
    my ($sslpem) = @_;
    if ( $< != 0 ) {
        msglog( "Trying to generate cpgs.pem as a non-root user: " . scalar( getpwuid($<) ) );
        exit;
    }
    open2( \*CERT, \*SSL, "openssl req -new -x509 -nodes -days 999 -keyout $sslpem -out $sslpem 2>\&1 >/dev/null" );
    print SSL<<EOF;
GS
cPGS
cPGS
cPGS
cPGS
cPGS
cPGS
EOF
    close(CERT);
    close(SSL);

}
