Author: dylan
Date: 2004-06-04 22:08:39 -0400 (Fri, 04 Jun 2004)
New Revision: 222
Added:
trunk/haver-client/examples/
trunk/haver-client/lib/Haver/Client/POE.pm
trunk/haver-client/lib/POE/
trunk/haver-client/lib/POE/Component/
trunk/haver-client/lib/POE/Component/Client/
trunk/haver-client/lib/POE/Component/Client/Haver.pm
Removed:
trunk/poe-client/
Log:
poe-client is merged with haver-client
POE::Component::Client::Haver is deprecated,
but will still work. use Haver::Client::POE from now on.
Copied: trunk/haver-client/examples (from rev 217, trunk/poe-client/examples)
Copied: trunk/haver-client/lib/Haver/Client/POE.pm (from rev 217,
trunk/poe-client/lib/POE/Component/Client/Haver.pm)
===================================================================
--- trunk/poe-client/lib/POE/Component/Client/Haver.pm 2004-05-31 23:15:00 UTC
(rev 217)
+++ trunk/haver-client/lib/Haver/Client/POE.pm 2004-06-05 02:08:39 UTC (rev
222)
@@ -0,0 +1,860 @@
+# vim: set ft=perl ts=4 sw=4:
+# Haver::Client::POE - POE component haver client.
+#
+# Copyright (C) 2004 Bryan Donlan, Dylan William Hardison.
+#
+# This module is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This module is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this module; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+=head1 NAME
+
+Haver::Client::POE - POE Component for Haver clients.
+
+=head1 SYNOPSIS
+
+ use Haver::Client::POE;
+
+ new Haver::Client::POE('haver');
+ POE::Kernel->post('haver', 'connect', Host => 'example.com',
+ Port => 7070,
+ UID => 'example');
+
+=head1 DESCRIPTION
+
+Haver::Client::POE is a POE component for writing Haver clients.
+Generally one will create a session with new(), register for all events with
+register(), and then send commands and receive events from the session.
+
+=head1 METHODS
+
+=cut
+
+package Haver::Client::POE;
+use strict;
+use warnings;
+
+use POE qw(Wheel::ReadWrite
+ Wheel::SocketFactory);
+use Haver::Preprocessor;
+use Haver::Misc qw(format_datetime);
+use POE::Filter::Haver;
+use Haver::Formats::Error;
+use Carp;
+use Digest::SHA1 qw(sha1_base64);
+require Exporter;
+
+our $VERSION = 0.06;
+
+sub _call {
+ return POE::Kernel->call(POE::Kernel->get_active_session(), @_);
+}
+
+sub _dprint {
+ my ($level, @text) = @_;
+ return unless POE::Kernel->get_active_session()->get_heap()->{debug} >=
$level;
+ print STDERR @text;
+}
+
+sub _dprintf {
+ my ($level, $fmt, @text) = @_;
+ return unless POE::Kernel->get_active_session()->get_heap()->{debug} >=
$level;
+ printf STDERR $fmt, @text;
+}
+
+
+### SETUP
+
+=head2 new($Z<>alias [, option => value ...])
+
+Creates a new Haver::Client::POE session with alias $alias, and optionally
+sets one or more options (see C<setoptions>)
+
+=cut
+
+sub new {
+ my ($class, $alias, @options) = @_;
+ carp "Can't call ->new on a ".(ref $class)." instance" if ref $class;
+ #carp "Haver::Client can't be subclassed" if ($class ne __PACKAGE__);
+ ## This is stupid restriction.
+ POE::Session->create(package_states =>
+ [ __PACKAGE__,
+ [qw{
+ _start
+ setoptions
+
+ register
+ unregister
+ dispatch
+ dispatch_ref
+
+ connect
+ connected
+ connectfail
+
+ input
+ send_raw
+ send
+ net_error
+
+ destroy
+ disconnect
+ force_close
+ flushed
+ cleanup
+ _stop
+
+ login
+ join
+ part
+ msg
+ pmsg
+ users
+ make
+ chans
+
+ event_WANT
+ event_ACCEPT
+ event_REJECT
+ event_PING
+ event_CLOSE
+ event_IN
+
+ event_JOIN
+ event_PART
+ event_MSG
+ event_USERS
+ event_BYE
+ event_QUIT
+ event_CHANS
+ event_WARN
+ event_DIE
+
+ _default
+
+ }]],
+ args => [$alias, @options]
+ );
+ return 1;
+}
+
+sub _start {
+ my ($kernel, $heap, $session, $alias, @args) =
@_[KERNEL,HEAP,SESSION,ARG0..$#_];
+ $kernel->alias_set($alias);
+ %$heap = (alias => $alias,
+ registrations => {},
+ scope => undef,
+ debug => 0,
+ autorespond => { 'PING?' => 1, 'TIME?' => 1 },
+ version => "Haver::Client::POE/$VERSION",
+ );
+ if (@args) {
+ _call('setoptions', @args);
+ }
+}
+
+=head1 MESSAGES
+
+=head2 setoptions(option => value [, ...])
+
+Sets one or more options to the given value. The following options are
available:
+
+=head3 debug => level
+
+Sets debugging to the given level. 0 will disable debugging.
+
+=head3 autorespond => [ ... ]
+
+Enables autoresponding to certain types of messages. Currently only PING? and
TIME? are supported.
+
+=head3 version => "foobar/0.1"
+
+Sets the string sent in response to S: WANT VERSION
+
+=cut
+
+sub setoptions {
+ my ($kernel, $heap, %args) = @_[KERNEL,HEAP,ARG0..$#_];
+ my %setters = (
+ debug => sub { $heap->{debug} = $_[0]; },
+ autorespond => sub { $heap->{autorespond} = map { ($_ => 1) }
@_ },
+ version => sub { $heap->{version} = $_[0]; },
+ );
+ for (keys %args) {
+ $setters{$_}->($args{$_}) if exists $setters{$_};
+ }
+}
+
+### DISPATCH
+
+=head2 register($Z<>event1 [,...])
+
+Registers to receive the events listed. When a matching event is dispatched,
it will be sent to
+the calling session as 'haver_eventname'. The special event name 'all' may be
specified to register for all
+events. A given event will not be sent to any given session more than once.
+
+=cut
+
+sub register {
+ my ($kernel, $heap, $sender, @events) =
@_[KERNEL,HEAP,SENDER,ARG0..$#_];
+ for(@events) {
+ if (!exists $heap->{registrations}->{$_}->{$sender->ID}) {
+ $heap->{registrations}->{$_}->{$sender->ID} =
$heap->{alias} . "##$_";
+ $kernel->refcount_increment($sender->ID, $heap->{alias}
. "##$_");
+ }
+ }
+}
+
+=head2 unregister($Z<>event1 [,...])
+
+Unregisters from the specified event. Events registered using 'all' must be
unregistered using 'all'.
+
+=cut
+
+sub unregister {
+ my ($kernel, $heap, $sender, @events) =
@_[KERNEL,HEAP,SENDER,ARG0..$#_];
+ for(@events) {
+ if (exists $heap->{registrations}->{$_}->{$sender->ID}) {
+ delete $heap->{registrations}->{$_}->{$sender->ID};
+ $kernel->refcount_decrement($sender->ID, $heap->{alias}
. "##$_");
+ }
+ }
+}
+
+sub dispatch_ref {
+ my ($kernel, $heap, $event, $args) = @_[KERNEL,HEAP,ARG0,ARG1];
+ my %targets = (map { $_ => 1 }
(keys(%{$heap->{registrations}->{$event}}),
+
keys(%{$heap->{registrations}->{all}})));
+ $kernel->post($_, "haver_$event", $args, $heap->{scope}) for keys
%targets;
+}
+
+sub dispatch {
+ my @pre = @_[0..ARG0];
+ my $payload = [EMAIL PROTECTED];
+ @_ = (@pre, $payload);
+ goto &dispatch_ref;
+}
+
+### SESSION MANAGEMENT
+
+=head2 B<connect(Host => $Z<>host, [Port => $Z<>port, UID => $Z<>uid, Password
=> $Z<>password])
+
+Connects to the haver server. The Host option is mandatory, all others are
optional.
+If it is already connected, it will disconnect, then connect with the new
parameters.
+
+Password is deprecated, and will be removed some time after SSL is supported.
+
+=cut
+
+sub connect {
+ my ($kernel, $heap, %args) = @_[KERNEL,HEAP,ARG0..$#_];
+# XXX: Better error reporting
+ croak "Missing required parameter Host" unless exists $args{Host};
+ if (exists $heap->{conn}) {
+ $kernel->yield('disconnect') unless exists
$heap->{pending_connection};
+ $heap->{pending_connection} = [%args];
+ return;
+ }
+ $heap->{UID} = $args{UID};
+ $heap->{PASS} = $args{Password};
+ $heap->{Host} = $args{Host};
+ $args{Port} ||= 7070;
+ $heap->{connect_wheel} =
+ POE::Wheel::SocketFactory->new(
+ RemoteAddress => $args{Host},
+ RemotePort => $args{Port},
+ SuccessEvent => 'connected',
+ FailureEvent => 'connectfail'
+ );
+}
+
+=head2 disconnect(Z<>)
+
+Disconnects from the Haver server. If not already connected, does nothing.
This event
+may not complete immediately.
+
+=cut
+
+sub disconnect {
+ my ($kernel, $heap) = @_[KERNEL, HEAP];
+ return if $heap->{closing};
+ $heap->{closing} = 1;
+ if ($heap->{want}) {
+ $kernel->yield('cleanup');
+ } else {
+ $kernel->yield('send', 'QUIT');
+ $kernel->delay('force_close', 5);
+ }
+}
+
+sub connected {
+ my ($kernel, $heap) = @_[KERNEL, HEAP];
+ my ($handle, $id) = @_[ARG0,ARG3];
+ if (!exists $heap->{connect_wheel} ||
+ $heap->{connect_wheel}->ID() != $id){
+ close $handle;
+ return;
+ }
+ binmode $handle, ':utf8';
+ $heap->{conn} =
+ POE::Wheel::ReadWrite->new(
+ Handle => $handle,
+ Driver => POE::Driver::SysRW->new(),
+ Filter => POE::Filter::Haver->new(),
+ InputEvent => 'input',
+ FlushedEvent => 'flushed',
+ ErrorEvent => 'net_error'
+ );
+ delete $heap->{connect_wheel};
+ $heap->{flushed} = 1;
+ _call('dispatch', 'connected');
+}
+
+sub connectfail {
+ my ($kernel, $heap, $enum, $estr) = @_[KERNEL,HEAP,ARG1,ARG2];
+ _call('dispatch', 'connect_fail', $enum, $estr);
+ delete $heap->{connect_wheel};
+}
+
+sub net_error {
+ my ($kernel, $heap, $enum, $estr) = @_[KERNEL,HEAP,ARG1,ARG2];
+ _call('dispatch', 'disconnected', $enum, $estr);
+ $kernel->yield('cleanup');
+}
+
+### IO
+
+sub input {
+ my ($kernel, $heap, $event) = @_[KERNEL,HEAP,ARG0];
+ _dprint 1, "S: ", join("\t", @$event), "\n" unless defined
$heap->{scope};
+ my $ename = shift @$event;
+ _call('dispatch', 'raw_in', $ename, @$event) unless defined
$heap->{scope};
+ _call("event_$ename", @$event);
+}
+
+=head2 send_raw(@args)
+
+Sends the arguments specified to the haver server. No checking is performed,
though escaping may occur.
+
+=cut
+
+sub send_raw {
+ my ($kernel, $heap, @message) = @_[KERNEL,HEAP,ARG0..$#_];
+ return if ($heap->{want} && $heap->{want} eq "!impossible");
+ eval { $heap->{conn}->put([EMAIL PROTECTED]); };
+ if ($@) {
+ # Ack, lost connection unexpectedly!
+ # Hopefully we get net_error soon
+ $heap->{want} = "!impossible";
+ return;
+ }
+ _dprint 1, "C: ", join("\t", map { defined($_) ? $_ : '~UNDEF~' }
@message), "\n";
+ _call('dispatch', 'raw_out', @message);
+}
+
+sub send {
+ my ($kernel, $heap, @message) = @_[KERNEL,HEAP,ARG0..$#_];
+ if ($heap->{want}) {
+ if (($heap->{want} ne uc $message[0]) &&
+ ((uc $message[0] ne 'CANT') || ($message[1] ne
$heap->{want}))) {
+ _dprint 1, "(blocked) C: ", join("\t", @message), "\n";
+ push @{$heap->{messageq} ||= []}, [EMAIL PROTECTED];
+ return;
+ }
+ delete $heap->{want};
+ }
+ $kernel->yield('send_raw', @message);
+ if (exists $heap->{messageq}) {
+ for (@{$heap->{messageq}}) {
+ $kernel->yield('send', @$_);
+ }
+ delete $heap->{messageq};
+ }
+ $heap->{flushed} = 0;
+}
+
+### SERVER EVENTS
+
+# XXX: Make a more extensible WANT system later
+#
+# hmm, I reformatted this a bit so it is possible
+# to easily edit in vim. :P (dylan)
+sub event_WANT {
+ my ($kernel, $heap, $wanted, @arg) = @_[KERNEL,HEAP,ARG0,ARG1];
+ $wanted = uc $wanted;
+ $heap->{want} = $wanted;
+ my %wants = (
+ IDENT => sub {
+ $kernel->yield('send', 'IDENT', $heap->{UID}, 'user',
$heap->{version});
+ },
+ AUTH => sub {
+ # XXX: More extensible AUTH system later too
+ my @methods = split ',', $arg[0];
+ # XXX: only pass for now
+ unless(grep { $_ eq 'pass' } @methods) {
+ $kernel->yield('send', 'CANT', 'AUTH');
+ return;
+ }
+ $kernel->yield('send', 'AUTH', 'pass');
+ $heap->{auth} = 'pass';
+ },
+ 'AUTH:PASS' => sub {
+ # XXX: Better support for namespaces
+ if($heap->{PASS}) {
+ $kernel->yield('login', $heap->{PASS});
+ return;
+ }
+ $kernel->yield('dispatch', 'login_request');
+ },
+ );
+ if (exists $wants{$wanted}) {
+ $wants{$wanted}->();
+ } else {
+ $kernel->yield('send', 'CANT', $wanted);
+ }
+}
+
+sub event_ACCEPT {
+ my ($kernel, $heap) = @_[KERNEL,HEAP];
+ $heap->{logged_in} = 1;
+ _call('dispatch', 'login');
+}
+
+sub event_REJECT {
+ my ($kernel, $heap, $uid, $err) = @_[KERNEL,HEAP,ARG0,ARG1];
+ my $e = new Haver::Formats::Error;
+ _call('dispatch', 'login_fail',
+ $err,
+ $e->get_short_desc($err),
+ $e->format( $e->get_long_desc($err), $uid )
+ );
+ delete $heap->{UID};
+ delete $heap->{PASS};
+ $heap->{want} = 'UID';
+}
+
+sub event_PING {
+ my ($kernel, $heap, @junk) = @_[KERNEL,HEAP,ARG0..$#_];
+ $kernel->yield('send', 'PONG', @junk);
+}
+
+sub event_CLOSE {
+ my ($kernel, $heap, $etyp, $estr) = @_[KERNEL,HEAP,ARG0,ARG1];
+ _call('dispatch', 'close', $etyp, $estr);
+}
+
+sub event_JOIN {
+ my ($kernel, $heap, $uid) = @_[KERNEL,HEAP,ARG0,ARG1];
+ _call('dispatch', ($uid eq '.' ||
+ $uid eq $heap->{UID}) ? 'joined' : 'join',
+ $uid);
+}
+
+sub event_PART {
+ my ($kernel, $heap, $uid) = @_[KERNEL,HEAP,ARG0,ARG1];
+ _call('dispatch', ($uid eq '.' ||
+ $uid eq $heap->{UID}) ? 'parted' : 'part',
+ $uid);
+}
+
+my %autorespond = (
+ 'PING?' => sub {
+ my ($kernel, $heap, $who, @junk) = @_[KERNEL,HEAP,ARG0..$#_];
+ if ([EMAIL PROTECTED]) {
+ @junk = (''); # This silences a warning elsewhere
+ }
+ $kernel->yield('pmsg', 'PING', $who, @junk);
+ },
+ 'TIME?' => sub {
+ my ($kernel, $heap, $who) = @_[KERNEL,HEAP,ARG0];
+ $kernel->yield('pmsg', 'TIME', $who, format_datetime(time()));
+ },
+);
+
+sub event_MSG {
+ my ($kernel, $heap, $uid, $type, @text) = @_[KERNEL,HEAP,ARG0..$#_];
+ if ($heap->{autorespond}->{$type} && exists $autorespond{$type}) {
+ $autorespond{$type}->(@_[0..ARG0-1], $uid, @text);
+ }
+
+ # I can't help but feel that argument order of msg and pmsg
+ # should better match reality.... (dylan)
+ if ($heap->{scope}) {
+ _call('dispatch', 'msg', $type, $uid, @text);
+ } else {
+ _call('dispatch', 'pmsg', $type, $uid, @text);
+ }
+
+}
+
+sub event_USERS {
+ my ($kernel, $heap, @who) = @_[KERNEL,HEAP,ARG0..$#_];
+
+ _call('dispatch_ref', 'users', [EMAIL PROTECTED]);
+}
+
+sub event_BYE {
+ my ($kernel, $heap, $why) = @_[KERNEL,HEAP,ARG0];
+ _call('dispatch', 'bye', $why);
+}
+
+sub event_QUIT {
+ my ($kernel, $heap, $who, $why) = @_[KERNEL,HEAP,ARG0,ARG1];
+ _call('dispatch', 'quit', $who, $why);
+}
+
+sub event_CHANS {
+ my ($kernel, $heap, @channels) = @_[KERNEL,HEAP,ARG0..$#_];
+ _call('dispatch', 'chans', @channels);
+}
+
+sub event_WARN {
+ my ($kernel, $err, @args) = @_[KERNEL,ARG0..$#_];
+ my $e = new Haver::Formats::Error;
+ _call('dispatch', 'warn',
+ $err,
+ $e->get_short_desc($err),
+ $e->format( $e->get_long_desc($err), @args ),
+ @args
+ );
+}
+
+sub event_DIE {
+ my ($kernel, $err, @args) = @_[KERNEL,ARG0..$#_];
+ my $e = new Haver::Formats::Error;
+ _call('dispatch', 'die',
+ $err,
+ $e->get_short_desc($err),
+ $e->format( $e->get_long_desc($err), @args ),
+ @args
+ );
+}
+
+sub event_IN {
+ my ($kernel, $heap, $scope, @cmd) = @_[KERNEL,HEAP,ARG0..$#_];
+ my $save = $heap->{scope};
+ $heap->{scope} = $scope;
+ _call('input', [EMAIL PROTECTED]);
+ $heap->{scope} = $save;
+}
+
+### CLIENT EVENTS
+
+=head2 login($Z<>pass)
+
+Specify a password to use for the next login. If already logged in, this takes
effect on the next connection
+unless overridden by connect(). If the server is waiting for a login, takes
effect immediately.
+
+=cut
+
+sub login {
+ my ($kernel, $heap, $pass) = @_[KERNEL,HEAP,ARG0,ARG1];
+ $heap->{PASS} = $pass if $pass;
+ if ($heap->{want} eq 'AUTH:PASS') {
+ if (defined $heap->{PASS}) {
+ $kernel->yield('send', 'AUTH:PASS',
sha1_base64($heap->{UID} . $heap->{PASS} . $heap->{Host}));
+ } else {
+ $kernel->yield('send', 'CANT', 'AUTH:PASS');
+ }
+ }
+}
+
+=head2 join($Z<>channel)
+
+Attempts to join $channel
+
+=cut
+
+sub join {
+ my ($kernel, $heap, $where) = @_[KERNEL,HEAP,ARG0];
+ $kernel->yield('send', 'JOIN', $where);
+}
+
+=head2 part($Z<>channel)
+
+Parts $Z<>channel
+
+=cut
+
+sub part {
+ my ($kernel, $heap, $where) = @_[KERNEL,HEAP,ARG0];
+ $kernel->yield('send', 'PART', $where);
+}
+
+=head2 make($Z<>channel)
+
+Ask the server to create a channel $channel.
+
+=cut
+
+sub make {
+ my ($kernel, $heap, $cid) = @_[KERNEL,HEAP,ARG0];
+ $kernel->yield('send', 'MAKE', $cid);
+}
+
+=head2 B<msg($Z<>type, $Z<>channel, $Z<>text)>
+
+Sends a message with specified type and text to $channel.
+
+=cut
+
+sub msg {
+ my ($kernel, $heap, $type, $where, $message) =
@_[KERNEL,HEAP,ARG0..ARG2];
+ $kernel->yield('send', 'IN', $where, 'MSG', $type, $message);
+}
+
+=head2 B<pmsg($Z<>type, $Z<>uid, $Z<>text)>
+
+Sends a private message with specified type and text to $uid.
+
+=cut
+
+sub pmsg {
+ my ($kernel, $heap, $type, $where, $message) =
@_[KERNEL,HEAP,ARG0..ARG2];
+ $kernel->yield('send', 'TO', $where, 'MSG', $type, $message);
+}
+
+=head2 users($Z<>channel)
+
+Ask the server which users are on $channel.
+
+=cut
+
+sub users {
+ my ($kernel, $heap, $where) = @_[KERNEL,HEAP,ARG0];
+ $kernel->yield('send', 'IN', $where, 'USERS');
+}
+
+=head2 chans(Z<>)
+
+Ask the server for a list of all channels
+
+=cut
+
+sub chans {
+ my $kernel = $_[KERNEL];
+ $kernel->yield('send', 'CHANS');
+}
+
+### SHUTDOWN
+
+sub force_close {
+ my ($kernel, $heap) = @_[KERNEL, HEAP];
+ return if $heap->{closing} == 3;
+ if ($heap->{closing} == 2 || $heap->{flushed}){ # Flushed or flush
timeout
+ $kernel->yield('cleanup');
+ _call('dispatch', 'disconnected', -1, 'Disconnected');
+ $kernel->delay('force_close');
+ $heap->{closing} = 3;
+ return;
+ }
+ $heap->{closing} = 2;
+ $kernel->delay('force_close', 5);
+}
+
+sub flushed {
+ my ($kernel, $heap) = @_[KERNEL, HEAP];
+ if (defined $heap->{closing} && $heap->{closing} == 2) {
+ $kernel->yield('force_close');
+ }
+ $heap->{flushed} = 1;
+}
+
+sub cleanup {
+ my ($kernel, $heap) = @_[KERNEL, HEAP];
+ delete $heap->{$_} for qw(conn flushed closing UID PASS want messageq);
+ $kernel->delay('force_close');
+
+ if ($heap->{destroy_pending}) {
+ $kernel->yield('destroy');
+ } elsif (exists $heap->{pending_connection}) {
+ $kernel->yield('connect', @{$heap->{pending_connection}});
+ delete $heap->{pending_connection};
+ }
+}
+
+=head2 destroy(Z<>)
+
+Disconnects from the Haver server, and destroys the session. This event may
not complete
+immediately, so you should not attempt to create another session with the same
alias
+until it finishes.
+
+=cut
+
+sub destroy {
+ my ($kernel, $heap) = @_[KERNEL, HEAP];
+ _dprint 1, "Destroying.\n";
+ if (exists $heap->{conn}){
+ $heap->{destroy_pending} = 1;
+ $kernel->yield('disconnect');
+ return;
+ }
+ $kernel->alias_remove($heap->{alias});
+}
+
+sub _stop {
+ my ($kernel, $heap) = @_[KERNEL, HEAP];
+ foreach my $evt (keys %{$heap->{registrations}}) {
+ my $ehash = $heap->{registrations}->{$evt};
+ foreach my $session (keys %$ehash) {
+ my $refcount = $ehash->{$session};
+ $kernel->refcount_decrement($session, $refcount);
+ }
+ }
+}
+
+sub _default {
+ my ( $kernel, $state, $event, $args, $heap ) = @_[ KERNEL, STATE, ARG0,
ARG1, HEAP ];
+ $args ||= []; # Prevents uninitialized-value warnings.
+ DEBUG: "default: $state = $event. Args:\n";
+ DUMP: $args;
+ return 0;
+}
+
+
+1;
+__END__
+
+=head1 EVENTS
+
+Event callbacks are called with the frist argument being the event arguments
and
+the second argument being the scope set by IN (or undef if not set). Example:
+
+ sub haver_connect_fail {
+ my ($args, $scope) = @_[ARG0,ARG1];
+ my ($enum, $estr) = @$args;
+ # ...
+ }
+
+=head2 haver_connected(Z<>)
+
+This event is sent when a connection is established (but before it is logged
in)
+
+=head2 haver_connect_fail($Z<>enum, $Z<>estr)
+
+The connection could not be established. An error code is in $enum, and the
human-readable
+version is in $estr
+
+=head2 haver_disconnected($Z<>enum, $Z<>estr)
+
+The connection has been lost. If the server closed the connection, $enum will
be 0 and $estr will
+be meaningless. If the user closed the connection, and the server failed to
respond, $enum will be -1.
+Otherwise, $enum will contain an error code, and $estr the human-readable
version.
+
+=head2 haver_raw_in(@data)
+
+The client has received @data from the Haver server. Mostly useful for
debugging.
+
+=head2 haver_raw_out(@data)
+
+The client has sent @data to the Haver server. Mostly useful for debugging.
+
+=head2 haver_login_request(Z<>)
+
+The server is asking for a login, and one was not provided in connect(). The
connection will not proceed until
+login() is sent with the password.
+
+=head2 haver_login(Z<>)
+
+The client has successfully logged in.
+
+=head2 haver_login_fail($Z<>error, $Z<>error_short, $Z<>error_long, $Z<>uid)
+
+Login with UID $uid has failed with error $error. Human-readable short and
long versions, respectively, are
+in $error_short and $error_long.
+
+=head2 haver_close($Z<>etyp, $Z<>estr)
+Z<XXX: Describe args>
+
+Server is closing connection, and sent $etyp and $estr
+
+=head2 haver_join($Z<>uid)
+
+$uid has joined $scope
+
+=head2 haver_joined($Z<>uid)
+
+The client has joined $scope.
+
+=head2 haver_part($Z<>cid, $Z<>uid)
+
+$uid has left $cid.
+
+=head2 haver_parted($Z<>cid)
+
+The client has left $Z<>cid.
+
+=head2 haver_msg($Z<>type, $Z<>cid, $Z<>uid, $Z<>text)
+
+A public message with type $type and contents $text was sent on channel $cid
by user $uid.
+
+=head2 haver_pmsg($Z<>type, $z<>uid, $Z<>text)
+
+A private message with type $type and contents $text was sent to you by user
$uid.
+
+=head2 haver_users($Z<>where, @Z<>who)
+
+Channel $where has the users listed in @who in it. Sent in response to message
users().
+
+=head2 haver_bye($Z<>why)
+
+The server is disconnecting you due to the reason in $why
+
+=head2 haver_chans(@Z<>channels)
+
+The server has the channels listed in @channels. Sent in response to message
chans()
+
+=head2 haver_warn($Z<>err, $Z<>short, $Z<>long, @Z<>args)
+
+The server has sent a non-fatal error message with code $err and arguments
@args. $short and $long have the
+short and long human-readable forms, respectively.
+
+=head2 haver_die($Z<>err, $Z<>short, $Z<>long, @Z<>args)
+
+The server has sent a fatal error message with code $err and arguments @args.
$short and $long have the
+short and long human-readable forms, respectively. The connection will be
closed shortly.
+
+=head2 haver_quit($Z<>uid, $Z<>why)
+
+UID $uid has disconnected due to the reason in $why.
+
+=head1 SEE ALSO
+
+L<http://wiki.chani3.com/wiki/ProjectHaver/>
+
+=head1 AUTHOR
+
+Bryan Donlan, E<lt>[EMAIL PROTECTED]<gt> and
+Dylan William Hardison, E<lt>[EMAIL PROTECTED]<gt>
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright (C) 2004 by Bryan Donlan, Dylan William Hardison
+
+This library is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this module; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+=cut
Added: trunk/haver-client/lib/POE/Component/Client/Haver.pm
===================================================================
--- trunk/haver-client/lib/POE/Component/Client/Haver.pm 2004-06-04
06:06:14 UTC (rev 221)
+++ trunk/haver-client/lib/POE/Component/Client/Haver.pm 2004-06-05
02:08:39 UTC (rev 222)
@@ -0,0 +1,36 @@
+# vim: set ft=perl ts=4 sw=4:
+# This module is deprecated.
+#
+# Copyright (C) 2004 Bryan Donlan, Dylan William Hardison.
+#
+# This module is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This module is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this module; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+package POE::Component::Client::Haver;
+use strict;
+use warnings;
+use Haver::Client::POE;
+use base 'Haver::Client::POE';
+
+
+BEGIN {
+ warn __PACKAGE__, " is deprecated! use Haver::Client::POE instead!";
+}
+
+sub new {
+ my $this = shift;
+
+ warn __PACKAGE__, " is deprecated! use Haver::Client::POE instead!";
+
+ $this->SUPER::new(@_);
+}