improve error reporting from IMAP list parsing
so far, we'd print only a generic message - except in two cases, where the generic error would be preceded by a specific one. now we always print a single reasonably specific message.
This commit is contained in:
parent
c7f50a3069
commit
a07be5f175
|
@ -75,6 +75,7 @@ typedef struct {
|
||||||
list_t *head, **stack[MAX_LIST_DEPTH];
|
list_t *head, **stack[MAX_LIST_DEPTH];
|
||||||
int (*callback)( imap_store_t *ctx, list_t *list );
|
int (*callback)( imap_store_t *ctx, list_t *list );
|
||||||
int level, need_bytes;
|
int level, need_bytes;
|
||||||
|
const char *err;
|
||||||
} parse_list_state_t;
|
} parse_list_state_t;
|
||||||
|
|
||||||
typedef struct imap_cmd imap_cmd_t;
|
typedef struct imap_cmd imap_cmd_t;
|
||||||
|
@ -109,6 +110,7 @@ union imap_store {
|
||||||
uint caps; // CAPABILITY results
|
uint caps; // CAPABILITY results
|
||||||
string_list_t *auth_mechs;
|
string_list_t *auth_mechs;
|
||||||
parse_list_state_t parse_list_sts;
|
parse_list_state_t parse_list_sts;
|
||||||
|
const char *parse_list_what;
|
||||||
char *parse_list_cmd;
|
char *parse_list_cmd;
|
||||||
// Command queue
|
// Command queue
|
||||||
imap_cmd_t *pending, **pending_append;
|
imap_cmd_t *pending, **pending_append;
|
||||||
|
@ -819,8 +821,10 @@ parse_imap_list( imap_store_t *ctx, char **sp, parse_list_state_t *sts )
|
||||||
goto getbytes;
|
goto getbytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!s)
|
if (!s) {
|
||||||
|
sts->err = "missing value";
|
||||||
return LIST_BAD;
|
return LIST_BAD;
|
||||||
|
}
|
||||||
for (;;) {
|
for (;;) {
|
||||||
while (isspace( (uchar)*s ))
|
while (isspace( (uchar)*s ))
|
||||||
s++;
|
s++;
|
||||||
|
@ -836,7 +840,7 @@ parse_imap_list( imap_store_t *ctx, char **sp, parse_list_state_t *sts )
|
||||||
if (*s == '(') {
|
if (*s == '(') {
|
||||||
/* sublist */
|
/* sublist */
|
||||||
if (sts->level == MAX_LIST_DEPTH)
|
if (sts->level == MAX_LIST_DEPTH)
|
||||||
goto bail;
|
goto toodeep;
|
||||||
s++;
|
s++;
|
||||||
cur->val = LIST;
|
cur->val = LIST;
|
||||||
sts->stack[sts->level++] = curp;
|
sts->stack[sts->level++] = curp;
|
||||||
|
@ -846,11 +850,12 @@ parse_imap_list( imap_store_t *ctx, char **sp, parse_list_state_t *sts )
|
||||||
} else if (ctx && *s == '{') {
|
} else if (ctx && *s == '{') {
|
||||||
/* literal */
|
/* literal */
|
||||||
bytes = (int)(cur->len = strtoul( s + 1, &s, 10 ));
|
bytes = (int)(cur->len = strtoul( s + 1, &s, 10 ));
|
||||||
if (*s != '}' || *++s)
|
if (*s != '}' || *++s) {
|
||||||
|
sts->err = "malformed literal";
|
||||||
goto bail;
|
goto bail;
|
||||||
|
}
|
||||||
if ((uint)bytes >= INT_MAX) {
|
if ((uint)bytes >= INT_MAX) {
|
||||||
error( "IMAP error: excessively large literal from %s "
|
sts->err = "excessively large literal - THIS MIGHT BE AN ATTEMPT TO HACK YOU!";
|
||||||
"- THIS MIGHT BE AN ATTEMPT TO HACK YOU!\n", ctx->conn.name );
|
|
||||||
goto bail;
|
goto bail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -861,7 +866,7 @@ parse_imap_list( imap_store_t *ctx, char **sp, parse_list_state_t *sts )
|
||||||
n = socket_read( &ctx->conn, s, (uint)bytes );
|
n = socket_read( &ctx->conn, s, (uint)bytes );
|
||||||
if (n < 0) {
|
if (n < 0) {
|
||||||
badeof:
|
badeof:
|
||||||
error( "IMAP error: unexpected EOF from %s\n", ctx->conn.name );
|
sts->err = "unexpected EOF";
|
||||||
goto bail;
|
goto bail;
|
||||||
}
|
}
|
||||||
bytes -= n;
|
bytes -= n;
|
||||||
|
@ -891,8 +896,10 @@ parse_imap_list( imap_store_t *ctx, char **sp, parse_list_state_t *sts )
|
||||||
while ((c = *s++) != '"') {
|
while ((c = *s++) != '"') {
|
||||||
if (c == '\\')
|
if (c == '\\')
|
||||||
c = *s++;
|
c = *s++;
|
||||||
if (!c)
|
if (!c) {
|
||||||
|
sts->err = "unterminated quoted string";
|
||||||
goto bail;
|
goto bail;
|
||||||
|
}
|
||||||
*d++ = c;
|
*d++ = c;
|
||||||
}
|
}
|
||||||
cur->len = (uint)(d - p);
|
cur->len = (uint)(d - p);
|
||||||
|
@ -914,8 +921,10 @@ parse_imap_list( imap_store_t *ctx, char **sp, parse_list_state_t *sts )
|
||||||
if (!sts->level)
|
if (!sts->level)
|
||||||
break;
|
break;
|
||||||
next2:
|
next2:
|
||||||
if (!*s)
|
if (!*s) {
|
||||||
|
sts->err = "unterminated list";
|
||||||
goto bail;
|
goto bail;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
*sp = s;
|
*sp = s;
|
||||||
return LIST_OK;
|
return LIST_OK;
|
||||||
|
@ -926,6 +935,8 @@ parse_imap_list( imap_store_t *ctx, char **sp, parse_list_state_t *sts )
|
||||||
sts->need_bytes = bytes;
|
sts->need_bytes = bytes;
|
||||||
return LIST_PARTIAL;
|
return LIST_PARTIAL;
|
||||||
}
|
}
|
||||||
|
toodeep:
|
||||||
|
sts->err = "too deeply nested lists";
|
||||||
bail:
|
bail:
|
||||||
free_list( sts->head );
|
free_list( sts->head );
|
||||||
sts->level = 0;
|
sts->level = 0;
|
||||||
|
@ -937,6 +948,7 @@ parse_list_init( parse_list_state_t *sts )
|
||||||
{
|
{
|
||||||
sts->need_bytes = -1;
|
sts->need_bytes = -1;
|
||||||
sts->level = 1;
|
sts->level = 1;
|
||||||
|
sts->err = NULL;
|
||||||
sts->head = NULL;
|
sts->head = NULL;
|
||||||
sts->stack[0] = &sts->head;
|
sts->stack[0] = &sts->head;
|
||||||
}
|
}
|
||||||
|
@ -964,12 +976,23 @@ parse_next_list( imap_store_t *ctx, int (*cb)( imap_store_t *ctx, list_t *list )
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
parse_list( imap_store_t *ctx, char *s, int (*cb)( imap_store_t *ctx, list_t *list ) )
|
parse_list( imap_store_t *ctx, char *s, int (*cb)( imap_store_t *ctx, list_t *list ), const char *what )
|
||||||
{
|
{
|
||||||
ctx->parse_list_cmd = s;
|
ctx->parse_list_cmd = s;
|
||||||
|
ctx->parse_list_what = what;
|
||||||
return parse_next_list( ctx, cb );
|
return parse_next_list( ctx, cb );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
parse_list_perror( imap_store_t *ctx )
|
||||||
|
{
|
||||||
|
if (!ctx->parse_list_sts.err)
|
||||||
|
ctx->parse_list_sts.err = "unexpected value";
|
||||||
|
error( "IMAP error: malformed %s response from %s: %s\n",
|
||||||
|
ctx->parse_list_what, ctx->conn.name, ctx->parse_list_sts.err );
|
||||||
|
return LIST_BAD;
|
||||||
|
}
|
||||||
|
|
||||||
static int parse_namespace_rsp_p2( imap_store_t *, list_t * );
|
static int parse_namespace_rsp_p2( imap_store_t *, list_t * );
|
||||||
static int parse_namespace_rsp_p3( imap_store_t *, list_t * );
|
static int parse_namespace_rsp_p3( imap_store_t *, list_t * );
|
||||||
|
|
||||||
|
@ -981,8 +1004,7 @@ parse_namespace_rsp( imap_store_t *ctx, list_t *list )
|
||||||
|
|
||||||
if (!list) {
|
if (!list) {
|
||||||
bad:
|
bad:
|
||||||
error( "IMAP error: malformed NAMESPACE response\n" );
|
return parse_list_perror( ctx );
|
||||||
return LIST_BAD;
|
|
||||||
}
|
}
|
||||||
if (list->val != NIL) {
|
if (list->val != NIL) {
|
||||||
if (list->val != LIST)
|
if (list->val != LIST)
|
||||||
|
@ -1121,10 +1143,8 @@ parse_fetch_rsp( imap_store_t *ctx, list_t *list )
|
||||||
uint uid = 0, size = 0, msgid_len = 0;
|
uint uid = 0, size = 0, msgid_len = 0;
|
||||||
time_t date = 0;
|
time_t date = 0;
|
||||||
|
|
||||||
if (!is_list( list )) {
|
if (!is_list( list ))
|
||||||
error( "IMAP error: bogus FETCH response\n" );
|
return parse_list_perror( ctx );
|
||||||
return LIST_BAD;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (tmp = list->child; tmp; tmp = tmp->next) {
|
for (tmp = list->child; tmp; tmp = tmp->next) {
|
||||||
if (!is_atom( tmp )) {
|
if (!is_atom( tmp )) {
|
||||||
|
@ -1354,10 +1374,8 @@ parse_list_rsp( imap_store_t *ctx, list_t *list )
|
||||||
{
|
{
|
||||||
list_t *lp;
|
list_t *lp;
|
||||||
|
|
||||||
if (!is_list( list )) {
|
if (!is_list( list ))
|
||||||
error( "IMAP error: malformed LIST response\n" );
|
return parse_list_perror( ctx );
|
||||||
return LIST_BAD;
|
|
||||||
}
|
|
||||||
for (lp = list->child; lp; lp = lp->next)
|
for (lp = list->child; lp; lp = lp->next)
|
||||||
if (is_atom( lp ) && !strcasecmp( lp->val, "\\NoSelect" ))
|
if (is_atom( lp ) && !strcasecmp( lp->val, "\\NoSelect" ))
|
||||||
return LIST_OK;
|
return LIST_OK;
|
||||||
|
@ -1367,10 +1385,8 @@ parse_list_rsp( imap_store_t *ctx, list_t *list )
|
||||||
static int
|
static int
|
||||||
parse_list_rsp_p1( imap_store_t *ctx, list_t *list )
|
parse_list_rsp_p1( imap_store_t *ctx, list_t *list )
|
||||||
{
|
{
|
||||||
if (!is_opt_atom( list )) {
|
if (!is_opt_atom( list ))
|
||||||
error( "IMAP error: malformed LIST response\n" );
|
return parse_list_perror( ctx );
|
||||||
return LIST_BAD;
|
|
||||||
}
|
|
||||||
if (!ctx->delimiter[0] && is_atom( list ))
|
if (!ctx->delimiter[0] && is_atom( list ))
|
||||||
ctx->delimiter[0] = list->val[0];
|
ctx->delimiter[0] = list->val[0];
|
||||||
return parse_next_list( ctx, parse_list_rsp_p2 );
|
return parse_next_list( ctx, parse_list_rsp_p2 );
|
||||||
|
@ -1413,10 +1429,8 @@ parse_list_rsp_p2( imap_store_t *ctx, list_t *list )
|
||||||
int argl;
|
int argl;
|
||||||
uint l;
|
uint l;
|
||||||
|
|
||||||
if (!is_atom( list )) {
|
if (!is_atom( list ))
|
||||||
error( "IMAP error: malformed LIST response\n" );
|
return parse_list_perror( ctx );
|
||||||
return LIST_BAD;
|
|
||||||
}
|
|
||||||
arg = list->val;
|
arg = list->val;
|
||||||
argl = (int)list->len;
|
argl = (int)list->len;
|
||||||
if (argl > 1000) {
|
if (argl > 1000) {
|
||||||
|
@ -1623,10 +1637,10 @@ imap_socket_read( void *aux )
|
||||||
} else if (!strcmp( "CAPABILITY", arg )) {
|
} else if (!strcmp( "CAPABILITY", arg )) {
|
||||||
parse_capability( ctx, cmd );
|
parse_capability( ctx, cmd );
|
||||||
} else if (!strcmp( "LIST", arg ) || !strcmp( "LSUB", arg )) {
|
} else if (!strcmp( "LIST", arg ) || !strcmp( "LSUB", arg )) {
|
||||||
resp = parse_list( ctx, cmd, parse_list_rsp );
|
resp = parse_list( ctx, cmd, parse_list_rsp, "LIST" );
|
||||||
goto listret;
|
goto listret;
|
||||||
} else if (!strcmp( "NAMESPACE", arg )) {
|
} else if (!strcmp( "NAMESPACE", arg )) {
|
||||||
resp = parse_list( ctx, cmd, parse_namespace_rsp );
|
resp = parse_list( ctx, cmd, parse_namespace_rsp, "NAMESPACE" );
|
||||||
goto listret;
|
goto listret;
|
||||||
} else if ((arg1 = next_arg( &cmd ))) {
|
} else if ((arg1 = next_arg( &cmd ))) {
|
||||||
if (!strcmp( "EXISTS", arg1 )) {
|
if (!strcmp( "EXISTS", arg1 )) {
|
||||||
|
@ -1645,7 +1659,7 @@ imap_socket_read( void *aux )
|
||||||
if (!(seq = strtoul( arg, &arg1, 10 )) || *arg1)
|
if (!(seq = strtoul( arg, &arg1, 10 )) || *arg1)
|
||||||
goto badseq;
|
goto badseq;
|
||||||
ctx->fetch_seq = seq;
|
ctx->fetch_seq = seq;
|
||||||
resp = parse_list( ctx, cmd, parse_fetch_rsp );
|
resp = parse_list( ctx, cmd, parse_fetch_rsp, "FETCH" );
|
||||||
goto listret;
|
goto listret;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user