make parse_list() callback based

this allows us to parse IMAP literals ({}) in every list.
This commit is contained in:
Oswald Buddenhagen 2013-07-27 14:31:13 +02:00
parent bf049d6466
commit fc77feacc5

View File

@ -70,8 +70,11 @@ typedef struct _list {
#define MAX_LIST_DEPTH 5 #define MAX_LIST_DEPTH 5
struct imap_store;
typedef struct parse_list_state { typedef struct parse_list_state {
list_t *head, **stack[MAX_LIST_DEPTH]; list_t *head, **stack[MAX_LIST_DEPTH];
int (*callback)( struct imap_store *ctx, list_t *list, char *cmd );
int level, need_bytes; int level, need_bytes;
} parse_list_state_t; } parse_list_state_t;
@ -642,18 +645,63 @@ parse_list_init( parse_list_state_t *sts )
sts->stack[0] = &sts->head; sts->stack[0] = &sts->head;
} }
static list_t * static int
parse_list( char **sp ) parse_list_continue( imap_store_t *ctx, char *s )
{ {
parse_list_state_t sts; list_t *list;
parse_list_init( &sts ); int resp;
if (parse_imap_list( 0, sp, &sts ) == LIST_OK) if ((resp = parse_imap_list( ctx, &s, &ctx->parse_list_sts )) != LIST_PARTIAL) {
return sts.head; list = (resp == LIST_BAD) ? 0 : ctx->parse_list_sts.head;
return NULL; ctx->parse_list_sts.head = 0;
resp = ctx->parse_list_sts.callback( ctx, list, s );
}
return resp;
} }
static int static int
parse_fetch( imap_store_t *ctx, list_t *list ) parse_list( imap_store_t *ctx, char *s, int (*cb)( imap_store_t *ctx, list_t *list, char *s ) )
{
parse_list_init( &ctx->parse_list_sts );
ctx->parse_list_sts.callback = cb;
return parse_list_continue( ctx, s );
}
static int parse_namespace_rsp_p2( imap_store_t *, list_t *, char * );
static int parse_namespace_rsp_p3( imap_store_t *, list_t *, char * );
static int
parse_namespace_rsp_fail( void )
{
error( "IMAP error: malformed NAMESPACE response\n" );
return LIST_BAD;
}
static int
parse_namespace_rsp( imap_store_t *ctx, list_t *list, char *s )
{
if (!(ctx->ns_personal = list))
return parse_namespace_rsp_fail();
return parse_list( ctx, s, parse_namespace_rsp_p2 );
}
static int
parse_namespace_rsp_p2( imap_store_t *ctx, list_t *list, char *s )
{
if (!(ctx->ns_other = list))
return parse_namespace_rsp_fail();
return parse_list( ctx, s, parse_namespace_rsp_p3 );
}
static int
parse_namespace_rsp_p3( imap_store_t *ctx, list_t *list, char *s ATTR_UNUSED )
{
if (!(ctx->ns_shared = list))
return parse_namespace_rsp_fail();
return LIST_OK;
}
static int
parse_fetch_rsp( imap_store_t *ctx, list_t *list, char *s ATTR_UNUSED )
{ {
list_t *tmp, *flags; list_t *tmp, *flags;
char *body = 0, *tuid = 0; char *body = 0, *tuid = 0;
@ -666,7 +714,7 @@ parse_fetch( imap_store_t *ctx, list_t *list )
if (!is_list( list )) { if (!is_list( list )) {
error( "IMAP error: bogus FETCH response\n" ); error( "IMAP error: bogus FETCH response\n" );
free_list( list ); free_list( list );
return -1; return LIST_BAD;
} }
for (tmp = list->child; tmp; tmp = tmp->next) { for (tmp = list->child; tmp; tmp = tmp->next) {
@ -741,7 +789,7 @@ parse_fetch( imap_store_t *ctx, list_t *list )
goto gotuid; goto gotuid;
error( "IMAP error: unexpected FETCH response (UID %d)\n", uid ); error( "IMAP error: unexpected FETCH response (UID %d)\n", uid );
free_list( list ); free_list( list );
return -1; return LIST_BAD;
gotuid: gotuid:
msgdata = ((struct imap_cmd_fetch_msg *)cmdp)->msg_data; msgdata = ((struct imap_cmd_fetch_msg *)cmdp)->msg_data;
msgdata->data = body; msgdata->data = body;
@ -768,7 +816,7 @@ parse_fetch( imap_store_t *ctx, list_t *list )
} }
free_list( list ); free_list( list );
return 0; return LIST_OK;
} }
static void static void
@ -832,19 +880,21 @@ parse_response_code( imap_store_t *ctx, struct imap_cmd *cmd, char *s )
} }
static int static int
parse_list_rsp( imap_store_t *ctx, char *cmd ) parse_list_rsp( imap_store_t *ctx, list_t *list, char *cmd )
{ {
char *arg; char *arg;
list_t *list, *lp; list_t *lp;
int l; int l;
if (!(list = parse_list( &cmd ))) if (!list) {
return -1; error( "IMAP error: malformed LIST response\n" );
return LIST_BAD;
}
if (list->val == LIST) if (list->val == LIST)
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" )) {
free_list( list ); free_list( list );
return 0; return LIST_OK;
} }
free_list( list ); free_list( list );
arg = next_arg( &cmd ); arg = next_arg( &cmd );
@ -854,22 +904,22 @@ parse_list_rsp( imap_store_t *ctx, char *cmd )
if (memcmp( arg, "INBOX", 5 ) || (arg[5] && arg[5] != ctx->delimiter)) { if (memcmp( arg, "INBOX", 5 ) || (arg[5] && arg[5] != ctx->delimiter)) {
l = strlen( ctx->gen.conf->path ); l = strlen( ctx->gen.conf->path );
if (memcmp( arg, ctx->gen.conf->path, l )) if (memcmp( arg, ctx->gen.conf->path, l ))
return 0; return LIST_OK;
arg += l; arg += l;
if (!memcmp( arg, "INBOX", 5 ) && (!arg[5] || arg[5] == ctx->delimiter)) { if (!memcmp( arg, "INBOX", 5 ) && (!arg[5] || arg[5] == ctx->delimiter)) {
if (!arg[5]) if (!arg[5])
warn( "IMAP warning: ignoring INBOX in %s\n", ctx->gen.conf->path ); warn( "IMAP warning: ignoring INBOX in %s\n", ctx->gen.conf->path );
return 0; return LIST_OK;
} }
} }
if (!memcmp( arg + strlen( arg ) - 5, ".lock", 5 )) /* workaround broken servers */ if (!memcmp( arg + strlen( arg ) - 5, ".lock", 5 )) /* workaround broken servers */
return 0; return LIST_OK;
if (map_name( arg, ctx->delimiter, '/') < 0) { if (map_name( arg, ctx->delimiter, '/') < 0) {
warn( "IMAP warning: ignoring mailbox %s (reserved character '/' in name)\n", arg ); warn( "IMAP warning: ignoring mailbox %s (reserved character '/' in name)\n", arg );
return 0; return LIST_OK;
} }
add_string_list( &ctx->gen.boxes, arg ); add_string_list( &ctx->gen.boxes, arg );
return 0; return LIST_OK;
} }
static int static int
@ -922,11 +972,16 @@ imap_socket_read( void *aux )
int resp, resp2, tag, greeted; int resp, resp2, tag, greeted;
greeted = ctx->greeting; greeted = ctx->greeting;
if (ctx->parse_list_sts.level) {
cmd = 0;
goto do_fetch;
}
for (;;) { for (;;) {
if (ctx->parse_list_sts.level) {
resp = parse_list_continue( ctx, 0 );
listret:
if (resp == LIST_PARTIAL)
return;
if (resp == LIST_BAD)
break;
continue;
}
if (!(cmd = socket_read_line( &ctx->conn ))) if (!(cmd = socket_read_line( &ctx->conn )))
return; return;
@ -943,12 +998,8 @@ imap_socket_read( void *aux )
} }
if (!strcmp( "NAMESPACE", arg )) { if (!strcmp( "NAMESPACE", arg )) {
if (!(ctx->ns_personal = parse_list( &cmd )) || resp = parse_list( ctx, cmd, parse_namespace_rsp );
!(ctx->ns_other = parse_list( &cmd )) || goto listret;
!(ctx->ns_shared = parse_list( &cmd ))) {
error( "IMAP error: malformed NAMESPACE response\n" );
break;
}
} else if (ctx->greeting == GreetingPending && !strcmp( "PREAUTH", arg )) { } else if (ctx->greeting == GreetingPending && !strcmp( "PREAUTH", arg )) {
ctx->greeting = GreetingPreauth; ctx->greeting = GreetingPreauth;
parse_response_code( ctx, 0, cmd ); parse_response_code( ctx, 0, cmd );
@ -961,24 +1012,16 @@ 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 )) { } else if (!strcmp( "LIST", arg )) {
if (parse_list_rsp( ctx, cmd ) < 0) { resp = parse_list( ctx, cmd, parse_list_rsp );
error( "IMAP error: malformed LIST response\n" ); goto listret;
break;
}
} else if ((arg1 = next_arg( &cmd ))) { } else if ((arg1 = next_arg( &cmd ))) {
if (!strcmp( "EXISTS", arg1 )) if (!strcmp( "EXISTS", arg1 ))
ctx->gen.count = atoi( arg ); ctx->gen.count = atoi( arg );
else if (!strcmp( "RECENT", arg1 )) else if (!strcmp( "RECENT", arg1 ))
ctx->gen.recent = atoi( arg ); ctx->gen.recent = atoi( arg );
else if(!strcmp ( "FETCH", arg1 )) { else if(!strcmp ( "FETCH", arg1 )) {
parse_list_init( &ctx->parse_list_sts ); resp = parse_list( ctx, cmd, parse_fetch_rsp );
do_fetch: goto listret;
if ((resp = parse_imap_list( ctx, &cmd, &ctx->parse_list_sts )) == LIST_BAD)
break; /* stream is likely to be useless now */
if (resp == LIST_PARTIAL)
return;
if (parse_fetch( ctx, ctx->parse_list_sts.head ) < 0)
break; /* this may mean anything, so prefer not to spam the log */
} }
} else { } else {
error( "IMAP error: unrecognized untagged response '%s'\n", arg ); error( "IMAP error: unrecognized untagged response '%s'\n", arg );