File Coverage

blib/lib/POE/Component/Server/IRC/Backend.pm
Criterion Covered Total %
statement 359 472 76.0
branch 100 218 45.8
condition 40 104 38.4
subroutine 46 58 79.3
pod 18 22 81.8
total 563 874 64.4


line stmt bran cond sub pod time code
1             package POE::Component::Server::IRC::Backend;
2             our $AUTHORITY = 'cpan:BINGOS';
3             $POE::Component::Server::IRC::Backend::VERSION = '1.62';
4 183     183   113742 use strict;
  183         467  
  183         6321  
5 183     183   1051 use warnings;
  183         485  
  183         6041  
6 183     183   1071 use Carp qw(carp croak);
  183         445  
  183         10900  
7 183     183   1216 use List::Util qw(first);
  183         521  
  183         12158  
8 183         1481 use POE qw(Wheel::SocketFactory Wheel::ReadWrite Filter::Stackable
9 183     183   1748 Filter::Line Filter::IRCD Filter::ThruPut);
  183         37736  
10 183     183   3239426 use Net::Netmask;
  183         42318400  
  183         29645  
11 183     183   3198 use Net::CIDR ();
  183         6401  
  183         5728  
12 183     183   120342 use Net::IP::Minimal qw[ip_is_ipv6];
  183         170650  
  183         13991  
13 183     183   1623 use Socket qw(getnameinfo NI_NUMERICHOST NI_NUMERICSERV AF_INET6);
  183         506  
  183         13612  
14 183     183   1349 use base qw(POE::Component::Syndicator);
  183         524  
  183         113210  
15              
16             use constant {
17 183         1255804 OBJECT_STATES_HASHREF => {
18             syndicator_started => '_start',
19             add_connector => '_add_connector',
20             add_listener => '_add_listener',
21             del_listener => '_del_listener',
22             send_output => '_send_output',
23             shutdown => '_shutdown',
24             },
25             OBJECT_STATES_ARRAYREF => [qw(
26             _accept_connection
27             _accept_failed
28             _conn_alarm
29             _conn_input
30             _conn_error
31             _conn_flushed
32             _event_dispatcher
33             _sock_failed
34             _sock_up
35             _sock_ssl
36             )],
37 183     183   1965354 };
  183         610  
