App-Termcast

 view release on metacpan or  search on metacpan

lib/App/Termcast.pm  view on Meta::CPAN

package App::Termcast;
BEGIN {
  $App::Termcast::AUTHORITY = 'cpan:DOY';
}
$App::Termcast::VERSION = '0.13';
use Moose;
# ABSTRACT: broadcast your terminal sessions for remote viewing

with 'MooseX::Getopt::Dashes';

use IO::Select;
use IO::Socket::INET;
use JSON;
use Scalar::Util 'weaken';
use Term::Filter::Callback;
use Term::ReadKey;
use Try::Tiny;



has host => (
    is      => 'rw',
    isa     => 'Str',
    default => 'noway.ratry.ru',
    documentation => 'Hostname of the termcast server to connect to',
);


has port => (
    is      => 'rw',
    isa     => 'Int',
    default => 31337,
    documentation => 'Port to connect to on the termcast server',
);


has user => (
    is      => 'rw',
    isa     => 'Str',
    default => sub { $ENV{USER} },
    documentation => 'Username for the termcast server',
);


has password => (
    is      => 'rw',
    isa     => 'Str',
    default => 'asdf', # really unimportant
    documentation => "Password for the termcast server\n"
                   . "                              (mostly unimportant)",
);


has bell_on_watcher => (
    is      => 'rw',
    isa     => 'Bool',
    default => 0,
    documentation => "Send a terminal bell when a watcher connects\n"
                   . "                              or disconnects",
);


has timeout => (
    is      => 'rw',
    isa     => 'Int',
    default => 5,
    documentation => "Timeout length for the connection to the termcast server",
);


has establishment_message => (
    traits     => ['NoGetopt'],
    is         => 'ro',
    isa        => 'Str',

lib/App/Termcast.pm  view on Meta::CPAN

            if ($fh == $socket) {
                ReadMode(0, $self->input)
                    if $self->_has_term && $self->_term->_raw_mode;
                Carp::croak("Invalid password");
            }
        }
        for my $fh (@$r) {
            if ($fh == $socket) {
                my $buf;
                $socket->recv($buf, 4096);
                if (!defined $buf || length $buf == 0) {
                    ReadMode(0, $self->input)
                        if $self->_has_term && $self->_term->_raw_mode;
                    Carp::croak("Invalid password");
                }
                elsif ($buf ne ('hello, ' . $self->user . "\n")) {
                    ReadMode(0, $self->input)
                        if $self->_has_term && $self->_term->_raw_mode;
                    Carp::carp("Unknown login response from server: $buf");
                    ReadMode(5, $self->input)
                        if $self->_has_term && $self->_term->_raw_mode;
                }
            }
        }
    }

    ReadMode(5, $self->input)
        if $self->_has_term && $self->_term->_raw_mode;
    return $socket;
}

before clear_socket => sub {
    my $self = shift;
    Carp::carp("Lost connection to server ($!), reconnecting...");
    $self->socket->close;
    ReadMode(0, $self->input)
        if $self->_has_term && $self->_term->_raw_mode;
};

sub _new_socket {
    my $self = shift;
    $self->_term->remove_input_handle($self->socket);
    $self->clear_socket;
    $self->_term->add_input_handle($self->socket);
}

has _needs_termsize_update => (
    traits  => ['NoGetopt'],
    is      => 'rw',
    isa     => 'Bool',
    default => 0,
);

has _term => (
    is        => 'ro',
    does      => 'Term::Filter',
    lazy      => 1,
    predicate => '_has_term',
    default   => sub {
        my $_self = shift;
        weaken(my $self = $_self);
        # XXX using ::Callback for now because we need to be able to
        # instantiate App::Termcast objects without initializing the terminal
        # (in case of just calling write_to_termcast). This should
        # eventually be deprecated in favor of moving the termcast interaction
        # code out to an App::Termcast::Writer module or something, and
        # this module should be a simple wrapper that combines that module
        # with Term::Filter.
        Term::Filter::Callback->new(
            callbacks => {
                setup => sub {
                    my ($term) = @_;
                    $term->add_input_handle($self->socket);
                },
                winch => sub {
                    # for the sake of sending a clear to the client anyway
                    syswrite $self->output, "\e[H\e[2J";
                    $self->_needs_termsize_update(1);
                },
                read_error => sub {
                    my ($term, $fh) = @_;
                    if ($fh == $self->socket) {
                        $self->_new_socket;
                    }
                },
                read => sub {
                    my ($term, $fh) = @_;
                    if ($fh == $self->socket) {
                        my $got = $term->_read_from_handle(
                            $self->socket, "socket"
                        );
                        $self->_new_socket unless defined $got;

                        if ($self->bell_on_watcher) {
                            # something better to do here?
                            syswrite $self->output, "\a";
                        }
                    }
                },
                munge_output => sub {
                    my ($term, $buf) = @_;
                    $self->write_to_termcast($buf);
                    $buf;
                },
            },
        );
    },
    handles => [ 'run', 'input', 'output' ],
);


sub write_to_termcast {
    my $self = shift;
    my ($buf) = @_;

    my $socket = $self->socket;
    my $select = IO::Select->new($socket);

    my (undef, $w, $e) = IO::Select->select(
        undef, $select, $select, $self->timeout,
    );



( run in 2.878 seconds using v1.01-cache-2.11-cpan-d8267643d1d )