1/*++
2/* NAME
3/*	milter 3
4/* SUMMARY
5/*	generic MTA-side mail filter interface
6/* SYNOPSIS
7/*	#include <milter.h>
8/*
9/*	MILTERS	*milter_create(milter_names, conn_timeout, cmd_timeout,
10/*					msg_timeout, protocol, def_action,
11/*					conn_macros, helo_macros,
12/*					mail_macros, rcpt_macros,
13/*					data_macros, eoh_macros,
14/*					eod_macros, unk_macros)
15/*	const char *milter_names;
16/*	int	conn_timeout;
17/*	int	cmd_timeout;
18/*	int	msg_timeout;
19/*	const char *protocol;
20/*	const char *def_action;
21/*	const char *conn_macros;
22/*	const char *helo_macros;
23/*	const char *mail_macros;
24/*	const char *rcpt_macrps;
25/*	const char *data_macros;
26/*	const char *eoh_macros;
27/*	const char *eod_macros;
28/*	const char *unk_macros;
29/*
30/*	void	milter_free(milters)
31/*	MILTERS	*milters;
32/*
33/*	void	milter_macro_callback(milters, mac_lookup, mac_context)
34/*	const char *(*mac_lookup)(const char *name, void *context);
35/*	void	*mac_context;
36/*
37/*	void	milter_edit_callback(milters, add_header, upd_header,
38/*					ins_header, del_header, chg_from,
39/*					add_rcpt, add_rcpt_par, del_rcpt,
40/*					repl_body, context)
41/*	MILTERS	*milters;
42/*	MILTER_ADD_HEADER_FN add_header;
43/*	MILTER_EDIT_HEADER_FN upd_header;
44/*	MILTER_EDIT_HEADER_FN ins_header;
45/*	MILTER_DEL_HEADER_FN del_header;
46/*	MILTER_EDIT_FROM_FN chg_from;
47/*	MILTER_EDIT_RCPT_FN add_rcpt;
48/*	MILTER_EDIT_RCPT_PAR_FN add_rcpt_par;
49/*	MILTER_EDIT_RCPT_FN del_rcpt;
50/*	MILTER_EDIT_BODY_FN repl_body;
51/*	void	*context;
52/*
53/*	const char *milter_conn_event(milters, client_name, client_addr,
54/*					client_port, addr_family)
55/*	MILTERS	*milters;
56/*	const char *client_name;
57/*	const char *client_addr;
58/*	const char *client_port;
59/*	int	addr_family;
60/*
61/*	const char *milter_disc_event(milters)
62/*	MILTERS	*milters;
63/*
64/*	const char *milter_helo_event(milters, helo_name, esmtp_flag)
65/*	MILTERS	*milters;
66/*	const char *helo_name;
67/*	int	esmtp_flag;
68/*
69/*	const char *milter_mail_event(milters, argv)
70/*	MILTERS	*milters;
71/*	const char **argv;
72/*
73/*	const char *milter_rcpt_event(milters, flags, argv)
74/*	MILTERS	*milters;
75/*	int	flags;
76/*	const char **argv;
77/*
78/*	const char *milter_data_event(milters)
79/*	MILTERS	*milters;
80/*
81/*	const char *milter_unknown_event(milters, command)
82/*	MILTERS	*milters;
83/*	const char *command;
84/*
85/*	const char *milter_other_event(milters)
86/*	MILTERS	*milters;
87/*
88/*	const char *milter_message(milters, qfile, data_offset)
89/*	MILTERS	*milters;
90/*	VSTREAM *qfile;
91/*	off_t	data_offset;
92/*
93/*	const char *milter_abort(milters)
94/*	MILTERS	*milters;
95/*
96/*	int	milter_send(milters, fp)
97/*	MILTERS	*milters;
98/*	VSTREAM *fp;
99/*
100/*	MILTERS	*milter_receive(fp, count)
101/*	VSTREAM	*fp;
102/*	int	count;
103/*
104/*	int	milter_dummy(milters, fp)
105/*	MILTERS	*milters;
106/*	VSTREAM *fp;
107/* DESCRIPTION
108/*	The functions in this module manage one or more milter (mail
109/*	filter) clients. Currently, only the Sendmail 8 filter
110/*	protocol is supported.
111/*
112/*	The functions that inspect content or envelope commands
113/*	return either an SMTP reply ([45]XX followed by enhanced
114/*	status code and text), "D" (discard), "H" (quarantine),
115/*	"S" (shutdown connection), or a null pointer, which means
116/*	"no news is good news".
117/*
118/*	milter_create() instantiates the milter clients specified
119/*	with the milter_names argument.  The conn_macros etc.
120/*	arguments specify the names of macros that are sent to the
121/*	mail filter applications upon a connect etc. event. This
122/*	function should be called during process initialization,
123/*	before entering a chroot jail. The timeout parameters specify
124/*	time limits for the completion of the specified request
125/*	classes. The protocol parameter specifies a protocol version
126/*	and optional extensions.  When the milter application is
127/*	unavailable, the milter client will go into a suitable error
128/*	state as specified with the def_action parameter (i.e.
129/*	reject, tempfail or accept all subsequent events).
130/*
131/*	milter_free() disconnects from the milter instances that
132/*	are still opened, and destroys the data structures created
133/*	by milter_create(). This function is safe to call at any
134/*	point after milter_create().
135/*
136/*	milter_macro_callback() specifies a call-back function and
137/*	context for macro lookup. This function must be called
138/*	before milter_conn_event().
139/*
140/*	milter_edit_callback() specifies call-back functions and
141/*	context for editing the queue file after the end-of-data
142/*	is received. This function must be called before milter_message();
143/*
144/*	milter_conn_event() reports an SMTP client connection event
145/*	to the specified milter instances, after sending the macros
146/*	specified with the milter_create() conn_macros argument.
147/*	This function must be called before reporting any other
148/*	events.
149/*
150/*	milter_disc_event() reports an SMTP client disconnection
151/*	event to the specified milter instances. No events can
152/*	reported after this call. To simplify usage, redundant calls
153/*	of this function are NO-OPs and don't raise a run-time
154/*	error.
155/*
156/*	milter_helo_event() reports a HELO or EHLO event to the
157/*	specified milter instances, after sending the macros that
158/*	were specified with the milter_create() helo_macros argument.
159/*
160/*	milter_mail_event() reports a MAIL FROM event to the specified
161/*	milter instances, after sending the macros that were specified
162/*	with the milter_create() mail_macros argument.
163/*
164/*	milter_rcpt_event() reports an RCPT TO event to the specified
165/*	milter instances, after sending the macros that were specified
166/*	with the milter_create() rcpt_macros argument. The flags
167/*	argument supports the following:
168/* .IP MILTER_FLAG_WANT_RCPT_REJ
169/*	When this flag is cleared, invoke all milters.  When this
170/*	flag is set, invoke only milters that want to receive
171/*	rejected recipients; with Sendmail V8 Milters, {rcpt_mailer}
172/*	is set to "error", {rcpt_host} is set to an enhanced status
173/*	code, and {rcpt_addr} is set to descriptive text.
174/* .PP
175/*	milter_data_event() reports a DATA event to the specified
176/*	milter instances, after sending the macros that were specified
177/*	with the milter_create() data_macros argument.
178/*
179/*	milter_unknown_event() reports an unknown command event to
180/*	the specified milter instances, after sending the macros
181/*	that were specified with the milter_create() unk_macros
182/*	argument.
183/*
184/*	milter_other_event() returns the current default mail filter
185/*	reply for the current SMTP connection state; it does not
186/*	change milter states. A null pointer result means that all
187/*	is well. This function can be used for SMTP commands such
188/*	as AUTH, STARTTLS that don't have their own milter event
189/*	routine.
190/*
191/*	milter_message() sends the message header and body to the
192/*	to the specified milter instances, and sends the macros
193/*	specified with the milter_create() eoh_macros after the
194/*	message header, and with the eod_macros argument at
195/*	the end.  Each milter sees the result of any changes made
196/*	by a preceding milter. This function must be called with
197/*	as argument an open Postfix queue file.
198/*
199/*	milter_abort() cancels a mail transaction in progress.  To
200/*	simplify usage, redundant calls of this function are NO-OPs
201/*	and don't raise a run-time error.
202/*
203/*	milter_send() sends a list of mail filters over the specified
204/*	stream. When given a null list pointer, a "no filter"
205/*	indication is sent.  The result is non-zero in case of
206/*	error.
207/*
208/*	milter_receive() receives the specified number of mail
209/*	filters over the specified stream. The result is a null
210/*	pointer when no milters were sent, or when an error happened.
211/*
212/*	milter_dummy() is like milter_send(), except that it sends
213/*	a dummy, but entirely valid, mail filter list.
214/* SEE ALSO
215/*	milter8(3) Sendmail 8 Milter protocol
216/* DIAGNOSTICS
217/*	Panic: interface violation.
218/*	Fatal errors: memory allocation problem.
219/* LICENSE
220/* .ad
221/* .fi
222/*	The Secure Mailer license must be distributed with this software.
223/* AUTHOR(S)
224/*	Wietse Venema
225/*	IBM T.J. Watson Research
226/*	P.O. Box 704
227/*	Yorktown Heights, NY 10598, USA
228/*--*/
229
230/* System library. */
231
232#include <sys_defs.h>
233
234/* Utility library. */
235
236#include <msg.h>
237#include <mymalloc.h>
238#include <stringops.h>
239#include <argv.h>
240#include <attr.h>
241
242/* Global library. */
243
244#include <mail_proto.h>
245#include <record.h>
246#include <rec_type.h>
247
248/* Postfix Milter library. */
249
250#include <milter.h>
251
252/* Application-specific. */
253
254 /*
255  * SLMs.
256  */
257#define STR(x)	vstring_str(x)
258
259/* milter_macro_lookup - look up macros */
260
261static ARGV *milter_macro_lookup(MILTERS *milters, const char *macro_names)
262{
263    const char *myname = "milter_macro_lookup";
264    char   *saved_names = mystrdup(macro_names);
265    char   *cp = saved_names;
266    ARGV   *argv = argv_alloc(10);
267    const char *value;
268    const char *name;
269
270    while ((name = mystrtok(&cp, ", \t\r\n")) != 0) {
271	if (msg_verbose)
272	    msg_info("%s: \"%s\"", myname, name);
273	if ((value = milters->mac_lookup(name, milters->mac_context)) != 0) {
274	    if (msg_verbose)
275		msg_info("%s: result \"%s\"", myname, value);
276	    argv_add(argv, name, value, (char *) 0);
277	}
278    }
279    myfree(saved_names);
280    return (argv);
281}
282
283/* milter_macro_callback - specify macro lookup */
284
285void    milter_macro_callback(MILTERS *milters,
286		           const char *(*mac_lookup) (const char *, void *),
287			              void *mac_context)
288{
289    milters->mac_lookup = mac_lookup;
290    milters->mac_context = mac_context;
291}
292
293/* milter_edit_callback - specify queue file edit call-back information */
294
295void    milter_edit_callback(MILTERS *milters,
296			             MILTER_ADD_HEADER_FN add_header,
297			             MILTER_EDIT_HEADER_FN upd_header,
298			             MILTER_EDIT_HEADER_FN ins_header,
299			             MILTER_DEL_HEADER_FN del_header,
300			             MILTER_EDIT_FROM_FN chg_from,
301			             MILTER_EDIT_RCPT_FN add_rcpt,
302			             MILTER_EDIT_RCPT_PAR_FN add_rcpt_par,
303			             MILTER_EDIT_RCPT_FN del_rcpt,
304			             MILTER_EDIT_BODY_FN repl_body,
305			             void *chg_context)
306{
307    milters->add_header = add_header;
308    milters->upd_header = upd_header;
309    milters->ins_header = ins_header;
310    milters->del_header = del_header;
311    milters->chg_from = chg_from;
312    milters->add_rcpt = add_rcpt;
313    milters->add_rcpt_par = add_rcpt_par;
314    milters->del_rcpt = del_rcpt;
315    milters->repl_body = repl_body;
316    milters->chg_context = chg_context;
317}
318
319/* milter_conn_event - report connect event */
320
321const char *milter_conn_event(MILTERS *milters,
322			              const char *client_name,
323			              const char *client_addr,
324			              const char *client_port,
325			              unsigned addr_family)
326{
327    const char *resp;
328    MILTER *m;
329    ARGV   *global_macros = 0;
330    ARGV   *any_macros;
331
332#define MILTER_MACRO_EVAL(global_macros, m, milters, member) \
333	((m->macros && m->macros->member[0]) ? \
334	    milter_macro_lookup(milters, m->macros->member) : \
335		global_macros ? global_macros : \
336		    (global_macros = \
337		         milter_macro_lookup(milters, milters->macros->member)))
338
339    if (msg_verbose)
340	msg_info("report connect to all milters");
341    for (resp = 0, m = milters->milter_list; resp == 0 && m != 0; m = m->next) {
342	any_macros = MILTER_MACRO_EVAL(global_macros, m, milters, conn_macros);
343	resp = m->conn_event(m, client_name, client_addr, client_port,
344			     addr_family, any_macros);
345	if (any_macros != global_macros)
346	    argv_free(any_macros);
347    }
348    if (global_macros)
349	argv_free(global_macros);
350    return (resp);
351}
352
353/* milter_helo_event - report helo event */
354
355const char *milter_helo_event(MILTERS *milters, const char *helo_name,
356			              int esmtp_flag)
357{
358    const char *resp;
359    MILTER *m;
360    ARGV   *global_macros = 0;
361    ARGV   *any_macros;
362
363    if (msg_verbose)
364	msg_info("report helo to all milters");
365    for (resp = 0, m = milters->milter_list; resp == 0 && m != 0; m = m->next) {
366	any_macros = MILTER_MACRO_EVAL(global_macros, m, milters, helo_macros);
367	resp = m->helo_event(m, helo_name, esmtp_flag, any_macros);
368	if (any_macros != global_macros)
369	    argv_free(any_macros);
370    }
371    if (global_macros)
372	argv_free(global_macros);
373    return (resp);
374}
375
376/* milter_mail_event - report mail from event */
377
378const char *milter_mail_event(MILTERS *milters, const char **argv)
379{
380    const char *resp;
381    MILTER *m;
382    ARGV   *global_macros = 0;
383    ARGV   *any_macros;
384
385    if (msg_verbose)
386	msg_info("report sender to all milters");
387    for (resp = 0, m = milters->milter_list; resp == 0 && m != 0; m = m->next) {
388	any_macros = MILTER_MACRO_EVAL(global_macros, m, milters, mail_macros);
389	resp = m->mail_event(m, argv, any_macros);
390	if (any_macros != global_macros)
391	    argv_free(any_macros);
392    }
393    if (global_macros)
394	argv_free(global_macros);
395    return (resp);
396}
397
398/* milter_rcpt_event - report rcpt to event */
399
400const char *milter_rcpt_event(MILTERS *milters, int flags, const char **argv)
401{
402    const char *resp;
403    MILTER *m;
404    ARGV   *global_macros = 0;
405    ARGV   *any_macros;
406
407    if (msg_verbose)
408	msg_info("report recipient to all milters (flags=0x%x)", flags);
409    for (resp = 0, m = milters->milter_list; resp == 0 && m != 0; m = m->next) {
410	if ((flags & MILTER_FLAG_WANT_RCPT_REJ) == 0
411	    || (m->flags & MILTER_FLAG_WANT_RCPT_REJ) != 0) {
412	    any_macros =
413		MILTER_MACRO_EVAL(global_macros, m, milters, rcpt_macros);
414	    resp = m->rcpt_event(m, argv, any_macros);
415	    if (any_macros != global_macros)
416		argv_free(any_macros);
417	}
418    }
419    if (global_macros)
420	argv_free(global_macros);
421    return (resp);
422}
423
424/* milter_data_event - report data event */
425
426const char *milter_data_event(MILTERS *milters)
427{
428    const char *resp;
429    MILTER *m;
430    ARGV   *global_macros = 0;
431    ARGV   *any_macros;
432
433    if (msg_verbose)
434	msg_info("report data to all milters");
435    for (resp = 0, m = milters->milter_list; resp == 0 && m != 0; m = m->next) {
436	any_macros = MILTER_MACRO_EVAL(global_macros, m, milters, data_macros);
437	resp = m->data_event(m, any_macros);
438	if (any_macros != global_macros)
439	    argv_free(any_macros);
440    }
441    if (global_macros)
442	argv_free(global_macros);
443    return (resp);
444}
445
446/* milter_unknown_event - report unknown command */
447
448const char *milter_unknown_event(MILTERS *milters, const char *command)
449{
450    const char *resp;
451    MILTER *m;
452    ARGV   *global_macros = 0;
453    ARGV   *any_macros;
454
455    if (msg_verbose)
456	msg_info("report unknown command to all milters");
457    for (resp = 0, m = milters->milter_list; resp == 0 && m != 0; m = m->next) {
458	any_macros = MILTER_MACRO_EVAL(global_macros, m, milters, unk_macros);
459	resp = m->unknown_event(m, command, any_macros);
460	if (any_macros != global_macros)
461	    argv_free(any_macros);
462    }
463    if (global_macros)
464	argv_free(global_macros);
465    return (resp);
466}
467
468/* milter_other_event - other SMTP event */
469
470const char *milter_other_event(MILTERS *milters)
471{
472    const char *resp;
473    MILTER *m;
474
475    if (msg_verbose)
476	msg_info("query milter states for other event");
477    for (resp = 0, m = milters->milter_list; resp == 0 && m != 0; m = m->next)
478	resp = m->other_event(m);
479    return (resp);
480}
481
482/* milter_message - inspect message content */
483
484const char *milter_message(MILTERS *milters, VSTREAM *fp, off_t data_offset)
485{
486    const char *resp;
487    MILTER *m;
488    ARGV   *global_eoh_macros = 0;
489    ARGV   *global_eod_macros = 0;
490    ARGV   *any_eoh_macros;
491    ARGV   *any_eod_macros;
492
493    if (msg_verbose)
494	msg_info("inspect content by all milters");
495    for (resp = 0, m = milters->milter_list; resp == 0 && m != 0; m = m->next) {
496	any_eoh_macros = MILTER_MACRO_EVAL(global_eoh_macros, m, milters, eoh_macros);
497	any_eod_macros = MILTER_MACRO_EVAL(global_eod_macros, m, milters, eod_macros);
498	resp = m->message(m, fp, data_offset, any_eoh_macros, any_eod_macros);
499	if (any_eoh_macros != global_eoh_macros)
500	    argv_free(any_eoh_macros);
501	if (any_eod_macros != global_eod_macros)
502	    argv_free(any_eod_macros);
503    }
504    if (global_eoh_macros)
505	argv_free(global_eoh_macros);
506    if (global_eod_macros)
507	argv_free(global_eod_macros);
508    return (resp);
509}
510
511/* milter_abort - cancel message receiving state, all milters */
512
513void    milter_abort(MILTERS *milters)
514{
515    MILTER *m;
516
517    if (msg_verbose)
518	msg_info("abort all milters");
519    for (m = milters->milter_list; m != 0; m = m->next)
520	m->abort(m);
521}
522
523/* milter_disc_event - report client disconnect event to all milters */
524
525void    milter_disc_event(MILTERS *milters)
526{
527    MILTER *m;
528
529    if (msg_verbose)
530	msg_info("disconnect event to all milters");
531    for (m = milters->milter_list; m != 0; m = m->next)
532	m->disc_event(m);
533}
534
535/* milter_new - create milter list */
536
537MILTERS *milter_new(const char *names,
538		            int conn_timeout,
539		            int cmd_timeout,
540		            int msg_timeout,
541		            const char *protocol,
542		            const char *def_action,
543		            MILTER_MACROS *macros)
544{
545    MILTERS *milters;
546    MILTER *head = 0;
547    MILTER *tail = 0;
548    char   *name;
549    MILTER *milter;
550    const char *sep = ", \t\r\n";
551
552    /*
553     * Parse the milter list.
554     */
555    milters = (MILTERS *) mymalloc(sizeof(*milters));
556    if (names != 0) {
557	char   *saved_names = mystrdup(names);
558	char   *cp = saved_names;
559
560	while ((name = mystrtok(&cp, sep)) != 0) {
561	    milter = milter8_create(name, conn_timeout, cmd_timeout,
562				    msg_timeout, protocol, def_action,
563				    milters);
564	    if (head == 0) {
565		head = milter;
566	    } else {
567		tail->next = milter;
568	    }
569	    tail = milter;
570	}
571	myfree(saved_names);
572    }
573    milters->milter_list = head;
574    milters->mac_lookup = 0;
575    milters->mac_context = 0;
576    milters->macros = macros;
577    milters->add_header = 0;
578    milters->upd_header = milters->ins_header = 0;
579    milters->del_header = 0;
580    milters->add_rcpt = milters->del_rcpt = 0;
581    milters->repl_body = 0;
582    milters->chg_context = 0;
583    return (milters);
584}
585
586/* milter_free - destroy all milters */
587
588void    milter_free(MILTERS *milters)
589{
590    MILTER *m;
591    MILTER *next;
592
593    if (msg_verbose)
594	msg_info("free all milters");
595    for (m = milters->milter_list; m != 0; m = next)
596	next = m->next, m->free(m);
597    if (milters->macros)
598	milter_macros_free(milters->macros);
599    myfree((char *) milters);
600}
601
602/* milter_dummy - send empty milter list */
603
604int     milter_dummy(MILTERS *milters, VSTREAM *stream)
605{
606    MILTERS dummy = *milters;
607
608    dummy.milter_list = 0;
609    return (milter_send(&dummy, stream));
610}
611
612/* milter_send - send Milter instances over stream */
613
614int     milter_send(MILTERS *milters, VSTREAM *stream)
615{
616    MILTER *m;
617    int     status = 0;
618    int     count = 0;
619
620    /*
621     * XXX Optimization: send only the filters that are actually used in the
622     * remote process. No point sending a filter that looks at HELO commands
623     * to a cleanup server. For now we skip only the filters that are known
624     * to be disabled (either in global error state or in global accept
625     * state).
626     *
627     * XXX We must send *some* information, even when there are no active
628     * filters, otherwise the cleanup server would try to apply its own
629     * non_smtpd_milters settings.
630     */
631    if (milters != 0)
632	for (m = milters->milter_list; m != 0; m = m->next)
633	    if (m->active(m))
634		count++;
635    (void) rec_fprintf(stream, REC_TYPE_MILT_COUNT, "%d", count);
636
637    if (msg_verbose)
638	msg_info("send %d milters", count);
639
640    /*
641     * XXX Optimization: don't send or receive further information when there
642     * aren't any active filters.
643     */
644    if (count <= 0)
645	return (0);
646
647    /*
648     * Send the filter macro name lists.
649     */
650    (void) attr_print(stream, ATTR_FLAG_MORE,
651		      ATTR_TYPE_FUNC, milter_macros_print,
652		      (void *) milters->macros,
653		      ATTR_TYPE_END);
654
655    /*
656     * Send the filter instances.
657     */
658    for (m = milters->milter_list; m != 0; m = m->next)
659	if (m->active(m) && (status = m->send(m, stream)) != 0)
660	    break;
661
662    /*
663     * Over to you.
664     */
665    if (status != 0
666	|| attr_scan(stream, ATTR_FLAG_STRICT,
667		     ATTR_TYPE_INT, MAIL_ATTR_STATUS, &status,
668		     ATTR_TYPE_END) != 1
669	|| status != 0) {
670	msg_warn("cannot send milters to service %s", VSTREAM_PATH(stream));
671	return (-1);
672    }
673    return (0);
674}
675
676/* milter_receive - receive milters from stream */
677
678MILTERS *milter_receive(VSTREAM *stream, int count)
679{
680    MILTERS *milters;
681    MILTER *head = 0;
682    MILTER *tail = 0;
683    MILTER *milter = 0;
684
685    if (msg_verbose)
686	msg_info("receive %d milters", count);
687
688    /*
689     * XXX We must instantiate a MILTERS structure even when the sender has
690     * no active filters, otherwise the cleanup server would try to use its
691     * own non_smtpd_milters settings.
692     */
693#define NO_MILTERS	((char *) 0)
694#define NO_TIMEOUTS	0, 0, 0
695#define NO_PROTOCOL	((char *) 0)
696#define NO_ACTION	((char *) 0)
697#define NO_MACROS	((MILTER_MACROS *) 0)
698
699    milters = milter_new(NO_MILTERS, NO_TIMEOUTS, NO_PROTOCOL, NO_ACTION,
700			 NO_MACROS);
701
702    /*
703     * XXX Optimization: don't send or receive further information when there
704     * aren't any active filters.
705     */
706    if (count <= 0)
707	return (milters);
708
709    /*
710     * Receive the global macro name lists.
711     */
712    milters->macros = milter_macros_alloc(MILTER_MACROS_ALLOC_ZERO);
713    if (attr_scan(stream, ATTR_FLAG_STRICT | ATTR_FLAG_MORE,
714		  ATTR_TYPE_FUNC, milter_macros_scan,
715		  (void *) milters->macros,
716		  ATTR_TYPE_END) != 1) {
717	milter_free(milters);
718	return (0);
719    }
720
721    /*
722     * Receive the filters.
723     */
724    for (; count > 0; count--) {
725	if ((milter = milter8_receive(stream, milters)) == 0) {
726	    msg_warn("cannot receive milters via service %s socket",
727		     VSTREAM_PATH(stream));
728	    milter_free(milters);
729	    return (0);
730	}
731	if (head == 0) {
732	    /* Coverity: milter_free() depends on milters->milter_list. */
733	    milters->milter_list = head = milter;
734	} else {
735	    tail->next = milter;
736	}
737	tail = milter;
738    }
739
740    /*
741     * Over to you.
742     */
743    (void) attr_print(stream, ATTR_FLAG_NONE,
744		      ATTR_TYPE_INT, MAIL_ATTR_STATUS, 0,
745		      ATTR_TYPE_END);
746    return (milters);
747}
748
749#ifdef TEST
750
751 /*
752  * Proof-of-concept test program. This can be used interactively, but is
753  * typically used for automated regression tests from a script.
754  */
755
756/* System library. */
757
758#include <sys/socket.h>
759#include <stdlib.h>
760#include <string.h>
761
762/* Utility library. */
763
764#include "msg_vstream.h"
765#include "vstring_vstream.h"
766
767/* Global library. */
768
769#include <mail_params.h>
770
771int     var_milt_conn_time = 10;
772int     var_milt_cmd_time = 10;
773int     var_milt_msg_time = 100;
774char   *var_milt_protocol = DEF_MILT_PROTOCOL;
775char   *var_milt_def_action = DEF_MILT_DEF_ACTION;
776
777static void usage(void)
778{
779    vstream_fprintf(VSTREAM_ERR, "usage: \n"
780		    "	create names...		create and connect\n"
781#if 0
782		    "	conn_macros names...	define connect macros\n"
783		    "	helo_macros names...	define helo command macros\n"
784		    "	mail_macros names...	define mail command macros\n"
785		    "	rcpt_macros names...	define rcpt command macros\n"
786		    "	data_macros names...	define data command macros\n"
787		    "	unk_macros names...	unknown command macros\n"
788		    "	message_macros names...	define message macros\n"
789#endif
790		    "	free			disconnect and destroy\n"
791		    "	connect name addr port family\n"
792		    "	helo hostname\n"
793		    "	ehlo hostname\n"
794		    "	mail from sender...\n"
795		    "	rcpt to recipient...\n"
796		    "	data\n"
797		    "	disconnect\n"
798		    "	unknown command\n");
799    vstream_fflush(VSTREAM_ERR);
800}
801
802int     main(int argc, char **argv)
803{
804    MILTERS *milters = 0;
805    char   *conn_macros, *helo_macros, *mail_macros, *rcpt_macros;
806    char   *data_macros, *eoh_macros, *eod_macros, *unk_macros;
807    VSTRING *inbuf = vstring_alloc(100);
808    char   *bufp;
809    char   *cmd;
810    int     ch;
811    int     istty = isatty(vstream_fileno(VSTREAM_IN));
812
813    conn_macros = helo_macros = mail_macros = rcpt_macros = data_macros
814	= eoh_macros = eod_macros = unk_macros = "";
815
816    msg_vstream_init(argv[0], VSTREAM_ERR);
817    while ((ch = GETOPT(argc, argv, "V:v")) > 0) {
818	switch (ch) {
819	default:
820	    msg_fatal("usage: %s [-a action] [-p protocol] [-v]", argv[0]);
821	case 'a':
822	    var_milt_def_action = optarg;
823	    break;
824	case 'p':
825	    var_milt_protocol = optarg;
826	    break;
827	case 'v':
828	    msg_verbose++;
829	    break;
830	}
831    }
832    optind = OPTIND;
833
834    for (;;) {
835	const char *resp = 0;
836	ARGV   *argv;
837	char  **args;
838
839	if (istty) {
840	    vstream_printf("- ");
841	    vstream_fflush(VSTREAM_OUT);
842	}
843	if (vstring_fgets_nonl(inbuf, VSTREAM_IN) <= 0)
844	    break;
845	bufp = vstring_str(inbuf);
846	if (!istty) {
847	    vstream_printf("> %s\n", bufp);
848	    vstream_fflush(VSTREAM_OUT);
849	}
850	if (*bufp == '#')
851	    continue;
852	cmd = mystrtok(&bufp, " ");
853	if (cmd == 0) {
854	    usage();
855	    continue;
856	}
857	argv = argv_split(bufp, " ");
858	args = argv->argv;
859	if (strcmp(cmd, "create") == 0 && argv->argc == 1) {
860	    if (milters != 0) {
861		msg_warn("deleting existing milters");
862		milter_free(milters);
863	    }
864	    milters = milter_create(args[0], var_milt_conn_time,
865				    var_milt_cmd_time, var_milt_msg_time,
866				    var_milt_protocol, var_milt_def_action,
867				    conn_macros, helo_macros, mail_macros,
868				    rcpt_macros, data_macros, eoh_macros,
869				    eod_macros, unk_macros);
870	} else if (strcmp(cmd, "free") == 0 && argv->argc == 0) {
871	    if (milters == 0) {
872		msg_warn("no milters");
873		continue;
874	    }
875	    milter_free(milters);
876	    milters = 0;
877	} else if (strcmp(cmd, "connect") == 0 && argv->argc == 4) {
878	    if (milters == 0) {
879		msg_warn("no milters");
880		continue;
881	    }
882	    resp = milter_conn_event(milters, args[0], args[1], args[2],
883				 strcmp(args[3], "AF_INET") == 0 ? AF_INET :
884			       strcmp(args[3], "AF_INET6") == 0 ? AF_INET6 :
885				 strcmp(args[3], "AF_UNIX") == 0 ? AF_UNIX :
886				     AF_UNSPEC);
887	} else if (strcmp(cmd, "helo") == 0 && argv->argc == 1) {
888	    if (milters == 0) {
889		msg_warn("no milters");
890		continue;
891	    }
892	    resp = milter_helo_event(milters, args[0], 0);
893	} else if (strcmp(cmd, "ehlo") == 0 && argv->argc == 1) {
894	    if (milters == 0) {
895		msg_warn("no milters");
896		continue;
897	    }
898	    resp = milter_helo_event(milters, args[0], 1);
899	} else if (strcmp(cmd, "mail") == 0 && argv->argc > 0) {
900	    if (milters == 0) {
901		msg_warn("no milters");
902		continue;
903	    }
904	    resp = milter_mail_event(milters, (const char **) args);
905	} else if (strcmp(cmd, "rcpt") == 0 && argv->argc > 0) {
906	    if (milters == 0) {
907		msg_warn("no milters");
908		continue;
909	    }
910	    resp = milter_rcpt_event(milters, 0, (const char **) args);
911	} else if (strcmp(cmd, "unknown") == 0 && argv->argc > 0) {
912	    if (milters == 0) {
913		msg_warn("no milters");
914		continue;
915	    }
916	    resp = milter_unknown_event(milters, args[0]);
917	} else if (strcmp(cmd, "data") == 0 && argv->argc == 0) {
918	    if (milters == 0) {
919		msg_warn("no milters");
920		continue;
921	    }
922	    resp = milter_data_event(milters);
923	} else if (strcmp(cmd, "disconnect") == 0 && argv->argc == 0) {
924	    if (milters == 0) {
925		msg_warn("no milters");
926		continue;
927	    }
928	    milter_disc_event(milters);
929	} else {
930	    usage();
931	}
932	if (resp != 0)
933	    msg_info("%s", resp);
934	argv_free(argv);
935    }
936    if (milters != 0)
937	milter_free(milters);
938    vstring_free(inbuf);
939    return (0);
940}
941
942#endif
943