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