1/*++
2/* NAME
3/*	bounce_templates 3
4/* SUMMARY
5/*	bounce template group support
6/* SYNOPSIS
7/*	#include <bounce_template.h>
8/*
9/*	typedef struct {
10/* .in +4
11/*		BOUNCE_TEMPLATE *failure;
12/*		BOUNCE_TEMPLATE *delay;
13/*		BOUNCE_TEMPLATE *success;
14/*		BOUNCE_TEMPLATE *verify;
15/* .in -4
16/*	} BOUNCE_TEMPLATES;
17/*
18/*	BOUNCE_TEMPLATES *bounce_templates_create(void)
19/*
20/*	void	bounce_templates_free(templates)
21/*	BOUNCE_TEMPLATES *templates;
22/*
23/*	void	bounce_templates_load(stream, templates)
24/*	VSTREAM	*stream;
25/*	BOUNCE_TEMPLATES *templates;
26/*
27/*	void	bounce_templates_expand(stream, templates)
28/*	VSTREAM	*stream;
29/*	BOUNCE_TEMPLATES *templates;
30/*
31/*	void	bounce_templates_dump(stream, templates)
32/*	VSTREAM	*stream;
33/*	BOUNCE_TEMPLATES *templates;
34/* DESCRIPTION
35/*	This module implements support for bounce template groups
36/*	(i.e. groups that contain one template of each type).
37/*
38/*	bounce_templates_create() creates a bounce template group,
39/*	with default settings.
40/*
41/*	bounce_templates_free() destroys a bounce template group.
42/*
43/*	bounce_templates_load() reads zero or more bounce templates
44/*	from the specified file to override built-in templates.
45/*
46/*	bounce_templates_expand() expands $name macros and writes
47/*	the text portions of the specified bounce template group
48/*	to the specified stream.
49/*
50/*	bounce_templates_dump() writes the complete content of the
51/*	specified bounce template group to the specified stream.
52/*	The format is compatible with bounce_templates_load().
53/* DIAGNOSTICS
54/*	Fatal error: out of memory, undefined macro name in template.
55/* SEE ALSO
56/*	bounce_template(3) bounce template support
57/* LICENSE
58/* .ad
59/* .fi
60/*	The Secure Mailer license must be distributed with this software.
61/* AUTHOR(S)
62/*	Wietse Venema
63/*	IBM T.J. Watson Research
64/*	P.O. Box 704
65/*	Yorktown Heights, NY 10598, USA
66/*--*/
67
68/* System library. */
69
70#include <sys_defs.h>
71#include <ctype.h>
72#include <string.h>
73
74/* Utility library. */
75
76#include <msg.h>
77#include <mymalloc.h>
78#include <stringops.h>
79#include <vstring.h>
80#include <vstream.h>
81#include <vstring_vstream.h>
82
83/* Global library. */
84
85#include <mail_addr.h>
86#include <mail_proto.h>
87
88/* Application-specific. */
89
90#include <bounce_template.h>
91
92 /*
93  * The fail template is for permanent failure.
94  */
95static const char *def_bounce_failure_body[] = {
96    "This is the mail system at host $myhostname.",
97    "",
98    "I'm sorry to have to inform you that your message could not",
99    "be delivered to one or more recipients. It's attached below.",
100    "",
101    "For further assistance, please send mail to " MAIL_ADDR_POSTMASTER ".",
102    "",
103    "If you do so, please include this problem report. You can",
104    "delete your own text from the attached returned message.",
105    "",
106    "                   The mail system",
107    0,
108};
109
110static const BOUNCE_TEMPLATE def_bounce_failure_template = {
111    0,
112    BOUNCE_TMPL_CLASS_FAILURE,
113    "[built-in]",
114    "us-ascii",
115    MAIL_ATTR_ENC_7BIT,
116    MAIL_ADDR_MAIL_DAEMON " (Mail Delivery System)",
117    "Undelivered Mail Returned to Sender",
118    "Postmaster Copy: Undelivered Mail",
119    def_bounce_failure_body,
120    &def_bounce_failure_template,
121};
122
123 /*
124  * The delay template is for delayed mail notifications.
125  */
126static const char *def_bounce_delay_body[] = {
127    "This is the mail system at host $myhostname.",
128    "",
129    "####################################################################",
130    "# THIS IS A WARNING ONLY.  YOU DO NOT NEED TO RESEND YOUR MESSAGE. #",
131    "####################################################################",
132    "",
133    "Your message could not be delivered for more than $delay_warning_time_hours hour(s)."
134    ,
135    "It will be retried until it is $maximal_queue_lifetime_days day(s) old.",
136    "",
137    "For further assistance, please send mail to " MAIL_ADDR_POSTMASTER ".",
138    "",
139    "If you do so, please include this problem report. You can",
140    "delete your own text from the attached returned message.",
141    "",
142    "                   The mail system",
143    0,
144};
145
146static const BOUNCE_TEMPLATE def_bounce_delay_template = {
147    0,
148    BOUNCE_TMPL_CLASS_DELAY,
149    "[built-in]",
150    "us-ascii",
151    MAIL_ATTR_ENC_7BIT,
152    MAIL_ADDR_MAIL_DAEMON " (Mail Delivery System)",
153    "Delayed Mail (still being retried)",
154    "Postmaster Warning: Delayed Mail",
155    def_bounce_delay_body,
156    &def_bounce_delay_template
157};
158
159 /*
160  * The success template is for "delivered", "expanded" and "relayed" success
161  * notifications.
162  */
163static const char *def_bounce_success_body[] = {
164    "This is the mail system at host $myhostname.",
165    "",
166    "Your message was successfully delivered to the destination(s)",
167    "listed below. If the message was delivered to mailbox you will",
168    "receive no further notifications. Otherwise you may still receive",
169    "notifications of mail delivery errors from other systems.",
170    "",
171    "                   The mail system",
172    0,
173};
174
175static const BOUNCE_TEMPLATE def_bounce_success_template = {
176    0,
177    BOUNCE_TMPL_CLASS_SUCCESS,
178    "[built-in]",
179    "us-ascii",
180    MAIL_ATTR_ENC_7BIT,
181    MAIL_ADDR_MAIL_DAEMON " (Mail Delivery System)",
182    "Successful Mail Delivery Report",
183    0,
184    def_bounce_success_body,
185    &def_bounce_success_template,
186};
187
188 /*
189  * The "verify" template is for verbose delivery (sendmail -v) and for
190  * address verification (sendmail -bv).
191  */
192static const char *def_bounce_verify_body[] = {
193    "This is the mail system at host $myhostname.",
194    "",
195    "Enclosed is the mail delivery report that you requested.",
196    "",
197    "                   The mail system",
198    0,
199};
200
201static const BOUNCE_TEMPLATE def_bounce_verify_template = {
202    0,
203    BOUNCE_TMPL_CLASS_VERIFY,
204    "[built-in]",
205    "us-ascii",
206    MAIL_ATTR_ENC_7BIT,
207    MAIL_ADDR_MAIL_DAEMON " (Mail Delivery System)",
208    "Mail Delivery Status Report",
209    0,
210    def_bounce_verify_body,
211    &def_bounce_verify_template,
212};
213
214 /*
215  * SLMs.
216  */
217#define STR(x)	vstring_str(x)
218
219/* bounce_templates_create - create template group */
220
221BOUNCE_TEMPLATES *bounce_templates_create(void)
222{
223    BOUNCE_TEMPLATES *bs;
224
225    bs = (BOUNCE_TEMPLATES *) mymalloc(sizeof(*bs));
226    bs->failure = bounce_template_create(&def_bounce_failure_template);
227    bs->delay = bounce_template_create(&def_bounce_delay_template);
228    bs->success = bounce_template_create(&def_bounce_success_template);
229    bs->verify = bounce_template_create(&def_bounce_verify_template);
230    return (bs);
231}
232
233/* bounce_templates_free - destroy template group */
234
235void    bounce_templates_free(BOUNCE_TEMPLATES *bs)
236{
237    bounce_template_free(bs->failure);
238    bounce_template_free(bs->delay);
239    bounce_template_free(bs->success);
240    bounce_template_free(bs->verify);
241    myfree((char *) bs);
242}
243
244/* bounce_templates_load - load template or group from stream */
245
246void    bounce_templates_load(VSTREAM *fp, BOUNCE_TEMPLATES *ts)
247{
248    VSTRING *line_buf;
249    char   *member_name;
250    VSTRING *multi_line_buf = 0;
251    VSTRING *saved_member_name = 0;
252    VSTRING *saved_end_marker = 0;
253    char   *value;
254    int     lineno;
255    const char *err;
256    char   *cp;
257    int     len;			/* Grr... */
258
259    /*
260     * XXX That's a lot of non-reusable code to parse a configuration file.
261     * Unfortunately, much of the "name = value" infrastructure is married to
262     * the dict(3) class which doesn't really help here.
263     */
264    line_buf = vstring_alloc(100);
265    lineno = 1;
266    while (vstring_get_nonl(line_buf, fp) > 0) {
267	lineno++;
268	cp = STR(line_buf) + strspn(STR(line_buf), " \t\n\v\f\r");
269	if (*cp == 0 || *cp == '#')
270	    continue;
271	if ((err = split_nameval(STR(line_buf), &member_name, &value)) != 0)
272	    msg_fatal("%s, line %d: %s: \"%s\"",
273		      VSTREAM_PATH(fp), lineno, err, STR(line_buf));
274	if (value[0] == '<' && value[1] == '<') {
275	    value += 2;
276	    while (ISSPACE(*value))
277		value++;
278	    if (*value == 0)
279		msg_fatal("%s, line %d: missing end marker after <<",
280			  VSTREAM_PATH(fp), lineno);
281	    if (!ISALNUM(*value))
282		msg_fatal("%s, line %d: malformed end marker after <<",
283			  VSTREAM_PATH(fp), lineno);
284	    if (multi_line_buf == 0) {
285		saved_member_name = vstring_alloc(100);
286		saved_end_marker = vstring_alloc(100);
287		multi_line_buf = vstring_alloc(100);
288	    } else
289		VSTRING_RESET(multi_line_buf);
290	    vstring_strcpy(saved_member_name, member_name);
291	    vstring_strcpy(saved_end_marker, value);
292	    while (vstring_get_nonl(line_buf, fp) > 0) {
293		lineno++;
294		if (strcmp(STR(line_buf), STR(saved_end_marker)) == 0)
295		    break;
296		if (VSTRING_LEN(multi_line_buf) > 0)
297		    vstring_strcat(multi_line_buf, "\n");
298		vstring_strcat(multi_line_buf, STR(line_buf));
299	    }
300	    if (vstream_feof(fp))
301		msg_warn("%s, line %d: missing \"%s\" end marker",
302			  VSTREAM_PATH(fp), lineno, value);
303	    member_name = STR(saved_member_name);
304	    value = STR(multi_line_buf);
305	}
306#define MATCH_TMPL_NAME(tname, tname_len, mname) \
307    (strncmp(tname, mname, tname_len = strlen(tname)) == 0 \
308	&& strcmp(mname + tname_len, "_template") == 0)
309
310	if (MATCH_TMPL_NAME(ts->failure->class, len, member_name))
311	    bounce_template_load(ts->failure, VSTREAM_PATH(fp), value);
312	else if (MATCH_TMPL_NAME(ts->delay->class, len, member_name))
313	    bounce_template_load(ts->delay, VSTREAM_PATH(fp), value);
314	else if (MATCH_TMPL_NAME(ts->success->class, len, member_name))
315	    bounce_template_load(ts->success, VSTREAM_PATH(fp), value);
316	else if (MATCH_TMPL_NAME(ts->verify->class, len, member_name))
317	    bounce_template_load(ts->verify, VSTREAM_PATH(fp), value);
318	else
319	    msg_warn("%s, line %d: unknown template name: %s "
320		     "-- ignoring this template",
321		     VSTREAM_PATH(fp), lineno, member_name);
322    }
323    vstring_free(line_buf);
324    if (multi_line_buf) {
325	vstring_free(saved_member_name);
326	vstring_free(saved_end_marker);
327	vstring_free(multi_line_buf);
328    }
329}
330
331/* bounce_plain_out - output line as plain text */
332
333static int bounce_plain_out(VSTREAM *fp, const char *text)
334{
335    vstream_fprintf(fp, "%s\n", text);
336    return (0);
337}
338
339/* bounce_templates_expand - dump expanded template group text to stream */
340
341void    bounce_templates_expand(VSTREAM *fp, BOUNCE_TEMPLATES *ts)
342{
343    BOUNCE_TEMPLATE *tp;
344
345    tp = ts->failure;
346    vstream_fprintf(fp, "expanded_%s_text = <<EOF\n", tp->class);
347    bounce_template_expand(bounce_plain_out, fp, tp);
348    vstream_fprintf(fp, "EOF\n\n");
349
350    tp = ts->delay;
351    vstream_fprintf(fp, "expanded_%s_text = <<EOF\n", tp->class);
352    bounce_template_expand(bounce_plain_out, fp, tp);
353    vstream_fprintf(fp, "EOF\n\n");
354
355    tp = ts->success;
356    vstream_fprintf(fp, "expanded_%s_text = <<EOF\n", tp->class);
357    bounce_template_expand(bounce_plain_out, fp, tp);
358    vstream_fprintf(fp, "EOF\n\n");
359
360    tp = ts->verify;
361    vstream_fprintf(fp, "expanded_%s_text = <<EOF\n", tp->class);
362    bounce_template_expand(bounce_plain_out, fp, tp);
363    vstream_fprintf(fp, "EOF\n");
364}
365
366/* bounce_templates_dump - dump bounce template group to stream */
367
368void    bounce_templates_dump(VSTREAM *fp, BOUNCE_TEMPLATES *ts)
369{
370    BOUNCE_TEMPLATE *tp;
371
372    tp = ts->failure;
373    vstream_fprintf(fp, "%s_template = <<EOF\n", tp->class);
374    bounce_template_dump(fp, tp);
375    vstream_fprintf(fp, "EOF\n\n");
376
377    tp = ts->delay;
378    vstream_fprintf(fp, "%s_template = <<EOF\n", tp->class);
379    bounce_template_dump(fp, tp);
380    vstream_fprintf(fp, "EOF\n\n");
381
382    tp = ts->success;
383    vstream_fprintf(fp, "%s_template = <<EOF\n", tp->class);
384    bounce_template_dump(fp, tp);
385    vstream_fprintf(fp, "EOF\n\n");
386
387    tp = ts->verify;
388    vstream_fprintf(fp, "%s_template = <<EOF\n", tp->class);
389    bounce_template_dump(fp, tp);
390    vstream_fprintf(fp, "EOF\n");
391}
392