[Mailman-Users] Writing a custom handler
Chris Nulk
cnulk at scu.edu
Tue Jul 2 22:49:35 CEST 2013
On 7/2/2013 12:17 PM, Mark Sapiro wrote:
> On 07/02/2013 11:38 AM, Chris Nulk wrote:
>>
>> I did forget about some of my other questions. I plan on writing
>> another custom handler for a list-specific issue. Where would I look if
>> I wanted to intercept messages related to subscribing, unsubscribing,
>> and options processing?
>
> These are messages to the -request, -join, -subscribe, -leave and
> -unsubscribe addresses and are all processed by CommandRunner. Only
> IncomingRunner processes a pipeline of handlers. There is no comparable
> processing or custom handler option for CommandRunner. You would have to
> modify Mailman/Queue/CommandRunner.py itself.
Hmmm. I have a few lists where unsubscribes are not allowed (not my
decision). On one of them, a persistent member keeps trying to
unsubscribe. I wanted to setup an automated response and discard mechanism.
>
>
>> How would I tell the difference between a
>> subscribe message and an unsubscribe message? Also, is there a
>> difference if the person does the subscribing or unsubscribing via the
>> web? Can I trap those?
>
> The web functions are handled by modules in Mailman/Cgi. They don't
> involve qrunners at all.
I have made enough mods to Mailman. Don't want make any more if I don't
have to so I will just keep discarding the unsubscribe requests.
>> Below is the latest update incorporating your suggestions.
>
> See some inline comments.
>
>
> def process(mlist, msg, msgdata):
> # Upstream pipeline handler marked message approved -
> Or message contained an Approved: <password> header.
Yep.
>
>> # respect the decision
>> if msgdata.get('approved'):
>> return
>>
>> # ban_file gets its value from mm_cfg.GLOBALBANLIST_FILENAME. If
>> # mm_cfg.GLOBALBANLIST_FILENAME is not defined neither is
>> # ban_file, so simply return.
>> if not ban_file:
>> return
>>
>> # Read in the global ban list of email addresses
>> if Ban_File_Changed(ban_file, ban_mlist):
>
> ban_mtime, not ban_mlist.
Yeah, I fixed it after I sent the message.
>
>
>> # Global Ban list has changed (or ban_mlist = -1),
>
> ban_mtime.
Same here.
>
>
>> # read in the changes
>> rc = Read_GlobalBan_File(ban_file)
>> if not rc:
>> # Problems reading the GlobalBan list
>> return
>
> Or just
> if not Read_GlobalBan_File(ban_file):
> return
I actually had more code here. As I reduced the code, I tightened the
code. Missed this one.
>> # Stat the ban file to get the modification time and compare it to the
>> # last time the file was changed. If a changed occured, update
>> # ban_mtime to current change time
>> def Ban_File_Changed(ban_file, ban_mtime):
>> try:
>> statinfo = os.stat(ban_file)
>> except IOError, e:
>> # cannot stat the global ban list for whatever reason
>> # log it and continue with the next pipeline handler
>> syslog('error',
>> "Can't stat %s: %s" % (ban_file, e)
>> )
>> return False
>> 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 False
>
> Do you really want to do this? The Mailman philosophy is all caught and
> handled exceptions should be explicit. An unanticipated exception will
> be caught at the top qrunner level which will log the error with
> traceback in the 'error' log and shunt the message rather than
> continuing processing with a message that triggered an unanticipated
> exception.
Instead of returning, should I raise an exception? For the IOError or
any error, whether or not the file can be read or perform a stat isn't a
big deal. I would rather log the error, clear the exception and keep
going. If there is a problem in this function, returning False just
means the ban_file hasn't changed so I will still be running sender
addresses against a potentially older banlist.
>>
>> # Read the Global Ban file and populate the banlist.
>> def Read_GlobalBan_File(ban_file):
>> try:
>> with open(ban_file) as f:
>> for addr in f:
>> # if addr is not in banlist, add it - to avoid duplicates
>> if addr not in banlist:
>> 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" % (ban_file, e)
>> )
>> return False
>> 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 False
>
> See comment above about generic exceptions.
The same applies here also. If there is a problem, the banlist doesn't
get updated.
I would rather not have the code produce a traceback for a file system
error. If for some reason, the file became unreadable, then the error
would kill Mailman repeatedly since this custom handler is going into
the global pipeline.
Can I get rid of the exception handling while still logging those errors?
Thanks,
Chris
More information about the Mailman-Users
mailing list