7 /* bounce all recipients
9 /* #include "cleanup.h"
11 /* void cleanup_bounce(state)
12 /* CLEANUP_STATE *state;
14 /* cleanup_bounce() updates the bounce log on request by client
15 /* programs that cannot handle such problems themselves.
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.
23 /* Queue file and message processing state. This state is
24 /* updated as records are processed and as errors happen.
28 /* The Secure Mailer license must be distributed with this software.
31 /* IBM T.J. Watson Research
33 /* Yorktown Heights, NY 10598, USA
40 /* Utility library. */
43 #include <stringops.h>
48 #include <cleanup_user.h>
49 #include <mail_params.h>
50 #include <mail_proto.h>
56 #include <mail_queue.h>
57 #include <rec_attr_map.h>
59 /* Application-specific. */
63 #define STR(x) vstring_str(x)
65 /* cleanup_bounce_append - update bounce logfile */
67 static void cleanup_bounce_append(CLEANUP_STATE
*state
, RECIPIENT
*rcpt
,
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
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
;
85 /* cleanup_bounce - bounce all recipients */
87 int cleanup_bounce(CLEANUP_STATE
*state
)
89 const char *myname
= "cleanup_bounce";
90 VSTRING
*buf
= vstring_alloc(100);
91 const CLEANUP_STAT_DETAIL
*detail
;
93 const char *dsn_status
;
107 const char *encoding
;
108 const char *dsn_envid
;
113 * Parse the failure reason if one was given, otherwise use a generic
114 * mapping from cleanup-internal error code to (DSN + text).
117 dsn_split(&dp
, "5.0.0", state
->reason
);
118 dsn_status
= DSN_STATUS(dp
.dsn
);
121 detail
= cleanup_stat_detail(state
->errs
);
122 dsn_status
= detail
->dsn
;
123 dsn_text
= detail
->text
;
127 * Create a bounce logfile with one entry for each final recipient.
128 * Degrade gracefully in case of no recipients or no queue file.
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:
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.
139 if (vstream_ferror(state
->dst
) || vstream_fflush(state
->dst
)) {
140 (void) vstream_fpurge(state
->dst
, VSTREAM_PURGE_BOTH
);
141 vstream_clearerr(state
->dst
);
143 if (vstream_fseek(state
->dst
, 0L, SEEK_SET
) < 0)
144 msg_fatal("%s: seek %s: %m", myname
, cleanup_path
);
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
)
153 if (rec_type
== REC_TYPE_ATTR
) {
154 if (split_nameval(STR(buf
), &attr_name
, &attr_value
) != 0
157 /* Map attribute names to pseudo record type. */
158 if ((junk
= rec_attr_map(attr_name
)) != 0) {
164 case REC_TYPE_DSN_ORCPT
: /* RCPT TO ORCPT parameter */
165 if (dsn_orcpt
!= 0) /* can't happen */
167 dsn_orcpt
= mystrdup(start
);
169 case REC_TYPE_DSN_NOTIFY
: /* RCPT TO NOTIFY parameter */
170 if (alldig(start
) && (junk
= atoi(start
)) > 0
171 && DSN_NOTIFY_OK(junk
))
176 case REC_TYPE_ORCP
: /* unmodified RCPT TO address */
177 if (orig_rcpt
!= 0) /* can't happen */
179 orig_rcpt
= mystrdup(start
);
181 case REC_TYPE_RCPT
: /* rewritten RCPT TO address */
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
);
189 case REC_TYPE_DRCP
: /* canceled recipient */
190 case REC_TYPE_DONE
: /* can't happen */
191 if (orig_rcpt
!= 0) {
195 if (dsn_orcpt
!= 0) {
203 if (orig_rcpt
!= 0) /* can't happen */
205 if (dsn_orcpt
!= 0) /* can't happen */
209 * No recipients. Yes, this can happen.
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
);
219 * Flush the bounce logfile to the sender. See also qmgr_active.c.
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 dsn_ret
= (state
->errs
& (CLEANUP_STAT_CONT
| CLEANUP_STAT_SIZE
)) ?
227 DSN_RET_HDRS
: state
->dsn_ret
;
229 if (state
->verp_delims
== 0 || var_verp_bounce_off
) {
231 bounce_flush(BOUNCE_FLAG_CLEAN
,
232 state
->queue_name
, state
->queue_id
,
233 encoding
, state
->sender
, dsn_envid
,
237 bounce_flush_verp(BOUNCE_FLAG_CLEAN
,
238 state
->queue_name
, state
->queue_id
,
239 encoding
, state
->sender
, dsn_envid
,
240 dsn_ret
, state
->verp_delims
);
242 if (bounce_err
!= 0) {
243 msg_warn("%s: bounce message failure", state
->queue_id
);
244 state
->errs
|= CLEANUP_STAT_WRITE
;
249 * Schedule this message (and trace logfile) for deletion when all is
250 * well. When all is not well these files would be deleted too, but the
251 * client would get a different completion status so we have to carefully
252 * maintain the bits anyway.
254 if ((state
->errs
&= CLEANUP_STAT_WRITE
) == 0)
255 state
->flags
|= CLEANUP_FLAG_DISCARD
;
257 return (state
->errs
);