aboutsummaryrefslogtreecommitdiffstats
path: root/Mailman/Handlers
diff options
context:
space:
mode:
Diffstat (limited to 'Mailman/Handlers')
-rw-r--r--Mailman/Handlers/MimeDel.py42
-rw-r--r--Mailman/Handlers/Scrubber.py18
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