+Option --dry: Everything w/o write_message(), smssend() or bell()
[PerlMail.git] / perlmail-accept
index e0b6dca..ab934b0 100755 (executable)
@@ -59,7 +59,8 @@ my $SMSmailRcpt=$SMSwebRcpt.'@sms.eurotel.cz';
 my $SMScontact='<short@ucw.cz>';
 
 our($Message,$Audit,@AuditStored,$store_ignore,$store_ignorenewmail,$store_profile,$DoBell);
-our(%audit_profile,@sms_squeezes);     # imported
+our(%audit_profile,@sms_squeezes,@alternates_host);    # imported
+my %alternates_host;   # from @alternates_host
 
 # from RedHat "procmail-3.22-5"
 # /i should be only $procmailFROM_DAEMON but how it can hurt to /i all?
@@ -74,6 +75,7 @@ our $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;
 
 
 sub process;
@@ -430,10 +432,9 @@ sub smssend
 {
 my($ignorenewmail,$smscount,%args)=@_;
 
-       my %aliases=muttrc_aliases();
        my $text=audit_sms(
                        "subject"=>unmime($Audit->subject()),
-                       "from"=>[ map({ $_=$_->address(); $_="\L$_"; $aliases{$_} || $_; } Mail::Address->parse(unmime($Audit->from()))) ],
+                       "from"=>[ Mail::Address->parse(unmime($Audit->from())) ],
                        "body"=>substr(body_simple(),0,$MaxBodySMS*(1+0.25*$smscount)),
                        %args);
        my $texthead="";
@@ -479,8 +480,8 @@ sub cut
        return "<???>" if !defined($_) || /^\s*$/s;
        s/^\s*//s;
        s/\s*$//s;
-       return $_ if length($_)<64;
-       return substr($_,0,64)."...";
+       return $_ if length($_)<128;
+       return substr($_,0,128)."...";
 }
 
 our $profile_eval_depth=0;
