# kickban.pl by arza <arza@arza.us>: A new, customizable kickban command

# This program is free software: you can modify/redistribute it under the terms of
# GNU General Public License by Free Software Foundation, either version 3 or later
# which you can get from <http://www.gnu.org/licenses/>.
# This program is distributed in the hope that it will be useful, but without any warranty.

# This script provides command /kickban2. You probably want to alias it to /kb: /alias kb kickban2

# Changelog:
# 24.03.2014 0.3 fix bug with uninitialized variable
# 15.10.2011 0.2 fix bug with ban when host isn't found in memory
# 08.07.2011 0.1 initial release

weechat::register("kickban", "arza <arza\@arza.us>", "0.3", "GPL3", "A new, customizable kickban command", "", "");
weechat::hook_command(
  "kickban2",
  "A new, customizable kickban command", "[-nuhsd#] nick[,nick2...] [reason]",
"The ban mask can be specified by setting plugins.var.perl.kickban.banmask or by a switch (default: u,h):
  n: nick
  u: username
  h: full host
  s: subdomain
  d: domain

Timeout for unban in minutes can be set by setting plugins.var.perl.kickban.time or by a number in a switch. (default: 0, don't unban)

Multiple nicks is supported. If nick isn't found in the channel, the mask is looked up automatically. (v>=0.3.4)

The default kick reason can be set by plugins.var.perl.kickban.kick_reason.

Whether kicking will be done before banning can be speficied by plugins.var.perl.kickban.kick_first.
There may be a delay of irc.server_default.anti_flood_prio_high between commands.

Examples:

Kick nick lamer and ban its nick!*\@*.domain:
  /kickban2 -nd lamer
Kick nicks badone and badtwo with reason \"Bye bye\", and ban them by *!user\@*.domain for ten minutes:
  /kickban2 -10ud badone,badtwo Bye bye",
  "%(nick)", "kickban", "");

my $version=weechat::info_get("version_number", "") || 0;
weechat::hook_hsignal("irc_redirection_sigwhois_whois", "get_whois", "") if $version>=0x00030400;

my ($buffer, %banmask, $time, $reason);

init_config();

sub init_config {
  
  if($version>=0x00030500){ # descriptions for settings
    weechat::config_set_desc_plugin("banmask", "mask used for banning, default: u,h (*!user\@host)");
    weechat::config_set_desc_plugin("time", "time in minutes to unban after banning, 0=never (default)");
    weechat::config_set_desc_plugin("kick_first", "kick before ban (default: on)");
    weechat::config_set_desc_plugin("kick_reason", "default kick reason");
  }
  
  my %options = ( # default options
                 "banmask" => "u,h",
                 "time" => "0",
                 "kick_first" => "on",
                 "kick_reason" => "",
                );
  
  foreach my $option (keys %options){ # sync the defaults
    weechat::config_set_plugin($option, $options{$option}) unless weechat::config_is_set_plugin($option);
  }
  
}

sub kickban {
  
  if(!$_[2]){ weechat::command("", "/help kickban2"); return weechat::WEECHAT_RC_OK; }
  
  $buffer=$_[1];
  my @args=split(/ /, $_[2]);
  $time=weechat::config_get_plugin("time");
  %banmask=();
  my @nicks;
  
  for(my $i=0; $i<=$#args+1; $i++){ my $arg=$args[$i] || last; # go through arguments
    if($arg=~/^\-/){ # begins '-': banmask/time switches
      foreach("n","u","h","s","d"){ $banmask{$_}=1 if $arg=~/$_/; } # set banmask type
      if($arg=~/(\d+)/){ $time=$1; } # any number = unban time
    }else{ # the rest is nicks (and reason)
      @nicks=split(/,/, $arg);
      $reason=join(' ', @args[$i+1..$#args]); # the reason
      last;
    }
  }
  
  return weechat::WEECHAT_RC_ERROR unless @nicks; # return if didn't get nicks
  
  if(!%banmask){ $banmask{$_}=1 foreach (split(/,/, weechat::config_get_plugin("banmask"))); } # get the banmask from the setting if it's not given as an argument
  
  if($banmask{"h"}){ $banmask{"s"}=$banmask{"d"}=1; } # host -> subdomain and domain
  
  $reason=weechat::config_get_plugin("kick_reason") unless $reason; # get the reason from the setting if it's not given as an argument
  
  foreach my $nick (@nicks) { # go through nicks to be kicked
    
    my $infolist = weechat::infolist_get( # get the irc_nick infolist
                                         "irc_nick",
                                         "",
                                         weechat::buffer_get_string($buffer, "localvar_server").
                                          ",".
                                          weechat::buffer_get_string($buffer, "localvar_channel").
                                          ",".
                                          $nick
                                        );
    next unless $infolist;
    
    if(weechat::infolist_next($infolist) && lc(weechat::infolist_string($infolist, "name")) eq lc($nick)){
      my $host=weechat::infolist_string($infolist, "host") || next;
      my $ban=gen_mask($nick, split(/@/, $host)); # split variable host from infolist to user and host, get banmask
      weechat::command($buffer, "/kick $nick $reason") if weechat::config_get_plugin("kick_first") ne "off"; # kick before ban
      weechat::command($buffer, "/ban $ban"); # ban
      weechat::command($buffer, "/kick $nick $reason") if weechat::config_get_plugin("kick_first") eq "off"; # kick after ban
      weechat::command($buffer, "/wait ".60*$time." /unban $ban") if $time;
      $nick='';
    }
    weechat::infolist_free($infolist);
  }
  
  if($version>=0x00030400){ # hook_hsignal reguires v>=0.3.4
    my $server=weechat::buffer_get_string($buffer, "localvar_server"); # current server
    foreach my $nick (@nicks) { # the nicks that weren't found in the channel
      if($nick){
        weechat::hook_hsignal_send("irc_redirect_command", { "server" => "$server", "pattern" => "whois", "signal" => "sigwhois" }); # redirection
        weechat::hook_signal_send("irc_input_send", weechat::WEECHAT_HOOK_SIGNAL_STRING, "$server;;1;;/whois $nick"); # send whois command
      }
    }
  }
  
}

sub get_whois { my %hashtable=%{$_[2]}; # get the answer for whois
  if($hashtable{"output"}=~/^:\S+ 311 \S+ (\S+) (\S+) (\S+)/){
    my $ban=gen_mask($1,$2,$3);
    weechat::command($buffer, "/ban $ban"); # ban
    weechat::command($buffer, "/wait ".60*$time." /unban $ban") if $time; # unban
    return weechat::WEECHAT_RC_OK;
  }elsif($hashtable{"output"}=~/^:\S+ 40[12] \S+ (\S+)/){
    weechat::print("", weechat::prefix("error")."Kickban: Didn't find nick $1");
    return weechat::WEECHAT_RC_OK;
  }
}

sub gen_mask { my ($nick, $user, $fullhost) = @_; # generate banmask
  
  my ($ban, $sub, $domain);
  
  if($fullhost=~/\w/){ # if there are letters in the host (it's not an ip)
    my @hostparts=split(/\./, $fullhost); # split host to subdomain and domain
    if(@hostparts>2){ # if there are at least three parts
      $sub=join(".", @hostparts[0..$#hostparts-2]); # subdomain is the beginning
      $domain=$hostparts[$#hostparts-1].".".$hostparts[$#hostparts]; # domain is the last two parts
    }
  }
  
  if($banmask{"n"}){ # if nick is in banmask
    $ban=$nick."!";
  }else{
    $ban="*!";
  }
  
  if($banmask{"u"}){ # if user is in banmask
    $ban.=$user."\@";
  }else{
    $ban.="*\@";
  }
  
  if($banmask{"s"}){ # if subdomain is in banmask
    if($banmask{"d"} || !$sub){ # use the full host if also domain is in banmask or anyway if subdomain isn't specified
      $ban.=$fullhost;
    }else{
      $ban.=$sub.".*";
    } 
  }elsif($banmask{"d"}){ # if domain is in banmask but subdomain isn't
    if($domain){ $ban.="*.$domain"; }
    else{ $ban.=$fullhost; } # use the full host anyway if domain isn't separated from subdomain
  }else{
    $ban.="*";
  }
  
  return $ban;
  
}
