#!/usr/bin/env perl
use warnings;  
use strict;  

use IO::Socket;
use Term::ReadKey;
use POSIX qw( :sys_wait_h );
use Data::UUID;
use IO::Poll qw( POLLIN POLLHUP POLLOUT POLLERR);
use Term::Size;
use Digest::MD5;
use Sys::Hostname;

use MYDan::Agent::Client;
use MYDan::Agent::Proxy;
use MYDan::Util::OptConf;

$| ++;

=head1 SYNOPSIS

 $0 --host host

    get a shell from remote machine

 $0 --host host [--user user(default `id -un`)] [--sudo sudoer]

    [--cmd top] [--ictrl 0|1]

=cut

$MYDan::Util::OptConf::THIS = 'agent';
my $option = MYDan::Util::OptConf->load();
my ( %o, %proxy ) = $option->get( qw( host=s user=s sudo=s cmd=s ictrl=i ) )->dump();
$option->assert( 'host' );

if( $o{proxy} )
{
    my $proxy =  MYDan::Agent::Proxy->new( $o{proxy} );
    %proxy = $proxy->search( $o{host} );
}

my $proxy = $proxy{$o{host}};

$o{ictrl} = $o{cmd} ? 1 : 0  unless defined $o{ictrl};
$o{user} = `id -un` and chop $o{user}  unless $o{user};

my $uuid = Data::UUID->new->create_str();
my $md5 = Digest::MD5->new()->add( hostname.$$.time.rand 100000 )->hexdigest();

my ($cols, $rows) = Term::Size::chars *STDOUT{IO};

my %query = (
    env => +{ TERM => 'linux' },
    code => 'shell',
    argv => [ $proxy ? undef : '127.0.0.1', $o{port}, $uuid, $rows, $cols, $md5, $o{cmd}, $o{ictrl} ],
    map{ $_ => $o{$_} }qw( user sudo )
);

my $host = delete $o{host};
my %result = MYDan::Agent::Client->new( 
    $host 
)->run( %o, query => \%query ); 

my $call = $result{$host};
die "call fail:$call\n" 
    unless $call && $call =~ /--- 0\n$/;

my $soc = IO::Socket::INET->new(
    PeerAddr => $proxy || $host,
    PeerPort => $o{port},
    Proto    => 'tcp'
);

die( sprintf "Connect %s fail\n", $proxy || $host ) unless $soc;

my $head = "MYDanConnect_::${md5}::_MYDanConnect";
syswrite( $soc, $head, length $head );

$soc->blocking(0);

my $poll = IO::Poll->new();
$poll->mask( $soc => POLLIN  );
$poll->mask( \*STDIN => POLLIN );

ReadMode(4);

syswrite( $soc, $uuid, 36 );

while ( $poll->handles && $soc ) {
    $poll->poll();
    for my $handle ( $poll->handles( POLLIN ) ) 
    {
        my ( $data, $byte );
        if ( $handle eq $soc )
        {
            if ( $byte = sysread( $soc, $data, 1024 ) ) { syswrite( STDOUT, $data, $byte ); }
            else { $soc->shutdown(2); goto EXIT; }
        }

        syswrite( $soc, $data, $byte ) 
            if ( $handle eq \*STDIN )
            &&  ( $byte = sysread( STDIN, $data, 1024 ) );
    }
    if( $poll->handles( POLLHUP | POLLERR) )
    {
        $soc->shutdown( 2 );
        goto EXIT;
    }
}

EXIT:
ReadMode(0);
system 'echo -e "\033[?25h"';
