diff options
Diffstat (limited to 'Mailman/Handlers/Approve.py')
| -rw-r--r-- | Mailman/Handlers/Approve.py | 64 |
1 files changed, 26 insertions, 38 deletions
diff --git a/Mailman/Handlers/Approve.py b/Mailman/Handlers/Approve.py index 1ff58abf0..1198e6ea7 100644 --- a/Mailman/Handlers/Approve.py +++ b/Mailman/Handlers/Approve.py @@ -15,13 +15,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, # USA. -"""Determine whether the message is approved for delivery. - -This module only tests for definitive approvals. IOW, this module only -determines whether the message is definitively approved or definitively -denied. Situations that could hold a message for approval or confirmation are -not tested by this module. -""" +"""Determine whether the message is pre-approved for delivery.""" import re @@ -30,7 +24,7 @@ from email.Iterators import typed_subpart_iterator from Mailman import Errors from Mailman.configuration import config -NL = '\n' +EMPTYSTRING = '' @@ -38,18 +32,15 @@ def process(mlist, msg, msgdata): # Short circuits if msgdata.get('approved'): # Digests, Usenet postings, and some other messages come pre-approved. - # TBD: we may want to further filter Usenet messages, so the test - # above may not be entirely correct. + # XXX we may want to further filter Usenet messages, so the test above + # may not be entirely correct. return # See if the message has an Approved or Approve header with a valid - # list-moderator, list-admin. Also look at the first non-whitespace line - # in the file to see if it looks like an Approved header. We are - # specifically /not/ allowing the site admins password to work here - # because we want to discourage the practice of sending the site admin - # password through email in the clear. - missing = [] - passwd = msg.get('approved', msg.get('approve', missing)) - if passwd is missing: + # moderator password. Also look at the first non-whitespace line in the + # file to see if it looks like an Approved header. + missing = object() + password = msg.get('approved', msg.get('approve', missing)) + if password is missing: # Find the first text/plain part in the message part = None stripped = False @@ -57,22 +48,19 @@ def process(mlist, msg, msgdata): break # XXX I'm not entirely sure why, but it is possible for the payload of # the part to be None, and you can't splitlines() on None. - if part is not None and part.get_payload() is not None: - lines = part.get_payload(decode=True).splitlines() - line = '' - for lineno, line in zip(range(len(lines)), lines): + if part and part.get_payload() is not None: + lines = part.get_payload(decode=True).splitlines(True) + for lineno, line in enumerate(lines): if line.strip(): break - i = line.find(':') - if i >= 0: - name = line[:i] - value = line[i+1:] - if name.lower() in ('approve', 'approved'): - passwd = value.lstrip() + if ':' in line: + header, value = line.split(':', 1) + if header.lower() in ('approved', 'approve'): + password = value.strip() # Now strip the first line from the payload so the # password doesn't leak. del lines[lineno] - reset_payload(part, NL.join(lines)) + reset_payload(part, EMPTYSTRING.join(lines)) stripped = True if stripped: # MAS: Bug 1181161 - Now try all the text parts in case it's @@ -84,35 +72,35 @@ def process(mlist, msg, msgdata): # # This will process all the multipart/alternative parts in the # message as well as all other text parts. We shouldn't find the - # pattern outside the mp/a parts, but if we do, it is probably - # best to delete it anyway as it does contain the password. + # pattern outside the multipart/alternative parts, but if we do, + # it is probably best to delete it anyway as it does contain the + # password. # # Make a pattern to delete. We can't just delete a line because # line of HTML or other fancy text may include additional message # text. This pattern works with HTML. It may not work with rtf # or whatever else is possible. - pattern = name + ':(\s| )*' + re.escape(passwd) + pattern = header + ':(\s| )*' + re.escape(password) for part in typed_subpart_iterator(msg, 'text'): if part is not None and part.get_payload() is not None: lines = part.get_payload(decode=True) if re.search(pattern, lines): reset_payload(part, re.sub(pattern, '', lines)) - if passwd is not missing and mlist.Authenticate((config.AuthListModerator, - config.AuthListAdmin), - passwd): + if password is not missing and password == mlist.moderator_password: # BAW: should we definitely deny if the password exists but does not # match? For now we'll let it percolate up for further determination. msgdata['approved'] = True # Used by the Emergency module msgdata['adminapproved'] = True - # has this message already been posted to this list? + # Has this message already been posted to this list? beentheres = [s.strip().lower() for s in msg.get_all('x-beenthere', [])] - if mlist.GetListEmail().lower() in beentheres: + if mlist.posting_address in beentheres: raise Errors.LoopError + def reset_payload(part, payload): # Set decoded payload maintaining content-type, format and delsp. - # TK: Message with 'charset=' cause trouble. So, instead of + # TK: Messages with 'charset=' cause trouble. So, instead of # part.get_content_charset('us-ascii') ... cset = part.get_content_charset() or 'us-ascii' ctype = part.get_content_type() |
