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