summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormniip2015-01-13 17:01:18 +0300
committermniip2015-01-13 17:41:40 +0300
commitdaf758db38e4fe125ffe1f7281325744776f233e (patch)
tree5492b4260f6fe2deb9be13f2e32ff0798e159603
parent2407581eba0ab35b32774e06461bf951545cde07 (diff)
downloadscripts.irssi.org-daf758db38e4fe125ffe1f7281325744776f233e.tar.bz2
Added isbanned.pl
-rw-r--r--_data/scripts.yaml11
-rw-r--r--scripts/isbanned.pl626
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);