]> git.99rst.org Git - irssi-scripts.irssi.org.git/commitdiff
Added isbanned.pl
authormniip <redacted>
Tue, 13 Jan 2015 14:01:18 +0000 (17:01 +0300)
committermniip <redacted>
Tue, 13 Jan 2015 14:41:40 +0000 (17:41 +0300)
_data/scripts.yaml
scripts/isbanned.pl [new file with mode: 0644]

index dbe7d575b3ccb69a9b24fd4654c6e08e61f8208f..67fdef53035ead5502869596f1db95ceae017389 100644 (file)
   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 (file)
index 0000000..3feaca2
--- /dev/null
@@ -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);
git clone https://git.99rst.org/PROJECT