[Freeswitch-svn] [commit] r3116 - in freeswitch/trunk/scripts: POE-Filter-FSSocket POE-Filter-FSSocket/examples POE-Filter-FSSocket/lib POE-Filter-FSSocket/lib/POE POE-Filter-FSSocket/lib/POE/Filter POE-Filter-FSSocket/t socket

Freeswitch SVN ptinsley at freeswitch.org
Fri Oct 20 02:20:40 EDT 2006


Author: ptinsley
Date: Fri Oct 20 02:20:39 2006
New Revision: 3116

Added:
   freeswitch/trunk/scripts/POE-Filter-FSSocket/
   freeswitch/trunk/scripts/POE-Filter-FSSocket/CHANGES
   freeswitch/trunk/scripts/POE-Filter-FSSocket/INSTALL
   freeswitch/trunk/scripts/POE-Filter-FSSocket/LICENSE
   freeswitch/trunk/scripts/POE-Filter-FSSocket/Makefile.PL
   freeswitch/trunk/scripts/POE-Filter-FSSocket/README
   freeswitch/trunk/scripts/POE-Filter-FSSocket/TODO
   freeswitch/trunk/scripts/POE-Filter-FSSocket/examples/
   freeswitch/trunk/scripts/POE-Filter-FSSocket/examples/fsconsole.pl
   freeswitch/trunk/scripts/POE-Filter-FSSocket/examples/poetest.pl
   freeswitch/trunk/scripts/POE-Filter-FSSocket/lib/
   freeswitch/trunk/scripts/POE-Filter-FSSocket/lib/POE/
   freeswitch/trunk/scripts/POE-Filter-FSSocket/lib/POE/Filter/
   freeswitch/trunk/scripts/POE-Filter-FSSocket/lib/POE/Filter/FSSocket.pm   (contents, props changed)
   freeswitch/trunk/scripts/POE-Filter-FSSocket/t/
   freeswitch/trunk/scripts/POE-Filter-FSSocket/t/01_basic.t
   freeswitch/trunk/scripts/socket/fsconsole.pl

Log:
Initial checkin of the POE::Filter::FSSocket module and fsconsole.pl which is a curses, multi-window console for freeswitch mod_event_socket.  See http://search.cpan.org/~ptinsley/POE-Filter-FSSocket-0.04/lib/POE/Filter/FSSocket.pm for more info on the perl module.


Added: freeswitch/trunk/scripts/POE-Filter-FSSocket/CHANGES
==============================================================================
--- (empty file)
+++ freeswitch/trunk/scripts/POE-Filter-FSSocket/CHANGES	Fri Oct 20 02:20:39 2006
@@ -0,0 +1,50 @@
+=========================
+2006-10-14 00:08:00 v0_05
+=========================
+
+  2006-10-14 00:08:00 (r5) by ptinsley
+
+    Added some "bullet proofing" in the module to handle bad/unknown input
+    Updated the curses example (fsconsole.pl)
+    Added debug and strict parsing as part of the bullet proofing
+     to enable debug add debug => 1 in your new() and if you want the module
+     to croak when it doesn't properly parse something use strict => 1 in the
+     new().  Example Poe::Filter::FSSocket->new(debug => 1, strict => 1)
+     The default for debug and strict is 0
+
+=========================
+2006-10-14 00:08:00 v0_04
+=========================
+
+  2006-10-14 00:08:00 (r4) by ptinsley
+
+    Fixed support for log/data
+    Added an example that is a quick curses console for freeswitch
+
+=========================
+2006-09-18 21:08:00 v0_03
+=========================
+
+  2006-09-18 21:08:00 (r3) by ptinsley
+
+    Added support for log/data
+
+=========================
+2006-09-17 23:57:00 v0_02
+=========================
+
+  2006-09-17 23:57:00 (r2) by ptinsley
+
+    Added support for api/response type, data ends up in api-response variable.
+
+=========================
+2006-09-17 22:19:20 v0_01
+=========================
+
+  2006-09-17 22:19:20 (r1) by ptinsley
+
+    Initial package of the module.
+
+==============
+End of Excerpt
+==============

Added: freeswitch/trunk/scripts/POE-Filter-FSSocket/INSTALL
==============================================================================
--- (empty file)
+++ freeswitch/trunk/scripts/POE-Filter-FSSocket/INSTALL	Fri Oct 20 02:20:39 2006
@@ -0,0 +1,4 @@
+perl Makefile.PL
+make
+make test
+make install

Added: freeswitch/trunk/scripts/POE-Filter-FSSocket/LICENSE
==============================================================================
--- (empty file)
+++ freeswitch/trunk/scripts/POE-Filter-FSSocket/LICENSE	Fri Oct 20 02:20:39 2006
@@ -0,0 +1,18 @@
+The contents of this file are subject to the Mozilla Public License
+Version 1.1 (the "License"); you may not use this file except in
+compliance with the License. You may obtain a copy of the License at
+http://www.mozilla.org/MPL/
+
+Software distributed under the License is distributed on an "AS IS"
+basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+License for the specific language governing rights and limitations
+under the License.
+
+The Original Code is POE::Filter::FSSocket.
+
+The Initial Developer of the Original Code is Paul Tinsley <pdt at jackhammer.org>.
+Portions created by the Initial Developer are Copyright (C) 2006
+the Initial Developer. All Rights Reserved.
+
+Contributor(s): 
+	Paul Tinsley <pdt at jackhammer.org>

