diff options
| author | mniip | 2015-01-13 17:01:18 +0300 |
|---|---|---|
| committer | mniip | 2015-01-13 17:41:40 +0300 |
| commit | daf758db38e4fe125ffe1f7281325744776f233e (patch) | |
| tree | 5492b4260f6fe2deb9be13f2e32ff0798e159603 | |
| parent | 2407581eba0ab35b32774e06461bf951545cde07 (diff) | |
| download | scripts.irssi.org-daf758db38e4fe125ffe1f7281325744776f233e.tar.bz2 | |
Added isbanned.pl
| -rw-r--r-- | _data/scripts.yaml | 11 | ||||
| -rw-r--r-- | scripts/isbanned.pl | 626 |
2 files changed, 637 insertions, 0 deletions
diff --git a/_data/scripts.yaml b/_data/scripts.yaml index dbe7d57..67fdef5 100644 --- a/_data/scripts.yaml +++ b/_data/scripts.yaml @@ -1964,6 +1964,17 @@ name: "IrssiQ" version: "2003231101" +- authors: "mniip" + commands: "isbanned ismuted islisted isreset" + contact: "mniip @ freenode" + description: "freenode-specific script that checks whether someone is banned on some channel" + filename: "isbanned.pl" + modified: "2015-01-13" + modules: "Socket" + license: "Public domain" + name: "isbanned" + version: "0.6.1" + - authors: "Uli Baumann" contact: "f-zappa@irc-muenster.de" description: "Displays incoming ISDN calls" diff --git a/scripts/isbanned.pl b/scripts/isbanned.pl new file mode 100644 index 0000000..3feaca2 --- /dev/null +++ b/scripts/isbanned.pl @@ -0,0 +1,626 @@ +use Irssi; +use strict; +use warnings; +use bignum; +use Socket; +use vars qw($VERSION %IRSSI); + +%IRSSI = +( + name => "isbanned", + description => "freenode-specific script that checks whether someone is banned on some channel", + commands => "isbanned ismuted islisted isreset", + authors => "mniip", + contact => "mniip \@ freenode", + license => "Public domain", + modified => "2015-01-13" +); +$VERSION = "0.6.2"; + +# Commands: +# /isbanned <channel> <user> +# Check whether <user> is banned on <channel> +# /ismuted <channel> <user> +# Check whether <user> is muted on <channel> +# /islisted <channel> <mode> <user> +# Check whether <user> is listed in <channel>'s +# <mode> list (can be b, q, e, or I) +# /isreset +# If something screws up, this resets the state to inactive +# +# <user> can either be a nickname, or a hostmask in the form +# nick!ident@host#gecos$account +# where some parts can be omitted. Strictly speaking the form is +# [nick] ['!' ident] ['@' host] ['#' gecos] ['$' account] +# If any part is omitted, it is assumed to be empty string, except for +# account: if the account part is omitted the user is assumed to be +# unidentified (as opposed to identified as empty string) +# +# Supports all kinds of features, misfeatures, and quirks freenode uses. +# Supports $a, $j, $r, $x, and $z extbans, and any edge cases of those. +# Supports CIDR bans, both IPv4 and IPv6, and the misfeature by which +# an invalid IP address is not parsed and zeroes are matched instead. +# Supports the +ikmrS modes. Supports the RFC2812 casemapping in patterns, +# which are, by the way, parsed without backtracking and thus effeciently. +# +# Changelog: +# 0.6.0 (2015.01.12) +# Ported from the hexchat script version 0.6 +# +# 0.6.1 (2015.01.13) +# Fixed a few porting issues: the original host is now included +# in the hosts list too. Fixed IPv6 parsing. Fixed $x and $~x. +# +# 0.6.2 (2015.01.13) +# Fixed a few warnings. + +my $active = 0; +my $user; +my $channel; +my $orig_list; +my $modes; +my @whois; +my $lists_left; +my @bans; + +sub parse_ipv6_word +{ + my ($w) = @_; + return hex $w if $w =~ /^0*[0-9a-fA-F]{1,4}$/; + die "Invalid IPv6 word"; +} + +sub parse_ip +{ + my ($ip, $strict) = @_; + if($ip =~ /:/) + { + if($ip =~ /::/) + { + my $edge = ($ip =~ /::$/) || ($ip =~ /^::/); + $ip = $ip . "0" if $ip =~ /::$/; + $ip = "0" . $ip if $ip =~ /^::/; + my ($head, $tail) = split /::/, $ip, 2; + my @headwords = split /:/, $head, 8; + my @tailwords = split /:/, $tail, 8; + if(@headwords + @tailwords <= ($edge ? 8 : 7)) + { + my $result; + eval + { + @headwords = map { parse_ipv6_word($_) } @headwords; + @tailwords = map { parse_ipv6_word($_) } @tailwords; + my @words = (@headwords, (0) x (8 - @headwords - @tailwords), @tailwords); + $result = 0; + for(my $i = 0; $i < 8; $i++) + { + $result |= $words[$i] << ((7 - $i) * 16); + } + }; + return $result if defined $result; + } + } + else + { + my @words = split /:/, $ip, 8; + if(scalar @words == 8) + { + my $result; + eval + { + @words = map { parse_ipv6_word($_) } @words; + $result = 0; + for(my $i = 0; $i < 8; $i++) + { + $result |= $words[$i] << ((7 - $i) * 16); + } + }; + return $result if defined $result; + } + } + die "Invalid IPv6" if $strict; + return 0; + } + else + { + if($ip =~ /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/) + { + my ($o1, $o2, $o3, $o4) = (0 + $1, 0 + $2, 0 + $3, 0 + $4); + return $o1 << 24 | $o2 << 16 | $o3 << 8 | $o4 + if $o1 < 256 && $o2 < 256 && $o3 < 256 && $o4 < 256; + } + die "Invalid IPv4" if $strict; + return 0; + } +} + +my %char_classes = + ( + "[" => "[\[{]", + "{" => "[{\[]", + "|" => "[|\\]", + "\\" => "[\\|]", + "]" => "[\]}]", + "}" => "[}\]]", + "~" => "[~^]", + "?" => "." + ); +$char_classes{$_} = $_ foreach split //, '-_`^0123456789'; +for(my $c = 0; $c < 25; $c++) +{ + my $lc = chr($c + ord "a"); + my $uc = chr($c + ord "A"); + $char_classes{$lc} = "[$lc$uc]"; + $char_classes{$uc} = "[$uc$lc]"; +} + +sub match_pattern +{ + my ($string, $pattern) = @_; + $pattern =~ s|[?*]+|"?" x ($& =~ tr/?/?/) . ($& =~ /\*/ ? "*" : "")|ge; + + my $last_pos = 0; + my @pieces = split /\*/, $pattern; + push @pieces, "" if $pattern =~ /\*$/; + push @pieces, "" if $pattern eq "*"; + for(my $i = 0; $i < scalar @pieces; $i++) + { + my $regex = ""; + $regex .= "^" if !$i; + $regex .= defined $char_classes{$_} ? $char_classes{$_} : "\\$_" for split //, $pieces[$i]; + $regex .= "\$" if $i == $#pieces; + if((substr $string, $last_pos) =~ qr/$regex/) + { + $last_pos += $+[0]; + } + else + { + return 0; + } + } + return 1; +} + +my @found_modes; +sub add_ban +{ + push @found_modes, "\x0302$_[1] $_[0]\x0F in \x0306$_[2]\x0F set by \x0310$_[3]\x0F on \x0308" . (scalar localtime $_[4]) . "\x0F"; +} + +sub analyze +{ + $active = 0; + my ($nick, $ident, $host, $gecos, $account, $ssl) = @whois; + @found_modes = (); + my @hostile_modes = (); + for my $c(split //, $modes) + { + push @hostile_modes, $c if $orig_list eq "b" && ($c eq "i" || ($c eq "r" && !$account) || ($c eq "S" && !$ssl)); + push @hostile_modes, $c if $orig_list eq "q" && ($c eq "m" || ($c eq "r" && !$account)); + } + push @found_modes, ("\x0302+" . join("", @hostile_modes) . "\x0F in \x0306$channel\x0F") if(@hostile_modes); + for my $b(@bans) + { + my ($ban) = split /(?<!^)\$/, $b->[0], 2; + if($ban =~ /^\$/) + { + add_ban(@$b) if $ban eq '$a' && $account; + if($ban =~ /^\$a:(.*)$/) + { + add_ban(@$b) if $account && nick_eq($account, $1); + } + add_ban(@$b) if $ban eq '$~a' && !$account; + if($ban =~ /^\$~a:(.*)$/) + { + add_ban(@$b) if !$account || !nick_eq($account, $1); + } + add_ban(@$b) if $ban =~ /^\$~j:/; + if($ban =~ /^\$r:(.*)$/) + { + add_ban(@$b) if match_pattern($gecos, $1); + } + if($ban =~ /^\$~r:(.*)$/) + { + add_ban(@$b) if !match_pattern($gecos, $1); + } + if($ban =~ /^\$x:(.*)$/) + { + for my $h(@$host) + { + if(match_pattern("$nick!$ident\@$h#$gecos", $1)) + { + add_ban(@$b); + last; + } + } + } + if($ban =~ /^\$~x:(.*)$/) + { + my $found = 0; + for my $h(@$host) + { + if(match_pattern("$nick!$ident\@$h#$gecos", $1)) + { + $found = 1; + last; + } + } + add_ban(@$b) if !$found; + } + add_ban(@$b) if $ban eq '$z' && $ssl; + add_ban(@$b) if $ban eq '$~z' && !$ssl; + } + else + { + my ($v, $bhost) = split /@/, $ban, 2; + my ($bnick, $bident) = split /!/, $v, 2; + if(match_pattern($nick, $bnick) && match_pattern($ident, $bident)) + { + my $found = 0; + for my $h(@$host) + { + if(match_pattern($h, $bhost)) + { + $found = 1; + add_ban(@$b); + last; + } + } + if(!$found) + { + my ($ip, $width) = split /\//, $bhost, 2; + if(defined $width && $width =~ /^[0-9]+$/) + { + my $is_v4 = !($ip =~ /:/); + $width = ($is_v4 ? 32 : 128) - $width; + $width = 0 if $width < 0; + $ip = parse_ip($ip); + for my $h(@$host) + { + if(!($h =~ /:/) == $is_v4) + { + eval + { + my $h = parse_ip($h, 1); + if(($ip >> $width) == ($h >> $width)) + { + add_ban(@$b); + last; + } + } + } + } + } + } + } # omg so many } + } + } + if(@found_modes) + { + if($orig_list eq "b") + { + Irssi::print("The following are preventing \x0310$user\x0F from joining \x0306$channel\x0F:"); + } + elsif($orig_list eq "q") + { + Irssi::print("The following are preventing \x0310$user\x0F from speaking in \x0306$channel\x0F:"); + } + else + { + Irssi::print("The following \x0302+$orig_list\x0F modes affect \x0310$user\x0F in \x0306$channel\x0F:"); + } + Irssi::print($_) for(@found_modes); + } + else + { + if($orig_list eq "b") + { + Irssi::print("Nothing is preventing \x0310$user\x0F from joining \x0306$channel\x0F"); + } + elsif($orig_list eq "q") + { + Irssi::print("Nothing is preventing \x0310$user\x0F from speaking in \x0306$channel\x0F"); + } + else + { + Irssi::print("No \x0302+$orig_list\x0F modes affect \x0310$user\x0F in \x0306$channel\x0F"); + } + } +} + +sub reset +{ + $active = 0; +} + +sub lookup_host +{ + my ($host) = @_; + Irssi::print("\x0302Resolving <$host>"); + my @addresses = gethostbyname($host); + if(@addresses) + { + @addresses = map { inet_ntoa($_) } @addresses[4 .. $#addresses]; + my %seen; + @addresses = grep { !$seen{$_}++ } (@addresses, $host); + Irssi::print("\x0302IPs: <@addresses>"); + return @addresses; + } + else + { + Irssi::print("\x0302Found nothing, will use <$host>"); + return $host; + } +} + +sub query_list +{ + $lists_left++; + my ($server, $channel, $mode) = @_; + $server->command("quote MODE $channel"); + $server->command("quote MODE $channel $mode"); +} + +sub query_whois +{ + my ($server, $nick) = @_; + $server->command("quote WHOIS $nick"); +} + +sub ignored +{ + Irssi::signal_stop() if $active; +} + +sub nick_eq +{ + my ($n1, $n2) = @_; + $n1 = lc $n1; + $n1 =~ tr/[]\\/{}|/; + $n2 = lc $n2; + $n2 =~ tr/[]\\/{}|/; + return $n1 eq $n2; +} + +sub modes +{ + if($active) + { + my ($server, $data, $nick, $address) = @_; + my @w = split / /, $data; + Irssi::print("\x0302Channel $w[1] is +s, report may be incomplete") if $w[2] =~ /s/; + if(nick_eq($w[1], $channel)) + { + $modes = $w[2]; + analyze() if !$lists_left && @whois; + } + Irssi::signal_stop(); + } +} + + +sub list_entry_b { list_entry("+b", @_); } +sub list_entry_q { list_entry("+q", @_); } +sub list_entry_I { list_entry("+I", @_); } +sub list_entry_e { list_entry("+e", @_); } + +sub list_entry +{ + if($active) + { + my ($u, $server, $data, $nick, $address) = @_; + my @w = split / /, $data; + splice @w, 2, 1 if $u eq "+q"; + if(nick_eq($w[1], $channel) && $w[2] =~ /^\$j:(.*)$/) + { + query_list($server, $1 =~ s/\$.*$//r, "b") + } + else + { + push @bans, [$w[2], $u, $w[1], $w[3], $w[4]]; + } + Irssi::signal_stop(); + } +} + +sub list_end +{ + if($active) + { + $lists_left--; + analyze() if !$lists_left && @whois && $modes; + Irssi::signal_stop(); + } +} + +sub no_modes +{ + if($active) + { + Irssi::print("\x0304Attempted to get modes for a nickname, did you put the arguments in the wrong order?"); + if(!$modes) + { + $modes = "+"; + } + else + { + $lists_left--; + analyze() if !$lists_left && @whois; + } + Irssi::signal_stop(); + } +} + +sub mode_error +{ + if($active) + { + Irssi::print("\x0304Something went wrong with the modes, report may be incomplete"); + if(!$modes) + { + $modes = "+"; + analyze() if !$lists_left && @whois; + } + else + { + $lists_left--; + analyze() if !$lists_left && @whois; + } + Irssi::signal_stop(); + } +} + +sub no_list +{ + if($active) + { + my ($server, $data, $nick, $address) = @_; + my @w = split / /, $data; + Irssi::print("\x0304Could not obtain modes for $w[1], report may be incomplete"); + Irssi::print("eq nolist"); + if(nick_eq($w[1], $channel) && !$modes) + { + $modes = "+"; + analyze() if !$lists_left && @whois; + } + else + { + $lists_left--; + analyze() if !$lists_left && @whois && $modes; + } + Irssi::signal_stop(); + } +} + +my @wh; +my $ac; +my $ssl; + +sub whois_start +{ + if($active) + { + my ($server, $data, $nick, $address) = @_; + my @w = split / /, $data; + @wh = ($w[1], $w[2], [lookup_host($w[3])], substr((join " ", @w[5 .. $#w]), 1)); + undef $ac; + undef $ssl; + Irssi::signal_stop(); + } +} + +sub whois_ssl +{ + if($active) + { + $ssl = 1; + Irssi::signal_stop(); + } +} + +sub whois_account +{ + if($active) + { + my ($server, $data, $nick, $address) = @_; + my @w = split / /, $data; + $ac = $w[2]; + Irssi::signal_stop(); + } +} + +sub whois_end +{ + if($active) + { + my ($server, $data, $nick, $address) = @_; + if(@wh) + { + @whois = (@wh, $ac, $ssl); + @wh = (); + analyze() if !$lists_left && $modes; + } + else + { + Irssi::print("\x0304Whois failed, aborting!"); + $active = 0; + } + Irssi::signal_stop(); + } +} + +sub start_search +{ + my ($server, $ch, $u, $mode) = @_; + @whois = (); + $orig_list = $mode; + $channel = $ch; + $user = $u; + if($user =~ /[!@#\$]/) + { + my ($account, $rname, $host, $nick, $ident, $v); + ($v, $account) = split /\$/, $user, 2; + ($v, $rname) = split /#/, $v, 2; + ($v, $host) = split /@/, $v, 2; + ($nick, $ident) = split /!/, $v, 2; + @whois = ($nick, $ident, [lookup_host($host)], $rname, $account, 0); + } + undef $modes; + $lists_left = 0; + @bans = (); + $active = 1; + query_list($server, $channel, $mode); + query_whois($server, $user) if !@whois; +} + +sub isbanned +{ + my ($arg, $server, $witem) = @_; + my ($chan, $user) = split / /, $arg, 2; + start_search($server, $chan, $user, "b"); +} + +sub ismuted +{ + my ($arg, $server, $witem) = @_; + my ($chan, $user) = split / /, $arg, 2; + start_search($server, $chan, $user, "q"); + query_list($server, $chan, "b"); +} + +sub islisted +{ + my ($arg, $server, $witem) = @_; + my ($chan, $mode, $user) = split / /, $arg, 3; + start_search($server, $chan, $user, $mode =~ s/^\+//r); +} + +Irssi::signal_add("event 329", \&ignored); +Irssi::signal_add("event 276", \&ignored); +Irssi::signal_add("event 317", \&ignored); +Irssi::signal_add("event 378", \&ignored); +Irssi::signal_add("event 319", \&ignored); +Irssi::signal_add("event 312", \&ignored); +Irssi::signal_add("event 324", \&modes); +Irssi::signal_add("event 367", \&list_entry_b); +Irssi::signal_add("event 728", \&list_entry_q); +Irssi::signal_add("event 346", \&list_entry_I); +Irssi::signal_add("event 348", \&list_entry_e); +Irssi::signal_add("event 368", \&list_end); +Irssi::signal_add("event 729", \&list_end); +Irssi::signal_add("event 347", \&list_end); +Irssi::signal_add("event 349", \&list_end); +Irssi::signal_add("event 502", \&no_modes); +Irssi::signal_add("event 221", \&no_modes); +Irssi::signal_add("event 472", \&mode_error); +Irssi::signal_add("event 501", \&mode_error); +Irssi::signal_add("event 403", \&no_list); +Irssi::signal_add("event 482", \&no_list); +Irssi::signal_add("event 311", \&whois_start); +Irssi::signal_add("event 671", \&whois_ssl); +Irssi::signal_add("event 330", \&whois_account); +Irssi::signal_add("event 318", \&whois_end); +Irssi::command_bind("isbanned", \&isbanned); +Irssi::command_bind("ismuted", \&ismuted); +Irssi::command_bind("islisted", \&islisted); +Irssi::command_bind("isreset", \&reset); |
