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