tttm

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

04442b2399951d0857e91143b93ea94222c1cb37

Author: Vasily Kolobkov on 05/27/2016

Committer: Vasily Kolobkov on 05/27/2016

Send user-supplied data in literal strings

Though slightly unwieldy, it spares you the bad luck of falling into a
notorious trap of mingling protocol with user-supplied data. Thus, even
more demanding users like mister '1 1\r\nx select inbox\r\n y store 1:*
+flags (\deleted)\r\nz expunge\r\n...' should be fine.

Stats

errors.c |   2 +
errors.h |   2 +
imap.c   | 109 +++++++-
3 files changed, 104 insertions(+), 9 deletions(-)

Patch

diff --git a/errors.c b/errors.c
index 44e1d23..8e0064a 100644
--- a/errors.c
+++ b/errors.c
@@ -14,6 +14,8 @@ const char *errmsgs[] = {
 	[TE_BAD]        = "got BAD result",
 	[TE_BYE]        = "server terminated the session",
 	[TE_NIL]        = "got NIL message",
+	[TE_CMDFMT]     = "command draws unsupported conversion specifier",
+	[TE_XLCMD]      = "command is too long",
 	[TE_IN]         = "input error",
 	[TE_OUT]        = "output error",
 	[TE_VM]         = "cache vm mapping",
diff --git a/errors.h b/errors.h
index 46fc1fb..914aa31 100644
--- a/errors.h
+++ b/errors.h
@@ -16,6 +16,8 @@ enum {
 	TE_BAD,
 	TE_BYE,
 	TE_NIL,
+	TE_CMDFMT,
+	TE_XLCMD,
 
 	TE_ERRNO,
 	TE_IN = TE_ERRNO,
diff --git a/imap.c b/imap.c
index 7d2998e..a1e2fc5 100644
--- a/imap.c
+++ b/imap.c
@@ -1,7 +1,9 @@
+#include <limits.h>
 #include <stdarg.h>
 #include <stddef.h>
 #include <stdint.h>
 #include <stdio.h>
+#include <string.h>
 #include <strings.h>
 #include <unistd.h>
 
@@ -43,6 +45,8 @@ struct fetargs {
 
 static int imap_cmd(struct imapctx *, int, const char *, struct respcard *,
     struct respcard *, int, ...);
+static int imap_confirm(struct imapctx *, const char *, struct respcard *,
+    va_list);
 static int imap_cooloff(struct imapctx *);
 static int imap_matchres(struct imapctx *, int, int *);
 static int imap_matchresp(struct imapctx *, struct respcard *, int, int *);
@@ -50,6 +54,8 @@ static int imap_parcaps(struct imapctx *, union parnode *);
 static int imap_readln(struct imapctx *);
 static int imap_readlnc(struct imapctx *, int, int *);
 static int imap_recvgreets(struct imapctx *);
+static int imap_send(struct imapctx *, const char *, struct respcard *,
+    va_list);
 
 static int dig_bye(struct imapctx *, int, void *);
 static int dig_uxbye(struct imapctx *, int, void *);
@@ -63,11 +69,6 @@ static int str2cap(struct capmap *, char *, size_t);
 static int isprod(union parnode *, int);
 static int islit(union parnode *, int);
 
-static struct respcard freeresp[] = {
-	{ IP_RESPBYE, dig_uxbye },
-	{ -1 },
-};
-
 #define UNILAT_COMMON \
 	{ IP_EXISTS, dig_exists },\
 	{ IP_RESPBYE, dig_uxbye }
@@ -115,14 +116,14 @@ imap_discocap(struct imapctx *con)
 int
 imap_login(struct imapctx *con, const char *name, const char *pass)
 {
-	return imap_cmd(con, IS_AUTH, "%u LOGIN %s %s\r\n", 0, unilat,
+	return imap_cmd(con, IS_AUTH, "%u LOGIN %LS %LS\r\n", 0, unilat,
 	    -1, con->tag++, name, pass);
 }
 
 int
 imap_select(struct imapctx *con, const char *mb)
 {
-	return imap_cmd(con, IS_SEL, "%u SELECT %s\r\n", selresp, unilat,
+	return imap_cmd(con, IS_SEL, "%u SELECT %LS\r\n", selresp, unilat,
 	    -1, con->tag++, mb);
 }
 
@@ -201,7 +202,7 @@ imap_cmd(struct imapctx *con, int nstate, const char *cmd,
 	if ((e = imap_cooloff(con))) {
 		goto exit;
 	}
-	if (vdprintf(con->out, cmd, ap) < 0) {
+	if ((e = imap_send(con, cmd, unilat, ap))) {
 		goto exit;
 	}
 	for (c = cmdresp; c && c->sel != -1; c++) {
@@ -235,6 +236,31 @@ imap_cmd(struct imapctx *con, int nstate, const char *cmd,
 	goto exit;
 }
 
+int
+imap_confirm(struct imapctx *con, const char *excerpt,
+    struct respcard *unilat, va_list ap)
+{
+	int e;
+
+	if (vdprintf(con->out, excerpt, ap) < 0)
+		goto eout;
+
+	while (!(e = imap_readln(con))) {
+		if (isprod(con->par, IP_CONTREQ) ||
+		    imap_matchres(con, 1, &e)) {
+			break;
+		}
+		if (imap_matchresp(con, unilat, 1, &e) && e) {
+			break;
+		}
+	}
+ exit:
+	return e;
+ eout:
+	e = TE_OUT;
+	goto exit;
+}
+
 int
 imap_cooloff(struct imapctx *con)
 {
@@ -243,7 +269,7 @@ imap_cooloff(struct imapctx *con)
 	while (!(e = laxsrc_ishot(&con->in))) {
 		if ((e = imap_readln(con)))
 			goto exit;
-		if (imap_matchresp(con, freeresp, 1, &e) && e)
+		if (imap_matchresp(con, unisexp, 1, &e) && e)
 			goto exit;
 	}
 	if (e == TE_TIMEOUT) {
@@ -350,6 +376,71 @@ imap_recvgreets(struct imapctx *con)
 	goto exit;
 }
 
+int
+imap_send(struct imapctx *con, const char *cmd, struct respcard *unilat,
+   va_list ap)
+{
+	int e, n;
+	va_list nap;
+	const char *pend, *ls, *s;
+	const char *lit;
+	char pad[128];
+	size_t xlen;
+
+	e = 0;
+	for (pend = cmd; (ls = strstr(pend, "%LS")); pend = ls + 3) {
+		va_copy(nap, ap);
+		for (s = pend; (s = strchr(s, '%')) && s < ls; s++) {
+			switch (*(s + 1)) {
+			case 'd':
+			case 'i':
+			case 'o':
+			case 'u':
+			case 'x':
+			case 'X':
+			case 'c':
+				va_arg(nap, unsigned);
+				break;
+			case 's':
+			case 'p':
+				va_arg(nap, void *);
+				break;
+			default:
+				goto efmt;
+			}
+		}
+		lit = va_arg(nap, char *);
+		if ((xlen = ls - pend) > INT_MAX)
+			goto exl;
+		n = snprintf(pad, LEN(pad), "%.*s{%u}\r\n",
+		    (int)xlen, pend, strlen(lit));
+		if (n < 0 || n >= LEN(pad))
+			goto exl;
+		if ((e = imap_confirm(con, pad, unilat, ap)))
+			goto exit;
+		if (dprintf(con->out, "%s", lit) < 0)
+			goto eout;
+		va_end(ap);
+		ap = nap;
+	}
+	if (*pend && vdprintf(con->out, pend, ap) < 0)
+		goto eout;
+ exit:
+	va_end(ap);
+	if (ls)
+		va_end(nap);
+	return e;
+ efmt:
+	e = TE_CMDFMT;
+	goto exit;
+ eout:
+	e = TE_OUT;
+	goto exit;
+ exl:
+	e = TE_XLCMD;
+	goto exit;
+}
+
 int
 dig_bye(struct imapctx *con, int buf, void *ctx)
 {