#! /usr/bin/python ######################################################### # # Copyright 2000-2003 by Ralf Hildebrandt # # last changes: # # 15/01/2002 # BUGFIX: Use sendmail -i instead of just sendmail! # # 13/01/2002 # Added Exception handlers # # 08/09/2002 # Added a chdir("/tmp") # License change to the GPL # # 15/05/2002 # I made fuzzy exit with EX_TEMPFAIL to prevent mail from # bouncing in case of an error. # Also, I made some additions to the German text # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # ######################################################### import os, popen2, string, sys, MimeWriter, mimetools, sys, re import agrepy, syslog EX_TEMPFAIL = 75 # temporary error code, causes postfix to retry max_errors = 4 # max. 4 typos min_length = 4 # min. 4 characters long email address max_recipients = 5 # maximum number of recipients returned to the sender # of the mail sendmail_binary = "/usr/sbin/sendmail -i -f \"\"" # yes, injection via SMTP would be more performant def extract_parameter(parameter): postconf = "/usr/sbin/postconf -h" try: result = os.popen(string.join([postconf,parameter])).readline() except: print "%s: Can't run \'%s\' from Postfix\'s local!" % string.join([postconf,parameter]) sys.exit(EX_TEMPFAIL) if re.search("^\$", result): return extract_parameter(result[1:]) return result[:len(result)-1] # these should be parsed from main.cf mydomain = extract_parameter("mydomain") myorigin = extract_parameter("myorigin") myhostname = extract_parameter("myhostname") double_bounce_notice_recipient = extract_parameter("2bounce_notice_recipient") # A Bcc: Address for checking the behaviour of fuzzy #bccaddress = "postmaster@" + myorigin bccaddress = "" try: syslog.openlog("fuzzy", (syslog.LOG_PID | syslog.LOG_NDELAY), syslog.LOG_MAIL) except: print "%s: Cannot write to syslog" sys.exit(EX_TEMPFAIL) try: os.chdir("/tmp") except: print "%s: Can't chdir(\"/tmp\")" sys.exit(EX_TEMPFAIL) if len(sys.argv) == 1: print "%s: No arguments given!\n" % (sys.argv[0]) sys.exit(EX_TEMPFAIL) try: extension = os.environ['EXTENSION'] sender = os.environ['SENDER'] except: print "%s: You're not running %s from Postfix\'s local!" % (sys.argv[0], sys.argv[0]) sys.exit(EX_TEMPFAIL) if sender == "": sender = double_bounce_notice_recipient # create a bounce which will go to postmaster #syslog.syslog(syslog.LOG_NOTICE, "sender: " + `sender` + " extension: " + `extension` ) lines = [] for filename in sys.argv[1:]: try: infile = open(filename, 'r') except: print "%s: Cannot open text file %s for reading\n" % (sys.argv[0], filename) sys.exit(EX_TEMPFAIL) lines = lines + infile.readlines() infile.close() # "lines" now contains all lines of all files # We should remove lines matching "^#" lhs = [] commentline = re.compile("^#") whitespace = re.compile("/W+") for line in lines: if not commentline.match(line): lhs.append(whitespace.split(line)[0] ) lines = lhs intended_recipients = [] cur_errors = 0 while ((intended_recipients == []) and (cur_errors < max_errors)): cur_errors = cur_errors + 1 pattern = agrepy.compile(extension, len(extension), cur_errors) # compile a new pattern with our current number of allowed errors if len(extension) >= min_length: # perform no matching if the length of the original recipient is smaller than min_length characters for line in lines: try: recipient = string.split(line)[0] except: recipient = "" if agrepy.agrepy(extension, len(extension), recipient, len(recipient), cur_errors, pattern): if recipient not in intended_recipients: intended_recipients.append( recipient ) if ( 0 < len(intended_recipients) <= max_recipients ): syslog.syslog(syslog.LOG_NOTICE, "found " + `len(intended_recipients)` + " possible recipients, allowing " + `cur_errors` + " errors" ) else: syslog.syslog(syslog.LOG_NOTICE, "found " + `len(intended_recipients)` + " possible recipients (too many!), allowing " + `cur_errors` + " errors" ) syslog.closelog() sendmail = popen2.Popen3(string.join([sendmail_binary, sender, bccaddress])).tochild # create a MIME message bounce = MimeWriter.MimeWriter(sendmail) # Write headers bounce.addheader("From", "MAILER-DAEMON@" + myorigin + " (Mail Delivery System)") bounce.addheader("To", sender) bounce.addheader("Reply-To", "postmaster@" + myorigin) bounce.addheader("Subject", "Undelivered Mail Returned to Sender / Tippfehler in Empfaengeradresse") bounce.addheader("MIME-Version", "1.0") bounce.flushheaders() # Start mulitpart bounce.startmultipartbody("mixed") # Start first part firstpart = bounce.nextpart() firstpart.addheader("Content-Description", "Notification") # Start body of first part firstpartfile = firstpart.startbody("text/plain") #firstpart.addheader("Content-Description", "Notification")firstpart.flushheaders() firstpartfile.write( "Dies ist Postfix auf der Maschine " + myhostname + "\n\n") firstpartfile.write( "Ich bedauere Ihnen mitteilen zu muessen, dass die folgende\n") firstpartfile.write( "Nachricht an einen oder mehrere Empfaenger nicht zugestellt\n") firstpartfile.write( "werden konnte.\n\n") firstpartfile.write( "This is the Postfix program at " + myhostname + "\n") firstpartfile.write( "I\'m sorry to have to inform you that the message returned\n") firstpartfile.write( "below could not be delivered to one or more destinations.\n\n") if ( 0 < len(intended_recipients) <= max_recipients ): firstpartfile.write( "Moegliche Empfaenger fuer den unbekannten Benutzer\n\n") firstpartfile.write( " <" + extension + "@" + mydomain + ">\n\n") firstpartfile.write( "sind:\n\n" ) for intended_recipient in intended_recipients: firstpartfile.write(" <" + intended_recipient + ">\n") firstpartfile.write("Bitte lesen Sie GENAU! Da ist ein winziger Unterschied den Sie sonst uebersehen!\n\n") firstpartfile.write("Bitte senden Sie die Mail selbst an den korrekten Empfaenger.\n\n") firstpartfile.write("Die Adminstratoren erhalten aus Datenschutzgruenden KEINE Kopie dieser Mail\n") firstpartfile.write("und koennen daher die Mail nicht an Ihrer Statt weiterleiten!\n") firstpartfile.write("Normalerweise lautet die Emailadresse \n") firstpartfile.write( "Possible recipients for unknown user\n") firstpartfile.write( " <" + extension + "@" + mydomain + ">\n\n") firstpartfile.write( "are:\n\n" ) for intended_recipient in intended_recipients: firstpartfile.write(" <" + intended_recipient + ">\n") firstpartfile.write("Please look CLOSELY! There is a difference you usually will not notice!\n\n") firstpartfile.write("Please send your Mail to the correct recpient yourself.\n\n") firstpartfile.write("The administrators do not receive a copy of this mail, due to German data\n") firstpartfile.write("protection laws and thus cannot forward the mail for you!\n") firstpartfile.write("Usually the Emailaddress is \n") else: firstpartfile.write("Benutzer unbekannt:\n") firstpartfile.write(" <" + extension + "@" + mydomain + ">\n") firstpartfile.write("User unknown:\n") firstpartfile.write(" <" + extension + "@" + mydomain + ">\n") firstpartfile.write( "\n") firstpartfile.write( "Wenn Sie verzweifelt sind, kontaktieren Sie bitte \n\n") firstpartfile.write( " Das Postfix Programm\n\n") firstpartfile.write( "If you need help, please contact \n\n") firstpartfile.write( "If you do so, please include this problem report. You can\n") firstpartfile.write( "delete your own text from the message returned below.\n\n") firstpartfile.write( " The Postfix program\n\n") # Start second part secondpart = bounce.nextpart() secondpart.addheader("Content-Description", "Undelivered Message") # Start body of second part try: secondpartfile = secondpart.startbody("message/rfc822") except Exception, e: print "%s: Cannot Start body of second part\n" % sys.argv[0] print e sys.exit(EX_TEMPFAIL) # Read the mail from STDIN and write it to sendmail line = sys.stdin.readline() while line: try: secondpartfile.write(line) except Exception, e: print "%s: Cannot write a line of the second part\n" % sys.argv[0] print e sys.exit(EX_TEMPFAIL) line = sys.stdin.readline() #secondpartfile.write(".\n") bounce.lastpart ()