xref: /netbsd-src/external/ibm-public/postfix/dist/src/cleanup/cleanup_bounce.c (revision e89934bbf778a6d6d6894877c4da59d0c7835b0f)
1 /*	$NetBSD: cleanup_bounce.c,v 1.2 2017/02/14 01:16:44 christos Exp $	*/
2 
3 /*++
4 /* NAME
5 /*	cleanup_bounce 3
6 /* SUMMARY
7 /*	bounce all recipients
8 /* SYNOPSIS
9 /*	#include "cleanup.h"
10 /*
11 /*	void	cleanup_bounce(state)
12 /*	CLEANUP_STATE *state;
13 /* DESCRIPTION
14 /*	cleanup_bounce() updates the bounce log on request by client
15 /*	programs that cannot handle such problems themselves.
16 /*
17 /*	Upon successful completion, all error flags are reset,
18 /*	and the message is scheduled for deletion.
19 /*	Otherwise, the CLEANUP_STAT_WRITE error flag is raised.
20 /*
21 /*	Arguments:
22 /* .IP state
23 /*	Queue file and message processing state. This state is
24 /*	updated as records are processed and as errors happen.
25 /* LICENSE
26 /* .ad
27 /* .fi
28 /*	The Secure Mailer license must be distributed with this software.
29 /* AUTHOR(S)
30 /*	Wietse Venema
31 /*	IBM T.J. Watson Research
32 /*	P.O. Box 704
33 /*	Yorktown Heights, NY 10598, USA
34 /*--*/
35 
36 /* System library. */
37 
38 #include <sys_defs.h>
39 
40 /* Utility library. */
41 
42 #include <msg.h>
43 #include <stringops.h>
44 #include <stdlib.h>
45 
46 /* Global library. */
47 
48 #include <cleanup_user.h>
49 #include <mail_params.h>
50 #include <mail_proto.h>
51 #include <bounce.h>
52 #include <dsn_util.h>
53 #include <record.h>
54 #include <rec_type.h>
55 #include <dsn_mask.h>
56 #include <mail_queue.h>
57 #include <rec_attr_map.h>
58 
59 /* Application-specific. */
60 
61 #include "cleanup.h"
62 
63 #define STR(x) vstring_str(x)
64 
65 /* cleanup_bounce_append - update bounce logfile */
66 
cleanup_bounce_append(CLEANUP_STATE * state,RECIPIENT * rcpt,DSN * dsn)67 static void cleanup_bounce_append(CLEANUP_STATE *state, RECIPIENT *rcpt,
68 				          DSN *dsn)
69 {
70     MSG_STATS stats;
71 
72     /*
73      * Don't log a spurious warning (for example, when soft_bounce is turned
74      * on). bounce_append() already logs a record when the logfile can't be
75      * updated. Set the write error flag, so that a maildrop queue file won't
76      * be destroyed.
77      */
78     if (bounce_append(BOUNCE_FLAG_CLEAN, state->queue_id,
79 		      CLEANUP_MSG_STATS(&stats, state),
80 		      rcpt, "none", dsn) != 0) {
81 	state->errs |= CLEANUP_STAT_WRITE;
82     }
83 }
84 
85 /* cleanup_bounce - bounce all recipients */
86 
cleanup_bounce(CLEANUP_STATE * state)87 int     cleanup_bounce(CLEANUP_STATE *state)
88 {
89     const char *myname = "cleanup_bounce";
90     VSTRING *buf = vstring_alloc(100);
91     const CLEANUP_STAT_DETAIL *detail;
92     DSN_SPLIT dp;
93     const char *dsn_status;
94     const char *dsn_text;
95     char   *rcpt = 0;
96     RECIPIENT recipient;
97     DSN     dsn;
98     char   *attr_name;
99     char   *attr_value;
100     char   *dsn_orcpt = 0;
101     int     dsn_notify = 0;
102     char   *orig_rcpt = 0;
103     char   *start;
104     int     rec_type;
105     int     junk;
106     long    curr_offset;
107     const char *encoding;
108     const char *dsn_envid;
109     int     dsn_ret;
110     int     bounce_err;
111 
112     /*
113      * Parse the failure reason if one was given, otherwise use a generic
114      * mapping from cleanup-internal error code to (DSN + text).
115      */
116     if (state->reason) {
117 	dsn_split(&dp, "5.0.0", state->reason);
118 	dsn_status = DSN_STATUS(dp.dsn);
119 	dsn_text = dp.text;
120     } else {
121 	detail = cleanup_stat_detail(state->errs);
122 	dsn_status = detail->dsn;
123 	dsn_text = detail->text;
124     }
125 
126     /*
127      * Create a bounce logfile with one entry for each final recipient.
128      * Degrade gracefully in case of no recipients or no queue file.
129      *
130      * Victor Duchovni observes that the number of recipients in the queue file
131      * can potentially be very large due to virtual alias expansion. This can
132      * expand the recipient count by virtual_alias_expansion_limit (default:
133      * 1000) times.
134      *
135      * After a queue file write error, purge any unwritten data (so that
136      * vstream_fseek() won't fail while trying to flush it) and reset the
137      * stream error flags to avoid false alarms.
138      */
139     if (vstream_ferror(state->dst) || vstream_fflush(state->dst)) {
140 	(void) vstream_fpurge(state->dst, VSTREAM_PURGE_BOTH);
141 	vstream_clearerr(state->dst);
142     }
143     if (vstream_fseek(state->dst, 0L, SEEK_SET) < 0)
144 	msg_fatal("%s: seek %s: %m", myname, cleanup_path);
145 
146     while ((state->errs & CLEANUP_STAT_WRITE) == 0) {
147 	if ((curr_offset = vstream_ftell(state->dst)) < 0)
148 	    msg_fatal("%s: vstream_ftell %s: %m", myname, cleanup_path);
149 	if ((rec_type = rec_get(state->dst, buf, 0)) <= 0
150 	    || rec_type == REC_TYPE_END)
151 	    break;
152 	start = STR(buf);
153 	if (rec_type == REC_TYPE_ATTR) {
154 	    if (split_nameval(STR(buf), &attr_name, &attr_value) != 0
155 		|| *attr_value == 0)
156 		continue;
157 	    /* Map attribute names to pseudo record type. */
158 	    if ((junk = rec_attr_map(attr_name)) != 0) {
159 		start = attr_value;
160 		rec_type = junk;
161 	    }
162 	}
163 	switch (rec_type) {
164 	case REC_TYPE_DSN_ORCPT:		/* RCPT TO ORCPT parameter */
165 	    if (dsn_orcpt != 0)			/* can't happen */
166 		myfree(dsn_orcpt);
167 	    dsn_orcpt = mystrdup(start);
168 	    break;
169 	case REC_TYPE_DSN_NOTIFY:		/* RCPT TO NOTIFY parameter */
170 	    if (alldig(start) && (junk = atoi(start)) > 0
171 		&& DSN_NOTIFY_OK(junk))
172 		dsn_notify = junk;
173 	    else
174 		dsn_notify = 0;
175 	    break;
176 	case REC_TYPE_ORCP:			/* unmodified RCPT TO address */
177 	    if (orig_rcpt != 0)			/* can't happen */
178 		myfree(orig_rcpt);
179 	    orig_rcpt = mystrdup(start);
180 	    break;
181 	case REC_TYPE_RCPT:			/* rewritten RCPT TO address */
182 	    rcpt = start;
183 	    RECIPIENT_ASSIGN(&recipient, curr_offset,
184 			     dsn_orcpt ? dsn_orcpt : "", dsn_notify,
185 			     orig_rcpt ? orig_rcpt : rcpt, rcpt);
186 	    (void) DSN_SIMPLE(&dsn, dsn_status, dsn_text);
187 	    cleanup_bounce_append(state, &recipient, &dsn);
188 	    /* FALLTHROUGH */
189 	case REC_TYPE_DRCP:			/* canceled recipient */
190 	case REC_TYPE_DONE:			/* can't happen */
191 	    if (orig_rcpt != 0) {
192 		myfree(orig_rcpt);
193 		orig_rcpt = 0;
194 	    }
195 	    if (dsn_orcpt != 0) {
196 		myfree(dsn_orcpt);
197 		dsn_orcpt = 0;
198 	    }
199 	    dsn_notify = 0;
200 	    break;
201 	}
202     }
203     if (orig_rcpt != 0)				/* can't happen */
204 	myfree(orig_rcpt);
205     if (dsn_orcpt != 0)				/* can't happen */
206 	myfree(dsn_orcpt);
207 
208     /*
209      * No recipients. Yes, this can happen.
210      */
211     if ((state->errs & CLEANUP_STAT_WRITE) == 0 && rcpt == 0) {
212 	RECIPIENT_ASSIGN(&recipient, 0, "", 0, "", "unknown");
213 	(void) DSN_SIMPLE(&dsn, dsn_status, dsn_text);
214 	cleanup_bounce_append(state, &recipient, &dsn);
215     }
216     vstring_free(buf);
217 
218     /*
219      * Flush the bounce logfile to the sender. See also qmgr_active.c.
220      */
221     if ((state->errs & CLEANUP_STAT_WRITE) == 0) {
222 	if ((encoding = nvtable_find(state->attr, MAIL_ATTR_ENCODING)) == 0)
223 	    encoding = MAIL_ATTR_ENC_NONE;
224 	dsn_envid = state->dsn_envid ?
225 	    state->dsn_envid : "";
226 	/* Do not send unfiltered (body) content. */
227 	dsn_ret = (state->errs & (CLEANUP_STAT_CONT | CLEANUP_STAT_SIZE)) ?
228 	    DSN_RET_HDRS : state->dsn_ret;
229 
230 	if (state->verp_delims == 0 || var_verp_bounce_off) {
231 	    bounce_err =
232 		bounce_flush(BOUNCE_FLAG_CLEAN,
233 			     state->queue_name, state->queue_id,
234 			     encoding, state->smtputf8, state->sender,
235 			     dsn_envid, dsn_ret);
236 	} else {
237 	    bounce_err =
238 		bounce_flush_verp(BOUNCE_FLAG_CLEAN,
239 				  state->queue_name, state->queue_id,
240 				  encoding, state->smtputf8, state->sender,
241 				  dsn_envid, dsn_ret, state->verp_delims);
242 	}
243 	if (bounce_err != 0) {
244 	    msg_warn("%s: bounce message failure", state->queue_id);
245 	    state->errs |= CLEANUP_STAT_WRITE;
246 	}
247     }
248 
249     /*
250      * Schedule this message (and trace logfile) for deletion when all is
251      * well. When all is not well these files would be deleted too, but the
252      * client would get a different completion status so we have to carefully
253      * maintain the bits anyway.
254      */
255     if ((state->errs &= CLEANUP_STAT_WRITE) == 0)
256 	state->flags |= CLEANUP_FLAG_DISCARD;
257 
258     return (state->errs);
259 }
260