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