38              
39             sub create {
40 182     182 1 802 my $package = shift;
41 182 50       1073 croak("$package requires an even number of parameters") if @_ & 1;
42 182         992 my %args = @_;
43 182         1605 $args{ lc $_ } = delete $args{$_} for keys %args;
44 182         734 my $self = bless { }, $package;
45              
46 182 50       895 $self->{raw_events} = $args{raw_events} if defined $args{raw_events};
47             $self->{prefix} = defined $args{prefix}
48             ? $args{prefix}
49 182 50       2829 : 'ircd_';
50             $self->{antiflood} = defined $args{antiflood}
51             ? $args{antiflood}
52 182 50       952 : 1;
53              
54             $self->{auth} = defined $args{auth}
55             ? $args{auth}
56 182 50       798 : 1;
57              
58 182 100 66     4986 if ($args{sslify_options} && ref $args{sslify_options} eq 'ARRAY') {
59 12         34 eval {
60 12         97 require POE::Component::SSLify;
61 12         730 POE::Component::SSLify->import(
62             qw(SSLify_GetCTX SSLify_Options Server_SSLify Client_SSLify SSLify_ContextCreate)
63             );
64             };
65 12         41 chomp $@;
66 12 50       45 croak("Can't use ssl: $@") if $@;
67              
68 12         25 eval {
69 12         28 SSLify_Options(@{ $args{sslify_options} });
  12         79  
70 12         13373 my $ctx = SSLify_GetCTX();
71 12         179 require Net::SSLeay;
72 12     22   218 Net::SSLeay::CTX_set_verify( $ctx, 0x01, sub { return 1; } );
  22         100961  
73             };
74 12         36 chomp $@;
75 12 50       53 croak("Can't use ssl: $@") if $@;
76 12         45 $self->{got_ssl} = 1;
77             }
78              
79 182 100       761 if ($args{states}) {
80 181         1899 my $error = $self->_validate_states($args{states});
81 181 50       759 croak($error) if defined $error;
82             }
83              
84             $self->_syndicator_init(
85             prefix => $self->{prefix},
86             reg_prefix => 'PCSI_',
87             types => [ SERVER => 'IRCD', USER => 'U' ],
88             object_states => [
89             $self => OBJECT_STATES_HASHREF,
90             $self => OBJECT_STATES_ARRAYREF,
91             ($args{states}
92 362         3607 ? map { $self => $_ } @{ $args{states} }
  181         632  
93             : ()
94             ),
95             ],
96             ($args{plugin_debug} ? (debug => 1) : () ),
97 182 100       1351 (ref $args{options} eq 'HASH' ? (options => $args{options}) : ()),
    50          
    50          
98             );
99              
100 182 100       41144 if ($self->{auth}) {
101 1         653 require POE::Component::Server::IRC::Plugin::Auth;
102             $self->plugin_add(
103             'Auth_'.$self->session_id(),
104             POE::Component::Server::IRC::Plugin::Auth->new(
105             identport => $args{identport},
106 1         100 ),
107             );
108             }
109              
110 182         1394 return $self;
111             }
112              
113             sub _validate_states {
114 181     181   681 my ($self, $states) = @_;
115              
116 181         642 for my $events (@$states) {
117 362 100       1843 if (ref $events eq 'HASH') {
    50          
118 181         1308 for my $event (keys %$events) {
119 3982 50 33     12039 if (OBJECT_STATES_HASHREF->{$event}
120 39820     39820   60582 || first { $event eq $_ } @{ +OBJECT_STATES_ARRAYREF }) {
  3982         8252  
121 0         0 return "Event $event is reserved by ". __PACKAGE__;
122             }
123             }
124             }
125             elsif (ref $events eq 'ARRAY') {
126 181         616 for my $event (@$events) {
127 724 50 33     3860 if (OBJECT_STATES_HASHREF->{$event}
128 7240     7240   12357 || first { $event eq $_ } @{ +OBJECT_STATES_ARRAYREF }) {
  724         2635  
129 0         0 return "Event $event is reserved by ". __PACKAGE__;
130             }
131             }
132             }
133             }
134              
135 181         646 return;
136             }
137              
138             sub _start {
139 182     182   262902 my ($kernel, $self, $sender) = @_[KERNEL, OBJECT, SENDER];
140              
141 182         2059 $self->{ircd_filter} = POE::Filter::IRCD->new(
142             colonify => 1,
143             );
144 182         6089 $self->{line_filter} = POE::Filter::Line->new(
145             InputRegexp => '\015?\012',
146             OutputLiteral => "\015\012",
147             );
148             $self->{filter} = POE::Filter::Stackable->new(
149 182         13618 Filters => [$self->{line_filter}, $self->{ircd_filter}],
150             );
151              
152 182         4111 return;
153             }
154              
155             sub raw_events {
156 0     0 1 0 my ($self, $value) = @_;
157 0 0       0 $self->{raw_events} = 1 if $value;
158 0         0 return;
159             }
160              
161             sub shutdown {
162 1     1 1 3 my ($self) = shift;
163 1         20 $self->yield('shutdown', @_);
164 1         132 return;
165             }
166              
167             sub _shutdown {
168 195     195   377841 my ($kernel, $self) = @_[KERNEL, OBJECT];
169              
170 195         839 $self->{terminating} = 1;
171 195         1796 delete $self->{listeners};
172 195         48321 delete $self->{connectors};
173 195         866 delete $self->{wheels};
174 195         18080 $self->_syndicator_destroy();
175 195         77666 return;
176             }
177              
178             sub _accept_failed {
179 0     0   0 my ($kernel, $self, $operation, $errnum, $errstr, $listener_id)
180             = @_[KERNEL, OBJECT, ARG0..ARG3];
181              
182 0         0 my $port = $self->{listeners}{$listener_id}{port};
183 0         0 my $addr = $self->{listeners}{$listener_id}{addr};
184 0         0 delete $self->{listeners}{$listener_id};
185 0         0 $self->send_event(
186             "$self->{prefix}listener_failure",
187             $listener_id,
188             $operation,
189             $errnum,
190             $errstr,
191             $port,
192             $addr,
193             );
194 0         0 return;
195             }
196              
197             sub _accept_connection {
198 525     525   40228948 my ($kernel, $self, $socket, $listener_id)
199             = @_[KERNEL, OBJECT, ARG0, ARG3];
200              
201 525         11274 my (undef,$peeraddr,$peerport) = getnameinfo( CORE::getpeername( $socket ), NI_NUMERICHOST | NI_NUMERICSERV );
202 525         7547 my (undef,$sockaddr,$sockport) = getnameinfo( CORE::getsockname( $socket ), NI_NUMERICHOST | NI_NUMERICSERV );
203              
204 525         3545 s!^::ffff:!! for ( $sockaddr, $peeraddr );
205              
206 525         2044 my $listener = $self->{listeners}{$listener_id};
207              
208 525         1278 my $secured = 0;
209 525         1465 my $context = { };
210              
211 525 100 100     2782 if ($self->{got_ssl} && $listener->{usessl}) {
212 15         109 my $cb = $_[SESSION]->callback('_sock_ssl',$context);
213 15         1602 eval {
214 15         121 $socket = POE::Component::SSLify::Server_SSLify($socket,undef,$cb);
215 15         3541 $secured = 1;
216             };
217 15         52 chomp $@;
218 15 50       91 die "Failed to SSLify server socket: $@" if $@;
219             }
220              
221 525         5654 my $stats_filter = POE::Filter::ThruPut->new();
222              
223             my $wheel = POE::Wheel::ReadWrite->new(
224             Handle => $socket,
225             InputEvent => '_conn_input',
226             ErrorEvent => '_conn_error',
227             FlushedEvent => '_conn_flushed',
228             Filter => POE::Filter::Stackable->new(
229 525         9814 Filters => [$stats_filter, $self->{filter}],
230             ),
231             );
232              
233 525 50       229179 if ($wheel) {
234 525         2738 my $wheel_id = $wheel->ID();
235 525         3414 $context->{wheel_id} = $wheel_id;
236             my $ref = {
237             wheel => $wheel,
238             peeraddr => $peeraddr,
239             peerport => $peerport,
240             flooded => 0,
241             sockaddr => $sockaddr,
242             sockport => $sockport,
243             idle => $listener->{idle},
244             antiflood => $listener->{antiflood},
245 525         7605 compress => 0,
246             secured => $secured,
247             stats => $stats_filter,
248             _sent => 0,
249             };
250              
251 525 100 66     5145 my $needs_auth = $listener->{auth} && $self->{auth} ? 1 : 0;
252 525         4554 $self->send_event(
253             "$self->{prefix}connection",
254             $wheel_id,
255             $peeraddr,
256             $peerport,
257             $sockaddr,
258             $sockport,
259             $needs_auth,
260             $secured,
261             $stats_filter,
262             );
263              
264             $ref->{alarm} = $kernel->delay_set(
265             '_conn_alarm',
266             $listener->{idle},
267 525         70808 $wheel_id,
268             );
269 525         40034 $self->{wheels}{$wheel_id} = $ref;
270              
271 525 100       3864 if ( my $reason = $self->denied( $peeraddr ) ) {
272 6         22 $ref->{disconnecting} = $reason;
273 6         50 my $out = { command => 'ERROR', params => [ $reason ] };
274 6         30 $self->send_output( $out, $wheel_id );
275             }
276             }
277 525         2793 return;
278             }
279              
280             sub _sock_ssl {
281 15     15   29848 my ($kernel,$self,$first,$second) = @_[KERNEL,OBJECT,ARG0,ARG1];
282 15         52 my ($cont) = @$first;
283 15 50       97 return if !$cont->{wheel_id};
284 15         66 my $wheel_id = delete $cont->{wheel_id};
285 15 50       93 return if !$self->{wheels}{$wheel_id};
286 15         53 my ($sock,$stat,$err) = @$second;
287 15 50       52 return if !$stat;
288 15         67 my $sslinf = _get_ssl_info($sock);
289 15         43 my $ref = $self->{wheels}{$wheel_id};
290 15 50       71 $ref->{sslinf} = $sslinf if $sslinf;
291 15         59 return;
292             }
293              
294             sub _get_ssl_info {
295 29   50 29   109 my $sock = shift || return;
296 29         78 my $sslinf = eval {
297 29         270 require Net::SSLeay;
298 29         226 my $ssl = POE::Component::SSLify::SSLify_GetSSL($sock);
299 29         313 my $cipher = Net::SSLeay::get_cipher($ssl);
300 29         110 my $bits = Net::SSLeay::get_cipher_bits($ssl);
301 29         103 my $version = Net::SSLeay::version($ssl);
302 29 0       167 my $ver =
    0          
    0          
    0          
    0          
    50          
    50          
303             $version == 0x0304 ? 'TLSv1_3' :
304             $version == 0x0303 ? 'TLSv1_2' :
305             $version == 0x0302 ? 'TLSv1_1' :
306             $version == 0x0301 ? 'TLSv1' :
307             $version == 0x0300 ? 'SSLv3' :
308             $version == 0x0002 ? 'SSLv2' :
309             $version == 0xfeff ? 'DTLS1' :
310             undef;
311 29         156 return "$ver-$cipher-$bits";
312             };
313 29         75 return $sslinf;
314             }
315              
316             sub add_listener {
317 174     174 1 201238 my ($self) = shift;
318 174 50       993 croak('add_listener requires an even number of parameters') if @_ & 1;
319 174         1117 $self->yield('add_listener', @_);
320 174         15282 return;
321             }
322              
323             sub _add_listener {
324 178     178   180571 my ($kernel, $self) = @_[KERNEL, OBJECT];
325 178         1023 my %args = @_[ARG0..$#_];
326              
327 178         1013 $args{ lc($_) } = delete $args{$_} for keys %args;
328              
329 178   50     1624 my $bindaddr = $args{bindaddr} || '0.0.0.0';
330 178   50     1290 my $bindport = $args{port} || 0;
331 178   50     1134 my $idle = $args{idle} || 180;
332 178         553 my $auth = 1;
333 178         488 my $antiflood = 1;
334 178         554 my $usessl = 0;
335 178 100       788 $usessl = 1 if $args{usessl};
336 178 50 33     1100 $auth = 0 if defined $args{auth} && $args{auth} eq '0';
337 178 50 33     1030 $antiflood = 0 if defined $args{antiflood} && $args{antiflood} eq '0';
338              
339             my $listener = POE::Wheel::SocketFactory->new(
340             BindAddress => $bindaddr,
341             BindPort => $bindport,
342             SuccessEvent => '_accept_connection',
343             FailureEvent => '_accept_failed',
344             Reuse => 'on',
345             ( ip_is_ipv6( $bindaddr ) ? ( SocketDomain => AF_INET6 ) : () ),
346 178 50       1779 ($args{listenqueue} ? (ListenQueue => $args{listenqueue}) : ()),
    50          
347             );
348              
349 178         119244 my $id = $listener->ID();
350 178         1549 $self->{listeners}{$id}{wheel} = $listener;
351 178         665 $self->{listeners}{$id}{port} = $bindport;
352 178         654 $self->{listeners}{$id}{addr} = $bindaddr;
353 178         611 $self->{listeners}{$id}{idle} = $idle;
354 178         614 $self->{listeners}{$id}{auth} = $auth;
355 178         576 $self->{listeners}{$id}{antiflood} = $antiflood;
356 178         545 $self->{listeners}{$id}{usessl} = $usessl;
357              
358 178         1038 my (undef,$addr,$port) = getnameinfo( $listener->getsockname, NI_NUMERICHOST | NI_NUMERICSERV );
359 178         6729 $addr =~ s!^::ffff:!!;
360 178 50       870 if ($port) {
361 178         753 $self->{listeners}{$id}{port} = $port;
362             $self->send_event(
363 178         3213 $self->{prefix} . 'listener_add',
364             $port,
365             $id,
366             $bindaddr,
367             $usessl,
368             );
369             }
370 178         25996 return;
371             }
372              
373             sub del_listener {
374 10     10 1 13195 my ($self) = shift;
375 10 50       64 croak("add_listener requires an even number of parameters") if @_ & 1;
376 10         67 $self->yield('del_listener', @_);
377 10         1487 return;
378             }
379              
380             sub _del_listener {
381 10     10   9209 my ($kernel, $self) = @_[KERNEL, OBJECT];
382 10         69 my %args = @_[ARG0..$#_];
383              
384 10         93 $args{lc $_} = delete $args{$_} for keys %args;
385 10         42 my $listener_id = delete $args{listener};
386 10         36 my $port = delete $args{port};
387              
388 10 50       132 if ($self->_listener_exists($listener_id)) {
    50          
389 0         0 my $port = $self->{listeners}{$listener_id}{port};
390 0         0 my $addr = $self->{listeners}{$listener_id}{addr};
391 0         0 delete $self->{listeners}{$listener_id};
392             $self->send_event(
393 0         0 $self->{prefix} . 'listener_del',
394             $port,
395             $listener_id,
396             $addr,
397             );
398             }
399             elsif (defined $port) {
400 10         31 while (my ($id, $listener) = each %{ $self->{listeners } }) {
  20         4286  
401 10 50       62 if ($listener->{port} == $port) {
402 10         35 my $addr = $listener->{addr};
403 10         27 delete $self->{listeners}{$id};
404             $self->send_event(
405 10         77 $self->{prefix} . 'listener_del',
406             $port,
407             $listener_id,
408             $addr,
409             );
410             }
411             }
412             }
413              
414 10         69 return;
415             }
416              
417             sub _listener_exists {
418 10     10   36 my $self = shift;
419 10   50     95 my $listener_id = shift || return;
420 0 0       0 return 1 if defined $self->{listeners}{$listener_id};
421 0         0 return;
422             }
423              
424             sub add_connector {
425 3     3 1 16 my $self = shift;
426 3 50       16 croak("add_connector requires an even number of parameters") if @_ & 1;
427 3         21 $self->yield('add_connector', @_);
428 3         556 return;
429             }
430              
431             sub _add_connector {
432 3     3   717 my ($kernel, $self, $sender) = @_[KERNEL, OBJECT, SENDER];
433 3         25 my %args = @_[ARG0..$#_];
434              
435 3         29 $args{lc $_} = delete $args{$_} for keys %args;
436              
437 3         11 my $remoteaddress = $args{remoteaddress};
438 3         8 my $remoteport = $args{remoteport};
439              
440 3 50 33     24 return if !$remoteaddress || !$remoteport;
441              
442 3   50     18 $args{idle} = $args{idle} || 180;
443              
444             my $wheel = POE::Wheel::SocketFactory->new(
445             SocketProtocol => 'tcp',
446             RemoteAddress => $remoteaddress,
447             RemotePort => $remoteport,
448             SuccessEvent => '_sock_up',
449             FailureEvent => '_sock_failed',
450 3 50       36 ($args{bindaddress} ? (BindAddress => $args{bindaddress}) : ()),
    50          
451             (ip_is_ipv6($remoteaddress) ? (SocketDomain => AF_INET6) : () ),
452             );
453              
454 3 50       2334 if ($wheel) {
455 3         12 $args{wheel} = $wheel;
456 3         18 $self->{connectors}{$wheel->ID()} = \%args;
457             }
458 3         25 return;
459             }
460              
461             sub _sock_failed {
462 0     0   0 my ($kernel, $self, $op, $errno, $errstr, $connector_id)
463             = @_[KERNEL, OBJECT, ARG0..ARG3];
464              
465 0         0 my $ref = delete $self->{connectors}{$connector_id};
466 0         0 delete $ref->{wheel};
467 0         0 $self->send_event("$self->{prefix}socketerr", $ref, $op, $errno, $errstr);
468 0         0 return;
469             }
470              
471             sub _sock_up {
472 3     3   1451 my ($kernel, $self, $socket, $connector_id)
473             = @_[KERNEL, OBJECT, ARG0, ARG3];
474              
475 3         60 my (undef,$peeraddr,$peerport) = getnameinfo( CORE::getpeername( $socket ), NI_NUMERICHOST | NI_NUMERICSERV );
476 3         42 my (undef,$sockaddr,$sockport) = getnameinfo( CORE::getsockname( $socket ), NI_NUMERICHOST | NI_NUMERICSERV );
477              
478 3         24 s!^::ffff:!! for ( $sockaddr, $peeraddr );
479              
480 3         16 my $cntr = delete $self->{connectors}{$connector_id};
481 3 50 66     20 if ($self->{got_ssl} && $cntr->{usessl}) {
482 1         2 eval {
483 1         2 my $ctx = SSLify_ContextCreate( @{ $self->{sslify_options} } );
  1         9  
484 1         138 $socket = POE::Component::SSLify::Client_SSLify($socket,undef,undef,$ctx);
485             };
486 1         378 chomp $@;
487 1 50       13 die "Failed to SSLify client socket: $@" if $@;
488             }
489              
490 3         34 my $stats_filter = POE::Filter::ThruPut->new();
491              
492             my $wheel = POE::Wheel::ReadWrite->new(
493             Handle => $socket,
494             InputEvent => '_conn_input',
495             ErrorEvent => '_conn_error',
496             FlushedEvent => '_conn_flushed',
497             Filter => POE::Filter::Stackable->new(
498 3         49 Filters => [$stats_filter, $self->{filter}],
499             ),
500             );
501              
502 3 50       1189 return if !$wheel;
503 3         24 my $wheel_id = $wheel->ID();
504             my $ref = {
505             wheel => $wheel,
506             peeraddr => $peeraddr,
507             peerport => $peerport,
508             sockaddr => $sockaddr,
509             sockport => $sockport,
510             idle => $cntr->{idle},
511 3         75 antiflood => 0,
512             compress => 0,
513             stats => $stats_filter,
514             _sent => 0,
515             };
516              
517 3         21 $self->{wheels}{$wheel_id} = $ref;
518             $self->send_event(
519             "$self->{prefix}connected",
520             $wheel_id,
521             $peeraddr,
522             $peerport,
523             $sockaddr,
524             $sockport,
525             $cntr->{name},
526 3         30 $stats_filter,
527             );
528 3         470 return;
529             }
530              
531             sub _anti_flood {
532 0     0   0 my ($self, $wheel_id, $input) = @_;
533 0         0 my $current_time = time();
534              
535 0 0 0     0 return if !$wheel_id || !$self->connection_exists($wheel_id) || !$input;
      0        
536              
537             SWITCH: {
538 0 0       0 if ($self->{wheels}->{ $wheel_id }->{flooded}) {
  0         0  
539 0         0 last SWITCH;
540             }
541 0 0 0     0 if (!$self->{wheels}{$wheel_id}{timer}
542             || $self->{wheels}{$wheel_id}{timer} < $current_time) {
543              
544 0         0 $self->{wheels}{$wheel_id}{timer} = $current_time;
545 0         0 my $event = "$self->{prefix}cmd_" . lc $input->{command};
546 0         0 $self->send_event($event, $wheel_id, $input);
547 0         0 last SWITCH;
548             }
549 0 0       0 if ($self->{wheels}{$wheel_id}{timer} <= $current_time + 10) {
550 0         0 $self->{wheels}{$wheel_id}{timer} += 1;
551 0         0 push @{ $self->{wheels}{$wheel_id}{msq} }, $input;
  0         0  
552 0         0 push @{ $self->{wheels}{$wheel_id}{alarm_ids} },
553             $poe_kernel->alarm_set(
554             '_event_dispatcher',
555             $self->{wheels}{$wheel_id}{timer},
556 0         0 $wheel_id
557             );
558 0         0 last SWITCH;
559             }
560              
561 0         0 $self->{wheels}{$wheel_id}{flooded} = 1;
562 0         0 $self->send_event("$self->{prefix}connection_flood", $wheel_id);
563             }
564              
565 0         0 return 1;
566             }
567              
568             sub _conn_error {
569 241     241   26929967 my ($self, $errstr, $wheel_id) = @_[OBJECT, ARG2, ARG3];
570 241 50       1171 return if !$self->connection_exists($wheel_id);
571             $self->_disconnected(
572             $wheel_id,
573             $errstr || $self->{wheels}{$wheel_id}{disconnecting}
574 241   66     2355 );
575 241         75060 return;
576             }
577              
578             sub _conn_alarm {
579 0     0   0 my ($kernel, $self, $wheel_id) = @_[KERNEL, OBJECT, ARG0];
580 0 0       0 return if !$self->connection_exists($wheel_id);
581 0         0 my $conn = $self->{wheels}{$wheel_id};
582              
583             $self->send_event(
584             "$self->{prefix}connection_idle",
585             $wheel_id,
586             $conn->{idle},
587 0         0 );
588             $conn->{alarm} = $kernel->delay_set(
589             '_conn_alarm',
590             $conn->{idle},
591 0         0 $wheel_id,
592             );
593              
594 0         0 return;
595             }
596              
597             sub _conn_flushed {
598 2794     2794   2511927 my ($kernel, $self, $wheel_id) = @_[KERNEL, OBJECT, ARG0];
599 2794 50       9234 return if !$self->connection_exists($wheel_id);
600              
601             {
602 2794         5245 my $sent = $self->{wheels}{$wheel_id}{stats}->send();
  2794         12838  
603 2794         16452 my $tally = $sent - $self->{wheels}{$wheel_id}{_sent};
604 2794         6241 $self->{wheels}{$wheel_id}{_sent} = $sent;
605 2794         6784 $self->{_globalstats}{sent} += $tally;
606             }
607              
608 2794 100       8372 if ($self->{wheels}{$wheel_id}{disconnecting}) {
609             $self->_disconnected(
610             $wheel_id,
611             $self->{wheels}{$wheel_id}{disconnecting},
612 246         4049 );
613 246         88156 return;
614             }
615              
616 2548 100       6947 if ($self->{wheels}{$wheel_id}{compress_pending}) {
617 1         2 delete $self->{wheels}{$wheel_id}{compress_pending};
618 1         6 $self->{wheels}{$wheel_id}{wheel}->get_input_filter()->unshift(
619             POE::Filter::Zlib::Stream->new(),
620             );
621 1         1236 $self->send_event("$self->{prefix}compressed_conn", $wheel_id);
622 1         127 return;
623             }
624 2547         6775 return;
625             }
626              
627             sub _conn_input {
628 3941     3941   103122458 my ($kernel, $self, $input, $wheel_id) = @_[KERNEL, OBJECT, ARG0, ARG1];
629 3941         10504 my $conn = $self->{wheels}{$wheel_id};
630              
631             # We aren't interested if they are disconnecting
632 3941 100       12343 return if $conn->{disconnecting};
633              
634             {
635 3940         6884 require bytes;
  3940         25042  
636 3940         14772 $self->{_globalstats}{recv} += bytes::length($input->{raw_line}) + 2;
637             }
638              
639 3940 50       18940 if ($self->{raw_events}) {
640             $self->send_event(
641             "$self->{prefix}raw_input",
642             $wheel_id,
643             $input->{raw_line},
644 0         0 );
645             }
646 3940         8418 $conn->{msgs}{recv}++;
647 3940         8454 $conn->{seen} = time();
648 3940         16043 $kernel->delay_adjust($conn->{alarm}, $conn->{idle});
649              
650             # TODO: Antiflood code
651 3940 50       972794 if ($self->antiflood($wheel_id)) {
652 0         0 $self->_anti_flood($wheel_id, $input);
653             }
654             else {
655 3940         14721 my $event = "$self->{prefix}cmd_" . lc $input->{command};
656 3940         15134 $self->send_event($event, $wheel_id, $input);
657             }
658 3940         483391 return;
659             }
660              
661             sub _event_dispatcher {
662 0     0   0 my ($kernel, $self, $wheel_id) = @_[KERNEL, OBJECT, ARG0];
663              
664 0 0 0     0 if (!$self->connection_exists($wheel_id)
665             || $self->{wheels}{$wheel_id}{flooded}) {
666 0         0 return;
667             }
668              
669 0         0 shift @{ $self->{wheels}{$wheel_id}{alarm_ids} };
  0         0  
670 0         0 my $input = shift @{ $self->{wheels}{$wheel_id}{msq} };
  0         0  
671              
672 0 0       0 if ($input) {
673 0         0 my $event = "$self->{prefix}cmd_" . lc $input->{command};
674 0         0 $self->send_event($event, $wheel_id, $input);
675             }
676 0         0 return;
677             }
678              
679             sub send_output {
680 12695     12695 1 33779 my ($self, $output) = splice @_, 0, 2;
681              
682 12695 100 66     55587 if ($output && ref $output eq 'HASH') {
683 12465         27447 for my $id (grep { $self->connection_exists($_) } @_) {
  9225         21769  
684 9146 50       92096 if ($self->{raw_events}) {
685 0         0 my $out = $self->{filter}->put([$output])->[0];
686 0         0 $out =~ s/\015\012$//;
687 0         0 $self->send_event("$self->{prefix}raw_output", $id, $out);
688             }
689 9146         20088 $self->{wheels}{$id}{msgs}{sent}++;
690 9146         31217 $self->{wheels}{$id}{wheel}->put($output);
691             }
692             }
693              
694 12695         1089438 return;
695             }
696              
697             sub _send_output {
698 0     0   0 $_[OBJECT]->send_output(@_[ARG0..$#_]);
699 0         0 return;
700             }
701              
702             sub antiflood {
703 4482     4482 1 11133 my ($self, $wheel_id, $value) = @_;
704              
705 4482 50       12784 return if !$self->connection_exists($wheel_id);
706 4482 50       16603 return 0 if !$self->{antiflood};
707 0 0       0 return $self->{wheels}{$wheel_id}{antiflood} if !defined $value;
708              
709 0 0       0 if (!$value) {
710             # Flush pending messages from that wheel
711 0         0 while (my $alarm_id = shift @{ $self->{wheels}{$wheel_id}{alarm_ids} }) {
  0         0  
712 0         0 $poe_kernel->alarm_remove($alarm_id);
713 0         0 my $input = shift @{ $self->{wheels}{$wheel_id}{msq} };
  0         0  
714              
715 0 0       0 if ($input) {
716 0         0 my $event = "$self->{prefix}cmd_" . lc $input->{command};
717 0         0 $self->send_event($event, $wheel_id, $input);
718             }
719             }
720             }
721              
722 0         0 $self->{wheels}{$wheel_id}{antiflood} = $value;
723 0         0 return;
724             }
725              
726             sub compressed_link {
727 2     2 1 7 my ($self, $wheel_id, $value, $cntr) = @_;
728 2 50       6 return if !$self->connection_exists($wheel_id);
729 2 50       6 return $self->{wheels}{$wheel_id}{compress} if !defined $value;
730              
731 2 50       5 if ($value) {
732 2 50       7 if (!$self->{got_zlib}) {
733 2         4 eval {
734 2         13 require POE::Filter::Zlib::Stream;
735 2         5 $self->{got_zlib} = 1;
736             };
737 2         6 chomp $@;
738 2 50       5 croak($@) if !$self->{got_zlib};
739             }
740 2 100       6 if ($cntr) {
741 1         5 $self->{wheels}{$wheel_id}{wheel}->get_input_filter()->unshift(
742             POE::Filter::Zlib::Stream->new()
743             );
744 1         1021 $self->send_event(
745             "$self->{prefix}compressed_conn",
746             $wheel_id,
747             );
748             }
749             else {
750 1         5 $self->{wheels}{$wheel_id}{compress_pending} = 1;
751             }
752             }
753             else {
754 0         0 $self->{wheels}{$wheel_id}{wheel}->get_input_filter()->shift();
755             }
756              
757 2         126 $self->{wheels}{$wheel_id}{compress} = $value;
758 2         6 return;
759             }
760              
761             sub disconnect {
762 240     240 1 918 my ($self, $wheel_id, $string) = @_;
763 240 50 33     1764 return if !$wheel_id || !$self->connection_exists($wheel_id);
764 240   50     2317 $self->{wheels}{$wheel_id}{disconnecting} = $string || 'Client Quit';
765 240         894 return;
766             }
767              
768             sub _disconnected {
769 487     487   1705 my ($self, $wheel_id, $errstr) = @_;
770 487 50 33     2810 return if !$wheel_id || !$self->connection_exists($wheel_id);
771              
772 487         1686 my $conn = delete $self->{wheels}{$wheel_id};
773 487         1330 for my $alarm_id ($conn->{alarm}, @{ $conn->{alarm_ids} }) {
  487         2922  
774 487         2638 $poe_kernel->alarm_remove($_);
775             }
776             $self->send_event(
777 487   100     14698 "$self->{prefix}disconnected",
778             $wheel_id,
779             $errstr || 'Remote host closed the connection',
780             );
781              
782 487 50       69207 if ( $^O =~ /(cygwin|MSWin)/ ) {
783 0         0 $conn->{wheel}->shutdown_input();
784 0         0 $conn->{wheel}->shutdown_output();
785             }
786              
787 487         4185 return 1;
788             }
789              
790             sub connection_info {
791 0     0 1 0 my ($self, $wheel_id) = @_;
792 0 0       0 return if !$self->connection_exists($wheel_id);
793             return map {
794 0         0 $self->{wheels}{$wheel_id}{$_}
  0         0  
795             } qw(peeraddr peerport sockaddr sockport);
796             }
797              
798             sub connection_exists {
799 17473     17473 1 35516 my ($self, $wheel_id) = @_;
800 17473 100 66     76858 return if !$wheel_id || !defined $self->{wheels}{$wheel_id};
801 17394         51233 return 1;
802             }
803              
804             sub connection_secured {
805 484     484 0 1622 my ($self, $wheel_id) = @_;
806 484 50 33     3534 return if !$wheel_id || !defined $self->{wheels}{$wheel_id};
807 484 100       3218 return if !$self->{wheels}{$wheel_id}{secured};
808 14         39 my $sslinfo = $self->{wheels}{$wheel_id}{sslinfo};
809 14 50       48 if (!$sslinfo) {
810 14         65 my $sock = $self->{wheels}{$wheel_id}{wheel}->get_input_handle();
811 14         117 $sslinfo = _get_ssl_info($sock);
812 14 50       88 $self->{wheels}{$wheel_id}{sslinfo} = $sslinfo
813             if $sslinfo;
814             }
815 14         88 return $sslinfo;
816             }
817              
818             sub connection_certfp {
819 12     12 0 50 my ($self, $wheel_id) = @_;
820 12 50 33     103 return if !$wheel_id || !defined $self->{wheels}{$wheel_id};
821 12 50       51 return if !$self->{wheels}{$wheel_id}{secured};
822 12         71 my $sock = $self->{wheels}{$wheel_id}{wheel}->get_input_handle();
823 12         167 my $fp = eval {
824 12         63 my $ssl = POE::Component::SSLify::SSLify_GetSSL($sock);
825 12         151 my $x509 = Net::SSLeay::get_peer_certificate($ssl);
826 12         378 return Net::SSLeay::X509_get_fingerprint($x509,'sha256');
827             };
828 12 100       157 $fp =~ s!:!!g if $fp;
829 12         66 return $fp;
830             }
831              
832             sub connection_stats {
833 0     0 0 0 my ($self, $wheel_id) = @_;
834 0 0 0     0 return if !$wheel_id || !defined $self->{wheels}{$wheel_id};
835 0         0 return $self->{wheels}{$wheel_id}{stats}->stats();
836             }
837              
838             sub connection_msgs {
839 2     2 0 9 my ($self, $wheel_id) = @_;
840 2 50 33     10 return if !$wheel_id || !defined $self->{wheels}{$wheel_id};
841 2         6 return [ map { $self->{wheels}{$wheel_id}{msgs}{$_} } qw[sent recv] ];
  4         13  
842             }
843              
844             sub _conn_flooded {
845 0     0   0 my $self = shift;
846 0   0     0 my $conn_id = shift || return;
847 0 0       0 return if !$self->connection_exists($conn_id);
848 0         0 return $self->{wheels}{$conn_id}{flooded};
849             }
850              
851             sub add_denial {
852 6     6 1 24 my $self = shift;
853 6   50     30 my $netmask = shift || return;
854 6   50     22 my $reason = shift || 'Denied';
855              
856 6 50       16 if ( ! eval { $netmask->isa('Net::Netmask') } ) {
  6         67  
857 6         36 $netmask = Net::CIDR::cidrvalidate( $netmask );
858             }
859              
860 6 50       3900 if ( !$netmask ) {
861 0         0 carp("Failed to validate netmask");
862 0         0 return;
863             }
864              
865 6         61 $self->{denials}{$netmask} = {
866             blk => $netmask,
867             reason => $reason,
868             };
869 6         21 return 1;
870             }
871              
872             sub del_denial {
873 5     5 1 19 my $self = shift;
874 5   50     24 my $netmask = shift || return;
875 5 50       39 return if !$self->{denials}{$netmask};
876 5         26 delete $self->{denials}{$netmask};
877 5         21 return 1;
878             }
879              
880             sub add_exemption {
881 0     0 1 0 my $self = shift;
882 0   0     0 my $netmask = shift || return;
883              
884 0 0       0 if ( !$netmask->isa('Net::Netmask') ) {
885 0         0 $netmask = Net::CIDR::cidrvalidate( $netmask );
886             }
887              
888 0 0       0 if ( !$netmask ) {
889 0         0 carp("Failed to validate netmask");
890 0         0 return;
891             }
892              
893 0 0       0 if (!$self->{exemptions}{$netmask}) {
894 0         0 $self->{exemptions}{$netmask} = $netmask;
895             }
896 0         0 return 1;
897             }
898              
899             sub del_exemption {
900 0     0 1 0 my $self = shift;
901 0   0     0 my $netmask = shift || return;
902 0 0       0 return if !$self->{exemptions}{$netmask};
903 0         0 delete $self->{exemptions}{$netmask};
904 0         0 return 1;
905             }
906              
907             sub denied {
908 537     537 1 33989 my $self = shift;
909 537   50     2292 my $ipaddr = shift || return;
910 537 50       3167 return if $self->exempted($ipaddr);
911              
912 537         1261 for my $mask (keys %{ $self->{denials} }) {
  537         2226  
913 11 50 33     23 if ( eval { $self->{denials}{$mask}{blk}->isa('Net::Netmask') } && $self->{denials}{$mask}{blk}->match($ipaddr)) {
  11 50       165  
914 0         0 return $self->{denials}{$mask}{reason};
915             }
916             elsif ( Net::CIDR::cidrlookup( $ipaddr, $self->{denials}{$mask}{blk} ) ) {
917 11         3833 return $self->{denials}{$mask}{reason};
918             }
919             }
920              
921 526         2285 return;
922             }
923              
924             sub exempted {
925 537     537 1 1326 my $self = shift;
926 537   50     1898 my $ipaddr = shift || return;
927 537         1216 for my $mask (keys %{ $self->{exemptions} }) {
  537         2787  
928 0 0 0     0 return 1 if $self->{exemptions}{$mask}->isa('Net::Netmask') && $self->{exemptions}{$mask}->match($ipaddr);
929 0 0       0 return 1 if Net::CIDR::cidrlookup( $ipaddr, $self->{exemptions}{$mask} );
930             }
931 537         2090 return;
932             }
933              
934             1;
935              
936             =encoding utf8
937              
938             =head1 NAME
939              
940             POE::Component::Server::IRC::Backend - A POE component class that provides network connection abstraction for POE::Component::Server::IRC
941              
942             =head1 SYNOPSIS
943              
944             package MyIRCD;
945              
946             use strict;
947             use warnings;
948             use base 'POE::Component::Server::IRC::Backend';
949              
950             sub spawn {
951             my ($package, %args) = @_;
952              
953             my $self = $package->create(prefix => 'ircd_', @_);
954              
955             # process %args ...
956              
957             return $self;
958             }
959              
960             =head1 DESCRIPTION
961              
962             POE::Component::Server::IRC::Backend - A POE component class that provides
963             network connection abstraction for
964             L. It uses a
965             plugin system. See
966             L
967             for details.
968              
969             =head1 CONSTRUCTOR
970              
971             =head2 C
972              
973             Returns an object. Accepts the following parameters, all are optional:
974              
975             =over 4
976              
977             =item * B<'alias'>, a POE::Kernel alias to set;
978              
979             =item * B<'auth'>, set to a false value to globally disable IRC
980             authentication, default is auth is enabled;
981              
982             =item * B<'antiflood'>, set to a false value to globally disable flood
983             protection, default is true;
984              
985             =item * B<'prefix'>, this is the prefix that is used to generate event
986             names that the component produces. The default is 'ircd_'.
987              
988             =item * B<'states'>, an array reference of extra objects states for the IRC
989             daemon's POE sessions. The elements can be array references of states
990             as well as hash references of state => handler pairs.
991              
992             =item * B<'plugin_debug'>, set to a true value to print plugin debug info.
993             Default is false.
994              
995             =item * B<'options'>, a hashref of options to L
996              
997             =item * B<'raw_events'>, whether to send L events.
998             False by default. Can be enabled later with L|/raw_events>;
999              
1000             =item * B<'sslify_options'>, an array reference of items that are passed
1001             to L C. Used to supply x509 certificate
1002             and key;
1003              
1004             =back
1005              
1006             If the component is created from within another session, that session will
1007             be automagcially registered with the component to receive events and get
1008             an 'ircd_backend_registered' event.
1009              
1010             =head1 METHODS
1011              
1012             =head2 General
1013              
1014             =head3 C
1015              
1016             Takes no arguments. Terminates the component. Removes all listeners and
1017             connectors. Disconnects all current client and server connections. This
1018             is a shorthand for C<< $ircd->yield('shutdown') >>.
1019              
1020             =head3 C
1021              
1022             I>
1023              
1024             Takes no arguments. Returns the ID of the component's session. Ideal for
1025             posting events to the component.
1026              
1027             =head3 C
1028              
1029             I>
1030              
1031             Takes no arguments. Returns the session alias that has been set through
1032             L|/create>'s B<'alias'> argument.
1033              
1034             =head3 C
1035              
1036             I>
1037              
1038             This method provides an alternative object based means of posting events
1039             to the component. First argument is the event to post, following arguments
1040             are sent as arguments to the resultant post.
1041              
1042             =head3 C
1043              
1044             I>
1045              
1046             This method provides an alternative object based means of calling events
1047             to the component. First argument is the event to call, following arguments
1048             are sent as arguments to the resultant call.
1049              
1050             =head3 C
1051              
1052             I>
1053              
1054             This method provides a way of posting delayed events to the component. The
1055             first argument is an arrayref consisting of the delayed command to post and
1056             any command arguments. The second argument is the time in seconds that one
1057             wishes to delay the command being posted.
1058              
1059             Returns an alarm ID that can be used with L|/delay_remove>
1060             to cancel the delayed event. This will be undefined if something went
1061             wrong.
1062              
1063             =head3 C
1064              
1065             I>
1066              
1067             This method removes a previously scheduled delayed event from the
1068             component. Takes one argument, the C that was returned by a
1069             L|/delay> method call.
1070              
1071             Returns an arrayref that was originally requested to be delayed.
1072              
1073             =head3 C
1074              
1075             I>
1076              
1077             Sends an event through the component's event handling system. These will
1078             get processed by plugins then by registered sessions. First argument is
1079             the event name, followed by any parameters for that event.
1080              
1081             =head3 C
1082              
1083             I>
1084              
1085             This sends an event right after the one that's currently being processed.
1086             Useful if you want to generate some event which is directly related to
1087             another event so you want them to appear together. This method can only be
1088             called when POE::Component::IRC is processing an event, e.g. from one of
1089             your event handlers. Takes the same arguments as
1090             L|/send_event>.
1091              
1092             =head3 C
1093              
1094             I>
1095              
1096             This will send an event to be processed immediately. This means that if an
1097             event is currently being processed and there are plugins or sessions which
1098             will receive it after you do, then an event sent with C
1099             will be received by those plugins/sessions I the current event.
1100             Takes the same arguments as L|/send_event>.
1101              
1102             =head3 C
1103              
1104             If called with a true value, raw events (L|/ircd_raw_input>
1105             and L|/ircd_raw_output>) will be enabled.
1106              
1107             =head2 Connections
1108              
1109             =head3 C
1110              
1111             Takes two arguments, a connection id and true/false value. If value is
1112             specified antiflood protection is enabled or disabled accordingly for the
1113             specified connection. If a value is not specified the current status of
1114             antiflood protection is returned. Returns undef on error.
1115              
1116             =head3 C
1117              
1118             Takes two arguments, a connection id and true/false value. If a value is
1119             specified, compression will be enabled or disabled accordingly for the
1120             specified connection. If a value is not specified the current status of
1121             compression is returned. Returns undef on error.
1122              
1123             =head3 C
1124              
1125             Requires on argument, the connection id you wish to disconnect. The
1126             component will terminate the connection the next time that the wheel input
1127             is flushed, so you may send some sort of error message to the client on
1128             that connection. Returns true on success, undef on error.
1129              
1130             =head3 C
1131              
1132             Requires one argument, a connection id. Returns true value if the connection
1133             exists, false otherwise.
1134              
1135             =head3 C
1136              
1137             Takes one argument, a connection_id. Returns a list consisting of: the IP
1138             address of the peer; the port on the peer; our socket address; our socket
1139             port. Returns undef on error.
1140              
1141             my ($peeraddr, $peerport, $sockaddr, $sockport) = $ircd->connection_info($conn_id);
1142              
1143             =head3 C
1144              
1145             Takes one mandatory argument and one optional. The first mandatory
1146             argument is an address or CIDR as understood by L::cidrvalidate that will be used to
1147             check connecting IP addresses against. The second optional argument is a
1148             reason string for the denial.
1149              
1150             =head3 C
1151              
1152             Takes one mandatory argument, an address or CIDR as understood by L::cidrvalidate to
1153             remove from the current denial list.
1154              
1155             =head3 C
1156              
1157             Takes one argument, an IP address. Returns true or false depending on
1158             whether that IP is denied or not.
1159              
1160             =head3 C
1161              
1162             Takes one mandatory argument, an address or CIDR as understood by L::cidrvalidate that
1163             will be checked against connecting IP addresses for exemption from denials.
1164              
1165             =head3 C
1166              
1167             Takes one mandatory argument, an address or CIDR as understood by L::cidrvalidate to
1168             remove from the current exemption list.
1169              
1170             =head3 C
1171              
1172             Takes one argument, an IP address. Returns true or false depending on
1173             whether that IP is exempt from denial or not.
1174              
1175             =head2 Plugins
1176              
1177             =head3 C
1178              
1179             I>
1180              
1181             Returns the L
1182             object.
1183              
1184             =head3 C
1185              
1186             I>
1187              
1188             Accepts two arguments:
1189              
1190             The alias for the plugin
1191             The actual plugin object
1192             Any number of extra arguments
1193              
1194             The alias is there for the user to refer to it, as it is possible to have
1195             multiple plugins of the same kind active in one Object::Pluggable object.
1196              
1197             This method goes through the pipeline's C method, which will call
1198             C<< $plugin->plugin_register($pluggable, @args) >>.
1199              
1200             Returns the number of plugins now in the pipeline if plugin was
1201             initialized, C/an empty list if not.
1202              
1203             =head3 C
1204              
1205             I>
1206              
1207             Accepts the following arguments:
1208              
1209             The alias for the plugin or the plugin object itself
1210             Any number of extra arguments
1211              
1212             This method goes through the pipeline's C method, which will call
1213             C<< $plugin->plugin_unregister($pluggable, @args) >>.
1214              
1215             Returns the plugin object if the plugin was removed, C/an empty list
1216             if not.
1217              
1218             =head3 C
1219              
1220             I>
1221              
1222             Accepts the following arguments:
1223              
1224             The alias for the plugin
1225              
1226             This method goes through the pipeline's C method.
1227              
1228             Returns the plugin object if it was found, C/an empty list if not.
1229              
1230             =head3 C
1231              
1232             I>
1233              
1234             Takes no arguments.
1235              
1236             Returns a hashref of plugin objects, keyed on alias, or an empty list if
1237             there are no plugins loaded.
1238              
1239             =head3 C
1240              
1241             I>
1242              
1243             Takes no arguments.
1244              
1245             Returns an arrayref of plugin objects, in the order which they are
1246             encountered in the pipeline.
1247              
1248             =head3 C
1249              
1250             I>
1251              
1252             Accepts the following arguments:
1253              
1254             The plugin object
1255             The type of the hook (the hook types are specified with _pluggable_init()'s 'types')
1256             The event name[s] to watch
1257              
1258             The event names can be as many as possible, or an arrayref. They correspond
1259             to the prefixed events and naturally, arbitrary events too.
1260              
1261             You do not need to supply events with the prefix in front of them, just the
1262             names.
1263              
1264             It is possible to register for all events by specifying 'all' as an event.
1265              
1266             Returns 1 if everything checked out fine, C/an empty list if
1267             something is seriously wrong.
1268              
1269             =head3 C
1270              
1271             I>
1272              
1273             Accepts the following arguments:
1274              
1275             The plugin object
1276             The type of the hook (the hook types are specified with _pluggable_init()'s 'types')
1277             The event name[s] to unwatch
1278              
1279             The event names can be as many as possible, or an arrayref. They correspond
1280             to the prefixed events and naturally, arbitrary events too.
1281              
1282             You do not need to supply events with the prefix in front of them, just the
1283             names.
1284              
1285             It is possible to register for all events by specifying 'all' as an event.
1286              
1287             Returns 1 if all the event name[s] was unregistered, undef if some was not
1288             found.
1289              
1290             =head1 INPUT EVENTS
1291              
1292             These are POE events that the component will accept:
1293              
1294             =head2 C
1295              
1296             I>
1297              
1298             Takes N arguments: a list of event names that your session wants to listen
1299             for, minus the C prefix.
1300              
1301             $ircd->yield('register', qw(connected disconnected));
1302              
1303             The special argument 'all' will register your session for all events.
1304             Registering will generate an L|/ircd_registered>
1305             event that your session can trap.
1306              
1307             =head2 C
1308              
1309             I>
1310              
1311             Takes N arguments: a list of event names which you I want to
1312             receive. If you've previously done a L|/register>
1313             for a particular event which you no longer care about, this event will
1314             tell the component to stop sending them to you. (If you haven't, it just
1315             ignores you. No big deal.)
1316              
1317             If you have registered with 'all', attempting to unregister individual
1318             events such as 'connected', etc. will not work. This is a 'feature'.
1319              
1320             =head2 C
1321              
1322             Takes a number of arguments. Adds a new listener.
1323              
1324             =over 4
1325              
1326             =item * B<'port'>, the TCP port to listen on. Default is a random port;
1327              
1328             =item * B<'auth'>, enable or disable auth sub-system for this listener.
1329             Enabled by default;
1330              
1331             =item * B<'bindaddr'>, specify a local address to bind the listener to;
1332              
1333             =item * B<'listenqueue'>, change the SocketFactory's ListenQueue;
1334              
1335             =item * B<'usessl'>, whether the listener should use SSL. Default is
1336             false;
1337              
1338             =item * B<'antiflood'>, whether the listener should use flood protection.
1339             Defaults is true;
1340              
1341             =item * B<'idle'>, the time, in seconds, after which a connection will be
1342             considered idle. Defaults is 180.
1343              
1344             =back
1345              
1346             =head2 C
1347              
1348             Takes one of the following arguments:
1349              
1350             =over 4
1351              
1352             =item * B<'listener'>, a previously returned listener ID;
1353              
1354             =item * B<'port'>, a listening port;
1355              
1356             =back
1357              
1358             The listener will be deleted. Note: any connected clients on that port
1359             will not be disconnected.
1360              
1361             =head2 C
1362              
1363             Takes two mandatory arguments, B<'remoteaddress'> and B<'remoteport'>.
1364             Opens a TCP connection to specified address and port.
1365              
1366             =over 4
1367              
1368             =item * B<'remoteaddress'>, hostname or IP address to connect to;
1369              
1370             =item * B<'remoteport'>, the TCP port on the remote host;
1371              
1372             =item * B<'bindaddress'>, a local address to bind from (optional);
1373              
1374             =item * B<'idle'>, the time, in seconds, after which a connection will be
1375             considered idle. Defaults is 180;
1376              
1377             =item * B<'usessl'>, whether the connection should use SSL. Default is
1378             false;
1379              
1380             =back
1381              
1382             =head2 C
1383              
1384             Takes a hashref and one or more connection IDs.
1385              
1386             $ircd->yield(
1387             'send_output',
1388             {
1389             prefix => 'blah!~blah@blah.blah.blah',
1390             command => 'PRIVMSG',
1391             params => ['#moo', 'cows go moo, not fish :D']
1392             },
1393             @list_of_connection_ids,
1394             );
1395              
1396             =head2 C
1397              
1398             I>
1399              
1400             Takes no arguments. Terminates the component. Removes all listeners and
1401             connectors. Disconnects all current client and server connections.
1402              
1403             =head1 OUTPUT EVENTS
1404              
1405             These following events are sent to interested sessions.
1406              
1407             =head2 C
1408              
1409             I>
1410              
1411             =over
1412              
1413             =item Emitted: when a session registers with the component;
1414              
1415             =item Target: the registering session;
1416              
1417             =item Args:
1418              
1419             =over 4
1420              
1421             =item * C: the component's object;
1422              
1423             =back
1424              
1425             =back
1426              
1427             =head2 C
1428              
1429             =over
1430              
1431             =item Emitted: when a client connects to one of the component's listeners;
1432              
1433             =item Target: all plugins and registered sessions
1434              
1435             =item Args:
1436              
1437             =over 4
1438              
1439             =item * C: the conn id;
1440              
1441             =item * C: their ip address;
1442              
1443             =item * C: their tcp port;
1444              
1445             =item * C: our ip address;
1446              
1447             =item * C: our socket port;
1448              
1449             =item * C: a boolean indicating whether the client needs to be authed
1450              
1451             =item * C: a boolean indicating whether the client is securely connected
1452              
1453             =back
1454              
1455             =back
1456              
1457             =head2 C
1458              
1459             =over
1460              
1461             =item Emitted: after a client has connected and the component has validated
1462             hostname and ident;
1463              
1464             =item Target: Target: all plugins and registered sessions;
1465              
1466             =item Args:
1467              
1468             =over 4
1469              
1470             =item * C, the connection id;
1471              
1472             =item * C, a HASHREF with the following keys: 'ident' and 'hostname';
1473              
1474             =back
1475              
1476             =back
1477              
1478             =head2 C
1479              
1480             =over
1481              
1482             =item Emitted: on a successful L|/add_listener> call;
1483              
1484             =item Target: all plugins and registered sessions;
1485              
1486             =item Args:
1487              
1488             =over 4
1489              
1490             =item * C, the listening port;
1491              
1492             =item * C, the listener id;
1493              
1494             =item * C, the listening address;
1495              
1496             =item * C, whether SSL is in use;
1497              
1498             =back
1499              
1500             =back
1501              
1502             =head2 C
1503              
1504             =over
1505              
1506             =item Emitted: on a successful L|/del_listener> call;
1507              
1508             =item Target: all plugins and registered sessions;
1509              
1510             =item Args:
1511              
1512             =over 4
1513              
1514             =item * C, the listening port;
1515              
1516             =item * C, the listener id;
1517              
1518             =item * C, the listener address;
1519              
1520             =back
1521              
1522             =back
1523              
1524             =head2 C
1525              
1526             =over
1527              
1528             =item Emitted: when a listener wheel fails;
1529              
1530             =item Target: all plugins and registered sessions;
1531              
1532             =item Args:
1533              
1534             =over 4
1535              
1536             =item * C, the listener id;
1537              
1538             =item * C, the name of the operation that failed;
1539              
1540             =item * C, numeric value for $!;
1541              
1542             =item * C, string value for $!;
1543              
1544             =item * C, the port it tried to listen on;
1545              
1546             =item * C, the address it tried to listen on;
1547              
1548             =back
1549              
1550             =back
1551              
1552             =head2 C
1553              
1554             =over
1555              
1556             =item Emitted: on the failure of an L|/add_connector> call
1557              
1558             =item Target: all plugins and registered sessions;
1559              
1560             =item Args:
1561              
1562             =over 4
1563              
1564             =item * C, a HASHREF containing the params that add_connector() was
1565             called with;
1566              
1567             =item * C, the name of the operation that failed;
1568              
1569             =item * C, numeric value for $!;
1570              
1571             =item * C, string value for $!;
1572              
1573             =back
1574              
1575             =back
1576              
1577             =head2 C
1578              
1579             =over
1580              
1581             =item Emitted: when the component establishes a connection with a peer;
1582              
1583             =item Target: all plugins and registered sessions;
1584              
1585             =item Args:
1586              
1587             =over 4
1588              
1589             =item * C, the connection id;
1590              
1591             =item * C, their ip address;
1592              
1593             =item * C, their tcp port;
1594              
1595             =item * C, our ip address;
1596              
1597             =item * C, our socket port;
1598              
1599             =item * C, the peer's name;
1600              
1601             =back
1602              
1603             =back
1604              
1605             =head2 C
1606              
1607             =over
1608              
1609             =item Emitted: when a client connection is flooded;
1610              
1611             =item Target: all plugins and registered sessions;
1612              
1613             =item Args:
1614              
1615             =over 4
1616              
1617             =item * C, the connection id;
1618              
1619             =back
1620              
1621             =back
1622              
1623             =head2 C
1624              
1625             =over
1626              
1627             =item Emitted: when a client connection has not sent any data for a set
1628             period;
1629              
1630             =item Target: all plugins and registered sessions;
1631              
1632             =item Args:
1633              
1634             =over 4
1635              
1636             =item * C, the connection id;
1637              
1638             =item * C, the number of seconds period we consider as idle;
1639              
1640             =back
1641              
1642             =back
1643              
1644             =head2 C
1645              
1646             =over
1647              
1648             =item Emitted: when compression has been enabled for a connection
1649              
1650             =item Target: all plugins and registered sessions;
1651              
1652             =item Args:
1653              
1654             =over 4
1655              
1656             =item * C, the connection id;
1657              
1658             =back
1659              
1660             =back
1661              
1662             =head2 C
1663              
1664             =over
1665              
1666             =item Emitted: when a client or peer sends a valid IRC line to us;
1667              
1668             =item Target: all plugins and registered sessions;
1669              
1670             =item Args:
1671              
1672             =over 4
1673              
1674             =item * C, the connection id;
1675              
1676             =item * C, a HASHREF containing the output record from
1677             POE::Filter::IRCD:
1678              
1679             {
1680             prefix => 'blah!~blah@blah.blah.blah',
1681             command => 'PRIVMSG',
1682             params => [ '#moo', 'cows go moo, not fish :D' ],
1683             raw_line => ':blah!~blah@blah.blah.blah.blah PRIVMSG #moo :cows go moo, not fish :D'
1684             }
1685              
1686             =back
1687              
1688             =back
1689              
1690             =head2 C
1691              
1692             =over
1693              
1694             =item Emitted: when a line of input is received from a connection
1695              
1696             =item Target: all plugins and registered sessions;
1697              
1698             =item Args:
1699              
1700             =over 4
1701              
1702             =item * C, the connection id;
1703              
1704             =item * C, the raw line of input
1705              
1706             =back
1707              
1708             =back
1709              
1710             =head2 C
1711              
1712             =over
1713              
1714             =item Emitted: when a line of output is sent over a connection
1715              
1716             =item Target: all plugins and registered sessions;
1717              
1718             =item Args:
1719              
1720             =over 4
1721              
1722             =item * C, the connection id;
1723              
1724             =item * C, the raw line of output
1725              
1726             =back
1727              
1728             =back
1729              
1730             =head2 C
1731              
1732             =over
1733              
1734             =item Emitted: when a client disconnects;
1735              
1736             =item Target: all plugins and registered sessions;
1737              
1738             =item Args:
1739              
1740             =over 4
1741              
1742             =item * C, the connection id;
1743              
1744             =item * C, the error or reason for disconnection;
1745              
1746             =back
1747              
1748             =back
1749              
1750             =head2 C
1751              
1752             I>
1753              
1754             =over
1755              
1756             =item Emitted: when the component has been asked to L|/shutdown>
1757              
1758             =item Target: all registered sessions;
1759              
1760             =item Args:
1761              
1762             =over 4
1763              
1764             =item * C: the session ID of the requesting component
1765              
1766             =back
1767              
1768             =back
1769              
1770             =head2 C
1771              
1772             I>
1773              
1774             =over
1775              
1776             =item Emitted: on a successful addition of a delayed event using the
1777             L|/delay> method
1778              
1779             =item Target: all plugins and registered sessions;
1780              
1781             =item Args:
1782              
1783             =over 4
1784              
1785             =item * C: the alarm id which can be used later with
1786             L|/delay_remove>
1787              
1788             =item * C: subsequent arguments are those which were passed to
1789             L|/delay>
1790              
1791             =back
1792              
1793             =back
1794              
1795             =head2 C
1796              
1797             I>
1798              
1799             =over
1800              
1801             =item Emitted: when a delayed command is successfully removed
1802              
1803             =item Target: all plugins and registered sessions;
1804              
1805             =item Args:
1806              
1807             =over 4
1808              
1809             =item * C: the alarm id which was removed
1810              
1811             =item * C: subsequent arguments are those which were passed to
1812             L|/delay>
1813              
1814             =back
1815              
1816             =back
1817              
1818             =head2 C
1819              
1820             I>
1821              
1822             =over
1823              
1824             =item Emitted: when a new plugin is added to the pipeline
1825              
1826             =item Target: all plugins and registered sessions;
1827              
1828             =item Args:
1829              
1830             =over 4
1831              
1832             =item * C: the plugin alias
1833              
1834             =item * C: the plugin object
1835              
1836             =back
1837              
1838             =back
1839              
1840             =head2 C
1841              
1842             I>
1843              
1844             =over
1845              
1846             =item Emitted: when a plugin is removed from the pipeline
1847              
1848             =item Target: all plugins and registered sessions;
1849              
1850             =item Args:
1851              
1852             =over 4
1853              
1854             =item * C: the plugin alias
1855              
1856             =item * C: the plugin object
1857              
1858             =back
1859              
1860             =back
1861              
1862             =head2 C
1863              
1864             I>
1865              
1866             =over
1867              
1868             =item Emitted: when an error occurs while executing a plugin handler
1869              
1870             =item Target: all plugins and registered sessions;
1871              
1872             =item Args:
1873              
1874             =over 4
1875              
1876             =item * C: the error message
1877              
1878             =item * C: the plugin alias
1879              
1880             =item * C: the plugin object
1881              
1882             =back
1883              
1884             =back
1885              
1886             =head1 AUTHOR
1887              
1888             Chris 'BinGOs' Williams
1889              
1890             =head1 LICENSE
1891              
1892             Copyright E Chris Williams
1893              
1894             This module may be used, modified, and distributed under the same terms as
1895             Perl itself. Please see the license that came with your Perl distribution
1896             for details.
1897              
1898             =head1 SEE ALSO
1899              
1900             L
1901              
1902             L
1903              
1904             L
1905              
1906             =cut