3
0
mirror of https://github.com/pragma-/pbot.git synced 2024-12-23 19:22:40 +01:00

Refactor message account linking

Linking of message accounts is now significantly less likely to produce
false-positives.

Previously, any hostmasks with matching nick!*@* would be strongly linked
together.  This led to falsely-linking accounts, either inadvertently or
intentionally.

For example, Bob might also be known as Bob_ and Bobby,
but primarily uses Bob as his main nick.  Somebody else might join with
Bobby and end up being linked to Bob.  Now both Bob and the new Bobby are
linked together as the same person, but likely with different *!user@host.

Now if the new Bobby ever gets banned, then Bob will also end up being
banned for evading Bobby's ban.

This was a sore spot in the previous linking implementation.

This new implementation has several adjustments to more intelligently link
accounts only when they're proven beyond a reasonable doubt to be the same
person (e.g. by matching nickserv accounts, etc).

Consequently, rather than aggressively linking accounts and catching more
ban-evaders at the risk of potentially falsely-linking accounts and falsely
detecting innocent people as ban-evaders, this new implementation will instead
link accounts more reliably at the risk of potential ban-evaders not yet being
linked together and thus being able to evade a ban.

This is a more preferable and reasonable risk.  Active channel ops should be
able to catch any obnoxious ban-evaders that slip through this net.
This commit is contained in:
Pragmatic Software 2016-08-17 20:34:45 -07:00
parent d77ea29b47
commit 857d1aa0d3
4 changed files with 227 additions and 85 deletions

View File

