tttm

git clone https://orangeshoelaces.net/git/tttm.git

629c01f5d1f4b1c59a81c22d280652cbf66bb2e9

Author: Vasily Kolobkov on 05/20/2016

Committer: Vasily Kolobkov on 05/20/2016

Carry out session setup and tear down

- update main driver
- stake out usefull productions and ditch the rest
- bail out early on literal mismatch rather than waiting for a possible
timeout in par_procure call

Stats

errors.c  |  20 +
errors.h  |  16 +-
imap.c    | 267 ++++++++
imap.h    |  22 +
laxsrc.c  |   8 +-
parser.c  | 255 ++++---
parser.h  | 124 +---
pshades.c |  49 +-
tttm.c    | 109 +--
9 files changed, 547 insertions(+), 323 deletions(-)

Patch

diff --git a/errors.c b/errors.c
new file mode 100644
index 0000000..a864212
--- /dev/null
+++ b/errors.c
@@ -0,0 +1,20 @@
+#include "errors.h"
+
+const char *errmsgs[] = {
+	[TE_EOF]        = "unexpected eof",
+	[TE_TIMEOUT]    = "source read timed out",
+	[TE_BBOFLOW]    = "source back buffer overflown",
+	[TE_PARSE]      = "input not recognized",
+	[TE_BUFOFLOW]   = "response buffer overflown",
+	[TE_CACHEOFLOW] = "cache overflown",
+	[TE_PTOFLOW]    = "cannot fit parse tree within supplied space",
+	[TE_XLSTR]      = "stalled upon a string longer than 2^32 - 1",
+	[TE_PROTO]      = "IMAP protocol violated",
+	[TE_NO]         = "got NO result",
+	[TE_BAD]        = "got BAD result",
+	[TE_BYE]        = "server terminated the session",
+	[TE_IN]         = "input error",
+	[TE_OUT]        = "output error",
+	[TE_VM]         = "cache vm mapping",
+	[TE_CACHEIO]    = "cache i/o",
+};
diff --git a/errors.h b/errors.h
index b6a0823..13ef768 100644
--- a/errors.h
+++ b/errors.h
@@ -2,15 +2,25 @@ enum {
 	TE_OK = 0,
 
 	TE_EOF,
-	TE_IO,
 	TE_TIMEOUT,
 	TE_BBOFLOW,
 
 	TE_PARSE,
 	TE_BUFOFLOW,
-	TE_CACHEIO,
 	TE_CACHEOFLOW,
 	TE_PTOFLOW,
-	TE_VM,
 	TE_XLSTR,
+
+	TE_PROTO,
+	TE_NO,
+	TE_BAD,
+	TE_BYE,
+
+	TE_ERRNO,
+	TE_IN = TE_ERRNO,
+	TE_OUT,
+	TE_VM,
+	TE_CACHEIO,
 };
