|
@@ -67,6 +67,13 @@ typedef struct _list {
|
|
int len;
|
|
int len;
|
|
} list_t;
|
|
} list_t;
|
|
|
|
|
|
|
|
+#define MAX_LIST_DEPTH 5
|
|
|
|
+
|
|
|
|
+typedef struct parse_list_state {
|
|
|
|
+ list_t *head, **stack[MAX_LIST_DEPTH];
|
|
|
|
+ int level, need_bytes;
|
|
|
|
+} parse_list_state_t;
|
|
|
|
+
|
|
struct imap_cmd;
|
|
struct imap_cmd;
|
|
|
|
|
|
typedef struct imap_store {
|
|
typedef struct imap_store {
|
|
@@ -79,6 +86,7 @@ typedef struct imap_store {
|
|
list_t *ns_personal, *ns_other, *ns_shared; /* NAMESPACE info */
|
|
list_t *ns_personal, *ns_other, *ns_shared; /* NAMESPACE info */
|
|
message_t **msgapp; /* FETCH results */
|
|
message_t **msgapp; /* FETCH results */
|
|
unsigned caps; /* CAPABILITY results */
|
|
unsigned caps; /* CAPABILITY results */
|
|
|
|
+ parse_list_state_t parse_list_sts;
|
|
/* command queue */
|
|
/* command queue */
|
|
int nexttag, num_in_progress, literal_pending;
|
|
int nexttag, num_in_progress, literal_pending;
|
|
struct imap_cmd *in_progress, **in_progress_append;
|
|
struct imap_cmd *in_progress, **in_progress_append;
|
|
@@ -433,66 +441,76 @@ free_list( list_t *list )
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+enum {
|
|
|
|
+ LIST_OK,
|
|
|
|
+ LIST_PARTIAL,
|
|
|
|
+ LIST_BAD
|
|
|
|
+};
|
|
|
|
+
|
|
static int
|
|
static int
|
|
-parse_imap_list_l( imap_store_t *ctx, char **sp, list_t **curp, int level )
|
|
|
|
|
|
+parse_imap_list( imap_store_t *ctx, char **sp, parse_list_state_t *sts )
|
|
{
|
|
{
|
|
- list_t *cur;
|
|
|
|
|
|
+ list_t *cur, **curp;
|
|
char *s = *sp, *p;
|
|
char *s = *sp, *p;
|
|
- int n, bytes;
|
|
|
|
|
|
+ int bytes;
|
|
|
|
+
|
|
|
|
+ assert( sts );
|
|
|
|
+ assert( sts->level > 0 );
|
|
|
|
+ curp = sts->stack[--sts->level];
|
|
|
|
+ bytes = sts->need_bytes;
|
|
|
|
+ if (bytes >= 0) {
|
|
|
|
+ sts->need_bytes = -1;
|
|
|
|
+ if (!bytes)
|
|
|
|
+ goto getline;
|
|
|
|
+ cur = (list_t *)((char *)curp - offsetof(list_t, next));
|
|
|
|
+ s = cur->val + cur->len - bytes;
|
|
|
|
+ goto getbytes;
|
|
|
|
+ }
|
|
|
|
|
|
for (;;) {
|
|
for (;;) {
|
|
while (isspace( (unsigned char)*s ))
|
|
while (isspace( (unsigned char)*s ))
|
|
s++;
|
|
s++;
|
|
- if (level && *s == ')') {
|
|
|
|
|
|
+ if (sts->level && *s == ')') {
|
|
s++;
|
|
s++;
|
|
- break;
|
|
|
|
|
|
+ curp = sts->stack[--sts->level];
|
|
|
|
+ goto next;
|
|
}
|
|
}
|
|
*curp = cur = nfmalloc( sizeof(*cur) );
|
|
*curp = cur = nfmalloc( sizeof(*cur) );
|
|
- curp = &cur->next;
|
|
|
|
cur->val = 0; /* for clean bail */
|
|
cur->val = 0; /* for clean bail */
|
|
|
|
+ curp = &cur->next;
|
|
|
|
+ *curp = 0; /* ditto */
|
|
if (*s == '(') {
|
|
if (*s == '(') {
|
|
/* sublist */
|
|
/* sublist */
|
|
|
|
+ if (sts->level == MAX_LIST_DEPTH)
|
|
|
|
+ goto bail;
|
|
s++;
|
|
s++;
|
|
cur->val = LIST;
|
|
cur->val = LIST;
|
|
- if (parse_imap_list_l( ctx, &s, &cur->child, level + 1 ))
|
|
|
|
- goto bail;
|
|
|
|
|
|
+ sts->stack[sts->level++] = curp;
|
|
|
|
+ curp = &cur->child;
|
|
|
|
+ *curp = 0; /* for clean bail */
|
|
|
|
+ goto next2;
|
|
} else if (ctx && *s == '{') {
|
|
} else if (ctx && *s == '{') {
|
|
/* literal */
|
|
/* literal */
|
|
bytes = cur->len = strtol( s + 1, &s, 10 );
|
|
bytes = cur->len = strtol( s + 1, &s, 10 );
|
|
- if (*s != '}')
|
|
|
|
|
|
+ if (*s != '}' || *++s)
|
|
goto bail;
|
|
goto bail;
|
|
|
|
|
|
s = cur->val = nfmalloc( cur->len );
|
|
s = cur->val = nfmalloc( cur->len );
|
|
|
|
|
|
- /* dump whats left over in the input buffer */
|
|
|
|
- n = ctx->conn.bytes - ctx->conn.offset;
|
|
|
|
-
|
|
|
|
- if (n > bytes)
|
|
|
|
- /* the entire message fit in the buffer */
|
|
|
|
- n = bytes;
|
|
|
|
-
|
|
|
|
- memcpy( s, ctx->conn.buf + ctx->conn.offset, n );
|
|
|
|
- s += n;
|
|
|
|
- bytes -= n;
|
|
|
|
|
|
+ getbytes:
|
|
|
|
+ bytes -= socket_read( &ctx->conn, s, bytes );
|
|
|
|
+ if (bytes > 0)
|
|
|
|
+ goto postpone;
|
|
|
|
|
|
- /* mark that we used part of the buffer */
|
|
|
|
- ctx->conn.offset += n;
|
|
|
|
-
|
|
|
|
- /* now read the rest of the message */
|
|
|
|
- while (bytes > 0) {
|
|
|
|
- if ((n = socket_read( &ctx->conn, s, bytes )) <= 0)
|
|
|
|
- goto bail;
|
|
|
|
- s += n;
|
|
|
|
- bytes -= n;
|
|
|
|
- }
|
|
|
|
if (DFlags & XVERBOSE) {
|
|
if (DFlags & XVERBOSE) {
|
|
puts( "=========" );
|
|
puts( "=========" );
|
|
fwrite( cur->val, cur->len, 1, stdout );
|
|
fwrite( cur->val, cur->len, 1, stdout );
|
|
puts( "=========" );
|
|
puts( "=========" );
|
|
}
|
|
}
|
|
|
|
|
|
- if (buffer_gets( &ctx->conn, &s ))
|
|
|
|
- goto bail;
|
|
|
|
|
|
+ getline:
|
|
|
|
+ if (!(s = socket_read_line( &ctx->conn )))
|
|
|
|
+ goto postpone;
|
|
} else if (*s == '"') {
|
|
} else if (*s == '"') {
|
|
/* quoted string */
|
|
/* quoted string */
|
|
s++;
|
|
s++;
|
|
@@ -509,7 +527,7 @@ parse_imap_list_l( imap_store_t *ctx, char **sp, list_t **curp, int level )
|
|
/* atom */
|
|
/* atom */
|
|
p = s;
|
|
p = s;
|
|
for (; *s && !isspace( (unsigned char)*s ); s++)
|
|
for (; *s && !isspace( (unsigned char)*s ); s++)
|
|
- if (level && *s == ')')
|
|
|
|
|
|
+ if (sts->level && *s == ')')
|
|
break;
|
|
break;
|
|
cur->len = s - p;
|
|
cur->len = s - p;
|
|
if (cur->len == 3 && !memcmp ("NIL", p, 3))
|
|
if (cur->len == 3 && !memcmp ("NIL", p, 3))
|
|
@@ -521,41 +539,50 @@ parse_imap_list_l( imap_store_t *ctx, char **sp, list_t **curp, int level )
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- if (!level)
|
|
|
|
|
|
+ next:
|
|
|
|
+ if (!sts->level)
|
|
break;
|
|
break;
|
|
|
|
+ next2:
|
|
if (!*s)
|
|
if (!*s)
|
|
goto bail;
|
|
goto bail;
|
|
}
|
|
}
|
|
*sp = s;
|
|
*sp = s;
|
|
- *curp = 0;
|
|
|
|
- return 0;
|
|
|
|
|
|
+ return LIST_OK;
|
|
|
|
|
|
|
|
+ postpone:
|
|
|
|
+ if (sts->level < MAX_LIST_DEPTH) {
|
|
|
|
+ sts->stack[sts->level++] = curp;
|
|
|
|
+ sts->need_bytes = bytes;
|
|
|
|
+ return LIST_PARTIAL;
|
|
|
|
+ }
|
|
bail:
|
|
bail:
|
|
- *curp = 0;
|
|
|
|
- return -1;
|
|
|
|
|
|
+ free_list( sts->head );
|
|
|
|
+ return LIST_BAD;
|
|
}
|
|
}
|
|
|
|
|
|
-static list_t *
|
|
|
|
-parse_imap_list( imap_store_t *ctx, char **sp )
|
|
|
|
|
|
+static void
|
|
|
|
+parse_list_init( parse_list_state_t *sts )
|
|
{
|
|
{
|
|
- list_t *head;
|
|
|
|
-
|
|
|
|
- if (!parse_imap_list_l( ctx, sp, &head, 0 ))
|
|
|
|
- return head;
|
|
|
|
- free_list( head );
|
|
|
|
- return NULL;
|
|
|
|
|
|
+ sts->need_bytes = -1;
|
|
|
|
+ sts->level = 1;
|
|
|
|
+ sts->head = 0;
|
|
|
|
+ sts->stack[0] = &sts->head;
|
|
}
|
|
}
|
|
|
|
|
|
static list_t *
|
|
static list_t *
|
|
parse_list( char **sp )
|
|
parse_list( char **sp )
|
|
{
|
|
{
|
|
- return parse_imap_list( 0, sp );
|
|
|
|
|
|
+ parse_list_state_t sts;
|
|
|
|
+ parse_list_init( &sts );
|
|
|
|
+ if (parse_imap_list( 0, sp, &sts ) == LIST_OK)
|
|
|
|
+ return sts.head;
|
|
|
|
+ return NULL;
|
|
}
|
|
}
|
|
|
|
|
|
static int
|
|
static int
|
|
-parse_fetch( imap_store_t *ctx, char *cmd ) /* move this down */
|
|
|
|
|
|
+parse_fetch( imap_store_t *ctx, list_t *list )
|
|
{
|
|
{
|
|
- list_t *tmp, *list, *flags;
|
|
|
|
|
|
+ list_t *tmp, *flags;
|
|
char *body = 0;
|
|
char *body = 0;
|
|
imap_message_t *cur;
|
|
imap_message_t *cur;
|
|
msg_data_t *msgdata;
|
|
msg_data_t *msgdata;
|
|
@@ -563,8 +590,6 @@ parse_fetch( imap_store_t *ctx, char *cmd ) /* move this down */
|
|
int uid = 0, mask = 0, status = 0, size = 0;
|
|
int uid = 0, mask = 0, status = 0, size = 0;
|
|
unsigned i;
|
|
unsigned i;
|
|
|
|
|
|
- list = parse_imap_list( ctx, &cmd );
|
|
|
|
-
|
|
|
|
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 );
|
|
@@ -784,8 +809,11 @@ get_cmd_result( imap_store_t *ctx, struct imap_cmd *tcmd )
|
|
|
|
|
|
greeted = ctx->greeting;
|
|
greeted = ctx->greeting;
|
|
for (;;) {
|
|
for (;;) {
|
|
- if (buffer_gets( &ctx->conn, &cmd ))
|
|
|
|
- break;
|
|
|
|
|
|
+ if (!(cmd = socket_read_line( &ctx->conn ))) {
|
|
|
|
+ if (socket_fill( &ctx->conn ) < 0)
|
|
|
|
+ break;
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
|
|
arg = next_arg( &cmd );
|
|
arg = next_arg( &cmd );
|
|
if (*arg == '*') {
|
|
if (*arg == '*') {
|
|
@@ -820,8 +848,17 @@ get_cmd_result( imap_store_t *ctx, struct imap_cmd *tcmd )
|
|
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 )) {
|
|
- if (parse_fetch( ctx, cmd ))
|
|
|
|
|
|
+ parse_list_init( &ctx->parse_list_sts );
|
|
|
|
+ do_fetch:
|
|
|
|
+ if ((resp = parse_imap_list( ctx, &cmd, &ctx->parse_list_sts )) == LIST_BAD)
|
|
break; /* stream is likely to be useless now */
|
|
break; /* stream is likely to be useless now */
|
|
|
|
+ if (resp == LIST_PARTIAL) {
|
|
|
|
+ if (socket_fill( &ctx->conn ) < 0)
|
|
|
|
+ break;
|
|
|
|
+ goto do_fetch;
|
|
|
|
+ }
|
|
|
|
+ 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 );
|