deprecate master/slave terminology

the underlying metaphor refers to an inhumane practice, so using it
casually is rightfully offensive to many people. it isn't even a
particularly apt metaphor, as it suggests a strict hierarchy that is
counter to mbsync's highly symmetrical mode of operation.

the far/near terminology has been chosen as the replacement, as it is a
natural fit for the push/pull terminology. on the downside, due to these
not being nouns, a few uses are a bit awkward, and several others had to
be amended to include 'side'. also, it's conceptually quite close to
remote/local, which matches the typical use case, but is maybe a bit too
suggestive of actually non-existing limitations.

the new f/n suffixes of the -C/-R/-X options clash with pre-existing
options, so direct concatenation of short options is even less practical
than before (some suffixes of -D already clashed), but doing that leads
to unreadable command lines anyway.

as with previous deprecations, all pre-existing command line and config
options keep working, but yield a warning. the state files are silently
upgraded.
This commit is contained in:
Oswald Buddenhagen 2020-07-22 19:44:26 +02:00
parent b514d9ddbc
commit c8f402e43f
10 changed files with 450 additions and 420 deletions

2
NEWS
View File

@ -15,6 +15,8 @@ The IMAP user query can be scripted now.
Added built-in support for macOS Keychain.
The use of Master/Slave terminology has been deprecated.
[1.3.0]
Network timeout handling has been added.

8
TODO
View File

@ -15,7 +15,7 @@ should complain when multiple Channels match the same folders.
propagate folder deletions even when the folders are non-empty.
- verify that "most" of the folders in the Channel are still there.
- refuse to delete unpropagated messages when trashing on the remote side.
- refuse to delete master if it has unpropagated messages. symmetry?
- refuse to delete far side if it has unpropagated messages. symmetry?
add message expiration based on arrival date (message date would be too
unreliable). MaxAge; probably mutually exclusive to MaxMessages.
@ -28,9 +28,9 @@ add support for event notification callbacks.
it would be also possible to report more differentiated exit codes, but
that seems too limiting in the general case.
make it possible to have different mailbox names for Master and Slave in
make it possible to have different mailbox names for far and near side in
Patterns.
- use master:slave for the pattern
- use far:near for the pattern
- for quoting, use more colons: the longest sequence of colons is the
separator
- this makes Groups mostly useless, as they are mostly a workaround for this
@ -63,7 +63,7 @@ use MULTIAPPEND and FETCH with multiple messages.
create dummies describing MIME structure of messages bigger than MaxSize.
flagging the dummy would fetch the real message. possibly remove --renew.
note that all interaction needs to happen on the slave side probably.
note that all interaction needs to happen on the near side probably.
don't SELECT boxes unless really needed; in particular not for appending,
and in write-only mode not before changes are made.

View File

