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
smtp_quoted(char * cp,SMTPD_TOKEN * arg,int start,int last)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
smtp_next_token(char * cp,SMTPD_TOKEN * arg)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
smtpd_token_init(char * ptr,ssize_t count)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
smtpd_token(char * cp,SMTPD_TOKEN ** argvp)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
main(int unused_argc,char ** unused_argv)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