[Mailman-Users] Writing a custom handler
mark at msapiro.net
Tue Jul 2 00:35:34 CEST 2013
On 7/1/2013 10:24 AM, Chris Nulk wrote:
> Hello user,
> I am writing a custom handler to globally ban email address from sending
> messages sent to Mailman. I know I can use Mark's add_banned.py script
> to add an address to all lists. However, if I add an address to be
> banned, as the administrator for all lists, I don't want a list admin to
> remove a pest from their list(s). I banned an address for being a pest
> to all lists (or a majority of them), therefore, the address stays banned.
Note that the ban_list only prevents the address from subscribing. If
the address was already a member when banned, it can still post.
> Before I put the custom handler in place and screw up my lists, I
> thought I would post it here so others more knowledgeable can review it
> and let me know if it will work, correct it, and/or improve it.
> #!/usr/bin/env python
> """This is a custom handler that will check all the sender addresses of
> a message against a global ban list. If any of the sender addresses are
> on the global ban list, the message will get logged and discarded.
> import sys
> from Mailman import mm_cfg
> from Mailman import Utils
> from Mailman import Message
> from Mailman import Errors
> from Mailman.i18n import _
> from Mailman.Logging.Syslog import syslog
> from Mailman.MailList import MailList
You don't actually use MailList so there's no need to import it. Also
Utils, Message, Errors and _ are used only by the discard code you
copied. You could instead just
>From Mailman.Handlers.Moderate import do_discard
and use that if it wasn't important to have specific messages. OTOH, see
> def process(mlist, msg, msgdata):
> # added because it was in Mailman/Handlers/Moderate.py
> # I am guessing it has to due in part with an upstream
> # pipeline handler marking the message approved and/or
> # because the handler can be moved to different parts
> # of the pipeline.
> # But, I have been wrong before.
> if msgdata.get('approved') or msgdata.get('fromusenet'):
There are two things in the above. The test for 'approved' says if this
message was pre-approved by inclusion of an Approved: <list_pw> header
or pseudo-header, or if this message was held for moderation by a prior
handler and approved by the moderator, then skip this handler.
The test for 'fromusenet' says if this post arrived via the
Mail<->Usenet gateway, then skip this handler.
You probably don't want the " or msgdata.get('fromusenet')" part even if
you are gating from usenet.
> # First, initialize the banlist
> banlist = 
> # Read in the global ban list of email addresses
> # mm_cfg.GLOBALBANLIST_FILENAME is defined in mm_cfg and should
> # be the full path to the file.
> with open(mm_cfg.GLOBALBANLIST_FILENAME) as f:
> for addr in f:
> except IOError:
> # cannot open the global ban list for whatever reason
> # log it and continue with the next pipeline handler
> syslog('error', 'An error occurred opening the global ban list')
I would put something like
except IOError, e:
"Can't open %s: %s" % (mm_cfg.GLOBALBANLIST_FILENAME, e)
> # unspecified error
> # log it and continue with the next pipeline handler
> syslog('error', "ERROR: Unknown error: ", sys.exc_info())
and here I would put
'ERROR: %s: %s' % (sys.exc_info(), sys.exc_info())
> # Go through possible senders. Check if any of them are
> # on the global ban list
> for sender in msg.get_senders():
> if sender.lower() in banlist:
> # None of the sender addresses were in the global ban
> # list so return and continue with the next pipeline
> # handler
> # A sender was found on the global ban list. Log it and
> # discard the message notifying the list owner
> if sender:
How can this be False?
> # Log banned sender to the vette log
> syslog('vette', '%s is banned by the global ban list', sender)
> # Perform message discard
> do_discard_globalban(mlist, msg)
You could just import do_discard from Mailman.Handlers.Moderate and use
that, but you may want the custom messages. If so, you may also want
do_discard_globalban(mlist, msg, sender)
> assert 0, 'Bad sender in GlobalBan.py'
> # copied almost verbatim from Mailman/Handlers/Moderate.py
> def do_discard_globalban(mlist, msg):
> sender = msg.get_sender
Actually, that should be
sender = msg.get_sender()
but as I indicate above it would be better to pass the bad sender in the
call because msg.get_senders() returns a list of sender addresses and
msg.get_sender() returns a single address which may not be the
get_senders() address you found in your blacklist.
> # forward auto-discards to list owners?
> if mlist.forward_auto_discards:
> lang = mlist.preferred_language
> # is varhelp used anywhere?
> varhelp = '%s/?VARHELP=privacy/sender/discard_these_nonmembers' % \
> mlist.GetScriptURL('admin', absolute=1)
It is the URL to the web admin (Details for discard_these_nonmembers)
page. Someone intended to include it in the auto-discard notice but
either never did or at some point it was dropped without removing the
above. It is currently superfluous.
> nmsg = Message.UserNotification(mlist.GetOwnerEmail(),
> _('Global Ban List Auto-discard
> text = MIMEText(Utils.wrap(_("""\
> The sender of the attached message is on the Global Ban list. Therefore,
> the message
> has been automatically discarded.""")),
> # Discard the message
> raise Errors.DiscardMessage
Mark Sapiro <mark at msapiro.net> The highway is for gamblers,
San Francisco Bay Area, California better use your sense - B. Dylan
More information about the Mailman-Users