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:
parent
b514d9ddbc
commit
c8f402e43f
2
NEWS
2
NEWS
|
@ -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
8
TODO
|
@ -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.
|
||||
|
|
95
src/config.c
95
src/config.c
|
@ -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 "/" );
|
||||
|
|
|
@ -32,6 +32,7 @@ typedef struct {
|
|||
int bufl;
|
||||
int line;
|
||||
int err;
|
||||
int ms_warn;
|
||||
char *cmd, *val, *rest;
|
||||
} conffile_t;
|
||||
|
||||
|
|
160
src/main.c
160
src/main.c
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
54
src/mbsync.1
54
src/mbsync.1
|
@ -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
|
||||
|
|
|
@ -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 *
|
||||
|
|
104
src/run-tests.pl
104
src/run-tests.pl
|
@ -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();
|
||||
|
|
410
src/sync.c
410
src/sync.c
File diff suppressed because it is too large
Load Diff
10
src/sync.h
10
src/sync.h
|
@ -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 */
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user