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/* dsb_simple - update status and informal text */ 258 259DSN_BUF *dsb_simple(DSN_BUF *dsb, const char *status, const char *format,...) 260{ 261 va_list ap; 262 263 vstring_strcpy(dsb->status, status); 264 DSB_TRUNCATE(dsb->action); 265 DSB_TRUNCATE(dsb->mtype); 266 DSB_TRUNCATE(dsb->mname); 267 DSB_TRUNCATE(dsb->dtype); 268 DSB_TRUNCATE(dsb->dtext); 269 va_start(ap, format); 270 vstring_vsprintf(dsb->reason, format, ap); 271 va_end(ap); 272 273 return (dsb); 274} 275 276/* dsb_unix - update status, UNIX diagnostic and informal text */ 277 278DSN_BUF *dsb_unix(DSN_BUF *dsb, const char *status, 279 const char *dtext, const char *format,...) 280{ 281 va_list ap; 282 283 vstring_strcpy(dsb->status, status); 284 DSB_TRUNCATE(dsb->action); 285 DSB_TRUNCATE(dsb->mtype); 286 DSB_TRUNCATE(dsb->mname); 287 vstring_strcpy(dsb->dtype, DSB_DTYPE_UNIX); 288 vstring_strcpy(dsb->dtext, dtext); 289 va_start(ap, format); 290 vstring_vsprintf(dsb->reason, format, ap); 291 va_end(ap); 292 293 return (dsb); 294} 295 296/* dsb_formal - update the formal fields */ 297 298DSN_BUF *dsb_formal(DSN_BUF *dsb, const char *status, const char *action, 299 const char *mtype, const char *mname, 300 const char *dtype, const char *dtext) 301{ 302 vstring_strcpy(dsb->status, status); 303 DSB_ACTION(dsb, status, action); 304 DSB_MTA(dsb, mtype, mname); 305 DSB_DIAG(dsb, dtype, dtext); 306 return (dsb); 307} 308 309/* dsb_status - update the status, reset other formal fields */ 310 311DSN_BUF *dsb_status(DSN_BUF *dsb, const char *status) 312{ 313 vstring_strcpy(dsb->status, status); 314 DSB_TRUNCATE(dsb->action); 315 DSB_TRUNCATE(dsb->mtype); 316 DSB_TRUNCATE(dsb->mname); 317 DSB_TRUNCATE(dsb->dtype); 318 DSB_TRUNCATE(dsb->dtext); 319 return (dsb); 320} 321 322/* dsb_reset - reset all fields */ 323 324void dsb_reset(DSN_BUF *dsb) 325{ 326 DSB_TRUNCATE(dsb->status); 327 DSB_TRUNCATE(dsb->action); 328 DSB_TRUNCATE(dsb->mtype); 329 DSB_TRUNCATE(dsb->mname); 330 DSB_TRUNCATE(dsb->dtype); 331 DSB_TRUNCATE(dsb->dtext); 332 DSB_TRUNCATE(dsb->reason); 333} 334