1 Date: Thu, 13 May 1999 12:17:03 -0500
2 From: mark david mcCreary <mdm@internet-tools.com>
4 Here is some sample code that might be useful for handling
5 X-Failed-Recipients headers generated by Exim, with mailing lists.
7 It will generate one bounce message for each bad address. The message
8 will be sent back to the mailing list that generated it.
10 It consists of some procmail routines to decipher which list generated
11 the error, with a Perl program to parse and generate the email message
12 for each email address in the X-Failed-Recipients: header.
14 You need Procmail 3.13 or better
16 You need Perl 5.004 plus the Internet::Mail Perl Module.
17 Or you can call your MTA directly from Perl, if you do not have
18 Internet::Mail Perl Module.
21 Step 1) Procmail recipe that gets called with bounces that Exim generates.
23 # The mail-list.com front-end for Smartlist Mailing Lists
25 # Copyright (c) 1999 Internet Tools, Inc.
27 # This program is free software; you can redistribute it and/or modify
28 # it under the terms of the GNU General Public License as published by
29 # the Free Software Foundation; either version 2 of the License, or
30 # (at your option) any later version.
35 # This code may be useful for deciphering error messages
38 # This routine will receive error messages generated by Exim
39 # Those messages will have a special header, called
41 # X-Failed-Recipients:
43 # Up to 50 email addresses can be listed on each header.
44 # There can be multiple X-Failed-Recipients: headers.
46 # This procmail procedure will determine the list name
48 # For example, if mailing list message was sent from
50 # elvis-admin@domain.com
52 # Then elvis would be the name of the list.
54 # Once the list name is determined based on the contents of the message,
55 # a perl program is called to parse the email addresess.
57 # In addition, a bounce error message is sent back to the mailing list,
58 # so it can be removed from the list.
63 PATH=.:/home/ftp/.bin:/bin:/usr/bin:/usr/local/bin:/usr/sbin:$PATH
70 LOGFILE=$HOME/procmailog
72 test=test # /usr/bin/test
73 mkdir=mkdir # /bin/mkdir
75 DOMAIN=domain.com # the common domain for all the lists
76 SUBDOMAIN=`hostname` # the fully qualified hostname
81 # save in folder for debugging purposes
84 # strip Reply-to and Sender addresses, as many people screw these up
85 # Then the formail -rtzx will try to grab that address
88 | formail -I "Reply-To:" -I "Sender:"
90 # log requestor email To: From: address
92 SENDER = `formail -rtzx To:`
93 SUBJECT = `formail -zxSubject:`
94 FROM = `formail -zxFrom:`
95 TODAY = `date "+%Y-%m-%d %T"`
97 # Figure out the listname being subscribed to
98 # Considered to be the base part, followed by -admin@domain-name.com
101 # If address does not end in -on, or otherwise cannot be determined, send to
102 # dummy-request for handling
107 # locate addresses within <> symbols first
110 * ^Envelope-to:.*[<]\/[^ ,@]+
114 INCLUDERC = /home/ftp/.etc/rc.rm_term
118 # otherwise, if no match above, just look for @ symbol to grab email address
121 * ^Envelope-to: \/[^ ,@]+
125 INCLUDERC = /home/ftp/.etc/rc.rm_term
129 # Exim generates bounce back messages with X-Failed-Recipients Headers
130 # If this email message is one of those, then process by a special
131 # perl program, which will generate a bounce message for each failed
136 * ^From:.*Mailer-Daemon@.*.domain.com
137 * ^X-Failed-Recipients:
138 | xfailed.pl $BASELIST-admin@[127.0.0.1]
143 Step 2) Procmail Sub routine to recurvisely parse email address
146 Place in /home/ftp or tweak script for where you place it.
149 # Copyright (c) 1997 Philip Guenther
152 # 3/05/97 Philip Guenther
154 # Remove a terminating string $TERM from $STRING, returning it in
155 # STRING. $TERM *must* start with a '-', as that's what this
156 # routine splits up STRING on. "found" is a temporary variable that
157 # must be empty on entry. The script will clear it on exit, so things
158 # should be fine as long as you don't use it yourself.
160 # Append the next 'word' in STRING to $found
162 # 10/15/97 mdm make result lower case for file name matchups
167 * STRING ?? $ ^^$\found\/${found+-}[^-]*
168 { found = "$found$MATCH" }
171 # This cannot happen unless found was set on entry!
172 LOG = "*** WARNING ***
173 variable 'found' was set to $found on entry to $_
174 one of the uses must be renamed for rm_term.rc to work!
183 * ! STRING ?? $ ^^$\found$TERM^^
190 # Return the match, and clear our temporary.
191 STRING = `echo $found | tr 'A-Z' 'a-z'`
198 Step 3) Sample Perl Code to parse X-Failed-Recipients: headers, and
199 send email message containing bounce error code.
203 # The mail-list.com front-end for Smartlist Mailing Lists
205 # Copyright (c) 1999 Internet Tools, Inc.
207 # This program is free software; you can redistribute it and/or modify
208 # it under the terms of the GNU General Public License as published by
209 # the Free Software Foundation; either version 2 of the License, or
210 # (at your option) any later version.
213 # When Exim is unable to deliver a message, it generates a bounceback
214 # with X-Failed_Recipients header(s).
216 # Each bad address is listed in these header(s).
218 # This program will generate bounce messages, and send them to the
219 # appropriate list-admin address.
221 # This program is invoked by a Procmail recipe, with the list-request
222 # address in the first and only argument.
224 # This program uses the Perl Module Mail::Internet to send each message.
229 ($list_address) = @ARGV;
233 chop(my $Date = `date "+%Y-%m-%d %T"`);
235 my $bounce_body = ' 550 '; # 550 is SMTP error code for user unknown
236 open(LOG,">>/tmp/xfailed.log") || die(" Could not open xfailed.log $!");
238 $ENV{'SMTPHOSTS'} = 'localhost';
240 my $mesg = new Mail::Internet \*STDIN;
242 # look at mail headers, and grab the data
244 my $from = $mesg->head->get('From'); chop($from);
246 # can be more than 1 X-Failed_Recipients header, so put into array
248 my @xfail = $mesg->head->get('X-Failed-Recipients');
250 $xfail = "@xfail"; # stick array of headers into scalar
251 chop($xfail); # remove final newline
253 # turn any newlines from middle of multiple X-Failed-Recipients into commas
257 my @tokens = split(/,\s/, $xfail); # put email address, minus commas into array
260 foreach $email_addr (@tokens) {
262 unless ($email_addr =~ m/\@/) {
266 unless ($list_address =~ m/\@/) {
270 # make the body of the message a simple bounce message that smartlist can handle
272 my $message_body = $bounce_body . "<" . $email_addr . ">" . "\n";
274 my $new_mesg = new Mail::Internet(
276 'Body' => [$message_body]
279 # this is who the mail is directed to via SMTP;
281 $ENV{MAILADDRESS} = $from;
283 # these are the addresses placed in the header block of the message.
285 $new_mesg->head()->add('From', $from);
286 $new_mesg->head()->add('To', $list_address);
287 $new_mesg->head()->add('Subject', 'bounce from xfailed');
289 # $new_mesg->print_header(\*LOG);
290 # $new_mesg->print_body(\*LOG);
292 print LOG "$Date\t$list_address\t$email_addr\n";
294 my @recips = $new_mesg->smtpsend;
296 unless (@recips > 0) {
297 print LOG "Failed to deliver ($from,$list_address,$email_addr) \n";