xref: /netbsd-src/external/ibm-public/postfix/dist/src/smtpd/smtpd_token.c (revision e89934bbf778a6d6d6894877c4da59d0c7835b0f)
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