4 # Copyright (C) 2002-2003 Jan Kratochvil <project-PerlMail@jankratochvil.net>
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 use vars qw($VERSION);
22 $VERSION=do { my @r=(q$Revision$=~/\d+/g); sprintf "%d.".("%03d"x$#r),@r; };
28 use lib $ENV{"PERLMAIL_BASEDIR"} || File::Basename::dirname($0);
34 use POSIX qw(WIFEXITED WEXITSTATUS WIFSIGNALED WTERMSIG WIFSTOPPED WSTOPSIG);
35 require MIME::Head; # inherits Mail::Header
36 require Mail::Address;
39 sub sendmail_show { return "\"$sendmail_orig\" ".join(",",map("\"$_\"",@ARGV)); }
41 sub sendmail_orig_exec
43 exec {$sendmail_orig} $0,@ARGV or die "exec(".sendmail_show()."): $!";
47 Getopt::Long::Configure(
51 # FIXME: workaround: 'unknown options' are considered the same as 'arguments'
52 # None of ($REQUIRE_ORDER, $PERMUTE, $RETURN_IN_ORDER) can help us.
53 # No preprocessing possible as it is hard to find option arguments.
62 our $opt_f; # not exported, just for local()
63 my $opt_perlmail_dry_run;
64 my @ARGV_save=@ARGV; # for non-bm mode
65 die if !Getopt::Long::GetOptions(
72 "perlmail-dry-run+",\$opt_perlmail_dry_run,
75 # RedHat sendmail-8.12.5-7/sendmail/main.c/\QDo a quick prescan of the argument list.\E
76 || grep({ File::Basename::basename($0) eq $_; } "newaliases","mailq","smtpd","hoststat","purgestat")
77 # -bm: Deliver mail in the usual way (default).
78 || (defined($opt_b) && $opt_b ne "m")
79 || defined $opt_q # MD_QUEUERUN
80 || defined $opt_Q # MD_QUEUERUN
87 # RedHat sendmail-8.9.3-20/src/main.c/main()/\Qif (FullName != NULL)\E
88 # for $opt_F is implemented by Mail::Address in our &FromAddress
90 my $head=MIME::Head->new(\*STDIN);
91 # options leave in @ARGV, addresses to @addr:
92 my @args=@ARGV; # temporary
94 my @addr=(); # addresses
95 push @{(/^-./ ? \@ARGV : \@addr)},$_ for (@args);
97 for my $addrobj (map({ Mail::Address->parse($_); } map({ ($head->get($_)); } @h_rcpt))) {
98 if (!$addrobj->address()) {
99 # bogus, shouldn't happen
100 warn "->address() not found in \"".$addrobj->format()."\"";
114 my $muttrc_From=parseone(scalar muttrc_get("from")); # may get undef()!; parseone() may be redundant
115 $muttrc_From=$muttrc_From->address() if $muttrc_From;
116 $opt_f=undef() if defined($opt_f) && $muttrc_From && lc($opt_f) eq lc($muttrc_From);
118 $from_headername=$_; # leave last item in $from_headername
119 next if !(my @from_val=$head->get($from_headername));
120 @from_val=map({ ($_->address()); } map({ (Mail::Address->parse($_)); } @from_val));
121 $from_headername=undef() if !(1==@from_val && $muttrc_From && lc($from_val[0]) eq lc($muttrc_From));
123 } # fallthru with $from_headername remaining set if last headername did not exist
124 # now $from_headername contains the header name to be replaced w/substituted value
127 # to be utilized later by &FromAddress
129 && do { local $_=$head->mime_attr("Content-Type"); $_ && ~m#^multipart/(?:signed|encrypted)$#; }
130 && do { local $_=$head->mime_attr("Content-Type.protocol"); $_ && ~m#^application/pgp\b#; }
134 # !defined($rcpt) if we have no recipients
135 # make the list unique to prevent dupes being normally filtered by sendmail(8)
136 # one '{' is block-wrapper, another '{' is hash-indirection!
137 # hash keys are just strings, never refs!
138 # unify the list as Mail::Address instances
139 my @rcpts=(!@addr ? (undef()) : values(%{{ map({
141 $obj=parseone $obj if !ref $obj;
142 (!defined $obj ? () : (lc($obj->address())=>$obj));
145 my $stdin_body=(@rcpts<=1 ? undef() : do { # store input data only if it will be used multiple times
149 for my $rcpt (@rcpts) {
153 if (defined $rcpt) { # !defined($rcpt) if we have no recipients
155 $opt_f=FromAddress($rcpt,1)->address() if !defined $opt_f;
156 if ($from_headername) {
157 if (my $fromaddr=FromAddress($rcpt,0)->format()) {
158 $head->replace($from_headername,$fromaddr);
163 1; # drop '-bm' if present as it is default anyway
164 1; # drop '-t' if present as we are looping now for it
165 push @ARGV,"-f",$opt_f if defined $opt_f;
166 # we don't handle "Full-Name" header thus pass "-F"
167 # "From/Resent-From" should be handled by our &FromAddress
168 push @ARGV,"-F",$opt_F if defined $opt_F;
169 push @ARGV,$rcpt->address() if defined $rcpt;
170 push @ARGV,@addr_addon;
172 local $SIG{"PIPE"}=sub { die "Got SIGPIPE from ".sendmail_show(); };
174 if ($opt_perlmail_dry_run) {
175 print sendmail_show()."\n";
179 defined (my $pid=open SENDMAIL,"|-") or die "Cannot fork to spawn ".sendmail_show().": $!";
180 sendmail_orig_exec() if !$pid; # child
182 $head->print(\*SENDMAIL);
183 print SENDMAIL "\n"; # MIME::Head->print() eats the empty line but it doesn't print it
184 if (defined($stdin_body)) {
185 print SENDMAIL $stdin_body;
194 next if $opt_perlmail_dry_run; # don't close our STDOUT as it is aliased to *SENDMAIL
195 close SENDMAIL or warn "close(".sendmail_show()."): $?=".join(",",
196 (!WIFEXITED($?) ? () : ("EXITSTATUS(".WEXITSTATUS($?).")")),
197 (!WIFSIGNALED($?) ? () : ("TERMSIG(" .WTERMSIG($?) .")")),
198 (!WIFSTOPPED($?) ? () : ("STOPSIG(" .WSTOPSIG($?) .")")),
200 my $gotcode=(!WIFEXITED($?) ? 99 : WEXITSTATUS($?));
201 $exitcode=$gotcode if $gotcode>$exitcode;