2015-03-17 05:08:25 +01:00
# File: BlackList.pm
# Author: pragma_
#
# Purpose: Manages list of hostmasks that are not allowed to join a channel.
2017-03-05 22:33:31 +01:00
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
2015-03-17 05:08:25 +01:00
package PBot::BlackList ;
2020-02-08 20:04:13 +01:00
use parent 'PBot::Class' ;
2015-03-17 05:08:25 +01:00
2020-02-08 20:04:13 +01:00
use warnings ; use strict ;
2019-07-11 03:40:53 +02:00
use feature 'unicode_strings' ;
2015-03-17 05:08:25 +01:00
use feature 'switch' ;
no if $] >= 5.018 , warnings = > "experimental::smartmatch" ;
use Time::HiRes qw( gettimeofday ) ;
sub initialize {
2020-02-15 23:38:32 +01:00
my ( $ self , % conf ) = @ _ ;
$ self - > { filename } = $ conf { filename } ;
$ self - > { blacklist } = { } ;
$ self - > { pbot } - > { commands } - > register ( sub { $ self - > blacklist ( @ _ ) } , "blacklist" , 1 ) ;
$ self - > { pbot } - > { capabilities } - > add ( 'admin' , 'can-blacklist' , 1 ) ;
$ self - > load_blacklist ;
2015-03-17 05:08:25 +01:00
}
sub add {
2020-02-15 23:38:32 +01:00
my ( $ self , $ channel , $ hostmask ) = @ _ ;
$ self - > { blacklist } - > { lc $ channel } - > { lc $ hostmask } = 1 ;
$ self - > save_blacklist ( ) ;
2015-03-17 05:08:25 +01:00
}
sub remove {
2020-02-15 23:38:32 +01:00
my $ self = shift ;
my ( $ channel , $ hostmask ) = @ _ ;
2015-03-17 05:08:25 +01:00
2020-02-15 23:38:32 +01:00
$ channel = lc $ channel ;
$ hostmask = lc $ hostmask ;
2015-03-17 05:08:25 +01:00
2020-02-15 23:38:32 +01:00
if ( exists $ self - > { blacklist } - > { $ channel } ) {
delete $ self - > { blacklist } - > { $ channel } - > { $ hostmask } ;
2015-03-17 05:08:25 +01:00
2020-02-15 23:38:32 +01:00
if ( keys % { $ self - > { blacklist } - > { $ channel } } == 0 ) { delete $ self - > { blacklist } - > { $ channel } ; }
2016-11-04 20:40:17 +01:00
}
2020-02-15 23:38:32 +01:00
$ self - > save_blacklist ( ) ;
2015-03-17 05:08:25 +01:00
}
2017-08-06 06:38:46 +02:00
sub clear_blacklist {
2020-02-15 23:38:32 +01:00
my $ self = shift ;
$ self - > { blacklist } = { } ;
2017-08-06 06:38:46 +02:00
}
2015-03-17 05:08:25 +01:00
sub load_blacklist {
2020-02-15 23:38:32 +01:00
my $ self = shift ;
my $ filename ;
if ( @ _ ) { $ filename = shift ; }
else { $ filename = $ self - > { filename } ; }
if ( not defined $ filename ) {
$ self - > { pbot } - > { logger } - > log ( "No blacklist path specified -- skipping loading of blacklist" ) ;
return ;
}
2015-03-17 05:08:25 +01:00
2020-02-15 23:38:32 +01:00
$ self - > { pbot } - > { logger } - > log ( "Loading blacklist from $filename ...\n" ) ;
2015-03-17 05:08:25 +01:00
2020-02-15 23:38:32 +01:00
open ( FILE , "< $filename" ) or Carp:: croak "Couldn't open $filename: $!\n" ;
my @ contents = <FILE> ;
close ( FILE ) ;
2019-06-26 18:34:19 +02:00
2020-02-15 23:38:32 +01:00
my $ i = 0 ;
2015-03-17 05:08:25 +01:00
2020-02-15 23:38:32 +01:00
foreach my $ line ( @ contents ) {
chomp $ line ;
$ i + + ;
2015-03-17 05:08:25 +01:00
2020-02-15 23:38:32 +01:00
my ( $ channel , $ hostmask ) = split ( /\s+/ , $ line ) ;
2015-03-17 05:08:25 +01:00
2020-02-15 23:38:32 +01:00
if ( not defined $ hostmask || not defined $ channel ) { Carp:: croak "Syntax error around line $i of $filename\n" ; }
2019-06-26 18:34:19 +02:00
2020-02-15 23:38:32 +01:00
if ( exists $ self - > { blacklist } - > { $ channel } - > { $ hostmask } ) { Carp:: croak "Duplicate blacklist entry [$hostmask][$channel] found in $filename around line $i\n" ; }
2019-06-26 18:34:19 +02:00
2020-02-15 23:38:32 +01:00
$ self - > { blacklist } - > { $ channel } - > { $ hostmask } = 1 ;
2015-03-17 05:08:25 +01:00
}
2020-02-15 23:38:32 +01:00
$ self - > { pbot } - > { logger } - > log ( " $i entries in blacklist\n" ) ;
2015-03-17 05:08:25 +01:00
}
sub save_blacklist {
2020-02-15 23:38:32 +01:00
my $ self = shift ;
my $ filename ;
2015-03-17 05:08:25 +01:00
2020-02-15 23:38:32 +01:00
if ( @ _ ) { $ filename = shift ; }
else { $ filename = $ self - > { filename } ; }
2015-03-17 05:08:25 +01:00
2020-02-15 23:38:32 +01:00
if ( not defined $ filename ) {
$ self - > { pbot } - > { logger } - > log ( "No blacklist path specified -- skipping saving of blacklist\n" ) ;
return ;
}
2015-03-17 05:08:25 +01:00
2020-02-15 23:38:32 +01:00
open ( FILE , "> $filename" ) or die "Couldn't open $filename: $!\n" ;
2015-03-17 05:08:25 +01:00
2020-02-15 23:38:32 +01:00
foreach my $ channel ( keys % { $ self - > { blacklist } } ) {
foreach my $ hostmask ( keys % { $ self - > { blacklist } - > { $ channel } } ) { print FILE "$channel $hostmask\n" ; }
2015-03-17 05:08:25 +01:00
}
2020-02-15 23:38:32 +01:00
close ( FILE ) ;
2015-03-17 05:08:25 +01:00
}
sub check_blacklist {
2020-02-15 23:38:32 +01:00
my $ self = shift ;
my ( $ hostmask , $ channel , $ nickserv , $ gecos ) = @ _ ;
return 0 if not defined $ channel ;
foreach my $ black_channel ( keys % { $ self - > { blacklist } } ) {
foreach my $ black_hostmask ( keys % { $ self - > { blacklist } - > { $ black_channel } } ) {
my $ flag = '' ;
$ flag = $ 1 if $ black_hostmask =~ s/^\$(.):// ;
my $ black_channel_escaped = quotemeta $ black_channel ;
my $ black_hostmask_escaped = quotemeta $ black_hostmask ;
$ black_channel_escaped =~ s/\\(\.|\*)/$1/g ;
$ black_hostmask_escaped =~ s/\\(\.|\*)/$1/g ;
next if $ channel !~ /^$black_channel_escaped$/ ;
if ( $ flag eq 'a' && defined $ nickserv && $ nickserv =~ /^$black_hostmask_escaped$/i ) {
$ self - > { pbot } - > { logger } - > log ( "$hostmask nickserv $nickserv blacklisted in channel $channel (matches [\$a:$black_hostmask] host and [$black_channel] channel)\n" ) ;
return 1 ;
} elsif ( $ flag eq 'r' && defined $ gecos && $ gecos =~ /^$black_hostmask_escaped$/i ) {
$ self - > { pbot } - > { logger } - > log ( "$hostmask GECOS $gecos blacklisted in channel $channel (matches [\$r:$black_hostmask] host and [$black_channel] channel)\n" ) ;
return 1 ;
} elsif ( $ flag eq '' && $ hostmask =~ /^$black_hostmask_escaped$/i ) {
$ self - > { pbot } - > { logger } - > log ( "$hostmask blacklisted in channel $channel (matches [$black_hostmask] host and [$black_channel] channel)\n" ) ;
return 1 ;
}
}
2015-03-17 05:08:25 +01:00
}
2020-02-15 23:38:32 +01:00
return 0 ;
2015-03-17 05:08:25 +01:00
}
sub blacklist {
2018-08-09 02:38:57 +02:00
my ( $ self , $ from , $ nick , $ user , $ host , $ arguments , $ stuff ) = @ _ ;
2015-03-17 05:08:25 +01:00
2018-08-09 02:38:57 +02:00
my $ arglist = $ stuff - > { arglist } ;
$ self - > { pbot } - > { interpreter } - > lc_args ( $ arglist ) ;
my $ command = $ self - > { pbot } - > { interpreter } - > shift_arg ( $ arglist ) ;
2015-03-17 05:08:25 +01:00
return "Usage: blacklist <command>, where commands are: list/show, add, remove" if not defined $ command ;
2018-08-09 02:38:57 +02:00
given ( $ command ) {
when ( $ _ eq "list" or $ _ eq "show" ) {
2020-02-15 23:38:32 +01:00
my $ text = "Blacklist:\n" ;
2015-03-17 05:08:25 +01:00
my $ entries = 0 ;
2020-02-15 23:38:32 +01:00
foreach my $ channel ( sort keys % { $ self - > { blacklist } } ) {
if ( $ channel eq '.*' ) { $ text . = " all channels:\n" ; }
else { $ text . = " $channel:\n" ; }
foreach my $ mask ( sort keys % { $ self - > { blacklist } - > { $ channel } } ) {
$ text . = " $mask,\n" ;
$ entries + + ;
}
2015-03-17 05:08:25 +01:00
}
$ text . = "none" if $ entries == 0 ;
2018-05-15 04:20:55 +02:00
return "/msg $nick $text" ;
2015-03-17 05:08:25 +01:00
}
2018-08-09 02:38:57 +02:00
when ( "add" ) {
my ( $ mask , $ channel ) = $ self - > { pbot } - > { interpreter } - > split_args ( $ arglist , 2 ) ;
2015-03-17 05:08:25 +01:00
return "Usage: blacklist add <hostmask regex> [channel]" if not defined $ mask ;
$ channel = '.*' if not defined $ channel ;
$ self - > { pbot } - > { logger } - > log ( "$nick!$user\@$host added [$mask] to blacklist for channel [$channel]\n" ) ;
$ self - > add ( $ channel , $ mask ) ;
2017-09-02 09:14:13 +02:00
return "/say $mask blacklisted in channel $channel" ;
2015-03-17 05:08:25 +01:00
}
2018-08-09 02:38:57 +02:00
when ( "remove" ) {
my ( $ mask , $ channel ) = $ self - > { pbot } - > { interpreter } - > split_args ( $ arglist , 2 ) ;
2015-03-17 05:08:25 +01:00
return "Usage: blacklist remove <hostmask regex> [channel]" if not defined $ mask ;
$ channel = '.*' if not defined $ channel ;
2019-05-28 18:19:42 +02:00
if ( exists $ self - > { blacklist } - > { $ channel } and not exists $ self - > { blacklist } - > { $ channel } - > { $ mask } ) {
2020-02-15 23:38:32 +01:00
$ self - > { pbot } - > { logger } - > log ( "$nick attempt to remove nonexistent [$mask][$channel] from blacklist\n" ) ;
return "/say $mask not found in blacklist for channel $channel (use `blacklist list` to display blacklist)" ;
2015-03-17 05:08:25 +01:00
}
$ self - > remove ( $ channel , $ mask ) ;
$ self - > { pbot } - > { logger } - > log ( "$nick!$user\@$host removed [$mask] from blacklist for channel [$channel]\n" ) ;
2017-09-02 09:14:13 +02:00
return "/say $mask removed from blacklist for channel $channel" ;
2015-03-17 05:08:25 +01:00
}
2020-02-15 23:38:32 +01:00
default { return "Unknown command '$command'; commands are: list/show, add, remove" ; }
2015-03-17 05:08:25 +01:00
}
}
1 ;