The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Mail::Decency::ContentFilter::Cookbook - How to write a content filter module

DESCRIPTION

This module contains a description on howto write a content filter module.

EXAMPLES

Hope this helps to understand what you can do. Have a look at the existing modules for more examples. Also look at Mail::Decency::ContentFilter::Core for available methods.

SIMPLE EXAMPLE

    package Mail::Decency::ContentFilter::MyModule;
    
    use Moose;
    extends 'Mail::Decency::ContentFilter::Core';
    
    has some_key => ( is => 'rw', isa => 'Bool', default => 0 );
    
    #
    # The init method is kind of a new or BUILD method, which should
    #   init all configurations from the YAML file
    #
    sub init {
        my ( $self ) = @_;
        
        # in YAML:
        #   ---
        #   some_key: 1
        $self->some_key( 1 )
            if $self->config->{ some_key };
    }
    
    #
    # The handle method will be called by the ContentFilter server each time a new
    #   mail is filtered
    #
    
    sub handle {
        my ( $self ) = @_;
        
        # get the temporary queue file
        my $file = $self->file;
        
        # read the size
        my $size = $self->file_size;
        
        # manipulate the MIME::Entity object of the current
        $self->mime->head->add( 'X-MyModule' => 'passed' );
        $self->write_mime;
        
        # get sender and recipient
        my $sender = $self->from;
        my $recipient = $self->to;
        
        # access the datbaase
        my $data_ref = $self->database->get( schema => table => $search_ref );
        $data_ref->{ some_attrib } = time();
        $self->database->get( schema => table => $search_ref, $data_ref );
        
        # access the cache
        my $cached_ref = $self->cache->get( "cache-name" ) || { something => 1 };
        $cached_ref->{ something } ++;
        $self->cache->set( "cache-name" => $cached_ref );
        
    }

SPAM FILTER EXAMPLE

    package Mail::Decency::ContentFilter::MySpamFilter;
    
    use Moose;
    extends qw/
        Mail::Decency::ContentFilter::Core::Spam
    /;
    
    
    sub handle {
        my ( $self ) = @_;
        
        # throws exception if spam is recognized
        $self->add_spam_score( -100, "You shall not send me mail" )
            if $self->from eq 'evil@sender.tld';
        
    }

VIRUS FILTER EXAMPLE

    package Mail::Decency::ContentFilter::MyVirusFilter;
    
    use Moose;
    extends qw/
        Mail::Decency::ContentFilter::Core::Virus
    /;
    
    sub handle {
        my ( $self ) = @_;
        
        # throws exception
        if ( time() % 86400 == 0 ) {
            $self->found_virus( "Your daily virus" );
        }
    }

HOOKS

There are two kinds of hooks which can be implemented by any modules. They exist, because not necessary all modules will be run in every session (eg if the first recognizes the mail as spam and throws an exception).

PRE FINISH HOOK

Called after the modules are processed. Has to return the status ("virus", "spam", "drop" or "ok") and the final code (CF_FINAL_* from Mail::Decency::ContentFilter::Core::Constants).

    package Mail::Decency::ContentFilter::MyPreHook;
    
    use Moose;
    extends 'Mail::Decency::ContentFilter::Core';
    use Mail::Decency::ContentFilter::Core::Constants;
    
    # example from the HoneyCollector modules, which
    #   assures marked mails to be collected
    sub hook_pre_finish {
        my ( $self, $status ) = @_;
        
        # has been flagged..
        return ( $status, CF_FINAL_OK )
            if ! $self->session_data->has_flag( 'honey' )
            || $self->session_data->has_flag( 'honey_collected' )
        ;
        
        # collect the honey
        $self->_collect_honey();
        
        # drop the mail
        return ( 'drop', CF_FINAL_OK );
    }

POST FINISH HOOK

Called after the finish_(ok|spam|virus) methods. Takes the status as arguments and has to return the status and the final code.

    package Mail::Decency::ContentFilter::MyPreHook;
    
    use Moose;
    extends 'Mail::Decency::ContentFilter::Core';
    use Mail::Decency::ContentFilter::Core::Constants;
    
    sub hook_post_finish {
        my ( $self, $status ) = @_;
        
        # force to pass all recognized virus and spams..
        if ( $status eq 'virus' || $status eq 'spam' ) {
            return ( ok => CF_FINAL_OK );
        }
        
        # delete all mails recognized as OK
        elsif ( $status eq 'ok' ) {
            return ( drop => CF_FINAL_OK );
        }
        
        # bounce mails supposed to be dropped
        elsif ( $status eq 'drop' ) {
            return ( ok => CF_FINAL_ERROR );
        }
    }

INCLUDE MODULE

To include the module, simple add it in your contnet filter

YAML

In content-filter.yml ...

    ---
    
    # ..
    
    filters:
        - MyModule:
            some_key: 1
        - MyModule: /path/to/my-module.yml
    

PERL

    my $content_filter = Mail::Decency::ContentFilter->new(
        # ..
        filters => [
            { MyModule => { some_key => 1 } }
        ]
    );

AUTHOR

Ulrich Kautz <uk@fortrabbit.de>

COPYRIGHT

Copyright (c) 2010 the "AUTHOR" as listed above

LICENCSE

This library is free software and may be distributed under the same terms as perl itself.