1/* $NetBSD$ */ 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 67static 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 87int 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->sender, dsn_envid, 235 dsn_ret); 236 } else { 237 bounce_err = 238 bounce_flush_verp(BOUNCE_FLAG_CLEAN, 239 state->queue_name, state->queue_id, 240 encoding, state->sender, dsn_envid, 241 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