All pastes #3026 Raw Edit

robert

public text v1 · immutable
#3026 ·published 2004-12-16 07:46 UTC
rendered paste body
#!/usr/bin/perl
# 
# Connection.pm
# Created: Tue Sep 15 14:26:26 1998 by jay.kominek@colorado.edu
# Revised: Tue Dec 12 18:13:47 2000 by jay.kominek@colorado.edu
# Copyright 1998 Jay F. Kominek (jay.kominek@colorado.edu)
#
# Consult the file 'LICENSE' for the complete terms under which you
# may use this file.
# 
#####################################################################
# Connection object for The Perl Internet Relay Chat Daemon
#####################################################################

package Connection;
use Utils;
use Socket;
use strict;
use UNIVERSAL qw(isa);

# Class constructor
sub new {
  my $proto = shift;
  my $class = ref($proto) || $proto;
  my $this  = { };

  $this->{'socket'}    = shift;
  $this->{'outbuffer'} = shift;
  $this->{'server'}    = shift;
  $this->{'connected'} = $this->{last_active} = time();

  $this->{'ssl'} = $this->{'socket'}->isa("IO::Socket::SSL");

  my($port,$iaddr)     = sockaddr_in(getpeername($this->{'socket'}));
  $this->{'host'}      = gethostbyaddr($iaddr,AF_INET) || inet_ntoa($iaddr);
  $this->{'host_ip'}   = inet_ntoa($iaddr);

  bless($this, $class);
  return $this;
}

## $comment begin: December 14, 2004 [3:01am]
#$ FUCKING HELL, why does he go one place and do something, and on one thing, and on a similar part, in ANOTHER part of the ircd, 
#$ DO IT COMPLETELY DIFFERENTLY?#$^%?@#$$^&@#%#^& FUCKING HELL MAN, CONSISTIENCY!!@#%!@$%@#%$ DO YOU HEAR ME?#@%$@#%@% 
#$ CONSISTIENCY!!!!!!$!!%$!$%
#$ ---
#$ *takes his medication* okay, Im fine...what I mean to say, is, WHY do we have consistiency issues here, people?
## $comment begin: December 14, [5:55am]
#$ Well, I finally did it, the old perl ircd is starting to fade away as I transition the code towards an actual 
#$ ircd, and not just a "perl irc server".

my $commands = ( 
		 PONG   => \&my_pong,
    		 NICK   => \&my_nick,
		 USER   => \&my_user,
		 SERVER => \&my_serv,
    		 PASS   => \&my_pass,
    		 QUIT   => \&my_quit 
               );

# Given a line of user input, process it and take appropriate action
sub handle {
    my($this, $line) = @_;

    Utils::do_handle($this,$line,$commands);
}


