Consolidate plugin documentation in docs/plugins.pod
* Include missing stuff from README.plugins into docs/plugins.pod * clear README.plugins to redirect to docs/plugins.pod
This commit is contained in:
parent
059771d31d
commit
8bce5f0278
381
README.plugins
381
README.plugins
@ -4,385 +4,10 @@
|
||||
|
||||
=head1 qpsmtpd plugin system; developer documentation
|
||||
|
||||
Plugin documentation is now in F<docs/plugins.pod>.
|
||||
|
||||
See the examples in plugins/ and ask questions on the qpsmtpd
|
||||
mailinglist; subscribe by sending mail to qpsmtpd-subscribe@perl.org.
|
||||
|
||||
=head1 General return codes
|
||||
|
||||
Each plugin must return an allowed constant for the hook and (usually)
|
||||
optionally a "message".
|
||||
|
||||
Generally all plugins for a hook are processed until one returns
|
||||
something other than "DECLINED".
|
||||
|
||||
Plugins are run in the order they are listed in the "plugins"
|
||||
configuration.
|
||||
|
||||
=over 4
|
||||
|
||||
=item OK
|
||||
|
||||
Action allowed
|
||||
|
||||
=item DENY
|
||||
|
||||
Action denied
|
||||
|
||||
=item DENYSOFT
|
||||
|
||||
Action denied; return a temporary rejection code (say 450 instead of 550).
|
||||
|
||||
=item DENY_DISCONNECT
|
||||
|
||||
Action denied; return a permanent rejection code and disconnect the client.
|
||||
Use this for "rude" clients. Note that you're not supposed to do this
|
||||
according to the SMTP specs, but bad clients don't listen sometimes.
|
||||
|
||||
=item DENYSOFT_DISCONNECT
|
||||
|
||||
Action denied; return a temporary rejection code and disconnect the client.
|
||||
|
||||
=item DECLINED
|
||||
|
||||
Plugin declined work; proceed as usual. This return code is _always_
|
||||
_allowed_ unless noted otherwise.
|
||||
|
||||
=item DONE
|
||||
|
||||
Finishing processing of the request. Usually used when the plugin
|
||||
sent the response to the client.
|
||||
|
||||
=back
|
||||
|
||||
See more detailed description for each hook below.
|
||||
|
||||
=head1 Hooks
|
||||
|
||||
=head2 pre-connection
|
||||
|
||||
Called by a controlling process (e.g. forkserver or Apache::Qpsmtpd) after
|
||||
accepting the remote server, but before beginning a new instance. Useful for
|
||||
load-management and rereading large config files at some frequency less than
|
||||
once per session. The hook doesn't have a predefined additional input value,
|
||||
but one can be passed as a hash of name/value pairs.
|
||||
|
||||
=head2 post-connection
|
||||
|
||||
Like pre-connection only it can be called after an instance has been
|
||||
completely finished (e.g. after the child process has ended in forkserver).
|
||||
The hook doesn't have a predefined additional input value, but one can be
|
||||
passed as a hash of name/value pairs.
|
||||
|
||||
|
||||
=head2 connect
|
||||
|
||||
Allowed return codes:
|
||||
|
||||
OK - Stop processing plugins, give the default response
|
||||
DECLINED - Process the next plugin
|
||||
DONE - Stop processing plugins and don't give the default response
|
||||
DENY - Return hard failure code and disconnect
|
||||
DENYSOFT - Return soft failure code and disconnect
|
||||
|
||||
Note: DENY_DISCONNECT and DENYSOFT_DISCONNECT are not supported here due to
|
||||
them having no meaning beyond what DENY and DENYSOFT already do.
|
||||
|
||||
|
||||
=head2 helo
|
||||
|
||||
Called on "helo" from the client.
|
||||
|
||||
DENY - Return a 550 code
|
||||
DENYSOFT - Return a 450 code
|
||||
DENY_DISCONNECT & DENYSOFT_DISCONNECT - as above but with disconnect
|
||||
DONE - Qpsmtpd won't do anything; the plugin sent the message
|
||||
DECLINED - Qpsmtpd will send the standard HELO message
|
||||
|
||||
|
||||
=head2 ehlo
|
||||
|
||||
Called on "ehlo" from the client.
|
||||
|
||||
DENY - Return a 550 code
|
||||
DENYSOFT - Return a 450 code
|
||||
DENY_DISCONNECT & DENYSOFT_DISCONNECT - as above but with disconnect
|
||||
DONE - Qpsmtpd won't do anything; the plugin sent the message
|
||||
DECLINED - Qpsmtpd will send the standard HELO message
|
||||
|
||||
|
||||
=head2 mail
|
||||
|
||||
Called right after the envelope sender address is passed. The plugin
|
||||
gets passed a Mail::Address object. Default is to allow the
|
||||
recipient.
|
||||
|
||||
Allowed return codes
|
||||
|
||||
OK - sender allowed
|
||||
DENY - Return a hard failure code
|
||||
DENYSOFT - Return a soft failure code
|
||||
DENY_DISCONNECT & DENYSOFT_DISCONNECT - as above but with disconnect
|
||||
DONE - skip further processing
|
||||
|
||||
|
||||
=head2 rcpt
|
||||
|
||||
Hook for the "rcpt" command. Defaults to deny the mail with a soft
|
||||
error code.
|
||||
|
||||
Allowed return codes
|
||||
|
||||
OK - recipient allowed
|
||||
DENY - Return a hard failure code
|
||||
DENYSOFT - Return a soft failure code
|
||||
DENY_DISCONNECT & DENYSOFT_DISCONNECT - as above but with disconnect
|
||||
DONE - skip further processing
|
||||
|
||||
|
||||
=head2 data
|
||||
|
||||
Hook for the "data" command. Defaults to '354, "go ahead"'.
|
||||
|
||||
DENY - Return a hard failure code
|
||||
DENYSOFT - Return a soft failure code
|
||||
DENY_DISCONNECT & DENYSOFT_DISCONNECT - as above but with disconnect
|
||||
DONE - Plugin took care of receiving data and calling the queue (not
|
||||
recommended)
|
||||
|
||||
|
||||
=head2 data_headers_end
|
||||
|
||||
Hook fires after all header lines of the message data has been received.
|
||||
Defaults to doing nothing, just continue processing. At this step,
|
||||
the sender is not waiting for a reply, but we can try and prevent him from
|
||||
sending the entire message by disconnecting immediately. (Although it is
|
||||
likely the packets are already in flight due to buffering and pipelining).
|
||||
|
||||
BE CAREFUL! If you drop the connection legal MTAs will retry again and again,
|
||||
spammers will probably not. This is not RFC compliant and can lead to
|
||||
an unpredictable mess. Use with caution.
|
||||
|
||||
Allowed return codes:
|
||||
|
||||
DENY_DISCONNECT - Return '554 Message denied' and disconnect
|
||||
DENYSOFT_DISCONNECT - Return '421 Message denied temporarily' and disconnect
|
||||
DECLINED - Do nothing
|
||||
|
||||
=head2 data_post
|
||||
|
||||
Hook after receiving all data; just before the message is queued.
|
||||
|
||||
DENY - Return a hard failure code
|
||||
DENYSOFT - Return a soft failure code
|
||||
DENY_DISCONNECT & DENYSOFT_DISCONNECT - as above but with disconnect
|
||||
DONE - skip further processing (message will not be queued)
|
||||
|
||||
All other codes and the message will be queued normally
|
||||
|
||||
|
||||
=head2 queue
|
||||
|
||||
Called on completion of the DATA command, after the data_post hook.
|
||||
|
||||
DONE - skip further processing (plugin gave response code)
|
||||
OK - Return success message
|
||||
DENY - Return hard failure code
|
||||
DENYSOFT - Return soft failure code
|
||||
|
||||
Any other code will return a soft failure code.
|
||||
|
||||
|
||||
=head2 quit
|
||||
|
||||
Called on the "quit" command.
|
||||
|
||||
Allowed return codes:
|
||||
|
||||
DONE
|
||||
|
||||
Works like the "connect" hook.
|
||||
|
||||
|
||||
=head2 unrecognized_command
|
||||
|
||||
Called when we get a command that isn't recognized.
|
||||
|
||||
DENY_DISCONNECT - Return 521 and disconnect the client
|
||||
DENY - Return 500
|
||||
DONE - Qpsmtpd won't do anything; the plugin responded
|
||||
Anything else - Return '500 Unrecognized command'
|
||||
|
||||
=head2 disconnect
|
||||
|
||||
Called just before we shutdown a connection.
|
||||
|
||||
The return code is ignored. If a plugin returns anything but DECLINED
|
||||
the following plugins will not be run (like with all other hooks).
|
||||
|
||||
=head2 deny
|
||||
|
||||
Called when another hook returns DENY or DENYSOFT. First parameter is
|
||||
the previous hook return code; the second parameter the message the
|
||||
hook returned.
|
||||
|
||||
Returning DONE or OK will stop the next deny hook from being run.
|
||||
DECLINED will make qpsmtpd run the remaining configured deny hooks.
|
||||
|
||||
=head2 vrfy
|
||||
|
||||
Hook for the "VRFY" command. Defaults to returning a message telling
|
||||
the user to just try sending the message.
|
||||
|
||||
Allowed return codes:
|
||||
|
||||
OK - Recipient Exists
|
||||
DENY - Return a hard failure code
|
||||
DONE - Return nothing and move on
|
||||
Anything Else - Return a 252
|
||||
|
||||
=head1 Return Values and Notes
|
||||
|
||||
Insert stuff here about how:
|
||||
|
||||
- if we're in a transaction, the results of a callback are stored
|
||||
in
|
||||
$self->transaction->notes( $code->{name})->{"hook_$hook"}->{return}
|
||||
|
||||
- if we're in a connection, store things in the connection notes instead.
|
||||
|
||||
=head2 received_line
|
||||
|
||||
If you wish to provide your own Received header line, do it here.
|
||||
|
||||
The hook is passed the following extra parameters (beyond $self and $transaction):
|
||||
|
||||
- $smtp - the SMTP type used (e.g. "SMTP" or "ESMTP").
|
||||
- $auth - the Auth header additionals.
|
||||
- $sslinfo - information about SSL for the header.
|
||||
|
||||
You're free to use or discard any of the above.
|
||||
|
||||
Allowed return codes:
|
||||
|
||||
OK, $string - use this string for the Received header.
|
||||
Anything Else - use the standard Received header.
|
||||
|
||||
|
||||
|
||||
=head1 Include Files
|
||||
|
||||
(put more about how the $Include stuff works here)
|
||||
|
||||
With the $Include stuff you order using the filename of the plugin.d
|
||||
file. So if you have a plugin called xyz but want it to come early on,
|
||||
you call it's config file 00_xyz, but that file still refers to the
|
||||
plugin called xyz.
|
||||
|
||||
=head1 Temporary Files
|
||||
|
||||
The temporary file and directory functions can be used for plugin specific
|
||||
workfiles and will automatically be deleted at the end of the current
|
||||
transaction.
|
||||
|
||||
=over 4
|
||||
|
||||
=item temp_file()
|
||||
|
||||
Returns a unique name of a file located in the default spool directory, but
|
||||
does not open that file (i.e. it is the name not a file handle).
|
||||
|
||||
=item temp_dir()
|
||||
|
||||
Returns the name of a unique directory located in the default spool
|
||||
directory, after creating the directory with 0700 rights. If you need a
|
||||
directory with different rights (say for an antivirus daemon), you will
|
||||
need to use the base function $self->qp->temp_dir() which takes a single
|
||||
parameter for the permissions requested (see L<mkdir> for details). A
|
||||
directory created like this will B<not> be deleted when the transaction is
|
||||
ended.
|
||||
|
||||
=item spool_dir()
|
||||
|
||||
Returns the configured system-wide spool directory.
|
||||
|
||||
=back
|
||||
|
||||
=head1 Naming Conventions
|
||||
|
||||
Plugins should be written using standard named hook subroutines. This
|
||||
allows them to be overloaded and extended easily.
|
||||
|
||||
Because some of our callback names have characters invalid in
|
||||
subroutine names, they must be translated. The current translation
|
||||
routine is: C< s/\W/_/g; >
|
||||
|
||||
=head2 Naming Map
|
||||
|
||||
hook method
|
||||
---------- ------------
|
||||
config hook_config
|
||||
queue hook_queue
|
||||
data hook_data
|
||||
data_headers_end hook_data_headers_end
|
||||
data_post hook_data_post
|
||||
quit hook_quit
|
||||
rcpt hook_rcpt
|
||||
mail hook_mail
|
||||
ehlo hook_ehlo
|
||||
helo hook_helo
|
||||
auth hook_auth
|
||||
auth-plain hook_auth_plain
|
||||
auth-login hook_auth_login
|
||||
auth-cram-md5 hook_auth_cram_md5
|
||||
connect hook_connect
|
||||
reset_transaction hook_reset_transaction
|
||||
unrecognized_command hook_unrecognized_command
|
||||
|
||||
=head1 Register
|
||||
|
||||
If you choose not to use the default naming convention, you need to
|
||||
register the hooks in your plugin. You do this with the C< register >
|
||||
method call on the plugin object.
|
||||
|
||||
sub register {
|
||||
my ($self, $qp) = @_;
|
||||
|
||||
$self->register_hook('mail', 'mail_handler');
|
||||
$self->register_hook('rcpt', 'rcpt_handler');
|
||||
$self->register_hook('disconnect', 'disconnect_handler');
|
||||
}
|
||||
|
||||
sub mail_handler { ... }
|
||||
sub rcpt_handler { ... }
|
||||
sub disconnect_handler { ... }
|
||||
|
||||
A single plugin can register as many hooks as it wants, and can
|
||||
register a hook multiple times.
|
||||
|
||||
The C< register > method is also often used for initialization and
|
||||
reading configuration.
|
||||
|
||||
=head1 Init
|
||||
|
||||
The 'init' method is the first method called after a plugin is
|
||||
loaded. It's mostly for inheritance, below.
|
||||
|
||||
=head1 Inheritance
|
||||
|
||||
Instead of modifying @ISA directly in your plugin, use the
|
||||
C< isa_plugin > method from the init subroutine.
|
||||
|
||||
# rcpt_ok_child
|
||||
sub init {
|
||||
my ($self, $qp) = @_;
|
||||
$self->isa_plugin('rcpt_ok');
|
||||
}
|
||||
|
||||
sub hook_rcpt {
|
||||
my ($self, $transaction, $recipient) = @_;
|
||||
# do something special here...
|
||||
$self->SUPER::hook_rcpt( $transaction, $recipient );
|
||||
}
|
||||
|
||||
|
||||
=cut
|
||||
|
||||
|
@ -111,7 +111,8 @@ is named C<hook_$hookname> is automagically added.
|
||||
Plugins should be written using standard named hook subroutines. This
|
||||
allows them to be overloaded and extended easily. Because some of the
|
||||
callback names have characters invalid in subroutine names , they must be
|
||||
translated. The current translation routine is C<s/\W/_/g;>. If you choose
|
||||
translated. The current translation routine is C<s/\W/_/g;>, see
|
||||
L</Hook - Subroutine translations> for more info. If you choose
|
||||
not to use the default naming convention, you need to register the hooks in
|
||||
your plugin in the C<register()> method (see below) with the
|
||||
C<register_hook()> call on the plugin object.
|
||||
@ -136,6 +137,40 @@ not for F<qpsmtpd> started by (x)inetd or tcpserver.
|
||||
|
||||
In short: don't do it if you want to write portable plugins.
|
||||
|
||||
=head2 Hook - Subroutine translations
|
||||
|
||||
As mentioned above, the hook name needs to be translated to a valid perl
|
||||
C<sub> name. This is done like
|
||||
|
||||
($sub = $hook) =~ s/\W/_/g;
|
||||
$sub = "hook_$sub";
|
||||
|
||||
Some examples follow, for a complete list of available (documented ;-))
|
||||
hooks (method names), use something like
|
||||
|
||||
$ perl -lne 'print if s/^=head2\s+(hook_\S+)/$1/' docs/plugins.pod
|
||||
|
||||
=head3 Translation table
|
||||
|
||||
hook method
|
||||
---------- ------------
|
||||
config hook_config
|
||||
queue hook_queue
|
||||
data hook_data
|
||||
data_post hook_data_post
|
||||
quit hook_quit
|
||||
rcpt hook_rcpt
|
||||
mail hook_mail
|
||||
ehlo hook_ehlo
|
||||
helo hook_helo
|
||||
auth hook_auth
|
||||
auth-plain hook_auth_plain
|
||||
auth-login hook_auth_login
|
||||
auth-cram-md5 hook_auth_cram_md5
|
||||
connect hook_connect
|
||||
reset_transaction hook_reset_transaction
|
||||
unrecognized_command hook_unrecognized_command
|
||||
|
||||
=head2 Inheritance
|
||||
|
||||
Inheriting methods from other plugins is an advanced topic. You can alter
|
||||
@ -740,6 +775,45 @@ Arguments are
|
||||
# $auth - the Auth header additionals.
|
||||
# $sslinfo - information about SSL for the header.
|
||||
|
||||
=head2 data_headers_end
|
||||
|
||||
This hook fires after all header lines of the message data has been received.
|
||||
Defaults to doing nothing, just continue processing. At this step,
|
||||
the sender is not waiting for a reply, but we can try and prevent him from
|
||||
sending the entire message by disconnecting immediately. (Although it is
|
||||
likely the packets are already in flight due to buffering and pipelining).
|
||||
|
||||
B<NOTE:> BE CAREFUL! If you drop the connection legal MTAs will retry again
|
||||
and again, spammers will probably not. This is not RFC compliant and can lead
|
||||
to an unpredictable mess. Use with caution.
|
||||
|
||||
Why this head may be useful for you, see
|
||||
L<http://www.nntp.perl.org/group/perl.qpsmtpd/2009/02/msg8502.html>, ff.
|
||||
|
||||
Allowed return codes:
|
||||
|
||||
=over 4
|
||||
|
||||
=item DENY_DISCONNECT
|
||||
|
||||
Return B<554 Message denied> and disconnect
|
||||
|
||||
=item DENYSOFT_DISCONNECT
|
||||
|
||||
Return B<421 Message denied temporarily> and disconnect
|
||||
|
||||
=item DECLINED
|
||||
|
||||
Do nothing
|
||||
|
||||
=back
|
||||
|
||||
Arguments:
|
||||
|
||||
my ($self, $transaction) = @_;
|
||||
|
||||
B<FIXME:> check arguments
|
||||
|
||||
=head2 hook_data_post
|
||||
|
||||
The C<data_post> hook is called after the client sent the final C<.\r\n>
|
||||
@ -1570,7 +1644,16 @@ should be configured to run I<last>, like B<rcpt_ok>.
|
||||
"Too many relaying attempts");
|
||||
}
|
||||
|
||||
=head2 TBC... :-)
|
||||
=head2 Results of other hooks
|
||||
|
||||
B<NOTE:> just copied from README.plugins
|
||||
|
||||
If we're in a transaction, the results of a callback are stored in
|
||||
|
||||
$self->transaction->notes( $code->{name})->{"hook_$hook"}->{return}
|
||||
|
||||
If we're in a connection, store things in the connection notes instead.
|
||||
B<FIXME>: does the above (regarding connection notes) work?
|
||||
|
||||
=cut
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user