1package POE::Component::IRC;
2our $AUTHORITY = 'cpan:HINRIK';
3$POE::Component::IRC::VERSION = '6.93';
4use strict;
5use warnings FATAL => 'all';
6use Carp;
7use POE qw(Wheel::SocketFactory Wheel::ReadWrite Driver::SysRW
8           Filter::Line Filter::Stream Filter::Stackable);
9use POE::Filter::IRCD;
10use POE::Filter::IRC::Compat;
11use POE::Component::IRC::Constants qw(:ALL);
12use POE::Component::IRC::Plugin qw(:ALL);
13use POE::Component::IRC::Plugin::DCC;
14use POE::Component::IRC::Plugin::ISupport;
15use POE::Component::IRC::Plugin::Whois;
16use Socket qw(AF_INET SOCK_STREAM unpack_sockaddr_in inet_ntoa inet_aton);
17use base qw(POE::Component::Syndicator);
18
19our ($GOT_SSL, $GOT_CLIENT_DNS, $GOT_SOCKET6, $GOT_ZLIB);
20
21BEGIN {
22    eval {
23        require POE::Component::SSLify;
24        import POE::Component::SSLify qw( Client_SSLify SSLify_ContextCreate );
25        $GOT_SSL = 1;
26    };
27    eval {
28        require POE::Component::Client::DNS;
29        $GOT_CLIENT_DNS = 1 if $POE::Component::Client::DNS::VERSION >= 0.99;
30    };
31    eval {
32        require POE::Filter::Zlib::Stream;
33        $GOT_ZLIB = 1 if $POE::Filter::Zlib::Stream::VERSION >= 1.96;
34    };
35    # Socket6 provides AF_INET6 where earlier Perls' Socket don't.
36    eval {
37        Socket->import(qw(AF_INET6 unpack_sockaddr_in6 inet_ntop));
38        $GOT_SOCKET6 = 1;
39    };
40    if (!$GOT_SOCKET6) {
41        eval {
42            require Socket6;
43            Socket6->import(qw(AF_INET6 unpack_sockaddr_in6 inet_ntop));
44            $GOT_SOCKET6 = 1;
45        };
46        if (!$GOT_SOCKET6) {
47            # provide a dummy sub so code compiles
48            *AF_INET6 = sub { ~0 };
49        }
50    }
51}
52
53# BINGOS: I have bundled up all the stuff that needs changing
54# for inherited classes into _create. This gets called from 'spawn'.
55# $self->{OBJECT_STATES_ARRAYREF} contains event mappings to methods that have
56# the same name, gets passed to POE::Session->create as $self => [ ];
57# $self->{OBJECT_STATES_HASHREF} contains event mappings to methods, where the
58# event and the method have diferent names.
59# $self->{IRC_CMDS} contains the traditional %irc_commands, mapping commands
60# to events and the priority that the command has.
61sub _create {
62    my ($self) = @_;
63
64    $self->{IRC_CMDS} = {
65        rehash    => [ PRI_HIGH,     'noargs',        ],
66        die       => [ PRI_HIGH,     'noargs',        ],
67        restart   => [ PRI_HIGH,     'noargs',        ],
68        quit      => [ PRI_NORMAL,   'oneoptarg',     ],
69        version   => [ PRI_HIGH,     'oneoptarg',     ],
70        time      => [ PRI_HIGH,     'oneoptarg',     ],
71        trace     => [ PRI_HIGH,     'oneoptarg',     ],
72        admin     => [ PRI_HIGH,     'oneoptarg',     ],
73        info      => [ PRI_HIGH,     'oneoptarg',     ],
74        away      => [ PRI_HIGH,     'oneoptarg',     ],
75        users     => [ PRI_HIGH,     'oneoptarg',     ],
76        lusers    => [ PRI_HIGH,     'oneoptarg',     ],
77        locops    => [ PRI_HIGH,     'oneoptarg',     ],
78        operwall  => [ PRI_HIGH,     'oneoptarg',     ],
79        wallops   => [ PRI_HIGH,     'oneoptarg',     ],
80        motd      => [ PRI_HIGH,     'oneoptarg',     ],
81        who       => [ PRI_HIGH,     'oneoptarg',     ],
82        nick      => [ PRI_HIGH,     'onlyonearg',    ],
83        oper      => [ PRI_HIGH,     'onlytwoargs',   ],
84        invite    => [ PRI_HIGH,     'onlytwoargs',   ],
85        squit     => [ PRI_HIGH,     'onlytwoargs',   ],
86        kill      => [ PRI_HIGH,     'onlytwoargs',   ],
87        privmsg   => [ PRI_NORMAL,   'privandnotice', ],
88        privmsglo => [ PRI_NORMAL+1, 'privandnotice', ],
89        privmsghi => [ PRI_NORMAL-1, 'privandnotice', ],
90        notice    => [ PRI_NORMAL,   'privandnotice', ],
91        noticelo  => [ PRI_NORMAL+1, 'privandnotice', ],
92        noticehi  => [ PRI_NORMAL-1, 'privandnotice', ],
93        squery    => [ PRI_NORMAL,   'privandnotice', ],
94        join      => [ PRI_HIGH,     'oneortwo',      ],
95        summon    => [ PRI_HIGH,     'oneortwo',      ],
96        sconnect  => [ PRI_HIGH,     'oneandtwoopt',  ],
97        whowas    => [ PRI_HIGH,     'oneandtwoopt',  ],
98        stats     => [ PRI_HIGH,     'spacesep',      ],
99        links     => [ PRI_HIGH,     'spacesep',      ],
100        mode      => [ PRI_HIGH,     'spacesep',      ],
101        servlist  => [ PRI_HIGH,     'spacesep',      ],
102        cap       => [ PRI_HIGH,     'spacesep',      ],
103        part      => [ PRI_HIGH,     'commasep',      ],
104        names     => [ PRI_HIGH,     'commasep',      ],
105        list      => [ PRI_HIGH,     'commasep',      ],
106        whois     => [ PRI_HIGH,     'commasep',      ],
107        ctcp      => [ PRI_HIGH,     'ctcp',          ],
108        ctcpreply => [ PRI_HIGH,     'ctcp',          ],
109        ping      => [ PRI_HIGH,     'oneortwo',      ],
110        pong      => [ PRI_HIGH,     'oneortwo',      ],
111    };
112
113    my %event_map = map {($_ => $self->{IRC_CMDS}->{$_}->[CMD_SUB])}
114        keys %{ $self->{IRC_CMDS} };
115
116    $self->{OBJECT_STATES_HASHREF} = {
117        %event_map,
118        quote => 'sl',
119    };
120
121    $self->{OBJECT_STATES_ARRAYREF} = [qw(
122        syndicator_started
123        _parseline
124        _sock_down
125        _sock_failed
126        _sock_up
127        _socks_proxy_connect
128        _socks_proxy_response
129        debug
130        connect
131        _resolve_addresses
132        _do_connect
133        _quit_timeout
134        _send_login
135        _got_dns_response
136        ison
137        kick
138        remove
139        nickserv
140        shutdown
141        sl
142        sl_login
143        sl_high
144        sl_delayed
145        sl_prioritized
146        topic
147        userhost
148    )];
149
150    return;
151}
152
153# BINGOS: the component can now configure itself via _configure() from
154# either spawn() or connect()
155## no critic (Subroutines::ProhibitExcessComplexity)
156sub _configure {
157    my ($self, $args) = @_;
158    my $spawned = 0;
159
160    if (ref $args eq 'HASH' && keys %{ $args }) {
161        $spawned = delete $args->{spawned};
162        $self->{use_localaddr} = delete $args->{localaddr};
163        @{ $self }{ keys %{ $args } } = values %{ $args };
164    }
165
166    if ($ENV{POCOIRC_DEBUG}) {
167        $self->{debug} = 1;
168        $self->{plugin_debug} = 1;
169    }
170
171    if ($self->{debug}) {
172        $self->{ircd_filter}->debug(1);
173        $self->{ircd_compat}->debug(1);
174    }
175
176    if ($self->{useipv6} && !$GOT_SOCKET6) {
177        warn "'useipv6' option specified, but Socket6 was not found\n";
178    }
179
180    if ($self->{usessl} && !$GOT_SSL) {
181        warn "'usessl' option specified, but POE::Component::SSLify was not found\n";
182    }
183
184    $self->{dcc}->nataddr($self->{nataddr}) if exists $self->{nataddr};
185    $self->{dcc}->dccports($self->{dccports}) if exists $self->{dccports};
186
187    $self->{port} = 6667 if !$self->{port};
188    $self->{msg_length} = 450 if !defined $self->{msg_length};
189
190    if ($self->{use_localaddr}) {
191        $self->{localaddr} = $self->{use_localaddr}
192            . ($self->{localport} ? (':'.$self->{localport}) : '');
193    }
194
195    # Make sure that we have reasonable defaults for all the attributes.
196    # The "IRC*" variables are ircII environment variables.
197    if (!defined $self->{nick}) {
198        $self->{nick} = $ENV{IRCNICK} || eval { scalar getpwuid($>) }
199            || $ENV{USER} || $ENV{LOGNAME} || 'WankerBot';
200    }
201
202    if (!defined $self->{username}) {
203        $self->{username} = eval { scalar getpwuid($>) } || $ENV{USER}
204            || $ENV{LOGNAME} || 'foolio';
205    }
206
207    if (!defined $self->{ircname}) {
208        $self->{ircname} = $ENV{IRCNAME} || eval { (getpwuid $>)[6] }
209            || 'Just Another Perl Hacker';
210    }
211
212    if (!defined $self->{server} && !$spawned) {
213        die "No IRC server specified\n" if !$ENV{IRCSERVER};
214        $self->{server} = $ENV{IRCSERVER};
215    }
216
217    if (defined $self->{webirc}) {
218        if (!ref $self->{webirc} ne 'HASH') {
219            die "webirc param expects a hashref";
220        }
221        for my $expect_key (qw(pass user host ip)) {
222            if (!exists $self->{webirc}{$expect_key}) {
223                die "webirc value is missing key '$expect_key'";
224            }
225        }
226    }
227
228    return;
229}
230
231sub debug {
232    my ($self, $switch) = @_[OBJECT, ARG0];
233
234    $self->{debug} = $switch;
235    $self->{ircd_filter}->debug( $switch );
236    $self->{ircd_compat}->debug( $switch );
237    return;
238}
239
240# Parse a message from the IRC server and generate the appropriate
241# event(s) for listening sessions.
242sub _parseline {
243    my ($session, $self, $ev) = @_[SESSION, OBJECT, ARG0];
244
245    return if !$ev->{name};
246    $self->send_event(irc_raw => $ev->{raw_line} ) if $self->{raw};
247
248    # record our nickname
249    if ( $ev->{name} eq '001' ) {
250        $self->{INFO}{RealNick} = ( split / /, $ev->{raw_line} )[2];
251    }
252
253    $ev->{name} = 'irc_' . $ev->{name};
254    $self->send_event( $ev->{name}, @{$ev->{args}} );
255
256    if ($ev->{name} =~ /^irc_ctcp_(.+)$/) {
257        $self->send_event(irc_ctcp => $1 => @{$ev->{args}});
258    }
259
260    return;
261}
262
263# Internal function called when a socket is closed.
264sub _sock_down {
265    my ($kernel, $self) = @_[KERNEL, OBJECT];
266
267    # Destroy the RW wheel for the socket.
268    delete $self->{socket};
269    delete $self->{localaddr};
270    $self->{connected} = 0;
271
272    # Stop any delayed sends.
273    $self->{send_queue} = [ ];
274    $self->{send_time}  = 0;
275    $kernel->delay( sl_delayed => undef );
276
277    # Reset the filters if necessary
278    $self->_compress_uplink( 0 );
279    $self->_compress_downlink( 0 );
280    $self->{ircd_compat}->chantypes( [ '#', '&' ] );
281    $self->{ircd_compat}->identifymsg(0);
282
283    # post a 'irc_disconnected' to each session that cares
284    $self->send_event(irc_disconnected => $self->{server} );
285    return;
286}
287
288sub disconnect {
289    my ($self) = @_;
290    $self->yield('_sock_down');
291    return;
292}
293
294# Internal function called when a socket fails to be properly opened.
295sub _sock_failed {
296    my ($self, $op, $errno, $errstr) = @_[OBJECT, ARG0..ARG2];
297
298    delete $self->{socketfactory};
299    $self->send_event(irc_socketerr => "$op error $errno: $errstr" );
300    return;
301}
302
303# Internal function called when a connection is established.
304sub _sock_up {
305    my ($kernel, $self, $session, $socket) = @_[KERNEL, OBJECT, SESSION, ARG0];
306
307    # We no longer need the SocketFactory wheel. Scrap it.
308    delete $self->{socketfactory};
309
310    # Remember what IP address we're connected through, for multihomed boxes.
311    my $localaddr;
312    if ($GOT_SOCKET6) {
313        eval {
314                $localaddr = (unpack_sockaddr_in6( getsockname $socket ))[1];
315                $localaddr = inet_ntop( AF_INET6, $localaddr );
316        };
317    }
318
319    if ( !$localaddr ) {
320        $localaddr = (unpack_sockaddr_in( getsockname $socket ))[1];
321        $localaddr = inet_ntoa($localaddr);
322    }
323
324    $self->{localaddr} = $localaddr;
325
326    if ( $self->{socks_proxy} ) {
327        $self->{socket} = POE::Wheel::ReadWrite->new(
328            Handle       => $socket,
329            Driver       => POE::Driver::SysRW->new(),
330            Filter       => POE::Filter::Stream->new(),
331            InputEvent   => '_socks_proxy_response',
332            ErrorEvent   => '_sock_down',
333        );
334
335        if ( !$self->{socket} ) {
336            $self->send_event(irc_socketerr =>
337                "Couldn't create ReadWrite wheel for SOCKS socket" );
338            return;
339        }
340
341        my $packet;
342        if ( _ip_is_ipv4( $self->{server} ) ) {
343            # SOCKS 4
344            $packet = pack ('CCn', 4, 1, $self->{port}) .
345            inet_aton($self->{server}) . ($self->{socks_id} || '') . (pack 'x');
346        }
347        else {
348            # SOCKS 4a
349            $packet = pack ('CCn', 4, 1, $self->{port}) .
350            inet_aton('0.0.0.1') . ($self->{socks_id} || '') . (pack 'x') .
351            $self->{server} . (pack 'x');
352        }
353
354        $self->{socket}->put( $packet );
355        return;
356    }
357
358    # ssl!
359    if ($GOT_SSL and $self->{usessl}) {
360        eval {
361            my ($ctx);
362
363            if( $self->{sslctx} )
364            {
365                $ctx = $self->{sslctx};
366            }
367            elsif( $self->{sslkey} && $self->{sslcert} )
368            {
369                $ctx = SSLify_ContextCreate( $self->{sslkey}, $self->{sslcert} );
370            }
371            else
372            {
373                $ctx = undef;
374            }
375
376            $socket = Client_SSLify($socket, undef, undef, $ctx);
377        };
378
379        if ($@) {
380         	chomp $@;
381            warn "Couldn't use an SSL socket: $@\n";
382            $self->{usessl} = 0;
383        }
384    }
385
386    if ( $self->{compress} ) {
387        $self->_compress_uplink(1);
388        $self->_compress_downlink(1);
389    }
390
391    # Create a new ReadWrite wheel for the connected socket.
392    $self->{socket} = POE::Wheel::ReadWrite->new(
393        Handle       => $socket,
394        Driver       => POE::Driver::SysRW->new(),
395        InputFilter  => $self->{srv_filter},
396        OutputFilter => $self->{out_filter},
397        InputEvent   => '_parseline',
398        ErrorEvent   => '_sock_down',
399    );
400
401    if ($self->{socket}) {
402        $self->{connected} = 1;
403    }
404    else {
405        $self->send_event(irc_socketerr => "Couldn't create ReadWrite wheel for IRC socket");
406        return;
407    }
408
409    # Post a 'irc_connected' event to each session that cares
410    $self->send_event(irc_connected => $self->{server} );
411
412    # CONNECT if we're using a proxy
413    if ($self->{proxy}) {
414        # The original proxy code, AFAIK, did not actually work
415        # with an HTTP proxy.
416        $self->call(
417            'sl_login',
418            'CONNECT ' . $self->{server} . ':' . $self->{port} . " HTTP/1.0\n\n",
419        );
420
421        # KLUDGE: Also, the original proxy code assumes the connection
422        # is instantaneous Since this is not always the case, mess with
423        # the queueing so that the sent text is delayed...
424        $self->{send_time} = time() + 10;
425    }
426
427    $kernel->yield('_send_login');
428    return;
429}
430
431sub _socks_proxy_response {
432    my ($kernel, $self, $session, $input) = @_[KERNEL, OBJECT, SESSION, ARG0];
433
434    if (length $input != 8) {
435        $self->send_event(
436            'irc_socks_failed',
437            'Mangled response from SOCKS proxy',
438            $input,
439        );
440        $self->disconnect();
441        return;
442    }
443
444    my @resp = unpack 'CCnN', $input;
445    if (@resp != 4 || $resp[0] ne '0' || $resp[1] !~ /^(?:90|91|92|93)$/) {
446        $self->send_event(
447            'irc_socks_failed',
448            'Mangled response from SOCKS proxy',
449            $input,
450        );
451        $self->disconnect();
452        return;
453    }
454
455    if ( $resp[1] eq '90' ) {
456        $kernel->call($session => '_socks_proxy_connect');
457        $self->{connected} = 1;
458        $self->send_event( 'irc_connected', $self->{server} );
459        $kernel->yield('_send_login');
460    }
461    else {
462        $self->send_event(
463            'irc_socks_rejected',
464            $resp[1],
465            $self->{socks_proxy},
466            $self->{socks_port},
467            $self->{socks_id},
468        );
469        $self->disconnect();
470    }
471
472    return;
473}
474
475sub _socks_proxy_connect {
476    my ($kernel, $self) = @_[KERNEL, OBJECT];
477    $self->{socket}->event( InputEvent => '_parseline' );
478    $self->{socket}->set_input_filter( $self->{srv_filter} );
479    $self->{socket}->set_output_filter( $self->{out_filter} );
480    return;
481}
482
483sub _send_login {
484    my ($kernel, $self, $session) = @_[KERNEL, OBJECT, SESSION];
485
486    # Now that we're connected, attempt to log into the server.
487
488    # for servers which support CAP, it's customary to start with that
489    $kernel->call($session, 'sl_login', 'CAP REQ :identify-msg');
490    $kernel->call($session, 'sl_login', 'CAP REQ :multi-prefix');
491    $kernel->call($session, 'sl_login', 'CAP LS');
492    $kernel->call($session, 'sl_login', 'CAP END');
493
494    # If we were told to use WEBIRC to spoof our host/IP, do so:
495    if (defined $self->{webirc}) {
496        $kernel->call($session => sl_login => 'WEBIRC '
497            . join " ", @{$self->{webirc}}{qw(pass user ip host)}
498        );
499    }
500
501    if (defined $self->{password}) {
502        $kernel->call($session => sl_login => 'PASS ' . $self->{password});
503    }
504    $kernel->call($session => sl_login => 'NICK ' . $self->{nick});
505    $kernel->call(
506        $session,
507        'sl_login',
508        'USER ' .
509        join(' ', $self->{username},
510            (defined $self->{bitmode} ? $self->{bitmode} : 8),
511            '*',
512            ':' . $self->{ircname}
513        ),
514    );
515
516    # If we have queued data waiting, its flush loop has stopped
517    # while we were disconnected.  Start that up again.
518    $kernel->delay(sl_delayed => 0);
519
520    return;
521}
522
523# Set up the component's IRC session.
524sub syndicator_started {
525    my ($kernel, $session, $sender, $self, $alias)
526        = @_[KERNEL, SESSION, SENDER, OBJECT, ARG0, ARG1 .. $#_];
527
528    # Send queue is used to hold pending lines so we don't flood off.
529    # The count is used to track the number of lines sent at any time.
530    $self->{send_queue} = [ ];
531    $self->{send_time}  = 0;
532
533    $self->{ircd_filter} = POE::Filter::IRCD->new(debug => $self->{debug});
534    $self->{ircd_compat} = POE::Filter::IRC::Compat->new(debug => $self->{debug});
535
536    my $srv_filters = [
537        POE::Filter::Line->new(
538            InputRegexp => '\015?\012',
539            OutputLiteral => '\015\012',
540        ),
541        $self->{ircd_filter},
542        $self->{ircd_compat},
543    ];
544
545    $self->{srv_filter} = POE::Filter::Stackable->new(Filters => $srv_filters);
546    $self->{out_filter} = POE::Filter::Stackable->new(Filters => [
547        POE::Filter::Line->new( OutputLiteral => "\015\012" ),
548    ]);
549
550    # Plugin 'irc_whois' and 'irc_whowas' support
551    $self->plugin_add('Whois_' . $self->session_id(),
552        POE::Component::IRC::Plugin::Whois->new()
553    );
554
555    $self->{isupport} = POE::Component::IRC::Plugin::ISupport->new();
556    $self->plugin_add('ISupport_' . $self->session_id(), $self->{isupport});
557    $self->{dcc} = POE::Component::IRC::Plugin::DCC->new();
558    $self->plugin_add('DCC_' . $self->session_id(), $self->{dcc});
559
560    return 1;
561}
562
563# The handler for commands which have N arguments, separated by commas.
564sub commasep {
565    my ($kernel, $self, $state, @args) = @_[KERNEL, OBJECT, STATE, ARG0 .. $#_];
566    my $args;
567
568    if ($state eq 'whois' and @args > 1 ) {
569        $args = shift @args;
570        $args .= ' ' . join ',', @args;
571    }
572    elsif ( $state eq 'part' and @args > 1 ) {
573        my $chantypes = join('', @{ $self->isupport('CHANTYPES') || ['#', '&']});
574        my $message;
575        if ($args[-1] =~ / +/ || $args[-1] !~ /^[$chantypes]/) {
576            $message = pop @args;
577        }
578        $args = join(',', @args);
579        $args .= " :$message" if defined $message;
580    }
581    else {
582        $args = join ',', @args;
583    }
584
585    my $pri = $self->{IRC_CMDS}->{$state}->[CMD_PRI];
586    $state = uc $state;
587    $state .= " $args" if defined $args;
588    $kernel->yield(sl_prioritized => $pri, $state );
589
590    return;
591}
592
593# Get variables in order for openning a connection
594sub connect {
595    my ($kernel, $self, $session, $sender, $args)
596        = @_[KERNEL, OBJECT, SESSION, SENDER, ARG0];
597
598    if ($args) {
599        my %arg;
600        %arg = @{ $args } if ref $args eq 'ARRAY';
601        %arg = %{ $args } if ref $args eq 'HASH';
602        $arg{ lc $_ } = delete $arg{$_} for keys %arg;
603        $self->_configure( \%arg );
604    }
605
606    if ( $self->{resolver} && $self->{res_addresses}
607        && @{ $self->{res_addresses} } ) {
608        push @{ $self->{res_addresses} }, $self->{server};
609        $self->{resolved_server} = shift @{ $self->{res_addresses} };
610    }
611
612    # try and use non-blocking resolver if needed
613    if ( $self->{resolver} && !_ip_get_version( $self->{server} )
614        && !$self->{nodns} ) {
615        $kernel->yield(
616            '_resolve_addresses',
617             $self->{server},
618             ( $self->{useipv6} && $GOT_SOCKET6 ? 'AAAA' : 'A' ),
619        );
620    }
621    else {
622        $kernel->yield('_do_connect');
623    }
624
625    $self->{INFO}{RealNick} = $self->{nick};
626    return;
627}
628
629sub _resolve_addresses {
630    my ($kernel, $self, $hostname, $type) = @_[KERNEL, OBJECT, ARG0 .. ARG1];
631
632    my $response = $self->{resolver}->resolve(
633        event => '_got_dns_response',
634        host => $hostname,
635        type => $type,
636        context => { },
637    );
638
639    $kernel->yield(_got_dns_response => $response) if $response;
640    return;
641}
642
643# open the connection
644sub _do_connect {
645    my ($kernel, $self, $session) = @_[KERNEL, OBJECT, SESSION];
646    my $domain = AF_INET;
647
648    # Disconnect if we're already logged into a server.
649    $kernel->call($session => 'quit') if $self->{socket};
650
651    if ($self->{socks_proxy} && !$self->{socks_port}) {
652        $self->{socks_port} = 1080;
653    }
654
655    for my $address (qw(socks_proxy proxy server resolved_server use_localaddr)) {
656        next if !$self->{$address} || !_ip_is_ipv6( $self->{$address} );
657        if (!$GOT_SOCKET6) {
658            warn "IPv6 address specified for '$address' but Socket6 not found\n";
659            return;
660        }
661        $domain = AF_INET6;
662    }
663
664    $self->{socketfactory} = POE::Wheel::SocketFactory->new(
665        SocketDomain   => $domain,
666        SocketType     => SOCK_STREAM,
667        SocketProtocol => 'tcp',
668        RemoteAddress  => $self->{socks_proxy} || $self->{proxy} || $self->{resolved_server} || $self->{server},
669        RemotePort     => $self->{socks_port} || $self->{proxyport} || $self->{port},
670        SuccessEvent   => '_sock_up',
671        FailureEvent   => '_sock_failed',
672        ($self->{use_localaddr} ? (BindAddress => $self->{use_localaddr}) : ()),
673    );
674
675    return;
676}
677
678# got response from POE::Component::Client::DNS
679sub _got_dns_response {
680    my ($kernel, $self, $response) = @_[KERNEL, OBJECT, ARG0];
681
682    my $type = uc $response->{type};
683    my $net_dns_packet = $response->{response};
684    my $net_dns_errorstring = $response->{error};
685    $self->{res_addresses} = [ ];
686
687    if (!defined $net_dns_packet) {
688        $self->send_event(irc_socketerr => $net_dns_errorstring );
689        return;
690    }
691
692    my @net_dns_answers = $net_dns_packet->answer;
693
694    for my $net_dns_answer (@net_dns_answers) {
695        next if $net_dns_answer->type !~ /^A/;
696        push @{ $self->{res_addresses} }, $net_dns_answer->rdatastr;
697    }
698
699    if ( !@{ $self->{res_addresses} } && $type eq 'AAAA') {
700        $kernel->yield(_resolve_addresses => $self->{server}, 'A');
701        return;
702    }
703
704    if ( !@{ $self->{res_addresses} } ) {
705        $self->send_event(irc_socketerr => 'Unable to resolve ' . $self->{server});
706        return;
707      }
708
709    if ( my $address = shift @{ $self->{res_addresses} } ) {
710        $self->{resolved_server} = $address;
711        $kernel->yield('_do_connect');
712        return;
713    }
714
715    $self->send_event(irc_socketerr => 'Unable to resolve ' . $self->{server});
716    return;
717}
718
719# Send a CTCP query or reply, with the same syntax as a PRIVMSG event.
720sub ctcp {
721    my ($kernel, $state, $self, $to) = @_[KERNEL, STATE, OBJECT, ARG0];
722    my $message = join ' ', @_[ARG1 .. $#_];
723
724    if (!defined $to || !defined $message) {
725        warn "The '$state' event requires two arguments\n";
726        return;
727    }
728
729    # CTCP-quote the message text.
730    ($message) = @{$self->{ircd_compat}->put([ $message ])};
731
732    # Should we send this as a CTCP request or reply?
733    $state = $state eq 'ctcpreply' ? 'notice' : 'privmsg';
734
735    $kernel->yield($state, $to, $message);
736    return;
737}
738
739# The way /notify is implemented in IRC clients.
740sub ison {
741    my ($kernel, @nicks) = @_[KERNEL, ARG0 .. $#_];
742    my $tmp = 'ISON';
743
744    if (!@nicks) {
745        warn "The 'ison' event requires one or more nicknames\n";
746        return;
747    }
748
749    # We can pass as many nicks as we want, as long as it's shorter than
750    # the maximum command length (510). If the list we get is too long,
751    # w'll break it into multiple ISON commands.
752    while (@nicks) {
753        my $nick = shift @nicks;
754        if (length($tmp) + length($nick) >= 509) {
755            $kernel->yield(sl_high => $tmp);
756            $tmp = 'ISON';
757        }
758        $tmp .= " $nick";
759    }
760
761    $kernel->yield(sl_high => $tmp);
762    return;
763}
764
765# Tell the IRC server to forcibly remove a user from a channel.
766sub kick {
767    my ($kernel, $chan, $nick) = @_[KERNEL, ARG0, ARG1];
768    my $message = join '', @_[ARG2 .. $#_];
769
770    if (!defined $chan || !defined $nick) {
771        warn "The 'kick' event requires at least two arguments\n";
772        return;
773    }
774
775    $nick .= " :$message" if defined $message;
776    $kernel->yield(sl_high => "KICK $chan $nick");
777    return;
778}
779
780# Tell the IRC server to forcibly remove a user from a channel. Freenode extension
781sub remove {
782    my ($kernel, $chan, $nick) = @_[KERNEL, ARG0, ARG1];
783    my $message = join '', @_[ARG2 .. $#_];
784
785    if (!defined $chan || !defined $nick) {
786        warn "The 'remove' event requires at least two arguments\n";
787        return;
788    }
789
790    $nick .= " :$message" if defined $message;
791    $kernel->yield(sl_high => "REMOVE $chan $nick");
792    return;
793}
794
795# Interact with NickServ
796sub nickserv {
797    my ($kernel, $self, $state) = @_[KERNEL, OBJECT, STATE];
798    my $args = join ' ', @_[ARG0 .. $#_];
799
800    my $command = 'NICKSERV';
801    my $version = $self->server_version();
802    $command = 'NS' if defined $version && $version =~ /ratbox/i;
803    $command .= " $args" if defined $args;
804
805    $kernel->yield(sl_high => $command);
806    return;
807}
808
809# Set up a new IRC component. Deprecated.
810sub new {
811    my ($package, $alias) = splice @_, 0, 2;
812    croak "$package options should be an even-sized list" if @_ & 1;
813    my %options = @_;
814
815    if (!defined $alias) {
816        croak 'Not enough arguments to POE::Component::IRC::new()';
817    }
818
819    carp "Use of ${package}->new() is deprecated, please use spawn()";
820
821    my $self = $package->spawn ( alias => $alias, options => \%options );
822    return $self;
823}
824
825# Set up a new IRC component. New interface.
826sub spawn {
827    my ($package) = shift;
828    croak "$package requires an even number of arguments" if @_ & 1;
829    my %params = @_;
830
831    $params{ lc $_ } = delete $params{$_} for keys %params;
832    delete $params{options} if ref $params{options} ne 'HASH';
833
834    my $self = bless { }, $package;
835    $self->_create();
836
837    if ($ENV{POCOIRC_DEBUG}) {
838        $params{debug} = 1;
839        $params{plugin_debug} = 1;
840    }
841
842    my $options      = delete $params{options};
843    my $alias        = delete $params{alias};
844    my $plugin_debug = delete $params{plugin_debug};
845
846    $self->_syndicator_init(
847        prefix          => 'irc_',
848        reg_prefix      => 'PCI_',
849        types           => [SERVER => 'S', USER => 'U'],
850        alias           => $alias,
851        register_signal => 'POCOIRC_REGISTER',
852        shutdown_signal => 'POCOIRC_SHUTDOWN',
853        object_states   => [
854            $self => delete $self->{OBJECT_STATES_HASHREF},
855            $self => delete $self->{OBJECT_STATES_ARRAYREF},
856        ],
857        ($plugin_debug ? (debug => 1) : () ),
858        (ref $options eq 'HASH' ? ( options => $options ) : ()),
859    );
860
861    $params{spawned} = 1;
862    $self->_configure(\%params);
863
864    if (!$params{nodns} && $GOT_CLIENT_DNS && !$self->{resolver}) {
865        $self->{resolver} = POE::Component::Client::DNS->spawn(
866            Alias => 'resolver' . $self->session_id()
867        );
868        $self->{mydns} = 1;
869    }
870
871    return $self;
872}
873
874# The handler for all IRC commands that take no arguments.
875sub noargs {
876    my ($kernel, $state, $arg) = @_[KERNEL, STATE, ARG0];
877    my $pri = $_[OBJECT]->{IRC_CMDS}->{$state}->[CMD_PRI];
878
879    if (defined $arg) {
880        warn "The '$state' event takes no arguments\n";
881        return;
882    }
883
884    $state = uc $state;
885    $kernel->yield(sl_prioritized => $pri, $state);
886    return;
887}
888
889# The handler for commands that take one required and two optional arguments.
890sub oneandtwoopt {
891    my ($kernel, $state) = @_[KERNEL, STATE];
892    my $arg = join '', @_[ARG0 .. $#_];
893    my $pri = $_[OBJECT]->{IRC_CMDS}->{$state}->[CMD_PRI];
894
895    $state = 'connect' if $state eq 'sconnect';
896    $state = uc $state;
897    if (defined $arg) {
898        $arg = ':' . $arg if $arg =~ /\x20/;
899        $state .= " $arg";
900    }
901
902    $kernel->yield(sl_prioritized => $pri, $state);
903    return;
904}
905
906# The handler for commands that take at least one optional argument.
907sub oneoptarg {
908    my ($kernel, $state) = @_[KERNEL, STATE];
909    my $pri = $_[OBJECT]->{IRC_CMDS}->{$state}->[CMD_PRI];
910    $state = uc $state;
911
912    if (defined $_[ARG0]) {
913        my $arg = join '', @_[ARG0 .. $#_];
914        $arg = ':' . $arg if $arg =~ /\x20/;
915        $state .= " $arg";
916    }
917
918    $kernel->yield(sl_prioritized => $pri, $state);
919    return;
920}
921
922# The handler for commands which take one required and one optional argument.
923sub oneortwo {
924    my ($kernel, $state, $one) = @_[KERNEL, STATE, ARG0];
925    my $two = join '', @_[ARG1 .. $#_];
926    my $pri = $_[OBJECT]->{IRC_CMDS}->{$state}->[CMD_PRI];
927
928    if (!defined $one) {
929        warn "The '$state' event requires at least one argument\n";
930        return;
931    }
932
933    $state = uc( $state ) . " $one";
934    $state .= " $two" if defined $two;
935    $kernel->yield(sl_prioritized => $pri, $state);
936    return;
937}
938
939# Handler for commands that take exactly one argument.
940sub onlyonearg {
941    my ($kernel, $state) = @_[KERNEL, STATE];
942    my $arg = join '', @_[ARG0 .. $#_];
943    my $pri = $_[OBJECT]->{IRC_CMDS}->{$state}->[CMD_PRI];
944
945    if (!defined $arg) {
946        warn "The '$state' event requires one argument\n";
947        return;
948    }
949
950    $state = uc $state;
951    $arg = ':' . $arg if $arg =~ /\x20/;
952    $state .= " $arg";
953    $kernel->yield(sl_prioritized => $pri, $state);
954    return;
955}
956
957# Handler for commands that take exactly two arguments.
958sub onlytwoargs {
959    my ($kernel, $state, $one) = @_[KERNEL, STATE, ARG0];
960    my ($two) = join '', @_[ARG1 .. $#_];
961    my $pri = $_[OBJECT]->{IRC_CMDS}->{$state}->[CMD_PRI];
962
963    if (!defined $one || !defined $two) {
964        warn "The '$state' event requires two arguments\n";
965        return;
966    }
967
968    $state = uc $state;
969    $two = ':' . $two if $two =~ /\x20/;
970    $state .= " $one $two";
971    $kernel->yield(sl_prioritized => $pri, $state);
972    return;
973}
974
975# Handler for privmsg or notice events.
976sub privandnotice {
977    my ($kernel, $state, $to, $msg) = @_[KERNEL, STATE, ARG0, ARG1];
978    my $pri = $_[OBJECT]->{IRC_CMDS}->{$state}->[CMD_PRI];
979
980    $state =~ s/privmsglo/privmsg/;
981    $state =~ s/privmsghi/privmsg/;
982    $state =~ s/noticelo/notice/;
983    $state =~ s/noticehi/notice/;
984
985    if (!defined $to || !defined $msg) {
986        warn "The '$state' event requires two arguments\n";
987        return;
988    }
989
990    $to = join ',', @$to if ref $to eq 'ARRAY';
991    $state = uc $state;
992
993    $kernel->yield(sl_prioritized => $pri, "$state $to :$msg");
994    return;
995}
996
997# Tell the IRC session to go away.
998sub shutdown {
999    my ($kernel, $self, $sender, $session) = @_[KERNEL, OBJECT, SENDER, SESSION];
1000    return if $self->{_shutdown};
1001    $self->{_shutdown} = $sender->ID();
1002
1003    if ($self->logged_in()) {
1004        my ($msg, $timeout) = @_[ARG0, ARG1];
1005        $msg = '' if !defined $msg;
1006        $timeout = 5 if !defined $timeout;
1007        $msg = ":$msg" if $msg =~ /\x20/;
1008        my $cmd = "QUIT $msg";
1009        $kernel->call($session => sl_high => $cmd);
1010        $kernel->delay('_quit_timeout', $timeout);
1011        $self->{_waiting} = 1;
1012    }
1013    elsif ($self->connected()) {
1014        $self->disconnect();
1015    }
1016    else {
1017        $self->_shutdown();
1018    }
1019
1020    return;
1021}
1022
1023sub _quit_timeout {
1024    my ($self) = $_[OBJECT];
1025    $self->disconnect();
1026    return;
1027}
1028
1029sub _shutdown {
1030    my ($self) = @_;
1031
1032    $self->_syndicator_destroy($self->{_shutdown});
1033    delete $self->{$_} for qw(socketfactory dcc wheelmap);
1034    $self->{resolver}->shutdown() if $self->{resolver} && $self->{mydns};
1035    return;
1036}
1037
1038# Send a line of login-priority IRC output.  These are things which
1039# must go first.
1040sub sl_login {
1041    my ($kernel, $self) = @_[KERNEL, OBJECT];
1042    my $arg = join ' ', @_[ARG0 .. $#_];
1043    $kernel->yield(sl_prioritized => PRI_LOGIN, $arg );
1044    return;
1045}
1046
1047# Send a line of high-priority IRC output.  Things like channel/user
1048# modes, kick messages, and whatever.
1049sub sl_high {
1050    my ($kernel, $self) = @_[KERNEL, OBJECT];
1051    my $arg = join ' ', @_[ARG0 .. $#_];
1052    $kernel->yield(sl_prioritized => PRI_HIGH, $arg );
1053    return;
1054}
1055
1056# Send a line of normal-priority IRC output to the server.  PRIVMSG
1057# and other random chatter.  Uses sl() for compatibility with existing
1058# code.
1059sub sl {
1060    my ($kernel, $self) = @_[KERNEL, OBJECT];
1061    my $arg = join ' ', @_[ARG0 .. $#_];
1062    $kernel->yield(sl_prioritized => PRI_NORMAL, $arg );
1063    return;
1064}
1065
1066# Prioritized sl().  This keeps the queue ordered by priority, low to
1067# high in the UNIX tradition.  It also throttles transmission
1068# following the hybrid ircd's algorithm, so you can't accidentally
1069# flood yourself off.  Thanks to Raistlin for explaining how ircd
1070# throttles messages.
1071sub sl_prioritized {
1072    my ($kernel, $self, $priority, @args) = @_[KERNEL, OBJECT, ARG0, ARG1];
1073
1074    if (my ($event) = $args[0] =~ /^(\w+)/ ) {
1075        # Let the plugin system process this
1076        return 1 if $self->send_user_event($event, \@args) == PCI_EAT_ALL;
1077    }
1078    else {
1079        warn "Unable to extract the event name from '$args[0]'\n";
1080    }
1081
1082    my $msg = $args[0];
1083    my $now = time();
1084    $self->{send_time} = $now if $self->{send_time} < $now;
1085
1086    # if we find a newline in the message, take that to be the end of it
1087    $msg =~ s/[\015\012].*//s;
1088
1089    if (bytes::length($msg) > $self->{msg_length} - bytes::length($self->nick_name())) {
1090        $msg = bytes::substr($msg, 0, $self->{msg_length} - bytes::length($self->nick_name()));
1091    }
1092
1093    if (!$self->{flood} && @{ $self->{send_queue} }) {
1094        my $i = @{ $self->{send_queue} };
1095        $i-- while ($i && $priority < $self->{send_queue}->[$i-1]->[MSG_PRI]);
1096        splice( @{ $self->{send_queue} }, $i, 0, [ $priority, $msg ] );
1097    }
1098    elsif ( !$self->{flood} && $self->{send_time} - $now >= 10
1099        || !defined $self->{socket} ) {
1100        push( @{$self->{send_queue}}, [ $priority, $msg ] );
1101        $kernel->delay( sl_delayed => $self->{send_time} - $now - 10 );
1102    }
1103    else {
1104        warn ">>> $msg\n" if $self->{debug};
1105        $self->send_event(irc_raw_out => $msg) if $self->{raw};
1106        $self->{send_time} += 2 + length($msg) / 120;
1107        $self->{socket}->put($msg);
1108    }
1109
1110    return;
1111}
1112
1113# Send delayed lines to the ircd.  We manage a virtual "send time"
1114# that progresses into the future based on hybrid ircd's rules every
1115# time a message is sent.  Once we find it ten or more seconds into
1116# the future, we wait for the realtime clock to catch up.
1117sub sl_delayed {
1118    my ($kernel, $self) = @_[KERNEL, OBJECT];
1119
1120    return if !defined $self->{socket};
1121
1122    my $now = time();
1123    $self->{send_time} = $now if $self->{send_time} < $now;
1124
1125    while (@{ $self->{send_queue} } && ($self->{send_time} - $now < 10)) {
1126        my $arg = (shift @{$self->{send_queue}})->[MSG_TEXT];
1127        warn ">>> $arg\n" if $self->{debug};
1128        $self->send_event(irc_raw_out => $arg) if $self->{raw};
1129        $self->{send_time} += 2 + length($arg) / 120;
1130        $self->{socket}->put($arg);
1131    }
1132
1133    if (@{ $self->{send_queue} }) {
1134        $kernel->delay( sl_delayed => $self->{send_time} - $now - 10 );
1135    }
1136
1137    return;
1138}
1139
1140# The handler for commands which have N arguments, separated by spaces.
1141sub spacesep {
1142    my ($kernel, $state) = @_[KERNEL, STATE];
1143    my $args = join ' ', @_[ARG0 .. $#_];
1144    my $pri = $_[OBJECT]->{IRC_CMDS}->{$state}->[CMD_PRI];
1145
1146    $state = uc $state;
1147    $state .= " $args" if defined $args;
1148    $kernel->yield(sl_prioritized => $pri, $state );
1149    return;
1150}
1151
1152# Set or query the current topic on a channel.
1153sub topic {
1154    my ($kernel, $chan, @args) = @_[KERNEL, ARG0..$#_];
1155    my $topic;
1156    $topic = join '', @args if @args;
1157
1158    if (defined $topic) {
1159        $chan .= " :";
1160        $chan .= $topic if length $topic;
1161    }
1162
1163    $kernel->yield(sl_prioritized => PRI_NORMAL, "TOPIC $chan");
1164    return;
1165}
1166
1167# Asks the IRC server for some random information about particular nicks.
1168sub userhost {
1169    my ($kernel, @nicks) = @_[KERNEL, ARG0 .. $#_];
1170
1171    if (!@nicks) {
1172        warn "The 'userhost' event requires at least one nickname\n";
1173        return;
1174    }
1175
1176    # According to the RFC, you can only send 5 nicks at a time.
1177    while (@nicks) {
1178        $kernel->yield(
1179            'sl_prioritized',
1180            PRI_HIGH,
1181            'USERHOST ' . join(' ', splice(@nicks, 0, 5)),
1182        );
1183    }
1184
1185    return;
1186}
1187
1188# Non-event methods
1189
1190sub server {
1191    my ($self) = @_;
1192    return $self->{server};
1193}
1194
1195sub port {
1196    my ($self) = @_;
1197    return $self->{port};
1198}
1199
1200sub server_name {
1201    my ($self) = @_;
1202    return $self->{INFO}{ServerName};
1203}
1204
1205sub server_version {
1206    my ($self) = @_;
1207    return $self->{INFO}{ServerVersion};
1208}
1209
1210sub localaddr {
1211    my ($self) = @_;
1212    return $self->{localaddr};
1213}
1214
1215sub nick_name {
1216    my ($self) = @_;
1217    return $self->{INFO}{RealNick};
1218}
1219
1220sub send_queue {
1221    my ($self) = @_;
1222
1223    if (defined $self->{send_queue} && ref $self->{send_queue} eq 'ARRAY' ) {
1224        return scalar @{ $self->{send_queue} };
1225    }
1226    return;
1227}
1228
1229sub raw_events {
1230    my ($self, $value) = @_;
1231    return $self->{raw} if !defined $value;
1232    $self->{raw} = $value;
1233    return;
1234}
1235
1236sub connected {
1237    my ($self) = @_;
1238    return $self->{connected};
1239}
1240
1241sub logged_in {
1242    my ($self) = @_;
1243    return 1 if $self->{INFO}{LoggedIn};
1244    return;
1245}
1246
1247sub _compress_uplink {
1248    my ($self, $value) = @_;
1249
1250    return if !$GOT_ZLIB;
1251    return $self->{uplink} if !defined $value;
1252
1253    if ($value) {
1254        $self->{out_filter}->unshift( POE::Filter::Zlib::Stream->new() ) if !$self->{uplink};
1255        $self->{uplink} = 1;
1256    }
1257    else {
1258        $self->{out_filter}->shift() if $self->{uplink};
1259        $self->{uplink} = 0;
1260    }
1261
1262    return $self->{uplink};
1263}
1264
1265sub _compress_downlink {
1266    my ($self, $value) = @_;
1267
1268    return if !$GOT_ZLIB;
1269    return $self->{downlink} if !defined $value;
1270
1271    if ($value) {
1272        $self->{srv_filter}->unshift( POE::Filter::Zlib::Stream->new() ) if !$self->{downlink};
1273        $self->{downlink} = 1;
1274    }
1275    else {
1276        $self->{srv_filter}->shift() if $self->{uplink};
1277        $self->{downlink} = 0;
1278    }
1279
1280    return $self->{downlink};
1281}
1282
1283sub S_001 {
1284    my ($self, $irc) = splice @_, 0, 2;
1285    $self->{INFO}{ServerName} = ${ $_[0] };
1286    $self->{INFO}{LoggedIn}   = 1;
1287    return PCI_EAT_NONE;
1288}
1289
1290sub S_004 {
1291    my ($self, $irc) = splice @_, 0, 2;
1292    my $args = ${ $_[2] };
1293    $self->{INFO}{ServerVersion} = $args->[1];
1294    return PCI_EAT_NONE;
1295}
1296
1297sub S_error {
1298    my ($self, $irc) = splice @_, 0, 2;
1299    $self->{INFO}{LoggedIn} = 0;
1300    return PCI_EAT_NONE;
1301}
1302
1303sub S_disconnected {
1304    my ($self, $irc) = splice @_, 0, 2;
1305    $self->{INFO}{LoggedIn} = 0;
1306
1307    if ($self->{_waiting}) {
1308        $poe_kernel->delay('_quit_timeout');
1309        delete $self->{_waiting};
1310    }
1311
1312    $self->_shutdown() if $self->{_shutdown};
1313    return PCI_EAT_NONE;
1314}
1315
1316sub S_shutdown {
1317    my ($self, $irc) = splice @_, 0, 2;
1318    $self->{INFO}{LoggedIn} = 0;
1319    return PCI_EAT_NONE;
1320}
1321
1322# Automatically replies to a PING from the server. Do not confuse this
1323# with CTCP PINGs, which are a wholly different animal that evolved
1324# much later on the technological timeline.
1325sub S_ping {
1326    my ($self, $irc) = splice @_, 0, 2;
1327    my $arg = ${ $_[0] };
1328    $irc->yield(sl_login => "PONG :$arg");
1329    return PCI_EAT_NONE;
1330}
1331
1332# NICK messages for the purposes of determining our current nickname
1333sub S_nick {
1334    my ($self, $irc) = splice @_, 0, 2;
1335    my $nick = ( split /!/, ${ $_[0] } )[0];
1336    my $new = ${ $_[1] };
1337    $self->{INFO}{RealNick} = $new if ( $nick eq $self->{INFO}{RealNick} );
1338    return PCI_EAT_NONE;
1339}
1340
1341# tell POE::Filter::IRC::Compat to handle IDENTIFY-MSG
1342sub S_290 {
1343    my ($self, $irc) = splice @_, 0, 2;
1344    my $text = ${ $_[1] };
1345    $self->{ircd_compat}->identifymsg(1) if $text eq 'IDENTIFY-MSG';
1346    return PCI_EAT_NONE;
1347}
1348
1349sub S_cap {
1350    my ($self, $irc) = splice @_, 0, 2;
1351    my $cmd = ${ $_[0] };
1352
1353    if ($cmd eq 'ACK') {
1354        my $list = ${ $_[1] } eq '*' ? ${ $_[2] } : ${ $_[1] };
1355        my @enabled = split / /, $list;
1356
1357        if (grep { $_ =~ /^=?identify-msg$/ } @enabled) {
1358            $self->{ircd_compat}->identifymsg(1);
1359        }
1360        if (grep { $_ =~ /^-identify-msg$/ } @enabled) {
1361            $self->{ircd_compat}->identifymsg(0);
1362        }
1363    }
1364    return PCI_EAT_NONE;
1365}
1366
1367sub S_isupport {
1368    my ($self, $irc) = splice @_, 0, 2;
1369    my $isupport = ${ $_[0] };
1370    $self->{ircd_compat}->chantypes( $isupport->isupport('CHANTYPES') || [ '#', '&' ] );
1371    $irc->yield(sl_login => 'CAPAB IDENTIFY-MSG') if $isupport->isupport('CAPAB');
1372    $irc->yield(sl_login => 'PROTOCTL NAMESX') if $isupport->isupport('NAMESX');
1373    $irc->yield(sl_login => 'PROTOCTL UHNAMES') if $isupport->isupport('UHNAMES');
1374    return PCI_EAT_NONE;
1375}
1376
1377# accesses the ISupport plugin
1378sub isupport {
1379    my ($self, @args) = @_;
1380    return $self->{isupport}->isupport(@args);
1381}
1382
1383sub isupport_dump_keys {
1384    return $_[0]->{isupport}->isupport_dump_keys();
1385}
1386
1387sub resolver {
1388    return $_[0]->{resolver};
1389}
1390
1391sub _ip_get_version {
1392    my ($ip) = @_;
1393    return if !defined $ip;
1394
1395    # If the address does not contain any ':', maybe it's IPv4
1396    return 4 if $ip !~ /:/ && _ip_is_ipv4($ip);
1397
1398    # Is it IPv6 ?
1399    return 6 if _ip_is_ipv6($ip);
1400
1401    return;
1402}
1403
1404sub _ip_is_ipv4 {
1405    my ($ip) = @_;
1406    return if !defined $ip;
1407
1408    # Check for invalid chars
1409    return if $ip !~ /^[\d\.]+$/;
1410    return if $ip =~ /^\./;
1411    return if $ip =~ /\.$/;
1412
1413    # Single Numbers are considered to be IPv4
1414    return 1 if $ip =~ /^(\d+)$/ && $1 < 256;
1415
1416    # Count quads
1417    my $n = ($ip =~ tr/\./\./);
1418
1419    # IPv4 must have from 1 to 4 quads
1420    return if $n <= 0 || $n > 4;
1421
1422    # Check for empty quads
1423    return if $ip =~ /\.\./;
1424
1425    for my $quad (split /\./, $ip) {
1426        # Check for invalid quads
1427        return if $quad < 0 || $quad >= 256;
1428    }
1429    return 1;
1430}
1431
1432sub _ip_is_ipv6 {
1433    my ($ip) = @_;
1434    return if !defined $ip;
1435
1436    # Count octets
1437    my $n = ($ip =~ tr/:/:/);
1438    return if ($n <= 0 || $n >= 8);
1439
1440    # $k is a counter
1441    my $k;
1442
1443    for my $octet (split /:/, $ip) {
1444        $k++;
1445
1446        # Empty octet ?
1447        next if $octet eq '';
1448
1449        # Normal v6 octet ?
1450        next if $octet =~ /^[a-f\d]{1,4}$/i;
1451
1452        # Last octet - is it IPv4 ?
1453        if ($k == $n + 1) {
1454            next if (ip_is_ipv4($octet));
1455        }
1456
1457        return;
1458    }
1459
1460    # Does the IP address start with : ?
1461    return if $ip =~ m/^:[^:]/;
1462
1463    # Does the IP address finish with : ?
1464    return if $ip =~ m/[^:]:$/;
1465
1466    # Does the IP address have more than one '::' pattern ?
1467    return if $ip =~ s/:(?=:)//g > 1;
1468
1469    return 1;
1470}
1471
14721;
1473
1474=encoding utf8
1475
1476=head1 NAME
1477
1478POE::Component::IRC - A fully event-driven IRC client module
1479
1480=head1 SYNOPSIS
1481
1482 # A simple Rot13 'encryption' bot
1483
1484 use strict;
1485 use warnings;
1486 use POE qw(Component::IRC);
1487
1488 my $nickname = 'Flibble' . $$;
1489 my $ircname  = 'Flibble the Sailor Bot';
1490 my $server   = 'irc.perl.org';
1491
1492 my @channels = ('#Blah', '#Foo', '#Bar');
1493
1494 # We create a new PoCo-IRC object
1495 my $irc = POE::Component::IRC->spawn(
1496    nick => $nickname,
1497    ircname => $ircname,
1498    server  => $server,
1499 ) or die "Oh noooo! $!";
1500
1501 POE::Session->create(
1502     package_states => [
1503         main => [ qw(_default _start irc_001 irc_public) ],
1504     ],
1505     heap => { irc => $irc },
1506 );
1507
1508 $poe_kernel->run();
1509
1510 sub _start {
1511     my $heap = $_[HEAP];
1512
1513     # retrieve our component's object from the heap where we stashed it
1514     my $irc = $heap->{irc};
1515
1516     $irc->yield( register => 'all' );
1517     $irc->yield( connect => { } );
1518     return;
1519 }
1520
1521 sub irc_001 {
1522     my $sender = $_[SENDER];
1523
1524     # Since this is an irc_* event, we can get the component's object by
1525     # accessing the heap of the sender. Then we register and connect to the
1526     # specified server.
1527     my $irc = $sender->get_heap();
1528
1529     print "Connected to ", $irc->server_name(), "\n";
1530
1531     # we join our channels
1532     $irc->yield( join => $_ ) for @channels;
1533     return;
1534 }
1535
1536 sub irc_public {
1537     my ($sender, $who, $where, $what) = @_[SENDER, ARG0 .. ARG2];
1538     my $nick = ( split /!/, $who )[0];
1539     my $channel = $where->[0];
1540
1541     if ( my ($rot13) = $what =~ /^rot13 (.+)/ ) {
1542         $rot13 =~ tr[a-zA-Z][n-za-mN-ZA-M];
1543         $irc->yield( privmsg => $channel => "$nick: $rot13" );
1544     }
1545     return;
1546 }
1547
1548 # We registered for all events, this will produce some debug info.
1549 sub _default {
1550     my ($event, $args) = @_[ARG0 .. $#_];
1551     my @output = ( "$event: " );
1552
1553     for my $arg (@$args) {
1554         if ( ref $arg eq 'ARRAY' ) {
1555             push( @output, '[' . join(', ', @$arg ) . ']' );
1556         }
1557         else {
1558             push ( @output, "'$arg'" );
1559         }
1560     }
1561     print join ' ', @output, "\n";
1562     return;
1563 }
1564
1565=head1 DESCRIPTION
1566
1567POE::Component::IRC is a POE component (who'd have guessed?) which
1568acts as an easily controllable IRC client for your other POE
1569components and sessions. You create an IRC component and tell it what
1570events your session cares about and where to connect to, and it sends
1571back interesting IRC events when they happen. You make the client do
1572things by sending it events. That's all there is to it. Cool, no?
1573
1574[Note that using this module requires some familiarity with the
1575details of the IRC protocol. I'd advise you to read up on the gory
1576details of RFC 1459 (L<http://www.faqs.org/rfcs/rfc1459.html>) before you
1577get started. Keep the list of server numeric codes handy while you
1578program. Needless to say, you'll also need a good working knowledge of
1579POE, or this document will be of very little use to you.]
1580
1581The POE::Component::IRC distribution has a F<docs/> folder with a collection of
1582salient documentation including the pertinent RFCs.
1583
1584POE::Component::IRC consists of a POE::Session that manages the IRC connection
1585and dispatches C<irc_> prefixed events to interested sessions and
1586an object that can be used to access additional information using methods.
1587
1588Sessions register their interest in receiving C<irc_> events by sending
1589L<C<register>|/register> to the component. One would usually do this in
1590your C<_start> handler. Your session will continue to receive events until
1591you L<C<unregister>|/unregister>. The component will continue to stay
1592around until you tell it not to with L<C<shutdown>|/shutdown>.
1593
1594The L<SYNOPSIS|/SYNOPSIS> demonstrates a fairly basic bot.
1595
1596See L<POE::Component::IRC::Cookbook|POE::Component::IRC::Cookbook> for more
1597examples.
1598
1599=head2 Useful subclasses
1600
1601Included with POE::Component::IRC are a number of useful subclasses. As they
1602are subclasses they support all the methods, etc. documented here and have
1603additional methods and quirks which are documented separately:
1604
1605=over 4
1606
1607=item * L<POE::Component::IRC::State|POE::Component::IRC::State>
1608
1609POE::Component::IRC::State provides all the functionality of POE::Component::IRC
1610but also tracks IRC state entities such as nicks and channels.
1611
1612=item * L<POE::Component::IRC::Qnet|POE::Component::IRC::Qnet>
1613
1614POE::Component::IRC::Qnet is POE::Component::IRC tweaked for use on Quakenet IRC
1615network.
1616
1617=item * L<POE::Component::IRC::Qnet::State|POE::Component::IRC::Qnet::State>
1618
1619POE::Component::IRC::Qnet::State is a tweaked version of POE::Component::IRC::State
1620for use on the Quakenet IRC network.
1621
1622=back
1623
1624=head2 The Plugin system
1625
1626As of 3.7, PoCo-IRC sports a plugin system. The documentation for it can be
1627read by looking at L<POE::Component::IRC::Plugin|POE::Component::IRC::Plugin>.
1628That is not a subclass, just a placeholder for documentation!
1629
1630A number of useful plugins have made their way into the core distribution:
1631
1632=over 4
1633
1634=item * L<POE::Component::IRC::Plugin::DCC|POE::Component::IRC::Plugin::DCC>
1635
1636Provides DCC support. Loaded by default.
1637
1638=item * L<POE::Component::IRC::Plugin::AutoJoin|POE::Component::IRC::Plugin::AutoJoin>
1639
1640Keeps you on your favorite channels throughout reconnects and even kicks.
1641
1642=item * L<POE::Component::IRC::Plugin::Connector|POE::Component::IRC::Plugin::Connector>
1643
1644Glues an irc bot to an IRC network, i.e. deals with maintaining ircd connections.
1645
1646=item * L<POE::Component::IRC::Plugin::BotTraffic|POE::Component::IRC::Plugin::BotTraffic>
1647
1648Under normal circumstances irc bots do not normal the msgs and public msgs that
1649they generate themselves. This plugin enables you to handle those events.
1650
1651=item * L<POE::Component::IRC::Plugin::BotAddressed|POE::Component::IRC::Plugin::BotAddressed>
1652
1653Generates C<irc_bot_addressed> / C<irc_bot_mentioned> / C<irc_bot_mentioned_action>
1654events whenever your bot's name comes up in channel discussion.
1655
1656=item * L<POE::Component::IRC::Plugin::BotCommand|POE::Component::IRC::Plugin::BotCommand>
1657
1658Provides an easy way to handle commands issued to your bot.
1659
1660=item * L<POE::Component::IRC::Plugin::Console|POE::Component::IRC::Plugin::Console>
1661
1662See inside the component. See what events are being sent. Generate irc commands
1663manually. A TCP based console.
1664
1665=item * L<POE::Component::IRC::Plugin::FollowTail|POE::Component::IRC::Plugin::FollowTail>
1666
1667Follow the tail of an ever-growing file.
1668
1669=item * L<POE::Component::IRC::Plugin::Logger|POE::Component::IRC::Plugin::Logger>
1670
1671Log public and private messages to disk.
1672
1673=item * L<POE::Component::IRC::Plugin::NickServID|POE::Component::IRC::Plugin::NickServID>
1674
1675Identify with NickServ when needed.
1676
1677=item * L<POE::Component::IRC::Plugin::Proxy|POE::Component::IRC::Plugin::Proxy>
1678
1679A lightweight IRC proxy/bouncer.
1680
1681=item * L<POE::Component::IRC::Plugin::CTCP|POE::Component::IRC::Plugin::CTCP>
1682
1683Automagically generates replies to ctcp version, time and userinfo queries.
1684
1685=item * L<POE::Component::IRC::Plugin::PlugMan|POE::Component::IRC::Plugin::PlugMan>
1686
1687An experimental Plugin Manager plugin.
1688
1689=item * L<POE::Component::IRC::Plugin::NickReclaim|POE::Component::IRC::Plugin::NickReclaim>
1690
1691Automagically deals with your nickname being in use and reclaiming it.
1692
1693=item * L<POE::Component::IRC::Plugin::CycleEmpty|POE::Component::IRC::Plugin::CycleEmpty>
1694
1695Cycles (parts and rejoins) channels if they become empty and opless, in order
1696to gain ops.
1697
1698=back
1699
1700=head1 CONSTRUCTORS
1701
1702Both constructors return an object. The object is also available within 'irc_'
1703event handlers by using C<< $_[SENDER]->get_heap() >>. See also
1704L<C<register>|/register> and L<C<irc_registered>|/irc_registered>.
1705
1706=head2 C<spawn>
1707
1708Takes a number of arguments, all of which are optional. All the options
1709below may be supplied to the L<C<connect>|/connect> input event as well,
1710except for B<'alias'>, B<'options'>, B<'NoDNS'>, B<'debug'>, and
1711B<'plugin_debug'>.
1712
1713=over 4
1714
1715=item * B<'alias'>, a name (kernel alias) that this instance will be known
1716by;
1717
1718=item * B<'options'>, a hashref containing L<POE::Session|POE::Session>
1719options;
1720
1721=item * B<'Server'>, the server name;
1722
1723=item * B<'Port'>, the remote port number;
1724
1725=item * B<'Password'>, an optional password for restricted servers;
1726
1727=item * B<'Nick'>, your client's IRC nickname;
1728
1729=item * B<'Username'>, your client's username;
1730
1731=item * B<'Ircname'>, some cute comment or something.
1732
1733=item * B<'Bitmode'>, an integer representing your initial user modes set
1734in the USER command. See RFC 2812. If you do not set this, C<8> (+i) will
1735be used.
1736
1737=item *  B<'UseSSL'>, set to some true value if you want to connect using
1738SSL.
1739
1740=item *  B<'SSLCert'>, set to a SSL Certificate(PAM encoded) to connect using a client cert
1741
1742=item *  B<'SSLKey'>, set to a SSL Key(PAM encoded) to connect using a client cert
1743
1744=item *  B<'SSLCtx'>, set to a SSL Context to configure the SSL Connection
1745
1746The B<'SSLCert'> and B<'SSLKey'> both need to be specified. The B<'SSLCtx'> takes precedence specified.
1747
1748=item * B<'Raw'>, set to some true value to enable the component to send
1749L<C<irc_raw>|/irc_raw> and L<C<irc_raw_out>|/irc_raw_out> events.
1750
1751=item * B<'LocalAddr'>, which local IP address on a multihomed box to
1752connect as;
1753
1754=item * B<'LocalPort'>, the local TCP port to open your socket on;
1755
1756=item * B<'NoDNS'>, set this to 1 to disable DNS lookups using
1757PoCo-Client-DNS. (See note below).
1758
1759=item * B<'Flood'>, when true, it disables the component's flood
1760protection algorithms, allowing it to send messages to an IRC server at
1761full speed. Disconnects and k-lines are some common side effects of
1762flooding IRC servers, so care should be used when enabling this option.
1763Default is false.
1764
1765Two new attributes are B<'Proxy'> and B<'ProxyPort'> for sending your
1766=item * B<'Proxy'>, IP address or server name of a proxy server to use.
1767
1768=item * B<'ProxyPort'>, which tcp port on the proxy to connect to.
1769
1770=item * B<'NATAddr'>, what other clients see as your IP address.
1771
1772=item * B<'DCCPorts'>, an arrayref containing tcp ports that can be used
1773for DCC sends.
1774
1775=item * B<'Resolver'>, provide a L<POE::Component::Client::DNS|POE::Component::Client::DNS> object for the component to use.
1776
1777=item * B<'msg_length'>, the maximum length of IRC messages, in bytes.
1778Default is 450. The IRC component shortens all messages longer than this
1779value minus the length of your current nickname. IRC only allows raw
1780protocol lines messages that are 512 bytes or shorter, including the
1781trailing "\r\n". This is most relevant to long PRIVMSGs. The IRC component
1782can't be sure how long your user@host mask will be every time you send a
1783message, considering that most networks mangle the 'user' part and some
1784even replace the whole string (think FreeNode cloaks). If you have an
1785unusually long user@host mask you might want to decrease this value if
1786you're prone to sending long messages. Conversely, if you have an
1787unusually short one, you can increase this value if you want to be able to
1788send as long a message as possible. Be careful though, increase it too
1789much and the IRC server might disconnect you with a "Request too long"
1790message when you try to send a message that's too long.
1791
1792=item * B<'debug'>, if set to a true value causes the IRC component to
1793print every message sent to and from the server, as well as print some
1794warnings when it receives malformed messages. This option will be enabled
1795if the C<POCOIRC_DEBUG> environment variable is set to a true value.
1796
1797=item * B<'plugin_debug'>, set to some true value to print plugin debug
1798info, default 0. Plugins are processed inside an eval. When you enable
1799this option, you will be notified when (and why) a plugin raises an
1800exception. This option will be enabled if the C<POCOIRC_DEBUG> environment
1801variable is set to a true value.
1802
1803=item * B<'socks_proxy'>, specify a SOCKS4/SOCKS4a proxy to use.
1804
1805=item * B<'socks_port'>, the SOCKS port to use, defaults to 1080 if not
1806specified.
1807
1808=item * B<'socks_id'>, specify a SOCKS user_id. Default is none.
1809
1810=item * B<'useipv6'>, enable the use of IPv6 for connections.
1811
1812=item * B<'webirc'>, enable the use of WEBIRC to spoof host/IP.
1813You must have a WEBIRC password set up on the IRC server/network (so will
1814only work for servers which trust you to spoof the IP & host the connection
1815is from) - value should be a hashref containing keys C<pass>, C<user>,
1816C<host> and C<ip>.
1817
1818=back
1819
1820C<spawn> will supply reasonable defaults for any of these attributes
1821which are missing, so don't feel obliged to write them all out.
1822
1823If the component finds that L<POE::Component::Client::DNS|POE::Component::Client::DNS>
1824is installed it will use that to resolve the server name passed. Disable
1825this behaviour if you like, by passing: C<< NoDNS => 1 >>.
1826
1827IRC traffic through a proxy server. B<'Proxy'>'s value should be the IP
1828address or server name of the proxy. B<'ProxyPort'>'s value should be the
1829port on the proxy to connect to. L<C<connect>|/connect> will default
1830to using the I<actual> IRC server's port if you provide a proxy but omit
1831the proxy's port. These are for HTTP Proxies. See B<'socks_proxy'> for
1832SOCKS4 and SOCKS4a support.
1833
1834For those people who run bots behind firewalls and/or Network Address
1835Translation there are two additional attributes for DCC. B<'DCCPorts'>,
1836is an arrayref of ports to use when initiating DCC connections.
1837B<'NATAddr'>, is the NAT'ed IP address that your bot is hidden behind,
1838this is sent whenever you do DCC.
1839
1840SSL support requires L<POE::Component::SSLify|POE::Component::SSLify>, as
1841well as an IRC server that supports SSL connections. If you're missing
1842POE::Component::SSLify, specifying B<'UseSSL'> will do nothing. The
1843default is to not try to use SSL.
1844
1845B<'Resolver'>, requires a L<POE::Component::Client::DNS|POE::Component::Client::DNS>
1846object. Useful when spawning multiple poco-irc sessions, saves the
1847overhead of multiple dns sessions.
1848
1849B<'NoDNS'> has different results depending on whether it is set with
1850L<C<spawn>|/spawn> or L<C<connect>|/connect>. Setting it with
1851C<spawn>, disables the creation of the POE::Component::Client::DNS
1852completely. Setting it with L<C<connect>|/connect> on the other hand
1853allows the PoCo-Client-DNS session to be spawned, but will disable
1854any dns lookups using it.
1855
1856SOCKS4 proxy support is provided by B<'socks_proxy'>, B<'socks_port'> and
1857B<'socks_id'> parameters. If something goes wrong with the SOCKS connection
1858you should get a warning on STDERR. This is fairly experimental currently.
1859
1860IPv6 support is available for connecting to IPv6 enabled ircds (it won't
1861work for DCC though). To enable it, specify B<'useipv6'>. Perl >=5.14 or
1862L<Socket6|Socket6> (for older Perls) is required. If you that and
1863L<POE::Component::Client::DNS|POE::Component::Client::DNS> installed and
1864specify a hostname that resolves to an IPv6 address then IPv6 will be used.
1865If you specify an ipv6 B<'localaddr'> then IPv6 will be used.
1866
1867=head2 C<new>
1868
1869This method is deprecated. See the L<C<spawn>|/spawn> method instead.
1870The first argument should be a name (kernel alias) which this new
1871connection will be known by. Optionally takes more arguments (see
1872L<C<spawn>|/spawn> as name/value pairs. Returns a POE::Component::IRC
1873object. :)
1874
1875B<Note:> Use of this method will generate a warning. There are currently no
1876plans to make it die() >;]
1877
1878=head1 METHODS
1879
1880=head2 Information
1881
1882=head3 C<server>
1883
1884Takes no arguments. Returns the server host we are currently connected to
1885(or trying to connect to).
1886
1887=head3 C<port>
1888
1889Takes no arguments. Returns the server port we are currently connected to
1890(or trying to connect to).
1891
1892=head3 C<server_name>
1893
1894Takes no arguments. Returns the name of the IRC server that the component
1895is currently connected to.
1896
1897=head3 C<server_version>
1898
1899Takes no arguments. Returns the IRC server version.
1900
1901=head3 C<nick_name>
1902
1903Takes no arguments. Returns a scalar containing the current nickname that the
1904bot is using.
1905
1906=head3 C<localaddr>
1907
1908Takes no arguments. Returns the IP address being used.
1909
1910=head3 C<send_queue>
1911
1912The component provides anti-flood throttling. This method takes no arguments
1913and returns a scalar representing the number of messages that are queued up
1914waiting for dispatch to the irc server.
1915
1916=head3 C<logged_in>
1917
1918Takes no arguments. Returns true or false depending on whether the IRC
1919component is logged into an IRC network.
1920
1921=head3 C<connected>
1922
1923Takes no arguments. Returns true or false depending on whether the component's
1924socket is currently connected.
1925
1926=head3 C<disconnect>
1927
1928Takes no arguments. Terminates the socket connection disgracefully >;o]
1929
1930=head3 C<isupport>
1931
1932Takes one argument, a server capability to query. Returns C<undef> on failure
1933or a value representing the applicable capability. A full list of capabilities
1934is available at L<http://www.irc.org/tech_docs/005.html>.
1935
1936=head3 C<isupport_dump_keys>
1937
1938Takes no arguments, returns a list of the available server capabilities keys,
1939which can be used with L<C<isupport>|/isupport>.
1940
1941=head3 C<resolver>
1942
1943Returns a reference to the L<POE::Component::Client::DNS|POE::Component::Client::DNS>
1944object that is internally created by the component.
1945
1946=head2 Events
1947
1948=head3 C<session_id>
1949
1950I<Inherited from L<POE::Component::Syndicator|POE::Component::Syndicator/session_id>>
1951
1952Takes no arguments. Returns the ID of the component's session. Ideal for posting
1953events to the component.
1954
1955 $kernel->post($irc->session_id() => 'mode' => $channel => '+o' => $dude);
1956
1957=head3 C<session_alias>
1958
1959I<Inherited from L<POE::Component::Syndicator|POE::Component::Syndicator/session_alias>>
1960
1961Takes no arguments. Returns the session alias that has been set through
1962L<C<spawn>|/spawn>'s B<'alias'> argument.
1963
1964=head3 C<raw_events>
1965
1966With no arguments, returns true or false depending on whether
1967L<C<irc_raw>|/irc_raw> and L<C<irc_raw_out>|/irc_raw_out> events are being generated
1968or not. Provide a true or false argument to enable or disable this feature
1969accordingly.
1970
1971=head3 C<yield>
1972
1973I<Inherited from L<POE::Component::Syndicator|POE::Component::Syndicator/yield>>
1974
1975This method provides an alternative object based means of posting events to the
1976component. First argument is the event to post, following arguments are sent as
1977arguments to the resultant post.
1978
1979 $irc->yield(mode => $channel => '+o' => $dude);
1980
1981=head3 C<call>
1982
1983I<Inherited from L<POE::Component::Syndicator|POE::Component::Syndicator/call>>
1984
1985This method provides an alternative object based means of calling events to the
1986component. First argument is the event to call, following arguments are sent as
1987arguments to the resultant
1988call.
1989
1990 $irc->call(mode => $channel => '+o' => $dude);
1991
1992=head3 C<delay>
1993
1994I<Inherited from L<POE::Component::Syndicator|POE::Component::Syndicator/delay>>
1995
1996This method provides a way of posting delayed events to the component. The
1997first argument is an arrayref consisting of the delayed command to post and
1998any command arguments. The second argument is the time in seconds that one
1999wishes to delay the command being posted.
2000
2001 my $alarm_id = $irc->delay( [ mode => $channel => '+o' => $dude ], 60 );
2002
2003Returns an alarm ID that can be used with L<C<delay_remove>|/delay_remove>
2004to cancel the delayed event. This will be undefined if something went wrong.
2005
2006=head3 C<delay_remove>
2007
2008I<Inherited from L<POE::Component::Syndicator|POE::Component::Syndicator/delay_remove>>
2009
2010This method removes a previously scheduled delayed event from the component.
2011Takes one argument, the C<alarm_id> that was returned by a
2012L<C<delay>|/delay> method call.
2013
2014 my $arrayref = $irc->delay_remove( $alarm_id );
2015
2016Returns an arrayref that was originally requested to be delayed.
2017
2018=head3 C<send_event>
2019
2020I<Inherited from L<POE::Component::Syndicator|POE::Component::Syndicator/send_event>>
2021
2022Sends an event through the component's event handling system. These will get
2023processed by plugins then by registered sessions. First argument is the event
2024name, followed by any parameters for that event.
2025
2026=head3 C<send_event_next>
2027
2028I<Inherited from L<POE::Component::Syndicator|POE::Component::Syndicator/send_event_next>>
2029
2030This sends an event right after the one that's currently being processed.
2031Useful if you want to generate some event which is directly related to
2032another event so you want them to appear together. This method can only be
2033called when POE::Component::IRC is processing an event, e.g. from one of your
2034event handlers. Takes the same arguments as L<C<send_event>|/send_event>.
2035
2036=head3 C<send_event_now>
2037
2038I<Inherited from L<POE::Component::Syndicator|POE::Component::Syndicator/send_event_now>>
2039
2040This will send an event to be processed immediately. This means that if an
2041event is currently being processed and there are plugins or sessions which
2042will receive it after you do, then an event sent with C<send_event_now> will
2043be received by those plugins/sessions I<before> the current event. Takes the
2044same arguments as L<C<send_event>|/send_event>.
2045
2046=head2 Plugins
2047
2048=head3 C<pipeline>
2049
2050I<Inherited from L<Object::Pluggable|Object::Pluggable/pipeline>>
2051
2052Returns the L<Object::Pluggable::Pipeline|Object::Pluggable::Pipeline>
2053object.
2054
2055=head3 C<plugin_add>
2056
2057I<Inherited from L<Object::Pluggable|Object::Pluggable/plugin_add>>
2058
2059Accepts two arguments:
2060
2061 The alias for the plugin
2062 The actual plugin object
2063 Any number of extra arguments
2064
2065The alias is there for the user to refer to it, as it is possible to have
2066multiple plugins of the same kind active in one Object::Pluggable object.
2067
2068This method goes through the pipeline's C<push()> method, which will call
2069C<< $plugin->plugin_register($pluggable, @args) >>.
2070
2071Returns the number of plugins now in the pipeline if plugin was initialized,
2072C<undef>/an empty list if not.
2073
2074=head3 C<plugin_del>
2075
2076I<Inherited from L<Object::Pluggable|Object::Pluggable/plugin_del>>
2077
2078Accepts the following arguments:
2079
2080 The alias for the plugin or the plugin object itself
2081 Any number of extra arguments
2082
2083This method goes through the pipeline's C<remove()> method, which will call
2084C<< $plugin->plugin_unregister($pluggable, @args) >>.
2085
2086Returns the plugin object if the plugin was removed, C<undef>/an empty list
2087if not.
2088
2089=head3 C<plugin_get>
2090
2091I<Inherited from L<Object::Pluggable|Object::Pluggable/plugin_get>>
2092
2093Accepts the following arguments:
2094
2095 The alias for the plugin
2096
2097This method goes through the pipeline's C<get()> method.
2098
2099Returns the plugin object if it was found, C<undef>/an empty list if not.
2100
2101=head3 C<plugin_list>
2102
2103I<Inherited from L<Object::Pluggable|Object::Pluggable/plugin_list>>
2104
2105Takes no arguments.
2106
2107Returns a hashref of plugin objects, keyed on alias, or an empty list if
2108there are no plugins loaded.
2109
2110=head3 C<plugin_order>
2111
2112I<Inherited from L<Object::Pluggable|Object::Pluggable/plugin_order>>
2113
2114Takes no arguments.
2115
2116Returns an arrayref of plugin objects, in the order which they are
2117encountered in the pipeline.
2118
2119=head3 C<plugin_register>
2120
2121I<Inherited from L<Object::Pluggable|Object::Pluggable/plugin_register>>
2122
2123Accepts the following arguments:
2124
2125 The plugin object
2126 The type of the hook (the hook types are specified with _pluggable_init()'s 'types')
2127 The event name[s] to watch
2128
2129The event names can be as many as possible, or an arrayref. They correspond
2130to the prefixed events and naturally, arbitrary events too.
2131
2132You do not need to supply events with the prefix in front of them, just the
2133names.
2134
2135It is possible to register for all events by specifying 'all' as an event.
2136
2137Returns 1 if everything checked out fine, C<undef>/an empty list if something
2138is seriously wrong.
2139
2140=head3 C<plugin_unregister>
2141
2142I<Inherited from L<Object::Pluggable|Object::Pluggable/plugin_unregister>>
2143
2144Accepts the following arguments:
2145
2146 The plugin object
2147 The type of the hook (the hook types are specified with _pluggable_init()'s 'types')
2148 The event name[s] to unwatch
2149
2150The event names can be as many as possible, or an arrayref. They correspond
2151to the prefixed events and naturally, arbitrary events too.
2152
2153You do not need to supply events with the prefix in front of them, just the
2154names.
2155
2156It is possible to register for all events by specifying 'all' as an event.
2157
2158Returns 1 if all the event name[s] was unregistered, undef if some was not
2159found.
2160
2161=head1 INPUT EVENTS
2162
2163How to talk to your new IRC component... here's the events we'll accept.
2164These are events that are posted to the component, either via
2165C<< $poe_kernel->post() >> or via the object method L<C<yield>|/yield>.
2166
2167So the following would be functionally equivalent:
2168
2169 sub irc_001 {
2170     my ($kernel,$sender) = @_[KERNEL,SENDER];
2171     my $irc = $sender->get_heap(); # obtain the poco's object
2172
2173     $irc->yield( privmsg => 'foo' => 'Howdy!' );
2174     $kernel->post( $sender => privmsg => 'foo' => 'Howdy!' );
2175     $kernel->post( $irc->session_id() => privmsg => 'foo' => 'Howdy!' );
2176     $kernel->post( $irc->session_alias() => privmsg => 'foo' => 'Howdy!' );
2177
2178     return;
2179 }
2180
2181=head2 Important Commands
2182
2183=head3 C<register>
2184
2185I<Inherited from L<POE::Component::Syndicator|POE::Component::Syndicator/register>>
2186
2187Takes N arguments: a list of event names that your session wants to
2188listen for, minus the C<irc_> prefix. So, for instance, if you just
2189want a bot that keeps track of which people are on a channel, you'll
2190need to listen for JOINs, PARTs, QUITs, and KICKs to people on the
2191channel you're in. You'd tell POE::Component::IRC that you want those
2192events by saying this:
2193
2194 $kernel->post('my client', 'register', qw(join part quit kick));
2195
2196Then, whenever people enter or leave a channel your bot is on (forcibly
2197or not), your session will receive events with names like
2198L<C<irc_join>|/irc_join>, L<C<irc_kick>|/irc_kick>, etc.,
2199which you can use to update a list of people on the channel.
2200
2201Registering for B<'all'> will cause it to send all IRC-related events to
2202you; this is the easiest way to handle it. See the test script for an
2203example.
2204
2205Registering will generate an L<C<irc_registered>|/irc_registered>
2206event that your session can trap. C<ARG0> is the components object. Useful
2207if you want to bolt PoCo-IRC's new features such as Plugins into a bot
2208coded to the older deprecated API. If you are using the new API, ignore this :)
2209
2210Registering with multiple component sessions can be tricky, especially if
2211one wants to marry up sessions/objects, etc. Check the L<SIGNALS|/SIGNALS>
2212section for an alternative method of registering with multiple poco-ircs.
2213
2214Starting with version 4.96, if you spawn the component from inside another POE
2215session, the component will automatically register that session as wanting
2216B<'all'> irc events. That session will receive an
2217L<C<irc_registered>|/irc_registered> event indicating that the component
2218is up and ready to go.
2219
2220=head3 C<unregister>
2221
2222I<Inherited from L<POE::Component::Syndicator|POE::Component::Syndicator/unregister>>
2223
2224Takes N arguments: a list of event names which you I<don't> want to
2225receive. If you've previously done a L<C<register>|/register>
2226for a particular event which you no longer care about, this event will
2227tell the IRC connection to stop sending them to you. (If you haven't, it just
2228ignores you. No big deal.)
2229
2230If you have registered with 'all', attempting to unregister individual
2231events such as 'mode', etc. will not work. This is a 'feature'.
2232
2233=head3 C<connect>
2234
2235Takes one argument: a hash reference of attributes for the new connection,
2236see L<C<spawn>|/spawn> for details. This event tells the IRC client to
2237connect to a new/different server. If it has a connection already open, it'll
2238close it gracefully before reconnecting.
2239
2240=head3 C<ctcp> and C<ctcpreply>
2241
2242Sends a CTCP query or response to the nick(s) or channel(s) which you
2243specify. Takes 2 arguments: the nick or channel to send a message to
2244(use an array reference here to specify multiple recipients), and the
2245plain text of the message to send (the CTCP quoting will be handled
2246for you). The "/me" command in popular IRC clients is actually a CTCP action.
2247
2248 # Doing a /me
2249 $irc->yield(ctcp => $channel => 'ACTION dances.');
2250
2251=head3 C<join>
2252
2253Tells your IRC client to join a single channel of your choice. Takes
2254at least one arg: the channel name (required) and the channel key
2255(optional, for password-protected channels).
2256
2257=head3 C<kick>
2258
2259Tell the IRC server to forcibly evict a user from a particular
2260channel. Takes at least 2 arguments: a channel name, the nick of the
2261user to boot, and an optional witty message to show them as they sail
2262out the door.
2263
2264=head3 C<remove>
2265
2266Tell the IRC server to forcibly evict a user from a particular
2267channel. Takes at least 2 arguments: a channel name, the nick of the
2268user to boot, and an optional witty message to show them as they sail
2269out the door. Similar to KICK but does an enforced PART instead. Not
2270supported by all servers.
2271
2272=head3 C<mode>
2273
2274Request a mode change on a particular channel or user. Takes at least
2275one argument: the mode changes to effect, as a single string (e.g.
2276"#mychan +sm-p+o"), and any number of optional operands to the mode changes
2277(nicks, hostmasks, channel keys, whatever.) Or just pass them all as one
2278big string and it'll still work, whatever. I regret that I haven't the
2279patience now to write a detailed explanation, but serious IRC users know
2280the details anyhow.
2281
2282=head3 C<nick>
2283
2284Allows you to change your nickname. Takes exactly one argument: the
2285new username that you'd like to be known as.
2286
2287=head3 C<nickserv>
2288
2289Talks to NickServ, on networks which have it. Takes any number of
2290arguments.
2291
2292=head3 C<notice>
2293
2294Sends a NOTICE message to the nick(s) or channel(s) which you
2295specify. Takes 2 arguments: the nick or channel to send a notice to
2296(use an array reference here to specify multiple recipients), and the
2297text of the notice to send.
2298
2299=head3 C<part>
2300
2301Tell your IRC client to leave the channels which you pass to it. Takes
2302any number of arguments: channel names to depart from. If the last argument
2303doesn't begin with a channel name identifier or contains a space character,
2304it will be treated as a PART message and dealt with accordingly.
2305
2306=head3 C<privmsg>
2307
2308Sends a public or private message to the nick(s) or channel(s) which
2309you specify. Takes 2 arguments: the nick or channel to send a message
2310to (use an array reference here to specify multiple recipients), and
2311the text of the message to send.
2312
2313Have a look at the constants in L<IRC::Utils|IRC::Utils> if you would
2314like to use formatting and color codes in your messages.
2315
2316 $irc->yield('primvsg', '#mychannel', 'Hello there');
2317
2318 # same, but with a green Hello
2319 use IRC::Utils qw(GREEN NORMAL);
2320 $irc->yield('primvsg', '#mychannel', GREEN.'Hello'.NORMAL.' there');
2321
2322=head3 C<quit>
2323
2324Tells the IRC server to disconnect you. Takes one optional argument:
2325some clever, witty string that other users in your channels will see
2326as you leave. You can expect to get an
2327L<C<irc_disconnected>|/irc_disconnected> event shortly after sending this.
2328
2329=head3 C<shutdown>
2330
2331By default, POE::Component::IRC sessions never go away. Even after
2332they're disconnected, they're still sitting around in the background,
2333waiting for you to call L<C<connect>|/connect> on them again to
2334reconnect. (Whether this behavior is the Right Thing is doubtful, but I
2335don't want to break backwards compatibility at this point.) You can send
2336the IRC session a C<shutdown> event manually to make it delete itself.
2337
2338If you are logged into an IRC server, C<shutdown> first will send a quit
2339message and wait to be disconnected. It will wait for up to 5 seconds before
2340forcibly disconnecting from the IRC server. If you provide an argument, that
2341will be used as the QUIT message. If you provide two arguments, the second
2342one will be used as the timeout (in seconds).
2343
2344Terminating multiple components can be tricky. Check the L<SIGNALS|/SIGNALS>
2345section for a method of shutting down multiple poco-ircs.
2346
2347=head3 C<topic>
2348
2349Retrieves or sets the topic for particular channel. If called with just
2350the channel name as an argument, it will ask the server to return the
2351current topic. If called with the channel name and a string, it will
2352set the channel topic to that string. Supply an empty string to unset a
2353channel topic.
2354
2355=head3 C<debug>
2356
2357Takes one argument: 0 to turn debugging off or 1 to turn debugging on.
2358This flips the debugging flag in L<POE::Filter::IRCD|POE::Filter::IRCD>,
2359L<POE::Filter::IRC::Compat|POE::Filter::IRC::Compat>, and
2360POE::Component::IRC. This has the same effect as setting Debug in
2361L<C<spawn>|/spawn> or L<C<connect>|/connect>.
2362
2363=head2 Not-So-Important Commands
2364
2365=head3 C<admin>
2366
2367Asks your server who your friendly neighborhood server administrators
2368are. If you prefer, you can pass it a server name to query, instead of
2369asking the server you're currently on.
2370
2371=head3 C<away>
2372
2373When sent with an argument (a message describig where you went), the
2374server will note that you're now away from your machine or otherwise
2375preoccupied, and pass your message along to anyone who tries to
2376communicate with you. When sent without arguments, it tells the server
2377that you're back and paying attention.
2378
2379=head3 C<cap>
2380
2381Used to query/enable/disable IRC protocol capabilities. Takes any number of
2382arguments.
2383
2384=head3 C<dcc*>
2385
2386See the L<DCC plugin|POE::Component::IRC::Plugin/COMMANDS> (loaded by default)
2387documentation for DCC-related commands.
2388
2389=head3 C<info>
2390
2391Basically the same as the L<C<version>|/version> command, except that the
2392server is permitted to return any information about itself that it thinks is
2393relevant. There's some nice, specific standards-writing for ya, eh?
2394
2395=head3 C<invite>
2396
2397Invites another user onto an invite-only channel. Takes 2 arguments:
2398the nick of the user you wish to admit, and the name of the channel to
2399invite them to.
2400
2401=head3 C<ison>
2402
2403Asks the IRC server which users out of a list of nicknames are
2404currently online. Takes any number of arguments: a list of nicknames
2405to query the IRC server about.
2406
2407=head3 C<links>
2408
2409Asks the server for a list of servers connected to the IRC
2410network. Takes two optional arguments, which I'm too lazy to document
2411here, so all you would-be linklooker writers should probably go dig up
2412the RFC.
2413
2414=head3 C<list>
2415
2416Asks the server for a list of visible channels and their topics. Takes
2417any number of optional arguments: names of channels to get topic
2418information for. If called without any channel names, it'll list every
2419visible channel on the IRC network. This is usually a really big list,
2420so don't do this often.
2421
2422=head3 C<motd>
2423
2424Request the server's "Message of the Day", a document which typically
2425contains stuff like the server's acceptable use policy and admin
2426contact email addresses, et cetera. Normally you'll automatically
2427receive this when you log into a server, but if you want it again,
2428here's how to do it. If you'd like to get the MOTD for a server other
2429than the one you're logged into, pass it the server's hostname as an
2430argument; otherwise, no arguments.
2431
2432=head3 C<names>
2433
2434Asks the server for a list of nicknames on particular channels. Takes
2435any number of arguments: names of channels to get lists of users
2436for. If called without any channel names, it'll tell you the nicks of
2437everyone on the IRC network. This is a really big list, so don't do
2438this much.
2439
2440=head3 C<quote>
2441
2442Sends a raw line of text to the server. Takes one argument: a string
2443of a raw IRC command to send to the server. It is more optimal to use
2444the events this module supplies instead of writing raw IRC commands
2445yourself.
2446
2447=head3 C<stats>
2448
2449Returns some information about a server. Kinda complicated and not
2450terribly commonly used, so look it up in the RFC if you're
2451curious. Takes as many arguments as you please.
2452
2453=head3 C<time>
2454
2455Asks the server what time it thinks it is, which it will return in a
2456human-readable form. Takes one optional argument: a server name to
2457query. If not supplied, defaults to current server.
2458
2459=head3 C<trace>
2460
2461If you pass a server name or nick along with this request, it asks the
2462server for the list of servers in between you and the thing you
2463mentioned. If sent with no arguments, it will show you all the servers
2464which are connected to your current server.
2465
2466=head3 C<users>
2467
2468Asks the server how many users are logged into it. Defaults to the
2469server you're currently logged into; however, you can pass a server
2470name as the first argument to query some other machine instead.
2471
2472=head3 C<version>
2473
2474Asks the server about the version of ircd that it's running. Takes one
2475optional argument: a server name to query. If not supplied, defaults
2476to current server.
2477
2478=head3 C<who>
2479
2480Lists the logged-on users matching a particular channel name, hostname,
2481nickname, or what-have-you. Takes one optional argument: a string for
2482it to search for. Wildcards are allowed; in the absence of this
2483argument, it will return everyone who's currently logged in (bad
2484move). Tack an "o" on the end if you want to list only IRCops, as per
2485the RFC.
2486
2487=head3 C<whois>
2488
2489Queries the IRC server for detailed information about a particular
2490user. Takes any number of arguments: nicknames or hostmasks to ask for
2491information about. As of version 3.2, you will receive an
2492L<C<irc_whois>|/irc_whois> event in addition to the usual numeric
2493responses. See below for details.
2494
2495=head3 C<whowas>
2496
2497Asks the server for information about nickname which is no longer
2498connected. Takes at least one argument: a nickname to look up (no
2499wildcards allowed), the optional maximum number of history entries to
2500return, and the optional server hostname to query. As of version 3.2,
2501you will receive an L<C<irc_whowas>|/irc_whowas> event in addition
2502to the usual numeric responses. See below for details.
2503
2504=head3 C<ping> and C<pong>
2505
2506Included for completeness sake. The component will deal with ponging to
2507pings automatically. Don't worry about it.
2508
2509=head2 Purely Esoteric Commands
2510
2511=head3 C<die>
2512
2513Tells the IRC server you're connect to, to terminate. Only useful for
2514IRCops, thank goodness. Takes no arguments.
2515
2516=head3 C<locops>
2517
2518Opers-only command. This one sends a message to all currently
2519logged-on local-opers (+l). This option is specific to EFNet.
2520
2521=head3 C<oper>
2522
2523In the exceedingly unlikely event that you happen to be an IRC
2524operator, you can use this command to authenticate with your IRC
2525server. Takes 2 arguments: your username and your password.
2526
2527=head3 C<operwall>
2528
2529Opers-only command. This one sends a message to all currently
2530logged-on global opers. This option is specific to EFNet.
2531
2532=head3 C<rehash>
2533
2534Tells the IRC server you're connected to, to rehash its configuration
2535files. Only useful for IRCops. Takes no arguments.
2536
2537=head3 C<restart>
2538
2539Tells the IRC server you're connected to, to shut down and restart itself.
2540Only useful for IRCops, thank goodness. Takes no arguments.
2541
2542=head3 C<sconnect>
2543
2544Tells one IRC server (which you have operator status on) to connect to
2545another. This is actually the CONNECT command, but I already had an
2546event called L<C<connect>|/connect>, so too bad. Takes the args
2547you'd expect: a server to connect to, an optional port to connect on,
2548and an optional remote server to connect with, instead of the one you're
2549currently on.
2550
2551=head3 C<squit>
2552
2553Operator-only command used to disconnect server links. Takes two arguments,
2554the server to disconnect and a message explaining your action.
2555
2556=head3 C<summon>
2557
2558Don't even ask.
2559
2560=head3 C<servlist>
2561
2562Lists the currently connected services on the network that are visible to you.
2563Takes two optional arguments, a mask for matching service names against, and
2564a service type.
2565
2566=head3 C<squery>
2567
2568Sends a message to a service. Takes the same arguments as
2569L<C<privmsg>|/privmsg>.
2570
2571=head3 C<userhost>
2572
2573Asks the IRC server for information about particular nicknames. (The
2574RFC doesn't define exactly what this is supposed to return.) Takes any
2575number of arguments: the nicknames to look up.
2576
2577=head3 C<wallops>
2578
2579Another opers-only command. This one sends a message to all currently
2580logged-on opers (and +w users); sort of a mass PA system for the IRC
2581server administrators. Takes one argument: some clever, witty message
2582to send.
2583
2584=head1 OUTPUT EVENTS
2585
2586The events you will receive (or can ask to receive) from your running
2587IRC component. Note that all incoming event names your session will
2588receive are prefixed by C<irc_>, to inhibit event namespace pollution.
2589
2590If you wish, you can ask the client to send you every event it
2591generates. Simply register for the event name "all". This is a lot
2592easier than writing a huge list of things you specifically want to
2593listen for.
2594
2595FIXME: I'd really like to classify these somewhat ("basic", "oper", "ctcp",
2596"dcc", "raw" or some such), and I'd welcome suggestions for ways to make
2597this easier on the user, if you can think of some.
2598
2599In your event handlers, C<$_[SENDER]> is the particular component session that
2600sent you the event. C<< $_[SENDER]->get_heap() >> will retrieve the component's
2601object. Useful if you want on-the-fly access to the object and its methods.
2602
2603=head2 Important Events
2604
2605=head3 C<irc_registered>
2606
2607I<Inherited from L<POE::Component::Syndicator|POE::Component::Syndicator/syndicator_registered>>
2608
2609Sent once to the requesting session on registration (see
2610L<C<register>|/register>). C<ARG0> is a reference tothe component's object.
2611
2612=head3 C<irc_shutdown>
2613
2614I<Inherited from L<POE::Component::Syndicator|POE::Component::Syndicator/syndicator_shutdown>>
2615
2616Sent to all registered sessions when the component has been asked to
2617L<C<shutdown>|/shutdown>. C<ARG0> will be the session ID of the requesting
2618session.
2619
2620=head3 C<irc_connected>
2621
2622The IRC component will send an C<irc_connected> event as soon as it
2623establishes a connection to an IRC server, before attempting to log
2624in. C<ARG0> is the server name.
2625
2626B<NOTE:> When you get an C<irc_connected> event, this doesn't mean you
2627can start sending commands to the server yet. Wait until you receive
2628an L<C<irc_001>|/All numeric events> event (the server welcome message)
2629before actually sending anything back to the server.
2630
2631=head3 C<irc_ctcp>
2632
2633C<irc_ctcp> events are generated upon receipt of CTCP messages, in addition to
2634the C<irc_ctcp_*> events mentioned below. They are identical in every way to
2635these, with one difference: instead of the * being in the method name, it
2636is prepended to the argument list. For example, if someone types C</ctcp
2637Flibble foo bar>, an C<irc_ctcp> event will be sent with B<'foo'> as C<ARG0>,
2638and the rest as given below.
2639
2640It is not recommended that you register for both C<irc_ctcp> and C<irc_ctcp_*>
2641events, since they will both be fired and presumably cause duplication.
2642
2643=head3 C<irc_ctcp_*>
2644
2645C<irc_ctcp_whatever> events are generated upon receipt of CTCP messages.
2646For instance, receiving a CTCP PING request generates an C<irc_ctcp_ping>
2647event, CTCP ACTION (produced by typing "/me" in most IRC clients)
2648generates an C<irc_ctcp_action> event, blah blah, so on and so forth. C<ARG0>
2649is the nick!hostmask of the sender. C<ARG1> is the channel/recipient
2650name(s). C<ARG2> is the text of the CTCP message. On servers supporting the
2651IDENTIFY-MSG feature (e.g. FreeNode), CTCP ACTIONs will have C<ARG3>, which
2652will be C<1> if the sender has identified with NickServ, C<0> otherwise.
2653
2654Note that DCCs are handled separately -- see the
2655L<DCC plugin|POE::Component::IRC::Plugin::DCC>.
2656
2657=head3 C<irc_ctcpreply_*>
2658
2659C<irc_ctcpreply_whatever> messages are just like C<irc_ctcp_whatever>
2660messages, described above, except that they're generated when a response
2661to one of your CTCP queries comes back. They have the same arguments and
2662such as C<irc_ctcp_*> events.
2663
2664=head3 C<irc_disconnected>
2665
2666The counterpart to L<C<irc_connected>|/irc_connected>, sent whenever
2667a socket connection to an IRC server closes down (whether intentionally or
2668unintentionally). C<ARG0> is the server name.
2669
2670=head3 C<irc_error>
2671
2672You get this whenever the server sends you an ERROR message. Expect
2673this to usually be accompanied by the sudden dropping of your
2674connection. C<ARG0> is the server's explanation of the error.
2675
2676=head3 C<irc_join>
2677
2678Sent whenever someone joins a channel that you're on. C<ARG0> is the
2679person's nick!hostmask. C<ARG1> is the channel name.
2680
2681=head3 C<irc_invite>
2682
2683Sent whenever someone offers you an invitation to another channel. C<ARG0>
2684is the person's nick!hostmask. C<ARG1> is the name of the channel they want
2685you to join.
2686
2687=head3 C<irc_kick>
2688
2689Sent whenever someone gets booted off a channel that you're on. C<ARG0>
2690is the kicker's nick!hostmask. C<ARG1> is the channel name. C<ARG2> is the
2691nick of the unfortunate kickee. C<ARG3> is the explanation string for the
2692kick.
2693
2694=head3 C<irc_mode>
2695
2696Sent whenever someone changes a channel mode in your presence, or when
2697you change your own user mode. C<ARG0> is the nick!hostmask of that
2698someone. C<ARG1> is the channel it affects (or your nick, if it's a user
2699mode change). C<ARG2> is the mode string (i.e., "+o-b"). The rest of the
2700args (C<ARG3 .. $#_>) are the operands to the mode string (nicks,
2701hostmasks, channel keys, whatever).
2702
2703=head3 C<irc_msg>
2704
2705Sent whenever you receive a PRIVMSG command that was addressed to you
2706privately. C<ARG0> is the nick!hostmask of the sender. C<ARG1> is an array
2707reference containing the nick(s) of the recipients. C<ARG2> is the text
2708of the message. On servers supporting the IDENTIFY-MSG feature (e.g.
2709FreeNode), there will be an additional argument, C<ARG3>, which will be
2710C<1> if the sender has identified with NickServ, C<0> otherwise.
2711
2712=head3 C<irc_nick>
2713
2714Sent whenever you, or someone around you, changes nicks. C<ARG0> is the
2715nick!hostmask of the changer. C<ARG1> is the new nick that they changed
2716to.
2717
2718=head3 C<irc_notice>
2719
2720Sent whenever you receive a NOTICE command. C<ARG0> is the nick!hostmask
2721of the sender. C<ARG1> is an array reference containing the nick(s) or
2722channel name(s) of the recipients. C<ARG2> is the text of the NOTICE
2723message.
2724
2725=head3 C<irc_part>
2726
2727Sent whenever someone leaves a channel that you're on. C<ARG0> is the
2728person's nick!hostmask. C<ARG1> is the channel name. C<ARG2> is the part
2729message.
2730
2731=head3 C<irc_public>
2732
2733Sent whenever you receive a PRIVMSG command that was sent to a channel.
2734C<ARG0> is the nick!hostmask of the sender. C<ARG1> is an array
2735reference containing the channel name(s) of the recipients. C<ARG2> is the
2736text of the message. On servers supporting the IDENTIFY-MSG feature (e.g.
2737FreeNode), there will be an additional argument, C<ARG3>, which will be
2738C<1> if the sender has identified with NickServ, C<0> otherwise.
2739
2740=head3 C<irc_quit>
2741
2742Sent whenever someone on a channel with you quits IRC (or gets
2743KILLed). C<ARG0> is the nick!hostmask of the person in question. C<ARG1> is
2744the clever, witty message they left behind on the way out.
2745
2746=head3 C<irc_socketerr>
2747
2748Sent when a connection couldn't be established to the IRC server. C<ARG0>
2749is probably some vague and/or misleading reason for what failed.
2750
2751=head3 C<irc_topic>
2752
2753Sent when a channel topic is set or unset. C<ARG0> is the nick!hostmask of the
2754sender. C<ARG1> is the channel affected. C<ARG2> will be either: a string if the
2755topic is being set; or a zero-length string (i.e. '') if the topic is being
2756unset. Note: replies to queries about what a channel topic *is*
2757(i.e. TOPIC #channel), are returned as numerics, not with this event.
2758
2759=head3 C<irc_whois>
2760
2761Sent in response to a WHOIS query. C<ARG0> is a hashref, with the following
2762keys:
2763
2764=over 4
2765
2766=item * B<'nick'>, the users nickname;
2767
2768=item * B<'user'>, the users username;
2769
2770=item * B<'host'>, their hostname;
2771
2772=item * B<'real'>, their real name;
2773
2774=item * B<'idle'>, their idle time in seconds;
2775
2776=item * B<'signon'>, the epoch time they signed on (will be undef if ircd
2777does not support this);
2778
2779=item * B<'channels'>, an arrayref listing visible channels they are on,
2780the channel is prefixed with '@','+','%' depending on whether they have
2781+o +v or +h;
2782
2783=item * B<'server'>, their server (might not be useful on some networks);
2784
2785=item * B<'oper'>, whether they are an IRCop, contains the IRC operator
2786string if they are, undef if they aren't.
2787
2788=item * B<'actually'>, some ircds report the user's actual ip address,
2789that'll be here;
2790
2791=item * B<'identified'>. if the user has identified with NICKSERV
2792(ircu, seven, Plexus)
2793
2794=item * B<'modes'>, a string describing the user's modes (Rizon)
2795
2796=back
2797
2798=head3 C<irc_whowas>
2799
2800Similar to the above, except some keys will be missing.
2801
2802=head3 C<irc_raw>
2803
2804Enabled by passing C<< Raw => 1 >> to L<C<spawn>|/spawn> or
2805L<C<connect>|/connect>, or by calling L<C<raw_events>|/raw_events> with
2806a true argument. C<ARG0> is the raw IRC string received by the component from
2807the IRC server, before it has been mangled by filters and such like.
2808
2809=head3 C<irc_raw_out>
2810
2811Enabled by passing C<< Raw => 1 >> to L<C<spawn>|/spawn> or
2812L<C<connect>|/connect>, or by calling L<C<raw_events>|/raw_events> with
2813a true argument. C<ARG0> is the raw IRC string sent by the component to the
2814the IRC server.
2815
2816=head3 C<irc_isupport>
2817
2818Emitted by the first event after an L<C<irc_005>|/All numeric events>, to
2819indicate that isupport information has been gathered. C<ARG0> is the
2820L<POE::Component::IRC::Plugin::ISupport|POE::Component::IRC::Plugin::ISupport>
2821object.
2822
2823=head3 C<irc_socks_failed>
2824
2825Emitted whenever we fail to connect successfully to a SOCKS server or the
2826SOCKS server is not actually a SOCKS server. C<ARG0> will be some vague reason
2827as to what went wrong. Hopefully.
2828
2829=head3 C<irc_socks_rejected>
2830
2831Emitted whenever a SOCKS connection is rejected by a SOCKS server. C<ARG0> is
2832the SOCKS code, C<ARG1> the SOCKS server address, C<ARG2> the SOCKS port and
2833C<ARG3> the SOCKS user id (if defined).
2834
2835=head3 C<irc_plugin_add>
2836
2837I<Inherited from L<Object::Pluggable|Object::Pluggable/_pluggable_event>>
2838
2839Emitted whenever a new plugin is added to the pipeline. C<ARG0> is the
2840plugin alias. C<ARG1> is the plugin object.
2841
2842=head3 C<irc_plugin_del>
2843
2844I<Inherited from L<Object::Pluggable|Object::Pluggable/_pluggable_event>>
2845
2846Emitted whenever a plugin is removed from the pipeline. C<ARG0> is the
2847plugin alias. C<ARG1> is the plugin object.
2848
2849=head3 C<irc_plugin_error>
2850
2851I<Inherited from L<Object::Pluggable|Object::Pluggable/_pluggable_event>>
2852
2853Emitted when an error occurs while executing a plugin handler. C<ARG0> is
2854the error message. C<ARG1> is the plugin alias. C<ARG2> is the plugin object.
2855
2856=head2 Somewhat Less Important Events
2857
2858=head3 C<irc_cap>
2859
2860A reply from the server regarding protocol capabilities. C<ARG0> is the
2861CAP subcommand (e.g. 'LS'). C<ARG1> is the result of the subcommand, unless
2862this is a multi-part reply, in which case C<ARG1> is '*' and C<ARG2> contains
2863the result.
2864
2865=head3 C<irc_dcc_*>
2866
2867See the L<DCC plugin|POE::Component::IRC::Plugin/OUTPUT> (loaded by default)
2868documentation for DCC-related events.
2869
2870=head3 C<irc_ping>
2871
2872An event sent whenever the server sends a PING query to the
2873client. (Don't confuse this with a CTCP PING, which is another beast
2874entirely. If unclear, read the RFC.) Note that POE::Component::IRC will
2875automatically take care of sending the PONG response back to the
2876server for you, although you can still register to catch the event for
2877informational purposes.
2878
2879=head3 C<irc_snotice>
2880
2881A weird, non-RFC-compliant message from an IRC server. Usually sent during
2882to you during an authentication phase right after you connect, while the
2883server does a hostname lookup or similar tasks. C<ARG0> is the text of the
2884server's message. C<ARG1> is the target, which could be B<'*'> or B<'AUTH'>
2885or whatever. Servers vary as to whether these notices include a server name
2886as the sender, or no sender at all. C<ARG1> is the sender, if any.
2887
2888=head3 C<irc_delay_set>
2889
2890I<Inherited from L<POE::Component::Syndicator|POE::Component::Syndicator/syndicator_delay_set>>
2891
2892Emitted on a successful addition of a delayed event using the
2893L<C<delay>|/delay> method. C<ARG0> will be the alarm_id which can be used
2894later with L<C<delay_remove>|/delay_remove>. Subsequent parameters are
2895the arguments that were passed to L<C<delay>|/delay>.
2896
2897=head3 C<irc_delay_removed>
2898
2899I<Inherited from L<POE::Component::Syndicator|POE::Component::Syndicator/syndicator_delay_removed>>
2900
2901Emitted when a delayed command is successfully removed. C<ARG0> will be the
2902alarm_id that was removed. Subsequent parameters are the arguments that were
2903passed to L<C<delay>|/delay>.
2904
2905=head2 All numeric events
2906
2907Most messages from IRC servers are identified only by three-digit
2908numeric codes with undescriptive constant names like RPL_UMODEIS and
2909ERR_NOTOPLEVEL. (Actually, the list of codes in the RFC is kind of
2910out-of-date... the list in the back of Net::IRC::Event.pm is more
2911complete, and different IRC networks have different and incompatible
2912lists. Ack!) As an example, say you wanted to handle event 376
2913(RPL_ENDOFMOTD, which signals the end of the MOTD message). You'd
2914register for '376', and listen for C<irc_376> events. Simple, no? C<ARG0>
2915is the name of the server which sent the message. C<ARG1> is the text of
2916the message. C<ARG2> is an array reference of the parsed message, so there
2917is no need to parse C<ARG1> yourself.
2918
2919=head1 SIGNALS
2920
2921The component will handle a number of custom signals that you may send using
2922L<POE::Kernel|POE::Kernel>'s C<signal> method.
2923
2924=head2 C<POCOIRC_REGISTER>
2925
2926I<Inherited from L<POE::Component::Syndicator|POE::Component::Syndicator/SYNDICATOR_REGISTER>>
2927
2928Registering with multiple PoCo-IRC components has been a pita. Well, no more,
2929using the power of L<POE::Kernel|POE::Kernel> signals.
2930
2931If the component receives a C<POCOIRC_REGISTER> signal it'll register the
2932requesting session and trigger an L<C<irc_registered>|/irc_registered>
2933event. From that event one can get all the information necessary such as the
2934poco-irc object and the SENDER session to do whatever one needs to build a
2935poco-irc dispatch table.
2936
2937The way the signal handler in PoCo-IRC is written also supports sending the
2938C<POCOIRC_REGISTER> to multiple sessions simultaneously, by sending the signal
2939to the POE Kernel itself.
2940
2941Pass the signal your session, session ID or alias, and the IRC events (as
2942specified to L<C<register>|/register>).
2943
2944To register with multiple PoCo-IRCs one can do the following in your session's
2945_start handler:
2946
2947 sub _start {
2948     my ($kernel, $session) = @_[KERNEL, SESSION];
2949
2950     # Registering with multiple pocoircs for 'all' IRC events
2951     $kernel->signal($kernel, 'POCOIRC_REGISTER', $session->ID(), 'all');
2952
2953     return:
2954 }
2955
2956Each poco-irc will send your session an
2957L<C<irc_registered>|/irc_registered> event:
2958
2959 sub irc_registered {
2960     my ($kernel, $sender, $heap, $irc_object) = @_[KERNEL, SENDER, HEAP, ARG0];
2961
2962     # Get the poco-irc session ID
2963     my $sender_id = $sender->ID();
2964
2965     # Or it's alias
2966     my $poco_alias = $irc_object->session_alias();
2967
2968     # Store it in our heap maybe
2969     $heap->{irc_objects}->{ $sender_id } = $irc_object;
2970
2971     # Make the poco connect
2972     $irc_object->yield(connect => { });
2973
2974     return;
2975 }
2976
2977=head2 C<POCOIRC_SHUTDOWN>
2978
2979I<Inherited from L<POE::Component::Syndicator|POE::Component::Syndicator/SYNDICATOR_SHUTDOWN>>
2980
2981Telling multiple poco-ircs to shutdown was a pita as well. The same principle as
2982with registering applies to shutdown too.
2983
2984Send a C<POCOIRC_SHUTDOWN> to the POE Kernel to terminate all the active
2985poco-ircs simultaneously.
2986
2987 $poe_kernel->signal($poe_kernel, 'POCOIRC_SHUTDOWN');
2988
2989Any additional parameters passed to the signal will become your quit messages
2990on each IRC network.
2991
2992=head1 ENCODING
2993
2994This can be an issue. Take a look at L<IRC::Utils' section|IRC::Utils/ENCODING>
2995on it.
2996
2997=head1 BUGS
2998
2999A few have turned up in the past and they are sure to again. Please use
3000L<http://rt.cpan.org/> to report any. Alternatively, email the current
3001maintainer.
3002
3003=head1 DEVELOPMENT
3004
3005You can find the latest source on github:
3006L<http://github.com/bingos/poe-component-irc>
3007
3008The project's developers usually hang out in the C<#poe> IRC channel on
3009irc.perl.org. Do drop us a line.
3010
3011=head1 MAINTAINERS
3012
3013Chris C<BinGOs> Williams <chris@bingosnet.co.uk>
3014
3015Hinrik E<Ouml>rn SigurE<eth>sson <hinrik.sig@gmail.com>
3016
3017=head1 AUTHOR
3018
3019Dennis Taylor.
3020
3021=head1 LICENCE
3022
3023Copyright (c) Dennis Taylor, Chris Williams and Hinrik E<Ouml>rn SigurE<eth>sson
3024
3025This module may be used, modified, and distributed under the same
3026terms as Perl itself. Please see the license that came with your Perl
3027distribution for details.
3028
3029=head1 MAD PROPS
3030
3031The maddest of mad props go out to Rocco "dngor" Caputo
3032<troc@netrus.net>, for inventing something as mind-bogglingly
3033cool as POE, and to Kevin "oznoid" Lenzo E<lt>lenzo@cs.cmu.eduE<gt>,
3034for being the attentive parent of our precocious little infobot on
3035#perl.
3036
3037Further props to a few of the studly bughunters who made this module not
3038suck: Abys <abys@web1-2-3.com>, Addi <addi@umich.edu>, ResDev
3039<ben@reser.org>, and Roderick <roderick@argon.org>. Woohoo!
3040
3041Kudos to Apocalypse, <apocal@cpan.org>, for the plugin system and to
3042Jeff 'japhy' Pinyan, <japhy@perlmonk.org>, for Pipeline.
3043
3044Thanks to the merry band of POE pixies from #PoE @ irc.perl.org,
3045including ( but not limited to ), ketas, ct, dec, integral, webfox,
3046immute, perigrin, paulv, alias.
3047
3048IP functions are shamelessly 'borrowed' from L<Net::IP|Net::IP> by Manuel
3049Valente
3050
3051Check out the Changes file for further contributors.
3052
3053=head1 SEE ALSO
3054
3055RFC 1459 L<http://www.faqs.org/rfcs/rfc1459.html>
3056
3057L<http://www.irchelp.org/>,
3058
3059L<http://poe.perl.org/>,
3060
3061L<http://www.infobot.org/>,
3062
3063Some good examples reside in the POE cookbook which has a whole section
3064devoted to IRC programming L<http://poe.perl.org/?POE_Cookbook>.
3065
3066The examples/ folder of this distribution.
3067
3068=cut
3069