1171802Sdelphij/* $NetBSD$ */ 2170808Sdelphij 3182739Sdelphij/*++ 4171802Sdelphij/* NAME 5170808Sdelphij/* dsn_util 3 6170808Sdelphij/* SUMMARY 7170808Sdelphij/* DSN status parsing routines 8170808Sdelphij/* SYNOPSIS 9170808Sdelphij/* #include <dsn_util.h> 10170808Sdelphij/* 11170808Sdelphij/* #define DSN_SIZE ... 12170808Sdelphij/* 13170808Sdelphij/* typedef struct { ... } DSN_BUF; 14170808Sdelphij/* 15170808Sdelphij/* typedef struct { 16170808Sdelphij/* .in +4 17170808Sdelphij/* DSN_STAT dsn; /* RFC 3463 status */ 18170808Sdelphij/* const char *text; /* Free text */ 19170808Sdelphij/* .in -4 20170808Sdelphij/* } DSN_SPLIT; 21170808Sdelphij/* 22170808Sdelphij/* DSN_SPLIT *dsn_split(dp, def_dsn, text) 23170808Sdelphij/* DSN_SPLIT *dp; 24170808Sdelphij/* const char *def_dsn; 25170808Sdelphij/* const char *text; 26170808Sdelphij/* 27170808Sdelphij/* char *dsn_prepend(def_dsn, text) 28170808Sdelphij/* const char *def_dsn; 29170808Sdelphij/* const char *text; 30170808Sdelphij/* 31170808Sdelphij/* size_t dsn_valid(text) 32170808Sdelphij/* const char *text; 33170808Sdelphij/* 34170808Sdelphij/* void DSN_UPDATE(dsn_buf, dsn, len) 35170808Sdelphij/* DSN_BUF dsn_buf; 36170808Sdelphij/* const char *dsn; 37170808Sdelphij/* size_t len; 38170808Sdelphij/* 39170808Sdelphij/* const char *DSN_CODE(dsn_buf) 40170808Sdelphij/* DSN_BUF dsn_buf; 41170808Sdelphij/* 42170808Sdelphij/* char *DSN_CLASS(dsn_buf) 43170808Sdelphij/* DSN_BUF dsn_buf; 44170808Sdelphij/* DESCRIPTION 45170808Sdelphij/* The functions in this module manipulate pairs of RFC 3463 46170808Sdelphij/* status codes and descriptive free text. 47170808Sdelphij/* 48245115Sgleb/* dsn_split() splits text into an RFC 3463 status code and 49170808Sdelphij/* descriptive free text. When the text does not start with 50170808Sdelphij/* a status code, the specified default status code is used 51170808Sdelphij/* instead. Whitespace before the optional status code or 52170808Sdelphij/* text is skipped. dsn_split() returns a copy of the RFC 53171087Sdelphij/* 3463 status code, and returns a pointer to (not copy of) 54170808Sdelphij/* the remainder of the text. The result value is the first 55170808Sdelphij/* argument. 56170808Sdelphij/* 57170808Sdelphij/* dsn_prepend() prepends the specified default RFC 3463 status 58245115Sgleb/* code to the specified text if no status code is present in 59245115Sgleb/* the text. This function produces the same result as calling 60245115Sgleb/* concatenate() with the results from dsn_split(). The result 61170808Sdelphij/* should be passed to myfree(). Whitespace before the optional 62245115Sgleb/* status code or text is skipped. 63245115Sgleb/* 64245115Sgleb/* dsn_valid() returns the length of the RFC 3463 status code 65245115Sgleb/* at the beginning of text, or zero. It does not skip initial 66245115Sgleb/* whitespace. 67245115Sgleb/* 68245115Sgleb/* Arguments: 69245115Sgleb/* .IP def_dsn 70245115Sgleb/* Null-terminated default RFC 3463 status code that will be 71170808Sdelphij/* used when the free text does not start with one. 72245115Sgleb/* .IP dp 73245115Sgleb/* Pointer to storage for copy of DSN status code, and for 74245115Sgleb/* pointer to free text. 75245115Sgleb/* .IP dsn 76245115Sgleb/* Null-terminated RFC 3463 status code. 77245115Sgleb/* .IP text 78170808Sdelphij/* Null-terminated free text. 79245115Sgleb/* SEE ALSO 80245115Sgleb/* msg(3) diagnostics interface 81245115Sgleb/* DIAGNOSTICS 82170808Sdelphij/* Panic: invalid default DSN code. 83211598Sed/* LICENSE 84211598Sed/* .ad 85170808Sdelphij/* .fi 86245115Sgleb/* The Secure Mailer license must be distributed with this software. 87245115Sgleb/* AUTHOR(S) 88245115Sgleb/* Wietse Venema 89245115Sgleb/* IBM T.J. Watson Research 90245115Sgleb/* P.O. Box 704 91245115Sgleb/* Yorktown Heights, NY 10598, USA 92245115Sgleb/*--*/ 93245115Sgleb 94245115Sgleb/* System library. */ 95170808Sdelphij 96170808Sdelphij#include <sys_defs.h> 97245115Sgleb#include <stdarg.h> 98170808Sdelphij#include <string.h> 99170808Sdelphij#include <ctype.h> 100245115Sgleb 101170808Sdelphij/* Utility library. */ 102170808Sdelphij 103245115Sgleb#include <msg.h> 104170808Sdelphij#include <mymalloc.h> 105170808Sdelphij#include <vstring.h> 106170808Sdelphij#include <stringops.h> 107170808Sdelphij 108170808Sdelphij/* Global library. */ 109245115Sgleb 110170808Sdelphij#include <dsn_util.h> 111171802Sdelphij 112171802Sdelphij/* dsn_valid - check RFC 3463 enhanced status code, return length or zero */ 113245115Sgleb 114245115Sglebsize_t dsn_valid(const char *text) 115171802Sdelphij{ 116171802Sdelphij const unsigned char *cp = (unsigned char *) text; 117245115Sgleb size_t len; 118245115Sgleb 119171802Sdelphij /* First portion is one digit followed by dot. */ 120245115Sgleb if ((cp[0] != '2' && cp[0] != '4' && cp[0] != '5') || cp[1] != '.') 121245115Sgleb return (0); 122170808Sdelphij 123245115Sgleb /* Second portion is 1-3 digits followed by dot. */ 124245115Sgleb cp += 2; 125245115Sgleb if ((len = strspn((char *) cp, "0123456789")) < 1 || len > DSN_DIGS2 126245115Sgleb || cp[len] != '.') 127245115Sgleb return (0); 128245115Sgleb 129245115Sgleb /* Last portion is 1-3 digits followed by end-of-string or whitespace. */ 130245115Sgleb cp += len + 1; 131245115Sgleb if ((len = strspn((char *) cp, "0123456789")) < 1 || len > DSN_DIGS3 132245115Sgleb || (cp[len] != 0 && !ISSPACE(cp[len]))) 133171802Sdelphij return (0); 134170808Sdelphij 135170808Sdelphij return (((char *) cp - text) + len); 136170808Sdelphij} 137170808Sdelphij 138170808Sdelphij/* dsn_split - split text into DSN and text */ 139170808Sdelphij 140170808SdelphijDSN_SPLIT *dsn_split(DSN_SPLIT *dp, const char *def_dsn, const char *text) 141170808Sdelphij{ 142170808Sdelphij const char *myname = "dsn_split"; 143170808Sdelphij const char *cp = text; 144170808Sdelphij size_t len; 145170808Sdelphij 146170808Sdelphij /* 147170808Sdelphij * Look for an optional RFC 3463 enhanced status code. 148170808Sdelphij * 149170808Sdelphij * XXX If we want to enforce that the first digit of the status code in the 150170808Sdelphij * text matches the default status code, then pipe_command() needs to be 151170808Sdelphij * changed. It currently auto-detects the reply code without knowing in 152170808Sdelphij * advance if the result will start with '4' or '5'. 153170808Sdelphij */ 154170808Sdelphij while (ISSPACE(*cp)) 155170808Sdelphij cp++; 156170808Sdelphij if ((len = dsn_valid(cp)) > 0) { 157170808Sdelphij strncpy(dp->dsn.data, cp, len); 158170808Sdelphij dp->dsn.data[len] = 0; 159170808Sdelphij cp += len + 1; 160170808Sdelphij } else if ((len = dsn_valid(def_dsn)) > 0) { 161170808Sdelphij strncpy(dp->dsn.data, def_dsn, len); 162170808Sdelphij dp->dsn.data[len] = 0; 163170808Sdelphij } else { 164170808Sdelphij msg_panic("%s: bad default status \"%s\"", myname, def_dsn); 165170808Sdelphij } 166170808Sdelphij 167170808Sdelphij /* 168170808Sdelphij * The remainder is free text. 169170808Sdelphij */ 170170808Sdelphij while (ISSPACE(*cp)) 171170808Sdelphij cp++; 172170808Sdelphij dp->text = cp; 173170808Sdelphij 174170808Sdelphij return (dp); 175170808Sdelphij} 176170808Sdelphij 177170808Sdelphij/* dsn_prepend - prepend optional status to text, result on heap */ 178248597Spjd 179170808Sdelphijchar *dsn_prepend(const char *def_dsn, const char *text) 180170808Sdelphij{ 181170808Sdelphij DSN_SPLIT dp; 182170808Sdelphij 183170808Sdelphij dsn_split(&dp, def_dsn, text); 184170808Sdelphij return (concatenate(DSN_STATUS(dp.dsn), " ", dp.text, (char *) 0)); 185170808Sdelphij} 186170808Sdelphij