diff options
Diffstat (limited to 'src/smtpd/smtpd_token.c')
-rw-r--r-- | src/smtpd/smtpd_token.c | 233 |
1 files changed, 233 insertions, 0 deletions
diff --git a/src/smtpd/smtpd_token.c b/src/smtpd/smtpd_token.c new file mode 100644 index 0000000..927088f --- /dev/null +++ b/src/smtpd/smtpd_token.c @@ -0,0 +1,233 @@ +/*++ +/* NAME +/* smtpd_token 3 +/* SUMMARY +/* tokenize SMTPD command +/* SYNOPSIS +/* #include <smtpd_token.h> +/* +/* typedef struct { +/* .in +4 +/* int tokval; +/* char *strval; +/* /* other stuff... */ +/* .in -4 +/* } SMTPD_TOKEN; +/* +/* int smtpd_token(str, argvp) +/* char *str; +/* SMTPD_TOKEN **argvp; +/* DESCRIPTION +/* smtpd_token() routine converts the string in \fIstr\fR to an +/* array of tokens in \fIargvp\fR. The number of tokens is returned +/* via the function return value. +/* +/* Token types: +/* .IP SMTPD_TOK_OTHER +/* The token is something else. +/* .IP SMTPD_TOK_ERROR +/* A malformed token. +/* BUGS +/* This tokenizer understands just enough to tokenize SMTPD commands. +/* It understands backslash escapes, white space, quoted strings, +/* and addresses (including quoted text) enclosed by < and >. +/* The input is broken up into tokens by whitespace, except for +/* whitespace that is protected by quotes etc. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include <sys_defs.h> +#include <ctype.h> +#include <string.h> +#include <unistd.h> + +#ifdef STRCASECMP_IN_STRINGS_H +#include <strings.h> +#endif + +/* Utility library. */ + +#include <mymalloc.h> +#include <mvect.h> + +/* Application-specific. */ + +#include "smtpd_token.h" + +/* smtp_quoted - read until closing quote */ + +static char *smtp_quoted(char *cp, SMTPD_TOKEN *arg, int start, int last) +{ + static VSTRING *stack; + int wanted; + int c; + + /* + * Parser stack. `ch' is always the most-recently entered character. + */ +#define ENTER_CHAR(buf, ch) VSTRING_ADDCH(buf, ch); +#define LEAVE_CHAR(buf, ch) { \ + vstring_truncate(buf, VSTRING_LEN(buf) - 1); \ + ch = vstring_end(buf)[-1]; \ + } + + if (stack == 0) + stack = vstring_alloc(1); + VSTRING_RESET(stack); + ENTER_CHAR(stack, wanted = last); + + VSTRING_ADDCH(arg->vstrval, start); + for (;;) { + if ((c = *cp) == 0) + break; + cp++; + VSTRING_ADDCH(arg->vstrval, c); + if (c == '\\') { /* parse escape sequence */ + if ((c = *cp) == 0) + break; + cp++; + VSTRING_ADDCH(arg->vstrval, c); + } else if (c == wanted) { /* closing quote etc. */ + if (VSTRING_LEN(stack) == 1) + return (cp); + LEAVE_CHAR(stack, wanted); + } else if (c == '"') { + ENTER_CHAR(stack, wanted = '"'); /* highest precedence */ + } else if (c == '<' && wanted == '>') { + ENTER_CHAR(stack, wanted = '>'); /* lowest precedence */ + } + } + arg->tokval = SMTPD_TOK_ERROR; /* missing end */ + return (cp); +} + +/* smtp_next_token - extract next token from input, update cp */ + +static char *smtp_next_token(char *cp, SMTPD_TOKEN *arg) +{ + int c; + + VSTRING_RESET(arg->vstrval); + arg->tokval = SMTPD_TOK_OTHER; + +#define STR(x) vstring_str(x) +#define LEN(x) VSTRING_LEN(x) +#define STREQ(x,y,l) (strncasecmp((x), (y), (l)) == 0) + + for (;;) { + if ((c = *cp) == 0) /* end of input */ + break; + cp++; + if (ISSPACE(c)) { /* whitespace, skip */ + while (*cp && ISSPACE(*cp)) + cp++; + if (LEN(arg->vstrval) > 0) /* end of token */ + break; + } else if (c == '<') { /* <stuff> */ + cp = smtp_quoted(cp, arg, c, '>'); + } else if (c == '"') { /* "stuff" */ + cp = smtp_quoted(cp, arg, c, c); + } else if (c == ':') { /* this is gross, but... */ + VSTRING_ADDCH(arg->vstrval, c); + if (STREQ(STR(arg->vstrval), "to:", LEN(arg->vstrval)) + || STREQ(STR(arg->vstrval), "from:", LEN(arg->vstrval))) + break; + } else { /* other */ + if (c == '\\') { + VSTRING_ADDCH(arg->vstrval, c); + if ((c = *cp) == 0) + break; + cp++; + } + VSTRING_ADDCH(arg->vstrval, c); + } + } + if (LEN(arg->vstrval) <= 0) /* no token found */ + return (0); + VSTRING_TERMINATE(arg->vstrval); + arg->strval = vstring_str(arg->vstrval); + return (cp); +} + +/* smtpd_token_init - initialize token structures */ + +static void smtpd_token_init(char *ptr, ssize_t count) +{ + SMTPD_TOKEN *arg; + int n; + + for (arg = (SMTPD_TOKEN *) ptr, n = 0; n < count; arg++, n++) + arg->vstrval = vstring_alloc(10); +} + +/* smtpd_token - tokenize SMTPD command */ + +int smtpd_token(char *cp, SMTPD_TOKEN **argvp) +{ + static SMTPD_TOKEN *smtp_argv; + static MVECT mvect; + int n; + + if (smtp_argv == 0) + smtp_argv = (SMTPD_TOKEN *) mvect_alloc(&mvect, sizeof(*smtp_argv), 1, + smtpd_token_init, (MVECT_FN) 0); + for (n = 0; /* void */ ; n++) { + smtp_argv = (SMTPD_TOKEN *) mvect_realloc(&mvect, n + 1); + if ((cp = smtp_next_token(cp, smtp_argv + n)) == 0) + break; + } + *argvp = smtp_argv; + return (n); +} + +#ifdef TEST + + /* + * Test program for the SMTPD command tokenizer. + */ + +#include <stdlib.h> +#include <vstream.h> +#include <vstring_vstream.h> + +int main(int unused_argc, char **unused_argv) +{ + VSTRING *vp = vstring_alloc(10); + int tok_argc; + SMTPD_TOKEN *tok_argv; + int i; + + for (;;) { + if (isatty(STDIN_FILENO)) + vstream_printf("enter SMTPD command: "); + vstream_fflush(VSTREAM_OUT); + if (vstring_get_nonl(vp, VSTREAM_IN) == VSTREAM_EOF) + break; + if (*vstring_str(vp) == '#') + continue; + if (!isatty(STDIN_FILENO)) + vstream_printf("%s\n", vstring_str(vp)); + tok_argc = smtpd_token(vstring_str(vp), &tok_argv); + for (i = 0; i < tok_argc; i++) { + vstream_printf("Token type: %s\n", + tok_argv[i].tokval == SMTPD_TOK_OTHER ? "other" : + tok_argv[i].tokval == SMTPD_TOK_ERROR ? "error" : + "unknown"); + vstream_printf("Token value: %s\n", tok_argv[i].strval); + } + } + vstring_free(vp); + exit(0); +} + +#endif |