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