1/*++
2/* NAME
3/*	dsn_buf 3
4/* SUMMARY
5/*	delivery status buffer
6/* SYNOPSIS
7/*	#include <dsn_buf.h>
8/*
9/*	typedef struct {
10/* .in +4
11/*		/* Convenience member */
12/*		DSN	dsn;		/* light-weight, dsn(3) */
13/*		/* Formal members... */
14/*		VSTRING *status;	/* RFC 3463 */
15/*		VSTRING *action;	/* RFC 3464 */
16/*		VSTRING	*mtype;		/* dns */
17/*		VSTRING	*mname;		/* host or domain */
18/*		VSTRING	*dtype;		/* smtp, x-unix */
19/*		VSTRING *dtext;		/* RFC 2821, sysexits.h */
20/*		/* Informal members... */
21/*		VSTRING	*reason;	/* informal text */
22/* .in -4
23/*	} DSN_BUF;
24/*
25/*	DSN_BUF	*dsb_create(void)
26/*
27/*	DSN_BUF	*dsb_update(dsb, status, action, mtype, mname, dtype,
28/*				dtext, reason_fmt, ...)
29/*	DSN_BUF	*dsb;
30/*	const char *status;
31/*	const char *action;
32/*	const char *mtype;
33/*	const char *mname;
34/*	const char *dtype;
35/*	const char *dtext;
36/*	const char *reason_fmt;
37/*
38/*	DSN_BUF	*dsb_simple(dsb, status, reason_fmt, ...)
39/*	DSN_BUF	*dsb;
40/*	const char *status;
41/*	const char *reason_fmt;
42/*
43/*	DSN_BUF	*dsb_unix(dsb, status, dtext, reason_fmt, ...)
44/*	DSN_BUF	*dsb;
45/*	const char *status;
46/*	const char *reason_fmt;
47/*
48/*	DSN_BUF	*dsb_formal(dsb, status, action, mtype, mname, dtype,
49/*				dtext)
50/*	DSN_BUF	*dsb;
51/*	const char *status;
52/*	const char *action;
53/*	const char *mtype;
54/*	const char *mname;
55/*	const char *dtype;
56/*	const char *dtext;
57/*
58/*	DSN_BUF	*dsb_status(dsb, status)
59/*	DSN_BUF	*dsb;
60/*	const char *status;
61/*
62/*	void	dsb_reset(dsb)
63/*	DSN_BUF	*dsb;
64/*
65/*	void	dsb_free(dsb)
66/*	DSN_BUF	*dsb;
67/*
68/*	DSN	*DSN_FROM_DSN_BUF(dsb)
69/*	DSN_BUF	*dsb;
70/* DESCRIPTION
71/*	This module implements a simple to update delivery status
72/*	buffer for Postfix-internal use. Typically it is filled in
73/*	the course of delivery attempt, and then formatted into a
74/*	DSN structure for external notification.
75/*
76/*	dsb_create() creates initialized storage for formal RFC 3464
77/*	attributes, and human-readable informal text.
78/*
79/*	dsb_update() updates all fields.
80/*
81/*	dsb_simple() updates the status and informal text, and resets all
82/*	other fields to defaults.
83/*
84/*	dsb_unix() updates the status, diagnostic code, diagnostic
85/*	text, and informal text, sets the diagnostic type to UNIX,
86/*	and resets all other fields to defaults.
87/*
88/*	dsb_formal() updates all fields except the informal text.
89/*
90/*	dsb_status() updates the status field, and resets all
91/*	formal fields to defaults.
92/*
93/*	dsb_reset() resets all fields in a DSN_BUF structure without
94/*	deallocating memory.
95/*
96/*	dsb_free() recycles the storage that was allocated by
97/*	dsb_create(), and so on.
98/*
99/*	DSN_FROM_DSN_BUF() populates the DSN member with a shallow
100/*	copy of the contents of the formal and informal fields, and
101/*	returns a pointer to the DSN member. This is typically used
102/*	for external reporting.
103/*
104/*	Arguments:
105/* .IP dsb
106/*	Delivery status buffer.
107/* .IP status
108/*	RFC 3463 "enhanced" status code.
109/* .IP action
110/*	RFC 3464 action code; specify DSB_DEF_ACTION to derive the
111/*	action from the status value. The only values that really
112/*	matter here are "expanded" and "relayed"; all other values
113/*	are already implied by the context.
114/* .IP mtype
115/*	The remote MTA type.
116/*	The only valid type is DSB_MTYPE_DNS.  The macro DSB_SKIP_RMTA
117/*	conveniently expands into a null argument list for the
118/*	remote MTA type and name.
119/* .IP mname
120/*	Remote MTA name.
121/* .IP dtype
122/*	The reply type.
123/*	DSB_DTYPE_SMTP or DSB_DTYPE_UNIX.  The macro DSB_SKIP_REPLY
124/*	conveniently expands into a null argument list for the reply
125/*	type and text.
126/* .IP dtext
127/*	The reply text. The reply text is reset when dtype is
128/*	DSB_SKIP_REPLY.
129/* .IP reason_fmt
130/*	The informal reason format.
131/* SEE ALSO
132/*	msg(3) diagnostics interface
133/* DIAGNOSTICS
134/*	Fatal: out of memory.
135/* LICENSE
136/* .ad
137/* .fi
138/*	The Secure Mailer license must be distributed with this software.
139/* AUTHOR(S)
140/*	Wietse Venema
141/*	IBM T.J. Watson Research
142/*	P.O. Box 704
143/*	Yorktown Heights, NY 10598, USA
144/*--*/
145
146/* System library. */
147
148#include <sys_defs.h>
149#include <stdlib.h>			/* 44BSD stdarg.h uses abort() */
150#include <stdarg.h>
151#include <string.h>
152
153/* Utility library. */
154
155#include <msg.h>
156#include <mymalloc.h>
157#include <vstring.h>
158
159/* Global library. */
160
161#include <dsn_buf.h>
162
163/* Application-specific. */
164
165#define STR(x)	vstring_str(x)
166
167/* dsb_create - create delivery status buffer */
168
169DSN_BUF *dsb_create(void)
170{
171    DSN_BUF *dsb;
172
173    /*
174     * Some fields aren't needed until we want to report an error.
175     */
176    dsb = (DSN_BUF *) mymalloc(sizeof(*dsb));
177    dsb->status = vstring_alloc(10);
178    dsb->action = vstring_alloc(10);
179    dsb->mtype = vstring_alloc(10);
180    dsb->mname = vstring_alloc(100);
181    dsb->dtype = vstring_alloc(10);
182    dsb->dtext = vstring_alloc(100);
183    dsb->reason = vstring_alloc(100);
184
185    return (dsb);
186}
187
188/* dsb_free - destroy storage */
189
190void    dsb_free(DSN_BUF *dsb)
191{
192    vstring_free(dsb->status);
193    vstring_free(dsb->action);
194    vstring_free(dsb->mtype);
195    vstring_free(dsb->mname);
196    vstring_free(dsb->dtype);
197    vstring_free(dsb->dtext);
198    vstring_free(dsb->reason);
199    myfree((char *) dsb);
200}
201
202 /*
203  * Initial versions of this code represented unavailable inputs with null
204  * pointers, which produced fragile and hard to maintain code. The current
205  * code uses empty strings instead of null pointers.
206  *
207  * For safety we keep the test for null pointers in input. It's cheap.
208  */
209#define DSB_TRUNCATE(s) \
210    do { VSTRING_RESET(s); VSTRING_TERMINATE(s); } while (0)
211
212#define NULL_OR_EMPTY(s) ((s) == 0 || *(s) == 0)
213
214#define DSB_ACTION(dsb, stat, act) \
215    vstring_strcpy((dsb)->action, !NULL_OR_EMPTY(act) ? (act) : "")
216
217#define DSB_MTA(dsb, type, name) do { \
218    if (NULL_OR_EMPTY(type) || NULL_OR_EMPTY(name)) { \
219	DSB_TRUNCATE((dsb)->mtype); \
220	DSB_TRUNCATE((dsb)->mname); \
221    } else { \
222	vstring_strcpy((dsb)->mtype, (type)); \
223	vstring_strcpy((dsb)->mname, (name)); \
224    } \
225} while (0)
226
227#define DSB_DIAG(dsb, type, text) do { \
228    if (NULL_OR_EMPTY(type) || NULL_OR_EMPTY(text)) { \
229	DSB_TRUNCATE((dsb)->dtype); \
230	DSB_TRUNCATE((dsb)->dtext); \
231    } else { \
232	vstring_strcpy((dsb)->dtype, (type)); \
233	vstring_strcpy((dsb)->dtext, (text)); \
234    } \
235} while (0)
236
237/* dsb_update - update formal attributes and informal text */
238
239DSN_BUF *dsb_update(DSN_BUF *dsb, const char *status, const char *action,
240		            const char *mtype, const char *mname,
241		            const char *dtype, const char *dtext,
242		            const char *format,...)
243{
244    va_list ap;
245
246    vstring_strcpy(dsb->status, status);
247    DSB_ACTION(dsb, status, action);
248    DSB_MTA(dsb, mtype, mname);
249    DSB_DIAG(dsb, dtype, dtext);
250    va_start(ap, format);
251    vstring_vsprintf(dsb->reason, format, ap);
252    va_end(ap);
253
254    return (dsb);
255}
256
257/* vdsb_simple - update status and informal text, va_list form */
258
259DSN_BUF *vdsb_simple(DSN_BUF *dsb, const char *status, const char *format,
260		             va_list ap)
261{
262    vstring_strcpy(dsb->status, status);
263    DSB_TRUNCATE(dsb->action);
264    DSB_TRUNCATE(dsb->mtype);
265    DSB_TRUNCATE(dsb->mname);
266    DSB_TRUNCATE(dsb->dtype);
267    DSB_TRUNCATE(dsb->dtext);
268    vstring_vsprintf(dsb->reason, format, ap);
269
270    return (dsb);
271}
272
273/* dsb_simple - update status and informal text */
274
275DSN_BUF *dsb_simple(DSN_BUF *dsb, const char *status, const char *format,...)
276{
277    va_list ap;
278
279    va_start(ap, format);
280    (void) vdsb_simple(dsb, status, format, ap);
281    va_end(ap);
282    return (dsb);
283}
284
285/* dsb_unix - update status, UNIX diagnostic and informal text */
286
287DSN_BUF *dsb_unix(DSN_BUF *dsb, const char *status,
288		          const char *dtext, const char *format,...)
289{
290    va_list ap;
291
292    vstring_strcpy(dsb->status, status);
293    DSB_TRUNCATE(dsb->action);
294    DSB_TRUNCATE(dsb->mtype);
295    DSB_TRUNCATE(dsb->mname);
296    vstring_strcpy(dsb->dtype, DSB_DTYPE_UNIX);
297    vstring_strcpy(dsb->dtext, dtext);
298    va_start(ap, format);
299    vstring_vsprintf(dsb->reason, format, ap);
300    va_end(ap);
301
302    return (dsb);
303}
304
305/* dsb_formal - update the formal fields */
306
307DSN_BUF *dsb_formal(DSN_BUF *dsb, const char *status, const char *action,
308		            const char *mtype, const char *mname,
309		            const char *dtype, const char *dtext)
310{
311    vstring_strcpy(dsb->status, status);
312    DSB_ACTION(dsb, status, action);
313    DSB_MTA(dsb, mtype, mname);
314    DSB_DIAG(dsb, dtype, dtext);
315    return (dsb);
316}
317
318/* dsb_status - update the status, reset other formal fields */
319
320DSN_BUF *dsb_status(DSN_BUF *dsb, const char *status)
321{
322    vstring_strcpy(dsb->status, status);
323    DSB_TRUNCATE(dsb->action);
324    DSB_TRUNCATE(dsb->mtype);
325    DSB_TRUNCATE(dsb->mname);
326    DSB_TRUNCATE(dsb->dtype);
327    DSB_TRUNCATE(dsb->dtext);
328    return (dsb);
329}
330
331/* dsb_reset - reset all fields */
332
333void    dsb_reset(DSN_BUF *dsb)
334{
335    DSB_TRUNCATE(dsb->status);
336    DSB_TRUNCATE(dsb->action);
337    DSB_TRUNCATE(dsb->mtype);
338    DSB_TRUNCATE(dsb->mname);
339    DSB_TRUNCATE(dsb->dtype);
340    DSB_TRUNCATE(dsb->dtext);
341    DSB_TRUNCATE(dsb->reason);
342}
343