1 /* $NetBSD: cleanup_extracted.c,v 1.1.1.1 2009/06/23 10:08: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 } 194 195 /* 196 * Extracted envelope recipient record processing. 197 */ 198 if (type == REC_TYPE_RCPT) { 199 if (state->sender == 0) { /* protect showq */ 200 msg_warn("%s: message rejected: envelope recipient precedes sender", 201 state->queue_id); 202 state->errs |= CLEANUP_STAT_BAD; 203 return; 204 } 205 if (state->orig_rcpt == 0) 206 state->orig_rcpt = mystrdup(buf); 207 cleanup_addr_recipient(state, buf); 208 if (cleanup_milters != 0 209 && state->milters == 0 210 && CLEANUP_MILTER_OK(state)) 211 cleanup_milter_emul_rcpt(state, cleanup_milters, state->recip); 212 myfree(state->orig_rcpt); 213 state->orig_rcpt = 0; 214 if (state->dsn_orcpt != 0) { 215 myfree(state->dsn_orcpt); 216 state->dsn_orcpt = 0; 217 } 218 state->dsn_notify = 0; 219 return; 220 } 221 if (type == REC_TYPE_DONE || type == REC_TYPE_DRCP) { 222 if (state->orig_rcpt != 0) { 223 myfree(state->orig_rcpt); 224 state->orig_rcpt = 0; 225 } 226 if (state->dsn_orcpt != 0) { 227 myfree(state->dsn_orcpt); 228 state->dsn_orcpt = 0; 229 } 230 state->dsn_notify = 0; 231 return; 232 } 233 if (type == REC_TYPE_DSN_ORCPT) { 234 if (state->dsn_orcpt) { 235 msg_warn("%s: ignoring out-of-order DSN original recipient record <%.200s>", 236 state->queue_id, state->dsn_orcpt); 237 myfree(state->dsn_orcpt); 238 } 239 state->dsn_orcpt = mystrdup(buf); 240 return; 241 } 242 if (type == REC_TYPE_DSN_NOTIFY) { 243 if (state->dsn_notify) { 244 msg_warn("%s: ignoring out-of-order DSN notify record <%d>", 245 state->queue_id, state->dsn_notify); 246 state->dsn_notify = 0; 247 } 248 if (!alldig(buf) || (junk = atoi(buf)) == 0 || DSN_NOTIFY_OK(junk) == 0) 249 msg_warn("%s: ignoring malformed dsn notify record <%.200s>", 250 state->queue_id, buf); 251 else 252 state->qmgr_opts |= 253 QMGR_READ_FLAG_FROM_DSN(state->dsn_notify = junk); 254 return; 255 } 256 if (type == REC_TYPE_ORCP) { 257 if (state->orig_rcpt != 0) { 258 msg_warn("%s: ignoring out-of-order original recipient record <%.200s>", 259 state->queue_id, buf); 260 myfree(state->orig_rcpt); 261 } 262 state->orig_rcpt = mystrdup(buf); 263 return; 264 } 265 if (type == REC_TYPE_END) { 266 /* Make room to append recipient. */ 267 if ((state->milters || cleanup_milters) 268 && state->append_rcpt_pt_offset < 0) { 269 if ((state->append_rcpt_pt_offset = vstream_ftell(state->dst)) < 0) 270 msg_fatal("%s: vstream_ftell %s: %m:", myname, cleanup_path); 271 cleanup_out_format(state, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT, 0L); 272 if ((state->append_rcpt_pt_target = vstream_ftell(state->dst)) < 0) 273 msg_fatal("%s: vstream_ftell %s: %m:", myname, cleanup_path); 274 } 275 state->flags &= ~CLEANUP_FLAG_INRCPT; 276 state->flags |= CLEANUP_FLAG_END_SEEN; 277 cleanup_extracted_finish(state); 278 return; 279 } 280 281 /* 282 * Extracted envelope non-recipient record processing. 283 */ 284 if (state->flags & CLEANUP_FLAG_INRCPT) 285 /* Tell qmgr that recipient records are mixed with other information. */ 286 state->qmgr_opts |= QMGR_READ_FLAG_MIXED_RCPT_OTHER; 287 cleanup_out(state, type, buf, len); 288 return; 289 } 290 291 /* cleanup_extracted_finish - complete the third message segment */ 292 293 void cleanup_extracted_finish(CLEANUP_STATE *state) 294 { 295 296 /* 297 * On the way out, add the optional automatic BCC recipient. 298 */ 299 if ((state->flags & CLEANUP_FLAG_BCC_OK) 300 && state->recip != 0 && *var_always_bcc) 301 cleanup_addr_bcc(state, var_always_bcc); 302 303 /* 304 * Terminate the extracted segment. 305 */ 306 cleanup_out_string(state, REC_TYPE_END, ""); 307 } 308