@@ -525,17 +526,19 @@ my($folder,$profile,%args)=@_;
 
        $profile=$store_profile if !$profile;
        my %do=map({ (!/=/ ? ($_=>1) : ($`=>$')); } profile_eval($profile));
-       Sys::Syslog::syslog("info","%s%s: %s: %s",
+       Sys::Syslog::syslog("info","%s%s%s: %s: %s",
+                                       (!$opt_dry ? "" : "--dry: "),
                                        (!$store_ignore ? "" : "IGNORED[$store_ignore]: "),
                                        map({ cut($_); } $folder,address_show(unmime($Audit->from())),unmime($Audit->subject())),
                                        )
-                       if $do{"syslog"};
-       $DoBell++ if $do{"bell"};
+                       if $do{"syslog"} || $opt_dry;
        $folder=~s/;.*$//s;
        $folder="$Mail/".$' if $folder=~/^=/;
-       write_message($folder) if !$store_ignore;
-       smssend_tryall $store_ignorenewmail,$do{"sms"},%args if $do{"sms"};
        push @AuditStored,$folder if $do{"did"};
+       return if $store_ignore || $opt_dry;
+       $DoBell++ if $do{"bell"};
+       write_message($folder);
+       smssend_tryall $store_ignorenewmail,$do{"sms"},%args if $do{"sms"};
 }
 
 our $did_last=0;
@@ -557,6 +560,7 @@ sub write_message
 {
 my($folder)=@_;
 
+       return if $opt_dry;
        local *F;
        open F,">>$folder" or do { warn "Append \"$folder\": $!"; return 0; };
        {
@@ -584,9 +588,11 @@ my($message)=@_;
        local $Audit=Mail::Audit->new(
                        "emergency"=>"$Mail/emergency",
                        "data"=>[map("$_\n",split("\n",$message))],
+                       "log"=>"$HOME/.lacemail.log",
+                       "loglevel"=>99,
                        );
        local @AuditStored=();
-       do { smssend $opt_smstest; return; } if $opt_smstest;
+       do { smssend 0,$opt_smstest; return; } if $opt_smstest;
        write_message("$Mail/input");
        audit();
        warn 'Corrupted $_, repaired' if defined($save_)!=defined($_) || (defined($_) && $save_ ne $_);
@@ -624,6 +630,7 @@ sub razor2
 }
 
 # NOTE: returns undef() if !wantarray and the first header is unrecognized
+# Returns also hosts
 sub Received_for
 {
        my @r=();
@@ -631,10 +638,35 @@ sub Received_for
                my($for)=($hdr=~/\bfor\s+\<?(\S+)\>?\b/);
                return $for if !wantarray();
                push @r,$for if $for;
+               my($from,$fromaddr)=($hdr=~/\bfrom\s+(\S+)\b.*?\[((?:\d{1,3}\.){3}\d{1,3})\]/);
+               push @r,"$from:$fromaddr" if $from;
                }
        return @r;
 }
 
+# Extended Mail::Audit::MAPS
+# $domain,$full,[$timeout]
+sub dnsbl
+{
+my($domain,$full,$timeout)=@_;
+
+       $timeout||=30;  # sec
+       $Mail::Audit::MAPS::host=$domain;
+       my @hosts=map({ s/^.*://; "[$_]"; }     # strip DNS part
+                       grep({ /^([^:@]*):/ && !$alternates_host{$1}; } (Received_for()))       # leave only foreign hosts
+                       );
+       splice @hosts,1 if !$full && @hosts;    # "&& @hosts" to prevent: WARN: splice() offset past end of array
+       {
+               package My::Audit::Faked;
+               sub received { return @{$_[0]->{"received"}}; }
+               }
+       my $self_faked={
+                       "received"=>[@hosts],
+                       };
+       bless $self_faked,"My::Audit::Faked";
+       return Mail::Audit::rblcheck($self_faked,$timeout);
+}
+
 our %muttrc_pending=();
 sub muttrc
 {
@@ -712,12 +744,26 @@ sub muttrc_aliases
                for my $addrobj (Mail::Address->parse($')) {
                        my $addr=$addrobj->address();
                        my $ref=\$r{"\L$addr"};
-                       $$ref=$key if !$$ref;
+                       $$ref=$key if !$$ref;   # use always the first occurence to prefer nicks
                        }
                }
        return %r;
 }
 
+# FIXME: Unify
+# BEGIN lacemail-sendmail
+# return: Mail::Address instance or undef()
+sub parseone
+{
+my($line)=@_;
+
+       return undef() if !defined $line;
+       my @r=Mail::Address->parse($line);
+       warn "Got ".scalar(@r)." addresses while wanting just one; when parsing: $line" if 1!=@r;
+       return $r[0];
+}
+# END lacemail-sendmail
+
 # FIXME: host may get multiple recipients and thus not showing "for <...>"
 # FIXME: muttrc_get("from") is too strict
 sub store_muttrc_alternates
@@ -728,11 +774,22 @@ my($prefix,$profile)=@_;
        my $alternatesre=qr/$alternates/si;
        my $From=muttrc_get("from") or return;
        my $Fromre=qr/^\Q$From\E$/si;
-       warn "'From' \"$From\" not matches by 'alternates': $alternatesre"
+       my $Fromobj=parseone $From or return;
+       warn "'From' \"$From\" not matched by 'alternates': $alternatesre"
                        if $From!~/$alternates/si;
        for my $for (reverse Received_for()) {
-               return if $for=~/$From/si;
-               next if $for!~/$alternatesre/si;
+               $for=~s/:.*$//; # strip IP address here
+               if ($Fromobj->user() ne "prog-mutt") {
+                       next if lc($for) eq lc($From);
+                       }
+               else {
+                       my $forobj=parseone $for;
+                       if ($forobj && $forobj->host()) {
+                               # it is 'for' our primary address
+                               next if lc($forobj->host()) eq lc($Fromobj->host());    # or 'return'? shouldn't matter
+                               }
+                       }
+               next if !$alternates_host{lc $for} && $for!~/$alternatesre/si;
                store "$prefix\L$for",($profile || []);
                return;
                }
@@ -811,6 +868,7 @@ $Getopt::Long::ignorecase=0;
 die "GetOptions error" if !Getopt::Long::GetOptions(
                  "inetd"    ,sub { $opt_mode=\&inetd; },
                  "stdin"    ,sub { $opt_mode=\&stdin; },
+                 "dry"      ,\$opt_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; },
@@ -826,6 +884,7 @@ open AUDIT,$filenameMyAudit or die "open \"$filenameMyAudit\": $!";
        local $/=undef();
        eval <AUDIT> or die "eval \"$filenameMyAudit\": $@";
        audit_init();
+       %alternates_host=map((lc($_)=>1),@alternates_host);
        }
 close AUDIT or warn "close \"$filenameMyAudit\": $!";