Commit 7592ba51 authored by Barry Warsaw's avatar Barry Warsaw

Pick lint. Down to 4137 lines of warnings.

parent 91020e5c
......@@ -48,6 +48,7 @@ except ImportError:
# however, we'll initialize it differently for tests. We have to do it this
# early so that module contents is set up before anything that needs it is
# imported.
# pylint: disable-msg=E0611
if sys.argv[0].split(os.sep)[-1] == 'test':
from mailman.testing.i18n import initialize
else:
......
......@@ -32,14 +32,14 @@ from zope.component import getUtility
from zope.interface import implements
from mailman import Utils
from mailman.config import config
from mailman.core.i18n import _
from mailman.email.message import Message, UserNotification
from mailman.email.message import UserNotification
from mailman.interfaces.handler import IHandler
from mailman.interfaces.languages import ILanguageManager
# pylint: disable-msg=W0232,R0201
class Acknowledge:
"""Send an acknowledgment."""
implements(IHandler)
......
......@@ -34,6 +34,7 @@ from mailman.interfaces.handler import IHandler
# pylint: disable-msg=W0232,R0201,W0613
class AfterDelivery:
"""Perform some bookkeeping after a successful post."""
......
......@@ -31,7 +31,7 @@ __all__ = [
]
from email.Utils import getaddresses, formataddr
from email.utils import getaddresses, formataddr
from zope.interface import implements
from mailman.core.i18n import _
......@@ -42,6 +42,7 @@ COMMASPACE = ', '
# pylint: disable-msg=W0232,R0201
class AvoidDuplicates:
"""If the user wishes it, do not send duplicates of the same message."""
......
......@@ -41,6 +41,7 @@ from mailman.interfaces.member import DeliveryStatus
# pylint: disable-msg=W0232,R0201
class CalculateRecipients:
"""Calculate the regular (i.e. non-digest) recipients of the message."""
......@@ -50,6 +51,7 @@ class CalculateRecipients:
description = _('Calculate the regular recipients of the message.')
def process(self, mlist, msg, msgdata):
"""See `IHandler`."""
# Short circuit if we've already calculated the recipients list,
# regardless of whether the list is empty or not.
if 'recipients' in msgdata:
......@@ -81,9 +83,10 @@ class CalculateRecipients:
# Bad Urgent: password, so reject it instead of passing it on.
# I think it's better that the sender know they screwed up
# than to deliver it normally.
# pylint: disable-msg=W0612
realname = mlist.real_name
text = _("""\
Your urgent message to the %(realname)s mailing list was not authorized for
Your urgent message to the $realname mailing list was not authorized for
delivery. The original message as received by Mailman is attached.
""")
raise errors.RejectMessage(Utils.wrap(text))
......@@ -102,6 +105,7 @@ delivery. The original message as received by Mailman is attached.
def do_topic_filters(mlist, msg, msgdata, recipients):
"""Filter out recipients based on topics."""
if not mlist.topics_enabled:
# MAS: if topics are currently disabled for the list, send to all
# regardless of ReceiveNonmatchingTopics
......
......@@ -27,7 +27,7 @@ __all__ = [
import logging
from email.Utils import formataddr
from email.utils import formataddr
from zope.interface import implements
from mailman.core.i18n import _
......@@ -39,6 +39,7 @@ log = logging.getLogger('mailman.post')
# pylint: disable-msg=W0232,R0201,W0613
class Cleanse:
"""Cleanse certain headers from all messages."""
......
......@@ -42,6 +42,7 @@ from mailman.interfaces.handler import IHandler
# pylint: disable-msg=W0232,R0201,W0613
class CleanseDKIM:
"""Remove DomainKeys headers."""
......
......@@ -48,10 +48,13 @@ nonascii = re.compile('[^\s!-~]')
def uheader(mlist, s, header_name=None, continuation_ws='\t', maxlinelen=None):
# Get the charset to encode the string in. Then search if there is any
# non-ascii character is in the string. If there is and the charset is
# us-ascii then we use iso-8859-1 instead. If the string is ascii only
# we use 'us-ascii' if another charset is specified.
"""Get the charset to encode the string in.
Then search if there is any non-ascii character is in the string. If
there is and the charset is us-ascii then we use iso-8859-1 instead. If
the string is ascii only we use 'us-ascii' if another charset is
specified.
"""
charset = mlist.preferred_language.charset
if nonascii.search(s):
# use list charset but ...
......@@ -65,6 +68,7 @@ def uheader(mlist, s, header_name=None, continuation_ws='\t', maxlinelen=None):
def process(mlist, msg, msgdata):
"""Process the headers of the message."""
# Set the "X-Ack: no" header if noack flag is set.
if msgdata.get('noack'):
del msg['x-ack']
......@@ -157,7 +161,7 @@ def process(mlist, msg, msgdata):
# Also skip Cc if this is an anonymous list as list posting address
# is already in From and Reply-To in this case.
if (mlist.personalize == Personalization.full and
mlist.reply_goes_to_list <> ReplyToMunging.point_to_list and
mlist.reply_goes_to_list != ReplyToMunging.point_to_list and
not mlist.anonymous_list):
# Watch out for existing Cc headers, merge, and remove dups. Note
# that RFC 2822 says only zero or one Cc header is allowed.
......@@ -179,7 +183,6 @@ def process(mlist, msg, msgdata):
if msgdata.get('_nolist') or not mlist.include_rfc2369_headers:
return
# This will act like an email address for purposes of formataddr()
cset = mlist.preferred_language.charset
if mlist.description:
# Don't wrap the header since here we just want to get it properly RFC
# 2047 encoded.
......@@ -237,9 +240,12 @@ def process(mlist, msg, msgdata):
def prefix_subject(mlist, msg, msgdata):
# Add the subject prefix unless the message is a digest or is being fast
# tracked (e.g. internally crafted, delivered to a single user such as the
# list admin).
"""Maybe add a subject prefix.
Add the subject prefix unless the message is a digest or is being fast
tracked (e.g. internally crafted, delivered to a single user such as the
list admin).
"""
if not mlist.subject_prefix.strip():
return
prefix = mlist.subject_prefix
......@@ -341,6 +347,7 @@ def ch_oneline(headerstr):
# pylint: disable-msg=W0232,R0201
class CookHeaders:
"""Modify message headers."""
......
......@@ -28,11 +28,10 @@ __all__ = [
import re
import logging
from email.MIMEText import MIMEText
from email.mime.text import MIMEText
from zope.component import getUtility
from zope.interface import implements
from mailman.config import config
from mailman.core.i18n import _
from mailman.email.message import Message
from mailman.interfaces.handler import IHandler
......@@ -45,7 +44,8 @@ log = logging.getLogger('mailman.error')
def process(mlist, msg, msgdata):
# Digests and Mailman-craft messages should not get additional headers
"""Decorate the message with headers and footers."""
# Digests and Mailman-craft messages should not get additional headers.
if msgdata.get('isdigest') or msgdata.get('nodecorate'):
return
d = {}
......@@ -96,7 +96,7 @@ def process(mlist, msg, msgdata):
wrap = True
if not msg.is_multipart() and msgtype == 'text/plain':
# Save the RFC-3676 format parameters.
format = msg.get_param('format')
format_param = msg.get_param('format')
delsp = msg.get_param('delsp')
# Save 'Content-Transfer-Encoding' header in case decoration fails.
cte = msg.get('content-transfer-encoding')
......@@ -120,8 +120,8 @@ def process(mlist, msg, msgdata):
except UnicodeError:
pass
else:
if format:
msg.set_param('format', format)
if format_param:
msg.set_param('format', format_param)
if delsp:
msg.set_param('delsp', delsp)
wrap = False
......@@ -196,6 +196,7 @@ def process(mlist, msg, msgdata):
def decorate(mlist, template, extradict=None):
"""Expand the decoration template."""
# Create a dictionary which includes the default set of interpolation
# variables allowed in headers and footers. These will be augmented by
# any key/value pairs in the extradict.
......@@ -216,6 +217,7 @@ def decorate(mlist, template, extradict=None):
# pylint: disable-msg=W0232,R0201
class Decorate:
"""Decorate a message with headers and footers."""
......
......@@ -35,6 +35,7 @@ from mailman.interfaces.handler import IHandler
# pylint: disable-msg=W0232,R0201,W0613
class FileRecipients:
"""Get the normal delivery recipients from an include file."""
......@@ -51,8 +52,8 @@ class FileRecipients:
try:
with open(filename) as fp:
addrs = set(line.strip() for line in fp)
except IOError, e:
if e.errno <> errno.ENOENT:
except IOError as error:
if error.errno != errno.ENOENT:
raise
msgdata['recipients'] = set()
return
......
......@@ -43,8 +43,8 @@ from mailman.email.message import UserNotification
## # situation and normal ModeratedPost reasons. Greg Ward and Stonewall
## # Ballard thought the language was too harsh and mentioned offense taken
## # by some list members. I'd still like this class's reason to be
## # different than the base class's reason, but we'll use this until someone
## # can come up with something more clever but inoffensive.
## # different than the base class's reason, but we'll use this until
## # someone can come up with something more clever but inoffensive.
## #
## # reason = _('Posts by member are currently quarantined for moderation')
## pass
......
......@@ -26,7 +26,9 @@ __all__ = [
# pylint: disable-msg=W0613
def process(mlist, msg, msgdata):
"""Add owner recipients."""
# The recipients are the owner and the moderator
msgdata['recipients'] = mlist.owner + mlist.moderator
# Don't decorate these messages with the header/footers
......
......@@ -26,15 +26,13 @@ __all__ = [
import logging
import datetime
from zope.component import getUtility
from zope.interface import implements
from mailman import Utils
from mailman.config import config
from mailman.core.i18n import _
from mailman.email.message import Message, UserNotification
from mailman.email.message import UserNotification
from mailman.interfaces.autorespond import (
ALWAYS_REPLY, IAutoResponseSet, Response, ResponseAction)
from mailman.interfaces.handler import IHandler
......@@ -47,6 +45,7 @@ log = logging.getLogger('mailman.error')
# pylint: disable-msg=W0232,R0201,R0911
class Replybot:
"""Send automatic responses."""
......@@ -66,7 +65,7 @@ class Replybot:
if ack == 'no' or msgdata.get('noack'):
return
precedence = msg.get('precedence', '').lower()
if ack <> 'yes' and precedence in ('bulk', 'junk', 'list'):
if ack != 'yes' and precedence in ('bulk', 'junk', 'list'):
return
# Check to see if the list is even configured to autorespond to this
# email message. Note: the incoming message processors should set the
......@@ -98,7 +97,7 @@ class Replybot:
if address is None:
address = user_manager.create_address(msg.sender)
grace_period = mlist.autoresponse_grace_period
if grace_period > ALWAYS_REPLY and ack <> 'yes':
if grace_period > ALWAYS_REPLY and ack != 'yes':
last = response_set.last_response(address, response_type)
if last is not None and last.date_sent + grace_period > today():
return
......
......@@ -68,49 +68,36 @@ log = logging.getLogger('mailman.error')
def guess_extension(ctype, ext):
# mimetypes maps multiple extensions to the same type, e.g. .doc, .dot,
# and .wiz are all mapped to application/msword. This sucks for finding
# the best reverse mapping. If the extension is one of the giving
# mappings, we'll trust that, otherwise we'll just guess. :/
all = guess_all_extensions(ctype, strict=False)
if ext in all:
"""Find the extension mapped to the given content-type.
mimetypes maps multiple extensions to the same type, e.g. .doc, .dot, and
.wiz are all mapped to application/msword. This sucks for finding the
best reverse mapping. If the extension is one of the giving mappings,
we'll trust that, otherwise we'll just guess. :/
"""
all_extensions = guess_all_extensions(ctype, strict=False)
if ext in all_extensions:
return ext
return all and all[0]
return (all_extensions[0] if len(all) > 0 else [])
# We're using a subclass of the standard Generator because we want to suppress
# headers in the subparts of multiparts. We use a hack -- the ctor argument
# skipheaders to accomplish this. It's set to true for the outer Message
# object, but false for all internal objects. We recognize that
# sub-Generators will get created passing only mangle_from_ and maxheaderlen
# to the ctors.
#
# This isn't perfect because we still get stuff like the multipart boundaries,
# but see below for how we corrupt that to our nefarious goals.
class ScrubberGenerator(Generator):
def __init__(self, outfp, mangle_from_=True,
maxheaderlen=78, skipheaders=True):
Generator.__init__(self, outfp, mangle_from_=False)
self.__skipheaders = skipheaders
def _write_headers(self, msg):
if not self.__skipheaders:
Generator._write_headers(self, msg)
def safe_strftime(fmt, t):
"""A time.strftime() that eats exceptions, returning None instead."""
try:
return time.strftime(fmt, t)
except (TypeError, ValueError, OverflowError):
return None
def calculate_attachments_dir(mlist, msg, msgdata):
# Calculate the directory that attachments for this message will go
# under. To avoid inode limitations, the scheme will be:
# archives/private/<listname>/attachments/YYYYMMDD/<msgid-hash>/<files>
# Start by calculating the date-based and msgid-hash components.
def calculate_attachments_dir(msg, msgdata):
"""Calculate the directory for attachements.
Calculate the directory that attachments for this message will go under.
To avoid inode limitations, the scheme will be:
archives/private/<listname>/attachments/YYYYMMDD/<msgid-hash>/<files>
Start by calculating the date-based and msgid-hash components.
"""
fmt = '%Y%m%d'
datestr = msg.get('Date')
if datestr:
......@@ -151,6 +138,7 @@ def calculate_attachments_dir(mlist, msg, msgdata):
def replace_payload_by_text(msg, text, charset):
"""Replace the payload of the message with some text."""
# TK: This is a common function in replacing the attachment and the main
# message by a text (scrubbing).
del msg['content-type']
......@@ -164,6 +152,7 @@ def replace_payload_by_text(msg, text, charset):
def process(mlist, msg, msgdata=None):
"""Process the message through the scrubber."""
sanitize = int(config.scrubber.archive_html_sanitizer)
outer = True
if msgdata is None:
......@@ -174,8 +163,8 @@ def process(mlist, msg, msgdata=None):
# check if the list owner want to scrub regular delivery
if not mlist.scrub_nondigest:
return
dir = calculate_attachments_dir(mlist, msg, msgdata)
charset = format = delsp = None
attachments_dir = calculate_attachments_dir(msg, msgdata)
charset = format_param = delsp = None
lcset = mlist.preferred_language.charset
lcset_out = Charset(lcset).output_charset or lcset
# Now walk over all subparts of this message and scrub out various types
......@@ -200,12 +189,12 @@ def process(mlist, msg, msgdata=None):
# can do without having get_payload() process the parameters.
if charset is None:
charset = part.get_content_charset(lcset)
format = part.get_param('format')
format_param = part.get_param('format')
delsp = part.get_param('delsp')
# TK: if part is attached then check charset and scrub if none
if part.get('content-disposition') and \
not part.get_content_charset():
url = save_attachment(mlist, part, dir)
url = save_attachment(mlist, part, attachments_dir)
filename = part.get_filename(_('not available'))
filename = oneline(filename, lcset)
replace_payload_by_text(part, _("""\
......@@ -229,7 +218,8 @@ URL: $url
# Pull it out as an attachment but leave it unescaped. This
# is dangerous, but perhaps useful for heavily moderated
# lists.
url = save_attachment(mlist, part, dir, filter_html=False)
url = save_attachment(mlist, part, attachments_dir,
filter_html=False)
replace_payload_by_text(part, _("""\
An HTML attachment was scrubbed...
URL: $url
......@@ -242,15 +232,15 @@ URL: $url
# non-breaking spaces, and tabs into 8 of those. Then use a
# mono-space font. Still looks hideous to me, but then I'd
# just as soon discard them.
def doreplace(s):
return s.replace(' ', '&nbsp;').replace('\t', '&nbsp'*8)
lines = [doreplace(s) for s in payload.split('\n')]
lines = [s.replace(' ', '&nbsp;').replace('\t', '&nbsp' * 8)
for s in payload.split('\n')]
payload = '<tt>\n' + BR.join(lines) + '\n</tt>\n'
part.set_payload(payload)
# We're replacing the payload with the decoded payload so this
# will just get in the way.
del part['content-transfer-encoding']
url = save_attachment(mlist, part, dir, filter_html=False)
url = save_attachment(mlist, part, attachments_dir,
filter_html=False)
replace_payload_by_text(part, _("""\
An HTML attachment was scrubbed...
URL: $url
......@@ -258,7 +248,7 @@ URL: $url
elif ctype == 'message/rfc822':
# This part contains a submessage, so it too needs scrubbing
submsg = part.get_payload(0)
url = save_attachment(mlist, part, dir)
url = save_attachment(mlist, part, attachments_dir)
subject = submsg.get('subject', _('no subject'))
date = submsg.get('date', _('no date'))
who = submsg.get('from', _('unknown sender'))
......@@ -286,7 +276,7 @@ URL: $url
if payload is None:
continue
size = len(payload)
url = save_attachment(mlist, part, dir)
url = save_attachment(mlist, part, attachments_dir)
desc = part.get('content-description', _('not available'))
desc = oneline(desc, lcset)
filename = part.get_filename(_('not available'))
......@@ -303,7 +293,7 @@ URL: $url
# We still have to sanitize multipart messages to flat text because
# Pipermail can't handle messages with list payloads. This is a kludge;
# def (n) clever hack ;).
if msg.is_multipart() and sanitize <> 2:
if msg.is_multipart() and sanitize != 2:
# By default we take the charset of the first text/plain part in the
# message, but if there was none, we'll use the list's preferred
# language's charset.
......@@ -327,7 +317,7 @@ URL: $url
continue
# All parts should be scrubbed to text/plain by now.
partctype = part.get_content_type()
if partctype <> 'text/plain':
if partctype != 'text/plain':
text.append(_('Skipped content of type $partctype\n'))
continue
try:
......@@ -372,17 +362,17 @@ URL: $url
except (UnicodeError, LookupError, ValueError, TypeError,
AssertionError):
pass
if format:
msg.set_param('format', format)
if format_param:
msg.set_param('format', format_param)
if delsp:
msg.set_param('delsp', delsp)
return msg
def save_attachment(mlist, msg, dir, filter_html=True):
def save_attachment(mlist, msg, attachments_dir, filter_html=True):
fsdir = os.path.join(config.PRIVATE_ARCHIVE_FILE_DIR,
mlist.fqdn_listname, dir)
mlist.fqdn_listname, attachments_dir)
makedirs(fsdir)
# Figure out the attachment type and get the decoded data
decodedpayload = msg.get_payload(decode=True)
......@@ -493,7 +483,8 @@ def save_attachment(mlist, msg, dir, filter_html=True):
base_url += '/'
# Trailing space will definitely be a problem with format=flowed.
# Bracket the URL instead.
url = '<' + base_url + '%s/%s%s%s>' % (dir, filebase, extra, ext)
url = '<' + base_url + '%s/%s%s%s>' % (
attachments_dir, filebase, extra, ext)
return url
......
......@@ -26,10 +26,8 @@ __all__ = [
import re
import email
import email.Errors
import email.Iterators
import email.Parser
import email.iterators
import email.parser
from zope.interface import implements
......@@ -45,6 +43,7 @@ NLTAB = '\n\t'
def process(mlist, msg, msgdata):
"""Tag the message for topics."""
if not mlist.topics_enabled:
return
# Extract the Subject:, Keywords:, and possibly body text
......@@ -60,11 +59,13 @@ def process(mlist, msg, msgdata):
else:
# Scan just some of the body lines
matchlines.extend(scanbody(msg, mlist.topics_bodylines_limit))
matchlines = filter(None, matchlines)
# Filter out any 'false' items.
matchlines = [item for item in matchlines if item]
# For each regular expression in the topics list, see if any of the lines
# of interest from the message match the regexp. If so, the message gets
# added to the specific topics bucket.
hits = {}
# pylint: disable-msg=W0612
for name, pattern, desc, emptyflag in mlist.topics:
pattern = OR.join(pattern.splitlines())
cre = re.compile(pattern, re.IGNORECASE)
......@@ -81,6 +82,7 @@ def process(mlist, msg, msgdata):
def scanbody(msg, numlines=None):
"""Scan the body for keywords."""
# We only scan the body of the message if it is of MIME type text/plain,
# or if the outer type is multipart/alternative and there is a text/plain
# part. Anything else, and the body is ignored for header-scan purposes.
......@@ -100,7 +102,7 @@ def scanbody(msg, numlines=None):
# the first numlines of body text.
lines = []
lineno = 0
reader = list(email.Iterators.body_line_iterator(msg))
reader = list(email.iterators.body_line_iterator(msg))
while numlines is None or lineno < numlines:
try:
line = bytes(reader.pop(0))
......@@ -119,13 +121,16 @@ def scanbody(msg, numlines=None):
class _ForgivingParser(email.Parser.HeaderParser):
# Be a little more forgiving about non-header/continuation lines, since
# we'll just read as much as we can from "header-like" lines in the body.
#
class _ForgivingParser(email.parser.HeaderParser):
"""An lax email parser.
Be a little more forgiving about non-header/continuation lines, since
we'll just read as much as we can from 'header-like' lines in the body.
"""
# BAW: WIBNI we didn't have to cut-n-paste this whole thing just to
# specialize the way it returns?
def _parseheaders(self, container, fp):
"""See `email.parser.HeaderParser`."""
# Parse the headers, returning a list of header/value pairs. None as
# the header means the Unix-From header.
lastheader = ''
......
......@@ -39,6 +39,7 @@ from mailman.utilities.mailbox import Mailbox
# pylint: disable-msg=W0232,R0201
class ToDigest:
"""Add the message to the digest, possibly sending it."""
......@@ -72,7 +73,7 @@ class ToDigest:
'digest.{0.volume}.{0.next_digest_number}.mmdf'.format(mlist))
volume = mlist.volume
digest_number = mlist.next_digest_number
bump(mlist)
bump_digest_number_and_volume(mlist)
os.rename(mailbox_path, mailbox_dest)
config.switchboards['digest'].enqueue(
Message(),
......@@ -83,7 +84,7 @@ class ToDigest:
def bump(mlist):
def bump_digest_number_and_volume(mlist):
"""Bump the digest number and volume."""
now = datetime.datetime.now()
if mlist.digest_last_sent_at is None:
......
......@@ -30,16 +30,15 @@ __all__ = [
]
from lazr.config import as_boolean
from zope.interface import implements
from mailman.config import config
from mailman.core.i18n import _
from mailman.interfaces.handler import IHandler
from mailman.interfaces.mailinglist import Personalization
# pylint: disable-msg=W0203,R0201
class ToOutgoing:
"""Send the message to the outgoing queue."""
......
......@@ -39,6 +39,7 @@ log = logging.getLogger('mailman.error')
# pylint: disable-msg=W0232,R0201
class ToUsenet:
"""Move the message to the outgoing news queue."""
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment