/usr/local/CPAN/Teamspeak/Teamspeak/Telnet.pm


# $Id: Telnet.pm 35 2007-10-21 22:23:54Z maletin $
# $URL: http://svn.berlios.de/svnroot/repos/cpan-teamspeak/cpan/trunk/lib/Teamspeak/Telnet.pm $

package Teamspeak::Telnet;

use 5.004;
use strict;
use Carp;
use vars qw( $VERSION );
use Teamspeak::Telnet::Channel;
$VERSION = '0.6';
my @ISA = qw( Teamspeak );

## Module import.
use Net::Telnet;

sub connect {
    my $self = shift;
    my $t    = Net::Telnet->new(
        Timeout => $self->{timeout},
        errmode => [ \&my_die, $self, 'Telnet Timeout' ]
    );
    if ( !$t ) {
        $self->my_die("can't create Telnet-Instance");
        return undef;
    }
    $t->open( Host => $self->{host}, Port => $self->{port} )
        or do {
        $self->my_die("Telnet open $t->errmsg");
        return undef;
        };
    $t->waitfor('/\[TS\]$/');
    $self->{sock} = $t;
}    # connect

sub new {
    my ( $class, %arg ) = @_;
    bless {
        host    => $arg{host}    || 'localhost',
        port    => $arg{port}    || 51234,
        timeout => $arg{timeout} || 4,
        },
        ref($class) || $class;
}    # new

# Server List:
sub sl {
    my $self = shift;
    $self->{sock}->print('sl');
    my ($answer) = $self->{sock}->waitfor('/OK$/');
    return grep( /^\d+$/, split( /\n/, $answer ) );
}

# Select Server:
sub sel {
    my ( $self, $server_id ) = @_;
    $self->{sock}->print("sel $server_id");
    my ($answer) = $self->{sock}->waitfor('/OK$/');
    return 1;
}    # sel

# Superadmin LOGIN:
sub slogin {
    my ( $self, $login, $pwd ) = @_;
    $self->{sock}->print("slogin $login $pwd");
    $self->{sock}->waitfor('/OK$/');
    $self->{slogin} = $login;
    return 1;
}    # slogin

# normal LOGIN:
sub login {
    my ( $self, $login, $pwd ) = @_;
    $self->{sock}->print("login $login $pwd");
    $self->{sock}->waitfor('/OK$/');
    $self->{login} = $login;
    return 1;
}    # login

# Database userlist:
sub dbuserlist {
    my $self   = shift;
    my @result = ();
    if ( !$self->logged_in ) {
        $self->my_die("command needs login");
        return undef;
    }
    $self->{sock}->print('dbuserlist');
    my ( $answer, $match ) = $self->{sock}->waitfor('/(OK|ERROR,.*)$/');
    return @result if ( $match =~ /no data/ );
    my @lines = split( /\n/, $answer );
    shift @lines;    # First Line is empty
    my $fields = shift @lines;
    return unless $fields;
    my @fields = split( /\t/, $fields );

    foreach my $line (@lines) {
        my @r = split( /\t/, $line );
        my %args = map {
            $r[$_] =~ s/^"(.*)"$/$1/;
            $r[$_] =~ s/^(\d\d)-(\d\d)-(\d{4})/$3-$2-$1/;
            $fields[$_] => $r[$_]
        } 0 .. @r - 1;
        push( @result, {%args} );
    }
    return @result;
}    # dbuserlist

# dbuserid
sub dbuserid {
    my $self = shift;
    my $nick = shift;
    $self->{sock}->print( 'dbuserid ' . $nick );
    my ( $answer, $match ) = $self->{sock}->waitfor('/(OK|ERROR*)$/');
    if ( !defined $match or $match =~ /ERROR/ ) {
        $self->my_die($match);
        return undef;
    }
    return int($answer);
}    # dbuserid

# Database userdelete:
sub delete_user {
    my ( $self, $user_id ) = @_;
    $self->{sock}->print("dbuserdel $user_id");
    $self->{sock}->waitfor('/OK$/');
    return 1;
}    # delete_user

# Database useradd:
sub add_user {
    my ( $self, %args ) = @_;
    $args{admin} = 0 if $args{admin} != 1;
    $self->{sock}
        ->print("dbuseradd $args{user} $args{pwd} $args{pwd} $args{admin}");
    $self->{sock}->waitfor('/OK$/');
    return 1;
}    # add_user

