Merge branch 'isync_1_2_branch'

Conflicts:
	configure.ac
	src/drv_imap.c
This commit is contained in:
Oswald Buddenhagen 2015-05-09 19:31:55 +02:00
commit bcd43e2c66
7 changed files with 145 additions and 149 deletions

2
README
View File

@ -61,7 +61,7 @@ isync executable still exists; it is a compatibility wrapper around mbsync.
* Requirements * Requirements
Berkley DB 4.2+ (optional) Berkeley DB 4.1+ (optional)
OpenSSL for TLS/SSL support (optional) OpenSSL for TLS/SSL support (optional)
* Installation * Installation

3
TODO
View File

@ -3,9 +3,6 @@ f{,data}sync() usage could be optimized by batching the calls.
add some marker about message being already [remotely] trashed. add some marker about message being already [remotely] trashed.
real transactions would be certainly not particularly useful ... 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". make SSL (connect) timeouts produce a bit more than "Unidentified socket error".
uidvalidity lock timeout handling would be a good idea. uidvalidity lock timeout handling would be a good idea.

View File

@ -139,19 +139,28 @@ if test "x$ob_cv_with_sasl" != xno; then
fi fi
AC_SUBST(SASL_LIBS) 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 [ac_cv_berkdb4=no
sav_LDFLAGS=$LDFLAGS
LDFLAGS="$LDFLAGS -ldb"
AC_TRY_LINK([#include <db.h>], AC_TRY_LINK([#include <db.h>],
[DB *db; [DB *db;
db_create(&db, 0, 0);
db->truncate(db, 0, 0, 0); db->truncate(db, 0, 0, 0);
db->open(db, 0, "foo", "foo", DB_HASH, DB_CREATE, 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 if test "x$ac_cv_berkdb4" = xyes; then
AC_SUBST([DB_LIBS], ["-ldb"]) 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 fi
have_zlib= have_zlib=
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_LIB([z], [deflate],
[AC_CHECK_HEADER(zlib.h, [AC_CHECK_HEADER(zlib.h,
[have_zlib=1 [have_zlib=1
@ -159,6 +168,7 @@ AC_CHECK_LIB([z], [deflate],
AC_DEFINE([HAVE_LIBZ], 1, [if you have the zlib library])] AC_DEFINE([HAVE_LIBZ], 1, [if you have the zlib library])]
)] )]
) )
fi
AC_ARG_ENABLE(compat, AC_ARG_ENABLE(compat,
AC_HELP_STRING([--disable-compat], [don't include isync compatibility wrapper [no]]), AC_HELP_STRING([--disable-compat], [don't include isync compatibility wrapper [no]]),
@ -189,8 +199,8 @@ else
AC_MSG_RESULT([Not using zlib]) AC_MSG_RESULT([Not using zlib])
fi fi
if test "x$ac_cv_berkdb4" = xyes; then if test "x$ac_cv_berkdb4" = xyes; then
AC_MSG_RESULT([Using Berkley DB]) AC_MSG_RESULT([Using Berkeley DB])
else else
AC_MSG_RESULT([Not using Berkley DB]) AC_MSG_RESULT([Not using Berkeley DB])
fi fi
AC_MSG_RESULT() AC_MSG_RESULT()

View File

@ -267,7 +267,7 @@ done_imap_cmd( imap_store_t *ctx, struct imap_cmd *cmd, int response )
free( cmd ); free( cmd );
} }
static int static void
send_imap_cmd( imap_store_t *ctx, struct imap_cmd *cmd ) send_imap_cmd( imap_store_t *ctx, struct imap_cmd *cmd )
{ {
int bufl, litplus, iovcnt = 1; int bufl, litplus, iovcnt = 1;
@ -313,8 +313,7 @@ send_imap_cmd( imap_store_t *ctx, struct imap_cmd *cmd )
iov[2].takeOwn = KeepOwn; iov[2].takeOwn = KeepOwn;
iovcnt = 3; iovcnt = 3;
} }
if (socket_write( &ctx->conn, iov, iovcnt ) < 0) socket_write( &ctx->conn, iov, iovcnt );
goto bail;
if (cmd->param.to_trash && ctx->trashnc == TrashUnknown) if (cmd->param.to_trash && ctx->trashnc == TrashUnknown)
ctx->trashnc = TrashChecking; ctx->trashnc = TrashChecking;
cmd->next = 0; 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->in_progress_append = &cmd->next;
ctx->num_in_progress++; ctx->num_in_progress++;
socket_expect_read( &ctx->conn, 1 ); socket_expect_read( &ctx->conn, 1 );
return 0;
bail:
done_imap_cmd( ctx, cmd, RESP_CANCEL );
return -1;
} }
static int 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; 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; 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 ) flush_imap_cmds( imap_store_t *ctx )
{ {
struct imap_cmd *cmd; 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)) if (!(ctx->pending = cmd->next))
ctx->pending_append = &ctx->pending; ctx->pending_append = &ctx->pending;
if (send_imap_cmd( ctx, cmd ) < 0) send_imap_cmd( ctx, cmd );
return -1;
} }
return 0;
} }
static void static void
@ -370,7 +362,7 @@ cancel_pending_imap_cmds( imap_store_t *ctx )
} }
static void static void
cancel_submitted_imap_cmds( imap_store_t *ctx ) cancel_sent_imap_cmds( imap_store_t *ctx )
{ {
struct imap_cmd *cmd; 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 ) submit_imap_cmd( imap_store_t *ctx, struct imap_cmd *cmd )
{ {
assert( ctx ); assert( ctx );
@ -390,7 +382,7 @@ submit_imap_cmd( imap_store_t *ctx, struct imap_cmd *cmd )
assert( cmd ); assert( cmd );
assert( cmd->param.done ); 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) { if (ctx->pending && cmd->param.high_prio) {
cmd->next = ctx->pending; cmd->next = ctx->pending;
ctx->pending = cmd; 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;
ctx->pending_append = &cmd->next; 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 /* 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, imap_exec( imap_store_t *ctx, struct imap_cmd *cmdp,
void (*done)( imap_store_t *ctx, struct imap_cmd *cmd, int response ), void (*done)( imap_store_t *ctx, struct imap_cmd *cmd, int response ),
const char *fmt, ... ) const char *fmt, ... )
@ -500,7 +491,7 @@ imap_exec( imap_store_t *ctx, struct imap_cmd *cmdp,
va_start( ap, fmt ); va_start( ap, fmt );
cmdp->cmd = imap_vprintf( fmt, ap ); cmdp->cmd = imap_vprintf( fmt, ap );
va_end( ap ); va_end( ap );
return submit_imap_cmd( ctx, cmdp ); submit_imap_cmd( ctx, cmdp );
} }
static void static void
@ -1337,8 +1328,7 @@ imap_socket_read( void *aux )
iov[1].buf = "\r\n"; iov[1].buf = "\r\n";
iov[1].len = 2; iov[1].len = 2;
iov[1].takeOwn = KeepOwn; iov[1].takeOwn = KeepOwn;
if (socket_write( &ctx->conn, iov, 2 ) < 0) socket_write( &ctx->conn, iov, 2 );
return;
} else if (cmdp->param.cont) { } else if (cmdp->param.cont) {
if (cmdp->param.cont( ctx, cmdp, cmd )) if (cmdp->param.cont( ctx, cmdp, cmd ))
return; return;
@ -1376,9 +1366,8 @@ imap_socket_read( void *aux )
cmd2->orig_cmd = cmdp; cmd2->orig_cmd = cmdp;
cmd2->gen.param.high_prio = 1; cmd2->gen.param.high_prio = 1;
p = strchr( cmdp->cmd, '"' ); p = strchr( cmdp->cmd, '"' );
if (imap_exec( ctx, &cmd2->gen, get_cmd_result_p2, imap_exec( ctx, &cmd2->gen, get_cmd_result_p2,
"CREATE %.*s", imap_strchr( p + 1, '"' ) - p + 1, p ) < 0) "CREATE %.*s", imap_strchr( p + 1, '"' ) - p + 1, p );
return;
continue; continue;
} }
resp = RESP_NO; resp = RESP_NO;
@ -1387,7 +1376,11 @@ imap_socket_read( void *aux )
} else /*if (!strcmp( "BAD", arg ))*/ } else /*if (!strcmp( "BAD", arg ))*/
resp = RESP_CANCEL; resp = RESP_CANCEL;
error( "IMAP command '%s' returned an error: %s %s\n", 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 : "" ); arg, cmd ? cmd : "" );
} }
doresp: doresp:
@ -1405,8 +1398,7 @@ imap_socket_read( void *aux )
return; return;
} }
} }
if (flush_imap_cmds( ctx ) < 0) flush_imap_cmds( ctx );
return;
} }
imap_invoke_bad_callback( ctx ); imap_invoke_bad_callback( ctx );
} }
@ -1440,7 +1432,7 @@ imap_cancel_store( store_t *gctx )
sasl_dispose( &ctx->sasl ); sasl_dispose( &ctx->sasl );
#endif #endif
socket_close( &ctx->conn ); socket_close( &ctx->conn );
cancel_submitted_imap_cmds( ctx ); cancel_sent_imap_cmds( ctx );
cancel_pending_imap_cmds( ctx ); cancel_pending_imap_cmds( ctx );
free_generic_messages( ctx->gen.msgs ); free_generic_messages( ctx->gen.msgs );
free_string_list( ctx->gen.boxes ); 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, socket_init( &ctx->conn, &srvc->sconf,
(void (*)( void * ))imap_invoke_bad_callback, (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 ); 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].len = 2;
iov[iovcnt].takeOwn = KeepOwn; iov[iovcnt].takeOwn = KeepOwn;
iovcnt++; iovcnt++;
return socket_write( &ctx->conn, iov, iovcnt ); socket_write( &ctx->conn, iov, iovcnt );
return 0;
bail: bail:
imap_open_store_bail( ctx, FAIL_FINAL ); imap_open_store_bail( ctx, FAIL_FINAL );
@ -2289,7 +2282,7 @@ imap_prepare_load_box( store_t *gctx, int opts )
gctx->opts = 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 static void
imap_load_box( store_t *gctx, int minuid, int maxuid, int newuid, int *excs, int nexcs, 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) if (i != j)
bl += sprintf( buf + bl, ":%d", excs[i] ); bl += sprintf( buf + bl, ":%d", excs[i] );
} }
if (imap_submit_load( ctx, buf, 0, sts ) < 0) imap_submit_load( ctx, buf, 0, sts );
goto done;
} }
if (maxuid == INT_MAX) if (maxuid == INT_MAX)
maxuid = ctx->gen.uidnext ? ctx->gen.uidnext - 1 : 1000000000; maxuid = ctx->gen.uidnext ? ctx->gen.uidnext - 1 : 1000000000;
if (maxuid >= minuid) { if (maxuid >= minuid) {
if ((ctx->gen.opts & OPEN_FIND) && minuid < newuid) { if ((ctx->gen.opts & OPEN_FIND) && minuid < newuid) {
sprintf( buf, "%d:%d", minuid, newuid - 1 ); sprintf( buf, "%d:%d", minuid, newuid - 1 );
if (imap_submit_load( ctx, buf, 0, sts ) < 0) imap_submit_load( ctx, buf, 0, sts );
goto done;
if (newuid > maxuid) if (newuid > maxuid)
goto done; goto done;
sprintf( buf, "%d:%d", newuid, maxuid ); sprintf( buf, "%d:%d", newuid, maxuid );
@ -2340,10 +2331,10 @@ 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 ) 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, imap_exec( ctx, imap_refcounted_new_cmd( sts ), imap_refcounted_done_box,
"UID FETCH %s (UID%s%s%s)", buf, "UID FETCH %s (UID%s%s%s)", buf,
(ctx->gen.opts & OPEN_FLAGS) ? " FLAGS" : "", (ctx->gen.opts & OPEN_FLAGS) ? " FLAGS" : "",
(ctx->gen.opts & OPEN_SIZE) ? " RFC822.SIZE" : "", (ctx->gen.opts & OPEN_SIZE) ? " RFC822.SIZE" : "",
@ -2404,14 +2395,14 @@ imap_make_flags( int flags, char *buf )
return d; return d;
} }
static int static void
imap_flags_helper( imap_store_t *ctx, int uid, char what, int flags, imap_flags_helper( imap_store_t *ctx, int uid, char what, int flags,
struct imap_cmd_refcounted_state *sts ) struct imap_cmd_refcounted_state *sts )
{ {
char buf[256]; char buf[256];
buf[imap_make_flags( flags, buf )] = 0; buf[imap_make_flags( flags, buf )] = 0;
return imap_exec( ctx, imap_refcounted_new_cmd( sts ), imap_set_flags_p2, imap_exec( ctx, imap_refcounted_new_cmd( sts ), imap_set_flags_p2,
"UID STORE %d %cFLAGS.SILENT %s", uid, what, buf ); "UID STORE %d %cFLAGS.SILENT %s", uid, what, buf );
} }
@ -2430,8 +2421,10 @@ imap_set_msg_flags( store_t *gctx, message_t *msg, int uid, int add, int del,
} }
if (add || del) { if (add || del) {
struct imap_cmd_refcounted_state *sts = imap_refcounted_new_state( cb, aux ); struct imap_cmd_refcounted_state *sts = imap_refcounted_new_state( cb, aux );
if ((add && imap_flags_helper( ctx, uid, '+', add, sts ) < 0) || if (add)
(del && imap_flags_helper( ctx, uid, '-', del, sts ) < 0)) {} imap_flags_helper( ctx, uid, '+', add, sts );
if (del)
imap_flags_helper( ctx, uid, '-', del, sts );
imap_refcounted_done( sts ); imap_refcounted_done( sts );
} else { } else {
cb( DRV_OK, aux ); cb( DRV_OK, aux );
@ -2482,9 +2475,8 @@ imap_close_box( store_t *gctx,
} }
if (!bl) if (!bl)
break; break;
if (imap_exec( ctx, imap_refcounted_new_cmd( sts ), imap_refcounted_done_box, imap_exec( ctx, imap_refcounted_new_cmd( sts ), imap_refcounted_done_box,
"UID EXPUNGE %s", buf ) < 0) "UID EXPUNGE %s", buf );
break;
} }
imap_refcounted_done( sts ); imap_refcounted_done( sts );
} else { } else {
@ -2625,13 +2617,12 @@ imap_list_store( store_t *gctx, int flags,
imap_store_t *ctx = (imap_store_t *)gctx; imap_store_t *ctx = (imap_store_t *)gctx;
struct imap_cmd_refcounted_state *sts = imap_refcounted_new_state( cb, aux ); 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 )) && 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, imap_exec( ctx, imap_refcounted_new_cmd( sts ), imap_refcounted_done_box,
"LIST \"\" \"%\\s*\"", ctx->prefix ) < 0) || "LIST \"\" \"%\\s*\"", ctx->prefix );
((flags & LIST_INBOX) && (!(flags & LIST_PATH) || *ctx->prefix) && if ((flags & LIST_INBOX) && (!(flags & LIST_PATH) || *ctx->prefix))
imap_exec( ctx, imap_refcounted_new_cmd( sts ), imap_refcounted_done_box, imap_exec( ctx, imap_refcounted_new_cmd( sts ), imap_refcounted_done_box,
"LIST \"\" INBOX*" ) < 0)) "LIST \"\" INBOX*" );
{}
imap_refcounted_done( sts ); imap_refcounted_done( sts );
} }
@ -2695,8 +2686,9 @@ imap_parse_store( conffile_t *cfg, store_conf_t **storep )
/* Legacy SSL options */ /* Legacy SSL options */
int require_ssl = -1, use_imaps = -1; int require_ssl = -1, use_imaps = -1;
int use_sslv2 = -1, use_sslv3 = -1, use_tlsv1 = -1, use_tlsv11 = -1, use_tlsv12 = -1; int use_sslv2 = -1, use_sslv3 = -1, use_tlsv1 = -1, use_tlsv11 = -1, use_tlsv12 = -1;
int require_cram = -1;
#endif #endif
/* Legacy SASL option */
int require_cram = -1;
if (!strcasecmp( "IMAPAccount", cfg->cmd )) { if (!strcasecmp( "IMAPAccount", cfg->cmd )) {
server = nfcalloc( sizeof(*server) ); server = nfcalloc( sizeof(*server) );
@ -2829,6 +2821,7 @@ imap_parse_store( conffile_t *cfg, store_conf_t **storep )
use_tlsv11 = parse_bool( cfg ); use_tlsv11 = parse_bool( cfg );
else if (!strcasecmp( "UseTLSv1.2", cfg->cmd )) else if (!strcasecmp( "UseTLSv1.2", cfg->cmd ))
use_tlsv12 = parse_bool( cfg ); use_tlsv12 = parse_bool( cfg );
#endif
else if (!strcasecmp( "AuthMech", cfg->cmd ) || else if (!strcasecmp( "AuthMech", cfg->cmd ) ||
!strcasecmp( "AuthMechs", cfg->cmd )) { !strcasecmp( "AuthMechs", cfg->cmd )) {
arg = cfg->val; 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 ))); while ((arg = get_arg( cfg, ARG_OPTIONAL, 0 )));
} else if (!strcasecmp( "RequireCRAM", cfg->cmd )) } else if (!strcasecmp( "RequireCRAM", cfg->cmd ))
require_cram = parse_bool( cfg ); require_cram = parse_bool( cfg );
#endif
else if (!strcasecmp( "Tunnel", cfg->cmd )) else if (!strcasecmp( "Tunnel", cfg->cmd ))
server->sconf.tunnel = nfstrdup( cfg->val ); server->sconf.tunnel = nfstrdup( cfg->val );
else if (store) { 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; server->ssl_type = server->sconf.tunnel ? SSL_None : SSL_STARTTLS;
} }
#endif #endif
#ifdef HAVE_LIBSSL
if (require_cram >= 0) { if (require_cram >= 0) {
if (server->auth_mechs) { if (server->auth_mechs) {
error( "%s '%s': The deprecated RequireCRAM option is mutually exlusive with AuthMech.\n", type, name ); 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) if (require_cram)
add_string_list(&server->auth_mechs, "CRAM-MD5"); add_string_list(&server->auth_mechs, "CRAM-MD5");
} }
#endif
if (!server->auth_mechs) if (!server->auth_mechs)
add_string_list( &server->auth_mechs, "*" ); add_string_list( &server->auth_mechs, "*" );
if (!server->sconf.port) if (!server->sconf.port)

View File

@ -192,17 +192,17 @@ See \fBRECOMMENDATIONS\fR and \fBINHERENT PROBLEMS\fR below.
(Default: none) (Default: none)
.. ..
.TP .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 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). remote Store has a \fBTrash\fR as well (with \fBTrashNewOnly\fR \fBno\fR).
(Default: \fIno\fR) (Default: \fBno\fR)
.. ..
.TP .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 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 Store's \fBTrash\fR. When using this, the remote Store does not need an own
\fBTrash\fR at all, yet all messages are archived. \fBTrash\fR at all, yet all messages are archived.
(Default: \fIno\fR) (Default: \fBno\fR)
.. ..
.SS Maildir Stores .SS Maildir Stores
The reference point for relative \fBPath\fRs is the current working directory. 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. Define the Maildir Store \fIname\fR, opening a section for its parameters.
.. ..
.TP .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. 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; This does not affect mailboxes that do already have a UID storage scheme;
use \fBmdconvert\fR to change it. use \fBmdconvert\fR to change it.
(Default: \fIno\fR) (Default: \fBno\fR)
.. ..
.TP .TP
\fBInbox\fR \fIpath\fR \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. Specify the DNS name or IP address of the IMAP server.
.br .br
If \fBTunnel\fR is used, this setting is needed only if \fBSSLType\fR is 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. in which case the host name is used for certificate subject verification.
.. ..
.TP .TP
@ -309,7 +309,7 @@ Specify the login name on the IMAP server.
.TP .TP
\fBPass\fR \fIpassword\fR \fBPass\fR \fIpassword\fR
Specify the password for \fIusername\fR on the IMAP server. 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 If neither a password nor a password command is specified in the
configuration file, \fBmbsync\fR will prompt you for a password. 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) (Default: \fB*\fR)
.. ..
.TP .TP
\fBSSLType\fR {\fINone\fR|\fISTARTTLS\fR|\fIIMAPS\fR} \fBSSLType\fR {\fBNone\fR|\fBSTARTTLS\fR|\fBIMAPS\fR}
Select the connection security/encryption method: Select the connection security/encryption method:
.br .br
\fINone\fR - no security. \fBNone\fR - no security.
This is the default when \fBTunnel\fR is set, as tunnels are usually secure. This is the default when \fBTunnel\fR is set, as tunnels are usually secure.
.br .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, after connecting the regular IMAP port 143. Most servers support this,
so it is the default (unless a tunnel is used). so it is the default (unless a tunnel is used).
.br .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. right after connecting the secure IMAP port 993.
.. ..
.TP .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. Select the acceptable SSL/TLS versions.
Use of SSLv2 is strongly discouraged for security reasons, but might be the Use of SSLv2 is strongly discouraged for security reasons, but might be the
only option on some very old servers. 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. servers, \fBTLSv1\fR is the default.
.. ..
.TP .TP
\fBSystemCertificates\fR \fIyes\fR|\fIno\fR \fBSystemCertificates\fR \fByes\fR|\fBno\fR
Whether the system's default root cerificate store should be loaded. Whether the system's default root cerificate store should be loaded.
(Default: \fIyes\fR) (Default: \fByes\fR)
.. ..
.TP .TP
\fBCertificateFile\fR \fIpath\fR \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. one Store only anyway.
.. ..
.TP .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 Selects whether the server's first "personal" NAMESPACE should be prefixed to
mailbox names. Disabling this makes sense for some broken IMAP servers. mailbox names. Disabling this makes sense for some broken IMAP servers.
This option is meaningless if a \fBPath\fR was specified. This option is meaningless if a \fBPath\fR was specified.
(Default: \fIyes\fR) (Default: \fByes\fR)
.. ..
.TP .TP
\fBPathDelimiter\fR \fIdelim\fR \fBPathDelimiter\fR \fIdelim\fR
Specify the server's hierarchy delimiter. Specify the server's hierarchy delimiter.
(Default: taken from the server's first "personal" NAMESPACE) (Default: taken from the server's first "personal" NAMESPACE)
.br .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. Use \fBFlatten\fR instead.
.. ..
.SS Channels .SS Channels
@ -471,56 +471,56 @@ If \fIcount\fR is 0, the maximum number of messages is \fBunlimited\fR
(Default: \fI0\fR). (Default: \fI0\fR).
.. ..
.TP .TP
\fBExpireUnread\fR \fIyes\fR|\fIno\fR \fBExpireUnread\fR \fByes\fR|\fBno\fR
Selects whether unread messages should be affected by \fBMaxMessages\fR. Selects whether unread messages should be affected by \fBMaxMessages\fR.
Normally, unread messages are considered important and thus never expired. Normally, unread messages are considered important and thus never expired.
This ensures that you never miss new messages even after an extended absence. 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, However, if your archive contains large amounts of unread messages by design,
treating them as important would practically defeat \fBMaxMessages\fR. In this treating them as important would practically defeat \fBMaxMessages\fR. In this
case you need to enable this option. case you need to enable this option.
(Default: \fIno\fR). (Default: \fBno\fR).
.. ..
.TP .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: Select the synchronization operation(s) to perform:
.br .br
\fIPull\fR - propagate changes from Master to Slave. \fBPull\fR - propagate changes from Master to Slave.
.br .br
\fIPush\fR - propagate changes from Slave to Master. \fBPush\fR - propagate changes from Slave to Master.
.br .br
\fINew\fR - propagate newly appeared messages. \fBNew\fR - propagate newly appeared messages.
.br .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 Useful after flagging affected messages in the source Store or enlarging
MaxSize in the destination Store. MaxSize in the destination Store.
.br .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 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 Store are marked as deleted only, i.e., they won't be really deleted until
that Store is expunged. that Store is expunged.
.br .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 well; this is particularly interesting if you use \fBmutt\fR with the
maildir_trash option. maildir_trash option.
.br .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. This is the global default.
.br .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. Useful if you want to expunge only.
.IP .IP
\fIPull\fR and \fIPush\fR are direction flags, while \fINew\fR, \fIReNew\fR, \fBPull\fR and \fBPush\fR are direction flags, while \fBNew\fR, \fBReNew\fR,
\fIDelete\fR and \fIFlags\fR are type flags. The two flag classes make up a \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 two-dimensional matrix (a table). Its cells are the individual actions to
perform. There are two styles of asserting the cells: perform. There are two styles of asserting the cells:
.br .br
In the first style, the flags select entire rows/colums in the matrix. Only 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. 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. 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, new messages and flag changes from the Master to the Slave,
"\fBSync\fR\ \fINew\fR\ \fIDelete\fR" will propagate message arrivals and "\fBSync\fR\ \fBNew\fR\ \fBDelete\fR" will propagate message arrivals and
deletions both ways, and "\fBSync\fR\ \fIPush\fR" will propagate all changes deletions both ways, and "\fBSync\fR\ \fBPush\fR" will propagate all changes
from the Slave to the Master. from the Slave to the Master.
.br .br
In the second style, direction flags are concatenated with type flags; every 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 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 the first style, they immediately assert all cells in their respective
row/column. For example, 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 message arrivals and deletions from the Master to the Slave and any changes
from the Slave to the Master. from the Slave to the Master.
Note that it is not allowed to assert a cell in two ways, e.g. Note that it is not allowed to assert a cell in two ways, e.g.
"\fBSync\fR\ \fIPullNew\fR\ \fIPull\fR" and "\fBSync\fR\ \fBPullNew\fR\ \fBPull\fR" and
"\fBSync\fR\ \fIPullNew\fR\ \fIDelete\fR\ \fIPush\fR" induce error messages. "\fBSync\fR\ \fBPullNew\fR\ \fBDelete\fR\ \fBPush\fR" induce error messages.
.. ..
.TP .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]. Automatically create missing mailboxes [on the Master/Slave].
Otherwise print an error message and skip that mailbox pair if a mailbox Otherwise print an error message and skip that mailbox pair if a mailbox
and the corresponding sync state does not exist. and the corresponding sync state does not exist.
(Global default: \fINone\fR) (Global default: \fBNone\fR)
.. ..
.TP .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]. Propagate mailbox deletions [to the Master/Slave].
Otherwise print an error message and skip that mailbox pair if a mailbox Otherwise print an error message and skip that mailbox pair if a mailbox
does not exist but the corresponding sync state does. does not exist but the corresponding sync state does.
@ -553,23 +553,23 @@ mark them as deleted. This ensures compatibility with \fBSyncState *\fR.
.br .br
Note that for safety, non-empty mailboxes are never deleted. Note that for safety, non-empty mailboxes are never deleted.
.br .br
(Global default: \fINone\fR) (Global default: \fBNone\fR)
.. ..
.TP .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. Permanently remove all messages [on the Master/Slave] marked for deletion.
See \fBRECOMMENDATIONS\fR below. See \fBRECOMMENDATIONS\fR below.
(Global default: \fINone\fR) (Global default: \fBNone\fR)
.. ..
.TP .TP
\fBCopyArrivalDate\fR {\fIyes\fR|\fIno\fR} \fBCopyArrivalDate\fR {\fByes\fR|\fBno\fR}
Selects whether their arrival time should be propagated together with Selects whether their arrival time should be propagated together with
the messages. the messages.
Enabling this makes sense in order to keep the time stamp based message Enabling this makes sense in order to keep the time stamp based message
sorting intact. sorting intact.
Note that IMAP does not guarantee that the time stamp (termed \fBinternal 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. date\fR) is actually the arrival time, but it is usually close enough.
(Default: \fIno\fR) (Default: \fBno\fR)
.. ..
.P .P
\fBSync\fR, \fBCreate\fR, \fBRemove\fR, \fBExpunge\fR, \fBSync\fR, \fBCreate\fR, \fBRemove\fR, \fBExpunge\fR,
@ -615,7 +615,7 @@ times within a Group.
.. ..
.SS Global Options .SS Global Options
.TP .TP
\fBFSync\fR \fIyes\fR|\fIno\fR \fBFSync\fR \fByes\fR|\fBno\fR
.br .br
Selects whether \fBmbsync\fR performs forced flushing, which determines Selects whether \fBmbsync\fR performs forced flushing, which determines
the level of data safety after system crashes and power outages. 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, 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 in particular modern systems like ext4, btrfs and xfs. The performance impact
on older file systems may be disproportionate. on older file systems may be disproportionate.
(Default: \fIyes\fR) (Default: \fByes\fR)
.. ..
.TP .TP
\fBFieldDelimiter\fR \fIdelim\fR \fBFieldDelimiter\fR \fIdelim\fR

