support backslashes and quotes in quoted IMAP strings
the RFCs require it - well hidden in the BNF at the bottom. patch somewhat inspired by "guns" <self@sungpae.com>.
This commit is contained in:
parent
c0bf867669
commit
2213d6976c
161
src/drv_imap.c
161
src/drv_imap.c
|
@ -355,6 +355,88 @@ submit_imap_cmd( imap_store_t *ctx, struct imap_cmd *cmd )
|
||||||
return send_imap_cmd( ctx, cmd );
|
return send_imap_cmd( ctx, cmd );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Minimal printf() replacement that supports an %\s format sequence to print backslash-escaped
|
||||||
|
* string literals. Note that this does not automatically add quotes around the printed string,
|
||||||
|
* so it is possible to concatenate multiple segments. */
|
||||||
|
static char *
|
||||||
|
imap_vprintf( const char *fmt, va_list ap )
|
||||||
|
{
|
||||||
|
const char *s, *es;
|
||||||
|
char *d, *ed;
|
||||||
|
int maxlen;
|
||||||
|
char c;
|
||||||
|
char buf[1024]; /* Minimal supported command buffer size per IMAP spec. */
|
||||||
|
|
||||||
|
d = buf;
|
||||||
|
ed = d + sizeof(buf);
|
||||||
|
s = fmt;
|
||||||
|
for (;;) {
|
||||||
|
c = *fmt;
|
||||||
|
if (!c || c == '%') {
|
||||||
|
int l = fmt - s;
|
||||||
|
if (d + l > ed)
|
||||||
|
oob();
|
||||||
|
memcpy( d, s, l );
|
||||||
|
d += l;
|
||||||
|
if (!c) {
|
||||||
|
l = d - buf;
|
||||||
|
ed = nfmalloc( l + 1 );
|
||||||
|
memcpy( ed, buf, l );
|
||||||
|
ed[l] = 0;
|
||||||
|
return ed;
|
||||||
|
}
|
||||||
|
maxlen = INT_MAX;
|
||||||
|
c = *++fmt;
|
||||||
|
if (c == '\\') {
|
||||||
|
c = *++fmt;
|
||||||
|
if (c != 's') {
|
||||||
|
fputs( "Fatal: unsupported escaped format specifier. Please report a bug.\n", stderr );
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
s = va_arg( ap, const char * );
|
||||||
|
while ((c = *s++)) {
|
||||||
|
if (d + 2 > ed)
|
||||||
|
oob();
|
||||||
|
if (c == '\\' || c == '"')
|
||||||
|
*d++ = '\\';
|
||||||
|
*d++ = c;
|
||||||
|
}
|
||||||
|
} else { /* \\ cannot be combined with anything else. */
|
||||||
|
if (c == '.') {
|
||||||
|
c = *++fmt;
|
||||||
|
if (c != '*') {
|
||||||
|
fputs( "Fatal: unsupported string length specification. Please report a bug.\n", stderr );
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
maxlen = va_arg( ap , int );
|
||||||
|
c = *++fmt;
|
||||||
|
}
|
||||||
|
if (c == 'c') {
|
||||||
|
if (d + 1 > ed)
|
||||||
|
oob();
|
||||||
|
*d++ = (char)va_arg( ap , int );
|
||||||
|
} else if (c == 's') {
|
||||||
|
s = va_arg( ap, const char * );
|
||||||
|
es = memchr( s, 0, maxlen );
|
||||||
|
l = es ? es - s : maxlen;
|
||||||
|
if (d + l > ed)
|
||||||
|
oob();
|
||||||
|
memcpy( d, s, l );
|
||||||
|
d += l;
|
||||||
|
} else if (c == 'd') {
|
||||||
|
d += nfsnprintf( d, ed - d, "%d", va_arg( ap , int ) );
|
||||||
|
} else {
|
||||||
|
fputs( "Fatal: unsupported format specifier. Please report a bug.\n", stderr );
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s = ++fmt;
|
||||||
|
} else {
|
||||||
|
fmt++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
imap_exec( imap_store_t *ctx, struct imap_cmd *cmdp,
|
imap_exec( imap_store_t *ctx, struct imap_cmd *cmdp,
|
||||||
void (*done)( imap_store_t *ctx, struct imap_cmd *cmd, int response ),
|
void (*done)( imap_store_t *ctx, struct imap_cmd *cmd, int response ),
|
||||||
|
@ -366,7 +448,7 @@ imap_exec( imap_store_t *ctx, struct imap_cmd *cmdp,
|
||||||
cmdp = new_imap_cmd( sizeof(*cmdp) );
|
cmdp = new_imap_cmd( sizeof(*cmdp) );
|
||||||
cmdp->param.done = done;
|
cmdp->param.done = done;
|
||||||
va_start( ap, fmt );
|
va_start( ap, fmt );
|
||||||
nfvasprintf( &cmdp->cmd, fmt, ap );
|
cmdp->cmd = imap_vprintf( fmt, ap );
|
||||||
va_end( ap );
|
va_end( ap );
|
||||||
return submit_imap_cmd( ctx, cmdp );
|
return submit_imap_cmd( ctx, cmdp );
|
||||||
}
|
}
|
||||||
|
@ -457,10 +539,25 @@ imap_refcounted_done_box( imap_store_t *ctx ATTR_UNUSED, struct imap_cmd *cmd, i
|
||||||
imap_refcounted_done( sts );
|
imap_refcounted_done( sts );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char *
|
||||||
|
imap_strchr( const char *s, char tc )
|
||||||
|
{
|
||||||
|
for (;; s++) {
|
||||||
|
char c = *s;
|
||||||
|
if (c == '\\')
|
||||||
|
c = *++s;
|
||||||
|
if (!c)
|
||||||
|
return 0;
|
||||||
|
if (c == tc)
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static char *
|
static char *
|
||||||
next_arg( char **ps )
|
next_arg( char **ps )
|
||||||
{
|
{
|
||||||
char *ret, *s;
|
char *ret, *s, *d;
|
||||||
|
char c;
|
||||||
|
|
||||||
assert( ps );
|
assert( ps );
|
||||||
s = *ps;
|
s = *ps;
|
||||||
|
@ -473,20 +570,30 @@ next_arg( char **ps )
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (*s == '"') {
|
if (*s == '"') {
|
||||||
++s;
|
s++;
|
||||||
ret = s;
|
ret = d = s;
|
||||||
s = strchr( s, '"' );
|
while ((c = *s++) != '"') {
|
||||||
|
if (c == '\\')
|
||||||
|
c = *s++;
|
||||||
|
if (!c) {
|
||||||
|
*ps = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
*d++ = c;
|
||||||
|
}
|
||||||
|
*d = 0;
|
||||||
} else {
|
} else {
|
||||||
ret = s;
|
ret = s;
|
||||||
while (*s && !isspace( (unsigned char)*s ))
|
while ((c = *s)) {
|
||||||
|
if (isspace( (unsigned char)c )) {
|
||||||
|
*s++ = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
s++;
|
s++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (s) {
|
if (!*s)
|
||||||
if (*s)
|
s = 0;
|
||||||
*s++ = 0;
|
|
||||||
if (!*s)
|
|
||||||
s = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
*ps = s;
|
*ps = s;
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -529,8 +636,9 @@ static int
|
||||||
parse_imap_list( imap_store_t *ctx, char **sp, parse_list_state_t *sts )
|
parse_imap_list( imap_store_t *ctx, char **sp, parse_list_state_t *sts )
|
||||||
{
|
{
|
||||||
list_t *cur, **curp;
|
list_t *cur, **curp;
|
||||||
char *s = *sp, *p;
|
char *s = *sp, *d, *p;
|
||||||
int bytes;
|
int bytes;
|
||||||
|
char c;
|
||||||
|
|
||||||
assert( sts );
|
assert( sts );
|
||||||
assert( sts->level > 0 );
|
assert( sts->level > 0 );
|
||||||
|
@ -595,12 +703,15 @@ parse_imap_list( imap_store_t *ctx, char **sp, parse_list_state_t *sts )
|
||||||
} else if (*s == '"') {
|
} else if (*s == '"') {
|
||||||
/* quoted string */
|
/* quoted string */
|
||||||
s++;
|
s++;
|
||||||
p = s;
|
p = d = s;
|
||||||
for (; *s != '"'; s++)
|
while ((c = *s++) != '"') {
|
||||||
if (!*s)
|
if (c == '\\')
|
||||||
|
c = *s++;
|
||||||
|
if (!c)
|
||||||
goto bail;
|
goto bail;
|
||||||
cur->len = s - p;
|
*d++ = c;
|
||||||
s++;
|
}
|
||||||
|
cur->len = d - p;
|
||||||
cur->val = nfmalloc( cur->len + 1 );
|
cur->val = nfmalloc( cur->len + 1 );
|
||||||
memcpy( cur->val, p, cur->len );
|
memcpy( cur->val, p, cur->len );
|
||||||
cur->val[cur->len] = 0;
|
cur->val[cur->len] = 0;
|
||||||
|
@ -1140,7 +1251,7 @@ imap_socket_read( void *aux )
|
||||||
cmd2->gen.param.high_prio = 1;
|
cmd2->gen.param.high_prio = 1;
|
||||||
p = strchr( cmdp->cmd, '"' );
|
p = strchr( cmdp->cmd, '"' );
|
||||||
if (imap_exec( ctx, &cmd2->gen, get_cmd_result_p2,
|
if (imap_exec( ctx, &cmd2->gen, get_cmd_result_p2,
|
||||||
"CREATE %.*s", strchr( p + 1, '"' ) - p + 1, p ) < 0)
|
"CREATE %.*s", imap_strchr( p + 1, '"' ) - p + 1, p ) < 0)
|
||||||
return;
|
return;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -1571,7 +1682,7 @@ imap_open_store_authenticate2( imap_store_t *ctx )
|
||||||
#endif
|
#endif
|
||||||
warn( "*** IMAP Warning *** Password is being sent in the clear\n" );
|
warn( "*** IMAP Warning *** Password is being sent in the clear\n" );
|
||||||
imap_exec( ctx, 0, imap_open_store_authenticate2_p2,
|
imap_exec( ctx, 0, imap_open_store_authenticate2_p2,
|
||||||
"LOGIN \"%s\" \"%s\"", srvc->user, srvc->pass );
|
"LOGIN \"%\\s\" \"%\\s\"", srvc->user, srvc->pass );
|
||||||
return;
|
return;
|
||||||
|
|
||||||
bail:
|
bail:
|
||||||
|
@ -1695,7 +1806,7 @@ imap_select( store_t *gctx, int create,
|
||||||
cmd->gen.param.create = create;
|
cmd->gen.param.create = create;
|
||||||
cmd->gen.param.trycreate = 1;
|
cmd->gen.param.trycreate = 1;
|
||||||
imap_exec( ctx, &cmd->gen, imap_done_simple_box,
|
imap_exec( ctx, &cmd->gen, imap_done_simple_box,
|
||||||
"SELECT \"%s\"", buf );
|
"SELECT \"%\\s\"", buf );
|
||||||
free( buf );
|
free( buf );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1912,7 +2023,7 @@ imap_trash_msg( store_t *gctx, message_t *msg,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
imap_exec( ctx, &cmd->gen, imap_done_simple_msg,
|
imap_exec( ctx, &cmd->gen, imap_done_simple_msg,
|
||||||
"UID COPY %d \"%s\"", msg->uid, buf );
|
"UID COPY %d \"%\\s\"", msg->uid, buf );
|
||||||
free( buf );
|
free( buf );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1966,10 +2077,10 @@ imap_store_msg( store_t *gctx, msg_data_t *data, int to_trash,
|
||||||
# pragma GCC diagnostic pop
|
# pragma GCC diagnostic pop
|
||||||
#endif
|
#endif
|
||||||
imap_exec( ctx, &cmd->gen, imap_store_msg_p2,
|
imap_exec( ctx, &cmd->gen, imap_store_msg_p2,
|
||||||
"APPEND \"%s\" %s\"%s\" ", buf, flagstr, datestr );
|
"APPEND \"%\\s\" %s\"%\\s\" ", buf, flagstr, datestr );
|
||||||
} else {
|
} else {
|
||||||
imap_exec( ctx, &cmd->gen, imap_store_msg_p2,
|
imap_exec( ctx, &cmd->gen, imap_store_msg_p2,
|
||||||
"APPEND \"%s\" %s", buf, flagstr );
|
"APPEND \"%\\s\" %s", buf, flagstr );
|
||||||
}
|
}
|
||||||
free( buf );
|
free( buf );
|
||||||
}
|
}
|
||||||
|
@ -2023,7 +2134,7 @@ imap_list( store_t *gctx, int flags,
|
||||||
|
|
||||||
if (((flags & LIST_PATH) &&
|
if (((flags & LIST_PATH) &&
|
||||||
imap_exec( ctx, imap_refcounted_new_cmd( sts ), imap_refcounted_done_box,
|
imap_exec( ctx, imap_refcounted_new_cmd( sts ), imap_refcounted_done_box,
|
||||||
"LIST \"\" \"%s*\"", ctx->prefix ) < 0) ||
|
"LIST \"\" \"%\\s*\"", ctx->prefix ) < 0) ||
|
||||||
((flags & LIST_INBOX) && (!(flags & LIST_PATH) || *ctx->prefix) &&
|
((flags & LIST_INBOX) && (!(flags & LIST_PATH) || *ctx->prefix) &&
|
||||||
imap_exec( ctx, imap_refcounted_new_cmd( sts ), imap_refcounted_done_box,
|
imap_exec( ctx, imap_refcounted_new_cmd( sts ), imap_refcounted_done_box,
|
||||||
"LIST \"\" INBOX*" ) < 0))
|
"LIST \"\" INBOX*" ) < 0))
|
||||||
|
|
Loading…
Reference in New Issue
Block a user