No empty .Rs/.Re
[netbsd-mini2440.git] / external / ibm-public / postfix / dist / src / bounce / bounce_template.c
blob217a1935dd521505d4f754d4ec230e5164a412ec
1 /* $NetBSD$ */
3 /*++
4 /* NAME
5 /* bounce_template 3
6 /* SUMMARY
7 /* bounce template support
8 /* SYNOPSIS
9 /* #include <bounce_template.h>
11 /* BOUNCE_TEMPLATE *bounce_template_create(def_template)
12 /* const BOUNCE_TEMPLATE *def_template;
14 /* void bounce_template_free(template)
15 /* BOUNCE_TEMPLATE *template;
17 /* void bounce_template_load(template, stream, buffer, origin)
18 /* BOUNCE_TEMPLATE *template;
19 /* VSTREAM *stream;
20 /* const char *buffer;
21 /* const char *origin;
23 /* void bounce_template_headers(out_fn, stream, template,
24 /* rcpt, postmaster_copy)
25 /* int (*out_fn)(VSTREAM *, const char *, ...);
26 /* VSTREAM *stream;
27 /* BOUNCE_TEMPLATE *template;
28 /* const char *rcpt;
29 /* int postmaster_copy;
31 /* const char *bounce_template_encoding(template)
32 /* BOUNCE_TEMPLATE *template;
34 /* const char *bounce_template_charset(template)
35 /* BOUNCE_TEMPLATE *template;
37 /* void bounce_template_expand(out_fn, stream, template)
38 /* int (*out_fn)(VSTREAM *, const char *);
39 /* VSTREAM *stream;
40 /* BOUNCE_TEMPLATE *template;
42 /* void bounce_template_dump(stream, template)
43 /* VSTREAM *stream;
44 /* BOUNCE_TEMPLATE *template;
46 /* int IS_FAILURE_TEMPLATE(template)
47 /* int IS_DELAY_TEMPLATE(template)
48 /* int IS_SUCCESS_TEMPLATE(template)
49 /* int IS_VERIFY_TEMPLATE(template)
50 /* BOUNCE_TEMPLATE *template;
51 /* DESCRIPTION
52 /* This module implements the built-in and external bounce
53 /* message template support. The content of a template are
54 /* private. To access information within a template, use
55 /* the API described in this document.
57 /* bounce_template_create() creates a template, with the
58 /* specified default settings. The template defaults are not
59 /* copied.
61 /* bounce_template_free() destroys a bounce message template.
63 /* bounce_template_load() overrides a bounce template with the
64 /* specified buffer from the specified origin. The buffer and
65 /* origin are copied. Specify a null buffer and origin pointer
66 /* to reset the template to the defaults specified with
67 /* bounce_template_create().
69 /* bounce_template_headers() sends the postmaster or non-postmaster
70 /* From/Subject/To message headers to the specified stream.
71 /* The recipient address is expected to be in RFC822 external
72 /* form. The postmaster_copy argument is one of POSTMASTER_COPY
73 /* or NO_POSTMASTER_COPY.
75 /* bounce_template_encoding() returns the encoding (MAIL_ATTR_ENC_7BIT
76 /* or MAIL_ATTR_ENC_8BIT) for the bounce template message text.
78 /* bounce_template_charset() returns the character set for the
79 /* bounce template message text.
81 /* bounce_template_expand() expands the body text of the
82 /* specified template and writes the result to the specified
83 /* stream.
85 /* bounce_template_dump() dumps the specified template to the
86 /* specified stream.
88 /* The IS_MUMBLE_TEMPLATE() macros are predicates that
89 /* determine whether the template is of the specified type.
90 /* DIAGNOSTICS
91 /* Fatal error: out of memory, undefined macro name in template.
92 /* SEE ALSO
93 /* bounce_templates(3) bounce template group support
94 /* LICENSE
95 /* .ad
96 /* .fi
97 /* The Secure Mailer license must be distributed with this software.
98 /* AUTHOR(S)
99 /* Wietse Venema
100 /* IBM T.J. Watson Research
101 /* P.O. Box 704
102 /* Yorktown Heights, NY 10598, USA
103 /*--*/
105 /* System library. */
107 #include <sys_defs.h>
108 #include <string.h>
109 #include <ctype.h>
111 #ifdef STRCASECMP_IN_STRINGS_H
112 #include <strings.h>
113 #endif
115 /* Utility library. */
117 #include <msg.h>
118 #include <mac_expand.h>
119 #include <split_at.h>
120 #include <stringops.h>
121 #include <mymalloc.h>
123 /* Global library. */
125 #include <mail_params.h>
126 #include <mail_proto.h>
127 #include <mail_conf.h>
128 #include <is_header.h>
130 /* Application-specific. */
132 #include <bounce_template.h>
135 * The following tables implement support for bounce template expansions of
136 * $<parameter_name>_days ($<parameter_name>_hours, etc.). The expansion of
137 * these is the actual parameter value divided by the number of seconds in a
138 * day (hour, etc.), so that we can produce nicely formatted bounce messages
139 * with time values converted into the appropriate units.
141 * Ideally, the bounce template processor would strip the _days etc. suffix
142 * from the parameter name, and use the parameter name to look up the actual
143 * parameter value and its default value (the default value specifies the
144 * default time unit of that parameter (seconds, minutes, etc.)), and use
145 * this to convert the parameter string value into the corresponding number
146 * of seconds. The bounce template processor would then use the _hours etc.
147 * suffix from the bounce template to divide this number by the number of
148 * seconds in an hour, etc. and produce the number that is needed for the
149 * template.
151 * Unfortunately, there exists no code to look up default values by parameter
152 * name. If such code existed, then we could do the _days, _hours, etc.
153 * conversion with every main.cf time parameter without having to know in
154 * advance what time parameter names exist.
156 * So we have to either maintain our own table of all time related main.cf
157 * parameter names and defaults (like the postconf command does) or we make
158 * a special case for a few parameters of special interest.
160 * We go for the second solution. There are only a few parameters that need
161 * this treatment, and there will be more special cases when individual
162 * queue files get support for individual expiration times, and when other
163 * queue file information needs to be reported in bounce template messages.
165 * A really lame implementation would simply strip the optional s, h, d, etc.
166 * suffix from the actual (string) parameter value and not do any conversion
167 * at all to hours, days or weeks. But then the information in delay warning
168 * notices could be seriously incorrect.
170 typedef struct {
171 const char *suffix; /* days, hours, etc. */
172 int suffix_len; /* byte count */
173 int divisor; /* divisor */
174 } BOUNCE_TIME_DIVISOR;
176 #define STRING_AND_LEN(x) (x), (sizeof(x) - 1)
178 static const BOUNCE_TIME_DIVISOR time_divisors[] = {
179 STRING_AND_LEN("seconds"), 1,
180 STRING_AND_LEN("minutes"), 60,
181 STRING_AND_LEN("hours"), 60 * 60,
182 STRING_AND_LEN("days"), 24 * 60 * 60,
183 STRING_AND_LEN("weeks"), 7 * 24 * 60 * 60,
184 0, 0,
188 * The few special-case main.cf parameters that have support for _days, etc.
189 * suffixes for automatic conversion when expanded into a bounce template.
191 typedef struct {
192 const char *param_name; /* parameter name */
193 int param_name_len; /* name length */
194 int *value; /* parameter value */
195 } BOUNCE_TIME_PARAMETER;
197 static const BOUNCE_TIME_PARAMETER time_parameter[] = {
198 STRING_AND_LEN(VAR_DELAY_WARN_TIME), &var_delay_warn_time,
199 STRING_AND_LEN(VAR_MAX_QUEUE_TIME), &var_max_queue_time,
200 0, 0,
204 * SLMs.
206 #define STR(x) vstring_str(x)
208 /* bounce_template_create - create one template */
210 BOUNCE_TEMPLATE *bounce_template_create(const BOUNCE_TEMPLATE *prototype)
212 BOUNCE_TEMPLATE *tp;
214 tp = (BOUNCE_TEMPLATE *) mymalloc(sizeof(*tp));
215 *tp = *prototype;
216 return (tp);
219 /* bounce_template_free - destroy one template */
221 void bounce_template_free(BOUNCE_TEMPLATE *tp)
223 if (tp->buffer) {
224 myfree(tp->buffer);
225 myfree((char *) tp->origin);
227 myfree((char *) tp);
230 /* bounce_template_reset - reset template to default */
232 static void bounce_template_reset(BOUNCE_TEMPLATE *tp)
234 myfree(tp->buffer);
235 myfree((char *) tp->origin);
236 *tp = *(tp->prototype);
239 /* bounce_template_load - override one template */
241 void bounce_template_load(BOUNCE_TEMPLATE *tp, const char *origin,
242 const char *buffer)
246 * Clean up after a previous call.
248 if (tp->buffer)
249 bounce_template_reset(tp);
252 * Postpone the work of template parsing until it is really needed. Most
253 * bounce service calls never need a template.
255 if (buffer && origin) {
256 tp->flags |= BOUNCE_TMPL_FLAG_NEW_BUFFER;
257 tp->buffer = mystrdup(buffer);
258 tp->origin = mystrdup(origin);
262 /* bounce_template_parse_buffer - initialize template */
264 static void bounce_template_parse_buffer(BOUNCE_TEMPLATE *tp)
266 char *tval = tp->buffer;
267 char *cp;
268 char **cpp;
269 int cpp_len;
270 int cpp_used;
271 int hlen;
272 char *hval;
275 * Sanity check.
277 if ((tp->flags & BOUNCE_TMPL_FLAG_NEW_BUFFER) == 0)
278 msg_panic("bounce_template_parse_buffer: nothing to do here");
279 tp->flags &= ~BOUNCE_TMPL_FLAG_NEW_BUFFER;
282 * Discard the unusable template and use the default one instead.
284 #define CLEANUP_AND_RETURN() do { \
285 bounce_template_reset(tp); \
286 return; \
287 } while (0)
290 * Parse pseudo-header labels and values.
292 #define GETLINE(line, buf) \
293 (((line) = (buf)) != 0 ? ((buf) = split_at((buf), '\n'), (line)) : 0)
295 while ((GETLINE(cp, tval)) != 0 && (hlen = is_header(cp)) > 0) {
296 for (hval = cp + hlen; *hval && (*hval == ':' || ISSPACE(*hval)); hval++)
297 *hval = 0;
298 if (*hval == 0) {
299 msg_warn("%s: empty \"%s\" header value in %s template "
300 "-- ignoring this template",
301 tp->origin, cp, tp->class);
302 CLEANUP_AND_RETURN();
304 if (!allascii(hval)) {
305 msg_warn("%s: non-ASCII \"%s\" header value in %s template "
306 "-- ignoring this template",
307 tp->origin, cp, tp->class);
308 CLEANUP_AND_RETURN();
310 if (strcasecmp("charset", cp) == 0) {
311 tp->mime_charset = hval;
312 } else if (strcasecmp("from", cp) == 0) {
313 tp->from = hval;
314 } else if (strcasecmp("subject", cp) == 0) {
315 tp->subject = hval;
316 } else if (strcasecmp("postmaster-subject", cp) == 0) {
317 if (tp->postmaster_subject == 0) {
318 msg_warn("%s: inapplicable \"%s\" header label in %s template "
319 "-- ignoring this template",
320 tp->origin, cp, tp->class);
321 CLEANUP_AND_RETURN();
323 tp->postmaster_subject = hval;
324 } else {
325 msg_warn("%s: unknown \"%s\" header label in %s template "
326 "-- ignoring this template",
327 tp->origin, cp, tp->class);
328 CLEANUP_AND_RETURN();
333 * Skip blank lines between header and message text.
335 while (cp && (*cp == 0 || allspace(cp)))
336 (void) GETLINE(cp, tval);
337 if (cp == 0) {
338 msg_warn("%s: missing message text in %s template "
339 "-- ignoring this template",
340 tp->origin, tp->class);
341 CLEANUP_AND_RETURN();
345 * Is this 7bit or 8bit text? If the character set is US-ASCII, then
346 * don't allow 8bit text. Don't assume 8bit when charset was changed.
348 #define NON_ASCII(p) ((p) && *(p) && !allascii((p)))
350 if (NON_ASCII(cp) || NON_ASCII(tval)) {
351 if (strcasecmp(tp->mime_charset, "us-ascii") == 0) {
352 msg_warn("%s: 8-bit message text in %s template",
353 tp->origin, tp->class);
354 msg_warn("please specify a charset value other than us-ascii");
355 msg_warn("-- ignoring this template for now");
356 CLEANUP_AND_RETURN();
358 tp->mime_encoding = MAIL_ATTR_ENC_8BIT;
362 * Collect the message text and null-terminate the result.
364 cpp_len = 10;
365 cpp_used = 0;
366 cpp = (char **) mymalloc(sizeof(*cpp) * cpp_len);
367 while (cp) {
368 cpp[cpp_used++] = cp;
369 if (cpp_used >= cpp_len) {
370 cpp = (char **) myrealloc((char *) cpp,
371 sizeof(*cpp) * 2 * cpp_len);
372 cpp_len *= 2;
374 (void) GETLINE(cp, tval);
376 cpp[cpp_used] = 0;
377 tp->message_text = (const char **) cpp;
380 /* bounce_template_lookup - lookup $name value */
382 static const char *bounce_template_lookup(const char *key, int unused_mode,
383 char *context)
385 BOUNCE_TEMPLATE *tp = (BOUNCE_TEMPLATE *) context;
386 const BOUNCE_TIME_PARAMETER *bp;
387 const BOUNCE_TIME_DIVISOR *bd;
388 static VSTRING *buf;
389 int result;
392 * Look for parameter names that can have a time unit suffix, and scale
393 * the time value according to the suffix.
395 for (bp = time_parameter; bp->param_name; bp++) {
396 if (strncmp(key, bp->param_name, bp->param_name_len) == 0
397 && key[bp->param_name_len] == '_') {
398 for (bd = time_divisors; bd->suffix; bd++) {
399 if (strcmp(key + bp->param_name_len + 1, bd->suffix) == 0) {
400 result = bp->value[0] / bd->divisor;
401 if (result > 999 && bd->divisor < 86400) {
402 msg_warn("%s: excessive result \"%d\" in %s "
403 "template conversion of parameter \"%s\"",
404 tp->origin, result, tp->class, key);
405 msg_warn("please increase time unit \"%s\" of \"%s\" "
406 "in %s template", bd->suffix, key, tp->class);
407 msg_warn("for instructions see the bounce(5) manual");
408 } else if (result == 0 && bp->value[0] && bd->divisor > 1) {
409 msg_warn("%s: zero result in %s template "
410 "conversion of parameter \"%s\"",
411 tp->origin, tp->class, key);
412 msg_warn("please reduce time unit \"%s\" of \"%s\" "
413 "in %s template", bd->suffix, key, tp->class);
414 msg_warn("for instructions see the bounce(5) manual");
416 if (buf == 0)
417 buf = vstring_alloc(10);
418 vstring_sprintf(buf, "%d", result);
419 return (STR(buf));
422 msg_fatal("%s: unrecognized suffix \"%s\" in parameter \"%s\"",
423 tp->origin,
424 key + bp->param_name_len + 1, key);
427 return (mail_conf_lookup_eval(key));
430 /* bounce_template_headers - send template headers */
432 void bounce_template_headers(BOUNCE_XP_PRN_FN out_fn, VSTREAM *fp,
433 BOUNCE_TEMPLATE *tp,
434 const char *rcpt,
435 int postmaster_copy)
437 if (tp->flags & BOUNCE_TMPL_FLAG_NEW_BUFFER)
438 bounce_template_parse_buffer(tp);
440 out_fn(fp, "From: %s", tp->from);
441 out_fn(fp, "Subject: %s", tp->postmaster_subject && postmaster_copy ?
442 tp->postmaster_subject : tp->subject);
443 out_fn(fp, "To: %s", rcpt);
446 /* bounce_template_expand - expand template to stream */
448 void bounce_template_expand(BOUNCE_XP_PUT_FN out_fn, VSTREAM *fp,
449 BOUNCE_TEMPLATE *tp)
451 VSTRING *buf = vstring_alloc(100);
452 const char **cpp;
453 int stat;
454 const char *filter = "\t !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~";
456 if (tp->flags & BOUNCE_TMPL_FLAG_NEW_BUFFER)
457 bounce_template_parse_buffer(tp);
459 for (cpp = tp->message_text; *cpp; cpp++) {
460 stat = mac_expand(buf, *cpp, MAC_EXP_FLAG_NONE, filter,
461 bounce_template_lookup, (char *) tp);
462 if (stat & MAC_PARSE_ERROR)
463 msg_fatal("%s: bad $name syntax in %s template: %s",
464 tp->origin, tp->class, *cpp);
465 if (stat & MAC_PARSE_UNDEF)
466 msg_fatal("%s: undefined $name in %s template: %s",
467 tp->origin, tp->class, *cpp);
468 out_fn(fp, STR(buf));
470 vstring_free(buf);
473 /* bounce_template_dump - dump template to stream */
475 void bounce_template_dump(VSTREAM *fp, BOUNCE_TEMPLATE *tp)
477 const char **cpp;
479 if (tp->flags & BOUNCE_TMPL_FLAG_NEW_BUFFER)
480 bounce_template_parse_buffer(tp);
482 vstream_fprintf(fp, "Charset: %s\n", tp->mime_charset);
483 vstream_fprintf(fp, "From: %s\n", tp->from);
484 vstream_fprintf(fp, "Subject: %s\n", tp->subject);
485 if (tp->postmaster_subject)
486 vstream_fprintf(fp, "Postmaster-Subject: %s\n",
487 tp->postmaster_subject);
488 vstream_fprintf(fp, "\n");
489 for (cpp = tp->message_text; *cpp; cpp++)
490 vstream_fprintf(fp, "%s\n", *cpp);