The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package Mail::Decency::ContentFilter::Cookbook;

use strict;
use warnings;

use version 0.74; our $VERSION = qv( "v0.1.4" );

=head1 NAME

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

=head1 DESCRIPTION

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

=head1 EXAMPLES

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

=head2 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 );
        
    }

=head2 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';
        
    }

=head2 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" );
        }
    }

=head2 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).

=head3 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 L<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 );
    }

=head2 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 );
        }
    }

=head1 INCLUDE MODULE

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

=head2 YAML

In content-filter.yml ...

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

=head2 PERL

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

=head1 AUTHOR

Ulrich Kautz <uk@fortrabbit.de>

=head1 COPYRIGHT

Copyright (c) 2010 the L</AUTHOR> as listed above

=head1 LICENCSE

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

=cut


1;