qpsmtpd/README.plugins

352 lines
10 KiB
Plaintext
Raw Normal View History

#
# read this with 'perldoc README.plugins' ...
#
=head1 qpsmtpd plugin system; developer documentation
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_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.
=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_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 );
}