@ -174,21 +174,21 @@ getopt_helper( conffile_t *cfile, int *cops, channel_conf_t *conf )
else if (!strcasecmp( "Flags", arg ))
*cops |= OP_FLAGS;
else if (!strcasecmp( "PullReNew", arg ))
conf->ops[S] |= OP_RENEW;
conf->ops[N] |= OP_RENEW;
else if (!strcasecmp( "PullNew", arg ))
conf->ops[S] |= OP_NEW;
conf->ops[N] |= OP_NEW;
else if (!strcasecmp( "PullDelete", arg ))
conf->ops[S] |= OP_DELETE;
conf->ops[N] |= OP_DELETE;
else if (!strcasecmp( "PullFlags", arg ))
conf->ops[S] |= OP_FLAGS;
conf->ops[N] |= OP_FLAGS;
else if (!strcasecmp( "PushReNew", arg ))
conf->ops[M] |= OP_RENEW;
conf->ops[F] |= OP_RENEW;
else if (!strcasecmp( "PushNew", arg ))
conf->ops[M] |= OP_NEW;
conf->ops[F] |= OP_NEW;
else if (!strcasecmp( "PushDelete", arg ))
conf->ops[M] |= OP_DELETE;
conf->ops[F] |= OP_DELETE;
else if (!strcasecmp( "PushFlags", arg ))
conf->ops[M] |= OP_FLAGS;
conf->ops[F] |= OP_FLAGS;
else if (!strcasecmp( "All", arg ) || !strcasecmp( "Full", arg ))
*cops |= XOP_PULL|XOP_PUSH;
else if (strcasecmp( "None", arg ) && strcasecmp( "Noop", arg )) {
@ -197,7 +197,7 @@ getopt_helper( conffile_t *cfile, int *cops, channel_conf_t *conf )
cfile->err = 1;
}
while ((arg = get_arg( cfile, ARG_OPTIONAL, NULL )));
conf->ops[M] |= XOP_HAVE_TYPE;
conf->ops[F] |= XOP_HAVE_TYPE;
} else if (!strcasecmp( "SyncState", cfile->cmd ))
conf->sync_state = expand_strdup( cfile->val );
else if (!strcasecmp( "CopyArrivalDate", cfile->cmd ))
@ -214,17 +214,23 @@ getopt_helper( conffile_t *cfile, int *cops, channel_conf_t *conf )
do {
if (!strcasecmp( "Both", arg )) {
*cops |= op;
} else if (!strcasecmp( "Master", arg )) {
conf->ops[M] |= op;
} else if (!strcasecmp( "Slave", arg )) {
conf->ops[S] |= op;
} else if (!strcasecmp( "Far", arg )) {
conf->ops[F] |= op;
} else if (!strcasecmp( "Master", arg )) { // Pre-1.4 legacy
conf->ops[F] |= op;
cfile->ms_warn = 1;
} else if (!strcasecmp( "Near", arg )) {
conf->ops[N] |= op;
} else if (!strcasecmp( "Slave", arg )) { // Pre-1.4 legacy
conf->ops[N] |= op;
cfile->ms_warn = 1;
} else if (strcasecmp( "None", arg )) {
error( "%s:%d: invalid %s arg '%s'\n",
cfile->file, cfile->line, boxOps[i].name, arg );
cfile->err = 1;
}
} while ((arg = get_arg( cfile, ARG_OPTIONAL, NULL )));
conf->ops[M] |= op * (XOP_HAVE_EXPUNGE / OP_EXPUNGE);
conf->ops[F] |= op * (XOP_HAVE_EXPUNGE / OP_EXPUNGE);
return 1;
}
}
@ -265,25 +271,25 @@ merge_ops( int cops, int ops[] )
int aops, op;
uint i;
aops = ops[M] | ops[S];
if (ops[M] & XOP_HAVE_TYPE) {
aops = ops[F] | ops[N];
if (ops[F] & XOP_HAVE_TYPE) {
if (aops & OP_MASK_TYPE) {
if (aops & cops & OP_MASK_TYPE) {
cfl:
error( "Conflicting Sync args specified.\n" );
return 1;
}
ops[M] |= cops & OP_MASK_TYPE;
ops[S] |= cops & OP_MASK_TYPE;
ops[F] |= cops & OP_MASK_TYPE;
ops[N] |= cops & OP_MASK_TYPE;
if (cops & XOP_PULL) {
if (ops[S] & OP_MASK_TYPE)
if (ops[N] & OP_MASK_TYPE)
goto cfl;
ops[S] |= OP_MASK_TYPE;
ops[N] |= OP_MASK_TYPE;
}
if (cops & XOP_PUSH) {
if (ops[M] & OP_MASK_TYPE)
if (ops[F] & OP_MASK_TYPE)
goto cfl;
ops[M] |= OP_MASK_TYPE;
ops[F] |= OP_MASK_TYPE;
}
} else if (cops & (OP_MASK_TYPE|XOP_MASK_DIR)) {
if (!(cops & OP_MASK_TYPE))
@ -291,20 +297,20 @@ merge_ops( int cops, int ops[] )
else if (!(cops & XOP_MASK_DIR))
cops |= XOP_PULL|XOP_PUSH;
if (cops & XOP_PULL)
ops[S] |= cops & OP_MASK_TYPE;
ops[N] |= cops & OP_MASK_TYPE;
if (cops & XOP_PUSH)
ops[M] |= cops & OP_MASK_TYPE;
ops[F] |= cops & OP_MASK_TYPE;
}
}
for (i = 0; i < as(boxOps); i++) {
op = boxOps[i].op;
if (ops[M] & (op * (XOP_HAVE_EXPUNGE / OP_EXPUNGE))) {
if (ops[F] & (op * (XOP_HAVE_EXPUNGE / OP_EXPUNGE))) {
if (aops & cops & op) {
error( "Conflicting %s args specified.\n", boxOps[i].name );
return 1;
}
ops[M] |= cops & op;
ops[S] |= cops & op;
ops[F] |= cops & op;
ops[N] |= cops & op;
}
}
return 0;
@ -320,7 +326,7 @@ load_config( const char *where, int pseudo )
string_list_t *chanlist, **chanlistapp;
char *arg, *p;
uint len, max_size;
int cops, gcops, ms, i;
int cops, gcops, fn, i;
char path[_POSIX_PATH_MAX];
char buf[1024];
@ -343,6 +349,7 @@ load_config( const char *where, int pseudo )
cfile.bufl = sizeof(buf) - 1;
cfile.line = 0;
cfile.err = 0;
cfile.ms_warn = 0;
cfile.rest = NULL;
gcops = 0;
@ -384,11 +391,19 @@ load_config( const char *where, int pseudo )
add_string_list( &channel->patterns, arg );
while ((arg = get_arg( &cfile, ARG_OPTIONAL, NULL )));
}
else if (!strcasecmp( "Master", cfile.cmd )) {
ms = M;
else if (!strcasecmp( "Far", cfile.cmd )) {
fn = F;
goto linkst;
} else if (!strcasecmp( "Slave", cfile.cmd )) {
ms = S;
} else if (!strcasecmp( "Master", cfile.cmd )) { // Pre-1.4 legacy
fn = F;
goto olinkst;
} else if (!strcasecmp( "Near", cfile.cmd )) {
fn = N;
goto linkst;
} else if (!strcasecmp( "Slave", cfile.cmd )) { // Pre-1.4 legacy
fn = N;
olinkst:
cfile.ms_warn = 1;
linkst:
if (*cfile.val != ':' || !(p = strchr( cfile.val + 1, ':' ))) {
error( "%s:%d: malformed mailbox spec\n",
@ -399,7 +414,7 @@ load_config( const char *where, int pseudo )
*p = 0;
for (store = stores; store; store = store->next)
if (!strcmp( store->name, cfile.val + 1 )) {
channel->stores[ms] = store;
channel->stores[fn] = store;
goto stpcom;
}
error( "%s:%d: unknown store '%s'\n",
@ -408,17 +423,17 @@ load_config( const char *where, int pseudo )
continue;
stpcom:
if (*++p)
channel->boxes[ms] = nfstrdup( p );
channel->boxes[fn] = nfstrdup( p );
} else if (!getopt_helper( &cfile, &cops, channel )) {
error( "%s:%d: unknown keyword '%s'\n", cfile.file, cfile.line, cfile.cmd );
cfile.err = 1;
}
}
if (!channel->stores[M]) {
error( "channel '%s' refers to no master store\n", channel->name );
if (!channel->stores[F]) {
error( "channel '%s' refers to no far side store\n", channel->name );
cfile.err = 1;
} else if (!channel->stores[S]) {
error( "channel '%s' refers to no slave store\n", channel->name );
} else if (!channel->stores[N]) {
error( "channel '%s' refers to no near side store\n", channel->name );
cfile.err = 1;
} else if (merge_ops( cops, channel->ops ))
cfile.err = 1;
@ -426,7 +441,7 @@ load_config( const char *where, int pseudo )
if (max_size != UINT_MAX) {
if (!max_size)
max_size = UINT_MAX;
channel->stores[M]->max_size = channel->stores[S]->max_size = max_size;
channel->stores[F]->max_size = channel->stores[N]->max_size = max_size;
}
*channelapp = channel;
channelapp = &channel->next;
@ -505,6 +520,8 @@ load_config( const char *where, int pseudo )
}
}
fclose (cfile.fp);
if (cfile.ms_warn)
warn( "Notice: Master/Slave are deprecated; use Far/Near instead.\n" );
cfile.err |= merge_ops( gcops, global_conf.ops );
if (!global_conf.sync_state)
global_conf.sync_state = expand_strdup( "~/." EXE "/" );

View File

@ -32,6 +32,7 @@ typedef struct {
int bufl;
int line;
int err;
int ms_warn;
char *cmd, *val, *rest;
} conffile_t;

View File

@ -78,8 +78,8 @@ PACKAGE " " VERSION " - mailbox synchronizer\n"
" -d, --delete propagate message deletions\n"
" -f, --flags propagate message flag changes\n"
" -N, --renew propagate previously not propagated new messages\n"
" -L, --pull propagate from master to slave\n"
" -H, --push propagate from slave to master\n"
" -L, --pull propagate from far to near side\n"
" -H, --push propagate from near to far side\n"
" -C, --create propagate creations of mailboxes\n"
" -R, --remove propagate deletions of mailboxes\n"
" -X, --expunge expunge deleted messages\n"
@ -92,7 +92,7 @@ PACKAGE " " VERSION " - mailbox synchronizer\n"
"\nIf neither --pull nor --push are specified, both are active.\n"
"If neither --new, --delete, --flags nor --renew are specified, all are active.\n"
"Direction and operation can be concatenated like --pull-new, etc.\n"
"--create, --remove, and --expunge can be suffixed with -master/-slave.\n"
"--create, --remove, and --expunge can be suffixed with -far/-near.\n"
"See the man page for details.\n"
"\nSupported mailbox formats are: IMAP4rev1, Maildir\n"
"\nCompile time options:\n"
@ -205,7 +205,7 @@ stats( void )
if (l > cls)
buf[t][cls - 1] = '~';
}
progress( "\r%s M: %.*s S: %.*s", buf[2], cls, buf[0], cls, buf[1] );
progress( "\r%s F: %.*s N: %.*s", buf[2], cls, buf[0], cls, buf[1] );
}
static int
@ -297,18 +297,18 @@ filter_boxes( string_list_t *boxes, const char *prefix, string_list_t *patterns
static void
merge_actions( channel_conf_t *chan, int ops[], int have, int mask, int def )
{
if (ops[M] & have) {
chan->ops[M] &= ~mask;
chan->ops[M] |= ops[M] & mask;
chan->ops[S] &= ~mask;
chan->ops[S] |= ops[S] & mask;
} else if (!(chan->ops[M] & have)) {
if (global_conf.ops[M] & have) {
chan->ops[M] |= global_conf.ops[M] & mask;
chan->ops[S] |= global_conf.ops[S] & mask;
if (ops[F] & have) {
chan->ops[F] &= ~mask;
chan->ops[F] |= ops[F] & mask;
chan->ops[N] &= ~mask;
chan->ops[N] |= ops[N] & mask;
} else if (!(chan->ops[F] & have)) {
if (global_conf.ops[F] & have) {
chan->ops[F] |= global_conf.ops[F] & mask;
chan->ops[N] |= global_conf.ops[N] & mask;
} else {
chan->ops[M] |= def;
chan->ops[S] |= def;
chan->ops[F] |= def;
chan->ops[N] |= def;
}
}
}
@ -380,7 +380,7 @@ add_named_channel( chan_ent_t ***chanapp, char *channame, int ops[] )
mbox->name = nfstrndup( boxp, boxl );
else
mbox->name = nfstrndup( "INBOX", 5 );
mbox->present[M] = mbox->present[S] = BOX_POSSIBLE;
mbox->present[F] = mbox->present[N] = BOX_POSSIBLE;
mbox->next = NULL;
*mboxapp = mbox;
mboxapp = &mbox->next;
@ -431,7 +431,7 @@ main( int argc, char **argv )
channel_conf_t *chan;
string_list_t *channame;
char *config = NULL, *opt, *ochar;
int oind, cops = 0, op, ops[2] = { 0, 0 }, pseudo = 0;
int oind, cops = 0, op, ops[2] = { 0, 0 }, pseudo = 0, ms_warn = 0;
tzset();
gethostname( Hostname, sizeof(Hostname) );
@ -504,22 +504,26 @@ main( int argc, char **argv )
goto badopt;
DFlags |= op;
} else if (!strcmp( opt, "pull" ))
cops |= XOP_PULL, ops[M] |= XOP_HAVE_TYPE;
cops |= XOP_PULL, ops[F] |= XOP_HAVE_TYPE;
else if (!strcmp( opt, "push" ))
cops |= XOP_PUSH, ops[M] |= XOP_HAVE_TYPE;
cops |= XOP_PUSH, ops[F] |= XOP_HAVE_TYPE;
else if (starts_with( opt, -1, "create", 6 )) {
opt += 6;
op = OP_CREATE|XOP_HAVE_CREATE;
lcop:
if (!*opt)
cops |= op;
else if (!strcmp( opt, "-master" ))
ops[M] |= op;
else if (!strcmp( opt, "-slave" ))
ops[S] |= op;
else if (!strcmp( opt, "-far" ))
ops[F] |= op;
else if (!strcmp( opt, "-master" )) // Pre-1.4 legacy
ops[F] |= op, ms_warn = 1;
else if (!strcmp( opt, "-near" ))
ops[N] |= op;
else if (!strcmp( opt, "-slave" )) // Pre-1.4 legacy
ops[N] |= op, ms_warn = 1;
else
goto badopt;
ops[M] |= op & (XOP_HAVE_CREATE|XOP_HAVE_REMOVE|XOP_HAVE_EXPUNGE);
ops[F] |= op & (XOP_HAVE_CREATE|XOP_HAVE_REMOVE|XOP_HAVE_EXPUNGE);
} else if (starts_with( opt, -1, "remove", 6 )) {
opt += 6;
op = OP_REMOVE|XOP_HAVE_REMOVE;
@ -529,15 +533,15 @@ main( int argc, char **argv )
op = OP_EXPUNGE|XOP_HAVE_EXPUNGE;
goto lcop;
} else if (!strcmp( opt, "no-expunge" ))
ops[M] |= XOP_HAVE_EXPUNGE;
ops[F] |= XOP_HAVE_EXPUNGE;
else if (!strcmp( opt, "no-create" ))
ops[M] |= XOP_HAVE_CREATE;
ops[F] |= XOP_HAVE_CREATE;
else if (!strcmp( opt, "no-remove" ))
ops[M] |= XOP_HAVE_REMOVE;
ops[F] |= XOP_HAVE_REMOVE;
else if (!strcmp( opt, "full" ))
ops[M] |= XOP_HAVE_TYPE|XOP_PULL|XOP_PUSH;
ops[F] |= XOP_HAVE_TYPE|XOP_PULL|XOP_PUSH;
else if (!strcmp( opt, "noop" ))
ops[M] |= XOP_HAVE_TYPE;
ops[F] |= XOP_HAVE_TYPE;
else if (starts_with( opt, -1, "pull", 4 )) {
op = XOP_PULL;
lcac:
@ -569,11 +573,11 @@ main( int argc, char **argv )
return 1;
}
switch (op & XOP_MASK_DIR) {
case XOP_PULL: ops[S] |= op & OP_MASK_TYPE; break;
case XOP_PUSH: ops[M] |= op & OP_MASK_TYPE; break;
case XOP_PULL: ops[N] |= op & OP_MASK_TYPE; break;
case XOP_PUSH: ops[F] |= op & OP_MASK_TYPE; break;
default: cops |= op; break;
}
ops[M] |= XOP_HAVE_TYPE;
ops[F] |= XOP_HAVE_TYPE;
}
continue;
}
@ -604,15 +608,19 @@ main( int argc, char **argv )
case 'C':
op = OP_CREATE|XOP_HAVE_CREATE;
cop:
if (*ochar == 'm')
ops[M] |= op, ochar++;
else if (*ochar == 's')
ops[S] |= op, ochar++;
if (*ochar == 'f')
ops[F] |= op, ochar++;
else if (*ochar == 'm') // Pre-1.4 legacy
ops[F] |= op, ms_warn = 1, ochar++;
else if (*ochar == 'n')
ops[N] |= op, ochar++;
else if (*ochar == 's') // Pre-1.4 legacy
ops[N] |= op, ms_warn = 1, ochar++;
else if (*ochar == '-')
ochar++;
else
cops |= op;
ops[M] |= op & (XOP_HAVE_CREATE|XOP_HAVE_REMOVE|XOP_HAVE_EXPUNGE);
ops[F] |= op & (XOP_HAVE_CREATE|XOP_HAVE_REMOVE|XOP_HAVE_EXPUNGE);
break;
case 'R':
op = OP_REMOVE|XOP_HAVE_REMOVE;
@ -624,7 +632,7 @@ main( int argc, char **argv )
cops |= XOP_PULL|XOP_PUSH;
FALLTHROUGH
case '0':
ops[M] |= XOP_HAVE_TYPE;
ops[F] |= XOP_HAVE_TYPE;
break;
case 'n':
case 'd':
@ -647,13 +655,13 @@ main( int argc, char **argv )
}
if (op & OP_MASK_TYPE)
switch (op & XOP_MASK_DIR) {
case XOP_PULL: ops[S] |= op & OP_MASK_TYPE; break;
case XOP_PUSH: ops[M] |= op & OP_MASK_TYPE; break;
case XOP_PULL: ops[N] |= op & OP_MASK_TYPE; break;
case XOP_PUSH: ops[F] |= op & OP_MASK_TYPE; break;
default: cops |= op; break;
}
else
cops |= op;
ops[M] |= XOP_HAVE_TYPE;
ops[F] |= XOP_HAVE_TYPE;
break;
case 'L':
op = XOP_PULL;
@ -722,6 +730,8 @@ main( int argc, char **argv )
return 1;
}
}
if (ms_warn)
warn( "Notice: -master/-slave/m/s suffixes are deprecated; use -far/-near/f/n instead.\n" );
if (!(DFlags & (QUIET | DEBUG_ALL)) && isatty( 1 ))
DFlags |= PROGRESS;
@ -839,17 +849,17 @@ sync_chans( main_vars_t *mvars, int ent )
int st = mvars->chan->stores[t]->driver->get_fail_state( mvars->chan->stores[t] );
if (st != FAIL_TEMP) {
info( "Skipping due to %sfailed %s store %s.\n",
(st == FAIL_WAIT) ? "temporarily " : "", str_ms[t], mvars->chan->stores[t]->name );
(st == FAIL_WAIT) ? "temporarily " : "", str_fn[t], mvars->chan->stores[t]->name );
mvars->skip = 1;
}
}
if (mvars->skip)
goto next2;
mvars->state[M] = mvars->state[S] = ST_FRESH;
if ((DFlags & DEBUG_DRV) || (mvars->chan->stores[M]->driver->get_caps( NULL ) & mvars->chan->stores[S]->driver->get_caps( NULL ) & DRV_VERBOSE))
labels[M] = "M: ", labels[S] = "S: ";
mvars->state[F] = mvars->state[N] = ST_FRESH;
if ((DFlags & DEBUG_DRV) || (mvars->chan->stores[F]->driver->get_caps( NULL ) & mvars->chan->stores[N]->driver->get_caps( NULL ) & DRV_VERBOSE))
labels[F] = "F: ", labels[N] = "N: ";
else
labels[M] = labels[S] = "";
labels[F] = labels[N] = "";
for (t = 0; t < 2; t++) {
driver_t *drv = mvars->chan->stores[t]->driver;
store_t *ctx = drv->alloc_store( mvars->chan->stores[t], labels[t] );
@ -862,7 +872,7 @@ sync_chans( main_vars_t *mvars, int ent )
drv->set_bad_callback( ctx, store_bad, AUX );
}
for (t = 0; ; t++) {
info( "Opening %s store %s...\n", str_ms[t], mvars->chan->stores[t]->name );
info( "Opening %s store %s...\n", str_fn[t], mvars->chan->stores[t]->name );
mvars->drv[t]->connect_store( mvars->ctx[t], store_connected, AUX );
if (t || mvars->skip)
break;
@ -872,35 +882,35 @@ sync_chans( main_vars_t *mvars, int ent )
opened:
if (mvars->skip)
goto next;
if (mvars->state[M] != ST_OPEN || mvars->state[S] != ST_OPEN)
if (mvars->state[F] != ST_OPEN || mvars->state[N] != ST_OPEN)
return;
if (!mvars->chanptr->boxlist && mvars->chan->patterns) {
mvars->chanptr->boxlist = 2;
boxes[M] = filter_boxes( mvars->boxes[M], mvars->chan->boxes[M], mvars->chan->patterns );
boxes[S] = filter_boxes( mvars->boxes[S], mvars->chan->boxes[S], mvars->chan->patterns );
boxes[F] = filter_boxes( mvars->boxes[F], mvars->chan->boxes[F], mvars->chan->patterns );
boxes[N] = filter_boxes( mvars->boxes[N], mvars->chan->boxes[N], mvars->chan->patterns );
mboxapp = &mvars->chanptr->boxes;
for (mb = sb = 0; ; ) {
char *mname = boxes[M] ? boxes[M][mb] : NULL;
char *sname = boxes[S] ? boxes[S][sb] : NULL;
char *mname = boxes[F] ? boxes[F][mb] : NULL;
char *sname = boxes[N] ? boxes[N][sb] : NULL;
if (!mname && !sname)
break;
mbox = nfmalloc( sizeof(*mbox) );
if (!(cmp = !mname - !sname) && !(cmp = cmp_box_names( &mname, &sname ))) {
mbox->name = mname;
free( sname );
mbox->present[M] = mbox->present[S] = BOX_PRESENT;
mbox->present[F] = mbox->present[N] = BOX_PRESENT;
mb++;
sb++;
} else if (cmp < 0) {
mbox->name = mname;
mbox->present[M] = BOX_PRESENT;
mbox->present[S] = (!mb && !strcmp( mbox->name, "INBOX" )) ? BOX_PRESENT : BOX_ABSENT;
mbox->present[F] = BOX_PRESENT;
mbox->present[N] = (!mb && !strcmp( mbox->name, "INBOX" )) ? BOX_PRESENT : BOX_ABSENT;
mb++;
} else {
mbox->name = sname;
mbox->present[M] = (!sb && !strcmp( mbox->name, "INBOX" )) ? BOX_PRESENT : BOX_ABSENT;
mbox->present[S] = BOX_PRESENT;
mbox->present[F] = (!sb && !strcmp( mbox->name, "INBOX" )) ? BOX_PRESENT : BOX_ABSENT;
mbox->present[N] = BOX_PRESENT;
sb++;
}
mbox->next = NULL;
@ -908,8 +918,8 @@ sync_chans( main_vars_t *mvars, int ent )
mboxapp = &mbox->next;
boxes_total++;
}
free( boxes[M] );
free( boxes[S] );
free( boxes[F] );
free( boxes[N] );
if (!mvars->list)
stats();
}
@ -938,7 +948,7 @@ sync_chans( main_vars_t *mvars, int ent )
if (!mvars->skip)
goto syncml;
} else
printf( "%s <=> %s\n", nz( mvars->chan->boxes[M], "INBOX" ), nz( mvars->chan->boxes[S], "INBOX" ) );
printf( "%s <=> %s\n", nz( mvars->chan->boxes[F], "INBOX" ), nz( mvars->chan->boxes[N], "INBOX" ) );
}
next:
@ -956,7 +966,7 @@ sync_chans( main_vars_t *mvars, int ent )
}
}
mvars->cben = 1;
if (mvars->state[M] != ST_CLOSED || mvars->state[S] != ST_CLOSED) {
if (mvars->state[F] != ST_CLOSED || mvars->state[N] != ST_CLOSED) {
mvars->skip = 1;
return;
}
@ -1067,7 +1077,7 @@ store_listed( int sts, string_list_t *boxes, void *aux )
}
}
if (mvars->ctx[t]->conf->map_inbox) {
debug( "adding mapped inbox to %s: %s\n", str_ms[t], mvars->ctx[t]->conf->map_inbox );
debug( "adding mapped inbox to %s store: %s\n", str_fn[t], mvars->ctx[t]->conf->map_inbox );
add_string_list( &mvars->boxes[t], mvars->ctx[t]->conf->map_inbox );
}
break;
@ -1082,19 +1092,19 @@ store_listed( int sts, string_list_t *boxes, void *aux )
static int
sync_listed_boxes( main_vars_t *mvars, box_ent_t *mbox )
{
if (mvars->chan->boxes[M] || mvars->chan->boxes[S]) {
const char *mpfx = nz( mvars->chan->boxes[M], "" );
const char *spfx = nz( mvars->chan->boxes[S], "" );
if (mvars->chan->boxes[F] || mvars->chan->boxes[N]) {
const char *mpfx = nz( mvars->chan->boxes[F], "" );
const char *spfx = nz( mvars->chan->boxes[N], "" );
if (!mvars->list) {
nfasprintf( &mvars->names[M], "%s%s", mpfx, mbox->name );
nfasprintf( &mvars->names[S], "%s%s", spfx, mbox->name );
nfasprintf( &mvars->names[F], "%s%s", mpfx, mbox->name );
nfasprintf( &mvars->names[N], "%s%s", spfx, mbox->name );
sync_boxes( mvars->ctx, (const char * const *)mvars->names, mbox->present, mvars->chan, done_sync_2_dyn, mvars );
return 1;
}
printf( "%s%s <=> %s%s\n", mpfx, mbox->name, spfx, mbox->name );
} else {
if (!mvars->list) {
mvars->names[M] = mvars->names[S] = mbox->name;
mvars->names[F] = mvars->names[N] = mbox->name;
sync_boxes( mvars->ctx, (const char * const *)mvars->names, mbox->present, mvars->chan, done_sync, mvars );
return 1;
}
@ -1108,8 +1118,8 @@ done_sync_2_dyn( int sts, void *aux )
{
main_vars_t *mvars = (main_vars_t *)aux;
free( mvars->names[M] );
free( mvars->names[S] );
free( mvars->names[F] );
free( mvars->names[N] );
done_sync( sts, aux );
}
@ -1123,11 +1133,11 @@ done_sync( int sts, void *aux )
stats();
if (sts) {
mvars->ret = 1;
if (sts & (SYNC_BAD(M) | SYNC_BAD(S))) {
if (sts & SYNC_BAD(M))
mvars->state[M] = ST_CLOSED;
if (sts & SYNC_BAD(S))
mvars->state[S] = ST_CLOSED;
if (sts & (SYNC_BAD(F) | SYNC_BAD(N))) {
if (sts & SYNC_BAD(F))
mvars->state[F] = ST_CLOSED;
if (sts & SYNC_BAD(N))
mvars->state[N] = ST_CLOSED;
mvars->skip = 1;
}
}

View File

@ -57,13 +57,13 @@ line are ignored.
Don't synchronize anything, but list all mailboxes in the selected channels
and exit.
.TP
\fB-C\fR[\fBm\fR][\fBs\fR], \fB--create\fR[\fB-master\fR|\fB-slave\fR]
\fB-C\fR[\fBf\fR][\fBn\fR], \fB--create\fR[\fB-far\fR|\fB-near\fR]
Override any \fBCreate\fR options from the config file. See below.
.TP
\fB-R\fR[\fBm\fR][\fBs\fR], \fB--remove\fR[\fB-master\fR|\fB-slave\fR]
\fB-R\fR[\fBf\fR][\fBn\fR], \fB--remove\fR[\fB-far\fR|\fB-near\fR]
Override any \fBRemove\fR options from the config file. See below.
.TP
\fB-X\fR[\fBm\fR][\fBs\fR], \fB--expunge\fR[\fB-master\fR|\fB-slave\fR]
\fB-X\fR[\fBf\fR][\fBn\fR], \fB--expunge\fR[\fB-far\fR|\fB-near\fR]
Override any \fBExpunge\fR options from the config file. See below.
.TP
{\fB-n\fR|\fB-N\fR|\fB-d\fR|\fB-f\fR|\fB-0\fR|\fB-F\fR},\
@ -174,7 +174,7 @@ If \fIsize\fR is 0, the maximum message size is \fBunlimited\fR.
\fBMapInbox\fR \fImailbox\fR
Create a virtual mailbox (relative to \fBPath\fR) which aliases
the \fBINBOX\fR. Makes sense in conjunction with \fBPatterns\fR in the
Channels section, though with a Maildir slave, you probably want to
Channels section, though with a Maildir near side, you probably want to
place \fBInbox\fR under \fBPath\fR instead.
This virtual mailbox does not support subfolders.
.
@ -511,8 +511,8 @@ This option make sense only in conjunction with \fBPatterns\fR.
Define the Channel \fIname\fR, opening a section for its parameters.
.
.TP
{\fBMaster\fR|\fBSlave\fR} \fB:\fIstore\fB:\fR[\fImailbox\fR]
Specify the Master resp. Slave Store to be connected by this Channel.
{\fBFar\fR|\fBNear\fR} \fB:\fIstore\fB:\fR[\fImailbox\fR]
Specify the far resp. near side Store to be connected by this Channel.
If \fBPatterns\fR are specified, \fImailbox\fR is interpreted as a
prefix which is not matched against the patterns, and which is not
affected by mailbox list overrides.
@ -521,8 +521,8 @@ Otherwise, if \fImailbox\fR is omitted, \fBINBOX\fR is assumed.
.TP
\fBPattern\fR[\fBs\fR] [\fB!\fR]\fIpattern\fR ...
Instead of synchronizing only one mailbox pair, synchronize all mailboxes
that match the \fIpattern\fR(s). The mailbox names are the same on both
Master and Slave. Patterns are IMAP4 patterns, i.e., \fB*\fR matches anything
that match the \fIpattern\fR(s). The mailbox names are the same on the far
and near side. Patterns are IMAP4 patterns, i.e., \fB*\fR matches anything
and \fB%\fR matches anything up to the next hierarchy delimiter. Prepending
\fB!\fR to a pattern makes it an exclusion. Multiple patterns can be specified
(either by supplying multiple arguments or by using \fBPattern\fR multiple
@ -539,12 +539,12 @@ Example: "\fBPatterns\fR\ \fI%\ !Trash\fR"
.TP
\fBMaxSize\fR \fIsize\fR[\fBk\fR|\fBm\fR][\fBb\fR]
Analogous to the homonymous option in the Stores section, but applies equally
to Master and Slave. Note that this actually modifies the Stores, so take care
to Far and Near. Note that this actually modifies the Stores, so take care
not to provide conflicting settings if you use the Stores in multiple Channels.
.
.TP
\fBMaxMessages\fR \fIcount\fR
Sets the maximum number of messages to keep in each Slave mailbox.
Sets the maximum number of messages to keep in each near side mailbox.
This is useful for mailboxes where you keep a complete archive on the server,
but want to mirror only the last messages (for instance, for mailing lists).
The messages that were the first to arrive in the mailbox (independently of
@ -568,9 +568,9 @@ case you need to enable this option.
\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
\fBPull\fR - propagate changes from Master to Slave.
\fBPull\fR - propagate changes from far to near side.
.br
\fBPush\fR - propagate changes from Slave to Master.
\fBPush\fR - propagate changes from near to far side.
.br
\fBNew\fR - propagate newly appeared messages.
.br
@ -602,10 +602,10 @@ 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\ \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 far side to the near side,
"\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.
from the near side to the far side.
.br
In the second style, direction flags are concatenated with type flags; every
compound flag immediately asserts a cell in the matrix. In addition to at least
@ -613,22 +613,22 @@ 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\ \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.
message arrivals and deletions from the far side to the near side and any
changes from the near side to the far side.
Note that it is not allowed to assert a cell in two ways, e.g.
"\fBSync\fR\ \fBPullNew\fR\ \fBPull\fR" and
"\fBSync\fR\ \fBPullNew\fR\ \fBDelete\fR\ \fBPush\fR" induce error messages.
.
.TP
\fBCreate\fR {\fBNone\fR|\fBMaster\fR|\fBSlave\fR|\fBBoth\fR}
Automatically create missing mailboxes [on the Master/Slave].
\fBCreate\fR {\fBNone\fR|\fBFar\fR|\fBNear\fR|\fBBoth\fR}
Automatically create missing mailboxes [on the far/near side].
Otherwise print an error message and skip that mailbox pair if a mailbox
and the corresponding sync state does not exist.
(Global default: \fBNone\fR)
.
.TP
\fBRemove\fR {\fBNone\fR|\fBMaster\fR|\fBSlave\fR|\fBBoth\fR}
Propagate mailbox deletions [to the Master/Slave].
\fBRemove\fR {\fBNone\fR|\fBFar\fR|\fBNear\fR|\fBBoth\fR}
Propagate mailbox deletions [to the far/near side].
Otherwise print an error message and skip that mailbox pair if a mailbox
does not exist but the corresponding sync state does.
.br
@ -640,8 +640,8 @@ Note that for safety, non-empty mailboxes are never deleted.
(Global default: \fBNone\fR)
.
.TP
\fBExpunge\fR {\fBNone\fR|\fBMaster\fR|\fBSlave\fR|\fBBoth\fR}
Permanently remove all messages [on the Master/Slave] marked for deletion.
\fBExpunge\fR {\fBNone\fR|\fBFar\fR|\fBNear\fR|\fBBoth\fR}
Permanently remove all messages [on the far/near side] marked for deletion.
See \fBRECOMMENDATIONS\fR below.
(Global default: \fBNone\fR)
.
@ -666,15 +666,15 @@ which in turn are overridden by command line switches.
\fBSyncState\fR {\fB*\fR|\fIpath\fR}
Set the location of this Channel's synchronization state files.
\fB*\fR means that the state should be saved in a file named .mbsyncstate
in the Slave mailbox itself; this has the advantage that you do not need
in the near side mailbox itself; this has the advantage that you do not need
to handle the state file separately if you delete the mailbox, but it works
only with Maildir mailboxes, obviously.
Otherwise this is interpreted as a string to prepend to the Slave mailbox
Otherwise this is interpreted as a string to prepend to the near side mailbox
name to make up a complete path.
.br
This option can be used outside any section for a global effect. In this case
the appended string is made up according to the pattern
\fB:\fImaster\fB:\fImaster-box\fB_:\fIslave\fB:\fIslave-box\fR
\fB:\fIfar-store\fB:\fIfar-box\fB_:\fInear-store\fB:\fInear-box\fR
(see also \fBFieldDelimiter\fR below).
.br
(Global default: \fI~/.mbsync/\fR).
@ -734,11 +734,11 @@ If \fBmbsync\fR's output is connected to a console, it will print progress
counters by default. The output will look like this:
.P
.in +4
C: 1/2 B: 3/4 M: +13/13 *23/42 #0/0 S: +0/7 *0/0 #0/0
C: 1/2 B: 3/4 F: +13/13 *23/42 #0/0 N: +0/7 *0/0 #0/0
.in -4
.P
This represents the cumulative progress over channels, boxes, and messages
affected on master and slave, respectively.
affected on the far and near side, respectively.
The message counts represent added messages, messages with updated flags,
and trashed messages, respectively.
No attempt is made to calculate the totals in advance, so they grow over

View File

@ -23,9 +23,9 @@ Pass xxxxxxxx
#PassCmd "echo -ne 'GET myIsp\\tpassword' | pwmc datafile"
Channel work
Master :work:
Slave :local:work
Expunge Slave
Far :work:
Near :local:work
Expunge Near
Sync PullNew Push
@ -35,8 +35,8 @@ Port 6789
RequireSSL no
Channel personal
Master :personal:
Slave :local:personal
Far :personal:
Near :local:personal
Expunge Both
MaxMessages 150
MaxSize 200k
@ -45,8 +45,8 @@ IMAPStore remote
Tunnel "ssh -q host.remote.com /usr/sbin/imapd"
Channel remote
Master :remote:
Slave :local:remote
Far :remote:
Near :local:remote
Group boxes
@ -65,8 +65,8 @@ RequireSSL no
UseTLSv1 no
Channel rst
Master :st1:somebox
Slave :st2:
Far :st1:somebox
Near :st2:
IMAPAccount server
@ -84,14 +84,14 @@ Path ~/Maildir/
SubFolders Verbatim
Channel o2o
Master :server:
Slave :mirror:
Far :server:
Near :mirror:
Patterns %
Group partial o2o:inbox,sent-mail,foobar
# INBOX => server, INBOX.foo => server.foo, etc.
Channel inbox
Master :server:INBOX
Slave :mirror:server
Far :server:INBOX
Near :mirror:server
Patterns *

View File

@ -37,9 +37,9 @@ sub test($$$@);
################################################################################
# Format of the test defs: [ master, slave, state ]
# master/slave: [ maxuid, { seq, uid, flags }... ]
# state: [ MaxPulledUid, MaxExpiredMasterUid, MaxPushedUid, { muid, suid, flags }... ]
# Format of the test defs: [ far, near, state ]
# far/near: [ maxuid, { seq, uid, flags }... ]
# state: [ MaxPulledUid, MaxExpiredFarUid, MaxPushedUid, { muid, suid, flags }... ]
use enum qw(:=1 A..Z);
sub mn($) { chr(64 + shift) }
@ -78,7 +78,7 @@ my @X02 = (
);
test("full + expunge both", \@x01, \@X02, @O02);
my @O03 = ("", "", "Expunge Slave\n");
my @O03 = ("", "", "Expunge Near\n");
#show("01", "03", "03");
my @X03 = (
[ 10,
@ -88,7 +88,7 @@ my @X03 = (
[ 9, 0, 9,
1, 1, "F", 2, 2, "F", 3, 3, "FS", 4, 4, "", 5, 0, "T", 6, 0, "", 7, 0, "T", 10, 9, "", 9, 10, "" ],
);
test("full + expunge slave", \@x01, \@X03, @O03);
test("full + expunge near side", \@x01, \@X03, @O03);
my @O04 = ("", "", "Sync Pull\n");
#show("01", "04", "04");
@ -183,7 +183,7 @@ my @X22 = (
[ 2, 0, 1,
3, 1, "", 1, 2, "", 2, 0, "^" ],
);
test("slave max size", \@X11, \@X22, @O22);
test("near side max size", \@X11, \@X22, @O22);
# expiration tests
@ -258,7 +258,7 @@ sub qm($)
return $_;
}
# $master, $slave, $channel
# $far, $near, $channel
sub writecfg($$$)
{
open(FILE, ">", ".mbsyncrc") or
@ -266,17 +266,17 @@ sub writecfg($$$)
print FILE
"FSync no
MaildirStore master
MaildirStore far
Path ./
Inbox ./master
Inbox ./far
".shift()."
MaildirStore slave
MaildirStore near
Path ./
Inbox ./slave
Inbox ./near
".shift()."
Channel test
Master :master:
Slave :slave:
Far :far:
Near :near:
SyncState *
".shift();
close FILE;
@ -371,8 +371,8 @@ sub showbox($)
# $filename
# Output:
# [ maxuid[M], mmaxxuid, maxuid[S],
# uid[M], uid[S], "flags", ... ],
# [ maxuid[F], maxxfuid, maxuid[N],
# uid[F], uid[N], "flags", ... ],
sub showstate($)
{
my ($fn) = @_;
@ -400,7 +400,7 @@ sub showstate($)
return;
}
my @T = ($hdr{'MaxPulledUid'} // "missing",
$hdr{'MaxExpiredMasterUid'} // "0",
$hdr{'MaxExpiredFarUid'} // "0",
$hdr{'MaxPushedUid'} // "missing");
for (@ls) {
/^(\d+) (\d+) (.*)$/;
@ -414,8 +414,8 @@ sub showchan($)
{
my ($fn) = @_;
showbox("master");
showbox("slave");
showbox("far");
showbox("near");
showstate($fn);
}
@ -428,17 +428,17 @@ sub show($$$)
eval "\@sfx = \@O$sfxn";
mkchan($sp[0], $sp[1], @{ $sp[2] });
print "my \@x$sx = (\n";
showchan("slave/.mbsyncstate");
showchan("near/.mbsyncstate");
print ");\n";
&writecfg(@sfx);
runsync("", "");
killcfg();
print "my \@X$tx = (\n";
showchan("slave/.mbsyncstate");
showchan("near/.mbsyncstate");
print ");\n";
print "test(\"\", \\\@x$sx, \\\@X$tx, \@O$sfxn);\n\n";
rmtree "slave";
rmtree "master";
rmtree "near";
rmtree "far";
}
# $boxname, $maxuid, @msgs
@ -462,16 +462,16 @@ sub mkbox($$@)
}
}
# \@master, \@slave, @syncstate
# \@far, \@near, @syncstate
sub mkchan($$@)
{
my ($m, $s, @t) = @_;
&mkbox("master", @{ $m });
&mkbox("slave", @{ $s });
open(FILE, ">", "slave/.mbsyncstate") or
&mkbox("far", @{ $m });
&mkbox("near", @{ $s });
open(FILE, ">", "near/.mbsyncstate") or
die "Cannot create sync state.\n";
print FILE "MasterUidValidity 1\nMaxPulledUid ".shift(@t)."\n".
"SlaveUidValidity 1\nMaxExpiredMasterUid ".shift(@t)."\nMaxPushedUid ".shift(@t)."\n\n";
print FILE "FarUidValidity 1\nMaxPulledUid ".shift(@t)."\n".
"NearUidValidity 1\nMaxExpiredFarUid ".shift(@t)."\nMaxPushedUid ".shift(@t)."\n\n";
while (@t) {
print FILE shift(@t)." ".shift(@t)." ".shift(@t)."\n";
}
@ -514,13 +514,13 @@ sub ckbox($$@)
# $filename, @syncstate
sub ckstate($@)
{
my ($fn, $mmaxuid, $mmaxxuid, $smaxuid, @T) = @_;
my ($fn, $fmaxuid, $maxxfuid, $nmaxuid, @T) = @_;
my %hdr;
$hdr{'MasterUidValidity'} = "1";
$hdr{'SlaveUidValidity'} = "1";
$hdr{'MaxPulledUid'} = $mmaxuid;
$hdr{'MaxPushedUid'} = $smaxuid;
$hdr{'MaxExpiredMasterUid'} = $mmaxxuid if ($mmaxxuid ne 0);
$hdr{'FarUidValidity'} = "1";
$hdr{'NearUidValidity'} = "1";
$hdr{'MaxPulledUid'} = $fmaxuid;
$hdr{'MaxPushedUid'} = $nmaxuid;
$hdr{'MaxExpiredFarUid'} = $maxxfuid if ($maxxfuid ne 0);
open(FILE, "<", $fn) or die "Cannot read sync state $fn.\n";
chomp(my @ls = <FILE>);
close FILE;
@ -573,8 +573,8 @@ sub ckchan($$)
{
my ($F, $cs) = @_;
my $rslt = ckstate($F, @{ $$cs[2] });
$rslt |= &ckbox("master", @{ $$cs[0] });
$rslt |= &ckbox("slave", @{ $$cs[1] });
$rslt |= &ckbox("far", @{ $$cs[0] });
$rslt |= &ckbox("near", @{ $$cs[1] });
return $rslt;
}
@ -618,8 +618,8 @@ sub printchan($)
{
my ($cs) = @_;
&printbox("master", @{ $$cs[0] });
&printbox("slave", @{ $$cs[1] });
&printbox("far", @{ $$cs[0] });
&printbox("near", @{ $$cs[1] });
printstate(@{ $$cs[2] });
}
@ -645,7 +645,7 @@ sub test($$$@)
mkchan($$sx[0], $$sx[1], @{ $$sx[2] });
my ($xc, @ret) = runsync("-J", "1-initial.log");
if ($xc || ckchan("slave/.mbsyncstate.new", $tx)) {
if ($xc || ckchan("near/.mbsyncstate.new", $tx)) {
print "Input:\n";
printchan($sx);
print "Options:\n";
@ -654,16 +654,16 @@ sub test($$$@)
print "Expected result:\n";
printchan($tx);
print "Actual result:\n";
showchan("slave/.mbsyncstate.new");
showchan("near/.mbsyncstate.new");
}
print "Debug output:\n";
print @ret;
exit 1;
}
my @nj = readfile("slave/.mbsyncstate.journal");
my @nj = readfile("near/.mbsyncstate.journal");
my ($jxc, @jret) = runsync("-0 --no-expunge", "2-replay.log");
if ($jxc || ckstate("slave/.mbsyncstate", @{ $$tx[2] })) {
if ($jxc || ckstate("near/.mbsyncstate", @{ $$tx[2] })) {
print "Journal replay failed.\n";
print "Options:\n";
print " [ ".join(", ", map('"'.qm($_).'"', @sfx))." ], [ \"-0\", \"--no-expunge\" ]\n";
@ -674,7 +674,7 @@ sub test($$$@)
print "Expected New State:\n";
printstate(@{ $$tx[2] });
print "New State:\n";
showstate("slave/.mbsyncstate");
showstate("near/.mbsyncstate");
}
print "Debug output:\n";
print @jret;
@ -682,7 +682,7 @@ sub test($$$@)
}
my ($ixc, @iret) = runsync("", "3-verify.log");
if ($ixc || ckchan("slave/.mbsyncstate", $tx)) {
if ($ixc || ckchan("near/.mbsyncstate", $tx)) {
print "Idempotence verification run failed.\n";
print "Input == Expected result:\n";
printchan($tx);
@ -690,15 +690,15 @@ sub test($$$@)
print " [ ".join(", ", map('"'.qm($_).'"', @sfx))." ]\n";
if (!$ixc) {
print "Actual result:\n";
showchan("slave/.mbsyncstate");
showchan("near/.mbsyncstate");
}
print "Debug output:\n";
print @iret;
exit 1;
}
rmtree "slave";
rmtree "master";
rmtree "near";
rmtree "far";
my $njl = (@nj - 1) * 2;
for (my $l = 2; $l < $njl; $l++) {
@ -713,28 +713,28 @@ sub test($$$@)
}
($nxc, @nret) = runsync("-J", "5-resume.log");
if ($nxc || ckchan("slave/.mbsyncstate.new", $tx)) {
if ($nxc || ckchan("near/.mbsyncstate.new", $tx)) {
print "Resuming from step $l/$njl failed.\n";
print "Input:\n";
printchan($sx);
print "Options:\n";
print " [ ".join(", ", map('"'.qm($_).'"', @sfx))." ]\n";
my @nnj = readfile("slave/.mbsyncstate.journal");
my @nnj = readfile("near/.mbsyncstate.journal");
print "Journal:\n".join("", @nnj[0..($l / 2 - 1)])."-------\n".join("", @nnj[($l / 2)..$#nnj])."\n";
print "Full journal:\n".join("", @nj)."\n";
if (!$nxc) {
print "Expected result:\n";
printchan($tx);
print "Actual result:\n";
showchan("slave/.mbsyncstate.new");
showchan("near/.mbsyncstate.new");
}
print "Debug output:\n";
print @nret;
exit 1;
}
rmtree "slave";
rmtree "master";
rmtree "near";
rmtree "far";
}
killcfg();

File diff suppressed because it is too large Load Diff

View File

@ -25,8 +25,8 @@
#include "driver.h"
#define M 0 /* master */
#define S 1 /* slave */
#define F 0 // far side
#define N 1 // near side
#define OP_NEW (1<<0)
#define OP_RENEW (1<<1)
@ -52,7 +52,7 @@ typedef struct channel_conf {
char *sync_state;
string_list_t *patterns;
int ops[2];
int max_messages; /* for slave only */
int max_messages; // For near side only.
signed char expire_unread;
char use_internal_date;
} channel_conf_t;
@ -67,11 +67,11 @@ extern channel_conf_t global_conf;
extern channel_conf_t *channels;
extern group_conf_t *groups;
extern const char *str_ms[2], *str_hl[2];
extern const char *str_fn[2], *str_hl[2];
#define SYNC_OK 0 /* assumed to be 0 */
#define SYNC_FAIL 1
#define SYNC_BAD(ms) (4<<(ms))
#define SYNC_BAD(fn) (4<<(fn))
#define SYNC_NOGOOD 16 /* internal */
#define SYNC_CANCELED 32 /* internal */