use Mail::Decency::ContentFilter;
    my $content_filter = Mail::Decency::ContentFilter->new( {
        config => '/etc/decency/content-filter.yml'
    } );


Postfix:Decency::ContentFilter implements multiple content filter


You have to edit two files: and in /etc/postfix

Add the following to the end of your file:

    # the decency server itself
    decency     unix  -       -       n       -       4        smtp
        -o smtp_send_xforward_command=yes
        -o disable_dns_lookups=yes
        -o max_use=20
        -o smtp_send_xforward_command=yes
        -o disable_mime_output_conversion=yes
        -o smtp_destination_recipient_limit=1
    # re-inject mails from decency for delivery      inet  n       -       -       -       -       smtpd
        -o content_filter= 
        -o receive_override_options=no_unknown_recipient_checks,no_header_body_checks,no_milters
        -o smtpd_helo_restrictions=
        -o smtpd_client_restrictions=
        -o smtpd_sender_restrictions=
        -o smtpd_recipient_restrictions=permit_mynetworks,reject_unauth_destination,permit
        -o mynetworks=
        -o smtpd_authorized_xforward_hosts=

There are two possible ways you can include this content filter into postfix. The first is via content_filter, the second via check_*_access, eg check_client_access.

  • content_filter

    The advantage: it is easy. The disadvantage: all mails (incoming, outgoing) will be filtered. In a one-mailserver-for-all configuration this might be ugly.

        content_filter = decency:
  • Via check_*_access

    And example using pcre on all mails would be:

        smtpd_client_restrictions =
            # ...
            check_client_access = pcre:/etc/postfix/decency-filter, reject
            # ...

    Then in the /etc/postfix/decency-filter file:

        # /path/to/access
        /./ FILTER decency:


Provide either a hashref or a YAML file.


    spool_dir: /var/spool/decency
    accept_scoring: 1
    notification_from: 'Postmaster <postmaster@localhost>'
    enable_stats: 1
        - logging.yml
        - database.yml
        - cache.yml
        port: 16000
        instances: 3
        port: 10250
        behavior: scoring
        threshold: -50
        handle: tag
        noisy_headers: 1
        spam_subject_prefix: "SPAM:"
        # for handle: bounce or delete:
        #notify_recipient: 1
        #recipient_template: 'templates/spam-recipient-notify.tmpl'
        #recipient_subject: 'Spam detection notification'
        handle: bounce
        # for handle: bounce, delete or quarantine
        notify_sender: 1
        notify_recipient: 1
        sender_template: 'templates/virus-sender-notify.tmpl'
        sender_subject: 'Virus detection notification'
        recipient_template: 'templates/virus-recipient-notify.tmpl'
        recipient_subject: 'Virus detection notification'
        #- MimeAttribs: "content-filter/mime-attribs.yml"
        - DKIM: "content-filter/dkim.yml"
        # - ClamAV: content-filter/clamav.yml
        - Bogofilter: content-filter/bogofilter.yml
        #- DSPAM: content-filter/dspam.yml
        - CRM114: content-filter/crm114.yml
        - Razor: content-filter/razor.yml
        - HoneyCollector: content-filter/honey-collector.yml
        # -
        #     SpamAssassin:
        #         disable: 0
        #         default_user:
        #         weight_translate:
        #             1: -100
        #             -2: 0
        #             -3: 100
        - Archive: content-filter/archive.yml


spool_dir : Str

The directory where to save received mails before filtering

temp_dir : Str

Holds temp files for modules

queue_dir : Str

Holds queued mails (currently working on)

mime_output_dir : Str

Directory for temporary mime output .. required by MIME::Parser

Defaults to spool_dir/mime

reinject_failure_dir : Str

Directory for reinjection failures

Defaults to spool_dir/failure

quarantine_dir : Str

Directory for quarantined mails (virus, spam)

Defaults to spool_dir/quarantine


There is either spam scoring, strict or keep.

Keep account on positive or negative score per file. Each filter module may increment or decrement score on handling the file. The overall score determines in the end wheter to bounce or re-inject the mail.

spam_behavior : Str

How to determine what is spam. Either scoring, strict or ignore