Added: freeswitch/trunk/scripts/POE-Filter-FSSocket/Makefile.PL
==============================================================================
--- (empty file)
+++ freeswitch/trunk/scripts/POE-Filter-FSSocket/Makefile.PL	Fri Oct 20 02:20:39 2006
@@ -0,0 +1,22 @@
+#!/usr/bin/perl
+
+use ExtUtils::MakeMaker;
+
+WriteMakefile(
+	NAME         => 'POE::Filter::FSSocket',
+	AUTHOR       => 'Paul Tinsley <pdt at jackhammer.org>',
+	ABSTRACT     => 'POE filter for getting events out of FreeSWITCH',
+	VERSION_FROM => 'lib/POE/Filter/FSSocket.pm',
+
+	PM           => { 'lib/POE/Filter/FSSocket.pm' => '$(INST_LIBDIR)/FSSocket.pm' },
+	PREREQ_PM    => {
+		POE        => 0.3101,
+		Test::More => 0,
+		POE::Component::Client::TCP => 0,
+		POE::Filter::Line => 0,
+	},
+	dist         => {
+		COMPRESS   => 'gzip -9f',
+		SUFFIX     => 'gz',
+	},
+);

Added: freeswitch/trunk/scripts/POE-Filter-FSSocket/README
==============================================================================
--- (empty file)
+++ freeswitch/trunk/scripts/POE-Filter-FSSocket/README	Fri Oct 20 02:20:39 2006
@@ -0,0 +1,3 @@
+A POE filter for FreeSWITCH (http://www.freeswitch.org) that parses event/log/etc... messages for you.  You must ask for events in plain mode.
+
+perldoc POE::Filter::FSSocket for more info and an example.

Added: freeswitch/trunk/scripts/POE-Filter-FSSocket/TODO
==============================================================================
--- (empty file)
+++ freeswitch/trunk/scripts/POE-Filter-FSSocket/TODO	Fri Oct 20 02:20:39 2006
@@ -0,0 +1,5 @@
+- Support bgapi output
+- more example scripts
+- more documentation
+- sister component that uses this filter
+- reconnect handling

Added: freeswitch/trunk/scripts/POE-Filter-FSSocket/examples/fsconsole.pl
==============================================================================
--- (empty file)
+++ freeswitch/trunk/scripts/POE-Filter-FSSocket/examples/fsconsole.pl	Fri Oct 20 02:20:39 2006
@@ -0,0 +1,361 @@
+#!/usr/bin/perl
+use strict;
+use warnings;
+
+sub POE::Kernel::ASSERT_DEFAULT () { 1 };
+sub Term::Visual::DEBUG () { 1 }
+sub Term::Visual::DEBUG_FILE () { 'test.log' }
+use IO::Socket;
+use POE qw/Filter::FSSocket Component::Client::TCP/;
+use Data::Dumper;
+use Term::Visual;
+
+
+local *D;
+if (Term::Visual::DEBUG) {
+    *D = *Term::Visual::ERRS;
+}
+
+#local *ERROR = *STDERR;
+
+
+$SIG{__DIE__} = sub {
+    if (Term::Visual::DEBUG) {
+        print Term::Visual::ERRS "Died: @_\n";
+    }
+};
+
+###############################################################################
+## BEGIN Globals ##############################################################
+###############################################################################
+our $server_address = "127.0.0.1";
+our $server_port    = "8021";
+our $server_secret  = "ClueCon";
+
+#this is where you can customize the color scheme
+our %Pallet = (
+	'warn_bullet' => 'bold yellow',
+	'err_bullet'  => 'bold red',
+	'out_bullet'  => 'bold green',
+	'access'      => 'bright red on blue',
+	'current'     => 'bright yellow on blue',
+);
+
+our $terminal;
+my %sockets;
+my %windows;
+my %unread_count;
+my %commands = (
+	'window' => 1,
+	'w'      => 1,
+	'win'    => 1,
+);
+###############################################################################
+##   END Globals ##############################################################
+###############################################################################
+
+#setup our session
+POE::Session->create(
+	'inline_states' => {
+		'_start'       => \&handle_start,        #session start
+		'_stop'        => \&handle_stop,         #session stop
+		'curses_input' => \&handle_curses_input, #input from the keyboard
+		'update_time'  => \&handle_update_time,  #update the status line clock
+		'quit'         => \&handle_quit,         #handler to do any cleanup
+		'server_input' => \&handle_server_input,
+		'_default'      => \&handle_default,
+	},
+	'heap' => {
+		'terminal'     => undef,
+		'freeswitch'   => undef,
+	},
+);
+
+#start the kernel a chugging along
+$poe_kernel->run;
+
+###############################################################################
+## BEGIN Handlers #############################################################
+###############################################################################
+#handles any startup functions for our session
+sub handle_default {
+}
+
+sub handle_start {
+	my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
+
+	#setup our terminal
+	 $heap->{'terminal'} = Term::Visual->new(
+		'Alias'        => 'terminal', #poe alias for this
+		'History_Size' => 300,        #number of things to keep in history
+		'Common_Input' => 1,          #all windows share input and history
+		'Tab_Complete' => \&tab_complete,
+	);
+
+	$terminal = $heap->{'terminal'};
+
+	#setup the color palette
+	$terminal->set_palette(%Pallet);
+
+	#create a base window
+	my $window_id = $terminal->create_window(
+		'Window_Name' => 'console',
+		'Buffer_Size' => 3000,
+		'Title'       => 'FreeSWITCH Console',
+		'Status'      => {
+			'0' => {
+				'format' => '%s',
+				'fields' => ['time'],
+			},
+			'1' => {
+				'format' => '%s',
+				'fields' => ['window_status'],
+			},
+		},
+	);
+
+	$windows{'console'} = $window_id;
+
+	$window_id = $terminal->create_window(
+		'Window_Name' => 'log',
+		'Buffer_Size' => 3000,
+		'Title'       => 'FreeSWITCH Logs',
+		'Status'      => {
+			'0' => {
+				'format' => '%s',
+				'fields' => ['time'],
+			},
+			'1' => {
+				'format' => '%s',
+				'fields' => ['window_status'],
+			},
+		},
+	);
+
+	$windows{'log'} = $window_id;
+
+	$window_id = $terminal->create_window(
+		'Window_Name' => 'event',
+		'Buffer_Size' => 3000,
+		'Title'       => 'FreeSWITCH Event',
+		'Status'      => {
+			'0' => {
+				'format' => '%s',
+				'fields' => ['time'],
+			},
+			'1' => {
+				'format' => '%s',
+				'fields' => ['window_status'],
+			},
+		},
+	);
+
+	$windows{'event'} = $window_id;
+
+	#tell the terminal what to call when there is input from the keyboard
+	$kernel->post('terminal' => 'send_me_input' => 'curses_input');
+
+	$terminal->change_window(0);
+	$kernel->delay_set('update_time' => 1);
+	$terminal->set_status_field(0, 'time' => scalar(localtime));
+	new_message('destination_window' => 0, 'message' =>  "
+Welcome to the FreeSWITCH POE Curses Console!
+  The console is split into three windows:
+    - 'console' for api response messages
+    - 'log'     for freeswitch log output (simply send the log level you want 
+                  to start seeing events eg: 'log all')
+    - 'event'   for freeswitch event output (must subscribe in plain format
+                  eg: 'event plain all')
+
+To switch between windows type 'w <windowname' so 'w log' for example.
+
+Coming soon:
+  - Tab completion
+  - command history
+  - window status in the bar (messages added since last view, etc...)
+
+Send any bug reports or comments to jackhammer\@gmail.com
+
+Thanks,
+Paul\n");
+
+	$terminal->set_status_field($terminal->current_window, 'window_status' => format_window_status());
+
+	#connect to freeswitch
+	$heap->{'freeswitch'} = POE::Component::Client::TCP->new(
+		'RemoteAddress' => $server_address,
+		'RemotePort'    => $server_port,
+		'ServerInput'   => \&handle_server_input,
+		'Connected'     => \&handle_fs_connected,
+		'ServerError'   => \&handle_server_error,
+		'Disconnected'  => \&handle_server_disconnect,
+		'Domain'        => AF_INET,
+		'Filter'       => POE::Filter::FSSocket->new(),
+	);
+
+}
+
+#called when users enter commands in a window
+sub handle_curses_input {
+	my ($kernel, $heap, $input, $context) = @_[KERNEL, HEAP, ARG0, ARG1];
+
+	#get the id of the window that is responsible for the input
+	my $window = $heap->{'terminal'}->current_window;
+
+	open(ERROR, ">>error.log");
+
+	if($input eq "quit") {
+		$kernel->yield('quit');
+	} elsif ($input =~ /^w\ (.*)$/) {
+		#get the id of the requested window
+		eval {
+			my $window_id = $windows{$1};
+
+			#see if it's real
+			if(defined($window_id)) {
+				$unread_count{$window_id} = 0;
+				$terminal->change_window($window_id);
+				$terminal->set_status_field($window_id, 'window_status' => &format_window_status());
+			}
+		};
+		if($@) {
+			print ERROR "put error: $@\n";
+		}
+	} else {
+		#see if we got connected at some point
+		if(defined($sockets{'localhost'})) {
+			#send the command
+			$sockets{'localhost'}->put($input);
+		}
+	}
+}
+
+sub handle_fs_connected {
+	my ($kernel, $heap) = @_[KERNEL, HEAP];
+
+	eval {
+		$sockets{'localhost'} = $heap->{'server'};
+	}
+}
+
+#this is responsible for doing any cleanup and returning the terminal to the previous
+#state before we mucked with it
+sub handle_quit {
+	my ($kernel, $heap) = @_[KERNEL, HEAP];
+
+	#tell curses to clean up it's crap
+	$kernel->post('terminal' => 'shutdown');
+
+	#there is probably a more elegant way, but this works for now
+	exit;
+}
+
+#data from freeswitch
+sub handle_server_input {
+        my ($kernel,$heap,$input) = @_[KERNEL,HEAP,ARG0];
+
+	eval {
+		#terminal HATES null
+		if(defined($input->{'__DATA__'})) {
+			$input->{'__DATA__'} =~ s/[\x00]//g;
+		}
+
+		#handle the login
+		if($input->{'Content-Type'} eq "auth/request") {
+			$heap->{'server'}->put("auth $server_secret");
+		} elsif ($input->{'Content-Type'} eq "api/response") {
+			new_message('destination_window' => 0, 'message' => 'Response: ');
+			new_message('destination_window' => 0, 'message' => $input->{'__DATA__'});
+		} elsif ($input->{'Content-Type'} eq "log/data") {
+			new_message('destination_window' => 1, 'message' => $input->{'__DATA__'});
+		} elsif ($input->{'Content-Type'} eq "text/event-plain") {
+			new_message('destination_window' => 2, 'message' => Dumper $input);
+		}
+	};
+
+	if($@) {
+		open(ERROR, ">>error.log");
+		print ERROR "died: $@\n";
+		print ERROR Dumper $heap;
+		close(ERROR);
+	}
+}
+
+sub handle_server_error {
+}
+
+sub handle_server_disconnect {
+}
+
+sub tab_complete {
+	my $left = shift;
+
+	my @return;
+
+	if(defined($commands{$left})) {
+		return [$left . " "];
+	#} elsif () {
+	}
+		
+}
+
+sub handle_update_time {
+	my ($kernel, $heap) = @_[KERNEL, HEAP];
+	$terminal->set_status_field($terminal->current_window, 'time' => scalar(localtime));
+	$kernel->delay_set('update_time' => 1);
+}
+###############################################################################
+##   END Handlers #############################################################
+###############################################################################
+
+sub new_message {
+	my %args = @_;
+
+	my $message            = $args{'message'};
+	my $destination_window = $args{'destination_window'};
+
+	my $status_field;
+
+	#see if we are on the window being updated
+	if($terminal->current_window != $destination_window) {
+		#increment the unread count for the window
+		#FIXME, should we count messages or lines?
+		$unread_count{$destination_window}++;
+
+
+		#update the status bar
+		eval {
+			$terminal->set_status_field($terminal->current_window, 'window_status' => &format_window_status());
+		};
+
+		if($@) {
+			print $@;
+		}
+	}
+
+
+	#deliver the message
+	$terminal->print($destination_window, $message);
+}
+
+sub format_window_status {
+	my $status_field;
+
+	#put all the windows in the bar with their current unread count
+	foreach my $window (sort {$windows{$a} <=> $windows{$b}} keys %windows) {
+		#see if we are printing the current window
+		if($terminal->current_window == $windows{$window}) {
+			$status_field .= "[\0(current)$window\0(st_frames)";
+		} else {
+			$status_field .= "[$window";
+		}
+
+		if($unread_count{$windows{$window}}) {
+			$status_field .= " (" . $unread_count{$windows{$window}} . ")";
+		}
+
+		$status_field .= "] ";
+	}
+
+	return $status_field;
+}

Added: freeswitch/trunk/scripts/POE-Filter-FSSocket/examples/poetest.pl
==============================================================================
--- (empty file)
+++ freeswitch/trunk/scripts/POE-Filter-FSSocket/examples/poetest.pl	Fri Oct 20 02:20:39 2006
@@ -0,0 +1,42 @@
+#!/usr/bin/perl
+  
+use warnings;
+use strict;
+  
+use POE qw(Component::Client::TCP Filter::FSSocket);
+use Data::Dumper;
+
+my $auth_sent = 0;
+my $password = "ClueCon";
+  
+POE::Component::Client::TCP->new(
+          'RemoteAddress' => '127.0.0.1',
+          'RemotePort'    => '8021',
+          'ServerInput'   => \&handle_server_input,
+          'Filter'        => POE::Filter::FSSocket->new(),
+);
+  
+POE::Kernel->run();
+exit;
+  
+  
+sub handle_server_input {
+          my ($heap,$input) = @_[HEAP,ARG0];
+  
+          print Dumper $input;
+  
+  
+	if($input->{'Content-Type'} eq "auth/request") {
+	$auth_sent = 1;
+	$heap->{'server'}->put("auth $password");
+	} elsif ($input->{'Content-Type'} eq "command/reply") {
+		if($auth_sent == 1) {
+			$auth_sent = -1;
+  
+			#do post auth stuff
+			$heap->{'server'}->put("events plain all");
+			$heap->{'server'}->put("log");
+			$heap->{'server'}->put("api show channels");
+		}
+	}
+}

Added: freeswitch/trunk/scripts/POE-Filter-FSSocket/lib/POE/Filter/FSSocket.pm
==============================================================================
--- (empty file)
+++ freeswitch/trunk/scripts/POE-Filter-FSSocket/lib/POE/Filter/FSSocket.pm	Fri Oct 20 02:20:39 2006
@@ -0,0 +1,343 @@
+=head1 NAME
+
+POE::Filter::FSSocket - a POE filter that parses FreeSWITCH events into hashes
+
+=head1 SYNOPSIS
+
+  #!/usr/bin/perl
+  
+  use warnings;
+  use strict;
+  
+  use POE qw(Component::Client::TCP Filter::FSSocket);
+  use Data::Dumper;
+
+  my $auth_sent = 0;
+  my $password = "ClueCon";
+  
+  POE::Component::Client::TCP->new(
+          'RemoteAddress' => '127.0.0.1',
+          'RemotePort'    => '8021',
+          'ServerInput'   => \&handle_server_input,
+          'Filter'        => 'POE::Filter::FSSocket',
+  );
+  
+  POE::Kernel->run();
+  exit;
+  
+  sub handle_server_input {
+          my ($heap,$input) = @_[HEAP,ARG0];
+  
+          print Dumper $input;
+  
+  
+          if($input->{'Content-Type'} eq "auth/request") {
+                  $auth_sent = 1;
+                  $heap->{'server'}->put("auth $password");
+          } elsif ($input->{'Content-Type'} eq "command/reply") {
+                  if($auth_sent == 1) {
+                          $auth_sent = -1;
+  
+                          #do post auth stuff
+                          $heap->{'server'}->put("events plain all");
+                  }
+          }
+  }
+
+=head1 EXAMPLES
+
+See examples in the examples directory of the distribution.
+
+=head1 DESCRIPTION
+
+POE::Filter::FSSocket parses output from FreeSWITCH into hashes.  FreeSWITCH 
+events have a very wide range of keys, the only consistant one being
+Content-Type.  The keys are dependant on the type of events.  You must use the 
+plain event type as that is what the filter knows how to parse.  You can ask for
+as many event types as you like or all for everything.  You specify a list of
+event types by putting spaces between them ex: "events plain api log talk"
+
+Currently known event types (Event-Name):
+
+  CUSTOM
+  CHANNEL_CREATE
+  CHANNEL_DESTROY
+  CHANNEL_STATE
+  CHANNEL_ANSWER
+  CHANNEL_HANGUP
+  CHANNEL_EXECUTE
+  CHANNEL_BRIDGE
+  CHANNEL_UNBRIDGE
+  CHANNEL_PROGRESS
+  CHANNEL_OUTGOING
+  CHANNEL_PARK
+  CHANNEL_UNPARK
+  API
+  LOG
+  INBOUND_CHAN
+  OUTBOUND_CHAN
+  STARTUP
+  SHUTDOWN
+  PUBLISH
+  UNPUBLISH
+  TALK
+  NOTALK
+  SESSION_CRASH
+  MODULE_LOAD
+  DTMF
+  MESSAGE
+  CODEC
+  BACKGROUND_JOB
+  ALL
+
+Currently handled FreeSWITCH messages (Content-Type):
+
+  auth/request
+  command/response
+  text/event-plain
+  api/response (data in __DATA__ variable)
+  log/data (data in __DATA__ variable)
+
+=cut
+
+
+package POE::Filter::FSSocket;
+
+use warnings;
+use strict;
+
+use Carp qw(carp croak);
+use vars qw($VERSION);
+use base qw(POE::Filter);
+
+$VERSION = '0.05';
+
+use POE::Filter::Line;
+use Data::Dumper;
+
+#self array
+sub LINE_FILTER()      {1}
+sub PARSER_STATE()     {2}
+sub PARSER_STATENEXT() {3}
+sub PARSED_RECORD()    {4}
+sub CURRENT_LENGTH()   {5}
+sub STRICT_PARSE()     {6}
+sub DEBUG_LEVEL()      {7}
+
+#states of the parser
+sub STATE_WAITING()     {1} #looking for new input
+sub STATE_CLEANUP()     {2} #wipe out record separators
+sub STATE_GETDATA()     {3} #have header, get data
+sub STATE_FLUSH()       {4} #puts us back in wait state and tells us to kill the parsed_record
+sub STATE_TEXTRESPONSE() {5} #used for api output
+
+sub new {
+	my $class = shift;
+	my %args = @_;
+
+	my $strict = 0;
+	my $debug  = 0;
+
+	if(defined($args{'debug'})) {
+		$debug = $args{'debug'};
+	}
+
+	if(defined($args{'strict'}) && $args{'strict'} == 1) {
+		$strict = $args{'strict'};
+	}
+
+	#our filter is line based, don't reinvent the wheel
+	my $line_filter = POE::Filter::Line->new();
+
+	my $self = bless [
+		"",            #not used by me but the baseclass clone wants it here
+		$line_filter,  #LINE_FILTER
+		STATE_WAITING, #PARSER_STATE
+		undef,         #PARSER_STATE
+		{},            #PARSED_RECORD
+		0,             #length tracking (for Content-Length when needed)
+		$strict,       #whether we should bail on a bad parse or try and save the session
+		$debug,        #debug level
+	], $class;
+
+	return $self;
+}
+
+
+sub get_one_start {
+	my ($self, $stream) = @_;
+	$self->[LINE_FILTER]->get_one_start($stream);
+}
+
+sub get_one {
+	my $self = shift;
+
+	while(1) {
+		#grab a line from the filter
+		my $line = $self->[LINE_FILTER]->get_one();
+
+		#quit if we can't get any lines
+		return [] unless @$line;
+
+		#get the actual line
+		$line = $line->[0];
+
+		if(($self->[PARSER_STATE] == STATE_WAITING) || ($self->[PARSER_STATE] == STATE_FLUSH)) {
+			#see if we need to wipe out the parsed_record info
+			if($self->[PARSER_STATE] == STATE_FLUSH) {
+				delete $self->[PARSED_RECORD];
+				$self->[CURRENT_LENGTH] = 0;
+
+				$self->[PARSER_STATE] = STATE_WAITING;
+			}
+
+			if($line =~ /Content-Length:\ (\d+)$/) {
+				#store the length
+				$self->[PARSED_RECORD]{'Content-Length'} = $1;
+
+				#see if we had a place to go from here (we should)
+				if(defined($self->[PARSER_STATENEXT])) {
+					$self->[PARSER_STATE] = $self->[PARSER_STATENEXT];
+					$self->[PARSER_STATENEXT] = undef;
+				}
+			} elsif($line =~ /Content-Type:\ (.*)$/) {
+				#store the type of request
+				$self->[PARSED_RECORD]{'Content-Type'} = $1;
+
+				if($1 eq "auth/request") {
+					$self->[PARSER_STATE]     = STATE_CLEANUP;
+					$self->[PARSER_STATENEXT] = STATE_FLUSH;
+					return [ $self->[PARSED_RECORD] ];
+				} elsif ($1 eq "command/reply") { #do something with this later
+					$self->[PARSER_STATE] = STATE_GETDATA;
+				} elsif ($1 eq "text/event-plain") {
+					$self->[PARSER_STATE]     = STATE_CLEANUP;
+					$self->[PARSER_STATENEXT] = STATE_GETDATA;
+				} elsif ($1 eq "api/response") {
+					$self->[PARSER_STATENEXT] = STATE_TEXTRESPONSE;
+				} elsif ($1 eq "log/data") {
+					$self->[PARSER_STATENEXT] = STATE_TEXTRESPONSE;
+				} else { #unexpected input
+					croak ref($self) . " unknown input [" . $self->[PARSER_STATE] . "] (" . $line . ")";
+				}
+			} else {
+				#already in wait state, if we are not in strict, keep going
+				if($self->[STRICT_PARSE]) {
+					croak ref($self) . " unknown input [STATE_WAITING] (" . $line . ")";
+				}
+			}
+		} elsif ($self->[PARSER_STATE] == STATE_CLEANUP) {
+			if($line eq "") {
+				if(defined($self->[PARSER_STATENEXT])) {
+					$self->[PARSER_STATE] = $self->[PARSER_STATENEXT];
+					$self->[PARSER_STATENEXT] = undef;
+				} else {
+					$self->[PARSER_STATE] = STATE_WAITING;
+				}
+			} else {
+				#see if we should bail
+				if($self->[STRICT_PARSE]) {
+					croak ref($self) . " unknown input [STATE_CLEANUP] (" . $line . ")";
+				} else {
+					#we are not supposed to bail so try and save our session...
+					#since we are think we should be cleaning up, flush it all away
+					$self->[PARSER_STATE] = STATE_FLUSH;
+
+					#parser fail should be considered critical, if any debug at all, print dump
+					if($self->[DEBUG_LEVEL]) {
+						print STDERR "Parse failed on ($line) in STATE_CLEANUP:\n";
+						print STDERR Dumper $self->[PARSED_RECORD];
+					}
+				}
+			}
+		} elsif ($self->[PARSER_STATE] == STATE_GETDATA) {
+			if($line =~ /^([^:]+):\ (.*)$/) {
+				$self->[PARSED_RECORD]{$1} = $2;
+			} elsif ($line eq "") { #end of event 
+				$self->[PARSER_STATE] = STATE_FLUSH;
+
+				return [ $self->[PARSED_RECORD] ];
+			} else {
+				if($self->[STRICT_PARSE]) {
+					croak ref($self) . " unknown input [STATE_GETDATA] (" . $line . ")";
+				} else {
+					#flush and run
+					$self->[PARSER_STATE] = STATE_FLUSH;
+
+					#parser fail should be considered critical, if any debug at all, print dump
+					if($self->[DEBUG_LEVEL]) {
+						print STDERR "Parse failed on ($line) in STATE_GETDATA:\n";
+						print STDERR Dumper $self->[PARSED_RECORD];
+					}
+				}
+			}
+		} elsif ($self->[PARSER_STATE] == STATE_TEXTRESPONSE) {
+			if($self->[CURRENT_LENGTH] == -1) {
+				$self->[CURRENT_LENGTH] = 0;
+				next;
+			}
+
+			$self->[CURRENT_LENGTH] += (length($line) + 1);
+
+			if(($self->[CURRENT_LENGTH] - 1) == $self->[PARSED_RECORD]{'Content-Length'}) {
+				$self->[PARSER_STATE] = STATE_FLUSH;
+				$self->[PARSED_RECORD]{'__DATA__'} .= $line;
+
+				return [$self->[PARSED_RECORD]];
+			} else {
+				$self->[PARSED_RECORD]{'__DATA__'} .= $line . "\n";
+			}
+		}
+	}
+}
+
+sub put {
+	my ($self, $lines)  = @_;
+
+	my @row;
+	foreach my $line (@$lines) {
+		push @row, $line . "\n\n";
+	}
+
+	return \@row;
+	
+}
+
+sub get_pending {
+	my $self = shift;
+	return $self->[LINE_FILTER]->get_pending();
+}
+
+sub get {
+	my ($self, $stream) = @_;
+	my @return;
+
+	$self->get_one_start($stream);
+	while(1) {
+		my $next = $self->get_one();
+		last unless @$next;
+		push @return, @$next;
+	}
+
+	return \@return;
+}
+
+1;
+
+=head1 SEE ALSO
+
+FreeSWITCH - http://www.freeswitch.org/
+
+=head1 AUTHORS
+
+POE::Filter::FSSocket is written by Paul Tinsley.  You can reach him by e-mail
+at pdt at jackhammer.org.
+
+=head1 COPYRIGHT
+
+Copyright 2006, Paul Tinsley. All rights are reserved.
+
+POE::Filter::FSSocket is free software; it is currently licensed under the MPL
+license version 1.1.
+
+=cut

Added: freeswitch/trunk/scripts/POE-Filter-FSSocket/t/01_basic.t
==============================================================================
--- (empty file)
+++ freeswitch/trunk/scripts/POE-Filter-FSSocket/t/01_basic.t	Fri Oct 20 02:20:39 2006
@@ -0,0 +1,10 @@
+#!/usr/bin/env perl
+
+use warnings;
+use strict;
+
+use Test::More tests => 1;
+
+use_ok("POE::Filter::FSSocket");
+
+exit;

Added: freeswitch/trunk/scripts/socket/fsconsole.pl
==============================================================================
--- (empty file)
+++ freeswitch/trunk/scripts/socket/fsconsole.pl	Fri Oct 20 02:20:39 2006
@@ -0,0 +1,361 @@
+#!/usr/bin/perl
+use strict;
+use warnings;
+
+sub POE::Kernel::ASSERT_DEFAULT () { 1 };
+sub Term::Visual::DEBUG () { 1 }
+sub Term::Visual::DEBUG_FILE () { 'test.log' }
+use IO::Socket;
+use POE qw/Filter::FSSocket Component::Client::TCP/;
+use Data::Dumper;
+use Term::Visual;
+
+
+local *D;
+if (Term::Visual::DEBUG) {
+    *D = *Term::Visual::ERRS;
+}
+
+#local *ERROR = *STDERR;
+
+
+$SIG{__DIE__} = sub {
+    if (Term::Visual::DEBUG) {
+        print Term::Visual::ERRS "Died: @_\n";
+    }
+};
+
+###############################################################################
+## BEGIN Globals ##############################################################
+###############################################################################
+our $server_address = "127.0.0.1";
+our $server_port    = "8021";
+our $server_secret  = "ClueCon";
+
+#this is where you can customize the color scheme
+our %Pallet = (
+	'warn_bullet' => 'bold yellow',
+	'err_bullet'  => 'bold red',
+	'out_bullet'  => 'bold green',
+	'access'      => 'bright red on blue',
+	'current'     => 'bright yellow on blue',
+);
+
+our $terminal;
+my %sockets;
+my %windows;
+my %unread_count;
+my %commands = (
+	'window' => 1,
+	'w'      => 1,
+	'win'    => 1,
+);
+###############################################################################
+##   END Globals ##############################################################
+###############################################################################
+
+#setup our session
+POE::Session->create(
+	'inline_states' => {
+		'_start'       => \&handle_start,        #session start
+		'_stop'        => \&handle_stop,         #session stop
+		'curses_input' => \&handle_curses_input, #input from the keyboard
+		'update_time'  => \&handle_update_time,  #update the status line clock
+		'quit'         => \&handle_quit,         #handler to do any cleanup
+		'server_input' => \&handle_server_input,
+		'_default'      => \&handle_default,
+	},
+	'heap' => {
+		'terminal'     => undef,
+		'freeswitch'   => undef,
+	},
+);
+
+#start the kernel a chugging along
+$poe_kernel->run;
+
+###############################################################################
+## BEGIN Handlers #############################################################
+###############################################################################
+#handles any startup functions for our session
+sub handle_default {
+}
+
+sub handle_start {
+	my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
+
+	#setup our terminal
+	 $heap->{'terminal'} = Term::Visual->new(
+		'Alias'        => 'terminal', #poe alias for this
+		'History_Size' => 300,        #number of things to keep in history
+		'Common_Input' => 1,          #all windows share input and history
+		'Tab_Complete' => \&tab_complete,
+	);
+
+	$terminal = $heap->{'terminal'};
+
+	#setup the color palette
+	$terminal->set_palette(%Pallet);
+
+	#create a base window
+	my $window_id = $terminal->create_window(
+		'Window_Name' => 'console',
+		'Buffer_Size' => 3000,
+		'Title'       => 'FreeSWITCH Console',
+		'Status'      => {
+			'0' => {
+				'format' => '%s',
+				'fields' => ['time'],
+			},
+			'1' => {
+				'format' => '%s',
+				'fields' => ['window_status'],
+			},
+		},
+	);
+
+	$windows{'console'} = $window_id;
+
+	$window_id = $terminal->create_window(
+		'Window_Name' => 'log',
+		'Buffer_Size' => 3000,
+		'Title'       => 'FreeSWITCH Logs',
+		'Status'      => {
+			'0' => {
+				'format' => '%s',
+				'fields' => ['time'],
+			},
+			'1' => {
+				'format' => '%s',
+				'fields' => ['window_status'],
+			},
+		},
+	);
+
+	$windows{'log'} = $window_id;
+
+	$window_id = $terminal->create_window(
+		'Window_Name' => 'event',
+		'Buffer_Size' => 3000,
+		'Title'       => 'FreeSWITCH Event',
+		'Status'      => {
+			'0' => {
+				'format' => '%s',
+				'fields' => ['time'],
+			},
+			'1' => {
+				'format' => '%s',
+				'fields' => ['window_status'],
+			},
+		},
+	);
+
+	$windows{'event'} = $window_id;
+
+	#tell the terminal what to call when there is input from the keyboard
+	$kernel->post('terminal' => 'send_me_input' => 'curses_input');
+
+	$terminal->change_window(0);
+	$kernel->delay_set('update_time' => 1);
+	$terminal->set_status_field(0, 'time' => scalar(localtime));
+	new_message('destination_window' => 0, 'message' =>  "
+Welcome to the FreeSWITCH POE Curses Console!
+  The console is split into three windows:
+    - 'console' for api response messages
+    - 'log'     for freeswitch log output (simply send the log level you want 
+                  to start seeing events eg: 'log all')
+    - 'event'   for freeswitch event output (must subscribe in plain format
+                  eg: 'event plain all')
+
+To switch between windows type 'w <windowname' so 'w log' for example.
+
+Coming soon:
+  - Tab completion
+  - command history
+  - window status in the bar (messages added since last view, etc...)
+
+Send any bug reports or comments to jackhammer\@gmail.com
+
+Thanks,
+Paul\n");
+
+	$terminal->set_status_field($terminal->current_window, 'window_status' => format_window_status());
+
+	#connect to freeswitch
+	$heap->{'freeswitch'} = POE::Component::Client::TCP->new(
+		'RemoteAddress' => $server_address,
+		'RemotePort'    => $server_port,
+		'ServerInput'   => \&handle_server_input,
+		'Connected'     => \&handle_fs_connected,
+		'ServerError'   => \&handle_server_error,
+		'Disconnected'  => \&handle_server_disconnect,
+		'Domain'        => AF_INET,
+		'Filter'       => POE::Filter::FSSocket->new(),
+	);
+
+}
+
+#called when users enter commands in a window
+sub handle_curses_input {
+	my ($kernel, $heap, $input, $context) = @_[KERNEL, HEAP, ARG0, ARG1];
+
+	#get the id of the window that is responsible for the input
+	my $window = $heap->{'terminal'}->current_window;
+
+	open(ERROR, ">>error.log");
+
+	if($input eq "quit") {
+		$kernel->yield('quit');
+	} elsif ($input =~ /^w\ (.*)$/) {
+		#get the id of the requested window
+		eval {
+			my $window_id = $windows{$1};
+
+			#see if it's real
+			if(defined($window_id)) {
+				$unread_count{$window_id} = 0;
+				$terminal->change_window($window_id);
+				$terminal->set_status_field($window_id, 'window_status' => &format_window_status());
+			}
+		};
+		if($@) {
+			print ERROR "put error: $@\n";
+		}
+	} else {
+		#see if we got connected at some point
+		if(defined($sockets{'localhost'})) {
+			#send the command
+			$sockets{'localhost'}->put($input);
+		}
+	}
+}
+
+sub handle_fs_connected {
+	my ($kernel, $heap) = @_[KERNEL, HEAP];
+
+	eval {
+		$sockets{'localhost'} = $heap->{'server'};
+	}
+}
+
+#this is responsible for doing any cleanup and returning the terminal to the previous
+#state before we mucked with it
+sub handle_quit {
+	my ($kernel, $heap) = @_[KERNEL, HEAP];
+
+	#tell curses to clean up it's crap
+	$kernel->post('terminal' => 'shutdown');
+
+	#there is probably a more elegant way, but this works for now
+	exit;
+}
+
+#data from freeswitch
+sub handle_server_input {
+        my ($kernel,$heap,$input) = @_[KERNEL,HEAP,ARG0];
+
+	eval {
+		#terminal HATES null
+		if(defined($input->{'__DATA__'})) {
+			$input->{'__DATA__'} =~ s/[\x00]//g;
+		}
+
+		#handle the login
+		if($input->{'Content-Type'} eq "auth/request") {
+			$heap->{'server'}->put("auth $server_secret");
+		} elsif ($input->{'Content-Type'} eq "api/response") {
+			new_message('destination_window' => 0, 'message' => 'Response: ');
+			new_message('destination_window' => 0, 'message' => $input->{'__DATA__'});
+		} elsif ($input->{'Content-Type'} eq "log/data") {
+			new_message('destination_window' => 1, 'message' => $input->{'__DATA__'});
+		} elsif ($input->{'Content-Type'} eq "text/event-plain") {
+			new_message('destination_window' => 2, 'message' => Dumper $input);
+		}
+	};
+
+	if($@) {
+		open(ERROR, ">>error.log");
+		print ERROR "died: $@\n";
+		print ERROR Dumper $heap;
+		close(ERROR);
+	}
+}
+
+sub handle_server_error {
+}
+
+sub handle_server_disconnect {
+}
+
+sub tab_complete {
+	my $left = shift;
+
+	my @return;
+
+	if(defined($commands{$left})) {
+		return [$left . " "];
+	#} elsif () {
+	}
+		
+}
+
+sub handle_update_time {
+	my ($kernel, $heap) = @_[KERNEL, HEAP];
+	$terminal->set_status_field($terminal->current_window, 'time' => scalar(localtime));
+	$kernel->delay_set('update_time' => 1);
+}
+###############################################################################
+##   END Handlers #############################################################
+###############################################################################
+
+sub new_message {
+	my %args = @_;
+
+	my $message            = $args{'message'};
+	my $destination_window = $args{'destination_window'};
+
+	my $status_field;
+
+	#see if we are on the window being updated
+	if($terminal->current_window != $destination_window) {
+		#increment the unread count for the window
+		#FIXME, should we count messages or lines?
+		$unread_count{$destination_window}++;
+
+
+		#update the status bar
+		eval {
+			$terminal->set_status_field($terminal->current_window, 'window_status' => &format_window_status());
+		};
+
+		if($@) {
+			print $@;
+		}
+	}
+
+
+	#deliver the message
+	$terminal->print($destination_window, $message);
+}
+
+sub format_window_status {
+	my $status_field;
+
+	#put all the windows in the bar with their current unread count
+	foreach my $window (sort {$windows{$a} <=> $windows{$b}} keys %windows) {
+		#see if we are printing the current window
+		if($terminal->current_window == $windows{$window}) {
+			$status_field .= "[\0(current)$window\0(st_frames)";
+		} else {
+			$status_field .= "[$window";
+		}
+
+		if($unread_count{$windows{$window}}) {
+			$status_field .= " (" . $unread_count{$windows{$window}} . ")";
+		}
+
+		$status_field .= "] ";
+	}
+
+	return $status_field;
+}



More information about the Freeswitch-svn mailing list