summaryrefslogtreecommitdiffstats
path: root/src/cleanup/cleanup_out_recipient.c
blob: 5e965fa67134097b981a7e2531accb3fdf03b38d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
/*++
/* NAME
/*	cleanup_out_recipient 3
/* SUMMARY
/*	envelope recipient output filter
/* SYNOPSIS
/*	#include "cleanup.h"
/*
/*	void	cleanup_out_recipient(state, dsn_orig_recipient,
/*					dsn_notify, orig_recipient,
/*					recipient)
/*	CLEANUP_STATE *state;
/*	const char *dsn_orig_recipient;
/*	const char *dsn_notify;
/*	const char *orig_recipient;
/*	const char *recipient;
/* DESCRIPTION
/*	This module implements an envelope recipient output filter.
/*
/*	cleanup_out_recipient() performs virtual table expansion
/*	and recipient duplicate filtering, and appends the
/*	resulting recipients to the output stream. It also
/*	generates DSN SUCCESS notifications.
/*
/*	Arguments:
/* .IP state
/*	Cleanup server state.
/* .IP dsn_orig_recipient
/*	DSN original recipient information.
/* .IP dsn_notify
/*	DSN notify flags.
/* .IP orig_recipient
/*	Envelope recipient as received by Postfix.
/* .IP recipient
/*	Envelope recipient as rewritten by Postfix.
/* CONFIGURATION
/* .ad
/* .fi
/* .IP enable_original_recipient
/*	Enable orig_recipient support.
/* .IP local_duplicate_filter_limit
/*	Upper bound to the size of the recipient duplicate filter.
/*	Zero means no limit; this may cause the mail system to
/*	become stuck.
/* .IP virtual_alias_maps
/*	list of virtual address lookup tables.
/* LICENSE
/* .ad
/* .fi
/*	The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/*	Wietse Venema
/*	IBM T.J. Watson Research
/*	P.O. Box 704
/*	Yorktown Heights, NY 10598, USA
/*
/*	Wietse Venema
/*	Google, Inc.
/*	111 8th Avenue
/*	New York, NY 10011, USA
/*--*/

/* System library. */

#include <sys_defs.h>
#include <string.h>

/* Utility library. */

#include <argv.h>
#include <msg.h>

/* Global library. */

#include <been_here.h>
#include <mail_params.h>
#include <rec_type.h>
#include <ext_prop.h>
#include <cleanup_user.h>
#include <dsn_mask.h>
#include <recipient_list.h>
#include <dsn.h>
#include <trace.h>
#include <verify.h>
#include <mail_queue.h>			/* cleanup_trace_path */
#include <mail_proto.h>
#include <msg_stats.h>

/* Application-specific. */

#include "cleanup.h"

/* cleanup_trace_append - update trace logfile */

static void cleanup_trace_append(CLEANUP_STATE *state, RECIPIENT *rcpt,
				         DSN *dsn)
{
    MSG_STATS stats;

    if (cleanup_trace_path == 0) {
	cleanup_trace_path = vstring_alloc(10);
	mail_queue_path(cleanup_trace_path, MAIL_QUEUE_TRACE,
			state->queue_id);
    }
    if (trace_append(BOUNCE_FLAG_CLEAN, state->queue_id,
		     CLEANUP_MSG_STATS(&stats, state),
		     rcpt, "none", dsn) != 0) {
	msg_warn("%s: trace logfile update error", state->queue_id);
	state->errs |= CLEANUP_STAT_WRITE;
    }
}

/* cleanup_verify_append - update verify daemon */

static void cleanup_verify_append(CLEANUP_STATE *state, RECIPIENT *rcpt,
				          DSN *dsn, int verify_status)
{
    MSG_STATS stats;

    if (verify_append(state->queue_id, CLEANUP_MSG_STATS(&stats, state),
		      rcpt, "none", dsn, verify_status) != 0) {
	msg_warn("%s: verify service update error", state->queue_id);
	state->errs |= CLEANUP_STAT_WRITE;
    }
}

/* cleanup_out_recipient - envelope recipient output filter */

