File Coverage

blib/lib/POE/Component/IRC/Service/P10.pm
Criterion Covered Total %
statement 242 983 24.6
branch 68 362 18.7
condition 36 256 14.0
subroutine 35 125 28.0
pod 34 87 39.0
total 415 1813 22.8


line stmt bran cond sub pod time code
1             # Author: Chris "BinGOs" Williams
2             # Derived from code by Dennis Taylor
3             #
4             # This module may be used, modified, and distributed under the same
5             # terms as Perl itself. Please see the license that came with your Perl
6             # distribution for details.
7             #
8              
9             package POE::Component::IRC::Service::P10;
10              
11 1     1   6 use strict;
  1         2  
  1         30  
12 1         5 use POE qw( Wheel::SocketFactory Wheel::ReadWrite Driver::SysRW
13 1     1   12 Filter::Line Filter::Stream );
  1         2  
14 1     1   14024 use POE::Filter::IRC::P10;
  1         2  
  1         26  
15 1     1   325 use POE::Filter::CTCP::P10;
  1         3  
  1         29  
16 1     1   6 use Carp;
  1         2  
  1         43  
17 1     1   6 use Socket;
  1         2  
  1         415  
18 1     1   6 use Sys::Hostname;
  1         1  
  1         46  
19 1     1   5 use Time::HiRes qw (gettimeofday);
  1         2  
  1         8  
20 1     1   174 use vars qw($VERSION);
  1         2  
  1         163  
21              
22             $VERSION = '0.998';
23              
24             my %cmd2token = ('ACCOUNT' => 'AC', 'ADMIN' => 'AD', 'ASLL' => 'LL', 'AWAY' => 'A', 'BURST' => 'B', 'CLEARMODE' => 'CM', 'CLOSE' => 'CLOSE', 'CNOTICE' => 'CN', 'CONNECT' => 'CO', 'CPRIVMSG' => 'CP', 'CREATE' => 'C', 'DESTRUCT' => 'DE', 'DESYNCH' => 'DS', 'DIE' => 'DIE', 'DNS' => 'DNS', 'END_OF_BURST' => 'EB', 'EOB_ACK' => 'EA', 'ERROR' => 'Y', 'GET' => 'GET', 'GLINE' => 'GL', 'HASH' => 'HASH', 'HELP' => 'HELP', 'INFO' => 'F', 'INVITE' => 'I', 'ISON' => 'ISON', 'JOIN' => 'J', 'JUPE' => 'JU', 'KICK' => 'K', 'KILL' => 'D', 'LINKS' => 'LI', 'LIST' => 'LIST', 'LUSERS' => 'LU', 'MAP' => 'MAP', 'MODE' => 'M', 'MOTD' => 'MO', 'NAMES' => 'E', 'NICK' => 'N', 'NOTICE' => 'O', 'OPER' => 'OPER', 'OPMODE' => 'OM', 'PART' => 'L', 'PASS' => 'PA', 'PING' => 'G', 'PONG' => 'Z', 'POST' => 'POST', 'PRIVMSG' => 'P', 'PRIVS' => 'PRIVS', 'PROTO' => 'PROTO', 'QUIT' => 'Q', 'REHASH' => 'REHASH', 'RESET' => 'RESET', 'RESTART' => 'RESTART', 'RPING' => 'RI', 'RPONG' => 'RO', 'SERVER' => 'S', 'SET' => 'SET', 'SETTIME' => 'SE', 'SILENCE' => 'U', 'SQUIT' => 'SQ', 'STATS' => 'R', 'TIME' => 'TI', 'TOPIC' => 'T', 'TRACE' => 'TR', 'UPING' => 'UP', 'USER' => 'USER', 'USERHOST' => 'USERHOST', 'USERIP' => 'USERIP', 'VERSION' => 'V', 'WALLCHOPS' => 'WC', 'WALLOPS' => 'WA', 'WALLUSERS' => 'WU', 'WALLVOICES' => 'WV', 'WHO' => 'H', 'WHOIS' => 'W', 'WHOWAS' => 'X');
25              
26 1     1   6 use constant PCI_REFCOUNT_TAG => "P::C::I registered";
  1         3  
  1         68  
27              
28 1     1   8 use constant CMD_PRI => 0;
  1         2  
  1         37  
29 1     1   6 use constant CMD_SUB => 1;
  1         2  
  1         9733  
