Compare commits

...

6 Commits

Author SHA1 Message Date
Oswald Buddenhagen 15c7e02e4a accept zero-sized messages from IMAP 1 month ago
Behnam Lal d7305e12d9 mbsync-get-cert: add support for STARTTLS 1 month ago
Oswald Buddenhagen a1be7e9a36 make summary more concise 1 month ago
Oswald Buddenhagen 1e7a75095b fix crash when resuming message propagation with MaxMessages 1 month ago
Oswald Buddenhagen 5f953c5162 fix omissions in making expiration target side configurable 1 month ago
Oswald Buddenhagen bf34d9fd29 do not let both-sided uidvalidity change deter us 1 month ago
  1. 30
      mbsync-get-cert
  2. 5
      src/drv_imap.c
  3. 32
      src/main_sync.c
  4. 2
      src/mbsync.1.in
  5. 1
      src/socket.c
  6. 37
      src/sync.c
  7. 5
      src/util.c

30
mbsync-get-cert

@ -9,9 +9,25 @@
# from a trusted source. # from a trusted source.
# #
if [ $# != 1 ]; then usage() {
echo "Usage: $0 <host>" >&2 echo "Usage: $0 [-s] <host>" >&2
echo " -s Use IMAP+STARTTLS (port 143) instead of IMAPS (port 993)" >&2
exit 1 exit 1
}
STARTTLS=false
while getopts "s" opt; do
case $opt in
s) STARTTLS=true ;;
*) usage ;;
esac
done
shift `expr $OPTIND - 1`
if [ $# -ne 1 ]; then
usage
fi fi
HOST=$1 HOST=$1
@ -33,7 +49,15 @@ TMPFILE=$TMPDIR/get-cert
ERRFILE=$TMPDIR/get-cert-err ERRFILE=$TMPDIR/get-cert-err
CERTFILE=$TMPDIR/cert CERTFILE=$TMPDIR/cert
echo QUIT | openssl s_client -connect $HOST:993 -showcerts \ if $STARTTLS; then
FLAGS="-starttls imap"
PORT=143
else
FLAGS=
PORT=993
fi
echo QUIT | openssl s_client $FLAGS -connect $HOST:$PORT -showcerts \
> $TMPFILE 2> $ERRFILE > $TMPFILE 2> $ERRFILE
sed -e '1,/^-----BEGIN CERTIFICATE-----/d' \ sed -e '1,/^-----BEGIN CERTIFICATE-----/d' \
-e '/^-----END CERTIFICATE-----/,$d' < $TMPFILE > $CERTFILE -e '/^-----END CERTIFICATE-----/,$d' < $TMPFILE > $CERTFILE

5
src/drv_imap.c

@ -780,6 +780,8 @@ parse_imap_list( imap_store_t *ctx, char **sp, parse_list_state_t *sts )
if (sts->callback->atom( ctx, NULL, bytes, AtomChunkedLiteral ) != LIST_OK) if (sts->callback->atom( ctx, NULL, bytes, AtomChunkedLiteral ) != LIST_OK)
goto bail; goto bail;
sts->in_literal = ChunkedLiteral; sts->in_literal = ChunkedLiteral;
if (!bytes)
goto nobytes;
sts->big_literal = 1; sts->big_literal = 1;
get_chunked: get_chunked:
n = 1; n = 1;
@ -806,7 +808,7 @@ parse_imap_list( imap_store_t *ctx, char **sp, parse_list_state_t *sts )
} else if (DFlags & DEBUG_NET_ALL) { } else if (DFlags & DEBUG_NET_ALL) {
printf( "%s=========\n", ctx->label ); printf( "%s=========\n", ctx->label );
fwrite( p, n, 1, stdout ); fwrite( p, n, 1, stdout );
if (p[n - 1] != '\n') if (n && p[n - 1] != '\n')
fputs( "\n(no-nl) ", stdout ); fputs( "\n(no-nl) ", stdout );
printf( "%s=========\n", ctx->label ); printf( "%s=========\n", ctx->label );
} else { } else {
@ -819,6 +821,7 @@ parse_imap_list( imap_store_t *ctx, char **sp, parse_list_state_t *sts )
bytes -= n; bytes -= n;
if (bytes > 0) if (bytes > 0)
goto postpone; goto postpone;
nobytes:
if (sts->in_literal == ChunkedLiteral && sts->callback->atom( ctx, NULL, 0, AtomLiteral ) != LIST_OK) if (sts->in_literal == ChunkedLiteral && sts->callback->atom( ctx, NULL, 0, AtomLiteral ) != LIST_OK)
goto bail; goto bail;
getline: getline:

32
src/main_sync.c

@ -9,7 +9,6 @@
#define nz(a, b) ((a) ? (a) : (b)) #define nz(a, b) ((a) ? (a) : (b))
static int ops_any[2], trash_any[2], expunge_any[2];
static int chans_total, chans_done; static int chans_total, chans_done;
static int boxes_total, boxes_done; static int boxes_total, boxes_done;
@ -84,25 +83,10 @@ summary( void )
if (!boxes_done) if (!boxes_done)
return; // Shut up if we errored out early. return; // Shut up if we errored out early.
printf( "Processed %d box(es) in %d channel(s)", boxes_done, chans_done ); printf( "Channels: %d Boxes: %d Far: +%d *%d #%d -%d Near: +%d *%d #%d -%d\n",
for (int t = 2; --t >= 0; ) { chans_done, boxes_done,
if (ops_any[t]) new_done[F], flags_done[F], trash_done[F], expunge_done[F],
printf( (DFlags & DRYRUN) ? new_done[N], flags_done[N], trash_done[N], expunge_done[N] );
",\nwould %s %d new message(s) and %d flag update(s)" :
",\n%sed %d new message(s) and %d flag update(s)",
str_hl[t], new_done[t], flags_done[t] );
if (trash_any[t])
printf( (DFlags & DRYRUN) ?
",\nwould move %d %s message(s) to trash" :
",\nmoved %d %s message(s) to trash",
trash_done[t], str_fn[t] );
if (expunge_any[t])
printf( (DFlags & DRYRUN) ?
",\nwould expunge %d message(s) from %s" :
",\nexpunged %d message(s) from %s",
expunge_done[t], str_fn[t] );
}
puts( "." );
} }
static int static int
@ -244,14 +228,6 @@ add_channel( chan_ent_t ***chanapp, channel_conf_t *chan, int ops[] )
free( ce ); free( ce );
return NULL; return NULL;
} }
if (chan->ops[t] & OP_MASK_TYPE)
ops_any[t] = 1;
if (chan->ops[t] & (OP_EXPUNGE | OP_EXPUNGE_SOLO)) {
expunge_any[t] = 1;
if (chan->stores[t]->trash ||
(chan->stores[t^1]->trash && chan->stores[t^1]->trash_remote_new))
trash_any[t] = 1;
}
} }
**chanapp = ce; **chanapp = ce;

