From 46b5b95d54d645b3a935aa8acad5a5d3f80be9b7 Mon Sep 17 00:00:00 2001 From: Pragmatic Software Date: Fri, 11 Jul 2014 12:57:18 +0000 Subject: [PATCH] Escape undesired SQL wildcards in certain statements; add `aka` command to list also-known-as entries --- PBot/MessageHistory.pm | 32 ++++++++- PBot/MessageHistory_SQLite.pm | 129 ++++++++++++++++++++++++++++++---- PBot/VERSION.pm | 2 +- 3 files changed, 149 insertions(+), 14 deletions(-) diff --git a/PBot/MessageHistory.pm b/PBot/MessageHistory.pm index 2ea1a8ab..4ac63df5 100644 --- a/PBot/MessageHistory.pm +++ b/PBot/MessageHistory.pm @@ -46,7 +46,8 @@ sub initialize { $self->{pbot}->{registry}->add_default('text', 'messagehistory', 'max_messages', $conf{max_messages} // 32); - $self->{pbot}->{commands}->register(sub { $self->recall_message(@_) }, "recall", 0); + $self->{pbot}->{commands}->register(sub { $self->recall_message(@_) }, "recall", 0); + $self->{pbot}->{commands}->register(sub { $self->list_also_known_as(@_) }, "aka", 0); $self->{pbot}->{atexit}->register(sub { $self->{database}->end(); return; }); } @@ -61,6 +62,35 @@ sub add_message { $self->{database}->add_message($account, $mask, $channel, { timestamp => scalar gettimeofday, msg => $text, mode => $mode }); } +sub list_also_known_as { + my ($self, $from, $nick, $user, $host, $arguments) = @_; + + if(not length $arguments) { + return "Usage: aka "; + } + + my @akas = $self->{database}->get_also_known_as($arguments); + if(@akas) { + my $result = "$arguments also known as: "; + + my %uniq; + foreach my $aka (@akas) { + my ($nick) = $aka =~ /^([^!]+)!/; + $uniq{$nick} = $nick; + } + + my $sep = ""; + foreach my $aka (sort keys %uniq) { + next if $aka =~ /^Guest\d+$/; + $result .= "$sep$aka"; + $sep = ", "; + } + return $result; + } else { + return "I don't know anybody named $arguments."; + } +} + sub recall_message { my ($self, $from, $nick, $user, $host, $arguments) = @_; diff --git a/PBot/MessageHistory_SQLite.pm b/PBot/MessageHistory_SQLite.pm index a540845c..783493e3 100644 --- a/PBot/MessageHistory_SQLite.pm +++ b/PBot/MessageHistory_SQLite.pm @@ -240,8 +240,9 @@ sub find_message_account_by_nick { my ($self, $nick) = @_; my ($id, $hostmask) = eval { - my $sth = $self->{dbh}->prepare('SELECT id,hostmask FROM Hostmasks WHERE hostmask LIKE ? LIMIT 1'); - $sth->bind_param(1, "$nick!%"); + my $sth = $self->{dbh}->prepare('SELECT id,hostmask FROM Hostmasks WHERE hostmask LIKE ? ESCAPE "\" LIMIT 1'); + my $qnick = quotemeta $nick; + $sth->bind_param(1, "$qnick!%"); $sth->execute(); my $row = $sth->fetchrow_hashref(); return ($row->{id}, $row->{hostmask}); @@ -268,13 +269,14 @@ sub find_message_accounts_by_nickserv { sub find_message_accounts_by_mask { my ($self, $mask) = @_; - $mask =~ s/\*/%/g; - $mask =~ s/\?/_/g; - $mask =~ s/\$.*$//; + my $qmask = quotemeta $mask; + $qmask =~ s/\\\*/%/g; + $qmask =~ s/\\\?/_/g; + $qmask =~ s/\\\$.*$//; my $accounts = eval { - my $sth = $self->{dbh}->prepare('SELECT id FROM Hostmasks WHERE hostmask LIKE ?'); - $sth->bind_param(1, $mask); + my $sth = $self->{dbh}->prepare('SELECT id FROM Hostmasks WHERE hostmask LIKE ? ESCAPE "\"'); + $sth->bind_param(1, $qmask); $sth->execute(); return $sth->fetchall_arrayref(); }; @@ -290,8 +292,9 @@ sub get_message_account { return $id if defined $id; my $rows = eval { - my $sth = $self->{dbh}->prepare('SELECT id,hostmask FROM Hostmasks WHERE hostmask LIKE ? ORDER BY last_seen DESC'); - $sth->bind_param(1, "$nick!%"); + my $sth = $self->{dbh}->prepare('SELECT id,hostmask FROM Hostmasks WHERE hostmask LIKE ? ESCAPE "\" ORDER BY last_seen DESC'); + my $qnick = quotemeta $nick; + $sth->bind_param(1, "$qnick!%"); $sth->execute(); my $rows = $sth->fetchall_arrayref({}); @@ -353,7 +356,7 @@ sub update_hostmask_data { $comma = ', '; } - $sql .= ' WHERE hostmask LIKE ?'; + $sql .= ' WHERE hostmask LIKE ? ESCAPE "\"'; my $sth = $self->{dbh}->prepare($sql); @@ -362,7 +365,8 @@ sub update_hostmask_data { $sth->bind_param($param++, $data->{$key}); } - $sth->bind_param($param, $mask); + my $qmask = quotemeta $mask; + $sth->bind_param($param, $qmask); $sth->execute(); $self->{new_entries}++; }; @@ -632,7 +636,7 @@ sub get_channel_datas_with_enter_abuses { my ($self) = @_; my $channel_datas = eval { - my $sth = $self->{dbh}->prepare('SELECT id, channel, enter_abuses FROM Channels WHERE enter_abuses > 0'); + my $sth = $self->{dbh}->prepare('SELECT id, channel, enter_abuses, last_offense FROM Channels WHERE enter_abuses > 0'); $sth->execute(); return $sth->fetchall_arrayref({}); }; @@ -655,6 +659,107 @@ sub devalidate_all_channels { $self->{pbot}->{logger}->log($@) if $@; } +sub get_also_known_as { + my ($self, $nick) = @_; + + $self->{pbot}->{logger}->log("Looking for AKAs for nick [$nick]\n"); + + my @list = eval { + my %aka_hostmasks; + my $sth = $self->{dbh}->prepare('SELECT hostmask FROM Hostmasks WHERE hostmask LIKE ? ESCAPE "\" ORDER BY last_seen DESC'); + my $qnick = quotemeta $nick; + $sth->bind_param(1, "$qnick!%"); + $sth->execute(); + my $rows = $sth->fetchall_arrayref({}); + + my %hostmasks; + foreach my $row (@$rows) { + $hostmasks{$row->{hostmask}} = $row->{hostmask}; + $self->{pbot}->{logger}->log("Found matching nick for hostmask $row->{hostmask}\n"); + } + + my %ids; + foreach my $hostmask (keys %hostmasks) { + my $id = $self->get_message_account_id($hostmask); + next if exists $ids{$id}; + $ids{$id} = $id; + + $sth = $self->{dbh}->prepare('SELECT hostmask FROM Hostmasks WHERE id == ?'); + $sth->bind_param(1, $id); + $sth->execute(); + $rows = $sth->fetchall_arrayref({}); + + foreach my $row (@$rows) { + $aka_hostmasks{$row->{hostmask}} = $row->{hostmask}; + $self->{pbot}->{logger}->log("Adding matching id AKA hostmask $row->{hostmask}\n"); + $hostmasks{$row->{hostmask}} = $row->{hostmask}; + } + } + + foreach my $hostmask (keys %hostmasks) { + my ($host) = $hostmask =~ /(\@.*)$/; + $sth = $self->{dbh}->prepare('SELECT id FROM Hostmasks WHERE hostmask LIKE ?'); + $sth->bind_param(1, "\%$host"); + $sth->execute(); + $rows = $sth->fetchall_arrayref({}); + + foreach my $row (@$rows) { + next if exists $ids{$row->{id}}; + $ids{$row->{id}} = $row->{id}; + + $sth = $self->{dbh}->prepare('SELECT hostmask FROM Hostmasks WHERE id == ?'); + $sth->bind_param(1, $row->{id}); + $sth->execute(); + my $rows = $sth->fetchall_arrayref({}); + + foreach my $row (@$rows) { + $aka_hostmasks{$row->{hostmask}} = $row->{hostmask}; + $self->{pbot}->{logger}->log("Adding matching host AKA hostmask $row->{hostmask}\n"); + } + } + } + + my %nickservs; + foreach my $id (keys %ids) { + $sth = $self->{dbh}->prepare('SELECT nickserv FROM Nickserv WHERE id == ?'); + $sth->bind_param(1, $id); + $sth->execute(); + $rows = $sth->fetchall_arrayref({}); + + foreach my $row (@$rows) { + $nickservs{$row->{nickserv}} = $row->{nickserv}; + } + } + + foreach my $nickserv (keys %nickservs) { + $sth = $self->{dbh}->prepare('SELECT id FROM Nickserv WHERE nickserv == ?'); + $sth->bind_param(1, $nickserv); + $sth->execute(); + $rows = $sth->fetchall_arrayref({}); + + foreach my $row (@$rows) { + next if exists $ids{$row->{id}}; + $ids{$row->{id}} = $row->{id}; + + $sth = $self->{dbh}->prepare('SELECT hostmask FROM Hostmasks WHERE id == ?'); + $sth->bind_param(1, $row->{id}); + $sth->execute(); + my $rows = $sth->fetchall_arrayref({}); + + foreach my $row (@$rows) { + $aka_hostmasks{$row->{hostmask}} = $row->{hostmask}; + $self->{pbot}->{logger}->log("Adding matching nickserv AKA hostmask $row->{hostmask}\n"); + } + } + } + + return sort keys %aka_hostmasks; + }; + + $self->{pbot}->{logger}->log($@) if $@; + return @list; +} + # End of public API, the remaining are internal support routines for this module sub get_new_account_id { diff --git a/PBot/VERSION.pm b/PBot/VERSION.pm index e4746133..b56d6a25 100644 --- a/PBot/VERSION.pm +++ b/PBot/VERSION.pm @@ -13,7 +13,7 @@ use warnings; # These are set automatically by the build/commit script use constant { BUILD_NAME => "PBot", - BUILD_REVISION => 730, + BUILD_REVISION => 731, BUILD_DATE => "2014-07-11", };