NetBSD/gnu/dist/postfix/smtpd/smtpd_token.c

229 lines
5.4 KiB
C

/*++
/* 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>
/* 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, int 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>
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);
}
}
exit(0);
}
#endif