1/*++
2/* NAME
3/*	smtp_reply_footer 3
4/* SUMMARY
5/*	SMTP reply footer text support
6/* SYNOPSIS
7/*	#include <smtp_reply_footer.h>
8/*
9/*	int	smtp_reply_footer(buffer, start, template, filter,
10/*					lookup, context)
11/*	VSTRING	*buffer;
12/*	ssize_t	start;
13/*	char	*template;
14/*	const char *filter;
15/*	const char *(*lookup) (const char *name, char *context);
16/*	char	*context;
17/* DESCRIPTION
18/*	smtp_reply_footer() expands a reply template, and appends
19/*	the result to an existing reply text.
20/*
21/*	Arguments:
22/* .IP buffer
23/*	Result buffer. This should contain a properly formatted
24/*	one-line or multi-line SMTP reply, with or without the final
25/*	<CR><LF>. The reply code and optional enhanced status code
26/*	will be replicated in the footer text.  One space character
27/*	after the SMTP reply code is replaced by '-'. If the existing
28/*	reply ends in <CR><LF>, the result text will also end in
29/*	<CR><LF>.
30/* .IP start
31/*	The beginning of the SMTP reply that the footer will be
32/*	appended to. This supports applications that buffer up
33/*	multiple responses in one buffer.
34/* .IP template
35/*	Template text, with optional $name attributes that will be
36/*	expanded. The two-character sequence "\n" is replaced by a
37/*	line break followed by a copy of the original SMTP reply
38/*	code and optional enhanced status code.
39/*	The two-character sequence "\c" at the start of the template
40/*	suppresses the line break between the reply text and the
41/*	template text.
42/* .IP filter
43/*	The set of characters that are allowed in attribute expansion.
44/* .IP lookup
45/*	Attribute name/value lookup function. The result value must
46/*	be a null for a name that is not found, otherwise a pointer
47/*	to null-terminated string.
48/* .IP context
49/*	Call-back context for the lookup function.
50/* SEE ALSO
51/*	mac_expand(3) macro expansion
52/* DIAGNOSTICS
53/*	smtp_reply_footer() returns 0 upon success, -1 if the
54/*	existing reply text is malformed.
55/*
56/*	Fatal errors: memory allocation problem.
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 <string.h>
72#include <ctype.h>
73
74/* Utility library. */
75
76#include <msg.h>
77#include <vstring.h>
78
79/* Global library. */
80
81#include <dsn_util.h>
82#include <smtp_reply_footer.h>
83
84/* SLMs. */
85
86#define STR	vstring_str
87
88int     smtp_reply_footer(VSTRING *buffer, ssize_t start,
89			          char *template,
90			          const char *filter,
91			          MAC_EXP_LOOKUP_FN lookup,
92			          char *context)
93{
94    const char *myname = "smtp_reply_footer";
95    char   *cp;
96    char   *next;
97    char   *end;
98    ssize_t dsn_len;
99    int     crlf_at_end = 0;
100    int     reply_patch_undo_offs = -1;
101
102    /*
103     * Sanity check.
104     */
105    if (start < 0 || start > VSTRING_LEN(buffer))
106	msg_panic("%s: bad start: %ld", myname, (long) start);
107    if (*template == 0)
108	msg_panic("%s: empty template", myname);
109
110    /*
111     * Scan and patch the original response. If the response is not what we
112     * expect, we stop making changes.
113     */
114    for (cp = STR(buffer) + start, end = cp + strlen(cp);;) {
115	if (!ISDIGIT(cp[0]) || !ISDIGIT(cp[1]) || !ISDIGIT(cp[2])
116	    || (cp[3] != ' ' && cp[3] != '-'))
117	    return (-1);
118	cp[3] = '-';
119	reply_patch_undo_offs = cp + 3 - STR(buffer);
120	if ((next = strstr(cp, "\r\n")) == 0) {
121	    next = end;
122	    break;
123	}
124	cp = next + 2;
125	if (cp == end) {
126	    crlf_at_end = 1;
127	    break;
128	}
129    }
130
131    /*
132     * Truncate text after the first null, and truncate the trailing CRLF.
133     */
134    if (next < vstring_end(buffer))
135	vstring_truncate(buffer, next - STR(buffer));
136
137    /*
138     * Append the footer text one line at a time. Caution: before we append
139     * parts from the buffer to itself, we must extend the buffer first,
140     * otherwise we would have a dangling pointer "read" bug.
141     */
142    dsn_len = dsn_valid(STR(buffer) + start + 4);
143    for (cp = template, end = cp + strlen(cp);;) {
144	if ((next = strstr(cp, "\\n")) != 0) {
145	    *next = 0;
146	} else {
147	    next = end;
148	}
149	if (cp == template && strncmp(cp, "\\c", 2) == 0) {
150	    /* Handle \c at start of template. */
151	    cp += 2;
152	} else {
153	    /* Append a clone of the SMTP reply code. */
154	    vstring_strcat(buffer, "\r\n");
155	    VSTRING_SPACE(buffer, 3);
156	    vstring_strncat(buffer, STR(buffer) + start, 3);
157	    vstring_strcat(buffer, next != end ? "-" : " ");
158	    /* Append a clone of the optional enhanced status code. */
159	    if (dsn_len > 0) {
160		VSTRING_SPACE(buffer, dsn_len);
161		vstring_strncat(buffer, STR(buffer) + start + 4, (int) dsn_len);
162		vstring_strcat(buffer, " ");
163	    }
164	    reply_patch_undo_offs = -1;
165	}
166	/* Append one line of footer text. */
167	mac_expand(buffer, cp, MAC_EXP_FLAG_APPEND, filter, lookup, context);
168	if (next < end) {
169	    *next = '\\';
170	    cp = next + 2;
171	} else
172	    break;
173    }
174    if (reply_patch_undo_offs > 0)
175	STR(buffer)[reply_patch_undo_offs] = ' ';
176    if (crlf_at_end)
177	vstring_strcat(buffer, "\r\n");
178    return (0);
179}
180