X-Git-Url: http://git.jankratochvil.net/?p=PerlMail.git;a=blobdiff_plain;f=perlmail-accept;h=75dfa4acfa93aecd56b1ff897a236ffea7054b05;hp=c647a8e45d798988b72b6bf2ff08a9361d044e01;hb=f258fd2941e62f8995cd48a3d2aa36785760461a;hpb=8ca17d26dca4ffc4d9850c791ad2733718f796ce diff --git a/perlmail-accept b/perlmail-accept index c647a8e..75dfa4a 100755 --- a/perlmail-accept +++ b/perlmail-accept @@ -69,6 +69,8 @@ require HTTP::Request; require LWP::UserAgent; use URI::Escape 'uri_escape'; require WWW::SMS; +require Authen::SASL; # Sanity check for &Net::SMTP::auth +use MIME::Base64; our($Message,@AuditStored,$DoBell); @@ -89,6 +91,7 @@ my $opt_mode; my $opt_smstest; # 1 or $smscount my $opt_idle; my $opt_dry; +my $opt_single; sub process; @@ -101,7 +104,7 @@ sub stdin local $_; while (<>) { die "Invalid 'From ' line: $_" if $message eq "" && !/^From /; - if (/^From / && $message) { + if (!$opt_single && /^From / && $message) { process $message; $message=""; } @@ -130,8 +133,8 @@ sub inetd $length==($_=read STDIN,$message,$length) or confess "Got $_ out of required $length bytes"; $length==length $message or confess "False read return ".length($message)." instead of $length"; { - local *STDOUT; - local *STDERR; + local *STDOUT; # FIXME: fd's inherited by spawned processes are not closed this way! + local *STDERR; # FIXME: fd's inherited by spawned processes are not closed this way! local $DoBell=0; process $message; if ($DoBell) { @@ -465,6 +468,7 @@ my($message)=@_; local $_=$_; my $save_=$_; + $message=~s/(\n)(From )/$1>$2/sg; local $Message=$message; # Cannot call 'local' for our-imported variable: my $Audit_save=$Audit; @@ -486,32 +490,26 @@ my($message)=@_; # utility functions: # return: true (error-message or "1") if is spam -sub razor2 +sub spamassassin { - # razor-check has exit code 1 if NOT spam, code 0 if IS spam +my($cmd)=@_; + + $cmd||="$HOME/bin/spamassassin --exit-code"; + # spamassassin has exit code 1 if IS spam, code 0 if NOT spam local *CHILD; - local $SIG{"PIPE"}=sub { warn "razor2 gave me SIGPIPE: broken pipe"; }; + local $SIG{"PIPE"}=sub { warn "spamassassin gave me SIGPIPE: broken pipe"; }; # prevent Razor2's: Can't call method "log" on unblessed reference at Razor2/Client/Agent.pm line 212. local $ENV{"HOME"}=$HOME; - open CHILD,'|' - .'('.'(razor-check 2>&1;echo >&3 $?)' - .'|sed "s/^/razor-check: /"' - .'|logger -t "perlmail['.$$.']" -p mail.crit' - .') 3>&1' - .'|exit `cat`' + # 2>/dev/null to prevent error messages to corrupt inetd() output of perlmail-accept(1) + open CHILD,"|$cmd --mbox >/dev/null 2>/dev/null" or return 0; print CHILD $Message; - my $return; - { - local $/=undef(); - $return= || 1; - } - close CHILD; + my $return=close CHILD; return undef() if !WIFEXITED($?); return undef() if WIFSIGNALED($?); return undef() if WIFSTOPPED($?); - return undef() if WEXITSTATUS($?); - return $return; # is-spam + return 1 if WEXITSTATUS($?); # is-spam + return 0; # not-spam } # NOTE: returns undef() if !wantarray and the first header is unrecognized @@ -660,6 +658,63 @@ my($header,$map)=@_; $Audit->replace_header($header,$text); } +# LMTP engine: +use Net::Cmd qw(CMD_OK CMD_MORE); +{ + package My::Net::SMTP::LMTP; + require Net::SMTP; + our @ISA=qw(Net::SMTP); + use Net::SMTP; + use Net::Cmd qw(CMD_OK); + use Carp qw(confess cluck); + + # Do not: sub _HELO + # as it would not set {'net_smtp_esmtp'} + sub _EHLO { shift->command("LHLO", @_)->response() == CMD_OK } + + sub clucked + { + my($self,$func,@args)=@_; + + do { return $_ if defined $_; } for $self->$func(@args); + cluck $func; + return; + } +} + + +sub lmtp_deliver +{ +my($admin_user,$admin_pwd,$user_from,$user_to)=@_; + + my $lmtp=My::Net::SMTP::LMTP->clucked("new","localhost","Port"=>"lmtp", +# "Debug"=>1, + ) or return; + bless $lmtp,"My::Net::SMTP::LMTP"; +# Prevent: +# due to: +# $lmtp->auth(Authen::SASL->new( +# "mechanism"=>"PLAIN", +# "callback"=>{ +# "user"=>$admin_user, +# "pass"=>$admin_pwd, +# # Prevent: "authname"=>$admin_user +# # as it causes: DIE: Unknown callback: 'authname'. (user|auth|language|pass) +# })); + # FIXME: Authentication hack: + $lmtp->command("AUTH PLAIN")->response()==CMD_MORE + or do { cluck "auth announce"; return; }; + $lmtp->clucked("command",encode_base64($user_from."\x00".$admin_user."\x00".$admin_pwd)) or return; + $lmtp->clucked("mail",$user_from) or return; + $lmtp->clucked("to",$user_to) or return; + $lmtp->clucked("data"); # Do not: or return; + # Prevent: 554 5.6.0 Message contains invalid header + (my $data=$Message)=~s/\AFrom .*\r?\n//; + $lmtp->clucked("datasend",$data) or return; + $lmtp->clucked("dataend") or return; + $lmtp->clucked("quit") or return; +} + # MAIN @@ -667,6 +722,7 @@ $Getopt::Long::ignorecase=0; die "GetOptions error" if !Getopt::Long::GetOptions( "inetd" ,sub { $opt_mode=\&inetd; }, "stdin" ,sub { $opt_mode=\&stdin; }, + "single!" ,\$opt_single, "dry" ,\$opt_dry, "smstest:s",sub { $opt_mode=\&stdin; $opt_smstest=($_[1] || 1); }, "idle!" ,\$opt_idle,