freeswitch/scripts/perl/FreeSWITCH/Client.pm

313 lines
5.7 KiB
Perl

package FreeSWITCH::Client;
$|=1;
use IO::Socket::INET;
use IO::Select;
use Data::Dumper;
$VERSION = "1.0";
sub init($;$) {
my $proto = shift;
my $args = shift;
my $class = ref($proto) || $proto;
$self->{_host} = $args->{-host} || "localhost";
$self->{_port} = $args->{-port} || 8021;
$self->{_password} = $args->{-password} || undef;
$self->{_tolerant} = $args->{-tolerant} || undef;
$self->{events} = [];
my $me = bless $self,$class;
if (!$self->{_password}) {
return $me;
}
if ($me->connect()) {
return $me;
} else {
return undef;
}
}
sub readhash($;$) {
my ($self,$to) = @_;
my ($can_read) = IO::Select::select($self->{_sel}, undef, undef, $to);
my $s = shift @{$can_read};
my @r = ();
my $crc = 0;
my $h;
if ($s) {
for (;;) {
my $line;
for (;;) {
my $i = 0;
recv $s, $i, 1, 0;
if ($i eq "") {
$h->{socketerror} = "yes";
return $h;
} elsif ($i eq "\n") {
$crc++;
last;
} else {
$crc = 0;
}
$line .= $i;
}
if (!$line) {
last;
}
push @r, $line;
}
if (!@r) {
return undef;
}
foreach(@r) {
my ($var, $val) = /^([^:]+):[\s\t]*(.*)$/;
$h->{lc $var} = $val;
}
if ($h->{'content-length'}) {
if(! defined $h->{body}) { $h->{body} = ""; }
while(length($h->{body}) < $h->{'content-length'}) {
my $buf;
recv $s, $buf, $h->{'content-length'} - length($h->{body}), 0;
if ($buf eq '') {
$h->{socketerror} = "yes";
return $h;
}
$h->{body} .= $buf;
}
}
if ($h->{'content-type'} eq "text/event-plain") {
my $e = $self->extract_event($h);
$h->{has_event} = 1;
$h->{event} = $e;
}
}
return $h;
}
sub error($$) {
my($self,$error) = @_;
if ($self->{"_tolerant"}) {
print "[DIE CROAKED] $error\n";
return 0;
}
else {
die $error;
}
}
sub output($$) {
my ($self,$data) = @_;
my $s = $self->{_sock};
print $s $data ;
}
sub get_events($) {
my $self = shift;
my $e = $self->{events};
$self->{events} = [];
return $e;
}
sub sendmsg($$$) {
my $self = shift;
my $sendmsg = shift;
my $to = shift;
my $e;
for(;;) {
$e = $self->readhash(.1);
if ($e && !$e->{socketerror}) {
push @{$self->{events}}, $e;
} else {
last;
}
}
$self->output($sendmsg->{command} . "\n");
foreach(keys %{$sendmsg}) {
next if ($_ eq "command");
$self->output("$_" . ": " . $sendmsg->{$_} . "\n");
}
$self->output("\n");
for(;;) {
$e = $self->readhash(undef);
last if $e->{socketerror} or $e->{'content-type'} eq 'command/reply'
or $e->{'content-type'} eq 'api/response';
push @{$self->{events}}, $e;
}
return $e;
}
sub command($$) {
my $self = shift;
my $reply;
my $r = $self->sendmsg({ 'command' => "api " . shift });
if ($r->{body} ne '') {
$reply = $r->{body};
} elsif ($r->{'reply-text'} ne '') {
$reply = $r->{'reply-text'};
} else {
$reply = "socketerror";
}
return $reply;
}
sub disconnect($) {
my $self = shift;
if ($self->{_sock}) {
$self->{_sock}->shutdown(2);
$self->{_sock}->close();
}
undef $self->{_sock};
delete $self->{_sock};
}
sub raw_command($) {
my $self = shift;
return $self->sendmsg({ 'command' => shift });
}
sub htdecode($;$) {
my $urlin = shift;
my $url = (ref $urlin) ? \$$urlin : \$urlin;
$$url =~ s/%([0-9A-Z]{2})/chr hex $1/ieg;
$$url;
}
sub extract_event($$) {
my $self = shift;
my $r = shift;
my %h = $r->{body} =~ /^([^:]+)\s*:\s*([^\n]*)/mg;
foreach (keys %h) {
my $new = lc $_;
if (!($new eq $_)) {
# do not delete keys that were already lowercase
$h{$new} = $h{$_};
delete $h{$_};
}
}
foreach(keys %h) {
htdecode(\$h{$_});
}
return \%h;
}
sub call_command($$$) {
my $self = shift;
my $app = shift;
my $arg = shift;
my $hash = {
'command' => "sendmsg",
'call-command' => "execute",
'execute-app-name' => $app,
'execute-app-arg' => $arg
};
return $self->sendmsg($hash);
}
sub unicast($$$$$$) {
my $self = shift;
my $hash = {
'command' => "sendmsg",
'call-command' => "unicast",
'local_ip' => $_[0],
'local_port' => $_[1],
'remote_ip' => $_[2],
'remote_port' => $_[3],
'transport' => $_[4]
};
return $self->sendmsg($hash);
}
sub call_data($) {
my $self = shift;
return $self->{call_data};
}
sub accept($;$$) {
my $self = shift;
my $ip = shift;
my $port = shift || 8084;
if (!$self->{_lsock}) {
$self->{_lsock} = IO::Socket::INET->new(Listen => 10000,
LocalAddr => $ip,
LocalPort => $port,
Reuse => 1,
Proto => "tcp") or return $self->error("Cannot listen");
}
$self->{_sock} = $self->{_lsock}->accept();
$self->{_sock}->autoflush(1);
$self->{_sel} = new IO::Select( $self->{_sock} );
$self->{call_data} = $self->sendmsg({ 'command' => "connect"});
foreach(keys %{$self->{call_data}}) {
htdecode(\$self->{call_data}->{$_});
}
if ($self->{call_data} =~ /socketerror/) {
return 0;
}
return 1;
};
sub connect($) {
my $self = shift;
$self->{_sock} = new IO::Socket::INET( Proto => 'tcp',
PeerAddr => $self->{_host},
PeerPort => $self->{_port}
) or return $self->error("Connection refused $self->{_host} port $self->{_port}");
$self->{_sock}->autoflush(1);
#$self->{_sock}->blocking(0);
$self->{_sel} = new IO::Select( $self->{_sock} );
my $h = $self->readhash(undef);
if ($h->{"content-type"} eq "auth/request") {
my $pass = $self->{"_password"};
$h = $self->sendmsg({command => "auth $pass"});
}
if ($h->{'reply-text'} =~ "OK") {
return 1;
}
return 0;
}
1;