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 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 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 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 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 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 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 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 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 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