You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@spamassassin.apache.org by km...@apache.org on 2015/04/29 19:04:11 UTC

svn commit: r1676792 [8/17] - in /spamassassin/site/full/3.4.x: ./ doc/

Added: spamassassin/site/full/3.4.x/doc/Mail_SpamAssassin_Plugin.html
URL: http://svn.apache.org/viewvc/spamassassin/site/full/3.4.x/doc/Mail_SpamAssassin_Plugin.html?rev=1676792&view=auto
==============================================================================
--- spamassassin/site/full/3.4.x/doc/Mail_SpamAssassin_Plugin.html (added)
+++ spamassassin/site/full/3.4.x/doc/Mail_SpamAssassin_Plugin.html Wed Apr 29 17:04:09 2015
@@ -0,0 +1,1170 @@
+<?xml version="1.0" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Mail::SpamAssassin::Plugin - SpamAssassin plugin base class</title>
+<meta http-equiv="content-type" content="text/html; charset=utf-8" />
+<link rev="made" href="mailto:root@localhost" />
+</head>
+
+<body style="background-color: white">
+
+
+<!-- INDEX BEGIN -->
+<div name="index">
+<p><a name="__index__"></a></p>
+
+<ul>
+
+	<li><a href="#name">NAME</a></li>
+	<li><a href="#synopsis">SYNOPSIS</a></li>
+	<ul>
+
+		<li><a href="#spamassassin_configuration_">SpamAssassin configuration:</a></li>
+		<li><a href="#perl_code_">Perl code:</a></li>
+	</ul>
+
+	<li><a href="#description">DESCRIPTION</a></li>
+	<li><a href="#interface">INTERFACE</a></li>
+	<li><a href="#methods">METHODS</a></li>
+	<li><a href="#helper_apis">HELPER APIS</a></li>
+	<li><a href="#logging">LOGGING</a></li>
+	<li><a href="#registering_eval_rules">REGISTERING EVAL RULES</a></li>
+	<li><a href="#standard_arguments_for_rule_types">STANDARD ARGUMENTS FOR RULE TYPES</a></li>
+	<li><a href="#backward_compatibility">BACKWARD COMPATIBILITY</a></li>
+	<li><a href="#see_also">SEE ALSO</a></li>
+</ul>
+
+<hr name="index" />
+</div>
+<!-- INDEX END -->
+
+<p>
+</p>
+<h1><a name="name">NAME</a></h1>
+<p>Mail::SpamAssassin::Plugin - SpamAssassin plugin base class</p>
+<p>
+</p>
+<hr />
+<h1><a name="synopsis">SYNOPSIS</a></h1>
+<p>
+</p>
+<h2><a name="spamassassin_configuration_">SpamAssassin configuration:</a></h2>
+<pre>
+  loadplugin MyPlugin /path/to/myplugin.pm</pre>
+<p>
+</p>
+<h2><a name="perl_code_">Perl code:</a></h2>
+<pre>
+  package MyPlugin;</pre>
+<pre>
+  use Mail::SpamAssassin::Plugin;
+  our @ISA = qw(Mail::SpamAssassin::Plugin);</pre>
+<pre>
+  sub new {
+    my ($class, $mailsa) = @_;
+    
+    # the usual perlobj boilerplate to create a subclass object
+    $class = ref($class) || $class;
+    my $self = $class-&gt;SUPER::new($mailsa);
+    bless ($self, $class);
+   
+    # then register an eval rule, if desired...
+    $self-&gt;register_eval_rule (&quot;check_for_foo&quot;);</pre>
+<pre>
+    # and return the new plugin object
+    return $self;
+  }</pre>
+<pre>
+  ...methods...</pre>
+<pre>
+  1;</pre>
+<p>
+</p>
+<hr />
+<h1><a name="description">DESCRIPTION</a></h1>
+<p>This is the base class for SpamAssassin plugins; all plugins must be objects
+that implement this class.</p>
+<p>This class provides no-op stub methods for all the callbacks that a plugin
+can receive.  It is expected that your plugin will override one or more
+of these stubs to perform its actions.</p>
+<p>SpamAssassin implements a plugin chain; each callback event is passed to each
+of the registered plugin objects in turn.  Any plugin can call
+<a href="#inhibit_further_callbacks"><code>$self-&gt;inhibit_further_callbacks()</code></a> to block delivery of that event to
+later plugins in the chain.  This is useful if the plugin has handled the
+event, and there will be no need for later plugins to handle it as well.</p>
+<p>If you're looking to write a simple eval rule, skip straight to 
+<a href="#register_eval_rule"><code>register_eval_rule()</code></a>, below.</p>
+<p>
+</p>
+<hr />
+<h1><a name="interface">INTERFACE</a></h1>
+<p>In all the plugin APIs below, <code>options</code> refers to a reference to a hash
+containing name-value pairs.   This is used to ensure future-compatibility, in
+that we can add new options in future without affecting objects built to an
+earlier version of the API.</p>
+<p>For example, here would be how to print out the <a href="#line"><code>line</code></a> item in a
+<a href="#parse_config"><code>parse_config()</code></a> method:</p>
+<pre>
+  sub parse_config {
+    my ($self, $opts) = @_;
+    print &quot;MyPlugin: parse_config got &quot;.$opts-&gt;{line}.&quot;\n&quot;;
+  }</pre>
+<p>
+</p>
+<hr />
+<h1><a name="methods">METHODS</a></h1>
+<p>The following methods can be overridden by subclasses to handle events.</p>
+<dl>
+<dt><strong><a name="new" class="item">$plugin = MyPluginClass-&gt;new ($mailsaobject)</a></strong></dt>
+
+<dd>
+<p>Constructor.  Plugins that need to register themselves will need to
+define their own; the default super-class constructor will work fine
+for plugins that just override a method.</p>
+<p>Note that subclasses must provide the <code>$mailsaobject</code> to the
+superclass constructor, like so:</p>
+<pre>
+  my $self = $class-&gt;SUPER::new($mailsaobject);</pre>
+<p>Lifecycle note: plugins that will need to store per-scan state should not store
+that on the Plugin object; instead this should be stored on the PerMsgStatus
+object, see <a href="#check_start"><code>check_start()</code></a> below.  It is also likewise recommended that
+configuration settings be stored on the Conf object; see <a href="#parse_config"><code>parse_config()</code></a>.</p>
+</dd>
+<dt><strong><a name="parse_config" class="item">$plugin-&gt;parse_config ( { options ... } )</a></strong></dt>
+
+<dd>
+<p>Parse a configuration line that hasn't already been handled.  <code>options</code>
+is a reference to a hash containing these options:</p>
+<dl>
+<dt><strong><a name="line" class="item">line</a></strong></dt>
+
+<dd>
+<p>The line of configuration text to parse.   This has leading and trailing
+whitespace, and comments, removed.</p>
+</dd>
+<dt><strong><a name="key" class="item">key</a></strong></dt>
+
+<dd>
+<p>The configuration key; ie. the first &quot;word&quot; on the line.</p>
+</dd>
+<dt><strong><a name="value" class="item">value</a></strong></dt>
+
+<dd>
+<p>The configuration value; everything after the first &quot;word&quot; and
+any whitespace after that.</p>
+</dd>
+<dt><strong><a name="conf" class="item">conf</a></strong></dt>
+
+<dd>
+<p>The <code>Mail::SpamAssassin::Conf</code> object on which the configuration
+data should be stored.</p>
+</dd>
+<dt><strong><a name="user_config" class="item">user_config</a></strong></dt>
+
+<dd>
+<p>A boolean: <code>1</code> if reading a user's configuration, <code>0</code> if reading the
+system-wide configuration files.</p>
+</dd>
+</dl>
+<p>If the configuration line was a setting that is handled by this plugin, the
+method implementation should call <a href="#inhibit_further_callbacks"><code>$self-&gt;inhibit_further_callbacks()</code></a>.</p>
+<p>If the setting is not handled by this plugin, the method should return <code>0</code> so
+that a later plugin may handle it, or so that SpamAssassin can output a warning
+message to the user if no plugin understands it.</p>
+<p>Lifecycle note: it is suggested that configuration be stored on the
+<code>Mail::SpamAssassin::Conf</code> object in use, instead of the plugin object itself.
+That can be found as <code>$plugin-&gt;{main}-&gt;{conf}</code>, or as &quot;conf&quot; in the
+<code>$options</code> hash reference above.   By storing it on <a href="#conf"><code>conf</code></a>, this allows
+per-user and system-wide configuration precedence to be dealt with correctly.</p>
+</dd>
+<dt><strong><a name="finish_parsing_start" class="item">$plugin-&gt;finish_parsing_start ( { options ... } )</a></strong></dt>
+
+<dd>
+<p>Signals that the system-wide configuration has been completely read,
+but internal data structures are not yet created. It is possible to
+use this hook to dynamically change the configuration already read in
+or add new config options.</p>
+<p><code>options</code> is a reference to a hash containing these options:</p>
+<dl>
+<dt><strong>conf</strong></dt>
+
+<dd>
+<p>The <code>Mail::SpamAssassin::Conf</code> object on which the configuration
+data should be stored.</p>
+</dd>
+</dl>
+<p>Note: there are no guarantees that the internal data structures of
+SpamAssassin will not change from release to release.  In particular to
+this plugin hook, if you modify the rules data structures in a
+third-party plugin, all bets are off until such time that an API is
+present for modifying that configuration data.</p>
+</dd>
+<dt><strong><a name="finish_parsing_end" class="item">$plugin-&gt;finish_parsing_end ( { options ... } )</a></strong></dt>
+
+<dd>
+<p>Signals that the system-wide configuration parsing has just finished, and
+SpamAssassin is nearly ready to check messages.</p>
+<p><code>options</code> is a reference to a hash containing these options:</p>
+<dl>
+<dt><strong>conf</strong></dt>
+
+<dd>
+<p>The <code>Mail::SpamAssassin::Conf</code> object on which the configuration
+data should be stored.</p>
+</dd>
+</dl>
+<p>Note: there are no guarantees that the internal data structures of
+SpamAssassin will not change from release to release.  In particular to
+this plugin hook, if you modify the rules data structures in a
+third-party plugin, all bets are off until such time that an API is
+present for modifying that configuration data.</p>
+</dd>
+<dt><strong><a name="user_conf_parsing_start" class="item">$plugin-&gt;user_conf_parsing_start ( { options ... } )</a></strong></dt>
+
+<dd>
+<p>Signals that the per-user configuration has been completely read, but
+not converted to internal data structures. It is possible to use this
+hook to dynamically change the configuration already read in or add
+new config options.</p>
+<p>If <code>allow_user_rules</code> is enabled in the configuration, it is possible
+that additional rules have been added since the <a href="#finish_parsing_start"><code>finish_parsing_start</code></a>
+plugin hook invocation was called.</p>
+<dl>
+<dt><strong>conf</strong></dt>
+
+<dd>
+<p>The <code>Mail::SpamAssassin::Conf</code> object on which the configuration
+data should be stored.</p>
+</dd>
+</dl>
+<p>Note: there are no guarantees that the internal data structures of
+SpamAssassin will not change from release to release.  In particular to
+this plugin hook, if you modify the rules data structures in a
+third-party plugin, all bets are off until such time that an API is
+present for modifying that configuration data.</p>
+</dd>
+<dt><strong><a name="user_conf_parsing_end" class="item">$plugin-&gt;user_conf_parsing_end ( { options ... } )</a></strong></dt>
+
+<dd>
+<p>Signals that the per-user configuration parsing has just finished, and
+SpamAssassin is nearly ready to check messages.   If <code>allow_user_rules</code> is
+enabled in the configuration, it is possible that additional rules have been
+added since the <a href="#finish_parsing_end"><code>finish_parsing_end</code></a> plugin hook invocation was called.</p>
+<p><code>options</code> is a reference to a hash containing these options:</p>
+<dl>
+<dt><strong>conf</strong></dt>
+
+<dd>
+<p>The <code>Mail::SpamAssassin::Conf</code> object on which the configuration
+data should be stored.</p>
+</dd>
+</dl>
+<p>Note: there are no guarantees that the internal data structures of
+SpamAssassin will not change from release to release.  In particular to
+this plugin hook, if you modify the rules data structures in a
+third-party plugin, all bets are off until such time that an API is
+present for modifying that configuration data.</p>
+</dd>
+<dt><strong><a name="signal_user_changed" class="item">$plugin-&gt;signal_user_changed ( { options ... } )</a></strong></dt>
+
+<dd>
+<p>Signals that the current user has changed for a new one.</p>
+<dl>
+<dt><strong><a name="username" class="item">username</a></strong></dt>
+
+<dd>
+<p>The new user's username.</p>
+</dd>
+<dt><strong><a name="user_dir" class="item">user_dir</a></strong></dt>
+
+<dd>
+<p>The new user's home directory. (equivalent to <code>~</code>.)</p>
+</dd>
+<dt><strong><a name="userstate_dir" class="item">userstate_dir</a></strong></dt>
+
+<dd>
+<p>The new user's storage directory. (equivalent to <code>~/.spamassassin</code>.)</p>
+</dd>
+</dl>
+</dd>
+<dt><strong><a name="services_authorized_for_username" class="item">$plugin-&gt;services_authorized_for_username ( { options ... } )</a></strong></dt>
+
+<dd>
+<p>Validates that a given username is authorized to use certain services.</p>
+<p>In order to authorize a user, the plugin should first check that it can
+handle any of the services passed into the method and then set the value
+for each allowed service to true (or any non-negative value).</p>
+<p>The current supported services are: bayessql</p>
+<dl>
+<dt><strong>username</strong></dt>
+
+<dd>
+<p>A username</p>
+</dd>
+<dt><strong><a name="services" class="item">services</a></strong></dt>
+
+<dd>
+<p>Reference to a hash containing the services you want to check.</p>
+<p>{</p>
+<pre>
+  'bayessql' =&gt; 0</pre>
+<p>}</p>
+</dd>
+<dt><strong>conf</strong></dt>
+
+<dd>
+<p>The <code>Mail::SpamAssassin::Conf</code> object on which the configuration
+data should be stored.</p>
+</dd>
+</dl>
+</dd>
+<dt><strong><a name="compile_now_start" class="item">$plugin-&gt;compile_now_start ( { options ... } )</a></strong></dt>
+
+<dd>
+<p>This is called at the beginning of Mail::SpamAssassin::compile_now() so
+plugins can do any necessary initialization for multi-process
+SpamAssassin (such as spamd or mass-check -j).</p>
+<dl>
+<dt><strong><a name="use_user_prefs" class="item">use_user_prefs</a></strong></dt>
+
+<dd>
+<p>The value of $use_user_prefs option in <code>compile_now()</code>.</p>
+</dd>
+<dt><strong><a name="keep_userstate" class="item">keep_userstate</a></strong></dt>
+
+<dd>
+<p>The value of $keep_userstate option in <code>compile_now()</code>.</p>
+</dd>
+</dl>
+</dd>
+<dt><strong><a name="compile_now_finish" class="item">$plugin-&gt;compile_now_finish ( { options ... } )</a></strong></dt>
+
+<dd>
+<p>This is called at the end of Mail::SpamAssassin::compile_now() so
+plugins can do any necessary initialization for multi-process
+SpamAssassin (such as spamd or mass-check -j).</p>
+<dl>
+<dt><strong>use_user_prefs</strong></dt>
+
+<dd>
+<p>The value of $use_user_prefs option in <code>compile_now()</code>.</p>
+</dd>
+<dt><strong>keep_userstate</strong></dt>
+
+<dd>
+<p>The value of $keep_userstate option in <code>compile_now()</code>.</p>
+</dd>
+</dl>
+</dd>
+<dt><strong><a name="check_start" class="item">$plugin-&gt;check_start ( { options ... } )</a></strong></dt>
+
+<dd>
+<p>Signals that a message check operation is starting.</p>
+<dl>
+<dt><strong><a name="permsgstatus" class="item">permsgstatus</a></strong></dt>
+
+<dd>
+<p>The <code>Mail::SpamAssassin::PerMsgStatus</code> context object for this scan.</p>
+<p>Lifecycle note: it is recommended that rules that need to track test state on a
+per-scan basis should store that state on this object, not on the plugin object
+itself, since the plugin object will be shared between all active scanners.</p>
+<p>The message being scanned is accessible through the
+<code>$permsgstatus-&gt;get_message()</code> API; there are a number of other public
+APIs on that object, too.  See <code>Mail::SpamAssassin::PerMsgStatus</code> perldoc.</p>
+</dd>
+</dl>
+</dd>
+<dt><strong><a name="check_main" class="item">$plugin-&gt;check_main ( { options ... } )</a></strong></dt>
+
+<dd>
+<p>Signals that a message should be checked.  Note that implementations of
+this hook should return <code>1</code>.</p>
+<dl>
+<dt><strong>permsgstatus</strong></dt>
+
+<dd>
+<p>The <code>Mail::SpamAssassin::PerMsgStatus</code> context object for this scan.</p>
+</dd>
+</dl>
+</dd>
+<dt><strong><a name="check_tick" class="item">$plugin-&gt;check_tick ( { options ... } )</a></strong></dt>
+
+<dd>
+<p>Called periodically during a message check operation.  A callback set for
+this method is a good place to run through an event loop dealing with
+network events triggered in a <code>parse_metadata</code> method, for example.</p>
+<dl>
+<dt><strong>permsgstatus</strong></dt>
+
+<dd>
+<p>The <code>Mail::SpamAssassin::PerMsgStatus</code> context object for this scan.</p>
+</dd>
+</dl>
+</dd>
+<dt><strong><a name="check_post_dnsbl" class="item">$plugin-&gt;check_post_dnsbl ( { options ... } )</a></strong></dt>
+
+<dd>
+<p>Called after the DNSBL results have been harvested.  This is a good
+place to harvest your own asynchronously-started network lookups.</p>
+<dl>
+<dt><strong>permsgstatus</strong></dt>
+
+<dd>
+<p>The <code>Mail::SpamAssassin::PerMsgStatus</code> context object for this scan.</p>
+</dd>
+</dl>
+</dd>
+<dt><strong><a name="check_post_learn" class="item">$plugin-&gt;check_post_learn ( { options ... } )</a></strong></dt>
+
+<dd>
+<p>Called after auto-learning may (or may not) have taken place.  If you
+wish to perform additional learning, whether or not auto-learning
+happens, this is the place to do it.</p>
+<dl>
+<dt><strong>permsgstatus</strong></dt>
+
+<dd>
+<p>The <code>Mail::SpamAssassin::PerMsgStatus</code> context object for this scan.</p>
+</dd>
+</dl>
+</dd>
+<dt><strong><a name="check_end" class="item">$plugin-&gt;check_end ( { options ... } )</a></strong></dt>
+
+<dd>
+<p>Signals that a message check operation has just finished, and the
+results are about to be returned to the caller.</p>
+<dl>
+<dt><strong>permsgstatus</strong></dt>
+
+<dd>
+<p>The <code>Mail::SpamAssassin::PerMsgStatus</code> context object for this scan.
+The current score, names of rules that hit, etc. can be retrieved
+using the public APIs on this object.</p>
+</dd>
+</dl>
+</dd>
+<dt><strong><a name="finish_tests" class="item">$plugin-&gt;finish_tests ( { options ... } )</a></strong></dt>
+
+<dd>
+<p>Called via <code>Mail::SpamAssassin::finish</code>.  This should clear up any tests that
+a plugin has added to the namespace.</p>
+<p>In certain circumstances, plugins may find it useful to compile
+perl functions from the ruleset, on the fly.  It is important to
+remove these once the <code>Mail::SpamAssassin</code> object is deleted,
+however, and this API allows this.</p>
+<p>Each plugin is responsible for its own generated perl functions.</p>
+<dl>
+<dt><strong>conf</strong></dt>
+
+<dd>
+<p>The <code>Mail::SpamAssassin::Conf</code> object on which the configuration
+data should be stored.</p>
+</dd>
+</dl>
+<p>See also the <a href="#register_generated_rule_method"><code>register_generated_rule_method</code></a> helper API, below.</p>
+</dd>
+<dt><strong><a name="extract_metadata" class="item">$plugin-&gt;extract_metadata ( { options ... } )</a></strong></dt>
+
+<dd>
+<p>Signals that a message is being mined for metadata.  Some plugins may wish
+to add their own metadata as well.</p>
+<dl>
+<dt><strong><a name="msg" class="item">msg</a></strong></dt>
+
+<dd>
+<p>The <code>Mail::SpamAssassin::Message</code> object for this message.</p>
+</dd>
+<dt><strong>permsgstatus</strong></dt>
+
+<dd>
+<p>The <code>Mail::SpamAssassin::PerMsgStatus</code> context object for this scan.</p>
+</dd>
+</dl>
+</dd>
+<dt><strong><a name="parsed_metadata" class="item">$plugin-&gt;parsed_metadata ( { options ... } )</a></strong></dt>
+
+<dd>
+<p>Signals that a message's metadata has been parsed, and can now be
+accessed by the plugin.</p>
+<dl>
+<dt><strong>permsgstatus</strong></dt>
+
+<dd>
+<p>The <code>Mail::SpamAssassin::PerMsgStatus</code> context object for this scan.</p>
+</dd>
+</dl>
+</dd>
+<dt><strong><a name="start_rules" class="item">$plugin-&gt;start_rules ( { options ... } )</a></strong></dt>
+
+<dd>
+<p>Called before testing a set of rules of a given type and priority.</p>
+<dl>
+<dt><strong>permsgstatus</strong></dt>
+
+<dd>
+<p>The <code>Mail::SpamAssassin::PerMsgStatus</code> context object for this scan.</p>
+</dd>
+<dt><strong><a name="ruletype" class="item">ruletype</a></strong></dt>
+
+<dd>
+<p>The type of the rules about to be performed.</p>
+</dd>
+<dt><strong><a name="priority" class="item">priority</a></strong></dt>
+
+<dd>
+<p>The priority level of the rules about to be performed.</p>
+</dd>
+</dl>
+</dd>
+<dt><strong><a name="hit_rule" class="item">$plugin-&gt;hit_rule ( { options ... } )</a></strong></dt>
+
+<dd>
+<p>Called when a rule fires.</p>
+<dl>
+<dt><strong>permsgstatus</strong></dt>
+
+<dd>
+<p>The <code>Mail::SpamAssassin::PerMsgStatus</code> context object for this scan.</p>
+</dd>
+<dt><strong>ruletype</strong></dt>
+
+<dd>
+<p>The type of the rule that fired.</p>
+</dd>
+<dt><strong><a name="rulename" class="item">rulename</a></strong></dt>
+
+<dd>
+<p>The name of the rule that fired.</p>
+</dd>
+<dt><strong><a name="score" class="item">score</a></strong></dt>
+
+<dd>
+<p>The rule's score in the active scoreset.</p>
+</dd>
+</dl>
+</dd>
+<dt><strong><a name="ran_rule" class="item">$plugin-&gt;ran_rule ( { options ... } )</a></strong></dt>
+
+<dd>
+<p>Called after a rule has been tested, whether or not it fired.  When the
+rule fires, the hit_rule callback is always called before this.</p>
+<dl>
+<dt><strong>permsgstatus</strong></dt>
+
+<dd>
+<p>The <code>Mail::SpamAssassin::PerMsgStatus</code> context object for this scan.</p>
+</dd>
+<dt><strong>ruletype</strong></dt>
+
+<dd>
+<p>The type of the rule that was tested.</p>
+</dd>
+<dt><strong>rulename</strong></dt>
+
+<dd>
+<p>The name of the rule that was tested.</p>
+</dd>
+</dl>
+</dd>
+<dt><strong><a name="autolearn_discriminator" class="item">$plugin-&gt;autolearn_discriminator ( { options ... } )</a></strong></dt>
+
+<dd>
+<p>Control whether a just-scanned message should be learned as either
+spam or ham.   This method should return one of <code>1</code> to learn
+the message as spam, <code>0</code> to learn as ham, or <code>undef</code> to not
+learn from the message at all.</p>
+<dl>
+<dt><strong>permsgstatus</strong></dt>
+
+<dd>
+<p>The <code>Mail::SpamAssassin::PerMsgStatus</code> context object for this scan.</p>
+</dd>
+</dl>
+</dd>
+<dt><strong><a name="autolearn" class="item">$plugin-&gt;autolearn ( { options ... } )</a></strong></dt>
+
+<dd>
+<p>Signals that a message is about to be auto-learned as either ham or spam.</p>
+<dl>
+<dt><strong>permsgstatus</strong></dt>
+
+<dd>
+<p>The <code>Mail::SpamAssassin::PerMsgStatus</code> context object for this scan.</p>
+</dd>
+<dt><strong><a name="isspam" class="item">isspam</a></strong></dt>
+
+<dd>
+<p><code>1</code> if the message is spam, <code>0</code> if ham.</p>
+</dd>
+</dl>
+</dd>
+<dt><strong><a name="per_msg_finish" class="item">$plugin-&gt;per_msg_finish ( { options ... } )</a></strong></dt>
+
+<dd>
+<p>Signals that a <code>Mail::SpamAssassin::PerMsgStatus</code> object is being
+destroyed, and any per-scan context held on that object by this
+plugin should be destroyed as well.</p>
+<p>Normally, any member variables on the <code>PerMsgStatus</code> object will be cleaned up
+automatically -- but if your plugin has made a circular reference on that
+object, this is the place to break them so that garbage collection can operate
+correctly.</p>
+<dl>
+<dt><strong>permsgstatus</strong></dt>
+
+<dd>
+<p>The <code>Mail::SpamAssassin::PerMsgStatus</code> context object for this scan.</p>
+</dd>
+</dl>
+</dd>
+<dt><strong><a name="have_shortcircuited" class="item">$plugin-&gt;have_shortcircuited ( { options ... } )</a></strong></dt>
+
+<dd>
+<p>Has the current scan operation 'short-circuited'?  In other words, can
+further scanning be skipped, since the message is already definitively
+classified as either spam or ham?</p>
+<p>Plugins should return <code>0</code> to indicate that scanning should continue,
+or <code>1</code> to indicate that short-circuiting has taken effect.</p>
+<dl>
+<dt><strong>permsgstatus</strong></dt>
+
+<dd>
+<p>The <code>Mail::SpamAssassin::PerMsgStatus</code> context object for this scan.</p>
+</dd>
+</dl>
+</dd>
+<dt><strong><a name="bayes_learn" class="item">$plugin-&gt;bayes_learn ( { options ... } )</a></strong></dt>
+
+<dd>
+<p>Called at the end of a bayes learn operation.</p>
+<p>This phase is the best place to map the raw (original) token value
+to the SHA1 hashed value.</p>
+<dl>
+<dt><strong><a name="toksref" class="item">toksref</a></strong></dt>
+
+<dd>
+<p>Reference to hash returned by call to tokenize.  The hash takes the
+format of:</p>
+<pre>
+  {
+    'SHA1 Hash Value' =&gt; 'raw (original) value',
+    ...
+  }</pre>
+<p>NOTE: This data structure has changed since it was originally introduced
+in version 3.0.0.  The values are no longer perl anonymous hashes, they
+are a single string containing the raw token value.  You can test for
+backward compatibility by checking to see if the value for a key is a
+reference to a perl HASH, for instance:</p>
+<p>if (ref($toksref-&gt;{$sometokenkey}) eq 'HASH') {...</p>
+<p>If it is, then you are using the old interface, otherwise you are using
+the current interface.</p>
+</dd>
+<dt><strong>isspam</strong></dt>
+
+<dd>
+<p>Boolean value stating what flavor of message the tokens represent, if
+true then message was specified as spam, false is nonspam.  Note, when
+function is scan then isspam value is not valid.</p>
+</dd>
+<dt><strong><a name="msgid" class="item">msgid</a></strong></dt>
+
+<dd>
+<p>Generated message id of the message just learned.</p>
+</dd>
+<dt><strong><a name="msgatime" class="item">msgatime</a></strong></dt>
+
+<dd>
+<p>Received date of the current message or current time if received date
+could not be determined.  In addition, if the receive date is more than
+24 hrs into the future it will be reset to current datetime.</p>
+</dd>
+</dl>
+</dd>
+<dt><strong><a name="bayes_forget" class="item">$plugin-&gt;bayes_forget ( { options ... } )</a></strong></dt>
+
+<dd>
+<p>Called at the end of a bayes forget operation.</p>
+<dl>
+<dt><strong>toksref</strong></dt>
+
+<dd>
+<p>Reference to hash returned by call to tokenize.  See bayes_learn
+documentation for additional information on the format.</p>
+</dd>
+<dt><strong>isspam</strong></dt>
+
+<dd>
+<p>Boolean value stating what flavor of message the tokens represent, if
+true then message was specified as spam, false is nonspam.  Note, when
+function is scan then isspam value is not valid.</p>
+</dd>
+<dt><strong>msgid</strong></dt>
+
+<dd>
+<p>Generated message id of the message just forgotten.</p>
+</dd>
+</dl>
+</dd>
+<dt><strong><a name="bayes_scan" class="item">$plugin-&gt;bayes_scan ( { options ... } )</a></strong></dt>
+
+<dd>
+<p>Called at the end of a bayes scan operation.  NOTE: Will not be
+called in case of error or if the message is otherwise skipped.</p>
+<dl>
+<dt><strong>toksref</strong></dt>
+
+<dd>
+<p>Reference to hash returned by call to tokenize.  See bayes_learn
+documentation for additional information on the format.</p>
+</dd>
+<dt><strong><a name="probsref" class="item">probsref</a></strong></dt>
+
+<dd>
+<p>Reference to hash of calculated probabilities for tokens found in
+the database.</p>
+<pre>
+  {
+    'SHA1 Hash Value' =&gt; {
+            'prob' =&gt; 'calculated probability',
+            'spam_count' =&gt; 'Total number of spam msgs w/ token',
+            'ham_count' =&gt; 'Total number of ham msgs w/ token',
+            'atime' =&gt; 'Atime value for token in database'
+          }
+  }</pre>
+</dd>
+<dt><strong>score</strong></dt>
+
+<dd>
+<p>Score calculated for this particular message.</p>
+</dd>
+<dt><strong>msgatime</strong></dt>
+
+<dd>
+<p>Calculated atime of the message just learned, note it may have been adjusted
+if it was determined to be too far into the future.</p>
+</dd>
+<dt><strong><a name="significant_tokens" class="item">significant_tokens</a></strong></dt>
+
+<dd>
+<p>Array ref of the tokens found to be significant in determining the score for
+this message.</p>
+</dd>
+</dl>
+</dd>
+<dt><strong><a name="plugin_report" class="item">$plugin-&gt;plugin_report ( { options ... } )</a></strong></dt>
+
+<dd>
+<p>Called if the message is to be reported as spam.  If the reporting system is
+available, the variable <code>$options-&gt;{report}-&gt;report_available}</code> should
+be set to <code>1</code>; if the reporting system successfully reported the message, the
+variable <code>$options-&gt;{report}-&gt;report_return}</code> should be set to <code>1</code>.</p>
+<dl>
+<dt><strong><a name="report" class="item">report</a></strong></dt>
+
+<dd>
+<p>Reference to the Reporter object (<code>$options-&gt;{report}</code> in the
+paragraph above.)</p>
+</dd>
+<dt><strong><a name="text" class="item">text</a></strong></dt>
+
+<dd>
+<p>Reference to a markup removed copy of the message in scalar string format.</p>
+</dd>
+<dt><strong>msg</strong></dt>
+
+<dd>
+<p>Reference to the original message object.</p>
+</dd>
+</dl>
+</dd>
+<dt><strong><a name="plugin_revoke" class="item">$plugin-&gt;plugin_revoke ( { options ... } )</a></strong></dt>
+
+<dd>
+<p>Called if the message is to be reported as ham (revokes a spam report). If the
+reporting system is available, the variable
+<code>$options-&gt;{revoke}-&gt;revoke_available}</code> should be set to <code>1</code>; if the
+reporting system successfully revoked the message, the variable
+<code>$options-&gt;{revoke}-&gt;revoke_return}</code> should be set to <code>1</code>.</p>
+<dl>
+<dt><strong><a name="revoke" class="item">revoke</a></strong></dt>
+
+<dd>
+<p>Reference to the Reporter object (<code>$options-&gt;{revoke}</code> in the
+paragraph above.)</p>
+</dd>
+<dt><strong>text</strong></dt>
+
+<dd>
+<p>Reference to a markup removed copy of the message in scalar string format.</p>
+</dd>
+<dt><strong>msg</strong></dt>
+
+<dd>
+<p>Reference to the original message object.</p>
+</dd>
+</dl>
+</dd>
+<dt><strong><a name="whitelist_address" class="item">$plugin-&gt;whitelist_address( { options ... } )</a></strong></dt>
+
+<dd>
+<p>Called when a request is made to add an address to a
+persistent address list.</p>
+<dl>
+<dt><strong><a name="address" class="item">address</a></strong></dt>
+
+<dd>
+<p>Address you wish to add.</p>
+</dd>
+<dt><strong><a name="cli_p" class="item">cli_p</a></strong></dt>
+
+<dd>
+<p>Indicate if the call is being made from a command line interface.</p>
+</dd>
+</dl>
+</dd>
+<dt><strong><a name="blacklist_address" class="item">$plugin-&gt;blacklist_address( { options ... } )</a></strong></dt>
+
+<dd>
+<p>Called when a request is made to add an address to a
+persistent address list.</p>
+<dl>
+<dt><strong>address</strong></dt>
+
+<dd>
+<p>Address you wish to add.</p>
+</dd>
+<dt><strong>cli_p</strong></dt>
+
+<dd>
+<p>Indicate if the call is being made from a command line interface.</p>
+</dd>
+</dl>
+</dd>
+<dt><strong><a name="remove_address" class="item">$plugin-&gt;remove_address( { options ... } )</a></strong></dt>
+
+<dd>
+<p>Called when a request is made to remove an address to a
+persistent address list.</p>
+<dl>
+<dt><strong>address</strong></dt>
+
+<dd>
+<p>Address you wish to remove.</p>
+</dd>
+<dt><strong>cli_p</strong></dt>
+
+<dd>
+<p>Indicate if the call is being made from a command line interface.</p>
+</dd>
+</dl>
+</dd>
+<dt><strong><a name="spamd_child_init" class="item">$plugin-&gt;spamd_child_init ()</a></strong></dt>
+
+<dd>
+<p>Called in each new child process when it starts up under spamd.</p>
+</dd>
+<dt><strong><a name="log_scan_result" class="item">$plugin-&gt;log_scan_result ( { options ... } )</a></strong></dt>
+
+<dd>
+<p>Called when spamd has completed scanning a message.  Currently,
+only spamd calls this API.</p>
+<dl>
+<dt><strong><a name="result" class="item">result</a></strong></dt>
+
+<dd>
+<p>The <a href="#result"><code>'result: ...'</code></a> line for this scan.  Format is as described
+at <strong><a href="http://wiki.apache.org/spamassassin/SpamdSyslogFormat">http://wiki.apache.org/spamassassin/SpamdSyslogFormat</a></strong>.</p>
+</dd>
+</dl>
+</dd>
+<dt><strong><a name="spamd_child_post_connection_close" class="item">$plugin-&gt;spamd_child_post_connection_close ()</a></strong></dt>
+
+<dd>
+<p>Called when child returns from handling a connection.</p>
+<p>If there was an accept failure, the child will die and this code will
+not be called.</p>
+</dd>
+<dt><strong><a name="finish" class="item">$plugin-&gt;finish ()</a></strong></dt>
+
+<dd>
+<p>Called when the <code>Mail::SpamAssassin</code> object is destroyed.</p>
+</dd>
+<dt><strong><a name="learner_new" class="item">$plugin-&gt;learner_new ()</a></strong></dt>
+
+<dd>
+<p>Used to support human-trained probabilistic classifiers like the BAYES_* ruleset.
+Called when a new <code>Mail::SpamAssassin::Bayes</code> object has been created; typically
+when a new user's scan is about to start.</p>
+</dd>
+<dt><strong><a name="learn_message" class="item">$plugin-&gt;learn_message ()</a></strong></dt>
+
+<dd>
+<p>Train the classifier with a training message.</p>
+<dl>
+<dt><strong>isspam</strong></dt>
+
+<dd>
+<p>1 if the message is spam, 0 if it's non-spam.</p>
+</dd>
+<dt><strong>msg</strong></dt>
+
+<dd>
+<p>The message's <code>Mail::SpamAssassin::Message</code> object.</p>
+</dd>
+<dt><strong><a name="id" class="item">id</a></strong></dt>
+
+<dd>
+<p>An optional message-identification string, used internally to tag the message.
+If it is <code>undef</code>, one will be generated.  It should be unique to that message.</p>
+</dd>
+</dl>
+</dd>
+<dt><strong><a name="forget_message" class="item">$plugin-&gt;forget_message ()</a></strong></dt>
+
+<dd>
+<p>Tell the classifier to 'forget' its training about a specific message.</p>
+<dl>
+<dt><strong>msg</strong></dt>
+
+<dd>
+<p>The message's <code>Mail::SpamAssassin::Message</code> object.</p>
+</dd>
+<dt><strong>id</strong></dt>
+
+<dd>
+<p>An optional message-identification string, used internally to tag the message.
+If it is <code>undef</code>, one will be generated.  It should be unique to that message.</p>
+</dd>
+</dl>
+</dd>
+<dt><strong><a name="learner_sync" class="item">$plugin-&gt;learner_sync ()</a></strong></dt>
+
+<dd>
+<p>Tell the classifier to 'sync' any pending changes against the current 
+user's training database.  This is called by <code>sa-learn --sync</code>.</p>
+<p>If you do not need to implement these for your classifier, create an
+implementation that just contains <code>return 1</code>.</p>
+</dd>
+<dt><strong><a name="learner_expire_old_training" class="item">$plugin-&gt;learner_expire_old_training ()</a></strong></dt>
+
+<dd>
+<p>Tell the classifier to perform infrequent, time-consuming cleanup of
+the current user's training database.  This is called by <code>sa-learn
+--force-expire</code>.</p>
+<p>If you do not need to implement these for your classifier, create an
+implementation that just contains <code>return 1</code>.</p>
+</dd>
+<dt><strong><a name="learner_is_scan_available" class="item">$plugin-&gt;learner_is_scan_available ()</a></strong></dt>
+
+<dd>
+<p>Should return 1 if it is possible to use the current user's training data for
+a message-scan operation, or 0 otherwise.</p>
+</dd>
+<dt><strong><a name="learner_dump_database" class="item">$plugin-&gt;learner_dump_database ()</a></strong></dt>
+
+<dd>
+<p>Dump information about the current user's training data to <code>stdout</code>.
+This is called by <code>sa-learn --dump</code>.</p>
+<dl>
+<dt><strong><a name="magic" class="item">magic</a></strong></dt>
+
+<dd>
+<p>Set to 1 if &quot;magic&quot; name-value metadata should be dumped.</p>
+</dd>
+<dt><strong><a name="toks" class="item">toks</a></strong></dt>
+
+<dd>
+<p>Set to 1 if the database of tokens should be dumped.</p>
+</dd>
+<dt><strong><a name="regex" class="item">regex</a></strong></dt>
+
+<dd>
+<p>Either <code>undef</code> to dump all tokens, or a value which specifies a regular expression
+subset of the tokens to dump.</p>
+</dd>
+</dl>
+</dd>
+<dt><strong><a name="learner_close" class="item">$plugin-&gt;learner_close ()</a></strong></dt>
+
+<dd>
+<p>Close any open databases.</p>
+<dl>
+<dt><strong><a name="quiet" class="item">quiet</a></strong></dt>
+
+<dd>
+<p>Set to 1 if warning messages should be suppressed.</p>
+</dd>
+</dl>
+</dd>
+</dl>
+<p>
+</p>
+<hr />
+<h1><a name="helper_apis">HELPER APIS</a></h1>
+<p>These methods provide an API for plugins to register themselves
+to receive specific events, or control the callback chain behaviour.</p>
+<dl>
+<dt><strong><a name="register_eval_rule" class="item">$plugin-&gt;register_eval_rule ($nameofevalsub)</a></strong></dt>
+
+<dd>
+<p>Plugins that implement an eval test will need to call this, so that
+SpamAssassin calls into the object when that eval test is encountered.
+See the <strong>REGISTERING EVAL RULES</strong> section for full details.</p>
+</dd>
+<dt><strong><a name="register_generated_rule_method" class="item">$plugin-&gt;register_generated_rule_method ($nameofsub)</a></strong></dt>
+
+<dd>
+<p>In certain circumstances, plugins may find it useful to compile
+perl functions from the ruleset, on the fly.  It is important to
+remove these once the <code>Mail::SpamAssassin</code> object is deleted,
+however, and this API allows this.</p>
+<p>Once the method <code>$nameofsub</code> has been generated, call this API
+with the name of the method (including full package scope).
+This indicates that it's a temporary piece of generated code,
+built from the SpamAssassin ruleset, and when 
+<a href="#finish"><code>Mail::SpamAssassin::finish()</code></a> is called, the method will
+be destroyed.</p>
+<p>This API was added in SpamAssassin 3.2.0.</p>
+</dd>
+<dt><strong><a name="register_method_priority" class="item">$plugin-&gt;register_method_priority($methodname, $priority)</a></strong></dt>
+
+<dd>
+<p>Indicate that the method named <code>$methodname</code> on the current object
+has a callback priority of <code>$priority</code>.</p>
+<p>This is used by the plugin handler to determine the relative order of
+callbacks; plugins with lower-numbered priorities are called before plugins
+with higher-numbered priorities.  Each method can have a different priority
+value.  The default value is <code>0</code>.  The ordering of callbacks to methods with
+equal priority is undefined.</p>
+<p>Typically, you only need to worry about this if you need to ensure your
+plugin's method is called before another plugin's implementation of that
+method.  It should be called from your plugin's constructor.</p>
+<p>This API was added in SpamAssassin 3.2.0.</p>
+</dd>
+<dt><strong><a name="inhibit_further_callbacks" class="item">$plugin-&gt;<code>inhibit_further_callbacks()</code></a></strong></dt>
+
+<dd>
+<p>Tells the plugin handler to inhibit calling into other plugins in the plugin
+chain for the current callback.  Frequently used when parsing configuration
+settings using <a href="#parse_config"><code>parse_config()</code></a>.</p>
+</dd>
+</dl>
+<p>
+</p>
+<hr />
+<h1><a name="logging">LOGGING</a></h1>
+<dl>
+<dt><strong><a name="dbg" class="item">Mail::SpamAssassin::Plugin::dbg($message)</a></strong></dt>
+
+<dd>
+<p>Output a debugging message <code>$message</code>, if the SpamAssassin object is running
+with debugging turned on.</p>
+<p><em>NOTE:</em> This function is not available in the package namespace
+of general plugins and can't be called via $self-&gt;<a href="#dbg"><code>dbg()</code></a>.  If a
+plugin wishes to output debug information, it should call
+<a href="#dbg"><code>Mail::SpamAssassin::Plugin::dbg($msg)</code></a>.</p>
+</dd>
+<dt><strong><a name="info" class="item">Mail::SpamAssassin::Plugin::info($message)</a></strong></dt>
+
+<dd>
+<p>Output an informational message <code>$message</code>, if the SpamAssassin object
+is running with informational messages turned on.</p>
+<p><em>NOTE:</em> This function is not available in the package namespace
+of general plugins and can't be called via $self-&gt;<a href="#info"><code>info()</code></a>.  If a
+plugin wishes to output debug information, it should call
+<a href="#info"><code>Mail::SpamAssassin::Plugin::info($msg)</code></a>.</p>
+<p>In general, it is better for plugins to use the <code>Mail::SpamAssassin::Logger</code>
+module to import <a href="#dbg"><code>dbg</code></a> and <a href="#info"><code>info</code></a> directly, like so:</p>
+<pre>
+  use Mail::SpamAssassin::Logger;
+  dbg(&quot;some message&quot;);
+  info(&quot;some other message&quot;);</pre>
+</dd>
+</dl>
+<p>
+</p>
+<hr />
+<h1><a name="registering_eval_rules">REGISTERING EVAL RULES</a></h1>
+<p>Plugins that implement an eval test must register the methods that can be
+called from rules in the configuration files, in the plugin class' constructor.</p>
+<p>For example,</p>
+<pre>
+  $plugin-&gt;register_eval_rule ('check_for_foo')</pre>
+<p>will cause <code>$plugin-&gt;check_for_foo()</code> to be called for this
+SpamAssassin rule:</p>
+<pre>
+  header   FOO_RULE     eval:check_for_foo()</pre>
+<p>Note that eval rules are passed the following arguments:</p>
+<dl>
+<dt><strong><a name="the_plugin_object_itself" class="item">- The plugin object itself</a></strong></dt>
+
+<dt><strong><a name="the_mail_spamassassin_permsgstatus_object_calling_the_rule" class="item">- The <code>Mail::SpamAssassin::PerMsgStatus</code> object calling the rule</a></strong></dt>
+
+<dt><strong><a name="standard_arguments_for_the_rule_type_in_use" class="item">- standard arguments for the rule type in use</a></strong></dt>
+
+<dt><strong><a name="any_and_all_arguments_as_specified_in_the_configuration_file" class="item">- any and all arguments as specified in the configuration file</a></strong></dt>
+
+</dl>
+<p>In other words, the eval test method should look something like this:</p>
+<pre>
+  sub check_for_foo {
+    my ($self, $permsgstatus, ...arguments...) = @_;
+    ...code returning 0 or 1
+  }</pre>
+<p>Note that the headers can be accessed using the <code>get()</code> method on the
+<code>Mail::SpamAssassin::PerMsgStatus</code> object, and the body by
+<code>get_decoded_stripped_body_text_array()</code> and other similar methods.
+Similarly, the <code>Mail::SpamAssassin::Conf</code> object holding the current
+configuration may be accessed through <code>$permsgstatus-&gt;{main}-&gt;{conf}</code>.</p>
+<p>The eval rule should return <code>1</code> for a hit, or <code>0</code> if the rule
+is not hit.</p>
+<p>State for a single message being scanned should be stored on the <code>$permsgstatus</code>
+object, not on the <code>$self</code> object, since <code>$self</code> persists between scan
+operations.  See the 'lifecycle note' on the <a href="#check_start"><code>check_start()</code></a> method above.</p>
+<p>
+</p>
+<hr />
+<h1><a name="standard_arguments_for_rule_types">STANDARD ARGUMENTS FOR RULE TYPES</a></h1>
+<p>Plugins will be called with the same arguments as a standard EvalTest.
+Different rule types receive different information by default:</p>
+<dl>
+<dt><strong><a name="header_tests_no_extra_arguments" class="item">- header tests: no extra arguments</a></strong></dt>
+
+<dt><strong><a name="body_tests_fully_rendered_message_as_array_reference" class="item">- body tests: fully rendered message as array reference</a></strong></dt>
+
+<dt><strong><a name="rawbody_tests_fully_decoded_message_as_array_reference" class="item">- rawbody tests: fully decoded message as array reference</a></strong></dt>
+
+<dt><strong><a name="full_tests_pristine_message_as_scalar_reference" class="item">- full tests: pristine message as scalar reference</a></strong></dt>
+
+</dl>
+<p>The configuration file arguments will be passed in after the standard
+arguments.</p>
+<p>
+</p>
+<hr />
+<h1><a name="backward_compatibility">BACKWARD COMPATIBILITY</a></h1>
+<p>Note that if you write a plugin and need to determine if a particular
+helper method is supported on <code>Mail::SpamAssassin::Plugin</code>, you
+can do this:</p>
+<pre>
+    if ($self-&gt;can(&quot;name_of_method&quot;)) {
+      eval {
+        $self-&gt;name_of_method();        # etc.
+      }
+    } else {
+      # take fallback action
+    }</pre>
+<p>The same applies for the public APIs on objects of other types, such as
+<code>Mail::SpamAssassin::PerMsgStatus</code>.</p>
+<p>
+</p>
+<hr />
+<h1><a name="see_also">SEE ALSO</a></h1>
+<p><code>Mail::SpamAssassin</code></p>
+<p><code>Mail::SpamAssassin::PerMsgStatus</code></p>
+<p><a href="http://wiki.apache.org/spamassassin/PluginWritingTips">http://wiki.apache.org/spamassassin/PluginWritingTips</a></p>
+<p><a href="http://issues.apache.org/SpamAssassin/show_bug.cgi?id=2163">http://issues.apache.org/SpamAssassin/show_bug.cgi?id=2163</a></p>
+
+</body>
+
+</html>

