1/*++ 2/* NAME 3/* cleanup_extracted 3 4/* SUMMARY 5/* process extracted segment 6/* SYNOPSIS 7/* #include "cleanup.h" 8/* 9/* void cleanup_extracted(state, type, buf, len) 10/* CLEANUP_STATE *state; 11/* int type; 12/* const char *buf; 13/* ssize_t len; 14/* DESCRIPTION 15/* This module processes message records with information extracted 16/* from message content, or with recipients that are stored after the 17/* message content. It updates recipient records, writes extracted 18/* information records to the output, and writes the queue 19/* file end marker. The queue file is left in a state that 20/* is suitable for Milter inspection, but the size record still 21/* contains dummy values. 22/* 23/* Arguments: 24/* .IP state 25/* Queue file and message processing state. This state is updated 26/* as records are processed and as errors happen. 27/* .IP type 28/* Record type. 29/* .IP buf 30/* Record content. 31/* .IP len 32/* Record content length. 33/* LICENSE 34/* .ad 35/* .fi 36/* The Secure Mailer license must be distributed with this software. 37/* AUTHOR(S) 38/* Wietse Venema 39/* IBM T.J. Watson Research 40/* P.O. Box 704 41/* Yorktown Heights, NY 10598, USA 42/*--*/ 43 44/* System library. */ 45 46#include <sys_defs.h> 47#include <unistd.h> 48#include <errno.h> 49#include <string.h> 50#include <stdlib.h> 51 52/* Utility library. */ 53 54#include <msg.h> 55#include <vstring.h> 56#include <vstream.h> 57#include <mymalloc.h> 58#include <nvtable.h> 59#include <stringops.h> 60 61/* Global library. */ 62 63#include <cleanup_user.h> 64#include <qmgr_user.h> 65#include <record.h> 66#include <rec_type.h> 67#include <mail_params.h> 68#include <mail_proto.h> 69#include <dsn_mask.h> 70#include <rec_attr_map.h> 71 72/* Application-specific. */ 73 74#include "cleanup.h" 75 76#define STR(x) vstring_str(x) 77 78static void cleanup_extracted_process(CLEANUP_STATE *, int, const char *, ssize_t); 79static void cleanup_extracted_finish(CLEANUP_STATE *); 80 81/* cleanup_extracted - initialize extracted segment */ 82 83void cleanup_extracted(CLEANUP_STATE *state, int type, 84 const char *buf, ssize_t len) 85{ 86 87 /* 88 * Start the extracted segment. 89 */ 90 cleanup_out_string(state, REC_TYPE_XTRA, ""); 91 92 /* 93 * Pass control to the actual envelope processing routine. 94 */ 95 state->action = cleanup_extracted_process; 96 cleanup_extracted_process(state, type, buf, len); 97} 98 99/* cleanup_extracted_process - process one extracted envelope record */ 100 101void cleanup_extracted_process(CLEANUP_STATE *state, int type, 102 const char *buf, ssize_t len) 103{ 104 const char *myname = "cleanup_extracted_process"; 105 const char *encoding; 106 char *attr_name; 107 char *attr_value; 108 const char *error_text; 109 int extra_opts; 110 int junk; 111 112#ifdef DELAY_ACTION 113 int defer_delay; 114 115#endif 116 117 if (msg_verbose) 118 msg_info("extracted envelope %c %.*s", type, (int) len, buf); 119 120 if (type == REC_TYPE_FLGS) { 121 /* Not part of queue file format. */ 122 extra_opts = atoi(buf); 123 if (extra_opts & ~CLEANUP_FLAG_MASK_EXTRA) 124 msg_warn("%s: ignoring bad extra flags: 0x%x", 125 state->queue_id, extra_opts); 126 else 127 state->flags |= extra_opts; 128 return; 129 } 130#ifdef DELAY_ACTION 131 if (type == REC_TYPE_DELAY) { 132 /* Not part of queue file format. */ 133 defer_delay = atoi(buf); 134 if (defer_delay <= 0) 135 msg_warn("%s: ignoring bad delay time: %s", state->queue_id, buf); 136 else 137 state->defer_delay = defer_delay; 138 return; 139 } 140#endif 141 142 if (strchr(REC_TYPE_EXTRACT, type) == 0) { 143 msg_warn("%s: message rejected: " 144 "unexpected record type %d in extracted envelope", 145 state->queue_id, type); 146 state->errs |= CLEANUP_STAT_BAD; 147 return; 148 } 149 150 /* 151 * Map DSN attribute name to pseudo record type so that we don't have to 152 * pollute the queue file with records that are incompatible with past 153 * Postfix versions. Preferably, people should be able to back out from 154 * an upgrade without losing mail. 155 */ 156 if (type == REC_TYPE_ATTR) { 157 vstring_strcpy(state->attr_buf, buf); 158 error_text = split_nameval(STR(state->attr_buf), &attr_name, &attr_value); 159 if (error_text != 0) { 160 msg_warn("%s: message rejected: malformed attribute: %s: %.100s", 161 state->queue_id, error_text, buf); 162 state->errs |= CLEANUP_STAT_BAD; 163 return; 164 } 165 /* Zero-length values are place holders for unavailable values. */ 166 if (*attr_value == 0) { 167 msg_warn("%s: spurious null attribute value for \"%s\" -- ignored", 168 state->queue_id, attr_name); 169 return; 170 } 171 if ((junk = rec_attr_map(attr_name)) != 0) { 172 buf = attr_value; 173 type = junk; 174 } 175 } 176 177 /* 178 * On the transition from non-recipient records to recipient records, 179 * emit optional information from header/body content. 180 */ 181 if ((state->flags & CLEANUP_FLAG_INRCPT) == 0 182 && strchr(REC_TYPE_EXT_RECIPIENT, type) != 0) { 183 if (state->filter != 0) 184 cleanup_out_string(state, REC_TYPE_FILT, state->filter); 185 if (state->redirect != 0) 186 cleanup_out_string(state, REC_TYPE_RDR, state->redirect); 187 if ((encoding = nvtable_find(state->attr, MAIL_ATTR_ENCODING)) != 0) 188 cleanup_out_format(state, REC_TYPE_ATTR, "%s=%s", 189 MAIL_ATTR_ENCODING, encoding); 190 state->flags |= CLEANUP_FLAG_INRCPT; 191 /* Make room to append more meta records. */ 192 if (state->milters || cleanup_milters) { 193 if ((state->append_meta_pt_offset = vstream_ftell(state->dst)) < 0) 194 msg_fatal("%s: vstream_ftell %s: %m:", myname, cleanup_path); 195 cleanup_out_format(state, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT, 0L); 196 if ((state->append_meta_pt_target = vstream_ftell(state->dst)) < 0) 197 msg_fatal("%s: vstream_ftell %s: %m:", myname, cleanup_path); 198 } 199 } 200 201 /* 202 * Extracted envelope recipient record processing. 203 */ 204 if (type == REC_TYPE_RCPT) { 205 if (state->sender == 0) { /* protect showq */ 206 msg_warn("%s: message rejected: envelope recipient precedes sender", 207 state->queue_id); 208 state->errs |= CLEANUP_STAT_BAD; 209 return; 210 } 211 if (state->orig_rcpt == 0) 212 state->orig_rcpt = mystrdup(buf); 213 cleanup_addr_recipient(state, buf); 214 if (cleanup_milters != 0 215 && state->milters == 0 216 && CLEANUP_MILTER_OK(state)) 217 cleanup_milter_emul_rcpt(state, cleanup_milters, state->recip); 218 myfree(state->orig_rcpt); 219 state->orig_rcpt = 0; 220 if (state->dsn_orcpt != 0) { 221 myfree(state->dsn_orcpt); 222 state->dsn_orcpt = 0; 223 } 224 state->dsn_notify = 0; 225 return; 226 } 227 if (type == REC_TYPE_DONE || type == REC_TYPE_DRCP) { 228 if (state->orig_rcpt != 0) { 229 myfree(state->orig_rcpt); 230 state->orig_rcpt = 0; 231 } 232 if (state->dsn_orcpt != 0) { 233 myfree(state->dsn_orcpt); 234 state->dsn_orcpt = 0; 235 } 236 state->dsn_notify = 0; 237 return; 238 } 239 if (type == REC_TYPE_DSN_ORCPT) { 240 if (state->dsn_orcpt) { 241 msg_warn("%s: ignoring out-of-order DSN original recipient record <%.200s>", 242 state->queue_id, state->dsn_orcpt); 243 myfree(state->dsn_orcpt); 244 } 245 state->dsn_orcpt = mystrdup(buf); 246 return; 247 } 248 if (type == REC_TYPE_DSN_NOTIFY) { 249 if (state->dsn_notify) { 250 msg_warn("%s: ignoring out-of-order DSN notify record <%d>", 251 state->queue_id, state->dsn_notify); 252 state->dsn_notify = 0; 253 } 254 if (!alldig(buf) || (junk = atoi(buf)) == 0 || DSN_NOTIFY_OK(junk) == 0) 255 msg_warn("%s: ignoring malformed dsn notify record <%.200s>", 256 state->queue_id, buf); 257 else 258 state->qmgr_opts |= 259 QMGR_READ_FLAG_FROM_DSN(state->dsn_notify = junk); 260 return; 261 } 262 if (type == REC_TYPE_ORCP) { 263 if (state->orig_rcpt != 0) { 264 msg_warn("%s: ignoring out-of-order original recipient record <%.200s>", 265 state->queue_id, buf); 266 myfree(state->orig_rcpt); 267 } 268 state->orig_rcpt = mystrdup(buf); 269 return; 270 } 271 if (type == REC_TYPE_END) { 272 /* Make room to append recipient. */ 273 if ((state->milters || cleanup_milters) 274 && state->append_rcpt_pt_offset < 0) { 275 if ((state->append_rcpt_pt_offset = vstream_ftell(state->dst)) < 0) 276 msg_fatal("%s: vstream_ftell %s: %m:", myname, cleanup_path); 277 cleanup_out_format(state, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT, 0L); 278 if ((state->append_rcpt_pt_target = vstream_ftell(state->dst)) < 0) 279 msg_fatal("%s: vstream_ftell %s: %m:", myname, cleanup_path); 280 } 281 state->flags &= ~CLEANUP_FLAG_INRCPT; 282 state->flags |= CLEANUP_FLAG_END_SEEN; 283 cleanup_extracted_finish(state); 284 return; 285 } 286 287 /* 288 * Extracted envelope non-recipient record processing. 289 */ 290 if (state->flags & CLEANUP_FLAG_INRCPT) 291 /* Tell qmgr that recipient records are mixed with other information. */ 292 state->qmgr_opts |= QMGR_READ_FLAG_MIXED_RCPT_OTHER; 293 cleanup_out(state, type, buf, len); 294 return; 295} 296 297/* cleanup_extracted_finish - complete the third message segment */ 298 299void cleanup_extracted_finish(CLEANUP_STATE *state) 300{ 301 302 /* 303 * On the way out, add the optional automatic BCC recipient. 304 */ 305 if ((state->flags & CLEANUP_FLAG_BCC_OK) 306 && state->recip != 0 && *var_always_bcc) 307 cleanup_addr_bcc(state, var_always_bcc); 308 309 /* 310 * Terminate the extracted segment. 311 */ 312 cleanup_out_string(state, REC_TYPE_END, ""); 313} 314