summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatarDoug Freed <dwfreed@mtu.edu>2015-12-04 12:12:28 +0400
committerLibravatarDoug Freed <dwfreed@mtu.edu>2015-12-04 12:12:28 +0400
commit0c1b6cc2808b4fd45779cce4835a6a80eae48265 (patch)
tree39cc7ca32ea5f39c1977d1fdd2147b160e278e9b
Initial commit
-rwxr-xr-xcgi-bin/query.pl130
-rw-r--r--cgi-bin/secret/.htaccess6
-rwxr-xr-xcgi-bin/secret/investigate.pl203
-rwxr-xr-xcgi-bin/secret/logs.pl34
-rwxr-xr-xcgi-bin/showUsers.pl75
-rw-r--r--checks.txt28
-rw-r--r--index.html37
-rw-r--r--investigate.html18
-rw-r--r--query.html50
-rw-r--r--syntax.txt175
10 files changed, 756 insertions, 0 deletions
diff --git a/cgi-bin/query.pl b/cgi-bin/query.pl
new file mode 100755
index 0000000..4a9ed26
--- /dev/null
+++ b/cgi-bin/query.pl
@@ -0,0 +1,130 @@
+#!/usr/bin/perl
+
+#use warnings;
+use Data::Dumper;
+use strict;
+use DBI;
+
+use CGI_Lite;
+
+my $dbh = DBI->connect("DBI:mysql:database=asm_main;host=localhost;port=3306", 'USER', 'PASSWORD');
+
+my $debug = 0;
+
+sub esc
+{
+ my ($arg) = @_;
+ $arg = $dbh->quote($arg);
+ $arg =~ s/\*/%/g;
+ $arg =~ s/_/\\_/g;
+ $arg =~ s/\?/_/g;
+ return $arg;
+}
+
+my $cgi = new CGI_Lite;
+my %data = $cgi->parse_form_data;
+
+$debug = int($data{debug}) if (defined($data{debug}));
+
+if ($debug) {
+ print "Content-type: text/plain", "\n\n";
+ print Dumper(\%data);
+} else {
+ print "Content-type: text/html", "\n\n";
+ print "<html><head><title>Query results</title></head><body>\n";
+}
+
+my ($channel, $nick, $user, $host);
+my ($level, $id, $reason);
+
+my $qry = "SELECT time, channel, nick, user, host, gecos, level, id, reason FROM alertlog WHERE ";
+
+if (defined($data{channel})) {
+ $qry = $qry . "channel like " . esc($data{channel});
+} else { die "channel not defined!\n"; }
+
+if (defined($data{nick}) && ($data{nick} ne "*") && ($data{nick} ne "")) {
+ $qry .= " and nick like " . esc($data{nick});
+}
+
+if (defined($data{user}) && ($data{user} ne "*") && ($data{user} ne "")) {
+ $qry .= " and user like " . esc($data{user});
+}
+
+if (defined($data{host}) && ($data{host} ne "*") && ($data{host} ne "")) {
+ $qry .= " and host like " . esc($data{host});
+}
+
+if (defined($data{gecos}) && ($data{gecos} ne "*") && ($data{gecos} ne "")) {
+ $qry .= " and gecos like " . esc($data{gecos});
+}
+
+if (defined($data{since})) {
+ $qry .= sprintf("and time > '%04d-%02d-%02d %02d:%02d:%02d'",
+ int($data{syear}), int($data{smonth}), int($data{sday}),
+ int($data{shour}), int($data{smin}), int($data{ssec}));
+}
+
+if (defined($data{before})) {
+ $qry .= sprintf("and time < '%04d-%02d-%02d %02d:%02d:%02d'",
+ int($data{byear}), int($data{bmonth}), int($data{bday}),
+ int($data{bhour}), int($data{bmin}), int($data{bsec}));
+}
+
+#if (defined($data{id})) {
+# $qry .= " and id = " . $dbh->quote($data{id});
+#}
+
+if (defined($data{level}) && ($data{level} ne "any")) {
+ $qry .= " and level = " . $dbh->quote($data{level});
+}
+
+if (defined($data{reason})) {
+ $qry .= " and reason like " . esc($data{reason});
+}
+
+if (defined($data{sort}) && defined($data{order}) && ($data{order} =~ /^[ad]$/ ) &&
+ ( $data{sort} =~ /^(time|nick|user|host|level|id|reason)$/ ) ) {
+ $qry .= " order by " . $data{sort};
+ $qry .= " desc" if $data{order} eq "d";
+}
+
+if ($debug) {
+ print "Querying: ";
+ print Dumper($qry);
+}
+
+my $sth = $dbh->prepare($qry);
+$sth->execute;
+my $names = $sth->{'NAME'};
+my $numFields = $sth->{'NUM_OF_FIELDS'};
+
+ print "<table border=\"1\"><tr>" unless $debug;
+
+for (my $i = 0; $i < $numFields; $i++) {
+ if ($debug) {
+ printf("%s%s", $i ? "," : "", $$names[$i]);
+ } else {
+ print "<th>" . $$names[$i] . "</th>";
+ }
+}
+
+print "</tr>" unless $debug;
+print "\n";
+
+while (my $ref = $sth->fetchrow_arrayref) {
+ print "<tr>" unless $debug;
+
+ for (my $i = 0; $i < $numFields; $i++) {
+ if ($debug) {
+ printf("%s%s", $i ? "," : "", $$ref[$i]);
+ } else {
+ print "<td>" . $$ref[$i] . "</td>";
+ }
+ }
+ print "</tr>" unless $debug;
+ print "\n";
+}
+unless ($debug) {
+ print "</table></body></html>";
+}
diff --git a/cgi-bin/secret/.htaccess b/cgi-bin/secret/.htaccess
new file mode 100644
index 0000000..14ea91d
--- /dev/null
+++ b/cgi-bin/secret/.htaccess
@@ -0,0 +1,6 @@
+AuthType Basic
+AuthName "Restricted Files"
+AuthUserFile /home/icxcnika/AntiSpamMeta/HTTP_ACCESS_USER
+AuthGroupFile /home/icxcnika/AntiSpamMeta/HTTP_ACCESS_GROUP
+Require group actionlogs
+#Require user icxcnika
diff --git a/cgi-bin/secret/investigate.pl b/cgi-bin/secret/investigate.pl
new file mode 100755
index 0000000..0716480
--- /dev/null
+++ b/cgi-bin/secret/investigate.pl
@@ -0,0 +1,203 @@
+#!/usr/bin/perl
+
+#use warnings;
+use Data::Dumper;
+use strict;
+use DBI;
+
+use CGI_Lite;
+
+my $dbh = DBI->connect("DBI:mysql:database=asm_main;host=localhost;port=3306", 'USER', 'PASSWORD');
+
+my $debug = 0;
+
+sub esc
+{
+ my ($arg) = @_;
+ $arg = $dbh->quote($arg);
+ $arg =~ s/\*/%/g;
+ $arg =~ s/_/\\_/g;
+ $arg =~ s/\?/_/g;
+ return $arg;
+}
+
+sub dottedQuadToInt
+{
+ my ($dottedquad) = @_;
+ my $ip_number = 0;
+ my @octets = split(/\./, $dottedquad);
+ foreach my $octet (@octets) {
+ $ip_number <<= 8;
+ $ip_number |= $octet;
+ }
+ return $ip_number;
+}
+
+my $cgi = new CGI_Lite;
+my %data = $cgi->parse_form_data;
+
+$debug = int($data{debug}) if (defined($data{debug}));
+
+if ( !defined($data{query}) ) {
+print "Content-type: text/html", "\n\n";
+print <<HTML;
+<html>
+ <head>
+ <title>AntiSpamMeta database query page</title>
+ </head>
+ <body>
+ <h3>Maintaining AntiSpamMeta takes work! Please
+<form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_top">
+<input type="hidden" name="cmd" value="_s-xclick">
+<input type="hidden" name="encrypted" value="-----BEGIN PKCS7-----MIIHLwYJKoZIhvcNAQcEoIIHIDCCBxwCAQExggEwMIIBLAIBADCBlDCBjjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtQYXlQYWwgSW5jLjETMBEGA1UECxQKbGl2ZV9jZXJ0czERMA8GA1UEAxQIbGl2ZV9hcGkxHDAaBgkqhkiG9w0BCQEWDXJlQHBheXBhbC5jb20CAQAwDQYJKoZIhvcNAQEBBQAEgYBTERkX6i0KluB0FD1F4tVcuUb79bnGJt+Zj3IcRi2cang3aID+FX0yG0+Ewv+43xGRdidASfXzk6gDx1ZT4TZbTsMCe1Q6Och+Cf+tEfTlhLRNS3dorcBunr1KOctWnMOV61g3CZu7470LmRAxexjTyDNpCRe4UAjKeW/gUbs2XTELMAkGBSsOAwIaBQAwgawGCSqGSIb3DQEHATAUBggqhkiG9w0DBwQISBjLqHYZWuKAgYjJvzf4GJw7NWKKAmAUnEEcBMSlG0RlDp2MHSq5PbW6M79d4PCNHjekXYhSluMjXPk/oH3t5A1cJ0iXTuk2BwVNRJZHdZ78weeDatVpV794kOJ5xg/TQX2ckzdrcvsNMeMkykuh32/XEQN1sDJxOv0ydtzPHS+5Cm0D2qD/NEnZ8h9KDtIkIesboIIDhzCCA4MwggLsoAMCAQICAQAwDQYJKoZIhvcNAQEFBQAwgY4xCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChMLUGF5UGFsIEluYy4xEzARBgNVBAsUCmxpdmVfY2VydHMxETAPBgNVBAMUCGxpdmVfYXBpMRwwGgYJKoZIhvcNAQkBFg1yZUBwYXlwYWwuY29tMB4XDTA0MDIxMzEwMTMxNVoXDTM1MDIxMzEwMTMxNVowgY4xCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChMLUGF5UGFsIEluYy4xEzARBgNVBAsUCmxpdmVfY2VydHMxETAPBgNVBAMUCGxpdmVfYXBpMRwwGgYJKoZIhvcNAQkBFg1yZUBwYXlwYWwuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDBR07d/ETMS1ycjtkpkvjXZe9k+6CieLuLsPumsJ7QC1odNz3sJiCbs2wC0nLE0uLGaEtXynIgRqIddYCHx88pb5HTXv4SZeuv0Rqq4+axW9PLAAATU8w04qqjaSXgbGLP3NmohqM6bV9kZZwZLR/klDaQGo1u9uDb9lr4Yn+rBQIDAQABo4HuMIHrMB0GA1UdDgQWBBSWn3y7xm8XvVk/UtcKG+wQ1mSUazCBuwYDVR0jBIGzMIGwgBSWn3y7xm8XvVk/UtcKG+wQ1mSUa6GBlKSBkTCBjjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtQYXlQYWwgSW5jLjETMBEGA1UECxQKbGl2ZV9jZXJ0czERMA8GA1UEAxQIbGl2ZV9hcGkxHDAaBgkqhkiG9w0BCQEWDXJlQHBheXBhbC5jb22CAQAwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQCBXzpWmoBa5e9fo6ujionW1hUhPkOBakTr3YCDjbYfvJEiv/2P+IobhOGJr85+XHhN0v4gUkEDI8r2/rNk1m0GA8HKddvTjyGw/XqXa+LSTlDYkqI8OwR8GEYj4efEtcRpRYBxV8KxAW93YDWzFGvruKnnLbDAF6VR5w/cCMn5hzGCAZowggGWAgEBMIGUMIGOMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDU1vdW50YWluIFZpZXcxFDASBgNVBAoTC1BheVBhbCBJbmMuMRMwEQYDVQQLFApsaXZlX2NlcnRzMREwDwYDVQQDFAhsaXZlX2FwaTEcMBoGCSqGSIb3DQEJARYNcmVAcGF5cGFsLmNvbQIBADAJBgUrDgMCGgUAoF0wGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMTQwNDIzMTYwMjQ3WjAjBgkqhkiG9w0BCQQxFgQUYjCdOhMR2kAw/gwZCNqiNV2A7sIwDQYJKoZIhvcNAQEBBQAEgYCTkxFvVlBxZQhZpkJUtqr+Ig7OasMsAreBPkeSZl0BhNTbTet+1Tt0KnMacAGrj3u+eHvGb6gkq2XSXQg5Us65R4stt6jCx7MmuRu9kWc3PErXfZtDbrRORAi+ZlIwxBg2f6n5IInAR4oWPOLwqAXy9gNxkJMHp5oe2pGYjfVHuQ==-----END PKCS7-----
+">
+<input type="image" src="https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif" border="0" name="submit" alt="PayPal - The safer, easier way to pay online!">
+<img alt="" border="0" src="https://www.paypalobjects.com/en_US/i/scr/pixel.gif" width="1" height="1">
+</form></h3>
+ <p>Matching is done based on field1 OR field2 OR field3 etc. Wildcards are supported,
+except for the realIP field, which must be blank or an IPv4 dotted quad.</p>
+ <form action="/cgi-bin/secret/investigate.pl" method="get">
+ <input type="hidden" name="query" value="1" />
+HTML
+print ' Nickname: <input type="text" name="nick" ' . (defined($data{nick}) ? 'value="'.$data{nick}.'" ' : '') . "/><br />\n";
+print ' User: <input type="text" name="user" ' . (defined($data{user}) ? 'value="'.$data{user}.'" ' : '') . "/><br />\n";
+print ' Hostname: <input type="text" name="host" ' . (defined($data{host}) ? 'value="'.$data{host}.'" ' : '') . "/><br />\n";
+print ' Gecos: <input type="text" name="gecos" ' . (defined($data{gecos}) ? 'value="'.$data{gecos}.'" ' : '') . "/><br />\n";
+print ' Account: <input type="text" name="account" ' . (defined($data{account}) ? 'value="'.$data{account}.'" ' : '') . "/><br />\n";
+print ' Real IP: <input type="text" name="realip" ' . (defined($data{realip}) ? 'value="'.$data{realip}.'" ' : '') . "/>\n";
+print <<HTML;
+ <br /><br /><input type="submit" value="Query!" />
+ </form>
+ </body>
+</html>
+HTML
+exit 0;
+}
+
+if ($debug) {
+ print "Content-type: text/plain", "\n\n";
+ print Dumper(\%data);
+} else {
+ print "Content-type: text/html", "\n\n";
+ print <<HTML;
+<html>
+<head>
+<title>Query results</title>
+<style>
+tr {font-size:90%;}
+.uhg {font-size:60%;}
+.desc {font-size:90%;}
+.time {font-size:80%;}
+.action {}
+.nick {}
+</style>
+</head>
+<body>
+HTML
+}
+
+
+##Queryable items:
+## nick, user, host, realip, gecos, account
+my $qry = 'SELECT * FROM actionlog WHERE ';
+
+if (defined($data{nick}) && ($data{nick} ne "*") && ($data{nick} ne "")) {
+ $qry .= " nick like " . esc($data{nick}) . ' or ';
+}
+
+if (defined($data{user}) && ($data{user} ne "*") && ($data{user} ne "")) {
+ $qry .= ' user like ' . esc($data{user}) . ' or ';
+}
+
+if (defined($data{host}) && ($data{host} ne "*") && ($data{host} ne "")) {
+ $qry .= ' host like ' . esc($data{host}) . ' or ';
+}
+
+if (defined($data{gecos}) && ($data{gecos} ne "*") && ($data{gecos} ne "")) {
+ $qry .= ' gecos like ' . esc($data{gecos}) . ' or ';
+}
+
+if (defined($data{account}) && ($data{account} ne "*") && ($data{account} ne "")) {
+ $qry .= ' account like ' . esc($data{account}) . ' or ';
+}
+
+if (defined($data{realip}) && ($data{realip} =~ /^\d+\.\d+\.\d+\.\d+$/)) {
+ $qry .= ' ip = ' . dottedQuadToInt($data{realip}) . ' or ';
+}
+
+$qry .= '(1 = 0)'; # rather than trying to get rid of a trailing 'or '
+
+if ($debug) {
+ print "Querying: ";
+ print Dumper($qry);
+}
+
+my $sth = $dbh->prepare($qry);
+$sth->execute;
+my $names = $sth->{'NAME'};
+my $numFields = $sth->{'NUM_OF_FIELDS'};
+
+#fields are index,time,action,reason,channel,nick,user,host,ip,gecos,account,bynick,byuser,byhost,bygecos,byaccount
+my %f = (
+ "index" => 0,
+ "time" => 1,
+ "action" => 2,
+ "reason" => 3,
+ "channel" => 4,
+ "nick" => 5,
+ "user" => 6,
+ "host" => 7,
+ "ip" => 8,
+ "gecos" => 9,
+ "account" => 10,
+ "bynick" => 11,
+ "byuser" => 12,
+ "byhost" => 13,
+ "bygecos" => 14,
+ "byaccount" => 15
+);
+
+print "<table border=\"0\">" unless $debug;
+if ($debug) {
+ for (my $i = 0; $i < $numFields; $i++) {
+ printf("%s%s", $i ? "," : "", $$names[$i]);
+ }
+}
+#print "</tr>" unless $debug;
+print "\n";
+
+while (my $ref = $sth->fetchrow_arrayref) {
+#fields are index,time,action,reason,channel,nick,user,host,ip,gecos,account,bynick,byuser,byhost,bygecos,byaccount
+ unless ($debug) {
+ print '<tr>';
+ print '<td><a href="logs.pl?index=' . $$ref[$f{'index'}] . '" class="index">#' . $$ref[$f{'index'}] . ':</a></td>';
+ print '<td nowrap="nowrap" class="time">' . $$ref[$f{'time'}] . '</td>';
+
+ print '<td nowrap="nowrap"><span class="nick">' . $$ref[$f{'nick'}] . '</span>';
+ print '<span class="uhg">!' . $$ref[$f{'user'}] . '@' . $$ref[$f{'host'}] . ' (' . $$ref[$f{'gecos'}] . ')';
+ print ' [' . $$ref[$f{'account'}] . ']' if ($$ref[$f{'account'}] ne '');
+ print '</span>';
+ print ' <span class="desc">received</span> <span class="action">' . $$ref[$f{'action'}] . '</span>';
+ print ' (' . $$ref[$f{'reason'}] . ')' if ($$ref[$f{'reason'}] ne '');
+ print ' <span class="desc">on</span> ' . $$ref[$f{'channel'}] if ($$ref[$f{'channel'}] ne '');
+ print ' ';
+# print '</td>';
+# print '<td>';
+ if ($$ref[$f{'bynick'}] ne '') {
+ print '<span class="desc">by</span> ' . $$ref[$f{'bynick'}];
+ print '<span class="uhg">!' . $$ref[$f{'byuser'}] . '@' . $$ref[$f{'byhost'}] . ' (' . $$ref[$f{'bygecos'}] . ')';
+ print ' [' . $$ref[$f{'byaccount'}] . ']' if ($$ref[$f{'byaccount'}] ne '');
+ print '</span>';
+ }
+ print '</td>';
+ print '</tr>';
+ } else {
+ for (my $i = 0; $i < $numFields; $i++) {
+ printf("%s%s", $i ? "," : "", $$ref[$i]);
+ }
+ }
+ print "\n";
+}
+unless ($debug) {
+ print "</table></body></html>";
+}
diff --git a/cgi-bin/secret/logs.pl b/cgi-bin/secret/logs.pl
new file mode 100755
index 0000000..419f450
--- /dev/null
+++ b/cgi-bin/secret/logs.pl
@@ -0,0 +1,34 @@
+#!/usr/bin/perl
+
+#use warnings;
+use Data::Dumper;
+use strict;
+use DBI;
+
+use CGI_Lite;
+my $cgi = new CGI_Lite;
+my %data = $cgi->parse_form_data;
+my $index = $data{index};
+print "Content-type: text/plain", "\n\n";
+if ( !defined($index) ) {
+ print "Nice hax!\n";
+ exit 0;
+}
+$index = int $index;
+
+if ( $index < 50000) {
+ my $block;
+ $block = "50K" if $index < 50000;
+ $block = "40K" if $index < 40000;
+ $block = "30K" if $index < 30000;
+ $block = "20K" if $index < 20000;
+ $block = "10K" if $index < 10000;
+ print "tar -Oxf /var/www/actionlogs/$block.tar.gz $index.txt\n\n";
+ print `tar -Oxf /var/www/actionlogs/$block.tar.gz $index.txt`;
+} elsif ( -e "/var/www/actionlogs/$index.txt.lzma" ) {
+ print `lzcat /var/www/actionlogs/$index.txt.lzma`;
+} elsif ( -e "/var/www/actionlogs/$index.txt" ) {
+ print `cat /var/www/actionlogs/$index.txt`;
+} else {
+ print "u wot m8?\n";
+}
diff --git a/cgi-bin/showUsers.pl b/cgi-bin/showUsers.pl
new file mode 100755
index 0000000..913ee22
--- /dev/null
+++ b/cgi-bin/showUsers.pl
@@ -0,0 +1,75 @@
+#!/usr/bin/perl
+
+#use warnings;
+use Data::Dumper;
+use strict;
+use DBI;
+use XML::Simple qw(:strict);
+
+
+print "Content-type: text/html", "\n\n";
+print <<HTML;
+<html>
+ <head>
+ <title>AntiSpamMeta User List</title>
+ </head>
+ <body>
+ <h3>Maintaining AntiSpamMeta takes work! Please
+<form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_top">
+<input type="hidden" name="cmd" value="_s-xclick">
+<input type="hidden" name="encrypted" value="-----BEGIN PKCS7-----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-----END PKCS7-----
+">
+<input type="image" src="https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif" border="0" name="submit" alt="PayPal - The safer, easier way to pay online!">
+<img alt="" border="0" src="https://www.paypalobjects.com/en_US/i/scr/pixel.gif" width="1" height="1">
+</form></h3>
+<table>
+<tr><th>NickServ account</th><th>h</th><th>s</th><th>a</th><th>d</th></tr>
+HTML
+
+my $xs1 = XML::Simple->new( KeyAttr => ['id'], Cache => [ qw/memcopy/ ]);
+my $users = $xs1->XMLin( "/home/icxcnika/AntiSpamMeta/config-main/users.xml", ForceArray => 'person');
+
+sub printout
+{
+ my ($user) = @_;
+ print "<tr><td style=\"text-align:right\">$user</td>";
+ print "<td>";
+ print "x" if (index($users->{person}->{$user}->{flags}, 'h') != -1);
+ print "</td>";
+ print "<td>";
+ print "x" if (index($users->{person}->{$user}->{flags}, 's') != -1);
+ print "</td>";
+ print "<td>";
+ print "x" if (index($users->{person}->{$user}->{flags}, 'a') != -1);
+ print "</td>";
+ print "<td>";
+ print "x" if (index($users->{person}->{$user}->{flags}, 'd') != -1);
+ print "</td>";
+ print "</tr>\n";
+}
+
+foreach my $user (keys %{$users->{person}}) {
+ if (index($users->{person}->{$user}->{flags}, 'd') != -1) {
+ printout($user);
+ delete $users->{person}->{$user};
+ }
+}
+foreach my $user (keys %{$users->{person}}) {
+ if (index($users->{person}->{$user}->{flags}, 'a') != -1) {
+ printout($user);
+ delete $users->{person}->{$user};
+ }
+}
+foreach my $user (keys %{$users->{person}}) {
+ if (index($users->{person}->{$user}->{flags}, 's') != -1) {
+ printout($user);
+ delete $users->{person}->{$user}
+ }
+}
+
+foreach my $user (keys %{$users->{person}}) {
+ printout($user);
+}
+print "</table></body></html>";
+
+exit 0;
diff --git a/checks.txt b/checks.txt
new file mode 100644
index 0000000..fed5951
--- /dev/null
+++ b/checks.txt
@@ -0,0 +1,28 @@
+Overall alert system workings:
+There are 6 alert levels. In order of least significant -> most significant, they are:
+
+debug: New rules that are in need of testing. These can be extremely useful, or extremely spammy.
+info: This is not meant to alert about a malicious action, rather to be a "heads up, keep an eye out because..."
+low: These are various rules that may have some false positives. The importance of response time to these varies.
+medium: These are rules that are not likely to be a false positive, and very likely mean the channel should receive immediate attention.
+high: These are rules with a 99% certainty that a user is attempting to be malicious.
+"opalert": The bot will never show an "opalert risk threat", rather, this level indicates who to ping when someone calls !ops
+
+A user set to be pinged for any given level will be pinged for all higher levels.
+For example, if a user is set to be pinged for "low", they'll be pinged for "opalert", but not "info".
+
+In no particular order, the bot:
+
+* Checks for what it thinks is a botnet cycling in a channel to spam
+* Checks for nicks that join just to spam something and then leave
+* Checks for various kinds of flooding - even distributed over multiple nicks - and has anti-anti-detection measures
+* Checks for ascii-art pasting
+* Checks channel messages against a large array of blacklisted strings
+* Detects several IRC exploits
+* Detects channel-ctcps (mostly deprecated thanks to cmode +C)
+* Detects channel-notices
+* Checks channel messages against a few regexes that are always spammy
+* Detects some phishing attempts
+* Detects some types of attempted ban evasion
+* Detects some malicious shorturls
+
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..696fabe
--- /dev/null
+++ b/index.html
@@ -0,0 +1,37 @@
+<html><head><title>AntiSpamMeta homepage</title>
+</head>
+<body>
+
+<p><h4>AntiSpamMeta is written in my free time. Feel free to give me more monies.<br />
+<form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_top">
+<input type="hidden" name="cmd" value="_s-xclick">
+<input type="hidden" name="encrypted" value="-----BEGIN PKCS7-----MIIHLwYJKoZIhvcNAQcEoIIHIDCCBxwCAQExggEwMIIBLAIBADCBlDCBjjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtQYXlQYWwgSW5jLjETMBEGA1UECxQKbGl2ZV9jZXJ0czERMA8GA1UEAxQIbGl2ZV9hcGkxHDAaBgkqhkiG9w0BCQEWDXJlQHBheXBhbC5jb20CAQAwDQYJKoZIhvcNAQEBBQAEgYBTERkX6i0KluB0FD1F4tVcuUb79bnGJt+Zj3IcRi2cang3aID+FX0yG0+Ewv+43xGRdidASfXzk6gDx1ZT4TZbTsMCe1Q6Och+Cf+tEfTlhLRNS3dorcBunr1KOctWnMOV61g3CZu7470LmRAxexjTyDNpCRe4UAjKeW/gUbs2XTELMAkGBSsOAwIaBQAwgawGCSqGSIb3DQEHATAUBggqhkiG9w0DBwQISBjLqHYZWuKAgYjJvzf4GJw7NWKKAmAUnEEcBMSlG0RlDp2MHSq5PbW6M79d4PCNHjekXYhSluMjXPk/oH3t5A1cJ0iXTuk2BwVNRJZHdZ78weeDatVpV794kOJ5xg/TQX2ckzdrcvsNMeMkykuh32/XEQN1sDJxOv0ydtzPHS+5Cm0D2qD/NEnZ8h9KDtIkIesboIIDhzCCA4MwggLsoAMCAQICAQAwDQYJKoZIhvcNAQEFBQAwgY4xCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChMLUGF5UGFsIEluYy4xEzARBgNVBAsUCmxpdmVfY2VydHMxETAPBgNVBAMUCGxpdmVfYXBpMRwwGgYJKoZIhvcNAQkBFg1yZUBwYXlwYWwuY29tMB4XDTA0MDIxMzEwMTMxNVoXDTM1MDIxMzEwMTMxNVowgY4xCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChMLUGF5UGFsIEluYy4xEzARBgNVBAsUCmxpdmVfY2VydHMxETAPBgNVBAMUCGxpdmVfYXBpMRwwGgYJKoZIhvcNAQkBFg1yZUBwYXlwYWwuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDBR07d/ETMS1ycjtkpkvjXZe9k+6CieLuLsPumsJ7QC1odNz3sJiCbs2wC0nLE0uLGaEtXynIgRqIddYCHx88pb5HTXv4SZeuv0Rqq4+axW9PLAAATU8w04qqjaSXgbGLP3NmohqM6bV9kZZwZLR/klDaQGo1u9uDb9lr4Yn+rBQIDAQABo4HuMIHrMB0GA1UdDgQWBBSWn3y7xm8XvVk/UtcKG+wQ1mSUazCBuwYDVR0jBIGzMIGwgBSWn3y7xm8XvVk/UtcKG+wQ1mSUa6GBlKSBkTCBjjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtQYXlQYWwgSW5jLjETMBEGA1UECxQKbGl2ZV9jZXJ0czERMA8GA1UEAxQIbGl2ZV9hcGkxHDAaBgkqhkiG9w0BCQEWDXJlQHBheXBhbC5jb22CAQAwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQCBXzpWmoBa5e9fo6ujionW1hUhPkOBakTr3YCDjbYfvJEiv/2P+IobhOGJr85+XHhN0v4gUkEDI8r2/rNk1m0GA8HKddvTjyGw/XqXa+LSTlDYkqI8OwR8GEYj4efEtcRpRYBxV8KxAW93YDWzFGvruKnnLbDAF6VR5w/cCMn5hzGCAZowggGWAgEBMIGUMIGOMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDU1vdW50YWluIFZpZXcxFDASBgNVBAoTC1BheVBhbCBJbmMuMRMwEQYDVQQLFApsaXZlX2NlcnRzMREwDwYDVQQDFAhsaXZlX2FwaTEcMBoGCSqGSIb3DQEJARYNcmVAcGF5cGFsLmNvbQIBADAJBgUrDgMCGgUAoF0wGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMTQwNDIzMTYwMjQ3WjAjBgkqhkiG9w0BCQQxFgQUYjCdOhMR2kAw/gwZCNqiNV2A7sIwDQYJKoZIhvcNAQEBBQAEgYCTkxFvVlBxZQhZpkJUtqr+Ig7OasMsAreBPkeSZl0BhNTbTet+1Tt0KnMacAGrj3u+eHvGb6gkq2XSXQg5Us65R4stt6jCx7MmuRu9kWc3PErXfZtDbrRORAi+ZlIwxBg2f6n5IInAR4oWPOLwqAXy9gNxkJMHp5oe2pGYjfVHuQ==-----END PKCS7-----
+">
+<input type="image" src="https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif" border="0" name="submit" alt="PayPal - The safer, easier way to pay online!">
+<img alt="" border="0" src="https://www.paypalobjects.com/en_US/i/scr/pixel.gif" width="1" height="1">
+</form>
+<h4>If you want, I'll even put your name or nick up here as extra thanks.<br />
+Thanks is also due to dwfreed, who helps me with some of the coding,<br />
+WildPikachu, who has provided reliable servers for ASM since its inception,<br />
+DLange, for hosting domain/DNS things,<br />
+and tomaw, who puts up with a lot of my crap.</h4>
+</p>
+<hr />
+<p><h2>About AntiSpamMeta:</h2>
+ASM was born on about June 14th, 2006.<br />
+It started out as "AntiSpamBot", a supybot plugin for some rudimentary spam detection.<br />
+It only monitored ##linux, which at the time was a constant target for spam and troll groups.<br />
+However, it quickly outgrew supybot's API, as well as needing to be able to run and process things *much* faster.<br />
+In response to this, the supybot was ditched entirely, and it was re-written using the (legacy) Net::IRC perl framework.<br />
+Despite being a decade old, the framework has proven to be very reliable and only needed a few small changes over time.<br />
+On February 28, 2012, AntiSpamMeta was officially recognized as a freenode FOSS project, and moved base from ##asb-nexus, to #antispammeta.<br />
+As of April 22, 2014, the bot keeps state tracking data for the 58 channels it is in, and the ~11,000 nicks it sees.<br />
+As of September 16, 2015, the bot is in 91 channels, sees ~13,200 nicks, and uses around 50MB of ram.<br />
+It monitors nearly every large channel on freenode, but is still just a client-connection, run as a side project (i.e. not a part of the freenode infrastructure).
+</p>
+<p>Looking for someone to bug that has a specific amount of access? <a href="/cgi-bin/showUsers.pl">Look no further!</a></p>
+<p>For syntax / command info, go <a href="syntax.txt">here.</a></p>
+<p>You can find a general idea of what AntiSpamMeta looks for <a href="checks.txt">here</a>.</p>
+<p><a href="https://gitlab.devlabs.linuxassist.net/asm/antispammeta/">Source code can be found here.</a></p>
+
+</body></html>
diff --git a/investigate.html b/investigate.html
new file mode 100644
index 0000000..e5f0694
--- /dev/null
+++ b/investigate.html
@@ -0,0 +1,18 @@
+<html>
+ <head>
+ <title>AntiSpamMeta database query page</title>
+ </head>
+ <body>
+ <p>Matching is done based on field1 OR field2 OR field3 etc. Wildcards are supported,
+except for the realIP field, which must be blank or an IPv4 dotted quad.</p>
+ <form action="/cgi-bin/secret/investigate.pl" method="get">
+ Nickname: <input type="text" name="nick" />
+ User: <input type="text" name="user" value="" />
+ Hostname: <input type="text" name="host" value="" /><br />
+ Gecos: <input type="text" name="gecos" value="" /><br />
+ Account: <input type="text" name="account" value="" /><br />
+ Real IP: <input type="text" name="realip" value="" /><br />
+ <br /><br /><input type="submit" value="Query!" />
+ </form>
+ </body>
+</html>
diff --git a/query.html b/query.html
new file mode 100644
index 0000000..74f98ff
--- /dev/null
+++ b/query.html
@@ -0,0 +1,50 @@
+<html>
+ <head>
+ <title>AntiSpamMeta database query page</title>
+ </head>
+ <body>
+ <form action="cgi-bin/query.pl" method="get">
+ Channel: <input type="text" name="channel" value="*" /><br />
+ Nickname: <input type="text" name="nick" value="" />
+ User: <input type="text" name="user" value="" />
+ Hostname/IP: <input type="text" name="host" value="" /><br />
+ Gecos: <input type="text" name="gecos" value="" /><br />
+ <input type="checkbox" name="since" value="1">Since time:</input>
+ <input type="text" name="syear" value="1990" maxlength="4" />-
+ <input type="text" name="smonth" value="07" maxlength="2" />-
+ <input type="text" name="sday" value="28" maxlength="2" />-&nbsp;
+ <input type="text" name="shour" value="00" maxlength="2" />:
+ <input type="text" name="smin" value="00" maxlength="2" />:
+ <input type="text" name="ssec" value="00" maxlength="2" /><br />
+ <input type="checkbox" name="before" value="1">Before time:</input>
+ <input type="text" name="byear" value="2007" maxlength="4" />-
+ <input type="text" name="bmonth" value="06" maxlength="2" />-
+ <input type="text" name="bday" value="20" maxlength="2" />-&nbsp;
+ <input type="text" name="bhour" value="20" maxlength="2" />:
+ <input type="text" name="bmin" value="02" maxlength="2" />:
+ <input type="text" name="bsec" value="58" maxlength="2" /><br />
+ Threat level: <select name="level">
+ <option value="any" selected="selected">any</option>
+ <option value="debug">debug</option>
+ <option value="info">info</option>
+ <option value="low">low</option>
+ <option value="medium">medium</option>
+ <option value="high">high</option>
+ </select>
+ &nbsp;&nbsp;Threat reason:<sup>1</sup> <input type="text" name="reason" value="*" /><br />
+ Sort by: <select name="sort">
+ <option value="time">time</option>
+ <option value="nick">nick</option>
+ <option value="user">user</option>
+ <option value="host">host</option>
+ <option value="level" selected="selected">threat level</option>
+ <option value="id">threat id</option>
+ <option value="reason">threat reason</option>
+ </select>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<select name="order">
+ <option value="a" selected="selected">ascending</option>
+ <option value="d">descending</option>
+ </select>
+ <br /><br /><input type="submit" value="Query!" />
+ </form>
+ </body>
+</html>
diff --git a/syntax.txt b/syntax.txt
new file mode 100644
index 0000000..0eecddf
--- /dev/null
+++ b/syntax.txt
@@ -0,0 +1,175 @@
+List of ASM's commands.
+
+!ops <optional:#channel> <optional: message to send>
+ Generates an alert, and includes the message in that alert. So, via channel message or PM,
+ you can do "!ops Hey Ops you need to see this" or "!ops #spammychannel bad stuff goin on"
+ or "!ops" or "!ops #spammychannel". You have to be on the channel in order to call this function,
+ and there are some stopgaps against abuse.
+
+;teredo <IPv6 IP>
+ This will "unmask" an IPv4 connection pretending to be IPv6 - it will report the user's "real" IP.
+ Use if their IP starts with "2001:0:"
+
+;status
+ Reports the bot's memory/cpu/network usage and more.
+
+;source
+ Responds with a URL to the bot's source
+
+;help
+ Refers you to ASM's website
+
+;ping
+ Replies "pong".
+
+*** These are commands used for check to see if a user has a "bad past" ***
+
+;db
+ Returns a link to a page where you can query ASM's alertlog database.
+
+;query <optional:channel> <nick!user@host>
+ nick/user/host takes standard wildcards (e.g. AfterDeat*!*@*).
+ If channel is not specified, checks against all channels.
+ Responds with how many matches there are for this in ASM's alertlog database.
+
+;investigate <nick>
+ <nick> MUST BE A NICK THAT ANTISPAMMETA CAN SEE.
+ Returns with the number of how many "incidents", e.g. quiets/kills/klines/bans/removes/etc
+ against that person that ASM is aware of.
+
+;investigate2 <nick> <optional:skip> Restricted to flag "s"
+ Like ;investigate, except ASM will PM you the details of the most recent 10 incidents,
+ unless you provide a skip number, in which case it will skip the first X incidents.
+ There are index numbers attached to the lines, which correspond with logfiles that may
+ explain why said person got banned/klined/whatever. These logfiles are stored in a password-
+ protected directory. See AfterDeath/the url provided/;;addwebuser for access.
+
+;mship <nick> Restricted to flag "s"
+ Reports channels that both ASM and <nick> is on.
+
+;;addwebuser <password> Restricted to flag "s"
+ THIS COMMAND MUST BE SENT IN PM, WITH TWO SEMICOLONS.
+ This will give you access to the restricted areas of ASM's database, using an http login of
+ [yourIRCnick] and [password].
+ No assurance of privacy is made with regards to your password. Make it secure, so that someone
+ else won't use your login, and make it unique - i.e. not something you use for /ns identify.
+
+
+
+*** These commands are for managing who has access to the bot ***
+
+;userx add <nickserv account> <flags> Restricted to flag "a"
+ Adds <nickserv account> to ASM's config and gives them the specified flags.
+ BE SURE to use the person's nickserv account, WHICH IS NOT NECESSARILY the nick they're using.
+ /whois them to be sure. You can't give them a flag you don't have yourself, and for extra security,
+ you can't ever give them the 'd' flag - this flag has to be assigned via manual config edit.
+ This means if someone has the 'd' flag, they can't have their flags changed to a new set that
+ still includes 'd' without a manual edit.
+
+;userx flags <nickserv account>
+ Shows what flags that nickserv account has access to.
+
+;userx flags <nickserv account> <flags> Restricted to flag "a"
+ Sets the flags for that nickserv account - NOT NECESSARILY THE SAME AS THEIR NICK - to the
+ provided set of flags. Once again, you can't give flags you don't already have, and you can't
+ ever give the 'd' flag.
+
+;userx del <nickserv account> Restricted to flag "a"
+ Removes the nickserv account from ASM's list of authorized users.
+
+
+
+*** These commands are for managing who/where the bot notifies of bad stuff ***
+
+;target <source channel> <target channel or nick> <optional:level> Restricted to flag "a"
+ Adds target to the list of places notified for source channel, with the optional level.
+ So, to send low-risk alerts and above concerning #spammychannel to #opschannel, you'd do
+ ;target #spammychannel #opschannel info
+ If a level isn't specified, it defaults to "debug".
+
+;detarget <source channel> <target channel> Restricted to flag "a"
+ Stops sending messages concerning <source channel> to <target channel>.
+
+;showhilights <nick> Restricted to flag "h"
+ shows all the channels that it's configured to hilight <nick> on, and what level it's for.
+
+;hilight <channel> <nick><optional:,nick2,nick3> <optional:level> Restricted to flag "h"
+ Adds <nick[s]> to the list of hilights for <channel> for risks of <level> and above.
+ If <level> is unspecified, it defaults to "info".
+
+;dehilight <channel> <nick><optional:,nick2,nick3> Restricted to flag "h"
+ Removes the list of nicks from the list of hilights for <channel>.
+
+
+*** Blacklist management commands ***
+
+;blacklist <string> Restricted to flag "s"
+ Adds <string> to the list of strings ASM will watch out for. No pattern matching or anything
+ like that is done, although it is case insensitive. Please for the love of everything that
+ is holy use common sense with this. Don't blacklist "nigger" or something like that...
+ but definitely blacklist "http://spammyurl.com" etc.
+
+;blreason <id> <reason> Restricted to flag "s"
+ use the ID returned by ;blacklist to set a reason for why you blacklisted it.
+
+;unblacklist <id> Restricted to flag "s"
+ fix an oopsie
+
+;bllookup <id> Restricted to flag "s"
+ Looks up the blacklisted string represented by a given ID and sends details in PM.
+
+
+*** Administrative commands ***
+
+;join <channel> Restricted to flag "a"
+ Tells the bot to join said channel.
+
+;part <channel> Restricted to flag "a"
+ Tells the bot to part said channel.
+
+;rehash Restricted to flag "a"
+ Re-reads the string blacklist files and config files etc etc.
+
+;monitor <chan> (yes|no) Restricted to flag "a"
+ Sets whether or not the channel is monitored for spam stuffs.
+ This is obviously "yes" by default, but some places turn it off for their #blah-ops channel.
+
+;silence <chan> (yes|no) Restricted to flag "a"
+ Sets whether or not ASM ignores commands (other than !ops) in the channel.
+ "no" by default; public channels often want this.
+
+;quit <reason> Restricted to flag "a"
+ Quits IRC using reason as the quit message. ASM is run in a while loop, so it'll probably
+ come right back.
+
+;sync <channel> Restricted to flag "a"
+ sends data to gather information about <channel>, in case a sync failed and needs to be
+ manually forced.
+
+;restrict (nick|account|host) <nick/acct/host> (+|-)<restriction> Restricted to flag "a"
+ Adds a restriction for the specified nick or nickserv account or hostname.
+ Useful restrictions are:
+ notrigger - prevents the target from causing the bot to generate a spam alert.
+ nomsgs - don't relay private messages from target to the master channel
+ nocommands - prevent target from sending the bot commands.
+
+
+*** Highly restricted debug commands ***
+
+;sql (main|log) <somestring> Restricted to flag "d"
+ No idea what this does right now.
+
+;sl <line> Restricted to flag "d"
+ Sends <line> to the IRC server.
+
+;ev <code> Restricted to flag "d"
+ Evaluates raw perl code - output is sent to STDOUT, not to IRC.
+
+
+*** Plugin interface ***
+
+;plugin <chan> <risk> <reason> Restricted to flag "p"
+ This is used so another bot can have ASM generate alerts. E.G.
+ <magicbot> ;plugin #spammychannel debug Magic Bad Stuff Detection Algorithm 1 ->
+ <AntiSpamMeta> debug risk threat [#spammychannel] - (magicbot plugin) - Magic Bad Stuff
+ Detection Algorithm 1 - ping lotsofpeople