Net-IRC

 view release on metacpan or  search on metacpan

Connection.pm  view on Meta::CPAN

#                                                                   #
#    Copyright (c) 2001 Pete Sergeant, Greg Bacon & Dennis Taylor.  #
#                       All rights reserved.                        #
#                                                                   #
#      This module is free software; you can redistribute or        #
#      modify it under the terms of Perl's Artistic License.        #
#                                                                   #
#####################################################################

package Net::IRC::Connection;

use Net::IRC::Event;
use Net::IRC::DCC;
use IO::Socket;
use IO::Socket::INET;
use Symbol;
use Carp;

# all this junk below just to conditionally load a module
# sometimes even perl is braindead...

eval 'use Time::HiRes qw(time)';
if(!$@) {
  sub time ();
  use subs 'time';
  require Time::HiRes;
  Time::HiRes->import('time');
}

use strict;

use vars (
	'$AUTOLOAD',
);


# The names of the methods to be handled by &AUTOLOAD.
my %autoloaded = ( 'ircname'  => undef,
		   'port'     => undef,
		   'username' => undef,
		   'socket'   => undef,
		   'verbose'  => undef,
		   'parent'   => undef,
                   'hostname' => undef,
		   'pacing'   => undef,
                   'ssl'      => undef,
		 );

# This hash will contain any global default handlers that the user specifies.

my %_udef = ();

# Creates a new IRC object and assigns some default attributes.
sub new {
  my $proto = shift;
  
  my $self = {                # obvious defaults go here, rest are user-set
    _debug      => $_[0]->{_debug},
    _port       => 6667,
    # Evals are for non-UNIX machines, just to make sure.
    _username   => eval { scalar getpwuid($>) } || $ENV{USER} || $ENV{LOGNAME} || "japh",
    _ircname    => $ENV{IRCNAME} || eval { (getpwuid($>))[6] } || "Just Another Perl Hacker",
    _nick       => $ENV{IRCNICK} || eval { scalar getpwuid($>) } || $ENV{USER} || $ENV{LOGNAME} || "WankerBot",
    _ignore     => {},
    _handler    => {},
    _verbose    =>  0,       # Is this an OK default?
    _parent     =>  shift,
    _frag       =>  '',
    _connected  =>  0,
    _maxlinelen =>  510,     # The RFC says we shouldn't exceed this.
    _lastsl     =>  0,
    _pacing     =>  0,       # no pacing by default
    _ssl	=>  0,       # no ssl by default
    _format     => { 'default' => "[%f:%t]  %m  <%d>", },
  };
  
  bless $self, $proto;
  # do any necessary initialization here
  $self->connect(@_) if @_;
  
  return $self;
}

# Takes care of the methods in %autoloaded
# Sets specified attribute, or returns its value if called without args.
sub AUTOLOAD {
    my $self = @_;  ## can't modify @_ for goto &name
    my $class = ref $self;  ## die here if !ref($self) ?
    my $meth;

    # -- #perl was here! --
    #  <Teratogen> absolute power corrupts absolutely, but it's a helluva lot
    #              of fun.
    #  <Teratogen> =)
    
    ($meth = $AUTOLOAD) =~ s/^.*:://;  ## strip fully qualified portion

    unless (exists $autoloaded{$meth}) {
	croak "No method called \"$meth\" for $class object.";
    }
    
    eval <<EOSub;
sub $meth {
    my \$self = shift;
	
    if (\@_) {
	my \$old = \$self->{"_$meth"};
	
	\$self->{"_$meth"} = shift;
	
	return \$old;
    }
    else {
	return \$self->{"_$meth"};
    }
}
EOSub
    
    # no reason to play this game every time
    goto &$meth;
}

# This sub is the common backend to add_handler and add_global_handler

Connection.pm  view on Meta::CPAN


# This sub will assign a user's custom function to a particular event which
# this connection might receive.  Same args as above.
sub add_handler {
  my ($self, $event, $ref, $rp) = @_;
  return $self->_add_generic_handler($event, $ref, $rp, $self->{_handler}, 'add_handler');
}

# Hooks every event we know about...
sub add_default_handler {
  my ($self, $ref, $rp) = @_;
  foreach my $eventtype (keys(%Net::IRC::Event::_names)) {
    $self->_add_generic_handler($eventtype, $ref, $rp, $self->{_handler}, 'add_default_handler');
  }
  return 1;
}

