2020-01-25 21:28:05 +01:00
# File: Users.pm
# Author: pragma_
#
# Purpose: Manages list of bot users/admins and their metadata.
# 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/.
package PBot::Users ;
2020-02-08 20:04:13 +01:00
use parent 'PBot::Class' ;
2020-01-25 21:28:05 +01:00
2020-02-08 20:04:13 +01:00
use warnings ; use strict ;
2020-01-25 21:28:05 +01:00
use feature 'unicode_strings' ;
sub initialize {
my ( $ self , % conf ) = @ _ ;
2020-02-04 02:19:04 +01:00
$ self - > { users } = PBot::DualIndexHashObject - > new ( name = > 'Users' , filename = > $ conf { filename } , pbot = > $ conf { pbot } ) ;
2020-01-25 21:28:05 +01:00
$ self - > load ;
2020-02-06 10:07:44 +01:00
$ self - > { pbot } - > { commands } - > register ( sub { $ self - > logincmd ( @ _ ) } , "login" , 0 ) ;
$ self - > { pbot } - > { commands } - > register ( sub { $ self - > logoutcmd ( @ _ ) } , "logout" , 0 ) ;
$ self - > { pbot } - > { commands } - > register ( sub { $ self - > useradd ( @ _ ) } , "useradd" , 1 ) ;
$ self - > { pbot } - > { commands } - > register ( sub { $ self - > userdel ( @ _ ) } , "userdel" , 1 ) ;
$ self - > { pbot } - > { commands } - > register ( sub { $ self - > userset ( @ _ ) } , "userset" , 1 ) ;
$ self - > { pbot } - > { commands } - > register ( sub { $ self - > userunset ( @ _ ) } , "userunset" , 1 ) ;
$ self - > { pbot } - > { commands } - > register ( sub { $ self - > mycmd ( @ _ ) } , "my" , 0 ) ;
2020-01-25 21:28:05 +01:00
2020-02-06 07:32:58 +01:00
$ self - > { pbot } - > { capabilities } - > add ( 'admin' , 'can-useradd' , 1 ) ;
$ self - > { pbot } - > { capabilities } - > add ( 'admin' , 'can-userdel' , 1 ) ;
$ self - > { pbot } - > { capabilities } - > add ( 'admin' , 'can-userset' , 1 ) ;
$ self - > { pbot } - > { capabilities } - > add ( 'admin' , 'can-userunset' , 1 ) ;
$ self - > { pbot } - > { capabilities } - > add ( 'can-modify-admins' , undef , 1 ) ;
2020-02-06 10:17:09 +01:00
$ self - > { pbot } - > { event_dispatcher } - > register_handler ( 'irc.join' , sub { $ self - > on_join ( @ _ ) } ) ;
2020-01-25 21:28:05 +01:00
}
sub on_join {
my ( $ self , $ event_type , $ event ) = @ _ ;
my ( $ nick , $ user , $ host , $ channel ) = ( $ event - > { event } - > nick , $ event - > { event } - > user , $ event - > { event } - > host , $ event - > { event } - > to ) ;
( $ nick , $ user , $ host ) = $ self - > { pbot } - > { irchandlers } - > normalize_hostmask ( $ nick , $ user , $ host ) ;
my $ u = $ self - > find_user ( $ channel , "$nick!$user\@$host" ) ;
if ( defined $ u ) {
if ( $ self - > { pbot } - > { chanops } - > can_gain_ops ( $ channel ) ) {
my $ modes = '+' ;
my $ targets = '' ;
if ( $ u - > { autoop } ) {
$ self - > { pbot } - > { logger } - > log ( "$nick!$user\@$host autoop in $channel\n" ) ;
$ modes . = 'o' ;
$ targets . = "$nick " ;
}
if ( $ u - > { autovoice } ) {
$ self - > { pbot } - > { logger } - > log ( "$nick!$user\@$host autovoice in $channel\n" ) ;
$ modes . = 'v' ;
$ targets . = "$nick " ;
}
if ( length $ modes > 1 ) {
$ self - > { pbot } - > { chanops } - > add_op_command ( $ channel , "mode $channel $modes $targets" ) ;
$ self - > { pbot } - > { chanops } - > gain_ops ( $ channel ) ;
}
}
if ( $ u - > { autologin } ) {
2020-02-04 02:19:04 +01:00
$ self - > { pbot } - > { logger } - > log ( "$nick!$user\@$host autologin to $u->{name} for $channel\n" ) ;
2020-01-31 21:33:20 +01:00
$ u - > { loggedin } = 1 ;
2020-01-25 21:28:05 +01:00
}
}
return 0 ;
}
sub add_user {
2020-02-04 02:19:04 +01:00
my ( $ self , $ name , $ channel , $ hostmask , $ capabilities , $ password , $ dont_save ) = @ _ ;
2020-01-25 21:28:05 +01:00
$ channel = '.*' if $ channel !~ m/^#/ ;
2020-02-04 02:19:04 +01:00
$ capabilities // = 'none' ;
2020-01-25 21:28:05 +01:00
$ password // = $ self - > { pbot } - > random_nick ( 16 ) ;
my $ data = {
name = > $ name ,
password = > $ password
} ;
2020-02-06 07:32:58 +01:00
foreach my $ cap ( split /\s*,\s*/ , lc $ capabilities ) {
2020-02-04 02:19:04 +01:00
next if $ cap eq 'none' ;
$ data - > { $ cap } = 1 ;
}
$ self - > { pbot } - > { logger } - > log ( "Adding new user (caps: $capabilities): name: $name hostmask: $hostmask channel: $channel\n" ) ;
2020-01-25 21:28:05 +01:00
$ self - > { users } - > add ( $ channel , $ hostmask , $ data , $ dont_save ) ;
return $ data ;
}
sub remove_user {
my ( $ self , $ channel , $ hostmask ) = @ _ ;
return $ self - > { users } - > remove ( $ channel , $ hostmask ) ;
}
sub load {
my $ self = shift ;
my $ filename ;
if ( @ _ ) { $ filename = shift ; } else { $ filename = $ self - > { users } - > { filename } ; }
if ( not defined $ filename ) {
Carp:: carp "No users path specified -- skipping loading of users" ;
return ;
}
$ self - > { users } - > load ;
my $ i = 0 ;
foreach my $ channel ( sort keys % { $ self - > { users } - > { hash } } ) {
foreach my $ hostmask ( sort keys % { $ self - > { users } - > { hash } - > { $ channel } } ) {
next if $ hostmask eq '_name' ;
$ i + + ;
my $ name = $ self - > { users } - > { hash } - > { $ channel } - > { $ hostmask } - > { name } ;
my $ password = $ self - > { users } - > { hash } - > { $ channel } - > { $ hostmask } - > { password } ;
2020-02-04 02:19:04 +01:00
if ( not defined $ name or not defined $ password ) {
2020-01-25 21:28:05 +01:00
Carp:: croak "A user in $filename is missing critical data\n" ;
}
}
}
$ self - > { pbot } - > { logger } - > log ( " $i users loaded.\n" ) ;
}
sub save {
my ( $ self ) = @ _ ;
$ self - > { users } - > save ;
}
2020-01-25 23:13:57 +01:00
sub find_user_account {
2020-01-25 21:28:05 +01:00
my ( $ self , $ channel , $ hostmask ) = @ _ ;
$ channel = lc $ channel ;
$ hostmask = lc $ hostmask ;
2020-01-29 03:01:25 +01:00
my ( $ found_channel , $ found_hostmask ) = ( undef , $ hostmask ) ;
2020-01-25 23:13:57 +01:00
foreach my $ chan ( keys % { $ self - > { users } - > { hash } } ) {
if ( $ channel !~ m/^#/ or $ channel =~ m/^$chan$/i ) {
if ( not exists $ self - > { users } - > { hash } - > { $ chan } - > { $ hostmask } ) {
# find hostmask by account name or wildcard
foreach my $ mask ( keys % { $ self - > { users } - > { hash } - > { $ chan } } ) {
next if $ mask eq '_name' ;
if ( lc $ self - > { users } - > { hash } - > { $ chan } - > { $ mask } - > { name } eq $ hostmask ) {
2020-02-04 02:19:04 +01:00
$ found_hostmask = $ mask ;
$ found_channel = $ chan ;
last ;
2020-01-25 21:28:05 +01:00
}
2020-01-25 23:13:57 +01:00
if ( $ mask =~ /[*?]/ ) {
# contains * or ? so it's converted to a regex
my $ mask_quoted = quotemeta $ mask ;
$ mask_quoted =~ s/\\\*/.*?/g ;
$ mask_quoted =~ s/\\\?/./g ;
if ( $ hostmask =~ m/^$mask_quoted$/i ) {
2020-02-04 02:19:04 +01:00
$ found_hostmask = $ mask ;
$ found_channel = $ chan ;
last ;
2020-01-25 21:28:05 +01:00
}
}
}
2020-01-26 21:28:38 +01:00
} else {
$ found_channel = $ chan ;
2020-02-04 02:19:04 +01:00
last ;
2020-01-25 21:28:05 +01:00
}
}
}
2020-01-25 23:13:57 +01:00
return ( $ found_channel , $ found_hostmask ) ;
2020-01-25 21:28:05 +01:00
}
2020-02-04 02:19:04 +01:00
sub find_user {
my ( $ self , $ channel , $ hostmask ) = @ _ ;
2020-01-25 23:13:57 +01:00
( $ channel , $ hostmask ) = $ self - > find_user_account ( $ channel , $ hostmask ) ;
2020-01-29 03:01:25 +01:00
$ channel = '.*' if not defined $ channel ;
2020-01-25 21:28:05 +01:00
$ hostmask = '.*' if not defined $ hostmask ;
$ hostmask = lc $ hostmask ;
my $ result = eval {
2020-02-04 02:19:04 +01:00
my $ user ;
2020-01-25 21:28:05 +01:00
foreach my $ channel_regex ( keys % { $ self - > { users } - > { hash } } ) {
if ( $ channel !~ m/^#/ or $ channel =~ m/^$channel_regex$/i ) {
foreach my $ hostmask_regex ( keys % { $ self - > { users } - > { hash } - > { $ channel_regex } } ) {
next if $ hostmask_regex eq '_name' ;
if ( $ hostmask_regex =~ m/[*?]/ ) {
# contains * or ? so it's converted to a regex
my $ hostmask_quoted = quotemeta $ hostmask_regex ;
$ hostmask_quoted =~ s/\\\*/.*?/g ;
$ hostmask_quoted =~ s/\\\?/./g ;
if ( $ hostmask =~ m/^$hostmask_quoted$/i ) {
2020-02-04 02:19:04 +01:00
return $ self - > { users } - > { hash } - > { $ channel_regex } - > { $ hostmask_regex } ;
2020-01-25 21:28:05 +01:00
}
} else {
# direct comparison
if ( $ hostmask eq lc $ hostmask_regex ) {
2020-02-04 02:19:04 +01:00
return $ self - > { users } - > { hash } - > { $ channel_regex } - > { $ hostmask_regex } ;
2020-01-25 21:28:05 +01:00
}
}
}
}
}
2020-02-04 02:19:04 +01:00
return undef ;
2020-01-25 21:28:05 +01:00
} ;
if ( $@ ) {
2020-02-04 02:19:04 +01:00
$ self - > { pbot } - > { logger } - > log ( "Error in find_user parameters: $@\n" ) ;
2020-01-25 21:28:05 +01:00
}
return $ result ;
}
2020-02-04 02:19:04 +01:00
sub find_admin {
2020-01-25 21:28:05 +01:00
my ( $ self , $ from , $ hostmask ) = @ _ ;
2020-02-04 02:19:04 +01:00
my $ user = $ self - > find_user ( $ from , $ hostmask ) ;
return undef if not defined $ user ;
return undef if not $ self - > { pbot } - > { capabilities } - > userhas ( $ user , 'admin' ) ;
return $ user ;
2020-01-25 21:28:05 +01:00
}
sub loggedin {
my ( $ self , $ channel , $ hostmask ) = @ _ ;
my $ user = $ self - > find_user ( $ channel , $ hostmask ) ;
2020-02-04 02:19:04 +01:00
return $ user if defined $ user and $ user - > { loggedin } ;
2020-01-25 21:28:05 +01:00
return undef ;
}
sub loggedin_admin {
my ( $ self , $ channel , $ hostmask ) = @ _ ;
my $ user = $ self - > loggedin ( $ channel , $ hostmask ) ;
2020-02-04 02:19:04 +01:00
return $ user if defined $ user and $ self - > { pbot } - > { capabilities } - > userhas ( $ user , 'admin' ) ;
2020-01-25 21:28:05 +01:00
return undef ;
}
sub login {
my ( $ self , $ channel , $ hostmask , $ password ) = @ _ ;
my $ user = $ self - > find_user ( $ channel , $ hostmask ) ;
2020-01-29 07:58:44 +01:00
my $ channel_text = $ channel eq '.*' ? '' : " for $channel" ;
2020-01-25 21:28:05 +01:00
if ( not defined $ user ) {
$ self - > { pbot } - > { logger } - > log ( "Attempt to login non-existent [$channel][$hostmask] failed\n" ) ;
2020-01-29 07:58:44 +01:00
return "You do not have a user account$channel_text." ;
2020-01-25 21:28:05 +01:00
}
if ( defined $ password and $ user - > { password } ne $ password ) {
$ self - > { pbot } - > { logger } - > log ( "Bad login password for [$channel][$hostmask]\n" ) ;
return "I don't think so." ;
}
$ user - > { loggedin } = 1 ;
2020-01-29 07:58:44 +01:00
$ self - > { pbot } - > { logger } - > log ( "$hostmask logged into $user->{name} ($hostmask)$channel_text.\n" ) ;
return "Logged into $user->{name} ($hostmask)$channel_text." ;
2020-01-25 21:28:05 +01:00
}
sub logout {
my ( $ self , $ channel , $ hostmask ) = @ _ ;
my $ user = $ self - > find_user ( $ channel , $ hostmask ) ;
delete $ user - > { loggedin } if defined $ user ;
}
2020-01-25 23:13:57 +01:00
sub get_loggedin_user_metadata {
my ( $ self , $ channel , $ hostmask , $ key ) = @ _ ;
my $ user = $ self - > loggedin ( $ channel , $ hostmask ) ;
if ( $ user ) {
return $ user - > { lc $ key } ;
}
return undef ;
}
2020-01-25 21:28:05 +01:00
sub logincmd {
my ( $ self , $ from , $ nick , $ user , $ host , $ arguments ) = @ _ ;
my $ channel = $ from ;
2020-02-04 02:19:04 +01:00
return "Usage: login [channel] password" if not $ arguments ;
2020-01-25 21:28:05 +01:00
if ( $ arguments =~ m/^([^ ]+)\s+(.+)/ ) {
$ channel = $ 1 ;
$ arguments = $ 2 ;
}
2020-01-29 07:58:44 +01:00
my ( $ user_channel , $ user_hostmask ) = $ self - > find_user_account ( $ channel , "$nick!$user\@$host" ) ;
2020-02-04 02:19:04 +01:00
return "/msg $nick You do not have a user account." if not defined $ user_channel ;
2020-01-29 07:58:44 +01:00
my $ u = $ self - > { users } - > { hash } - > { $ user_channel } - > { $ user_hostmask } ;
my $ channel_text = $ user_channel eq '.*' ? '' : " for $user_channel" ;
if ( $ u - > { loggedin } ) {
2020-01-29 08:14:17 +01:00
return "/msg $nick You are already logged into $u->{name} ($user_hostmask)$channel_text." ;
2020-01-25 21:28:05 +01:00
}
2020-01-29 07:58:44 +01:00
my $ result = $ self - > login ( $ user_channel , $ user_hostmask , $ arguments ) ;
2020-01-25 21:28:05 +01:00
return "/msg $nick $result" ;
}
sub logoutcmd {
my ( $ self , $ from , $ nick , $ user , $ host , $ arguments ) = @ _ ;
2020-01-29 07:58:44 +01:00
$ from = $ arguments if length $ arguments ;
my ( $ user_channel , $ user_hostmask ) = $ self - > find_user_account ( $ from , "$nick!$user\@$host" ) ;
if ( not defined $ user_channel ) {
return "/msg $nick You do not have a user account." ;
}
my $ u = $ self - > { users } - > { hash } - > { $ user_channel } - > { $ user_hostmask } ;
my $ channel_text = $ user_channel eq '.*' ? '' : " for $user_channel" ;
if ( not $ u - > { loggedin } ) {
return "/msg $nick You are not logged into $u->{name} ($user_hostmask)$channel_text." ;
}
$ self - > logout ( $ user_channel , $ user_hostmask ) ;
return "/msg $nick Logged out of $u->{name} ($user_hostmask)$channel_text." ;
2020-01-25 21:28:05 +01:00
}
sub useradd {
my ( $ self , $ from , $ nick , $ user , $ host , $ arguments , $ stuff ) = @ _ ;
2020-02-04 02:19:04 +01:00
my ( $ name , $ channel , $ hostmask , $ capabilities , $ password ) = $ self - > { pbot } - > { interpreter } - > split_args ( $ stuff - > { arglist } , 5 ) ;
$ capabilities // = 'none' ;
2020-01-25 21:28:05 +01:00
if ( not defined $ name or not defined $ channel or not defined $ hostmask ) {
2020-02-04 02:19:04 +01:00
return "Usage: useradd <account name> <channel> <hostmask> [capabilities [password]]" ;
2020-01-25 21:28:05 +01:00
}
$ channel = '.*' if $ channel !~ /^#/ ;
2020-02-04 02:19:04 +01:00
my $ u = $ self - > { pbot } - > { users } - > find_user ( $ channel , "$nick!$user\@$host" ) ;
2020-01-25 21:28:05 +01:00
2020-02-04 02:19:04 +01:00
if ( not defined $ u ) {
2020-01-25 23:13:57 +01:00
$ channel = 'global' if $ channel eq '.*' ;
2020-02-04 02:19:04 +01:00
return "You do not have a user account for $channel; cannot add users to that channel.\n" ;
2020-01-25 21:28:05 +01:00
}
2020-02-04 02:19:04 +01:00
if ( $ capabilities ne 'none' and not $ self - > { pbot } - > { capabilities } - > userhas ( $ u , 'can-modify-capabilities' ) ) {
return "Your user account does not have the can-modify-capabilities capability. You cannot create user accounts with capabilities." ;
2020-01-25 21:28:05 +01:00
}
2020-02-06 07:32:58 +01:00
foreach my $ cap ( split /\s*,\s*/ , lc $ capabilities ) {
2020-02-04 02:19:04 +01:00
next if $ cap eq 'none' ;
return "There is no such capability $cap." if not $ self - > { pbot } - > { capabilities } - > exists ( $ cap ) ;
if ( not $ self - > { pbot } - > { capabilities } - > userhas ( $ u , $ cap ) ) {
return "To set the $cap capability your user account must also have it." ;
}
2020-02-06 07:32:58 +01:00
if ( $ self - > { pbot } - > { capabilities } - > has ( $ cap , 'admin' ) and not $ self - > { pbot } - > { capabilities } - > userhas ( $ u , 'can-modify-admins' ) ) {
return "To set the $cap capability your user account must have the can-modify-admins capability." ;
}
2020-02-04 02:19:04 +01:00
}
$ self - > { pbot } - > { users } - > add_user ( $ name , $ channel , $ hostmask , $ capabilities , $ password ) ;
return "User added." ;
2020-01-25 21:28:05 +01:00
}
sub userdel {
my ( $ self , $ from , $ nick , $ user , $ host , $ arguments , $ stuff ) = @ _ ;
my ( $ channel , $ hostmask ) = $ self - > { pbot } - > { interpreter } - > split_args ( $ stuff - > { arglist } , 2 ) ;
if ( not defined $ channel or not defined $ hostmask ) {
2020-01-29 22:40:48 +01:00
return "Usage: userdel <channel> <hostmask or account name>" ;
2020-01-25 21:28:05 +01:00
}
2020-02-06 07:32:58 +01:00
my $ u = $ self - > find_user ( $ channel , "$nick!$user\@$host" ) ;
my $ t = $ self - > find_user ( $ channel , $ hostmask ) ;
if ( $ self - > { pbot } - > { capabilities } - > userhas ( $ t , 'botowner' ) and not $ self - > { pbot } - > { capabilities } - > userhas ( $ u , 'botowner' ) ) {
return "Only botowners may delete botowner user accounts." ;
}
if ( $ self - > { pbot } - > { capabilities } - > userhas ( $ t , 'admin' ) and not $ self - > { pbot } - > { capabilities } - > userhas ( $ u , 'can-modify-admins' ) ) {
return "To delete admin user accounts your user account must have the can-modify-admins capability." ;
}
2020-01-29 03:01:25 +01:00
my ( $ found_channel , $ found_hostmask ) = $ self - > find_user_account ( $ channel , $ hostmask ) ;
$ found_channel = $ channel if not defined $ found_channel ; # let DualIndexHashObject disambiguate
return $ self - > remove_user ( $ found_channel , $ found_hostmask ) ;
2020-01-25 21:28:05 +01:00
}
sub userset {
my ( $ self , $ from , $ nick , $ user , $ host , $ arguments , $ stuff ) = @ _ ;
2020-01-29 08:21:50 +01:00
if ( length $ arguments and $ stuff - > { arglist } [ 0 ] !~ m/^(#|\.\*$|global$)/ ) {
$ self - > { pbot } - > { interpreter } - > unshift_arg ( $ stuff - > { arglist } , $ from )
}
2020-01-25 21:28:05 +01:00
my ( $ channel , $ hostmask , $ key , $ value ) = $ self - > { pbot } - > { interpreter } - > split_args ( $ stuff - > { arglist } , 4 ) ;
2020-01-29 08:14:17 +01:00
if ( not defined $ hostmask ) {
2020-01-29 22:35:56 +01:00
return "Usage: userset [channel] <hostmask or account name> [key [value]]" ;
2020-01-25 21:28:05 +01:00
}
2020-02-04 02:19:04 +01:00
my $ u = $ self - > find_user ( $ channel , "$nick!$user\@$host" ) ;
2020-01-25 21:28:05 +01:00
my $ target = $ self - > find_user ( $ channel , $ hostmask ) ;
2020-02-04 02:19:04 +01:00
if ( not $ u ) {
2020-01-25 23:13:57 +01:00
$ channel = 'global' if $ channel eq '.*' ;
2020-02-04 02:19:04 +01:00
return "You do not have a user account for $channel; cannot modify their users." ;
2020-01-25 21:28:05 +01:00
}
if ( not $ target ) {
2020-01-29 08:14:17 +01:00
if ( $ channel !~ /^#/ ) {
return "There is no user account $hostmask." ;
} else {
return "There is no user account $hostmask for $channel." ;
}
2020-01-25 21:28:05 +01:00
}
2020-02-04 02:19:04 +01:00
if ( defined $ value and not $ self - > { pbot } - > { capabilities } - > userhas ( $ u , 'can-modify-capabilities' ) ) {
if ( $ key =~ m/^can-/i or $ self - > { pbot } - > { capabilities } - > exists ( $ key ) ) {
return "The $key metadata requires the can-modify-capabilities capability, which your user account does not have." ;
}
2020-01-25 21:28:05 +01:00
}
2020-02-06 07:32:58 +01:00
if ( defined $ value and $ self - > { pbot } - > { capabilities } - > userhas ( $ target , 'admin' ) and not $ self - > { pbot } - > { capabilities } - > userhas ( $ u , 'can-modify-admins' ) ) {
return "To modify admin user accounts your user account must have the can-modify-admins capability." ;
}
2020-02-04 02:19:04 +01:00
if ( $ self - > { pbot } - > { capabilities } - > exists ( $ key ) and not $ self - > { pbot } - > { capabilities } - > userhas ( $ u , $ key ) ) {
2020-02-04 03:13:03 +01:00
return "To set the $key capability your user account must also have it." unless $ self - > { pbot } - > { capabilities } - > userhas ( $ u , 'botowner' ) ;
2020-01-25 21:28:05 +01:00
}
2020-01-29 03:01:25 +01:00
my ( $ found_channel , $ found_hostmask ) = $ self - > find_user_account ( $ channel , $ hostmask ) ;
$ found_channel = $ channel if not defined $ found_channel ; # let DualIndexHashObject disambiguate
my $ result = $ self - > { users } - > set ( $ found_channel , $ found_hostmask , $ key , $ value ) ;
2020-01-25 23:31:10 +01:00
$ result =~ s/^password => .*;?$/password => <private>;/m ;
2020-01-25 21:28:05 +01:00
return $ result ;
}
sub userunset {
my ( $ self , $ from , $ nick , $ user , $ host , $ arguments , $ stuff ) = @ _ ;
2020-01-29 08:21:50 +01:00
if ( length $ arguments and $ stuff - > { arglist } [ 0 ] !~ m/^(#|\.\*$|global$)/ ) {
$ self - > { pbot } - > { interpreter } - > unshift_arg ( $ stuff - > { arglist } , $ from )
}
2020-01-25 21:28:05 +01:00
my ( $ channel , $ hostmask , $ key ) = $ self - > { pbot } - > { interpreter } - > split_args ( $ stuff - > { arglist } , 3 ) ;
2020-01-29 08:14:17 +01:00
if ( not defined $ hostmask ) {
return "Usage: userunset [channel] <hostmask or account name> <key>" ;
2020-01-25 21:28:05 +01:00
}
2020-02-06 07:32:58 +01:00
my $ u = $ self - > find_user ( $ channel , "$nick!$user\@$host" ) ;
2020-01-25 21:28:05 +01:00
my $ target = $ self - > find_user ( $ channel , $ hostmask ) ;
2020-02-04 02:19:04 +01:00
if ( not $ u ) {
2020-01-25 23:13:57 +01:00
$ channel = 'global' if $ channel eq '.*' ;
2020-02-04 02:19:04 +01:00
return "You do not have a user account for $channel; cannot modify their users." ;
2020-01-25 21:28:05 +01:00
}
if ( not $ target ) {
2020-01-29 08:14:17 +01:00
if ( $ channel !~ /^#/ ) {
return "There is no user account $hostmask." ;
} else {
return "There is no user account $hostmask for $channel." ;
}
2020-01-25 21:28:05 +01:00
}
2020-02-04 02:19:04 +01:00
if ( defined $ key and not $ self - > { pbot } - > { capabilities } - > userhas ( $ u , 'can-modify-capabilities' ) ) {
if ( $ key =~ m/^can-/i or $ self - > { pbot } - > { capabilities } - > exists ( $ key ) ) {
return "The $key metadata requires the can-modify-capabilities capability, which your user account does not have." ;
}
}
2020-02-06 07:32:58 +01:00
if ( defined $ key and $ self - > { pbot } - > { capabilities } - > userhas ( $ target , 'admin' ) and not $ self - > { pbot } - > { capabilities } - > userhas ( $ u , 'can-modify-admins' ) ) {
return "To modify admin user accounts your user account must have the can-modify-admins capability." ;
}
2020-02-04 02:19:04 +01:00
if ( defined $ key and $ self - > { pbot } - > { capabilities } - > exists ( $ key ) and not $ self - > { pbot } - > { capabilities } - > userhas ( $ u , $ key ) ) {
2020-02-04 03:13:03 +01:00
return "To unset the $key capability your user account must also have it." unless $ self - > { pbot } - > { capabilities } - > userhas ( $ u , 'botowner' ) ;
2020-01-25 21:28:05 +01:00
}
2020-01-29 03:01:25 +01:00
my ( $ found_channel , $ found_hostmask ) = $ self - > find_user_account ( $ channel , $ hostmask ) ;
$ found_channel = $ channel if not defined $ found_channel ; # let DualIndexHashObject disambiguate
return $ self - > { users } - > unset ( $ found_channel , $ found_hostmask , $ key ) ;
2020-01-25 21:28:05 +01:00
}
sub mycmd {
my ( $ self , $ from , $ nick , $ user , $ host , $ arguments , $ stuff ) = @ _ ;
my ( $ key , $ value ) = $ self - > { pbot } - > { interpreter } - > split_args ( $ stuff - > { arglist } , 2 ) ;
2020-01-26 01:30:39 +01:00
if ( defined $ value ) {
$ value =~ s/^is\s+// ;
$ value = undef if not length $ value ;
}
2020-01-25 21:28:05 +01:00
my $ channel = $ from ;
2020-01-25 23:13:57 +01:00
my $ hostmask = "$nick!$user\@$host" ;
2020-01-25 21:28:05 +01:00
2020-01-25 23:13:57 +01:00
my $ u = $ self - > find_user ( $ channel , $ hostmask ) ;
2020-01-25 21:28:05 +01:00
if ( not $ u ) {
$ channel = '.*' ;
2020-01-26 06:18:42 +01:00
$ hostmask = "$nick!$user\@" . $ self - > { pbot } - > { antiflood } - > address_to_mask ( $ host ) ;
2020-01-26 04:55:43 +01:00
my $ name = $ nick ;
my ( $ existing_channel , $ existing_hostmask ) = $ self - > find_user_account ( $ channel , $ name ) ;
2020-01-26 21:28:38 +01:00
if ( $ existing_hostmask ne lc $ name ) {
2020-01-26 04:55:43 +01:00
# user exists by name
2020-01-29 03:01:25 +01:00
return "There is already an user account named $name but its hostmask ($existing_hostmask) does not match your hostmask ($hostmask). Ask an admin for help." ;
2020-01-26 04:55:43 +01:00
}
$ u = $ self - > add_user ( $ name , $ channel , $ hostmask , undef , undef , 1 ) ;
2020-01-25 21:28:05 +01:00
$ u - > { loggedin } = 1 ;
2020-01-25 23:31:10 +01:00
$ u - > { stayloggedin } = 1 ;
$ u - > { autologin } = 1 ;
$ self - > save ;
2020-01-25 21:28:05 +01:00
}
2020-01-27 03:53:30 +01:00
my $ result = '' ;
if ( defined $ key ) {
$ key = lc $ key ;
2020-02-06 07:32:58 +01:00
if ( defined $ value ) {
if ( not $ self - > { pbot } - > { capabilities } - > userhas ( $ u , 'can-modify-capabilities' ) ) {
if ( $ key =~ m/^can-/ or $ self - > { pbot } - > { capabilities } - > exists ( $ key ) ) {
return "The $key metadata requires the can-modify-capabilities capability, which your user account does not have." ;
}
}
2020-01-27 03:53:30 +01:00
2020-02-06 07:32:58 +01:00
if ( not $ self - > { pbot } - > { capabilities } - > userhas ( $ u , 'botowner' ) ) {
my @ disallowed = qw/can-modify-admins botowner can-modify-capabilities/ ;
if ( grep { $ _ eq $ key } @ disallowed ) {
return "The $key metadata requires the botowner capability to set, which your user account does not have." ;
}
2020-01-27 03:53:30 +01:00
}
2020-01-25 21:28:05 +01:00
2020-02-06 07:32:58 +01:00
if ( not $ self - > { pbot } - > { capabilities } - > userhas ( $ u , 'admin' ) ) {
my @ disallowed = qw/name autoop autovoice/ ;
if ( grep { $ _ eq $ key } @ disallowed ) {
return "The $key metadata requires the admin capability to set, which your user account does not have." ;
}
2020-02-03 18:50:38 +01:00
}
2020-01-27 03:53:30 +01:00
}
} else {
$ result = "Usage: my <key> [value]; " ;
2020-01-26 01:09:43 +01:00
}
2020-01-29 03:01:25 +01:00
my ( $ found_channel , $ found_hostmask ) = $ self - > find_user_account ( $ channel , $ hostmask ) ;
$ found_channel = $ channel if not defined $ found_channel ; # let DualIndexHashObject disambiguate
$ result . = $ self - > { users } - > set ( $ found_channel , $ found_hostmask , $ key , $ value ) ;
2020-01-25 23:31:10 +01:00
$ result =~ s/^password => .*;?$/password => <private>;/m ;
2020-01-25 21:28:05 +01:00
return $ result ;
}
1 ;