#!/usr/bin/perl use strict; use warnings; $|++; use Socket; use POSIX qw( errno_h ); use PXR::NS qw/ :IQ :JABBER /; use POE qw( Wheel::ReadWrite Wheel::SocketFactory Filter::Stackable Filter::Stream Filter::XML ); my %Config = ( IP => 'localhost', PORT => 9999, HOSTNAME => 'localhost', ); my %routes = (); # => my %pending = (); # => [ , ... ] # Old Text Based Core # # input in this form -> ' ' # # my ($from, $to, $msg) = split /\s+/, $input, 3; # # unless ( exists $routes{$from} ) { # $routes{$from} = $heap->{wheel_client}; # } # # if ( exists $pending{$from} ) { # $routes{$from}->put( $_ ) for @{ $pending{$from} }; # } # # unless ( exists $routes{$to} ) { # push @{ $pending{$to} }, $input; # } else { # $routes{$to}->put( $input ); # } sub router_create { my ( $handle, $peer_host, $peer_port, $remote_addr, $remote_port ) = @_; POE::Session->new( _start => \&router_start, _stop => \&router_stop, client_input => \&router_client_input, client_error => \&router_client_error, output_handler => \&router_output_handler, auth_setup => \&router_auth_setup, auth_input => \&router_auth_input, user_input => \&router_user_input, [ $handle, $peer_host, $peer_port, $remote_addr, $remote_port ] ); } sub router_start { my ( $heap, $session, $socket, $peer_host, $peer_port, $remote_addr, $remote_port ) = @_[ HEAP, SESSION, ARG0, ARG1, ARG2, ARG3, ARG4 ]; $heap->{log} = $session->ID; $peer_host = inet_ntoa($peer_host); $heap->{peer_host} = $peer_host; $heap->{peer_port} = $peer_port; $heap->{remote_addr} = $remote_addr; $heap->{remote_port} = $remote_port; print "[$heap->{log}] Accepted session from $peer_host:$peer_port$/"; $heap->{socket} = $socket; $heap->{state} = 'connecting'; $heap->{wheel_client} = POE::Wheel::ReadWrite->new( Handle => $socket, Driver => POE::Driver::SysRW->new, InputFilter => POE::Filter::Stream->new, OutputFilter => POE::Filter::Stream->new, InputEvent => 'client_input', ErrorEvent => 'client_error', ); } # match the client's # send our sub router_client_input { my ( $kernel, $session, $heap, $input ) = @_[ KERNEL, SESSION, HEAP, ARG0 ]; print "[$heap->{log}] said: $input$/"; # XXX come up with better test. if ( $input =~ /call( $session, output_handler => <<_X_ ); _X_ $kernel->yield('auth_setup'); } } # try to change the Filters on our Wheel sub router_auth_setup { my ( $kernel, $session, $heap ) = @_[ KERNEL, SESSION, HEAP ]; print "Changing Filter$/"; my $if = $heap->{wheel_client}->get_input_filter; print "pending: ", $if->get_pending, $/; $heap->{wheel_client}->set_output_filter( POE::Filter::XML->new ); # $heap->{wheel_client}->set_input_filter( # POE::Filter::Stackable->new( Filters => [ # POE::Filter::Stream->new( # $heap->{wheel_client}->get_input_filter->get_pending # ), # POE::Filter::XML->new( # ), # ]) # ); print "Changed Filter$/"; $heap->{wheel_client}->event( InputEvent => 'auth_input' ); } sub DD { require Data::Dumper; print Data::Dumper->Dump(@_); } # we should be passing PXR::Node as input but Filter swap # has me miffed. sub router_auth_input { my ( $kernel, $session, $heap, $input ) = @_[ KERNEL, SESSION, HEAP, ARG0 ]; my $node; if (0) { $node = $input; } else { # one butt ugly hack my $flt = POE::Filter::XML->new($input); print "[$heap->{log}] noded: ", $input, $/; my $nodes = $flt->get_one( [] ); $node = $nodes->[0]; } #DD( [$node], ['node'] ); print "[$heap->{log}] noded: ", $node->to_str, $/; # create our reply node my $reply = PXR::Node->new('iq'); $reply->attr( type => +IQ_RESULT ); $reply->attr( id => $node->attr('id') ); if ( $node->name eq 'iq' ) { my $q = $reply->insert_tag( query => +NS_JABBER_AUTH ); if ( $node->attr('type') eq +IQ_GET ) { # ask for username/password $q->insert_tag('username'); $q->insert_tag('password'); } elsif ( $node->attr('type') eq +IQ_SET ) { # XXX do real auth check on username/password here. # we are authed! $heap->{wheel_client}->event( InputEvent => 'user_input' ); } } else { # XXX check actual format for errors. $reply->attr( type => 'error' ); } print "[$heap->{log}] reply: ", $reply->to_str, $/; $kernel->post( $session, output_handler => $reply ); } # this should be our final handler. # we'll dispatch each node to it's final destination sub router_user_input { my ( $kernel, $session, $heap, $input ) = @_[ KERNEL, SESSION, HEAP, ARG0 ]; my $node; if (0) { $node = $input; } else { $input =~ s/^\s+//; $node = ( POE::Filter::XML->new($input)->get_one( [] ) )->[0] if length $inpu t; } if ( defined $node ) { print "[$heap->{log}] user: ", $node->to_str, $/; # XXX route node to destination. } else { # Net::Jabber::Client keep-alive sends spaces. # These make empty nodes, we'll send a message to # client for a test. my $reply = PXR::Node->new('message'); $reply->attr( type => 'normal' ); $reply->attr( to => 'user2@localhost' ); $reply->attr( from => 'user1@localhost' ); $reply->insert_tag( 'body' )->data( q{woot!} ); $reply->insert_tag( 'subject' )->data( q{this is a test} ); $kernel->post( $session, output_handler => $reply ); } } sub router_output_handler { my ( $heap, $data ) = @_[ HEAP, ARG0 ]; $heap->{wheel_client}->put($data); } sub router_stop { my $heap = $_[HEAP]; print "[$heap->{log}] Closing session\n"; } sub router_client_error { my ( $kernel, $heap, $operation, $errnum, $errstr ) = @_[ KERNEL, HEAP, ARG0, ARG1, ARG2 ]; if ($errnum) { print "[$heap->{log}] Client connection encountered $operation error $errnum: $errstr$/ "; } else { print "[$heap->{log}] Client closed connection$/"; } delete $heap->{wheel_client}; } sub server_create { POE::Session->new( _start => \&server_start, _stop => \&server_stop, accept_success => \&server_accept_success, accept_failure => \&server_accept_failure, ); } sub server_start { my ($heap) = @_[HEAP]; print "Starting server$/"; $heap->{server_wheel} = POE::Wheel::SocketFactory->new( BindAddress => $Config{IP}, BindPort => $Config{PORT}, Reuse => 'yes', SuccessEvent => 'accept_success', FailureEvent => 'accept_failure', ); } sub server_stop { my ($heap) = @_[HEAP]; print "Stopping server$/"; } sub server_accept_success { my ( $heap, $socket, $peer_addr, $peer_port ) = @_[ HEAP, ARG0 .. ARG2 ]; router_create( $socket, $peer_addr, $peer_port, $heap->{remote_addr}, $heap->{remote_port} ); } sub server_accept_failure { my ( $heap, $operation, $errnum, $errstr ) = @_[ HEAP, ARG0, ARG1, ARG2 ]; print "Failure: $heap->{remote_addr} $heap->{remote_port} encountered $operation error $errnum: $errstr$/"; delete $heap->{server_wheel} if $errnum == ENFILE or $errnum == EMFILE; } server_create(); POE::Kernel->run(); __END__ $ perl testserver.pl Starting server [3] Accepted session from 127.0.0.1:41208 [3] said: Changing Filter Changed Filter [3] noded: username [3] noded: username [3] reply: [3] noded: passwordresourceusername [3] noded: passwordresourceusername [3] reply: [3] user: [3] Closing session Stopping server $ perl client.pl localhost 9999 username password resource Logged in to localhost:9999... === Message (normal) From: user1 () Subject: this is a test Body: woot! === woot!this is a test ===