# Why do I even bother writing subs this simple? Sends an ADMIN command.
# Takes 1 optional arg:  the name of the server you want to query.
sub admin {
  my $self = shift;        # Thank goodness for AutoLoader, huh?
                           # Perhaps we'll finally use it soon.
  
  $self->sl("ADMIN" . ($_[0] ? " $_[0]" : ""));
}

# Toggles away-ness with the server.  Optionally takes an away message.
sub away {
    my $self = shift;
    $self->sl("AWAY" . ($_[0] ? " :$_[0]" : ""));
}

# Attempts to connect to the specified IRC (server, port) with the specified
#   (nick, username, ircname). Will close current connection if already open.
sub connect {
  my $self = shift;
  my ($password, $sock);
  
  if (@_) {
    my (%arg) = @_;
    
    $self->hostname($arg{'LocalAddr'}) if exists $arg{'LocalAddr'};
    $password = $arg{'Password'} if exists $arg{'Password'};
    $self->nick($arg{'Nick'}) if exists $arg{'Nick'};
    $self->port($arg{'Port'}) if exists $arg{'Port'};
    $self->server($arg{'Server'}) if exists $arg{'Server'};
    $self->ircname($arg{'Ircname'}) if exists $arg{'Ircname'};
    $self->username($arg{'Username'}) if exists $arg{'Username'};
    $self->pacing($arg{'Pacing'}) if exists $arg{'Pacing'};
    $self->ssl($arg{'SSL'}) if exists $arg{'SSL'};
  }
  
  # Lots of error-checking claptrap first...
  unless ($self->server) {
    unless ($ENV{IRCSERVER}) {
      croak "No server address specified in connect()";
    }
    $self->server( $ENV{IRCSERVER} );
  }
  unless ($self->nick) {
    $self->nick($ENV{IRCNICK} || eval { scalar getpwuid($>) }
                || $ENV{USER} || $ENV{LOGNAME} || "WankerBot");
  }
  unless ($self->port) {
    $self->port($ENV{IRCPORT} || 6667);
  }
  unless ($self->ircname)  {
    $self->ircname($ENV{IRCNAME} || eval { (getpwuid($>))[6] }
                   || "Just Another Perl Hacker");
  }
  unless ($self->username) {
    $self->username(eval { scalar getpwuid($>) } || $ENV{USER}
                    || $ENV{LOGNAME} || "japh");
  }
  
  # Now for the socket stuff...
  if ($self->connected) {
    $self->quit("Changing servers");
  }
  
  if($self->ssl) {
    require IO::Socket::SSL;
    
    $self->socket(IO::Socket::SSL->new(PeerAddr  => $self->server,
                                       PeerPort  => $self->port,
                                       Proto     => "tcp",
                                       LocalAddr => $self->hostname,
                                       ));
  } else {
    
    $self->socket(IO::Socket::INET->new(PeerAddr  => $self->server,
                                        PeerPort  => $self->port,
                                        Proto     => "tcp",
                                        LocalAddr => $self->hostname,
                                        ));
  }
  
  if(!$self->socket) {
    carp (sprintf "Can't connect to %s:%s!",
          $self->server, $self->port);
    $self->error(1);
    return;
  }
  
  # Send a PASS command if they specified a password. According to
  # the RFC, we should do this as soon as we connect.
  if (defined $password) {
    $self->sl("PASS $password");
  }
  
  # Now, log in to the server...
  unless ($self->sl('NICK ' . $self->nick()) and
          $self->sl(sprintf("USER %s %s %s :%s",
                            $self->username(),
                            "foo.bar.com",
                            $self->server(),
                            $self->ircname()))) {
    carp "Couldn't send introduction to server: $!";
    $self->error(1);
    $! = "Couldn't send NICK/USER introduction to " . $self->server;
    return;
  }
  
  $self->{_connected} = 1;
  $self->parent->addconn($self);
}

# Returns a boolean value based on the state of the object's socket.
sub connected {
  my $self = shift;
  
  return ( $self->{_connected} and $self->socket() );



( run in 1.254 second using v1.01-cache-2.11-cpan-df04353d9ac )