#!/usr/bin/perl
use strict;
use Irssi;
use Irssi::Irc;
use vars qw($VERSION %IRSSI);
$VERSION = "1.3";
%IRSSI = (
authors => 'Nathan Handler, Joseph Price',
contact => '[email protected], [email protected]',
name => 'bansearch',
description => 'Searches for bans, quiets, and channel modes affecting a user',
license => 'GPLv3+',
);
my($channel,$person,$nick,$user,$host,$real,$account,$string,$issues,$running,@jchannels,@jchannelstocheck,$debug);
$running=0;
sub bansearch {
my($data,$server,$witem) = @_;
if($running) {
Irssi::print("bansearch is already running.");
}
$running=1;
@jchannels=();
@jchannelstocheck=();
#Clear variables and register redirects
&reset();
$debug = Irssi::settings_get_bool('bansearch_debug');
#Split command arguments into a nick and a channel separated by a space
($person,$channel)=split(/ /, $data, 2);
#If no channel is specified, use the current window if it is a channel
if($channel!~m/^#/ && $person!~m/^\s*$/ && $witem->{type} eq "CHANNEL") {
$channel=$witem->{name};
}
#Stop the script and display usage information if they did not specify a person or if we can't find a channel to use
if($channel!~m/^#/ || $person=~m/^\s*$/) {
Irssi::active_win()->print("\x02Usage\x02: /bansearch nick [#channel]");
$running=0;
return;
}
#Print the name of the channel we are running on
Irssi::active_win()->print("\x02Channel\x02: $channel");
#Perform a /who %uhnar
$server->redirect_event('who',1, '', 0, undef,
{
'event 352' => 'redir rpl_whoreply',
'event 354' => 'redir rpl_whospcrpl',
'event 315' => 'redir rpl_endofwho',
'event 401' => 'redir err_nosuchnick',
'' => 'event empty',
}
);
$server->send_raw("WHO $person %uhnar");
}
#Irssi::signal_add('event empty', 'EMPTY');
Irssi::signal_add('redir rpl_whoreply', 'RPL_WHOREPLY');
Irssi::signal_add('redir rpl_whospcrpl', 'RPL_WHOSPCRPL');
Irssi::signal_add('redir rpl_endofwho', 'RPL_ENDOFWHO');
Irssi::signal_add('redir err_nosuchnick', 'ERR_NOSUCHNICK');
Irssi::signal_add('redir err_nosuchchannel', 'ERR_NOSUCHCHANNEL');
Irssi::signal_add('redir rpl_banlist', sub { my($server,$data) = @_; RPL_BANLIST($server, "Ban $data"); });
Irssi::signal_add('redir rpl_endofbanlist', sub { my($server,$data) = @_; RPL_ENDOFBANLIST($server, "Ban $data"); });
Irssi::signal_add('redir rpl_quietlist', sub { my($server,$data) = @_; RPL_BANLIST($server, "Quiet $data"); });
Irssi::signal_add('redir rpl_endofquietlist', sub { my($server,$data) = @_; RPL_ENDOFBANLIST($server, "Quiet $data"); });
Irssi::signal_add('redir rpl_channelmodeis', 'RPL_CHANNELMODEIS');
sub EMPTY {
my($server, $data) = @_;
return if(!$running);
Irssi::print("\x02EMPTY\x02: $data");
}
sub RPL_BANLIST {
my($server, $data) = @_;
return if(!$running);
my($type, $mask, $setby, $banchannel, $jchannel);
if($data=~m/^Ban/) {
($type, undef, $banchannel, $mask, $setby, undef) = split(/ /, $data, 6);
}
elsif($data=~m/^Quiet/) {
($type, undef, $banchannel, undef, $mask, $setby, undef) = split(/ /, $data, 7);
}
my $maskreg = $mask;
$maskreg=~s/\$\#.*$//; #Support matching ban-forwards
$maskreg=~s/\./\\./g;
$maskreg=~s/\//\\\//g;
$maskreg=~s/\@/\\@/g;
$maskreg=~s/\[/\\[/g;
$maskreg=~s/\]/\\]/g;
$maskreg=~s/\|/\\|/g;
$maskreg=~s/\?/\./g;
$maskreg=~s/\*/\.\*\?/g;
#We only want to display who set the ban/quiet if it is listed as a person
if($setby!~m/!/) {
$setby='';
}
else {
$setby=" (Set by $setby)";
}
if($maskreg=~m/^\$/) { #extban
# account
if($maskreg=~m/^\$a:(.*?)$/i) {
if($account=~m/^$1$/i && $account!~m/^0$/) {
Irssi::active_win()->print("$type against \x02$mask\x02 in $banchannel matches $account" . $setby);
$issues++;
}
else {
Irssi::active_win()->print("$type against \x02$mask\x02 in $banchannel does NOT match $account" . $setby) if $debug;
}
}
# cannot join other channel
if($channel == $banchannel) {
if($maskreg=~m/^\$j:(.*?)$/i) {
$jchannel = $1;
if(!(grep {$jchannel eq $_} @jchannels)) {
push(@jchannels, $jchannel);
push(@jchannelstocheck, $jchannel);
Irssi::active_win()->print("Following bans in "
. $jchannel . " will " . $type . " " . $person . " in " . $channel . $setby);
}
}
}
# any logged-in user
if($maskreg=~m/^\$a$/i) {
if($account!~m/^0$/) {
Irssi::active_win()->print(
"$type against \x02$mask\x02 in $banchannel matches identified user" . $setby);
$issues++;
}
else {
Irssi::active_win()->print("$type against \x02$mask\x02 in $banchannel does NOT match unidentified user" . $setby) if $debug;
}
}
# any unidentified user
if($maskreg=~m/^\$\~a$/i) {
if($account=~m/^0$/) {
Irssi::active_win()->print(
"$type against \x02$mask\x02 in $banchannel matches unidentified user" . $setby);
$issues++;
}
else {
Irssi::active_win()->print("$type against \x02$mask\x02 in $banchannel does NOT match identified user" . $setby) if $debug;
}
}
# ircname
if($maskreg=~m/^\$r:(.*?)$/i) {
if($real=~m/^$1$/i) {
Irssi::active_win()->print(
"$type against \x02$mask\x02 in $banchannel matches real name of $real" . $setby);
$issues++;
}
else {
Irssi::active_win()->print("$type against \x02$mask\x02 in $banchannel does NOT match real name of $real" . $setby) if $debug;
}
}
# full match
if($maskreg=~m/^\$x:(.*?)$/i) {
my $full = "$nick!$user\@$host\#$real";
if($full=~m/^$1$/i) {
Irssi::active_win()->print(
"$type against \x02$mask\x02 in $banchannel matches $full" . $setby);
$issues++;
}
else {
Irssi::active_win()->print("$type against \x02$mask\x02 in $banchannel does NOT match $full" . $setby) if $debug;
}
}
}
else { #Normal Ban
if($string=~m/^$maskreg$/i) {
Irssi::active_win()->print("$type against \x02$mask\x02 in $banchannel matches $string" . $setby);
$issues++;
}
else {
Irssi::active_win()->print("$type against \x02$mask\x02 in $banchannel does NOT match $string" . $setby) if $debug;
}
}
}
sub RPL_ENDOFBANLIST {
my($server, $data) = @_;
return if(!$running);
# Irssi::active_win()->print("End of Ban List");
if($data=~m/^Ban/) {
$server->redirect_event('mode q',1, $channel, 0, undef,
{
'event 728' => 'redir rpl_quietlist',
'event 729' => 'redir rpl_endofquietlist',
'' => 'event empty',
}
);
$server->send_raw("MODE $channel q");
}
elsif($data=~m/^Quiet/) {
if (@jchannelstocheck) {
my $nextchannel = pop(@jchannelstocheck);
$server->redirect_event('mode b',1, $nextchannel, 0, undef,
{
'event 367' => 'redir rpl_banlist',
'event 368' => 'redir rpl_endofbanlist',
'event 403' => 'redir err_nosuchchannel',
'' => 'event empty',
}
);
$server->send_raw("MODE $nextchannel b");
} else {
$server->redirect_event('mode channel',1, $channel, 0, undef,
{
'event 324' => 'redir rpl_channelmodeis',
'' => 'event empty',
}
);
$server->send_raw("MODE $channel");
}
}
}
sub RPL_WHOREPLY {
my($server, $data) = @_;
return if(!$running);
# 0 1 2 3 4 5 6 7 8
# bw2 * ~pi rpi1.my irc.example.net rpi1 H :0 real name
(undef, undef, $user, $host, undef, $nick, undef, undef, $real) = split(/ /, $data,9);
$account='';
$real=~s/^://;
Irssi::active_win()->print("\x02User\x02: $nick [$account] ($real) $user\@$host");
}
sub RPL_WHOSPCRPL {
my($server, $data) = @_;
return if(!$running);
# $server->send_raw("WHO $person %uhnar");
# 0 1 2 3 4 5
# bw2 ~bw1 irc.example.net bw1 bw2 :real name
(undef, $user, $host, $nick, $account, $real) = split(/ /, $data,6);
$real=~s/^://;
Irssi::active_win()->print("\x02User\x02: $nick [$account] ($real) $user\@$host");
}
sub RPL_ENDOFWHO {
my($server, $data) = @_;
return if(!$running);
if($nick=~m/^$/ && $user=~m/^$/ && $host=~m/^$/) {
Irssi::active_win()->print("$person is currently offline.");
return;
}
$string="$nick!$user\@$host";
$server->redirect_event('mode b',1, $channel, 0, undef,
{
'event 367' => 'redir rpl_banlist',
'event 368' => 'redir rpl_endofbanlist',
'event 403' => 'redir err_nosuchchannel',
'' => 'event empty',
}
);
$server->send_raw("MODE $channel b");
}
sub ERR_NOSUCHNICK {
my($server, $data) = @_;
return if(!$running);
Irssi::active_win()->print("$person is currently offline.");
$running=0;
}
sub ERR_NOSUCHCHANNEL {
my($server, $data) = @_;
return if(!$running);
Irssi::active_win()->print("$channel does not exist.");
$running=0;
}
sub RPL_CHANNELMODEIS {
my($server, $data) = @_;
return if(!$running);
my(undef, undef, $modes, $args) = split(/ /, $data, 4);
Irssi::active_win()->print("\x02Channel Modes\x02: $modes");
if($modes=~m/i/) {
Irssi::active_win()->print("Channel is \x02invite-only\x02 (+i)");
$issues++;
}
if($modes=~m/k/) {
Irssi::active_win()->print("Channel has a \x02password\x02 (+k)");
$issues++;
}
if($modes=~m/r/) {
if($account=~m/^0$/) {
Irssi::active_win()->print("Channel is \x02blocking unidentified users\x02 (+r) and user is not identified");
$issues++;
}
}
if($modes=~m/m/) {
if($server->channel_find("$channel")) {
my $n = $server->channel_find("$channel")->nick_find("$nick");
if($n->{voice} == 0 && $n->{op} == 0) {
Irssi::active_win()->print("Channel is \x02moderated\x02 (+m) and user is not voiced or oped");
$issues++;
}
}
else {
Irssi::active_win()->print("Channel is \x02moderated\x02 (+m) and user might not be voiced or oped");
$issues++;
}
}
if($issues == 0) {
Irssi::active_win()->print("There does not appear to be anything preventing $person from joining/talking in $channel");
}
elsif ($issues == 1) {
Irssi::active_win()->print("There is \x02$issues issue\x02 that might be preventing $person from joining/talking in $channel");
}
else {
Irssi::active_win()->print("There are \x02$issues issues\x02 that might be preventing $person from joining/talking in $channel");
}
$running=0;
}
sub reset {
return if(!$running);
$channel='';
$person='';
$nick='';
$user='';
$host='';
$real='';
$account='';
$string='';
$issues=0;
®ister_redirects();
}
sub register_redirects {
return if(!$running);
#who
Irssi::Irc::Server::redirect_register('who', 0, 0,
{ "event 352" => 1, # start events
"event 354" => -1,
},
{ # stop events
"event 315" => 1, # End of Who List
"event 401" => 1, # No Such Nick
},
undef, # optional events
);
#mode b
Irssi::Irc::Server::redirect_register('mode b', 0, 0,
{ "event 367" => 1 }, # start events
{ # stop events
"event 368" => 1, # End of channel ban list
"event 403" => 1, # no such channel
"event 442" => 1, # "you're not on that channel"
"event 479" => 1 # "Cannot join channel (illegal name)"
},
undef, # optional events
);
#mode q
Irssi::Irc::Server::redirect_register('mode q', 0, 0,
{ "event 728" => 1 }, # start events
{ # stop events
"event 729" => 1, # End of channel quiet list
"event 403" => 1, # no such channel
"event 442" => 1, # "you're not on that channel"
"event 479" => 1, # "Cannot join channel (illegal name)"
},
undef, # optional events
);
#mode channel
Irssi::Irc::Server::redirect_register('mode channel', 0, 0, undef,
{ # stop events
"event 324" => 1, # MODE-reply
"event 403" => 1, # no such channel
"event 442" => 1, # "you're not on that channel"
"event 479" => 1 # "Cannot join channel (illegal name)"
},
{ "event 329" => 1 } # Channel create time
);
}
Irssi::command_bind('bansearch', 'bansearch');
Irssi::settings_add_bool('bansearch', 'bansearch_debug', 0);
# vim:set ts=8 sw=4: