From b227ca6bb0c172f998421b2119a9f59dc01a2bca Mon Sep 17 00:00:00 2001
From: Stuart Gathman <stuart@gathman.org>
Date: Tue, 21 Nov 2006 18:45:49 +0000
Subject: [PATCH] Update a use of deprecated rfc822.  Recognize
 report-type=delivery-status

---
 bms.py | 264 ++++++++-------------------------------------------------
 1 file changed, 34 insertions(+), 230 deletions(-)

diff --git a/bms.py b/bms.py
index accad52..4ab1dc2 100644
--- a/bms.py
+++ b/bms.py
@@ -1,217 +1,8 @@
 #!/usr/bin/env python
 # A simple milter that has grown quite a bit.
 # $Log$
-# Revision 1.68  2006/10/04 03:46:01  customdesigned
-# Fix defaults.
 #
-# Revision 1.67  2006/10/01 01:44:06  customdesigned
-# case_sensitive_localpart option, more delayed bounce heuristics,
-# optional smart_alias section.
-#
-# Revision 1.66  2006/07/26 16:42:26  customdesigned
-# Support CBV timeout
-#
-# Revision 1.65  2006/06/21 22:22:00  customdesigned
-# Handle multi-line headers in delayed dsns.
-#
-# Revision 1.64  2006/06/21 21:12:04  customdesigned
-# More delayed reject token headers.
-# Don't require HELO pass for CBV.
-#
-# Revision 1.63  2006/05/21 03:41:44  customdesigned
-# Fail dsn
-#
-# Revision 1.61  2006/05/17 21:28:07  customdesigned
-# Create GOSSiP record only when connection will procede to DATA.
-#
-# Revision 1.60  2006/05/12 16:14:48  customdesigned
-# Don't require SPF pass for white/black listing mail from trusted relay.
-# Support localpart wildcard for white and black lists.
-#
-# Revision 1.59  2006/04/06 18:14:17  customdesigned
-# Check whitelist/blacklist even when not checking SPF (e.g. trusted relay).
-#
-# Revision 1.58  2006/03/10 20:52:49  customdesigned
-# Use re to recognize failure DSNs.
-#
-# Revision 1.57  2006/03/07 20:50:54  customdesigned
-# Use signed Message-ID in delayed reject to blacklist senders
-#
-# Revision 1.56  2006/02/24 02:12:54  customdesigned
-# Properly report hard PermError (lax mode fails also) by always setting
-# perm_error attribute with PermError exception.  Improve reporting of
-# invalid domain PermError.
-#
-# Revision 1.55  2006/02/17 05:04:29  customdesigned
-# Use SRS sign domain list.
-# Accept but do not use for training whitelisted senders without SPF pass.
-# Immediate rejection of unsigned bounces.
-#
-# Revision 1.54  2006/02/16 02:16:36  customdesigned
-# User specific SPF receiver policy.
-#
-# Revision 1.53  2006/02/12 04:15:01  customdesigned
-# Remove spf dependency for iniplist
-#
-# Revision 1.52  2006/02/12 02:12:08  customdesigned
-# Use CIDR notation for internal connect list.
-#
-# Revision 1.51  2006/02/12 01:13:58  customdesigned
-# Don't check rcpt user list when signed MFROM.
-#
-# Revision 1.50  2006/02/09 20:39:43  customdesigned
-# Use CIDR notation for trusted_relay iplist
-#
-# Revision 1.49  2006/01/30 23:14:48  customdesigned
-# put back eom condition
-#
-# Revision 1.48  2006/01/12 20:31:24  customdesigned
-# Accelerate training via whitelist and blacklist.
-#
-# Revision 1.47  2005/12/29 04:49:10  customdesigned
-# Do not auto-whitelist autoreplys
-#
-# Revision 1.46  2005/12/28 20:17:29  customdesigned
-# Expire and renew AddrCache entries
-#
-# Revision 1.45  2005/12/23 22:34:46  customdesigned
-# Put guessed result in separate header.
-#
-# Revision 1.44  2005/12/23 21:47:07  customdesigned
-# Move Received-SPF header to top.
-#
-# Revision 1.43  2005/12/09 16:54:01  customdesigned
-# Select neutral DSN template for best_guess
-#
-# Revision 1.42  2005/12/01 22:42:32  customdesigned
-# improve gossip support.
-# Initialize srs_domain from srs.srs config property.  Should probably
-# always block unsigned DSN when signing all.
-#
-# Revision 1.41  2005/12/01 18:59:25  customdesigned
-# Fix neutral policy.  pobox.com -> openspf.org
-#
-# Revision 1.40  2005/11/07 21:22:35  customdesigned
-# GOSSiP support, local database only.
-#
-# Revision 1.39  2005/10/31 00:04:58  customdesigned
-# Simple implementation of trusted_forwarder list.  Inefficient for
-# more than 1 or 2 entries.
-#
-# Revision 1.38  2005/10/28 19:36:54  customdesigned
-# Don't check internal_domains for trusted_relay.
-#
-# Revision 1.37  2005/10/28 09:30:49  customdesigned
-# Do not send quarantine DSN when sender is DSN.
-#
-# Revision 1.36  2005/10/23 16:01:29  customdesigned
-# Consider MAIL FROM a match for supply_sender when a subdomain of From or Sender
-#
-# Revision 1.35  2005/10/20 18:47:27  customdesigned
-# Configure auto_whitelist senders.
-#
-# Revision 1.34  2005/10/19 21:07:49  customdesigned
-# access.db stores keys in lower case
-#
-# Revision 1.33  2005/10/19 19:37:50  customdesigned
-# Train screener on whitelisted messages.
-#
-# Revision 1.32  2005/10/14 16:17:31  customdesigned
-# Auto whitelist refinements.
-#
-# Revision 1.31  2005/10/14 01:14:08  customdesigned
-# Auto whitelist feature.
-#
-# Revision 1.30  2005/10/12 16:36:30  customdesigned
-# Release 0.8.3
-#
-# Revision 1.29  2005/10/11 22:50:07  customdesigned
-# Always check HELO except for SPF pass, temperror.
-#
-# Revision 1.28  2005/10/10 23:50:20  customdesigned
-# Use logging module to make logging threadsafe (avoid splitting log lines)
-#
-# Revision 1.27  2005/10/10 20:15:33  customdesigned
-# Configure SPF policy via sendmail access file.
-#
-# Revision 1.26  2005/10/07 03:23:40  customdesigned
-# Banned users option.  Experimental feature to supply Sender when
-# missing and MFROM domain doesn't match From.  Log cipher bits for
-# SMTP AUTH.  Sketch access file feature.
-#
-# Revision 1.25  2005/09/08 03:55:08  customdesigned
-# Handle perverse MFROM quoting.
-#
-# Revision 1.24  2005/08/18 03:36:54  customdesigned
-# Don't innoculate with SCREENED mail.
-#
-# Revision 1.23  2005/08/17 19:35:27  customdesigned
-# Send DSN before adding message to quarantine.
-#
-# Revision 1.22  2005/08/11 22:17:58  customdesigned
-# Consider SMTP AUTH connections internal.
-#
-# Revision 1.21  2005/08/04 21:21:31  customdesigned
-# Treat fail like softfail for selected (braindead) domains.
-# Treat mail according to extended processing results, but
-# report any PermError that would officially result via DSN.
-#
-# Revision 1.20  2005/08/02 18:04:35  customdesigned
-# Keep screened honeypot mail, but optionally discard honeypot only mail.
-#
-# Revision 1.19  2005/07/20 03:30:04  customdesigned
-# Check pydspam version for honeypot, include latest pyspf changes.
-#
-# Revision 1.18  2005/07/17 01:25:44  customdesigned
-# Log as well as use extended result for best guess.
-#
-# Revision 1.17  2005/07/15 20:25:36  customdesigned
-# Use extended results processing for best_guess.
-#
-# Revision 1.16  2005/07/14 03:23:33  customdesigned
-# Make SES package optional.  Initial honeypot support.
-#
-# Revision 1.15  2005/07/06 04:05:40  customdesigned
-# Initial SES integration.
-#
-# Revision 1.14  2005/07/02 23:27:31  customdesigned
-# Don't match hostnames for internal connects.
-#
-# Revision 1.13  2005/07/01 16:30:24  customdesigned
-# Always log trusted Received and Received-SPF headers.
-#
-# Revision 1.12  2005/06/20 22:35:35  customdesigned
-# Setreply for rejectvirus.
-#
-# Revision 1.11  2005/06/17 02:07:20  customdesigned
-# Release 0.8.1
-#
-# Revision 1.10  2005/06/16 18:35:51  customdesigned
-# Ignore HeaderParseError decoding header
-#
-# Revision 1.9  2005/06/14 21:55:29  customdesigned
-# Check internal_domains for outgoing mail.
-#
-# Revision 1.8  2005/06/06 18:24:59  customdesigned
-# Properly log exceptions from pydspam
-#
-# Revision 1.7  2005/06/04 19:41:16  customdesigned
-# Fix bugs from testing RPM
-#
-# Revision 1.6  2005/06/03 04:57:05  customdesigned
-# Organize config reader by section.  Create defang section.
-#
-# Revision 1.5  2005/06/02 15:00:17  customdesigned
-# Configure banned extensions.  Scan zipfile option with test case.
-#
-# Revision 1.4  2005/06/02 04:18:55  customdesigned
-# Update copyright notices after reading article on /.
-#
-# Revision 1.3  2005/06/02 02:09:00  customdesigned
-# Record timestamp in send_dsn.log
-#
-# Revision 1.2  2005/06/02 01:00:36  customdesigned
-# Support configurable templates for DSNs.
+# See ChangeLog
 #
 # Author: Stuart D. Gathman <stuart@bmsi.com>
 # Copyright 2001,2002,2003,2004,2005 Business Management Systems, Inc.
@@ -237,6 +28,7 @@ from Milter.dynip import is_dynip as dynip
 
 from fnmatch import fnmatchcase
 from email.Header import decode_header
+from email.Utils import getaddresses
 
 # Import gossip if available
 try:
@@ -1361,26 +1153,36 @@ class bmsMilter(Milter.Milter):
     for name,val,idx in self.new_headers:
       self.fp.write("%s: %s\n" % (name,val))	# add new headers to buffer
     self.fp.write("\n")				# terminate headers
-    # log when neither sender nor from domains matches mail from domain
-    if supply_sender and self.mailfrom != '<>' and not self.internal_connection:
-      mf_domain = self.canon_from.split('@')[-1]
-      self.fp.seek(0)
-      msg = rfc822.Message(self.fp)
-      for rn,hf in msg.getaddrlist('from')+msg.getaddrlist('sender'):
-	t = parse_addr(hf)
-	if len(t) == 2:
-	  hd = t[1].lower()
-	  if hd == mf_domain or mf_domain.endswith('.'+hd): break
-      else:
-	for f in msg.getallmatchingheaders('from'):
-	  self.log(f)
-	sender = msg.getallmatchingheaders('sender')
-	if sender:
-	  for f in sender:
-	    self.log(f)
+    if not self.internal_connection:
+      msg = None	# parse headers only if needed
+      if not self.delayed_failure:
+	self.fp.seek(0)
+	msg = email.message_from_file(self.fp)
+	if msg.get_param('report-type','').lower() == 'delivery-status':
+	  self.is_bounce = True
+	  self.delayed_failure = msg.get('subject','DSN')
+      # log when neither sender nor from domains matches mail from domain
+      if supply_sender and self.mailfrom != '<>':
+	if not msg:
+	  self.fp.seek(0)
+	  msg = email.message_from_file(self.fp)
+	mf_domain = self.canon_from.split('@')[-1]
+	for rn,hf in getaddresses(msg.get_all('from',[])
+		+ msg.get_all('sender',[])):
+	  t = parse_addr(hf)
+	  if len(t) == 2:
+	    hd = t[1].lower()
+	    if hd == mf_domain or mf_domain.endswith('.'+hd): break
 	else:
-	  self.log("NOTE: Supplying MFROM as Sender");
-	  self.add_header('Sender',self.mailfrom)
+	  for f in msg.get_all('from'):
+	    self.log(f)
+	  sender = msg.get_all('sender')
+	  if sender:
+	    for f in sender:
+	      self.log(f)
+	  else:
+	    self.log("NOTE: Supplying MFROM as Sender");
+	    self.add_header('Sender',self.mailfrom)
       del msg
     # copy headers to a temp file for scanning the body
     self.fp.seek(0)
@@ -1645,7 +1447,9 @@ class bmsMilter(Milter.Milter):
 		return Milter.DISCARD
 	    except: continue
 	  lnl = ln.lower()
-	  for k in ('message-id','x-mailer','sender'):
+	  if lnl.startswith('action:'):
+	    if lnl.split()[-1] != 'failed': break
+	  for k in ('message-id:','x-mailer:','sender:'):
 	    if lnl.startswith(k):
 	      lastln = ln
 	      break
-- 
GitLab