def decorate(mlist, template, what, extradict=None): # `what' is just a descriptive phrase used in the log message # # BAW: We've found too many situations where Python can be fooled into # interpolating too much revealing data into a format string. For # example, a footer of "% silly %(real_name)s" would give a header # containing all list attributes. While we've previously removed such # really bad ones like `password' and `passwords', it's much better to # provide a whitelist of known good attributes, then to try to remove a # blacklist of known bad ones. d = SafeDict({'real_name' : mlist.real_name, 'list_name' : mlist.internal_name(), # For backwards compatibility '_internal_name': mlist.internal_name(), 'host_name' : mlist.host_name, 'web_page_url' : mlist.web_page_url, 'description' : mlist.description, 'info' : mlist.info, 'cgiext' : mm_cfg.CGIEXT, }) if extradict is not None: d.update(extradict) # Using $-strings? if getattr(mlist, 'use_dollar_strings', 0): template = Utils.to_percent(template) # Interpolate into the template try: text = re.sub(r'(?m)(?<!^--) +(?=\n)', '', re.sub(r'\r\n', r'\n', template % d)) except (ValueError, TypeError), e: syslog('error', 'Exception while calculating %s:\n%s', what, e) text = template
def decorate(mlist, template, what, extradict=None): # `what' is just a descriptive phrase used in the log message # # BAW: We've found too many situations where Python can be fooled into # interpolating too much revealing data into a format string. For # example, a footer of "% silly %(real_name)s" would give a header # containing all list attributes. While we've previously removed such # really bad ones like `password' and `passwords', it's much better to # provide a whitelist of known good attributes, then to try to remove a # blacklist of known bad ones. d = SafeDict({ 'real_name': mlist.real_name, 'list_name': mlist.internal_name(), # For backwards compatibility '_internal_name': mlist.internal_name(), 'host_name': mlist.host_name, 'web_page_url': mlist.web_page_url, 'description': mlist.description, 'info': mlist.info, 'cgiext': mm_cfg.CGIEXT, }) if extradict is not None: d.update(extradict) # Using $-strings? if getattr(mlist, 'use_dollar_strings', 0): template = Utils.to_percent(template) # Interpolate into the template try: text = re.sub(r'(?m)(?<!^--) +(?=\n)', '', re.sub(r'\r\n', r'\n', template % d)) except (ValueError, TypeError), e: syslog('error', 'Exception while calculating %s:\n%s', what, e) text = template
def _convertString(self, mlist, property, alloweds, val, doc): # Is the list using $-strings? dollarp = getattr(mlist, 'use_dollar_strings', 0) if dollarp: ids = Utils.dollar_identifiers(val) else: # %-strings ids = Utils.percent_identifiers(val) # Here's the list of allowable interpolations for allowed in alloweds: if ids.has_key(allowed): del ids[allowed] if ids: # What's left are not allowed badkeys = ids.keys() badkeys.sort() bad = BADJOINER.join(badkeys) doc.addError(_( """The following illegal substitution variables were found in the <code>%(property)s</code> string: <code>%(bad)s</code> <p>Your list may not operate properly until you correct this problem."""), tag=_('Warning: ')) return val # Now if we're still using %-strings, do a roundtrip conversion and # see if the converted value is the same as the new value. If not, # then they probably left off a trailing `s'. We'll warn them and use # the corrected string. if not dollarp: fixed = Utils.to_percent(Utils.to_dollar(val)) if fixed <> val: doc.addError(_( """Your <code>%(property)s</code> string appeared to have some correctable problems in its new value. The fixed value will be used instead. Please double check that this is what you intended. """)) return fixed return val
def _convertString(self, mlist, property, alloweds, val, doc): # Is the list using $-strings? dollarp = getattr(mlist, 'use_dollar_strings', 0) if dollarp: ids = Utils.dollar_identifiers(val) else: # %-strings ids = Utils.percent_identifiers(val) # Here's the list of allowable interpolations for allowed in alloweds: if allowed in ids: del ids[allowed] if ids: # What's left are not allowed badkeys = list(ids.keys()) badkeys.sort() bad = BADJOINER.join(badkeys) doc.addError(_("""The following illegal substitution variables were found in the <code>%(property)s</code> string: <code>%(bad)s</code> <p>Your list may not operate properly until you correct this problem."""), tag=_('Warning: ')) return val # Now if we're still using %-strings, do a roundtrip conversion and # see if the converted value is the same as the new value. If not, # then they probably left off a trailing `s'. We'll warn them and use # the corrected string. if not dollarp: fixed = Utils.to_percent(Utils.to_dollar(val)) if fixed != val: doc.addError( _("""Your <code>%(property)s</code> string appeared to have some correctable problems in its new value. The fixed value will be used instead. Please double check that this is what you intended. """)) return fixed return val
def process(mlist, msg, msgdata): # Normally, the replybot should get a shot at this message, but there are # some important short-circuits, mostly to suppress 'bot storms, at least # for well behaved email bots (there are other governors for misbehaving # 'bots). First, if the original message has an "X-Ack: No" header, we # skip the replybot. Then, if the message has a Precedence header with # values bulk, junk, or list, and there's no explicit "X-Ack: yes" header, # we short-circuit. Finally, if the message metadata has a true 'noack' # key, then we skip the replybot too. ack = msg.get('x-ack', '').lower() if ack == 'no' or msgdata.get('noack'): return precedence = msg.get('precedence', '').lower() 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 mailowner script sets the `toadmin' or `toowner' key # (which for replybot purposes are equivalent), and the mailcmd script # sets the `torequest' key. toadmin = msgdata.get('toowner') torequest = msgdata.get('torequest') if ((toadmin and not mlist.autorespond_admin) or (torequest and not mlist.autorespond_requests) or \ (not toadmin and not torequest and not mlist.autorespond_postings)): return # Now see if we're in the grace period for this sender. graceperiod <= 0 # means always autorespond, as does an "X-Ack: yes" header (useful for # debugging). sender = msg.get_sender() now = time.time() graceperiod = mlist.autoresponse_graceperiod if graceperiod > 0 and ack <> 'yes': if toadmin: quiet_until = mlist.admin_responses.get(sender, 0) elif torequest: quiet_until = mlist.request_responses.get(sender, 0) else: quiet_until = mlist.postings_responses.get(sender, 0) if quiet_until > now: return # # Okay, we know we're going to auto-respond to this sender, craft the # message, send it, and update the database. realname = mlist.real_name subject = _( 'Auto-response for your message to the "%(realname)s" mailing list') # Do string interpolation d = SafeDict({'listname' : realname, 'listurl' : mlist.GetScriptURL('listinfo'), 'requestemail': mlist.GetRequestEmail(), # BAW: Deprecate adminemail; it's not advertised but still # supported for backwards compatibility. 'adminemail' : mlist.GetBouncesEmail(), 'owneremail' : mlist.GetOwnerEmail(), }) # Just because we're using a SafeDict doesn't mean we can't get all sorts # of other exceptions from the string interpolation. Let's be ultra # conservative here. if toadmin: rtext = mlist.autoresponse_admin_text elif torequest: rtext = mlist.autoresponse_request_text else: rtext = mlist.autoresponse_postings_text # Using $-strings? if getattr(mlist, 'use_dollar_strings', 0): rtext = Utils.to_percent(rtext) try: text = rtext % d except Exception: syslog('error', 'Bad autoreply text for list: %s\n%s', mlist.internal_name(), rtext) text = rtext # Wrap the response. text = Utils.wrap(text) outmsg = Message.UserNotification(sender, mlist.GetBouncesEmail(), subject, text, mlist.preferred_language) outmsg['X-Mailer'] = _('The Mailman Replybot') # prevent recursions and mail loops! outmsg['X-Ack'] = 'No' outmsg.send(mlist) # update the grace period database if graceperiod > 0: # graceperiod is in days, we need # of seconds quiet_until = now + graceperiod * 24 * 60 * 60 if toadmin: mlist.admin_responses[sender] = quiet_until elif torequest: mlist.request_responses[sender] = quiet_until else: mlist.postings_responses[sender] = quiet_until
def process(mlist, msg, msgdata): # Normally, the replybot should get a shot at this message, but there are # some important short-circuits, mostly to suppress 'bot storms, at least # for well behaved email bots (there are other governors for misbehaving # 'bots). First, if the original message has an "X-Ack: No" header, we # skip the replybot. Then, if the message has a Precedence header with # values bulk, junk, or list, and there's no explicit "X-Ack: yes" header, # we short-circuit. Finally, if the message metadata has a true 'noack' # key, then we skip the replybot too. ack = msg.get('x-ack', '').lower() if ack == 'no' or msgdata.get('noack'): return precedence = msg.get('precedence', '').lower() 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 owner script sets the `toowner' key, and the various # confirm, join, leave, request, subscribe and unsubscribe scripts set the # keys we use for `torequest'. toadmin = msgdata.get('toowner') torequest = msgdata.get('torequest') or msgdata.get('toconfirm') or \ msgdata.get('tojoin') or msgdata.get('toleave') if ((toadmin and not mlist.autorespond_admin) or (torequest and not mlist.autorespond_requests) or \ (not toadmin and not torequest and not mlist.autorespond_postings)): return # Now see if we're in the grace period for this sender. graceperiod <= 0 # means always autorespond, as does an "X-Ack: yes" header (useful for # debugging). sender = msg.get_sender() now = time.time() graceperiod = mlist.autoresponse_graceperiod if graceperiod > 0 and ack <> 'yes': if toadmin: quiet_until = mlist.admin_responses.get(sender, 0) elif torequest: quiet_until = mlist.request_responses.get(sender, 0) else: quiet_until = mlist.postings_responses.get(sender, 0) if quiet_until > now: return # # Okay, we know we're going to auto-respond to this sender, craft the # message, send it, and update the database. realname = mlist.real_name subject = _( 'Auto-response for your message to the "%(realname)s" mailing list') # Do string interpolation d = SafeDict({'listname' : realname, 'listurl' : mlist.GetScriptURL('listinfo'), 'requestemail': mlist.GetRequestEmail(), # BAW: Deprecate adminemail; it's not advertised but still # supported for backwards compatibility. 'adminemail' : mlist.GetBouncesEmail(), 'owneremail' : mlist.GetOwnerEmail(), }) # Just because we're using a SafeDict doesn't mean we can't get all sorts # of other exceptions from the string interpolation. Let's be ultra # conservative here. if toadmin: rtext = mlist.autoresponse_admin_text elif torequest: rtext = mlist.autoresponse_request_text else: rtext = mlist.autoresponse_postings_text # Using $-strings? if getattr(mlist, 'use_dollar_strings', 0): rtext = Utils.to_percent(rtext) try: text = rtext % d except Exception: syslog('error', 'Bad autoreply text for list: %s\n%s', mlist.internal_name(), rtext) text = rtext # Wrap the response. text = Utils.wrap(text) outmsg = Message.UserNotification(sender, mlist.GetBouncesEmail(), subject, text, mlist.preferred_language) outmsg['X-Mailer'] = _('The Mailman Replybot') # prevent recursions and mail loops! outmsg['X-Ack'] = 'No' outmsg.send(mlist) # update the grace period database if graceperiod > 0: # graceperiod is in days, we need # of seconds quiet_until = now + graceperiod * 24 * 60 * 60 if toadmin: mlist.admin_responses[sender] = quiet_until elif torequest: mlist.request_responses[sender] = quiet_until else: mlist.postings_responses[sender] = quiet_until