From 8ce8427bf97390cc56bfd9aea04774b808bf0951 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ask=20Bj=C3=B8rn=20Hansen?= Date: Tue, 6 Aug 2002 12:01:22 +0000 Subject: [PATCH] data_post hook spamassassin plugin git-svn-id: https://svn.perl.org/qpsmtpd/branches/v010@38 958fd67b-6ff1-0310-b445-bb7760255be9 --- config.sample/plugins | 2 ++ lib/Qpsmtpd.pm | 24 +++++++++------- lib/Qpsmtpd/Transaction.pm | 58 ++++++++++++++++++++++++++++++++------ plugins/spamassassin | 55 ++++++++++++++++++++++++++++++++++++ 4 files changed, 120 insertions(+), 19 deletions(-) create mode 100644 plugins/spamassassin diff --git a/config.sample/plugins b/config.sample/plugins index 209a023..344427c 100644 --- a/config.sample/plugins +++ b/config.sample/plugins @@ -5,3 +5,5 @@ dnsbl # this plugin needs to run after all other "rcpt" plugins check_relay + +spamassassin \ No newline at end of file diff --git a/lib/Qpsmtpd.pm b/lib/Qpsmtpd.pm index 0a1dbea..958126d 100644 --- a/lib/Qpsmtpd.pm +++ b/lib/Qpsmtpd.pm @@ -335,7 +335,8 @@ sub data { #. .. } - $buffer .= $_; + $self->transaction->body_write($_); + $size += length $_; } $self->log(5, "size is at $size\n") unless ($i % 300); @@ -346,27 +347,27 @@ sub data { $self->log(6, "max_size: $max_size / size: $size"); $self->transaction->header($header); - $self->transaction->body(\$buffer); # if we get here without seeing a terminator, the connection is # probably dead. $self->respond(451, "Incomplete DATA"), return 1 unless $complete; - # - # FIXME - Call plugins to work on the body here - # - $self->respond(550, $self->transaction->blocked),return 1 if ($self->transaction->blocked); - $self->respond(552, "Message too big!"),return 1 if $max_size and $size > $max_size; - - return $self->queue($self->transaction); + + my ($rc, $msg) = $self->run_hooks("data_post"); + if ($rc != DONE) { + warn "QPSM100"; + return $self->queue($self->transaction); + } } sub queue { my ($self, $transaction) = @_; + warn "QPSM2000"; + # these bits inspired by Peter Samuels "qmail-queue wrapper" pipe(MESSAGE_READER, MESSAGE_WRITER) or fault("Could not create message pipe"), exit; pipe(ENVELOPE_READER, ENVELOPE_WRITER) or fault("Could not create envelope pipe"), exit; @@ -391,7 +392,10 @@ sub queue { print MESSAGE_WRITER "X-smtpd: qpsmtpd/",$self->version,", http://develooper.com/code/qpsmtpd/\n"; $transaction->header->print(\*MESSAGE_WRITER); - print MESSAGE_WRITER ${$transaction->body}; + $transaction->body_resetpos; + while (my $line = $transaction->body_getline) { + print MESSAGE_WRITER $line; + } close MESSAGE_WRITER; my @rcpt = map { "T" . $_->address } $transaction->recipients; diff --git a/lib/Qpsmtpd/Transaction.pm b/lib/Qpsmtpd/Transaction.pm index b9a7448..a05453c 100644 --- a/lib/Qpsmtpd/Transaction.pm +++ b/lib/Qpsmtpd/Transaction.pm @@ -1,5 +1,10 @@ package Qpsmtpd::Transaction; use strict; +use IO::File qw(O_RDWR O_CREAT); + +# For unique filenames. We write to a local tmp dir so we don't need +# to make them unpredictable. +my $transaction_counter = 0; sub new { start(@_) } @@ -33,11 +38,11 @@ sub header { $self->{_header}; } -sub body { - my $self = shift; - @_ and $self->{_body} = shift; - $self->{_body}; -} +#sub body { +# my $self = shift; +# @_ and $self->{_body} = shift; +# $self->{_body}; +#} sub blocked { my $self = shift; @@ -52,10 +57,45 @@ sub notes { $self->{_notes}->{$key}; } -#sub add_header_line { -#} +sub add_header_line { + my $self = shift; + $self->{_header} .= shift; +} -#sub add_body_line { -#} +sub body_write { + my $self = shift; + my $data = shift; + #$self->{_body} .= shift; + unless ($self->{_body_file}) { + -d "tmp" or mkdir("tmp", 0700) or die "Could not create dir tmp: $!"; + $self->{_filename} = "/home/smtpd/qpsmtpd/tmp/" . join(":", time, $$, $transaction_counter++); + $self->{_body_file} = IO::File->new($self->{_filename}, O_RDWR|O_CREAT) + or die "Could not open file $self->{_filename} - $! "; # . $self->{_body_file}->error; + } + # go to the end of the file + seek($self->{_body_file},0,2) + unless $self->{_body_file_writing}; + $self->{_body_file_writing} = 1; + $self->{_body_file}->print(ref $data eq "SCALAR" ? $$data : $data); +} + +sub body_resetpos { + my $self = shift; + return unless $self->{_body_file}; + seek($self->{_body_file}, 0,0); + $self->{_body_file_writing} = 0; + 1; +} + +sub body_getline { + my $self = shift; + return unless $self->{_body_file}; + seek($self->{_body_file}, 0,0) + if $self->{_body_file_writing}; + $self->{_body_file_writing} = 0; + my $line = $self->{_body_file}->getline; + return $line; + +} 1; diff --git a/plugins/spamassassin b/plugins/spamassassin new file mode 100644 index 0000000..f3da86e --- /dev/null +++ b/plugins/spamassassin @@ -0,0 +1,55 @@ +use Socket qw(:DEFAULT :crlf); +use IO::Handle; + +sub register { + my ($self, $qp) = @_; + $self->register_hook("data_post", "check_spam"); +} + +#my $rv = check_spam(); +#die "failure!" unless defined $rv; +#print "rv: $rv\n"; + +sub check_spam { + my ($self, $transaction) = @_; + + my $remote = 'localhost'; + my $port = 783; + if ($port =~ /\D/) { $port = getservbyname($port, 'tcp') } + die "No port" unless $port; + my $iaddr = inet_aton($remote) || die "no host: $remote"; + my $paddr = sockaddr_in($port, $iaddr); + + my $proto = getprotobyname('tcp'); + socket(SPAMD, PF_INET, SOCK_STREAM, $proto) || die "socket: $!"; + connect(SPAMD, $paddr) || warn "SA: connect: $!", return undef; + + SPAMD->autoflush(1); + + $transaction->body_resetpos; + + print SPAMD "REPORT SPAMC/1.0" . CRLF; + # or CHECK or REPORT or SYMBOLS + + while (my $line = $transaction->body_getline) { + chomp $line; + print SPAMD $line, CRLF; + } + print SPAMD CRLF; + shutdown(SPAMD, 1); + my $line0 = ; # get the first protocol lines out + if ($line0) { + $transaction->header->add("X-Spam-Check-By", $self->qp->config('me')); + } + while () { + warn "GOT FROM SPAMD1: $_"; + next unless m/\S/; + s/\r?\n$/\n/; + my @h = split /: /, $_, 2; + + $transaction->header->add(@h); + last if $h[0] eq "Spam" and $h[1] =~ m/^False/; + + } + return (OK); +}