diff options
Diffstat (limited to 'src/global/dsn_util.c')
-rw-r--r-- | src/global/dsn_util.c | 183 |
1 files changed, 183 insertions, 0 deletions
diff --git a/src/global/dsn_util.c b/src/global/dsn_util.c new file mode 100644 index 0000000..52b997a --- /dev/null +++ b/src/global/dsn_util.c @@ -0,0 +1,183 @@ +/*++ +/* NAME +/* dsn_util 3 +/* SUMMARY +/* DSN status parsing routines +/* SYNOPSIS +/* #include <dsn_util.h> +/* +/* #define DSN_SIZE ... +/* +/* typedef struct { ... } DSN_BUF; +/* +/* typedef struct { +/* .in +4 +/* DSN_STAT dsn; /* RFC 3463 status */ +/* const char *text; /* Free text */ +/* .in -4 +/* } DSN_SPLIT; +/* +/* DSN_SPLIT *dsn_split(dp, def_dsn, text) +/* DSN_SPLIT *dp; +/* const char *def_dsn; +/* const char *text; +/* +/* char *dsn_prepend(def_dsn, text) +/* const char *def_dsn; +/* const char *text; +/* +/* size_t dsn_valid(text) +/* const char *text; +/* +/* void DSN_UPDATE(dsn_buf, dsn, len) +/* DSN_BUF dsn_buf; +/* const char *dsn; +/* size_t len; +/* +/* const char *DSN_CODE(dsn_buf) +/* DSN_BUF dsn_buf; +/* +/* char *DSN_CLASS(dsn_buf) +/* DSN_BUF dsn_buf; +/* DESCRIPTION +/* The functions in this module manipulate pairs of RFC 3463 +/* status codes and descriptive free text. +/* +/* dsn_split() splits text into an RFC 3463 status code and +/* descriptive free text. When the text does not start with +/* a status code, the specified default status code is used +/* instead. Whitespace before the optional status code or +/* text is skipped. dsn_split() returns a copy of the RFC +/* 3463 status code, and returns a pointer to (not copy of) +/* the remainder of the text. The result value is the first +/* argument. +/* +/* dsn_prepend() prepends the specified default RFC 3463 status +/* code to the specified text if no status code is present in +/* the text. This function produces the same result as calling +/* concatenate() with the results from dsn_split(). The result +/* should be passed to myfree(). Whitespace before the optional +/* status code or text is skipped. +/* +/* dsn_valid() returns the length of the RFC 3463 status code +/* at the beginning of text, or zero. It does not skip initial +/* whitespace. +/* +/* Arguments: +/* .IP def_dsn +/* Null-terminated default RFC 3463 status code that will be +/* used when the free text does not start with one. +/* .IP dp +/* Pointer to storage for copy of DSN status code, and for +/* pointer to free text. +/* .IP dsn +/* Null-terminated RFC 3463 status code. +/* .IP text +/* Null-terminated free text. +/* SEE ALSO +/* msg(3) diagnostics interface +/* DIAGNOSTICS +/* Panic: invalid default DSN code. +/* 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 <sys_defs.h> +#include <stdarg.h> +#include <string.h> +#include <ctype.h> + +/* Utility library. */ + +#include <msg.h> +#include <mymalloc.h> +#include <vstring.h> +#include <stringops.h> + +/* Global library. */ + +#include <dsn_util.h> + +/* dsn_valid - check RFC 3463 enhanced status code, return length or zero */ + +size_t dsn_valid(const char *text) +{ + const unsigned char *cp = (unsigned char *) text; + size_t len; + + /* First portion is one digit followed by dot. */ + if ((cp[0] != '2' && cp[0] != '4' && cp[0] != '5') || cp[1] != '.') + return (0); + + /* Second portion is 1-3 digits followed by dot. */ + cp += 2; + if ((len = strspn((char *) cp, "0123456789")) < 1 || len > DSN_DIGS2 + || cp[len] != '.') + return (0); + + /* Last portion is 1-3 digits followed by end-of-string or whitespace. */ + cp += len + 1; + if ((len = strspn((char *) cp, "0123456789")) < 1 || len > DSN_DIGS3 + || (cp[len] != 0 && !ISSPACE(cp[len]))) + return (0); + + return (((char *) cp - text) + len); +} + +/* dsn_split - split text into DSN and text */ + +DSN_SPLIT *dsn_split(DSN_SPLIT *dp, const char *def_dsn, const char *text) +{ + const char *myname = "dsn_split"; + const char *cp = text; + size_t len; + + /* + * Look for an optional RFC 3463 enhanced status code. + * + * XXX If we want to enforce that the first digit of the status code in the + * text matches the default status code, then pipe_command() needs to be + * changed. It currently auto-detects the reply code without knowing in + * advance if the result will start with '4' or '5'. + */ + while (ISSPACE(*cp)) + cp++; + if ((len = dsn_valid(cp)) > 0) { + strncpy(dp->dsn.data, cp, len); + dp->dsn.data[len] = 0; + cp += len + 1; + } else if ((len = dsn_valid(def_dsn)) > 0) { + strncpy(dp->dsn.data, def_dsn, len); + dp->dsn.data[len] = 0; + } else { + msg_panic("%s: bad default status \"%s\"", myname, def_dsn); + } + + /* + * The remainder is free text. + */ + while (ISSPACE(*cp)) + cp++; + dp->text = cp; + + return (dp); +} + +/* dsn_prepend - prepend optional status to text, result on heap */ + +char *dsn_prepend(const char *def_dsn, const char *text) +{ + DSN_SPLIT dp; + + dsn_split(&dp, def_dsn, text); + return (concatenate(DSN_STATUS(dp.dsn), " ", dp.text, (char *) 0)); +} |