git clone https://orangeshoelaces.net/git/tttm.git
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.
errors.c | 2 +
errors.h | 2 +
imap.c | 109 +++++++-
3 files changed, 104 insertions(+), 9 deletions(-)
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)
{