summaryrefslogtreecommitdiffstats
path: root/lib/ASM/Commander.pm
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ASM/Commander.pm')
-rw-r--r--lib/ASM/Commander.pm338
1 files changed, 180 insertions, 158 deletions
diff --git a/lib/ASM/Commander.pm b/lib/ASM/Commander.pm
index edad532..6de19fd 100644
--- a/lib/ASM/Commander.pm
+++ b/lib/ASM/Commander.pm
@@ -1,6 +1,7 @@
package ASM::Commander;
no autovivification;
+use v5.10;
use warnings;
use strict;
use IO::All;
@@ -8,45 +9,60 @@ use POSIX qw(strftime);
use Data::Dumper;
use URI::Escape;
use ASM::Shortener;
+use Const::Fast;
no if $] >= 5.017011, warnings => 'experimental::smartmatch';
+const my $secret => 'flag_secret';
+const my $hilights => 'flag_hilights';
+const my $admin => 'flag_admin';
+const my $plugin => 'flag_plugin';
+const my $debug => 'flag_debug';
+
+const my %letter_to_flag => (
+ s => $secret,
+ h => $hilights,
+ a => $admin,
+ p => $plugin,
+ d => $debug,
+);
+
+const my %flag_to_letter => reverse(%letter_to_flag);
+
my $cmdtbl = {
'^;wallop' => {
- 'flag' => 'd',
+ 'flag' => $debug,
'cmd' => \&cmd_wallop },
'^;;addwebuser (?<pass>.{6,})' => {
- 'flag' => 's',
+ 'flag' => $secret,
+ 'txn' => 1,
'cmd' => \&cmd_addwebuser },
- '^;delwebuser (?<user>\S+)' => {
- 'flag' => 'a',
- 'cmd' => \&cmd_delwebuser },
'^;teredo (?<ip>\S+)' => {
'cmd' => \&cmd_teredo },
'^;status$' => {
'cmd' => \&cmd_status },
'^;mship (?<nick>\S+)' => {
- 'flag' => 's',
+ 'flag' => $secret,
'cmd' => \&cmd_mship },
'^;source$' => {
'cmd' => \&cmd_source },
'^;monitor (?<chan>\S+) *$' => {
- 'flag' => 's',
+ 'flag' => $secret,
'cmd' => \&cmd_monitor },
'^;monitor (?<chan>\S+) ?(?<switch>yes|no)$' => {
- 'flag' => 'a',
+ 'flag' => $admin,
'cmd' => \&cmd_monitor2 },
'^;suppress (?<chan>\S+) *$' => {
- 'flag' => 's',
+ 'flag' => $secret,
'cmd' => \&cmd_suppress },
'^;unsuppress (?<chan>\S+) *$' => {
- 'flag' => 's',
+ 'flag' => $secret,
'cmd' => \&cmd_unsuppress },
'^;silence (?<chan>\S+) *$' => {
- 'flag' => 's',
+ 'flag' => $secret,
'cmd' => \&cmd_silence },
'^;silence (?<chan>\S+) (?<switch>yes|no) *$' => {
- 'flag' => 'a',
+ 'flag' => $admin,
'cmd' => \&cmd_silence2 },
'^;help$' => {
'cmd' => \&cmd_help },
@@ -57,89 +73,88 @@ my $cmdtbl = {
'^;query (\S+) ?(\S+)?$' => {
'cmd' => \&cmd_query },
'^;investigate (?<nick>\S+) *$' => {
- 'flag' => 's',
+ 'flag' => $secret,
'cmd' => \&cmd_investigate },
'^;investigate2 (?<nick>\S+) ?(?<skip>\d*) *$' => {
- 'flag' => 's',
+ 'flag' => $secret,
'cmd' => \&cmd_investigate2 },
- '^;userx? add (?<account>\S+) (?<flags>\S+)$' => {
- 'flag' => 'a',
- 'cmd' => \&cmd_user_add },
+ '^;userx? (?:add|flags) (?<account>\S+) (?<flags>\S+)$' => {
+ 'flag' => $admin,
+ 'txn' => 1,
+ 'cmd' => \&cmd_user_set_flags },
'^;userx? flags (?<account>\S+) ?$' => {
- 'cmd' => \&cmd_user_flags },
- '^;userx? flags (?<account>\S+) (?<flags>\S+)$' => {
- 'flag' => 'a',
- 'cmd' => \&cmd_user_flags2 },
+ 'cmd' => \&cmd_user_get_flags },
'^;userx? del (?<account>\S+)$' => {
- 'flag' => 'a',
+ 'flag' => $admin,
+ 'txn' => 1,
'cmd' => \&cmd_user_del },
'^;target (?<chan>\S+) (?<nickchan>\S+) ?(?<level>[a-z]*)$' => {
- 'flag' => 'a',
+ 'flag' => $admin,
'cmd' => \&cmd_target },
'^;detarget (?<chan>\S+) (?<nickchan>\S+)' => {
- 'flag' => 'a',
+ 'flag' => $admin,
'cmd' => \&cmd_detarget },
'^;showhilights (?<nick>\S+) *$' => {
- 'flag' => 'h',
+ 'flag' => $hilights,
'cmd' => \&cmd_showhilights },
'^;hilight (?<chan>\S+) (?<nicks>\S+) ?(?<level>[a-z]*)$' => {
- 'flag' => 'h',
+ 'flag' => $hilights,
'cmd' => \&cmd_hilight },
'^;dehilight (?<chan>\S+) (?<nicks>\S+)' => {
- 'flag' => 'h',
+ 'flag' => $hilights,
'cmd' => \&cmd_dehilight },
'^;join (?<chan>\S+)' => {
- 'flag' => 'a',
+ 'flag' => $admin,
'cmd' => \&cmd_join },
'^;part (?<chan>\S+)' => {
- 'flag' => 'a',
+ 'flag' => $admin,
'cmd' => \&cmd_part },
'^;sl (?<string>.+)' => {
- 'flag' => 'd',
+ 'flag' => $debug,
'cmd' => \&cmd_sl },
'^;quit ?(?<reason>.*)' => {
- 'flag' => 'a',
+ 'flag' => $admin,
'cmd' => \&cmd_quit },
'^;ev (?<string>.*)' => {
- 'flag' => 'd',
+ 'flag' => $debug,
'cmd' => \&cmd_ev },
'^;rehash$' => {
- 'flag' => 'a',
+ 'flag' => $admin,
'cmd' => \&cmd_rehash },
'^;restrict (?<type>nick|account|host) (?<who>\S+) (?<mode>\+|-)(?<restriction>[a-z0-9_-]+)$' => {
- 'flag' => 'a',
+ 'flag' => $admin,
'cmd' => \&cmd_restrict },
'^\s*\!ops ?(?<chan>#\S+)? ?(?<reason>.*)' => {
'nohush' => 'nohush',
'cmd' => \&cmd_ops },
'^;blacklist (?<string>.+)' => {
- 'flag' => 's',
+ 'flag' => $secret,
'cmd' => \&cmd_blacklist },
'^;blacklistpcre (?<string>.+)' => {
- 'flag' => 'a',
+ 'flag' => $admin,
'cmd' => \&cmd_blacklistpcre },
'^;unblacklist (?<id>[0-9a-f]+)$' => {
- 'flag' => 's',
+ 'flag' => $secret,
'cmd' => \&cmd_unblacklist },
'^;plugin (?<chan>\S+) (?<risk>\S+) (?<reason>.*)' => {
- 'flag' => 'p',
+ 'flag' => $plugin,
'cmd' => \&cmd_plugin },
'^;sync (?<chan>\S+)' => {
- 'flag' => 'a',
+ 'flag' => $admin,
'cmd' => \&cmd_sync },
'^;ping\s*$' => {
'cmd' => \&cmd_ping },
'^;ping (?<string>\S.*)$' => {
- 'flag' => 's',
+ 'flag' => $secret,
'cmd' => \&cmd_ping2 },
'^;blreason (?<id>[0-9a-f]+) (?<reason>.*)' => {
- 'flag' => 's',
+ 'flag' => $secret,
'cmd' => \&cmd_blreason },
'^;bllookup (?<id>[0-9a-f]+)$' => {
- 'flag' => 's',
+ 'flag' => $secret,
'cmd' => \&cmd_bllookup },
'^;falsematch\b' => {
- 'flag' => 's',
+ 'flag' => $secret,
'cmd' => \&cmd_falsematch },
'^;version$' => {
'cmd' => \&cmd_version },
@@ -174,34 +189,50 @@ sub command {
unless ( (ASM::Util->speak($event->{to}->[0])) ) {
next unless (defined($self->{cmdtbl}->{$command}->{nohush}));
}
- if (defined($self->{cmdtbl}->{$command}->{flag})) { #If the command is restricted,
- if (!defined($acct)) {
- $fail = 1;
- }
- elsif (!defined($::users->{person}->{$acct})) { #make sure the requester has an account
- $fail = 1;
- }
- elsif (!defined($::users->{person}->{$acct}->{flags})) { #make sure the requester has flags defined
- $fail = 1;
- }
- elsif (!(grep {$_ eq $self->{cmdtbl}->{$command}->{flag}} split('', $::users->{person}->{$acct}->{flags}))) { #make sure the requester has the needed flags
- $fail = 1;
- }
- }
if ($cmd=~/$command/) {
my $where = $event->{to}[0];
if (index($where, '#') == -1) {
$where = 'PM';
}
ASM::Util->dprint("$event->{from} told me in $where: $cmd", "commander");
+
if (!ASM::Util->notRestricted($nick, "nocommands")) {
$fail = 1;
}
- if ($fail == 1) {
- $conn->privmsg($nick, "You don't have permission to use that command, or you're not signed into nickserv.");
- } else {
- &{$self->{cmdtbl}->{$command}->{cmd}}($conn, $event);
+
+ my $check_and_run_command = sub {
+ # If the command is restricted,
+ if ( my $flag = $self->{cmdtbl}->{$command}->{flag} ) {
+ # require an account
+ if (!defined($acct)) {
+ $fail = 1;
+ }
+ else {
+ # and check for the flag
+ my $user = $::db->resultset('User')->by_name($acct);
+ if (!defined $user || !$user->$flag) {
+ $fail = 1;
+ }
+ }
+ }
+
+ if ($fail == 1) {
+ $conn->privmsg($nick, "You don't have permission to use that command, or you're not signed into nickserv.");
+ } else {
+ &{$self->{cmdtbl}->{$command}->{cmd}}($conn, $event);
+ }
+ };
+
+ # Do we need to wrap the entire command - including the permission check - in a transaction?
+ # Be careful; due to re-establishing a DB connection, this requires the command's code to
+ # be idempotent. See the DBIx::Class::Storage documentation on the txn_do method for details.
+ if ($self->{cmdtbl}{$command}{txn}) {
+ $::db->txn_do($check_and_run_command);
}
+ else {
+ $check_and_run_command->();
+ }
+
last;
}
}
@@ -234,30 +265,12 @@ sub cmd_addwebuser {
$conn->privmsg($event->replyto, "This command must be used in PM. Try again WITH A DIFFERENT PASSWORD!");
return;
}
- use Apache::Htpasswd; use Apache::Htgroup;
- my $o_Htpasswd = new Apache::Htpasswd({passwdFile => $::settings->{web}->{userfile}, UseMD5 => 1});
- my $o_Htgroup = new Apache::Htgroup($::settings->{web}->{groupfile});
- my $user = lc $::sn{lc $event->{nick}}->{account};
- $o_Htpasswd->htDelete($user);
- $o_Htpasswd->htpasswd($user, $pass);
- $o_Htpasswd->writeInfo($user, strftime("%F %T", gmtime));
- $o_Htgroup->adduser($user, 'actionlogs');
- $o_Htgroup->save();
- $conn->privmsg($event->replyto, "Added $user to the list of authorized web users.")
-}
-
-sub cmd_delwebuser {
- my ($conn, $event) = @_;
+ my $user = $::db->resultset('User')->by_name(lc $::sn{lc $event->{nick}}->{account});
+ $user->passphrase($pass);
+ $user->update;
- my $user = lc $+{user};
- use Apache::Htpasswd;
- use Apache::Htgroup;
- my $o_Htpasswd = new Apache::Htpasswd({passwdFile => $::settings->{web}->{userfile}, UseMD5 => 1});
- my $o_Htgroup = new Apache::Htgroup($::settings->{web}->{groupfile});
- $o_Htpasswd->htDelete($user);
- $o_Htgroup->deleteuser($user, 'actionlogs');
- $o_Htgroup->save();
- $conn->privmsg($event->replyto, "Removed $user from the list of authorized web users.")
+ my $name = $user->name;
+ $conn->privmsg($event->replyto, "Added $name to the list of authorized web users.")
}
sub cmd_teredo {
@@ -594,40 +607,89 @@ sub cmd_investigate2 {
$conn->privmsg($event->nick, "Only 10 results are shown at a time. For more, do ;investigate2 $nick " . ($skip+1) . '.');
}
-sub cmd_user_add {
+sub get_user_flagstring {
+ my ($user) = @_;
+
+ my $string = '';
+
+ for my $letter (sort keys %letter_to_flag) {
+ my $flag = $letter_to_flag{$letter};
+
+ $string .= $letter if $user->$flag;
+ }
+
+ return $string;
+}
+
+sub set_user_flagstring {
+ my ($user, $string) = @_;
+
+ while (my ($letter, $flag) = each %letter_to_flag) {
+ if (index($string, $letter) != -1) {
+ $user->$flag(1);
+ }
+ else {
+ $user->$flag(0);
+ }
+ }
+}
+
+sub is_flagstring_superset {
+ my ($super, $sub) = @_;
+ for my $letter (split //, $sub) {
+ return 0 if index($super, $letter) == -1;
+ }
+ return 1;
+}
+
+sub cmd_user_set_flags {
my ($conn, $event) = @_;
my $nick = lc $+{account};
my $account;
my $flags = $+{flags};
- my %hasflagshash = ();
- foreach my $item (split(//, $::users->{person}->{lc $::sn{lc $event->{nick}}->{account}}->{flags})) {
- $hasflagshash{$item} = 1;
+
+ # we need to be idempotent if interrupted halfway.
+ # TODO: this is rather ugly / error-prone.
+ state $sent_message = 0;
+
+ if ( (defined($::sn{$nick}->{account})) && ( ($account = lc $::sn{$nick}->{account}) ne $nick ) ) {
+ $conn->privmsg($event->replyto, "I'm assuming you mean " . $nick . "'s nickserv account, " . $account . '.')
+ if !($sent_message++);
+ $nick = $account;
}
- foreach my $flag (split(//, $flags)) {
- if (!defined($hasflagshash{$flag})) {
- $conn->privmsg($event->replyto, "You can't give a flag you don't already have.");
- return;
- }
+
+ my $giver = $::db->resultset('User')->by_name( lc $::sn{lc $event->{nick}}{account} );
+
+ my $own_flags = get_user_flagstring($giver);
+
+ if (!is_flagstring_superset($own_flags, $flags)) {
+ $conn->privmsg($event->replyto, "You can't give a flag you don't already have.");
+ $sent_message = 0;
+ return;
}
if ($flags =~ /d/) {
- $conn->privmsg($event->replyto, "The d flag may not be assigned over IRC. Edit the configuration manually.");
+ $conn->privmsg($event->replyto, "The d flag may not be assigned over IRC. Edit the database manually.");
+ $sent_message = 0;
return;
}
- if ( (defined($::sn{$nick}->{account})) && ( ($account = lc $::sn{$nick}->{account}) ne $nick ) ) {
- $conn->privmsg($event->replyto, "I'm assuming you mean " . $nick . "'s nickserv account, " . $account . '.');
- $nick = $account;
- }
- if (defined($::users->{person}->{$nick})) {
- $conn->privmsg($event->replyto, "The user $nick already exists. Use ;user flags $nick $flags to set their flags");
+
+ my $target = $::db->resultset('User')->by_name_or_new( $nick );
+
+ if ($target->flag_debug) {
+ $conn->privmsg($event->replyto, "Users with the 'd' flag are untouchable. Edit the database manually.");
+ $sent_message = 0;
return;
}
- $::users->{person}->{$nick} = { 'flags' => $flags };
- ASM::Config->writeUsers();
+
+ set_user_flagstring($target, $flags);
+ $target->update_or_insert;
+
+ $sent_message = 0;
$conn->privmsg($event->replyto, "Flags for NickServ account $nick set to $flags");
}
-sub cmd_user_flags {
+sub cmd_user_get_flags {
my ($conn, $event) = @_;
my $nick = lc $+{account};
@@ -637,72 +699,32 @@ sub cmd_user_flags {
$nick = $account;
}
my $sayNick = substr($nick, 0, 1) . "\x02\x02" . substr($nick, 1);
- if (defined($::users->{person}->{$nick}->{flags})) {
- $conn->privmsg($event->replyto, "Flags for $sayNick: $::users->{person}->{$nick}->{flags}");
- } else {
- $conn->privmsg($event->replyto, "$sayNick has no flags");
- }
-}
-sub cmd_user_flags2 {
- my ($conn, $event) = @_;
+ my $user = $::db->resultset('User')->by_name($nick);
- my $nick = lc $+{account};
- my $flags = $+{flags};
- my $account;
- my %hasflagshash = ();
- foreach my $item (split(//, $::users->{person}->{lc $::sn{lc $event->{nick}}->{account}}->{flags})) {
- $hasflagshash{$item} = 1;
+ if (defined $user and length( my $flags = get_user_flagstring($user) )) {
+ $conn->privmsg($event->replyto, "Flags for $sayNick: $flags");
}
- foreach my $flag (split(//, $flags)) {
- if (!defined($hasflagshash{$flag})) {
- $conn->privmsg($event->replyto, "You can't give a flag you don't already have.");
- return;
- }
- }
- if ($flags =~ /d/) {
- $conn->privmsg($event->replyto, "The d flag may not be assigned over IRC. Edit the configuration manually.");
- return;
- }
- if ( (defined($::sn{$nick}->{account})) && ( ($account = lc $::sn{$nick}->{account}) ne $nick ) ) {
- $conn->privmsg($event->replyto, "I'm assuming you mean " . $nick . "'s nickserv account, " . $account . '.');
- $nick = $account;
- }
- if (defined($::users->{person}->{$nick}) &&
- defined($::users->{person}->{$nick}->{flags}) &&
- ($::users->{person}->{$nick}->{flags} =~ /d/)) {
- return $conn->privmsg($event->replyto, "Users with the 'd' flag are untouchable. Edit the config file manually.");
- }
- if ($flags !~ /s/) {
- use Apache::Htpasswd; use Apache::Htgroup;
- my $o_Htpasswd = new Apache::Htpasswd({passwdFile => $::settings->{web}->{userfile}, UseMD5 => 1});
- my $o_Htgroup = new Apache::Htgroup($::settings->{web}->{groupfile});
- $o_Htpasswd->htDelete($nick);
- $o_Htgroup->deleteuser($nick, 'actionlogs');
- $o_Htgroup->save();
+ else {
+ $conn->privmsg($event->replyto, "$sayNick has no flags");
}
- $::users->{person}->{$nick}->{flags} = $flags;
- ASM::Config->writeUsers();
- $conn->privmsg($event->replyto, "Flags for $nick set to $flags");
}
sub cmd_user_del {
my ($conn, $event) = @_;
my $nick = lc $+{account};
- if (defined($::users->{person}->{$nick}) &&
- defined($::users->{person}->{$nick}->{flags}) &&
- ($::users->{person}->{$nick}->{flags} =~ /d/)) {
- return $conn->privmsg($event->replyto, "Users with the 'd' flag are untouchable. Edit the config file manually.");
+
+ my $target = $::db->resultset('User')->by_name($nick);
+ if (!defined $target) {
+ $conn->privmsg($event->replyto, "I know no user by that name. Make sure you specified the account name.");
+ return;
+ }
+ if ($target->flag_debug) {
+ $conn->privmsg($event->replyto, "Users with the 'd' flag are untouchable. Edit the database manually.");
+ return;
}
- delete($::users->{person}->{$nick});
- ASM::Config->writeUsers();
- use Apache::Htpasswd; use Apache::Htgroup;
- my $o_Htpasswd = new Apache::Htpasswd({passwdFile => $::settings->{web}->{userfile}, UseMD5 => 1});
- my $o_Htgroup = new Apache::Htgroup($::settings->{web}->{groupfile});
- $o_Htpasswd->htDelete($nick);
- $o_Htgroup->deleteuser($nick, 'actionlogs');
- $o_Htgroup->save();
+ $target->delete;
$conn->privmsg($event->replyto, "Removed $nick from authorized users." .
" MAKE SURE YOU PROVIDED a nickserv account to this command, rather than an altnick of the accountholder");
}