xref: /netbsd-src/external/ibm-public/postfix/dist/src/cleanup/cleanup_out_recipient.c (revision 67b9b338a7386232ac596b5fd0cd5a9cc8a03c71)
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 
cleanup_trace_append(CLEANUP_STATE * state,RECIPIENT * rcpt,DSN * dsn)97 static 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 
cleanup_verify_append(CLEANUP_STATE * state,RECIPIENT * rcpt,DSN * dsn,int verify_status)117 static 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 
cleanup_out_recipient(CLEANUP_STATE * state,const char * dsn_orcpt,int dsn_notify,const char * orcpt,const char * recip)131 void    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