Skip to content
This repository
Fetching contributors…

Cannot retrieve contributors at this time

file 366 lines (237 sloc) 10.781 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366
#
# 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.

=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_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 );
  }
Something went wrong with that request. Please try again.