Remove any LMTP.
[PerlMail.git] / perlmail-accept
index f2e12ec..e704077 100755 (executable)
@@ -46,6 +46,12 @@ INIT {
 use File::Basename;
 BEGIN {
        use lib $ENV{"PERLMAIL_BASEDIR"} || File::Basename::dirname($0);
+
+       # FIXME:
+       use lib $ENV{"HOME"}."/lib64/perl5";
+       use lib $ENV{"HOME"}."/lib/perl5";
+       use lib $ENV{"HOME"}."/share/perl5";
+
        use PerlMail::Config;
        use PerlMail::Lib;
        }
@@ -69,13 +75,13 @@ require HTTP::Request;
 require LWP::UserAgent;
 use URI::Escape 'uri_escape';
 require WWW::SMS;
-require Authen::SASL;  # Sanity check for &Net::SMTP::auth
+#require Authen::SASL; # Sanity check for &Net::SMTP::auth
 use MIME::Base64;
 use IPC::Open3;
 use POSIX ":sys_wait_h";
 
 
-our($Message,@AuditStored,$DoBell);
+our($Message,@AuditStored,$DoBell,$Dry);
 my %alternates_host;   # from @alternates_host
 my %dnsbl_whitelist;   # from @dnsbl_whitelist
 
@@ -92,7 +98,6 @@ $procmailFROM_MAILER=qr'^(((Resent-)?(From|Sender)|X-Envelope-From):|>?From )[^>
 my $opt_mode;
 my $opt_smstest;       # 1 or $smscount
 my $opt_idle;
-my $opt_dry;
 my $opt_single;
 
 
@@ -125,8 +130,9 @@ sub inetd
 
        while (1) {
                local $/="\n";
+               $!=undef();
                my $length=<STDIN>;
-               confess "Unexpected EOF" if !defined $length;
+               confess "Unexpected EOF: $!" if !defined $length;
                confess "Missing EOL" if $length!~s/\n$//s;
                exit 0 if $length eq "BYE";
                confess "Unrecognized length: $length" if $length!~/^\d+$/;
@@ -166,7 +172,7 @@ sub useridle
        my($idlebest,$linebest);
        for my $utmp (User::Utmp::getut(),{ "ut_line"=>"psaux" }) {
                local $_;
-               next if defined($_=$utmp->{"ut_type"}) && $_!=User::Utmp::USER_PROCESS;
+               next if defined($_=$utmp->{"ut_type"}) && $_!=User::Utmp::USER_PROCESS();
                next if defined($_=$utmp->{"ut_user"}) && !$valid_users{$_};
                my $line="/dev/".$utmp->{"ut_line"};
                my $atime=(stat $line)[8];
@@ -192,6 +198,12 @@ sub body_first
        return $first;
 }
 
+sub is_multipart
+{
+       return 0 if !$Audit->is_mime();
+       return $Audit->is_multipart();
+}
+
 sub mimehead
 {
 my($part)=@_;
@@ -380,7 +392,10 @@ my($name)=@_;
 
        die "Nesting profile: $name" if 0x10<=(local $profile_eval_depth=$profile_eval_depth+1);
        return @$name if ref $name;
-       die "Profile not found: $name" if !exists $audit_profile{$name};
+       if (!exists $audit_profile{$name}) {
+               cluck "Profile not found: $name";
+               return "did";
+       }
        my @this=@{$audit_profile{$name}};
        return (profile_eval($'),@this[1..$#this]) if $this[0] && $this[0]=~/^=/;
        return @this;
@@ -415,15 +430,15 @@ my($folder,$profile,%args)=@_;
        $profile=$store_profile if !$profile;
        my %do=map({ (!/=/ ? ($_=>1) : ($`=>$')); } profile_eval($profile));
        Sys::Syslog::syslog("info","%s%s%s: %s: %s",
-                                       (!$opt_dry ? "" : "--dry: "),
+                                       (!$Dry ? "" : "--dry: "),
                                        (!$store_ignore ? "" : "IGNORED[$store_ignore]: "),
                                        map({ cut($_); } $folder,address_show(unmime($Audit->from())),unmime($Audit->subject())),
                                        )
-                       if $do{"syslog"} || $opt_dry;
+                       if $do{"syslog"} || $Dry;
        $folder=~s/;.*$//s;
        $folder="$Mail/".$' if $folder=~/^=/;
        push @AuditStored,$folder if $do{"did"};
-       return if $store_ignore || $opt_dry;
+       return if $store_ignore || $Dry;
        $DoBell++ if $do{"bell"};
        write_message($folder) or die;
        smssend_tryall $store_ignorenewmail,$do{"sms"},%args if $do{"sms"};
@@ -443,17 +458,28 @@ my($funcref,@funcargs)=@_;
        return @AuditStored!=$did_last;
 }
 
+sub writeto
+{
+my($filename)=@_;
+
+       local *F;
+       open F,$filename or confess "open $filename: $!";
+       print F $Message or confess "write $filename: $!";
+       close F or confess "close $filename: $!";
+       return 1;
+}
+
 # Never use Mail::Audit->store() as it will reformat MIME bodies and possibly corrupt OpenPGP!
 sub write_message
 {
 my($folder)=@_;
 
-       return 1 if $opt_dry;   # simulate OK
+       return 1 if $Dry;       # simulate OK
        local *F;
        open F,">>$folder" or do { warn "Append \"$folder\": $!"; return 0; };
        {
                local $_;
-               ($_=Mail::Audit::audit_get_lock(\*F,$folder)) and do { warn "Lock \"$folder\": $!"; last; };
+               ($_=$Audit->_audit_get_lock(\*F,$folder)) and do { warn "Lock \"$folder\": $!"; last; };
                seek F,0,IO::Handle::SEEK_END or do { warn "Seek-end \"$folder\": $!"; last; };
                # FIXME: Check for '^From ' to not to rely on our network peer
                print F $Message or do { warn "Write to \"$folder\": $!"; last; };
@@ -512,7 +538,8 @@ sub spamassassin
 {
 my($cmd)=@_;
 
-       $cmd||="$HOME/bin/spamassassin --exit-code 1";
+       #$cmd||="nice spamassassin --exit-code 1 --mbox";
+       $cmd||="spamc -c -s 50000000";
        # spamassassin has the specified exit code if IS spam, code 0 if NOT spam
        # See &_spamchildcode for the code 1.
        local *CHILD;
@@ -520,7 +547,11 @@ my($cmd)=@_;
        # prevent Razor2's: Can't call method "log" on unblessed reference at Razor2/Client/Agent.pm line 212.
        local $ENV{"HOME"}=$HOME;
        # 2>/dev/null to prevent error messages to corrupt inetd() output of perlmail-accept(1)
-       open CHILD,"|$cmd --mbox >/dev/null 2>/dev/null"
+       open CHILD,"|$cmd &>/dev/null"
+                                       # Workaround: spamassassin-3.1.3-1.fc5
+                                       #.q{|awk '/^X-Spam-Flag: YES$/{if (!body) exit 1;}/^$/{body=1;}'}
+                                       # Original:
+                                       #." >/dev/null 2>/dev/null"
                        or return 0;
        print CHILD $Message;
        close CHILD;
@@ -550,7 +581,7 @@ sub dnsbl
 {
 my($domain,$full,$timeout)=@_;
 
-       $timeout||=30;  # sec
+       $timeout||=2;   # sec
        $Mail::Audit::MAPS::host=$domain;
        for my $host (Received_for()) {
                next if $host!~/^([^:@]*):/;
@@ -558,15 +589,18 @@ my($domain,$full,$timeout)=@_;
                # $1 is DNS name, $ip is IP address
                next if $alternates_host{$1};   # leave only foreign hosts
                next if $dnsbl_whitelist{$ip};
-               {
-                       package My::Audit::Faked;
-                       sub received { return @{$_[0]->{"received"}}; }
-                       }
-               my $self_faked={
-                               "received"=>["[$ip]"],
-                               };
-               bless $self_faked,"My::Audit::Faked";
-               my $code=Mail::Audit::rblcheck($self_faked,$timeout);
+
+#              FIXME: Faking
+#              {
+#                      package My::Audit::Faked;
+#                      sub received { return @{$_[0]->{"received"}}; }
+#                      }
+#              my $self_faked=Mail::Audit->new();
+#              $self_faked->{"received"}=["[$ip]"];
+#              bless $self_faked,"My::Audit::Faked";
+#              my $code=Mail::Audit::rblcheck($self_faked,$timeout);
+               my $code=$Audit->rblcheck($timeout);
+
                next if !$code;
                # Some 0.0.0.0 etc. found for <linux-kernel@>, see: &Mail::Audit::MAPS::_checkit
                # Do not: $code!='1 Invalid IP address '
@@ -581,7 +615,7 @@ sub clamscan
 {
 my($cmd)=@_;
 
-       $cmd||='clamscan --no-summary -';
+       $cmd||='nice clamscan --no-summary -';
        # clamscan has exit code 1 if IS virus , code 0 if NOT virus
        # Do not use IPC::Open2 as it would try to use our STDERR which is not valid by: local *STDERR;
        local(*WR,*RD,*ERR);
@@ -700,6 +734,7 @@ sub headeris
 {
 my($header,$string)=@_;
 
+       cluck if !defined $string;
        return _headercore(qr/\Q$string\E/i,1,$header,$string);
 }
 
@@ -718,64 +753,6 @@ 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
 
 $Getopt::Long::ignorecase=0;
@@ -783,7 +760,7 @@ die "GetOptions error" if !Getopt::Long::GetOptions(
                  "inetd"    ,sub { $opt_mode=\&inetd; },
                  "stdin"    ,sub { $opt_mode=\&stdin; },
                  "single!"  ,\$opt_single,
-                 "dry"      ,\$opt_dry,
+                 "dry"      ,\$Dry,
                  "smstest:s",sub { $opt_mode=\&stdin; $opt_smstest=($_[1] || 1); },
                  "idle!"    ,\$opt_idle,
                  "idletest" ,sub { syslogging_restore(); print((defined($_=useridle()) ? $_ : "<undef>")."\n"); exit 0; },