AnyEvent-Ident

 view release on metacpan or  search on metacpan

lib/AnyEvent/Ident/Client.pm  view on Meta::CPAN

package AnyEvent::Ident::Client;

use strict;
use warnings;
use AnyEvent::Socket qw( tcp_connect );
use AnyEvent::Handle;
use Carp qw( carp );

# ABSTRACT: Simple asynchronous ident client
our $VERSION = '0.08'; # VERSION


sub new
{
  my $class = shift;
  my $args     = ref $_[0] eq 'HASH' ? (\%{$_[0]}) : ({@_});
  my $port = $args->{port};
  $port = 113 unless defined $port;
  bless { 
    hostname       => $args->{hostname}       || '127.0.0.1',  
    port           => $port,
    on_error       => $args->{on_error}       || sub { carp $_[0] },
    response_class => $args->{response_class} || 'AnyEvent::Ident::Response',
  }, $class;
}


sub ident
{
  my($self, $server_port, $client_port, $cb) = @_;
  
  unless(eval { $self->{response_class}->can('new') })
  {
    eval 'use ' . $self->{response_class};
    die $@ if $@;
  }
  
  my $key = join ':', $server_port, $client_port;
  push @{ $self->{$key} }, $cb;
  return if @{ $self->{$key} } > 1;
  
  # if handle is defined then the connection is open and we can push 
  # the request right away.
  if(defined $self->{handle})
  {
    $self->{handle}->push_write("$server_port,$client_port\015\012");
    return;
  }
  
  # if handle is not defined, but wait is, then we are waiting for
  # the connection, and we queue up the request
  if(defined $self->{wait})
  {
    push @{ $self->{wait} }, "$server_port,$client_port\015\012";
    return;
  }
  
  $self->{wait} = [];
  
  tcp_connect $self->{hostname}, $self->{port}, sub {
    my($fh) = @_;
    return $self->_cleanup->{on_error}->("unable to connect: $!") unless $fh;
    
    $self->{handle} = AnyEvent::Handle->new(
      fh       => $fh,
      on_error => sub {
        my ($hdl, $fatal, $msg) = @_;
        $self->{on_error}->($msg);
        $self->_cleanup;

lib/AnyEvent/Ident/Client.pm  view on Meta::CPAN

    delete $self->{wait};
    
    $self->{handle}->on_read(sub {
      $self->{handle}->push_read( line => sub {
        my($handle, $line) = @_;
        $line =~ s/\015?\012//g;
        my $res = $self->{response_class}->new($line);
        my $key = $res->_key;
        if(defined $self->{$key})
        {
          $_->($res) for @{ $self->{$key} };
          delete $self->{$key};
        }
      });
    });
  };
  
  return $self;
}


sub _cleanup
{
  my $self = shift;
  foreach my $key (grep /^(\d+):(\d+)$/, keys %$self)
  {
    $_->($self->{response_class}->new("$1,$2:ERROR:UNKNOWN-ERROR"))
      for @{ $self->{$key} };
    delete $self->{$key};
  }
  $self;
}

sub close
{
  my $self = shift;
  if(defined $self->{handle})
  {
    $self->_cleanup;
    $self->{handle}->destroy;
    delete $self->{handle};
    delete $self->{wait};
  }
}

sub DESTROY
{
  shift->close;
}

1;

__END__

=pod

=encoding UTF-8

=head1 NAME

AnyEvent::Ident::Client - Simple asynchronous ident client

=head1 VERSION

version 0.08

=head1 SYNOPSIS

 use AnyEvent::Ident::Client;
 
 my $client = AnyEvent::Ident::Client->new( hostname => 127.0.0.1' );
 $client->ident($server_port, $client_port, sub {
   my($res) = @_; # isa AnyEvent::Client::Response 
   if($res->is_success)
   {
     print "user: ", $res->username, "\n";
     print "os:   ", $res->os, "\n";
   }
   else
   {
     warn "Ident error: " $res->error_type;
   }
 });

=head1 CONSTRUCTOR

 my $client = AnyEvent::Ident::Client->new(%args);

The constructor takes the following optional arguments:

=head2 hostname

default 127.0.0.1

The hostname to connect to.

=head2 port

default 113

The port to connect to.

=head2 on_error

default carp error

A callback subref to be called on error (either connection or transmission error).
Passes the error string as the first argument to the callback.

=head2 response_close

default L<AnyEvent::Ident::Response>

Bless the response object into the given class.  This class SHOULD inherit from
L<AnyEvent::Ident::Response>, or at least mimic its interface.  This allows you
to define your own methods for the response class.

=head1 METHODS

=head2 ident



( run in 0.620 second using v1.01-cache-2.11-cpan-39bf76dae61 )