[ create a new paste ] login | about

Link: http://codepad.org/HQmnr0AG    [ raw code | fork ]

Perl, pasted on Nov 10:
package RT::Interface::Email::Filter::SpamAssassin;

our $VERSION = .95;
our $statERR;

BEGIN{
    if( $RT::SpamAssassinClient == 0 ){
	require Mail::Field;
	require Mail::Header;

	$statERR = 'There were no X-Spam fields.';
	*_score = sub {
	  my $msg = Mail::Header->new( [split(/\n/, $_[0])] );
	  #Make long headers like X-Spam-Status approx. single-lines.
	  $msg->fold(1024);

	  my $status;

	  my $report = Mail::Field->extract('X-Spam-Status', $msg);
	  $report &&= $report->stringify;
	  $status->{score} = $1 if $report =~ /score=(\d+(?:\.\d+)?)/;
	  $status->{threshold} = $1 if $report =~ /required=(\d+(?:\.\d+)?)/;

	  local $_ = Mail::Field->extract('X-Spam-Flag', $msg);
	  $status->{isspam} = $_->stringify if $_;

	  return $status;
	  };

    }
    elsif( $RT::SpamAssassinClient > 0 ){
	require Mail::SpamAssassin::Client;
	$statERR = 'Is spamd running?';
	*_score = sub{
	  my $client = Mail::SpamAssassin::Client->new({
		host=>($RT::SpamAssassinClient eq '1' ?
		       'localhost' : $RT::SpamAssassinClient),
		port=>($RT::SpamAssassinPort || 783)
	});
	  $client->check( $_[0] );
	};
    }
    else{
	require Mail::SpamAssassin;
	$statERR = 'A problem doing it ourselves';
	*_score = sub{
	  my $spamcheck = Mail::SpamAssassin->new();
	  my $return = $spamcheck->check( $spamcheck->parse($_[0]) );

	  # make a compatible return value
	  my $status;
	  $status->{isspam} = 'True' if $return->is_spam();
	  $status->{score} = $return->get_score();
	  $status->{threshold} = $return->get_required_score();
	  return $status;
	};
    }
}

sub GetCurrentUser {
    my %args = @_;

    my $status = _score( ${$args{RawMessageRef}} );

    unless( $status ){
    	$RT::Logger &&
	  $RT::Logger->error("SpamAssassin returned undef. $statERR");
    	return ( $args{'CurrentUser'}, $args{'AuthLevel'} );
    }

    my $msgfrom = $args{'Message'}->head->get('From');
    $msgfrom =~ s/\s{2,}|\n//;
    my $msginfo = "score " . $status->{score}  . " from [$msgfrom]";

    $RT::Logger && $RT::Logger->debug("SpamAssassin returned $msginfo");

    # add the new header...  so a scrip can deal with it later
    if( $RT::SpamAssassinClient != 0 ){
      $args{'Message'}->head->delete('X-Spam-Score');
      $args{'Message'}->head->add('X-Spam-Score', $status->{score});
    }

    return ( $args{'CurrentUser'}, $args{'AuthLevel'} )
      unless $status && _spamdbool($status->{isspam});

    # punt especially spammy messages
    if( $status->{score} > $status->{threshold}*($RT::SpamAssassinMax||1.5) ){
        $RT::Logger && $RT::Logger->info("SpamAssassin thinks $msginfo is very spammy, punting");
        return ( $args{'CurrentUser'}, -1 );
    }

    if( $RT::SpamAssassinQueue ){
        $RT::Logger && $RT::Logger->info("SpamAssassin rerouting $msginfo to queue " . $RT::SpamAssassinQueue);
        $args{'Queue'}->Load( $RT::SpamAssassinQueue );
    }
    else{
        $RT::Logger && $RT::Logger->debug("SpamAssassin ignoring $msginfo, because SpamAssassinQueue not set.");
    }

    return ( $args{'CurrentUser'}, $args{'AuthLevel'} );
}

sub _spamdbool {
	my ($bool) = @_;
	# in case someone changes things, as they always seem to
	return ($bool eq 'True' ||
		$bool eq 'YES' ||
		$bool eq '1' ||
		$bool eq 'T');
}

=head1 NAME

RT::Interface::Email::Filter::SpamAssassin - Spamassassin filter for RT

=head1 SYNOPSIS

F<RT_SiteConfig.pm>

    #Enable spam filtering, but make sure you authenticate users first!
    Set(@MailPlugins, 'Filter::SpamAssassin', ... 'Auth::MailFrom');

    #Enable per-message calls to SpamAssassin on localhost
    Set($RT::SpamAssassinClient, 'mail.example.org')

    #Refile suspect messages
    #Set($RT::SpamAssassinQueue, 'SPAM');

    #Conservative punting of messages
    Set($RT::SpamAssassinMax, 4);

=head1 DESCRIPTION

This plugin checks to see if an incoming mail is spam, optionally running it
through SpamAssassin. If the mail is very definitely spam, then it is punted.
Otherwise, it may be passed on as normal or directed to a special queue if
suspect.

=over

=item B<$RT:SpamAssassinClient>

Whether or not to (re)check messages.

=over

=item I<hostname>

Connect to I<hostname> with L<Mail::SpamAssassin::Client> to process messsage.

=item I<-1>

Process message with L<Mail::SpamAssassin::PerMsgStatus>. This is very slow.

=item I<0>

Rely on existing headers. This is very fast.

=item I<1>

Equivalent to I<localhost>.

=back

=item B<$RT::SpamAssassinPort>

The port to connect to if B<$RT::SpamAssassinClient> is true. Default is 783.

=item B<$RT::SpamAssassinMax>

If the message's score is at least this many times greather than SpamAssassin's
threshold, discard the message.

The default is 1.5, so messages scoring > 1.5*threshold are discarded.

=item B<$RT::SpamAssassinQueue>

If set, messages meeting the local threshold will be shunted to the
queue specified in the variable. This can be a safe way to avoid
excess notifications to your AdminCCs, without dropping messages.

=back

=head1 AUTHOR

=over

=item Jerrad Pierce

VERSION 0.95 (20081010)

=item Erik Aronesty

VERSION: 0.9 (20080807)

=item Best Practical

=back

=head1 LICENSE

GPL 2

=cut

eval "require RT::Interface::Email::Filter::SpamAssassin_Vendor";
die $@ if ($@ && $@ !~ qr{^Can't locate RT/Interface/Email/Filter/SpamAssassin_Vendor.pm});
eval "require RT::Interface::Email::Filter::SpamAssassin_Local";
die $@ if ($@ && $@ !~ qr{^Can't locate RT/Interface/Email/Filter/SpamAssassin_Local.pm});

1;


Create a new paste based on this one


Comments: