diff options
Diffstat (limited to 'Mailman/Handlers/MimeDel.py')
-rw-r--r-- | Mailman/Handlers/MimeDel.py | 42 |
1 files changed, 39 insertions, 3 deletions
diff --git a/Mailman/Handlers/MimeDel.py b/Mailman/Handlers/MimeDel.py index 3bcdaffa..79efa620 100644 --- a/Mailman/Handlers/MimeDel.py +++ b/Mailman/Handlers/MimeDel.py @@ -26,6 +26,7 @@ contents. import os import errno import tempfile +from os.path import splitext from email.Iterators import typed_subpart_iterator @@ -36,6 +37,7 @@ from Mailman.Queue.sbcache import get_switchboard from Mailman.Logging.Syslog import syslog from Mailman.Version import VERSION from Mailman.i18n import _ +from Mailman.Utils import oneline @@ -59,12 +61,23 @@ def process(mlist, msg, msgdata): if passtypes and not (ctype in passtypes or mtype in passtypes): dispose(mlist, msg, msgdata, _("The message's content type was not explicitly allowed")) + # Filter by file extensions + filterexts = mlist.filter_filename_extensions + passexts = mlist.pass_filename_extensions + fext = get_file_ext(msg) + if fext: + if fext in filterexts: + dispose(mlist, msg, msgdata, + _("The message's file extension was explicitly disallowed")) + if passexts and not (fext in passexts): + dispose(mlist, msg, msgdata, + _("The message's file extension was not explicitly allowed")) numparts = len([subpart for subpart in msg.walk()]) # If the message is a multipart, filter out matching subparts if msg.is_multipart(): # Recursively filter out any subparts that match the filter list prelen = len(msg.get_payload()) - filter_parts(msg, filtertypes, passtypes) + filter_parts(msg, filtertypes, passtypes, filterexts, passexts) # If the outer message is now an empty multipart (and it wasn't # before!) then, again it gets discarded. postlen = len(msg.get_payload()) @@ -121,7 +134,7 @@ def reset_payload(msg, subpart): -def filter_parts(msg, filtertypes, passtypes): +def filter_parts(msg, filtertypes, passtypes, filterexts, passexts): # Look at all the message's subparts, and recursively filter if not msg.is_multipart(): return 1 @@ -129,7 +142,8 @@ def filter_parts(msg, filtertypes, passtypes): prelen = len(payload) newpayload = [] for subpart in payload: - keep = filter_parts(subpart, filtertypes, passtypes) + keep = filter_parts(subpart, filtertypes, passtypes, + filterexts, passexts) if not keep: continue ctype = subpart.get_content_type() @@ -140,6 +154,13 @@ def filter_parts(msg, filtertypes, passtypes): if passtypes and not (ctype in passtypes or mtype in passtypes): # Throw this subpart away continue + # check file extension + fext = get_file_ext(subpart) + if fext: + if fext in filterexts: + continue + if passexts and not (fext in passexts): + continue newpayload.append(subpart) # Check to see if we discarded all the subparts postlen = len(newpayload) @@ -218,3 +239,18 @@ are receiving the only remaining copy of the discarded message. badq.enqueue(msg, msgdata) # Most cases also discard the message raise Errors.DiscardMessage + +def get_file_ext(m): + """ + Get filename extension. Caution: some virus don't put filename + in 'Content-Disposition' header. +""" + fext = '' + filename = m.get_filename('') or m.get_param('name', '') + if filename: + fext = splitext(oneline(filename,'utf-8'))[1] + if len(fext) > 1: + fext = fext[1:] + else: + fext = '' + return fext |