[Mimedefang] Next generation mimedefang-filter

Nik Clayton nik at ngo.org.uk
Thu May 12 06:36:22 EDT 2005


Hi all,

I'm thinking out loud here.

I've got a number of different systems running MimeDefang, grouped in to 
'classes'.  Each system in the same class has identical functionality, 
and some, but not all, functionality can be shared by different classes.

At the moment each class has a custom mimedefang-filter file.  There's a 
lot of duplication between files, which is bad, and it makes it tricky 
to verify that the same functionality is implemented in the same way.

It's also a pain to test.

So here's a radical approach.

Ditch mimedefang-filter.  Have mimedefang.pl use something like 
Module::Pluggable to find everything in the MimeDefang::Filter::Plugin 
namespace.

Then have every filter_* subroutine in mimedefang-filter be something like:

   sub filter_sender {

     # Assume @plugins is a list of references to
     # MimeDefang::Filter::Plugin::* objects

     foreach my $plugin (@plugins) {
       if $plugin->can('filter_sender') {
         $plugin->filter_sender(@_);
       }

       return if message_rejected();
     }
   }

In other words, it calls each plugin's filter_sender() method (if it 
exists), and allows each plugin's method to reject the message (and 
short circuit the rest of the processing) as necessary.

There needs to be some sort of ordering mechanism in here to ensure that 
multiple plugins with a filter_sender() method get called in the right 
order.  That might be provided by the plugins themselves (perhaps they 
specify whether or not they're 'heavy' or 'light', and MD runs all the 
'light' plugins first; or perhaps there's a config file managed by the 
user that specifies an implicit ordering).

A config file is probably the right approach -- this also lets you put 
per-plugin configuration data in there.  Perhaps .ini style;

   # Assume something's in the MimeDefang::Filter::Plugin namespace
   # unless it says otherwise

   # Specify an order to run plugins
   [global]
   order=Whitelist, Blacklist, AntiVirus, SpamAssassin

   # MimeDefang::Filter::Plugin::Whitelist options
   [Whitelist]
   whitelist=/path/to/whitelist.txt

   # Options for the ::SpamAssassin plugin
   [SpamAssassin]
   bayes_db=/path/to/bayes.db
   network_tests=1

and so on.

This abstracts specific functionality out in to separate modules -- 
these are then (hopefully) easier to understand, and, much more 
importantly, easier to unit test outside of MimeDefang.  It's also 
easier to distribute them to others -- if you've come up with a great 
way of doing greylisting, for example, you don't need to cut and paste 
from your filter file, you just distribute your 
M::F::Plugin::GreyListing file.

Bringing existing mimedefang-filter files in to line with this is 
probably as simple as putting this at the top of the file:

   package MimeDefang::Filter::Plugin::MyBigPluginThatDoesEverything;
   use base 'MimeDefang::Filter::Plugin::base';

which pre-supposes the existence of a ::base class that provides methods 
  like "action_bounce()" (which would come from mimedefang.pl).

Poking through the suggested-minimum-filter-for-windows-clients that 
ships with 2.51, a plugin split along the lines of:

  Attachments - filters attachments based on filenames and extensions

  Attachments::Zip - filters .zip files based on the files in Attachments

  HTMLCleaner - cleans HTML in messages

  Partial - blocks message/partial messages

  Virus - scans for viruses

  SpamAssassin - runs SpamAssassin over the message

Some additional plugins that would be easy to write:

  Blacklist - filters messages according to a blacklist

  Whitelist - filters messages according to a whitelist

  SPF - carries out SPF checks

  Headers - verifies that certain headers exist and have valid values

Thoughts?  Would anyone use this?  David, would you be happy to see 
MimeDefang go in this sort of direction?

N



More information about the MIMEDefang mailing list