[Mailman-Users] Writing a custom handler

Chris Nulk cnulk at scu.edu
Tue Jul 2 17:48:29 CEST 2013


Thank you Mark for your time.

I went through and made the syslog change, removed the usenet bit, and 
correct my typing error.

On 7/1/2013 3:35 PM, Mark Sapiro wrote:
> 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.

I know the ban_list only prevents the address from subscribing.  My goal 
for this custom handler is to stop a pesky addresses from subscribing, 
unsubscribing, posting, or having any interaction with any of my lists.  
I have add a few addresses try to subscribe to all the lists on my 
site.  I highly doubt those are all legit so I want to ban and have 
nothing to do with those addresses in the future.

> 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
> below.

I removed the MailList line.  As for the do_discard, I actually was 
going to import it, however, I decided I wanted to have a different 
discard message indicating the global ban list caused the discard. Then, 
the list owners would know it wasn't something with their list 
configuration.

>>
>>      # 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:
>>              break
>>      else:
>>          # None of the sender addresses were in the global ban
>>          #   list so return and continue with the next pipeline
>>          #   handler
>>          return
>>
>>      # 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?

If I understand the code properly, sender should never be False or None 
but if something happens that I do not expect, I have a way to catch 
it.  Or, am I missing something?

> 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)
>> 
>> # copied almost verbatim from Mailman/Handlers/Moderate.py
>> def do_discard_globalban(mlist, msg):
>>      sender = msg.get_sender

I take it that should I change the line -

     do_discard_globalban(mlist, msg, sender)

I should also update the def for it to -

     def do_discard_globalban(mlist, msg, sender)

and I can remove the line -

     sender = msg.get_sender()


I have a few more questions.  My plan for the custom handler is install 
it as the first handler in the global pipeline for all lists.  I have 
seen how to do that in the faqs.   I also believe that once it is 
running with Mailman, it will be in memory and executable - no loading 
and unloading of the handler.  Once Mailman loads, it loads all the 
handlers and is ready to go.

My handler reads a file every time it runs.  Not very efficient. Once I 
have read in the banlist from the file, will the banlist always be 
available for future iterations through the code?

What I would like to do is read the global ban list file once to build 
the ban list but update the ban list if there has been a change in the 
global ban list file.

Thanks for any assistance,

Chris


----------------------- Updated Global Ban list custom handler 
--------------------

#!/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

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.
     if msgdata.get('approved'):
         return

     # 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.
     try:
         with open(mm_cfg.GLOBALBANLIST_FILENAME) as f:
             for addr in f:
                 banlist.append(addr.lower().strip())
     except IOError, e:
         # cannot open the global ban list for whatever reason
         # log it and continue with the next pipeline handler
         syslog('error',
                "Can't open %s: %s" % (mm_cfg.GLOBALBANLIST_FILENAME, e)
                )
         return
     except:
         # unspecified error
         # log it and continue with the next pipeline handler
         syslog('error',
                'ERROR: %s: %s' % (sys.exc_info()[0], sys.exc_info()[1])
               )
         return

     # 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:
             break
     else:
         # None of the sender addresses were in the global ban
         #   list so return and continue with the next pipeline
         #   handler
         return

     # A sender was found on the global ban list.  Log it and
     #   discard the message notifying the list owner
     if sender:
         # 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, sender)
     else:
         assert 0, 'Bad sender in GlobalBan.py'

# copied almost verbatim from Mailman/Handlers/Moderate.py
def do_discard_globalban(mlist, msg, sender):
     # forward auto-discards to list owners?
     if mlist.forward_auto_discards:
         lang = mlist.preferred_language
         nmsg = Message.UserNotification(mlist.GetOwnerEmail(),
                                         mlist.GetBouncesEmail(),
                                         _('Global Ban List Auto-discard 
notification'),
                                         lang=lang)
         nmsg.set_type('multipart/mixed')
         text = MIMEText(Utils.wrap(_("""\
The sender of the attached message is on the Global Ban list. Therefore, 
the message
has been automatically discarded.""")),
                         _charset=Utils.GetCharSet(lang))
         nmsg.attach(text)
         nmsg.attach(MIMEMessage(msg))
         nmsg.send(mlist)
     # Discard the message
     raise Errors.DiscardMessage





More information about the Mailman-Users mailing list