Browse Source

[Feature] Add experimental CGP integration

tags/1.3.2
Vsevolod Stakhov 8 years ago
parent
commit
ddca96c2b8
1 changed files with 194 additions and 0 deletions
  1. 194
    0
      utils/cgp_rspamd.pl

+ 194
- 0
utils/cgp_rspamd.pl View File

@@ -0,0 +1,194 @@
#!/usr/bin/env perl

use warnings;
use strict;
use JSON::XS;
use AnyEvent;
use AnyEvent::HTTP;
use AnyEvent::IO;
use EV;
use Pod::Usage;
use Getopt::Long;

my $rspamd_host = "localhost:11333";
my $man = 0;
my $help = 0;
my $local = 0;
my $header = "X-Spam: yes";
my $reject_message = "Spam message rejected";

GetOptions(
"host=s" => \$rspamd_host,
"header=s" => \$header,
"local" => \$local,
"reject-message=s" => \$reject_message,
"help|?" => \$help,
"man" => \$man
) or pod2usage(2);

pod2usage(1) if $help;
pod2usage(-exitval => 0, -verbose => 2) if $man;

sub cgp_string {
my ($in) = @_;

$in =~ s/\"/\\"/;
$in =~ s/\n/\\n/;
$in =~ s/\r/\\r/;

return "\"$in\"";
}

sub rspamd_scan {
my ($tag, $file) = @_;

my $http_callback = sub {
my ($body, $hdr) = @_;

if ($hdr->{Status} =~ /^2/) {
my $js = decode_json($body);

if (!$js) {
print "* Rspamd: Bad response for $file: invalid JSON: parse error\n";
print "$tag FAILURE\n";
}
else {
my $def = $js->{'default'};

if (!$def) {
print "* Rspamd: Bad response for $file: invalid JSON: default is missing\n";
print "$tag FAILURE\n";
}
else {
my $action = $def->{'action'};
my $id = $js->{'message-id'};

my $symbols = "";
while (my ($k, $s) = each(%{$def})) {
if (ref($s) eq "HASH" && $s->{'score'}) {
$symbols .= sprintf "%s(%.2f);", $k, $s->{'score'};
}
}

printf "* Rspamd: Scanned %s; id: <%s>; Score: %.2f / %.2f; Symbols: [%s]\n",
$file, $id, $def->{'score'}, $def->{'required_score'}, $symbols;

if ($action eq 'reject') {
print "$tag ERROR " . cgp_string($reject_message) . "\n";
}
elsif ($action eq 'add header' || $action eq 'rewrite subject') {
print "$tag ADDHEADER " . cgp_string($header) . " OK\n";
}
elsif ($action eq 'soft reject') {
print "$tag REJECT Try again later\n";
}
else {
print "$tag OK\n";
}
}
}
} else {
print "* Rspamd: Bad response for $file: HTTP error: $hdr->{Status} $hdr->{Reason}\n";
print "$tag FAILURE\n";
}
};

if ($local) {
# Use file scan
http_get("http://$rspamd_host/symbols?file=$file", $http_callback);
}
else {
aio_load($file, sub {
my ($data) = @_ or return print "* Cannot open $file: $!\n$tag FAILURE\n";

http_post("http://$rspamd_host/symbols", $data, $http_callback);
});
}
}

# Show informational message
print "* Rspamd CGP filter has been started\n";

my $w = AnyEvent->io(
fh => \*STDIN,
poll => 'r', cb => sub {
chomp (my $input = <STDIN>);

if ($input =~ /^(\d+)\s+(\S+)(\s+(\S+)\s*)?$/) {
my $tag = $1;
my $cmd = $2;

if ($cmd eq "INTF") {
print "$input\n";
}
elsif ($cmd eq "FILE" && $4) {
my $file = $4;
print "* Scanning file $file\n";
rspamd_scan $tag, $file;
}
}
}
);

EV::run;

__END__

=head1 NAME

cgp_rspamd - implements Rspamd filter for CommunigatePro MTA

=head1 SYNOPSIS

cgp_rspamd [options]

Options:
--host=hostport Rspamd host to connect (localhost:11333 by default)
--local Rspamd runs locally and can access CGP files (false by default)
--header Add specific header for a spam message ("X-Spam: yes" by default)
--reject-message Rejection message for spam mail ("Spam message rejected" by default)
--help brief help message
--man full documentation

=head1 OPTIONS

=over 8

=item B<--host>

Specifies Rspamd host to use for scanning

=item B<--local>

Should be used if Rspamd runs on the same machine and can access CGP files

=item B<--header>

Specifies the header that should be added when Rspamd action is B<add header>
or B<rewrite subject>.

=item B<--reject-message>

Specifies the rejection message for spam.

=item B<--help>

Print a brief help message and exits.

=item B<--man>

Prints the manual page and exits.

=back

=head1 DESCRIPTION

B<cgp_rspamd> is intended to scan messages processed with B<CommunigatePro> MTA
on some Rspamd scanner. It reads standard input and parses CGP helpers
protocol. On scan requests, this filter can query Rspamd to process a message.
B<cgp_rspamd> can tell CGP to add header or reject SPAM messages depending on
Rspamd scan result.

=back

=cut

Loading…
Cancel
Save