7 /* generic MTA-side mail filter interface
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;
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;
32 /* void milter_free(milters)
35 /* void milter_macro_callback(milters, mac_lookup, mac_context)
36 /* const char *(*mac_lookup)(const char *name, void *context);
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)
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;
55 /* const char *milter_conn_event(milters, client_name, client_addr,
56 /* client_port, addr_family)
58 /* const char *client_name;
59 /* const char *client_addr;
60 /* const char *client_port;
63 /* const char *milter_disc_event(milters)
66 /* const char *milter_helo_event(milters, helo_name, esmtp_flag)
68 /* const char *helo_name;
71 /* const char *milter_mail_event(milters, argv)
75 /* const char *milter_rcpt_event(milters, flags, argv)
80 /* const char *milter_data_event(milters)
83 /* const char *milter_unknown_event(milters, command)
85 /* const char *command;
87 /* const char *milter_other_event(milters)
90 /* const char *milter_message(milters, qfile, data_offset)
95 /* const char *milter_abort(milters)
98 /* int milter_send(milters, fp)
102 /* MILTERS *milter_receive(fp, count)
106 /* int milter_dummy(milters, fp)
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.
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".
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).
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().
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().
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();
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
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
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.
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.
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.
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.
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
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
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.
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.
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
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.
214 /* milter_dummy() is like milter_send(), except that it sends
215 /* a dummy, but entirely valid, mail filter list.
217 /* milter8(3) Sendmail 8 Milter protocol
219 /* Panic: interface violation.
220 /* Fatal errors: memory allocation problem.
224 /* The Secure Mailer license must be distributed with this software.
227 /* IBM T.J. Watson Research
229 /* Yorktown Heights, NY 10598, USA
232 /* System library. */
234 #include <sys_defs.h>
236 /* Utility library. */
239 #include <mymalloc.h>
240 #include <stringops.h>
244 /* Global library. */
246 #include <mail_proto.h>
248 #include <rec_type.h>
250 /* Postfix Milter library. */
254 /* Application-specific. */
259 #define STR(x) vstring_str(x)
261 /* milter_macro_lookup - look up macros */
263 static ARGV
*milter_macro_lookup(MILTERS
*milters
, const char *macro_names
)
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);
272 while ((name
= mystrtok(&cp
, ", \t\r\n")) != 0) {
274 msg_info("%s: \"%s\"", myname
, name
);
275 if ((value
= milters
->mac_lookup(name
, milters
->mac_context
)) != 0) {
277 msg_info("%s: result \"%s\"", myname
, value
);
278 argv_add(argv
, name
, value
, (char *) 0);
285 /* milter_macro_callback - specify macro lookup */
287 void milter_macro_callback(MILTERS
*milters
,
288 const char *(*mac_lookup
) (const char *, void *),
291 milters
->mac_lookup
= mac_lookup
;
292 milters
->mac_context
= mac_context
;
295 /* milter_edit_callback - specify queue file edit call-back information */
297 void 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
,
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
;
321 /* milter_conn_event - report connect event */
323 const 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
)
331 ARGV
*global_macros
= 0;
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 : \
339 milter_macro_lookup(milters, milters->macros->member)))
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
);
351 argv_free(global_macros
);
355 /* milter_helo_event - report helo event */
357 const char *milter_helo_event(MILTERS
*milters
, const char *helo_name
,
362 ARGV
*global_macros
= 0;
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
);
374 argv_free(global_macros
);
378 /* milter_mail_event - report mail from event */
380 const char *milter_mail_event(MILTERS
*milters
, const char **argv
)
384 ARGV
*global_macros
= 0;
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
);
396 argv_free(global_macros
);
400 /* milter_rcpt_event - report rcpt to event */
402 const char *milter_rcpt_event(MILTERS
*milters
, int flags
, const char **argv
)
406 ARGV
*global_macros
= 0;
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) {
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
);
422 argv_free(global_macros
);
426 /* milter_data_event - report data event */
428 const char *milter_data_event(MILTERS
*milters
)
432 ARGV
*global_macros
= 0;
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
);
444 argv_free(global_macros
);
448 /* milter_unknown_event - report unknown command */
450 const char *milter_unknown_event(MILTERS
*milters
, const char *command
)
454 ARGV
*global_macros
= 0;
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
);
466 argv_free(global_macros
);
470 /* milter_other_event - other SMTP event */
472 const char *milter_other_event(MILTERS
*milters
)
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
);
484 /* milter_message - inspect message content */
486 const char *milter_message(MILTERS
*milters
, VSTREAM
*fp
, off_t data_offset
)
490 ARGV
*global_eoh_macros
= 0;
491 ARGV
*global_eod_macros
= 0;
492 ARGV
*any_eoh_macros
;
493 ARGV
*any_eod_macros
;
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
);
506 if (global_eoh_macros
)
507 argv_free(global_eoh_macros
);
508 if (global_eod_macros
)
509 argv_free(global_eod_macros
);
513 /* milter_abort - cancel message receiving state, all milters */
515 void milter_abort(MILTERS
*milters
)
520 msg_info("abort all milters");
521 for (m
= milters
->milter_list
; m
!= 0; m
= m
->next
)
525 /* milter_disc_event - report client disconnect event to all milters */
527 void milter_disc_event(MILTERS
*milters
)
532 msg_info("disconnect event to all milters");
533 for (m
= milters
->milter_list
; m
!= 0; m
= m
->next
)
537 /* milter_new - create milter list */
539 MILTERS
*milter_new(const char *names
,
543 const char *protocol
,
544 const char *def_action
,
545 MILTER_MACROS
*macros
)
552 const char *sep
= ", \t\r\n";
555 * Parse the milter list.
557 milters
= (MILTERS
*) mymalloc(sizeof(*milters
));
559 char *saved_names
= mystrdup(names
);
560 char *cp
= saved_names
;
562 while ((name
= mystrtok(&cp
, sep
)) != 0) {
563 milter
= milter8_create(name
, conn_timeout
, cmd_timeout
,
564 msg_timeout
, protocol
, def_action
,
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;
588 /* milter_free - destroy all milters */
590 void milter_free(MILTERS
*milters
)
596 msg_info("free all milters");
597 for (m
= milters
->milter_list
; m
!= 0; m
= next
)
598 next
= m
->next
, m
->free(m
);
600 milter_macros_free(milters
->macros
);
601 myfree((char *) milters
);
604 /* milter_dummy - send empty milter list */
606 int milter_dummy(MILTERS
*milters
, VSTREAM
*stream
)
608 MILTERS dummy
= *milters
;
610 dummy
.milter_list
= 0;
611 return (milter_send(&dummy
, stream
));
614 /* milter_send - send Milter instances over stream */
616 int milter_send(MILTERS
*milters
, VSTREAM
*stream
)
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
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.
634 for (m
= milters
->milter_list
; m
!= 0; m
= m
->next
)
637 (void) rec_fprintf(stream
, REC_TYPE_MILT_COUNT
, "%d", count
);
640 * XXX Optimization: don't send or receive further information when there
641 * aren't any active filters.
647 * Send the filter macro name lists.
649 (void) attr_print(stream
, ATTR_FLAG_MORE
,
650 ATTR_TYPE_FUNC
, milter_macros_print
,
651 (void *) milters
->macros
,
655 * Send the filter instances.
657 for (m
= milters
->milter_list
; m
!= 0; m
= m
->next
)
658 if (m
->active(m
) && (status
= m
->send(m
, stream
)) != 0)
665 || attr_scan(stream
, ATTR_FLAG_STRICT
,
666 ATTR_TYPE_INT
, MAIL_ATTR_STATUS
, &status
,
669 msg_warn("cannot send milters to service %s", VSTREAM_PATH(stream
));
675 /* milter_receive - receive milters from stream */
677 MILTERS
*milter_receive(VSTREAM
*stream
, int count
)
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.
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)
695 milters
= milter_new(NO_MILTERS
, NO_TIMEOUTS
, NO_PROTOCOL
, NO_ACTION
,
699 * XXX Optimization: don't send or receive further information when there
700 * aren't any active filters.
706 * Receive the global macro name lists.
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
);
718 * Receive the filters.
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
);
728 /* Coverity: milter_free() depends on milters->milter_list. */
729 milters
->milter_list
= head
= milter
;
739 (void) attr_print(stream
, ATTR_FLAG_NONE
,
740 ATTR_TYPE_INT
, MAIL_ATTR_STATUS
, 0,
748 * Proof-of-concept test program. This can be used interactively, but is
749 * typically used for automated regression tests from a script.
752 /* System library. */
754 #include <sys/socket.h>
758 /* Utility library. */
760 #include "msg_vstream.h"
761 #include "vstring_vstream.h"
763 /* Global library. */
765 #include <mail_params.h>
767 int var_milt_conn_time
= 10;
768 int var_milt_cmd_time
= 10;
769 int var_milt_msg_time
= 100;
770 char *var_milt_protocol
= DEF_MILT_PROTOCOL
;
771 char *var_milt_def_action
= DEF_MILT_DEF_ACTION
;
773 static void usage(void)
775 vstream_fprintf(VSTREAM_ERR
, "usage: \n"
776 " create names... create and connect\n"
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"
786 " free disconnect and destroy\n"
787 " connect name addr port family\n"
790 " mail from sender...\n"
791 " rcpt to recipient...\n"
794 " unknown command\n");
795 vstream_fflush(VSTREAM_ERR
);
798 int main(int argc
, char **argv
)
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);
807 int istty
= isatty(vstream_fileno(VSTREAM_IN
));
809 conn_macros
= helo_macros
= mail_macros
= rcpt_macros
= data_macros
810 = eoh_macros
= eod_macros
= unk_macros
= "";
812 msg_vstream_init(argv
[0], VSTREAM_ERR
);
813 while ((ch
= GETOPT(argc
, argv
, "V:v")) > 0) {
816 msg_fatal("usage: %s [-a action] [-p protocol] [-v]", argv
[0]);
818 var_milt_def_action
= optarg
;
821 var_milt_protocol
= optarg
;
831 const char *resp
= 0;
836 vstream_printf("- ");
837 vstream_fflush(VSTREAM_OUT
);
839 if (vstring_fgets_nonl(inbuf
, VSTREAM_IN
) <= 0)
841 bufp
= vstring_str(inbuf
);
843 vstream_printf("> %s\n", bufp
);
844 vstream_fflush(VSTREAM_OUT
);
848 cmd
= mystrtok(&bufp
, " ");
853 argv
= argv_split(bufp
, " ");
855 if (strcmp(cmd
, "create") == 0 && argv
->argc
== 1) {
857 msg_warn("deleting existing milters");
858 milter_free(milters
);
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) {
868 msg_warn("no milters");
871 milter_free(milters
);
873 } else if (strcmp(cmd
, "connect") == 0 && argv
->argc
== 4) {
875 msg_warn("no milters");
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
:
883 } else if (strcmp(cmd
, "helo") == 0 && argv
->argc
== 1) {
885 msg_warn("no milters");
888 resp
= milter_helo_event(milters
, args
[0], 0);
889 } else if (strcmp(cmd
, "ehlo") == 0 && argv
->argc
== 1) {
891 msg_warn("no milters");
894 resp
= milter_helo_event(milters
, args
[0], 1);
895 } else if (strcmp(cmd
, "mail") == 0 && argv
->argc
> 0) {
897 msg_warn("no milters");
900 resp
= milter_mail_event(milters
, (const char **) args
);
901 } else if (strcmp(cmd
, "rcpt") == 0 && argv
->argc
> 0) {
903 msg_warn("no milters");
906 resp
= milter_rcpt_event(milters
, (const char **) args
);
907 } else if (strcmp(cmd
, "unknown") == 0 && argv
->argc
> 0) {
909 msg_warn("no milters");
912 resp
= milter_unknown_event(milters
, args
[0]);
913 } else if (strcmp(cmd
, "data") == 0 && argv
->argc
== 0) {
915 msg_warn("no milters");
918 resp
= milter_data_event(milters
);
919 } else if (strcmp(cmd
, "disconnect") == 0 && argv
->argc
== 0) {
921 msg_warn("no milters");
924 milter_disc_event(milters
);
929 msg_info("%s", resp
);
933 milter_free(milters
);