Added: spamassassin/site/full/3.4.x/doc/Mail_SpamAssassin_Plugin.txt
URL: http://svn.apache.org/viewvc/spamassassin/site/full/3.4.x/doc/Mail_SpamAssassin_Plugin.txt?rev=1676792&view=auto
==============================================================================
--- spamassassin/site/full/3.4.x/doc/Mail_SpamAssassin_Plugin.txt (added)
+++ spamassassin/site/full/3.4.x/doc/Mail_SpamAssassin_Plugin.txt Wed Apr 29 17:04:09 2015
@@ -0,0 +1,838 @@
+NAME
+    Mail::SpamAssassin::Plugin - SpamAssassin plugin base class
+
+SYNOPSIS
+  SpamAssassin configuration:
+      loadplugin MyPlugin /path/to/myplugin.pm
+
+  Perl code:
+      package MyPlugin;
+
+      use Mail::SpamAssassin::Plugin;
+      our @ISA = qw(Mail::SpamAssassin::Plugin);
+
+      sub new {
+        my ($class, $mailsa) = @_;
+    
+        # the usual perlobj boilerplate to create a subclass object
+        $class = ref($class) || $class;
+        my $self = $class->SUPER::new($mailsa);
+        bless ($self, $class);
+   
+        # then register an eval rule, if desired...
+        $self->register_eval_rule ("check_for_foo");
+
+        # and return the new plugin object
+        return $self;
+      }
+
+      ...methods...
+
+      1;
+
+DESCRIPTION
+    This is the base class for SpamAssassin plugins; all plugins must be
+    objects that implement this class.
+
+    This class provides no-op stub methods for all the callbacks that a
+    plugin can receive. It is expected that your plugin will override one or
+    more of these stubs to perform its actions.
+
+    SpamAssassin implements a plugin chain; each callback event is passed to
+    each of the registered plugin objects in turn. Any plugin can call
+    "$self->inhibit_further_callbacks()" to block delivery of that event to
+    later plugins in the chain. This is useful if the plugin has handled the
+    event, and there will be no need for later plugins to handle it as well.
+
+    If you're looking to write a simple eval rule, skip straight to
+    "register_eval_rule()", below.
+
+INTERFACE
+    In all the plugin APIs below, "options" refers to a reference to a hash
+    containing name-value pairs. This is used to ensure
+    future-compatibility, in that we can add new options in future without
+    affecting objects built to an earlier version of the API.
+
+    For example, here would be how to print out the "line" item in a
+    "parse_config()" method:
+
+      sub parse_config {
+        my ($self, $opts) = @_;
+        print "MyPlugin: parse_config got ".$opts->{line}."\n";
+      }
+
+METHODS
+    The following methods can be overridden by subclasses to handle events.
+
+    $plugin = MyPluginClass->new ($mailsaobject)
+        Constructor. Plugins that need to register themselves will need to
+        define their own; the default super-class constructor will work fine
+        for plugins that just override a method.
+
+        Note that subclasses must provide the $mailsaobject to the
+        superclass constructor, like so:
+
+          my $self = $class->SUPER::new($mailsaobject);
+
+        Lifecycle note: plugins that will need to store per-scan state
+        should not store that on the Plugin object; instead this should be
+        stored on the PerMsgStatus object, see "check_start()" below. It is
+        also likewise recommended that configuration settings be stored on
+        the Conf object; see "parse_config()".
+
+    $plugin->parse_config ( { options ... } )
+        Parse a configuration line that hasn't already been handled.
+        "options" is a reference to a hash containing these options:
+
+        line
+            The line of configuration text to parse. This has leading and
+            trailing whitespace, and comments, removed.
+
+        key The configuration key; ie. the first "word" on the line.
+
+        value
+            The configuration value; everything after the first "word" and
+            any whitespace after that.
+
+        conf
+            The "Mail::SpamAssassin::Conf" object on which the configuration
+            data should be stored.
+
+        user_config
+            A boolean: 1 if reading a user's configuration, 0 if reading the
+            system-wide configuration files.
+
+        If the configuration line was a setting that is handled by this
+        plugin, the method implementation should call
+        "$self->inhibit_further_callbacks()".
+
+        If the setting is not handled by this plugin, the method should
+        return 0 so that a later plugin may handle it, or so that
+        SpamAssassin can output a warning message to the user if no plugin
+        understands it.
+
+        Lifecycle note: it is suggested that configuration be stored on the
+        "Mail::SpamAssassin::Conf" object in use, instead of the plugin
+        object itself. That can be found as "$plugin->{main}->{conf}", or as
+        "conf" in the $options hash reference above. By storing it on
+        "conf", this allows per-user and system-wide configuration
+        precedence to be dealt with correctly.
+
+    $plugin->finish_parsing_start ( { options ... } )
+        Signals that the system-wide configuration has been completely read,
+        but internal data structures are not yet created. It is possible to
+        use this hook to dynamically change the configuration already read
+        in or add new config options.
+
+        "options" is a reference to a hash containing these options:
+
+        conf
+            The "Mail::SpamAssassin::Conf" object on which the configuration
+            data should be stored.
+
+        Note: there are no guarantees that the internal data structures of
+        SpamAssassin will not change from release to release. In particular
+        to this plugin hook, if you modify the rules data structures in a
+        third-party plugin, all bets are off until such time that an API is
+        present for modifying that configuration data.
+
+    $plugin->finish_parsing_end ( { options ... } )
+        Signals that the system-wide configuration parsing has just
+        finished, and SpamAssassin is nearly ready to check messages.
+
+        "options" is a reference to a hash containing these options:
+
+        conf
+            The "Mail::SpamAssassin::Conf" object on which the configuration
+            data should be stored.
+
+        Note: there are no guarantees that the internal data structures of
+        SpamAssassin will not change from release to release. In particular
+        to this plugin hook, if you modify the rules data structures in a
+        third-party plugin, all bets are off until such time that an API is
+        present for modifying that configuration data.
+
+    $plugin->user_conf_parsing_start ( { options ... } )
+        Signals that the per-user configuration has been completely read,
+        but not converted to internal data structures. It is possible to use
+        this hook to dynamically change the configuration already read in or
+        add new config options.
+
+        If "allow_user_rules" is enabled in the configuration, it is
+        possible that additional rules have been added since the
+        "finish_parsing_start" plugin hook invocation was called.
+
+        conf
+            The "Mail::SpamAssassin::Conf" object on which the configuration
+            data should be stored.
+
+        Note: there are no guarantees that the internal data structures of
+        SpamAssassin will not change from release to release. In particular
+        to this plugin hook, if you modify the rules data structures in a
+        third-party plugin, all bets are off until such time that an API is
+        present for modifying that configuration data.
+
+    $plugin->user_conf_parsing_end ( { options ... } )
+        Signals that the per-user configuration parsing has just finished,
+        and SpamAssassin is nearly ready to check messages. If
+        "allow_user_rules" is enabled in the configuration, it is possible
+        that additional rules have been added since the "finish_parsing_end"
+        plugin hook invocation was called.
+
+        "options" is a reference to a hash containing these options:
+
+        conf
+            The "Mail::SpamAssassin::Conf" object on which the configuration
+            data should be stored.
+
+        Note: there are no guarantees that the internal data structures of
+        SpamAssassin will not change from release to release. In particular
+        to this plugin hook, if you modify the rules data structures in a
+        third-party plugin, all bets are off until such time that an API is
+        present for modifying that configuration data.
+
+    $plugin->signal_user_changed ( { options ... } )
+        Signals that the current user has changed for a new one.
+
+        username
+            The new user's username.
+
+        user_dir
+            The new user's home directory. (equivalent to "~".)
+
+        userstate_dir
+            The new user's storage directory. (equivalent to
+            "~/.spamassassin".)
+
+    $plugin->services_authorized_for_username ( { options ... } )
+        Validates that a given username is authorized to use certain
+        services.
+
+        In order to authorize a user, the plugin should first check that it
+        can handle any of the services passed into the method and then set
+        the value for each allowed service to true (or any non-negative
+        value).
+
+        The current supported services are: bayessql
+
+        username
+            A username
+
+        services
+            Reference to a hash containing the services you want to check.
+
+            {
+
+              'bayessql' => 0
+
+            }
+
+        conf
+            The "Mail::SpamAssassin::Conf" object on which the configuration
+            data should be stored.
+
+    $plugin->compile_now_start ( { options ... } )
+        This is called at the beginning of Mail::SpamAssassin::compile_now()
+        so plugins can do any necessary initialization for multi-process
+        SpamAssassin (such as spamd or mass-check -j).
+
+        use_user_prefs
+            The value of $use_user_prefs option in compile_now().
+
+        keep_userstate
+            The value of $keep_userstate option in compile_now().
+
+    $plugin->compile_now_finish ( { options ... } )
+        This is called at the end of Mail::SpamAssassin::compile_now() so
+        plugins can do any necessary initialization for multi-process
+        SpamAssassin (such as spamd or mass-check -j).
+
+        use_user_prefs
+            The value of $use_user_prefs option in compile_now().
+
+        keep_userstate
+            The value of $keep_userstate option in compile_now().
+
+    $plugin->check_start ( { options ... } )
+        Signals that a message check operation is starting.
+
+        permsgstatus
+            The "Mail::SpamAssassin::PerMsgStatus" context object for this
+            scan.
+
+            Lifecycle note: it is recommended that rules that need to track
+            test state on a per-scan basis should store that state on this
+            object, not on the plugin object itself, since the plugin object
+            will be shared between all active scanners.
+
+            The message being scanned is accessible through the
+            "$permsgstatus->get_message()" API; there are a number of other
+            public APIs on that object, too. See
+            "Mail::SpamAssassin::PerMsgStatus" perldoc.
+
+    $plugin->check_main ( { options ... } )
+        Signals that a message should be checked. Note that implementations
+        of this hook should return 1.
+
+        permsgstatus
+            The "Mail::SpamAssassin::PerMsgStatus" context object for this
+            scan.
+
+    $plugin->check_tick ( { options ... } )
+        Called periodically during a message check operation. A callback set
+        for this method is a good place to run through an event loop dealing
+        with network events triggered in a "parse_metadata" method, for
+        example.
+
+        permsgstatus
+            The "Mail::SpamAssassin::PerMsgStatus" context object for this
+            scan.
+
+    $plugin->check_post_dnsbl ( { options ... } )
+        Called after the DNSBL results have been harvested. This is a good
+        place to harvest your own asynchronously-started network lookups.
+
+        permsgstatus
+            The "Mail::SpamAssassin::PerMsgStatus" context object for this
+            scan.
+
+    $plugin->check_post_learn ( { options ... } )
+        Called after auto-learning may (or may not) have taken place. If you
+        wish to perform additional learning, whether or not auto-learning
+        happens, this is the place to do it.
+
+        permsgstatus
+            The "Mail::SpamAssassin::PerMsgStatus" context object for this
+            scan.
+
+    $plugin->check_end ( { options ... } )
+        Signals that a message check operation has just finished, and the
+        results are about to be returned to the caller.
+
+        permsgstatus
+            The "Mail::SpamAssassin::PerMsgStatus" context object for this
+            scan. The current score, names of rules that hit, etc. can be
+            retrieved using the public APIs on this object.
+
+    $plugin->finish_tests ( { options ... } )
+        Called via "Mail::SpamAssassin::finish". This should clear up any
+        tests that a plugin has added to the namespace.
+
+        In certain circumstances, plugins may find it useful to compile perl
+        functions from the ruleset, on the fly. It is important to remove
+        these once the "Mail::SpamAssassin" object is deleted, however, and
+        this API allows this.
+
+        Each plugin is responsible for its own generated perl functions.
+
+        conf
+            The "Mail::SpamAssassin::Conf" object on which the configuration
+            data should be stored.
+
+        See also the "register_generated_rule_method" helper API, below.
+
+    $plugin->extract_metadata ( { options ... } )
+        Signals that a message is being mined for metadata. Some plugins may
+        wish to add their own metadata as well.
+
+        msg The "Mail::SpamAssassin::Message" object for this message.
+
+        permsgstatus
+            The "Mail::SpamAssassin::PerMsgStatus" context object for this
+            scan.
+
+    $plugin->parsed_metadata ( { options ... } )
+        Signals that a message's metadata has been parsed, and can now be
+        accessed by the plugin.
+
+        permsgstatus
+            The "Mail::SpamAssassin::PerMsgStatus" context object for this
+            scan.
+
+    $plugin->start_rules ( { options ... } )
+        Called before testing a set of rules of a given type and priority.
+
+        permsgstatus
+            The "Mail::SpamAssassin::PerMsgStatus" context object for this
+            scan.
+
+        ruletype
+            The type of the rules about to be performed.
+
+        priority
+            The priority level of the rules about to be performed.
+
+    $plugin->hit_rule ( { options ... } )
+        Called when a rule fires.
+
+        permsgstatus
+            The "Mail::SpamAssassin::PerMsgStatus" context object for this
+            scan.
+
+        ruletype
+            The type of the rule that fired.
+
+        rulename
+            The name of the rule that fired.
+
+        score
+            The rule's score in the active scoreset.
+
+    $plugin->ran_rule ( { options ... } )
+        Called after a rule has been tested, whether or not it fired. When
+        the rule fires, the hit_rule callback is always called before this.
+
+        permsgstatus
+            The "Mail::SpamAssassin::PerMsgStatus" context object for this
+            scan.
+
+        ruletype
+            The type of the rule that was tested.
+
+        rulename
+            The name of the rule that was tested.
+
+    $plugin->autolearn_discriminator ( { options ... } )
+        Control whether a just-scanned message should be learned as either
+        spam or ham. This method should return one of 1 to learn the message
+        as spam, 0 to learn as ham, or "undef" to not learn from the message
+        at all.
+
+        permsgstatus
+            The "Mail::SpamAssassin::PerMsgStatus" context object for this
+            scan.
+
+    $plugin->autolearn ( { options ... } )
+        Signals that a message is about to be auto-learned as either ham or
+        spam.
+
+        permsgstatus
+            The "Mail::SpamAssassin::PerMsgStatus" context object for this
+            scan.
+
+        isspam
+            1 if the message is spam, 0 if ham.
+
+    $plugin->per_msg_finish ( { options ... } )
+        Signals that a "Mail::SpamAssassin::PerMsgStatus" object is being
+        destroyed, and any per-scan context held on that object by this
+        plugin should be destroyed as well.
+
+        Normally, any member variables on the "PerMsgStatus" object will be
+        cleaned up automatically -- but if your plugin has made a circular
+        reference on that object, this is the place to break them so that
+        garbage collection can operate correctly.
+
+        permsgstatus
+            The "Mail::SpamAssassin::PerMsgStatus" context object for this
+            scan.
+
+    $plugin->have_shortcircuited ( { options ... } )
+        Has the current scan operation 'short-circuited'? In other words,
+        can further scanning be skipped, since the message is already
+        definitively classified as either spam or ham?
+
+        Plugins should return 0 to indicate that scanning should continue,
+        or 1 to indicate that short-circuiting has taken effect.
+
+        permsgstatus
+            The "Mail::SpamAssassin::PerMsgStatus" context object for this
+            scan.
+
+    $plugin->bayes_learn ( { options ... } )
+        Called at the end of a bayes learn operation.
+
+        This phase is the best place to map the raw (original) token value
+        to the SHA1 hashed value.
+
+        toksref
+            Reference to hash returned by call to tokenize. The hash takes
+            the format of:
+
+              {
+                'SHA1 Hash Value' => 'raw (original) value',
+                ...
+              }
+
+            NOTE: This data structure has changed since it was originally
+            introduced in version 3.0.0. The values are no longer perl
+            anonymous hashes, they are a single string containing the raw
+            token value. You can test for backward compatibility by checking
+            to see if the value for a key is a reference to a perl HASH, for
+            instance:
+
+            if (ref($toksref->{$sometokenkey}) eq 'HASH') {...
+
+            If it is, then you are using the old interface, otherwise you
+            are using the current interface.
+
+        isspam
+            Boolean value stating what flavor of message the tokens
+            represent, if true then message was specified as spam, false is
+            nonspam. Note, when function is scan then isspam value is not
+            valid.
+
+        msgid
+            Generated message id of the message just learned.
+
+        msgatime
+            Received date of the current message or current time if received
+            date could not be determined. In addition, if the receive date
+            is more than 24 hrs into the future it will be reset to current
+            datetime.
+
+    $plugin->bayes_forget ( { options ... } )
+        Called at the end of a bayes forget operation.
+
+        toksref
+            Reference to hash returned by call to tokenize. See bayes_learn
+            documentation for additional information on the format.
+
+        isspam
+            Boolean value stating what flavor of message the tokens
+            represent, if true then message was specified as spam, false is
+            nonspam. Note, when function is scan then isspam value is not
+            valid.
+
+        msgid
+            Generated message id of the message just forgotten.
+
+    $plugin->bayes_scan ( { options ... } )
+        Called at the end of a bayes scan operation. NOTE: Will not be
+        called in case of error or if the message is otherwise skipped.
+
+        toksref
+            Reference to hash returned by call to tokenize. See bayes_learn
+            documentation for additional information on the format.
+
+        probsref
+            Reference to hash of calculated probabilities for tokens found
+            in the database.
+
+              {
+                'SHA1 Hash Value' => {
+                        'prob' => 'calculated probability',
+                        'spam_count' => 'Total number of spam msgs w/ token',
+                        'ham_count' => 'Total number of ham msgs w/ token',
+                        'atime' => 'Atime value for token in database'
+                      }
+              }
+
+        score
+            Score calculated for this particular message.
+
+        msgatime
+            Calculated atime of the message just learned, note it may have
+            been adjusted if it was determined to be too far into the
+            future.
+
+        significant_tokens
+            Array ref of the tokens found to be significant in determining
+            the score for this message.
+
+    $plugin->plugin_report ( { options ... } )
+        Called if the message is to be reported as spam. If the reporting
+        system is available, the variable
+        "$options->{report}->report_available}" should be set to 1; if the
+        reporting system successfully reported the message, the variable
+        "$options->{report}->report_return}" should be set to 1.
+
+        report
+            Reference to the Reporter object ("$options->{report}" in the
+            paragraph above.)
+
+        text
+            Reference to a markup removed copy of the message in scalar
+            string format.
+
+        msg Reference to the original message object.
+
+    $plugin->plugin_revoke ( { options ... } )
+        Called if the message is to be reported as ham (revokes a spam
+        report). If the reporting system is available, the variable
+        "$options->{revoke}->revoke_available}" should be set to 1; if the
+        reporting system successfully revoked the message, the variable
+        "$options->{revoke}->revoke_return}" should be set to 1.
+
+        revoke
+            Reference to the Reporter object ("$options->{revoke}" in the
+            paragraph above.)
+
+        text
+            Reference to a markup removed copy of the message in scalar
+            string format.
+
+        msg Reference to the original message object.
+
+    $plugin->whitelist_address( { options ... } )
+        Called when a request is made to add an address to a persistent
+        address list.
+
+        address
+            Address you wish to add.
+
+        cli_p
+            Indicate if the call is being made from a command line
+            interface.
+
+    $plugin->blacklist_address( { options ... } )
+        Called when a request is made to add an address to a persistent
+        address list.
+
+        address
+            Address you wish to add.
+
+        cli_p
+            Indicate if the call is being made from a command line
+            interface.
+
+    $plugin->remove_address( { options ... } )
+        Called when a request is made to remove an address to a persistent
+        address list.
+
+        address
+            Address you wish to remove.
+
+        cli_p
+            Indicate if the call is being made from a command line
+            interface.
+
+    $plugin->spamd_child_init ()
+        Called in each new child process when it starts up under spamd.
+
+    $plugin->log_scan_result ( { options ... } )
+        Called when spamd has completed scanning a message. Currently, only
+        spamd calls this API.
+
+        result
+            The 'result: ...' line for this scan. Format is as described at
+            http://wiki.apache.org/spamassassin/SpamdSyslogFormat.
+
+    $plugin->spamd_child_post_connection_close ()
+        Called when child returns from handling a connection.
+
+        If there was an accept failure, the child will die and this code
+        will not be called.
+
+    $plugin->finish ()
+        Called when the "Mail::SpamAssassin" object is destroyed.
+
+    $plugin->learner_new ()
+        Used to support human-trained probabilistic classifiers like the
+        BAYES_* ruleset. Called when a new "Mail::SpamAssassin::Bayes"
+        object has been created; typically when a new user's scan is about
+        to start.
+
+    $plugin->learn_message ()
+        Train the classifier with a training message.
+
+        isspam
+            1 if the message is spam, 0 if it's non-spam.
+
+        msg The message's "Mail::SpamAssassin::Message" object.
+
+        id  An optional message-identification string, used internally to
+            tag the message. If it is "undef", one will be generated. It
+            should be unique to that message.
+
+    $plugin->forget_message ()
+        Tell the classifier to 'forget' its training about a specific
+        message.
+
+        msg The message's "Mail::SpamAssassin::Message" object.
+
+        id  An optional message-identification string, used internally to
+            tag the message. If it is "undef", one will be generated. It
+            should be unique to that message.
+
+    $plugin->learner_sync ()
+        Tell the classifier to 'sync' any pending changes against the
+        current user's training database. This is called by "sa-learn
+        --sync".
+
+        If you do not need to implement these for your classifier, create an
+        implementation that just contains "return 1".
+
+    $plugin->learner_expire_old_training ()
+        Tell the classifier to perform infrequent, time-consuming cleanup of
+        the current user's training database. This is called by "sa-learn
+        --force-expire".
+
+        If you do not need to implement these for your classifier, create an
+        implementation that just contains "return 1".
+
+    $plugin->learner_is_scan_available ()
+        Should return 1 if it is possible to use the current user's training
+        data for a message-scan operation, or 0 otherwise.
+
+    $plugin->learner_dump_database ()
+        Dump information about the current user's training data to "stdout".
+        This is called by "sa-learn --dump".
+
+        magic
+            Set to 1 if "magic" name-value metadata should be dumped.
+
+        toks
+            Set to 1 if the database of tokens should be dumped.
+
+        regex
+            Either "undef" to dump all tokens, or a value which specifies a
+            regular expression subset of the tokens to dump.
+
+    $plugin->learner_close ()
+        Close any open databases.
+
+        quiet
+            Set to 1 if warning messages should be suppressed.
+
+HELPER APIS
+    These methods provide an API for plugins to register themselves to
+    receive specific events, or control the callback chain behaviour.
+
+    $plugin->register_eval_rule ($nameofevalsub)
+        Plugins that implement an eval test will need to call this, so that
+        SpamAssassin calls into the object when that eval test is
+        encountered. See the REGISTERING EVAL RULES section for full
+        details.
+
+    $plugin->register_generated_rule_method ($nameofsub)
+        In certain circumstances, plugins may find it useful to compile perl
+        functions from the ruleset, on the fly. It is important to remove
+        these once the "Mail::SpamAssassin" object is deleted, however, and
+        this API allows this.
+
+        Once the method $nameofsub has been generated, call this API with
+        the name of the method (including full package scope). This
+        indicates that it's a temporary piece of generated code, built from
+        the SpamAssassin ruleset, and when "Mail::SpamAssassin::finish()" is
+        called, the method will be destroyed.
+
+        This API was added in SpamAssassin 3.2.0.
+
+    $plugin->register_method_priority($methodname, $priority)
+        Indicate that the method named $methodname on the current object has
+        a callback priority of $priority.
+
+        This is used by the plugin handler to determine the relative order
+        of callbacks; plugins with lower-numbered priorities are called
+        before plugins with higher-numbered priorities. Each method can have
+        a different priority value. The default value is 0. The ordering of
+        callbacks to methods with equal priority is undefined.
+
+        Typically, you only need to worry about this if you need to ensure
+        your plugin's method is called before another plugin's
+        implementation of that method. It should be called from your
+        plugin's constructor.
+
+        This API was added in SpamAssassin 3.2.0.
+
+    $plugin->inhibit_further_callbacks()
+        Tells the plugin handler to inhibit calling into other plugins in
+        the plugin chain for the current callback. Frequently used when
+        parsing configuration settings using "parse_config()".
+
+LOGGING
+    Mail::SpamAssassin::Plugin::dbg($message)
+        Output a debugging message $message, if the SpamAssassin object is
+        running with debugging turned on.
+
+        *NOTE:* This function is not available in the package namespace of
+        general plugins and can't be called via $self->dbg(). If a plugin
+        wishes to output debug information, it should call
+        "Mail::SpamAssassin::Plugin::dbg($msg)".
+
+    Mail::SpamAssassin::Plugin::info($message)
+        Output an informational message $message, if the SpamAssassin object
+        is running with informational messages turned on.
+
+        *NOTE:* This function is not available in the package namespace of
+        general plugins and can't be called via $self->info(). If a plugin
+        wishes to output debug information, it should call
+        "Mail::SpamAssassin::Plugin::info($msg)".
+
+        In general, it is better for plugins to use the
+        "Mail::SpamAssassin::Logger" module to import "dbg" and "info"
+        directly, like so:
+
+          use Mail::SpamAssassin::Logger;
+          dbg("some message");
+          info("some other message");
+
+REGISTERING EVAL RULES
+    Plugins that implement an eval test must register the methods that can
+    be called from rules in the configuration files, in the plugin class'
+    constructor.
+
+    For example,
+
+      $plugin->register_eval_rule ('check_for_foo')
+
+    will cause "$plugin->check_for_foo()" to be called for this SpamAssassin
+    rule:
+
+      header   FOO_RULE     eval:check_for_foo()
+
+    Note that eval rules are passed the following arguments:
+
+    - The plugin object itself
+    - The "Mail::SpamAssassin::PerMsgStatus" object calling the rule
+    - standard arguments for the rule type in use
+    - any and all arguments as specified in the configuration file
+
+    In other words, the eval test method should look something like this:
+
+      sub check_for_foo {
+        my ($self, $permsgstatus, ...arguments...) = @_;
+        ...code returning 0 or 1
+      }
+
+    Note that the headers can be accessed using the "get()" method on the
+    "Mail::SpamAssassin::PerMsgStatus" object, and the body by
+    "get_decoded_stripped_body_text_array()" and other similar methods.
+    Similarly, the "Mail::SpamAssassin::Conf" object holding the current
+    configuration may be accessed through "$permsgstatus->{main}->{conf}".
+
+    The eval rule should return 1 for a hit, or 0 if the rule is not hit.
+
+    State for a single message being scanned should be stored on the
+    $permsgstatus object, not on the $self object, since $self persists
+    between scan operations. See the 'lifecycle note' on the "check_start()"
+    method above.
+
+STANDARD ARGUMENTS FOR RULE TYPES
+    Plugins will be called with the same arguments as a standard EvalTest.
+    Different rule types receive different information by default:
+
+    - header tests: no extra arguments
+    - body tests: fully rendered message as array reference
+    - rawbody tests: fully decoded message as array reference
+    - full tests: pristine message as scalar reference
+
+    The configuration file arguments will be passed in after the standard
+    arguments.
+
+BACKWARD COMPATIBILITY
+    Note that if you write a plugin and need to determine if a particular
+    helper method is supported on "Mail::SpamAssassin::Plugin", you can do
+    this:
+
+        if ($self->can("name_of_method")) {
+          eval {
+            $self->name_of_method();        # etc.
+          }
+        } else {
+          # take fallback action
+        }
+
+    The same applies for the public APIs on objects of other types, such as
+    "Mail::SpamAssassin::PerMsgStatus".
+
+SEE ALSO
+    "Mail::SpamAssassin"
+
+    "Mail::SpamAssassin::PerMsgStatus"
+
+    http://wiki.apache.org/spamassassin/PluginWritingTips
+
+    http://issues.apache.org/SpamAssassin/show_bug.cgi?id=2163
+

Added: spamassassin/site/full/3.4.x/doc/Mail_SpamAssassin_PluginHandler.html
URL: http://svn.apache.org/viewvc/spamassassin/site/full/3.4.x/doc/Mail_SpamAssassin_PluginHandler.html?rev=1676792&view=auto
==============================================================================
--- spamassassin/site/full/3.4.x/doc/Mail_SpamAssassin_PluginHandler.html (added)
+++ spamassassin/site/full/3.4.x/doc/Mail_SpamAssassin_PluginHandler.html Wed Apr 29 17:04:09 2015
@@ -0,0 +1,33 @@
+<?xml version="1.0" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Mail::SpamAssassin::PluginHandler - SpamAssassin plugin handler</title>
+<meta http-equiv="content-type" content="text/html; charset=utf-8" />
+<link rev="made" href="mailto:root@localhost" />
+</head>
+
+<body style="background-color: white">
+
+
+<!-- INDEX BEGIN -->
+<div name="index">
+<p><a name="__index__"></a></p>
+
+<ul>
+
+	<li><a href="#name">NAME</a></li>
+</ul>
+
+<hr name="index" />
+</div>
+<!-- INDEX END -->
+
+<p>
+</p>
+<h1><a name="name">NAME</a></h1>
+<p>Mail::SpamAssassin::PluginHandler - SpamAssassin plugin handler</p>
+
+</body>
+
+</html>

Added: spamassassin/site/full/3.4.x/doc/Mail_SpamAssassin_PluginHandler.txt
URL: http://svn.apache.org/viewvc/spamassassin/site/full/3.4.x/doc/Mail_SpamAssassin_PluginHandler.txt?rev=1676792&view=auto
==============================================================================
--- spamassassin/site/full/3.4.x/doc/Mail_SpamAssassin_PluginHandler.txt (added)
+++ spamassassin/site/full/3.4.x/doc/Mail_SpamAssassin_PluginHandler.txt Wed Apr 29 17:04:09 2015
@@ -0,0 +1,3 @@
+NAME
+    Mail::SpamAssassin::PluginHandler - SpamAssassin plugin handler
+

Added: spamassassin/site/full/3.4.x/doc/Mail_SpamAssassin_Plugin_ASN.html
URL: http://svn.apache.org/viewvc/spamassassin/site/full/3.4.x/doc/Mail_SpamAssassin_Plugin_ASN.html?rev=1676792&view=auto
==============================================================================
--- spamassassin/site/full/3.4.x/doc/Mail_SpamAssassin_Plugin_ASN.html (added)
+++ spamassassin/site/full/3.4.x/doc/Mail_SpamAssassin_Plugin_ASN.html Wed Apr 29 17:04:09 2015
@@ -0,0 +1,152 @@
+<?xml version="1.0" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Mail::SpamAssassin::Plugin::ASN - SpamAssassin plugin to look up the
+Autonomous System Number of the connecting IP address.</title>
+<meta http-equiv="content-type" content="text/html; charset=utf-8" />
+<link rev="made" href="mailto:root@localhost" />
+</head>
+
+<body style="background-color: white">
+
+
+<!-- INDEX BEGIN -->
+<div name="index">
+<p><a name="__index__"></a></p>
+
+<ul>
+
+	<li><a href="#name">NAME</a></li>
+	<li><a href="#synopsis">SYNOPSIS</a></li>
+	<li><a href="#description">DESCRIPTION</a></li>
+	<li><a href="#template_tags">TEMPLATE TAGS</a></li>
+	<li><a href="#configuration">CONFIGURATION</a></li>
+	<li><a href="#see_also">SEE ALSO</a></li>
+	<li><a href="#status">STATUS</a></li>
+	<li><a href="#administrator_settings">ADMINISTRATOR SETTINGS</a></li>
+</ul>
+
+<hr name="index" />
+</div>
+<!-- INDEX END -->
+
+<p>
+</p>
+<h1><a name="name">NAME</a></h1>
+<p>Mail::SpamAssassin::Plugin::ASN - SpamAssassin plugin to look up the
+Autonomous System Number (ASN) of the connecting IP address.</p>
+<p>
+</p>
+<hr />
+<h1><a name="synopsis">SYNOPSIS</a></h1>
+<pre>
+ loadplugin Mail::SpamAssassin::Plugin::ASN</pre>
+<pre>
+ asn_lookup asn.routeviews.org _ASN_ _ASNCIDR_</pre>
+<pre>
+ add_header all ASN _ASN_ _ASNCIDR_</pre>
+<p>
+</p>
+<hr />
+<h1><a name="description">DESCRIPTION</a></h1>
+<p>This plugin uses DNS lookups to the services of an external DNS zone such
+as at <code>http://www.routeviews.org/</code> to do the actual work. Please make
+sure that your use of the plugin does not overload their infrastructure -
+this generally means that <strong>you should not use this plugin in a
+high-volume environment</strong> or that you should use a local mirror of the
+zone (see <code>ftp://ftp.routeviews.org/dnszones/</code>).  Other similar zones
+may also be used.</p>
+<p>
+</p>
+<hr />
+<h1><a name="template_tags">TEMPLATE TAGS</a></h1>
+<p>This plugin allows you to create template tags containing the connecting
+IP's AS number and route info for that AS number.</p>
+<p>The default config will add a header field that looks like this:</p>
+<pre>
+ X-Spam-ASN: AS24940 213.239.192.0/18</pre>
+<p>where &quot;24940&quot; is the ASN and &quot;213.239.192.0/18&quot; is the route
+announced by that ASN where the connecting IP address came from.
+If the AS announces multiple networks (more/less specific), they will
+all be added to the <code>_ASNCIDR_</code> tag, separated by spaces, eg:</p>
+<pre>
+ X-Spam-ASN: AS1680 89.138.0.0/15 89.139.0.0/16</pre>
+<p>Note that the literal &quot;AS&quot; before the ASN in the _ASN_ tag is configurable
+through the <em>asn_prefix</em> directive and may be set to an empty string.</p>
+<p>
+</p>
+<hr />
+<h1><a name="configuration">CONFIGURATION</a></h1>
+<p>The standard ruleset contains a configuration that will add a header field
+containing ASN data to scanned messages.  The bayes tokenizer will use the
+added header field for bayes calculations, and thus affect which BAYES_* rule
+will trigger for a particular message.</p>
+<p><strong>Note</strong> that in most cases you should not score on the ASN data directly.
+Bayes learning will probably trigger on the _ASNCIDR_ tag, but probably not
+very well on the _ASN_ tag alone.</p>
+<p>
+</p>
+<hr />
+<h1><a name="see_also">SEE ALSO</a></h1>
+<p><a href="http://www.routeviews.org/">http://www.routeviews.org/</a> - all data regarding routing, ASNs, etc....</p>
+<p><a href="http://issues.apache.org/SpamAssassin/show_bug.cgi?id=4770">http://issues.apache.org/SpamAssassin/show_bug.cgi?id=4770</a> -
+SpamAssassin Issue #4770 concerning this plugin</p>
+<p>
+</p>
+<hr />
+<h1><a name="status">STATUS</a></h1>
+<p>No in-depth analysis of the usefulness of bayes tokenization of ASN data has
+been performed.</p>
+<p>
+</p>
+<hr />
+<h1><a name="administrator_settings">ADMINISTRATOR SETTINGS</a></h1>
+<dl>
+<dt><strong><a name="asn_lookup_asn_zone_example_com_asntag_asncidrtag" class="item">asn_lookup asn-zone.example.com [ _ASNTAG_ _ASNCIDRTAG_ ]</a></strong></dt>
+
+<dd>
+<p>Use this to lookup the ASN info in the specified zone for the first external
+IP address and add the AS number to the first specified tag and routing info
+to the second specified tag.</p>
+<p>If no tags are specified the AS number will be added to the _ASN_ tag and the
+routing info will be added to the _ASNCIDR_ tag.  You must specify either none
+or both of the tag names.  Tag names must start and end with an underscore.</p>
+<p>If two or more <em>asn_lookup</em>s use the same set of template tags, the results of
+their lookups will be appended to each other in the template tag values in no
+particular order.  Duplicate results will be omitted when combining results.
+In a similar fashion, you can also use the same template tag for both the AS
+number tag and the routing info tag.</p>
+<p>Examples:</p>
+<pre>
+  asn_lookup asn.routeviews.org</pre>
+<pre>
+  asn_lookup asn.routeviews.org _ASN_ _ASNCIDR_
+  asn_lookup myview.example.com _MYASN_ _MYASNCIDR_</pre>
+<pre>
+  asn_lookup asn.routeviews.org _COMBINEDASN_ _COMBINEDASNCIDR_
+  asn_lookup myview.example.com _COMBINEDASN_ _COMBINEDASNCIDR_</pre>
+<pre>
+  asn_lookup in1tag.example.net _ASNDATA_ _ASNDATA_</pre>
+</dd>
+</dl>
+<dl>
+<dt><strong><a name="clear_asn_lookups" class="item">clear_asn_lookups</a></strong></dt>
+
+</dl>
+<p>Removes any previously declared <em>asn_lookup</em> entries from a list of queries.</p>
+<dl>
+<dt><strong><a name="asn_prefix_prefix_string_default_as" class="item">asn_prefix 'prefix_string'       (default: 'AS')</a></strong></dt>
+
+<dd>
+<p>The string specified in the argument is prepended to each ASN when storing
+it as a tag. This prefix is rather redundant, but its default value 'AS'
+is kept for backward compatibility with versions of SpamAssassin earlier
+than 3.4.0. A sensible setting is an empty string. The argument may be (but
+need not be) enclosed in single or double quotes for clarity.</p>
+</dd>
+</dl>
+
+</body>
+
+</html>