.\" Automatically generated by Pod::Man 2.28 (Pod::Simple 3.28) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{ . if \nF \{ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "Courier::Filter::Overview 3pm" .TH Courier::Filter::Overview 3pm "2015-11-28" "perl v5.20.2" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" Courier::Filter::Overview \- Architectural and administrative overview of Courier::Filter .SH "DESCRIPTION" .IX Header "DESCRIPTION" Courier::Filter is a purely Perl-based mail filter framework for the Courier \&\s-1MTA.\s0 .SS "The Courier \s-1MTA\s0's \fBcourierfilter\fP interface" .IX Subsection "The Courier MTA's courierfilter interface" Courier offers an interface for daemon-style processes to act as mail filters, called \fBcourierfilter\fRs. For every incoming mail message, right after the \&\s-1DATA\s0 command in the \s-1SMTP\s0 transaction phase has completed, Courier calls every registered mail filter through a \s-1UNIX\s0 domain socket the filter is listening on, and feeds it the file names of the incoming message and one or more control files. The mail filter processes the message and its control file(s), and returns an SMTP-style status response. If the status response is a positive (\*(L"2xx\*(R") one, Courier accepts the message. Otherwise, Courier rejects the message using the returned response code and text. For details about the courierfilter interface, see courierfilter. .SS "About \fBCourier::Filter\fP" .IX Subsection "About Courier::Filter" Courier::Filter implements the courierfilter interface as a framework for mail filter modules that frees modules from the duties of creating and handling the \&\s-1UNIX\s0 domain sockets, waiting for connections from Courier, and reading and parsing message and control files. Thus, authors of filter modules can concentrate on writing the actual filter logic without having to care about things that can easily be abstracted and can be performed by the framework. .PP Courier::Filter allows multiple filter modules to be installed, and filter modules can be stacked and grouped hierarchically, and even a module's polarity can be reversed, so some modules can be used for explicitly \fIaccepting\fR messages while others are used in the traditional way for \fIrejecting\fR messages. .SS "Courier::Filter compared to other courierfilter implementations" .IX Subsection "Courier::Filter compared to other courierfilter implementations" There are some alternative implementations of the courierfilter interface: .IP "\fIWriting your own standalone courierfilter\fR" 4 .IX Item "Writing your own standalone courierfilter" If you need an ultra-high performance mail filter, writing a standalone courierfilter in C/\*(C+ is a good choice. You will have maximum freedom for optimizing your filter for performance and resource consumption. But regardless of which language you use, you will have to implement all the \s-1UNIX\s0 domain socket and connection handling and message and control file processing yourself. And you don't get the modularity and module grouping capabilities for free either. .IP "\fIcourierperlfilter\fR" 4 .IX Item "courierperlfilter" Courier brings a sample Perl-based courierfilter called \fBcourierperlfilter\fR. It is a C executable that employs Perl embedding (see perlembed) to execute a Perl script for every incoming message, which is about as performant as the purely Perl-based Courier::Filter. But for every Perl-based courierfilter you want to run, you have to use a separate instance of courierperlfilter, or implement your own modularity and module grouping. Also, the included template Perl script is not very modular in itself, and Courier::Filter's message and control file parsing features are missing. .IP "\fIpythonfilter\fR" 4 .IX Item "pythonfilter" \&\fBpythonfilter\fR by Gordon Messmer is a purely Python-based, modular, threaded courierfilter framework, similar to Courier::Filter. If you primarily speak Python, this is clearly your choice. pythonfilter also provides infrastructure to filter modules for modifying messages, even with versions of Courier prior to 0.57.1, which did not directly allow global mail filters to modify messages. As of version 1.1, pythonfilter supports only a linear topology in the configuration of its filter modules. .SS "Using Courier::Filter" .IX Subsection "Using Courier::Filter" First, Courier::Filter (of course) and the filter modules that you plan to use for filtering your incoming mail need to be installed somewhere in your Perl include path (see the last lines of `perl \-V`). You may also need to adjust the \fBCourier::Config\fR class (in \f(CW\*(C`Courier/Config.pm\*(C'\fR) to reflect your system's paths. As for the filter modules, you can either use prepared ones (see \&\*(L"Bundled Courier::Filter modules\*(R" for a list of modules that come with Courier::Filter), or you can write your own (see \*(L"How filter modules work\*(R"). .PP Second, you need to create a configuration file for Courier::Filter. Courier::Filter usually seeks for it at \&\f(CW\*(C`/etc/courier/filters/courier\-filter\-perl.conf\*(C'\fR (see Courier::Config on how to configure that). This file is a Perl snippet that must \f(CW\*(C`use\*(C'\fR the filter modules you want to use, and then fill in the \f(CW$options\fR global variable with the desired configuration options, instantiating filter modules and loggers as required. .PP If you plan to use non-ASCII string literals in your configuration file, it should be encoded in \s-1UTF\-8 \s0(which is the native internal character encoding of Perl 5.8+ and Courier::Filter), and if it is, it \fImust\fR do \f(CW\*(C`use utf8\*(C'\fR. (It is possible for the configuration file to be encoded differently, but you still \fImust\fR explicitly specify the used encoding, see encoding for how to do that.) .PP For example, this is how a simple configuration file could look like: .PP .Vb 1 \& use utf8; \& \& use Courier::Filter::Logger::File; \& use Courier::Filter::Module::Header; \& \& $options = { \& logger => Courier::Filter::Logger::File\->new( \& file_name => \*(Aq/var/log/courier\-filter\-perl.log\*(Aq, \& timestamp => 1 \& }, \& \& modules => [ \& Courier::Filter::Module::Header\->new( \& fields => { \& subject => qr/fuzzybuzzy/ \& }, \& response => \*(AqNo fuzzybuzzy, please!\*(Aq \& ) \& ] \& }; .Ve .PP These options will be used when creating the \f(CW\*(C`Courier::Filter\*(C'\fR object. For a detailed explanation of supported configuration options and how filter modules can be grouped, even hierarchically, see \*(L"\fInew()\fR\*(R" in Courier::Filter. .PP Third, you need to make Courier aware of Courier::Filter by installing a symlink in \f(CW\*(C`/etc/courier/filters/active/\*(C'\fR pointing to the \&\f(CW\*(C`courier\-filter\-perl\*(C'\fR executable (which is used for bootstrapping Courier::Filter): .PP .Vb 1 \& $ ln \-s $PATH_TO/courier\-filter\-perl /etc/courier/filters/active/ .Ve .PP Finally, you may start (or restart) Courier::Filter (including any other installed courierfilters; Courier must of course already be running): .PP .Vb 1 \& $ sudo courierfilter restart .Ve .PP In syslog, you should see the following message and no further error messages: .PP .Vb 1 \& Jan 24 01:42:15 yourhost courierfilter: Starting courier\-filter\-perl .Ve .PP Any errors occurring while Courier::Filter is running will appear in syslog as well. A broken filter module will not crash Courier::Filter, but will record any Perl error messages in syslog, and \fIreject\fR incoming mail messages with a \&\fItemporary\fR status code, so as not to enable attackers to circumvent the configured Courier::Filter mail filtering. .SS "How filter modules work" .IX Subsection "How filter modules work" Filter modules are Perl classes that are derived from the class \&\fBCourier::Filter::Module\fR. See perlobj for an explanation of Perl's object orientation features. .PP Filter modules are to be instantiated in the \f(CW\*(C`courier\-filter\-perl.conf\*(C'\fR configuration file, with either \fBnormal polarity\fR (the default) or \fBinverse polarity\fR. Then, for every incoming mail message, Courier::Filter asks each configured filter module in turn for consideration of the message's acceptability. .PP Every module tries to match its filter criteria against the current message, yielding a so-called \fImatch result\fR, which can be either an \fBexplicit match\fR, an \fBimplicit mismatch\fR, or an \fBexplicit mismatch\fR. (Filter modules usually never return an \fIexplicit\fR mismatch, but only an \fIimplicit\fR one; see \&\*(L"Writing filter modules\*(R" if you want to know why.) .PP According to the filter module's polarity, the match result is then translated into a so-called \fIacceptability result\fR, which can be either an \fBexplicit reject\fR, an \fBimplicit accept\fR, or an \fBexplicit accept\fR. .PP This is how \fImatch results\fR are translated into \fIacceptability results\fR under normal and inverse polarity: .PP .Vb 9 \& polarity | match result | acceptability result \& \-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- \& | explicit match | explicit reject \& normal | implicit mismatch | implicit accept \& | explicit mismatch | explicit accept \& \-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- \& | explicit match | explicit accept \& inverse | implicit mismatch | implicit accept \& | explicit mismatch | explicit reject .Ve .PP Generally, Courier::Filter interprets the acceptability result as follows: .IP "\(bu" 4 If a module states an \fBexplicit reject\fR for the current message, Courier::Filter aborts the consideration process and rejects the message. .IP "\(bu" 4 If a module states an \fBimplicit accept\fR, Courier::Filter continues the consideration process with the next module in turn. .IP "\(bu" 4 If a module states an \fBexplicit accept\fR, Courier::Filter skips the rest of the group of modules and assumes the whole group to be an \fBimplicit accept\fR. .PP If no \fBexplicit reject\fR has occured when Courier::Filter finishes asking all filter modules, the message is accepted. .PP (For details on how to use advanced \fIfilter module grouping\fR, see the description of the \f(CW\*(C`modules\*(C'\fR option in \*(L"\fInew()\fR\*(R" in Courier::Filter.) .PP Abstracting the concept of a \*(L"match\*(R" from the concept of \*(L"acceptance\*(R" makes it possible to use filter modules with normal polarity for \*(L"black-listing\*(R" certain message characteristics, and filter modules with inverse polarity for \&\*(L"white-listing\*(R", while still allowing all modules to be written in a uniform sense of logic. That is, there are no dedicated \*(L"accepting\*(R" and \*(L"rejecting\*(R" modules, but only \*(L"matching\*(R" modules. (E.g. there are no \*(L"HeaderAccept\*(R" and \&\*(L"HeaderReject\*(R" modules, but only a \*(L"Header\*(R" module.) .SS "Writing filter modules" .IX Subsection "Writing filter modules" The main objective of Courier::Filter is to make it very easy to write new filter modules, so while the previous section described how filter modules work in general, we will now look at the details of writing your own filter modules. From here on you really should know what you are doing, so if you are not familiar with Perl's object orientation features, now is the time to read perlobj plus any documents referenced from there. .PP As already mentioned, filter modules are Perl classes derived from the class \&\fBCourier::Filter::Module\fR, which is an abstract base class and thus cannot be instantiated itself. .PP To ask a filter module for consideration of the message, Courier::Filter calls \&\f(CW\*(C`$module\->consider()\*(C'\fR, passing a \fBCourier::Message\fR object. \&\f(CW\*(C`$module\->consider()\*(C'\fR (if not overrided from \fBCourier::Filter::Module\fR) then calls \f(CW\*(C`$module\->match()\*(C'\fR, passing through the message object. .PP The \f(CW\*(C`match()\*(C'\fR method really is where a filter module decides whether a message matches the filter criteria, and this is usually the only method of \&\fBCourier::Filter::Module\fR that needs to be overrided. That method may use any configuration information from the filter module object (see Courier::Filter::Module, and of course your own class), and any information from the message object (see Courier::Message). .PP If a filter module wants to call external commands using \f(CW\*(C`system()\*(C'\fR, or functions from Perl modules that directly operate on files, it can efficiently bypass the message and control files processing features of Courier::Message by using the message object's \f(CW\*(C`control_file_names\*(C'\fR and \f(CW\*(C`file_name\*(C'\fR properties only. .PP Finally, after the message has been examined, \f(CW\*(C`match()\*(C'\fR must return a \fImatch result\fR of... .IP "\fBtrue\fR" 4 .IX Item "true" if the module wants to state an \fBexplicit match\fR (the first return value being the \s-1SMTP\s0 status response \fItext\fR, an optional second one being the \s-1SMTP\s0 status response \fIcode\fR), .IP "\fBundef\fR" 4 .IX Item "undef" if the module wants to state an \fBimplicit mismatch\fR, that is, indifference of whether the message should be accepted or rejected, .IP "\fBfalse\fR" 4 .IX Item "false" if the module wants to state an \fBexplicit mismatch\fR. .PP \&\f(CW\*(C`consider()\*(C'\fR then translates the \fImatch result\fR into a \fIacceptability result\fR as described in \*(L"How filter modules work\*(R". .PP \&\f(CW\*(C`match()\*(C'\fR should usually never return an \fIexplicit\fR mismatch (\fBfalse\fR), but an \fIimplicit\fR one (\fBundef\fR) instead for the message to pass \fIthis filter module\fR, while still allowing any further modules to \fIexplicitly\fR reject (under normal polarity) or accept (under inverse polarity) the message. See the description of the \f(CW\*(C`modules\*(C'\fR option in \*(L"\fInew()\fR\*(R" in Courier::Filter for specifics on how Courier::Filter uses acceptability results. .SS "A sample HeaderSimple filter module" .IX Subsection "A sample HeaderSimple filter module" Now let's see in practice how to write a simple filter module. For instance, we will create a simple variant of the \f(CW\*(C`Header\*(C'\fR module that matches a specified message header field against a specified string. Let's call it \&\f(CW\*(C`HeaderSimple\*(C'\fR. .PP First, we create a Perl module for the class \&\fBCourier::Filter::Module::HeaderSimple\fR, with the file name \&\f(CW\*(C`Courier/Filter/Module/HeaderSimple.pm\*(C'\fR. (That is, you need to install the file \f(CW\*(C`HeaderSimple.pm\*(C'\fR in the proper place in your Perl include path.) .PP Second, in that Perl module, we state the package/class name, and the name of the base class, which is usually \fBCourier::Filter::Module\fR: .PP .Vb 2 \& package Courier::Filter::Module::HeaderSimple; \& use base qw(Courier::Filter::Module); .Ve .PP Third, we override the \f(CW\*(C`match()\*(C'\fR method by defining a rudimentary \f(CW\*(C`match\*(C'\fR sub: .PP .Vb 2 \& sub match { \& my ($self, $message) = @_; \& \& # ... \& } .Ve .PP The first argument of the \f(CW\*(C`match()\*(C'\fR method is (as usual in Perl's object orientation model) the module object itself, which provides access to its configuration options. The second argument is the message object that is to be examined. The ellipsis (\*(L"...\*(R") is where we will place our own filter logic. .PP Now, we expect our module to be instantiated like this: .PP .Vb 5 \& Courier::Filter::Module::HeaderSimple\->new( \& field => \*(Aqsubject\*(Aq, \& value => \*(Aqviagra\*(Aq, \& response => \*(AqGo away, spammer!\*(Aq \& ) .Ve .PP which makes the configuration options available from the hash keys \&\f(CW\*(C`$self\->{field}\*(C'\fR, \f(CW\*(C`$self\->{value}\*(C'\fR, and \f(CW\*(C`$self\->{response}\*(C'\fR. .PP We want to test whether the configured header field of the message matches the configured value, and if so, return the configured response, so we write: .PP .Vb 4 \& return $self\->{response} \& if $message\->header($self\->{field}) =~ m/\eQ$self\->{value}\eE/; \& return undef; \& # otherwise. .Ve .PP That's it. This is how the complete filter module looks like: .PP .Vb 2 \& package Courier::Filter::Module::HeaderSimple; \& use base qw(Courier::Filter::Module); \& \& sub match { \& my ($self, $message) = @_; \& \& return $self\->{response} \& if $message\->header($self\->{field}) =~ m/\eQ$self\->{value}\eE/; \& return undef; \& # otherwise. \& } .Ve .SS "Testing Courier::Filter modules" .IX Subsection "Testing Courier::Filter modules" You may dry-test filter modules using the \f(CW\*(C`test\-filter\-module\*(C'\fR utility. See its manpage for details. .PP You may also switch any or all installed filter modules into \*(L"testing\*(R" mode so you can test them without risking messages being actually rejected. See \&\*(L"\fInew()\fR\*(R" in Courier::Filter and \*(L"\fInew()\fR\*(R" in Courier::Filter::Module. .SS "Bundled Courier::Filter modules" .IX Subsection "Bundled Courier::Filter modules" The following prepared filter modules are included with this version of Courier::Filter: .IP "\fIBlankBody\fR" 4 .IX Item "BlankBody" Detection of messages with blank bodies (symptom of stupid spammers) .IP "\fI\s-1DNSBL\s0\fR" 4 .IX Item "DNSBL" Checking of the calling \s-1MTA\s0's \s-1IP\s0 address against one or more \s-1DNS\s0 black-lists .IP "\fI\s-1SPF\s0\fR" 4 .IX Item "SPF" \&\s-1SPF \s0(Sender Policy Framework) authorization checking of the calling \s-1MTA\s0's \s-1IP\s0 address against the envelope sender domain (classic inbound \s-1SPF\s0 checking) .IP "\fISPFout\fR" 4 .IX Item "SPFout" \&\s-1SPF\s0 authorization checking of the local system's \s-1IP\s0 address against the envelope sender domain (so-called outbound \s-1SPF\s0 checking) .IP "\fIEnvelope\fR" 4 .IX Item "Envelope" Literal and reg-exp matching of one or more \s-1RFC 2821\s0 message envelope fields .IP "\fIHeader\fR" 4 .IX Item "Header" Literal and reg-exp matching of one or more \s-1RFC 2822\s0 message header fields .IP "\fIFakeDate\fR" 4 .IX Item "FakeDate" Detection of implausible and malformed \*(L"Date\*(R" and \*(L"Resent-Date\*(R" header fields .IP "\fIClamAVd\fR" 4 .IX Item "ClamAVd" Malware detection using the ClamAV anti-virus scanner .IP "\fISpamAssassin\fR" 4 .IX Item "SpamAssassin" Spam detection using SpamAssassin .IP "\fIParts\fR" 4 .IX Item "Parts" .PD 0 .IP "\fIMIMEParts\fR (\s-1DEPRECATED\s0)" 4 .IX Item "MIMEParts (DEPRECATED)" .PD Size and \s-1MD5\s0 sum matching of message (\s-1MIME\s0 multipart and \s-1ZIP\s0 archive) parts .IP "\fISendCopy\fR" 4 .IX Item "SendCopy" Pseudo-filter for sending message copies to additional recipients .SS "Bundled Courier::Filter loggers" .IX Subsection "Bundled Courier::Filter loggers" The following prepared loggers are included with this version of Courier::Filter: .IP "\fIIOHandle\fR" 4 .IX Item "IOHandle" Logging to I/O handles .IP "\fISyslog\fR" 4 .IX Item "Syslog" Logging to syslog (based on the \fBIOHandle\fR logger) .IP "\fIFile\fR" 4 .IX Item "File" Logging to files (based on the \fBIOHandle\fR logger) .SH "SEE ALSO" .IX Header "SEE ALSO" courier-filter-perl, test-filter-module, Courier::Filter, Courier::Filter::Module, Courier::Message, Courier::Config .SH "REFERENCES" .IX Header "REFERENCES" .IP "The \fBcourierfilter\fR interface" 4 .IX Item "The courierfilter interface" .IP "\fBcourierperlfilter\fR" 4 .IX Item "courierperlfilter" .IP "\fBpythonfilter\fR" 4 .IX Item "pythonfilter" .SH "AVAILABILITY and SUPPORT" .IX Header "AVAILABILITY and SUPPORT" The latest version of Courier::Filter is available on \s-1CPAN\s0 and at . .PP Support is usually (but not guaranteed to be) given by the author, Julian Mehnle , preferably through the Courier \s-1MTA\s0's courier-users mailing list , which is subscribable through . .SH "AUTHOR and LICENSE" .IX Header "AUTHOR and LICENSE" Courier::Filter is Copyright (C) 2003\-2008 Julian Mehnle . All rights reserved. .PP Courier::Filter is free software. You may use, modify, and distribute it under the same terms as Perl itself, i.e. under the \s-1GNU GPL\s0 or the Artistic License.