Client.pm 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. package FreeSWITCH::Client;
  2. $|=1;
  3. use IO::Socket::INET;
  4. use IO::Select;
  5. use Data::Dumper;
  6. $VERSION = "1.0";
  7. sub init($;$) {
  8. my $proto = shift;
  9. my $args = shift;
  10. my $class = ref($proto) || $proto;
  11. $self->{_host} = $args->{-host} || "localhost";
  12. $self->{_port} = $args->{-port} || 8021;
  13. $self->{_password} = $args->{-password} || undef;
  14. $self->{_tolerant} = $args->{-tolerant} || undef;
  15. $self->{events} = [];
  16. my $me = bless $self,$class;
  17. if (!$self->{_password}) {
  18. return $me;
  19. }
  20. if ($me->connect()) {
  21. return $me;
  22. } else {
  23. return undef;
  24. }
  25. }
  26. sub readhash($;$) {
  27. my ($self,$to) = @_;
  28. my ($can_read) = IO::Select::select($self->{_sel}, undef, undef, $to);
  29. my $s = shift @{$can_read};
  30. my @r = ();
  31. my $crc = 0;
  32. my $h;
  33. if ($s) {
  34. for (;;) {
  35. my $line;
  36. for (;;) {
  37. my $i = 0;
  38. recv $s, $i, 1, 0;
  39. if ($i eq "") {
  40. $h->{socketerror} = "yes";
  41. return $h;
  42. } elsif ($i eq "\n") {
  43. $crc++;
  44. last;
  45. } else {
  46. $crc = 0;
  47. }
  48. $line .= $i;
  49. }
  50. if (!$line) {
  51. last;
  52. }
  53. push @r, $line;
  54. }
  55. if (!@r) {
  56. return undef;
  57. }
  58. foreach(@r) {
  59. my ($var, $val) = /^([^:]+):[\s\t]*(.*)$/;
  60. $h->{lc $var} = $val;
  61. }
  62. if ($h->{'content-length'}) {
  63. if(! defined $h->{body}) { $h->{body} = ""; }
  64. while(length($h->{body}) < $h->{'content-length'}) {
  65. my $buf;
  66. recv $s, $buf, $h->{'content-length'} - length($h->{body}), 0;
  67. if ($buf eq '') {
  68. $h->{socketerror} = "yes";
  69. return $h;
  70. }
  71. $h->{body} .= $buf;
  72. }
  73. }
  74. if ($h->{'content-type'} eq "text/event-plain") {
  75. my $e = $self->extract_event($h);
  76. $h->{has_event} = 1;
  77. $h->{event} = $e;
  78. }
  79. }
  80. return $h;
  81. }
  82. sub error($$) {
  83. my($self,$error) = @_;
  84. if ($self->{"_tolerant"}) {
  85. print "[DIE CROAKED] $error\n";
  86. return 0;
  87. }
  88. else {
  89. die $error;
  90. }
  91. }
  92. sub output($$) {
  93. my ($self,$data) = @_;
  94. my $s = $self->{_sock};
  95. print $s $data ;
  96. }
  97. sub get_events($) {
  98. my $self = shift;
  99. my $e = $self->{events};
  100. $self->{events} = [];
  101. return $e;
  102. }
  103. sub sendmsg($$$) {
  104. my $self = shift;
  105. my $sendmsg = shift;
  106. my $to = shift;
  107. my $e;
  108. for(;;) {
  109. $e = $self->readhash(.1);
  110. if ($e && !$e->{socketerror}) {
  111. push @{$self->{events}}, $e;
  112. } else {
  113. last;
  114. }
  115. }
  116. $self->output($sendmsg->{command} . "\n");
  117. foreach(keys %{$sendmsg}) {
  118. next if ($_ eq "command");
  119. $self->output("$_" . ": " . $sendmsg->{$_} . "\n");
  120. }
  121. $self->output("\n");
  122. for(;;) {
  123. $e = $self->readhash(undef);
  124. last if $e->{socketerror} or $e->{'content-type'} eq 'command/reply'
  125. or $e->{'content-type'} eq 'api/response';
  126. push @{$self->{events}}, $e;
  127. }
  128. return $e;
  129. }
  130. sub command($$) {
  131. my $self = shift;
  132. my $reply;
  133. my $r = $self->sendmsg({ 'command' => "api " . shift });
  134. if ($r->{body} ne '') {
  135. $reply = $r->{body};
  136. } elsif ($r->{'reply-text'} ne '') {
  137. $reply = $r->{'reply-text'};
  138. } else {
  139. $reply = "socketerror";
  140. }
  141. return $reply;
  142. }
  143. sub disconnect($) {
  144. my $self = shift;
  145. if ($self->{_sock}) {
  146. $self->{_sock}->shutdown(2);
  147. $self->{_sock}->close();
  148. }
  149. undef $self->{_sock};
  150. delete $self->{_sock};
  151. }
  152. sub raw_command($) {
  153. my $self = shift;
  154. return $self->sendmsg({ 'command' => shift });
  155. }
  156. sub htdecode($;$) {
  157. my $urlin = shift;
  158. my $url = (ref $urlin) ? \$$urlin : \$urlin;
  159. $$url =~ s/%([0-9A-Z]{2})/chr hex $1/ieg;
  160. $$url;
  161. }
  162. sub extract_event($$) {
  163. my $self = shift;
  164. my $r = shift;
  165. my %h = $r->{body} =~ /^([^:]+)\s*:\s*([^\n]*)/mg;
  166. foreach (keys %h) {
  167. my $new = lc $_;
  168. if (!($new eq $_)) {
  169. # do not delete keys that were already lowercase
  170. $h{$new} = $h{$_};
  171. delete $h{$_};
  172. }
  173. }
  174. foreach(keys %h) {
  175. htdecode(\$h{$_});
  176. }
  177. return \%h;
  178. }
  179. sub call_command($$$) {
  180. my $self = shift;
  181. my $app = shift;
  182. my $arg = shift;
  183. my $hash = {
  184. 'command' => "sendmsg",
  185. 'call-command' => "execute",
  186. 'execute-app-name' => $app,
  187. 'execute-app-arg' => $arg
  188. };
  189. return $self->sendmsg($hash);
  190. }
  191. sub unicast($$$$$$) {
  192. my $self = shift;
  193. my $hash = {
  194. 'command' => "sendmsg",
  195. 'call-command' => "unicast",
  196. 'local_ip' => $_[0],
  197. 'local_port' => $_[1],
  198. 'remote_ip' => $_[2],
  199. 'remote_port' => $_[3],
  200. 'transport' => $_[4]
  201. };
  202. return $self->sendmsg($hash);
  203. }
  204. sub call_data($) {
  205. my $self = shift;
  206. return $self->{call_data};
  207. }
  208. sub accept($;$$) {
  209. my $self = shift;
  210. my $ip = shift;
  211. my $port = shift || 8084;
  212. if (!$self->{_lsock}) {
  213. $self->{_lsock} = IO::Socket::INET->new(Listen => 10000,
  214. LocalAddr => $ip,
  215. LocalPort => $port,
  216. Reuse => 1,
  217. Proto => "tcp") or return $self->error("Cannot listen");
  218. }
  219. $self->{_sock} = $self->{_lsock}->accept();
  220. $self->{_sock}->autoflush(1);
  221. $self->{_sel} = new IO::Select( $self->{_sock} );
  222. $self->{call_data} = $self->sendmsg({ 'command' => "connect"});
  223. foreach(keys %{$self->{call_data}}) {
  224. htdecode(\$self->{call_data}->{$_});
  225. }
  226. if ($self->{call_data} =~ /socketerror/) {
  227. return 0;
  228. }
  229. return 1;
  230. };
  231. sub connect($) {
  232. my $self = shift;
  233. $self->{_sock} = new IO::Socket::INET( Proto => 'tcp',
  234. PeerAddr => $self->{_host},
  235. PeerPort => $self->{_port}
  236. ) or return $self->error("Connection refused $self->{_host} port $self->{_port}");
  237. $self->{_sock}->autoflush(1);
  238. #$self->{_sock}->blocking(0);
  239. $self->{_sel} = new IO::Select( $self->{_sock} );
  240. my $h = $self->readhash(undef);
  241. if ($h->{"content-type"} eq "auth/request") {
  242. my $pass = $self->{"_password"};
  243. $h = $self->sendmsg({command => "auth $pass"});
  244. }
  245. if ($h->{'reply-text'} =~ "OK") {
  246. return 1;
  247. }
  248. return 0;
  249. }
  250. 1;