commit 9e15ab4a5a2938318816f3972708176462cb9d5f Author: Oswald Buddenhagen <o...@users.sf.net> Date: Sun Feb 15 12:15:46 2015 +0100
refactor socket EOF handling handling EOF already at the socket level isn't a very good idea - it breaks the abstraction, and makes implementing sane semantics hard. src/drv_imap.c | 32 ++++++++++++++++++++++++++------ src/socket.c | 29 +++++++++++++++++++++-------- src/socket.h | 9 --------- 3 files changed, 47 insertions(+), 23 deletions(-) diff --git a/src/drv_imap.c b/src/drv_imap.c index 447bc6e..7146d26 100644 --- a/src/drv_imap.c +++ b/src/drv_imap.c @@ -114,6 +114,8 @@ typedef struct imap_store { /* Used during sequential operations like connect */ enum { GreetingPending = 0, GreetingBad, GreetingOk, GreetingPreauth } greeting; + int expectBYE; /* LOGOUT is in progress */ + int expectEOF; /* received LOGOUT's OK or unsolicited BYE */ int canceling; /* imap_cancel() is in progress */ union { void (*imap_open)( store_t *srv, void *aux ); @@ -674,7 +676,7 @@ parse_imap_list( imap_store_t *ctx, char **sp, parse_list_state_t *sts ) { list_t *cur, **curp; char *s = *sp, *d, *p; - int bytes; + int n, bytes; char c; assert( sts ); @@ -724,7 +726,13 @@ parse_imap_list( imap_store_t *ctx, char **sp, parse_list_state_t *sts ) s[cur->len] = 0; getbytes: - bytes -= socket_read( &ctx->conn, s, bytes ); + n = socket_read( &ctx->conn, s, bytes ); + if (n < 0) { + badeof: + error( "IMAP error: unexpected EOF from %s\n", ctx->conn.name ); + goto bail; + } + bytes -= n; if (bytes > 0) goto postpone; @@ -738,6 +746,8 @@ parse_imap_list( imap_store_t *ctx, char **sp, parse_list_state_t *sts ) getline: if (!(s = socket_read_line( &ctx->conn ))) goto postpone; + if (s == (void *)~0) + goto badeof; if (DFlags & VERBOSE) { printf( "%s%s\n", ctx->label, s ); fflush( stdout ); @@ -1218,6 +1228,12 @@ imap_socket_read( void *aux ) } if (!(cmd = socket_read_line( &ctx->conn ))) return; + if (cmd == (void *)~0) { + if (!ctx->expectEOF) + error( "IMAP error: unexpected EOF from %s\n", ctx->conn.name ); + /* A clean shutdown sequence ends with bad_callback as well (see imap_cleanup()). */ + break; + } if (DFlags & VERBOSE) { printf( "%s%s\n", ctx->label, cmd ); fflush( stdout ); @@ -1250,12 +1266,14 @@ imap_socket_read( void *aux ) goto dogreet; } } else if (!strcmp( "BYE", arg )) { - if (ctx->conn.state != SCK_CLOSING) { - ctx->conn.state = SCK_CLOSING; + if (!ctx->expectBYE) { ctx->greeting = GreetingBad; error( "IMAP error: unexpected BYE response: %s\n", cmd ); + /* We just wait for the server to close the connection now. */ + ctx->expectEOF = 1; + } else { + /* We still need to wait for the LOGOUT's tagged OK. */ } - /* We just wait for the server to close the connection now. */ } else if (ctx->greeting == GreetingPending) { error( "IMAP error: bogus greeting response %s\n", arg ); break; @@ -1470,7 +1488,7 @@ imap_cleanup( void ) for (ctx = unowned; ctx; ctx = nctx) { nctx = ctx->next; set_bad_callback( ctx, (void (*)(void *))imap_cancel_store, ctx ); - ((imap_store_t *)ctx)->conn.state = SCK_CLOSING; + ((imap_store_t *)ctx)->expectBYE = 1; imap_exec( (imap_store_t *)ctx, 0, imap_cleanup_p2, "LOGOUT" ); } } @@ -1481,6 +1499,8 @@ imap_cleanup_p2( imap_store_t *ctx, { if (response == RESP_NO) imap_cancel_store( &ctx->gen ); + else if (response == RESP_OK) + ctx->expectEOF = 1; } /******************* imap_open_store *******************/ diff --git a/src/socket.c b/src/socket.c index d70488a..9452cd0 100644 --- a/src/socket.c +++ b/src/socket.c @@ -43,6 +43,15 @@ # include <openssl/x509v3.h> #endif +enum { + SCK_CONNECTING, +#ifdef HAVE_LIBSSL + SCK_STARTTLS, +#endif + SCK_READY, + SCK_EOF +}; + static void socket_fail( conn_t *conn ) { @@ -67,11 +76,12 @@ ssl_return( const char *func, conn_t *conn, int ret ) case SSL_ERROR_SSL: if (!(err = ERR_get_error())) { if (ret == 0) { - if (conn->state != SCK_CLOSING) - error( "Socket error: secure %s %s: unexpected EOF\n", func, conn->name ); - } else { - sys_error( "Socket error: secure %s %s", func, conn->name ); + /* Callers take the short path out, so signal higher layers from here. */ + conn->state = SCK_EOF; + conn->read_callback( conn->callback_aux ); + return 0; } + sys_error( "Socket error: secure %s %s", func, conn->name ); } else { error( "Socket error: secure %s %s: %s\n", func, conn->name, ERR_error_string( err, 0 ) ); } @@ -582,10 +592,9 @@ do_read( conn_t *sock, char *buf, int len ) sys_error( "Socket error: read from %s", sock->name ); socket_fail( sock ); } else if (!n) { - if (sock->state != SCK_CLOSING) - error( "Socket error: read from %s: unexpected EOF\n", sock->name ); - socket_fail( sock ); - return -1; + /* EOF. Callers take the short path out, so signal higher layers from here. */ + sock->state = SCK_EOF; + sock->read_callback( sock->callback_aux ); } } @@ -656,6 +665,8 @@ int socket_read( conn_t *conn, char *buf, int len ) { int n = conn->bytes; + if (!n && conn->state == SCK_EOF) + return -1; if (n > len) n = len; memcpy( buf, conn->buf + conn->offset, n ); @@ -680,6 +691,8 @@ socket_read_line( conn_t *b ) memmove( b->buf, b->buf + b->offset, b->bytes ); b->offset = 0; } + if (b->state == SCK_EOF) + return (void *)~0; return 0; } n = p + 1 - s; diff --git a/src/socket.h b/src/socket.h index 20c3033..a420e49 100644 --- a/src/socket.h +++ b/src/socket.h @@ -29,15 +29,6 @@ #include <zlib.h> #endif -enum { - SCK_CONNECTING, -#ifdef HAVE_LIBSSL - SCK_STARTTLS, -#endif - SCK_READY, - SCK_CLOSING -}; - #ifdef HAVE_LIBSSL typedef struct ssl_st SSL; typedef struct ssl_ctx_st SSL_CTX; ------------------------------------------------------------------------------ Dive into the World of Parallel Programming. The Go Parallel Website, sponsored by Intel and developed in partnership with Slashdot Media, is your hub for all things parallel software development, from weekly thought leadership blogs to news, videos, case studies, tutorials and more. Take a look and join the conversation now. http://goparallel.sourceforge.net/ _______________________________________________ isync-devel mailing list isync-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/isync-devel