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