Postfix3.3.1
bounce_template.c
[詳解]
1 /*++
2 /* NAME
3 /* bounce_template 3
4 /* SUMMARY
5 /* bounce template support
6 /* SYNOPSIS
7 /* #include <bounce_template.h>
8 /*
9 /* BOUNCE_TEMPLATE *bounce_template_create(def_template)
10 /* const BOUNCE_TEMPLATE *def_template;
11 /*
12 /* void bounce_template_free(template)
13 /* BOUNCE_TEMPLATE *template;
14 /*
15 /* void bounce_template_load(template, stream, buffer, origin)
16 /* BOUNCE_TEMPLATE *template;
17 /* VSTREAM *stream;
18 /* const char *buffer;
19 /* const char *origin;
20 /*
21 /* void bounce_template_headers(out_fn, stream, template,
22 /* rcpt, postmaster_copy)
23 /* int (*out_fn)(VSTREAM *, const char *, ...);
24 /* VSTREAM *stream;
25 /* BOUNCE_TEMPLATE *template;
26 /* const char *rcpt;
27 /* int postmaster_copy;
28 /*
29 /* const char *bounce_template_encoding(template)
30 /* BOUNCE_TEMPLATE *template;
31 /*
32 /* const char *bounce_template_charset(template)
33 /* BOUNCE_TEMPLATE *template;
34 /*
35 /* void bounce_template_expand(out_fn, stream, template)
36 /* int (*out_fn)(VSTREAM *, const char *);
37 /* VSTREAM *stream;
38 /* BOUNCE_TEMPLATE *template;
39 /*
40 /* void bounce_template_dump(stream, template)
41 /* VSTREAM *stream;
42 /* BOUNCE_TEMPLATE *template;
43 /*
44 /* int IS_FAILURE_TEMPLATE(template)
45 /* int IS_DELAY_TEMPLATE(template)
46 /* int IS_SUCCESS_TEMPLATE(template)
47 /* int IS_VERIFY_TEMPLATE(template)
48 /* BOUNCE_TEMPLATE *template;
49 /* DESCRIPTION
50 /* This module implements the built-in and external bounce
51 /* message template support. The content of a template are
52 /* private. To access information within a template, use
53 /* the API described in this document.
54 /*
55 /* bounce_template_create() creates a template, with the
56 /* specified default settings. The template defaults are not
57 /* copied.
58 /*
59 /* bounce_template_free() destroys a bounce message template.
60 /*
61 /* bounce_template_load() overrides a bounce template with the
62 /* specified buffer from the specified origin. The buffer and
63 /* origin are copied. Specify a null buffer and origin pointer
64 /* to reset the template to the defaults specified with
65 /* bounce_template_create().
66 /*
67 /* bounce_template_headers() sends the postmaster or non-postmaster
68 /* From/Subject/To message headers to the specified stream.
69 /* The recipient address is expected to be in RFC822 external
70 /* form. The postmaster_copy argument is one of POSTMASTER_COPY
71 /* or NO_POSTMASTER_COPY.
72 /*
73 /* bounce_template_encoding() returns the encoding (MAIL_ATTR_ENC_7BIT
74 /* or MAIL_ATTR_ENC_8BIT) for the bounce template message text.
75 /*
76 /* bounce_template_charset() returns the character set for the
77 /* bounce template message text.
78 /*
79 /* bounce_template_expand() expands the body text of the
80 /* specified template and writes the result to the specified
81 /* stream.
82 /*
83 /* bounce_template_dump() dumps the specified template to the
84 /* specified stream.
85 /*
86 /* The IS_MUMBLE_TEMPLATE() macros are predicates that
87 /* determine whether the template is of the specified type.
88 /* DIAGNOSTICS
89 /* Fatal error: out of memory, undefined macro name in template.
90 /* SEE ALSO
91 /* bounce_templates(3) bounce template group support
92 /* LICENSE
93 /* .ad
94 /* .fi
95 /* The Secure Mailer license must be distributed with this software.
96 /* AUTHOR(S)
97 /* Wietse Venema
98 /* IBM T.J. Watson Research
99 /* P.O. Box 704
100 /* Yorktown Heights, NY 10598, USA
101 /*--*/
102 
103 /* System library. */
104 
105 #include <sys_defs.h>
106 #include <string.h>
107 #include <ctype.h>
108 
109 #ifdef STRCASECMP_IN_STRINGS_H
110 #include <strings.h>
111 #endif
112 
113 /* Utility library. */
114 
115 #include <msg.h>
116 #include <mac_expand.h>
117 #include <split_at.h>
118 #include <stringops.h>
119 #include <mymalloc.h>
120 #ifndef NO_EAI
121 #include <midna_domain.h>
122 #endif
123 
124 /* Global library. */
125 
126 #include <mail_params.h>
127 #include <mail_proto.h>
128 #include <mail_conf.h>
129 #include <is_header.h>
130 
131 /* Application-specific. */
132 
133 #include <bounce_template.h>
134 
135  /*
136  * The following tables implement support for bounce template expansions of
137  * $<parameter_name>_days ($<parameter_name>_hours, etc.). The expansion of
138  * these is the actual parameter value divided by the number of seconds in a
139  * day (hour, etc.), so that we can produce nicely formatted bounce messages
140  * with time values converted into the appropriate units.
141  *
142  * Ideally, the bounce template processor would strip the _days etc. suffix
143  * from the parameter name, and use the parameter name to look up the actual
144  * parameter value and its default value (the default value specifies the
145  * default time unit of that parameter (seconds, minutes, etc.)), and use
146  * this to convert the parameter string value into the corresponding number
147  * of seconds. The bounce template processor would then use the _hours etc.
148  * suffix from the bounce template to divide this number by the number of
149  * seconds in an hour, etc. and produce the number that is needed for the
150  * template.
151  *
152  * Unfortunately, there exists no code to look up default values by parameter
153  * name. If such code existed, then we could do the _days, _hours, etc.
154  * conversion with every main.cf time parameter without having to know in
155  * advance what time parameter names exist.
156  *
157  * So we have to either maintain our own table of all time related main.cf
158  * parameter names and defaults (like the postconf command does) or we make
159  * a special case for a few parameters of special interest.
160  *
161  * We go for the second solution. There are only a few parameters that need
162  * this treatment, and there will be more special cases when individual
163  * queue files get support for individual expiration times, and when other
164  * queue file information needs to be reported in bounce template messages.
165  *
166  * A really lame implementation would simply strip the optional s, h, d, etc.
167  * suffix from the actual (string) parameter value and not do any conversion
168  * at all to hours, days or weeks. But then the information in delay warning
169  * notices could be seriously incorrect.
170  */
171 typedef struct {
172  const char *suffix; /* days, hours, etc. */
173  int suffix_len; /* byte count */
174  int divisor; /* divisor */
176 
177 #define STRING_AND_LEN(x) (x), (sizeof(x) - 1)
178 
179 static const BOUNCE_TIME_DIVISOR time_divisors[] = {
180  STRING_AND_LEN("seconds"), 1,
181  STRING_AND_LEN("minutes"), 60,
182  STRING_AND_LEN("hours"), 60 * 60,
183  STRING_AND_LEN("days"), 24 * 60 * 60,
184  STRING_AND_LEN("weeks"), 7 * 24 * 60 * 60,
185  0, 0,
186 };
187 
188  /*
189  * The few special-case main.cf parameters that have support for _days, etc.
190  * suffixes for automatic conversion when expanded into a bounce template.
191  */
192 typedef struct {
193  const char *param_name; /* parameter name */
194  int param_name_len; /* name length */
195  int *value; /* parameter value */
197 
198 static const BOUNCE_TIME_PARAMETER time_parameter[] = {
201  0, 0,
202 };
203 
204  /*
205  * Parameters whose value may have to be converted to UTF-8 for presentation
206  * purposes.
207  */
208 typedef struct {
209  const char *param_name; /* parameter name */
210  char **value; /* parameter value */
212 
213 static const BOUNCE_STR_PARAMETER str_parameter[] = {
216  0, 0,
217 };
218 
219  /*
220  * SLMs.
221  */
222 #define STR(x) vstring_str(x)
223 
224 /* bounce_template_create - create one template */
225 
227 {
228  BOUNCE_TEMPLATE *tp;
229 
230  tp = (BOUNCE_TEMPLATE *) mymalloc(sizeof(*tp));
231  *tp = *prototype;
232  return (tp);
233 }
234 
235 /* bounce_template_free - destroy one template */
236 
238 {
239  if (tp->buffer) {
240  myfree(tp->buffer);
241  myfree((void *) tp->origin);
242  }
243  myfree((void *) tp);
244 }
245 
246 /* bounce_template_reset - reset template to default */
247 
248 static void bounce_template_reset(BOUNCE_TEMPLATE *tp)
249 {
250  myfree(tp->buffer);
251  myfree((void *) tp->origin);
252  *tp = *(tp->prototype);
253 }
254 
255 /* bounce_template_load - override one template */
256 
257 void bounce_template_load(BOUNCE_TEMPLATE *tp, const char *origin,
258  const char *buffer)
259 {
260 
261  /*
262  * Clean up after a previous call.
263  */
264  if (tp->buffer)
265  bounce_template_reset(tp);
266 
267  /*
268  * Postpone the work of template parsing until it is really needed. Most
269  * bounce service calls never need a template.
270  */
271  if (buffer && origin) {
273  tp->buffer = mystrdup(buffer);
274  tp->origin = mystrdup(origin);
275  }
276 }
277 
278 /* bounce_template_parse_buffer - initialize template */
279 
280 static void bounce_template_parse_buffer(BOUNCE_TEMPLATE *tp)
281 {
282  char *tval = tp->buffer;
283  char *cp;
284  char **cpp;
285  int cpp_len;
286  int cpp_used;
287  int hlen;
288  char *hval;
289 
290  /*
291  * Sanity check.
292  */
293  if ((tp->flags & BOUNCE_TMPL_FLAG_NEW_BUFFER) == 0)
294  msg_panic("bounce_template_parse_buffer: nothing to do here");
296 
297  /*
298  * Discard the unusable template and use the default one instead.
299  */
300 #define CLEANUP_AND_RETURN() do { \
301  bounce_template_reset(tp); \
302  return; \
303  } while (0)
304 
305  /*
306  * Parse pseudo-header labels and values.
307  *
308  * XXX EAI: allow UTF8 in template headers when responding to SMTPUTF8
309  * message. Sending SMTPUTF8 in response to non-SMTPUTF8 mail would make
310  * no sense.
311  */
312 #define GETLINE(line, buf) \
313  (((line) = (buf)) != 0 ? ((buf) = split_at((buf), '\n'), (line)) : 0)
314 
315  while ((GETLINE(cp, tval)) != 0 && (hlen = is_header(cp)) > 0) {
316  for (hval = cp + hlen; *hval && (*hval == ':' || ISSPACE(*hval)); hval++)
317  *hval = 0;
318  if (*hval == 0) {
319  msg_warn("%s: empty \"%s\" header value in %s template "
320  "-- ignoring this template",
321  tp->origin, cp, tp->class);
323  }
324  if (!allascii(hval)) {
325  msg_warn("%s: non-ASCII \"%s\" header value in %s template "
326  "-- ignoring this template",
327  tp->origin, cp, tp->class);
329  }
330  if (strcasecmp("charset", cp) == 0) {
331  tp->mime_charset = hval;
332  } else if (strcasecmp("from", cp) == 0) {
333  tp->from = hval;
334  } else if (strcasecmp("subject", cp) == 0) {
335  tp->subject = hval;
336  } else if (strcasecmp("postmaster-subject", cp) == 0) {
337  if (tp->postmaster_subject == 0) {
338  msg_warn("%s: inapplicable \"%s\" header label in %s template "
339  "-- ignoring this template",
340  tp->origin, cp, tp->class);
342  }
343  tp->postmaster_subject = hval;
344  } else {
345  msg_warn("%s: unknown \"%s\" header label in %s template "
346  "-- ignoring this template",
347  tp->origin, cp, tp->class);
349  }
350  }
351 
352  /*
353  * Skip blank lines between header and message text.
354  */
355  while (cp && (*cp == 0 || allspace(cp)))
356  (void) GETLINE(cp, tval);
357  if (cp == 0) {
358  msg_warn("%s: missing message text in %s template "
359  "-- ignoring this template",
360  tp->origin, tp->class);
362  }
363 
364  /*
365  * Is this 7bit or 8bit text? If the character set is US-ASCII, then
366  * don't allow 8bit text. Don't assume 8bit when charset was changed.
367  */
368 #define NON_ASCII(p) ((p) && *(p) && !allascii((p)))
369 
370  if (NON_ASCII(cp) || NON_ASCII(tval)) {
371  if (strcasecmp(tp->mime_charset, "us-ascii") == 0) {
372  msg_warn("%s: 8-bit message text in %s template",
373  tp->origin, tp->class);
374  msg_warn("please specify a charset value other than us-ascii");
375  msg_warn("-- ignoring this template for now");
377  }
379  }
380 
381  /*
382  * Collect the message text and null-terminate the result.
383  */
384  cpp_len = 10;
385  cpp_used = 0;
386  cpp = (char **) mymalloc(sizeof(*cpp) * cpp_len);
387  while (cp) {
388  cpp[cpp_used++] = cp;
389  if (cpp_used >= cpp_len) {
390  cpp = (char **) myrealloc((void *) cpp,
391  sizeof(*cpp) * 2 * cpp_len);
392  cpp_len *= 2;
393  }
394  (void) GETLINE(cp, tval);
395  }
396  cpp[cpp_used] = 0;
397  tp->message_text = (const char **) cpp;
398 }
399 
400 /* bounce_template_lookup - lookup $name value */
401 
402 static const char *bounce_template_lookup(const char *key, int unused_mode,
403  void *context)
404 {
405  BOUNCE_TEMPLATE *tp = (BOUNCE_TEMPLATE *) context;
406  const BOUNCE_TIME_PARAMETER *bp;
407  const BOUNCE_TIME_DIVISOR *bd;
408  const BOUNCE_STR_PARAMETER *sp;
409  static VSTRING *buf;
410  int result;
411  const char *asc_val;
412  const char *utf8_val;
413 
414  /*
415  * Look for parameter names that can have a time unit suffix, and scale
416  * the time value according to the suffix.
417  */
418  for (bp = time_parameter; bp->param_name; bp++) {
419  if (strncmp(key, bp->param_name, bp->param_name_len) == 0
420  && key[bp->param_name_len] == '_') {
421  for (bd = time_divisors; bd->suffix; bd++) {
422  if (strcmp(key + bp->param_name_len + 1, bd->suffix) == 0) {
423  result = bp->value[0] / bd->divisor;
424  if (result > 999 && bd->divisor < 86400) {
425  msg_warn("%s: excessive result \"%d\" in %s "
426  "template conversion of parameter \"%s\"",
427  tp->origin, result, tp->class, key);
428  msg_warn("please increase time unit \"%s\" of \"%s\" "
429  "in %s template", bd->suffix, key, tp->class);
430  msg_warn("for instructions see the bounce(5) manual");
431  } else if (result == 0 && bp->value[0] && bd->divisor > 1) {
432  msg_warn("%s: zero result in %s template "
433  "conversion of parameter \"%s\"",
434  tp->origin, tp->class, key);
435  msg_warn("please reduce time unit \"%s\" of \"%s\" "
436  "in %s template", bd->suffix, key, tp->class);
437  msg_warn("for instructions see the bounce(5) manual");
438  }
439  if (buf == 0)
440  buf = vstring_alloc(10);
441  vstring_sprintf(buf, "%d", result);
442  return (STR(buf));
443  }
444  }
445  msg_fatal("%s: unrecognized suffix \"%s\" in parameter \"%s\"",
446  tp->origin,
447  key + bp->param_name_len + 1, key);
448  }
449  }
450 
451  /*
452  * Look for parameter names that may have to be up-converted for
453  * presentation purposes.
454  */
455 #ifndef NO_EAI
456  if (var_smtputf8_enable) {
457  for (sp = str_parameter; sp->param_name; sp++) {
458  if (strcmp(key, sp->param_name) == 0) {
459  asc_val = sp->value[0];
460  if (!allascii(asc_val)) {
461  msg_warn("%s: conversion \"%s\" failed: "
462  "non-ASCII input value: \"%s\"",
463  tp->origin, key, asc_val);
464  return (asc_val);
465  } else if ((utf8_val = midna_domain_to_utf8(asc_val)) == 0) {
466  msg_warn("%s: conversion \"%s\" failed: "
467  "input value: \"%s\"",
468  tp->origin, key, asc_val);
469  return (asc_val);
470  } else {
471  return (utf8_val);
472  }
473  }
474  }
475  }
476 #endif
477  return (mail_conf_lookup_eval(key));
478 }
479 
480 /* bounce_template_headers - send template headers */
481 
482 void bounce_template_headers(BOUNCE_XP_PRN_FN out_fn, VSTREAM *fp,
483  BOUNCE_TEMPLATE *tp,
484  const char *rcpt,
485  int postmaster_copy)
486 {
488  bounce_template_parse_buffer(tp);
489 
490  out_fn(fp, "From: %s", tp->from);
491  out_fn(fp, "Subject: %s", tp->postmaster_subject && postmaster_copy ?
492  tp->postmaster_subject : tp->subject);
493  out_fn(fp, "To: %s", rcpt);
494 }
495 
496 /* bounce_template_expand - expand template to stream */
497 
499  BOUNCE_TEMPLATE *tp)
500 {
501  VSTRING *buf = vstring_alloc(100);
502  const char **cpp;
503  int stat;
504 
506  bounce_template_parse_buffer(tp);
507 
508  for (cpp = tp->message_text; *cpp; cpp++) {
509  stat = mac_expand(buf, *cpp, MAC_EXP_FLAG_PRINTABLE, (char *) 0,
510  bounce_template_lookup, (void *) tp);
511  if (stat & MAC_PARSE_ERROR)
512  msg_fatal("%s: bad $name syntax in %s template: %s",
513  tp->origin, tp->class, *cpp);
514  if (stat & MAC_PARSE_UNDEF)
515  msg_fatal("%s: undefined $name in %s template: %s",
516  tp->origin, tp->class, *cpp);
517  out_fn(fp, STR(buf));
518  }
519  vstring_free(buf);
520 }
521 
522 /* bounce_template_dump - dump template to stream */
523 
525 {
526  const char **cpp;
527 
529  bounce_template_parse_buffer(tp);
530 
531  vstream_fprintf(fp, "Charset: %s\n", tp->mime_charset);
532  vstream_fprintf(fp, "From: %s\n", tp->from);
533  vstream_fprintf(fp, "Subject: %s\n", tp->subject);
534  if (tp->postmaster_subject)
535  vstream_fprintf(fp, "Postmaster-Subject: %s\n",
536  tp->postmaster_subject);
537  vstream_fprintf(fp, "\n");
538  for (cpp = tp->message_text; *cpp; cpp++)
539  vstream_fprintf(fp, "%s\n", *cpp);
540 }
const char * mime_charset
#define BOUNCE_TMPL_FLAG_NEW_BUFFER
void myfree(void *ptr)
Definition: mymalloc.c:207
#define MAC_EXP_FLAG_PRINTABLE
Definition: mac_expand.h:27
#define NON_ASCII(p)
char * mystrdup(const char *str)
Definition: mymalloc.c:225
#define STRING_AND_LEN(x)
int mac_expand(VSTRING *result, const char *pattern, int flags, const char *filter, MAC_EXP_LOOKUP_FN lookup, void *context)
Definition: mac_expand.c:593
const char * class
NORETURN msg_panic(const char *fmt,...)
Definition: msg.c:295
#define stat(p, s)
Definition: warn_stat.h:18
void * myrealloc(void *ptr, ssize_t len)
Definition: mymalloc.c:175
const struct BOUNCE_TEMPLATE * prototype
const char * subject
char * var_mydomain
Definition: mail_params.c:224
void bounce_template_free(BOUNCE_TEMPLATE *tp)
int var_smtputf8_enable
Definition: mail_params.c:343
int allspace(const char *string)
Definition: allspace.c:39
#define is_header(str)
Definition: is_header.h:17
VSTREAM * vstream_fprintf(VSTREAM *stream, const char *fmt,...)
Definition: vstream.c:1348
const char * midna_domain_to_utf8(const char *name)
Definition: midna_domain.c:275
const char ** message_text
#define VAR_MYHOSTNAME
Definition: mail_params.h:140
void msg_warn(const char *fmt,...)
Definition: msg.c:215
VSTRING * vstring_alloc(ssize_t len)
Definition: vstring.c:353
#define VAR_DELAY_WARN_TIME
Definition: mail_params.h:765
VSTRING * vstring_sprintf(VSTRING *vp, const char *format,...)
Definition: vstring.c:602
void bounce_template_expand(BOUNCE_XP_PUT_FN out_fn, VSTREAM *fp, BOUNCE_TEMPLATE *tp)
BOUNCE_TEMPLATE * bounce_template_create(const BOUNCE_TEMPLATE *prototype)
#define allascii(s)
Definition: stringops.h:66
int var_delay_warn_time
Definition: bounce.c:192
NORETURN msg_fatal(const char *fmt,...)
Definition: msg.c:249
const char * mail_conf_lookup_eval(const char *name)
Definition: mail_conf.c:262
#define VAR_MYDOMAIN
Definition: mail_params.h:143
const char * mime_encoding
int strcasecmp(const char *s1, const char *s2)
Definition: strcasecmp.c:41
const char * origin
const char * from
VSTRING * vstring_free(VSTRING *vp)
Definition: vstring.c:380
#define MAC_PARSE_UNDEF
Definition: mac_parse.h:28
#define ISSPACE(c)
Definition: sys_defs.h:1753
#define STR(x)
char * var_myhostname
Definition: mail_params.c:223
int const char typedef int(* BOUNCE_XP_PUT_FN)(VSTREAM *, const char *)
#define MAIL_ATTR_ENC_8BIT
Definition: mail_proto.h:203
void bounce_template_headers(BOUNCE_XP_PRN_FN out_fn, VSTREAM *fp, BOUNCE_TEMPLATE *tp, const char *rcpt, int postmaster_copy)
void bounce_template_dump(VSTREAM *fp, BOUNCE_TEMPLATE *tp)
void bounce_template_load(BOUNCE_TEMPLATE *tp, const char *origin, const char *buffer)
#define CLEANUP_AND_RETURN()
#define GETLINE(line, buf)
#define VAR_MAX_QUEUE_TIME
Definition: mail_params.h:753
int var_max_queue_time
Definition: bounce.c:191
#define MAC_PARSE_ERROR
Definition: mac_parse.h:27
void * mymalloc(ssize_t len)
Definition: mymalloc.c:150
const char * postmaster_subject