1 /* $NetBSD: dsn_util.c,v 1.1.1.1 2009/06/23 10:08:45 tron Exp $ */ 2 3 /*++ 4 /* NAME 5 /* dsn_util 3 6 /* SUMMARY 7 /* DSN status parsing routines 8 /* SYNOPSIS 9 /* #include <dsn_util.h> 10 /* 11 /* #define DSN_SIZE ... 12 /* 13 /* typedef struct { ... } DSN_BUF; 14 /* 15 /* typedef struct { 16 /* .in +4 17 /* DSN_STAT dsn; /* RFC 3463 status */ 18 /* const char *text; /* Free text */ 19 /* .in -4 20 /* } DSN_SPLIT; 21 /* 22 /* DSN_SPLIT *dsn_split(dp, def_dsn, text) 23 /* DSN_SPLIT *dp; 24 /* const char *def_dsn; 25 /* const char *text; 26 /* 27 /* char *dsn_prepend(def_dsn, text) 28 /* const char *def_dsn; 29 /* const char *text; 30 /* 31 /* size_t dsn_valid(text) 32 /* const char *text; 33 /* 34 /* void DSN_UPDATE(dsn_buf, dsn, len) 35 /* DSN_BUF dsn_buf; 36 /* const char *dsn; 37 /* size_t len; 38 /* 39 /* const char *DSN_CODE(dsn_buf) 40 /* DSN_BUF dsn_buf; 41 /* 42 /* char *DSN_CLASS(dsn_buf) 43 /* DSN_BUF dsn_buf; 44 /* DESCRIPTION 45 /* The functions in this module manipulate pairs of RFC 3463 46 /* status codes and descriptive free text. 47 /* 48 /* dsn_split() splits text into an RFC 3463 status code and 49 /* descriptive free text. When the text does not start with 50 /* a status code, the specified default status code is used 51 /* instead. Whitespace before the optional status code or 52 /* text is skipped. dsn_split() returns a copy of the RFC 53 /* 3463 status code, and returns a pointer to (not copy of) 54 /* the remainder of the text. The result value is the first 55 /* argument. 56 /* 57 /* dsn_prepend() prepends the specified default RFC 3463 status 58 /* code to the specified text if no status code is present in 59 /* the text. This function produces the same result as calling 60 /* concatenate() with the results from dsn_split(). The result 61 /* should be passed to myfree(). Whitespace before the optional 62 /* status code or text is skipped. 63 /* 64 /* dsn_valid() returns the length of the RFC 3463 status code 65 /* at the beginning of text, or zero. It does not skip initial 66 /* whitespace. 67 /* 68 /* Arguments: 69 /* .IP def_dsn 70 /* Null-terminated default RFC 3463 status code that will be 71 /* used when the free text does not start with one. 72 /* .IP dp 73 /* Pointer to storage for copy of DSN status code, and for 74 /* pointer to free text. 75 /* .IP dsn 76 /* Null-terminated RFC 3463 status code. 77 /* .IP text 78 /* Null-terminated free text. 79 /* SEE ALSO 80 /* msg(3) diagnostics interface 81 /* DIAGNOSTICS 82 /* Panic: invalid default DSN code. 83 /* LICENSE 84 /* .ad 85 /* .fi 86 /* The Secure Mailer license must be distributed with this software. 87 /* AUTHOR(S) 88 /* Wietse Venema 89 /* IBM T.J. Watson Research 90 /* P.O. Box 704 91 /* Yorktown Heights, NY 10598, USA 92 /*--*/ 93 94 /* System library. */ 95 96 #include <sys_defs.h> 97 #include <stdarg.h> 98 #include <string.h> 99 #include <ctype.h> 100 101 /* Utility library. */ 102 103 #include <msg.h> 104 #include <mymalloc.h> 105 #include <vstring.h> 106 #include <stringops.h> 107 108 /* Global library. */ 109 110 #include <dsn_util.h> 111 112 /* dsn_valid - check RFC 3463 enhanced status code, return length or zero */ 113 114 size_t dsn_valid(const char *text) 115 { 116 const unsigned char *cp = (unsigned char *) text; 117 size_t len; 118 119 /* First portion is one digit followed by dot. */ 120 if ((cp[0] != '2' && cp[0] != '4' && cp[0] != '5') || cp[1] != '.') 121 return (0); 122 123 /* Second portion is 1-3 digits followed by dot. */ 124 cp += 2; 125 if ((len = strspn((char *) cp, "0123456789")) < 1 || len > DSN_DIGS2 126 || cp[len] != '.') 127 return (0); 128 129 /* Last portion is 1-3 digits followed by end-of-string or whitespace. */ 130 cp += len + 1; 131 if ((len = strspn((char *) cp, "0123456789")) < 1 || len > DSN_DIGS3 132 || (cp[len] != 0 && !ISSPACE(cp[len]))) 133 return (0); 134 135 return (((char *) cp - text) + len); 136 } 137 138 /* dsn_split - split text into DSN and text */ 139 140 DSN_SPLIT *dsn_split(DSN_SPLIT *dp, const char *def_dsn, const char *text) 141 { 142 const char *myname = "dsn_split"; 143 const char *cp = text; 144 size_t len; 145 146 /* 147 * Look for an optional RFC 3463 enhanced status code. 148 * 149 * XXX If we want to enforce that the first digit of the status code in the 150 * text matches the default status code, then pipe_command() needs to be 151 * changed. It currently auto-detects the reply code without knowing in 152 * advance if the result will start with '4' or '5'. 153 */ 154 while (ISSPACE(*cp)) 155 cp++; 156 if ((len = dsn_valid(cp)) > 0) { 157 strncpy(dp->dsn.data, cp, len); 158 dp->dsn.data[len] = 0; 159 cp += len + 1; 160 } else if ((len = dsn_valid(def_dsn)) > 0) { 161 strncpy(dp->dsn.data, def_dsn, len); 162 dp->dsn.data[len] = 0; 163 } else { 164 msg_panic("%s: bad default status \"%s\"", myname, def_dsn); 165 } 166 167 /* 168 * The remainder is free text. 169 */ 170 while (ISSPACE(*cp)) 171 cp++; 172 dp->text = cp; 173 174 return (dp); 175 } 176 177 /* dsn_prepend - prepend optional status to text, result on heap */ 178 179 char *dsn_prepend(const char *def_dsn, const char *text) 180 { 181 DSN_SPLIT dp; 182 183 dsn_split(&dp, def_dsn, text); 184 return (concatenate(DSN_STATUS(dp.dsn), " ", dp.text, (char *) 0)); 185 } 186