/*++ /* NAME /* dsn_buf 3 /* SUMMARY /* delivery status buffer /* SYNOPSIS /* #include /* /* typedef struct { /* .in +4 /* /* Convenience member */ /* DSN dsn; /* light-weight, dsn(3) */ /* /* Formal members... */ /* VSTRING *status; /* RFC 3463 */ /* VSTRING *action; /* RFC 3464 */ /* VSTRING *mtype; /* dns */ /* VSTRING *mname; /* host or domain */ /* VSTRING *dtype; /* smtp, x-unix */ /* VSTRING *dtext; /* RFC 2821, sysexits.h */ /* /* Informal members... */ /* VSTRING *reason; /* informal text */ /* .in -4 /* } DSN_BUF; /* /* DSN_BUF *dsb_create(void) /* /* DSN_BUF *dsb_update(dsb, status, action, mtype, mname, dtype, /* dtext, reason_fmt, ...) /* DSN_BUF *dsb; /* const char *status; /* const char *action; /* const char *mtype; /* const char *mname; /* const char *dtype; /* const char *dtext; /* const char *reason_fmt; /* /* DSN_BUF *dsb_simple(dsb, status, reason_fmt, ...) /* DSN_BUF *dsb; /* const char *status; /* const char *reason_fmt; /* /* DSN_BUF *dsb_unix(dsb, status, dtext, reason_fmt, ...) /* DSN_BUF *dsb; /* const char *status; /* const char *reason_fmt; /* /* DSN_BUF *dsb_formal(dsb, status, action, mtype, mname, dtype, /* dtext) /* DSN_BUF *dsb; /* const char *status; /* const char *action; /* const char *mtype; /* const char *mname; /* const char *dtype; /* const char *dtext; /* /* DSN_BUF *dsb_status(dsb, status) /* DSN_BUF *dsb; /* const char *status; /* /* void dsb_reset(dsb) /* DSN_BUF *dsb; /* /* void dsb_free(dsb) /* DSN_BUF *dsb; /* /* DSN *DSN_FROM_DSN_BUF(dsb) /* DSN_BUF *dsb; /* DESCRIPTION /* This module implements a simple to update delivery status /* buffer for Postfix-internal use. Typically it is filled in /* the course of delivery attempt, and then formatted into a /* DSN structure for external notification. /* /* dsb_create() creates initialized storage for formal RFC 3464 /* attributes, and human-readable informal text. /* /* dsb_update() updates all fields. /* /* dsb_simple() updates the status and informal text, and resets all /* other fields to defaults. /* /* dsb_unix() updates the status, diagnostic code, diagnostic /* text, and informal text, sets the diagnostic type to UNIX, /* and resets all other fields to defaults. /* /* dsb_formal() updates all fields except the informal text. /* /* dsb_status() updates the status field, and resets all /* formal fields to defaults. /* /* dsb_reset() resets all fields in a DSN_BUF structure without /* deallocating memory. /* /* dsb_free() recycles the storage that was allocated by /* dsb_create(), and so on. /* /* DSN_FROM_DSN_BUF() populates the DSN member with a shallow /* copy of the contents of the formal and informal fields, and /* returns a pointer to the DSN member. This is typically used /* for external reporting. /* /* Arguments: /* .IP dsb /* Delivery status buffer. /* .IP status /* RFC 3463 "enhanced" status code. /* .IP action /* RFC 3464 action code; specify DSB_DEF_ACTION to derive the /* action from the status value. The only values that really /* matter here are "expanded" and "relayed"; all other values /* are already implied by the context. /* .IP mtype /* The remote MTA type. /* The only valid type is DSB_MTYPE_DNS. The macro DSB_SKIP_RMTA /* conveniently expands into a null argument list for the /* remote MTA type and name. /* .IP mname /* Remote MTA name. /* .IP dtype /* The reply type. /* DSB_DTYPE_SMTP or DSB_DTYPE_UNIX. The macro DSB_SKIP_REPLY /* conveniently expands into a null argument list for the reply /* type and text. /* .IP dtext /* The reply text. The reply text is reset when dtype is /* DSB_SKIP_REPLY. /* .IP reason_fmt /* The informal reason format. /* SEE ALSO /* msg(3) diagnostics interface /* DIAGNOSTICS /* Fatal: out of memory. /* LICENSE /* .ad /* .fi /* The Secure Mailer license must be distributed with this software. /* AUTHOR(S) /* Wietse Venema /* IBM T.J. Watson Research /* P.O. Box 704 /* Yorktown Heights, NY 10598, USA /*--*/ /* System library. */ #include #include /* 44BSD stdarg.h uses abort() */ #include #include /* Utility library. */ #include #include #include /* Global library. */ #include /* Application-specific. */ #define STR(x) vstring_str(x) /* dsb_create - create delivery status buffer */ DSN_BUF *dsb_create(void) { DSN_BUF *dsb; /* * Some fields aren't needed until we want to report an error. */ dsb = (DSN_BUF *) mymalloc(sizeof(*dsb)); dsb->status = vstring_alloc(10); dsb->action = vstring_alloc(10); dsb->mtype = vstring_alloc(10); dsb->mname = vstring_alloc(100); dsb->dtype = vstring_alloc(10); dsb->dtext = vstring_alloc(100); dsb->reason = vstring_alloc(100); return (dsb); } /* dsb_free - destroy storage */ void dsb_free(DSN_BUF *dsb) { vstring_free(dsb->status); vstring_free(dsb->action); vstring_free(dsb->mtype); vstring_free(dsb->mname); vstring_free(dsb->dtype); vstring_free(dsb->dtext); vstring_free(dsb->reason); myfree((void *) dsb); } /* * Initial versions of this code represented unavailable inputs with null * pointers, which produced fragile and hard to maintain code. The current * code uses empty strings instead of null pointers. * * For safety we keep the test for null pointers in input. It's cheap. */ #define DSB_TRUNCATE(s) \ do { VSTRING_RESET(s); VSTRING_TERMINATE(s); } while (0) #define NULL_OR_EMPTY(s) ((s) == 0 || *(s) == 0) #define DSB_ACTION(dsb, stat, act) \ vstring_strcpy((dsb)->action, !NULL_OR_EMPTY(act) ? (act) : "") #define DSB_MTA(dsb, type, name) do { \ if (NULL_OR_EMPTY(type) || NULL_OR_EMPTY(name)) { \ DSB_TRUNCATE((dsb)->mtype); \ DSB_TRUNCATE((dsb)->mname); \ } else { \ vstring_strcpy((dsb)->mtype, (type)); \ vstring_strcpy((dsb)->mname, (name)); \ } \ } while (0) #define DSB_DIAG(dsb, type, text) do { \ if (NULL_OR_EMPTY(type) || NULL_OR_EMPTY(text)) { \ DSB_TRUNCATE((dsb)->dtype); \ DSB_TRUNCATE((dsb)->dtext); \ } else { \ vstring_strcpy((dsb)->dtype, (type)); \ vstring_strcpy((dsb)->dtext, (text)); \ } \ } while (0) /* dsb_update - update formal attributes and informal text */ DSN_BUF *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,...) { va_list ap; vstring_strcpy(dsb->status, status); DSB_ACTION(dsb, status, action); DSB_MTA(dsb, mtype, mname); DSB_DIAG(dsb, dtype, dtext); va_start(ap, format); vstring_vsprintf(dsb->reason, format, ap); va_end(ap); return (dsb); } /* vdsb_simple - update status and informal text, va_list form */ DSN_BUF *vdsb_simple(DSN_BUF *dsb, const char *status, const char *format, va_list ap) { vstring_strcpy(dsb->status, status); DSB_TRUNCATE(dsb->action); DSB_TRUNCATE(dsb->mtype); DSB_TRUNCATE(dsb->mname); DSB_TRUNCATE(dsb->dtype); DSB_TRUNCATE(dsb->dtext); vstring_vsprintf(dsb->reason, format, ap); return (dsb); } /* dsb_simple - update status and informal text */ DSN_BUF *dsb_simple(DSN_BUF *dsb, const char *status, const char *format,...) { va_list ap; va_start(ap, format); (void) vdsb_simple(dsb, status, format, ap); va_end(ap); return (dsb); } /* dsb_unix - update status, UNIX diagnostic and informal text */ DSN_BUF *dsb_unix(DSN_BUF *dsb, const char *status, const char *dtext, const char *format,...) { va_list ap; vstring_strcpy(dsb->status, status); DSB_TRUNCATE(dsb->action); DSB_TRUNCATE(dsb->mtype); DSB_TRUNCATE(dsb->mname); vstring_strcpy(dsb->dtype, DSB_DTYPE_UNIX); vstring_strcpy(dsb->dtext, dtext); va_start(ap, format); vstring_vsprintf(dsb->reason, format, ap); va_end(ap); return (dsb); } /* dsb_formal - update the formal fields */ DSN_BUF *dsb_formal(DSN_BUF *dsb, const char *status, const char *action, const char *mtype, const char *mname, const char *dtype, const char *dtext) { vstring_strcpy(dsb->status, status); DSB_ACTION(dsb, status, action); DSB_MTA(dsb, mtype, mname); DSB_DIAG(dsb, dtype, dtext); return (dsb); } /* dsb_status - update the status, reset other formal fields */ DSN_BUF *dsb_status(DSN_BUF *dsb, const char *status) { vstring_strcpy(dsb->status, status); DSB_TRUNCATE(dsb->action); DSB_TRUNCATE(dsb->mtype); DSB_TRUNCATE(dsb->mname); DSB_TRUNCATE(dsb->dtype); DSB_TRUNCATE(dsb->dtext); return (dsb); } /* dsb_reset - reset all fields */ void dsb_reset(DSN_BUF *dsb) { DSB_TRUNCATE(dsb->status); DSB_TRUNCATE(dsb->action); DSB_TRUNCATE(dsb->mtype); DSB_TRUNCATE(dsb->mname); DSB_TRUNCATE(dsb->dtype); DSB_TRUNCATE(dsb->dtext); DSB_TRUNCATE(dsb->reason); }