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

Reply via email to