2
src/mbsync.1.in

@ -802,7 +802,7 @@ No attempt is made to calculate the totals in advance, so they grow over
time as more information is gathered. time as more information is gathered.
.P .P
Irrespective of output redirection, \fBmbsync\fR will print a summary Irrespective of output redirection, \fBmbsync\fR will print a summary
of the above in plain language upon completion, except in quiet mode. of the above upon completion, except in quiet mode.
. .
.SH RECOMMENDATIONS .SH RECOMMENDATIONS
Make sure your IMAP server does not auto-expunge deleted messages - it is Make sure your IMAP server does not auto-expunge deleted messages - it is

1
src/socket.c

@ -952,7 +952,6 @@ socket_expect_bytes( conn_t *conn, uint len )
char * char *
socket_read( conn_t *conn, uint min_len, uint max_len, uint *out_len ) socket_read( conn_t *conn, uint min_len, uint max_len, uint *out_len )
{ {
assert( min_len > 0 );
assert( min_len <= sizeof(conn->buf) ); assert( min_len <= sizeof(conn->buf) );
assert( min_len <= max_len ); assert( min_len <= max_len );

37
src/sync.c

@ -511,7 +511,7 @@ box_opened2( sync_vars_t *svars, int t )
channel_conf_t *chan; channel_conf_t *chan;
sync_rec_t *srec; sync_rec_t *srec;
uint_array_alloc_t mexcs; uint_array_alloc_t mexcs;
uint opts[2], fails, minwuid; uint opts[2], minwuid;
svars->state[t] |= ST_SELECTED; svars->state[t] |= ST_SELECTED;
if (!(svars->state[t^1] & ST_SELECTED)) if (!(svars->state[t^1] & ST_SELECTED))
@ -520,23 +520,13 @@ box_opened2( sync_vars_t *svars, int t )
ctx[1] = svars->ctx[1]; ctx[1] = svars->ctx[1];
chan = svars->chan; chan = svars->chan;
fails = 0; if (!lock_state( svars )) {
for (t = 0; t < 2; t++)
if (svars->uidval[t] != UIDVAL_BAD && svars->uidval[t] != svars->newuidval[t])
fails++;
// If only one side changed UIDVALIDITY, we will try to re-approve it further down.
if (fails == 2) {
error( "Error: channel %s: UIDVALIDITY of both far side %s and near side %s changed.\n",
svars->chan->name, svars->orig_name[F], svars->orig_name[N]);
bail: bail:
svars->ret = SYNC_FAIL; svars->ret = SYNC_FAIL;
sync_bail( svars ); sync_bail( svars );
return; return;
} }
if (!lock_state( svars ))
goto bail;
int any_dummies[2] = { 0, 0 }; int any_dummies[2] = { 0, 0 };
int any_purges[2] = { 0, 0 }; int any_purges[2] = { 0, 0 };
int any_upgrades[2] = { 0, 0 }; int any_upgrades[2] = { 0, 0 };
@ -574,9 +564,11 @@ box_opened2( sync_vars_t *svars, int t )
} }
opts[F] = opts[N] = 0; opts[F] = opts[N] = 0;
if (fails)
opts[F] = opts[N] = OPEN_PAIRED | OPEN_PAIRED_IDS;
for (t = 0; t < 2; t++) { for (t = 0; t < 2; t++) {
if (svars->uidval[t] != UIDVAL_BAD && svars->uidval[t] != svars->newuidval[t]) {
opts[F] |= OPEN_PAIRED | OPEN_PAIRED_IDS;
opts[N] |= OPEN_PAIRED | OPEN_PAIRED_IDS;
}
if (any_purges[t]) { if (any_purges[t]) {
debug( "resuming %d %s purge(s)\n", any_purges[t], str_fn[t] ); debug( "resuming %d %s purge(s)\n", any_purges[t], str_fn[t] );
opts[t] |= OPEN_SETFLAGS; opts[t] |= OPEN_SETFLAGS;
@ -859,8 +851,15 @@ box_loaded( int sts, message_t *msgs, int total_msgs, int recent_msgs, void *aux
if (!srec->msg[t^1]) if (!srec->msg[t^1])
continue; // Partner disappeared. continue; // Partner disappeared.
if (!srec->msg[t^1]->msgid || strcmp( srec->msg[F]->msgid, srec->msg[N]->msgid )) { if (!srec->msg[t^1]->msgid || strcmp( srec->msg[F]->msgid, srec->msg[N]->msgid )) {
error( "Error: channel %s, %s box %s: UIDVALIDITY genuinely changed (at UID %u).\n", if (svars->uidval[t^1] != svars->newuidval[t^1]) {
svars->chan->name, str_fn[t], svars->orig_name[t], srec->uid[t] ); error( "Error: channel %s, %s box %s (at UID %u):"
" Unable to recover from both-sided UIDVALIDITY change,"
" as it is genuine on at least one side.\n",
svars->chan->name, str_fn[t], svars->orig_name[t], srec->uid[t] );
} else {
error( "Error: channel %s, %s box %s (at UID %u): UIDVALIDITY genuinely changed.\n",
svars->chan->name, str_fn[t], svars->orig_name[t], srec->uid[t] );
}
uvchg: uvchg:
svars->ret |= SYNC_FAIL; svars->ret |= SYNC_FAIL;
cancel_sync( svars ); cancel_sync( svars );
@ -1014,7 +1013,7 @@ box_loaded( int sts, message_t *msgs, int total_msgs, int recent_msgs, void *aux
sflags = sanitize_flags( sflags, svars, t ); sflags = sanitize_flags( sflags, svars, t );
if ((t != xt) && (srec->status & (S_EXPIRE | S_EXPIRED))) { if ((t != xt) && (srec->status & (S_EXPIRE | S_EXPIRED))) {
/* Don't propagate deletion resulting from expiration. */ /* Don't propagate deletion resulting from expiration. */
debug( " near side expiring\n" ); debug( " %s side expiring\n", str_fn[xt] );
sflags &= ~F_DELETED; sflags &= ~F_DELETED;
} }
if (srec->status & S_DUMMY(t^1)) { if (srec->status & S_DUMMY(t^1)) {
@ -1205,7 +1204,7 @@ box_loaded( int sts, message_t *msgs, int total_msgs, int recent_msgs, void *aux
if (!srec->uid[xt^1]) if (!srec->uid[xt^1])
continue; continue;
if (!(srec->status & S_PENDING)) { if (!(srec->status & S_PENDING)) {
// We ignore unpaired far-side messages, as there is obviously nothing // We ignore unpaired keep-side messages, as there is obviously nothing
// to expire in the first place. // to expire in the first place.
if (!srec->msg[xt]) if (!srec->msg[xt])
continue; continue;
@ -1230,6 +1229,8 @@ box_loaded( int sts, message_t *msgs, int total_msgs, int recent_msgs, void *aux
// but we may be pulling in the real ones. // but we may be pulling in the real ones.
nflags = (srec->pflags | srec->aflags[xt]) & ~srec->dflags[xt]; nflags = (srec->pflags | srec->aflags[xt]) & ~srec->dflags[xt];
} else { } else {
if (!srec->msg[xt^1])
continue;
nflags = srec->msg[xt^1]->flags; nflags = srec->msg[xt^1]->flags;
} }
} }

5
src/util.c

@ -637,6 +637,11 @@ nfmalloc( size_t sz )
{ {
void *ret; void *ret;
#ifndef __GLIBC__
// The C standard allows NULL returns for zero-sized allocations.
if (!sz)
sz = 1;
#endif
if (!(ret = malloc( sz ))) if (!(ret = malloc( sz )))
oom(); oom();
return ret; return ret;

Loading…
Cancel
Save