30              
31             my %irc_commands =
32             ('quit' => \&oneoptarg_client,
33             'nick' => \&onlyonearg_client,
34             'invite' => \&onlytwoargs_client,
35             'kill' => \&onlytwoargs,
36             'account' => \&onlytwoargs,
37             'clearmode' => \&onlytwoargs,
38             'opmode' => \&spacesep,
39             'gline' => \&spacesep,
40             'jupe' => \&spacesep,
41             'privmsg' => \&privandnotice,
42             'notice' => \&privandnotice,
43             'stats' => \&spacesep_client,
44             'links' => \&spacesep_client,
45             'mode' => \&spacesep_client,
46             'part' => \&commasep_client,
47             'ctcp' => \&ctcp,
48             'ctcpreply' => \&ctcp,
49             );
50              
51             # Create a new IRC Service
52              
53             sub new {
54 1     1 1 5 my ($package,$alias,$hash) = splice @_, 0, 3;
55 1         2 my ($package_events);
56              
57 1 50 33     28 unless ($alias and $hash) {
58 0         0 croak "Not enough parameters to POE::Component::IRC::Service::P10::new()";
59             }
60              
61 1 50       6 unless (ref $hash eq 'HASH') {
62 0         0 croak "Second argument to POE::Component::IRC::Service::P10::new() must be a hash reference";
63             }
64            
65 1 50 33     7 $hash->{EventMode} = 1 unless ( defined ( $hash->{EventMode} ) and $hash->{EventMode} == 0 );
66              
67 1 50 33     7 $hash->{Reconnect} = 0 unless ( defined ( $hash->{Reconnect} ) and $hash->{Reconnect} == 1 );
68              
69 1 50 33     8 $hash->{Debug} = 0 unless ( defined ( $hash->{Debug} ) and $hash->{Debug} == 1 );
70              
71 1 50       5 if ( $hash->{EventMode} == 1 ) {
72 1         14 $package_events = [qw( _start
73             _stop
74             _parseline
75             _sock_up
76             _sock_down
77             _sock_failed
78             autoping
79             addnick
80             connect
81             topic
82             irc_p10_disconnected
83             irc_p10_socketerr
84             irc_p10_stats
85             irc_p10_version
86             irc_p10_server_link
87             irc_p10_server
88             irc_p10_squit
89             irc_p10_burst
90             irc_p10_end_of_burst
91             irc_p10_eob_ack
92             irc_p10_ping
93             irc_p10_quit
94             irc_p10_kill
95             irc_p10_nick
96             irc_p10_whois
97             irc_p10_account
98             irc_p10_create
99             irc_p10_join
100             irc_p10_part
101             irc_p10_kick
102             irc_p10_mode
103             irc_p10_opmode
104             irc_p10_clearmode
105             kick
106             join
107             register
108             sl_server
109             sl_client
110             shutdown
111             squit
112             unregister)];
113             } else {
114 0         0 $package_events = [qw( _start
115             _stop
116             _parseline
117             _sock_up
118             _sock_down
119             _sock_failed
120             autoping
121             addnick
122             connect
123             topic
124             irc_p10_disconnected
125             irc_p10_socketerr
126             irc_p10_stats
127             irc_p10_version
128             irc_p10_server_link
129             irc_p10_server
130             irc_p10_squit
131             irc_p10_end_of_burst
132             irc_p10_eob_ack
133             irc_p10_ping
134             irc_p10_quit
135             irc_p10_kill
136             irc_p10_nick
137             irc_p10_whois
138             irc_p10_account
139             irc_p10_mode
140             kick
141             join
142             register
143             sl_server
144             sl_client
145             shutdown
146             squit
147             unregister)];
148             }
149              
150             # Create our object
151 1         4 my ($self) = { };
152 1         2 bless ($self);
153              
154             # Parse the passed hash reference
155 1 50 33     12 unless ($hash->{'ServerNumeric'} and $hash->{'ServerName'} and $hash->{'RemoteServer'} and $hash->{'Password'} and $hash->{'ServerPort'}) {
      33        
      33        
      33        
156 0         0 croak "You must specify ServerNumeric, ServerName, RemoteServer, Password and ServerPort in your hash reference.";
157             }
158              
159             # Is numeric in proper format ie. P10 Base64 if not convert it
160 1 50       7 if ($hash->{'ServerNumeric'} =~ /^[0-9]+?$/) {
161 1 50 33     10 if ($hash->{'ServerNumeric'} < 0 or $hash->{'ServerNumeric'} > 4095) {
162 0         0 die "ServerNumeric must be either 0-4095 or a valid P10 Base64 numeric\n";
163             } else {
164 1         6 $hash->{'ServerNumeric'} = dectobase64($hash->{'ServerNumeric'});
165             }
166             }
167              
168 1 50       8 if ($hash->{'ServerNumeric'} !~ /^[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\[\]]{2}$/) {
169 0         0 die "ServerNumeric must be either 0-4095 or a valid P10 Base64 numeric\n";
170             }
171              
172 1 50       4 $hash->{ServerDesc} = "*** POE::Component::IRC::Service ***" unless defined ($hash->{ServerDesc});
173 1 50       11 $hash->{Version} = "POE-Component-IRC-Service-P10-$VERSION" unless defined ($hash->{Version});
174              
175 1         10 my @event_map = map {($_, $irc_commands{$_})} keys %irc_commands;
  17         35  
176              
177 1 50       6 $hash->{'PingFreq'} = 90 unless ( defined ( $hash->{'PingFreq'} ) );
178              
179             POE::Session->create( inline_states => { @event_map },
180             package_states => [
181             $package => $package_events, ],
182             args => [ $alias, @_ ],
183             heap => { State => $self,
184             servernum => $hash->{'ServerNumeric'},
185             servername => $hash->{'ServerName'},
186             serverdesc => $hash->{'ServerDesc'},
187             remoteserver => $hash->{'RemoteServer'},
188             serverport => $hash->{'ServerPort'},
189             password => $hash->{'Password'},
190             localaddr => $hash->{'LocalAddr'},
191             pingfreq => $hash->{'PingFreq'},
192             eventmode => $hash->{'EventMode'},
193             reconnect => $hash->{'Reconnect'},
194             debug => $hash->{'Debug'},
195             his_servername => $hash->{'HIS_SERVERNAME'},
196             his_serverinfo => $hash->{'HIS_SERVERINFO'},
197 1         33 version => $hash->{'Version'}, },
198             );
199 1         156 return $self;
200             }
201              
202             # Register and unregister to receive events
203              
204             sub register {
205 2     2 1 1042 my ($kernel, $heap, $session, $sender, @events) =
206             @_[KERNEL, HEAP, SESSION, SENDER, ARG0 .. $#_];
207              
208 2 50       8 die "Not enough arguments" unless @events;
209              
210             # FIXME: What "special" event names go here? (ie, "errors")
211             # basic, dcc (implies ctcp), ctcp, oper ...what other categories?
212 2         5 foreach (@events) {
213 24 50       65 $_ = "irc_p10_" . $_ unless /^_/;
214 24         57 $heap->{events}->{$_}->{$sender} = $sender;
215 24         47 $heap->{sessions}->{$sender}->{'ref'} = $sender;
216 24 100 100     73 unless ($heap->{sessions}->{$sender}->{refcnt}++ or $session == $sender) {
217 1         4 $kernel->refcount_increment($sender->ID(), PCI_REFCOUNT_TAG);
218             }
219             }
220             }
221              
222             sub unregister {
223 1     1 1 710 my ($kernel, $heap, $session, $sender, @events) =
224             @_[KERNEL, HEAP, SESSION, SENDER, ARG0 .. $#_];
225              
226 1 50       4 die "Not enough arguments" unless @events;
227              
228 1         4 foreach (@events) {
229 1         3 delete $heap->{events}->{$_}->{$sender};
230 1 50       8 if (--$heap->{sessions}->{$sender}->{refcnt} <= 0) {
231 1         4 delete $heap->{sessions}->{$sender};
232 1 50       4 unless ($session == $sender) {
233 1         3 $kernel->refcount_decrement($sender->ID(), PCI_REFCOUNT_TAG);
234             }
235             }
236             }
237             }
238              
239             # Session starts or stops
240              
241             sub _start {
242 1     1   1018 my ($kernel, $session, $heap, $alias) = @_[KERNEL, SESSION, HEAP, ARG0];
243 1         4 my @options = @_[ARG1 .. $#_];
244              
245 1 50       3 $session->option( @options ) if @options;
246 1         7 $kernel->alias_set($alias);
247 1         54 $kernel->yield( 'register', qw(stats server_link server squit burst end_of_burst eob_ack ping quit kill nick whois account create join part kick mode opmode clearmode version disconnected socketerr) );
248 1         114 $heap->{irc_filter} = POE::Filter::IRC::P10->new();
249 1         9 $heap->{ctcp_filter} = POE::Filter::CTCP::P10->new();
250 1 50       4 $heap->{irc_filter}->debug(1) if ( $heap->{debug} );
251 1         2 $heap->{nextnick} = 0;
252 1         29 $heap->{connected} = 0;
253 1         3 $heap->{serverlink} = "";
254 1         8 $heap->{starttime} = time();
255             }
256              
257             sub _stop {
258 1     1   106 my ($kernel, $heap, $quitmsg) = @_[KERNEL, HEAP, ARG0];
259              
260 1 50       5 if ($heap->{connected}) {
261 0         0 $kernel->call( $_[SESSION], 'shutdown', $quitmsg );
262             }
263             }
264              
265             # Disconnect the IRC Service from IRC Network
266              
267             sub squit {
268 0     0 1 0 my ($kernel, $heap) = @_[KERNEL,HEAP];
269              
270             # Don't give a f**k about any parameters passed
271              
272 0 0       0 if ( $heap->{'socket'} ) {
273 0         0 delete ( $heap->{'socket'} );
274             }
275             # $kernel->call( $_[SESSION], 'shutdown' );
276             }
277              
278             # Connect to IRC Network
279              
280             sub connect {
281 0     0 1 0 my ($kernel, $heap, $session) = @_[KERNEL, HEAP, SESSION];
282              
283 0 0       0 if ($heap->{'socket'}) {
284 0         0 $kernel->call ($session, 'squit');
285             }
286              
287             $heap->{socketfactory} = POE::Wheel::SocketFactory->new(
288             SocketDomain => AF_INET,
289             SocketType => SOCK_STREAM,
290             SocketProtocol => 'tcp',
291             RemoteAddress => $heap->{'remoteserver'},
292             RemotePort => $heap->{'serverport'},
293             SuccessEvent => '_sock_up',
294             FailureEvent => '_sock_failed',
295 0 0       0 ( $heap->{localaddr} ? (BindAddress => $heap->{localaddr}) : () ),
296             );
297              
298             }
299              
300             # Internal function called when a socket is closed.
301             sub _sock_down {
302 0     0   0 my ($kernel, $heap) = @_[KERNEL, HEAP];
303              
304             # Destroy the RW wheel for the socket.
305 0         0 delete $heap->{'socket'};
306 0         0 $heap->{connected} = 0;
307              
308             # post a 'irc_disconnected' to each session that cares
309 0         0 foreach (keys %{$heap->{sessions}}) {
  0         0  
310             $kernel->post( $heap->{sessions}->{$_}->{'ref'},
311 0         0 'irc_p10_disconnected', $heap->{server} );
312             }
313             }
314              
315             sub _sock_up {
316 0     0   0 my ($kernel,$heap,$session,$socket) = @_[KERNEL,HEAP,SESSION,ARG0];
317 0         0 $heap->{connecttime} = time();
318 0         0 $heap->{State}->_burst_create();
319              
320 0         0 delete $heap->{socketfactory};
321              
322 0         0 $heap->{localaddr} = (unpack_sockaddr_in( getsockname $socket))[1];
323              
324 0         0 $heap->{'socket'} = new POE::Wheel::ReadWrite
325             (
326             Handle => $socket,
327             Driver => POE::Driver::SysRW->new(),
328             Filter => POE::Filter::Line->new(),
329             InputEvent => '_parseline',
330             ErrorEvent => '_sock_down',
331             );
332              
333 0 0       0 if ($heap->{'socket'}) {
334 0         0 $heap->{connected} = 1;
335             } else {
336 0         0 _send_event ( $kernel, $heap, 'irc_p10_socketerr', "Couldn't create ReadWrite wheel for IRC socket" );
337             }
338              
339 0         0 foreach (keys %{$heap->{sessions}}) {
  0         0  
340 0         0 $kernel->post( $heap->{sessions}->{$_}->{'ref'}, 'irc_p10_connected', $heap->{remoteserver}, $heap->{servernum} );
341             }
342              
343 0         0 $heap->{socket}->put("PASS $heap->{password}\n");
344 0         0 $heap->{socket}->put("SERVER $heap->{servername} 1 $heap->{starttime} $heap->{connecttime} J10 $heap->{servernum}A]] 0 :$heap->{serverdesc}\n");
345             # Sit back and wait for the uplink to finish its burst
346             }
347              
348             sub _sock_failed {
349 0     0   0 my ($kernel, $heap, $op, $errno, $errstr) = @_[KERNEL, HEAP, ARG0..ARG2];
350              
351 0         0 _send_event( $kernel, $heap, 'irc_p10_socketerr', "$op error $errno: $errstr" );
352             }
353              
354             # Parse each line from received at the socket
355              
356             # Parse a message from the IRC server and generate the appropriate
357             # event(s) for listening sessions.
358             sub _parseline {
359 2     2   389 my ($kernel, $session, $heap, $line) = @_[KERNEL, SESSION, HEAP, ARG0];
360 2         5 my (@events, @cooked);
361              
362             # Feed the proper Filter object the raw IRC text and get the
363             # "cooked" events back for sending, then deliver each event. We
364             # handle CTCPs separately from normal IRC messages here, to avoid
365             # silly module dependencies later.
366              
367 0         0 @cooked = ($line =~ tr/\001// ? @{$heap->{ctcp_filter}->get( [$line] )}
368 2 50       6 : @{$heap->{irc_filter}->get( [$line] )} );
  2         10  
369              
370 2         7 foreach my $ev (@cooked) {
371 2         7 $ev->{name} = 'irc_p10_' . $ev->{name};
372 2         4 _send_event( $kernel, $heap, $session, $ev->{name}, @{$ev->{args}} );
  2         8  
373             }
374             }
375              
376              
377             # Sends an event to all interested sessions. This is a separate sub
378             # because I do it so much, but it's not an actual POE event because it
379             # doesn't need to be one and I don't need the overhead.
380             # BinGOs: Added a hack to try and improve performance for IRC Service
381             # use a CALL instead of a POST if the session ID is our own
382             sub _send_event {
383 2     2   6 my ($kernel, $heap, $session, $event, @args) = @_;
384 2         4 my %sessions;
385              
386 2         3 foreach (values %{$heap->{events}->{'irc_p10_all'}},
  2         5  
387 2         7 values %{$heap->{events}->{$event}}) {
388 4         10 $sessions{$_} = $_;
389             }
390             # Make sure our session gets notified of any requested events before any other bugger
391 2 50       13 $kernel->call( $session, $event, @args ) if ( defined ($sessions{$session}) );
392 2         18 foreach (values %sessions) {
393 4 100       196 $kernel->post( $_, $event, @args ) unless ( $_ eq $session );
394             }
395             }
396              
397             sub addnick {
398 1     1 1 141 my ($kernel, $heap, $session, $args) = @_[KERNEL, HEAP, SESSION, ARG0];
399 1         3 my $connecttime = time();
400              
401 1 50       4 if ($args) {
402 1         2 my %arg;
403 1 50       5 if (ref $args eq 'HASH') {
404 1         5 %arg = %$args;
405             } else {
406 0         0 die "First argument to addnick() should be a hash reference";
407             }
408              
409             # Gentlemen, lets get down to business
410             # Mandatory fields we must must must have these, damnit
411 1 50       6 my $nickname = $arg{'NickName'} if exists $arg{'NickName'};
412 1 50       4 my $username = $arg{'UserName'} if exists $arg{'UserName'};
413 1 50       4 my $hostname = $arg{'HostName'} if exists $arg{'HostName'};
414 1 50       3 my $authname = $arg{'AuthName'} if exists $arg{'AuthName'};
415 1 50       4 my $umode = $arg{'Umode'} if exists $arg{'Umode'};
416 1 50       7 my $description = $arg{'Description'} if exists $arg{'Description'};
417 1 50       5 my $localaddr = $arg{'LocalAddr'} if exists $arg{'LocalAddr'};
418              
419 1 50       3 unless (defined $nickname) {
420 0         0 die "You must specify at least a NickName to addnick";
421             }
422              
423             # Check if NickName already exists. Issue a KILL if it does and call addnick again with a delay
424 1 50       6 if (not $heap->{State}->nick_numeric($nickname) ) {
425              
426 1         4 my $numeric = dectobase64($heap->{nextnick},3);
427 1         3 $heap->{nextnick}++;
428              
429             # Default everything else
430              
431 1         4 my $cmd = "N $nickname 1 $connecttime ";
432 1 50       7 $cmd .= lc $nickname . " " unless (defined $username);
433 1 50       7 $cmd .= "$username " if (defined $username);
434 1 50       6 $cmd .= "$heap->{servername} " unless (defined $hostname);
435 1 50       6 $cmd .= "$hostname " if (defined $hostname);
436 1 50       4 $umode = "+odk" unless (defined $umode);
437 1 50 33     7 $umode = "+" . $umode unless ($umode =~ /^\+/ or not defined $umode);
438 1 50 33     6 $umode .= "r" unless (not defined $authname or $umode =~ /r/);
439 1 50 33     9 $authname = $nickname unless (defined $authname or $umode !~ /r/);
440 1 50 33     10 $umode .= " $authname" unless (not defined $authname or $umode !~ /r/);
441 1 50       5 $description = $heap->{serverdesc} unless (defined $description);
442 1 50       4 $localaddr = "127.0.0.1" unless (defined $localaddr);
443              
444 1         3 $localaddr = inttobase64($localaddr);
445              
446 1         6 $cmd .= "$umode $localaddr $heap->{servernum}$numeric :$description";
447              
448 1         5 $kernel->yield ( 'sl_server', $cmd );
449              
450             } else {
451             # Issue the KILL ... bye bye sucker!
452 0         0 my ($target) = $heap->{State}->nick_numeric($nickname);
453 0         0 $kernel->yield ( 'sl_server', $cmd2token{'KILL'} . " $target :" . $heap->{servername} . " (Using a Services nickname)" );
454 0         0 $kernel->delay ( 'addnick' => 5 => \%arg );
455             }
456              
457             } else {
458 0         0 die "First argument to addnick() should be a hash or array reference";
459             }
460              
461             }
462              
463             # Deal with STATS requests
464              
465             sub irc_p10_stats {
466 0     0 0 0 my ($kernel,$heap,$who,$what) = @_[KERNEL,HEAP,ARG0,ARG1];
467              
468 0         0 my ($first,undef) = split(/ :/,$what,2);
469             SWITCH: {
470 0 0       0 if ($first eq "u") {
  0         0  
471 0         0 my ($string) = timestring($heap->{starttime});
472 0         0 my ($clients) = $heap->{State}->server_clients($heap->{servernum});
473 0         0 $kernel->yield( 'sl_server', "242 $who :$string" );
474 0         0 $kernel->yield( 'sl_server', "250 $who :Highest connection count: " . ( $clients + 1 ) . " ($clients clients)" );
475 0         0 $kernel->yield( 'sl_server', "219 $who $first :End of /STATS report" );
476 0         0 last SWITCH;
477             }
478             }
479             }
480              
481             # Remove client if we get a quit for it
482              
483             sub irc_p10_quit {
484 0     0 0 0 my ($heap, $who) = @_[HEAP,ARG0];
485              
486 0         0 $heap->{State}->_nick_del($who);
487             }
488              
489             # Or if it is killed
490              
491             sub irc_p10_kill {
492 0     0 0 0 my ($heap, $who, $what) = @_[HEAP,ARG0,ARG1];
493              
494 0         0 $heap->{State}->_nick_del(substr($what,0,5));
495             }
496              
497             # Server related handlers
498             # Who is our uplink ... needed so that we don't respond to every single EOB
499              
500             sub irc_p10_server_link {
501 0     0 0 0 my ($heap, @args) = @_[HEAP,ARG0 .. $#_];
502              
503 0         0 $heap->{serverlink} = substr($args[5],0,2);
504 0         0 $heap->{State}->{serverlink} = $heap->{serverlink};
505 0         0 $heap->{State}->_server_add($heap->{serverlink},$args[0],$args[1],$heap->{servernum});
506             }
507              
508             sub irc_p10_server {
509 0     0 0 0 my ($heap, $what, $args) = @_[HEAP,ARG0,ARG1];
510              
511 0         0 my ($first,$second) = split(/ :/,$args);
512 0         0 my (@args) = split(/ /,$first);
513 0         0 $heap->{State}->_server_add(substr($args[5],0,2),$args[0],$args[1],$what);
514             }
515              
516             sub irc_p10_squit {
517 0     0 0 0 my ($heap, $what, $args) = @_[HEAP,ARG0,ARG1];
518              
519 0         0 my ($first,$second) = split(/ :/,$args);
520 0         0 my (@args) = split(/ /,$first);
521 0         0 my ($who) = $heap->{State}->server_numeric($args[0]);
522 0 0       0 if ($who eq $heap->{servernum}) {
523 0         0 $_[KERNEL]->delay( 'autoping' => undef );
524 0         0 $who = $heap->{serverlink};
525             }
526 0         0 $heap->{State}->_server_del($who);
527             }
528              
529             sub irc_p10_disconnected {
530 0     0 0 0 my ($kernel,$heap) = @_[KERNEL,HEAP];
531              
532 0 0       0 if ( $heap->{Reconnect} ) {
533 0         0 $kernel->yield( 'connect' );
534             }
535             }
536              
537             sub irc_p10_socketerr {
538 0     0 0 0 my ($kernel,$heap) = @_[KERNEL,HEAP];
539              
540 0 0       0 if ( $heap->{Reconnect} ) {
541 0         0 $kernel->yield( 'connect' );
542             }
543             }
544              
545             sub irc_p10_version {
546 0     0 0 0 my ($kernel, $heap, $who) = @_[KERNEL,HEAP,ARG0];
547              
548 0         0 $kernel->yield( 'sl_server', "351 $who $heap->{version}. $heap->{servername} :" );
549             }
550              
551             # Generate an automatic pong in response to IRC Server's ping
552              
553             sub irc_p10_ping {
554 0     0 0 0 my ($kernel, $heap, $arg) = @_[KERNEL, HEAP, ARG0];
555              
556 0 0       0 $kernel->yield( 'sl_server', "$cmd2token{PONG} $arg") unless ( $arg eq $heap->{servernum} );
557             }
558              
559             # Generate an automatic end_of_burst_ack in response to IRC Server's end_of_burst. It's only polite of course :)
560              
561             sub irc_p10_end_of_burst {
562 0     0 0 0 my ($kernel, $heap, $arg) = @_[KERNEL, HEAP, ARG0];
563              
564 0 0       0 if ($arg eq $heap->{serverlink}) {
565 0         0 $kernel->yield( 'sl_server', "$cmd2token{EOB_ACK}");
566             # Now we burst our shit to the uplink
567 0         0 foreach ($heap->{State}->_burst_info()) {
568 0         0 $kernel->yield( 'sl_server', $_ );
569             }
570 0         0 $kernel->yield( 'sl_server', "$cmd2token{END_OF_BURST}" );
571 0         0 $heap->{State}->_burst_destroy();
572             }
573             }
574              
575             sub irc_p10_eob_ack {
576 0     0 0 0 my ($kernel,$heap) = @_[KERNEL,HEAP];
577              
578             # Lazy, but we can assume that it is our uplink that ACK'ed
579             # Start PINGing uplink server
580              
581 0         0 $kernel->yield( 'autoping' );
582             }
583              
584             sub autoping {
585 0     0 0 0 my ($kernel,$heap) = @_[KERNEL,HEAP];
586              
587 0 0       0 if ( $heap->{'socket'} ) {
588 0         0 my ($uplink) = $heap->{State}->server_name($heap->{serverlink});
589 0         0 my ($seconds,$micro) = gettimeofday();
590              
591 0         0 $kernel->yield( 'sl_server', "G !$seconds.$micro $uplink $seconds.$micro" );
592 0         0 $kernel->delay( 'autoping' => $heap->{pingfreq} );
593             }
594             }
595              
596             sub irc_p10_whois {
597 0     0 0 0 my ($kernel,$heap,$who,$what) = @_[KERNEL,HEAP,ARG0,ARG1];
598 0         0 my ($target) = ( split /:/,$what )[1];
599              
600 0         0 my ($numeric) = $heap->{State}->nick_numeric($target);
601 0         0 my (@info) = $heap->{State}->nick_info($numeric);
602              
603 0         0 $kernel->yield( 'sl_server' => "311 $who $info[0] " . $info[1] . " $info[2] * :$info[3]" );
604 0 0 0     0 if ( defined ( $heap->{his_servername} ) and defined ( $heap->{his_serverinfo} ) and not $heap->{State}->is_operator($who) ) {
      0        
605 0         0 $kernel->yield( 'sl_server' => "312 $who $info[0] $heap->{his_servername} :$heap->{his_serverinfo}" );
606             } else {
607 0         0 $kernel->yield( 'sl_server' => "312 $who $info[0] $heap->{servername} :$heap->{serverdesc}" );
608             }
609 0 0       0 $kernel->yield( 'sl_server' => "313 $who $info[0] :is an IRC Operator" ) if ( $info[4] =~ /o/ );
610 0 0       0 $kernel->yield( 'sl_server' => "330 $who $info[0] $info[5] :is authed as" ) if ( defined ($info[5]) );
611 0         0 $kernel->yield( 'sl_server' => "317 $who $info[0] 0 $heap->{starttime} :seconds idle, signon time" );
612 0         0 $kernel->yield( 'sl_server' => "318 $who $info[0] :End of /WHOIS list." );
613             }
614              
615             # Track New Nicks and Nick Changes
616              
617             sub irc_p10_nick {
618 1     1 0 51 my ($kernel, $heap, $who) = @_[KERNEL,HEAP,ARG0];
619              
620             # New nick on the network or a burst
621 1 50       5 if ($who =~ /^.{2}$/) {
622 1         3 my ($oper,$authname);
623 1         4 my ($first,$second) = split(/ :/,$_[ARG1],2);
624 1         12 my (@args) = split(/ /,$first);
625 1 50       6 if ($args[5] =~ /^\+([a-zA-Z]+)/) {
626 1         3 $oper = $1;
627 1 50       5 if ($args[5] =~ /r/) {
628 1         2 $authname = $args[6];
629             }
630             }
631 1         8 $heap->{State}->_nick_add($args[$#args],$args[0],$args[3],$args[4],$args[$#args-1],$args[2],$oper,$authname,$second);
632              
633             # Or a user changed their nick
634             } else {
635 0         0 my ($first,undef) = split(/ /,$_[ARG1]);
636 0         0 $heap->{State}->_nick_change($who,$first);
637             }
638              
639             }
640              
641             sub irc_p10_account {
642 0     0 0 0 my ($kernel, $heap, $what) = @_[KERNEL,HEAP,ARG1];
643              
644 0         0 my ($who,$account) = split(/ /,$what);
645             # Strip any leading colons
646 0         0 $account =~ s/^://;
647 0         0 $heap->{State}->_nick_account($who,$account);
648             }
649              
650             sub irc_p10_burst {
651 0     0 0 0 my ($kernel,$heap,$who,$what) = @_[KERNEL,HEAP,ARG0,ARG1];
652              
653 0         0 $heap->{State}->_channel_burst($who,$what);
654             }
655              
656             sub irc_p10_create {
657 1     1 0 43 my ($kernel,$heap,$who,$what) = @_[KERNEL,HEAP,ARG0,ARG1];
658              
659 1         5 my ($channel,$timestamp) = split(/ /,$what);
660 1         5 $heap->{State}->_channel_join($channel,$who,$timestamp);
661             }
662              
663             sub irc_p10_join {
664 0     0 0 0 my ($kernel,$heap,$who,$what) = @_[KERNEL,HEAP,ARG0,ARG1];
665              
666 0         0 my ($channel,$timestamp) = split(/ /,$what);
667 0         0 $heap->{State}->_channel_join($channel,$who,$timestamp);
668             }
669              
670             sub irc_p10_part {
671 0     0 0 0 my ($kernel,$heap,$who,$what) = @_[KERNEL,HEAP,ARG0,ARG1];
672              
673 0         0 my ($channel,undef) = split(/ :/,$what);
674 0         0 $heap->{State}->_channel_part($channel,$who);
675             }
676              
677             sub irc_p10_kick {
678 0     0 0 0 my ($kernel,$heap,$who,$channel,$victim) = @_[KERNEL,HEAP,ARG0,ARG1,ARG2];
679              
680 0         0 $heap->{State}->_channel_part($channel,$victim);
681             }
682              
683             sub irc_p10_mode {
684 0     0 0 0 my ($kernel,$heap,$who,$what,$mode) = @_[KERNEL,HEAP,ARG0,ARG1,ARG2];
685              
686             # Assume that if it isnt a channel its a umode change
687 0 0       0 if ($what !~ /^#/) {
688 0         0 $heap->{State}->_nick_umode($who,$mode);
689             } else {
690             # Okay its a channel mode
691 0 0       0 if ( $heap->{eventmode} ) {
692 0         0 my ($args) = join(" ",@_[ARG3..$#_]);
693 0 0       0 $mode .= " " . $args if ( defined ($args) );
694 0         0 $heap->{State}->_channel_mode($what,$mode,$who);
695             }
696             }
697             }
698              
699             sub irc_p10_opmode {
700 0     0 0 0 my ($kernel,$heap,$who,$what,$mode) = @_[KERNEL,HEAP,ARG0,ARG1,ARG2];
701              
702             # Assume that if it isnt a channel its a umode change
703 0 0       0 if ($what !~ /^#/) {
704 0         0 $heap->{State}->_nick_umode($who,$mode);
705             } else {
706             # Okay its a channel mode
707 0         0 my ($args) = join(" ",@_[ARG3..$#_]);
708 0 0       0 $mode .= " " . $args if ( defined ($args) );
709 0         0 $heap->{State}->_channel_mode($what,$mode,$who);
710             }
711             }
712              
713             sub irc_p10_clearmode {
714 0     0 0 0 my ($kernel,$heap,$who,$what) = @_[KERNEL,HEAP,ARG0,ARG1];
715              
716 0         0 my ($channel,$flags) = split(/ /,$what);
717 0         0 $heap->{State}->_channel_clearmode($channel,$flags);
718             }
719              
720             # Our event handlers for events sent to us
721              
722             # The handler for commands which have N arguments, separated by commas.
723             sub commasep {
724 0     0 0 0 my ($kernel, $state) = @_[KERNEL, STATE];
725 0         0 my $args = join ',', @_[ARG0 .. $#_];
726              
727 0         0 $state = $cmd2token{uc( $state )};
728 0 0       0 $state .= " $args" if defined $args;
729 0         0 $kernel->yield( 'sl_server', $state );
730             }
731              
732             # The handler for commands which have N arguments, separated by commas. Client hacked.
733             sub commasep_client {
734 0     0 0 0 my ($kernel, $state, $numeric) = @_[KERNEL, STATE, ARG0];
735 0         0 my $args = join ',', @_[ARG1 .. $#_];
736              
737 0         0 $state = $cmd2token{uc( $state )};
738 0 0       0 $state .= " $args" if defined $args;
739 0         0 $kernel->yield( 'sl_client', "$numeric $state" );
740             }
741              
742             # Send a CTCP query or reply, with the same syntax as a PRIVMSG event.
743             sub ctcp {
744 0     0 1 0 my ($kernel, $state, $heap, $numeric, $to) = @_[KERNEL, STATE, HEAP, ARG0, ARG1];
745 0         0 my $message = join ' ', @_[ARG2 .. $#_];
746              
747 0 0 0     0 unless (defined $numeric and defined $to and defined $message) {
      0        
748 0         0 die "The POE::Component::IRC event \"$state\" requires three arguments";
749             }
750              
751             # CTCP-quote the message text.
752 0         0 ($message) = @{$heap->{ctcp_filter}->put([ $message ])};
  0         0  
753              
754             # Should we send this as a CTCP request or reply?
755 0 0       0 $state = $state eq 'ctcpreply' ? 'notice' : 'privmsg';
756              
757 0         0 $kernel->yield( $state, $numeric, $to, $message );
758             }
759              
760             # Tell the IRC server to forcibly remove a user from a channel.
761             sub kick {
762 0     0 0 0 my ($kernel, $numeric, $chan, $nick) = @_[KERNEL, ARG0, ARG1, ARG2];
763 0         0 my $message = join '', @_[ARG3 .. $#_];
764              
765 0 0 0     0 unless (defined $numeric and defined $chan and defined $nick) {
      0        
766 0         0 die "The POE::Component::IRC event \"kick\" requires at least three arguments";
767             }
768              
769 0 0       0 $nick .= " :$message" if defined $message;
770 0         0 $kernel->yield('sl_client', "$numeric K $chan $nick" );
771             }
772              
773             # The handler for all IRC commands that take no arguments.
774             sub noargs {
775 0     0 0 0 my ($kernel, $state, $arg) = @_[KERNEL, STATE, ARG0];
776              
777 0 0       0 if (defined $arg) {
778 0         0 die "The POE::Component::IRC event \"$state\" takes no arguments";
779             }
780 0         0 $kernel->yield( 'sl_server', $cmd2token{uc( $state )} );
781             }
782              
783             # The handler for all IRC commands that take no arguments. Client hacked.
784             sub noargs_client {
785 0     0 0 0 my ($kernel, $state, $numeric, $arg) = @_[KERNEL, STATE, ARG0, ARG1];
786              
787 0 0       0 unless (defined $numeric) {
788 0         0 die "The POE::Component::IRC event \"$state\" requires at least one argument";
789             }
790              
791 0 0       0 if (defined $arg) {
792 0         0 die "The POE::Component::IRC event \"$state\" takes no arguments";
793             }
794 0         0 $kernel->yield( 'sl_client', "$numeric " . $cmd2token{uc( $state )} );
795             }
796              
797             # The handler for commands that take one required and two optional arguments.
798             sub oneandtwoopt {
799 0     0 0 0 my ($kernel, $state) = @_[KERNEL, STATE];
800 0         0 my $arg = join '', @_[ARG0 .. $#_];
801              
802 0         0 $state = $cmd2token{uc( $state )};
803 0 0       0 if (defined $arg) {
804 0 0       0 $arg = ':' . $arg if $arg =~ /\s/;
805 0         0 $state .= " $arg";
806             }
807 0         0 $kernel->yield( 'sl_server', $state );
808             }
809              
810             # The handler for commands that take one required and two optional arguments. Client hacked.
811             sub oneandtwoopt_client {
812 0     0 0 0 my ($kernel, $state, $numeric) = @_[KERNEL, STATE, ARG0];
813 0         0 my $arg = join '', @_[ARG1 .. $#_];
814              
815 0 0       0 unless (defined $numeric) {
816 0         0 die "The POE::Component::IRC event \"$state\" requires at least one argument";
817             }
818              
819 0         0 $state = $cmd2token{uc( $state )};
820 0 0       0 if (defined $arg) {
821 0 0       0 $arg = ':' . $arg if $arg =~ /\s/;
822 0         0 $state .= " $arg";
823             }
824 0         0 $kernel->yield( 'sl_client', "$numeric $state" );
825             }
826              
827             # The handler for commands that take at least one optional argument.
828             sub oneoptarg {
829 0     0 0 0 my ($kernel, $state) = @_[KERNEL, STATE];
830 0 0       0 my $arg = join '', @_[ARG0 .. $#_] if defined $_[ARG0];
831              
832 0         0 $state = $cmd2token{uc( $state )};
833 0 0       0 if (defined $arg) {
834 0 0       0 $arg = ':' . $arg if $arg =~ /\s/;
835 0         0 $state .= " $arg";
836             }
837 0         0 $kernel->yield( 'sl_server', $state );
838             }
839              
840             # The handler for commands that take at least one optional argument. Client hacked.
841             sub oneoptarg_client {
842 0     0 0 0 my ($kernel, $state, $numeric) = @_[KERNEL, STATE, ARG0];
843 0 0       0 my $arg = join '', @_[ARG1 .. $#_] if defined $_[ARG1];
844              
845 0 0       0 unless (defined $numeric) {
846 0         0 die "The POE::Component::IRC event \"$state\" requires at least one argument";
847             }
848              
849 0         0 $state = $cmd2token{uc( $state )};
850 0 0       0 if (defined $arg) {
851 0 0       0 $arg = ':' . $arg if $arg =~ /\s/;
852 0         0 $state .= " $arg";
853             }
854 0         0 $kernel->yield( 'sl_client', "$numeric $state" );
855             }
856              
857             # The handler for commands which take one required and one optional argument.
858             sub oneortwo {
859 0     0 0 0 my ($kernel, $state, $one) = @_[KERNEL, STATE, ARG0];
860 0         0 my $two = join '', @_[ARG1 .. $#_];
861              
862 0 0       0 unless (defined $one) {
863 0         0 die "The POE::Component::IRC event \"$state\" requires at least one argument";
864             }
865              
866 0         0 $state = $cmd2token{uc( $state )} . " $one";
867 0 0       0 $state .= " $two" if defined $two;
868 0         0 $kernel->yield( 'sl_server', $state );
869             }
870              
871             # The handler for commands which take one required and one optional argument. Client hacked.
872             sub oneortwo_client {
873 0     0 0 0 my ($kernel, $state, $numeric, $one) = @_[KERNEL, STATE, ARG0, ARG1];
874 0         0 my $two = join '', @_[ARG2 .. $#_];
875              
876 0 0 0     0 unless (defined $numeric and defined $one) {
877 0         0 die "The POE::Component::IRC event \"$state\" requires at least two argument";
878             }
879              
880 0         0 $state = $cmd2token{uc( $state )} . " $one";
881 0 0       0 $state .= " $two" if defined $two;
882 0         0 $kernel->yield( 'sl_client', "$numeric $state" );
883             }
884              
885             # Handler for commands that take exactly one argument.
886             sub onlyonearg {
887 0     0 0 0 my ($kernel, $state) = @_[KERNEL, STATE];
888 0         0 my $arg = join '', @_[ARG0 .. $#_];
889              
890 0 0       0 unless (defined $arg) {
891 0         0 die "The POE::Component::IRC event \"$state\" requires one argument";
892             }
893              
894 0         0 $state = $cmd2token{uc( $state )};
895 0 0       0 $arg = ':' . $arg if $arg =~ /\s/;
896 0         0 $state .= " $arg";
897 0         0 $kernel->yield( 'sl_server', $state );
898             }
899              
900             # Handler for commands that take exactly one argument. Client hacked.
901             sub onlyonearg_client {
902 0     0 0 0 my ($kernel, $state, $numeric) = @_[KERNEL, STATE, ARG0];
903 0         0 my $arg = join '', @_[ARG1 .. $#_];
904              
905 0 0 0     0 unless (defined $numeric and defined $arg) {
906 0         0 die "The POE::Component::IRC::Service::P10 event \"$state\" requires two argument";
907             }
908              
909 0         0 $state = $cmd2token{uc( $state )};
910 0 0       0 $arg = ':' . $arg if $arg =~ /\s/;
911 0         0 $state .= " $arg";
912 0         0 $kernel->yield( 'sl_client', "$numeric $state" );
913             }
914              
915             # Handler for commands that take exactly two arguments.
916             sub onlytwoargs {
917 0     0 0 0 my ($heap, $kernel, $state, $one) = @_[HEAP, KERNEL, STATE, ARG0];
918 0         0 my ($two) = join '', @_[ARG1 .. $#_];
919              
920 0 0 0     0 unless (defined $one and defined $two) {
921 0         0 die "The POE::Component::IRC::Service::P10 event \"$state\" requires two arguments";
922             }
923              
924 0         0 $state = $cmd2token{uc( $state )};
925 0 0       0 $two = ':' . $two if $two =~ /\s/;
926 0         0 $kernel->yield( 'sl_server', "$state $one $two" );
927             }
928              
929             # Handler for commands that take exactly two arguments. Client hacked.
930             sub onlytwoargs_client {
931 0     0 0 0 my ($heap, $kernel, $state, $numeric, $one) = @_[HEAP, KERNEL, STATE, ARG0, ARG1];
932 0         0 my ($two) = join '', @_[ARG2 .. $#_];
933              
934 0 0 0     0 unless (defined $numeric and defined $one and defined $two) {
      0        
935 0         0 die "The POE::Component::IRC::Service::P10 event \"$state\" requires three arguments";
936             }
937              
938 0         0 $state = $cmd2token{uc( $state )};
939 0 0       0 $two = ':' . $two if $two =~ /\s/;
940 0         0 $kernel->yield( 'sl_client', "$numeric $state $two" );
941             }
942              
943             # Handler for privmsg or notice events.
944             sub privandnotice {
945 0     0 0 0 my ($kernel, $state, $numeric, $to) = @_[KERNEL, STATE, ARG0, ARG1];
946 0         0 my $message = join ' ', @_[ARG2 .. $#_];
947              
948 0 0 0     0 unless (defined $numeric and defined $to and defined $message) {
      0        
949 0         0 die "The POE::Component::IRC event \"$state\" requires three arguments";
950             }
951              
952 0 0       0 if (ref $to eq 'ARRAY') {
953 0         0 $to = join ',', @$to;
954             }
955              
956 0         0 $state = $cmd2token{uc( $state )};
957 0         0 $state .= " $to :$message";
958 0         0 $kernel->yield( 'sl_client', "$numeric $state" );
959             }
960              
961             # Tell the IRC session to go away.
962             sub shutdown {
963 1     1 1 151 my ($kernel, $heap) = @_[KERNEL, HEAP];
964              
965 1         5 foreach ($kernel->alias_list( $_[SESSION] )) {
966 1         43 $kernel->alias_remove( $_ );
967             }
968              
969 1         49 foreach (qw(socket sock socketfactory dcc wheelmap)) {
970 5         11 delete $heap->{$_};
971             }
972             }
973              
974             # The handler for commands which have N arguments, separated by spaces.
975             sub spacesep {
976 0     0 0 0 my ($kernel, $state) = @_[KERNEL, STATE];
977 0         0 my $args = join ' ', @_[ARG0 .. $#_];
978              
979 0         0 $state = $cmd2token{uc( $state )};
980 0 0       0 $state .= " $args" if defined $args;
981 0         0 $kernel->yield( 'sl_server', $state );
982             }
983              
984             # The handler for commands which have N arguments, separated by spaces. Client hacked.
985             sub spacesep_client {
986 0     0 0 0 my ($kernel, $state, $numeric) = @_[KERNEL, STATE, ARG0];
987 0         0 my $args = join ' ', @_[ARG1 .. $#_];
988              
989 0         0 $state = $cmd2token{uc( $state )};
990 0 0       0 $state .= " $args" if defined $args;
991 0         0 $kernel->yield( 'sl_server', "$numeric $state" );
992             }
993              
994             # Dish out server initiated commands
995              
996             sub sl_server {
997 1     1 1 272 my ($kernel, $heap, $cmd) = @_[KERNEL, HEAP, ARG0];
998              
999             # TODO: need to categorise events so that we don't send crap to the uplink
1000             # eg. if we send events to our clients
1001             # TBH I don't suppose the uplink cares that much :o)
1002              
1003 1 50       6 $heap->{socket}->put("$heap->{servernum} $cmd\n") if ( $heap->{'socket'} );
1004 1         5 $kernel->yield('_parseline',"$heap->{servernum} $cmd");
1005             }
1006              
1007             # Dish out client (whichever is specified) initiated commands
1008              
1009             sub sl_client {
1010 1     1 1 199 my ($kernel, $heap, $cmd) = @_[KERNEL, HEAP, ARG0];
1011              
1012 1 50       4 $heap->{socket}->put("$cmd\n") if ($heap->{'socket'});
1013 1         4 $kernel->yield('_parseline',$cmd);
1014             }
1015              
1016             # Set or query the current topic on a channel.
1017             sub topic {
1018 0     0 0 0 my ($kernel,$heap, $numeric, $chan) = @_[KERNEL,HEAP, ARG0, ARG1];
1019 0         0 my $topic = join '', @_[ARG2 .. $#_];
1020              
1021 0 0       0 $chan .= " :$topic" if length $topic;
1022 0         0 $kernel->yield('sl_client',"$numeric T $chan");
1023             }
1024              
1025             # Base64 P10 Stylee functions
1026              
1027             # Convert decimal to Base64 optionally provide the length of the Base64 returned
1028             sub dectobase64 {
1029 3   100 3 0 14 my ($number) = shift || 0;
1030 3   100     12 my ($output) = shift || 2;
1031 3         7 my ($numeric) = "";
1032              
1033 3 100       10 if ($number == 0) {
1034 1         8 for (my $i = length($numeric); $i < $output; $i++) {
1035 3         9 $numeric = "A" . $numeric;
1036             }
1037 1         4 return $numeric;
1038             }
1039              
1040 2         5 my ($b64chars) = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789[]";
1041 2         21 my (@d2b64) = split(//,$b64chars);
1042              
1043 2         8 my (@convert); my ($g); my ($r);
  2         0  
1044              
1045 2         3 LOOP: while (1) {
1046 6         13 $g = $number / 64;
1047 6         9 $r = $number % 64;
1048 6 100       15 if ($g >= 64) {
1049 4         10 $number = $g;
1050 4         7 push(@convert,$r);
1051             } else {
1052 2         4 push(@convert,$r);
1053 2         6 push(@convert,int $g);
1054 2         6 last LOOP;
1055             }
1056             }
1057 2         6 foreach (reverse @convert) {
1058 8         15 $numeric .= $d2b64[$_];
1059             }
1060 2         8 for (my $i = length($numeric); $i < $output; $i++) {
1061 0         0 $numeric = "A" . $numeric;
1062             }
1063 2         12 return $numeric;
1064             }
1065              
1066             # Convert from Base64 to decimal
1067             sub base64todec {
1068 0   0 0 0 0 my ($numeric) = shift || return undef;
1069              
1070 0         0 my ($b64chars) = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789[]";
1071 0         0 my (@d2b64) = split(//,$b64chars);
1072 0         0 my (%b642d) = ();
1073 0         0 for (my $i = 0; $i <= $#d2b64; $i++) {
1074 0         0 $b642d{$d2b64[$i]} = $i;
1075             }
1076              
1077 0         0 my (@numeric) = reverse split(//,$numeric);
1078 0         0 my ($number) = 0;
1079              
1080 0         0 for (my $i=0; $i <= $#numeric; $i++) {
1081 0         0 $number += (64**$i) * $b642d{$numeric[$i]};
1082             }
1083 0         0 return $number;
1084             }
1085              
1086             # Convoluted method to convert from IP quad to Base64 /me *sighs*
1087             sub inttobase64 {
1088 1   50 1 0 4 my ($quad) = shift || return undef;
1089              
1090 1         7 return dectobase64(hex(int2hex(dotq2int($quad))));
1091             }
1092              
1093             # The following two functions are taken from :-
1094             # http://www.math.ucla.edu/~jimc/jvtun
1095             # Copyright © 2003 by James F. Carter. 2003-08-02, Perl-5.8.0
1096              
1097             sub dotq2int {
1098 1     1 0 7 my @dotq = split /[.\/]/, $_[0];
1099 1 50       4 push(@dotq, 32) if @dotq == 4;
1100 1         13 my($ip) = unpack("N", pack("C4", splice(@dotq, 0, 4)));
1101 1 50       8 my($mask) = (@dotq > 1) ? unpack("N", pack("C4", @dotq)) :
    50          
1102             $dotq[0] ? ~((1 << (32-$dotq[0]))-1) : 0;
1103              
1104 1         7 ($ip, $mask);
1105             }
1106              
1107             sub int2hex {
1108 1     1 0 17 sprintf("%08X", $_[0]);
1109             }
1110              
1111             # Our own little function to return a proper uppercase nickname or channel name IRC stylee
1112             # See the RFC for the details
1113              
1114             sub u_irc {
1115 7   50 7 0 20 my ($value) = shift || return undef;
1116              
1117 7         15 $value =~ tr/a-z{}|/A-Z[]\\/;
1118 7         25 return $value;
1119             }
1120              
1121              
1122             # Return a correctly formatted string for STATS u requests
1123              
1124             sub timestring {
1125 0   0 0 0 0 my ($timeval) = shift || return 0;
1126 0         0 my $uptime = time() - $timeval;
1127            
1128 0         0 my $days = int $uptime / 86400;
1129 0         0 my $remain = $uptime % 86400;
1130 0         0 my $hours = int $remain / 3600;
1131 0         0 $remain %= 3600;
1132 0         0 my $mins = int $remain / 60;
1133 0         0 $remain %= 60;
1134 0         0 return sprintf("Server Up %d days, %2.2d:%2.2d:%2.2d",$days,$hours,$mins,$remain);
1135             }
1136              
1137             sub retOpflags {
1138 0   0 0 0 0 my ($opflags) = shift || return undef;
1139 0         0 my (@opflags) = ();
1140 0         0 my ($action) = "";
1141              
1142 0         0 for (my $i = 0; $i < length($opflags); $i++) {
1143 0         0 my $char = substr($opflags,$i,1);
1144 0 0 0     0 if ($char eq "+" or $char eq "-") {
1145 0         0 $action = $char;
1146             } else {
1147 0         0 push (@opflags,"$action$char");
1148             }
1149             }
1150 0         0 return @opflags;
1151             }
1152              
1153             # Object Methods
1154             # Private methods begin with _
1155              
1156             sub _server_add {
1157 0     0   0 my ($self) = shift;
1158            
1159 0         0 my ($server) = { Numeric => $_[0],
1160             Name => $_[1],
1161             Hops => $_[2],
1162             Link => $_[3]
1163             };
1164              
1165 0         0 $self->{servers_numeric}->{ $server->{Numeric} } = $server;
1166 0         0 $self->{servers_name}->{ $server->{Name} } = $server;
1167 0         0 return 1;
1168             }
1169              
1170             sub _server_del {
1171 0     0   0 my ($self) = shift;
1172 0   0     0 my ($server) = shift || return 0;
1173              
1174 0         0 $self->{servers_numeric}->{$server}->{ToDelete} = 1;
1175 0         0 foreach ( keys %{ $self->{servers_numeric} } ) {
  0         0  
1176 0 0 0     0 if ( $server eq $self->{servers_numeric}->{$_}->{Link} and not defined ( $self->{servers_numeric}->{$server}->{ToDelete}) ) {
1177 0         0 $self->_server_del($self->{servers_numeric}->{$_}->{Link});
1178             }
1179             }
1180 0         0 my (@numerics) = grep /^$server/, keys %{ $self->{bynumeric} };
  0         0  
1181 0         0 foreach (@numerics) {
1182 0         0 $self->_nick_del($_);
1183             }
1184 0         0 my ($servername) = $self->{servers_numeric}->{$server}->{Name};
1185 0         0 delete ( $self->{servers_numeric}->{$server} );
1186 0         0 delete ( $self->{servers_name}->{$servername} );
1187 0         0 return 1;
1188             }
1189              
1190             sub _nick_add {
1191 1     1   3 my ($self) = shift;
1192 1   50     5 my ($numeric) = $_[0] || return 0;
1193 1   50     4 my ($nickname) = $_[1] || return 0;
1194 1   50     4 my ($username) = $_[2] || return 0;
1195 1   50     4 my ($hostname) = $_[3] || return 0;
1196 1   50     3 my ($ipaddr) = $_[4] || return 0;
1197 1   33     4 my ($timestamp) = $_[5] || time();
1198 1   50     4 my ($umode) = $_[6] || undef;
1199 1   50     4 my ($authname) = $_[7] || undef;
1200 1   50     4 my ($ircname) = $_[8] || undef;
1201              
1202             # Does the nickname already exist in our state, ie. one of our clients
1203             # If so kludge the timestamp on ours so it is older and they will get KILLed mwuahahahaha :o)
1204 1 50       3 if ( defined ( $self->{bynickname}->{ u_irc ($nickname) } ) ) {
1205 1         4 my ($kludge) = $timestamp - 30;
1206 1         3 $self->{bynickname}->{ u_irc ( $nickname ) }->{TimeStamp} = $kludge;
1207 1 50       4 if ( defined ( $self->{burst_nicks}->{ $self->{bynickname}->{ u_irc ( $nickname ) }->{Numeric} } ) ) {
1208 0         0 $self->{burst_nicks}->{ $self->{bynickname}->{ u_irc ( $nickname ) }->{Numeric} }->{TimeStamp} = $kludge;
1209             }
1210             }
1211              
1212 1 50       8 if ( not defined ( $self->{bynumeric}->{$numeric} ) ) {
1213 1         9 my ($record) = { Numeric => $numeric,
1214             NickName => $nickname,
1215             UserName => $username,
1216             HostName => $hostname,
1217             IRCName => $ircname,
1218             IPAddr => $ipaddr,
1219             TimeStamp => $timestamp,
1220             UMode => $umode,
1221             AuthName => $authname };
1222 1         4 $self->{bynumeric}->{ $record->{Numeric} } = $record;
1223 1         4 $self->{bynickname}->{ u_irc ( $record->{NickName} ) } = $record;
1224             }
1225            
1226 1         6 return 1;
1227             }
1228              
1229             sub _nick_del {
1230 0     0   0 my ($self) = shift;
1231 0   0     0 my ($numeric) = shift || return 0;
1232              
1233 0         0 foreach ( keys %{ $self->{bynumeric}->{$numeric}->{Channels} } ) {
  0         0  
1234 0         0 delete ( $self->{channels}->{$_}->{Members}->{$numeric} );
1235 0 0       0 if ( scalar ( keys % { $self->{channels}->{$_}->{Members} } ) == 0 ) {
  0         0  
1236 0         0 delete ( $self->{channels}->{$_} );
1237             }
1238             }
1239 0         0 my ($nickname) = u_irc ( $self->{bynumeric}->{$numeric}->{NickName} );
1240 0         0 delete ( $self->{bynumeric}->{$numeric} );
1241 0         0 delete ( $self->{bynickname}->{$nickname} );
1242 0         0 return 1;
1243             }
1244              
1245             sub _nick_change {
1246 0     0   0 my ($self) = shift;
1247 0   0     0 my ($numeric) = shift || return 0;
1248 0   0     0 my ($newnick) = shift || return 0;
1249              
1250 0         0 my ($currentnick) = u_irc( $self->{bynumeric}->{$numeric}->{NickName} );
1251 0         0 $self->{bynumeric}->{$numeric}->{NickName} = $newnick;
1252 0         0 $self->{bynumeric}->{$numeric}->{TimeStamp} = time();
1253 0         0 my ($record) = $self->{bynumeric}->{$numeric};
1254 0         0 delete $self->{bynickname}->{$currentnick};
1255 0         0 $self->{bynickname}->{ u_irc( $record->{NickName} ) } = $record;
1256 0         0 return 1;
1257             }
1258              
1259             sub _nick_account {
1260 0     0   0 my ($self) = shift;
1261 0   0     0 my ($numeric) = $_[0] || return 0;
1262 0   0     0 my ($account) = $_[1] || return 0;
1263              
1264 0         0 $self->{bynumeric}->{$numeric}->{AuthName} = $account;
1265 0         0 $self->_nick_umode($numeric,"+r");
1266             }
1267              
1268             sub _nick_umode {
1269 0     0   0 my ($self) = shift;
1270 0   0     0 my ($numeric) = $_[0] || return 0;
1271 0   0     0 my ($umode) = $_[1] || return 0;
1272              
1273 0         0 my ($currentumode) = $self->{bynumeric}->{$numeric}->{UMode};
1274 0         0 foreach (retOpflags($umode)) {
1275             SWITCH: {
1276 0 0       0 if (/^\+(.+)/) {
  0         0  
1277 0 0       0 if ( not defined ($currentumode) ) {
1278 0         0 $currentumode = $1;
1279             } else {
1280 0         0 $currentumode .= $1;
1281 0         0 $currentumode = join("",sort(split(//,$currentumode)));
1282             }
1283 0         0 last SWITCH;
1284             }
1285 0 0       0 if (/^-(.+)/) {
1286 0 0       0 if ( defined ($currentumode) ) {
1287 0         0 $currentumode =~ s/$1//g;
1288             }
1289 0         0 last SWITCH;
1290             }
1291             }
1292             }
1293 0 0 0     0 if ( defined ($currentumode) and $currentumode ) {
1294 0         0 $self->{bynumeric}->{$numeric}->{UMode} = $currentumode;
1295             } else {
1296 0         0 delete ( $self->{bynumeric}->{$numeric}->{UMode} );
1297             }
1298 0         0 return 1;
1299             }
1300              
1301             sub _channel_join {
1302 1     1   4 my ($self) = shift;
1303 1   50     4 my ($channel) = $_[0] || return 0;
1304 1   50     4 my ($numeric) = $_[1] || return 0;
1305 1         3 my ($timestamp) = $_[2];
1306 1         3 my ($usermode) = 0;
1307 1         3 my ($channelname) = $channel;
1308 1         3 $channel = u_irc ( $channel );
1309            
1310 1 50       5 if (not exists $self->{channels}->{$channel}) {
1311 1         4 $self->{channels}->{$channel}->{Channel} = $channelname;
1312 1         4 $self->{channels}->{$channel}->{TimeStamp} = $timestamp;
1313 1         2 $usermode = 2;
1314             }
1315 1         3 $self->{channels}->{$channel}->{Members}->{$numeric} = $usermode;
1316 1         3 $self->{bynumeric}->{$numeric}->{Channels}->{$channel} = $usermode;
1317 1         3 return 1;
1318             }
1319              
1320             sub _channel_part {
1321 0     0   0 my ($self) = shift;
1322 0   0     0 my ($channel) = $_[0] || return 0;
1323 0   0     0 my ($numeric) = $_[1] || return 0;
1324 0         0 my ($channelname) = $channel;
1325 0         0 $channel = u_irc ( $channel );
1326              
1327 0         0 delete ( $self->{channels}->{$channel}->{Members}->{$numeric} );
1328 0 0       0 if ( scalar ( keys % { $self->{channels}->{$_}->{Members} } ) == 0 ) {
  0         0  
1329 0         0 delete ( $self->{channels}->{$_} );
1330             }
1331 0         0 delete ( $self->{bynumeric}->{$numeric}->{Channels}->{$channel} );
1332 0         0 return 1;
1333             }
1334              
1335             sub _channel_topic {
1336 0     0   0 my ($self) = shift;
1337 0   0     0 my ($channel) = u_irc( $_[0] ) || return 0;
1338 0   0     0 my ($topic) = $_[1] || return 0;
1339 0   0     0 my ($set_by) = $_[2] || return 0;
1340 0   0     0 my ($timestamp) = $_[3] || return 0;
1341              
1342 0         0 $self->{channels}->{$channel}->{Topic} = $topic;
1343 0         0 $self->{channels}->{$channel}->{Set_By} = $set_by;
1344 0         0 $self->{channels}->{$channel}->{TopicTS} = $timestamp;
1345 0         0 return 1;
1346             }
1347              
1348             sub _channel_untopic {
1349 0     0   0 my ($self) = shift;
1350 0   0     0 my ($channel) = u_irc( $_[0] ) || return 0;
1351              
1352 0         0 delete ( $self->{channels}->{$channel}->{Topic} );
1353 0         0 delete ( $self->{channels}->{$channel}->{Set_By} );
1354 0         0 delete ( $self->{channels}->{$channel}->{TopicTS} );
1355 0         0 return 1;
1356             }
1357              
1358             sub _channel_mode {
1359 0     0   0 my ($self) = shift;
1360 0   0     0 my ($channel) = u_irc( $_[0] ) || return 0;
1361 0   0     0 my ($string) = $_[1] || return 0;
1362 0   0     0 my ($who) = $_[2] || return 0; # This is either a server or client numeric only used for bans tbh
1363              
1364 0         0 my ($modes,@args) = split(/ /,$string);
1365 0         0 my (@modes) = retOpflags($modes);
1366 0         0 my ($currentmode) = $self->{channels}->{$channel}->{Mode};
1367 0         0 foreach (@modes) {
1368 0         0 my $argument;
1369 0 0       0 $argument = shift(@args) if (/\+[ovbkl]/);
1370 0 0       0 $argument = shift(@args) if (/-[ovb]/);
1371             SWITCH: {
1372 0 0       0 if (/b/) {
  0         0  
1373 0         0 $self->_channel_ban($channel,$_,$argument,$who);
1374 0         0 last SWITCH;
1375             }
1376 0 0       0 if (/l/) {
1377 0 0       0 if (/^\+(.+)/) {
1378 0         0 $self->{channels}->{$channel}->{ChanLimit} = $argument;
1379 0         0 $currentmode .= $1;
1380             } else {
1381 0         0 delete ( $self->{channels}->{$channel}->{ChanLimit} );
1382 0         0 $currentmode =~ s/$1//g;
1383             }
1384 0         0 last SWITCH;
1385             }
1386 0 0       0 if (/k/) {
1387 0 0       0 if (/^\+(.+)/) {
1388 0         0 $self->{channels}->{$channel}->{ChanKey} = $argument;
1389 0         0 $currentmode .= $1;
1390             } else {
1391 0         0 delete ( $self->{channels}->{$channel}->{ChanKey} );
1392 0         0 $currentmode =~ s/$1//g;
1393             }
1394 0         0 last SWITCH;
1395             }
1396 0 0       0 if (/[ov]/) {
1397 0         0 my ($value) = 0;
1398 0 0       0 if (/\+o/) { $value = 2; }
  0         0  
1399 0 0       0 if (/-o/) { $value = -2; }
  0         0  
1400 0 0       0 if (/\+v/) { $value = 1; }
  0         0  
1401 0 0       0 if (/-v/) { $value = -1; }
  0         0  
1402 0         0 $self->{channels}->{$channel}->{Members}->{$argument} += $value;
1403 0         0 $self->{bynumeric}->{$argument}->{Channels}->{$channel} += $value;
1404 0         0 last SWITCH;
1405             }
1406 0 0       0 if (/^\+(.+)/) {
1407 0         0 $currentmode .= $1;
1408 0         0 last SWITCH;
1409             }
1410 0 0       0 if (/^-(.+)/) {
1411 0         0 $currentmode =~ s/$1//g;
1412 0         0 last SWITCH;
1413             }
1414             }
1415             }
1416 0         0 $self->{channels}->{$channel}->{Mode} = join("",sort(split(//,$currentmode)));
1417 0         0 return 1;
1418             }
1419              
1420             sub _channel_clearmode {
1421 0     0   0 my ($self) = shift;
1422 0   0     0 my ($channel) = u_irc( $_[0] ) || return 0;
1423 0   0     0 my ($modes) = $_[1] || return 0;
1424              
1425 0         0 my ($currentmodes) = $self->{channels}->{$channel}->{Mode};
1426 0         0 foreach (split(//,$modes)) {
1427 0         0 $currentmodes =~ s/$_//g;
1428             }
1429 0         0 $self->{channels}->{$channel}->{Mode} = $currentmodes;
1430 0 0       0 delete ( $self->{channels}->{$channel}->{Bans} ) if ( $modes =~ /b/ );
1431 0         0 foreach ( %{ $self->{channels}->{$channel}->{Members} } ) {
  0         0  
1432 0 0 0     0 if ( $modes =~ /o/ and $self->{channels}->{$channel}->{Members}->{$_} > 1 ) {
1433 0         0 $self->{channels}->{$channel}->{Members}->{$_} -= 2;
1434 0         0 $self->{bynumeric}->{$_}->{Channels}->{$channel} = $self->{channels}->{$channel}->{Members}->{$_};
1435             }
1436 0 0 0     0 if ( $modes =~ /v/ and $self->{channels}->{$channel}->{Members}->{$_} > 0 ) {
1437 0         0 $self->{channels}->{$channel}->{Members}->{$_} -= 1;
1438 0         0 $self->{bynumeric}->{$_}->{Channels}->{$channel} = $self->{channels}->{$channel}->{Members}->{$_};
1439             }
1440             }
1441 0         0 return 1;
1442             }
1443              
1444             sub _channel_ban {
1445 0     0   0 my ($self) = shift;
1446 0   0     0 my ($channel) = u_irc( $_[0] ) || return 0;
1447 0   0     0 my ($operation) = $_[1] || return 0;
1448 0   0     0 my ($banmask) = $_[2] || return 0;
1449 0   0     0 my ($who) = $_[3] || return 0;
1450            
1451 0 0       0 if ($operation eq "+b") {
1452 0         0 $self->{channels}->{$channel}->{Bans}->{$banmask}->{Time} = time();
1453 0         0 $self->{channels}->{$channel}->{Bans}->{$banmask}->{Who} = $who;
1454             } else {
1455 0         0 delete ( $self->{channels}->{$channel}->{Bans}->{$banmask} );
1456             }
1457 0         0 return 1;
1458             }
1459              
1460             sub _channel_burst {
1461 0     0   0 my ($self) = shift;
1462 0         0 my ($who) = shift;
1463 0         0 my ($args) = shift;
1464              
1465 0         0 my ($first,$second) = split(/:%/,$args);
1466 0         0 my (@args) = split(/ /,$first);
1467 0 0       0 push(@args,":%$second") if ( defined ($second) );
1468 0         0 my ($channelname,$timestamp) = @args[0..1];
1469 0         0 my ($channel) = u_irc ( $channelname );
1470             # Kludge channel timestamp here
1471 0 0       0 if ( exists $self->{channels}->{$channel} ) {
1472 0 0       0 if ( $timestamp < $self->{channels}->{$channel}->{TimeStamp} ) {
1473 0         0 $self->{channels}->{$channel}->{TimeStamp} = $timestamp;
1474 0         0 $self->{burst_channels}->{$channel}->{TimeStamp} = $timestamp;
1475             }
1476             } else {
1477 0         0 $self->{channels}->{$channel}->{Channel} = $channelname;
1478 0         0 $self->{channels}->{$channel}->{TimeStamp} = $timestamp;
1479             }
1480 0         0 my ($channelmodes) = "";
1481 0         0 for (my $i = 2; $i <= $#args; $i++) {
1482             SWITCH: {
1483 0 0       0 if ($args[$i] =~ /^\+(.+)/) {
  0         0  
1484 0         0 $channelmodes = $1;
1485 0         0 $self->{channels}->{$channel}->{Mode} = $channelmodes;
1486 0         0 my ($l) = index ( $1, "l" );
1487 0         0 my ($k) = index ( $1, "k" );
1488             SWITCH2: {
1489 0 0 0     0 if ( $l > $k and $k != -1 ) {
  0         0  
1490 0         0 $i++;
1491 0         0 $self->{channels}->{$channel}->{ChanKey} = $args[$i];
1492 0         0 $i++;
1493 0         0 $self->{channels}->{$channel}->{ChanLimit} = $args[$i];
1494 0         0 last SWITCH2;
1495             }
1496 0 0 0     0 if ( $l > $k and $k == -1 ) {
1497 0         0 $i++;
1498 0         0 $self->{channels}->{$channel}->{ChanLimit} = $args[$i];
1499 0         0 last SWITCH2;
1500             }
1501 0 0 0     0 if ( $k > $l and $l != -1 ) {
1502 0         0 $i++;
1503 0         0 $self->{channels}->{$channel}->{ChanLimit} = $args[$i];
1504 0         0 $i++;
1505 0         0 $self->{channels}->{$channel}->{ChanKey} = $args[$i];
1506 0         0 last SWITCH2;
1507             }
1508 0 0 0     0 if ( $k > $l and $l == -1 ) {
1509 0         0 $i++;
1510 0         0 $self->{channels}->{$channel}->{ChanKey} = $args[$i];
1511 0         0 last SWITCH2;
1512             }
1513             }
1514 0         0 last SWITCH;
1515             }
1516 0 0       0 if ($args[$i] =~ /^:%(.+)$/) {
1517 0         0 foreach (split(/ /,$1)) {
1518 0         0 $self->_channel_ban($channel,"+b",$_,$who);
1519             }
1520 0         0 last SWITCH;
1521             }
1522             # Hey, it must be a list of nicks then :o)
1523 0         0 my ($lastmodes);
1524 0         0 foreach (split(/,/,$args[$i])) {
1525 0         0 my ($numeric,$modes) = split(/:/);
1526 0 0       0 if (defined ($modes)) {
1527 0         0 $lastmodes = $modes;
1528             }
1529             # Add nick here
1530 0         0 my ($value) = 0;
1531             SWITCH2: {
1532 0 0       0 if ( $lastmodes eq "ov" ) {
  0         0  
1533 0         0 $value = 3;
1534 0         0 last SWITCH2;
1535             }
1536 0 0       0 if ( $lastmodes eq "o" ) {
1537 0         0 $value = 2;
1538 0         0 last SWITCH2;
1539             }
1540 0 0       0 if ( $lastmodes eq "v" ) {
1541 0         0 $value = 1;
1542 0         0 last SWITCH2;
1543             }
1544             }
1545 0         0 $self->{channels}->{$channel}->{Members}->{$numeric} = $value;
1546 0         0 $self->{bynumeric}->{$numeric}->{Channels}->{$channel} = $value;
1547             }
1548             }
1549             }
1550             }
1551              
1552             sub _burst_create {
1553 0     0   0 my ($self) = shift;
1554              
1555             # First a list of nicks
1556 0         0 foreach ( keys %{ $self->{bynumeric} } ) {
  0         0  
1557 0         0 $self->{burst_nicks}->{$_}->{Numeric} = $self->{bynumeric}->{$_}->{Numeric};
1558 0         0 $self->{burst_nicks}->{$_}->{NickName} = $self->{bynumeric}->{$_}->{NickName};
1559 0         0 $self->{burst_nicks}->{$_}->{UserName} = $self->{bynumeric}->{$_}->{UserName};
1560 0         0 $self->{burst_nicks}->{$_}->{HostName} = $self->{bynumeric}->{$_}->{HostName};
1561 0         0 $self->{burst_nicks}->{$_}->{IPAddr} = $self->{bynumeric}->{$_}->{IPAddr};
1562 0         0 $self->{burst_nicks}->{$_}->{UMode} = $self->{bynumeric}->{$_}->{UMode};
1563 0         0 $self->{burst_nicks}->{$_}->{AuthName} = $self->{bynumeric}->{$_}->{AuthName};
1564 0         0 $self->{burst_nicks}->{$_}->{IRCName} = $self->{bynumeric}->{$_}->{IRCName};
1565 0         0 $self->{burst_nicks}->{$_}->{TimeStamp} = $self->{bynumeric}->{$_}->{TimeStamp};
1566             }
1567             # And now a list of channels
1568 0         0 foreach ( keys %{ $self->{channels} } ) {
  0         0  
1569 0         0 $self->{burst_channels}->{$_}->{Channel} = $self->{channels}->{$_}->{Channel};
1570 0         0 $self->{burst_channels}->{$_}->{TimeStamp} = $self->{channels}->{$_}->{TimeStamp};
1571 0 0       0 $self->{burst_channels}->{$_}->{Mode} = $self->{channels}->{$_}->{Mode} if ( exists $self->{channels}->{$_}->{Mode} );
1572 0 0       0 $self->{burst_channels}->{$_}->{ChanKey} = $self->{channels}->{$_}->{ChanKey} if ( exists $self->{channels}->{$_}->{ChanKey} );
1573 0 0       0 $self->{burst_channels}->{$_}->{ChanLimit} = $self->{channels}->{$_}->{ChanLimit} if ( exists $self->{channels}->{$_}->{ChanLimit} );
1574 0         0 foreach my $ban ( keys %{ $self->{channels}->{$_}->{Bans} } ) {
  0         0  
1575 0         0 push(@{ $self->{burst_channels}->{$_}->{Bans} },$ban);
  0         0  
1576             }
1577 0         0 foreach my $user ( keys %{ $self->{channels}->{$_}->{Members} } ) {
  0         0  
1578 0         0 $self->{burst_channels}->{$_}->{Members}->{$user} = $self->{channels}->{$_}->{Members}->{$user};
1579             }
1580             }
1581 0         0 return 1;
1582             }
1583              
1584             sub _burst_info {
1585 0     0   0 my ($self) = shift;
1586 0         0 my (@burst);
1587 0         0 my (@mode) = ( "","v","o","ov" );
1588              
1589             # Return an array of correctly formatted lines suitable for spewing at our uplink
1590             # As per P10 protocol nicks come first
1591 0         0 foreach ( keys %{ $self->{burst_nicks} } ) {
  0         0  
1592 0         0 my ($burstline) = "N " . $self->{burst_nicks}->{$_}->{NickName} . " ";
1593 0         0 $burstline .= "1 " . $self->{burst_nicks}->{$_}->{TimeStamp} . " ";
1594 0         0 $burstline .= $self->{burst_nicks}->{$_}->{UserName} . " " . $self->{burst_nicks}->{$_}->{HostName} . " ";
1595 0 0       0 $burstline .= "+" . $self->{burst_nicks}->{$_}->{UMode} . " " if ( defined ($self->{burst_nicks}->{$_}->{UMode}) );
1596 0 0       0 $burstline .= $self->{burst_nicks}->{$_}->{AuthName} . " " if ( defined ($self->{burst_nicks}->{$_}->{AuthName}) );
1597 0         0 $burstline .= $self->{burst_nicks}->{$_}->{IPAddr} . " " . $_ . " :";
1598 0 0       0 $burstline .= $self->{burst_nicks}->{$_}->{IRCName} if ( defined ($self->{burst_nicks}->{$_}->{IRCName}) );
1599 0         0 push(@burst,$burstline);
1600             }
1601             # Followed by channels
1602 0         0 foreach ( keys %{ $self->{burst_channels} } ) {
  0         0  
1603 0         0 my ($burstline) = "B " . $self->{burst_channels}->{$_}->{Channel} . " " . $self->{burst_channels}->{$_}->{TimeStamp} . " ";
1604 0 0       0 $burstline .= "+" . $self->{burst_channels}->{$_}->{Mode} . " " if ( defined ($self->{burst_channels}->{$_}->{Mode}) );
1605 0 0       0 $burstline .= $self->{burst_channels}->{$_}->{ChanKey} . " " if ( defined ($self->{burst_channels}->{$_}->{ChanKey}) );
1606 0 0       0 $burstline .= $self->{burst_channels}->{$_}->{ChanLimit} . " " if ( defined ($self->{burst_channels}->{$_}->{ChanLimit}) );
1607 0         0 my ($lastmode) = 0; my (@users);
  0         0  
1608 0         0 foreach my $member ( sort { $self->{burst_channels}->{$_}->{Members}->{$a} <=> $self->{burst_channels}->{$_}->{Members}->{$b} } keys %{ $self->{burst_channels}->{$_}->{Members} } ) {
  0         0  
  0         0  
1609 0         0 my ($user) = $member;
1610 0 0       0 if ($self->{burst_channels}->{$_}->{Members}->{$member} > $lastmode) {
1611 0         0 $user .= ":" . $mode[$self->{burst_channels}->{$_}->{Members}->{$member}];
1612             }
1613 0         0 push(@users,$user);
1614             }
1615 0         0 $burstline .= join(",",@users) . " ";
1616 0 0       0 my ($bans) = join(" ",@{ $self->{burst_channels}->{$_}->{Bans} }) if ( defined ($self->{burst_channels}->{$_}->{Bans}) );
  0         0  
1617 0 0       0 $burstline .= ":%" . $bans if ( defined ($bans) );
1618 0         0 push(@burst,$burstline);
1619             }
1620 0         0 return @burst;
1621             }
1622              
1623             sub _burst_destroy {
1624 0     0   0 my ($self) = shift;
1625              
1626 0         0 delete ( $self->{burst_nicks} );
1627 0         0 delete ( $self->{burst_channels} );
1628 0         0 return 1;
1629             }
1630              
1631             sub _dump_state {
1632 0     0   0 my ($self) = shift;
1633 0         0 my (@mode) = ( '','+','@','@+' );
1634 0         0 my (@results);
1635              
1636             # servers
1637 0         0 foreach ( keys %{ $self->{servers_numeric} } ) {
  0         0  
1638 0         0 push ( @results, $_ . " " . $self->{servers_numeric}->{$_}->{Name} . " " . $self->{servers_numeric}->{$_}->{Hops} . " " . $self->{servers_numeric}->{$_}->{Link} );
1639             }
1640             # users
1641 0         0 foreach ( keys %{ $self->{bynumeric} } ) {
  0         0  
1642 0         0 push ( @results, $_ . " " . $self->{bynumeric}->{$_}->{NickName} );
1643             }
1644             # channels
1645 0         0 foreach ( keys %{ $self->{channels} } ) {
  0         0  
1646 0         0 my ($line) = $_ . " ";
1647 0         0 foreach my $member ( keys %{ $self->{channels}->{$_}->{Members} } ) {
  0         0  
1648 0         0 $line .= $mode[$self->{channels}->{$_}->{Members}->{$member}] . $self->{bynumeric}->{$member}->{NickName};
1649             }
1650 0         0 push ( @results, $line );
1651             }
1652 0         0 return @results;
1653             }
1654              
1655             # Public Methods
1656              
1657             sub cmd_token {
1658 0     0 1 0 my ($self) = shift;
1659 0   0     0 my ($command) = uc ( $_[0] ) || return undef;
1660              
1661 0         0 return $cmd2token{$command};
1662             }
1663              
1664             sub server_numeric {
1665 0     0 1 0 my ($self) = shift;
1666 0   0     0 my ($name) = shift || return undef;
1667              
1668 0         0 return $self->{servers_name}->{$name}->{Numeric};
1669             }
1670              
1671             sub server_name {
1672 0     0 1 0 my ($self) = shift;
1673 0   0     0 my ($numeric) = shift || return undef;
1674              
1675 0         0 return $self->{servers_numeric}->{$numeric}->{Name};
1676             }
1677              
1678             sub server_clients {
1679 0     0 1 0 my ($self) = shift;
1680 0   0     0 my ($server) = shift || return 0;
1681              
1682 0         0 my (@numerics) = grep /^$server/, keys %{ $self->{bynumeric} };
  0         0  
1683              
1684 0         0 return scalar( @numerics );
1685             }
1686              
1687             sub server_link {
1688 0     0 0 0 my ($self) = shift;
1689              
1690 0         0 return $self->{serverlink};
1691             }
1692              
1693             sub nick_info {
1694 0     0 1 0 my ($self) = shift;
1695 0   0     0 my ($numeric) = shift || return undef;
1696 0         0 my (@returnvalues);
1697            
1698 0         0 $returnvalues[0] = $self->{bynumeric}->{$numeric}->{NickName};
1699 0         0 $returnvalues[1] = $self->{bynumeric}->{$numeric}->{UserName};
1700 0         0 $returnvalues[2] = $self->{bynumeric}->{$numeric}->{HostName};
1701 0         0 $returnvalues[3] = $self->{bynumeric}->{$numeric}->{IRCName};
1702 0         0 $returnvalues[4] = $self->{bynumeric}->{$numeric}->{UMode};
1703 0         0 $returnvalues[5] = $self->{bynumeric}->{$numeric}->{AuthName};
1704              
1705 0         0 return @returnvalues;
1706             }
1707              
1708             sub nick_numeric {
1709 1     1 1 4 my ($self) = shift;
1710 1   50     5 my ($nickname) = u_irc( $_[0] ) || return undef;
1711              
1712 1         6 return $self->{bynickname}->{$nickname}->{Numeric};
1713             }
1714              
1715             sub nick_name {
1716 0     0 1 0 my ($self) = shift;
1717 0   0     0 my ($numeric) = $_[0] || return undef;
1718              
1719 0         0 return $self->{bynumeric}->{$numeric}->{NickName};
1720             }
1721              
1722             sub nick_channels {
1723 0     0 1 0 my ($self) = shift;
1724 0   0     0 my ($numeric) = shift || return undef;
1725 0         0 my (@returnvalues);
1726              
1727 0         0 foreach ( keys %{ $self->{bynumeric}->{$numeric}->{Channels} } ) {
  0         0  
1728             SWITCH: {
1729 0 0       0 if ( $self->{bynumeric}->{$numeric}->{Channels}->{$_} == 1 ) {
  0         0  
1730 0         0 push(@returnvalues,"+$_");
1731 0         0 last SWITCH;
1732             }
1733 0 0       0 if ( $self->{bynumeric}->{$numeric}->{Channels}->{$_} >= 2 ) {
1734 0         0 push(@returnvalues,"\@$_");
1735 0         0 last SWITCH;
1736             }
1737 0         0 push(@returnvalues,"$_");
1738             }
1739             }
1740 0         0 return @returnvalues;
1741             }
1742              
1743             sub channel_exists {
1744 1     1 1 3 my ($self) = shift;
1745 1   50     7 my ($channel) = u_irc ( $_[0] ) || return undef;
1746              
1747 1 50       5 if ( defined ( $self->{channels}->{$channel} ) ) {
1748 0         0 return $self->{channels}->{$channel}->{TimeStamp};
1749             }
1750 1         3 return undef;
1751             }
1752              
1753             sub is_channel_operator {
1754 0     0 1 0 my ($self) = shift;
1755 0   0     0 my ($channel) = u_irc ( $_[0] ) || return 0;
1756 0   0     0 my ($numeric) = $_[1] || return 0;
1757              
1758 0 0 0     0 if ( defined ( $self->{channels}->{$channel}->{Members}->{$numeric} ) and $self->{channels}->{$channel}->{Members}->{$numeric} >= 2 ) {
1759 0         0 return 1;
1760             } else {
1761 0         0 return 0;
1762             }
1763             }
1764              
1765             sub has_channel_voice {
1766 0     0 1 0 my ($self) = shift;
1767 0   0     0 my ($channel) = u_irc ( $_[0] ) || return 0;
1768 0   0     0 my ($numeric) = $_[1] || return 0;
1769              
1770 0 0 0     0 if ( defined ( $self->{channels}->{$channel}->{Members}->{$numeric} ) and $self->{channels}->{$channel}->{Members}->{$numeric} == 2 or $self->{channels}->{$channel}->{Members}->{$numeric} == 3 ) {
      0        
1771 0         0 return 1;
1772             } else {
1773 0         0 return 0;
1774             }
1775             }
1776              
1777             sub is_operator {
1778 0     0 1 0 my ($self) = shift;
1779 0   0     0 my ($numeric) = $_[0] || return 0;
1780              
1781 0 0 0     0 if ( defined ( $self->{bynumeric}->{$numeric}->{UMode} ) and $self->{bynumeric}->{$numeric}->{UMode} =~ /o/ ) {
1782 0         0 return 1;
1783             } else {
1784 0         0 return 0;
1785             }
1786             }
1787              
1788             sub has_account_set {
1789 0     0 1 0 my ($self) = shift;
1790 0   0     0 my ($numeric) = $_[0] || return undef;
1791              
1792             return $self->{bynumeric}->{$numeric}->{AuthName}
1793 0         0 }
1794              
1795             sub channel_banlist {
1796 0     0 1 0 my ($self) = shift;
1797 0   0     0 my ($channel) = u_irc ( $_[0] ) || return undef;
1798              
1799 0         0 return keys %{ $self->{channels}->{$channel}->{Bans} };
  0         0  
1800             }
1801              
1802             sub nick_long_form {
1803 0     0 1 0 my ($self) = shift;
1804 0   0     0 my ($numeric) = $_[0] || return undef;
1805              
1806 0 0       0 if ( defined ( $self->{bynumeric}->{$numeric} ) ) {
1807 0         0 return $self->{bynumeric}->{$numeric}->{NickName} . "!" . $self->{bynumeric}->{$numeric}->{UserName} . "\@" . $self->{bynumeric}->{$numeric}->{HostName};
1808             } else {
1809 0         0 return undef;
1810             }
1811             }
1812              
1813             sub channels_list {
1814 0     0 1 0 my ($self) = shift;
1815 0         0 my (@channels);
1816              
1817 0         0 foreach ( keys %{ $self->{channels} } ) {
  0         0  
1818 0         0 push (@channels,$self->{channels}->{$_}->{Channel});
1819             }
1820 0         0 return @channels;
1821             }
1822              
1823             sub irc_network_stats {
1824 0     0 1 0 my ($self) = shift;
1825 0         0 my (@results);
1826              
1827             # Number of servers
1828 0         0 push (@results,scalar( keys %{ $self->{servers_numeric} } ));
  0         0  
1829             # Number of users
1830 0         0 push (@results,scalar( keys %{ $self->{bynumeric} } ));
  0         0  
1831             # Number of channels
1832 0         0 push (@results,scalar( keys %{ $self->{channels} }));
  0         0  
1833 0         0 return @results;
1834             }
1835              
1836             sub channel_users {
1837 0     0 1 0 my ($self) = shift;
1838 0   0     0 my ($channel) = u_irc ( $_[0] ) || return undef;
1839            
1840 0         0 return keys %{ $self->{channels}->{$channel}->{Members} };
  0         0  
1841             }
1842              
1843             sub network_users {
1844 0     0 0 0 my ($self) = shift;
1845              
1846 0         0 return keys %{ $self->{bynumeric} };
  0         0  
1847             }
1848              
1849             sub channel_mode {
1850 0     0 1 0 my ($self) = shift;
1851 0   0     0 my ($channel) = u_irc ( $_[0] ) || return undef;
1852              
1853 0         0 return $self->{channels}->{$channel}->{Mode};
1854             }
1855              
1856             sub channel_limit {
1857 0     0 1 0 my ($self) = shift;
1858 0   0     0 my ($channel) = u_irc ( $_[0] ) || return undef;
1859              
1860 0         0 return $self->{channels}->{$channel}->{ChanLimit};
1861             }
1862              
1863             sub channel_key {
1864 0     0 1 0 my ($self) = shift;
1865 0   0     0 my ($channel) = u_irc ( $_[0] ) || return undef;
1866              
1867 0         0 return $self->{channels}->{$channel}->{ChanKey};
1868             }
1869              
1870             sub decimal_to_base64 {
1871 0     0 1 0 my ($self) = shift;
1872 0   0     0 my ($number) = $_[0] || return undef;
1873 0   0     0 my ($output) = $_[1] || 2;
1874              
1875 0 0       0 if ( $number =~ /^[0-9]*$/ ) {
1876 0         0 return dectobase64($number,$output);
1877             } else {
1878 0         0 return undef;
1879             }
1880             }
1881              
1882             sub base64_to_decimal {
1883 0     0 1 0 my ($self) = shift;
1884 0   0     0 my ($base64) = $_[0] || return undef;
1885              
1886 0 0       0 if ( $base64 =~ /^[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\[\]]*$/ ) {
1887 0         0 return base64todec($base64);
1888             } else {
1889 0         0 return undef;
1890             }
1891             }
1892              
1893             # Join a channel. Do a CREATE or JOIN appropriately
1894             sub join {
1895 1     1 1 441 my ($kernel,$heap,$numeric,$channel,$modes) = @_[KERNEL,HEAP,ARG0,ARG1,ARG2];
1896              
1897 1 50 33     7 unless (defined $numeric and defined $channel) {
1898 0         0 die "The POE::Component::IRC event \"join\" requires at least two arguments";
1899             }
1900            
1901 1         3 my ($timestamp) = time();
1902 1 50       5 if ( $heap->{State}->channel_exists($channel) ) {
1903             # Join channel
1904 0         0 $kernel->yield( 'sl_client' => "$numeric J $channel $timestamp" );
1905             } else {
1906             # Create channel
1907 1         7 $kernel->yield( 'sl_client' => "$numeric C $channel $timestamp" );
1908             }
1909             }
1910              
1911             1;
1912             __END__