6 $VERSION=do { my @r=(q$Revision$=~/\d+/g); sprintf "%d.".("%03d"x$#r),@r; };
11 use POSIX qw(WIFEXITED WEXITSTATUS WIFSIGNALED WTERMSIG WIFSTOPPED WSTOPSIG);
13 require Mail::Address;
14 require File::Basename;
17 my $sendmail_orig=(-x ($_="/usr/sbin/sendmail-orig") ? $_ : "/usr/sbin/sendmail");
18 my $HOME="/home/short";
19 # Mail-Alias-1.12 defaults to "/etc/mail/aliases" which does not exist on RedHat sendmail-8.12.5-7
20 # Mail-Alias-1.12 will clutter $_ !
21 my @addr_addon=(Mail::Alias->new("/etc/aliases")->exists("sentout") ? ("sentout") : ());
25 my($rcpt,$iserror)=@_;
27 return Mail::Address->new(
28 (defined $opt_F ? $opt_F : "Jan Kratochvil"),
29 (!$iserror ? 'rcpt' : 'rcpterr')
31 .(defined($rcpt->user()) ? $rcpt->user() : "NOUSER")
33 .(defined($rcpt->host()) ? $rcpt->host() : "LOCAL")
34 .'@jankratochvil.net',
38 # RedHat sendmail-8.9.3-20/src/conf.c/HdrInfo[]/\Q/* destination fields */\E
39 # FIXME: Recognize "Resent-$_" headers for -t but when we are in 'resent' mode?
40 my @h_rcpt=( # case in-sensitive!
47 # ordering matters; first header found is substituted
48 # last header is subsituted if no one is found
55 # FIXME: modularized unification with 'lacemail-accept'
56 # BEGIN lacemail-accept
57 our %muttrc_pending=();
62 $muttrc||="$HOME/.muttrc";
63 $muttrc=~s/^\~/$HOME/;
64 do { warn "Looping muttrc, ignoring: $muttrc"; return (); } if $muttrc_pending{$muttrc};
65 local $muttrc_pending{$muttrc}=1;
67 open MUTTRC,$muttrc or do { warn "open \"$muttrc\": $!"; return (); };
71 # far emulation mutt/init.c/mutt_parse_rc_line()
76 next if !/^(\S+)\s*/s;
79 do { warn "Wrong 'source' parameters at $muttrc:$.: $_"; next; } if !/^\S+$/;
85 close MUTTRC or warn "close \"$muttrc\": $!";
86 return wantarray() ? @r : join("",map("$_\n",@r));
89 my %mutteval_charmap=( # WARNING: Don't use "" or "0" here, see below for "|| warn"!
97 # mutt/init.c/mutt_extract_token()
101 return $_ if !s/^"//;
102 do { warn "Missing trailing quote in: $_"; return $_; } if !s/"$//;
103 s/\\(.)/$mutteval_charmap{$1} || warn "Undefined '\\$1' sequence in: $_";/ges;
111 my @r=map({ (ref $_ ? $_ : qr/^\s*set\s+\Q$_\E\s*=\s*(.*?)\s*$/si); } @headers);
112 my %r=map(($_=>undef()),@r);
116 $r{$ritem}=mutteval $1;
119 for my $var (grep { !defined($r{$_}) } @r) {
120 warn "Variable '$var' not found in muttrc";
123 return wantarray() ? %r : $r{$r[0]};
125 # END lacemail-accept
128 sub sendmail_show { return "\"$sendmail_orig\" ".join(",",map("\"$_\"",@ARGV)); }
130 sub sendmail_orig_exec
132 exec {$sendmail_orig} $0,@ARGV or die "exec(".sendmail_show()."): $!";
136 Getopt::Long::Configure(
140 # FIXME: workaround: 'unknown options' are considered the same as 'arguments'
141 # None of ($REQUIRE_ORDER, $PERMUTE, $RETURN_IN_ORDER) can help us.
142 # No preprocessing possible as it is hard to find option arguments.
152 #my $opt_F; # declared before &FromAddress already
153 my $opt_lacemail_dry_run;
154 my @ARGV_save=@ARGV; # for non-bm mode
155 die if !Getopt::Long::GetOptions(
162 "lacemail-dry-run+",\$opt_lacemail_dry_run,
165 # RedHat sendmail-8.12.5-7/sendmail/main.c/\QDo a quick prescan of the argument list.\E
166 || grep({ File::Basename::basename($0) eq $_; } "newaliases","mailq","smtpd","hoststat","purgestat")
167 # -bm: Deliver mail in the usual way (default).
168 || (defined($opt_b) && $opt_b ne "m")
169 || defined $opt_q # MD_QUEUERUN
170 || defined $opt_Q # MD_QUEUERUN
173 sendmail_orig_exec();
177 # RedHat sendmail-8.9.3-20/src/main.c/main()/\Qif (FullName != NULL)\E
178 # for $opt_F is implemented by Mail::Address in our &FromAddress
180 my $head=Mail::Header->new(\*STDIN);
181 # We may (=will) change the contents and send it multiple times
182 if (defined(my $msgid=$head->get("Message-ID"))) {
183 $head->delete("Message-ID");
184 $head->replace("X-LaceMail-sendmail-Message-ID",$msgid);
186 # options leave in @ARGV, addresses to @addr:
187 my @args=@ARGV; # temporary
189 my @addr=(); # addresses
190 push @{(/^-./ ? \@ARGV : \@addr)},$_ for (@args);
192 for my $addrobj (map({ Mail::Address->parse($_); } map({ ($head->get($_)); } @h_rcpt))) {
193 if (!$addrobj->address()) {
194 # bogus, shouldn't happen
195 warn "->address() not found in \"".$addrobj->format()."\"";
202 # return: Mail::Address instance or undef()
207 return undef() if !defined $line;
208 my @r=Mail::Address->parse($line);
209 warn "Got ".scalar(@r)." addresses while wanting just one; when parsing: $line" if 1!=@r;
220 my $muttrc_From=parseone(scalar muttrc_get("from")); # may get undef()!; parseone() may be redundant
221 $muttrc_From=$muttrc_From->address() if $muttrc_From;
222 $opt_f=undef() if defined($opt_f) && $muttrc_From && lc($opt_f) eq lc($muttrc_From);
224 $from_headername=$_; # leave last item in $from_headername
225 next if !(my @from_val=$head->get($from_headername));
226 @from_val=map({ ($_->address()); } map({ (Mail::Address->parse($_)); } @from_val));
227 $from_headername=undef() if !(1==@from_val && $muttrc_From && lc($from_val[0]) eq lc($muttrc_From));
229 } # fallthru with $from_headername remaining set if last headername did not exist
230 # now $from_headername contains the header name to be replaced w/substituted value
234 my @rcpts=(@addr ? @addr : (undef())); # !defined($rcpt) if we have no recipients
235 my $stdin_body=(@rcpts<=1 ? undef() : do { # store input data only if it will be used multiple times
239 for my $rcpt (@rcpts) {
243 if (defined $rcpt) { # !defined($rcpt) if we have no recipients
246 $rcpt=parseone $rcpt;
247 next if !defined $rcpt;
249 $opt_f=FromAddress($rcpt,1)->address() if !defined $opt_f;
250 $head->replace($from_headername,FromAddress($rcpt,0)->format()) if $from_headername;
253 1; # drop '-bm' if present as it is default anyway
254 1; # drop '-t' if present as we are looping now for it
255 push @ARGV,"-f",$opt_f if defined $opt_f;
256 # we don't handle "Full-Name" header thus pass "-F"
257 # "From/Resent-From" should be handled by our &FromAddress
258 push @ARGV,"-F",$opt_F if defined $opt_F;
259 push @ARGV,$rcpt->address() if defined $rcpt;
260 push @ARGV,@addr_addon;
262 local $SIG{"PIPE"}=sub { die "Got SIGPIPE from ".sendmail_show(); };
264 if ($opt_lacemail_dry_run) {
265 print sendmail_show()."\n";
269 defined (my $pid=open SENDMAIL,"|-") or die "Cannot fork to spawn ".sendmail_show().": $!";
270 sendmail_orig_exec() if !$pid; # child
272 $head->print(\*SENDMAIL);
273 print SENDMAIL "\n"; # Mail::Header->print() eats the empty line but it doesn't print it
274 if (defined($stdin_body)) {
275 print SENDMAIL $stdin_body;
284 next if $opt_lacemail_dry_run; # don't close our STDOUT as it is aliased to *SENDMAIL
285 close SENDMAIL or warn "close(".sendmail_show()."): $?=".join(",",
286 (!WIFEXITED($?) ? () : ("EXITSTATUS(".WEXITSTATUS($?).")")),
287 (!WIFSIGNALED($?) ? () : ("TERMSIG(" .WTERMSIG($?) .")")),
288 (!WIFSTOPPED($?) ? () : ("STOPSIG(" .WSTOPSIG($?) .")")),
290 my $gotcode=(!WIFEXITED($?) ? 99 : WEXITSTATUS($?));
291 $exitcode=$gotcode if $gotcode>$exitcode;