diff options
Diffstat (limited to 'Mailman/Handlers')
-rw-r--r-- | Mailman/Handlers/MimeDel.py | 42 | ||||
-rw-r--r-- | Mailman/Handlers/Scrubber.py | 18 |
2 files changed, 55 insertions, 5 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 diff --git a/Mailman/Handlers/Scrubber.py b/Mailman/Handlers/Scrubber.py index 8c1124ec..7429c0b4 100644 --- a/Mailman/Handlers/Scrubber.py +++ b/Mailman/Handlers/Scrubber.py @@ -168,6 +168,12 @@ def process(mlist, msg, msgdata=None): outer = True if msgdata is None: msgdata = {} + if msgdata: + # msgdata is available if it is in GLOBAL_PIPELINE + # ie. not in digest or archiver + # check if the list owner want to scrub regular delivery + if not mlist.scrub_nondigest: + return dir = calculate_attachments_dir(mlist, msg, msgdata) charset = None lcset = Utils.GetCharSet(mlist.preferred_language) @@ -389,8 +395,16 @@ def save_attachment(mlist, msg, dir, filter_html=True): # e.g. image/jpg (should be image/jpeg). For now we just store such # things as application/octet-streams since that seems the safest. ctype = msg.get_content_type() - fnext = os.path.splitext(msg.get_filename(''))[1] - ext = guess_extension(ctype, fnext) + # i18n file name is encoded + lcset = Utils.GetCharSet(mlist.preferred_language) + filename = Utils.oneline(msg.get_filename(''), lcset) + fnext = os.path.splitext(filename)[1] + # For safety, we should confirm this is valid ext for content-type + # but we can use fnext if we introduce fnext filtering + if mm_cfg.SCRUBBER_USE_ATTACHMENT_FILENAME_EXTENSION: + ext = fnext + else: + ext = guess_extension(ctype, fnext) if not ext: # We don't know what it is, so assume it's just a shapeless # application/octet-stream, unless the Content-Type: is |