Security Advisories (1)
CVE-2010-3438 (2019-11-12)

libpoe-component-irc-perl before v6.32 does not remove carriage returns and line feeds. This can be used to execute arbitrary IRC commands by passing an argument such as \"some text\\rQUIT\" to the 'privmsg' handler, which would cause the client to disconnect from the server.

NAME

POE::Component::IRC::Cookbook::Seen - Implement the 'seen' command

SYNOPSIS

This little bot tracks the whereabouts of users and allows you to retrieve that information on command.

19:59:51 * seen_bot (n=hinrik@pool-71-164-43-32.chrlwv.east.verizon.net) has joined #test_channel1
19:59:55 <foo> bar
20:00:16 * seen_bot has quit (Remote closed the connection)
20:00:27 * seen_bot (n=hinrik@pool-71-164-43-32.chrlwv.east.verizon.net) has joined #test_channel1
20:00:29 <literal> seen_bot: seen seen_bot
20:00:29 <seen_bot> literal: I last saw seen_bot at Mon Sep 22 20:00:27 2008 joining #test_channel1
20:00:34 <literal> seen_bot: seen foo
20:00:40 <seen_bot> literal: I last saw foo at Mon Sep 22 19:59:56 2008 on #test_channel1 saying: bar
20:00:45 <literal> seen_bot: seen baz
20:00:48 <seen_bot> literal: I haven't seen baz

DESCRIPTION

#!/usr/bin/env perl

use strict;
use warnings;
use POE;
use POE::Component::IRC::Common qw(parse_user l_irc);
use POE::Component::IRC::State;
use POE::Component::IRC::Plugin::AutoJoin;
use POE::Component::IRC::Plugin::BotCommand;
use Storable;

use constant {
    USER_DATE     => 0,
    USER_MSG      => 1,
    DATA_FILE     => 'seen',
    SAVE_INTERVAL => 20 * 60,   # save state every 20 mins
};

my $seen = { };
$seen = retrieve(DATA_FILE) if -s DATA_FILE;

POE::Session->create(
    package_states => [
        main => [ qw(
            _start
            irc_botcmd_seen
            irc_ctcp_action
            irc_join
            irc_part
            irc_public
            irc_quit
            save
        )]
    ],
);

$poe_kernel->run();

sub _start {
    my ($kernel, $heap) = @_[KERNEL, HEAP];
    my $irc = POE::Component::IRC::State->spawn(
        Nick   => 'seen_bot',
        Server => 'irc.freenode.net',
    );
    $heap->{irc} = $irc;

    $irc->plugin_add('AutoJoin', POE::Component::IRC::Plugin::AutoJoin->new(
        Channels => [ '#test_channel1', '#test_channel2' ]
    ));

    $irc->plugin_add('BotCommand', POE::Component::IRC::Plugin::BotCommand->new(
        Commands => {
           seen => 'Usage: seen <nick>'
        }
    ));

    $irc->yield(register => qw(ctcp_action join part public quit botcmd_seen));
    $irc->yield('connect');
    $kernel->delay_set('save', SAVE_INTERVAL);
    return;
}

sub save {
    my $kernel = $_[KERNEL];
    warn "storing\n";
    store($seen, DATA_FILE) or die "Can't save state";
    $kernel->delay_set('save', SAVE_INTERVAL);
}

sub irc_ctcp_action {
    my $nick = parse_user($_[ARG0]);
    my $chan = $_[ARG1]->[0];
    my $text = $_[ARG2];
    
    add_nick($nick, "on $chan doing: * $nick $text");
}

sub irc_join {
    my $nick = parse_user($_[ARG0]);
    my $chan = $_[ARG1];
    
    add_nick($nick, "joining $chan"); 
}

sub irc_part {
    my $nick = parse_user($_[ARG0]);
    my $chan = $_[ARG1];
    my $text = $_[ARG2];
    
    my $msg = 'parting $chan';
    $msg .= " with message '$text'" if defined $text;
    
    add_nick($nick, $msg);
}

sub irc_public {
    my $nick = parse_user($_[ARG0]);
    my $chan = $_[ARG1]->[0];
    my $text = $_[ARG2];
    
    add_nick($nick, "on $chan saying: $text");
}

sub irc_quit {
    my $nick = parse_user($_[ARG0]);
    my $text = $_[ARG1];
    
    my $msg = 'quitting';
    $msg .= " with message '$text'" if defined $text;
    
    add_nick($nick, $msg);
}

sub add_nick {
    my ($nick, $msg) = @_;
    $seen->{l_irc($nick)} = [time, $msg];
}

sub irc_botcmd_seen {
    my ($heap, $nick, $channel, $target) = @_[HEAP, ARG0..$#_];
    $nick = parse_user($nick);
    my $irc = $heap->{irc};

    if ($seen->{l_irc($target)}) {
        my $date = localtime $seen->{l_irc($target)}->[USER_DATE];
        my $msg = $seen->{l_irc($target)}->[USER_MSG];
        $irc->yield(privmsg => $channel, "$nick: I last saw $target at $date $msg");
    }
    else {
        $irc->yield(privmsg => $channel, "$nick: I haven't seen $target");
    }
}

AUTHOR

Hinrik Örn Sigurðsson, hinrik.sig@gmail.com