+
+extern const char *errmsgs[];
diff --git a/imap.c b/imap.c
new file mode 100644
index 0000000..61db883
--- /dev/null
+++ b/imap.c
@@ -0,0 +1,267 @@
+#include <stddef.h>
+#include <stdio.h>
+#include <strings.h>
+
+#include "errors.h"
+#include "laxsrc.h"
+#include "parser.h"
+#include "imap.h"
+
+#define LEN(x) (sizeof(x) / sizeof((x)[0]))
+
+static struct capmap {
+	int           cap;
+	char *        srep;
+	size_t        slen;
+} caps[] = {
+	{ CAP_IMAP4R1,    "IMAP4rev1",     9  },
+	{ CAP_NOLOGIN,    "LOGINDISABLED", 13 },
+	{ 0 },
+}, authcaps[] = {
+	{ CAP_AUTHPLAIN,  "PLAIN",         5  },
+	{ 0 },
+};
+
+static int imap_begreeted(struct imapctx *);
+static int imap_digcapdata(struct imapctx *, union parnode *);
+static int imap_digunsol(struct imapctx *);
+static int imap_readln(struct imapctx *);
+static int imap_pollres(struct imapctx *);
+static int par2res(union parnode *);
+static int str2cap(struct capmap *, char *, size_t);
+static int isprod(union parnode *, int);
+static int islit(union parnode *, int);
+
+int
+imap_init(struct imapctx *con, int in, int out)
+{
+	int e;
+
+	bzero(con, sizeof(*con));
+	con->state = IS_GREET;
+	laxsrc_init(&con->in, in);
+	con->out = out;
+	if ((e = imap_begreeted(con)))
+		goto exit;
+	if (!(con->caps & CAP_KNOWN))
+		e = imap_discocap(con);
+ exit:
+	return e;
+}
+
+int
+imap_discocap(struct imapctx *con)
+{
+	int e;
+	union parnode *n;
+
+	if (dprintf(con->out, "%u CAPABILITY\r\n", con->tag++) < 0)
+		goto eout;
+
+	while (!(e = imap_readln(con))) {
+		if ((n = par_sel(con->par, IP_RESPDATA, IP_CAPDATA, -1))) {
+			e = imap_digcapdata(con, n);
+			break;
+		}
+		if ((e = imap_digunsol(con)))
+			goto exit;
+	}
+ exit:
+	return e;
+ eout:
+	e = TE_OUT;
+	goto exit;
+}
+
+int
+imap_login(struct imapctx *con, const char *name, const char *pass)
+{
+	int e;
+	union parnode *s;
+
+	if (dprintf(con->out, "%u LOGIN %s %s\r\n", con->tag++, name, pass) < 0)
+		goto eout;
+	e = imap_pollres(con);
+ exit:
+	return e;
+ eout:
+	e = TE_OUT;
+	goto exit;
+}
+
+int
+imap_select(struct imapctx *con, const char *mb)
+{
+	int e;
+
+	if (dprintf(con->out, "%u SELECT %s\r\n", con->tag++, mb) < 0)
+		goto eout;
+	e = imap_pollres(con);
+ exit:
+	return e;
+ eout:
+	e = TE_OUT;
+	goto exit;
+}
+
+int
+imap_close(struct imapctx *con)
+{
+	int e;
+
+	if (dprintf(con->out, "%u CLOSE\r\n", con->tag++) < 0)
+		goto eout;
+	e = imap_pollres(con);
+ exit:
+	return e;
+ eout:
+	e = TE_OUT;
+	goto exit;
+}
+
+int
+imap_logout(struct imapctx *con)
+{
+	int e;
+
+	if (dprintf(con->out, "%u LOGOUT\r\n", con->tag++) < 0)
+		goto eout;
+	e = imap_pollres(con);
+	if (e != TE_BYE)
+		goto exit;
+	e = imap_pollres(con);
+ exit:
+	return e;
+ eout:
+	e = TE_OUT;
+	goto exit;
+}
+
+int
+imap_begreeted(struct imapctx *con)
+{
+	int e;
+	union parnode *n;
+
+	if ((e = imap_readln(con))) {
+		con->state = IS_LOGOUT;
+		goto exit;
+	}
+
+	if ((n = par_sel(con->par, IP_RESPDATA, IP_RESPAUTH, -1))) {
+		con->state = IS_AUTH;
+	} else if ((n = par_sel(con->par, IP_RESPDATA, IP_RESPSTATE, -1))) {
+		con->state = IS_NAUTH;
+	} else {
+		goto eproto;
+	}
+
+	if ((n = par_sel(n, n->inter.prod, IP_RESPTXTCODE, IP_CAPDATA, -1)))
+		e = imap_digcapdata(con, n);
+
+ exit:
+	return e;
+ eproto:
+	e = TE_PROTO;
+	goto exit;
+}
+
+int
+imap_digcapdata(struct imapctx *con, union parnode *n)
+{
+	union parnode *nend, *atom;
+	char *str;
+	size_t len;
+
+	for (nend = n + n->inter.len + 1, n++; n < nend; n = par_nn(n)) {
+		if (!isprod(n, IP_CAP))
+			continue;
+		atom = par_sel(n, IP_CAP, IP_ATOM, -1);
+		str = con->rep + (atom + 1)->str.off;
+		len = (atom + 1)->str.len;
+		if (islit(n + 1, IL_AUTHEQ)) {
+			con->caps |= str2cap(authcaps, str, len);
+		} else {
+			con->caps |= str2cap(caps, str, len);
+		}
+	}
+	con->caps |= CAP_KNOWN;
+	return 0;
+}
+
+int
+imap_digunsol(struct imapctx *con)
+{
+	return 0;
+}
+
+int
+imap_readln(struct imapctx *con)
+{
+	int e;
+
+	e = par_readln(&con->in, con->rep, LEN(con->rep),
+	    -1, con->par, LEN(con->par));
+	if (!e && par_sel(con->par, IP_RESPDATA, IP_RESPBYE, -1))
+		e = TE_BYE;
+
+	return e;
+}
+
+int
+imap_pollres(struct imapctx *con)
+{
+	int e;
+
+	while (!(e = imap_readln(con))) {
+		if (isprod(con->par, IP_TAGGEDRESP)) {
+			e = par2res(con->par);
+			break;
+		}
+		if ((e = imap_digunsol(con)))
+			goto exit;
+	}
+ exit:
+	return e;
+}
+
+int
+par2res(union parnode *resp)
+{
+	int e;
+	union parnode *s;
+
+	e = TE_OK;
+	s = par_sel(resp, IP_TAGGEDRESP, IP_RESPSTATE, -1);
+	switch ((s + 1)->lit.val) {
+	case IL_NO:
+		e = TE_NO;
+		break;
+	case IL_BAD:
+		e = TE_BAD;
+		break;
+	}
+	return e;
+}
+
+int
+str2cap(struct capmap *m, char *str, size_t len)
+{
+	for (; m->srep; m++) {
+		if (m->slen == len && (strncasecmp(m->srep, str, len) == 0))
+			return m->cap;
+	}
+	return 0;
+}
+
+int
+isprod(union parnode *n, int prod)
+{
+	return n->type == PN_INTER && n->inter.prod == prod;
+}
+
+int
+islit(union parnode *n, int lit)
+{
+	return n->type == PN_LIT && n->lit.val == lit;
+}
diff --git a/imap.h b/imap.h
new file mode 100644
index 0000000..0a69c29
--- /dev/null
+++ b/imap.h
@@ -0,0 +1,22 @@
+enum { IS_GREET, IS_NAUTH, IS_AUTH, IS_SEL, IS_LOGOUT };
+
+enum { CAP_KNOWN = 1, CAP_IMAP4R1, CAP_NOLOGIN, CAP_AUTHPLAIN };
+
+struct imapctx {
+	int           state;
+	struct laxsrc in;
+	int           out;
+
+	unsigned int  tag;
+	char          rep[2048];
+	union parnode par[1024];
+
+	int           caps;
+};
+
+int	imap_init(struct imapctx *, int, int);
+int	imap_discocap(struct imapctx *);
+int	imap_login(struct imapctx *, const char *, const char *);
+int	imap_select(struct imapctx *, const char *);
+int	imap_close(struct imapctx *);
+int	imap_logout(struct imapctx *);
diff --git a/laxsrc.c b/laxsrc.c
index 09eaef3..8d75eef 100644
--- a/laxsrc.c
+++ b/laxsrc.c
@@ -45,13 +45,13 @@ laxsrc_read(struct laxsrc *s, char *dst, size_t lo, size_t hi, int *e)
 	while (len < lo) {
 		ready = poll(&pd, 1, timeout);
 
-		if (ready == -1) goto eio;
+		if (ready == -1) goto ein;
 		if (ready == 0) goto eto;
 
 		if (pd.revents & (POLLIN | POLLHUP)) {
 			n = read(pd.fd, dst + len, hi - len);
 			if (n == -1) {
-				goto eio;
+				goto ein;
 			} else if (n == 0) {
 				goto eof;
 			} else {
@@ -62,8 +62,8 @@ laxsrc_read(struct laxsrc *s, char *dst, size_t lo, size_t hi, int *e)
 
  exit:
 	return len;
- eio:
-	*e = TE_IO;
+ ein:
+	*e = TE_IN;
 	len = 0;
 	goto exit;
  eto:
diff --git a/parser.c b/parser.c
index 7b8a854..2d62847 100644
--- a/parser.c
+++ b/parser.c
@@ -1,9 +1,10 @@
 /*
-   Be warned! Most internal parser functions bear unconventional call
-   semantics returning 1 on success and 0 otherwise. Where applicable
-   specific error code or 0 is dispensed to the parse context.
+   Be warned! p_* functions bear unconventional call semantics
+   returning 1 on success and 0 otherwise. Specific error code
+   or 0 is dispensed to the parse context.
 */
 
+#include <stdarg.h>
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
@@ -102,6 +103,7 @@ static struct literal {
 	[IL_PARSE]              = { "PARSE",             5  },
 	[IL_PERMFL]             = { "PERMANENTFLAGS",    14 },
 	[IL_PLUS]               = { "+",                 1  },
+	[IL_PREAUTH]            = { "PREAUTH",           7  },
 	[IL_QPRN]               = { "QUOTED-PRINTABLE",  16 },
 	[IL_RECENT]             = { "RECENT",            6  },
 	[IL_RFC822]             = { "RFC822",            6  },
@@ -155,8 +157,6 @@ struct parctx {
 
 typedef int parfn(struct parctx *);
 
-int par_readln(struct laxsrc *, char *, size_t, int, union parnode *, size_t);
-
 static int cmpchr(const void *, const void *);
 static int contains(const char *, size_t, char);
 
@@ -224,17 +224,7 @@ static int p_dayfix(struct parctx *);
 static int p_dig(struct parctx *, size_t);
 static int p_dquotedchar(struct parctx *);
 static int p_env(struct parctx *);
-static int p_envaddr(struct parctx *, int);
-static int p_envbcc(struct parctx *);
-static int p_envcc(struct parctx *);
-static int p_envdate(struct parctx *);
-static int p_envfrom(struct parctx *);
-static int p_envinrepto(struct parctx *);
-static int p_envmsgid(struct parctx *);
-static int p_envrepto(struct parctx *);
-static int p_envsender(struct parctx *);
-static int p_envsubj(struct parctx *);
-static int p_envto(struct parctx *);
+static int p_envaddr(struct parctx *);
 static int p_fetchrec(struct parctx *);
 static int p_flag(struct parctx *);
 static int p_flext(struct parctx *);
@@ -284,6 +274,7 @@ static int p_permflcode(struct parctx *);
 static int p_qchar(struct parctx *);
 static int p_qchars(struct parctx *);
 static int p_qstr(struct parctx *);
+static int p_respauth(struct parctx *);
 static int p_respbye(struct parctx *);
 static int p_respdata(struct parctx *);
 static int p_respln(struct parctx *);
@@ -327,6 +318,54 @@ par_readln(struct laxsrc *in, char *buf, size_t buflen, int cache,
 	return e ? e : p.e;
 }
 
+union parnode *
+par_nn(union parnode *n)
+{
+	if (n->type == PN_INTER)
+	    n += n->inter.len;
+
+	return n + 1;
+}
+
+union parnode *
+par_sel(union parnode *n, ...)
+{
+	int prod;
+	union parnode *beg, *end;
+	va_list ap;
+
+	beg = n;
+	va_start(ap, n);
+	if ((prod = va_arg(ap, int)) == -1)
+		goto exit;
+	if (n->type == PN_INTER && n->inter.prod == prod) {
+		end = n + n->inter.len + 1;
+		n++;
+	} else {
+		n = 0;
+		goto exit;
+	}
+
+	while ((prod = va_arg(ap, int)) != -1) {
+		for (; n < end; n = par_nn(n)) {
+			if (n->type == PN_INTER && n->inter.prod == prod) {
+				n++;
+				break;
+			}
+		}
+		if (n == end) {
+			n = 0;
+			break;
+		}
+	}
+ exit:
+	if (n != 0 && n != beg)
+		n--;
+
+	va_end(ap);
+	return n;
+}
+
 int
 cmpchr(const void *a, const void *b)
 {
@@ -536,14 +575,15 @@ int
 par_backup(struct parctx *p)
 {
 	int e;
-	size_t nwnd;
-	size_t wndlag;
-	size_t off;
+	size_t w, shed, nwnd;
+	size_t off, wndlag;
 
 	e = TE_OK;
-	nwnd = (p->strlen + wndcap - 1) / wndcap;
+	shed = p->cur.off / wndcap + 1;
+	nwnd = p->strlen / wndcap + (p->strlen % wndcap > 0);
+	off = (nwnd - 1) * wndcap;
 
-	for (off = (nwnd - 1) * wndcap; off >= p->cur.off; off -= wndcap) {
+	for (w = nwnd; w >= shed; w--, off -= wndcap) {
 		if ((e = par_movewnd(p, off)))
 			break;
 
@@ -805,9 +845,9 @@ p_astr(struct parctx *p)
 {
 	struct parcur b;
 
-	return p_beg(p, &b, IP_ASTR) &&
+	return p_chk(p, &b) &&
 	    (p_repchr(p, astr_specials, LEN(astr_specials), PN_STR) ||
-	    p_str(p)) && p_end(p, &b) || p_rwd(p, &b);
+	    p_str(p)) || p_rwd(p, &b);
 }
 
 int
@@ -1041,11 +1081,11 @@ p_bodyxmpt(struct parctx *p)
 int
 p_cap(struct parctx *p)
 {
-	struct parcur b;
+	struct parcur b, c;
 
-	return p_beg(p, &b, IP_CAP) && p_sp(p) &&
+	return p_chk(p, &b) && p_sp(p) && p_beg(p, &c, IP_CAP) &&
 	    (p_2xcombo(p, IL_AUTHEQ, p_authtype) || p_atom(p)) &&
-	    p_end(p, &b) || p_rwd(p, &b);
+	    p_end(p, &c) || p_rwd(p, &b);
 }
 
 int
@@ -1053,8 +1093,8 @@ p_capdata(struct parctx *p)
 {
 	struct parcur b;
 
-	return p_chk(p, &b) && p_lit(p, IL_CAP) &&
-	    p_rep(p, p_cap) || p_rwd(p, &b);
+	return p_beg(p, &b, IP_CAPDATA) && (p_lit(p, IL_CAP) &&
+	    p_rep(p, p_cap) || p_rwd(p, &b)) && p_end(p, &b);
 }
 
 int
@@ -1062,10 +1102,9 @@ p_contreq(struct parctx *p)
 {
 	struct parcur b;
 
-	return p_chk(p, &b) &&
-	    p_lit(p, IL_PLUS) && p_sp(p) &&
-	    (p_resptext(p) || p_base64(p)) &&
-	    p_lit(p, IL_EOL) || p_rwd(p, &b);
+	return p_beg(p, &b, IP_CONTREQ) && (p_lit(p, IL_PLUS) && p_sp(p) &&
+	    (p_resptext(p) || p_base64(p)) && p_lit(p, IL_EOL) ||
+	    p_rwd(p, &b)) && p_end(p, &b);
 }
 
 int
@@ -1108,7 +1147,7 @@ p_dig(struct parctx *p, size_t count)
 		leeway = p->wndoff + p->wndlee;
 
 		for (prp = p->wnd + wndlag; pr < leeway; pr++, prp++) {
-			if (count == 0 || (*prp < '0' && *prp > '9'))
+			if (count == 0 || *prp < '0' || *prp > '9')
 				goto stop;
 			prev = n;
 			n = UINT32_MAX & (10 * n + (*prp - 48));
@@ -1157,82 +1196,21 @@ p_env(struct parctx *p)
 {
 	struct parcur b;
 
-	return p_chk(p, &b) && p_opar(p) && p_envdate(p) && p_sp(p) &&
-	    p_envsubj(p) && p_sp(p) && p_envfrom(p) && p_sp(p) &&
-	    p_envsender(p) && p_sp(p) && p_envrepto(p) && p_sp(p) &&
-	    p_envto(p) && p_sp(p) && p_envcc(p) && p_sp(p) &&
-	    p_envbcc(p) && p_sp(p) && p_envinrepto(p) && p_sp(p) &&
-	    p_envmsgid(p) && p_cpar(p) || p_rwd(p, &b);
+	return p_chk(p, &b) && p_opar(p) && p_nstr(p) && p_sp(p) &&
+	    p_nstr(p) && p_sp(p) && p_envaddr(p) && p_sp(p) &&
+	    p_envaddr(p) && p_sp(p) && p_envaddr(p) && p_sp(p) &&
+	    p_envaddr(p) && p_sp(p) && p_envaddr(p) && p_sp(p) &&
+	    p_envaddr(p) && p_sp(p) && p_nstr(p) && p_sp(p) &&
+	    p_nstr(p) && p_cpar(p) || p_rwd(p, &b);
 }
 
 int
-p_envaddr(struct parctx *p, int prod)
+p_envaddr(struct parctx *p)
 {
 	struct parcur b;
 
-	return p_beg(p, &b, prod) &&
-	    (p_opar(p) && p_rep(p, p_addr) && p_cpar(p) ||
-	    p_rwd(p, &b) || p_lit(p, IL_NIL)) && p_end(p, &b);
-}
-
-int
-p_envbcc(struct parctx *p)
-{
-	return p_envaddr(p, IP_ENVBCC);
-}
-
-int
-p_envcc(struct parctx *p)
-{
-	return p_envaddr(p, IP_ENVCC);
-}
-
-int
-p_envdate(struct parctx *p)
-{
-	return p_nstr(p);
-}
-
-int
-p_envfrom(struct parctx *p)
-{
-	return p_envaddr(p, IP_ENVFROM);
-}
-
-int
-p_envinrepto(struct parctx *p)
-{
-	return p_nstr(p);
-}
-
-int
-p_envmsgid(struct parctx *p)
-{
-	return p_nstr(p);
-}
-
-int
-p_envrepto(struct parctx *p)
-{
-	return p_envaddr(p, IP_ENVREPTO);
-}
-
-int
-p_envsender(struct parctx *p)
-{
-	return p_envaddr(p, IP_ENVSENDER);
-}
-
-int
-p_envsubj(struct parctx *p)
-{
-	return p_nstr(p);
-}
-
-int
-p_envto(struct parctx *p)
-{
-	return p_envaddr(p, IP_ENVTO);
+	return p_chk(p, &b) && p_opar(p) && p_rep(p, p_addr) && p_cpar(p) ||
+	    p_rwd(p, &b) || p_lit(p, IL_NIL);
 }
 
 int
@@ -1353,24 +1331,32 @@ int
 p_lit(struct parctx *p, int val)
 {
 	struct literal *lit;
-	size_t wndlag;
+	size_t match, chunk, wndlag;
 
 	lit = literals + val;
+	match = 0;
 
-	if ((p->e = par_procure(p, p->cur.off, lit->slen)))
-		goto exit;
-
-	wndlag = p->cur.off - p->wndoff;
-	if (strncasecmp(p->wnd + wndlag, lit->srep, lit->slen) == 0) {
-		if (p_inslit(p, val)) {
-			p->cur.off += lit->slen;
-			p->e = TE_OK;
+	while (match < lit->slen) {
+		if ((p->e = par_procure(p, p->cur.off + match, 1)))
+			goto exit;
+		wndlag = p->cur.off + match - p->wndoff;
+		chunk = MIN(lit->slen - match, p->wndlee - wndlag);
+		if (strncasecmp(p->wnd + wndlag + match, lit->srep + match, chunk) == 0) {
+			match += chunk;
+		} else {
+			goto eparse;
 		}
-	} else {
-		p->e = TE_PARSE;
+	}
+
+	if (p_inslit(p, val)) {
+		p->cur.off += lit->slen;
+		p->e = TE_OK;
 	}
  exit:
 	return p->e == TE_OK;
+ eparse:
+	p->e = TE_PARSE;
+	goto exit;
 }
 
 int
@@ -1835,13 +1821,22 @@ p_qstr(struct parctx *p)
 	    p_dquote(p) || p_rwd(p, &b);
 }
 
+int
+p_respauth(struct parctx *p)
+{
+	struct parcur b;
+
+	return p_beg(p, &b, IP_RESPAUTH) && (p_lit(p, IL_PREAUTH) && p_sp(p) &&
+	    p_resptext(p) || p_rwd(p, &b)) && p_end(p, &b);
+}
+
 int
 p_respbye(struct parctx *p)
 {
 	struct parcur b;
 
-	return p_chk(p, &b) && p_lit(p, IL_BYE) && p_sp(p) &&
-	    p_resptext(p) || p_rwd(p, &b);
+	return p_beg(p, &b, IP_RESPBYE) && (p_lit(p, IL_BYE) && p_sp(p) &&
+	    p_resptext(p) || p_rwd(p, &b)) && p_end(p, &b);
 }
 
 int
@@ -1849,10 +1844,10 @@ p_respdata(struct parctx *p)
 {
 	struct parcur b;
 
-	return p_chk(p, &b) && p_lit(p, IL_ASTERISK) &&
-	    p_sp(p) && (p_respstate(p) ||
-	    p_respbye(p) || p_mbdata(p) || p_msgdata(p) ||
-	    p_capdata(p)) && p_lit(p, IL_EOL) || p_rwd(p, &b);
+	return p_beg(p, &b, IP_RESPDATA) && (p_lit(p, IL_ASTERISK) && p_sp(p) &&
+	    (p_respauth(p) || p_respstate(p) || p_respbye(p) ||
+	    p_mbdata(p) || p_msgdata(p) || p_capdata(p)) &&
+	    p_lit(p, IL_EOL) || p_rwd(p, &b)) && p_end(p, &b);
 }
 
 int
@@ -1866,9 +1861,9 @@ p_respstate(struct parctx *p)
 {
 	struct parcur b;
 
-	return p_chk(p, &b) &&
-	    (p_lit(p, IL_OK) || p_lit(p, IL_NO) || p_lit(p, IL_BAD)) &&
-	    p_sp(p) && p_resptext(p) || p_rwd(p, &b);
+	return p_beg(p, &b, IP_RESPSTATE) &&
+	    ((p_lit(p, IL_OK) || p_lit(p, IL_NO) || p_lit(p, IL_BAD)) &&
+	    p_sp(p) && p_resptext(p) || p_rwd(p, &b)) && p_end(p, &b);
 }
 
 int
@@ -1887,12 +1882,14 @@ p_resptext(struct parctx *p)
 int
 p_resptextcode(struct parctx *p)
 {
-	return p_lit(p, IL_ALERT) || p_badcscode(p) ||
-	    p_capdata(p) || p_lit(p, IL_PARSE) ||
-	    p_permflcode(p) || p_lit(p, IL_RO) ||
-	    p_lit(p, IL_RW) || p_lit(p, IL_TRYC) ||
-	    p_uidncode(p) || p_uidvcode(p) ||
-	    p_unseencode(p) || p_gencode(p);
+	struct parcur b;
+
+	return p_beg(p, &b, IP_RESPTXTCODE) &&
+	    (p_lit(p, IL_ALERT) || p_badcscode(p) || p_capdata(p) ||
+	    p_lit(p, IL_PARSE) || p_permflcode(p) || p_lit(p, IL_RO) ||
+	    p_lit(p, IL_RW) || p_lit(p, IL_TRYC) || p_uidncode(p) ||
+	    p_uidvcode(p) || p_unseencode(p) || p_gencode(p) ||
+	    p_rwd(p, &b)) && p_end(p, &b);
 }
 
 int
diff --git a/parser.h b/parser.h
index c649e16..3295245 100644
--- a/parser.h
+++ b/parser.h
@@ -1,108 +1,30 @@
 enum {
-	IL_7BIT,
-	IL_8BIT,
-	IL_ALERT,
-	IL_APP,
-	IL_APR,
-	IL_ASTERISK,
-	IL_AUDIO,
-	IL_AUG,
-	IL_AUTHEQ,
-	IL_B64,
-	IL_BAD,
-	IL_BADCS,
-	IL_BIN,
-	IL_BODY,
-	IL_BSLASH,
-	IL_BYE,
-	IL_CAP,
-	IL_CBRACE,
-	IL_CBRACK,
-	IL_COLON,
-	IL_CPAR,
-	IL_DEC,
-	IL_DHDR,
-	IL_DNOT,
-	IL_DOT,
-	IL_DQUOTE,
-	IL_DTXT,
-	IL_ENVELOPE,
-	IL_EOL,
-	IL_EXISTS,
-	IL_EXPUNGE,
-	IL_FEB,
-	IL_FETCH,
-	IL_FLAGS,
-	IL_FLANSWERED,
-	IL_FLANY,
-	IL_FLDELETED,
-	IL_FLDRAFT,
-	IL_FLFLAGGED,
-	IL_FLMARKED,
-	IL_FLNOINFERIORS,
-	IL_FLNOSEL,
-	IL_FLRECENT,
-	IL_FLSEEN,
-	IL_FLUNMARKED,
-	IL_GT,
-	IL_HDR,
-	IL_HDRFIELDS,
-	IL_IMG,
-	IL_INBOX,
-	IL_INTDATE,
-	IL_JAN,
-	IL_JUL,
-	IL_JUN,
-	IL_LIST,
-	IL_LSUB,
-	IL_LT,
-	IL_MAR,
-	IL_MAY,
-	IL_MIME,
-	IL_MINUS,
-	IL_MSG,
-	IL_MSGS,
-	IL_NIL,
-	IL_NO,
-	IL_NOV,
-	IL_OBRACE,
-	IL_OBRACK,
-	IL_OCT,
-	IL_OK,
-	IL_OPAR,
-	IL_PARSE,
-	IL_PERMFL,
-	IL_PLUS,
-	IL_QPRN,
-	IL_RECENT,
-	IL_RFC822,
-	IL_RFC822SIZE,
-	IL_RO,
-	IL_RW,
-	IL_SEARCH,
-	IL_SEP,
-	IL_SP,
-	IL_STATUS,
-	IL_STRUCT,
-	IL_TRYC,
-	IL_TXT,
-	IL_UID,
-	IL_UIDNEXT,
-	IL_UIDVAL,
-	IL_UNSEEN,
-	IL_VIDEO,
+	IL_7BIT, IL_8BIT, IL_ALERT, IL_APP, IL_APR, IL_ASTERISK, IL_AUDIO,
+	IL_AUG, IL_AUTHEQ, IL_B64, IL_BAD, IL_BADCS, IL_BIN, IL_BODY,
+	IL_BSLASH, IL_BYE, IL_CAP, IL_CBRACE, IL_CBRACK, IL_COLON, IL_CPAR,
+	IL_DEC, IL_DHDR, IL_DNOT, IL_DOT, IL_DQUOTE, IL_DTXT, IL_ENVELOPE,
+	IL_EOL, IL_EXISTS, IL_EXPUNGE, IL_FEB, IL_FETCH, IL_FLAGS,
+	IL_FLANSWERED, IL_FLANY, IL_FLDELETED, IL_FLDRAFT, IL_FLFLAGGED,
+	IL_FLMARKED, IL_FLNOINFERIORS, IL_FLNOSEL, IL_FLRECENT, IL_FLSEEN,
+	IL_FLUNMARKED, IL_GT, IL_HDR, IL_HDRFIELDS, IL_IMG, IL_INBOX,
+	IL_INTDATE, IL_JAN, IL_JUL, IL_JUN, IL_LIST, IL_LSUB, IL_LT, IL_MAR,
+	IL_MAY, IL_MIME, IL_MINUS, IL_MSG, IL_MSGS, IL_NIL, IL_NO, IL_NOV,
+	IL_OBRACE, IL_OBRACK, IL_OCT, IL_OK, IL_OPAR, IL_PARSE, IL_PERMFL,
+	IL_PLUS, IL_PREAUTH, IL_QPRN, IL_RECENT, IL_RFC822, IL_RFC822SIZE,
+	IL_RO, IL_RW, IL_SEARCH, IL_SEP, IL_SP, IL_STATUS, IL_STRUCT,
+	IL_TRYC, IL_TXT, IL_UID, IL_UIDNEXT, IL_UIDVAL, IL_UNSEEN, IL_VIDEO,
 };
 
 enum {
-	IP_ASTR,
 	IP_ATOM,
 	IP_CAP,
-	IP_ENVBCC,
-	IP_ENVCC,
-	IP_ENVFROM,
-	IP_ENVREPTO,
-	IP_ENVSENDER,
-	IP_ENVTO,
+	IP_CAPDATA,
+	IP_CONTREQ,
+	IP_RESPAUTH,
+	IP_RESPBYE,
+	IP_RESPDATA,
+	IP_RESPSTATE,
+	IP_RESPTXTCODE,
 	IP_TAGGEDRESP,
 };
 
@@ -132,4 +54,6 @@ union parnode {
 
 struct laxsrc;
 
-int par_readln(struct laxsrc *, char *, size_t, int, union parnode *, size_t);
+int	par_readln(struct laxsrc *, char *, size_t, int, union parnode *, size_t);
+union parnode *par_nn(union parnode *);
+union parnode *par_sel(union parnode *, ...);
diff --git a/pshades.c b/pshades.c
index 8b98251..e3fabc1 100644
--- a/pshades.c
+++ b/pshades.c
@@ -11,31 +11,16 @@
 
 #define LEN(a) (sizeof(a) / sizeof(a)[0])
 
-char *errors[] = {
-	[TE_EOF]        = "unexpected eof",
-	[TE_IO]         = "source i/o",
-	[TE_TIMEOUT]    = "source read timed out",
-	[TE_BBOFLOW]    = "source back buffer overflown",
-	[TE_PARSE]      = "input not recognized",
-	[TE_BUFOFLOW]   = "response buffer overflown",
-	[TE_CACHEIO]    = "cache i/o",
-	[TE_CACHEOFLOW] = "cache overflown",
-	[TE_PTOFLOW]    = "cannot fit parse tree within supplied space",
-	[TE_VM]         = "cache vm mapping",
-	[TE_XLSTR]      = "stalled upon a string longer than 2^32 - 1",
-
-};
-
 char *prod[] = {
-	"ASTR",
 	"ATOM",
 	"CAP",
-	"ENVBCC",
-	"ENVCC",
-	"ENVFROM",
-	"ENVREPTO",
-	"ENVSENDER",
-	"ENVTO",
+	"CAPDATA",
+	"CONTREQ",
+	"RESPAUTH",
+	"RESPBYE",
+	"RESPDATA",
+	"RESPSTATE",
+	"RESPTXTCODE",
 	"TAGGEDRESP",
 };
 
@@ -50,13 +35,12 @@ char *literals[] = {
 	"INBOX", "INTDATE", "JAN", "JUL", "JUN", "LIST", "LSUB", "LT",
 	"MAR", "MAY", "MIME", "MINUS", "MSG", "MSGS", "NIL", "NO", "NOV",
 	"OBRACE", "OBRACK", "OCT", "OK", "OPAR", "PARSE", "PERMFL", "PLUS",
-	"QPRN", "RECENT", "RFC822", "RFC822SIZE", "RO", "RW", "SEARCH",
-	"SEP", "SP", "STATUS", "STRUCT", "TRYC", "TXT", "UID", "UIDNEXT",
-	"UIDVAL", "UNSEEN", "VIDEO"
+	"PREAUTH", "QPRN", "RECENT", "RFC822", "RFC822SIZE", "RO", "RW",
+	"SEARCH", "SEP", "SP", "STATUS", "STRUCT", "TRYC", "TXT", "UID",
+	"UIDNEXT", "UIDVAL", "UNSEEN", "VIDEO"
 };
 
 void ptprint(union parnode *, char *, size_t);
-union parnode *ptnext(union parnode *);
 
 int
 main(int argc, char **argv)
@@ -68,21 +52,12 @@ main(int argc, char **argv)
 
 	laxsrc_init(&in, STDIN_FILENO);
 	if ((e = par_readln(&in, ln, LEN(ln), -1, pt, LEN(pt))))
-		errx(1, "error parsing input: %s",  errors[e]);
+		errx(1, "error parsing input: %s",  errmsgs[e]);
 
 	ptprint(pt, ln, 0);
 	return 0;
 }
 
-union parnode *
-ptnext(union parnode *n)
-{
-	if (n->type == PN_INTER)
-	    n += n->inter.len;
-
-	return n + 1;
-}
-
 void
 ptprint(union parnode *n, char *ln, size_t depth)
 {
@@ -94,7 +69,7 @@ ptprint(union parnode *n, char *ln, size_t depth)
 	switch (n->type) {
 	case PN_INTER:
 		printf("%s\n", prod[n->inter.prod]);
-		for (nend = n + n->inter.len + 1, n++; n < nend; n = ptnext(n))
+		for (nend = n + n->inter.len + 1, n++; n < nend; n = par_nn(n))
 			ptprint(n, ln, depth + 1);
 		break;
 	case PN_LIT:
diff --git a/tttm.c b/tttm.c
index ea909b8..fc7684c 100644
--- a/tttm.c
+++ b/tttm.c
@@ -1,60 +1,69 @@
-enum { TOK_NIL, TOK_ATOM, TOK_SPEC
-int
-imap_readline()
-{
-}
+#include <err.h>
+#include <unistd.h>
+
+#include "errors.h"
+#include "laxsrc.h"
+#include "parser.h"
+#include "imap.h"
+
+static void fin(int, const char *);
 
 int
-imap_init(struct imap_ctx *c, char *name, char *password)
+main(int argc, char **argv)
 {
-	l = imap_readline();
-	if (l.kind != UNTAGGED)
-		return 1;
-	switch (l.cond) {
-	case OK:
-		c.state = NOT_AUTHENTICATED;
-		if (hascap(l))
-			imap_proccap(l.cap);
-		else
-			imap_discocap(c);
-		break;
-	case PREAUTH:
-		c.state = AUTHENTICATED;
-		break;
-	case BYE:
-		return 1;
-	default:
-		return 1;
+	int e;
+	struct imapctx con;
+	char *name, *pass;
+
+	if ((e = imap_init(&con, STDIN_FILENO, STDOUT_FILENO)))
+		fin(e, "failed to initialize session");
+
+	if (con.state == IS_AUTH)
+		goto select;
+
+	name = argv[1];
+	pass = argv[2];
+	e = imap_login(&con, name, pass);
+	if (e == TE_NO) {
+		warnx("login credentials rejected");
+		goto logout;
+	} else if (e == TE_BAD) {
+		warnx("failed to log in: %s", errmsgs[e]);
+		goto logout;
+	} else if (e) {
+		fin(e, "failed to log in");
 	}
+
+ select:
+	e = imap_select(&con, "inbox");
+	if (e == TE_NO || e == TE_BAD) {
+		warnx("can't access mailbox: %s", errmsgs[e]);
+		goto logout;
+	} else if (e) {
+		fin(e, "failed to select mailbox");
+	}
+
+	e = imap_close(&con);
+	if (e == TE_BAD) {
+		warnx("failed to close mailbox: %s", errmsgs[e]);
+		goto logout;
+	} else if (e) {
+		fin(e, "failed to close mailbox");
+	}
+
+ logout:
+	if ((e = imap_logout(&con)))
+		fin(e, "failed to log out");
+
 	return 0;
 }
 
-int
-main(int argc, char **argv)
+void
+fin(int e, const char *msg)
 {
-	/*
-		- read greeting and decide on state (OK/PREAUTH/BYE)
-		- auth
-		- ls mailboxes
-		- for each mailbox
-			- open mailbox
-			- fetch all messages (store under /tmp if necessary)
-			- pipe
-			- store +\Deleted
-			- expunge
-	*/
-	ctx = imap_ctxinit();
-	imap_auth(name, password);
-	boxes = imap_list(ctx);
-	for (box = boxes; box; box = box->next) {
-		imap_select(box);
-		while (box->nmsg) {
-			h = imap_fetch(1);
-			pipe(h);
-			imap_delete(h);
-			expunge(ctx);
-		}
-		imap_close(box);
+	if (e > TE_ERRNO) {
+		err(1, "%s: %s", msg, errmsgs[e]);
+	} else {
+		errx(1, "%s: %s", msg, errmsgs[e]);
 	}
-	return 0;
 }