sub my_pong {
my($this,$dummy,$response) = @_;
if($this->{doing_nospoof}>0) {
		if($response eq $this->{doing_nospoof}) {
		$this->{doing_nospoof} = -1;
		if($this->{user}) {
			delete($this->{doing_nospoof});
			$this->{ready} = 1;
		} else { 
			$this->sendreply("513 :To connect, type /QUOTE PONG ".$this->{doing_nospoof}); 
		}
	}
}

sub my_nick {
        my($this,$dummy,$str)=@_;
        &donick($this,$dummy,$str);
}
sub my_user {
        my($this,$dummy,$username,$host,$server,$ircname)=@_;

        $this->{user} = "~$username";
        $this->{ircname}  = $ircname;
        if($this->{doing_nospoof}<0) { $this->{ready} = 1; }
}

sub my_user {
        my($this,$dummy,$username,$host,$server,$ircname)=@_;

        $this->{user} = "~$username";
        $this->{ircname}  = $ircname;
        if($this->{doing_nospoof}<0) { $this->{ready} = 1; }
}

sub my_server {
        my($this,$dummy,$servername,$distance,$timea,$timeb,$proto,
           $description)=@_;

        $this->{servername}  = $servername;
        $this->{proto}       = $proto;
        $this->{distance}    = $distance;
        $this->{description} = $description;
        if($this->{password}) { $this->{ready} = 1; }
}

sub my_pass {
        my($this,$dummy,$password)=@_;

        # We don't actually do anything with the password yet
        $this->{password} = $password;

        if($this->{servername}) { $this->{ready} = 1; } else { $this->{ready} = 0; }
}

sub my_quit {
        my($this,$dummy,$excuse)=@_;
        $this->quit($excuse);
}

# excuses should always be discarded for Connections.
# people who don't completely connect to the server/network should
# not be allowed to transmit any data via it.
sub quit {
  my($this,$msg)=@_;
  &main::finishclient($this->{'socket'});
}

sub last_active {
  my $this = shift;
  return $this->{last_active};
}

sub ping_in_air {
  return 1;
}

sub readytofinalize {
  my $this = shift;
  if($this->{ready}) {
    return 1;
  } else {
    return 0;
  }
}

sub server {
  my $this = shift;
  return $this->{'server'};
}

sub finalize {
  my $this = shift;
  if($this->{nick}) {
    # Since we have a nick stored, that means that we're destined to
    # become a user.
    foreach my $mask (keys %{$this->server->{'klines'}}) {
      if($this->{'host'} =~ /$mask/) {
	my @kline = @{$this->server->{'klines'}->{$mask}};
	if($this->{'user'} =~ /$kline[0]/) {
	  $this->sendnumeric($this->server,465,"*** $kline[2]");
	  $this->senddata("ERROR :Closing Link: ".$this->{'nick'}."[".$this->{'host'}."] by ".$this->server->name." (K-lined)\r\n");
	  $this->{'socket'}->send($this->{'outbuffer'}->{$this->{'socket'}},0);
	  return undef;
	}
      }
   }


    # Okay, we're safe, keep going.
    my $user = User->new($this);

    # We used to have to tell User objects that they were local, but
    # now they figure it out for themselves based on the fact that they
    # have sockets. Pretty clever of them, eh?

    return $user;
    } elsif {
    # We're trying to finalize as an IRC server
    my $server = Server->new($this);

    return $server;
  }
  return undef;
}

# send a numeric response to the peer
sub sendnumeric {
  my $this      = shift;
  my $from      = shift;
  my $numeric   = shift;
  my $msg       = pop;
  my @arguments = @_;
  my $destnick;
  my $fromstr;

  if($from->isa("User")) {
    $fromstr = $from->nick."!".$from->username."\@".$from->host;
  } elsif($from->isa("Server")) {
    $fromstr = $from->name;
  }

  if(length($numeric)<3) {
    $numeric = ("0" x (3 - length($numeric))).$numeric;
  }

  $destnick = '*' unless defined($destnick=$this->{nick});

  $this->senddata(":".join(' ',$fromstr,$numeric,$destnick,@arguments,defined($msg)?":".$msg:( ))."\r\n");
}

# send one or more reply line to a peer
# this is a nicer substitute for sendnumeric and wrapper for senddata.
# each argument is a string, which is sent as a reply line. if the string
# starts with a colon, that is used as the "from", otherwise the server name
# is used. the rest if the string is a code, then a space, then data.
# if the code contains a ">", then whatever follows the > is used as the
# destination nick. otherwise, the destination nick will automatically be set
# based on the destination object.
# if the first passed argument after the destination user is also a user
# object, then the default source will be the nick!user@host of that user
# object (instead of the server name).
# sendreply will insert the destination nick between the code and the data.
# if part of the data is a multi-word argument, the colon must be explicitly
# included; sendreply won't magically add one in.
sub sendreply {
  my($this,$src,@replies)=@_;
  my($reply,$fromstr,$repcode,$data,$destnick);

  ($src,@replies)=(undef,$src,@replies) if(!ref($src) || !isa($src,"User"));
  
  defined($destnick=$this->{nick}) or $destnick='*';
  
  foreach $reply (@replies) {
    if($reply=~/^:/) {
      ($fromstr,$reply)=$reply=~/(\S+)\s+(.*)/;
    } elsif(defined($src)) {
      $fromstr=":$$src{nick}!$$src{user}\@$$src{host}";
    } else {
      $fromstr=":${$$this{'server'}}{'name'}";
    }
    ($repcode,$data)=$reply=~/(\S*)(.*)/;
    if($repcode=~/>/) {
      ($repcode,$destnick)=$repcode=~/([^>]+)>(.*)/;
    }
    $this->senddata("$fromstr $repcode $destnick$data\r\n");
  }
}

# queue some data to be sent to the peer on this connection
sub senddata {
    my $this=shift;

  $this->{'outbuffer'}->{$this->{'socket'}} .= join('',@_);
}

# handle a NICK request
sub donick {
  my($this,$dummy,$newnick)=@_;
  my $timeleft=$this->{'next_nickchange'}-time();
  my $channel;

  if($newnick eq $this->{'nick'}) {
    return; # silently discard stupid nick changes
  }
  if ($timeleft>0) {
    $this->sendnumeric($this->server,438,$newnick,
		       "Nick change too fast. Please wait $timeleft more seconds.");
  } elsif (!Utils::validnick($newnick)) {
    $this->sendnumeric($this->server,432,$newnick,"Erroneous nickname.");
  } elsif (defined(Utils::lookup($newnick)) && (Utils::irclc($newnick) ne Utils::irclc($this->{'nick'}))) {
    $this->sendnumeric($this->server,433,$newnick,
		       "Nickname already in use");
  } elsif (!defined($this->{'nick'})) {
    $this->{'nick'}=$newnick;
    $this->{doing_nospoof} = int(rand()*4294967296);
    $this->senddata("PING :$this->{doing_nospoof}\r\n");
  } else {
    $this->{'oldnick'} = $this->{'nick'};
    $this->{'nick'}    = $newnick;
    $this->senddata(":$$this{oldnick}!$$this{user}\@$$this{host} NICK :".$this->{'nick'}."\r\n");
    if ($this->isa('User')) {	# not just an unpromoted connection
      unshift @Utils::nickhistory, { 'nick' => $this->{'oldnick'},
				     'newnick' => $this->{'nick'},
				     'username' => $this->username,
				     'host' => $this->host,
				     'ircname' => $this->ircname,
				     'server' => $this->server->name,
				     'time' => time };
      delete(Utils::users()->{$this->{'oldnick'}});
      Utils::users()->{$this->{'nick'}}=$this;
      $this->server->removeuser($this->{'oldnick'});
      $this->server->adduser($this);

      # So that no given user will receive the nick change twice -
      # we build this hash and then send the message to those users.
      my %userlist;
      foreach my $channel (keys(%{$this->{'channels'}})) {
	my %storage = %{$this->{channels}->{$channel}->nickchange($this)};
	foreach (keys %storage) {
	  $userlist{$_} = $storage{$_} if $storage{$_} != $this;
	}
      }
      foreach my $user (keys %userlist) {
	next unless $userlist{$user}->islocal();
	$userlist{$user}->senddata(":$$this{oldnick}!$$this{user}\@$$this{host} NICK :$$this{nick}\r\n");
      }
      # FIXME should propogate to other servers
    }
    $this->{'next_nickchange'}=time()+30;
  }
1;