View File

@ -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. */ /* Callers take the short path out, so signal higher layers from here. */
conn->state = SCK_EOF; conn->state = SCK_EOF;
conn->read_callback( conn->callback_aux ); conn->read_callback( conn->callback_aux );
return 0; return -1;
} }
sys_error( "Socket error: secure %s %s", func, conn->name ); sys_error( "Socket error: secure %s %s", func, conn->name );
} else { } else {
@ -771,6 +771,7 @@ do_queued_write( conn_t *conn )
return -1; return -1;
if (n != len) { if (n != len) {
conn->write_offset += n; conn->write_offset += n;
conn->writing = 1;
return 0; return 0;
} }
conn->write_offset = 0; conn->write_offset = 0;
@ -780,7 +781,9 @@ do_queued_write( conn_t *conn )
if (conn->ssl && SSL_pending( conn->ssl )) if (conn->ssl && SSL_pending( conn->ssl ))
conf_wakeup( &conn->ssl_fake, 0 ); conf_wakeup( &conn->ssl_fake, 0 );
#endif #endif
return conn->write_callback( conn->callback_aux ); conn->writing = 0;
conn->write_callback( conn->callback_aux );
return -1;
} }
static void static void
@ -803,6 +806,8 @@ do_flush( conn_t *conn )
#ifdef HAVE_LIBZ #ifdef HAVE_LIBZ
if (conn->out_z) { if (conn->out_z) {
int buf_avail = conn->append_avail; int buf_avail = conn->append_avail;
if (!conn->z_written)
return;
do { do {
if (!bc) { if (!bc) {
buf_avail = WRITE_CHUNK_SIZE; buf_avail = WRITE_CHUNK_SIZE;
@ -828,6 +833,7 @@ do_flush( conn_t *conn )
} while (!conn->out_z->avail_out); } while (!conn->out_z->avail_out);
conn->append_buf = bc; conn->append_buf = bc;
conn->append_avail = buf_avail; conn->append_avail = buf_avail;
conn->z_written = 0;
} else } else
#endif #endif
if (bc) { if (bc) {
@ -839,15 +845,15 @@ do_flush( conn_t *conn )
} }
} }
int void
socket_write( conn_t *conn, conn_iovec_t *iov, int iovcnt ) socket_write( conn_t *conn, conn_iovec_t *iov, int iovcnt )
{ {
int i, buf_avail, len, offset = 0, total = 0; 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++) for (i = 0; i < iovcnt; i++)
total += iov[i].len; 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. */ /* If the new data is too big, queue the pending buffer to avoid latency. */
do_flush( conn ); 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; bc->len = (char *)conn->out_z->next_out - bc->data;
buf_avail = conn->out_z->avail_out; buf_avail = conn->out_z->avail_out;
len -= conn->out_z->avail_in; len -= conn->out_z->avail_in;
conn->z_written = 1;
} else } else
#endif #endif
{ {
@ -914,17 +921,7 @@ socket_write( conn_t *conn, conn_iovec_t *iov, int iovcnt )
#ifdef HAVE_LIBZ #ifdef HAVE_LIBZ
conn->append_avail = buf_avail; conn->append_avail = buf_avail;
#endif #endif
/* Queue the pending write once the main loop goes idle. */ conf_wakeup( &conn->fd_fake, 0 );
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;
} }
static void static void
@ -979,10 +976,10 @@ socket_fake_cb( void *aux )
{ {
conn_t *conn = (conn_t *)aux; conn_t *conn = (conn_t *)aux;
buff_chunk_t *exwb = conn->write_buf; /* Ensure that a pending write gets queued. */
do_flush( conn ); do_flush( conn );
/* If no writes were queued before, ensure that flushing commences. */ /* If no writes are ongoing, start writing now. */
if (!exwb) if (!conn->writing)
do_queued_write( conn ); do_queued_write( conn );
} }

