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