@ -48,6 +48,7 @@ sub initialize {
$self->{channels} = {}; # per-channel statistics, e.g. for optimized tracking of last spoken nick for enter-abuse detection, etc
$self->{nickflood} = {}; # statistics to track nickchange flooding
$self->{whois_pending} = {}; # prevents multiple whois for nick joining multiple channels at once
$self->{changinghost} = {}; # tracks nicks changing hosts/identifying to strongly link them
my $filename = delete $conf{whitelist_file} // $self->{pbot}->{registry}->get_value('general', 'data_dir') . '/whitelist';
$self->{whitelist} = PBot::DualIndexHashObject->new(name => 'Whitelist', filename => $filename);
@ -128,8 +129,8 @@ sub whitelist {
return "Usage: whitelist <command>, where commands are: list/show, add, remove, set, unset" if not defined $command;
given($command) {
when($_ eq "list" or $_ eq "show") {
given ($command) {
when ($_ eq "list" or $_ eq "show") {
my $text = "Whitelist:\n";
my $entries = 0;
foreach my $channel (keys %{ $self->{whitelist}->hash }) {
@ -147,7 +148,7 @@ sub whitelist {
$text .= "none" if $entries == 0;
return $text;
}
when("set") {
when ("set") {
my ($channel, $mask, $flag, $value) = split / /, $args, 4;
return "Usage: whitelist set <channel> <mask> [flag] [value]" if not defined $channel or not defined $mask;
@ -188,7 +189,7 @@ sub whitelist {
$self->{whitelist}->save;
return "Flag set.";
}
when("unset") {
when ("unset") {
my ($channel, $mask, $flag) = split / /, $args, 3;
return "Usage: whitelist unset <channel> <mask> <flag>" if not defined $channel or not defined $mask or not defined $flag;
@ -208,7 +209,7 @@ sub whitelist {
$self->{whitelist}->save;
return "Flag unset.";
}
when("add") {
when ("add") {
my ($channel, $mask, $mode) = split / /, $args, 3;
return "Usage: whitelist add <channel> <mask> [mode (user or ban, default: user)]" if not defined $channel or not defined $mask;
@ -225,7 +226,7 @@ sub whitelist {
$self->{whitelist}->save;
return "$mask whitelisted in channel $channel";
}
when("remove") {
when ("remove") {
my ($channel, $mask) = split / /, $args, 2;
return "Usage: whitelist remove <channel> <mask>" if not defined $channel or not defined $mask;
@ -287,8 +288,39 @@ sub check_flood {
$channel = lc $channel;
my $mask = "$nick!$user\@$host";
my $account = $self->{pbot}->{messagehistory}->get_message_account($nick, $user, $host);
my $oldnick = $nick;
my $account;
if ($mode == $self->{pbot}->{messagehistory}->{MSG_JOIN} and exists $self->{changinghost}->{$nick}) {
$self->{pbot}->{logger}->log("Finalizing changinghost for $nick!\n");
$account = delete $self->{changinghost}->{$nick};
my $id = $self->{pbot}->{messagehistory}->{database}->get_message_account_id($mask);
if (defined $id) {
if ($id != $account) {
$self->{pbot}->{logger}->log("Linking $mask [$id] to account $account\n");
$self->{pbot}->{messagehistory}->{database}->unlink_alias($account, $id);
$self->{pbot}->{messagehistory}->{database}->link_alias($account, $id, $self->{pbot}->{messagehistory}->{database}->{alias_type}->{STRONG});
} else {
$self->{pbot}->{logger}->log("New hostmask already belongs to original account.\n");
}
$account = $id;
} else {
$self->{pbot}->{logger}->log("Adding $mask to account $account\n");
$self->{pbot}->{messagehistory}->{database}->add_message_account($mask, $account, $self->{pbot}->{messagehistory}->{database}->{alias_type}->{STRONG});
}
$self->{pbot}->{messagehistory}->{database}->devalidate_all_channels($account);
my @nickserv_accounts = $self->{pbot}->{messagehistory}->{database}->get_nickserv_accounts($account);
foreach my $nickserv_account (@nickserv_accounts) {
$self->{pbot}->{logger}->log("$nick!$user\@$host [$account] seen with nickserv account [$nickserv_account]\n");
$self->check_nickserv_accounts($nick, $nickserv_account, "$nick!$user\@$host");
}
} else {
$account = $self->{pbot}->{messagehistory}->get_message_account($nick, $user, $host);
}
$self->{pbot}->{messagehistory}->{database}->update_hostmask_data($mask, { last_seen => scalar gettimeofday });
if($mode == $self->{pbot}->{messagehistory}->{MSG_NICKCHANGE}) {
$self->{pbot}->{logger}->log(sprintf("%-18s | %-65s | %s\n", "NICKCHANGE", $mask, $text));
@ -319,6 +351,12 @@ sub check_flood {
}
$self->{pbot}->{messagehistory}->{database}->devalidate_all_channels($account);
if ($text eq 'QUIT Changing host') {
$self->{pbot}->{logger}->log("$mask [$account] changing host!\n");
$self->{changinghost}->{$nick} = $account;
}
# don't do flood processing for QUIT events
return;
}
@ -591,6 +629,7 @@ sub unbanme {
foreach my $alias (keys %aliases) {
next if $aliases{$alias}->{type} == $self->{pbot}->{messagehistory}->{database}->{alias_type}->{WEAK};
next if $aliases{$alias}->{nickchange} == 1;
my ($anick, $auser, $ahost) = $alias =~ m/([^!]+)!([^@]+)@(.*)/;
my $banmask = address_to_mask($ahost);
@ -654,9 +693,9 @@ sub address_to_mask {
if($address =~ m/^([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)$/) {
my ($a, $b, $c, $d) = ($1, $2, $3, $4);
given($a) {
when($_ <= 127) { $banmask = "$a.*"; }
when($_ <= 191) { $banmask = "$a.$b.*"; }
given ($a) {
when ($_ <= 127) { $banmask = "$a.*"; }
when ($_ <= 191) { $banmask = "$a.$b.*"; }
default { $banmask = "$a.$b.$c.*"; }
}
} elsif($address =~ m{^gateway/([^/]+)/([^/]+)/}) {
@ -709,7 +748,7 @@ sub check_bans {
my $current_nickserv_account = $self->{pbot}->{messagehistory}->{database}->get_current_nickserv_account($message_account);
if ($current_nickserv_account) {
if (defined $current_nickserv_account and length $current_nickserv_account) {
$self->{pbot}->{logger}->log("anti-flood: [check-bans] current nickserv [$current_nickserv_account] found for $mask\n") if $debug_checkban >= 2;
my $channel_data = $self->{pbot}->{messagehistory}->{database}->get_channel_data($message_account, $channel, 'validated');
if ($channel_data->{validated} & $self->{NEEDS_CHECKBAN}) {

View File

@ -344,7 +344,7 @@ sub on_nickchange {
my $newnick_account = $self->{pbot}->{messagehistory}->{database}->get_message_account($newnick, $user, $host, $nick);
$self->{pbot}->{messagehistory}->{database}->devalidate_all_channels($newnick_account, $self->{pbot}->{antiflood}->{NEEDS_CHECKBAN});
$self->{pbot}->{messagehistory}->{database}->update_hostmask_data($newnick_account, { last_seen => scalar gettimeofday });
$self->{pbot}->{messagehistory}->{database}->update_hostmask_data("$newnick!$user\@$host", { last_seen => scalar gettimeofday });
$self->{pbot}->{antiflood}->check_flood("$nick!$user\@$host", $nick, $user, $host, "NICKCHANGE $newnick",
$self->{pbot}->{registry}->get_value('antiflood', 'nick_flood_threshold'),

View File

@ -176,6 +176,8 @@ sub list_also_known_as {
} else {
$result .= "$sep$aka";
}
$result .= "!" if $akas{$aka}->{nickchange} == 1;
$result .= " ($akas{$aka}->{nickserv})" if $show_nickserv and exists $akas{$aka}->{nickserv};
$result .= " {$akas{$aka}->{gecos}}" if $show_gecos and exists $akas{$aka}->{gecos};

View File

@ -12,6 +12,7 @@ use DBI;
use Carp qw(shortmess);
use Time::HiRes qw(gettimeofday);
use Text::CSV;
use Text::Levenshtein qw/fastdistance/;
sub new {
if(ref($_[1]) eq 'HASH') {
@ -82,7 +83,8 @@ sub begin {
CREATE TABLE IF NOT EXISTS Hostmasks (
hostmask TEXT PRIMARY KEY UNIQUE,
id INTEGER,
last_seen NUMERIC
last_seen NUMERIC,
nickchange INTEGER
)
SQL
@ -210,7 +212,12 @@ sub get_current_nickserv_account {
my $sth = $self->{dbh}->prepare('SELECT nickserv FROM Accounts WHERE id = ?');
$sth->bind_param(1, $id);
$sth->execute();
return $sth->fetchrow_hashref()->{'nickserv'};
my $row = $sth->fetchrow_hashref();
if (defined $row) {
return $row->{'nickserv'};
} else {
return undef;
}
};
$self->{pbot}->{logger}->log($@) if $@;
return $nickserv;
@ -281,33 +288,42 @@ sub update_gecos {
}
sub add_message_account {
my ($self, $mask, $link_id) = @_;
my ($self, $mask, $link_id, $link_type) = @_;
my $id;
if(defined $link_id) {
if(defined $link_id and $link_type == $self->{alias_type}->{STRONG}) {
$id = $link_id;
} else {
$id = $self->get_new_account_id();
$self->{pbot}->{logger}->log("Got new account id $id\n");
}
eval {
my $sth = $self->{dbh}->prepare('INSERT INTO Hostmasks VALUES (?, ?, 0)');
my $sth = $self->{dbh}->prepare('INSERT INTO Hostmasks VALUES (?, ?, 0, 0)');
$sth->bind_param(1, $mask);
$sth->bind_param(2, $id);
$sth->execute();
$self->{new_entries}++;
if(not defined $link_id) {
if((not defined $link_id) || ((defined $link_id) && ($link_type == $self->{alias_type}->{WEAK}))) {
$sth = $self->{dbh}->prepare('INSERT INTO Accounts VALUES (?, ?, ?)');
$sth->bind_param(1, $id);
$sth->bind_param(2, $mask);
$sth->bind_param(3, "");
$sth->execute();
$self->{new_entries}++;
$self->{pbot}->{logger}->log("Added new account $id for mask $mask\n");
}
};
$self->{pbot}->{logger}->log($@) if $@;
if (defined $link_id && $link_type == $self->{alias_type}->{WEAK}) {
$self->{pbot}->{logger}->log("Weakly linking $id to $link_id\n");
$self->link_alias($id, $link_id, $link_type);
}
return $id;
}
@ -361,54 +377,138 @@ sub find_message_accounts_by_mask {
}
sub get_message_account {
my ($self, $nick, $user, $host, $link_nick) = @_;
my ($self, $nick, $user, $host, $orig_nick) = @_;
=cut
use Devel::StackTrace;
my $trace = Devel::StackTrace->new(indent => 1, ignore_class => ['PBot::PBot', 'PBot::IRC']);
$self->{pbot}->{logger}->log("get_message_account stacktrace: " . $trace->as_string() . "\n");
=cut
my $mask = "$nick!$user\@$host";
my $id = $self->get_message_account_id($mask);
return $id if defined $id;
my $rows = eval {
my $sth = $self->{dbh}->prepare('SELECT id, hostmask FROM Hostmasks WHERE hostmask LIKE ? ESCAPE "\" ORDER BY last_seen DESC');
my $do_nothing = 0;
my ($rows, $link_type) = eval {
my $sth = $self->{dbh}->prepare('SELECT id, hostmask, last_seen FROM Hostmasks WHERE hostmask LIKE ? ESCAPE "\" ORDER BY last_seen DESC');
if (defined $orig_nick) {
my $orig_id = $self->get_message_account_id("$orig_nick!$user\@$host");
my $qnick = quotemeta $nick;
$qnick =~ s/_/\\_/g;
$sth->bind_param(1, "$qnick!%");
$sth->execute();
my $rows = $sth->fetchall_arrayref({});
if (not defined $rows->[0]) {
$rows->[0] = { id => $orig_id, hostmask => "$orig_nick!$user\@$host" };
return ($rows, $self->{alias_type}->{STRONG});
}
foreach my $row (@$rows) {
$self->{pbot}->{logger}->log("Found matching nickchange account: [$row->{id}] $row->{hostmask}\n");
if ($row->{id} == $orig_id || $row->{hostmask} =~ m/^.*!\Q$nick\E\@\Q$host\E$/i) {
$self->{pbot}->{logger}->log("Using this match.\n");
$rows->[0] = { id => $orig_id, hostmask => $row->{hostmask} };
return ($rows, $self->{alias_type}->{STRONG});
}
}
$self->{pbot}->{logger}->log("Creating new nickchange account!\n");
my $new_id = $self->add_message_account($mask);
$self->link_alias($orig_id, $new_id, $self->{alias_type}->{WEAK});
$self->update_hostmask_data($mask, { nickchange => 1 });
$do_nothing = 1;
$rows->[0] = { id => $new_id };
return ($rows, 0);
}
if ($host =~ m{^gateway/web/irccloud.com}) {
$sth->bind_param(1, "%!$user\@gateway/web/irccloud.com/%");
$sth->execute();
my $rows = $sth->fetchall_arrayref({});
if (defined $rows->[0]) {
return $rows;
return ($rows, $self->{alias_type}->{STRONG});
}
}
my $qnick = quotemeta (defined $link_nick ? $link_nick : $nick);
if ($host =~ m{^nat/([^/]+)/}) {
$sth->bind_param(1, "$nick!$user\@nat/$1/%");
$sth->execute();
my $rows = $sth->fetchall_arrayref({});
if (defined $rows->[0]) {
return ($rows, $self->{alias_type}->{STRONG});
}
}
if ($host =~ m{^unaffiliated/}) {
$sth->bind_param(1, "%\@$host");
$sth->execute();
my $rows = $sth->fetchall_arrayref({});
if (defined $rows->[0]) {
return ($rows, $self->{alias_type}->{STRONG});
}
}
my $link_type = $self->{alias_type}->{WEAK};
my $qnick = quotemeta $nick;
$qnick =~ s/_/\\_/g;
$sth->bind_param(1, "$qnick!%");
$sth->execute();
my $rows = $sth->fetchall_arrayref({});
=cut
foreach my $row (@$rows) {
$self->{pbot}->{logger}->log("Found matching nick $row->{hostmask} with id $row->{id}\n");
}
=cut
my ($thost) = $row->{hostmask} =~ m/@(.*)$/;
if(not defined $rows->[0]) {
if ($thost =~ m{(^unaffiliated|/staff/|/member/)} and $host ne $thost) {
$self->{pbot}->{logger}->log("Skipping non-matching cloaked hosts: $host vs $thost\n");
next;
}
my $distance = fastdistance($host, $thost);
my $length = (length($host) > length($thost)) ? length $host : length $thost;
$self->{pbot}->{logger}->log("distance: " . ($distance / $length) . " -- $host vs $thost\n") if $length != 0;
if ($length != 0 && $distance / $length < 0.50) {
$rows->[0] = $row;
return ($rows, $self->{alias_type}->{STRONG});
}
}
if (not defined $rows->[0]) {
$link_type = $self->{alias_type}->{STRONG};
$sth->bind_param(1, "%!$user\@$host");
$sth->execute();
$rows = $sth->fetchall_arrayref({});
if (defined $rows->[0] and gettimeofday - $rows->[0]->{last_seen} > 60 * 60 * 48) {
$link_type = $self->{alias_type}->{WEAK};
$self->{pbot}->{logger}->log("Longer than 48 hours (" . (gettimeofday - $rows->[0]->{last_seen}) . ") for $rows->[0]->{hostmask} for $nick!$user\@$host, degrading to weak link\n");
}
=cut
foreach my $row (@$rows) {
$self->{pbot}->{logger}->log("Found matching user\@host mask $row->{hostmask} with id $row->{id}\n");
}
=cut
}
return $rows;
return ($rows, $link_type);
};
$self->{pbot}->{logger}->log($@) if $@;
if(defined $rows->[0]) {
$self->{pbot}->{logger}->log("message-history: [get-account] $nick!$user\@$host linked to $rows->[0]->{hostmask} with id $rows->[0]->{id}\n");
$self->add_message_account("$nick!$user\@$host", $rows->[0]->{id});
return $rows->[0]->{id} if $do_nothing;
if (defined $rows->[0]) {
$self->{pbot}->{logger}->log("message-history: [get-account] $nick!$user\@$host " . ($link_type == $self->{alias_type}->{WEAK} ? "weakly linked to" : "added to account") . " $rows->[0]->{hostmask} with id $rows->[0]->{id}\n");
$self->add_message_account("$nick!$user\@$host", $rows->[0]->{id}, $link_type);
$self->devalidate_all_channels($rows->[0]->{id});
$self->update_hostmask_data("$nick!$user\@$host", { last_seen => scalar gettimeofday });
my @nickserv_accounts = $self->get_nickserv_accounts($rows->[0]->{id});
@ -906,9 +1006,9 @@ sub link_aliases {
my %ids;
if ($hostmask) {
my ($host) = $hostmask =~ /(\@.*)$/;
my ($nick, $host) = $hostmask =~ /^([^!]+)![^@]+@(.*)$/;
my $sth = $self->{dbh}->prepare('SELECT id, last_seen FROM Hostmasks WHERE hostmask LIKE ?');
$sth->bind_param(1, "\%$host");
$sth->bind_param(1, "\%\@$host");
$sth->execute();
my $rows = $sth->fetchall_arrayref({});
@ -916,27 +1016,41 @@ sub link_aliases {
foreach my $row (@$rows) {
if ($now - $row->{last_seen} <= 60 * 60 * 48) {
$ids{$row->{id}} = { id => $row->{id}, type => $self->{alias_type}->{STRONG} };
$self->{pbot}->{logger}->log("found STRONG matching id $row->{id} for host [$host]\n") if $debug_link;
$ids{$row->{id}} = { id => $row->{id}, type => $self->{alias_type}->{STRONG}, force => 1 };
$self->{pbot}->{logger}->log("found STRONG matching id $row->{id} for host [$host]\n") if $debug_link && $row->{id} != $account;
} else {
$ids{$row->{id}} = { id => $row->{id}, type => $self->{alias_type}->{WEAK} };
$self->{pbot}->{logger}->log("found WEAK matching id $row->{id} for host [$host]\n") if $debug_link;
$self->{pbot}->{logger}->log("found WEAK matching id $row->{id} for host [$host]\n") if $debug_link && $row->{id} != $account;
}
}
my ($nick) = $hostmask =~ m/([^!]+)/;
unless ($nick =~ m/^Guest\d+$/) {
my $qnick = quotemeta $nick;
$qnick =~ s/_/\\_/g;
my $sth = $self->{dbh}->prepare('SELECT id FROM Hostmasks WHERE hostmask LIKE ? ESCAPE "\"');
my $sth = $self->{dbh}->prepare('SELECT id, hostmask FROM Hostmasks WHERE hostmask LIKE ? ESCAPE "\"');
$sth->bind_param(1, "$qnick!%");
$sth->execute();
my $rows = $sth->fetchall_arrayref({});
foreach my $row (@$rows) {
$ids{$row->{id}} = { id => $row->{id}, type => $self->{alias_type}->{STRONG} };
$self->{pbot}->{logger}->log("found STRONG matching id $row->{id} for nick [$qnick]\n") if $debug_link;
next if $row->{id} == $account;
my ($thost) = $row->{hostmask} =~ m/@(.*)$/;
if ($thost =~ m{(^unaffiliated|/staff/|/member/)} and $host ne $thost) {
$self->{pbot}->{logger}->log("Skipping non-matching cloaked hosts: $host vs $thost\n");
next;
}
my $distance = fastdistance($host, $thost);
my $length = (length($host) > length($thost)) ? length $host : length $thost;
$self->{pbot}->{logger}->log("distance: " . ($distance / $length) . " -- $host vs $thost\n") if $length != 0;
if ($length != 0 && $distance / $length < 0.50) {
$ids{$row->{id}} = { id => $row->{id}, type => $self->{alias_type}->{STRONG} }; # don't force linking
$self->{pbot}->{logger}->log("found STRONG matching id $row->{id} for nick [$qnick]\n") if $debug_link;
}
}
}
}
@ -948,15 +1062,24 @@ sub link_aliases {
my $rows = $sth->fetchall_arrayref({});
foreach my $row (@$rows) {
$ids{$row->{id}} = { id => $row->{id}, type => $self->{alias_type}->{STRONG} };
$self->{pbot}->{logger}->log("found STRONG matching id $row->{id} for nickserv [$nickserv]\n") if $debug_link;
$ids{$row->{id}} = { id => $row->{id}, type => $self->{alias_type}->{STRONG}, force => 1 };
$self->{pbot}->{logger}->log("found STRONG matching id $row->{id} for nickserv [$nickserv]\n") if $debug_link && $row->{id} != $account;
}
}
my $sth = $self->{dbh}->prepare('REPLACE INTO Aliases (id, alias, type) VALUES (?, ?, ?)');
#my $sth = $self->{dbh}->prepare('REPLACE INTO Aliases (id, alias, type) VALUES (?, ?, ?)');
foreach my $id (sort keys %ids) {
next if $account == $id;
if (exists $ids{$id}->{force}) {
$self->{pbot}->{logger}->log("Forcing link of $account and $id!\n");
$self->unlink_alias($account, $id);
}
$self->link_alias($account, $id, $ids{$id}->{type});
=cut
$sth->bind_param(1, $account);
$sth->bind_param(2, $id);
$sth->bind_param(3, $ids{$id}->{type});
@ -974,6 +1097,7 @@ sub link_aliases {
$self->{pbot}->{logger}->log("Linked $id to $account [$ids{$id}->{type}]\n") if $debug_link;
$self->{new_entries}++;
}
=cut
}
};
$self->{pbot}->{logger}->log($@) if $@;
@ -982,55 +1106,32 @@ sub link_aliases {
sub link_alias {
my ($self, $id, $alias, $type) = @_;
my $ret = eval {
my $ret = 0;
$self->{pbot}->{logger}->log("Attempting to " . ($type == $self->{alias_type}->{STRONG} ? "strongly" : "weakly") . " link $id to $alias\n");
my $sth = $self->{dbh}->prepare('INSERT INTO Aliases SELECT ?, ?, ? WHERE NOT EXISTS (SELECT 1 FROM Aliases WHERE id = ? AND alias = ?)');
my $ret = eval {
my $sth = $self->{dbh}->prepare('SELECT * FROM Aliases WHERE id = ? AND alias = ? LIMIT 1');
$sth->bind_param(1, $alias);
$sth->bind_param(2, $id);
$sth->execute();
my $row = $sth->fetchrow_hashref();
if (defined $row) {
$self->{pbot}->{logger}->log("$id already " . ($row->{'type'} == $self->{alias_type}->{STRONG} ? "strongly" : "weakly") . " linked to $alias, ignoring\n");
return 0;
}
$sth = $self->{dbh}->prepare('INSERT INTO Aliases VALUES (?, ?, ?)');
$sth->bind_param(1, $alias);
$sth->bind_param(2, $id);
$sth->bind_param(3, $type);
$sth->bind_param(4, $alias);
$sth->bind_param(5, $id);
$sth->execute();
if ($sth->rows) {
$self->{new_entries}++;
$ret = 1;
} else {
$sth = $self->{dbh}->prepare('UPDATE Aliases SET type = ? WHERE id = ? AND alias = ?');
$sth->bind_param(1, $type);
$sth->bind_param(2, $id);
$sth->bind_param(3, $alias);
$sth->execute();
if ($sth->rows) {
$self->{new_entries}++;
$ret = 1;
}
}
$sth = $self->{dbh}->prepare('INSERT INTO Aliases SELECT ?, ?, ? WHERE NOT EXISTS (SELECT 1 FROM Aliases WHERE id = ? AND alias = ?)');
$sth->bind_param(1, $id);
$sth->bind_param(2, $alias);
$sth->bind_param(3, $type);
$sth->bind_param(4, $id);
$sth->bind_param(5, $alias);
$sth->execute();
if ($sth->rows) {
$self->{new_entries}++;
$ret = 1;
} else {
$sth = $self->{dbh}->prepare('UPDATE Aliases SET type = ? WHERE id = ? AND alias = ?');
$sth->bind_param(1, $type);
$sth->bind_param(2, $alias);
$sth->bind_param(3, $id);
$sth->execute();
if ($sth->rows) {
$self->{new_entries}++;
$ret = 1;
} else {
$ret = 0;
}
}
return $ret;
$self->{pbot}->{logger}->log("Linked.\n");
return 1;
};
$self->{pbot}->{logger}->log($@) if $@;
return $ret;
@ -1166,7 +1267,7 @@ sub get_also_known_as {
last if not $new_aliases;
}
my $hostmask_sth = $self->{dbh}->prepare('SELECT hostmask FROM Hostmasks WHERE id = ?');
my $hostmask_sth = $self->{dbh}->prepare('SELECT hostmask, nickchange FROM Hostmasks WHERE id = ?');
my $nickserv_sth = $self->{dbh}->prepare('SELECT nickserv FROM Nickserv WHERE id = ?');
my $gecos_sth = $self->{dbh}->prepare('SELECT gecos FROM Gecos WHERE id = ?');
@ -1178,7 +1279,7 @@ sub get_also_known_as {
$rows = $hostmask_sth->fetchall_arrayref({});
foreach my $row (@$rows) {
$akas{$row->{hostmask}} = { hostmask => $row->{hostmask}, id => $id, alias => $ids{$id}->{id}, type => $ids{$id}->{type} };
$akas{$row->{hostmask}} = { hostmask => $row->{hostmask}, id => $id, alias => $ids{$id}->{id}, type => $ids{$id}->{type}, nickchange => $row->{nickchange} };
$self->{pbot}->{logger}->log("[$id] Adding hostmask $row->{hostmask} -> $ids{$id}->{id} [type $ids{$id}->{type}]\n") if $debug;
}