Patrick Welche <prlw1@cam.ac.uk>
[netbsd-mini2440.git] / external / ibm-public / postfix / dist / src / milter / milter.c
blob9a80b5405885a010d137d67a699be871679e85f4
1 /* $NetBSD$ */
3 /*++
4 /* NAME
5 /* milter 3
6 /* SUMMARY
7 /* generic MTA-side mail filter interface
8 /* SYNOPSIS
9 /* #include <milter.h>
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;
32 /* void milter_free(milters)
33 /* MILTERS *milters;
35 /* void milter_macro_callback(milters, mac_lookup, mac_context)
36 /* const char *(*mac_lookup)(const char *name, void *context);
37 /* void *mac_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)
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;
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;
63 /* const char *milter_disc_event(milters)
64 /* MILTERS *milters;
66 /* const char *milter_helo_event(milters, helo_name, esmtp_flag)
67 /* MILTERS *milters;
68 /* const char *helo_name;
69 /* int esmtp_flag;
71 /* const char *milter_mail_event(milters, argv)
72 /* MILTERS *milters;
73 /* const char **argv;
75 /* const char *milter_rcpt_event(milters, flags, argv)
76 /* MILTERS *milters;
77 /* int flags;
78 /* const char **argv;
80 /* const char *milter_data_event(milters)
81 /* MILTERS *milters;
83 /* const char *milter_unknown_event(milters, command)
84 /* MILTERS *milters;
85 /* const char *command;
87 /* const char *milter_other_event(milters)
88 /* MILTERS *milters;
90 /* const char *milter_message(milters, qfile, data_offset)
91 /* MILTERS *milters;
92 /* VSTREAM *qfile;
93 /* off_t data_offset;
95 /* const char *milter_abort(milters)
96 /* MILTERS *milters;
98 /* int milter_send(milters, fp)
99 /* MILTERS *milters;
100 /* VSTREAM *fp;
102 /* MILTERS *milter_receive(fp, count)
103 /* VSTREAM *fp;
104 /* int count;
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.
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
150 /* events.
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.
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.
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.
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.
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.
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
208 /* error.
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.
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 /*--*/
232 /* System library. */
234 #include <sys_defs.h>
236 /* Utility library. */
238 #include <msg.h>
239 #include <mymalloc.h>
240 #include <stringops.h>
241 #include <argv.h>
242 #include <attr.h>
244 /* Global library. */
246 #include <mail_proto.h>
247 #include <record.h>
248 #include <rec_type.h>
250 /* Postfix Milter library. */
252 #include <milter.h>
254 /* Application-specific. */
257 * SLMs.
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);
269 const char *value;
270 const char *name;
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);
281 myfree(saved_names);
282 return (argv);
285 /* milter_macro_callback - specify macro lookup */
287 void milter_macro_callback(MILTERS *milters,
288 const char *(*mac_lookup) (const char *, void *),
289 void *mac_context)
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,
307 void *chg_context)
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)
329 const char *resp;
330 MILTER *m;
331 ARGV *global_macros = 0;
332 ARGV *any_macros;
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)))
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);
350 if (global_macros)
351 argv_free(global_macros);
352 return (resp);
355 /* milter_helo_event - report helo event */
357 const char *milter_helo_event(MILTERS *milters, const char *helo_name,
358 int esmtp_flag)
360 const char *resp;
361 MILTER *m;
362 ARGV *global_macros = 0;
363 ARGV *any_macros;
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);
373 if (global_macros)
374 argv_free(global_macros);
375 return (resp);
378 /* milter_mail_event - report mail from event */
380 const char *milter_mail_event(MILTERS *milters, const char **argv)
382 const char *resp;
383 MILTER *m;
384 ARGV *global_macros = 0;
385 ARGV *any_macros;
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);
395 if (global_macros)
396 argv_free(global_macros);
397 return (resp);
400 /* milter_rcpt_event - report rcpt to event */
402 const char *milter_rcpt_event(MILTERS *milters, int flags, const char **argv)
404 const char *resp;
405 MILTER *m;
406 ARGV *global_macros = 0;
407 ARGV *any_macros;
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);
421 if (global_macros)
422 argv_free(global_macros);
423 return (resp);
426 /* milter_data_event - report data event */
428 const char *milter_data_event(MILTERS *milters)
430 const char *resp;
431 MILTER *m;
432 ARGV *global_macros = 0;
433 ARGV *any_macros;
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);
443 if (global_macros)
444 argv_free(global_macros);
445 return (resp);
448 /* milter_unknown_event - report unknown command */
450 const char *milter_unknown_event(MILTERS *milters, const char *command)
452 const char *resp;
453 MILTER *m;
454 ARGV *global_macros = 0;
455 ARGV *any_macros;
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);
465 if (global_macros)
466 argv_free(global_macros);
467 return (resp);
470 /* milter_other_event - other SMTP event */
472 const char *milter_other_event(MILTERS *milters)
474 const char *resp;
475 MILTER *m;
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);
484 /* milter_message - inspect message content */
486 const char *milter_message(MILTERS *milters, VSTREAM *fp, off_t data_offset)
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;
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);
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);
513 /* milter_abort - cancel message receiving state, all milters */
515 void milter_abort(MILTERS *milters)
517 MILTER *m;
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);
525 /* milter_disc_event - report client disconnect event to all milters */
527 void milter_disc_event(MILTERS *milters)
529 MILTER *m;
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);
537 /* milter_new - create milter list */
539 MILTERS *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)
547 MILTERS *milters;
548 MILTER *head = 0;
549 MILTER *tail = 0;
550 char *name;
551 MILTER *milter;
552 const char *sep = ", \t\r\n";
555 * Parse the milter list.
557 milters = (MILTERS *) mymalloc(sizeof(*milters));
558 if (names != 0) {
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,
565 milters);
566 if (head == 0) {
567 head = milter;
568 } else {
569 tail->next = milter;
571 tail = milter;
573 myfree(saved_names);
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);
588 /* milter_free - destroy all milters */
590 void milter_free(MILTERS *milters)
592 MILTER *m;
593 MILTER *next;
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);
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)
618 MILTER *m;
619 int status = 0;
620 int count = 0;
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).
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.
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);
640 * XXX Optimization: don't send or receive further information when there
641 * aren't any active filters.
643 if (count <= 0)
644 return (0);
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,
652 ATTR_TYPE_END);
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)
659 break;
662 * Over to you.
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);
672 return (0);
675 /* milter_receive - receive milters from stream */
677 MILTERS *milter_receive(VSTREAM *stream, int count)
679 MILTERS *milters;
680 MILTER *head = 0;
681 MILTER *tail = 0;
682 MILTER *milter = 0;
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,
696 NO_MACROS);
699 * XXX Optimization: don't send or receive further information when there
700 * aren't any active filters.
702 if (count <= 0)
703 return (milters);
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);
714 return (0);
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);
725 return (0);
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;
733 tail = milter;
737 * Over to you.
739 (void) attr_print(stream, ATTR_FLAG_NONE,
740 ATTR_TYPE_INT, MAIL_ATTR_STATUS, 0,
741 ATTR_TYPE_END);
742 return (milters);
745 #ifdef TEST
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>
755 #include <stdlib.h>
756 #include <string.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"
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);
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);
804 char *bufp;
805 char *cmd;
806 int ch;
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) {
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;
828 optind = OPTIND;
830 for (;;) {
831 const char *resp = 0;
832 ARGV *argv;
833 char **args;
835 if (istty) {
836 vstream_printf("- ");
837 vstream_fflush(VSTREAM_OUT);
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);
846 if (*bufp == '#')
847 continue;
848 cmd = mystrtok(&bufp, " ");
849 if (cmd == 0) {
850 usage();
851 continue;
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);
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;
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;
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;
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;
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;
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;
906 resp = milter_rcpt_event(milters, (const char **) args);
907 } else if (strcmp(cmd, "unknown") == 0 && argv->argc > 0) {
908 if (milters == 0) {
909 msg_warn("no milters");
910 continue;
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;
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;
924 milter_disc_event(milters);
925 } else {
926 usage();
928 if (resp != 0)
929 msg_info("%s", resp);
930 argv_free(argv);
932 if (milters != 0)
933 milter_free(milters);
934 vstring_free(inbuf);
935 return (0);
938 #endif