chiark / gitweb /
REORG Delete everything that's not innduct or build system or changed for innduct
[innduct.git] / doc / pod / hook-perl.pod
diff --git a/doc/pod/hook-perl.pod b/doc/pod/hook-perl.pod
deleted file mode 100644 (file)
index a860393..0000000
+++ /dev/null
@@ -1,614 +0,0 @@
-=head1 INN Perl Filtering and Authentication Support
-
-This is $Revision: 7860 $ dated $Date: 2008-06-07 05:46:49 -0700 (Sat, 07 Jun 2008) $.
-
-This file documents INN's built-in support for Perl filtering and reader
-authentication.  The code is based very heavily on work by Christophe
-Wolfhugel <wolf@pasteur.fr>, and his work was in turn inspired by the
-existing TCL support.  Please send any bug reports to inn-bugs@isc.org,
-not to Christophe, as the code has been modified heavily since he
-originally wrote it.
-
-The Perl filtering support is described in more detail below.  Basically,
-it allows you to supply a Perl function that is invoked on every article
-received by innd from a peer (the innd filter) or by nnrpd from a reader
-(the nnrpd filter).  This function can decide whether to accept or reject
-the article, and can optionally do other, more complicated processing
-(such as add history entries, cancel articles, spool local posts into a
-holding area, or even modify the headers of locally submitted posts).
-The Perl authentication hooks allow you to replace or supplement the
-readers.conf mechanism used by nnrpd.
-
-For Perl filtering support, you need to have Perl version 5.004 or newer.
-Earlier versions of Perl will fail with a link error at compilation time.
-http://language.perl.com/info/software.html should have the latest Perl
-version.
-
-To enable Perl support, you have to specify B<--with-perl> when you run
-configure.  See F<INSTALL> for more information.
-
-=head1 The innd Perl Filter
-
-When innd starts, it first loads the file _PATH_PERL_STARTUP_INND (defined
-in F<include/paths.h>, by default F<startup_innd.pl>) and then loads the
-file _PATH_PERL_FILTER_INND (also defined in F<include/paths.h>, by
-default F<filter_innd.pl>).  Both of these files must be located in the
-directory specified by pathfilter in F<inn.conf>
-(F</usr/local/news/bin/filter> by default).  The default directory for
-filter code can be specified at configure time by giving the flag
-B<--with-filter-dir> to configure.
-
-INN doesn't care what Perl functions you define in which files.  The only
-thing that's different about the two files is when they're loaded.
-F<startup_innd.pl> is loaded only once, when innd first starts, and is
-never reloaded as long as innd is running.  Any modifications to that file
-won't be noticed by innd; only stopping and restarting innd can cause it
-to be reloaded.
-
-F<filter_innd.pl>, on the other hand, can be reloaded on command (with
-C<ctlinnd reload filter.perl 'reason'>).  Whenever F<filter_innd.pl> is loaded,
-including the first time at innd startup, the Perl function
-filter_before_reload() is called before it's reloaded and the function
-filter_after_reload() is called after it's reloaded (if the functions
-exist).  Additionally, any code in either F<startup_innd.pl> or
-F<filter_innd.pl> at the top level (in other words, not inside a sub { })
-is automatically executed by Perl when the files are loaded.
-
-This allows one to do things like write out filter statistics whenever the
-filter is reloaded, load a cache into memory, flush cached data to disk,
-or other similar operations that should only happen at particular times or
-with manual intervention.  Remember, any code not inside functions in
-F<startup_innd.pl> is executed when that file is loaded, and it's loaded
-only once when innd first starts.  That makes it the ideal place to put
-initialization code that should only run once, or code to load data that
-was preserved on disk across a stop and restart of innd (perhaps using
-filter_mode() -- see below).
-
-As mentioned above, C<ctlinnd reload filter.perl 'reason'> (or C<ctlinnd reload
-all 'reason'>) will cause F<filter_innd.pl> to be reloaded.  If the function
-filter_art() is defined after the file has been reloaded, filtering is
-turned on.  Otherwise, filtering is turned off.  (Note that due to the way
-Perl stores functions, once you've defined filter_art(), you can't
-undefine it just by deleting it from the file and reloading the filter.
-You'll need to replace it with an empty sub.)
-
-The Perl function filter_art() is the heart of a Perl filter.  Whenever an
-article is received from a peer, via either IHAVE or TAKETHIS,
-filter_art() is called if Perl filtering is turned on.  It receives no
-arguments, and should return a single scalar value.  That value should be
-the empty string to indicate that INN should accept the article, or some
-rejection message to indicate that the article should be rejected.
-
-filter_art() has access to a global hash named %hdr, which contains all of
-the standard headers present in the article and their values.  The
-standard headers are:
-
-    Also-Control, Approved, Bytes, Cancel-Key, Cancel-Lock,
-    Content-Base, Content-Disposition, Content-Transfer-Encoding,
-    Content-Type, Control, Date, Date-Received, Distribution, Expires,
-    Face, Followup-To, From, In-Reply-To, Injection-Date, Injection-Info,
-    Keywords, Lines, List-ID, Message-ID, MIME-Version, Newsgroups,
-    NNTP-Posting-Date, NNTP-Posting-Host, Organization, Originator,
-    Path, Posted, Posting-Version, Received, References, Relay-Version,
-    Reply-To, Sender, Subject, Supersedes, User-Agent,
-    X-Auth, X-Canceled-By, X-Cancelled-By, X-Complaints-To, X-Face,
-    X-HTTP-UserAgent, X-HTTP-Via, X-Mailer, X-Modbot, X-Modtrace,
-    X-Newsposter, X-Newsreader, X-No-Archive, X-Original-Message-ID,
-    X-Original-Trace, X-Originating-IP, X-PGP-Key, X-PGP-Sig,
-    X-Poster-Trace, X-Postfilter, X-Proxy-User, X-Submissions-To,
-    X-Trace, X-Usenet-Provider, Xref.
-
-Note that all the above headers are as they arrived, not modified by
-your INN (especially, the Xref: header, if present, is the one of
-the remote site which sent you the article, and not yours).
-
-For example, the Newsgroups: header of the article is accessible
-inside the Perl filter as C<$hdr{'Newsgroups'}>.  In addition,
-C<$hdr{'__BODY__'}> will contain the full body of the article and
-C<$hdr{'__LINES__'}> will contain the number of lines in the body of the
-article.
-
-The contents of the %hdr hash for a typical article may therefore look
-something like this:
-
-    %hdr = (Subject      => 'MAKE MONEY FAST!!', 
-        From         => 'Joe Spamer <him@example.com>',
-        Date         => '10 Sep 1996 15:32:28 UTC',
-        Newsgroups   => 'alt.test',
-        Path         => 'news.example.com!not-for-mail',
-        Organization => 'Spammers Anonymous',
-        Lines        => '5',
-        Distribution => 'usa',
-        'Message-ID' => '<6.20232.842369548@example.com>',
-        __BODY__     => 'Send five dollars to the ISC, c/o ...',
-        __LINES__    => 5
-    );
-
-Note that the value of C<$hdr{Lines}> is the contents of the Lines: header
-of the article and may bear no resemblence to the actual length of the
-article.  C<$hdr{__LINES__}> is the line count calculated by INN, and is
-guaranteed to be accurate.
-
-The %hdr hash should not be modified inside filter_art().  Instead, if any
-of the contents need to be modified temporarily during filtering (smashing
-case, for example), copy them into a seperate variable first and perform
-the modifications on the copy.  Currently, C<$hdr{__BODY__}> is the only
-data that will cause your filter to die if you modify it, but in the
-future other keys may also contain live data.  Modifying live INN data in
-Perl will hopefully only cause a fatal exception in your Perl code that
-disables Perl filtering until you fix it, but it's possible for it to
-cause article munging or even core dumps in INN.  So always, always make a
-copy first.
-
-As mentioned above, if filter_art() returns the empty string (''), the
-article is accepted.  Note that this must be the empty string, not 0 or
-undef.  Otherwise, the article is rejected, and whatever scalar
-filter_art() returns (typically a string) will be taken as the reason why
-the article was rejected.  This reason will be returned to the remote peer
-as well as logged to the news logs.  (innreport, in its nightly report,
-will summarize the number of articles rejected by the Perl filter and
-include a count of how many articles were rejected with each reason
-string.)
-
-One other type of filtering is also supported.  If Perl filtering is
-turned on and the Perl function filter_messageid() is defined, that
-function will be called for each message ID received from a peer (via
-either CHECK or IHAVE).  The function receives a single argument, the
-message ID, and like filter_art() should return an empty string to accept
-the article or an error string to refuse the article.  This function is
-called before any history lookups and for every article offered to innd
-with CHECK or IHAVE (before the actual article is sent).  Accordingly, the
-message ID is the only information it has about the article (the %hdr hash
-will be empty).  This code would sit in a performance-critical hot path in
-a typical server, and therefore should be as fast as possible, but it can
-do things like refuse articles from certain hosts or cancels for already
-rejected articles (if they follow the $alz convention) without having to
-take the network bandwidth hit of accepting the entire article first.
-
-Note that you cannot rely on filter_messageid() being called for every
-incoming article; articles sent via TAKETHIS without an earlier CHECK will
-never pass through filter_messageid() and will only go through
-filter_art().
-
-Finally, whenever ctlinnd throttle, ctlinnd pause, or ctlinnd go is run,
-the Perl function filter_mode() is called if it exists.  It receives no
-arguments and returns no value, but it has access to a global hash %mode
-that contains three values:
-
-    Mode       The current server mode (throttled, paused, or running)
-    NewMode    The new mode the server is going to
-    reason     The reason that was given to ctlinnd
-
-One possible use for this function is to save filter state across a
-restart of innd.  There isn't any Perl function which is called when INN
-shuts down, but using filter_mode() the Perl filter can dump it's state to
-disk whenever INN is throttled.  Then, if the news administrator follows
-the strongly recommended shutdown procedure of throttling the server
-before shutting it down, the filter state will be safely saved to disk and
-can be reloaded when innd restarts (possibly by F<startup_innd.pl>).
-
-The state of the Perl interpretor in which all of these Perl functions run
-is preserved over the lifetime of innd.  In other words, it's permissible for
-the Perl code to create its own global Perl variables, data structures,
-saved state, and the like, and all of that will be available to
-filter_art() and filter_messageid() each time they're called.  The only
-variable INN fiddles with (or pays any attention to at all) is %hdr, which
-is cleared after each call to filter_art().
-
-Perl filtering can be turned off with C<ctlinnd perl n> and back on again
-with C<ctlinnd perl y>.  Perl filtering is turned off automatically if
-loading of the filter fails or if the filter code returns any sort of a
-fatal error (either due to Perl itself or due to a C<die> in the Perl code).
-
-=head1 Supported innd Callbacks
-
-innd makes seven functions available to any of its embedded Perl code.
-Those are:
-
-=over 4
-
-=item INN::addhist(I<messageid>, I<arrival>, I<articledate>, I<expire>, I<paths>)
-
-Adds I<messageid> to the history database.  All of the arguments except
-the first one are optional; the times default to the current time and the
-paths field defaults to the empty string.  (For those unfamiliar with the
-fields of a history(5) database entry, the I<arrival> is normally the time at
-which the server accepts the article, the I<articledate> is from the Date
-header of the article, the I<expire> is from the Expires header of the
-article, and the I<paths> field is the storage API token.  All three times
-as measured as a time_t since the epoch.)  Returns true on success, false
-otherwise.
-
-=item INN::article(I<messageid>)
-
-Returns the full article (as a simple string) identified by I<messageid>,
-or undef if it isn't found.  Each line will end with a simple \n, but
-leading periods may still be doubled if the article is stored in wire
-format.
-
-=item INN::cancel(I<messageid>)
-
-Cancels I<messageid>.  (This is equivalent to C<ctlinnd cancel>; it
-cancels the message on the local server, but doesn't post a cancel message
-or do anything else that affects anything other than the local server.)
-Returns true on success, false otherwise.
-
-=item INN::filesfor(I<messageid>)
-
-Returns the I<paths> field of the history entry for the given
-I<messageid>.  This will be the storage API token for the message.  If
-I<messageid> isn't found in the history database, returns undef.
-
-=item INN::havehist(I<messageid>)
-
-Looks up I<messageid> in the history database and returns true if it's
-found, false otherwise.
-
-=item INN::head(I<messageid>)
-
-Returns the header (as a simple string) of the article identified by
-I<messageid>, or undef if it isn't found.  Each line will end with a
-simple \n (in other words, regardless of the format of article storage,
-the returned string won't be in wire format).
-
-=item INN::newsgroup(I<newsgroup>)
-
-Returns the status of I<newsgroup> (the last field of the active file
-entry for that newsgroup).  See active(5) for a description of the
-possible values and their meanings (the most common are "y" for an
-unmoderated group and "m" for a moderated group).  If I<newsgroup> isn't
-in the active file, returns undef.
-
-=back
-
-These functions can only be used from inside the innd Perl filter; they're
-not available in the nnrpd filter.
-
-=head1 Common Callbacks
-
-The following additional function is available from inside filters
-embedded in innd, and is also available from filters embedded in nnrpd
-(see below):
-
-=over 4
-
-=item INN::syslog(level, message)
-
-Logs a message via syslog(2).  This is quite a bit more reliable and
-portable than trying to use Sys::Syslog from inside the Perl filter.  Only
-the first character of the level argument matters; the valid letters are
-the first letters of ALERT, CRIT, ERR, WARNING, NOTICE, INFO, and DEBUG
-(case-insensitive) and specify the priority at which the message is
-logged.  If a level that doesn't match any of those levels is given, the
-default priority level is LOG_NOTICE.  The second argument is the message
-to log; it will be prefixed by "filter: " and logged to syslog with
-facility LOG_NEWS.
-
-=back
-
-=head1 The nnrpd Posting Filter
-
-Whenever Perl support is needed in nnrpd, it first loads the file
-_PATH_PERL_FILTER_NNRPD (defined in F<include/paths.h>, by default
-F<filter_nnrpd.pl>).  This file must be located in the directory
-specified by pathfilter in F<inn.conf> (F</usr/local/news/bin/filter>
-by default).  The default directory for filter code can be specified
-at configure time by giving the flag B<--with-filter-dir> to
-configure.
-
-If F<filter_nnrpd.pl> loads successfully and defines the Perl function
-filter_post(), Perl filtering is turned on.  Otherwise, it's turned off.
-If filter_post() ever returns a fatal error (either from Perl or from a
-C<die> in the Perl code), Perl filtering is turned off for the life of that
-nnrpd process and any further posts made during that session won't go
-through the filter.
-
-While Perl filtering is on, every article received by nnrpd via the POST
-command is passed to the filter_post() Perl function before it is passed
-to INN (or mailed to the moderator of a moderated newsgroup).  If
-filter_post() returns an empty string (''), the article is accepted and
-normal processing of it continues.  Otherwise, the article is rejected and
-the string returned by filter_post() is returned to the client as the
-error message (with some exceptions; see below).
-
-filter_post() has access to a global hash %hdr, which contains all of the
-headers of the article.  (Unlike the innd Perl filter, %hdr for the nnrpd
-Perl filter contains *all* of the headers, not just the standard ones.  If
-any of the headers are duplicated, though, %hdr will contain only the
-value of the last occurance of the header.  nnrpd will reject the
-article before the filter runs if any of the standard headers are
-duplicated.)  It also has access to the full body of the article in the
-variable $body, and if the poster authenticated via AUTHINFO (or if either
-Perl authentication or a readers.conf authentication method is used and
-produces user information), it has access to the authenticated username of
-the poster in the variable $user.
-
-Unlike the innd Perl filter, the nnrpd Perl filter can modify the %hdr
-hash.  In fact, if the Perl variable $modify_headers is set to true after
-filter_post() returns, the contents of the %hdr hash will be written back
-to the article replacing the original headers.  filter_post() can
-therefore make any modifications it wishes to the headers and those
-modifications will be reflected in the article as it's finally posted.
-The article body cannot be modified in this way; any changes to $body will
-just be ignored.
-
-Be careful when using the ability to modify headers.  filter_post() runs
-after all the normal consistency checks on the headers and after server
-supplied headers (like Message-ID: and Date:) are filled in.  Deleting
-required headers or modifying headers that need to follow a strict format
-can result in nnrpd trying to post nonsense articles (which will probably
-then be rejected by innd).  If $modify_headers is set, I<everything> in
-the %hdr hash is taken to be article headers and added to the article.
-
-If filter_post() returns something other than the empty string, this
-message is normally returned to the client as an error.  There are two
-exceptions:  If the string returned begins with "DROP", the post will be
-silently discarded and success returned to the client.  If the string
-begins with "SPOOL", success is returned to the client, but the post is
-saved in a directory named "spam" under the directory specified by
-pathincoming in F<inn.conf> (in a directory named "spam/mod" if the post
-is to a moderated group).  This is intended to allow manual inspection of
-the suspect messages; if they should be posted, they can be manually moved
-out of the subdirectory to the directory specified by pathincoming in
-F<inn.conf>, where they can be posted by running C<rnews -U>.  If you use
-this functionality, make sure those directories exist.
-
-=head1 Changes to Perl Authentication Support for nnrpd
-
-The old authentication functionality has been combined with the new
-readers.conf mechanism by Erik Klavon <erik@eriq.org>; bug reports
-should however go to inn-bugs@isc.org, not Erik.
-
-The remainder of this section is an introduction to the new mechanism
-(which uses the perl_auth: and perl_access: F<readers.conf> parameters)
-with porting/migration suggestions for people familiar with the old
-mechanism (identifiable by the nnrpperlauth: parameter in F<inn.conf>).
-
-Other people should skip this section.
-
-The perl_auth parameter allows the use of Perl to authenticate a user.
-Scripts (like those from the old mechanism) are listed in F<readers.conf>
-using perl_auth in the same manner other authenticators are using auth:
-
-    perl_auth: "/path/to/script/auth1.pl"
-
-The file given as argument to perl_auth should contain the same
-procedures as before. The global hash %attributes remains the same,
-except for the removal of the "type" entry which is no longer needed
-in this modification and the addition of several new entries (port,
-intipaddr, intport) described below. The return array now only
-contains either two or three elements, the first of which is the NNTP
-return code. The second is an error string which is passed to the
-client if the error code indicates that the authentication attempt has
-failed. This allows a specific error message to be generated by the
-perl script in place of "Authentication failed". An optional third
-return element if present will be used to match the connection with
-the users: parameter in access groups and will also be the username
-logged. If this element is absent, the username supplied by the client
-during authentication will be used as was the previous behavior.
-
-The perl_access parameter (described below) is also new; it allows the
-dynamic generation of an access group for an incoming connection using
-a Perl script.  If a connection matches an auth group which has a
-perl_access parameter, all access groups in readers.conf are ignored;
-instead the procedure described below is used to generate an access group.
-This concept is due to Jeffrey M. Vinocur.
-
-The new functionality should provide all of the existing capabilities
-of the Perl hook, in combination with the flexibility of readers.conf
-and the use of other authentication and resolving programs.  To use
-Perl authentication code that predates the readers.conf mechanism, you
-would need to modify the code slightly (see below for the new
-specification) and supply a simple readers.conf file.  If you don't want
-to modify your code, the samples directory has F<nnrpd_auth_wrapper.pl>
-and F<nnrpd_access_wrapper.pl> which should allow you to use your old
-code without needing to change it.
-
-However, before trying to use your old Perl code, you may want to
-consider replacing it entirely with non-Perl authentication.  (With
-readers.conf and the regular authenticator and resolver programs, much
-of what once required Perl can be done directly.)  Even if the
-functionality is not available directly, you may wish to write a new
-authenticator or resolver (which can be done in whatever language you
-prefer to work in).
-
-
-=head1 Perl Authentication Support for nnrpd
-
-Support for authentication via Perl is provided in nnrpd by the
-inclusion of a perl_auth: parameter in a F<readers.conf> auth
-group. perl_auth: works exactly like the auth: parameter in
-F<readers.conf>, except that it calls the script given as argument using
-the Perl hook rather then treating it as an external program.
-
-If the processing of readers.conf requires that a perl_auth: statement
-be used for authentication, Perl is loaded (if it has yet to be) and
-the file given as argument to the perl_auth: parameter is loaded as
-well. If a Perl function auth_init() is defined by that file, it is called
-immediately after the file is loaded.  It takes no arguments and returns
-nothing.
-
-Provided the file loads without errors, auth_init() (if present) runs
-without fatal errors, and a Perl function authenticate() is defined,
-authenticate() will then be called. authenticate() takes no arguments,
-but it has access to a global hash %attributes which contains
-information about the connection as follows: C<$attributes{hostname}>
-will contain the hostname (or the IP address if it doesn't resolve) of
-the client machine, C<$attributes{ipaddress}> will contain its IP
-address (as a string), C<$attributes{port}> will contain the client
-port (as an integer), C<$attributes{interface}> contains the hostname
-of the interface the client connected on, C<$attributes{intipaddr}>
-contains the IP address (as a string) of the interface the client
-connected on, C<$attributes{intport}> contains the port (as an
-integer) on the interface the client connected on,
-C<$attributes{username}> will contain the provided username and
-C<$attributes{password}> the password.
-
-authenticate() should return a two or three element array.  The first
-element is the NNTP response code to return to the client, the second
-element is an error string which is passed to the client if the
-response code indicates that the authentication attempt has failed. An
-optional third return element if present will be used to match the
-connection with the users: parameter in access groups and will also be
-the username logged. If this element is absent, the username supplied
-by the client during authentication will be used for matching and
-logging.
-
-The NNTP response code should probably be either 281 (authentication
-successful) or 502 (authentication unsuccessful).  If the code
-returned is anything other than 281, nnrpd will print an
-authentication error message and drop the connection and exit.
-
-If authenticate() dies (either due to a Perl error or due to calling die),
-or if it returns anything other than the two or three element array
-described above, an internal error will be reported to the client, the
-exact error will be logged to syslog, and nnrpd will drop the
-connection and exit.
-
-
-=head1 Dynamic Generation of Access Groups
-
-A Perl script may be used to dynamically generate an access group
-which is then used to determine the access rights of the client. This
-occurs whenever the perl_access: is specified in an auth group which
-has successfully matched the client. Only one perl_access:
-statement is allowed in an auth group. This parameter should not be
-mixed with a python_access: statement in the same auth group.
-
-When a perl_access: parameter is encountered, Perl is loaded (if it
-has yet to be) and the file given as argument is loaded as
-well. Provided the file loads without errors, and a Perl function
-access() is defined, access() will then be called. access() takes no
-arguments, but it has access to a global hash %attributes which
-contains information about the connection as follows:
-C<$attributes{hostname}> will contain the hostname (or the IP address
-if it doesn't resolve) of the client machine,
-C<$attributes{ipaddress}> will contain its IP address (as a string),
-C<$attributes{port}> will contain the client port (as an integer),
-C<$attributes{interface}> contains the hostname of the interface the
-client connected on, C<$attributes{intipaddr}> contains the IP address
-(as a string) of the interface the client connected on,
-C<$attributes{intport}> contains the port (as an integer) on the
-interface the client connected on, C<$attributes{username}> will
-contain the provided username and domain (in username@domain form).
-
-access() returns a hash, containing the desired access parameters and
-values.  Here is an untested example showing how to dynamically generate a
-list of newsgroups based on the client's username and domain.
-
-     my %hosts = ( "example.com" => "example.*", "isc.org" => "isc.*" );
-
-     sub access {
-        %return_hash = (
-           "max_rate" => "10000",
-          "addnntppostinghost" => "true",
-     #     ...
-        );
-        if( defined $attributes{username} &&
-            $attributes{username} =~ /.*@(.*)/ )
-        {
-           $return_hash{"virtualhost"} = "true";
-          $return_hash{"path"} = $1;
-           $return_hash{"newsgroups"} = $hosts{$1};
-        } else {
-           $return_hash{"read"} = "*";
-          $return_hash{"post"} = "local.*"
-        }
-        return %return_hash;
-     }
-
-Note that both the keys and values are quoted strings. These values
-are to be returned to a C program and must be quoted strings. For
-values containing one or more spaces, it is not necessary to include
-extra quotes inside the string.
-
-While you may include the users: parameter in a dynamically generated
-access group, some care should be taken (unless your pattern is just
-* which is equivalent to leaving the parameter out). The group created
-with the values returned from the Perl script is the only one
-considered when nnrpd attempts to find an access group matching the
-connection. If a users: parameter is included and it doesn't match the
-connection, then the client will be denied access since there are no
-other access groups which could match the connection.
-
-If access() dies (either due to a Perl error or due to calling die),
-or if it returns anything other than a hash as described
-above, an internal error will be reported to the client, the exact error
-will be logged to syslog, and nnrpd will drop the connection and exit.
-
-=head1 Notes on Writing Embedded Perl
-
-All Perl evaluation is done inside an implicit eval block, so calling die
-in Perl code will not kill the innd or nnrpd process.  Neither will Perl
-errors (such as syntax errors).  However, such errors will have negative
-effects (fatal errors in the innd or nnrpd filter will cause filtering to
-be disabled, and fatal errors in the nnrpd authentication code will cause
-the client connection to be terminated).
-
-Calling exit directly, however, *will* kill the innd or nnrpd process, so
-don't do that.  Similarly, you probably don't want to call fork (or any
-other function that results in a fork such as system, IPC::Open3::open3(),
-or any use of backticks) since there are possibly unflushed buffers that
-could get flushed twice, lots of open state that may not get closed
-properly, and innumerable other potential problems.  In general, be aware
-that all Perl code is running inside a large and complicated C program,
-and Perl code that impacts the process as a whole is best avoided.
-
-You can use print and warn inside Perl code to send output to STDOUT or
-STDERR, but you probably shouldn't.  Instead, open a log file and print to
-it instead (or, in the innd filter, use INN::syslog() to write messages
-via syslog like the rest of INN).  If you write to STDOUT or STDERR, where
-that data will go depends on where the filter is running; inside innd, it
-will go to the news log or the errlog, and inside nnrpd it will probably
-go nowhere but could go to the client.  The nnrpd filter takes some steps
-to try to keep output from going across the network connection to the
-client (which would probably result in a very confused client), but best
-not to take the chance.
-
-For similar reasons, try to make your Perl code -w clean, since Perl
-warnings are written to STDERR.  (INN won't run your code under -w, but
-better safe than sorry, and some versions of Perl have some mandatory
-warnings you can't turn off.)
-
-You *can* use modules in your Perl code, just like you would in an
-ordinary Perl script.  You can even use modules that dynamically load C
-code.  Just make sure that none of the modules you use go off behind your
-back to do any of the things above that are best avoided.
-
-Whenever you make any modifications to the Perl code, and particularly
-before starting INN or reloading filter.perl with new code, you should run
-perl -wc on the file.  This will at least make sure you don't have any
-glaring syntax errors.  Remember, if there are errors in your code,
-filtering will be disabled, which could mean that posts you really wanted
-to reject will leak through and authentication of readers may be totally
-broken.
-
-The samples directory has example F<startup_innd.pl>, F<filter_innd.pl>,
-F<filter_nnrpd.pl>, and F<nnrpd_auth.pl> files that contain some
-simplistic examples.  Look them over as a starting point when writing your
-own.
-
-=head1 Available Packages
-
-This is an unofficial list of known filtering packages at the time of
-publication.  This is not an endorsement of these filters by the ISC or
-the INN developers, but is included as assistance in locating packages
-which make use of this filter mechanism.
-
-  CleanFeed               Jeremy Nixon <jeremy@exit109.com>
-  <URL:http://www.exit109.com/~jeremy/news/cleanfeed.html>
-        A spam filter catching excessive multi-posting and a host of
-        other things.  Uses filter_innd.pl exclusively, requires the MD5
-        Perl module.  Probably the most popular and widely-used Perl
-        filter around.
-
-  Usenet II Filter        Edward S. Marshall <emarshal@xnet.com>
-  <URL:http://www.xnet.com/~emarshal/inn/filter_nnrpd.pl>
-        Checks for "soundness" according to Usenet II guidelines in the
-        net.* hierarchy.  Designed to use filter_nnrpd.pl.
-
-  News Gizmo              Aidan Cully <aidan@panix.com>
-  <URL:http://www.panix.com/gizmo/>
-        A posting filter for helping a site enforce Usenet-II soundness,
-        and for quotaing the number of messages any user can post to
-        Usenet daily.