summaryrefslogtreecommitdiffstats
path: root/src/smtpd/smtpd_token.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/smtpd/smtpd_token.c')
-rw-r--r--src/smtpd/smtpd_token.c233
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