package Finger;

use strict;
use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);

require Exporter;
@ISA = qw(Exporter);

@EXPORT = qw(
);
@EXPORT_OK = qw(
);
$VERSION = '0.93';

use Carp;
use FileHandle;
use Socket;

use vars qw( $Long $Debug $ConnectTimeout );

sub Finger::query {
  my ($faddr, $fport) = @_;
  my ($port, $proto, $query, $host, $iaddr);

  $proto = getprotobyname('tcp');
  $port = $fport || 'finger';
  $port = getservbyname($port, 'tcp') if ($port =~ /\D/);
  $port || do { carp "Invalid port: '$fport'"; return undef  };

  $faddr =~ /@([^@]+)$/
    || do {  carp "Invalid finger destination: '$faddr'"; return undef  };
  ($query, $host) = ($`, $1);
  print STDERR "Connecting to $host, port $port (proto=$proto)...\n"
    if $Debug;

  $iaddr = inet_aton($host)
    || do { carp "Invalid host: '$host'"; return undef  };
  print STDERR "Connecting to @{[inet_ntoa($iaddr)]}.$port...\n"
    if $Debug;

  my ($fh, $sin) = new FileHandle;
  socket($fh, PF_INET, SOCK_STREAM, $proto)
    || do { carp "socket(PF_INET, SOCK_STREAM, $proto): $!"; return undef };
  $sin = sockaddr_in($port, $iaddr);

  # perl5.004 f...ing started f...ing restarting f...ing system f...ing calls
  # without f...ing telling f...ing anyone.  argh.
  eval {
    # NB: a "croak" here just exist the eval, where the text gets
    # passed to "warn".   In short, it's only a "carp" plus non-local return.
    local $SIG{'ALRM'} = sub { croak "timeout exceeded" };

    alarm $ConnectTimeout if defined $ConnectTimeout;
    connect($fh, $sin)
      or do {
	my $err = $!;
	alarm 0 if defined $ConnectTimeout;
	croak $err;
      };

    alarm 0 if defined $ConnectTimeout;
    1;   # indicate success (eval returns undef if we die/croak)
  }
  or do {
    alarm 0 if defined $ConnectTimeout;
    warn "connect($host:$port): $@";
    return undef;
  };
  alarm 0 if defined $ConnectTimeout;

  print STDERR "Connection established, sending data\n"
    if $Debug;

  $fh->autoflush(1);
  $fh->print(($Long ? '/w ' : '') . $query . "\r\n");
  return $fh;
}

sub Finger::data {
  my ($conn) = query(@_)
    || croak "Can't open a Finger connection";
  my ($data) = join('', <$conn>)
    || croak "No Finger data";
  $data =~ s/\r\n/\n/g;

  $data;
}

1;
__END__

=head1 NAME

Finger - give some host a finger

=head1 SYNOPSIS

  use Finger;
  $fh = Finger::query("foo@bar.baz");
  @data = <$fh>;
  close($fh);

  $text = Finger::data("foo@bar.baz", 79);

=head1 DESCRIPTION

  Finger::query("user@host", $port) opens a connection to host "host"
  on port $port and queries for user "user".  Long output is requested
  if $Finger::Long is set to true.  If the port is omitted, the
  default finger port is used.  The function returns a FileHandle
  which can be used to retrieve the finger information.

  Finger::data("user@host", $port) creates a connection by calling
  Finger::query, reads all data from the socket until the socket is
  closed by the remote host, and returns all data read.

=head1 BUGS

  Finger::query (and, consequently, Finger::data) clears the alarm
  timer.

=head1 AUTHOR

Albert Dvornik, bert@mit.edu

=head1 SEE ALSO

=cut