View File

@ -84,11 +84,12 @@ typedef struct {
#ifdef HAVE_LIBZ #ifdef HAVE_LIBZ
z_streamp in_z, out_z; z_streamp in_z, out_z;
wakeup_t z_fake; wakeup_t z_fake;
int z_written;
#endif #endif
void (*bad_callback)( void *aux ); /* async fail while sending or listening */ void (*bad_callback)( void *aux ); /* async fail while sending or listening */
void (*read_callback)( void *aux ); /* data available for reading */ 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 { union {
void (*connect)( int ok, void *aux ); void (*connect)( int ok, void *aux );
void (*starttls)( int ok, void *aux ); void (*starttls)( int ok, void *aux );
@ -102,6 +103,7 @@ typedef struct {
/* writing */ /* writing */
buff_chunk_t *append_buf; /* accumulating buffer */ buff_chunk_t *append_buf; /* accumulating buffer */
buff_chunk_t *write_buf, **write_buf_append; /* buffer head & tail */ buff_chunk_t *write_buf, **write_buf_append; /* buffer head & tail */
int writing;
#ifdef HAVE_LIBZ #ifdef HAVE_LIBZ
int append_avail; /* space left in accumulating buffer */ int append_avail; /* space left in accumulating buffer */
#endif #endif
@ -123,7 +125,7 @@ static INLINE void socket_init( conn_t *conn,
const server_conf_t *conf, const server_conf_t *conf,
void (*bad_callback)( void *aux ), void (*bad_callback)( void *aux ),
void (*read_callback)( void *aux ), void (*read_callback)( void *aux ),
int (*write_callback)( void *aux ), void (*write_callback)( void *aux ),
void *aux ) void *aux )
{ {
conn->conf = conf; conn->conf = conf;
@ -148,6 +150,6 @@ typedef struct conn_iovec {
int len; int len;
ownership_t takeOwn; ownership_t takeOwn;
} conn_iovec_t; } 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 #endif