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;
}
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
my $opt_mode;
my $opt_smstest; # 1 or $smscount
my $opt_idle;
-my $opt_dry;
my $opt_single;
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+$/;
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];
return $first;
}
+sub is_multipart
+{
+ return 0 if !$Audit->is_mime();
+ return $Audit->is_multipart();
+}
+
sub mimehead
{
my($part)=@_;
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;
$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"};
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; };
my($err,$isspam)=@_;
$err=$? if !defined $err;
- return undef() if !WIFEXITED($?);
- return undef() if WIFSIGNALED($?);
- return undef() if WIFSTOPPED($?);
- return $isspam||1 if WEXITSTATUS($?); # is-spam
- return 0; # not-spam
+ return undef() if !WIFEXITED($?);
+ return undef() if WIFSIGNALED($?);
+ return undef() if WIFSTOPPED($?);
+ return 0 if !WEXITSTATUS($?);
+ return $isspam||1 if 1==WEXITSTATUS($?); # isspam
+ cluck "Possible FIXME or your system is broken (WEXITSTATUS==".WEXITSTATUS($?).")";
+ return 0; # simulate as not spam
}
# return: true (error-message or "1") if is spam
{
my($cmd)=@_;
- $cmd||="$HOME/bin/spamassassin --exit-code";
- # spamassassin has exit code 1 if IS spam, code 0 if NOT spam
+ #$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;
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;
# 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;
{
my($domain,$full,$timeout)=@_;
- $timeout||=30; # sec
+ $timeout||=2; # sec
$Mail::Audit::MAPS::host=$domain;
for my $host (Received_for()) {
next if $host!~/^([^:@]*):/;
# $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 '
{
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);
{
my($header,$string)=@_;
+ cluck if !defined $string;
return _headercore(qr/\Q$string\E/i,1,$header,$string);
}
$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;
"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; },