# Channel List:
sub cl {
    my $self = shift;
    $self->{sock}->print('cl');
    my ( $answer, $match ) = $self->{sock}->waitfor('/(OK|ERROR.*)$/');
    if ( !defined $match or $match =~ /ERROR/ ) {
        $self->my_die($match);
        return undef;
    }
    my @lines = split( /\n/, $answer );
    shift @lines;    # First Line is empty
    my $fields = shift @lines;
    my @fields = split( /\t/, $fields );
    my @result = ();
    foreach my $line (@lines) {
        my @r = split( /\t/, $line );
        my %args = map {
            $r[$_] =~ s/^"(.*)"$/$1/;
            $r[$_] =~ s/^(\d\d)-(\d\d)-(\d{4})/$3-$2-$1/;
            $fields[$_] => $r[$_]
        } 0 .. @r - 1;
        my $ch = Teamspeak::Telnet::Channel->new(%args);
        $ch->{tsh} = $self;
        $self->{channel}{ $r[0] } = $ch;
    }
    return scalar keys %{ $self->{channel} };
}    # cl

# Player Information
sub pi {
    my $self     = shift;
    my $playerid = shift;
    $self->{sock}->print( 'pi ' . $playerid );
    my ( $answer, $match ) = $self->{sock}->waitfor('/(OK|ERROR.*)$/');
    if ( !defined $match or $match =~ /ERROR/ ) {
        $self->my_die($match);
        return undef;
    }
    my @lines = split( /\n/, $answer );
    shift @lines;    # First Line is empty
    my $fields = shift @lines;
    my @fields = split( /\t/, $fields );
    my @result = ();
    my $line   = shift @lines;
    my @r      = split( /\t/, $line );
    my %args   = map {
        $r[$_] =~ s/^"(.*)"$/$1/;
        $fields[$_] => $r[$_]
    } 0 .. @r - 1;
    return \%args;
}    # pi

# Player List:
sub pl {
    my $self = shift;
    $self->{sock}->print('pl');
    my ( $answer, $match ) = $self->{sock}->waitfor('/(OK|ERROR.*)$/');
    if ( !defined $match or $match =~ /ERROR/ ) {
        $self->my_die($match);
        return undef;
    }
    my @lines = split( /\n/, $answer );
    shift @lines;    # First Line is empty
    my $fields = shift @lines;
    my @fields = split( /\t/, $fields );
    my @result = ();
    foreach my $line (@lines) {
        my @r = split( /\t/, $line );
        my %args = map {
            $r[$_] =~ s/^"(.*)"$/$1/;
            $r[$_] =~ s/^(\d\d)-(\d\d)-(\d{4})/$3-$2-$1/;
            $fields[$_] => $r[$_]
        } 0 .. @r - 1;
        push( @result, {%args} );
    }
    return @result;
}    # pl

# Find Player(s):
sub fp {
    my $self = shift;
    my $nick = shift;
    if ( $nick =~ / / ) {

       # the nickname contains a space-char and we cannot escape that space
       # for the fp-command, so we use the output of pl to simulate the result
        my @plresult = $self->pl();
        if (@plresult) {
            my @result = ();
            foreach my $playerref (@plresult) {
                my %player = %{$playerref};
                if (   ( $player{nick} =~ /$nick/ )
                    || ( $player{loginname} =~ /$nick/ ) )
                {
                    my %args = (
                        p_id      => $player{p_id},
                        p_dbid    => 0,
                        c_id      => $player{c_id},
                        nickname  => $player{nick},
                        loginname => $player{loginname},
                        ip        => $player{ip}
                    );
                    if ( $player{loginname} )
                    { # player has an loginname, so he has a dbid too, but we dont get that via pl, so ask pi
                        my $piinforef = $self->pi( $player{p_id} );
                        if ($piinforef) {
                            my %piinfo = %{$piinforef};
                            $args{p_dbid} = $piinfo{p_dbid};
                        }
                    }
                    push( @result, \%args );
                }
            }
            return @result;
        }
        else {
            return undef;
        }
    }
    else {
        $self->{sock}->print( 'fp ' . $nick );
        my ( $answer, $match ) = $self->{sock}->waitfor('/(OK|ERROR.*)$/');
        if ( !defined $match or $match =~ /ERROR/ ) {
            $self->my_die($match);
            return undef;
        }
        my @lines = split( /\n/, $answer );
        shift @lines;    # First Line is empty
        my $fields = shift @lines;
        my @fields = split( /\t/, $fields );
        my @result = ();
        foreach my $line (@lines) {
            my @r = split( /\t/, $line );
            my %args = map {
                $r[$_] =~ s/^"(.*)"$/$1/;
                $fields[$_] => $r[$_]
            } 0 .. @r - 1;
            push( @result, {%args} );
        }
        return @result;
    }
}    # fp

