1 /* $NetBSD: smtpd_token.c,v 1.2 2017/02/14 01:16:48 christos Exp $ */ 2 3 /*++ 4 /* NAME 5 /* smtpd_token 3 6 /* SUMMARY 7 /* tokenize SMTPD command 8 /* SYNOPSIS 9 /* #include <smtpd_token.h> 10 /* 11 /* typedef struct { 12 /* .in +4 13 /* int tokval; 14 /* char *strval; 15 /* /* other stuff... */ 16 /* .in -4 17 /* } SMTPD_TOKEN; 18 /* 19 /* int smtpd_token(str, argvp) 20 /* char *str; 21 /* SMTPD_TOKEN **argvp; 22 /* DESCRIPTION 23 /* smtpd_token() routine converts the string in \fIstr\fR to an 24 /* array of tokens in \fIargvp\fR. The number of tokens is returned 25 /* via the function return value. 26 /* 27 /* Token types: 28 /* .IP SMTPD_TOK_OTHER 29 /* The token is something else. 30 /* .IP SMTPD_TOK_ERROR 31 /* A malformed token. 32 /* BUGS 33 /* This tokenizer understands just enough to tokenize SMTPD commands. 34 /* It understands backslash escapes, white space, quoted strings, 35 /* and addresses (including quoted text) enclosed by < and >. 36 /* The input is broken up into tokens by whitespace, except for 37 /* whitespace that is protected by quotes etc. 38 /* LICENSE 39 /* .ad 40 /* .fi 41 /* The Secure Mailer license must be distributed with this software. 42 /* AUTHOR(S) 43 /* Wietse Venema 44 /* IBM T.J. Watson Research 45 /* P.O. Box 704 46 /* Yorktown Heights, NY 10598, USA 47 /*--*/ 48 49 /* System library. */ 50 51 #include <sys_defs.h> 52 #include <ctype.h> 53 #include <string.h> 54 #include <unistd.h> 55 56 #ifdef STRCASECMP_IN_STRINGS_H 57 #include <strings.h> 58 #endif 59 60 /* Utility library. */ 61 62 #include <mymalloc.h> 63 #include <mvect.h> 64 65 /* Application-specific. */ 66 67 #include "smtpd_token.h" 68 69 /* smtp_quoted - read until closing quote */ 70 71 static char *smtp_quoted(char *cp, SMTPD_TOKEN *arg, int start, int last) 72 { 73 static VSTRING *stack; 74 int wanted; 75 int c; 76 77 /* 78 * Parser stack. `ch' is always the most-recently entered character. 79 */ 80 #define ENTER_CHAR(buf, ch) VSTRING_ADDCH(buf, ch); 81 #define LEAVE_CHAR(buf, ch) { \ 82 vstring_truncate(buf, VSTRING_LEN(buf) - 1); \ 83 ch = vstring_end(buf)[-1]; \ 84 } 85 86 if (stack == 0) 87 stack = vstring_alloc(1); 88 VSTRING_RESET(stack); 89 ENTER_CHAR(stack, wanted = last); 90 91 VSTRING_ADDCH(arg->vstrval, start); 92 for (;;) { 93 if ((c = *cp) == 0) 94 break; 95 cp++; 96 VSTRING_ADDCH(arg->vstrval, c); 97 if (c == '\\') { /* parse escape sequence */ 98 if ((c = *cp) == 0) 99 break; 100 cp++; 101 VSTRING_ADDCH(arg->vstrval, c); 102 } else if (c == wanted) { /* closing quote etc. */ 103 if (VSTRING_LEN(stack) == 1) 104 return (cp); 105 LEAVE_CHAR(stack, wanted); 106 } else if (c == '"') { 107 ENTER_CHAR(stack, wanted = '"'); /* highest precedence */ 108 } else if (c == '<' && wanted == '>') { 109 ENTER_CHAR(stack, wanted = '>'); /* lowest precedence */ 110 } 111 } 112 arg->tokval = SMTPD_TOK_ERROR; /* missing end */ 113 return (cp); 114 } 115 116 /* smtp_next_token - extract next token from input, update cp */ 117 118 static char *smtp_next_token(char *cp, SMTPD_TOKEN *arg) 119 { 120 int c; 121 122 VSTRING_RESET(arg->vstrval); 123 arg->tokval = SMTPD_TOK_OTHER; 124 125 #define STR(x) vstring_str(x) 126 #define LEN(x) VSTRING_LEN(x) 127 #define STREQ(x,y,l) (strncasecmp((x), (y), (l)) == 0) 128 129 for (;;) { 130 if ((c = *cp) == 0) /* end of input */ 131 break; 132 cp++; 133 if (ISSPACE(c)) { /* whitespace, skip */ 134 while (*cp && ISSPACE(*cp)) 135 cp++; 136 if (LEN(arg->vstrval) > 0) /* end of token */ 137 break; 138 } else if (c == '<') { /* <stuff> */ 139 cp = smtp_quoted(cp, arg, c, '>'); 140 } else if (c == '"') { /* "stuff" */ 141 cp = smtp_quoted(cp, arg, c, c); 142 } else if (c == ':') { /* this is gross, but... */ 143 VSTRING_ADDCH(arg->vstrval, c); 144 if (STREQ(STR(arg->vstrval), "to:", LEN(arg->vstrval)) 145 || STREQ(STR(arg->vstrval), "from:", LEN(arg->vstrval))) 146 break; 147 } else { /* other */ 148 if (c == '\\') { 149 VSTRING_ADDCH(arg->vstrval, c); 150 if ((c = *cp) == 0) 151 break; 152 cp++; 153 } 154 VSTRING_ADDCH(arg->vstrval, c); 155 } 156 } 157 if (LEN(arg->vstrval) <= 0) /* no token found */ 158 return (0); 159 VSTRING_TERMINATE(arg->vstrval); 160 arg->strval = vstring_str(arg->vstrval); 161 return (cp); 162 } 163 164 /* smtpd_token_init - initialize token structures */ 165 166 static void smtpd_token_init(char *ptr, ssize_t count) 167 { 168 SMTPD_TOKEN *arg; 169 int n; 170 171 for (arg = (SMTPD_TOKEN *) ptr, n = 0; n < count; arg++, n++) 172 arg->vstrval = vstring_alloc(10); 173 } 174 175 /* smtpd_token - tokenize SMTPD command */ 176 177 int smtpd_token(char *cp, SMTPD_TOKEN **argvp) 178 { 179 static SMTPD_TOKEN *smtp_argv; 180 static MVECT mvect; 181 int n; 182 183 if (smtp_argv == 0) 184 smtp_argv = (SMTPD_TOKEN *) mvect_alloc(&mvect, sizeof(*smtp_argv), 1, 185 smtpd_token_init, (MVECT_FN) 0); 186 for (n = 0; /* void */ ; n++) { 187 smtp_argv = (SMTPD_TOKEN *) mvect_realloc(&mvect, n + 1); 188 if ((cp = smtp_next_token(cp, smtp_argv + n)) == 0) 189 break; 190 } 191 *argvp = smtp_argv; 192 return (n); 193 } 194 195 #ifdef TEST 196 197 /* 198 * Test program for the SMTPD command tokenizer. 199 */ 200 201 #include <stdlib.h> 202 #include <vstream.h> 203 #include <vstring_vstream.h> 204 205 int main(int unused_argc, char **unused_argv) 206 { 207 VSTRING *vp = vstring_alloc(10); 208 int tok_argc; 209 SMTPD_TOKEN *tok_argv; 210 int i; 211 212 for (;;) { 213 if (isatty(STDIN_FILENO)) 214 vstream_printf("enter SMTPD command: "); 215 vstream_fflush(VSTREAM_OUT); 216 if (vstring_get_nonl(vp, VSTREAM_IN) == VSTREAM_EOF) 217 break; 218 if (*vstring_str(vp) == '#') 219 continue; 220 if (!isatty(STDIN_FILENO)) 221 vstream_printf("%s\n", vstring_str(vp)); 222 tok_argc = smtpd_token(vstring_str(vp), &tok_argv); 223 for (i = 0; i < tok_argc; i++) { 224 vstream_printf("Token type: %s\n", 225 tok_argv[i].tokval == SMTPD_TOK_OTHER ? "other" : 226 tok_argv[i].tokval == SMTPD_TOK_ERROR ? "error" : 227 "unknown"); 228 vstream_printf("Token value: %s\n", tok_argv[i].strval); 229 } 230 } 231 vstring_free(vp); 232 exit(0); 233 } 234 235 #endif 236