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.
|
Added built-in support for macOS Keychain.
|
||||||
|
|
||||||
|
The use of Master/Slave terminology has been deprecated.
|
||||||
|
|
||||||
[1.3.0]
|
[1.3.0]
|
||||||
|
|
||||||
Network timeout handling has been added.
|
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.
|
propagate folder deletions even when the folders are non-empty.
|
||||||
- verify that "most" of the folders in the Channel are still there.
|
- 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 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
|
add message expiration based on arrival date (message date would be too
|
||||||
unreliable). MaxAge; probably mutually exclusive to MaxMessages.
|
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
|
it would be also possible to report more differentiated exit codes, but
|
||||||
that seems too limiting in the general case.
|
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.
|
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
|
- for quoting, use more colons: the longest sequence of colons is the
|
||||||
separator
|
separator
|
||||||
- this makes Groups mostly useless, as they are mostly a workaround for this
|
- 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.
|
create dummies describing MIME structure of messages bigger than MaxSize.
|
||||||
flagging the dummy would fetch the real message. possibly remove --renew.
|
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,
|
don't SELECT boxes unless really needed; in particular not for appending,
|
||||||
and in write-only mode not before changes are made.
|
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 ))
|
else if (!strcasecmp( "Flags", arg ))
|
||||||
*cops |= OP_FLAGS;
|
*cops |= OP_FLAGS;
|
||||||
else if (!strcasecmp( "PullReNew", arg ))
|
else if (!strcasecmp( "PullReNew", arg ))
|
||||||
conf->ops[S] |= OP_RENEW;
|
conf->ops[N] |= OP_RENEW;
|
||||||
else if (!strcasecmp( "PullNew", arg ))
|
else if (!strcasecmp( "PullNew", arg ))
|
||||||
conf->ops[S] |= OP_NEW;
|
conf->ops[N] |= OP_NEW;
|
||||||
else if (!strcasecmp( "PullDelete", arg ))
|
else if (!strcasecmp( "PullDelete", arg ))
|
||||||
conf->ops[S] |= OP_DELETE;
|
conf->ops[N] |= OP_DELETE;
|
||||||
else if (!strcasecmp( "PullFlags", arg ))
|
else if (!strcasecmp( "PullFlags", arg ))
|
||||||
conf->ops[S] |= OP_FLAGS;
|
conf->ops[N] |= OP_FLAGS;
|
||||||
else if (!strcasecmp( "PushReNew", arg ))
|
else if (!strcasecmp( "PushReNew", arg ))
|
||||||
conf->ops[M] |= OP_RENEW;
|
conf->ops[F] |= OP_RENEW;
|
||||||
else if (!strcasecmp( "PushNew", arg ))
|
else if (!strcasecmp( "PushNew", arg ))
|
||||||
conf->ops[M] |= OP_NEW;
|
conf->ops[F] |= OP_NEW;
|
||||||
else if (!strcasecmp( "PushDelete", arg ))
|
else if (!strcasecmp( "PushDelete", arg ))
|
||||||
conf->ops[M] |= OP_DELETE;
|
conf->ops[F] |= OP_DELETE;
|
||||||
else if (!strcasecmp( "PushFlags", arg ))
|
else if (!strcasecmp( "PushFlags", arg ))
|
||||||
conf->ops[M] |= OP_FLAGS;
|
conf->ops[F] |= OP_FLAGS;
|
||||||
else if (!strcasecmp( "All", arg ) || !strcasecmp( "Full", arg ))
|
else if (!strcasecmp( "All", arg ) || !strcasecmp( "Full", arg ))
|
||||||
*cops |= XOP_PULL|XOP_PUSH;
|
*cops |= XOP_PULL|XOP_PUSH;
|
||||||
else if (strcasecmp( "None", arg ) && strcasecmp( "Noop", arg )) {
|
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;
|
cfile->err = 1;
|
||||||
}
|
}
|
||||||
while ((arg = get_arg( cfile, ARG_OPTIONAL, NULL )));
|
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 ))
|
} else if (!strcasecmp( "SyncState", cfile->cmd ))
|
||||||
conf->sync_state = expand_strdup( cfile->val );
|
conf->sync_state = expand_strdup( cfile->val );
|
||||||
else if (!strcasecmp( "CopyArrivalDate", cfile->cmd ))
|
else if (!strcasecmp( "CopyArrivalDate", cfile->cmd ))
|
||||||
|
@ -214,17 +214,23 @@ getopt_helper( conffile_t *cfile, int *cops, channel_conf_t *conf )
|
||||||
do {
|
do {
|
||||||
if (!strcasecmp( "Both", arg )) {
|
if (!strcasecmp( "Both", arg )) {
|
||||||
*cops |= op;
|
*cops |= op;
|
||||||
} else if (!strcasecmp( "Master", arg )) {
|
} else if (!strcasecmp( "Far", arg )) {
|
||||||
conf->ops[M] |= op;
|
conf->ops[F] |= op;
|
||||||
} else if (!strcasecmp( "Slave", arg )) {
|
} else if (!strcasecmp( "Master", arg )) { // Pre-1.4 legacy
|
||||||
conf->ops[S] |= op;
|
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 )) {
|
} else if (strcasecmp( "None", arg )) {
|
||||||
error( "%s:%d: invalid %s arg '%s'\n",
|
error( "%s:%d: invalid %s arg '%s'\n",
|
||||||
cfile->file, cfile->line, boxOps[i].name, arg );
|
cfile->file, cfile->line, boxOps[i].name, arg );
|
||||||
cfile->err = 1;
|
cfile->err = 1;
|
||||||
}
|
}
|
||||||
} while ((arg = get_arg( cfile, ARG_OPTIONAL, NULL )));
|
} 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;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -265,25 +271,25 @@ merge_ops( int cops, int ops[] )
|
||||||
int aops, op;
|
int aops, op;
|
||||||
uint i;
|
uint i;
|
||||||
|
|
||||||
aops = ops[M] | ops[S];
|
aops = ops[F] | ops[N];
|
||||||
if (ops[M] & XOP_HAVE_TYPE) {
|
if (ops[F] & XOP_HAVE_TYPE) {
|
||||||
if (aops & OP_MASK_TYPE) {
|
if (aops & OP_MASK_TYPE) {
|
||||||
if (aops & cops & OP_MASK_TYPE) {
|
if (aops & cops & OP_MASK_TYPE) {
|
||||||
cfl:
|
cfl:
|
||||||
error( "Conflicting Sync args specified.\n" );
|
error( "Conflicting Sync args specified.\n" );
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
ops[M] |= cops & OP_MASK_TYPE;
|
ops[F] |= cops & OP_MASK_TYPE;
|
||||||
ops[S] |= cops & OP_MASK_TYPE;
|
ops[N] |= cops & OP_MASK_TYPE;
|
||||||
if (cops & XOP_PULL) {
|
if (cops & XOP_PULL) {
|
||||||
if (ops[S] & OP_MASK_TYPE)
|
if (ops[N] & OP_MASK_TYPE)
|
||||||
goto cfl;
|
goto cfl;
|
||||||
ops[S] |= OP_MASK_TYPE;
|
ops[N] |= OP_MASK_TYPE;
|
||||||
}
|
}
|
||||||
if (cops & XOP_PUSH) {
|
if (cops & XOP_PUSH) {
|
||||||
if (ops[M] & OP_MASK_TYPE)
|
if (ops[F] & OP_MASK_TYPE)
|
||||||
goto cfl;
|
goto cfl;
|
||||||
ops[M] |= OP_MASK_TYPE;
|
ops[F] |= OP_MASK_TYPE;
|
||||||
}
|
}
|
||||||
} else if (cops & (OP_MASK_TYPE|XOP_MASK_DIR)) {
|
} else if (cops & (OP_MASK_TYPE|XOP_MASK_DIR)) {
|
||||||
if (!(cops & OP_MASK_TYPE))
|
if (!(cops & OP_MASK_TYPE))
|
||||||
|
@ -291,20 +297,20 @@ merge_ops( int cops, int ops[] )
|
||||||
else if (!(cops & XOP_MASK_DIR))
|
else if (!(cops & XOP_MASK_DIR))
|
||||||
cops |= XOP_PULL|XOP_PUSH;
|
cops |= XOP_PULL|XOP_PUSH;
|
||||||
if (cops & XOP_PULL)
|
if (cops & XOP_PULL)
|
||||||
ops[S] |= cops & OP_MASK_TYPE;
|
ops[N] |= cops & OP_MASK_TYPE;
|
||||||
if (cops & XOP_PUSH)
|
if (cops & XOP_PUSH)
|
||||||
ops[M] |= cops & OP_MASK_TYPE;
|
ops[F] |= cops & OP_MASK_TYPE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (i = 0; i < as(boxOps); i++) {
|
for (i = 0; i < as(boxOps); i++) {
|
||||||
op = boxOps[i].op;
|
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) {
|
if (aops & cops & op) {
|
||||||
error( "Conflicting %s args specified.\n", boxOps[i].name );
|
error( "Conflicting %s args specified.\n", boxOps[i].name );
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
ops[M] |= cops & op;
|
ops[F] |= cops & op;
|
||||||
ops[S] |= cops & op;
|
ops[N] |= cops & op;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -320,7 +326,7 @@ load_config( const char *where, int pseudo )
|
||||||
string_list_t *chanlist, **chanlistapp;
|
string_list_t *chanlist, **chanlistapp;
|
||||||
char *arg, *p;
|
char *arg, *p;
|
||||||
uint len, max_size;
|
uint len, max_size;
|
||||||
int cops, gcops, ms, i;
|
int cops, gcops, fn, i;
|
||||||
char path[_POSIX_PATH_MAX];
|
char path[_POSIX_PATH_MAX];
|
||||||
char buf[1024];
|
char buf[1024];
|
||||||
|
|
||||||
|
@ -343,6 +349,7 @@ load_config( const char *where, int pseudo )
|
||||||
cfile.bufl = sizeof(buf) - 1;
|
cfile.bufl = sizeof(buf) - 1;
|
||||||
cfile.line = 0;
|
cfile.line = 0;
|
||||||
cfile.err = 0;
|
cfile.err = 0;
|
||||||
|
cfile.ms_warn = 0;
|
||||||
cfile.rest = NULL;
|
cfile.rest = NULL;
|
||||||
|
|
||||||
gcops = 0;
|
gcops = 0;
|
||||||
|
@ -384,11 +391,19 @@ load_config( const char *where, int pseudo )
|
||||||
add_string_list( &channel->patterns, arg );
|
add_string_list( &channel->patterns, arg );
|
||||||
while ((arg = get_arg( &cfile, ARG_OPTIONAL, NULL )));
|
while ((arg = get_arg( &cfile, ARG_OPTIONAL, NULL )));
|
||||||
}
|
}
|
||||||
else if (!strcasecmp( "Master", cfile.cmd )) {
|
else if (!strcasecmp( "Far", cfile.cmd )) {
|
||||||
ms = M;
|
fn = F;
|
||||||
goto linkst;
|
goto linkst;
|
||||||
} else if (!strcasecmp( "Slave", cfile.cmd )) {
|
} else if (!strcasecmp( "Master", cfile.cmd )) { // Pre-1.4 legacy
|
||||||
ms = S;
|
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:
|
linkst:
|
||||||
if (*cfile.val != ':' || !(p = strchr( cfile.val + 1, ':' ))) {
|
if (*cfile.val != ':' || !(p = strchr( cfile.val + 1, ':' ))) {
|
||||||
error( "%s:%d: malformed mailbox spec\n",
|
error( "%s:%d: malformed mailbox spec\n",
|
||||||
|
@ -399,7 +414,7 @@ load_config( const char *where, int pseudo )
|
||||||
*p = 0;
|
*p = 0;
|
||||||
for (store = stores; store; store = store->next)
|
for (store = stores; store; store = store->next)
|
||||||
if (!strcmp( store->name, cfile.val + 1 )) {
|
if (!strcmp( store->name, cfile.val + 1 )) {
|
||||||
channel->stores[ms] = store;
|
channel->stores[fn] = store;
|
||||||
goto stpcom;
|
goto stpcom;
|
||||||
}
|
}
|
||||||
error( "%s:%d: unknown store '%s'\n",
|
error( "%s:%d: unknown store '%s'\n",
|
||||||
|
@ -408,17 +423,17 @@ load_config( const char *where, int pseudo )
|
||||||
continue;
|
continue;
|
||||||
stpcom:
|
stpcom:
|
||||||
if (*++p)
|
if (*++p)
|
||||||
channel->boxes[ms] = nfstrdup( p );
|
channel->boxes[fn] = nfstrdup( p );
|
||||||
} else if (!getopt_helper( &cfile, &cops, channel )) {
|
} else if (!getopt_helper( &cfile, &cops, channel )) {
|
||||||
error( "%s:%d: unknown keyword '%s'\n", cfile.file, cfile.line, cfile.cmd );
|
error( "%s:%d: unknown keyword '%s'\n", cfile.file, cfile.line, cfile.cmd );
|
||||||
cfile.err = 1;
|
cfile.err = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!channel->stores[M]) {
|
if (!channel->stores[F]) {
|
||||||
error( "channel '%s' refers to no master store\n", channel->name );
|
error( "channel '%s' refers to no far side store\n", channel->name );
|
||||||
cfile.err = 1;
|
cfile.err = 1;
|
||||||
} else if (!channel->stores[S]) {
|
} else if (!channel->stores[N]) {
|
||||||
error( "channel '%s' refers to no slave store\n", channel->name );
|
error( "channel '%s' refers to no near side store\n", channel->name );
|
||||||
cfile.err = 1;
|
cfile.err = 1;
|
||||||
} else if (merge_ops( cops, channel->ops ))
|
} else if (merge_ops( cops, channel->ops ))
|
||||||
cfile.err = 1;
|
cfile.err = 1;
|
||||||
|
@ -426,7 +441,7 @@ load_config( const char *where, int pseudo )
|
||||||
if (max_size != UINT_MAX) {
|
if (max_size != UINT_MAX) {
|
||||||
if (!max_size)
|
if (!max_size)
|
||||||
max_size = UINT_MAX;
|
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;
|
||||||
channelapp = &channel->next;
|
channelapp = &channel->next;
|
||||||
|
@ -505,6 +520,8 @@ load_config( const char *where, int pseudo )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fclose (cfile.fp);
|
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 );
|
cfile.err |= merge_ops( gcops, global_conf.ops );
|
||||||
if (!global_conf.sync_state)
|
if (!global_conf.sync_state)
|
||||||
global_conf.sync_state = expand_strdup( "~/." EXE "/" );
|
global_conf.sync_state = expand_strdup( "~/." EXE "/" );
|
||||||
|
|
|
@ -32,6 +32,7 @@ typedef struct {
|
||||||
int bufl;
|
int bufl;
|
||||||
int line;
|
int line;
|
||||||
int err;
|
int err;
|
||||||
|
int ms_warn;
|
||||||
char *cmd, *val, *rest;
|
char *cmd, *val, *rest;
|
||||||
} conffile_t;
|
} 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"
|
" -d, --delete propagate message deletions\n"
|
||||||
" -f, --flags propagate message flag changes\n"
|
" -f, --flags propagate message flag changes\n"
|
||||||
" -N, --renew propagate previously not propagated new messages\n"
|
" -N, --renew propagate previously not propagated new messages\n"
|
||||||
" -L, --pull propagate from master to slave\n"
|
" -L, --pull propagate from far to near side\n"
|
||||||
" -H, --push propagate from slave to master\n"
|
" -H, --push propagate from near to far side\n"
|
||||||
" -C, --create propagate creations of mailboxes\n"
|
" -C, --create propagate creations of mailboxes\n"
|
||||||
" -R, --remove propagate deletions of mailboxes\n"
|
" -R, --remove propagate deletions of mailboxes\n"
|
||||||
" -X, --expunge expunge deleted messages\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"
|
"\nIf neither --pull nor --push are specified, both are active.\n"
|
||||||
"If neither --new, --delete, --flags nor --renew are specified, all 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"
|
"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"
|
"See the man page for details.\n"
|
||||||
"\nSupported mailbox formats are: IMAP4rev1, Maildir\n"
|
"\nSupported mailbox formats are: IMAP4rev1, Maildir\n"
|
||||||
"\nCompile time options:\n"
|
"\nCompile time options:\n"
|
||||||
|
@ -205,7 +205,7 @@ stats( void )
|
||||||
if (l > cls)
|
if (l > cls)
|
||||||
buf[t][cls - 1] = '~';
|
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
|
static int
|
||||||
|
@ -297,18 +297,18 @@ filter_boxes( string_list_t *boxes, const char *prefix, string_list_t *patterns
|
||||||
static void
|
static void
|
||||||
merge_actions( channel_conf_t *chan, int ops[], int have, int mask, int def )
|
merge_actions( channel_conf_t *chan, int ops[], int have, int mask, int def )
|
||||||
{
|
{
|
||||||
if (ops[M] & have) {
|
if (ops[F] & have) {
|
||||||
chan->ops[M] &= ~mask;
|
chan->ops[F] &= ~mask;
|
||||||
chan->ops[M] |= ops[M] & mask;
|
chan->ops[F] |= ops[F] & mask;
|
||||||
chan->ops[S] &= ~mask;
|
chan->ops[N] &= ~mask;
|
||||||
chan->ops[S] |= ops[S] & mask;
|
chan->ops[N] |= ops[N] & mask;
|
||||||
} else if (!(chan->ops[M] & have)) {
|
} else if (!(chan->ops[F] & have)) {
|
||||||
if (global_conf.ops[M] & have) {
|
if (global_conf.ops[F] & have) {
|
||||||
chan->ops[M] |= global_conf.ops[M] & mask;
|
chan->ops[F] |= global_conf.ops[F] & mask;
|
||||||
chan->ops[S] |= global_conf.ops[S] & mask;
|
chan->ops[N] |= global_conf.ops[N] & mask;
|
||||||
} else {
|
} else {
|
||||||
chan->ops[M] |= def;
|
chan->ops[F] |= def;
|
||||||
chan->ops[S] |= 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 );
|
mbox->name = nfstrndup( boxp, boxl );
|
||||||
else
|
else
|
||||||
mbox->name = nfstrndup( "INBOX", 5 );
|
mbox->name = nfstrndup( "INBOX", 5 );
|
||||||
mbox->present[M] = mbox->present[S] = BOX_POSSIBLE;
|
mbox->present[F] = mbox->present[N] = BOX_POSSIBLE;
|
||||||
mbox->next = NULL;
|
mbox->next = NULL;
|
||||||
*mboxapp = mbox;
|
*mboxapp = mbox;
|
||||||
mboxapp = &mbox->next;
|
mboxapp = &mbox->next;
|
||||||
|
@ -431,7 +431,7 @@ main( int argc, char **argv )
|
||||||
channel_conf_t *chan;
|
channel_conf_t *chan;
|
||||||
string_list_t *channame;
|
string_list_t *channame;
|
||||||
char *config = NULL, *opt, *ochar;
|
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();
|
tzset();
|
||||||
gethostname( Hostname, sizeof(Hostname) );
|
gethostname( Hostname, sizeof(Hostname) );
|
||||||
|
@ -504,22 +504,26 @@ main( int argc, char **argv )
|
||||||
goto badopt;
|
goto badopt;
|
||||||
DFlags |= op;
|
DFlags |= op;
|
||||||
} else if (!strcmp( opt, "pull" ))
|
} 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" ))
|
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 )) {
|
else if (starts_with( opt, -1, "create", 6 )) {
|
||||||
opt += 6;
|
opt += 6;
|
||||||
op = OP_CREATE|XOP_HAVE_CREATE;
|
op = OP_CREATE|XOP_HAVE_CREATE;
|
||||||
lcop:
|
lcop:
|
||||||
if (!*opt)
|
if (!*opt)
|
||||||
cops |= op;
|
cops |= op;
|
||||||
else if (!strcmp( opt, "-master" ))
|
else if (!strcmp( opt, "-far" ))
|
||||||
ops[M] |= op;
|
ops[F] |= op;
|
||||||
else if (!strcmp( opt, "-slave" ))
|
else if (!strcmp( opt, "-master" )) // Pre-1.4 legacy
|
||||||
ops[S] |= op;
|
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
|
else
|
||||||
goto badopt;
|
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 )) {
|
} else if (starts_with( opt, -1, "remove", 6 )) {
|
||||||
opt += 6;
|
opt += 6;
|
||||||
op = OP_REMOVE|XOP_HAVE_REMOVE;
|
op = OP_REMOVE|XOP_HAVE_REMOVE;
|
||||||
|
@ -529,15 +533,15 @@ main( int argc, char **argv )
|
||||||
op = OP_EXPUNGE|XOP_HAVE_EXPUNGE;
|
op = OP_EXPUNGE|XOP_HAVE_EXPUNGE;
|
||||||
goto lcop;
|
goto lcop;
|
||||||
} else if (!strcmp( opt, "no-expunge" ))
|
} else if (!strcmp( opt, "no-expunge" ))
|
||||||
ops[M] |= XOP_HAVE_EXPUNGE;
|
ops[F] |= XOP_HAVE_EXPUNGE;
|
||||||
else if (!strcmp( opt, "no-create" ))
|
else if (!strcmp( opt, "no-create" ))
|
||||||
ops[M] |= XOP_HAVE_CREATE;
|
ops[F] |= XOP_HAVE_CREATE;
|
||||||
else if (!strcmp( opt, "no-remove" ))
|
else if (!strcmp( opt, "no-remove" ))
|
||||||
ops[M] |= XOP_HAVE_REMOVE;
|
ops[F] |= XOP_HAVE_REMOVE;
|
||||||
else if (!strcmp( opt, "full" ))
|
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" ))
|
else if (!strcmp( opt, "noop" ))
|
||||||
ops[M] |= XOP_HAVE_TYPE;
|
ops[F] |= XOP_HAVE_TYPE;
|
||||||
else if (starts_with( opt, -1, "pull", 4 )) {
|
else if (starts_with( opt, -1, "pull", 4 )) {
|
||||||
op = XOP_PULL;
|
op = XOP_PULL;
|
||||||
lcac:
|
lcac:
|
||||||
|
@ -569,11 +573,11 @@ main( int argc, char **argv )
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
switch (op & XOP_MASK_DIR) {
|
switch (op & XOP_MASK_DIR) {
|
||||||
case XOP_PULL: ops[S] |= op & OP_MASK_TYPE; break;
|
case XOP_PULL: ops[N] |= op & OP_MASK_TYPE; break;
|
||||||
case XOP_PUSH: ops[M] |= op & OP_MASK_TYPE; break;
|
case XOP_PUSH: ops[F] |= op & OP_MASK_TYPE; break;
|
||||||
default: cops |= op; break;
|
default: cops |= op; break;
|
||||||
}
|
}
|
||||||
ops[M] |= XOP_HAVE_TYPE;
|
ops[F] |= XOP_HAVE_TYPE;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -604,15 +608,19 @@ main( int argc, char **argv )
|
||||||
case 'C':
|
case 'C':
|
||||||
op = OP_CREATE|XOP_HAVE_CREATE;
|
op = OP_CREATE|XOP_HAVE_CREATE;
|
||||||
cop:
|
cop:
|
||||||
if (*ochar == 'm')
|
if (*ochar == 'f')
|
||||||
ops[M] |= op, ochar++;
|
ops[F] |= op, ochar++;
|
||||||
else if (*ochar == 's')
|
else if (*ochar == 'm') // Pre-1.4 legacy
|
||||||
ops[S] |= op, ochar++;
|
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 == '-')
|
else if (*ochar == '-')
|
||||||
ochar++;
|
ochar++;
|
||||||
else
|
else
|
||||||
cops |= op;
|
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;
|
break;
|
||||||
case 'R':
|
case 'R':
|
||||||
op = OP_REMOVE|XOP_HAVE_REMOVE;
|
op = OP_REMOVE|XOP_HAVE_REMOVE;
|
||||||
|
@ -624,7 +632,7 @@ main( int argc, char **argv )
|
||||||
cops |= XOP_PULL|XOP_PUSH;
|
cops |= XOP_PULL|XOP_PUSH;
|
||||||
FALLTHROUGH
|
FALLTHROUGH
|
||||||
case '0':
|
case '0':
|
||||||
ops[M] |= XOP_HAVE_TYPE;
|
ops[F] |= XOP_HAVE_TYPE;
|
||||||
break;
|
break;
|
||||||
case 'n':
|
case 'n':
|
||||||
case 'd':
|
case 'd':
|
||||||
|
@ -647,13 +655,13 @@ main( int argc, char **argv )
|
||||||
}
|
}
|
||||||
if (op & OP_MASK_TYPE)
|
if (op & OP_MASK_TYPE)
|
||||||
switch (op & XOP_MASK_DIR) {
|
switch (op & XOP_MASK_DIR) {
|
||||||
case XOP_PULL: ops[S] |= op & OP_MASK_TYPE; break;
|
case XOP_PULL: ops[N] |= op & OP_MASK_TYPE; break;
|
||||||
case XOP_PUSH: ops[M] |= op & OP_MASK_TYPE; break;
|
case XOP_PUSH: ops[F] |= op & OP_MASK_TYPE; break;
|
||||||
default: cops |= op; break;
|
default: cops |= op; break;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
cops |= op;
|
cops |= op;
|
||||||
ops[M] |= XOP_HAVE_TYPE;
|
ops[F] |= XOP_HAVE_TYPE;
|
||||||
break;
|
break;
|
||||||
case 'L':
|
case 'L':
|
||||||
op = XOP_PULL;
|
op = XOP_PULL;
|
||||||
|
@ -722,6 +730,8 @@ main( int argc, char **argv )
|
||||||
return 1;
|
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 ))
|
if (!(DFlags & (QUIET | DEBUG_ALL)) && isatty( 1 ))
|
||||||
DFlags |= PROGRESS;
|
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] );
|
int st = mvars->chan->stores[t]->driver->get_fail_state( mvars->chan->stores[t] );
|
||||||
if (st != FAIL_TEMP) {
|
if (st != FAIL_TEMP) {
|
||||||
info( "Skipping due to %sfailed %s store %s.\n",
|
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;
|
mvars->skip = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (mvars->skip)
|
if (mvars->skip)
|
||||||
goto next2;
|
goto next2;
|
||||||
mvars->state[M] = mvars->state[S] = ST_FRESH;
|
mvars->state[F] = mvars->state[N] = ST_FRESH;
|
||||||
if ((DFlags & DEBUG_DRV) || (mvars->chan->stores[M]->driver->get_caps( NULL ) & mvars->chan->stores[S]->driver->get_caps( NULL ) & DRV_VERBOSE))
|
if ((DFlags & DEBUG_DRV) || (mvars->chan->stores[F]->driver->get_caps( NULL ) & mvars->chan->stores[N]->driver->get_caps( NULL ) & DRV_VERBOSE))
|
||||||
labels[M] = "M: ", labels[S] = "S: ";
|
labels[F] = "F: ", labels[N] = "N: ";
|
||||||
else
|
else
|
||||||
labels[M] = labels[S] = "";
|
labels[F] = labels[N] = "";
|
||||||
for (t = 0; t < 2; t++) {
|
for (t = 0; t < 2; t++) {
|
||||||
driver_t *drv = mvars->chan->stores[t]->driver;
|
driver_t *drv = mvars->chan->stores[t]->driver;
|
||||||
store_t *ctx = drv->alloc_store( mvars->chan->stores[t], labels[t] );
|
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 );
|
drv->set_bad_callback( ctx, store_bad, AUX );
|
||||||
}
|
}
|
||||||
for (t = 0; ; t++) {
|
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 );
|
mvars->drv[t]->connect_store( mvars->ctx[t], store_connected, AUX );
|
||||||
if (t || mvars->skip)
|
if (t || mvars->skip)
|
||||||
break;
|
break;
|
||||||
|
@ -872,35 +882,35 @@ sync_chans( main_vars_t *mvars, int ent )
|
||||||
opened:
|
opened:
|
||||||
if (mvars->skip)
|
if (mvars->skip)
|
||||||
goto next;
|
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;
|
return;
|
||||||
|
|
||||||
if (!mvars->chanptr->boxlist && mvars->chan->patterns) {
|
if (!mvars->chanptr->boxlist && mvars->chan->patterns) {
|
||||||
mvars->chanptr->boxlist = 2;
|
mvars->chanptr->boxlist = 2;
|
||||||
boxes[M] = filter_boxes( mvars->boxes[M], mvars->chan->boxes[M], mvars->chan->patterns );
|
boxes[F] = filter_boxes( mvars->boxes[F], mvars->chan->boxes[F], mvars->chan->patterns );
|
||||||
boxes[S] = filter_boxes( mvars->boxes[S], mvars->chan->boxes[S], mvars->chan->patterns );
|
boxes[N] = filter_boxes( mvars->boxes[N], mvars->chan->boxes[N], mvars->chan->patterns );
|
||||||
mboxapp = &mvars->chanptr->boxes;
|
mboxapp = &mvars->chanptr->boxes;
|
||||||
for (mb = sb = 0; ; ) {
|
for (mb = sb = 0; ; ) {
|
||||||
char *mname = boxes[M] ? boxes[M][mb] : NULL;
|
char *mname = boxes[F] ? boxes[F][mb] : NULL;
|
||||||
char *sname = boxes[S] ? boxes[S][sb] : NULL;
|
char *sname = boxes[N] ? boxes[N][sb] : NULL;
|
||||||
if (!mname && !sname)
|
if (!mname && !sname)
|
||||||
break;
|
break;
|
||||||
mbox = nfmalloc( sizeof(*mbox) );
|
mbox = nfmalloc( sizeof(*mbox) );
|
||||||
if (!(cmp = !mname - !sname) && !(cmp = cmp_box_names( &mname, &sname ))) {
|
if (!(cmp = !mname - !sname) && !(cmp = cmp_box_names( &mname, &sname ))) {
|
||||||
mbox->name = mname;
|
mbox->name = mname;
|
||||||
free( sname );
|
free( sname );
|
||||||
mbox->present[M] = mbox->present[S] = BOX_PRESENT;
|
mbox->present[F] = mbox->present[N] = BOX_PRESENT;
|
||||||
mb++;
|
mb++;
|
||||||
sb++;
|
sb++;
|
||||||
} else if (cmp < 0) {
|
} else if (cmp < 0) {
|
||||||
mbox->name = mname;
|
mbox->name = mname;
|
||||||
mbox->present[M] = BOX_PRESENT;
|
mbox->present[F] = BOX_PRESENT;
|
||||||
mbox->present[S] = (!mb && !strcmp( mbox->name, "INBOX" )) ? BOX_PRESENT : BOX_ABSENT;
|
mbox->present[N] = (!mb && !strcmp( mbox->name, "INBOX" )) ? BOX_PRESENT : BOX_ABSENT;
|
||||||
mb++;
|
mb++;
|
||||||
} else {
|
} else {
|
||||||
mbox->name = sname;
|
mbox->name = sname;
|
||||||
mbox->present[M] = (!sb && !strcmp( mbox->name, "INBOX" )) ? BOX_PRESENT : BOX_ABSENT;
|
mbox->present[F] = (!sb && !strcmp( mbox->name, "INBOX" )) ? BOX_PRESENT : BOX_ABSENT;
|
||||||
mbox->present[S] = BOX_PRESENT;
|
mbox->present[N] = BOX_PRESENT;
|
||||||
sb++;
|
sb++;
|
||||||
}
|
}
|
||||||
mbox->next = NULL;
|
mbox->next = NULL;
|
||||||
|
@ -908,8 +918,8 @@ sync_chans( main_vars_t *mvars, int ent )
|
||||||
mboxapp = &mbox->next;
|
mboxapp = &mbox->next;
|
||||||
boxes_total++;
|
boxes_total++;
|
||||||
}
|
}
|
||||||
free( boxes[M] );
|
free( boxes[F] );
|
||||||
free( boxes[S] );
|
free( boxes[N] );
|
||||||
if (!mvars->list)
|
if (!mvars->list)
|
||||||
stats();
|
stats();
|
||||||
}
|
}
|
||||||
|
@ -938,7 +948,7 @@ sync_chans( main_vars_t *mvars, int ent )
|
||||||
if (!mvars->skip)
|
if (!mvars->skip)
|
||||||
goto syncml;
|
goto syncml;
|
||||||
} else
|
} 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:
|
next:
|
||||||
|
@ -956,7 +966,7 @@ sync_chans( main_vars_t *mvars, int ent )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mvars->cben = 1;
|
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;
|
mvars->skip = 1;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1067,7 +1077,7 @@ store_listed( int sts, string_list_t *boxes, void *aux )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (mvars->ctx[t]->conf->map_inbox) {
|
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 );
|
add_string_list( &mvars->boxes[t], mvars->ctx[t]->conf->map_inbox );
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -1082,19 +1092,19 @@ store_listed( int sts, string_list_t *boxes, void *aux )
|
||||||
static int
|
static int
|
||||||
sync_listed_boxes( main_vars_t *mvars, box_ent_t *mbox )
|
sync_listed_boxes( main_vars_t *mvars, box_ent_t *mbox )
|
||||||
{
|
{
|
||||||
if (mvars->chan->boxes[M] || mvars->chan->boxes[S]) {
|
if (mvars->chan->boxes[F] || mvars->chan->boxes[N]) {
|
||||||
const char *mpfx = nz( mvars->chan->boxes[M], "" );
|
const char *mpfx = nz( mvars->chan->boxes[F], "" );
|
||||||
const char *spfx = nz( mvars->chan->boxes[S], "" );
|
const char *spfx = nz( mvars->chan->boxes[N], "" );
|
||||||
if (!mvars->list) {
|
if (!mvars->list) {
|
||||||
nfasprintf( &mvars->names[M], "%s%s", mpfx, mbox->name );
|
nfasprintf( &mvars->names[F], "%s%s", mpfx, mbox->name );
|
||||||
nfasprintf( &mvars->names[S], "%s%s", spfx, 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 );
|
sync_boxes( mvars->ctx, (const char * const *)mvars->names, mbox->present, mvars->chan, done_sync_2_dyn, mvars );
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
printf( "%s%s <=> %s%s\n", mpfx, mbox->name, spfx, mbox->name );
|
printf( "%s%s <=> %s%s\n", mpfx, mbox->name, spfx, mbox->name );
|
||||||
} else {
|
} else {
|
||||||
if (!mvars->list) {
|
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 );
|
sync_boxes( mvars->ctx, (const char * const *)mvars->names, mbox->present, mvars->chan, done_sync, mvars );
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -1108,8 +1118,8 @@ done_sync_2_dyn( int sts, void *aux )
|
||||||
{
|
{
|
||||||
main_vars_t *mvars = (main_vars_t *)aux;
|
main_vars_t *mvars = (main_vars_t *)aux;
|
||||||
|
|
||||||
free( mvars->names[M] );
|
free( mvars->names[F] );
|
||||||
free( mvars->names[S] );
|
free( mvars->names[N] );
|
||||||
done_sync( sts, aux );
|
done_sync( sts, aux );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1123,11 +1133,11 @@ done_sync( int sts, void *aux )
|
||||||
stats();
|
stats();
|
||||||
if (sts) {
|
if (sts) {
|
||||||
mvars->ret = 1;
|
mvars->ret = 1;
|
||||||
if (sts & (SYNC_BAD(M) | SYNC_BAD(S))) {
|
if (sts & (SYNC_BAD(F) | SYNC_BAD(N))) {
|
||||||
if (sts & SYNC_BAD(M))
|
if (sts & SYNC_BAD(F))
|
||||||
mvars->state[M] = ST_CLOSED;
|
mvars->state[F] = ST_CLOSED;
|
||||||
if (sts & SYNC_BAD(S))
|
if (sts & SYNC_BAD(N))
|
||||||
mvars->state[S] = ST_CLOSED;
|
mvars->state[N] = ST_CLOSED;
|
||||||
mvars->skip = 1;
|
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
|
Don't synchronize anything, but list all mailboxes in the selected channels
|
||||||
and exit.
|
and exit.
|
||||||
.TP
|
.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.
|
Override any \fBCreate\fR options from the config file. See below.
|
||||||
.TP
|
.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.
|
Override any \fBRemove\fR options from the config file. See below.
|
||||||
.TP
|
.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.
|
Override any \fBExpunge\fR options from the config file. See below.
|
||||||
.TP
|
.TP
|
||||||
{\fB-n\fR|\fB-N\fR|\fB-d\fR|\fB-f\fR|\fB-0\fR|\fB-F\fR},\
|
{\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
|
\fBMapInbox\fR \fImailbox\fR
|
||||||
Create a virtual mailbox (relative to \fBPath\fR) which aliases
|
Create a virtual mailbox (relative to \fBPath\fR) which aliases
|
||||||
the \fBINBOX\fR. Makes sense in conjunction with \fBPatterns\fR in the
|
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.
|
place \fBInbox\fR under \fBPath\fR instead.
|
||||||
This virtual mailbox does not support subfolders.
|
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.
|
Define the Channel \fIname\fR, opening a section for its parameters.
|
||||||
.
|
.
|
||||||
.TP
|
.TP
|
||||||
{\fBMaster\fR|\fBSlave\fR} \fB:\fIstore\fB:\fR[\fImailbox\fR]
|
{\fBFar\fR|\fBNear\fR} \fB:\fIstore\fB:\fR[\fImailbox\fR]
|
||||||
Specify the Master resp. Slave Store to be connected by this Channel.
|
Specify the far resp. near side Store to be connected by this Channel.
|
||||||
If \fBPatterns\fR are specified, \fImailbox\fR is interpreted as a
|
If \fBPatterns\fR are specified, \fImailbox\fR is interpreted as a
|
||||||
prefix which is not matched against the patterns, and which is not
|
prefix which is not matched against the patterns, and which is not
|
||||||
affected by mailbox list overrides.
|
affected by mailbox list overrides.
|
||||||
|
@ -521,8 +521,8 @@ Otherwise, if \fImailbox\fR is omitted, \fBINBOX\fR is assumed.
|
||||||
.TP
|
.TP
|
||||||
\fBPattern\fR[\fBs\fR] [\fB!\fR]\fIpattern\fR ...
|
\fBPattern\fR[\fBs\fR] [\fB!\fR]\fIpattern\fR ...
|
||||||
Instead of synchronizing only one mailbox pair, synchronize all mailboxes
|
Instead of synchronizing only one mailbox pair, synchronize all mailboxes
|
||||||
that match the \fIpattern\fR(s). The mailbox names are the same on both
|
that match the \fIpattern\fR(s). The mailbox names are the same on the far
|
||||||
Master and Slave. Patterns are IMAP4 patterns, i.e., \fB*\fR matches anything
|
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
|
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
|
\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
|
(either by supplying multiple arguments or by using \fBPattern\fR multiple
|
||||||
|
@ -539,12 +539,12 @@ Example: "\fBPatterns\fR\ \fI%\ !Trash\fR"
|
||||||
.TP
|
.TP
|
||||||
\fBMaxSize\fR \fIsize\fR[\fBk\fR|\fBm\fR][\fBb\fR]
|
\fBMaxSize\fR \fIsize\fR[\fBk\fR|\fBm\fR][\fBb\fR]
|
||||||
Analogous to the homonymous option in the Stores section, but applies equally
|
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.
|
not to provide conflicting settings if you use the Stores in multiple Channels.
|
||||||
.
|
.
|
||||||
.TP
|
.TP
|
||||||
\fBMaxMessages\fR \fIcount\fR
|
\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,
|
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).
|
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
|
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}
|
\fBSync\fR {\fBNone\fR|[\fBPull\fR] [\fBPush\fR] [\fBNew\fR] [\fBReNew\fR] [\fBDelete\fR] [\fBFlags\fR]|\fBAll\fR}
|
||||||
Select the synchronization operation(s) to perform:
|
Select the synchronization operation(s) to perform:
|
||||||
.br
|
.br
|
||||||
\fBPull\fR - propagate changes from Master to Slave.
|
\fBPull\fR - propagate changes from far to near side.
|
||||||
.br
|
.br
|
||||||
\fBPush\fR - propagate changes from Slave to Master.
|
\fBPush\fR - propagate changes from near to far side.
|
||||||
.br
|
.br
|
||||||
\fBNew\fR - propagate newly appeared messages.
|
\fBNew\fR - propagate newly appeared messages.
|
||||||
.br
|
.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.
|
the cells which are selected both horizontally and vertically are asserted.
|
||||||
Specifying no flags from a class is like specifying all flags from this class.
|
Specifying no flags from a class is like specifying all flags from this class.
|
||||||
For example, "\fBSync\fR\ \fBPull\fR\ \fBNew\fR\ \fBFlags\fR" will propagate
|
For example, "\fBSync\fR\ \fBPull\fR\ \fBNew\fR\ \fBFlags\fR" will propagate
|
||||||
new messages and flag changes from the Master to the Slave,
|
new messages and flag changes from the far side to the near side,
|
||||||
"\fBSync\fR\ \fBNew\fR\ \fBDelete\fR" will propagate message arrivals and
|
"\fBSync\fR\ \fBNew\fR\ \fBDelete\fR" will propagate message arrivals and
|
||||||
deletions both ways, and "\fBSync\fR\ \fBPush\fR" will propagate all changes
|
deletions both ways, and "\fBSync\fR\ \fBPush\fR" will propagate all changes
|
||||||
from the Slave to the Master.
|
from the near side to the far side.
|
||||||
.br
|
.br
|
||||||
In the second style, direction flags are concatenated with type flags; every
|
In the second style, direction flags are concatenated with type flags; every
|
||||||
compound flag immediately asserts a cell in the matrix. In addition to at least
|
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
|
the first style, they immediately assert all cells in their respective
|
||||||
row/column. For example,
|
row/column. For example,
|
||||||
"\fBSync\fR\ \fBPullNew\fR\ \fBPullDelete\fR\ \fBPush\fR" will propagate
|
"\fBSync\fR\ \fBPullNew\fR\ \fBPullDelete\fR\ \fBPush\fR" will propagate
|
||||||
message arrivals and deletions from the Master to the Slave and any changes
|
message arrivals and deletions from the far side to the near side and any
|
||||||
from the Slave to the Master.
|
changes from the near side to the far side.
|
||||||
Note that it is not allowed to assert a cell in two ways, e.g.
|
Note that it is not allowed to assert a cell in two ways, e.g.
|
||||||
"\fBSync\fR\ \fBPullNew\fR\ \fBPull\fR" and
|
"\fBSync\fR\ \fBPullNew\fR\ \fBPull\fR" and
|
||||||
"\fBSync\fR\ \fBPullNew\fR\ \fBDelete\fR\ \fBPush\fR" induce error messages.
|
"\fBSync\fR\ \fBPullNew\fR\ \fBDelete\fR\ \fBPush\fR" induce error messages.
|
||||||
.
|
.
|
||||||
.TP
|
.TP
|
||||||
\fBCreate\fR {\fBNone\fR|\fBMaster\fR|\fBSlave\fR|\fBBoth\fR}
|
\fBCreate\fR {\fBNone\fR|\fBFar\fR|\fBNear\fR|\fBBoth\fR}
|
||||||
Automatically create missing mailboxes [on the Master/Slave].
|
Automatically create missing mailboxes [on the far/near side].
|
||||||
Otherwise print an error message and skip that mailbox pair if a mailbox
|
Otherwise print an error message and skip that mailbox pair if a mailbox
|
||||||
and the corresponding sync state does not exist.
|
and the corresponding sync state does not exist.
|
||||||
(Global default: \fBNone\fR)
|
(Global default: \fBNone\fR)
|
||||||
.
|
.
|
||||||
.TP
|
.TP
|
||||||
\fBRemove\fR {\fBNone\fR|\fBMaster\fR|\fBSlave\fR|\fBBoth\fR}
|
\fBRemove\fR {\fBNone\fR|\fBFar\fR|\fBNear\fR|\fBBoth\fR}
|
||||||
Propagate mailbox deletions [to the Master/Slave].
|
Propagate mailbox deletions [to the far/near side].
|
||||||
Otherwise print an error message and skip that mailbox pair if a mailbox
|
Otherwise print an error message and skip that mailbox pair if a mailbox
|
||||||
does not exist but the corresponding sync state does.
|
does not exist but the corresponding sync state does.
|
||||||
.br
|
.br
|
||||||
|
@ -640,8 +640,8 @@ Note that for safety, non-empty mailboxes are never deleted.
|
||||||
(Global default: \fBNone\fR)
|
(Global default: \fBNone\fR)
|
||||||
.
|
.
|
||||||
.TP
|
.TP
|
||||||
\fBExpunge\fR {\fBNone\fR|\fBMaster\fR|\fBSlave\fR|\fBBoth\fR}
|
\fBExpunge\fR {\fBNone\fR|\fBFar\fR|\fBNear\fR|\fBBoth\fR}
|
||||||
Permanently remove all messages [on the Master/Slave] marked for deletion.
|
Permanently remove all messages [on the far/near side] marked for deletion.
|
||||||
See \fBRECOMMENDATIONS\fR below.
|
See \fBRECOMMENDATIONS\fR below.
|
||||||
(Global default: \fBNone\fR)
|
(Global default: \fBNone\fR)
|
||||||
.
|
.
|
||||||
|
@ -666,15 +666,15 @@ which in turn are overridden by command line switches.
|
||||||
\fBSyncState\fR {\fB*\fR|\fIpath\fR}
|
\fBSyncState\fR {\fB*\fR|\fIpath\fR}
|
||||||
Set the location of this Channel's synchronization state files.
|
Set the location of this Channel's synchronization state files.
|
||||||
\fB*\fR means that the state should be saved in a file named .mbsyncstate
|
\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
|
to handle the state file separately if you delete the mailbox, but it works
|
||||||
only with Maildir mailboxes, obviously.
|
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.
|
name to make up a complete path.
|
||||||
.br
|
.br
|
||||||
This option can be used outside any section for a global effect. In this case
|
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
|
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).
|
(see also \fBFieldDelimiter\fR below).
|
||||||
.br
|
.br
|
||||||
(Global default: \fI~/.mbsync/\fR).
|
(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:
|
counters by default. The output will look like this:
|
||||||
.P
|
.P
|
||||||
.in +4
|
.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
|
.in -4
|
||||||
.P
|
.P
|
||||||
This represents the cumulative progress over channels, boxes, and messages
|
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,
|
The message counts represent added messages, messages with updated flags,
|
||||||
and trashed messages, respectively.
|
and trashed messages, respectively.
|
||||||
No attempt is made to calculate the totals in advance, so they grow over
|
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"
|
#PassCmd "echo -ne 'GET myIsp\\tpassword' | pwmc datafile"
|
||||||
|
|
||||||
Channel work
|
Channel work
|
||||||
Master :work:
|
Far :work:
|
||||||
Slave :local:work
|
Near :local:work
|
||||||
Expunge Slave
|
Expunge Near
|
||||||
Sync PullNew Push
|
Sync PullNew Push
|
||||||
|
|
||||||
|
|
||||||
|
@ -35,8 +35,8 @@ Port 6789
|
||||||
RequireSSL no
|
RequireSSL no
|
||||||
|
|
||||||
Channel personal
|
Channel personal
|
||||||
Master :personal:
|
Far :personal:
|
||||||
Slave :local:personal
|
Near :local:personal
|
||||||
Expunge Both
|
Expunge Both
|
||||||
MaxMessages 150
|
MaxMessages 150
|
||||||
MaxSize 200k
|
MaxSize 200k
|
||||||
|
@ -45,8 +45,8 @@ IMAPStore remote
|
||||||
Tunnel "ssh -q host.remote.com /usr/sbin/imapd"
|
Tunnel "ssh -q host.remote.com /usr/sbin/imapd"
|
||||||
|
|
||||||
Channel remote
|
Channel remote
|
||||||
Master :remote:
|
Far :remote:
|
||||||
Slave :local:remote
|
Near :local:remote
|
||||||
|
|
||||||
|
|
||||||
Group boxes
|
Group boxes
|
||||||
|
@ -65,8 +65,8 @@ RequireSSL no
|
||||||
UseTLSv1 no
|
UseTLSv1 no
|
||||||
|
|
||||||
Channel rst
|
Channel rst
|
||||||
Master :st1:somebox
|
Far :st1:somebox
|
||||||
Slave :st2:
|
Near :st2:
|
||||||
|
|
||||||
|
|
||||||
IMAPAccount server
|
IMAPAccount server
|
||||||
|
@ -84,14 +84,14 @@ Path ~/Maildir/
|
||||||
SubFolders Verbatim
|
SubFolders Verbatim
|
||||||
|
|
||||||
Channel o2o
|
Channel o2o
|
||||||
Master :server:
|
Far :server:
|
||||||
Slave :mirror:
|
Near :mirror:
|
||||||
Patterns %
|
Patterns %
|
||||||
|
|
||||||
Group partial o2o:inbox,sent-mail,foobar
|
Group partial o2o:inbox,sent-mail,foobar
|
||||||
|
|
||||||
# INBOX => server, INBOX.foo => server.foo, etc.
|
# INBOX => server, INBOX.foo => server.foo, etc.
|
||||||
Channel inbox
|
Channel inbox
|
||||||
Master :server:INBOX
|
Far :server:INBOX
|
||||||
Slave :mirror:server
|
Near :mirror:server
|
||||||
Patterns *
|
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 ]
|
# Format of the test defs: [ far, near, state ]
|
||||||
# master/slave: [ maxuid, { seq, uid, flags }... ]
|
# far/near: [ maxuid, { seq, uid, flags }... ]
|
||||||
# state: [ MaxPulledUid, MaxExpiredMasterUid, MaxPushedUid, { muid, suid, flags }... ]
|
# state: [ MaxPulledUid, MaxExpiredFarUid, MaxPushedUid, { muid, suid, flags }... ]
|
||||||
|
|
||||||
use enum qw(:=1 A..Z);
|
use enum qw(:=1 A..Z);
|
||||||
sub mn($) { chr(64 + shift) }
|
sub mn($) { chr(64 + shift) }
|
||||||
|
@ -78,7 +78,7 @@ my @X02 = (
|
||||||
);
|
);
|
||||||
test("full + expunge both", \@x01, \@X02, @O02);
|
test("full + expunge both", \@x01, \@X02, @O02);
|
||||||
|
|
||||||
my @O03 = ("", "", "Expunge Slave\n");
|
my @O03 = ("", "", "Expunge Near\n");
|
||||||
#show("01", "03", "03");
|
#show("01", "03", "03");
|
||||||
my @X03 = (
|
my @X03 = (
|
||||||
[ 10,
|
[ 10,
|
||||||
|
@ -88,7 +88,7 @@ my @X03 = (
|
||||||
[ 9, 0, 9,
|
[ 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, "" ],
|
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");
|
my @O04 = ("", "", "Sync Pull\n");
|
||||||
#show("01", "04", "04");
|
#show("01", "04", "04");
|
||||||
|
@ -183,7 +183,7 @@ my @X22 = (
|
||||||
[ 2, 0, 1,
|
[ 2, 0, 1,
|
||||||
3, 1, "", 1, 2, "", 2, 0, "^" ],
|
3, 1, "", 1, 2, "", 2, 0, "^" ],
|
||||||
);
|
);
|
||||||
test("slave max size", \@X11, \@X22, @O22);
|
test("near side max size", \@X11, \@X22, @O22);
|
||||||
|
|
||||||
# expiration tests
|
# expiration tests
|
||||||
|
|
||||||
|
@ -258,7 +258,7 @@ sub qm($)
|
||||||
return $_;
|
return $_;
|
||||||
}
|
}
|
||||||
|
|
||||||
# $master, $slave, $channel
|
# $far, $near, $channel
|
||||||
sub writecfg($$$)
|
sub writecfg($$$)
|
||||||
{
|
{
|
||||||
open(FILE, ">", ".mbsyncrc") or
|
open(FILE, ">", ".mbsyncrc") or
|
||||||
|
@ -266,17 +266,17 @@ sub writecfg($$$)
|
||||||
print FILE
|
print FILE
|
||||||
"FSync no
|
"FSync no
|
||||||
|
|
||||||
MaildirStore master
|
MaildirStore far
|
||||||
Path ./
|
Path ./
|
||||||
Inbox ./master
|
Inbox ./far
|
||||||
".shift()."
|
".shift()."
|
||||||
MaildirStore slave
|
MaildirStore near
|
||||||
Path ./
|
Path ./
|
||||||
Inbox ./slave
|
Inbox ./near
|
||||||
".shift()."
|
".shift()."
|
||||||
Channel test
|
Channel test
|
||||||
Master :master:
|
Far :far:
|
||||||
Slave :slave:
|
Near :near:
|
||||||
SyncState *
|
SyncState *
|
||||||
".shift();
|
".shift();
|
||||||
close FILE;
|
close FILE;
|
||||||
|
@ -371,8 +371,8 @@ sub showbox($)
|
||||||
|
|
||||||
# $filename
|
# $filename
|
||||||
# Output:
|
# Output:
|
||||||
# [ maxuid[M], mmaxxuid, maxuid[S],
|
# [ maxuid[F], maxxfuid, maxuid[N],
|
||||||
# uid[M], uid[S], "flags", ... ],
|
# uid[F], uid[N], "flags", ... ],
|
||||||
sub showstate($)
|
sub showstate($)
|
||||||
{
|
{
|
||||||
my ($fn) = @_;
|
my ($fn) = @_;
|
||||||
|
@ -400,7 +400,7 @@ sub showstate($)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
my @T = ($hdr{'MaxPulledUid'} // "missing",
|
my @T = ($hdr{'MaxPulledUid'} // "missing",
|
||||||
$hdr{'MaxExpiredMasterUid'} // "0",
|
$hdr{'MaxExpiredFarUid'} // "0",
|
||||||
$hdr{'MaxPushedUid'} // "missing");
|
$hdr{'MaxPushedUid'} // "missing");
|
||||||
for (@ls) {
|
for (@ls) {
|
||||||
/^(\d+) (\d+) (.*)$/;
|
/^(\d+) (\d+) (.*)$/;
|
||||||
|
@ -414,8 +414,8 @@ sub showchan($)
|
||||||
{
|
{
|
||||||
my ($fn) = @_;
|
my ($fn) = @_;
|
||||||
|
|
||||||
showbox("master");
|
showbox("far");
|
||||||
showbox("slave");
|
showbox("near");
|
||||||
showstate($fn);
|
showstate($fn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -428,17 +428,17 @@ sub show($$$)
|
||||||
eval "\@sfx = \@O$sfxn";
|
eval "\@sfx = \@O$sfxn";
|
||||||
mkchan($sp[0], $sp[1], @{ $sp[2] });
|
mkchan($sp[0], $sp[1], @{ $sp[2] });
|
||||||
print "my \@x$sx = (\n";
|
print "my \@x$sx = (\n";
|
||||||
showchan("slave/.mbsyncstate");
|
showchan("near/.mbsyncstate");
|
||||||
print ");\n";
|
print ");\n";
|
||||||
&writecfg(@sfx);
|
&writecfg(@sfx);
|
||||||
runsync("", "");
|
runsync("", "");
|
||||||
killcfg();
|
killcfg();
|
||||||
print "my \@X$tx = (\n";
|
print "my \@X$tx = (\n";
|
||||||
showchan("slave/.mbsyncstate");
|
showchan("near/.mbsyncstate");
|
||||||
print ");\n";
|
print ");\n";
|
||||||
print "test(\"\", \\\@x$sx, \\\@X$tx, \@O$sfxn);\n\n";
|
print "test(\"\", \\\@x$sx, \\\@X$tx, \@O$sfxn);\n\n";
|
||||||
rmtree "slave";
|
rmtree "near";
|
||||||
rmtree "master";
|
rmtree "far";
|
||||||
}
|
}
|
||||||
|
|
||||||
# $boxname, $maxuid, @msgs
|
# $boxname, $maxuid, @msgs
|
||||||
|
@ -462,16 +462,16 @@ sub mkbox($$@)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# \@master, \@slave, @syncstate
|
# \@far, \@near, @syncstate
|
||||||
sub mkchan($$@)
|
sub mkchan($$@)
|
||||||
{
|
{
|
||||||
my ($m, $s, @t) = @_;
|
my ($m, $s, @t) = @_;
|
||||||
&mkbox("master", @{ $m });
|
&mkbox("far", @{ $m });
|
||||||
&mkbox("slave", @{ $s });
|
&mkbox("near", @{ $s });
|
||||||
open(FILE, ">", "slave/.mbsyncstate") or
|
open(FILE, ">", "near/.mbsyncstate") or
|
||||||
die "Cannot create sync state.\n";
|
die "Cannot create sync state.\n";
|
||||||
print FILE "MasterUidValidity 1\nMaxPulledUid ".shift(@t)."\n".
|
print FILE "FarUidValidity 1\nMaxPulledUid ".shift(@t)."\n".
|
||||||
"SlaveUidValidity 1\nMaxExpiredMasterUid ".shift(@t)."\nMaxPushedUid ".shift(@t)."\n\n";
|
"NearUidValidity 1\nMaxExpiredFarUid ".shift(@t)."\nMaxPushedUid ".shift(@t)."\n\n";
|
||||||
while (@t) {
|
while (@t) {
|
||||||
print FILE shift(@t)." ".shift(@t)." ".shift(@t)."\n";
|
print FILE shift(@t)." ".shift(@t)." ".shift(@t)."\n";
|
||||||
}
|
}
|
||||||
|
@ -514,13 +514,13 @@ sub ckbox($$@)
|
||||||
# $filename, @syncstate
|
# $filename, @syncstate
|
||||||
sub ckstate($@)
|
sub ckstate($@)
|
||||||
{
|
{
|
||||||
my ($fn, $mmaxuid, $mmaxxuid, $smaxuid, @T) = @_;
|
my ($fn, $fmaxuid, $maxxfuid, $nmaxuid, @T) = @_;
|
||||||
my %hdr;
|
my %hdr;
|
||||||
$hdr{'MasterUidValidity'} = "1";
|
$hdr{'FarUidValidity'} = "1";
|
||||||
$hdr{'SlaveUidValidity'} = "1";
|
$hdr{'NearUidValidity'} = "1";
|
||||||
$hdr{'MaxPulledUid'} = $mmaxuid;
|
$hdr{'MaxPulledUid'} = $fmaxuid;
|
||||||
$hdr{'MaxPushedUid'} = $smaxuid;
|
$hdr{'MaxPushedUid'} = $nmaxuid;
|
||||||
$hdr{'MaxExpiredMasterUid'} = $mmaxxuid if ($mmaxxuid ne 0);
|
$hdr{'MaxExpiredFarUid'} = $maxxfuid if ($maxxfuid ne 0);
|
||||||
open(FILE, "<", $fn) or die "Cannot read sync state $fn.\n";
|
open(FILE, "<", $fn) or die "Cannot read sync state $fn.\n";
|
||||||
chomp(my @ls = <FILE>);
|
chomp(my @ls = <FILE>);
|
||||||
close FILE;
|
close FILE;
|
||||||
|
@ -573,8 +573,8 @@ sub ckchan($$)
|
||||||
{
|
{
|
||||||
my ($F, $cs) = @_;
|
my ($F, $cs) = @_;
|
||||||
my $rslt = ckstate($F, @{ $$cs[2] });
|
my $rslt = ckstate($F, @{ $$cs[2] });
|
||||||
$rslt |= &ckbox("master", @{ $$cs[0] });
|
$rslt |= &ckbox("far", @{ $$cs[0] });
|
||||||
$rslt |= &ckbox("slave", @{ $$cs[1] });
|
$rslt |= &ckbox("near", @{ $$cs[1] });
|
||||||
return $rslt;
|
return $rslt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -618,8 +618,8 @@ sub printchan($)
|
||||||
{
|
{
|
||||||
my ($cs) = @_;
|
my ($cs) = @_;
|
||||||
|
|
||||||
&printbox("master", @{ $$cs[0] });
|
&printbox("far", @{ $$cs[0] });
|
||||||
&printbox("slave", @{ $$cs[1] });
|
&printbox("near", @{ $$cs[1] });
|
||||||
printstate(@{ $$cs[2] });
|
printstate(@{ $$cs[2] });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -645,7 +645,7 @@ sub test($$$@)
|
||||||
mkchan($$sx[0], $$sx[1], @{ $$sx[2] });
|
mkchan($$sx[0], $$sx[1], @{ $$sx[2] });
|
||||||
|
|
||||||
my ($xc, @ret) = runsync("-J", "1-initial.log");
|
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";
|
print "Input:\n";
|
||||||
printchan($sx);
|
printchan($sx);
|
||||||
print "Options:\n";
|
print "Options:\n";
|
||||||
|
@ -654,16 +654,16 @@ sub test($$$@)
|
||||||
print "Expected result:\n";
|
print "Expected result:\n";
|
||||||
printchan($tx);
|
printchan($tx);
|
||||||
print "Actual result:\n";
|
print "Actual result:\n";
|
||||||
showchan("slave/.mbsyncstate.new");
|
showchan("near/.mbsyncstate.new");
|
||||||
}
|
}
|
||||||
print "Debug output:\n";
|
print "Debug output:\n";
|
||||||
print @ret;
|
print @ret;
|
||||||
exit 1;
|
exit 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
my @nj = readfile("slave/.mbsyncstate.journal");
|
my @nj = readfile("near/.mbsyncstate.journal");
|
||||||
my ($jxc, @jret) = runsync("-0 --no-expunge", "2-replay.log");
|
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 "Journal replay failed.\n";
|
||||||
print "Options:\n";
|
print "Options:\n";
|
||||||
print " [ ".join(", ", map('"'.qm($_).'"', @sfx))." ], [ \"-0\", \"--no-expunge\" ]\n";
|
print " [ ".join(", ", map('"'.qm($_).'"', @sfx))." ], [ \"-0\", \"--no-expunge\" ]\n";
|
||||||
|
@ -674,7 +674,7 @@ sub test($$$@)
|
||||||
print "Expected New State:\n";
|
print "Expected New State:\n";
|
||||||
printstate(@{ $$tx[2] });
|
printstate(@{ $$tx[2] });
|
||||||
print "New State:\n";
|
print "New State:\n";
|
||||||
showstate("slave/.mbsyncstate");
|
showstate("near/.mbsyncstate");
|
||||||
}
|
}
|
||||||
print "Debug output:\n";
|
print "Debug output:\n";
|
||||||
print @jret;
|
print @jret;
|
||||||
|
@ -682,7 +682,7 @@ sub test($$$@)
|
||||||
}
|
}
|
||||||
|
|
||||||
my ($ixc, @iret) = runsync("", "3-verify.log");
|
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 "Idempotence verification run failed.\n";
|
||||||
print "Input == Expected result:\n";
|
print "Input == Expected result:\n";
|
||||||
printchan($tx);
|
printchan($tx);
|
||||||
|
@ -690,15 +690,15 @@ sub test($$$@)
|
||||||
print " [ ".join(", ", map('"'.qm($_).'"', @sfx))." ]\n";
|
print " [ ".join(", ", map('"'.qm($_).'"', @sfx))." ]\n";
|
||||||
if (!$ixc) {
|
if (!$ixc) {
|
||||||
print "Actual result:\n";
|
print "Actual result:\n";
|
||||||
showchan("slave/.mbsyncstate");
|
showchan("near/.mbsyncstate");
|
||||||
}
|
}
|
||||||
print "Debug output:\n";
|
print "Debug output:\n";
|
||||||
print @iret;
|
print @iret;
|
||||||
exit 1;
|
exit 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
rmtree "slave";
|
rmtree "near";
|
||||||
rmtree "master";
|
rmtree "far";
|
||||||
|
|
||||||
my $njl = (@nj - 1) * 2;
|
my $njl = (@nj - 1) * 2;
|
||||||
for (my $l = 2; $l < $njl; $l++) {
|
for (my $l = 2; $l < $njl; $l++) {
|
||||||
|
@ -713,28 +713,28 @@ sub test($$$@)
|
||||||
}
|
}
|
||||||
|
|
||||||
($nxc, @nret) = runsync("-J", "5-resume.log");
|
($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 "Resuming from step $l/$njl failed.\n";
|
||||||
print "Input:\n";
|
print "Input:\n";
|
||||||
printchan($sx);
|
printchan($sx);
|
||||||
print "Options:\n";
|
print "Options:\n";
|
||||||
print " [ ".join(", ", map('"'.qm($_).'"', @sfx))." ]\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 "Journal:\n".join("", @nnj[0..($l / 2 - 1)])."-------\n".join("", @nnj[($l / 2)..$#nnj])."\n";
|
||||||
print "Full journal:\n".join("", @nj)."\n";
|
print "Full journal:\n".join("", @nj)."\n";
|
||||||
if (!$nxc) {
|
if (!$nxc) {
|
||||||
print "Expected result:\n";
|
print "Expected result:\n";
|
||||||
printchan($tx);
|
printchan($tx);
|
||||||
print "Actual result:\n";
|
print "Actual result:\n";
|
||||||
showchan("slave/.mbsyncstate.new");
|
showchan("near/.mbsyncstate.new");
|
||||||
}
|
}
|
||||||
print "Debug output:\n";
|
print "Debug output:\n";
|
||||||
print @nret;
|
print @nret;
|
||||||
exit 1;
|
exit 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
rmtree "slave";
|
rmtree "near";
|
||||||
rmtree "master";
|
rmtree "far";
|
||||||
}
|
}
|
||||||
|
|
||||||
killcfg();
|
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"
|
#include "driver.h"
|
||||||
|
|
||||||
#define M 0 /* master */
|
#define F 0 // far side
|
||||||
#define S 1 /* slave */
|
#define N 1 // near side
|
||||||
|
|
||||||
#define OP_NEW (1<<0)
|
#define OP_NEW (1<<0)
|
||||||
#define OP_RENEW (1<<1)
|
#define OP_RENEW (1<<1)
|
||||||
|
@ -52,7 +52,7 @@ typedef struct channel_conf {
|
||||||
char *sync_state;
|
char *sync_state;
|
||||||
string_list_t *patterns;
|
string_list_t *patterns;
|
||||||
int ops[2];
|
int ops[2];
|
||||||
int max_messages; /* for slave only */
|
int max_messages; // For near side only.
|
||||||
signed char expire_unread;
|
signed char expire_unread;
|
||||||
char use_internal_date;
|
char use_internal_date;
|
||||||
} channel_conf_t;
|
} channel_conf_t;
|
||||||
|
@ -67,11 +67,11 @@ extern channel_conf_t global_conf;
|
||||||
extern channel_conf_t *channels;
|
extern channel_conf_t *channels;
|
||||||
extern group_conf_t *groups;
|
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_OK 0 /* assumed to be 0 */
|
||||||
#define SYNC_FAIL 1
|
#define SYNC_FAIL 1
|
||||||
#define SYNC_BAD(ms) (4<<(ms))
|
#define SYNC_BAD(fn) (4<<(fn))
|
||||||
#define SYNC_NOGOOD 16 /* internal */
|
#define SYNC_NOGOOD 16 /* internal */
|
||||||
#define SYNC_CANCELED 32 /* internal */
|
#define SYNC_CANCELED 32 /* internal */
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user