# adds an IP ban to the banlist (optional with time)
sub banadd {
    my $self = shift;
    my $ip   = shift;
    my $time = shift;
    $self->{sock}->print( 'banadd ' . $ip . ' ' . $time );
    my ( $answer, $match ) = $self->{sock}->waitfor('/(OK|ERROR.*)$/');
    if ( !defined $match or $match =~ /ERROR/ ) {
        $self->my_die($match);
        return undef;
    }
    return 1;
}    # banadd

# bans the IP of a currently connected player
sub banplayer {
    my $self      = shift;
    my $player_id = shift;
    my $time      = shift;
    $self->{sock}->print( 'banplayer ' . $player_id . ' ' . $time );
    my ( $answer, $match ) = $self->{sock}->waitfor('/(OK|ERROR.*)$/');
    if ( !defined $match or $match =~ /ERROR/ ) {
        $self->my_die($match);
        return undef;
    }
    return 1;
}    # banplayer

# kick a player of the server
sub kick {
    my $self      = shift;
    my $player_id = shift;
    $self->{sock}->print( 'kick ' . $player_id );
    my ( $answer, $match ) = $self->{sock}->waitfor('/(OK|ERROR.*)$/');
    if ( !defined $match or $match =~ /ERROR/ ) {
        $self->my_die($match);
        return undef;
    }
    return 1;
}    # kick

# set attributes of virtual servers
sub serverset {
    my $self            = shift;
    my $attribute_name  = shift;
    my $attribute_value = shift;

    #?? surround the value with "" ?
    $self->{sock}
        ->print( 'serverset ' . $attribute_name . ' ' . $attribute_value );
    my ( $answer, $match ) = $self->{sock}->waitfor('/(OK|ERROR.*)$/');
    if ( !defined $match or $match =~ /ERROR/ ) {
        $self->my_die($match);
        return undef;
    }
    return 1;
}    # serverset

# gets the average packet loss
sub gapl {
    my $self = shift;
    my $port = shift;
    if   ($port) { $self->{sock}->print( 'gapl ' . $port ); }
    else         { $self->{sock}->print('gapl'); }
    my ( $answer, $match ) = $self->{sock}->waitfor('/(OK|ERROR.*)$/');
    if ( !defined $match or $match =~ /ERROR/ ) {
        $self->my_die($match);
        return undef;
    }
    $answer =~ /=([\d\.]+)%/;
    return $1;
}    # gapl

# move a player to a channel
sub mptc {
    my $self       = shift;
    my $channel_id = shift;
    my $player_id  = shift;
    $self->{sock}->print( 'mptc ' . $channel_id . ' ' . $player_id );
    my ( $answer, $match ) = $self->{sock}->waitfor('/(OK|ERROR.*)$/');
    if ( !defined $match or $match =~ /ERROR/ ) {
        $self->my_die($match);
        return undef;
    }
    return 1;
}    # mptc

# disconnect a user silently from the server
sub removeclient {
    my $self      = shift;
    my $player_id = shift;
    $self->{sock}->print( 'removeclient ' . $player_id );
    my ( $answer, $match ) = $self->{sock}->waitfor('/(OK|ERROR.*)$/');
    if ( !defined $match or $match =~ /ERROR/ ) {
        $self->my_die($match);
        return undef;
    }
    return 1;
}    # removeclient

# Message to selected virtual server:
sub msg {
    my $self = shift;
    my $text = shift;
    $self->{sock}->print( 'msg ' . $text );
    my ( $answer, $match ) = $self->{sock}->waitfor('/(OK|ERROR.*)$/');
    if ( !defined $match or $match =~ /ERROR/ ) {
        $self->my_die($match);
        return undef;
    }
    return 1;
}    # msg

# Message to a user of the selected virtual server:
sub msgu {
    my $self = shift;
    my $dbid = shift;
    my $text = shift;
    $self->{sock}->print( 'msgu ' . $dbid . ' ' . $text );
    my ( $answer, $match ) = $self->{sock}->waitfor('/(OK|ERROR.*)$/');
    if ( !defined $match or $match =~ /ERROR/ ) {
        $self->my_die($match);
        return undef;
    }
    return 1;
}    # msgu

# Disconnect:
sub disconnect {
    my $self = shift;
    $self->{sock}->print('quit');
    delete $self->{sock};
}

sub my_die {
    my ( $self, @msg ) = @_;
    $self->{err} = 1;
    @msg = ('unknown error') if ( !@msg );
    $self->{errmsg} = "@msg";
    carp "my_die @msg";
}

sub logged_in {
    my $self = shift;
    return 2 if ( defined $self->{slogin} );
    return 1 if ( defined $self->{login} );
    return 0;
}

sub channels {
    if ( defined $_[0]->{channel} and ref( $_[0]->{channel} ) eq 'HASH' ) {
        return keys( %{ $_[0]->{channel} } );
    }
    else {
        return undef;
    }
}    # channels

1;