/
README.plugins
369 lines (237 loc) · 10.5 KB
/
README.plugins
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 );
}