void    cleanup_out_recipient(CLEANUP_STATE *state,
			              const char *dsn_orcpt,
			              int dsn_notify,
			              const char *orcpt,
			              const char *recip)
{
    ARGV   *argv;
    char  **cpp;

    /*
     * XXX Not elegant, but eliminates complexity in the record reading loop.
     */
    if (dsn_orcpt == 0)
	dsn_orcpt = "";

    /*
     * Distinguish between different original recipient addresses that map
     * onto the same mailbox. The recipient will use our original recipient
     * message header to figure things out.
     * 
     * Postfix 2.2 compatibility: when ignoring differences in Postfix original
     * recipient information, also ignore differences in DSN attributes. We
     * do, however, keep the DSN attributes of the recipient that survives
     * duplicate elimination.
     */
#define STREQ(x, y) (strcmp((x), (y)) == 0)

    if ((state->flags & CLEANUP_FLAG_MAP_OK) == 0
	|| cleanup_virt_alias_maps == 0) {
	/* Matches been_here_drop{,_fixed}() calls cleanup_del_rcpt(). */
	if ((var_enable_orcpt ?
	     been_here(state->dups, "%s\n%d\n%s\n%s",
		       dsn_orcpt, dsn_notify, orcpt, recip) :
	     been_here_fixed(state->dups, recip)) == 0) {
	    if (dsn_notify)
		cleanup_out_format(state, REC_TYPE_ATTR, "%s=%d",
				   MAIL_ATTR_DSN_NOTIFY, dsn_notify);
	    if (*dsn_orcpt)
		cleanup_out_format(state, REC_TYPE_ATTR, "%s=%s",
				   MAIL_ATTR_DSN_ORCPT, dsn_orcpt);
	    cleanup_out_string(state, REC_TYPE_ORCP, orcpt);
	    cleanup_out_string(state, REC_TYPE_RCPT, recip);
	    state->rcpt_count++;
	}
    }

    /*
     * XXX DSN. RFC 3461 gives us three options for multi-recipient aliases
     * (we're treating single recipient aliases as a special case of
     * multi-recipient aliases, one argument being that it is none of the
     * sender's business).
     * 
     * (a) Don't propagate ENVID, NOTIFY, RET, or ORCPT. If NOTIFY specified
     * SUCCESS, send a "relayed" DSN.
     * 
     * (b) Propagate ENVID, (NOTIFY minus SUCCESS), RET, and ORCPT. If NOTIFY
     * specified SUCCESS, send an "expanded" DSN.
     * 
     * (c) Propagate ENVID, NOTIFY, RET, and ORCPT to one recipient only. Send
     * no DSN.
     * 
     * In all three cases we are modifying at least one NOTIFY value. Either we
     * have to record explicit dsn_notify records, or we must not allow the
     * use of a per-message non-default NOTIFY value that applies to all
     * recipient records.
     * 
     * Alternatives (a) and (c) require that we store explicit per-recipient RET
     * and ENVID records, at least for the recipients that are excluded from
     * RET and ENVID propagation. This means storing explicit ENVID records
     * to indicate that the information does not exist. All this makes
     * alternative (b) more and more attractive. It is no surprise that we
     * use (b) here and in the local delivery agent.
     * 
     * In order to generate a SUCCESS notification from the cleanup server we
     * have to write the trace logfile record now. We're NOT going to flush
     * the trace file from the cleanup server; if we need to write bounce
     * logfile records, and the bounce service fails, we must be able to
     * cancel the entire cleanup request including any success or failure
     * notifications. The queue manager will flush the trace (and bounce)
     * logfile, possibly after it has generated its own success or failure
     * notification records.
     * 
     * Postfix 2.2 compatibility: when ignoring differences in Postfix original
     * recipient information, also ignore differences in DSN attributes. We
     * do, however, keep the DSN attributes of the recipient that survives
     * duplicate elimination.
     * 
     * In the case of a verify(8) request for a one-to-many alias, declare the
     * alias address as "deliverable". Do not verify the individual addresses
     * in the expansion because that results in multiple verify(8) updates
     * for one verify(8) request.
     * 
     * Multiple verify(8) updates for one verify(8) request would overwrite
     * each other's status, and if the last status update is "undeliverable",
     * then the whole alias is flagged as undeliverable.
     */
    else {
	RECIPIENT rcpt;
	DSN     dsn;

	argv = cleanup_map1n_internal(state, recip, cleanup_virt_alias_maps,
				  cleanup_ext_prop_mask & EXT_PROP_VIRTUAL);
	if (argv->argc > 1 && (state->tflags & DEL_REQ_FLAG_MTA_VRFY)) {
	    (void) DSN_SIMPLE(&dsn, "2.0.0", "aliased to multiple recipients");
	    dsn.action = "deliverable";
	    RECIPIENT_ASSIGN(&rcpt, 0, dsn_orcpt, dsn_notify, orcpt, recip);
	    cleanup_verify_append(state, &rcpt, &dsn, DEL_RCPT_STAT_OK);
	    argv_free(argv);
	    return;
	}
	if ((dsn_notify & DSN_NOTIFY_SUCCESS)
	    && (argv->argc > 1 || strcmp(recip, argv->argv[0]) != 0)) {
	    (void) DSN_SIMPLE(&dsn, "2.0.0", "alias expanded");
	    dsn.action = "expanded";
	    RECIPIENT_ASSIGN(&rcpt, 0, dsn_orcpt, dsn_notify, orcpt, recip);
	    cleanup_trace_append(state, &rcpt, &dsn);
	    dsn_notify = (dsn_notify == DSN_NOTIFY_SUCCESS ? DSN_NOTIFY_NEVER :
			  dsn_notify & ~DSN_NOTIFY_SUCCESS);
	}
	for (cpp = argv->argv; *cpp; cpp++) {
	    if ((var_enable_orcpt ?
		 been_here(state->dups, "%s\n%d\n%s\n%s",
			   dsn_orcpt, dsn_notify, orcpt, *cpp) :
		 been_here_fixed(state->dups, *cpp)) == 0) {
		if (dsn_notify)
		    cleanup_out_format(state, REC_TYPE_ATTR, "%s=%d",
				       MAIL_ATTR_DSN_NOTIFY, dsn_notify);
		if (*dsn_orcpt)
		    cleanup_out_format(state, REC_TYPE_ATTR, "%s=%s",
				       MAIL_ATTR_DSN_ORCPT, dsn_orcpt);
		cleanup_out_string(state, REC_TYPE_ORCP, orcpt);
		cleanup_out_string(state, REC_TYPE_RCPT, *cpp);
		state->rcpt_count++;
	    }
	}
	argv_free(argv);
    }
}