1/*	$NetBSD: cleanup_out_recipient.c,v 1.4 2022/10/08 16:12:45 christos Exp $	*/
2
3/*++
4/* NAME
5/*	cleanup_out_recipient 3
6/* SUMMARY
7/*	envelope recipient output filter
8/* SYNOPSIS
9/*	#include "cleanup.h"
10/*
11/*	void	cleanup_out_recipient(state, dsn_orig_recipient,
12/*					dsn_notify, orig_recipient,
13/*					recipient)
14/*	CLEANUP_STATE *state;
15/*	const char *dsn_orig_recipient;
16/*	const char *dsn_notify;
17/*	const char *orig_recipient;
18/*	const char *recipient;
19/* DESCRIPTION
20/*	This module implements an envelope recipient output filter.
21/*
22/*	cleanup_out_recipient() performs virtual table expansion
23/*	and recipient duplicate filtering, and appends the
24/*	resulting recipients to the output stream. It also
25/*	generates DSN SUCCESS notifications.
26/*
27/*	Arguments:
28/* .IP state
29/*	Cleanup server state.
30/* .IP dsn_orig_recipient
31/*	DSN original recipient information.
32/* .IP dsn_notify
33/*	DSN notify flags.
34/* .IP orig_recipient
35/*	Envelope recipient as received by Postfix.
36/* .IP recipient
37/*	Envelope recipient as rewritten by Postfix.
38/* CONFIGURATION
39/* .ad
40/* .fi
41/* .IP enable_original_recipient
42/*	Enable orig_recipient support.
43/* .IP local_duplicate_filter_limit
44/*	Upper bound to the size of the recipient duplicate filter.
45/*	Zero means no limit; this may cause the mail system to
46/*	become stuck.
47/* .IP virtual_alias_maps
48/*	list of virtual address lookup tables.
49/* LICENSE
50/* .ad
51/* .fi
52/*	The Secure Mailer license must be distributed with this software.
53/* AUTHOR(S)
54/*	Wietse Venema
55/*	IBM T.J. Watson Research
56/*	P.O. Box 704
57/*	Yorktown Heights, NY 10598, USA
58/*
59/*	Wietse Venema
60/*	Google, Inc.
61/*	111 8th Avenue
62/*	New York, NY 10011, USA
63/*--*/
64
65/* System library. */
66
67#include <sys_defs.h>
68#include <string.h>
69
70/* Utility library. */
71
72#include <argv.h>
73#include <msg.h>
74
75/* Global library. */
76
77#include <been_here.h>
78#include <mail_params.h>
79#include <rec_type.h>
80#include <ext_prop.h>
81#include <cleanup_user.h>
82#include <dsn_mask.h>
83#include <recipient_list.h>
84#include <dsn.h>
85#include <trace.h>
86#include <verify.h>
87#include <mail_queue.h>			/* cleanup_trace_path */
88#include <mail_proto.h>
89#include <msg_stats.h>
90
91/* Application-specific. */
92
93#include "cleanup.h"
94
95/* cleanup_trace_append - update trace logfile */
96
97static void cleanup_trace_append(CLEANUP_STATE *state, RECIPIENT *rcpt,
98				         DSN *dsn)
99{
100    MSG_STATS stats;
101
102    if (cleanup_trace_path == 0) {
103	cleanup_trace_path = vstring_alloc(10);
104	mail_queue_path(cleanup_trace_path, MAIL_QUEUE_TRACE,
105			state->queue_id);
106    }
107    if (trace_append(BOUNCE_FLAG_CLEAN, state->queue_id,
108		     CLEANUP_MSG_STATS(&stats, state),
109		     rcpt, "none", dsn) != 0) {
110	msg_warn("%s: trace logfile update error", state->queue_id);
111	state->errs |= CLEANUP_STAT_WRITE;
112    }
113}
114
115/* cleanup_verify_append - update verify daemon */
116
117static void cleanup_verify_append(CLEANUP_STATE *state, RECIPIENT *rcpt,
118				          DSN *dsn, int verify_status)
119{
120    MSG_STATS stats;
121
122    if (verify_append(state->queue_id, CLEANUP_MSG_STATS(&stats, state),
123		      rcpt, "none", dsn, verify_status) != 0) {
124	msg_warn("%s: verify service update error", state->queue_id);
125	state->errs |= CLEANUP_STAT_WRITE;
126    }
127}
128
129/* cleanup_out_recipient - envelope recipient output filter */
130
131void    cleanup_out_recipient(CLEANUP_STATE *state,
132			              const char *dsn_orcpt,
133			              int dsn_notify,
134			              const char *orcpt,
135			              const char *recip)
136{
137    ARGV   *argv;
138    char  **cpp;
139
140    /*
141     * XXX Not elegant, but eliminates complexity in the record reading loop.
142     */
143    if (dsn_orcpt == 0)
144	dsn_orcpt = "";
145
146    /*
147     * Distinguish between different original recipient addresses that map
148     * onto the same mailbox. The recipient will use our original recipient
149     * message header to figure things out.
150     *
151     * Postfix 2.2 compatibility: when ignoring differences in Postfix original
152     * recipient information, also ignore differences in DSN attributes. We
153     * do, however, keep the DSN attributes of the recipient that survives
154     * duplicate elimination.
155     */
156#define STREQ(x, y) (strcmp((x), (y)) == 0)
157
158    if ((state->flags & CLEANUP_FLAG_MAP_OK) == 0
159	|| cleanup_virt_alias_maps == 0) {
160	/* Matches been_here_drop{,_fixed}() calls cleanup_del_rcpt(). */
161	if ((var_enable_orcpt ?
162	     been_here(state->dups, "%s\n%d\n%s\n%s",
163		       dsn_orcpt, dsn_notify, orcpt, recip) :
164	     been_here_fixed(state->dups, recip)) == 0) {
165	    if (dsn_notify)
166		cleanup_out_format(state, REC_TYPE_ATTR, "%s=%d",
167				   MAIL_ATTR_DSN_NOTIFY, dsn_notify);
168	    if (*dsn_orcpt)
169		cleanup_out_format(state, REC_TYPE_ATTR, "%s=%s",
170				   MAIL_ATTR_DSN_ORCPT, dsn_orcpt);
171	    cleanup_out_string(state, REC_TYPE_ORCP, orcpt);
172	    cleanup_out_string(state, REC_TYPE_RCPT, recip);
173	    state->rcpt_count++;
174	}
175    }
176
177    /*
178     * XXX DSN. RFC 3461 gives us three options for multi-recipient aliases
179     * (we're treating single recipient aliases as a special case of
180     * multi-recipient aliases, one argument being that it is none of the
181     * sender's business).
182     *
183     * (a) Don't propagate ENVID, NOTIFY, RET, or ORCPT. If NOTIFY specified
184     * SUCCESS, send a "relayed" DSN.
185     *
186     * (b) Propagate ENVID, (NOTIFY minus SUCCESS), RET, and ORCPT. If NOTIFY
187     * specified SUCCESS, send an "expanded" DSN.
188     *
189     * (c) Propagate ENVID, NOTIFY, RET, and ORCPT to one recipient only. Send
190     * no DSN.
191     *
192     * In all three cases we are modifying at least one NOTIFY value. Either we
193     * have to record explicit dsn_notify records, or we must not allow the
194     * use of a per-message non-default NOTIFY value that applies to all
195     * recipient records.
196     *
197     * Alternatives (a) and (c) require that we store explicit per-recipient RET
198     * and ENVID records, at least for the recipients that are excluded from
199     * RET and ENVID propagation. This means storing explicit ENVID records
200     * to indicate that the information does not exist. All this makes
201     * alternative (b) more and more attractive. It is no surprise that we
202     * use (b) here and in the local delivery agent.
203     *
204     * In order to generate a SUCCESS notification from the cleanup server we
205     * have to write the trace logfile record now. We're NOT going to flush
206     * the trace file from the cleanup server; if we need to write bounce
207     * logfile records, and the bounce service fails, we must be able to
208     * cancel the entire cleanup request including any success or failure
209     * notifications. The queue manager will flush the trace (and bounce)
210     * logfile, possibly after it has generated its own success or failure
211     * notification records.
212     *
213     * Postfix 2.2 compatibility: when ignoring differences in Postfix original
214     * recipient information, also ignore differences in DSN attributes. We
215     * do, however, keep the DSN attributes of the recipient that survives
216     * duplicate elimination.
217     *
218     * In the case of a verify(8) request for a one-to-many alias, declare the
219     * alias address as "deliverable". Do not verify the individual addresses
220     * in the expansion because that results in multiple verify(8) updates
221     * for one verify(8) request.
222     *
223     * Multiple verify(8) updates for one verify(8) request would overwrite
224     * each other's status, and if the last status update is "undeliverable",
225     * then the whole alias is flagged as undeliverable.
226     */
227    else {
228	RECIPIENT rcpt;
229	DSN     dsn;
230
231	argv = cleanup_map1n_internal(state, recip, cleanup_virt_alias_maps,
232				  cleanup_ext_prop_mask & EXT_PROP_VIRTUAL);
233	if (argv->argc > 1 && (state->tflags & DEL_REQ_FLAG_MTA_VRFY)) {
234	    (void) DSN_SIMPLE(&dsn, "2.0.0", "aliased to multiple recipients");
235	    dsn.action = "deliverable";
236	    RECIPIENT_ASSIGN(&rcpt, 0, dsn_orcpt, dsn_notify, orcpt, recip);
237	    cleanup_verify_append(state, &rcpt, &dsn, DEL_RCPT_STAT_OK);
238	    argv_free(argv);
239	    return;
240	}
241	if ((dsn_notify & DSN_NOTIFY_SUCCESS)
242	    && (argv->argc > 1 || strcmp(recip, argv->argv[0]) != 0)) {
243	    (void) DSN_SIMPLE(&dsn, "2.0.0", "alias expanded");
244	    dsn.action = "expanded";
245	    RECIPIENT_ASSIGN(&rcpt, 0, dsn_orcpt, dsn_notify, orcpt, recip);
246	    cleanup_trace_append(state, &rcpt, &dsn);
247	    dsn_notify = (dsn_notify == DSN_NOTIFY_SUCCESS ? DSN_NOTIFY_NEVER :
248			  dsn_notify & ~DSN_NOTIFY_SUCCESS);
249	}
250	for (cpp = argv->argv; *cpp; cpp++) {
251	    if ((var_enable_orcpt ?
252		 been_here(state->dups, "%s\n%d\n%s\n%s",
253			   dsn_orcpt, dsn_notify, orcpt, *cpp) :
254		 been_here_fixed(state->dups, *cpp)) == 0) {
255		if (dsn_notify)
256		    cleanup_out_format(state, REC_TYPE_ATTR, "%s=%d",
257				       MAIL_ATTR_DSN_NOTIFY, dsn_notify);
258		if (*dsn_orcpt)
259		    cleanup_out_format(state, REC_TYPE_ATTR, "%s=%s",
260				       MAIL_ATTR_DSN_ORCPT, dsn_orcpt);
261		cleanup_out_string(state, REC_TYPE_ORCP, orcpt);
262		cleanup_out_string(state, REC_TYPE_RCPT, *cpp);
263		state->rcpt_count++;
264	    }
265	}
266	argv_free(argv);
267    }
268}
269