Initial revision
[exim-website.git] / config.samples / C021
1 Date: Thu, 13 May 1999 12:17:03 -0500
2 From: mark david mcCreary <mdm@internet-tools.com>
3
4 Here is some sample code that might be useful for handling
5 X-Failed-Recipients headers generated by Exim, with mailing lists.
6
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.
9
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.
13
14 You need Procmail 3.13 or better
15
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.
19
20
21 Step 1) Procmail recipe that gets called with bounces that Exim generates.
22
23 #   The mail-list.com front-end for Smartlist Mailing Lists
24 #
25 #       Copyright (c) 1999 Internet Tools, Inc.
26 #
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.
31 #
32 #
33 #   X-Failed Routine
34 #
35 #   This code may be useful for deciphering error messages
36 #   for mailing lists.
37 #
38 #       This routine will receive error messages generated by Exim
39 #   Those messages will have a special header, called
40 #
41 #         X-Failed-Recipients:
42 #
43 #   Up to 50 email addresses can be listed on each header.
44 #   There can be multiple X-Failed-Recipients: headers.
45 #
46 #   This procmail procedure will determine the list name
47 #
48 #   For example, if mailing list message was sent from
49 #
50 #       elvis-admin@domain.com
51 #
52 #   Then elvis would be the name of the list.
53 #
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.
56 #
57 #   In addition, a bounce error message is sent back to the mailing list,
58 #   so it can be removed from the list.
59 #
60 #
61 #
62
63 PATH=.:/home/ftp/.bin:/bin:/usr/bin:/usr/local/bin:/usr/sbin:$PATH
64 SHELL=/bin/sh
65
66 #VERBOSE=yes
67 #LOGABSTRACT=all
68 VERBOSE=no
69 LOGABSTRACT=all
70 LOGFILE=$HOME/procmailog
71 COMSAT=no
72 test=test               # /usr/bin/test
73 mkdir=mkdir     # /bin/mkdir
74
75 DOMAIN=domain.com               # the common domain for all the lists
76 SUBDOMAIN=`hostname`            # the fully qualified hostname
77
78 SUFFIX=-admin
79
80 #  detect mail loop
81 #  save in folder for debugging purposes
82 #  terminate
83
84 #  strip Reply-to and Sender addresses, as many people screw these up
85 #  Then the formail -rtzx will try to grab that address
86
87 :0 fh
88 | formail -I "Reply-To:" -I "Sender:"
89
90 # log requestor email To: From: address
91
92 SENDER  = `formail -rtzx To:`
93 SUBJECT = `formail -zxSubject:`
94 FROM = `formail -zxFrom:`
95 TODAY   = `date "+%Y-%m-%d %T"`
96
97 #  Figure out the listname being subscribed to
98 #  Considered to be the base part, followed by -admin@domain-name.com
99 #
100 #
101 #  If address does not end in -on, or otherwise cannot be determined, send to
102 #  dummy-request for handling
103 #
104
105 BASELIST=dummy
106
107 #  locate addresses within <> symbols first
108
109 :0
110 * ^Envelope-to:.*[<]\/[^ ,@]+
111 {
112     STRING = $MATCH
113     TERM = $SUFFIX
114     INCLUDERC = /home/ftp/.etc/rc.rm_term
115     BASELIST = $STRING
116 }
117
118 # otherwise, if no match above, just look for @ symbol to grab email address
119
120 :0 E
121 * ^Envelope-to: \/[^ ,@]+
122 {
123     STRING = $MATCH
124     TERM = $SUFFIX
125     INCLUDERC = /home/ftp/.etc/rc.rm_term
126     BASELIST = $STRING
127 }
128
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
132 #  email address.
133
134
135 :0 h
136 * ^From:.*Mailer-Daemon@.*.domain.com
137 * ^X-Failed-Recipients:
138 | xfailed.pl $BASELIST-admin@[127.0.0.1]
139
140
141
142
143 Step 2)  Procmail Sub routine to recurvisely parse email address
144          by Philip Guenther.
145
146          Place in /home/ftp or tweak script for where you place it.
147
148 #
149 #   Copyright (c) 1997 Philip Guenther
150 #
151 #
152 #  3/05/97              Philip Guenther
153 #
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.
159
160 # Append the next 'word' in STRING to $found
161
162 #  10/15/97  mdm   make result lower case for file name matchups
163 #
164
165
166 :0
167 * STRING ?? $ ^^$\found\/${found+-}[^-]*
168 { found = "$found$MATCH" }
169 :0 E
170 {
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!
175 "
176     # Help me, help me!
177     :0:
178     $DEFAULT
179 }
180
181 # Are we done?
182 :0
183 * ! STRING ?? $ ^^$\found$TERM^^
184 {
185     # Nope
186     INCLUDERC = $_
187 }
188 :0 E
189 {
190     # Return the match, and clear our temporary.
191     STRING = `echo $found | tr 'A-Z' 'a-z'`
192     found
193 }
194
195
196
197
198 Step 3) Sample Perl Code to parse X-Failed-Recipients: headers, and
199         send email message containing bounce error code.
200
201 #!/usr/bin/perl -w
202 #
203 #   The mail-list.com front-end for Smartlist Mailing Lists
204 #
205 #    Copyright (c) 1999 Internet Tools, Inc.
206 #
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.
211 #
212 #
213 #  When Exim is unable to deliver a message, it generates a bounceback
214 #  with X-Failed_Recipients header(s).
215 #
216 #  Each bad address is listed in these header(s).
217 #
218 #  This program will generate bounce messages, and send them to the
219 #  appropriate list-admin address.
220 #
221 #  This program is invoked by a Procmail recipe, with the list-request
222 #  address in the first and only argument.
223 #
224 #  This program uses the Perl Module  Mail::Internet to send each message.
225 #
226 #
227 #
228
229 ($list_address) = @ARGV;
230
231 use Mail::Internet;
232
233 chop(my $Date = `date "+%Y-%m-%d %T"`);
234
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 $!");
237
238 $ENV{'SMTPHOSTS'} = 'localhost';
239
240 my $mesg = new Mail::Internet \*STDIN;
241
242 # look at mail headers, and grab the data
243
244 my $from = $mesg->head->get('From'); chop($from);
245
246 # can be more than 1 X-Failed_Recipients header, so put into array
247
248 my @xfail = $mesg->head->get('X-Failed-Recipients');
249
250 $xfail = "@xfail";        # stick array of headers into scalar
251 chop($xfail);             # remove final newline
252
253 # turn any newlines from middle of multiple X-Failed-Recipients into commas
254
255 $xfail =~ s/\n/,/g;
256
257 my @tokens = split(/,\s/, $xfail);        # put email address, minus commas into array
258
259 my $email_addr;
260 foreach $email_addr (@tokens) {
261
262     unless ($email_addr =~ m/\@/) {
263         next;
264     }
265
266     unless ($list_address =~ m/\@/) {
267         next;
268     }
269
270     # make the body of the message a simple bounce message that smartlist can handle
271
272     my $message_body = $bounce_body . "<" . $email_addr . ">" . "\n";
273
274     my $new_mesg = new Mail::Internet(
275         [ ],
276         'Body' => [$message_body]
277         );
278
279     # this is who the mail is directed to via SMTP;
280
281     $ENV{MAILADDRESS} = $from;
282
283     # these are the addresses placed in the header block of the message.
284
285     $new_mesg->head()->add('From', $from);
286     $new_mesg->head()->add('To', $list_address);
287     $new_mesg->head()->add('Subject', 'bounce from xfailed');
288
289 #    $new_mesg->print_header(\*LOG);
290 #    $new_mesg->print_body(\*LOG);
291
292     print LOG  "$Date\t$list_address\t$email_addr\n";
293
294     my @recips =  $new_mesg->smtpsend;
295
296     unless (@recips > 0) {
297                print LOG "Failed to deliver ($from,$list_address,$email_addr) \n";
298         next;
299     }
300
301 }
302
303 exit;