def _handleForm(self, mlist, category, subcat, cgidata, doc): # TK: If there is no hdrfilter_* in cgidata, we should not touch # the header filter rules. if not cgidata.has_key('hdrfilter_rebox_01'): return # First deal with rules = [] # We start i at 1 and keep going until we no longer find items keyed # with the marked tags. i = 1 downi = None while True: deltag = 'hdrfilter_delete_%02d' % i reboxtag = 'hdrfilter_rebox_%02d' % i actiontag = 'hdrfilter_action_%02d' % i wheretag = 'hdrfilter_where_%02d' % i addtag = 'hdrfilter_add_%02d' % i newtag = 'hdrfilter_new_%02d' % i uptag = 'hdrfilter_up_%02d' % i downtag = 'hdrfilter_down_%02d' % i i += 1 # Was this a delete? If so, we can just ignore this entry if cgidata.has_key(deltag): continue # Get the data for the current box pattern = cgidata.getvalue(reboxtag) try: action = int(cgidata.getvalue(actiontag)) # We'll get a TypeError when the actiontag is missing and the # .getvalue() call returns None. except (ValueError, TypeError): action = mm_cfg.DEFER if pattern is None: # We came to the end of the boxes break if cgidata.has_key(newtag) and not pattern: # This new entry is incomplete. if i == 2: # OK it is the first. continue doc.addError(_("""Header filter rules require a pattern. Incomplete filter rules will be ignored.""")) continue # Make sure the pattern was a legal regular expression. # Convert it to unicode if necessary. mo = re.match('.*charset=([-_a-z0-9]+)', os.environ.get('CONTENT_TYPE', ''), re.IGNORECASE ) if mo: cset = mo.group(1) else: cset = Utils.GetCharSet(mlist.preferred_language) try: upattern = Utils.xml_to_unicode(pattern, cset) re.compile(upattern) pattern = upattern except (re.error, TypeError): safepattern = Utils.websafe(pattern) doc.addError(_("""The header filter rule pattern '%(safepattern)s' is not a legal regular expression. This rule will be ignored.""")) continue # Was this an add item? if cgidata.has_key(addtag): # Where should the new one be added? where = cgidata.getvalue(wheretag) if where == 'before': # Add a new empty rule box before the current one rules.append(('', mm_cfg.DEFER, True)) rules.append((pattern, action, False)) # Default is to add it after... else: rules.append((pattern, action, False)) rules.append(('', mm_cfg.DEFER, True)) # Was this an up movement? elif cgidata.has_key(uptag): # As long as this one isn't the first rule, move it up if rules: rules.insert(-1, (pattern, action, False)) else: rules.append((pattern, action, False)) # Was this the down movement? elif cgidata.has_key(downtag): downi = i - 2 rules.append((pattern, action, False)) # Otherwise, just retain this one in the list else: rules.append((pattern, action, False)) # Move any down button filter rule if downi is not None: rule = rules[downi] del rules[downi] rules.insert(downi+1, rule) mlist.header_filter_rules = rules
def _handleForm(self, mlist, category, subcat, cgidata, doc): # TK: If there is no hdrfilter_* in cgidata, we should not touch # the header filter rules. if not cgidata.has_key('hdrfilter_rebox_01'): return # First deal with rules = [] # We start i at 1 and keep going until we no longer find items keyed # with the marked tags. i = 1 downi = None while True: deltag = 'hdrfilter_delete_%02d' % i reboxtag = 'hdrfilter_rebox_%02d' % i actiontag = 'hdrfilter_action_%02d' % i wheretag = 'hdrfilter_where_%02d' % i addtag = 'hdrfilter_add_%02d' % i newtag = 'hdrfilter_new_%02d' % i uptag = 'hdrfilter_up_%02d' % i downtag = 'hdrfilter_down_%02d' % i i += 1 # Was this a delete? If so, we can just ignore this entry if cgidata.has_key(deltag): continue # Get the data for the current box pattern = cgidata.getfirst(reboxtag) try: action = int(cgidata.getfirst(actiontag)) # We'll get a TypeError when the actiontag is missing and the # .getvalue() call returns None. except (ValueError, TypeError): action = mm_cfg.DEFER if pattern is None: # We came to the end of the boxes break if cgidata.has_key(newtag) and not pattern: # This new entry is incomplete. if i == 2: # OK it is the first. continue doc.addError( _("""Header filter rules require a pattern. Incomplete filter rules will be ignored.""")) continue # Make sure the pattern was a legal regular expression. # Convert it to unicode if necessary. mo = re.match('.*charset=([-_a-z0-9]+)', os.environ.get('CONTENT_TYPE', ''), re.IGNORECASE) if mo: cset = mo.group(1) else: cset = Utils.GetCharSet(mlist.preferred_language) try: upattern = Utils.xml_to_unicode(pattern, cset) re.compile(upattern) pattern = upattern except (re.error, TypeError): safepattern = Utils.websafe(pattern) doc.addError( _("""The header filter rule pattern '%(safepattern)s' is not a legal regular expression. This rule will be ignored.""")) continue # Was this an add item? if cgidata.has_key(addtag): # Where should the new one be added? where = cgidata.getfirst(wheretag) if where == 'before': # Add a new empty rule box before the current one rules.append(('', mm_cfg.DEFER, True)) rules.append((pattern, action, False)) # Default is to add it after... else: rules.append((pattern, action, False)) rules.append(('', mm_cfg.DEFER, True)) # Was this an up movement? elif cgidata.has_key(uptag): # As long as this one isn't the first rule, move it up if rules: rules.insert(-1, (pattern, action, False)) else: rules.append((pattern, action, False)) # Was this the down movement? elif cgidata.has_key(downtag): downi = i - 2 rules.append((pattern, action, False)) # Otherwise, just retain this one in the list else: rules.append((pattern, action, False)) # Move any down button filter rule if downi is not None: rule = rules[downi] del rules[downi] rules.insert(downi + 1, rule) mlist.header_filter_rules = rules
def process(mlist, msg, msgdata): # Before anything else, check DMARC if necessary. We do this as early # as possible so reject/discard actions trump other holds/approvals and # wrap/munge actions get flagged even for approved messages. # But not for owner mail which should not be subject to DMARC reject or # discard actions. if not msgdata.get('toowner'): msgdata['from_is_list'] = 0 dn, addr = parseaddr(msg.get('from')) if addr and mlist.dmarc_moderation_action > 0: if Utils.IsDMARCProhibited(mlist, addr): # Note that for dmarc_moderation_action, 0 = Accept, # 1 = Munge, 2 = Wrap, 3 = Reject, 4 = Discard if mlist.dmarc_moderation_action == 1: msgdata['from_is_list'] = 1 elif mlist.dmarc_moderation_action == 2: msgdata['from_is_list'] = 2 elif mlist.dmarc_moderation_action == 3: # Reject text = mlist.dmarc_moderation_notice if text: text = Utils.wrap(text) else: listowner = mlist.GetOwnerEmail() text = Utils.wrap( _("""You are not allowed to post to this mailing list From: a domain which publishes a DMARC policy of reject or quarantine, and your message has been automatically rejected. If you think that your messages are being rejected in error, contact the mailing list owner at %(listowner)s.""")) raise Errors.RejectMessage, text elif mlist.dmarc_moderation_action == 4: raise Errors.DiscardMessage # Get member address if any. for sender in msg.get_senders(): if mlist.isMember(sender): break else: sender = msg.get_sender() if (mlist.member_verbosity_threshold > 0 and Utils.IsVerboseMember(mlist, sender)): mlist.setMemberOption(sender, mm_cfg.Moderate, 1) syslog('vette', '%s: Automatically Moderated %s for verbose postings.', mlist.real_name, sender) if msgdata.get('approved'): return # First do site hard coded header spam checks for header, regex in mm_cfg.KNOWN_SPAMMERS: cre = re.compile(regex, re.IGNORECASE) for value in msg.get_all(header, []): mo = cre.search(value) if mo: # we've detected spam, so throw the message away raise SpamDetected # Now do header_filter_rules # TK: Collect headers in sub-parts because attachment filename # extension may be a clue to possible virus/spam. headers = u'' # Get the character set of the lists preferred language for headers lcset = Utils.GetCharSet(mlist.preferred_language) for p in msg.walk(): headers += getDecodedHeaders(p, lcset) for patterns, action, empty in mlist.header_filter_rules: if action == mm_cfg.DEFER: continue for pattern in patterns.splitlines(): if pattern.startswith('#'): continue # ignore 'empty' patterns if not pattern.strip(): continue pattern = Utils.xml_to_unicode(pattern, lcset) pattern = normalize(mm_cfg.NORMALIZE_FORM, pattern) try: mo = re.search(pattern, headers, re.IGNORECASE | re.MULTILINE | re.UNICODE) except (re.error, TypeError): syslog('error', 'ignoring header_filter_rules invalid pattern: %s', pattern) if mo: if action == mm_cfg.DISCARD: raise Errors.DiscardMessage if action == mm_cfg.REJECT: if msgdata.get('toowner'): # Don't send rejection notice if addressed to '-owner' # because it may trigger a loop of notices if the # sender address is forged. We just discard it here. raise Errors.DiscardMessage raise Errors.RejectMessage( _('Message rejected by filter rule match')) if action == mm_cfg.HOLD: if msgdata.get('toowner'): # Don't hold '-owner' addressed message. We just # pass it here but list-owner can set this to be # discarded on the GUI if he wants. return hold_for_approval(mlist, msg, msgdata, HeaderMatchHold(pattern)) if action == mm_cfg.ACCEPT: return