Postfix3.3.1
cleanup_message.c
[詳解]
1 /*++
2 /* NAME
3 /* cleanup_message 3
4 /* SUMMARY
5 /* process message segment
6 /* SYNOPSIS
7 /* #include "cleanup.h"
8 /*
9 /* void cleanup_message(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 content records and copies the
16 /* result to the queue file. It validates the input, rewrites
17 /* sender/recipient addresses to canonical form, inserts missing
18 /* message headers, and extracts information from message headers
19 /* to be used later when generating the extracted output segment.
20 /* This routine absorbs but does not emit the content to extracted
21 /* boundary record.
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 /* Wietse Venema
44 /* Google, Inc.
45 /* 111 8th Avenue
46 /* New York, NY 10011, USA
47 /*--*/
48 
49 /* System library. */
50 
51 #include <sys_defs.h>
52 #include <ctype.h>
53 #include <string.h>
54 #include <time.h>
55 #include <unistd.h>
56 
57 #ifdef STRCASECMP_IN_STRINGS_H
58 #include <strings.h>
59 #endif
60 
61 /* Utility library. */
62 
63 #include <msg.h>
64 #include <vstring.h>
65 #include <vstream.h>
66 #include <argv.h>
67 #include <split_at.h>
68 #include <mymalloc.h>
69 #include <stringops.h>
70 #include <nvtable.h>
71 
72 /* Global library. */
73 
74 #include <record.h>
75 #include <rec_type.h>
76 #include <cleanup_user.h>
77 #include <tok822.h>
78 #include <lex_822.h>
79 #include <header_opts.h>
80 #include <quote_822_local.h>
81 #include <mail_params.h>
82 #include <mail_date.h>
83 #include <mail_addr.h>
84 #include <is_header.h>
85 #include <ext_prop.h>
86 #include <mail_proto.h>
87 #include <mime_state.h>
88 #include <lex_822.h>
89 #include <dsn_util.h>
90 #include <conv_time.h>
91 
92 /* Application-specific. */
93 
94 #include "cleanup.h"
95 
96 /* cleanup_fold_header - wrap address list header */
97 
98 static void cleanup_fold_header(CLEANUP_STATE *state, VSTRING *header_buf)
99 {
100  char *start_line = vstring_str(header_buf);
101  char *end_line;
102  char *next_line;
103  char *line;
104 
105  /*
106  * A rewritten address list contains one address per line. The code below
107  * replaces newlines by spaces, to fit as many addresses on a line as
108  * possible (without rearranging the order of addresses). Prepending
109  * white space to the beginning of lines is delegated to the output
110  * routine.
111  */
112  for (line = start_line; line != 0; line = next_line) {
113  end_line = line + strcspn(line, "\n");
114  if (line > start_line) {
115  if (end_line - start_line < 70) { /* TAB counts as one */
116  line[-1] = ' ';
117  } else {
118  start_line = line;
119  }
120  }
121  next_line = *end_line ? end_line + 1 : 0;
122  }
123  cleanup_out_header(state, header_buf);
124 }
125 
126 /* cleanup_extract_internal - save unquoted copy of extracted address */
127 
128 static char *cleanup_extract_internal(VSTRING *buffer, TOK822 *addr)
129 {
130 
131  /*
132  * A little routine to stash away a copy of an address that we extracted
133  * from a message header line.
134  */
135  tok822_internalize(buffer, addr->head, TOK822_STR_DEFL);
136  return (mystrdup(vstring_str(buffer)));
137 }
138 
139 /* cleanup_rewrite_sender - sender address rewriting */
140 
141 static void cleanup_rewrite_sender(CLEANUP_STATE *state,
142  const HEADER_OPTS *hdr_opts,
143  VSTRING *header_buf)
144 {
145  TOK822 *tree;
146  TOK822 **addr_list;
147  TOK822 **tpp;
148  int did_rewrite = 0;
149 
150  if (msg_verbose)
151  msg_info("rewrite_sender: %s", hdr_opts->name);
152 
153  /*
154  * Parse the header line, rewrite each address found, and regenerate the
155  * header line. Finally, pipe the result through the header line folding
156  * routine.
157  */
158  tree = tok822_parse_limit(vstring_str(header_buf)
159  + strlen(hdr_opts->name) + 1,
161  addr_list = tok822_grep(tree, TOK822_ADDR);
162  for (tpp = addr_list; *tpp; tpp++) {
163  did_rewrite |= cleanup_rewrite_tree(state->hdr_rewrite_context, *tpp);
164  if (state->flags & CLEANUP_FLAG_MAP_OK) {
167  did_rewrite |=
171  && (cleanup_comm_canon_flags & CLEANUP_CANON_FLAG_HDR_FROM))
172  did_rewrite |=
177  did_rewrite |=
179  }
180  }
181  if (did_rewrite) {
182  vstring_truncate(header_buf, strlen(hdr_opts->name));
183  vstring_strcat(header_buf, ": ");
184  tok822_externalize(header_buf, tree, TOK822_STR_HEAD);
185  }
186  myfree((void *) addr_list);
187  tok822_free_tree(tree);
188  if ((hdr_opts->flags & HDR_OPT_DROP) == 0) {
189  if (did_rewrite)
190  cleanup_fold_header(state, header_buf);
191  else
192  cleanup_out_header(state, header_buf);
193  }
194 }
195 
196 /* cleanup_rewrite_recip - recipient address rewriting */
197 
198 static void cleanup_rewrite_recip(CLEANUP_STATE *state,
199  const HEADER_OPTS *hdr_opts,
200  VSTRING *header_buf)
201 {
202  TOK822 *tree;
203  TOK822 **addr_list;
204  TOK822 **tpp;
205  int did_rewrite = 0;
206 
207  if (msg_verbose)
208  msg_info("rewrite_recip: %s", hdr_opts->name);
209 
210  /*
211  * Parse the header line, rewrite each address found, and regenerate the
212  * header line. Finally, pipe the result through the header line folding
213  * routine.
214  */
215  tree = tok822_parse_limit(vstring_str(header_buf)
216  + strlen(hdr_opts->name) + 1,
218  addr_list = tok822_grep(tree, TOK822_ADDR);
219  for (tpp = addr_list; *tpp; tpp++) {
220  did_rewrite |= cleanup_rewrite_tree(state->hdr_rewrite_context, *tpp);
221  if (state->flags & CLEANUP_FLAG_MAP_OK) {
224  did_rewrite |=
228  && (cleanup_comm_canon_flags & CLEANUP_CANON_FLAG_HDR_RCPT))
229  did_rewrite |=
234  did_rewrite |=
236  }
237  }
238  if (did_rewrite) {
239  vstring_truncate(header_buf, strlen(hdr_opts->name));
240  vstring_strcat(header_buf, ": ");
241  tok822_externalize(header_buf, tree, TOK822_STR_HEAD);
242  }
243  myfree((void *) addr_list);
244  tok822_free_tree(tree);
245  if ((hdr_opts->flags & HDR_OPT_DROP) == 0) {
246  if (did_rewrite)
247  cleanup_fold_header(state, header_buf);
248  else
249  cleanup_out_header(state, header_buf);
250  }
251 }
252 
253 /* cleanup_act_log - log action with context */
254 
255 static void cleanup_act_log(CLEANUP_STATE *state,
256  const char *action, const char *class,
257  const char *content, const char *text)
258 {
259  const char *attr;
260 
261  if ((attr = nvtable_find(state->attr, MAIL_ATTR_LOG_ORIGIN)) == 0)
262  attr = "unknown";
263  vstring_sprintf(state->temp1, "%s: %s: %s %.200s from %s;",
264  state->queue_id, action, class, content, attr);
265  if (state->sender)
266  vstring_sprintf_append(state->temp1, " from=<%s>", state->sender);
267  if (state->recip)
268  vstring_sprintf_append(state->temp1, " to=<%s>", state->recip);
269  if ((attr = nvtable_find(state->attr, MAIL_ATTR_LOG_PROTO_NAME)) != 0)
270  vstring_sprintf_append(state->temp1, " proto=%s", attr);
271  if ((attr = nvtable_find(state->attr, MAIL_ATTR_LOG_HELO_NAME)) != 0)
272  vstring_sprintf_append(state->temp1, " helo=<%s>", attr);
273  if (text && *text)
274  vstring_sprintf_append(state->temp1, ": %s", text);
275  msg_info("%s", vstring_str(state->temp1));
276 }
277 
278 #define CLEANUP_ACT_CTXT_HEADER "header"
279 #define CLEANUP_ACT_CTXT_BODY "body"
280 #define CLEANUP_ACT_CTXT_ANY "content"
281 
282 /* cleanup_act - act upon a header/body match */
283 
284 static const char *cleanup_act(CLEANUP_STATE *state, char *context,
285  const char *buf, const char *value,
286  const char *map_class)
287 {
288  const char *optional_text = value + strcspn(value, " \t");
289  int command_len = optional_text - value;
290 
291 #ifdef DELAY_ACTION
292  int defer_delay;
293 
294 #endif
295 
296  while (*optional_text && ISSPACE(*optional_text))
297  optional_text++;
298 
299 #define STREQUAL(x,y,l) (strncasecmp((x), (y), (l)) == 0 && (y)[l] == 0)
300 #define CLEANUP_ACT_DROP 0
301 
302  /*
303  * CLEANUP_STAT_CONT and CLEANUP_STAT_DEFER both update the reason
304  * attribute, but CLEANUP_STAT_DEFER takes precedence. It terminates
305  * queue record processing, and prevents bounces from being sent.
306  */
307  if (STREQUAL(value, "REJECT", command_len)) {
308  const CLEANUP_STAT_DETAIL *detail;
309 
310  if (state->reason)
311  myfree(state->reason);
312  if (*optional_text) {
313  state->reason = dsn_prepend("5.7.1", optional_text);
314  if (*state->reason != '4' && *state->reason != '5') {
315  msg_warn("bad DSN action in %s -- need 4.x.x or 5.x.x",
316  optional_text);
317  *state->reason = '4';
318  }
319  } else {
321  state->reason = dsn_prepend(detail->dsn, detail->text);
322  }
323  if (*state->reason == '4')
324  state->errs |= CLEANUP_STAT_DEFER;
325  else
326  state->errs |= CLEANUP_STAT_CONT;
327  state->flags &= ~CLEANUP_FLAG_FILTER_ALL;
328  cleanup_act_log(state, "reject", context, buf, state->reason);
329  return (buf);
330  }
331  if (STREQUAL(value, "WARN", command_len)) {
332  cleanup_act_log(state, "warning", context, buf, optional_text);
333  return (buf);
334  }
335  if (STREQUAL(value, "INFO", command_len)) {
336  cleanup_act_log(state, "info", context, buf, optional_text);
337  return (buf);
338  }
339  if (STREQUAL(value, "FILTER", command_len)) {
340  if (*optional_text == 0) {
341  msg_warn("missing FILTER command argument in %s map", map_class);
342  } else if (strchr(optional_text, ':') == 0) {
343  msg_warn("bad FILTER command %s in %s -- "
344  "need transport:destination",
345  optional_text, map_class);
346  } else {
347  if (state->filter)
348  myfree(state->filter);
349  state->filter = mystrdup(optional_text);
350  cleanup_act_log(state, "filter", context, buf, optional_text);
351  }
352  return (buf);
353  }
354  if (STREQUAL(value, "PASS", command_len)) {
355  cleanup_act_log(state, "pass", context, buf, optional_text);
356  state->flags &= ~CLEANUP_FLAG_FILTER_ALL;
357  return (buf);
358  }
359  if (STREQUAL(value, "DISCARD", command_len)) {
360  cleanup_act_log(state, "discard", context, buf, optional_text);
361  state->flags |= CLEANUP_FLAG_DISCARD;
362  state->flags &= ~CLEANUP_FLAG_FILTER_ALL;
363  return (buf);
364  }
365  if (STREQUAL(value, "HOLD", command_len)) {
366  if ((state->flags & (CLEANUP_FLAG_HOLD | CLEANUP_FLAG_DISCARD)) == 0) {
367  cleanup_act_log(state, "hold", context, buf, optional_text);
368  state->flags |= CLEANUP_FLAG_HOLD;
369  }
370  return (buf);
371  }
372 
373  /*
374  * The DELAY feature is disabled because it has too many problems. 1) It
375  * does not work on some remote file systems; 2) mail will be delivered
376  * anyway with "sendmail -q" etc.; 3) while the mail is queued it bogs
377  * down the deferred queue scan with huge amounts of useless disk I/O
378  * operations.
379  */
380 #ifdef DELAY_ACTION
381  if (STREQUAL(value, "DELAY", command_len)) {
382  if ((state->flags & (CLEANUP_FLAG_HOLD | CLEANUP_FLAG_DISCARD)) == 0) {
383  if (*optional_text == 0) {
384  msg_warn("missing DELAY argument in %s map", map_class);
385  } else if (conv_time(optional_text, &defer_delay, 's') == 0) {
386  msg_warn("ignoring bad DELAY argument %s in %s map",
387  optional_text, map_class);
388  } else {
389  cleanup_act_log(state, "delay", context, buf, optional_text);
390  state->defer_delay = defer_delay;
391  }
392  }
393  return (buf);
394  }
395 #endif
396  if (STREQUAL(value, "PREPEND", command_len)) {
397  if (*optional_text == 0) {
398  msg_warn("PREPEND action without text in %s map", map_class);
399  } else if (strcmp(context, CLEANUP_ACT_CTXT_HEADER) == 0) {
400  if (!is_header(optional_text)) {
401  msg_warn("bad PREPEND header text \"%s\" in %s map -- "
402  "need \"headername: headervalue\"",
403  optional_text, map_class);
404  }
405 
406  /*
407  * By design, cleanup_out_header() may modify content. Play safe
408  * and prepare for future developments.
409  */
410  else {
411  VSTRING *temp;
412 
413  cleanup_act_log(state, "prepend", context, buf, optional_text);
414  temp = vstring_strcpy(vstring_alloc(strlen(optional_text)),
415  optional_text);
416  cleanup_out_header(state, temp);
417  vstring_free(temp);
418  }
419  } else {
420  cleanup_act_log(state, "prepend", context, buf, optional_text);
421  cleanup_out_string(state, REC_TYPE_NORM, optional_text);
422  }
423  return (buf);
424  }
425  if (STREQUAL(value, "REPLACE", command_len)) {
426  if (*optional_text == 0) {
427  msg_warn("REPLACE action without text in %s map", map_class);
428  return (buf);
429  } else if (strcmp(context, CLEANUP_ACT_CTXT_HEADER) == 0
430  && !is_header(optional_text)) {
431  msg_warn("bad REPLACE header text \"%s\" in %s map -- "
432  "need \"headername: headervalue\"",
433  optional_text, map_class);
434  return (buf);
435  } else {
436  cleanup_act_log(state, "replace", context, buf, optional_text);
437  return (mystrdup(optional_text));
438  }
439  }
440  if (STREQUAL(value, "REDIRECT", command_len)) {
441  if (strchr(optional_text, '@') == 0) {
442  msg_warn("bad REDIRECT target \"%s\" in %s map -- "
443  "need user@domain",
444  optional_text, map_class);
445  } else {
446  if (state->redirect)
447  myfree(state->redirect);
448  state->redirect = mystrdup(optional_text);
449  cleanup_act_log(state, "redirect", context, buf, optional_text);
450  state->flags &= ~CLEANUP_FLAG_FILTER_ALL;
451  }
452  return (buf);
453  }
454  if (STREQUAL(value, "BCC", command_len)) {
455  if (strchr(optional_text, '@') == 0) {
456  msg_warn("bad BCC address \"%s\" in %s map -- "
457  "need user@domain",
458  optional_text, map_class);
459  } else {
460  if (state->hbc_rcpt == 0)
461  state->hbc_rcpt = argv_alloc(1);
462  argv_add(state->hbc_rcpt, optional_text, (char *) 0);
463  cleanup_act_log(state, "bcc", context, buf, optional_text);
464  }
465  return (buf);
466  }
467  if (STREQUAL(value, "STRIP", command_len)) {
468  cleanup_act_log(state, "strip", context, buf, optional_text);
469  return (CLEANUP_ACT_DROP);
470  }
471  /* Allow and ignore optional text after the action. */
472 
473  if (STREQUAL(value, "IGNORE", command_len))
474  return (CLEANUP_ACT_DROP);
475 
476  if (STREQUAL(value, "DUNNO", command_len)) /* preferred */
477  return (buf);
478 
479  if (STREQUAL(value, "OK", command_len)) /* compat */
480  return (buf);
481 
482  msg_warn("unknown command in %s map: %s", map_class, value);
483  return (buf);
484 }
485 
486 /* cleanup_header_callback - process one complete header line */
487 
488 static void cleanup_header_callback(void *context, int header_class,
489  const HEADER_OPTS *hdr_opts,
490  VSTRING *header_buf,
491  off_t unused_offset)
492 {
493  CLEANUP_STATE *state = (CLEANUP_STATE *) context;
494  const char *myname = "cleanup_header_callback";
495  char *hdrval;
496  struct code_map {
497  const char *name;
498  const char *encoding;
499  };
500  static struct code_map code_map[] = { /* RFC 2045 */
501  "7bit", MAIL_ATTR_ENC_7BIT,
502  "8bit", MAIL_ATTR_ENC_8BIT,
503  "binary", MAIL_ATTR_ENC_8BIT, /* XXX Violation */
504  "quoted-printable", MAIL_ATTR_ENC_7BIT,
505  "base64", MAIL_ATTR_ENC_7BIT,
506  0,
507  };
508  struct code_map *cmp;
509  MAPS *checks;
510  const char *map_class;
511 
512  if (msg_verbose)
513  msg_info("%s: '%.200s'", myname, vstring_str(header_buf));
514 
515  /*
516  * Crude header filtering. This stops malware that isn't sophisticated
517  * enough to use fancy header encodings.
518  */
519 #define CHECK(class, maps, var_name) \
520  (header_class == class && (map_class = var_name, checks = maps) != 0)
521 
522  if (hdr_opts && (hdr_opts->flags & HDR_OPT_MIME))
523  header_class = MIME_HDR_MULTIPART;
524 
525  /* Update the Received: header count before maybe dropping headers below. */
526  if (hdr_opts && hdr_opts->type == HDR_RECEIVED)
527  state->hop_count += 1;
528 
529  if ((state->flags & CLEANUP_FLAG_FILTER)
533  char *header = vstring_str(header_buf);
534  const char *value;
535 
536  if ((value = maps_find(checks, header, 0)) != 0) {
537  const char *result;
538 
539  if ((result = cleanup_act(state, CLEANUP_ACT_CTXT_HEADER,
540  header, value, map_class))
541  == CLEANUP_ACT_DROP) {
542  return;
543  } else if (result != header) {
544  vstring_strcpy(header_buf, result);
545  hdr_opts = header_opts_find(result);
546  myfree((void *) result);
547  }
548  } else if (checks->error) {
549  msg_warn("%s: %s map lookup problem -- "
550  "message not accepted, try again later",
551  state->queue_id, checks->title);
552  state->errs |= CLEANUP_STAT_WRITE;
553  }
554  }
555 
556  /*
557  * If this is an "unknown" header, just copy it to the output without
558  * even bothering to fold long lines. cleanup_out() will split long
559  * headers that do not fit a REC_TYPE_NORM record.
560  */
561  if (hdr_opts == 0) {
562  cleanup_out_header(state, header_buf);
563  return;
564  }
565 
566  /*
567  * Allow 8-bit type info to override 7-bit type info. XXX Should reuse
568  * the effort that went into MIME header parsing.
569  */
570  hdrval = vstring_str(header_buf) + strlen(hdr_opts->name) + 1;
571  while (ISSPACE(*hdrval))
572  hdrval++;
573  /* trimblanks(hdrval, 0)[0] = 0; */
575  && hdr_opts->type == HDR_CONTENT_TRANSFER_ENCODING) {
576  for (cmp = code_map; cmp->name != 0; cmp++) {
577  if (strcasecmp(hdrval, cmp->name) == 0) {
578  if (strcasecmp(cmp->encoding, MAIL_ATTR_ENC_8BIT) == 0)
580  cmp->encoding);
581  break;
582  }
583  }
584  }
585 
586  /*
587  * Copy attachment etc. header blocks without further inspection.
588  */
589  if (header_class != MIME_HDR_PRIMARY) {
590  cleanup_out_header(state, header_buf);
591  return;
592  }
593 
594  /*
595  * Known header. Remember that we have seen at least one. Find out what
596  * we should do with this header: delete, count, rewrite. Note that we
597  * should examine headers even when they will be deleted from the output,
598  * because the addresses in those headers might be needed elsewhere.
599  *
600  * XXX 2821: Return-path breakage.
601  *
602  * RFC 821 specifies: When the receiver-SMTP makes the "final delivery" of a
603  * message it inserts at the beginning of the mail data a return path
604  * line. The return path line preserves the information in the
605  * <reverse-path> from the MAIL command. Here, final delivery means the
606  * message leaves the SMTP world. Normally, this would mean it has been
607  * delivered to the destination user, but in some cases it may be further
608  * processed and transmitted by another mail system.
609  *
610  * And that is what Postfix implements. Delivery agents prepend
611  * Return-Path:. In order to avoid cluttering up the message with
612  * possibly inconsistent Return-Path: information (the sender can change
613  * as the result of mail forwarding or mailing list delivery), Postfix
614  * removes any existing Return-Path: headers.
615  *
616  * RFC 2821 Section 4.4 specifies: A message-originating SMTP system
617  * SHOULD NOT send a message that already contains a Return-path header.
618  * SMTP servers performing a relay function MUST NOT inspect the message
619  * data, and especially not to the extent needed to determine if
620  * Return-path headers are present. SMTP servers making final delivery
621  * MAY remove Return-path headers before adding their own.
622  */
623  else {
624  state->headers_seen |= (1 << hdr_opts->type);
625  if (hdr_opts->type == HDR_MESSAGE_ID)
626  msg_info("%s: message-id=%s", state->queue_id, hdrval);
627  if (hdr_opts->type == HDR_RESENT_MESSAGE_ID)
628  msg_info("%s: resent-message-id=%s", state->queue_id, hdrval);
629  if (hdr_opts->type == HDR_RECEIVED) {
630  if (state->hop_count >= var_hopcount_limit) {
631  msg_warn("%s: message rejected: hopcount exceeded",
632  state->queue_id);
633  state->errs |= CLEANUP_STAT_HOPS;
634  }
635  /* Save our Received: header after maybe updating headers above. */
636  if (state->hop_count == 1)
637  argv_add(state->auto_hdrs, vstring_str(header_buf), ARGV_END);
638  }
639  if (CLEANUP_OUT_OK(state)) {
640  if (hdr_opts->flags & HDR_OPT_RR)
641  state->resent = "Resent-";
642  if ((hdr_opts->flags & HDR_OPT_SENDER)
643  && state->hdr_rewrite_context) {
644  cleanup_rewrite_sender(state, hdr_opts, header_buf);
645  } else if ((hdr_opts->flags & HDR_OPT_RECIP)
646  && state->hdr_rewrite_context) {
647  cleanup_rewrite_recip(state, hdr_opts, header_buf);
648  } else if ((hdr_opts->flags & HDR_OPT_DROP) == 0) {
649  cleanup_out_header(state, header_buf);
650  }
651  }
652  }
653 }
654 
655 /* cleanup_header_done_callback - insert missing message headers */
656 
657 static void cleanup_header_done_callback(void *context)
658 {
659  const char *myname = "cleanup_header_done_callback";
660  CLEANUP_STATE *state = (CLEANUP_STATE *) context;
661  char time_stamp[1024]; /* XXX locale dependent? */
662  struct tm *tp;
663  TOK822 *token;
664  TOK822 *dummy_token;
665  time_t tv;
666 
667  /*
668  * XXX Workaround: when we reach the end of headers, mime_state_update()
669  * may execute up to three call-backs before returning to the caller:
670  * head_out(), head_end(), and body_out() or body_end(). As long as
671  * call-backs don't return a result, each call-back has to check for
672  * itself if the previous call-back experienced a problem.
673  */
674  if (CLEANUP_OUT_OK(state) == 0)
675  return;
676 
677  /*
678  * Future proofing: the Milter client's header suppression algorithm
679  * assumes that the MTA prepends its own Received: header. This
680  * assupmtion may be violated after some source-code update. The
681  * following check ensures consistency, at least for local submission.
682  */
683  if (state->hop_count < 1) {
684  msg_warn("%s: message rejected: no Received: header",
685  state->queue_id);
686  state->errs |= CLEANUP_STAT_BAD;
687  return;
688  }
689 
690  /*
691  * Add a missing (Resent-)Message-Id: header. The message ID gives the
692  * time in GMT units, plus the local queue ID.
693  *
694  * XXX Message-Id is not a required message header (RFC 822 and RFC 2822).
695  *
696  * XXX It is the queue ID non-inode bits that prevent messages from getting
697  * the same Message-Id within the same second.
698  *
699  * XXX An arbitrary amount of time may pass between the start of the mail
700  * transaction and the creation of a queue file. Since we guarantee queue
701  * ID uniqueness only within a second, we must ensure that the time in
702  * the message ID matches the queue ID creation time, as long as we use
703  * the queue ID in the message ID.
704  *
705  * XXX We log a dummy name=value record so that we (hopefully) don't break
706  * compatibility with existing logfile analyzers, and so that we don't
707  * complicate future code that wants to log more name=value attributes.
708  */
710  && (state->headers_seen & (1 << (state->resent[0] ?
712  if (var_long_queue_ids) {
713  vstring_sprintf(state->temp1, "%s@%s",
714  state->queue_id, var_myhostname);
715  } else {
716  tv = state->handle->ctime.tv_sec;
717  tp = gmtime(&tv);
718  strftime(time_stamp, sizeof(time_stamp), "%Y%m%d%H%M%S", tp);
719  vstring_sprintf(state->temp1, "%s.%s@%s",
720  time_stamp, state->queue_id, var_myhostname);
721  }
722  cleanup_out_format(state, REC_TYPE_NORM, "%sMessage-Id: <%s>",
723  state->resent, vstring_str(state->temp1));
724  msg_info("%s: %smessage-id=<%s>",
725  state->queue_id, *state->resent ? "resent-" : "",
726  vstring_str(state->temp1));
727  state->headers_seen |= (1 << (state->resent[0] ?
729  }
730  if ((state->headers_seen & (1 << HDR_MESSAGE_ID)) == 0)
731  msg_info("%s: message-id=<>", state->queue_id);
732 
733  /*
734  * Add a missing (Resent-)Date: header. The date is in local time units,
735  * with the GMT offset at the end.
736  */
738  && (state->headers_seen & (1 << (state->resent[0] ?
739  HDR_RESENT_DATE : HDR_DATE))) == 0) {
740  cleanup_out_format(state, REC_TYPE_NORM, "%sDate: %s",
741  state->resent, mail_date(state->arrival_time.tv_sec));
742  }
743 
744  /*
745  * Add a missing (Resent-)From: header.
746  */
748  && (state->headers_seen & (1 << (state->resent[0] ?
749  HDR_RESENT_FROM : HDR_FROM))) == 0) {
750  quote_822_local(state->temp1, *state->sender ?
751  state->sender : MAIL_ADDR_MAIL_DAEMON);
752  if (*state->sender && state->fullname && *state->fullname) {
753  char *cp;
754 
755  /* Enforce some sanity on full name content. */
756  while ((cp = strchr(state->fullname, '\r')) != 0
757  || (cp = strchr(state->fullname, '\n')) != 0)
758  *cp = ' ';
759 
760  switch (hfrom_format_code) {
761 
762  /*
763  * "From: phrase <route-addr>". Quote the phrase if it
764  * contains specials or the "%!" legacy address operators.
765  */
767  vstring_sprintf(state->temp2, "%sFrom: ", state->resent);
768  if (state->fullname[strcspn(state->fullname,
769  "%!" LEX_822_SPECIALS)] == 0) {
770  /* Normalize whitespace. */
771  token = tok822_scan_limit(state->fullname, &dummy_token,
773  } else {
774  token = tok822_alloc(TOK822_QSTRING, state->fullname);
775  }
776  tok822_externalize(state->temp2, token, TOK822_STR_NONE);
777  tok822_free(token);
778  vstring_sprintf_append(state->temp2, " <%s>",
779  vstring_str(state->temp1));
780  break;
781 
782  /*
783  * "From: addr-spec (ctext)". This is the obsolete form.
784  */
786  vstring_sprintf(state->temp2, "%sFrom: %s ",
787  state->resent, vstring_str(state->temp1));
788  vstring_sprintf(state->temp1, "(%s)", state->fullname);
789  token = tok822_parse(vstring_str(state->temp1));
790  tok822_externalize(state->temp2, token, TOK822_STR_NONE);
791  tok822_free_tree(token);
792  break;
793  default:
794  msg_panic("%s: unknown header format %d",
795  myname, hfrom_format_code);
796  }
797  }
798 
799  /*
800  * "From: addr-spec". This is the form in the absence of full name
801  * information, also used for mail from mailer-daemon.
802  */
803  else {
804  vstring_sprintf(state->temp2, "%sFrom: %s",
805  state->resent, vstring_str(state->temp1));
806  }
807  CLEANUP_OUT_BUF(state, REC_TYPE_NORM, state->temp2);
808  }
809 
810  /*
811  * XXX 2821: Appendix B: The return address in the MAIL command SHOULD,
812  * if possible, be derived from the system's identity for the submitting
813  * (local) user, and the "From:" header field otherwise. If there is a
814  * system identity available, it SHOULD also be copied to the Sender
815  * header field if it is different from the address in the From header
816  * field. (Any Sender field that was already there SHOULD be removed.)
817  * Similar wording appears in RFC 2822 section 3.6.2.
818  *
819  * Postfix presently does not insert a Sender: header if envelope and From:
820  * address differ. Older Postfix versions assumed that the envelope
821  * sender address specifies the system identity and inserted Sender:
822  * whenever envelope and From: differed. This was wrong with relayed
823  * mail, and was often not even desirable with original submissions.
824  *
825  * XXX 2822 Section 3.6.2, as well as RFC 822 Section 4.1: FROM headers can
826  * contain multiple addresses. If this is the case, then a Sender: header
827  * must be provided with a single address.
828  *
829  * Postfix does not count the number of addresses in a From: header
830  * (although doing so is trivial, once the address is parsed).
831  */
832 
833  /*
834  * Add a missing destination header.
835  */
836 #define VISIBLE_RCPT ((1 << HDR_TO) | (1 << HDR_RESENT_TO) \
837  | (1 << HDR_CC) | (1 << HDR_RESENT_CC))
838 
840  && (state->headers_seen & VISIBLE_RCPT) == 0 && *var_rcpt_witheld) {
841  if (!is_header(var_rcpt_witheld)) {
842  msg_warn("bad %s header text \"%s\" -- "
843  "need \"headername: headervalue\"",
845  } else {
847  }
848  }
849 
850  /*
851  * Place a dummy PTR record right after the last header so that we can
852  * append headers without having to worry about clobbering the
853  * end-of-content marker.
854  */
855  if (state->milters || cleanup_milters) {
856  if ((state->append_hdr_pt_offset = vstream_ftell(state->dst)) < 0)
857  msg_fatal("%s: vstream_ftell %s: %m", myname, cleanup_path);
859  if ((state->append_hdr_pt_target = vstream_ftell(state->dst)) < 0)
860  msg_fatal("%s: vstream_ftell %s: %m", myname, cleanup_path);
861  state->body_offset = state->append_hdr_pt_target;
862  }
863 }
864 
865 /* cleanup_body_callback - output one body record */
866 
867 static void cleanup_body_callback(void *context, int type,
868  const char *buf, ssize_t len,
869  off_t offset)
870 {
871  CLEANUP_STATE *state = (CLEANUP_STATE *) context;
872 
873  /*
874  * XXX Workaround: when we reach the end of headers, mime_state_update()
875  * may execute up to three call-backs before returning to the caller:
876  * head_out(), head_end(), and body_out() or body_end(). As long as
877  * call-backs don't return a result, each call-back has to check for
878  * itself if the previous call-back experienced a problem.
879  */
880  if (CLEANUP_OUT_OK(state) == 0)
881  return;
882 
883  /*
884  * Crude message body content filter for emergencies. This code has
885  * several problems: it sees one line at a time; it looks at long lines
886  * only in chunks of line_length_limit (2048) characters; it is easily
887  * bypassed with encodings and other tricks.
888  */
889  if ((state->flags & CLEANUP_FLAG_FILTER)
891  && (var_body_check_len == 0 || offset < var_body_check_len)) {
892  const char *value;
893 
894  if ((value = maps_find(cleanup_body_checks, buf, 0)) != 0) {
895  const char *result;
896 
897  if ((result = cleanup_act(state, CLEANUP_ACT_CTXT_BODY,
898  buf, value, VAR_BODY_CHECKS))
899  == CLEANUP_ACT_DROP) {
900  return;
901  } else if (result != buf) {
902  cleanup_out(state, type, result, strlen(result));
903  myfree((void *) result);
904  return;
905  }
906  } else if (cleanup_body_checks->error) {
907  msg_warn("%s: %s map lookup problem -- "
908  "message not accepted, try again later",
910  state->errs |= CLEANUP_STAT_WRITE;
911  }
912  }
913  cleanup_out(state, type, buf, len);
914 }
915 
916 /* cleanup_message_headerbody - process message content, header and body */
917 
918 static void cleanup_message_headerbody(CLEANUP_STATE *state, int type,
919  const char *buf, ssize_t len)
920 {
921  const char *myname = "cleanup_message_headerbody";
922  const MIME_STATE_DETAIL *detail;
923  const char *cp;
924  char *dst;
925 
926  /*
927  * Reject unwanted characters.
928  *
929  * XXX Possible optimization: simplify the loop when the "reject" set
930  * contains only one character.
931  */
932  if ((state->flags & CLEANUP_FLAG_FILTER) && cleanup_reject_chars) {
933  for (cp = buf; cp < buf + len; cp++) {
934  if (memchr(vstring_str(cleanup_reject_chars),
935  *(const unsigned char *) cp,
937  cleanup_act(state, CLEANUP_ACT_CTXT_ANY,
938  buf, "REJECT disallowed character",
939  "character reject");
940  return;
941  }
942  }
943  }
944 
945  /*
946  * Strip unwanted characters. Don't overwrite the input.
947  *
948  * XXX Possible space+time optimization: use a bitset.
949  *
950  * XXX Possible optimization: simplify the loop when the "strip" set
951  * contains only one character.
952  *
953  * XXX Possible optimization: copy the input only if we really have to.
954  */
955  if ((state->flags & CLEANUP_FLAG_FILTER) && cleanup_strip_chars) {
956  VSTRING_RESET(state->stripped_buf);
957  VSTRING_SPACE(state->stripped_buf, len + 1);
958  dst = vstring_str(state->stripped_buf);
959  for (cp = buf; cp < buf + len; cp++)
960  if (!memchr(vstring_str(cleanup_strip_chars),
961  *(const unsigned char *) cp,
963  *dst++ = *cp;
964  *dst = 0;
965  buf = vstring_str(state->stripped_buf);
966  len = dst - buf;
967  }
968 
969  /*
970  * Copy text record to the output.
971  */
972  if (type == REC_TYPE_NORM || type == REC_TYPE_CONT) {
973  state->mime_errs = mime_state_update(state->mime_state, type, buf, len);
974  }
975 
976  /*
977  * If we have reached the end of the message content segment, record the
978  * current file position so we can compute the message size lateron.
979  */
980  else if (type == REC_TYPE_XTRA) {
981  state->mime_errs = mime_state_update(state->mime_state, type, buf, len);
982  if (state->milters || cleanup_milters)
983  /* Make room for body modification. */
985  /* Ignore header truncation after primary message headers. */
986  state->mime_errs &= ~MIME_ERR_TRUNC_HEADER;
987  if (state->mime_errs && state->reason == 0) {
988  state->errs |= CLEANUP_STAT_CONT;
989  detail = mime_state_detail(state->mime_errs);
990  state->reason = dsn_prepend(detail->dsn, detail->text);
991  }
992  state->mime_state = mime_state_free(state->mime_state);
993  if ((state->xtra_offset = vstream_ftell(state->dst)) < 0)
994  msg_fatal("%s: vstream_ftell %s: %m", myname, cleanup_path);
995  state->cont_length = state->xtra_offset - state->data_offset;
996  state->action = cleanup_extracted;
997  }
998 
999  /*
1000  * This should never happen.
1001  */
1002  else {
1003  msg_warn("%s: message rejected: "
1004  "unexpected record type %d in message content", myname, type);
1005  state->errs |= CLEANUP_STAT_BAD;
1006  }
1007 }
1008 
1009 /* cleanup_mime_error_callback - error report call-back routine */
1010 
1011 static void cleanup_mime_error_callback(void *context, int err_code,
1012  const char *text, ssize_t len)
1013 {
1014  CLEANUP_STATE *state = (CLEANUP_STATE *) context;
1015  const char *origin;
1016 
1017  /*
1018  * Message header too large errors are handled after the end of the
1019  * primary message headers.
1020  */
1021  if ((err_code & ~MIME_ERR_TRUNC_HEADER) != 0) {
1022  if ((origin = nvtable_find(state->attr, MAIL_ATTR_LOG_ORIGIN)) == 0)
1023  origin = MAIL_ATTR_ORG_NONE;
1024 #define TEXT_LEN (len < 100 ? (int) len : 100)
1025  msg_info("%s: reject: mime-error %s: %.*s from %s; from=<%s> to=<%s>",
1026  state->queue_id, mime_state_error(err_code), TEXT_LEN, text,
1027  origin, state->sender, state->recip ? state->recip : "unknown");
1028  }
1029 }
1030 
1031 /* cleanup_message - initialize message content segment */
1032 
1033 void cleanup_message(CLEANUP_STATE *state, int type, const char *buf, ssize_t len)
1034 {
1035  const char *myname = "cleanup_message";
1036  int mime_options;
1037 
1038  /*
1039  * Write the start-of-content segment marker.
1040  */
1041  cleanup_out_string(state, REC_TYPE_MESG, "");
1042  if ((state->data_offset = vstream_ftell(state->dst)) < 0)
1043  msg_fatal("%s: vstream_ftell %s: %m", myname, cleanup_path);
1044 
1045  /*
1046  * Set up MIME processing options, if any. MIME_OPT_DISABLE_MIME disables
1047  * special processing of Content-Type: headers, and thus, causes all text
1048  * after the primary headers to be treated as the message body.
1049  */
1050  mime_options = 0;
1051  if (var_disable_mime_input) {
1052  mime_options |= MIME_OPT_DISABLE_MIME;
1053  } else {
1054  /* Turn off content checks if bouncing or forwarding mail. */
1055  if (state->flags & CLEANUP_FLAG_FILTER) {
1057  mime_options |= MIME_OPT_REPORT_8BIT_IN_HEADER;
1059  mime_options |= MIME_OPT_REPORT_8BIT_IN_7BIT_BODY;
1060  if (var_strict_encoding)
1061  mime_options |= MIME_OPT_REPORT_ENCODING_DOMAIN;
1065  || *var_nesthdr_checks)
1066  mime_options |= MIME_OPT_REPORT_NESTING;
1067  }
1068  }
1069  state->mime_state = mime_state_alloc(mime_options,
1070  cleanup_header_callback,
1071  cleanup_header_done_callback,
1072  cleanup_body_callback,
1073  (MIME_STATE_ANY_END) 0,
1074  cleanup_mime_error_callback,
1075  (void *) state);
1076 
1077  /*
1078  * XXX Workaround: truncate a long message header so that we don't exceed
1079  * the default Sendmail libmilter request size limit of 65535.
1080  */
1081 #define KLUDGE_HEADER_LIMIT 60000
1082  if ((cleanup_milters || state->milters)
1085 
1086  /*
1087  * Pass control to the header processing routine.
1088  */
1089  state->action = cleanup_message_headerbody;
1090  cleanup_message_headerbody(state, type, buf, len);
1091 }
int msg_verbose
Definition: msg.c:177
const CLEANUP_STAT_DETAIL * cleanup_stat_detail(unsigned status)
int var_hopcount_limit
Definition: cleanup_init.c:122
void myfree(void *ptr)
Definition: mymalloc.c:207
#define HDR_OPT_DROP
Definition: header_opts.h:62
#define ARGV_END
Definition: argv.h:52
void cleanup_out_format(CLEANUP_STATE *state, int type, const char *fmt,...)
Definition: cleanup_out.c:155
int var_strict_8bit_body
Definition: mail_params.c:326
void cleanup_extracted(CLEANUP_STATE *, int, const char *, ssize_t)
int hfrom_format_code
Definition: cleanup_init.c:284
int cleanup_rewrite_tree(const char *, TOK822 *)
char * mystrdup(const char *str)
Definition: mymalloc.c:225
char * recip
Definition: cleanup.h:60
#define MAIL_ATTR_ENCODING
Definition: mail_proto.h:202
#define TOK822_ADDR
Definition: tok822.h:46
#define VAR_MIMEHDR_CHECKS
Definition: mail_params.h:1938
char * var_nesthdr_checks
Definition: cleanup_init.c:134
char * fullname
Definition: cleanup.h:58
MIME_STATE * mime_state_alloc(int flags, MIME_STATE_HEAD_OUT head_out, MIME_STATE_ANY_END head_end, MIME_STATE_BODY_OUT body_out, MIME_STATE_ANY_END body_end, MIME_STATE_ERR_PRINT err_print, void *context)
Definition: mime_state.c:493
#define CLEANUP_ACT_CTXT_ANY
const char * mail_date(time_t when)
Definition: mail_date.c:54
#define CLEANUP_FLAG_MAP_OK
Definition: cleanup_user.h:23
TOK822 ** tok822_grep(TOK822 *, int)
Definition: tok822_tree.c:292
NORETURN msg_panic(const char *fmt,...)
Definition: msg.c:295
off_t body_offset
Definition: cleanup.h:77
MAPS * cleanup_body_checks
Definition: cleanup_init.c:257
int var_auto_8bit_enc_hdr
Definition: cleanup_init.c:171
NVTABLE_INFO * nvtable_update(NVTABLE *table, const char *key, const char *value)
Definition: nvtable.c:111
#define vstring_str(vp)
Definition: vstring.h:71
off_t vstream_ftell(VSTREAM *stream)
Definition: vstream.c:1157
#define HDR_RESENT_FROM
Definition: header_opts.h:43
#define HDR_DATE
Definition: header_opts.h:33
#define CLEANUP_MASQ_FLAG_HDR_RCPT
Definition: cleanup.h:189
MAPS * cleanup_rcpt_canon_maps
Definition: cleanup_init.c:250
int var_strict_8bitmime
Definition: mail_params.c:324
int mime_errs
Definition: cleanup.h:93
#define MIME_HDR_MULTIPART
Definition: mime_state.h:81
char * reason
Definition: cleanup.h:89
const char * text
Definition: cleanup_user.h:87
int conv_time(const char *strval, int *timval, int def_unit)
Definition: conv_time.c:67
#define MIME_OPT_REPORT_ENCODING_DOMAIN
Definition: mime_state.h:44
char * redirect
Definition: cleanup.h:96
Definition: tok822.h:27
#define CLEANUP_FLAG_FILTER
Definition: cleanup_user.h:19
char * var_rcpt_witheld
Definition: cleanup_init.c:141
#define tok822_parse(cp)
Definition: tok822.h:84
MILTERS * milters
Definition: cleanup.h:109
#define CHECK(class, maps, var_name)
Definition: maps.h:22
int var_disable_mime_input
Definition: mail_params.c:322
VSTRING * vstring_truncate(VSTRING *vp, ssize_t len)
Definition: vstring.c:415
#define CLEANUP_OUT_BUF(s, t, b)
Definition: cleanup.h:236
#define MAIL_ATTR_ORG_NONE
Definition: mail_proto.h:228
int cleanup_comm_canon_flags
Definition: cleanup_init.c:251
#define CLEANUP_FLAG_DISCARD
Definition: cleanup_user.h:21
#define HDR_MESSAGE_ID
Definition: header_opts.h:37
void argv_add(ARGV *argvp,...)
Definition: argv.c:197
VSTRING * temp2
Definition: cleanup.h:50
#define MIME_OPT_REPORT_8BIT_IN_HEADER
Definition: mime_state.h:43
#define LEX_822_SPECIALS
Definition: lex_822.h:23
struct timeval arrival_time
Definition: cleanup.h:57
#define CLEANUP_STAT_HOPS
Definition: cleanup_user.h:61
ARGV * cleanup_masq_domains
Definition: cleanup_init.c:259
char * cleanup_path
Definition: cleanup_init.c:111
ARGV * argv_alloc(ssize_t len)
Definition: argv.c:149
off_t xtra_offset
Definition: cleanup.h:78
#define MAIL_ATTR_LOG_HELO_NAME
Definition: mail_proto.h:210
#define VSTRING_LEN(vp)
Definition: vstring.h:72
#define VAR_BODY_CHECKS
Definition: mail_params.h:1946
int var_strict_7bit_hdrs
Definition: mail_params.c:325
#define EXT_PROP_CANONICAL
Definition: ext_prop.h:17
#define CLEANUP_FLAG_HOLD
Definition: cleanup_user.h:20
#define MAIL_ATTR_LOG_PROTO_NAME
Definition: mail_proto.h:211
#define CLEANUP_ACT_DROP
#define MIME_OPT_DISABLE_MIME
Definition: mime_state.h:47
#define CLEANUP_CANON_FLAG_HDR_RCPT
Definition: cleanup.h:181
#define VISIBLE_RCPT
#define HDR_OPT_SENDER
Definition: header_opts.h:63
NVTABLE * attr
Definition: cleanup.h:91
VSTRING * vstring_strcpy(VSTRING *vp, const char *src)
Definition: vstring.c:431
TOK822 * tok822_free_tree(TOK822 *)
Definition: tok822_tree.c:262
#define CLEANUP_CANON_FLAG_HDR_FROM
Definition: cleanup.h:180
int cleanup_ext_prop_mask
Definition: cleanup_init.c:274
#define MIME_OPT_REPORT_8BIT_IN_7BIT_BODY
Definition: mime_state.h:42
const char * name
Definition: header_opts.h:17
char * dsn_prepend(const char *def_dsn, const char *text)
Definition: dsn_util.c:177
MAPS * cleanup_nesthdr_checks
Definition: cleanup_init.c:256
int cleanup_rcpt_canon_flags
Definition: cleanup_init.c:253
#define TOK822_STR_HEAD
Definition: tok822.h:92
VSTRING * stripped_buf
Definition: cleanup.h:51
bool var_long_queue_ids
Definition: mail_params.c:339
#define is_header(str)
Definition: is_header.h:17
VSTRING * vstring_sprintf_append(VSTRING *vp, const char *format,...)
Definition: vstring.c:624
#define HDR_OPT_RECIP
Definition: header_opts.h:64
#define MAIL_ATTR_LOG_ORIGIN
Definition: mail_proto.h:212
int mime_state_update(MIME_STATE *state, int rec_type, const char *text, ssize_t len)
Definition: mime_state.c:755
#define HFROM_FORMAT_CODE_OBS
Definition: cleanup.h:356
int cleanup_map11_tree(CLEANUP_STATE *, TOK822 *, MAPS *, int)
VSTRING * cleanup_reject_chars
Definition: cleanup_init.c:268
#define VAR_NESTHDR_CHECKS
Definition: mail_params.h:1942
TOK822 * tok822_scan_limit(const char *, TOK822 **, int)
Definition: tok822_parse.c:427
#define REC_TYPE_PTR_FORMAT
Definition: rec_type.h:179
#define REC_TYPE_CONT
Definition: rec_type.h:58
struct timeval ctime
Definition: mail_stream.h:46
struct TOK822 * head
Definition: tok822.h:32
MIME_STATE * mime_state_free(MIME_STATE *state)
Definition: mime_state.c:530
#define CLEANUP_ACT_CTXT_BODY
char * sender
Definition: cleanup.h:59
#define MIME_ERR_TRUNC_HEADER
Definition: mime_state.h:67
char * title
Definition: maps.h:23
#define HDR_RESENT_DATE
Definition: header_opts.h:42
int var_always_add_hdrs
Definition: cleanup_init.c:172
void(* MIME_STATE_ANY_END)(void *)
Definition: mime_state.h:30
void cleanup_message(CLEANUP_STATE *state, int type, const char *buf, ssize_t len)
off_t append_hdr_pt_offset
Definition: cleanup.h:84
#define VSTRING_RESET(vp)
Definition: vstring.h:77
#define REC_TYPE_MESG
Definition: rec_type.h:56
#define REC_TYPE_PTR
Definition: rec_type.h:67
#define MIME_HDR_PRIMARY
Definition: mime_state.h:80
char * hdr_rewrite_context
Definition: cleanup.h:94
#define MAIL_ADDR_MAIL_DAEMON
Definition: mail_addr.h:18
#define HDR_FROM
Definition: header_opts.h:36
MAPS * cleanup_mimehdr_checks
Definition: cleanup_init.c:255
void msg_warn(const char *fmt,...)
Definition: msg.c:215
ARGV * hbc_rcpt
Definition: cleanup.h:65
int headers_seen
Definition: cleanup.h:71
VSTRING * vstring_alloc(ssize_t len)
Definition: vstring.c:353
#define HDR_RECEIVED
Definition: header_opts.h:38
char * var_header_checks
Definition: cleanup_init.c:132
char * filter
Definition: cleanup.h:95
#define HFROM_FORMAT_CODE_STD
Definition: cleanup.h:355
VSTRING * tok822_externalize(VSTRING *, TOK822 *, int)
Definition: tok822_parse.c:270
#define KLUDGE_HEADER_LIMIT
int cleanup_send_canon_flags
Definition: cleanup_init.c:252
void(* action)(struct CLEANUP_STATE *, int, const char *, ssize_t)
Definition: cleanup.h:75
ARGV * auto_hdrs
Definition: cleanup.h:64
VSTRING * vstring_sprintf(VSTRING *vp, const char *format,...)
Definition: vstring.c:602
const MIME_STATE_DETAIL * mime_state_detail(int error_code)
Definition: mime_state.c:1163
char * queue_id
Definition: cleanup.h:56
#define VAR_RCPT_WITHELD
Definition: mail_params.h:375
VSTRING * tok822_internalize(VSTRING *, TOK822 *, int)
Definition: tok822_parse.c:199
MAPS * cleanup_comm_canon_maps
Definition: cleanup_init.c:248
#define TOK822_STR_NONE
Definition: tok822.h:86
int hop_count
Definition: cleanup.h:72
NORETURN msg_fatal(const char *fmt,...)
Definition: msg.c:249
const char * dsn
Definition: mime_state.h:62
#define CLEANUP_MASQ_FLAG_HDR_FROM
Definition: cleanup.h:188
#define CLEANUP_FLAG_FILTER_ALL
Definition: cleanup_user.h:29
#define HDR_CONTENT_TRANSFER_ENCODING
Definition: header_opts.h:31
const char * mime_state_error(int error_code)
Definition: mime_state.c:1149
VSTREAM * dst
Definition: cleanup.h:53
MAPS * cleanup_header_checks
Definition: cleanup_init.c:254
#define CLEANUP_STAT_CONT
Definition: cleanup_user.h:60
#define CLEANUP_STAT_BAD
Definition: cleanup_user.h:57
int error
Definition: maps.h:25
TOK822 * tok822_alloc(int, const char *)
Definition: tok822_node.c:55
#define CLEANUP_OUT_OK(s)
Definition: cleanup.h:239
#define TEXT_LEN
TOK822 * tok822_free(TOK822 *)
Definition: tok822_node.c:73
VSTRING * temp1
Definition: cleanup.h:49
int var_body_check_len
Definition: cleanup_init.c:146
#define CLEANUP_ACT_CTXT_HEADER
#define TOK822_STR_DEFL
Definition: tok822.h:91
TOK822 * tok822_parse_limit(const char *, int)
Definition: tok822_parse.c:479
#define VSTRING_SPACE(vp, len)
Definition: vstring.h:70
#define quote_822_local(dst, src)
#define TOK822_QSTRING
Definition: tok822.h:43
int strcasecmp(const char *s1, const char *s2)
Definition: strcasecmp.c:41
int var_header_limit
Definition: mail_params.c:320
int cleanup_masq_flags
Definition: cleanup_init.c:261
void const char void cleanup_out_header(CLEANUP_STATE *, VSTRING *)
Definition: cleanup_out.c:170
int cleanup_masquerade_tree(CLEANUP_STATE *, TOK822 *, ARGV *)
off_t cont_length
Definition: cleanup.h:79
const char * text
Definition: mime_state.h:63
#define REC_TYPE_XTRA
Definition: rec_type.h:62
VSTRING * vstring_free(VSTRING *vp)
Definition: vstring.c:380
const HEADER_OPTS * header_opts_find(const char *string)
Definition: header_opts.c:156
MAPS * cleanup_send_canon_maps
Definition: cleanup_init.c:249
#define MIME_HDR_NESTED
Definition: mime_state.h:82
#define REC_TYPE_NORM
Definition: rec_type.h:59
int var_token_limit
Definition: mail_params.c:321
char * var_mimehdr_checks
Definition: cleanup_init.c:133
#define CLEANUP_STAT_WRITE
Definition: cleanup_user.h:58
#define nvtable_find(table, key)
Definition: nvtable.h:27
#define ISSPACE(c)
Definition: sys_defs.h:1753
void cleanup_out(CLEANUP_STATE *, int, const char *, ssize_t)
Definition: cleanup_out.c:102
off_t append_hdr_pt_target
Definition: cleanup.h:85
MAIL_STREAM * handle
Definition: cleanup.h:54
#define MIME_OPT_REPORT_NESTING
Definition: mime_state.h:48
void cleanup_out_string(CLEANUP_STATE *, int, const char *)
Definition: cleanup_out.c:148
#define CLEANUP_STAT_DEFER
Definition: cleanup_user.h:64
char * var_myhostname
Definition: mail_params.c:223
MILTERS * cleanup_milters
Definition: cleanup_init.c:279
#define HDR_OPT_RR
Definition: header_opts.h:65
#define MAIL_ATTR_ENC_8BIT
Definition: mail_proto.h:203
const char * maps_find(MAPS *maps, const char *name, int flags)
Definition: maps.c:162
#define HDR_OPT_MIME
Definition: header_opts.h:67
off_t data_offset
Definition: cleanup.h:76
MIME_STATE * mime_state
Definition: cleanup.h:92
VSTRING * vstring_strcat(VSTRING *vp, const char *src)
Definition: vstring.c:459
char * resent
Definition: cleanup.h:73
int var_strict_encoding
Definition: mail_params.c:327
#define MAIL_ATTR_ENC_7BIT
Definition: mail_proto.h:204
#define VAR_HEADER_CHECKS
Definition: mail_params.h:1934
VSTRING * cleanup_strip_chars
Definition: cleanup_init.c:269
const char * dsn
Definition: cleanup_user.h:86
#define STREQUAL(x, y, l)
#define HDR_RESENT_MESSAGE_ID
Definition: header_opts.h:44
void msg_info(const char *fmt,...)
Definition: msg.c:199