Default: scoring

spam_handle : Str

What to do with recognized spam. Either tag, bounce or delete

Default: tag

spam_subject_prefix : Str

If spam_handle is tag: "Subject"-Attribute prefix for recognized SPAM mails.

spam_threshold : Int

For spam_behavior: scoring. Each cann add/remove a score for the filtered mail. SPAM scores are negative, HAM scores positive. If this threshold is reached, the mail is considered SPAM.

Default: -100

spam_notify_recipient : Bool

If enabled -> send recipient notification if SPAM is recognized.

Default: 0

spam_recipient_template : Str

Path to template used for SPAM notification.

spam_recipient_subject : Str

Subject of the recipient's SPAM notification mail

Default: Spam detected

spam_noisy_headers : Bool

Wheter X-Decency headers in mail should contain detailed information.

Default: 0


Virus handling

virus_handle : Str

What to do with infected mails ? Either: bounce, delete or quarantine

Default: ignore

virus_notify_recipient : Bool

Wheter to notofy the recipient about infected mails.

Default: 0

virus_recipient_template : Str

Path to template used for recipient notification

virus_recipient_subject : Str

Subject of the recipient's notification mail

Default: Virus detected

virus_notify_sender : Str

Wheter to notify the sender of an infected mail (NOT A GOOD IDEA: BACKSCATTER!)

Default: 0

virus_sender_template : Str

Path to sender notification template

virus_sender_subject : Str

Subject of the sender notification

Default: Virus detected

accept_scoring : Bool

Wheter to accept scoring from (external) policy server.

Default: 0

policy_verify_key : Str

Path to public (verification) key for scoring verification

Default: 0

policy_verify_key_rsa : Crypt::OpenSSL::RSA

Instance of verification key (Crypt::OpenSSL::RSA)

session_data : Mail::Decency::Core::SessionItem::ContentFilter

SessionItem (Mail::Decency::Core::SessionItem::ContentFilter) of the current handle file

notification_from : Str

Notification sender (from address)

Default: Postmaster <postmaster@localhost>



Init cache, database, logger, dirs and content filter


Inits the queue, checks spool dir for existing files -> read them


Reads all content filters, creates instance and add to list of filters


Starts all POE servers without calling the POE::Kernel->run


Start and run the server via POE::Kernel->run


Train spam/ham into modules


Returns code ref to handlers

    my $handlers_ref = $content_filter->get_handlers();
    $handlers_ref->( {
        file => '/tmp/somefile',
        size => -s '/tmp/somefile',
        from => 'sender@domain.tld',
        to   => 'recipient@domain.tld',
    } );


Calls the handle method of all registered filters.

Will be called from the job queue


Called after modules have filtered the mail. Will perform according to spam_handle directive.

  • delete

    Remvoe mail silently

  • bounce

    Bounce mail back to sender

  • ignore

    Ignore mail, simply forward

  • tag

    Tag mail, insert X-Decency-Status and X-Decency-Score headers. If detailed: also X-Decency-Details header.


Mail has been recognized as infected. Handle it according to virus_handle

  • bounce

    Send back to sender

  • delete

    Silently remove

  • quarantine

    Do not deliver mail, move it into quarantine directory.

  • ignore

    Deliver to recipient


Called after mails has not been recognized as virus nor SPAM. Do deliver to recipient. With noisy_headers, include spam X-Decency-(Result|Score|Details) into header.


Reinject mails to postfix queue, or archive in send-queue


Send either spam or virus notification

    $content_filter->send_notify( virus => recipient => 'recipient@domain.tld' );


Inits the Mail::Decency::Core::SessionItem::ContentFilter session object for the current handled mail.


Write session to cache. Called at the end of the session.


Add spam score (positive/negative). If threshold is reached -> throw Mail::Decency::Core::Exception::Spam exception.


Virus is found. Throw Mail::Decency::Core::Exception::Virus exception.


Save a mail to some dir. Called from quarantine or reinjection failures


Ulrich Kautz <>


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


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

1 POD Error

The following errors were encountered while parsing the POD:

Around line 115:

You forgot a '=back' before '=head1'