isync

mailbox synchronization program
git clone https://git.code.sf.net/p/isync/isync
Log | Files | Refs | README | LICENSE

commit bcd43e2c66356eb136b96eb930cff564a20da21c
parent b8d6d833c61cba4e6c86271cd3abc279520a598c
Author: Oswald Buddenhagen <ossi@users.sf.net>
Date:   Sat,  9 May 2015 19:31:55 +0200

Merge branch 'isync_1_2_branch'

Conflicts:
	configure.ac
	src/drv_imap.c

Diffstat:
MREADME | 2+-
MTODO | 3---
Mconfigure.ac | 34++++++++++++++++++++++------------
Msrc/drv_imap.c | 116++++++++++++++++++++++++++++++++++++-------------------------------------------
Msrc/mbsync.1 | 96++++++++++++++++++++++++++++++++++++++++----------------------------------------
Msrc/socket.c | 35++++++++++++++++-------------------
Msrc/socket.h | 8+++++---
7 files changed, 145 insertions(+), 149 deletions(-)

diff --git a/README b/README @@ -61,7 +61,7 @@ isync executable still exists; it is a compatibility wrapper around mbsync. * Requirements - Berkley DB 4.2+ (optional) + Berkeley DB 4.1+ (optional) OpenSSL for TLS/SSL support (optional) * Installation diff --git a/TODO b/TODO @@ -3,9 +3,6 @@ f{,data}sync() usage could be optimized by batching the calls. add some marker about message being already [remotely] trashed. real transactions would be certainly not particularly useful ... -make sync_chans() aware of servers, so a bad server (e.g., wrong password) -won't cause the same error message for every attached store. - make SSL (connect) timeouts produce a bit more than "Unidentified socket error". uidvalidity lock timeout handling would be a good idea. diff --git a/configure.ac b/configure.ac @@ -139,26 +139,36 @@ if test "x$ob_cv_with_sasl" != xno; then fi AC_SUBST(SASL_LIBS) -AC_CACHE_CHECK([for Berkley DB >= 4.2], ac_cv_berkdb4, +AC_CACHE_CHECK([for Berkeley DB >= 4.1], ac_cv_berkdb4, [ac_cv_berkdb4=no + sav_LDFLAGS=$LDFLAGS + LDFLAGS="$LDFLAGS -ldb" AC_TRY_LINK([#include <db.h>], [DB *db; + db_create(&db, 0, 0); db->truncate(db, 0, 0, 0); db->open(db, 0, "foo", "foo", DB_HASH, DB_CREATE, 0)], - [ac_cv_berkdb4=yes])]) + [ac_cv_berkdb4=yes]) + LDFLAGS=$sav_LDFLAGS + ]) if test "x$ac_cv_berkdb4" = xyes; then AC_SUBST([DB_LIBS], ["-ldb"]) - AC_DEFINE(USE_DB, 1, [if Berkley DB should be used]) + AC_DEFINE(USE_DB, 1, [if Berkeley DB should be used]) fi have_zlib= -AC_CHECK_LIB([z], [deflate], - [AC_CHECK_HEADER(zlib.h, - [have_zlib=1 - AC_SUBST([Z_LIBS], ["-lz"]) - AC_DEFINE([HAVE_LIBZ], 1, [if you have the zlib library])] - )] -) +AC_ARG_WITH(zlib, + AS_HELP_STRING([--with-zlib], [use zlib [detect]]), + [ob_cv_with_zlib=$withval]) +if test "x$ob_cv_with_zlib" != xno; then + AC_CHECK_LIB([z], [deflate], + [AC_CHECK_HEADER(zlib.h, + [have_zlib=1 + AC_SUBST([Z_LIBS], ["-lz"]) + AC_DEFINE([HAVE_LIBZ], 1, [if you have the zlib library])] + )] + ) +fi AC_ARG_ENABLE(compat, AC_HELP_STRING([--disable-compat], [don't include isync compatibility wrapper [no]]), @@ -189,8 +199,8 @@ else AC_MSG_RESULT([Not using zlib]) fi if test "x$ac_cv_berkdb4" = xyes; then - AC_MSG_RESULT([Using Berkley DB]) + AC_MSG_RESULT([Using Berkeley DB]) else - AC_MSG_RESULT([Not using Berkley DB]) + AC_MSG_RESULT([Not using Berkeley DB]) fi AC_MSG_RESULT() diff --git a/src/drv_imap.c b/src/drv_imap.c @@ -267,7 +267,7 @@ done_imap_cmd( imap_store_t *ctx, struct imap_cmd *cmd, int response ) free( cmd ); } -static int +static void send_imap_cmd( imap_store_t *ctx, struct imap_cmd *cmd ) { int bufl, litplus, iovcnt = 1; @@ -313,8 +313,7 @@ send_imap_cmd( imap_store_t *ctx, struct imap_cmd *cmd ) iov[2].takeOwn = KeepOwn; iovcnt = 3; } - if (socket_write( &ctx->conn, iov, iovcnt ) < 0) - goto bail; + socket_write( &ctx->conn, iov, iovcnt ); if (cmd->param.to_trash && ctx->trashnc == TrashUnknown) ctx->trashnc = TrashChecking; cmd->next = 0; @@ -322,15 +321,10 @@ send_imap_cmd( imap_store_t *ctx, struct imap_cmd *cmd ) ctx->in_progress_append = &cmd->next; ctx->num_in_progress++; socket_expect_read( &ctx->conn, 1 ); - return 0; - - bail: - done_imap_cmd( ctx, cmd, RESP_CANCEL ); - return -1; } static int -cmd_submittable( imap_store_t *ctx, struct imap_cmd *cmd ) +cmd_sendable( imap_store_t *ctx, struct imap_cmd *cmd ) { struct imap_cmd *cmdp; @@ -343,18 +337,16 @@ cmd_submittable( imap_store_t *ctx, struct imap_cmd *cmd ) ctx->num_in_progress < ((imap_store_conf_t *)ctx->gen.conf)->server->max_in_progress; } -static int +static void flush_imap_cmds( imap_store_t *ctx ) { struct imap_cmd *cmd; - while ((cmd = ctx->pending) && cmd_submittable( ctx, cmd )) { + if ((cmd = ctx->pending) && cmd_sendable( ctx, cmd )) { if (!(ctx->pending = cmd->next)) ctx->pending_append = &ctx->pending; - if (send_imap_cmd( ctx, cmd ) < 0) - return -1; + send_imap_cmd( ctx, cmd ); } - return 0; } static void @@ -370,7 +362,7 @@ cancel_pending_imap_cmds( imap_store_t *ctx ) } static void -cancel_submitted_imap_cmds( imap_store_t *ctx ) +cancel_sent_imap_cmds( imap_store_t *ctx ) { struct imap_cmd *cmd; @@ -382,7 +374,7 @@ cancel_submitted_imap_cmds( imap_store_t *ctx ) } } -static int +static void submit_imap_cmd( imap_store_t *ctx, struct imap_cmd *cmd ) { assert( ctx ); @@ -390,7 +382,7 @@ submit_imap_cmd( imap_store_t *ctx, struct imap_cmd *cmd ) assert( cmd ); assert( cmd->param.done ); - if ((ctx->pending && !cmd->param.high_prio) || !cmd_submittable( ctx, cmd )) { + if ((ctx->pending && !cmd->param.high_prio) || !cmd_sendable( ctx, cmd )) { if (ctx->pending && cmd->param.high_prio) { cmd->next = ctx->pending; ctx->pending = cmd; @@ -399,10 +391,9 @@ submit_imap_cmd( imap_store_t *ctx, struct imap_cmd *cmd ) *ctx->pending_append = cmd; ctx->pending_append = &cmd->next; } - return 0; + } else { + send_imap_cmd( ctx, cmd ); } - - return send_imap_cmd( ctx, cmd ); } /* Minimal printf() replacement that supports an %\s format sequence to print backslash-escaped @@ -487,7 +478,7 @@ imap_vprintf( const char *fmt, va_list ap ) } } -static int +static void imap_exec( imap_store_t *ctx, struct imap_cmd *cmdp, void (*done)( imap_store_t *ctx, struct imap_cmd *cmd, int response ), const char *fmt, ... ) @@ -500,7 +491,7 @@ imap_exec( imap_store_t *ctx, struct imap_cmd *cmdp, va_start( ap, fmt ); cmdp->cmd = imap_vprintf( fmt, ap ); va_end( ap ); - return submit_imap_cmd( ctx, cmdp ); + submit_imap_cmd( ctx, cmdp ); } static void @@ -1337,8 +1328,7 @@ imap_socket_read( void *aux ) iov[1].buf = "\r\n"; iov[1].len = 2; iov[1].takeOwn = KeepOwn; - if (socket_write( &ctx->conn, iov, 2 ) < 0) - return; + socket_write( &ctx->conn, iov, 2 ); } else if (cmdp->param.cont) { if (cmdp->param.cont( ctx, cmdp, cmd )) return; @@ -1376,9 +1366,8 @@ imap_socket_read( void *aux ) cmd2->orig_cmd = cmdp; cmd2->gen.param.high_prio = 1; p = strchr( cmdp->cmd, '"' ); - if (imap_exec( ctx, &cmd2->gen, get_cmd_result_p2, - "CREATE %.*s", imap_strchr( p + 1, '"' ) - p + 1, p ) < 0) - return; + imap_exec( ctx, &cmd2->gen, get_cmd_result_p2, + "CREATE %.*s", imap_strchr( p + 1, '"' ) - p + 1, p ); continue; } resp = RESP_NO; @@ -1387,7 +1376,11 @@ imap_socket_read( void *aux ) } else /*if (!strcmp( "BAD", arg ))*/ resp = RESP_CANCEL; error( "IMAP command '%s' returned an error: %s %s\n", - !starts_with( cmdp->cmd, -1, "LOGIN", 5 ) ? cmdp->cmd : "LOGIN <user> <pass>", + starts_with( cmdp->cmd, -1, "LOGIN", 5 ) ? + "LOGIN <user> <pass>" : + starts_with( cmdp->cmd, -1, "AUTHENTICATE PLAIN", 18 ) ? + "AUTHENTICATE PLAIN <authdata>" : + cmdp->cmd, arg, cmd ? cmd : "" ); } doresp: @@ -1405,8 +1398,7 @@ imap_socket_read( void *aux ) return; } } - if (flush_imap_cmds( ctx ) < 0) - return; + flush_imap_cmds( ctx ); } imap_invoke_bad_callback( ctx ); } @@ -1440,7 +1432,7 @@ imap_cancel_store( store_t *gctx ) sasl_dispose( &ctx->sasl ); #endif socket_close( &ctx->conn ); - cancel_submitted_imap_cmds( ctx ); + cancel_sent_imap_cmds( ctx ); cancel_pending_imap_cmds( ctx ); free_generic_messages( ctx->gen.msgs ); free_string_list( ctx->gen.boxes ); @@ -1603,7 +1595,7 @@ imap_open_store( store_conf_t *conf, const char *label, socket_init( &ctx->conn, &srvc->sconf, (void (*)( void * ))imap_invoke_bad_callback, - imap_socket_read, (int (*)(void *))flush_imap_cmds, ctx ); + imap_socket_read, (void (*)(void *))flush_imap_cmds, ctx ); socket_connect( &ctx->conn, imap_open_store_connected ); } @@ -1929,7 +1921,8 @@ do_sasl_auth( imap_store_t *ctx, struct imap_cmd *cmdp ATTR_UNUSED, const char * iov[iovcnt].len = 2; iov[iovcnt].takeOwn = KeepOwn; iovcnt++; - return socket_write( &ctx->conn, iov, iovcnt ); + socket_write( &ctx->conn, iov, iovcnt ); + return 0; bail: imap_open_store_bail( ctx, FAIL_FINAL ); @@ -2289,7 +2282,7 @@ imap_prepare_load_box( store_t *gctx, int opts ) gctx->opts = opts; } -static int imap_submit_load( imap_store_t *, const char *, int, struct imap_cmd_refcounted_state * ); +static void imap_submit_load( imap_store_t *, const char *, int, struct imap_cmd_refcounted_state * ); static void imap_load_box( store_t *gctx, int minuid, int maxuid, int newuid, int *excs, int nexcs, @@ -2316,16 +2309,14 @@ imap_load_box( store_t *gctx, int minuid, int maxuid, int newuid, int *excs, int if (i != j) bl += sprintf( buf + bl, ":%d", excs[i] ); } - if (imap_submit_load( ctx, buf, 0, sts ) < 0) - goto done; + imap_submit_load( ctx, buf, 0, sts ); } if (maxuid == INT_MAX) maxuid = ctx->gen.uidnext ? ctx->gen.uidnext - 1 : 1000000000; if (maxuid >= minuid) { if ((ctx->gen.opts & OPEN_FIND) && minuid < newuid) { sprintf( buf, "%d:%d", minuid, newuid - 1 ); - if (imap_submit_load( ctx, buf, 0, sts ) < 0) - goto done; + imap_submit_load( ctx, buf, 0, sts ); if (newuid > maxuid) goto done; sprintf( buf, "%d:%d", newuid, maxuid ); @@ -2340,14 +2331,14 @@ imap_load_box( store_t *gctx, int minuid, int maxuid, int newuid, int *excs, int } } -static int +static void imap_submit_load( imap_store_t *ctx, const char *buf, int tuids, struct imap_cmd_refcounted_state *sts ) { - return imap_exec( ctx, imap_refcounted_new_cmd( sts ), imap_refcounted_done_box, - "UID FETCH %s (UID%s%s%s)", buf, - (ctx->gen.opts & OPEN_FLAGS) ? " FLAGS" : "", - (ctx->gen.opts & OPEN_SIZE) ? " RFC822.SIZE" : "", - tuids ? " BODY.PEEK[HEADER.FIELDS (X-TUID)]" : ""); + imap_exec( ctx, imap_refcounted_new_cmd( sts ), imap_refcounted_done_box, + "UID FETCH %s (UID%s%s%s)", buf, + (ctx->gen.opts & OPEN_FLAGS) ? " FLAGS" : "", + (ctx->gen.opts & OPEN_SIZE) ? " RFC822.SIZE" : "", + tuids ? " BODY.PEEK[HEADER.FIELDS (X-TUID)]" : ""); } /******************* imap_fetch_msg *******************/ @@ -2404,15 +2395,15 @@ imap_make_flags( int flags, char *buf ) return d; } -static int +static void imap_flags_helper( imap_store_t *ctx, int uid, char what, int flags, struct imap_cmd_refcounted_state *sts ) { char buf[256]; buf[imap_make_flags( flags, buf )] = 0; - return imap_exec( ctx, imap_refcounted_new_cmd( sts ), imap_set_flags_p2, - "UID STORE %d %cFLAGS.SILENT %s", uid, what, buf ); + imap_exec( ctx, imap_refcounted_new_cmd( sts ), imap_set_flags_p2, + "UID STORE %d %cFLAGS.SILENT %s", uid, what, buf ); } static void @@ -2430,8 +2421,10 @@ imap_set_msg_flags( store_t *gctx, message_t *msg, int uid, int add, int del, } if (add || del) { struct imap_cmd_refcounted_state *sts = imap_refcounted_new_state( cb, aux ); - if ((add && imap_flags_helper( ctx, uid, '+', add, sts ) < 0) || - (del && imap_flags_helper( ctx, uid, '-', del, sts ) < 0)) {} + if (add) + imap_flags_helper( ctx, uid, '+', add, sts ); + if (del) + imap_flags_helper( ctx, uid, '-', del, sts ); imap_refcounted_done( sts ); } else { cb( DRV_OK, aux ); @@ -2482,9 +2475,8 @@ imap_close_box( store_t *gctx, } if (!bl) break; - if (imap_exec( ctx, imap_refcounted_new_cmd( sts ), imap_refcounted_done_box, - "UID EXPUNGE %s", buf ) < 0) - break; + imap_exec( ctx, imap_refcounted_new_cmd( sts ), imap_refcounted_done_box, + "UID EXPUNGE %s", buf ); } imap_refcounted_done( sts ); } else { @@ -2625,13 +2617,12 @@ imap_list_store( store_t *gctx, int flags, imap_store_t *ctx = (imap_store_t *)gctx; struct imap_cmd_refcounted_state *sts = imap_refcounted_new_state( cb, aux ); - if (((flags & LIST_PATH) && (!(flags & LIST_INBOX) || !is_inbox( ctx, ctx->prefix, -1 )) && - imap_exec( ctx, imap_refcounted_new_cmd( sts ), imap_refcounted_done_box, - "LIST \"\" \"%\\s*\"", ctx->prefix ) < 0) || - ((flags & LIST_INBOX) && (!(flags & LIST_PATH) || *ctx->prefix) && - imap_exec( ctx, imap_refcounted_new_cmd( sts ), imap_refcounted_done_box, - "LIST \"\" INBOX*" ) < 0)) - {} + if ((flags & LIST_PATH) && (!(flags & LIST_INBOX) || !is_inbox( ctx, ctx->prefix, -1 ))) + imap_exec( ctx, imap_refcounted_new_cmd( sts ), imap_refcounted_done_box, + "LIST \"\" \"%\\s*\"", ctx->prefix ); + if ((flags & LIST_INBOX) && (!(flags & LIST_PATH) || *ctx->prefix)) + imap_exec( ctx, imap_refcounted_new_cmd( sts ), imap_refcounted_done_box, + "LIST \"\" INBOX*" ); imap_refcounted_done( sts ); } @@ -2695,8 +2686,9 @@ imap_parse_store( conffile_t *cfg, store_conf_t **storep ) /* Legacy SSL options */ int require_ssl = -1, use_imaps = -1; int use_sslv2 = -1, use_sslv3 = -1, use_tlsv1 = -1, use_tlsv11 = -1, use_tlsv12 = -1; - int require_cram = -1; #endif + /* Legacy SASL option */ + int require_cram = -1; if (!strcasecmp( "IMAPAccount", cfg->cmd )) { server = nfcalloc( sizeof(*server) ); @@ -2829,6 +2821,7 @@ imap_parse_store( conffile_t *cfg, store_conf_t **storep ) use_tlsv11 = parse_bool( cfg ); else if (!strcasecmp( "UseTLSv1.2", cfg->cmd )) use_tlsv12 = parse_bool( cfg ); +#endif else if (!strcasecmp( "AuthMech", cfg->cmd ) || !strcasecmp( "AuthMechs", cfg->cmd )) { arg = cfg->val; @@ -2837,7 +2830,6 @@ imap_parse_store( conffile_t *cfg, store_conf_t **storep ) while ((arg = get_arg( cfg, ARG_OPTIONAL, 0 ))); } else if (!strcasecmp( "RequireCRAM", cfg->cmd )) require_cram = parse_bool( cfg ); -#endif else if (!strcasecmp( "Tunnel", cfg->cmd )) server->sconf.tunnel = nfstrdup( cfg->val ); else if (store) { @@ -2917,7 +2909,6 @@ imap_parse_store( conffile_t *cfg, store_conf_t **storep ) server->ssl_type = server->sconf.tunnel ? SSL_None : SSL_STARTTLS; } #endif -#ifdef HAVE_LIBSSL if (require_cram >= 0) { if (server->auth_mechs) { error( "%s '%s': The deprecated RequireCRAM option is mutually exlusive with AuthMech.\n", type, name ); @@ -2928,7 +2919,6 @@ imap_parse_store( conffile_t *cfg, store_conf_t **storep ) if (require_cram) add_string_list(&server->auth_mechs, "CRAM-MD5"); } -#endif if (!server->auth_mechs) add_string_list( &server->auth_mechs, "*" ); if (!server->sconf.port) diff --git a/src/mbsync.1 b/src/mbsync.1 @@ -192,17 +192,17 @@ See \fBRECOMMENDATIONS\fR and \fBINHERENT PROBLEMS\fR below. (Default: none) .. .TP -\fBTrashNewOnly\fR \fIyes\fR|\fIno\fR +\fBTrashNewOnly\fR \fByes\fR|\fBno\fR When trashing, copy only not yet propagated messages. This makes sense if the -remote Store has a \fBTrash\fR as well (with \fBTrashNewOnly\fR \fIno\fR). -(Default: \fIno\fR) +remote Store has a \fBTrash\fR as well (with \fBTrashNewOnly\fR \fBno\fR). +(Default: \fBno\fR) .. .TP -\fBTrashRemoteNew\fR \fIyes\fR|\fIno\fR +\fBTrashRemoteNew\fR \fByes\fR|\fBno\fR When expunging the remote Store, copy not yet propagated messages to this Store's \fBTrash\fR. When using this, the remote Store does not need an own \fBTrash\fR at all, yet all messages are archived. -(Default: \fIno\fR) +(Default: \fBno\fR) .. .SS Maildir Stores The reference point for relative \fBPath\fRs is the current working directory. @@ -237,11 +237,11 @@ Use \fBmdconvert\fR to convert mailboxes from one scheme to the other. Define the Maildir Store \fIname\fR, opening a section for its parameters. .. .TP -\fBAltMap\fR \fIyes\fR|\fIno\fR +\fBAltMap\fR \fByes\fR|\fBno\fR Use the \fBalternative\fR UID storage scheme for mailboxes in this Store. This does not affect mailboxes that do already have a UID storage scheme; use \fBmdconvert\fR to change it. -(Default: \fIno\fR) +(Default: \fBno\fR) .. .TP \fBInbox\fR \fIpath\fR @@ -286,7 +286,7 @@ Define the IMAP4 Account \fIname\fR, opening a section for its parameters. Specify the DNS name or IP address of the IMAP server. .br If \fBTunnel\fR is used, this setting is needed only if \fBSSLType\fR is -not \fINone\fR and \fBCertificateFile\fR is not used, +not \fBNone\fR and \fBCertificateFile\fR is not used, in which case the host name is used for certificate subject verification. .. .TP @@ -309,7 +309,7 @@ Specify the login name on the IMAP server. .TP \fBPass\fR \fIpassword\fR Specify the password for \fIusername\fR on the IMAP server. -Note that this option is \fBNOT\fR required. +Note that this option is \fInot\fR required. If neither a password nor a password command is specified in the configuration file, \fBmbsync\fR will prompt you for a password. .. @@ -341,21 +341,21 @@ of this list, the list supplied by the server, and the installed SASL modules. (Default: \fB*\fR) .. .TP -\fBSSLType\fR {\fINone\fR|\fISTARTTLS\fR|\fIIMAPS\fR} +\fBSSLType\fR {\fBNone\fR|\fBSTARTTLS\fR|\fBIMAPS\fR} Select the connection security/encryption method: .br -\fINone\fR - no security. +\fBNone\fR - no security. This is the default when \fBTunnel\fR is set, as tunnels are usually secure. .br -\fISTARTTLS\fR - security is established via the STARTTLS extension +\fBSTARTTLS\fR - security is established via the STARTTLS extension after connecting the regular IMAP port 143. Most servers support this, so it is the default (unless a tunnel is used). .br -\fIIMAPS\fR - security is established by starting SSL/TLS negotiation +\fBIMAPS\fR - security is established by starting SSL/TLS negotiation right after connecting the secure IMAP port 993. .. .TP -\fBSSLVersions\fR [\fISSLv2\fR] [\fISSLv3\fR] [\fITLSv1\fR] [\fITLSv1.1\fR] [\fITLSv1.2\fR] +\fBSSLVersions\fR [\fBSSLv2\fR] [\fBSSLv3\fR] [\fBTLSv1\fR] [\fBTLSv1.1\fR] [\fBTLSv1.2\fR] Select the acceptable SSL/TLS versions. Use of SSLv2 is strongly discouraged for security reasons, but might be the only option on some very old servers. @@ -363,9 +363,9 @@ Generally, the newest TLS version is recommended, but as this confuses some servers, \fBTLSv1\fR is the default. .. .TP -\fBSystemCertificates\fR \fIyes\fR|\fIno\fR +\fBSystemCertificates\fR \fByes\fR|\fBno\fR Whether the system's default root cerificate store should be loaded. -(Default: \fIyes\fR) +(Default: \fByes\fR) .. .TP \fBCertificateFile\fR \fIpath\fR @@ -407,18 +407,18 @@ directly in the Store's section - this makes sense if an Account is used for one Store only anyway. .. .TP -\fBUseNamespace\fR \fIyes\fR|\fIno\fR +\fBUseNamespace\fR \fByes\fR|\fBno\fR Selects whether the server's first "personal" NAMESPACE should be prefixed to mailbox names. Disabling this makes sense for some broken IMAP servers. This option is meaningless if a \fBPath\fR was specified. -(Default: \fIyes\fR) +(Default: \fByes\fR) .. .TP \fBPathDelimiter\fR \fIdelim\fR Specify the server's hierarchy delimiter. (Default: taken from the server's first "personal" NAMESPACE) .br -Do \fBNOT\fR abuse this to re-interpret the hierarchy. +Do \fInot\fR abuse this to re-interpret the hierarchy. Use \fBFlatten\fR instead. .. .SS Channels @@ -471,56 +471,56 @@ If \fIcount\fR is 0, the maximum number of messages is \fBunlimited\fR (Default: \fI0\fR). .. .TP -\fBExpireUnread\fR \fIyes\fR|\fIno\fR +\fBExpireUnread\fR \fByes\fR|\fBno\fR Selects whether unread messages should be affected by \fBMaxMessages\fR. Normally, unread messages are considered important and thus never expired. This ensures that you never miss new messages even after an extended absence. However, if your archive contains large amounts of unread messages by design, treating them as important would practically defeat \fBMaxMessages\fR. In this case you need to enable this option. -(Default: \fIno\fR). +(Default: \fBno\fR). .. .TP -\fBSync\fR {\fINone\fR|[\fIPull\fR] [\fIPush\fR] [\fINew\fR] [\fIReNew\fR] [\fIDelete\fR] [\fIFlags\fR]|\fIAll\fR} +\fBSync\fR {\fBNone\fR|[\fBPull\fR] [\fBPush\fR] [\fBNew\fR] [\fBReNew\fR] [\fBDelete\fR] [\fBFlags\fR]|\fBAll\fR} Select the synchronization operation(s) to perform: .br -\fIPull\fR - propagate changes from Master to Slave. +\fBPull\fR - propagate changes from Master to Slave. .br -\fIPush\fR - propagate changes from Slave to Master. +\fBPush\fR - propagate changes from Slave to Master. .br -\fINew\fR - propagate newly appeared messages. +\fBNew\fR - propagate newly appeared messages. .br -\fIReNew\fR - previously refused messages are re-evaluated for propagation. +\fBReNew\fR - previously refused messages are re-evaluated for propagation. Useful after flagging affected messages in the source Store or enlarging MaxSize in the destination Store. .br -\fIDelete\fR - propagate message deletions. This applies only to messages that +\fBDelete\fR - propagate message deletions. This applies only to messages that are actually gone, i.e., were expunged. The affected messages in the remote Store are marked as deleted only, i.e., they won't be really deleted until that Store is expunged. .br -\fIFlags\fR - propagate flag changes. Note that Deleted/Trashed is a flag as +\fBFlags\fR - propagate flag changes. Note that Deleted/Trashed is a flag as well; this is particularly interesting if you use \fBmutt\fR with the maildir_trash option. .br -\fIAll\fR (\fB--full\fR on the command line) - all of the above. +\fBAll\fR (\fB--full\fR on the command line) - all of the above. This is the global default. .br -\fINone\fR (\fB--noop\fR on the command line) - don't propagate anything. +\fBNone\fR (\fB--noop\fR on the command line) - don't propagate anything. Useful if you want to expunge only. .IP -\fIPull\fR and \fIPush\fR are direction flags, while \fINew\fR, \fIReNew\fR, -\fIDelete\fR and \fIFlags\fR are type flags. The two flag classes make up a +\fBPull\fR and \fBPush\fR are direction flags, while \fBNew\fR, \fBReNew\fR, +\fBDelete\fR and \fBFlags\fR are type flags. The two flag classes make up a two-dimensional matrix (a table). Its cells are the individual actions to perform. There are two styles of asserting the cells: .br In the first style, the flags select entire rows/colums in the matrix. Only the cells which are selected both horizontally and vertically are asserted. Specifying no flags from a class is like specifying all flags from this class. -For example, "\fBSync\fR\ \fIPull\fR\ \fINew\fR\ \fIFlags\fR" will propagate +For example, "\fBSync\fR\ \fBPull\fR\ \fBNew\fR\ \fBFlags\fR" will propagate new messages and flag changes from the Master to the Slave, -"\fBSync\fR\ \fINew\fR\ \fIDelete\fR" will propagate message arrivals and -deletions both ways, and "\fBSync\fR\ \fIPush\fR" will propagate all changes +"\fBSync\fR\ \fBNew\fR\ \fBDelete\fR" will propagate message arrivals and +deletions both ways, and "\fBSync\fR\ \fBPush\fR" will propagate all changes from the Slave to the Master. .br In the second style, direction flags are concatenated with type flags; every @@ -528,22 +528,22 @@ compound flag immediately asserts a cell in the matrix. In addition to at least one compound flag, the individual flags can be used as well, but as opposed to the first style, they immediately assert all cells in their respective row/column. For example, -"\fBSync\fR\ \fIPullNew\fR\ \fIPullDelete\fR\ \fIPush\fR" will propagate +"\fBSync\fR\ \fBPullNew\fR\ \fBPullDelete\fR\ \fBPush\fR" will propagate message arrivals and deletions from the Master to the Slave and any changes from the Slave to the Master. Note that it is not allowed to assert a cell in two ways, e.g. -"\fBSync\fR\ \fIPullNew\fR\ \fIPull\fR" and -"\fBSync\fR\ \fIPullNew\fR\ \fIDelete\fR\ \fIPush\fR" induce error messages. +"\fBSync\fR\ \fBPullNew\fR\ \fBPull\fR" and +"\fBSync\fR\ \fBPullNew\fR\ \fBDelete\fR\ \fBPush\fR" induce error messages. .. .TP -\fBCreate\fR {\fINone\fR|\fIMaster\fR|\fISlave\fR|\fIBoth\fR} +\fBCreate\fR {\fBNone\fR|\fBMaster\fR|\fBSlave\fR|\fBBoth\fR} Automatically create missing mailboxes [on the Master/Slave]. Otherwise print an error message and skip that mailbox pair if a mailbox and the corresponding sync state does not exist. -(Global default: \fINone\fR) +(Global default: \fBNone\fR) .. .TP -\fBRemove\fR {\fINone\fR|\fIMaster\fR|\fISlave\fR|\fIBoth\fR} +\fBRemove\fR {\fBNone\fR|\fBMaster\fR|\fBSlave\fR|\fBBoth\fR} Propagate mailbox deletions [to the Master/Slave]. Otherwise print an error message and skip that mailbox pair if a mailbox does not exist but the corresponding sync state does. @@ -553,23 +553,23 @@ mark them as deleted. This ensures compatibility with \fBSyncState *\fR. .br Note that for safety, non-empty mailboxes are never deleted. .br -(Global default: \fINone\fR) +(Global default: \fBNone\fR) .. .TP -\fBExpunge\fR {\fINone\fR|\fIMaster\fR|\fISlave\fR|\fIBoth\fR} +\fBExpunge\fR {\fBNone\fR|\fBMaster\fR|\fBSlave\fR|\fBBoth\fR} Permanently remove all messages [on the Master/Slave] marked for deletion. See \fBRECOMMENDATIONS\fR below. -(Global default: \fINone\fR) +(Global default: \fBNone\fR) .. .TP -\fBCopyArrivalDate\fR {\fIyes\fR|\fIno\fR} +\fBCopyArrivalDate\fR {\fByes\fR|\fBno\fR} Selects whether their arrival time should be propagated together with the messages. Enabling this makes sense in order to keep the time stamp based message sorting intact. Note that IMAP does not guarantee that the time stamp (termed \fBinternal date\fR) is actually the arrival time, but it is usually close enough. -(Default: \fIno\fR) +(Default: \fBno\fR) .. .P \fBSync\fR, \fBCreate\fR, \fBRemove\fR, \fBExpunge\fR, @@ -615,7 +615,7 @@ times within a Group. .. .SS Global Options .TP -\fBFSync\fR \fIyes\fR|\fIno\fR +\fBFSync\fR \fByes\fR|\fBno\fR .br Selects whether \fBmbsync\fR performs forced flushing, which determines the level of data safety after system crashes and power outages. @@ -624,7 +624,7 @@ data=ordered mode. Enabling it is a wise choice for file systems mounted with data=writeback, in particular modern systems like ext4, btrfs and xfs. The performance impact on older file systems may be disproportionate. -(Default: \fIyes\fR) +(Default: \fByes\fR) .. .TP \fBFieldDelimiter\fR \fIdelim\fR diff --git a/src/socket.c b/src/socket.c @@ -80,7 +80,7 @@ ssl_return( const char *func, conn_t *conn, int ret ) /* Callers take the short path out, so signal higher layers from here. */ conn->state = SCK_EOF; conn->read_callback( conn->callback_aux ); - return 0; + return -1; } sys_error( "Socket error: secure %s %s", func, conn->name ); } else { @@ -771,6 +771,7 @@ do_queued_write( conn_t *conn ) return -1; if (n != len) { conn->write_offset += n; + conn->writing = 1; return 0; } conn->write_offset = 0; @@ -780,7 +781,9 @@ do_queued_write( conn_t *conn ) if (conn->ssl && SSL_pending( conn->ssl )) conf_wakeup( &conn->ssl_fake, 0 ); #endif - return conn->write_callback( conn->callback_aux ); + conn->writing = 0; + conn->write_callback( conn->callback_aux ); + return -1; } static void @@ -803,6 +806,8 @@ do_flush( conn_t *conn ) #ifdef HAVE_LIBZ if (conn->out_z) { int buf_avail = conn->append_avail; + if (!conn->z_written) + return; do { if (!bc) { buf_avail = WRITE_CHUNK_SIZE; @@ -828,6 +833,7 @@ do_flush( conn_t *conn ) } while (!conn->out_z->avail_out); conn->append_buf = bc; conn->append_avail = buf_avail; + conn->z_written = 0; } else #endif if (bc) { @@ -839,15 +845,15 @@ do_flush( conn_t *conn ) } } -int +void socket_write( conn_t *conn, conn_iovec_t *iov, int iovcnt ) { int i, buf_avail, len, offset = 0, total = 0; - buff_chunk_t *bc, *exwb = conn->write_buf; + buff_chunk_t *bc; for (i = 0; i < iovcnt; i++) total += iov[i].len; - if (total >= WRITE_CHUNK_SIZE && pending_wakeup( &conn->fd_fake )) { + if (total >= WRITE_CHUNK_SIZE) { /* If the new data is too big, queue the pending buffer to avoid latency. */ do_flush( conn ); } @@ -886,6 +892,7 @@ socket_write( conn_t *conn, conn_iovec_t *iov, int iovcnt ) bc->len = (char *)conn->out_z->next_out - bc->data; buf_avail = conn->out_z->avail_out; len -= conn->out_z->avail_in; + conn->z_written = 1; } else #endif { @@ -914,17 +921,7 @@ socket_write( conn_t *conn, conn_iovec_t *iov, int iovcnt ) #ifdef HAVE_LIBZ conn->append_avail = buf_avail; #endif - /* Queue the pending write once the main loop goes idle. */ - conf_wakeup( &conn->fd_fake, -#ifdef HAVE_LIBZ - /* Always give zlib a chance to flush its internal buffer. */ - conn->out_z || -#endif - bc ? 0 : -1 ); - /* If no writes were queued before, ensure that flushing commences. */ - if (!exwb) - return do_queued_write( conn ); - return 0; + conf_wakeup( &conn->fd_fake, 0 ); } static void @@ -979,10 +976,10 @@ socket_fake_cb( void *aux ) { conn_t *conn = (conn_t *)aux; - buff_chunk_t *exwb = conn->write_buf; + /* Ensure that a pending write gets queued. */ do_flush( conn ); - /* If no writes were queued before, ensure that flushing commences. */ - if (!exwb) + /* If no writes are ongoing, start writing now. */ + if (!conn->writing) do_queued_write( conn ); } diff --git a/src/socket.h b/src/socket.h @@ -84,11 +84,12 @@ typedef struct { #ifdef HAVE_LIBZ z_streamp in_z, out_z; wakeup_t z_fake; + int z_written; #endif void (*bad_callback)( void *aux ); /* async fail while sending or listening */ void (*read_callback)( void *aux ); /* data available for reading */ - int (*write_callback)( void *aux ); /* all *queued* data was sent */ + void (*write_callback)( void *aux ); /* all *queued* data was sent */ union { void (*connect)( int ok, void *aux ); void (*starttls)( int ok, void *aux ); @@ -102,6 +103,7 @@ typedef struct { /* writing */ buff_chunk_t *append_buf; /* accumulating buffer */ buff_chunk_t *write_buf, **write_buf_append; /* buffer head & tail */ + int writing; #ifdef HAVE_LIBZ int append_avail; /* space left in accumulating buffer */ #endif @@ -123,7 +125,7 @@ static INLINE void socket_init( conn_t *conn, const server_conf_t *conf, void (*bad_callback)( void *aux ), void (*read_callback)( void *aux ), - int (*write_callback)( void *aux ), + void (*write_callback)( void *aux ), void *aux ) { conn->conf = conf; @@ -148,6 +150,6 @@ typedef struct conn_iovec { int len; ownership_t takeOwn; } conn_iovec_t; -int socket_write( conn_t *sock, conn_iovec_t *iov, int iovcnt ); +void socket_write( conn_t *sock, conn_iovec_t *iov, int iovcnt ); #endif