119304Speter/* $NetBSD: cleanup_api.c,v 1.4 2022/10/08 16:12:45 christos Exp $ */ 219304Speter 319304Speter/*++ 419304Speter/* NAME 519304Speter/* cleanup_api 3 619304Speter/* SUMMARY 719304Speter/* cleanup callable interface, message processing 819304Speter/* SYNOPSIS 919304Speter/* #include "cleanup.h" 1019304Speter/* 1119304Speter/* CLEANUP_STATE *cleanup_open(src) 1219304Speter/* VSTREAM *src; 13254225Speter/* 1419304Speter/* void cleanup_control(state, flags) 1519304Speter/* CLEANUP_STATE *state; 1619304Speter/* int flags; 1719304Speter/* 18254225Speter/* void CLEANUP_RECORD(state, type, buf, len) 1919304Speter/* CLEANUP_STATE *state; 2019304Speter/* int type; 2119304Speter/* char *buf; 2219304Speter/* int len; 2319304Speter/* 2419304Speter/* int cleanup_flush(state) 2519304Speter/* CLEANUP_STATE *state; 26254225Speter/* 27170371Srafan/* int cleanup_free(state) 28254225Speter/* CLEANUP_STATE *state; 2919304Speter/* DESCRIPTION 3019304Speter/* This module implements a callable interface to the cleanup service 3119304Speter/* for processing one message and for writing it to queue file. 3219304Speter/* For a description of the cleanup service, see cleanup(8). 3319304Speter/* 3419304Speter/* cleanup_open() creates a new queue file and performs other 3519304Speter/* per-message initialization. The result is a handle that should be 3619304Speter/* given to the cleanup_control(), cleanup_record(), cleanup_flush() 3719304Speter/* and cleanup_free() routines. The name of the queue file is in the 3819304Speter/* queue_id result structure member. 3919304Speter/* 4019304Speter/* cleanup_control() processes per-message flags specified by the caller. 4119304Speter/* These flags control the handling of data errors, and must be set 4219304Speter/* before processing the first message record. 4319304Speter/* .IP CLEANUP_FLAG_BOUNCE 4419304Speter/* The cleanup server is responsible for returning undeliverable 4519304Speter/* mail (too many hops, message too large) to the sender. 4619304Speter/* .IP CLEANUP_FLAG_BCC_OK 4719304Speter/* It is OK to add automatic BCC recipient addresses. 4819304Speter/* .IP CLEANUP_FLAG_FILTER 49254225Speter/* Enable header/body filtering. This should be enabled only with mail 5019304Speter/* that enters Postfix, not with locally forwarded mail or with bounce 5119304Speter/* messages. 52254225Speter/* .IP CLEANUP_FLAG_MILTER 5319304Speter/* Enable Milter applications. This should be enabled only with mail 5419304Speter/* that enters Postfix, not with locally forwarded mail or with bounce 5519304Speter/* messages. 5619304Speter/* .IP CLEANUP_FLAG_MAP_OK 57254225Speter/* Enable canonical and virtual mapping, and address masquerading. 5819304Speter/* .PP 5919304Speter/* For convenience the CLEANUP_FLAG_MASK_EXTERNAL macro specifies 6019304Speter/* the options that are normally needed for mail that enters 61254225Speter/* Postfix from outside, and CLEANUP_FLAG_MASK_INTERNAL specifies 62254225Speter/* the options that are normally needed for internally generated or 63254225Speter/* forwarded mail. 64254225Speter/* 6519304Speter/* CLEANUP_RECORD() is a macro that processes one message record, 6619304Speter/* that copies the result to the queue file, and that maintains a 6719304Speter/* little state machine. The last record in a valid message has type 6819304Speter/* REC_TYPE_END. In order to find out if a message is corrupted, 6919304Speter/* the caller is encouraged to test the CLEANUP_OUT_OK(state) macro. 7019304Speter/* The result is false when further message processing is futile. 71254225Speter/* In that case, it is safe to call cleanup_flush() immediately. 72254225Speter/* 7319304Speter/* cleanup_flush() closes a queue file. In case of any errors, 7419304Speter/* the file is removed. The result value is non-zero in case of 7519304Speter/* problems. In some cases a human-readable text can be found in 7619304Speter/* the state->reason member. In all other cases, use cleanup_strerror() 7719304Speter/* to translate the result into human-readable text. 7819304Speter/* 7919304Speter/* cleanup_free() destroys its argument. 8019304Speter/* .IP CLEANUP_FLAG_SMTPUTF8 8119304Speter/* Request SMTPUTF8 support when delivering mail. 8219304Speter/* .IP CLEANUP_FLAG_AUTOUTF8 8319304Speter/* Autodetection: request SMTPUTF8 support if the message 8419304Speter/* contains an UTF8 message header, sender, or recipient. 8519304Speter/* DIAGNOSTICS 8619304Speter/* Problems and transactions are logged to \fBsyslogd\fR(8) 8719304Speter/* or \fBpostlogd\fR(8). 8819304Speter/* SEE ALSO 8919304Speter/* cleanup(8) cleanup service description. 9019304Speter/* cleanup_init(8) cleanup callable interface, initialization 9119304Speter/* LICENSE 9219304Speter/* .ad 9319304Speter/* .fi 9419304Speter/* The Secure Mailer license must be distributed with this software. 9519304Speter/* AUTHOR(S) 9619304Speter/* Wietse Venema 9719304Speter/* IBM T.J. Watson Research 9819304Speter/* P.O. Box 704 9919304Speter/* Yorktown Heights, NY 10598, USA 100254225Speter/* 101254225Speter/* Wietse Venema 102254225Speter/* Google, Inc. 10319304Speter/* 111 8th Avenue 104254225Speter/* New York, NY 10011, USA 105254225Speter/*--*/ 10619304Speter 10719304Speter/* System library. */ 10819304Speter 10919304Speter#include <sys_defs.h> 11019304Speter#include <errno.h> 11119304Speter 11219304Speter/* Utility library. */ 11319304Speter 11419304Speter#include <msg.h> 11519304Speter#include <vstring.h> 11619304Speter#include <mymalloc.h> 11719304Speter 11819304Speter/* Global library. */ 11919304Speter 12019304Speter#include <cleanup_user.h> 12119304Speter#include <mail_queue.h> 12219304Speter#include <mail_proto.h> 12319304Speter#include <bounce.h> 12419304Speter#include <mail_params.h> 12519304Speter#include <mail_stream.h> 12619304Speter#include <mail_flow.h> 12719304Speter#include <rec_type.h> 12819304Speter#include <smtputf8.h> 12919304Speter 13019304Speter/* Milter library. */ 13119304Speter 13219304Speter#include <milter.h> 13319304Speter 13419304Speter/* Application-specific. */ 13519304Speter 13619304Speter#include "cleanup.h" 137254225Speter 13819304Speter/* cleanup_open - open queue file and initialize */ 13919304Speter 14019304SpeterCLEANUP_STATE *cleanup_open(VSTREAM *src) 14119304Speter{ 14219304Speter CLEANUP_STATE *state; 14319304Speter static const char *log_queues[] = { 14419304Speter MAIL_QUEUE_DEFER, 14519304Speter MAIL_QUEUE_BOUNCE, 14619304Speter MAIL_QUEUE_TRACE, 14719304Speter 0, 14819304Speter }; 14919304Speter const char **cpp; 15019304Speter 15119304Speter /* 15219304Speter * Initialize private state. 15319304Speter */ 15419304Speter state = cleanup_state_alloc(src); 15519304Speter 15619304Speter /* 15719304Speter * Open the queue file. Save the queue file name in a global variable, so 15819304Speter * that the runtime error handler can clean up in case of problems. 15919304Speter * 16019304Speter * XXX For now, a lot of detail is frozen that could be more useful if it 16119304Speter * were made configurable. 16219304Speter */ 16319304Speter state->queue_name = mystrdup(MAIL_QUEUE_INCOMING); 16419304Speter state->handle = mail_stream_file(state->queue_name, 16519304Speter MAIL_CLASS_PUBLIC, var_queue_service, 0); 16619304Speter state->dst = state->handle->stream; 16719304Speter cleanup_path = mystrdup(VSTREAM_PATH(state->dst)); 16819304Speter state->queue_id = mystrdup(state->handle->id); 16919304Speter if (msg_verbose) 17019304Speter msg_info("cleanup_open: open %s", cleanup_path); 17119304Speter 17219304Speter /* 17319304Speter * If there is a time to get rid of spurious log files, this is it. The 17419304Speter * down side is that this costs performance for every message, while the 17519304Speter * probability of spurious log files is quite low. 17619304Speter * 17719304Speter * XXX The defer logfile is deleted when the message is moved into the 17819304Speter * active queue. We must also remove it now, otherwise mailq produces 17919304Speter * nonsense. 18019304Speter */ 18119304Speter for (cpp = log_queues; *cpp; cpp++) { 18219304Speter if (mail_queue_remove(*cpp, state->queue_id) == 0) 18319304Speter msg_warn("%s: removed spurious %s log", *cpp, state->queue_id); 18419304Speter else if (errno != ENOENT) 18519304Speter msg_fatal("%s: remove %s log: %m", *cpp, state->queue_id); 18619304Speter } 187254225Speter return (state); 18819304Speter} 18919304Speter 19019304Speter/* cleanup_control - process client options */ 19119304Speter 19219304Spetervoid cleanup_control(CLEANUP_STATE *state, int flags) 19319304Speter{ 19419304Speter 19519304Speter /* 19619304Speter * If the client requests us to do the bouncing in case of problems, 19719304Speter * throw away the input only in case of real show-stopper errors, such as 19819304Speter * unrecognizable data (which should never happen) or insufficient space 19919304Speter * for the queue file (which will happen occasionally). Otherwise, 20019304Speter * discard input after any lethal error. See the CLEANUP_OUT_OK() macro 20119304Speter * definition. 20219304Speter */ 20319304Speter if (msg_verbose) 20419304Speter msg_info("cleanup flags = %s", cleanup_strflags(flags)); 20519304Speter if ((state->flags = flags) & CLEANUP_FLAG_BOUNCE) { 20619304Speter state->err_mask = CLEANUP_STAT_MASK_INCOMPLETE; 20719304Speter } else { 20819304Speter state->err_mask = ~0; 20919304Speter } 21019304Speter if (state->flags & CLEANUP_FLAG_SMTPUTF8) 21119304Speter state->smtputf8 = SMTPUTF8_FLAG_REQUESTED; 21219304Speter} 21319304Speter 21419304Speter/* cleanup_flush - finish queue file */ 21519304Speter 21619304Speterint cleanup_flush(CLEANUP_STATE *state) 21719304Speter{ 21819304Speter int status; 21919304Speter char *junk; 22019304Speter VSTRING *trace_junk; 22119304Speter 22219304Speter /* 22319304Speter * Raise these errors only if we examined all queue file records. 22419304Speter */ 22519304Speter if (CLEANUP_OUT_OK(state)) { 22619304Speter if (state->recip == 0) 22719304Speter state->errs |= CLEANUP_STAT_RCPT; 22819304Speter if ((state->flags & CLEANUP_FLAG_END_SEEN) == 0) 22919304Speter state->errs |= CLEANUP_STAT_BAD; 23019304Speter } 23119304Speter 23219304Speter /* 23319304Speter * Status sanitization. Always report success when the discard flag was 23419304Speter * raised by some user-specified access rule. 23519304Speter */ 23619304Speter if (state->flags & CLEANUP_FLAG_DISCARD) 23719304Speter state->errs = 0; 23819304Speter 23919304Speter /* 24019304Speter * Apply external mail filter. 24119304Speter * 24219304Speter * XXX Include test for a built-in action to tempfail this message. 24319304Speter */ 24419304Speter if (CLEANUP_MILTER_OK(state)) { 24519304Speter if (state->milters) 24619304Speter cleanup_milter_inspect(state, state->milters); 24719304Speter else if (cleanup_milters) { 24819304Speter cleanup_milter_emul_data(state, cleanup_milters); 24919304Speter if (CLEANUP_MILTER_OK(state)) 250254225Speter cleanup_milter_inspect(state, cleanup_milters); 25119304Speter } 25219304Speter } 25319304Speter 25419304Speter /* 25519304Speter * Update the preliminary message size and count fields with the actual 25619304Speter * values. 25719304Speter */ 25819304Speter if (CLEANUP_OUT_OK(state)) 25919304Speter cleanup_final(state); 26019304Speter 26119304Speter /* 26219304Speter * If there was an error that requires us to generate a bounce message 26319304Speter * (mail submitted with the Postfix sendmail command, mail forwarded by 26419304Speter * the local(8) delivery agent, or mail re-queued with "postsuper -r"), 26519304Speter * send a bounce notification, reset the error flags in case of success, 26619304Speter * and request deletion of the incoming queue file and of the 26719304Speter * optional DSN SUCCESS records from virtual alias expansion. 26819304Speter * 26919304Speter * XXX It would make no sense to knowingly report success after we already 27019304Speter * have bounced all recipients, especially because the information in the 27119304Speter * DSN SUCCESS notice is completely redundant compared to the information 27219304Speter * in the bounce notice (however, both may be incomplete when the queue 27319304Speter * file size would exceed the safety limit). 27419304Speter * 27519304Speter * An alternative is to keep the DSN SUCCESS records and to delegate bounce 27619304Speter * notification to the queue manager, just like we already delegate 27719304Speter * success notification. This requires that we leave the undeliverable 27819304Speter * message in the incoming queue; versions up to 20050726 did exactly 27919304Speter * that. Unfortunately, this broke with over-size queue files, because 28019304Speter * the queue manager cannot handle incomplete queue files (and it should 28119304Speter * not try to do so). 28219304Speter */ 28319304Speter#define CAN_BOUNCE() \ 28419304Speter ((state->errs & CLEANUP_STAT_MASK_CANT_BOUNCE) == 0 \ 28519304Speter && state->sender != 0 \ 28619304Speter && (state->flags & CLEANUP_FLAG_BOUNCE) != 0) 28719304Speter 28819304Speter if (state->errs != 0 && CAN_BOUNCE()) 28919304Speter cleanup_bounce(state); 29019304Speter 29119304Speter /* 29219304Speter * Optionally, place the message on hold, but only if the message was 29319304Speter * received successfully and only if it's not being discarded for other 29419304Speter * reasons. This involves renaming the queue file before "finishing" it 29519304Speter * (or else the queue manager would grab it too early) and updating our 29619304Speter * own idea of the queue file name for error recovery and for error 29719304Speter * reporting purposes. 29819304Speter * 29919304Speter * XXX Include test for a built-in action to tempfail this message. 30019304Speter */ 30119304Speter if (state->errs == 0 && (state->flags & CLEANUP_FLAG_DISCARD) == 0) { 30219304Speter if ((state->flags & CLEANUP_FLAG_HOLD) != 0 30319304Speter#ifdef DELAY_ACTION 30419304Speter || state->defer_delay > 0 30519304Speter#endif 30619304Speter ) { 30719304Speter myfree(state->queue_name); 30819304Speter#ifdef DELAY_ACTION 30919304Speter state->queue_name = mystrdup((state->flags & CLEANUP_FLAG_HOLD) ? 31019304Speter MAIL_QUEUE_HOLD : MAIL_QUEUE_DEFERRED); 31119304Speter#else 31219304Speter state->queue_name = mystrdup(MAIL_QUEUE_HOLD); 31319304Speter#endif 31419304Speter mail_stream_ctl(state->handle, 31519304Speter CA_MAIL_STREAM_CTL_QUEUE(state->queue_name), 31619304Speter CA_MAIL_STREAM_CTL_CLASS((char *) 0), 31719304Speter CA_MAIL_STREAM_CTL_SERVICE((char *) 0), 31819304Speter#ifdef DELAY_ACTION 31919304Speter CA_MAIL_STREAM_CTL_DELAY(state->defer_delay), 32019304Speter#endif 32119304Speter CA_MAIL_STREAM_CTL_END); 32219304Speter junk = cleanup_path; 32319304Speter cleanup_path = mystrdup(VSTREAM_PATH(state->handle->stream)); 32419304Speter myfree(junk); 32519304Speter 32619304Speter /* 32719304Speter * XXX: When delivering to a non-incoming queue, do not consume 32819304Speter * in_flow tokens. Unfortunately we can't move the code that 32919304Speter * consumes tokens until after the mail is received, because that 33019304Speter * would increase the risk of duplicate deliveries (RFC 1047). 33119304Speter */ 33219304Speter (void) mail_flow_put(1); 33319304Speter } 33419304Speter state->errs = mail_stream_finish(state->handle, (VSTRING *) 0); 33519304Speter } else { 33619304Speter 33719304Speter /* 33819304Speter * XXX: When discarding mail, should we consume in_flow tokens? See 33919304Speter * also the comments above for mail that is placed on hold. 34019304Speter */ 34119304Speter#if 0 34219304Speter (void) mail_flow_put(1); 34319304Speter#endif 34419304Speter mail_stream_cleanup(state->handle); 34519304Speter } 34619304Speter state->handle = 0; 34719304Speter state->dst = 0; 34819304Speter 34919304Speter /* 35019304Speter * If there was an error, or if the message must be discarded for other 35119304Speter * reasons, remove the queue file and the optional trace file with DSN 35219304Speter * SUCCESS records from virtual alias expansion. 35319304Speter */ 35419304Speter if (state->errs != 0 || (state->flags & CLEANUP_FLAG_DISCARD) != 0) { 35519304Speter if (cleanup_trace_path) 35619304Speter (void) REMOVE(vstring_str(cleanup_trace_path)); 35719304Speter if (REMOVE(cleanup_path)) 35819304Speter msg_warn("remove %s: %m", cleanup_path); 35919304Speter } 36019304Speter 36119304Speter /* 36219304Speter * Make sure that our queue file will not be deleted by the error handler 36319304Speter * AFTER we have taken responsibility for delivery. Better to deliver 36419304Speter * twice than to lose mail. 36519304Speter */ 36619304Speter trace_junk = cleanup_trace_path; 36719304Speter cleanup_trace_path = 0; /* don't delete upon error */ 36819304Speter junk = cleanup_path; 36919304Speter cleanup_path = 0; /* don't delete upon error */ 37019304Speter 37119304Speter if (trace_junk) 37219304Speter vstring_free(trace_junk); 37319304Speter myfree(junk); 37419304Speter 37519304Speter /* 37619304Speter * Cleanup internal state. This is simply complementary to the 37786201Srwatson * initializations at the beginning of cleanup_open(). 37886201Srwatson */ 37919304Speter if (msg_verbose) 38019304Speter msg_info("cleanup_flush: status %d", state->errs); 38119304Speter status = state->errs; 38219304Speter return (status); 38319304Speter} 38419304Speter 38519304Speter/* cleanup_free - pay the last respects */ 38619304Speter 38719304Spetervoid cleanup_free(CLEANUP_STATE *state) 38819304Speter{ 38919304Speter 39019304Speter /* 391254225Speter * Emulate disconnect event. CLEANUP_FLAG_MILTER may be turned off after 39219304Speter * we have started. 39319304Speter */ 39419304Speter if (cleanup_milters != 0 && state->milters == 0) 39519304Speter milter_disc_event(cleanup_milters); 39619304Speter cleanup_state_free(state); 39719304Speter} 39819304Speter