123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312 |
- 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;
|