diff options
| author | Barry Warsaw | 2008-01-21 00:26:55 -0500 |
|---|---|---|
| committer | Barry Warsaw | 2008-01-21 00:26:55 -0500 |
| commit | 4460aad316db5c8af9b84c392e67441acaac9d72 (patch) | |
| tree | eabc714fdc3c9aff16ca48cfbc5f7974af01e1c1 /Mailman/app/chains.py | |
| parent | 2efcac1ef273b407668826c587c15a0fd8ec3d3c (diff) | |
| download | mailman-4460aad316db5c8af9b84c392e67441acaac9d72.tar.gz mailman-4460aad316db5c8af9b84c392e67441acaac9d72.tar.zst mailman-4460aad316db5c8af9b84c392e67441acaac9d72.zip | |
Diffstat (limited to 'Mailman/app/chains.py')
| -rw-r--r-- | Mailman/app/chains.py | 91 |
1 files changed, 67 insertions, 24 deletions
diff --git a/Mailman/app/chains.py b/Mailman/app/chains.py index cb5dfafb7..8ac9e9ce7 100644 --- a/Mailman/app/chains.py +++ b/Mailman/app/chains.py @@ -26,10 +26,16 @@ __all__ = [ 'RejectChain', ] __metaclass__ = type +__i18n_templates__ = True import logging +from email.mime.message import MIMEMessage +from email.mime.text import MIMEText +from email.utils import formatdate, make_msgid +from zope.interface import implements + from Mailman import i18n from Mailman.Message import UserNotification from Mailman.Utils import maketext, oneline, wrap, GetCharSet @@ -77,8 +83,14 @@ class HoldChain: def process(self, mlist, msg, msgdata): """See `IChain`.""" # Start by decorating the message with a header that contains a list - # of all the rules that matched. - msg['X-Mailman-Rule-Hits'] = SEMISPACE.join(msgdata['rules']) + # of all the rules that matched. These metadata could be None or an + # empty list. + rule_hits = msgdata.get('rule_hits') + if rule_hits: + msg['X-Mailman-Rule-Hits'] = SEMISPACE.join(rule_hits) + rule_misses = msgdata.get('rule_misses') + if rule_misses: + msg['X-Mailman-Rule-Misses'] = SEMISPACE.join(rule_misses) # Hold the message by adding it to the list's request database. # XXX How to calculate the reason? request_id = hold_message(mlist, msg, msgdata, None) @@ -90,6 +102,7 @@ class HoldChain: # Get the language to send the response in. If the sender is a # member, then send it in the member's language, otherwise send it in # the mailing list's preferred language. + sender = msg.get_sender() member = mlist.members.get_member(sender) language = (member.preferred_language if member else mlist.preferred_language) @@ -101,12 +114,13 @@ class HoldChain: else: original_subject = oneline(original_subject, charset) substitutions = { - 'listname' : mlist.fqdn_listname, - 'subject' : original_subject, - 'reason' : 'XXX', #reason, - 'confirmurl': '%s/%s' % (mlist.script_url('confirm'), token), + 'listname' : mlist.fqdn_listname, + 'subject' : original_subject, + 'sender' : sender, + 'reason' : 'XXX', #reason, + 'confirmurl' : '%s/%s' % (mlist.script_url('confirm'), token), + 'admindb_url': mlist.script_url('admindb'), } - sender = msg.get_sender() # At this point the message is held, but now we have to craft at least # two responses. The first will go to the original author of the # message and it will contain the token allowing them to approve or @@ -129,10 +143,12 @@ class HoldChain: # posting was held. subject = _( 'Your message to $mlist.fqdn_listname awaits moderator approval') - language = msgdata.get('lang', lang) + send_language = msgdata.get('lang', language) text = maketext('postheld.txt', substitutions, - lang=language, mlist=mlist) - nmsg = UserNotification(sender, adminaddr, subject, text, language) + lang=send_language, mlist=mlist) + adminaddr = mlist.bounces_address + nmsg = UserNotification(sender, adminaddr, subject, text, + send_language) nmsg.send(mlist) # Now the message for the list moderators. This one should appear to # come from <list>-owner since we really don't need to do bounce @@ -145,8 +161,8 @@ class HoldChain: charset = GetCharSet(language) # We need to regenerate or re-translate a few values in the # substitution dictionary. - d['reason'] = _(reason) - d['subject'] = original_subject + #d['reason'] = _(reason) # XXX reason + substitutions['subject'] = original_subject # craft the admin notification message and deliver it subject = _( '$mlist.fqdn_listname post from $sender requires approval') @@ -155,7 +171,7 @@ class HoldChain: subject, lang=language) nmsg.set_type('multipart/mixed') text = MIMEText( - maketext('postauth.txt', substitution, + maketext('postauth.txt', substitutions, raw=True, mlist=mlist), _charset=charset) dmsg = MIMEText(wrap(_("""\ @@ -164,19 +180,22 @@ discard the held message. Do this if the message is spam. If you reply to this message and include an Approved: header with the list password in it, the message will be approved for posting to the list. The Approved: header can also appear in the first line of the body of the reply.""")), - _charset=GetCharSet(lang)) + _charset=GetCharSet(language)) dmsg['Subject'] = 'confirm ' + token - dmsg['Sender'] = requestaddr - dmsg['From'] = requestaddr - dmsg['Date'] = email.utils.formatdate(localtime=True) - dmsg['Message-ID'] = email.utils.make_msgid() + dmsg['Sender'] = mlist.request_address + dmsg['From'] = mlist.request_address + dmsg['Date'] = formatdate(localtime=True) + dmsg['Message-ID'] = make_msgid() nmsg.attach(text) nmsg.attach(MIMEMessage(msg)) nmsg.attach(MIMEMessage(dmsg)) nmsg.send(mlist, **{'tomoderators': 1}) # Log the held message - log.info('HELD: %s post from %s held, message-id=%s: %s', - listname, sender, message_id, reason) + # XXX reason + reason = 'n/a' + log.info('HOLD: %s post from %s held, message-id=%s: %s', + mlist.fqdn_listname, sender, + msg.get('message-id', 'n/a'), reason) @@ -189,6 +208,15 @@ class RejectChain: def process(self, mlist, msg, msgdata): """See `IChain`.""" + # Start by decorating the message with a header that contains a list + # of all the rules that matched. These metadata could be None or an + # empty list. + rule_hits = msgdata.get('rule_hits') + if rule_hits: + msg['X-Mailman-Rule-Hits'] = SEMISPACE.join(rule_hits) + rule_misses = msgdata.get('rule_misses') + if rule_misses: + msg['X-Mailman-Rule-Misses'] = SEMISPACE.join(rule_misses) # XXX Exception/reason bounce_message(mlist, msg) log.info('REJECT: %s', msg.get('message-id', 'n/a')) @@ -204,6 +232,15 @@ class AcceptChain: def process(self, mlist, msg, msgdata): """See `IChain.`""" + # Start by decorating the message with a header that contains a list + # of all the rules that matched. These metadata could be None or an + # empty list. + rule_hits = msgdata.get('rule_hits') + if rule_hits: + msg['X-Mailman-Rule-Hits'] = SEMISPACE.join(rule_hits) + rule_misses = msgdata.get('rule_misses') + if rule_misses: + msg['X-Mailman-Rule-Misses'] = SEMISPACE.join(rule_misses) accept_queue = Switchboard(config.PREPQUEUE_DIR) accept_queue.enqueue(msg, msgdata) log.info('ACCEPT: %s', msg.get('message-id', 'n/a')) @@ -224,9 +261,12 @@ class Chain: implements(IMutableChain) def __init__(self, name, description): + assert name not in config.chains, 'Duplicate chain name: %s' % name self.name = name self.description = description self._links = [] + # Register the chain. + config.chains[name] = self def append_link(self, link): """See `IMutableChain`.""" @@ -238,23 +278,26 @@ class Chain: def process(self, mlist, msg, msgdata): """See `IMutableChain`.""" - msgdata['rules'] = rules = [] + msgdata['rule_hits'] = hits = [] + msgdata['rule_misses'] = misses = [] jump = None for link in self._links: # The None rule always match. if link.rule is None: jump = link.jump break - # If the rule hits, just to the given chain. + # If the rule hits, jump to the given chain. rule = config.rules.get(link.rule) if rule is None: elog.error('Rule not found: %s', rule) elif rule.check(mlist, msg, msgdata): - rules.append(link.rule.name) + hits.append(link.rule) # None is a special jump meaning "keep processing this chain". if link.jump is not None: jump = link.jump break + else: + misses.append(link.rule) else: # We got through the entire chain without a jumping rule match, so # we really don't know what to do. Rather than raise an @@ -280,7 +323,7 @@ def initialize(): 'Duplicate chain name: %s' % chain.name) config.chains[chain.name] = chain # Set up a couple of other default chains. - default = Chain('built-in', _('The built-in moderation chain'), 'accept') + default = Chain('built-in', _('The built-in moderation chain.')) default.append_link(Link('approved', 'accept')) default.append_link(Link('emergency', 'hold')) default.append_link(Link('loop', 'discard')) |
